This is an automated email from the ASF dual-hosted git repository.

clintropolis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 72f9c067eb6 fix: clustered segment catalog spec complex handling 
(#19842)
72f9c067eb6 is described below

commit 72f9c067eb68be8742c19a34d51ed6ebd48597d4
Author: Clint Wylie <[email protected]>
AuthorDate: Wed Aug 5 23:09:02 2026 -0700

    fix: clustered segment catalog spec complex handling (#19842)
---
 .../druid/msq/util/DimensionSchemaUtils.java       |  10 +-
 .../druid/segment/DimensionHandlerUtils.java       |  70 ++++++++++-
 .../druid/segment/DimensionHandlerUtilsTest.java   | 129 ++++++++++++++++++++-
 .../ClusteredValueGroupsBaseTableMetadata.java     |  14 +--
 .../ClusteredValueGroupsBaseTableMetadataTest.java | 121 ++++++++++++++++++-
 5 files changed, 311 insertions(+), 33 deletions(-)

diff --git 
a/multi-stage-query/src/main/java/org/apache/druid/msq/util/DimensionSchemaUtils.java
 
b/multi-stage-query/src/main/java/org/apache/druid/msq/util/DimensionSchemaUtils.java
index 424700c30f0..4b7e6e7eb2b 100644
--- 
a/multi-stage-query/src/main/java/org/apache/druid/msq/util/DimensionSchemaUtils.java
+++ 
b/multi-stage-query/src/main/java/org/apache/druid/msq/util/DimensionSchemaUtils.java
@@ -31,8 +31,6 @@ import org.apache.druid.java.util.emitter.EmittingLogger;
 import org.apache.druid.java.util.emitter.service.AlertEvent;
 import org.apache.druid.segment.AutoTypeColumnSchema;
 import org.apache.druid.segment.DimensionHandlerUtils;
-import org.apache.druid.segment.column.ColumnCapabilities;
-import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
 import org.apache.druid.segment.column.ColumnType;
 import org.apache.druid.segment.column.ValueType;
 
@@ -81,9 +79,7 @@ public class DimensionSchemaUtils
       // for complex types that are not COMPLEX<json>, we still want to use 
the handler since 'auto' typing
       // only works for the 'standard' built-in types
       if (queryType != null && queryType.is(ValueType.COMPLEX) && 
!ColumnType.NESTED_DATA.equals(queryType)) {
-        final ColumnCapabilities capabilities = 
ColumnCapabilitiesImpl.createDefault().setType(queryType);
-        return DimensionHandlerUtils.getHandlerFromCapabilities(column, 
capabilities, null)
-                                    .getDimensionSchema(capabilities);
+        return DimensionHandlerUtils.getComplexDimensionSchema(column, 
queryType);
       }
 
       if (queryType != null && (queryType.isPrimitive() || 
queryType.isPrimitiveArray())) {
@@ -111,9 +107,7 @@ public class DimensionSchemaUtils
       } else if (dimensionType.getType() == ValueType.ARRAY) {
         return new AutoTypeColumnSchema(column, dimensionType, null);
       } else {
-        final ColumnCapabilities capabilities = 
ColumnCapabilitiesImpl.createDefault().setType(dimensionType);
-        return DimensionHandlerUtils.getHandlerFromCapabilities(column, 
capabilities, null)
-                                    .getDimensionSchema(capabilities);
+        return DimensionHandlerUtils.getComplexDimensionSchema(column, 
dimensionType);
       }
     }
   }
diff --git 
a/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java 
b/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java
index e129ceb4177..e5d4804bcaa 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java
@@ -24,8 +24,10 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.primitives.Doubles;
 import com.google.common.primitives.Floats;
 import org.apache.druid.common.guava.GuavaUtils;
+import org.apache.druid.data.input.impl.DimensionSchema;
 import org.apache.druid.data.input.impl.DimensionSchema.MultiValueHandling;
 import org.apache.druid.error.DruidException;
+import org.apache.druid.error.InvalidInput;
 import org.apache.druid.java.util.common.IAE;
 import org.apache.druid.java.util.common.ISE;
 import org.apache.druid.java.util.common.StringUtils;
@@ -130,17 +132,75 @@ public final class DimensionHandlerUtils
     }
 
     if (capabilities.is(ValueType.COMPLEX) && 
capabilities.getComplexTypeName() != null) {
-      DimensionHandlerProvider provider = 
DIMENSION_HANDLER_PROVIDERS.get(capabilities.getComplexTypeName());
-      if (provider == null) {
-        throw new ISE("Can't find DimensionHandlerProvider for typeName [%s]", 
capabilities.getComplexTypeName());
-      }
-      return provider.get(dimensionName);
+      return getHandlerForComplexType(dimensionName, 
capabilities.getComplexTypeName());
     }
 
     // Return a StringDimensionHandler by default (null columns will be 
treated as String typed)
     return new StringDimensionHandler(dimensionName, multiValueHandling, true, 
false);
   }
 
+  /**
+   * The {@link DimensionHandler} registered for a complex type. Complex 
columns are stored by type-specific handlers,
+   * so a type contributed by an extension becomes storable as soon as that 
extension registers one.
+   *
+   * @throws DruidException if no handler is registered for the type, which 
usually means the extension defining it is
+   *                        not loaded
+   */
+  public static DimensionHandler<?, ?, ?> getHandlerForComplexType(String 
dimensionName, String complexTypeName)
+  {
+    final DimensionHandlerProvider provider = 
DIMENSION_HANDLER_PROVIDERS.get(complexTypeName);
+    if (provider == null) {
+      throw InvalidInput.exception(
+          "Complex type[%s] for dimension[%s] is not a valid type",
+          complexTypeName,
+          dimensionName
+      );
+    }
+    return provider.get(dimensionName);
+  }
+
+  /**
+   * The {@link DimensionSchema} to use when storing a complex column of the 
given type, for callers that have a
+   * declared type rather than an existing column. Handlers are free to 
consult the {@link ColumnCapabilities} they
+   * are given, so a default set describing the type is supplied on the 
caller's behalf.
+   * <p>
+   * The schema the handler produces must describe the column that was asked 
for, since a {@link DimensionSchema}
+   * selects its own handler at ingest time (via {@link 
DimensionSchema#getDimensionHandler()}, which reads
+   * {@link DimensionSchema#getColumnType()}). A schema of some other type 
would quietly store the column as that type
+   * instead, contradicting the type the caller declared, and one of some 
other name would store a different column
+   * entirely.
+   *
+   * @throws DruidException if the type is not a named complex type, if no 
handler is registered for it (which usually
+   *                        means the extension defining it is not loaded), or 
if the registered handler does not
+   *                        describe the column that was asked for
+   */
+  public static DimensionSchema getComplexDimensionSchema(String 
dimensionName, ColumnType type)
+  {
+    if (!type.is(ValueType.COMPLEX) || type.getComplexTypeName() == null) {
+      throw InvalidInput.exception("Type[%s] for dimension[%s] is not a named 
complex type", type, dimensionName);
+    }
+    final DimensionSchema schema = getHandlerForComplexType(dimensionName, 
type.getComplexTypeName())
+        
.getDimensionSchema(ColumnCapabilitiesImpl.createDefault().setType(type));
+    if (!type.equals(schema.getColumnType())) {
+      throw DruidException.defensive(
+          "Dimension handler for type[%s] produced a schema of type[%s] for 
dimension[%s]; a column cannot be stored"
+          + " as a type other than the one it declares",
+          type,
+          schema.getColumnType(),
+          dimensionName
+      );
+    }
+    if (!dimensionName.equals(schema.getName())) {
+      throw DruidException.defensive(
+          "Dimension handler for type[%s] produced a schema for dimension[%s] 
instead of dimension[%s]",
+          type,
+          schema.getName(),
+          dimensionName
+      );
+    }
+    return schema;
+  }
+
   public static List<ColumnType> 
getValueTypesFromDimensionSpecs(List<DimensionSpec> dimSpecs)
   {
     List<ColumnType> types = new ArrayList<>(dimSpecs.size());
diff --git 
a/processing/src/test/java/org/apache/druid/segment/DimensionHandlerUtilsTest.java
 
b/processing/src/test/java/org/apache/druid/segment/DimensionHandlerUtilsTest.java
index 1985aeb68d4..d48931122d6 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/DimensionHandlerUtilsTest.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/DimensionHandlerUtilsTest.java
@@ -26,6 +26,7 @@ import org.apache.druid.data.input.impl.FloatDimensionSchema;
 import org.apache.druid.data.input.impl.LongDimensionSchema;
 import org.apache.druid.data.input.impl.NewSpatialDimensionSchema;
 import org.apache.druid.data.input.impl.StringDimensionSchema;
+import org.apache.druid.error.DruidException;
 import org.apache.druid.java.util.common.ISE;
 import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
@@ -82,11 +83,15 @@ public class DimensionHandlerUtilsTest extends 
InitializedNullHandlingTest
   public void testGetHandlerFromUnknownComplexCapabilities()
   {
     ColumnCapabilities capabilities = new 
ColumnCapabilitiesImpl().setType(ColumnType.ofComplex("unknown"));
-    ISE ex = Assertions.assertThrows(
-        ISE.class,
-        () -> DimensionHandlerUtils.getHandlerFromCapabilities(DIM_NAME, 
capabilities, null)
+    Throwable t = Assertions.assertThrows(
+        DruidException.class,
+        () -> DimensionHandlerUtils.getHandlerFromCapabilities(
+            DIM_NAME,
+            capabilities,
+            null
+        )
     );
-    Assertions.assertTrue(ex.getMessage().contains("Can't find 
DimensionHandlerProvider for typeName [unknown]"));
+    Assertions.assertEquals("Complex type[unknown] for dimension[dim] is not a 
valid type", t.getMessage());
   }
 
   @Test
@@ -320,26 +325,138 @@ public class DimensionHandlerUtilsTest extends 
InitializedNullHandlingTest
 
   private static class TestDimensionSchema extends DimensionSchema
   {
+    private final String typeName;
 
     protected TestDimensionSchema(
         String name,
         MultiValueHandling multiValueHandling,
         boolean createBitmapIndex
     )
+    {
+      this(name, multiValueHandling, createBitmapIndex, TYPE);
+    }
+
+    protected TestDimensionSchema(
+        String name,
+        MultiValueHandling multiValueHandling,
+        boolean createBitmapIndex,
+        String typeName
+    )
     {
       super(name, multiValueHandling, createBitmapIndex);
+      this.typeName = typeName;
     }
 
     @Override
     public String getTypeName()
     {
-      return TYPE;
+      return typeName;
     }
 
     @Override
     public ColumnType getColumnType()
     {
-      return ColumnType.ofComplex(TYPE);
+      return ColumnType.ofComplex(typeName);
     }
   }
+
+  @Test
+  public void testGetComplexDimensionSchema()
+  {
+    Assertions.assertEquals(
+        new TestDimensionSchema("x", null, false),
+        DimensionHandlerUtils.getComplexDimensionSchema("x", 
ColumnType.ofComplex(TYPE))
+    );
+  }
+
+  @Test
+  public void testGetComplexDimensionSchemaUnregisteredType()
+  {
+    Throwable t = Assertions.assertThrows(
+        DruidException.class,
+        () -> DimensionHandlerUtils.getComplexDimensionSchema("x", 
ColumnType.ofComplex("noSuchType"))
+    );
+    Assertions.assertEquals("Complex type[noSuchType] for dimension[x] is not 
a valid type", t.getMessage());
+  }
+
+  @Test
+  public void testGetComplexDimensionSchemaRejectsNonComplexType()
+  {
+    Throwable t = Assertions.assertThrows(
+        DruidException.class,
+        () -> DimensionHandlerUtils.getComplexDimensionSchema("x", 
ColumnType.STRING)
+    );
+    Assertions.assertEquals("Type[STRING] for dimension[x] is not a named 
complex type", t.getMessage());
+  }
+
+  /**
+   * A schema selects its own handler at ingest time, so a handler that hands 
back a schema of some other type would
+   * quietly store the column as that type rather than the one the caller 
declared.
+   */
+  @Test
+  public void testGetComplexDimensionSchemaRejectsSchemaOfOtherType()
+  {
+    final String typeName = "otherTypeSchemaType";
+    DimensionHandlerUtils.registerDimensionHandlerProvider(
+        typeName,
+        d -> new DoubleDimensionHandler(d)
+        {
+          @Override
+          public DimensionSchema getDimensionSchema(ColumnCapabilities 
capabilities)
+          {
+            return new DoubleDimensionSchema(d);
+          }
+        }
+    );
+    Throwable t = Assertions.assertThrows(
+        DruidException.class,
+        () -> DimensionHandlerUtils.getComplexDimensionSchema("x", 
ColumnType.ofComplex(typeName))
+    );
+    Assertions.assertEquals(
+        "Dimension handler for type[COMPLEX<otherTypeSchemaType>] produced a 
schema of type[DOUBLE] for dimension[x];"
+        + " a column cannot be stored as a type other than the one it 
declares",
+        t.getMessage()
+    );
+  }
+
+  /**
+   * Likewise, a handler that hands back a schema for some other column would 
store a different column entirely.
+   */
+  @Test
+  public void testGetComplexDimensionSchemaRejectsSchemaOfOtherName()
+  {
+    final String typeName = "otherNameSchemaType";
+    DimensionHandlerUtils.registerDimensionHandlerProvider(
+        typeName,
+        d -> new DoubleDimensionHandler(d)
+        {
+          @Override
+          public DimensionSchema getDimensionSchema(ColumnCapabilities 
capabilities)
+          {
+            return new TestDimensionSchema("y", null, false, typeName);
+          }
+        }
+    );
+    Throwable t = Assertions.assertThrows(
+        DruidException.class,
+        () -> DimensionHandlerUtils.getComplexDimensionSchema("x", 
ColumnType.ofComplex(typeName))
+    );
+    Assertions.assertEquals(
+        "Dimension handler for type[COMPLEX<otherNameSchemaType>] produced a 
schema for dimension[y] instead of"
+        + " dimension[x]",
+        t.getMessage()
+    );
+  }
+
+  @Test
+  public void testGetHandlerForComplexType()
+  {
+    
Assertions.assertNotNull(DimensionHandlerUtils.getHandlerForComplexType("x", 
TYPE));
+    Throwable t = Assertions.assertThrows(
+        DruidException.class,
+        () -> DimensionHandlerUtils.getHandlerForComplexType("x", "noSuchType")
+    );
+    Assertions.assertEquals("Complex type[noSuchType] for dimension[x] is not 
a valid type", t.getMessage());
+  }
+
 }
diff --git 
a/server/src/main/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadata.java
 
b/server/src/main/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadata.java
index feaf647e402..dd0dca87794 100644
--- 
a/server/src/main/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadata.java
+++ 
b/server/src/main/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadata.java
@@ -27,10 +27,11 @@ import 
org.apache.druid.data.input.impl.ClusteredValueGroupsBaseTableProjectionS
 import org.apache.druid.data.input.impl.DimensionSchema;
 import org.apache.druid.error.InvalidInput;
 import org.apache.druid.segment.AutoTypeColumnSchema;
-import org.apache.druid.segment.NestedDataColumnSchema;
+import org.apache.druid.segment.DimensionHandlerUtils;
 import org.apache.druid.segment.VirtualColumns;
 import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ColumnType;
+import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.utils.CollectionUtils;
 
 import javax.annotation.Nullable;
@@ -209,14 +210,11 @@ public class ClusteredValueGroupsBaseTableMetadata 
implements DatasourceBaseTabl
       // FLOAT ARRAY as DOUBLE ARRAY).
       return DimensionSchema.getDefaultSchemaForBuiltInType(column.name(), 
druidType);
     }
-    if (ColumnType.NESTED_DATA.equals(druidType)) {
-      return new NestedDataColumnSchema(column.name(), 
NestedDataColumnSchema.DEFAULT_FORMAT_VERSION);
+    if (druidType.is(ValueType.COMPLEX)) {
+      return DimensionHandlerUtils.getComplexDimensionSchema(column.name(), 
druidType);
     }
-    // Other complex types cannot be ingested into a clustered base table: 
there is no dimension handler for them,
-    // and clustered base tables have no aggregators to produce them.
     throw InvalidInput.exception(
-        "column [%s] has unsupported type [%s] for a clustered base table; 
supported types are primitive, primitive"
-        + " array, and COMPLEX<json> columns",
+        "column [%s] has unsupported type [%s] for a clustered base table",
         column.name(),
         druidType
     );
@@ -260,8 +258,6 @@ public class ClusteredValueGroupsBaseTableMetadata 
implements DatasourceBaseTabl
       // The auto schema stores FLOAT as DOUBLE.
       expectedType = autoColumnType(declaredType);
     }
-    // A NestedDataColumnSchema always reports COMPLEX<json>, so this also 
restricts json schemas to columns declared
-    // as COMPLEX<json>.
     if (!expectedType.equals(customSchema.getColumnType())) {
       throw InvalidInput.exception(
           "columnSchemas entry [%s] of type [%s] does not match the column's 
declared type [%s]; column schemas"
diff --git 
a/server/src/test/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadataTest.java
 
b/server/src/test/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadataTest.java
index a31a8e4dad8..3d63673a3e0 100644
--- 
a/server/src/test/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadataTest.java
+++ 
b/server/src/test/java/org/apache/druid/catalog/model/ClusteredValueGroupsBaseTableMetadataTest.java
@@ -29,12 +29,18 @@ import 
org.apache.druid.data.input.impl.DoubleDimensionSchema;
 import org.apache.druid.data.input.impl.LongDimensionSchema;
 import org.apache.druid.data.input.impl.StringDimensionSchema;
 import org.apache.druid.error.DruidException;
+import org.apache.druid.guice.BuiltInTypesModule;
 import org.apache.druid.jackson.DefaultObjectMapper;
+import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.math.expr.ExprMacroTable;
 import org.apache.druid.segment.AutoTypeColumnSchema;
 import org.apache.druid.segment.DefaultColumnFormatConfig;
+import org.apache.druid.segment.DimensionHandler;
+import org.apache.druid.segment.DimensionHandlerUtils;
+import org.apache.druid.segment.DoubleDimensionHandler;
 import org.apache.druid.segment.NestedDataColumnSchema;
 import org.apache.druid.segment.VirtualColumns;
+import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnType;
 import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
 import org.apache.druid.testing.InitializedNullHandlingTest;
@@ -48,6 +54,10 @@ import java.util.Map;
 
 public class ClusteredValueGroupsBaseTableMetadataTest extends 
InitializedNullHandlingTest
 {
+  static {
+    BuiltInTypesModule.registerHandlersAndSerde();
+  }
+
   private final ObjectMapper mapper = new 
DefaultObjectMapper().setInjectableValues(
       new InjectableValues.Std()
           .addValue(ExprMacroTable.class, ExprMacroTable.nil())
@@ -428,7 +438,8 @@ public class ClusteredValueGroupsBaseTableMetadataTest 
extends InitializedNullHa
     );
     // Declared types are retained in the ingestion schema rather than left to 
inference: arrays cast an auto column
     // to the declared type (an all-null batch has no values to infer from; 
FLOAT ARRAY is stored as DOUBLE ARRAY by
-    // the auto schema), and COMPLEX<json> uses the dedicated nested column 
schema.
+    // the auto schema). COMPLEX<json> resolves through its dimension handler 
to an uncast auto column, which is how
+    // json columns are stored everywhere else.
     Assert.assertEquals(
         ClusteredValueGroupsBaseTableProjectionSpec.builder()
                                                    .columns(
@@ -437,8 +448,8 @@ public class ClusteredValueGroupsBaseTableMetadataTest 
extends InitializedNullHa
                                                        new 
AutoTypeColumnSchema("tags", ColumnType.STRING_ARRAY, null),
                                                        new 
AutoTypeColumnSchema("vals", ColumnType.LONG_ARRAY, null),
                                                        new 
AutoTypeColumnSchema("ratios", ColumnType.DOUBLE_ARRAY, null),
-                                                       new 
NestedDataColumnSchema("attrs", NestedDataColumnSchema.DEFAULT_FORMAT_VERSION),
-                                                       new 
NestedDataColumnSchema("attrs2", NestedDataColumnSchema.DEFAULT_FORMAT_VERSION),
+                                                       
AutoTypeColumnSchema.of("attrs"),
+                                                       
AutoTypeColumnSchema.of("attrs2"),
                                                        new 
AutoTypeColumnSchema("vals2", ColumnType.LONG_ARRAY, null)
                                                    )
                                                    .clusteringColumns("tenant")
@@ -447,8 +458,13 @@ public class ClusteredValueGroupsBaseTableMetadataTest 
extends InitializedNullHa
     );
   }
 
+  /**
+   * A complex type with no registered dimension handler cannot be stored, and 
the handler lookup reports it rather
+   * than the type being rejected as unsupported in general: the handler may 
simply belong to an extension that is not
+   * loaded on the service validating the spec.
+   */
   @Test
-  public void testCreateSpecUnsupportedComplexTypeFails()
+  public void testCreateSpecComplexTypeWithoutHandlerFails()
   {
     final DatasourceBaseTableMetadata metadata = new 
ClusteredValueGroupsBaseTableMetadata(
         Collections.singletonList("tenant"),
@@ -461,7 +477,75 @@ public class ClusteredValueGroupsBaseTableMetadataTest 
extends InitializedNullHa
         new ColumnSpec("unique_things", "COMPLEX<hyperUnique>", null)
     );
     final DruidException e = Assert.assertThrows(DruidException.class, () -> 
metadata.createSpec(columns));
-    Assert.assertTrue(e.getMessage().contains("column [unique_things] has 
unsupported type [COMPLEX<hyperUnique>]"));
+    Assert.assertEquals(
+        "Complex type[hyperUnique] for dimension[unique_things] is not a valid 
type",
+        e.getMessage()
+    );
+  }
+
+  /**
+   * A complex type that does have a registered handler resolves through it, 
which is how types contributed by
+   * extensions become declarable.
+   */
+  @Test
+  public void testCreateSpecComplexTypeWithRegisteredHandler()
+  {
+    final String typeName = "clusteredBaseTableTestType";
+    // Only getDimensionSchema is exercised; the handler's storage behavior is 
irrelevant to building a spec.
+    DimensionHandlerUtils.registerDimensionHandlerProvider(
+        typeName,
+        name -> new DoubleDimensionHandler(name)
+        {
+          @Override
+          public DimensionSchema getDimensionSchema(ColumnCapabilities 
capabilities)
+          {
+            return new TestComplexDimensionSchema(name, typeName);
+          }
+        }
+    );
+
+    final DatasourceBaseTableMetadata metadata = new 
ClusteredValueGroupsBaseTableMetadata(
+        Collections.singletonList("tenant"),
+        null,
+        null
+    );
+    final List<ColumnSpec> columns = Arrays.asList(
+        new ColumnSpec("tenant", Columns.SQL_VARCHAR, null),
+        new ColumnSpec(Columns.TIME_COLUMN, null, null),
+        new ColumnSpec("sketch", StringUtils.format("COMPLEX<%s>", typeName), 
null)
+    );
+
+    final List<DimensionSchema> specColumns = 
metadata.createSpec(columns).getDimensionsSpec().getDimensions();
+    final DimensionSchema stored = specColumns.get(specColumns.size() - 1);
+    Assert.assertEquals("sketch", stored.getName());
+    Assert.assertEquals(ColumnType.ofComplex(typeName), 
stored.getColumnType());
+  }
+
+  @Test
+  public void testCreateSpecNestedTypeUsesRegisteredHandler()
+  {
+    final DatasourceBaseTableMetadata metadata = new 
ClusteredValueGroupsBaseTableMetadata(
+        Collections.singletonList("tenant"),
+        null,
+        null
+    );
+    final List<ColumnSpec> columns = Arrays.asList(
+        new ColumnSpec("tenant", Columns.SQL_VARCHAR, null),
+        new ColumnSpec(Columns.TIME_COLUMN, null, null),
+        new ColumnSpec("payload", ColumnType.NESTED_DATA.asTypeString(), null)
+    );
+
+    final List<DimensionSchema> specColumns = 
metadata.createSpec(columns).getDimensionsSpec().getDimensions();
+    final DimensionSchema stored = specColumns.get(specColumns.size() - 1);
+    Assert.assertEquals(AutoTypeColumnSchema.of("payload"), stored);
+    Assert.assertEquals(ColumnType.NESTED_DATA, stored.getColumnType());
+    // The schema a json column used to get here, retained for backwards 
compatibility, selects the same handler
+    // (DimensionHandler has no equals, so compare the class and the dimension 
spec it hands out, which carries the
+    // type the handler stores).
+    final DimensionHandler<?, ?, ?> legacyHandler =
+        new NestedDataColumnSchema("payload", 
NestedDataColumnSchema.DEFAULT_FORMAT_VERSION).getDimensionHandler();
+    Assert.assertEquals(legacyHandler.getClass(), 
stored.getDimensionHandler().getClass());
+    Assert.assertEquals(legacyHandler.getDimensionSpec(), 
stored.getDimensionHandler().getDimensionSpec());
   }
 
   @Test
@@ -594,4 +678,31 @@ public class ClusteredValueGroupsBaseTableMetadataTest 
extends InitializedNullHa
                   .usingGetClass()
                   .verify();
   }
+
+  /**
+   * Minimal complex {@link DimensionSchema}, the shape an honest handler for 
a complex type returns: the column type
+   * it reports is the type it was registered for.
+   */
+  private static class TestComplexDimensionSchema extends DimensionSchema
+  {
+    private final String typeName;
+
+    TestComplexDimensionSchema(String name, String typeName)
+    {
+      super(name, null, false);
+      this.typeName = typeName;
+    }
+
+    @Override
+    public String getTypeName()
+    {
+      return typeName;
+    }
+
+    @Override
+    public ColumnType getColumnType()
+    {
+      return ColumnType.ofComplex(typeName);
+    }
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to