You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@flink.apache.org by "raminqaf (via GitHub)" <gi...@apache.org> on 2026/09/03 16:37:52 UTC

[PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

raminqaf opened a new pull request, #29092:
URL: https://github.com/apache/flink/pull/29092

   ## What is the purpose of the change
   
   This pull request adds `CAST` and `TRY_CAST` from `VARIANT` to the constructed types `ROW` and `STRUCTURED`, so a variant object can be given a schema. It is stacked on #29073 (VARIANT to `ARRAY`), reuses the same design, and should be reviewed and merged after it.
   
   A constructed cast is the scalar leaf cast applied to every field plus a shape check at each level. The recursion bottoms out at the same scalar casts the primitive and string rules perform, so no new leaf semantics are introduced.
   
   Key semantics:
   - Fields match by name, not by position, since a JSON object is unordered. Name matching is case sensitive. A `ROW` declared without field names uses the default names `f0`, `f1`, and so on, which must then be present in the object.
   - A field absent from the object fails the cast, whether the target field is nullable or not.
   - A field present but set to a variant null maps to SQL `NULL` when the field is nullable and fails the cast when it is `NOT NULL`.
   - Object fields the target does not name are dropped, so the row is a projection.
   - A `VARIANT` target field takes the identity cast and keeps a variant null as a variant null rather than turning it into SQL `NULL`. This matches `ARRAY<VARIANT>` and the top level VARIANT cast.
   - Leaves are never parsed. A stored string does not reach an integer target.
   - `STRUCTURED` shares the `RowData` representation and is served by the same rule.
   - If any field cast fails, the whole cast fails, and `TRY_CAST` returns `NULL` for the entire value.
   
   ## Brief change log
   
   - Add `VariantToRowCastRule` that casts a variant object to `ROW` or `STRUCTURED`, matching fields by name and casting each field with the existing VARIANT to leaf rules.
   - Allow the explicit cast in `LogicalTypeCasts` when `VARIANT` casts to every target field type.
   - Document the VARIANT to `ROW` and `STRUCTURED` cast in the data types reference.
   
   ## Verifying this change
   
   This change added tests and can be verified as follows:
   - `CastRulesTest`: by-name matching, free field order, absent field, present variant null in nullable and `NOT NULL` fields, projection of extra fields, nested rows and arrays, an unnamed `ROW`, `ROW<VARIANT>` with and without a variant null, and a `STRUCTURED` target.
   - `CastFunctionITCase`: end to end SQL and Table API cases, including `ROW<VARIANT>` round trips back to a concrete row.
   - `LogicalTypeCastsTest` and `CastRuleProviderTest`: castability and rule resolution.
   
   ## Does this pull request potentially affect one of the following parts:
   
     - Dependencies (does it add or upgrade a dependency): no
     - The public API, i.e., is any changed class annotated with `@Public(Evolving)`: no
     - The serializers: no
     - The runtime per-record code paths (performance sensitive): no
     - Anything that affects deployment or recovery: JobManager (and its components), Checkpointing, Kubernetes/Yarn, ZooKeeper: no
     - The S3 file system connector: no
   
   ## Documentation
   
     - Does this pull request introduce a new feature? yes
     - If yes, how is the feature documented? docs
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   <!--
   If generative AI tooling has been used in the process of authoring this PR, please
   change the checkbox below to `[X]` and replace the placeholder in the "Generated-by"
   line with the tool name and version. Otherwise remove the "Generated-by" line.
   See the ASF Generative Tooling Guidance for details:
   https://www.apache.org/legal/generative-tooling.html
   
   You are responsible for the quality and correctness of every change in this PR
   regardless of the tooling used. Low-effort AI-generated PRs will be closed. See
   AGENTS.md for the full guidance.
   -->
   
   - [X] Yes (please specify the tool below)
   
   Generated-by: Opus 4.8
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "twalthr (via GitHub)" <gi...@apache.org>.
twalthr merged PR #29092:
URL: https://github.com/apache/flink/pull/29092


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "raminqaf (via GitHub)" <gi...@apache.org>.
raminqaf commented on code in PR #29092:
URL: https://github.com/apache/flink/pull/29092#discussion_r3943662204


##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToRowCastRule.java:
##########
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.table.planner.functions.casting;
+
+import org.apache.flink.table.api.TableRuntimeException;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.data.binary.BinaryRowData;
+import org.apache.flink.table.data.writer.BinaryRowWriter;
+import org.apache.flink.table.planner.codegen.CodeGeneratorContext;
+import org.apache.flink.table.runtime.functions.VariantCastUtils;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.LogicalTypeRoot;
+import org.apache.flink.table.types.logical.utils.LogicalTypeChecks;
+import org.apache.flink.types.variant.Variant;
+
+import java.util.List;
+
+import static org.apache.flink.table.planner.codegen.CodeGenUtils.className;
+import static org.apache.flink.table.planner.codegen.CodeGenUtils.newName;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.binaryWriterWriteField;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.binaryWriterWriteNull;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.constructorCall;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.methodCall;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.staticCall;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.strLiteral;
+
+/**
+ * {@link LogicalTypeRoot#VARIANT} to {@link LogicalTypeRoot#ROW} and {@link
+ * LogicalTypeRoot#STRUCTURED_TYPE} cast rule.
+ *
+ * <p>The variant must be an object, otherwise the cast fails. Fields match <b>by name</b> rather
+ * than by position, because a JSON object is unordered. Name matching is case sensitive. A target

Review Comment:
   https://github.com/apache/flink/pull/29092#discussion_r3943661951



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRuleProviderTest.java:
##########
@@ -157,4 +157,17 @@ void testResolveVariantToArray() {
         // MULTISET has no variant counterpart
         assertThat(CastRuleProvider.exists(VARIANT, MULTISET(STRING()).getLogicalType())).isFalse();
     }
+
+    @Test
+    void testResolveVariantToRow() {
+        assertThat(CastRuleProvider.resolve(VARIANT, ROW(FIELD("f0", INT())).getLogicalType()))
+                .isSameAs(VariantToRowCastRule.INSTANCE);
+        // a structured target shares the ROW rule
+        assertThat(CastRuleProvider.resolve(VARIANT, STRUCTURED))
+                .isSameAs(VariantToRowCastRule.INSTANCE);
+
+        // a field with no variant counterpart makes the whole cast unresolvable
+        assertThat(CastRuleProvider.exists(VARIANT, ROW(FIELD("f0", TIME())).getLogicalType()))

Review Comment:
   https://github.com/apache/flink/pull/29092#discussion_r3943661578



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "raminqaf (via GitHub)" <gi...@apache.org>.
raminqaf commented on code in PR #29092:
URL: https://github.com/apache/flink/pull/29092#discussion_r3943665886


##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java:
##########
@@ -1991,7 +2067,99 @@ Stream<CastTestSpecBuilder> testCases() {
                                             VARIANT_INT_ARRAY_WITH_NULL.getElement(0),
                                             VARIANT_INT_ARRAY_WITH_NULL.getElement(1),
                                             VARIANT_INT_ARRAY_WITH_NULL.getElement(2)
-                                        })));
+                                        })),
+                CastTestSpecBuilder.testCastTo(ROW(FIELD("id", INT()), FIELD("name", STRING())))
+                        .fromCase(VARIANT(), null, null)
+                        .fromCase(
+                                VARIANT(), VARIANT_RECORD, GenericRowData.of(7, fromString("ada")))
+                        // an array or a scalar is not an object
+                        .fail(VARIANT(), VARIANT_INT_ARRAY, TableRuntimeException.class),

Review Comment:
   Added message



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "raminqaf (via GitHub)" <gi...@apache.org>.
raminqaf commented on code in PR #29092:
URL: https://github.com/apache/flink/pull/29092#discussion_r3943660719


##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1614,6 +1614,33 @@ CAST(m AS ARRAY<STRING>)   -- ['1', 'a'], a heterogeneous array still renders ea
 CAST(m AS ARRAY<VARIANT>)  -- [1, "a"] as variants, one level shredded
 ```
 
+A variant object casts to `ROW` or `STRUCTURED`, which likewise imposes a schema on it. The variant
+must be an object, otherwise the cast fails. Each field is itself a `VARIANT`, so it casts to its
+declared type by the same rules, recursively. Fields match by name rather than by position, since a
+JSON object is unordered, and name matching is case sensitive. A `ROW` declared without field names

Review Comment:
   Changed



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1614,6 +1614,33 @@ CAST(m AS ARRAY<STRING>)   -- ['1', 'a'], a heterogeneous array still renders ea
 CAST(m AS ARRAY<VARIANT>)  -- [1, "a"] as variants, one level shredded
 ```
 
+A variant object casts to `ROW` or `STRUCTURED`, which likewise imposes a schema on it. The variant
+must be an object, otherwise the cast fails. Each field is itself a `VARIANT`, so it casts to its
+declared type by the same rules, recursively. Fields match by name rather than by position, since a
+JSON object is unordered, and name matching is case sensitive. A `ROW` declared without field names
+uses the default names `f0`, `f1`, and so on, which must then be present in the object.
+
+- A field absent from the object fails the cast, whether the target field is nullable or not.
+- A field present but set to a variant null maps to SQL `NULL` when the field is nullable and fails
+  the cast when it is `NOT NULL`.

Review Comment:
   addressed 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "raminqaf (via GitHub)" <gi...@apache.org>.
raminqaf commented on code in PR #29092:
URL: https://github.com/apache/flink/pull/29092#discussion_r3943661252


##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1614,6 +1614,33 @@ CAST(m AS ARRAY<STRING>)   -- ['1', 'a'], a heterogeneous array still renders ea
 CAST(m AS ARRAY<VARIANT>)  -- [1, "a"] as variants, one level shredded
 ```
 
+A variant object casts to `ROW` or `STRUCTURED`, which likewise imposes a schema on it. The variant
+must be an object, otherwise the cast fails. Each field is itself a `VARIANT`, so it casts to its
+declared type by the same rules, recursively. Fields match by name rather than by position, since a
+JSON object is unordered, and name matching is case sensitive. A `ROW` declared without field names
+uses the default names `f0`, `f1`, and so on, which must then be present in the object.
+
+- A field absent from the object fails the cast, whether the target field is nullable or not.
+- A field present but set to a variant null maps to SQL `NULL` when the field is nullable and fails
+  the cast when it is `NOT NULL`.
+- Object fields the target does not name are dropped, so the row is a projection.
+- A `ROW` or `STRUCTURED` whose fields are `VARIANT` is the identity on those fields: it shreds one
+  level and keeps the rest semi-structured. A field set to a variant null stays a variant null
+  rather than becoming SQL `NULL`.
+
+If any field cast fails, the whole cast fails, and `TRY_CAST` returns `NULL` for the entire value
+rather than a partial result, exactly as for an array target.

Review Comment:
   removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "raminqaf (via GitHub)" <gi...@apache.org>.
raminqaf commented on code in PR #29092:
URL: https://github.com/apache/flink/pull/29092#discussion_r3943663705


##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java:
##########
@@ -267,6 +333,16 @@ class CastRulesTest {
                                                     "d", ARRAY(STRING()).getLogicalType())))
                             .build());
 
+    // The variant a VARIANT row field round-trips to. A ROW cast serializes each VARIANT field via
+    // BinaryRowWriter.writeVariant, which stores the field's sliced value and the object metadata
+    // but
+    // drops the position, so the read-back variant equals neither the original field view (non-zero
+    // pos) nor a freshly built scalar (empty metadata). This rebuilds that exact form at pos 0.

Review Comment:
   Shortened to 2 line



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "twalthr (via GitHub)" <gi...@apache.org>.
twalthr commented on code in PR #29092:
URL: https://github.com/apache/flink/pull/29092#discussion_r3934354114


##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1614,6 +1614,33 @@ CAST(m AS ARRAY<STRING>)   -- ['1', 'a'], a heterogeneous array still renders ea
 CAST(m AS ARRAY<VARIANT>)  -- [1, "a"] as variants, one level shredded
 ```
 
+A variant object casts to `ROW` or `STRUCTURED`, which likewise imposes a schema on it. The variant
+must be an object, otherwise the cast fails. Each field is itself a `VARIANT`, so it casts to its
+declared type by the same rules, recursively. Fields match by name rather than by position, since a
+JSON object is unordered, and name matching is case sensitive. A `ROW` declared without field names

Review Comment:
   ```suggestion
   declared type by the same rules, recursively. Fields match by name and name matching is case sensitive. A `ROW` declared without field names
   ```



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1614,6 +1614,33 @@ CAST(m AS ARRAY<STRING>)   -- ['1', 'a'], a heterogeneous array still renders ea
 CAST(m AS ARRAY<VARIANT>)  -- [1, "a"] as variants, one level shredded
 ```
 
+A variant object casts to `ROW` or `STRUCTURED`, which likewise imposes a schema on it. The variant
+must be an object, otherwise the cast fails. Each field is itself a `VARIANT`, so it casts to its
+declared type by the same rules, recursively. Fields match by name rather than by position, since a
+JSON object is unordered, and name matching is case sensitive. A `ROW` declared without field names
+uses the default names `f0`, `f1`, and so on, which must then be present in the object.
+
+- A field absent from the object fails the cast, whether the target field is nullable or not.
+- A field present but set to a variant null maps to SQL `NULL` when the field is nullable and fails
+  the cast when it is `NOT NULL`.

Review Comment:
   ```suggestion
     the cast when the target is `NOT NULL`.
   ```



##########
flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypeCastsTest.java:
##########
@@ -307,6 +308,23 @@ private static Stream<Arguments> testData() {
                                         YearMonthIntervalType.YearMonthResolution.MONTH)),
                         false,
                         false),
+                // A variant object casts to ROW or STRUCTURED when every field is castable; an
+                // empty
+                // row is vacuously castable and matching is by name
+                Arguments.of(new VariantType(), new RowType(List.of()), false, true),
+                Arguments.of(
+                        new VariantType(),
+                        new RowType(
+                                List.of(
+                                        new RowField("f0", new IntType()),
+                                        new RowField("f1", VarCharType.STRING_TYPE))),
+                        false,
+                        true),
+                Arguments.of(
+                        new VariantType(),
+                        new RowType(List.of(new RowField("f0", new TimeType()))),
+                        false,
+                        false),

Review Comment:
   Isn't this about to fail soon? Rather avoid to risk a red master.



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1614,6 +1614,33 @@ CAST(m AS ARRAY<STRING>)   -- ['1', 'a'], a heterogeneous array still renders ea
 CAST(m AS ARRAY<VARIANT>)  -- [1, "a"] as variants, one level shredded
 ```
 
+A variant object casts to `ROW` or `STRUCTURED`, which likewise imposes a schema on it. The variant
+must be an object, otherwise the cast fails. Each field is itself a `VARIANT`, so it casts to its
+declared type by the same rules, recursively. Fields match by name rather than by position, since a
+JSON object is unordered, and name matching is case sensitive. A `ROW` declared without field names
+uses the default names `f0`, `f1`, and so on, which must then be present in the object.
+
+- A field absent from the object fails the cast, whether the target field is nullable or not.
+- A field present but set to a variant null maps to SQL `NULL` when the field is nullable and fails
+  the cast when it is `NOT NULL`.
+- Object fields the target does not name are dropped, so the row is a projection.
+- A `ROW` or `STRUCTURED` whose fields are `VARIANT` is the identity on those fields: it shreds one
+  level and keeps the rest semi-structured. A field set to a variant null stays a variant null
+  rather than becoming SQL `NULL`.
+
+If any field cast fails, the whole cast fails, and `TRY_CAST` returns `NULL` for the entire value
+rather than a partial result, exactly as for an array target.

Review Comment:
   ```suggestion
   rather than a partial result.
   ```



##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/AbstractVariantToConstructedCastRule.java:
##########
@@ -30,9 +30,9 @@
  * a shape check at each level, so the recursion bottoms out at the same scalar cast the primitive
  * and string rules perform and no new leaf semantics are introduced.
  *
- * <p>A constructed cast can always fail, on a shape mismatch, an unreadable leaf, or a missing
- * {@code NOT NULL} field, so {@code TRY_CAST} wraps the whole value and returns {@code NULL} for
- * any failure rather than a partial result.
+ * <p>A constructed cast can always fail, on a shape mismatch, an unreadable leaf, a missing field,
+ * or a JSON {@code null} in a {@code NOT NULL} position, so {@code TRY_CAST} wraps the whole value

Review Comment:
   ```suggestion
    * or a VARIANT {@code null} in a {@code NOT NULL} position, so {@code TRY_CAST} wraps the whole value
   ```



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java:
##########
@@ -267,6 +333,16 @@ class CastRulesTest {
                                                     "d", ARRAY(STRING()).getLogicalType())))
                             .build());
 
+    // The variant a VARIANT row field round-trips to. A ROW cast serializes each VARIANT field via
+    // BinaryRowWriter.writeVariant, which stores the field's sliced value and the object metadata
+    // but
+    // drops the position, so the read-back variant equals neither the original field view (non-zero
+    // pos) nor a freshly built scalar (empty metadata). This rebuilds that exact form at pos 0.

Review Comment:
   remove this comment? You just create a deep copy right?



##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToRowCastRule.java:
##########
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.table.planner.functions.casting;
+
+import org.apache.flink.table.api.TableRuntimeException;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.data.binary.BinaryRowData;
+import org.apache.flink.table.data.writer.BinaryRowWriter;
+import org.apache.flink.table.planner.codegen.CodeGeneratorContext;
+import org.apache.flink.table.runtime.functions.VariantCastUtils;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.LogicalTypeRoot;
+import org.apache.flink.table.types.logical.utils.LogicalTypeChecks;
+import org.apache.flink.types.variant.Variant;
+
+import java.util.List;
+
+import static org.apache.flink.table.planner.codegen.CodeGenUtils.className;
+import static org.apache.flink.table.planner.codegen.CodeGenUtils.newName;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.binaryWriterWriteField;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.binaryWriterWriteNull;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.constructorCall;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.methodCall;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.staticCall;
+import static org.apache.flink.table.planner.functions.casting.CastRuleUtils.strLiteral;
+
+/**
+ * {@link LogicalTypeRoot#VARIANT} to {@link LogicalTypeRoot#ROW} and {@link
+ * LogicalTypeRoot#STRUCTURED_TYPE} cast rule.
+ *
+ * <p>The variant must be an object, otherwise the cast fails. Fields match <b>by name</b> rather
+ * than by position, because a JSON object is unordered. Name matching is case sensitive. A target

Review Comment:
   ```suggestion
    * than by position, because a VARIANT object is unordered. Name matching is case sensitive. A target
   ```



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRuleProviderTest.java:
##########
@@ -157,4 +157,17 @@ void testResolveVariantToArray() {
         // MULTISET has no variant counterpart
         assertThat(CastRuleProvider.exists(VARIANT, MULTISET(STRING()).getLogicalType())).isFalse();
     }
+
+    @Test
+    void testResolveVariantToRow() {
+        assertThat(CastRuleProvider.resolve(VARIANT, ROW(FIELD("f0", INT())).getLogicalType()))
+                .isSameAs(VariantToRowCastRule.INSTANCE);
+        // a structured target shares the ROW rule
+        assertThat(CastRuleProvider.resolve(VARIANT, STRUCTURED))
+                .isSameAs(VariantToRowCastRule.INSTANCE);
+
+        // a field with no variant counterpart makes the whole cast unresolvable
+        assertThat(CastRuleProvider.exists(VARIANT, ROW(FIELD("f0", TIME())).getLogicalType()))

Review Comment:
   same comment as above



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java:
##########
@@ -1991,7 +2067,99 @@ Stream<CastTestSpecBuilder> testCases() {
                                             VARIANT_INT_ARRAY_WITH_NULL.getElement(0),
                                             VARIANT_INT_ARRAY_WITH_NULL.getElement(1),
                                             VARIANT_INT_ARRAY_WITH_NULL.getElement(2)
-                                        })));
+                                        })),
+                CastTestSpecBuilder.testCastTo(ROW(FIELD("id", INT()), FIELD("name", STRING())))
+                        .fromCase(VARIANT(), null, null)
+                        .fromCase(
+                                VARIANT(), VARIANT_RECORD, GenericRowData.of(7, fromString("ada")))
+                        // an array or a scalar is not an object
+                        .fail(VARIANT(), VARIANT_INT_ARRAY, TableRuntimeException.class),

Review Comment:
   add expected error message substring



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "raminqaf (via GitHub)" <gi...@apache.org>.
raminqaf commented on code in PR #29092:
URL: https://github.com/apache/flink/pull/29092#discussion_r3943661578


##########
flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypeCastsTest.java:
##########
@@ -307,6 +308,23 @@ private static Stream<Arguments> testData() {
                                         YearMonthIntervalType.YearMonthResolution.MONTH)),
                         false,
                         false),
+                // A variant object casts to ROW or STRUCTURED when every field is castable; an
+                // empty
+                // row is vacuously castable and matching is by name
+                Arguments.of(new VariantType(), new RowType(List.of()), false, true),
+                Arguments.of(
+                        new VariantType(),
+                        new RowType(
+                                List.of(
+                                        new RowField("f0", new IntType()),
+                                        new RowField("f1", VarCharType.STRING_TYPE))),
+                        false,
+                        true),
+                Arguments.of(
+                        new VariantType(),
+                        new RowType(List.of(new RowField("f0", new TimeType()))),
+                        false,
+                        false),

Review Comment:
   Good catch replaced with INTEVAL



##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/AbstractVariantToConstructedCastRule.java:
##########
@@ -30,9 +30,9 @@
  * a shape check at each level, so the recursion bottoms out at the same scalar cast the primitive
  * and string rules perform and no new leaf semantics are introduced.
  *
- * <p>A constructed cast can always fail, on a shape mismatch, an unreadable leaf, or a missing
- * {@code NOT NULL} field, so {@code TRY_CAST} wraps the whole value and returns {@code NULL} for
- * any failure rather than a partial result.
+ * <p>A constructed cast can always fail, on a shape mismatch, an unreadable leaf, a missing field,
+ * or a JSON {@code null} in a {@code NOT NULL} position, so {@code TRY_CAST} wraps the whole value

Review Comment:
   Changed all JSON to VARIANT



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-37926][table] Support casting from VARIANT to ROW and STRUCTURED [flink]

Posted by "flinkbot (via GitHub)" <gi...@apache.org>.
flinkbot commented on PR #29092:
URL: https://github.com/apache/flink/pull/29092#issuecomment-5529131193

   <!--
   Meta data
   {
     "version" : 1,
     "metaDataEntries" : [ {
       "hash" : "ae81af5780a6715a5baadffad00a2f866a3771a1",
       "status" : "UNKNOWN",
       "url" : "TBD",
       "triggerID" : "ae81af5780a6715a5baadffad00a2f866a3771a1",
       "triggerType" : "PUSH"
     } ]
   }-->
   ## CI report:
   
   * ae81af5780a6715a5baadffad00a2f866a3771a1 UNKNOWN
   
   <details>
   <summary>Bot commands</summary>
     The @flinkbot bot supports the following commands:
   
    - `@flinkbot run azure` re-run the last Azure build
   </details>


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org