diff --git a/core/src/main/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactory.java b/core/src/main/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactory.java index b8ed9b6cd7e..a048d9b303b 100644 --- a/core/src/main/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactory.java +++ b/core/src/main/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactory.java @@ -252,6 +252,11 @@ public static ExprType convertSqlTypeNameToExprType(SqlTypeName sqlTypeName) { INTERVAL; case ARRAY -> ARRAY; case MAP -> STRUCT; + // Calcite spells a struct as ROW. convertExprTypeToRelDataType builds STRUCT as + // MAP since the v2 path only passes _source JSON through, so a genuine ROW — + // from an engine that builds a struct out of typed columns — matched nothing and fell + // through to UNKNOWN. + case ROW -> STRUCT; case GEOMETRY -> GEO_POINT; case NULL, ANY, OTHER -> UNDEFINED; default -> UNKNOWN; diff --git a/core/src/test/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactoryTest.java b/core/src/test/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactoryTest.java index 7e57aaa9bf9..8ae907e9e30 100644 --- a/core/src/test/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactoryTest.java +++ b/core/src/test/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactoryTest.java @@ -286,6 +286,12 @@ public void testConvertExprTypeBinaryToNullableVarbinary() { assertTrue(result.isNullable()); } + @Test + public void testConvertRowReturnsStructExprType() { + assertEquals( + ExprCoreType.STRUCT, OpenSearchTypeFactory.convertSqlTypeNameToExprType(SqlTypeName.ROW)); + } + // ---------- convertAnalyticsEngineRelDataTypeToExprType ---------- // UDT-aware variant for the response-schema path. Must agree with the // planner-internal convertRelDataTypeToExprType on every non-UDT input.