14
14
import org .apache .calcite .sql .type .SqlTypeFactoryImpl ;
15
15
import org .apache .calcite .sql .type .SqlTypeName ;
16
16
17
-
18
17
/** Converts between Avro and Calcite's RelDataType */
19
18
public final class AvroConverter {
20
19
@@ -23,13 +22,11 @@ private AvroConverter() {
23
22
24
23
public static Schema avro (String namespace , String name , RelDataType dataType ) {
25
24
if (dataType .isStruct ()) {
26
- List <Schema .Field > fields = dataType .getFieldList ()
27
- .stream ()
28
- .map (x -> new Schema .Field (sanitize (x .getName ()), avro (namespace , x .getName (), x .getType ()), describe (x ),
29
- null ))
25
+ List <Schema .Field > fields = dataType .getFieldList ().stream ()
26
+ .map (x -> new Schema .Field (sanitize (x .getName ()), avro (namespace , x .getName (), x .getType ()), describe (x ), null ))
30
27
.collect (Collectors .toList ());
31
- return createAvroSchemaWithNullability (
32
- Schema . createRecord ( sanitize ( name ), dataType . toString (), namespace , false , fields ), dataType .isNullable ());
28
+ return createAvroSchemaWithNullability (Schema . createRecord ( sanitize ( name ), dataType . toString (), namespace , false , fields ),
29
+ dataType .isNullable ());
33
30
} else {
34
31
switch (dataType .getSqlTypeName ()) {
35
32
case INTEGER :
@@ -51,10 +48,9 @@ public static Schema avro(String namespace, String name, RelDataType dataType) {
51
48
case ARRAY :
52
49
return createAvroSchemaWithNullability (Schema .createArray (avro (null , null , dataType .getComponentType ())),
53
50
dataType .isNullable ());
54
- // TODO support map types
55
- // Appears to require a Calcite version bump
56
- // case MAP:
57
- // return createAvroSchemaWithNullability(Schema.createMap(avroPrimitive(dataType.getValueType())), dataType.isNullable());
51
+ case MAP :
52
+ return createAvroSchemaWithNullability (Schema .createMap (avro (null , null , dataType .getValueType ())),
53
+ dataType .isNullable ());
58
54
case UNKNOWN :
59
55
case NULL :
60
56
return Schema .createUnion (Schema .create (Schema .Type .NULL ));
@@ -82,55 +78,74 @@ private static Schema createAvroTypeWithNullability(Schema.Type rawType, boolean
82
78
}
83
79
84
80
public static RelDataType rel (Schema schema , RelDataTypeFactory typeFactory ) {
81
+ return rel (schema , typeFactory , false );
82
+ }
83
+
84
+ /** Converts Avro Schema to RelDataType.
85
+ * Nullability is preserved except for array types, JDBC is incapable of interpreting e.g. "FLOAT NOT NULL ARRAY"
86
+ * causing "NOT NULL" arrays to get demoted to "ANY ARRAY" which is not desired.
87
+ */
88
+ public static RelDataType rel (Schema schema , RelDataTypeFactory typeFactory , boolean nullable ) {
85
89
RelDataType unknown = typeFactory .createUnknownType ();
86
90
switch (schema .getType ()) {
87
91
case RECORD :
88
- return typeFactory .createStructType (schema .getFields ()
89
- .stream ()
90
- .map (x -> new AbstractMap .SimpleEntry <>(x .name (), rel (x .schema (), typeFactory )))
92
+ return typeFactory .createTypeWithNullability (typeFactory .createStructType (schema .getFields ().stream ()
93
+ .map (x -> new AbstractMap .SimpleEntry <>(x .name (), rel (x .schema (), typeFactory , nullable )))
91
94
.filter (x -> x .getValue ().getSqlTypeName () != SqlTypeName .NULL )
92
95
.filter (x -> x .getValue ().getSqlTypeName () != unknown .getSqlTypeName ())
93
- .collect (Collectors .toList ()));
96
+ .collect (Collectors .toList ())), nullable ) ;
94
97
case INT :
95
- return createRelType (typeFactory , SqlTypeName .INTEGER );
98
+ return createRelType (typeFactory , SqlTypeName .INTEGER , nullable );
96
99
case LONG :
97
- return createRelType (typeFactory , SqlTypeName .BIGINT );
100
+ return createRelType (typeFactory , SqlTypeName .BIGINT , nullable );
98
101
case ENUM :
99
- case FIXED :
100
102
case STRING :
101
- return createRelType (typeFactory , SqlTypeName .VARCHAR );
103
+ return createRelType (typeFactory , SqlTypeName .VARCHAR , nullable );
104
+ case FIXED :
105
+ return createRelType (typeFactory , SqlTypeName .VARBINARY , schema .getFixedSize (), nullable );
106
+ case BYTES :
107
+ return createRelType (typeFactory , SqlTypeName .VARBINARY , nullable );
102
108
case FLOAT :
103
- return createRelType (typeFactory , SqlTypeName .FLOAT );
109
+ return createRelType (typeFactory , SqlTypeName .FLOAT , nullable );
104
110
case DOUBLE :
105
- return createRelType (typeFactory , SqlTypeName .DOUBLE );
111
+ return createRelType (typeFactory , SqlTypeName .DOUBLE , nullable );
106
112
case BOOLEAN :
107
- return createRelType (typeFactory , SqlTypeName .BOOLEAN );
113
+ return createRelType (typeFactory , SqlTypeName .BOOLEAN , nullable );
108
114
case ARRAY :
109
- return typeFactory .createArrayType ( rel ( schema . getElementType (), typeFactory ), - 1 );
110
- // TODO support map types
111
- // Appears to require a Calcite version bump
112
- // case MAP:
113
- // return typeFactory.createMapType(typeFactory.createSqlType(SqlTypeName.VARCHAR), rel(schema.getValueType(), typeFactory) );
115
+ return typeFactory .createTypeWithNullability (
116
+ typeFactory . createArrayType ( rel ( schema . getElementType (), typeFactory , true ), - 1 ), nullable );
117
+ case MAP :
118
+ return typeFactory . createTypeWithNullability (
119
+ typeFactory .createMapType (typeFactory .createSqlType (SqlTypeName .VARCHAR ), rel (schema .getValueType (), typeFactory , nullable )), nullable );
114
120
case UNION :
121
+ boolean isNullable = schema .isNullable ();
115
122
if (schema .isNullable () && schema .getTypes ().size () == 2 ) {
116
123
Schema innerType = schema .getTypes ().stream ().filter (x -> x .getType () != Schema .Type .NULL ).findFirst ().get ();
117
- return typeFactory .createTypeWithNullability (rel (innerType , typeFactory ), true );
118
- } else {
119
- // TODO support more elaborate union types
120
- return typeFactory .createTypeWithNullability (typeFactory .createUnknownType (), true );
124
+ return typeFactory .createTypeWithNullability (rel (innerType , typeFactory , true ), true );
121
125
}
126
+ return typeFactory .createTypeWithNullability (typeFactory .createStructType (schema .getTypes ().stream ()
127
+ .filter (x -> x .getType () != Schema .Type .NULL )
128
+ .map (x -> new AbstractMap .SimpleEntry <>(x .getName (), rel (x , typeFactory , isNullable )))
129
+ .filter (x -> x .getValue ().getSqlTypeName () != SqlTypeName .NULL )
130
+ .filter (x -> x .getValue ().getSqlTypeName () != unknown .getSqlTypeName ())
131
+ .collect (Collectors .toList ())), isNullable );
122
132
default :
123
- return typeFactory .createUnknownType ();
133
+ return typeFactory .createTypeWithNullability ( typeFactory . createUnknownType (), true );
124
134
}
125
135
}
126
136
127
137
public static RelDataType rel (Schema schema ) {
128
138
return rel (schema , new SqlTypeFactoryImpl (RelDataTypeSystem .DEFAULT ));
129
139
}
130
140
131
- private static RelDataType createRelType (RelDataTypeFactory typeFactory , SqlTypeName typeName ) {
132
- RelDataType rawType = typeFactory .createSqlType (typeName );
133
- return typeFactory .createTypeWithNullability (rawType , false );
141
+ private static RelDataType createRelType (RelDataTypeFactory typeFactory , SqlTypeName typeName , boolean nullable ) {
142
+ return createRelType (typeFactory , typeName , RelDataType .PRECISION_NOT_SPECIFIED , nullable );
143
+ }
144
+
145
+ private static RelDataType createRelType (RelDataTypeFactory typeFactory , SqlTypeName typeName ,
146
+ int precision , boolean nullable ) {
147
+ RelDataType rawType = typeFactory .createSqlType (typeName , precision );
148
+ return typeFactory .createTypeWithNullability (rawType , nullable );
134
149
}
135
150
136
151
public static RelProtoDataType proto (Schema schema ) {
0 commit comments