Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion docs/content.zh/docs/sql/reference/data-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,24 @@ CAST(o AS ROW<`id` INT, `phone` STRING>) -- fails, the field 'phone' is n
CAST(o AS ROW<`id` VARIANT, `email` VARIANT>) -- (7, null), each field kept as a variant, the variant null preserved
```

A variant object also casts to `MAP<STRING, V>`, the schemaless read of an object. Each field name
becomes a key and each value casts to `V` by the same rules, recursively. The key type must be a
character string, since a variant object's keys are always strings, and a non-string key type is
rejected at validation. This is the way to read an object whose keys are not known in advance.

- A value present but set to a variant null maps to SQL `NULL` when `V` is nullable and fails the
cast when `V` is `NOT NULL`. An empty object casts to an empty map.
- A `MAP<STRING, VARIANT>` is the identity on its values: it shreds one level and keeps each value a
variant, a variant null included.

The following examples reuse `o` for `PARSE_JSON('{"id": 7, "name": "ada", "email": null}')`:

```sql
CAST(o AS MAP<STRING, STRING>) -- {id=7, name=ada, email=NULL}, each value rendered like the scalar cast
CAST(o AS MAP<STRING, VARIANT>) -- values kept as variants, the variant null included
CAST(o AS MAP<INT, STRING>) -- fails at validation, a MAP key must be a character string
```

**Declaration**

{{< tabs "25c30432-8460-441d-a036-9416d8202882" >}}
Expand Down Expand Up @@ -1855,7 +1873,7 @@ COALESCE(TRY_CAST('non-number' AS INT), 0) --- 结果返回数字 0 的 INT 格
| `ROW` | Y | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | !³ | N | N | N | N |
| `STRUCTURED` | Y | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | !³ | N | N | N |
| `RAW` | Y | ! | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | Y⁴ | N | N |
| `VARIANT` | N | ! | ! | ! | ! | ! | ! | ! | ! | ! | ! | N | ! | ! | N | !³ | N | N | !³ | !³ | N | Y | N |
| `VARIANT` | N | ! | ! | ! | ! | ! | ! | ! | ! | ! | ! | N | ! | ! | N | !³ | N | | !³ | !³ | N | Y | N |
| `BITMAP` | Y | Y⁷ | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N |

备注:
Expand Down
20 changes: 19 additions & 1 deletion docs/content/docs/sql/reference/data-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,24 @@ CAST(o AS ROW<`id` INT, `phone` STRING>) -- fails, the field 'phone' is n
CAST(o AS ROW<`id` VARIANT, `email` VARIANT>) -- (7, null), each field kept as a variant, the variant null preserved
```

A variant object also casts to `MAP<STRING, V>`, the schemaless read of an object. Each field name
becomes a key and each value casts to `V` by the same rules, recursively. The key type must be a
character string, since a variant object's keys are always strings, and a non-string key type is
rejected at validation. This is the way to read an object whose keys are not known in advance.

- A value present but set to a variant null maps to SQL `NULL` when `V` is nullable and fails the
cast when `V` is `NOT NULL`. An empty object casts to an empty map.
- A `MAP<STRING, VARIANT>` is the identity on its values: it shreds one level and keeps each value a
variant, a variant null included.

The following examples reuse `o` for `PARSE_JSON('{"id": 7, "name": "ada", "email": null}')`:

```sql
CAST(o AS MAP<STRING, STRING>) -- {id=7, name=ada, email=NULL}, each value rendered like the scalar cast
CAST(o AS MAP<STRING, VARIANT>) -- values kept as variants, the variant null included
CAST(o AS MAP<INT, STRING>) -- fails at validation, a MAP key must be a character string
```

**Declaration**

{{< tabs "25c30432-8460-441d-a036-9416d8202882" >}}
Expand Down Expand Up @@ -1864,7 +1882,7 @@ The matrix below describes the supported cast pairs, where "Y" means supported,
| `ROW` | Y | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | !³ | N | N | N | N |
| `STRUCTURED` | Y | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | !³ | N | N | N |
| `RAW` | Y | ! | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | Y⁴ | N | N |
| `VARIANT` | N | ! | ! | ! | ! | ! | ! | ! | ! | ! | ! | N | ! | ! | N | !³ | N | N | !³ | !³ | N | Y | N |
| `VARIANT` | N | ! | ! | ! | ! | ! | ! | ! | ! | ! | ! | N | ! | ! | N | !³ | N | | !³ | !³ | N | Y | N |
| `BITMAP` | Y | Y⁷ | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N |

Notes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import static org.apache.flink.table.types.logical.LogicalTypeRoot.INTEGER;
import static org.apache.flink.table.types.logical.LogicalTypeRoot.INTERVAL_DAY_TIME;
import static org.apache.flink.table.types.logical.LogicalTypeRoot.INTERVAL_YEAR_MONTH;
import static org.apache.flink.table.types.logical.LogicalTypeRoot.MAP;
import static org.apache.flink.table.types.logical.LogicalTypeRoot.NULL;
import static org.apache.flink.table.types.logical.LogicalTypeRoot.RAW;
import static org.apache.flink.table.types.logical.LogicalTypeRoot.ROW;
Expand Down Expand Up @@ -690,6 +691,13 @@ private static boolean supportsCasting(
return allowExplicit
&& targetType.getChildren().stream()
.allMatch(field -> supportsCasting(sourceType, field, true));
} else if (sourceRoot == VARIANT && targetRoot == MAP) {
// A variant object casts to MAP<STRING, V> when the key is a character string, since
// VARIANT object keys are always strings, and VARIANT casts to the value type V.
final List<LogicalType> mapChildren = targetType.getChildren();
return allowExplicit
&& mapChildren.get(0).is(CHARACTER_STRING)
&& supportsCasting(sourceType, mapChildren.get(1), true);
} else if (sourceRoot == RAW
&& !targetType.is(BINARY_STRING)
&& !targetType.is(CHARACTER_STRING)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.flink.table.types.logical.IntType;
import org.apache.flink.table.types.logical.LocalZonedTimestampType;
import org.apache.flink.table.types.logical.LogicalType;
import org.apache.flink.table.types.logical.MapType;
import org.apache.flink.table.types.logical.MultisetType;
import org.apache.flink.table.types.logical.NullType;
import org.apache.flink.table.types.logical.RawType;
Expand Down Expand Up @@ -332,6 +333,31 @@ private static Stream<Arguments> testData() {
.MONTH)))),
false,
false),
// A variant object casts to MAP<STRING, V> when the key is a character string and
// the value is castable; a non-string key is rejected
Arguments.of(
new VariantType(),
new MapType(VarCharType.STRING_TYPE, new IntType()),
false,
true),
Arguments.of(
new VariantType(),
new MapType(VarCharType.STRING_TYPE, new VariantType()),
false,
true),
Arguments.of(
new VariantType(),
new MapType(new IntType(), new CharType()),
false,
false),
Arguments.of(
new VariantType(),
new MapType(
VarCharType.STRING_TYPE,
new YearMonthIntervalType(
YearMonthIntervalType.YearMonthResolution.MONTH)),
false,
false),
// MULTISET has no variant counterpart and stays unsupported
Arguments.of(
new VariantType(), new MultisetType(VarCharType.STRING_TYPE), false, false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public class CastRuleProvider {
.addRule(VariantToPrimitiveCastRule.INSTANCE)
.addRule(VariantToArrayCastRule.INSTANCE)
.addRule(VariantToRowCastRule.INSTANCE)
.addRule(VariantToMapCastRule.INSTANCE)
// Bitmap rules
.addRule(BitmapToStringCastRule.INSTANCE)
.addRule(BitmapToBinaryCastRule.INSTANCE)
Expand Down
Loading