1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *    http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.apache.arrow.gandiva.expression;
19 
20 import org.apache.arrow.flatbuf.DateUnit;
21 import org.apache.arrow.flatbuf.IntervalUnit;
22 import org.apache.arrow.flatbuf.TimeUnit;
23 import org.apache.arrow.flatbuf.Type;
24 import org.apache.arrow.gandiva.exceptions.GandivaException;
25 import org.apache.arrow.gandiva.exceptions.UnsupportedTypeException;
26 import org.apache.arrow.gandiva.ipc.GandivaTypes;
27 import org.apache.arrow.util.Preconditions;
28 import org.apache.arrow.vector.types.pojo.ArrowType;
29 import org.apache.arrow.vector.types.pojo.Field;
30 import org.apache.arrow.vector.types.pojo.Schema;
31 
32 /**
33  * Utility methods to convert between Arrow and Gandiva types.
34  */
35 public class ArrowTypeHelper {
ArrowTypeHelper()36   private ArrowTypeHelper() {}
37 
38   static final int WIDTH_8 = 8;
39   static final int WIDTH_16 = 16;
40   static final int WIDTH_32 = 32;
41   static final int WIDTH_64 = 64;
42 
initArrowTypeInt( ArrowType.Int intType, GandivaTypes.ExtGandivaType.Builder builder)43   private static void initArrowTypeInt(
44       ArrowType.Int intType, GandivaTypes.ExtGandivaType.Builder builder) throws GandivaException {
45     int width = intType.getBitWidth();
46 
47     if (intType.getIsSigned()) {
48       switch (width) {
49         case WIDTH_8: {
50           builder.setType(GandivaTypes.GandivaType.INT8);
51           return;
52         }
53         case WIDTH_16: {
54           builder.setType(GandivaTypes.GandivaType.INT16);
55           return;
56         }
57         case WIDTH_32: {
58           builder.setType(GandivaTypes.GandivaType.INT32);
59           return;
60         }
61         case WIDTH_64: {
62           builder.setType(GandivaTypes.GandivaType.INT64);
63           return;
64         }
65         default: {
66           throw new UnsupportedTypeException("Unsupported width for integer type");
67         }
68       }
69     }
70 
71     // unsigned int
72     switch (width) {
73       case WIDTH_8: {
74         builder.setType(GandivaTypes.GandivaType.UINT8);
75         return;
76       }
77       case WIDTH_16: {
78         builder.setType(GandivaTypes.GandivaType.UINT16);
79         return;
80       }
81       case WIDTH_32: {
82         builder.setType(GandivaTypes.GandivaType.UINT32);
83         return;
84       }
85       case WIDTH_64: {
86         builder.setType(GandivaTypes.GandivaType.UINT64);
87         return;
88       }
89       default: {
90         throw new UnsupportedTypeException("Unsupported width for integer type");
91       }
92     }
93   }
94 
initArrowTypeFloat( ArrowType.FloatingPoint floatType, GandivaTypes.ExtGandivaType.Builder builder)95   private static void initArrowTypeFloat(
96       ArrowType.FloatingPoint floatType, GandivaTypes.ExtGandivaType.Builder builder)
97       throws GandivaException {
98     switch (floatType.getPrecision()) {
99       case HALF: {
100         builder.setType(GandivaTypes.GandivaType.HALF_FLOAT);
101         break;
102       }
103       case SINGLE: {
104         builder.setType(GandivaTypes.GandivaType.FLOAT);
105         break;
106       }
107       case DOUBLE: {
108         builder.setType(GandivaTypes.GandivaType.DOUBLE);
109         break;
110       }
111       default: {
112         throw new UnsupportedTypeException("Floating point type with unknown precision");
113       }
114     }
115   }
116 
initArrowTypeDecimal(ArrowType.Decimal decimalType, GandivaTypes.ExtGandivaType.Builder builder)117   private static void initArrowTypeDecimal(ArrowType.Decimal decimalType,
118                                            GandivaTypes.ExtGandivaType.Builder builder) {
119     Preconditions.checkArgument(decimalType.getPrecision() > 0 &&
120             decimalType.getPrecision() <= 38, "Gandiva only supports decimals of upto 38 " +
121             "precision. Input precision : " + decimalType.getPrecision());
122     builder.setPrecision(decimalType.getPrecision());
123     builder.setScale(decimalType.getScale());
124     builder.setType(GandivaTypes.GandivaType.DECIMAL);
125   }
126 
initArrowTypeDate(ArrowType.Date dateType, GandivaTypes.ExtGandivaType.Builder builder)127   private static void initArrowTypeDate(ArrowType.Date dateType,
128                                         GandivaTypes.ExtGandivaType.Builder builder) {
129     short dateUnit = dateType.getUnit().getFlatbufID();
130     switch (dateUnit) {
131       case DateUnit.DAY: {
132         builder.setType(GandivaTypes.GandivaType.DATE32);
133         break;
134       }
135       case DateUnit.MILLISECOND: {
136         builder.setType(GandivaTypes.GandivaType.DATE64);
137         break;
138       }
139       default: {
140         // not supported
141         break;
142       }
143     }
144   }
145 
initArrowTypeTime(ArrowType.Time timeType, GandivaTypes.ExtGandivaType.Builder builder)146   private static void initArrowTypeTime(ArrowType.Time timeType,
147                                         GandivaTypes.ExtGandivaType.Builder builder) {
148     short timeUnit = timeType.getUnit().getFlatbufID();
149     switch (timeUnit) {
150       case TimeUnit.SECOND: {
151         builder.setType(GandivaTypes.GandivaType.TIME32);
152         builder.setTimeUnit(GandivaTypes.TimeUnit.SEC);
153         break;
154       }
155       case TimeUnit.MILLISECOND: {
156         builder.setType(GandivaTypes.GandivaType.TIME32);
157         builder.setTimeUnit(GandivaTypes.TimeUnit.MILLISEC);
158         break;
159       }
160       case TimeUnit.MICROSECOND: {
161         builder.setType(GandivaTypes.GandivaType.TIME64);
162         builder.setTimeUnit(GandivaTypes.TimeUnit.MICROSEC);
163         break;
164       }
165       case TimeUnit.NANOSECOND: {
166         builder.setType(GandivaTypes.GandivaType.TIME64);
167         builder.setTimeUnit(GandivaTypes.TimeUnit.NANOSEC);
168         break;
169       }
170       default: {
171         // not supported
172       }
173     }
174   }
175 
initArrowTypeTimestamp(ArrowType.Timestamp timestampType, GandivaTypes.ExtGandivaType.Builder builder)176   private static void initArrowTypeTimestamp(ArrowType.Timestamp timestampType,
177                                              GandivaTypes.ExtGandivaType.Builder builder) {
178     short timeUnit = timestampType.getUnit().getFlatbufID();
179     switch (timeUnit) {
180       case TimeUnit.SECOND: {
181         builder.setType(GandivaTypes.GandivaType.TIMESTAMP);
182         builder.setTimeUnit(GandivaTypes.TimeUnit.SEC);
183         break;
184       }
185       case TimeUnit.MILLISECOND: {
186         builder.setType(GandivaTypes.GandivaType.TIMESTAMP);
187         builder.setTimeUnit(GandivaTypes.TimeUnit.MILLISEC);
188         break;
189       }
190       case TimeUnit.MICROSECOND: {
191         builder.setType(GandivaTypes.GandivaType.TIMESTAMP);
192         builder.setTimeUnit(GandivaTypes.TimeUnit.MICROSEC);
193         break;
194       }
195       case TimeUnit.NANOSECOND: {
196         builder.setType(GandivaTypes.GandivaType.TIMESTAMP);
197         builder.setTimeUnit(GandivaTypes.TimeUnit.NANOSEC);
198         break;
199       }
200       default: {
201         // not supported
202       }
203     }
204   }
205 
initArrowTypeInterval(ArrowType.Interval interval, GandivaTypes.ExtGandivaType.Builder builder)206   private static void initArrowTypeInterval(ArrowType.Interval interval,
207                                              GandivaTypes.ExtGandivaType.Builder builder) {
208     short intervalUnit = interval.getUnit().getFlatbufID();
209     switch (intervalUnit) {
210       case IntervalUnit.YEAR_MONTH: {
211         builder.setType(GandivaTypes.GandivaType.INTERVAL);
212         builder.setIntervalType(GandivaTypes.IntervalType.YEAR_MONTH);
213         break;
214       }
215       case IntervalUnit.DAY_TIME: {
216         builder.setType(GandivaTypes.GandivaType.INTERVAL);
217         builder.setIntervalType(GandivaTypes.IntervalType.DAY_TIME);
218         break;
219       }
220       default: {
221         // not supported
222       }
223     }
224   }
225 
226   /**
227    * Converts an arrow type into a protobuf.
228    *
229    * @param arrowType Arrow type to be converted
230    * @return Protobuf representing the arrow type
231    */
arrowTypeToProtobuf(ArrowType arrowType)232   public static GandivaTypes.ExtGandivaType arrowTypeToProtobuf(ArrowType arrowType)
233       throws GandivaException {
234     GandivaTypes.ExtGandivaType.Builder builder = GandivaTypes.ExtGandivaType.newBuilder();
235 
236     byte typeId = arrowType.getTypeID().getFlatbufID();
237     switch (typeId) {
238       case Type.NONE: { // 0
239         builder.setType(GandivaTypes.GandivaType.NONE);
240         break;
241       }
242       case Type.Null: { // 1
243         // TODO: Need to handle this later
244         break;
245       }
246       case Type.Int: { // 2
247         ArrowTypeHelper.initArrowTypeInt((ArrowType.Int) arrowType, builder);
248         break;
249       }
250       case Type.FloatingPoint: { // 3
251         ArrowTypeHelper.initArrowTypeFloat((ArrowType.FloatingPoint) arrowType, builder);
252         break;
253       }
254       case Type.Binary: { // 4
255         builder.setType(GandivaTypes.GandivaType.BINARY);
256         break;
257       }
258       case Type.Utf8: { // 5
259         builder.setType(GandivaTypes.GandivaType.UTF8);
260         break;
261       }
262       case Type.Bool: { // 6
263         builder.setType(GandivaTypes.GandivaType.BOOL);
264         break;
265       }
266       case Type.Decimal: { // 7
267         ArrowTypeHelper.initArrowTypeDecimal((ArrowType.Decimal) arrowType, builder);
268         break;
269       }
270       case Type.Date: { // 8
271         ArrowTypeHelper.initArrowTypeDate((ArrowType.Date) arrowType, builder);
272         break;
273       }
274       case Type.Time: { // 9
275         ArrowTypeHelper.initArrowTypeTime((ArrowType.Time) arrowType, builder);
276         break;
277       }
278       case Type.Timestamp: { // 10
279         ArrowTypeHelper.initArrowTypeTimestamp((ArrowType.Timestamp) arrowType, builder);
280         break;
281       }
282       case Type.Interval: { // 11
283         ArrowTypeHelper.initArrowTypeInterval((ArrowType.Interval) arrowType, builder);
284         break;
285       }
286       case Type.List: { // 12
287         break;
288       }
289       case Type.Struct_: { // 13
290         break;
291       }
292       case Type.Union: { // 14
293         break;
294       }
295       case Type.FixedSizeBinary: { // 15
296         break;
297       }
298       case Type.FixedSizeList: { // 16
299         break;
300       }
301       case Type.Map: { // 17
302         break;
303       }
304       default: {
305         break;
306       }
307     }
308 
309     if (!builder.hasType()) {
310       // type has not been set
311       // throw an exception
312       throw new UnsupportedTypeException("Unsupported type " + arrowType.toString());
313     }
314 
315     return builder.build();
316   }
317 
318   /**
319    * Converts an arrow field object to a protobuf.
320    * @param field Arrow field to be converted
321    * @return Protobuf representing the arrow field
322    */
arrowFieldToProtobuf(Field field)323   public static GandivaTypes.Field arrowFieldToProtobuf(Field field) throws GandivaException {
324     GandivaTypes.Field.Builder builder = GandivaTypes.Field.newBuilder();
325     builder.setName(field.getName());
326     builder.setType(ArrowTypeHelper.arrowTypeToProtobuf(field.getType()));
327     builder.setNullable(field.isNullable());
328 
329     for (Field child : field.getChildren()) {
330       builder.addChildren(ArrowTypeHelper.arrowFieldToProtobuf(child));
331     }
332 
333     return builder.build();
334   }
335 
336   /**
337    * Converts a schema object to a protobuf.
338    * @param schema Schema object to be converted
339    * @return Protobuf representing a schema object
340    */
arrowSchemaToProtobuf(Schema schema)341   public static GandivaTypes.Schema arrowSchemaToProtobuf(Schema schema) throws GandivaException {
342     GandivaTypes.Schema.Builder builder = GandivaTypes.Schema.newBuilder();
343 
344     for (Field field : schema.getFields()) {
345       builder.addColumns(ArrowTypeHelper.arrowFieldToProtobuf(field));
346     }
347 
348     return builder.build();
349   }
350 }
351