4
4
import java .util .List ;
5
5
import java .util .Map ;
6
6
import java .util .Objects ;
7
-
8
7
import org .apache .avro .Schema ;
9
8
import org .apache .calcite .plan .RelOptUtil ;
10
9
import org .apache .calcite .rel .type .RelDataType ;
@@ -103,7 +102,7 @@ public void testAvroKeyPayloadSchemaNoKeyOptions() {
103
102
assertNull (result .getKey ()); // Key schema should be null
104
103
assertNotNull (result .getValue ()); // Payload schema should not be null
105
104
assertEquals ("payloadSchema" , result .getValue ().getName ());
106
- assertEquals ("namespace" , result .getValue ().getNamespace ());
105
+ assertEquals ("namespace.payloadSchema " , result .getValue ().getNamespace ());
107
106
assertEquals ("record" , result .getValue ().getType ().getName ());
108
107
assertEquals (1 , result .getValue ().getFields ().size ());
109
108
assertEquals ("field1" , result .getValue ().getFields ().get (0 ).name ());
@@ -138,14 +137,14 @@ public void testAvroKeyPayloadSchemaValidKeyOptions() {
138
137
139
138
assertNotNull (result .getKey ()); // Key schema should not be null
140
139
assertEquals ("keySchema" , result .getKey ().getName ());
141
- assertEquals ("namespace" , result .getKey ().getNamespace ());
140
+ assertEquals ("namespace.keySchema " , result .getKey ().getNamespace ());
142
141
assertEquals ("record" , result .getKey ().getType ().getName ());
143
142
assertEquals (1 , result .getKey ().getFields ().size ());
144
143
assertEquals ("field1" , result .getKey ().getFields ().get (0 ).name ()); // prefix should be stripped
145
144
assertEquals ("string" , result .getKey ().getFields ().get (0 ).schema ().getType ().getName ());
146
145
assertNotNull (result .getValue ()); // Payload schema should not be null
147
146
assertEquals ("payloadSchema" , result .getValue ().getName ());
148
- assertEquals ("namespace" , result .getValue ().getNamespace ());
147
+ assertEquals ("namespace.payloadSchema " , result .getValue ().getNamespace ());
149
148
assertEquals ("record" , result .getValue ().getType ().getName ());
150
149
assertEquals (1 , result .getValue ().getFields ().size ());
151
150
assertEquals ("field2" , result .getValue ().getFields ().get (0 ).name ());
@@ -168,7 +167,7 @@ public void testAvroKeyPayloadSchemaPrimitiveKey() {
168
167
assertEquals ("int" , result .getKey ().getType ().getName ());
169
168
assertNotNull (result .getValue ()); // Payload schema should not be null
170
169
assertEquals ("payloadSchema" , result .getValue ().getName ());
171
- assertEquals ("namespace" , result .getValue ().getNamespace ());
170
+ assertEquals ("namespace.payloadSchema " , result .getValue ().getNamespace ());
172
171
assertEquals ("record" , result .getValue ().getType ().getName ());
173
172
assertEquals (1 , result .getValue ().getFields ().size ());
174
173
assertEquals ("field1" , result .getValue ().getFields ().get (0 ).name ());
@@ -208,4 +207,55 @@ public void convertsNestedArray() {
208
207
assertEquals ("field1" , structElementSchema .getFields ().get (0 ).name ());
209
208
assertEquals ("field2" , structElementSchema .getFields ().get (1 ).name ());
210
209
}
210
+
211
+ @ Test
212
+ public void handlesNamespaceInNestedArrayAndMapElements () {
213
+ RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl (RelDataTypeSystem .DEFAULT );
214
+
215
+ // Create a "location" record type that will be reused - this mimics the real scenario
216
+ RelDataType locationType1 = typeFactory .createStructType (
217
+ List .of (typeFactory .createSqlType (SqlTypeName .VARCHAR ), typeFactory .createSqlType (SqlTypeName .VARCHAR )),
218
+ List .of ("countryCode" , "postalCode" ));
219
+
220
+ // Create another "location" record type with slightly different structure
221
+ RelDataType locationType2 = typeFactory .createStructType (
222
+ List .of (typeFactory .createSqlType (SqlTypeName .VARCHAR ), typeFactory .createSqlType (SqlTypeName .INTEGER )),
223
+ List .of ("countryCode" , "regionCode" ));
224
+
225
+ // Create structures that use these location types in different contexts
226
+ // This simulates the real scenario where multiple fields have the same name but different contexts
227
+ RelDataType profileStruct = typeFactory .createStructType (
228
+ List .of (locationType1 ),
229
+ List .of ("location" ));
230
+
231
+ RelDataType positionStruct = typeFactory .createStructType (
232
+ List .of (locationType2 ),
233
+ List .of ("location" ));
234
+
235
+ // Put both in a map structure - this creates the collision scenario
236
+ // Both will try to generate records named "location" with the same namespace
237
+ RelDataType positionsMap = typeFactory .createMapType (
238
+ typeFactory .createSqlType (SqlTypeName .VARCHAR ),
239
+ positionStruct );
240
+
241
+ // Create the main record that contains both location types
242
+ RelDataType mainRecord = typeFactory .createStructType (
243
+ List .of (profileStruct , positionsMap ),
244
+ List .of ("profile" , "positions" ));
245
+
246
+ // Schema creation should succeed
247
+ Schema schema = AvroConverter .avro ("com.linkedin" , "MemberProfile" , mainRecord );
248
+ assertNotNull (schema );
249
+
250
+ // Without the namespace-appending behavior in AvroConverter, this would fail with error "Can't redefine: com.linkedin.location"
251
+ // The issue occurs because multiple records named "location" are created with the same namespace,
252
+ // causing a collision when schema.toString(true) tries to serialize them
253
+ String schemaJson = schema .toString (true );
254
+ assertNotNull ("Schema toString(true) should succeed without 'Can't redefine' errors" , schemaJson );
255
+
256
+ // Verify the schema can be parsed back
257
+ Schema .Parser parser = new Schema .Parser ();
258
+ Schema reparsedSchema = parser .parse (schemaJson );
259
+ assertNotNull ("Generated schema must be parseable" , reparsedSchema );
260
+ }
211
261
}
0 commit comments