eldenmoon commented on code in PR #4170:
URL: https://github.com/apache/doris-website/pull/4170#discussion_r4079973999


##########
i18n/zh-CN/docusaurus-plugin-content-docs/current/sql-manual/basic-element/sql-data-types/semi-structured/VARIANT.md:
##########
@@ -19,269 +17,511 @@ VARIANT 类型用于存储半结构化 JSON 数据,可包含不同基础类型
 - 关键路径可以建立路径级索引,支持全文检索,同时继续受益于 Doris 的稀疏索引裁剪能力。
 - 
面向宽列场景的存储优化,让万级子列规模的自动子列列式提取(Subcolumnization)保持可用。若参与子列列式提取(Subcolumnization)的路径接近
 10000,对硬件要求会明显提高,通常应优先评估 DOC mode。
 
-如果你还在决定默认模式、Sparse、DOC mode 还是 Schema Template,建议先阅读 [VARIANT 
使用与配置指南](./variant-workload-guide)。本页主要提供语法、类型规则、索引、限制和配置参考。
+如果你还在决定默认模式、Sparse、DOC mode 还是 Schema Template,建议先阅读 [VARIANT 
使用与配置指南](./variant-workload-guide.md)。本页提供写入与解析、类型规则、CAST、NULL 
与比较语义、ALTER、索引、限制和配置的参考。
 :::
 
-## 使用 VARIANT 类型
+:::info 版本说明
+本页描述 Doris 5.0.0 及之后版本中的 VARIANT。与 Doris 4.x 相比,最容易影响已有 SQL 的差异有:
+
+- `INSERT` 会把字符串作为 VARIANT 字符串写入,不再按 JSON 解析,从 `s3()`、`hdfs()` 等表函数执行 `INSERT 
INTO ... SELECT` 也是如此。用 `INSERT` 写入 JSON 文本时请使用 `PARSE_TO_VARIANT`;Stream Load 
等导入作业仍会解析 JSON。
+- 整个 VARIANT 值支持 `GROUP BY`、`DISTINCT` 和集合运算;两个 VARIANT 值之间可以用 `=`、`!=`、`<=>` 
比较,也可以作为 Join 键、`ORDER BY` 键和窗口键。
+- 即使路径在 Schema Template 中声明了类型,`v['path']` 仍是 `VARIANT` 类型,需要显式 CAST。会话变量 
`enable_variant_schema_auto_cast` 已不再生效。
+- `VARIANT_TYPE` 返回单个类型名称(如 `object`),不再返回从路径到类型的映射。
+- VARIANT 数组可以用整数下标访问,下标从 1 开始。
 
-### 建表语法
+Doris 4.x 的行为请参阅本页的 4.x 版本。
+:::
 
-建表时将列类型声明为 VARIANT:
+## 快速上手 {#quick-start}
 
 ```sql
-CREATE TABLE IF NOT EXISTS ${table_name} (
-    k BIGINT,
-    v VARIANT
+CREATE TABLE events (
+    id BIGINT,
+    v  VARIANT
 )
-PROPERTIES("replication_num" = "1");
+DUPLICATE KEY(id)
+DISTRIBUTED BY HASH(id) BUCKETS 1
+PROPERTIES ("replication_num" = "1");
+
+-- INSERT 会把字符串字面量保留为 VARIANT 字符串,因此 JSON 文本需要显式解析。
+INSERT INTO events VALUES
+    (1, PARSE_TO_VARIANT('{"user": {"id": 42, "name": "alice"}, "tags": 
["doris", "sql"], "score": 9.5}')),
+    (2, PARSE_TO_VARIANT('{"user": {"id": 7, "name": "bob"}, "score": 3}'));
+
+SELECT id,
+       CAST(v['user']['name'] AS STRING) AS name,
+       v['tags'][1] AS first_tag
+FROM events
+WHERE CAST(v['score'] AS DOUBLE) > 5;
+```
+
+```text
++------+-------+-----------+
+| id   | name  | first_tag |
++------+-------+-----------+
+|    1 | alice | doris     |
++------+-------+-----------+
+```
+
+- `v['user']['name']` 和 `v['tags'][1]` 返回 `VARIANT` 值。数组下标从 1 开始。
+- 对路径做比较或计算之前,先把它 CAST 为具体类型。`v['score'] > 5` 
通过[隐式转换](#implicit-conversion)也能执行,但它按 `DECIMAL(38, 9)` 比较,而且无法利用索引。
+- Stream Load 等导入作业会自动解析 JSON 文本。参见[写入数据](#write-data)。
+
+## 定义 VARIANT 列 {#define-a-variant-column}
+
+```sql
+column_name VARIANT
+column_name VARIANT< field_definition [, field_definition ...] >
+column_name VARIANT< properties('key' = 'value' [, ...]) >
+column_name VARIANT< field_definition [, ...], properties('key' = 'value' [, 
...]) >
+
+field_definition:
+    [MATCH_NAME | MATCH_NAME_GLOB] 'path_or_pattern' : data_type [COMMENT 
'comment']
 ```
 
-通过 Schema Template 约束部分 Path 的类型(更多见“扩展类型”):
+- `field_definition` 列表就是 [Schema Template](#schema-template),用于固定部分路径的存储类型。
+- `properties(...)` 用于设置列级存储属性,参见[列属性](#column-properties)。
+- VARIANT 列可以是 `NULL` 或 `NOT NULL`,默认值只能是 `NULL`。
 
 ```sql
-CREATE TABLE IF NOT EXISTS ${table_name} (
+CREATE TABLE IF NOT EXISTS example_tbl (
     k BIGINT,
-    v VARIANT <
-        'id' : INT,            -- path 为 id 的子列被限制为 INT 类型
-        'message*' : STRING,   -- 前缀匹配 message* 的子列被限制为 STRING 类型
-        'tags*' : ARRAY<TEXT>  -- 前缀匹配 tags* 的子列被限制为 ARRAY<TEXT> 类型
-    >
+    v VARIANT<
+        'id' : INT,             -- 路径 id 以 INT 存储
+        'message*' : STRING,    -- 匹配 message* 的路径以 STRING 存储
+        'tags*' : ARRAY<TEXT>,  -- 匹配 tags* 的路径以 ARRAY<TEXT> 存储
+        properties('variant_max_subcolumns_count' = '2048')
+    > NULL
 )
-PROPERTIES("replication_num" = "1");
+DUPLICATE KEY(k)
+DISTRIBUTED BY HASH(k) BUCKETS 1
+PROPERTIES ("replication_num" = "1");
 ```
 
-### 查询语法
+VARIANT 列在表中的使用范围:
+
+| 用法 | 是否支持 | 说明 |
+| --- | --- | --- |
+| Duplicate Key、Unique Key、Aggregate Key 表的 Value 列 | 支持 | 在 Aggregate Key 
表中,聚合类型必须是 `REPLACE` 或 `REPLACE_IF_NOT_NULL`。 |
+| Key 列、分区列、分桶列 | 不支持 | |
+| 在表结构中嵌套在其他类型内(`ARRAY<VARIANT>`、`MAP`、`STRUCT`) | 不支持 | 查询结果仍可以是 
`ARRAY<VARIANT>`,例如 `COLLECT_LIST(v)` 的结果。 |
+| 默认值 | 只能是 `NULL` | `DEFAULT '{}'` 等非 NULL 默认值会被拒绝。 |
+
+## 写入数据 {#write-data}
+
+### 输入如何变成 VARIANT 值 {#how-input-becomes-a-variant-value}
+
+| 写入方式 | 结果 |
+| --- | --- |
+| `INSERT ... VALUES` 或 `INSERT ... SELECT` 写入 `CHAR`、`VARCHAR`、`STRING` 表达式 | 
VARIANT **字符串**。即使内容看起来像 JSON,也不会被解析。从 `s3()`、`hdfs()`、`local()` 等表函数执行 `INSERT 
INTO ... SELECT`、带 `http_stream` SQL 语句的 Stream Load,以及以 SQL 文本发送的 Group Commit 
INSERT,都是如此。 |
+| `INSERT` 写入 `PARSE_TO_VARIANT(expr)` 或 `TRY_PARSE_TO_VARIANT(expr)` | 解析后的 
JSON 值。参见[解析错误](#parse-errors)。 |
+| `INSERT` 写入 `JSON`/`JSONB` 表达式 | 按原结构直接转换。 |
+| `INSERT` 写入其他类型的表达式 | 带类型的值,参见[其他类型 CAST 为 VARIANT](#cast-to-variant)。 |
+| 通过 JDBC 服务端预编译语句(`useServerPrepStmts=true`)执行的 Group Commit INSERT | 
按导入作业的方式执行,因此字符串会按 JSON 解析。 |
+| 导入作业(Stream Load、Broker Load、Routine Load) | 写入 VARIANT 列的字符串字段会按 JSON 
解析,与文件格式无关:CSV 文本、Parquet 的 `STRING` 列、JSON 中的字符串值都是如此。Arrow 格式不能导入 VARIANT 
列。CSV 中的 `\N` 导入为 SQL `NULL`。 |
+| JSON 格式的导入作业 | 字段对应的 JSON 值。JSON 字符串会再按 JSON 文本解析一次:`"123"` 导入为数值 
`123`,`"true"` 导入为布尔值 `true`,`"{\"a\": 1}"` 导入为对象,`"hello"` 仍是字符串 `hello`。顶层的 
JSON 布尔值导入为数值 `1` 或 `0`。JSON `null` 或缺失的字段导入为 SQL `NULL`。 |
+
+`NOT NULL` 的 VARIANT 列不接受 SQL `NULL`:严格模式下 `INSERT` 会失败,导入作业会过滤该行,文本解析失败而得到 
SQL `NULL` 的行也会被过滤。被过滤的行计入 `max_filter_ratio`,其默认值为 `0`,因此导入作业会失败。
 
 ```sql
--- 访问嵌套字段(返回类型为 VARIANT,需要显式或隐式 CAST 才能聚合/比较)
-SELECT v['properties']['title'] FROM ${table_name};
+CREATE TABLE variant_tbl (k INT, v VARIANT)
+DUPLICATE KEY(k)
+DISTRIBUTED BY HASH(k) BUCKETS 1
+PROPERTIES ("replication_num" = "1");
 
--- 聚合前显式 CAST 为确定类型
-SELECT CAST(v['properties']['title'] AS STRING) AS title
-FROM ${table_name}
-GROUP BY title;
+INSERT INTO variant_tbl VALUES
+    (1, '{"a": 1}'),                     -- 写入为字符串
+    (2, PARSE_TO_VARIANT('{"a": 1}'));   -- 写入为对象
 
--- 数组查询示例
-SELECT *
-FROM ${table_name}
-WHERE ARRAY_CONTAINS(CAST(v['tags'] AS ARRAY<TEXT>), 'Doris');
+SELECT k, v, VARIANT_TYPE(v) AS type, v['a'] FROM variant_tbl ORDER BY k;
 ```
 
-## 创建和访问值
+```text
++------+----------+--------+--------+
+| k    | v        | type   | v['a'] |
++------+----------+--------+--------+
+|    1 | {"a": 1} | string | NULL   |
+|    2 | {"a":1}  | object | 1      |
++------+----------+--------+--------+
+```
 
-:::info 版本说明
-本节所述行为适用于 Doris 4.2 及后续版本。
-:::
+字符串根值输出时不带引号,因此写入的字符串看起来可能和 JSON 一样,可以用 `VARIANT_TYPE` 区分。如需把这类字符串转成结构化值,请通过 
`PARSE_TO_VARIANT(CAST(v AS STRING))` 重新写入。
 
-VARIANT 值可以从 JSON 文本、JSON/JSONB 值或带确定类型的 SQL 表达式创建:
+分步骤的导入示例请参阅[导入 VARIANT 
数据](../../../../data-operate/import/complex-types/variant.md)。
 
-- 如果要将字符串或 JSON/JSONB 表达式解析为结构化 VARIANT 值,请使用 
[PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/parse-to-variant)。
-- 如果要将受支持的 SQL 值转换为 VARIANT,请使用 `CAST(expression AS VARIANT)`。字符串会保留为 VARIANT 
字符串值,该 CAST 不解析 JSON。
+### 解析错误 {#parse-errors}
 
-### 解析 JSON 文本
+[PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/parse-to-variant.md)、[TRY_PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/try-parse-to-variant.md)
 和导入作业使用同一个 JSON 解析器。导入作业处理错误的方式与 `TRY_PARSE_TO_VARIANT` 相同:
 
-```sql
-SELECT PARSE_TO_VARIANT('{"user": {"id": 42}, "active": true}');
-SELECT PARSE_TO_VARIANT('[10, 20, 30]');
-SELECT PARSE_TO_VARIANT(CAST('{"user": {"id": 42}}' AS JSON));
-```
+| 输入 | `PARSE_TO_VARIANT` | `TRY_PARSE_TO_VARIANT` 与导入作业 |
+| --- | --- | --- |
+| 合法 JSON | 解析后的值 | 解析后的值 |
+| 非法 JSON 文本,如 `hello` 或 `{"id":` | 保留为 VARIANT 字符串 | 保留为 VARIANT 字符串 |
+| 包含超出 [-2^63, 2^64 - 1] 的整数,或超出 `DOUBLE` 范围的数值的 JSON,如 `{"a": 1, "n": 
100000000000000000000}` | 整段文本保留为 VARIANT 字符串,因此 `v['a']` 返回 `NULL` | 整段文本保留为 
VARIANT 字符串 |
+| 空字符串 | 空对象 `{}` | 空对象 `{}` |
+| 嵌套超过 128 层 | 报错 | SQL `NULL` |
+| 对象 key 超过 `variant_max_json_key_length` 字节(BE 配置,默认 255) | 报错 | SQL `NULL` |
+| 同一对象中有重复 key | 报错 | SQL `NULL` |
+| 不是合法 UTF-8 的字符串 | 报错 | SQL `NULL` |
+
+嵌套深到 JSON 解析器本身拒绝的文档(约 1000 层)属于非法 JSON,因此会保留为字符串。
 
-如果非法 JSON 应该返回 SQL `NULL` 而不是使查询失败,请使用 
[TRY_PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/try-parse-to-variant)。
+有两个 BE 配置会改变上述规则:
 
-### 访问对象和数组
+- `variant_throw_exeception_on_invalid_json`(默认 `false`):设置为 `true` 
后,解析器无法接受的文本(包括含超范围数值的 JSON)不再保留为 VARIANT 字符串,而是对 `PARSE_TO_VARIANT` 报错,对 
`TRY_PARSE_TO_VARIANT` 和导入作业返回 SQL `NULL`。
+- `variant_enable_duplicate_json_path_check`(默认 `false`):设置为 `true` 后,对象中重复的 
key 保留第一个值,不再报错。含 `.` 的 key 与嵌套路径重复时,写入也不再失败,只会存储其中一个值。
 
-对象字段可以使用字符串 key 访问。在 Doris 4.2 及后续版本中,VARIANT 数组的正数索引从 1 
开始,负数索引从数组末尾倒数。提取出的值仍是 `VARIANT`,如需按确定类型比较、计算或聚合,请先 CAST。
+如需让含超范围整数的文档保持结构,可以先转换为 JSON:`CAST(CAST(text AS JSON) AS VARIANT)`。不超过 38 
位的整数保持为精确的数值,更大但仍在 `LARGEINT` 范围内的整数变为字符串,再大的整数变为 
`DOUBLE`。`PARSE_TO_VARIANT(CAST(text AS JSON))` 无法解决这个问题,因为它会重新解析 JSON 文本。
 
 ```sql
-SELECT CAST(PARSE_TO_VARIANT('{"user": {"id": 42}}')['user']['id'] AS BIGINT);
-SELECT ELEMENT_AT(PARSE_TO_VARIANT('[10, 20, 30]'), 1);  -- 10
-SELECT ELEMENT_AT(PARSE_TO_VARIANT('[10, 20, 30]'), -1); -- 30
+SELECT VARIANT_TYPE(PARSE_TO_VARIANT('{"id": 1}')) AS valid_json,    -- object
+       VARIANT_TYPE(PARSE_TO_VARIANT('{"id":'))    AS invalid_json,  -- string
+       VARIANT_TYPE(CAST('{"id": 1}' AS VARIANT))  AS string_cast;   -- string
 ```
 
-对象和数组访问的详细说明请参见 
[ELEMENT_AT](../../../sql-functions/scalar-functions/variant-functions/element-at)。
+### 写入后值的规范化 {#what-storage-keeps}
+
+值写入表时会被规范化,读回的值可能与写入前计算出的值不同:
+
+| 写入前 | 从表中读回 |
+| --- | --- |
+| 值为 JSON `null` 的对象成员,如 `{"a": null, "b": 1}` | 该成员被移除:`{"b":1}`;`v['a']` 返回 
SQL `NULL`。 |
+| 值为空对象或空数组的对象成员,以及按这些规则变空的对象,如 `{"a": {}, "b": [], "c": {"d": null}}` | 
被移除:`{}` |
+| 值为只含 `null` 元素的数组的对象成员,如 `{"p": [null, null], "q": 1}` | 可能被移除:`{"q":1}` |
+| 数组内的值,如 `{"arr": [{"a": null}, {}, [], null]}` | 原样保留;`v['arr'][1]['a']` 是 
VARIANT `null`。 |
+| 根值为 JSON `null`,如 `PARSE_TO_VARIANT('null')` | 空对象 `{}` |
+| 根值为空数组 `[]` 或空对象 `{}` | 保留 |
+| 不在 Schema Template 路径上的 `DATE`、`DATETIME` 值,如 `CAST(date_col AS VARIANT)` | 
以文本形式存储,读回时是字符串 |
+| 同一路径上混有布尔值和数值 | 布尔值可能读回为 `1` 或 `0`,取决于同一次写入中值的先后顺序以及 Compaction |
+| 含 `.` 的 key,如 `{"a.b": 1}` | 按嵌套路径存储:`{"a":{"b":1}}`,`v['a.b']` 和 
`v['a']['b']` 都返回 `1`。如果一个文档同时包含 key `a.b` 和 `a` 下的 key `b`,整条 INSERT 
或整个导入作业都会失败,除非 `variant_enable_duplicate_json_path_check` 为 `true`。 |
+| 对象 key | 按字节序返回 |
 
-## CAST 规则
+这些规则会受同一次写入的其他行以及 DOC mode 影响,因此不要依赖 `null` 值或空容器在写入后仍然保留。在 Schema Template 
中声明过的路径会转换为声明的类型,参见 [Schema Template](#schema-template)。
 
-VARIANT 的 CAST 包括两个方向:把受支持的 SQL 值转换为 VARIANT,以及把 VARIANT 中兼容的值转换为具体 SQL 类型。
+## 类型推断与类型冲突 {#type-inference-and-type-conflicts}
 
-### 其他类型 CAST 为 VARIANT
+没有 Schema Template 时,Doris 在解析 JSON 时推断每个值的类型,`VARIANT_TYPE` 返回的就是这个类型:
 
-| 源类型 | 行为 |
+| JSON 值 | 类型 |
 | --- | --- |
-| `CHAR`、`VARCHAR`、`STRING` | 将输入保留为 VARIANT 字符串,不解析看起来像 JSON 的文本。 |
-| `BOOLEAN` | 保留 Boolean 值。 |
-| `TINYINT`、`SMALLINT`、`INT`、`BIGINT`、`LARGEINT` | 保留整数值。 |
-| `FLOAT`、`DOUBLE` | 保留浮点数值。 |
-| `DECIMALV2`、`DECIMAL(p, s)`(`p <= 38`) | 保留 Decimal 值,但需满足下文限制。 |
-| `DATE`、`DATETIME`、`TIMESTAMP_NS`、`TIMESTAMPTZ` | 保留对应的逻辑类型和值。 |
-| `IPV4`、`IPV6` | 保留 IP 地址值。 |
-| `JSON` / `JSONB` | 将结构化值直接转换为 VARIANT;如果输入包含 VARIANT 无法表示的 JSONB 值类型,BE 会报错。 
|
-| `ARRAY<T>` | 当 `T` 为 `VARIANT` 或也在该白名单中时递归转换每个元素,并保留 SQL NULL 元素。 |
-
-仅支持上表列出的源类型。其他源类型,包括 `MAP`、`STRUCT`、`TIME`、precision 超过 38 的 
Decimal,以及包含不支持元素类型的数组,都会由 BE 报错。
+| `BIGINT` 范围内的整数 | `tinyint`、`smallint`、`int` 或 `bigint` 中能容纳该值的最小类型 |
+| 超出 `BIGINT` 范围、不超过 18446744073709551615 的整数 | `decimal` |
+| 带小数部分或指数的数值 | `double` |
+| 超出 [-2^63, 2^64 - 1] 的整数,或超出 `DOUBLE` 范围的数值 | 不作为数值处理:整段文本都是非法 
JSON(参见[解析错误](#parse-errors)) |
+| 字符串 | `string` |
+| `true`、`false` | `bool` |
+| `null` | `null` |
+| 数组、对象 | `array`、`object` |
 
-```sql
--- 字符串会保留为 VARIANT 字符串根值,即使内容看起来像 JSON。
-SELECT CAST(CAST('{"id": 1}' AS VARIANT) AS STRING) AS string_value,
-       VARIANT_TYPE(CAST('{"id": 1}' AS VARIANT)) AS root_type;
--- string_value:{"id": 1};root_type:string
+数据存储时,每个路径只有一种存储类型:
 
--- 需要结构化 VARIANT 值时,显式解析 JSON 文本。
-SELECT PARSE_TO_VARIANT('{"id": 1}') AS parsed_object;
--- {"id":1}
+- 整数存为 `BIGINT`,需要时存为 `LARGEINT`;浮点数存为 `DOUBLE`,定点数存为 `DECIMAL`,字符串存为 
`STRING`,布尔值存为 `BOOLEAN`,由标量组成的数组存为 `ARRAY<T>`。
+- 对象数组、嵌套数组,以及值的类型不一致的路径(例如整数与浮点数、数值与字符串、标量与数组),存为 `JSONB`(`DESC` 中显示为 
`json`)。元素类型冲突的数组存为 `ARRAY<JSONB>`。
 
--- JSON/JSONB 输入按结构转换。
-SELECT CAST(CAST('{"id": 1}' AS JSON) AS VARIANT) AS parsed_object;
--- {"id":1}
+```sql
+{"a" : 12345678}
+{"a" : "HelloWorld"}
+-- a 存为 JSONB
 ```
 
-字符串 CAST 不解析 JSON,因此非法 JSON 文本仍是合法的 VARIANT 字符串。如需严格解析 JSON,请使用 
`PARSE_TO_VARIANT`;如需在解析失败时返回 SQL `NULL`,请使用 `TRY_PARSE_TO_VARIANT`。
+`JSONB` 
路径保留所有值,但失去类型化存储的能力:索引和基于类型的裁剪都不再作用于该路径。布尔值是一个特例:如果路径上的第一个值是布尔值、后续的值是数值,该路径可能按数值存储,布尔值读回为
 `1` 或 `0`。需要稳定类型的路径,请在 Schema Template 中声明。
 
-### VARIANT CAST 为其他类型
+`VARIANT_TYPE` 报告的是每个值的类型,因此从 `BIGINT` 路径读出的整数仍可能是 `tinyint`。要查看每个路径的存储类型,请执行 
`SET describe_extend_variant_column = true;` 后再执行 `DESC 
table_name;`,参见[查看子列与类型](#inspect-subcolumns-and-types)。
 
-VARIANT 可以 CAST 为兼容的标量、JSON/JSONB 或数组类型:
+## 访问路径与输出 {#access-paths-and-output}
 
-| 目标类型 | 行为 |
-| --- | --- |
-| `BOOLEAN` | 转换兼容的 Boolean 或标量根值。 |
-| `TINYINT`、`SMALLINT`、`INT`、`BIGINT`、`LARGEINT` | 将兼容的标量根值转换为指定整数类型。 |
-| `FLOAT`、`DOUBLE` | 转换兼容的数值根。 |
-| `DECIMALV2`、`DECIMAL(p, s)` | 将兼容的数值根转换为指定 Decimal 类型。 |
-| `DATE`、`DATETIME`、`TIMESTAMP_NS`、`TIMESTAMPTZ` | 转换兼容的日期时间根值。 |
-| `CHAR`、`VARCHAR`、`STRING` | 标量根值返回对应文本,对象和数组返回 JSON 文本。Variant/JSON `null` 
返回字符串 `null`,外层 SQL `NULL` 仍是 SQL `NULL`。 |
-| `IPV4`、`IPV6` | 将兼容的 IP 地址根值转换为指定的 IP 地址类型。 |
-| `JSON` / `JSONB` | 按结构转换;如果 VARIANT 值包含 JSON/JSONB 无法表示的类型,BE 会报错。 |
-| `ARRAY<T>` | 当 `T` 为 `VARIANT` 或也在该白名单中时逐元素转换;不兼容元素遵循目标类型的 CAST 规则。 |
-
-仅支持上表列出的目标类型。其他目标类型,包括 `MAP`、`STRUCT` 和 `TIME`,都会由 BE 
报错。对于受支持的目标类型,值形状不兼容、文本非法或数值越界时,按照对应 CAST 模式报错或返回 SQL `NULL`。
+- `v['key']` 和 `v['a']['b']` 读取对象成员;`v['arr'][1]` 读取数组元素,下标从 1 开始,`-1` 
表示最后一个元素。[ELEMENT_AT](../../../sql-functions/scalar-functions/variant-functions/element-at.md)
 与之等价。
+- 结果是 `VARIANT` 值。key 不存在、下标为 `0` 或越界、对数组使用字符串 key、对对象使用整数下标、对标量值使用 key,都返回 
SQL `NULL`。
+- 在计算出的值中,含 `.` 的 key 是一个整体:`v['a.b']` 读取 key `a.b`,而 `v['a']['b']` 读取 `a` 下的 
`b`。存储不保留这种区分,参见[写入后值的规范化](#what-storage-keeps)。
+- 路径不会自动展开数组。对于 `{"a": [{"b": 1}]}`,`v['a']['b']` 返回 `NULL`,应写作 
`v['a'][1]['b']`。
 
 ```sql
-SELECT CAST(PARSE_TO_VARIANT('42') AS BIGINT) AS id;
--- 42
+SELECT v['user']['id']      AS id,       -- 42
+       v['tags'][-1]        AS last_tag, -- sql
+       v['user']['missing'] AS missing   -- NULL
+FROM events
+WHERE id = 1;
+```
 
-SELECT CAST(PARSE_TO_VARIANT('[1, null, 3]') AS ARRAY<INT>) AS values;
--- [1, NULL, 3]
+常见写法是把路径 CAST 为查询需要的类型:
 
-SELECT CAST(PARSE_TO_VARIANT('{"id": 1}') AS JSON) AS json_value;
--- {"id":1}
+```sql
+SELECT * FROM tbl WHERE ARRAY_CONTAINS(CAST(v['tags'] AS ARRAY<TEXT>), 
'Doris');
+SELECT * FROM tbl WHERE CAST(v['date'] AS DATE) = '2021-01-02';
+SELECT * FROM tbl WHERE v['bool'];                -- 隐式 CAST 为 BOOLEAN
+SELECT * FROM tbl WHERE v['str'] MATCH 'Doris';   -- 使用该路径上的倒排索引
 ```
 
-### Decimal 与日期时间转换限制
+读取整个 VARIANT 值会返回 JSON 文本。对象 key 
按字节序输出,且不含空白,因此与输入文本并非按字节完全一致。字符串根值输出时不带引号。`CAST(v AS STRING)` 则按对应 SQL 
类型的格式输出标量根值,例如布尔根值变为 `1` 或 `0`。
+
+```sql
+INSERT INTO variant_tbl VALUES (3, PARSE_TO_VARIANT('{ "b": 2, "a": 1, "c": { 
"y": 20, "x": 10 } }'));
 
-| Doris 输入类型 | VARIANT 支持情况 |
+SELECT v FROM variant_tbl WHERE k = 3;
+-- {"a":1,"b":2,"c":{"x":10,"y":20}}
+```
+
+## CAST 与隐式转换 {#cast-and-implicit-conversion}
+
+### 其他类型 CAST 为 VARIANT {#cast-to-variant}
+
+| 源类型 | 结果 |
+| --- | --- |
+| `CHAR`、`VARCHAR`、`STRING` | VARIANT 字符串,不解析 JSON 文本。字符串必须是合法的 UTF-8,否则 CAST 
报错。 |
+| `BOOLEAN` | 布尔值。 |
+| `TINYINT`、`SMALLINT`、`INT`、`BIGINT` | 整数。 |
+| `LARGEINT` | 定点数。绝对值超过 10^38 - 1 的值会变成字符串。 |
+| `FLOAT`、`DOUBLE` | 浮点数。 |
+| `DECIMALV2`、`DECIMAL(p, s)`(`p <= 38`) | 定点数。 |
+| `DATE`、`DATETIME(p)`、`TIMESTAMP_NS` | 日期,或不带时区的时间戳。 |
+| `IPV4`、`IPV6` | 字符串,内容为该值的文本形式。 |
+| `JSON` / `JSONB` | 保持原结构。包含 VARIANT 无法表示的值(如 `DECIMAL256` 数值),或对象中有重复 key 
时,CAST 报错。 |
+| `ARRAY<T>` | 数组,逐个转换元素。`T` 必须是 `VARIANT` 或本表中的类型。 |
+| `MAP`、`STRUCT`、`TIME`、`TIMESTAMPTZ`、`VARBINARY` 等其他类型 | 不支持,语句报错。 |
+
+源值还必须是其类型的合法值,非法值会被拒绝,不会被自动修复。CAST 不会解析字符串:`CAST('{"id": 1}' AS VARIANT)` 
得到字符串 `{"id": 1}`。需要解析 JSON 文本时请使用 `PARSE_TO_VARIANT`。
+
+### VARIANT CAST 为其他类型 {#cast-from-variant}
+
+| 目标类型 | 结果 |
+| --- | --- |
+| `BOOLEAN` | 布尔值保持不变;数值非零即为 `true`;字符串按 `CAST(string AS BOOLEAN)` 的规则转换。 |
+| `TINYINT`、`SMALLINT`、`INT`、`BIGINT`、`LARGEINT` | 整数。定点数和浮点数的小数部分被截断(`1.5` 变为 
`1`);布尔值变为 `1` 或 `0`;`"123"` 这样的数字字符串会被转换。 |
+| `FLOAT`、`DOUBLE`、`DECIMAL(p, s)` | 数值和数字字符串。`DECIMAL` 按 scale `s` 舍入。超出 
`FLOAT` 范围的值变为 `Infinity`。 |
+| `DATE`、`DATETIME(p)`、`TIMESTAMP_NS` | 日期时间值、日期时间格式的字符串,以及 `20240102` 这样的数值。 |
+| `TIMESTAMPTZ(p)` | 日期时间值,以及日期时间格式的字符串。 |
+| `IPV4`、`IPV6` | IP 地址格式的字符串。 |
+| `CHAR`、`VARCHAR`、`STRING` | 字符串根值原样返回,不带引号;对象和数组返回 JSON 文本;其他标量按对应 SQL 
类型的格式输出:布尔根值为 `1` 或 `0`,`DATETIME` 值带 6 位小数,`TIMESTAMP_NS` 值带 9 位小数。VARIANT 
`null` 返回字符串 `null`。 |
+| `JSON` / `JSONB` | 保持原结构。没有 JSON 对应类型的值(如日期、时间戳)会变成 JSON 
字符串;带时区的时间戳按会话时区格式化。 |
+| `ARRAY<T>` | 按元素转换的数组,无法转换的元素变为 `NULL`。内容为 JSON 数组的字符串(如 `"[1, 
2]"`)也会被转换。其他值返回 `NULL`。 |
+| `MAP`、`STRUCT`、`TIME` 等其他类型 | 不支持,语句报错。 |
+
+值无法转换为目标类型时返回 SQL `NULL`,开启 `enable_strict_cast` 
时也是如此。例外是值的类型与目标类型之间根本没有转换,例如把数值 CAST 为 `TIMESTAMPTZ`、`IPV4`、`IPV6`,或把日期、时间戳 
CAST 为 `BOOLEAN`:这时语句会报错。
+
+```sql
+SELECT CAST(PARSE_TO_VARIANT('"123"') AS INT)      AS from_string,   -- 123
+       CAST(PARSE_TO_VARIANT('"abc"') AS INT)      AS not_a_number,  -- NULL
+       CAST(PARSE_TO_VARIANT('1.5') AS INT)        AS truncated,     -- 1
+       CAST(PARSE_TO_VARIANT('300') AS TINYINT)    AS overflow,      -- NULL
+       CAST(PARSE_TO_VARIANT('{"a": 1}') AS INT)   AS from_object,   -- NULL
+       CAST(PARSE_TO_VARIANT('[1, "2", null, "x"]') AS ARRAY<INT>) AS arr;  -- 
[1, 2, null, null]
+
+SELECT CAST(PARSE_TO_VARIANT('true') AS STRING)        AS bool_root,     -- 1
+       CAST(PARSE_TO_VARIANT('{"b": true}') AS STRING) AS object_text,   -- 
{"b":true}
+       CAST(PARSE_TO_VARIANT('"abc"') AS STRING)       AS string_root,   -- abc
+       CAST(PARSE_TO_VARIANT('null') AS STRING)        AS variant_null;  -- 
null
+```
+
+### Decimal 与日期时间值 {#decimal-and-datetime-values}
+
+把以下类型转换为 VARIANT 时:
+
+| Doris 类型 | 行为 |
 | --- | --- |
 | 旧版 `DECIMALV2` | 精确保留 precision 不超过 27、scale 不超过 9 的值。 |
-| `DECIMAL(p, s)` | 精确保留 `1 <= p <= 38` 且 `0 <= s <= p` 的值;不支持需要超过 38 位 
precision 的值。 |
-| `DATE` | 保留为不含时间和时区的日历日期。 |
-| 旧版 `DATETIME` | 保留到秒,不进行时区调整。 |
-| `DATETIME(p)` | 支持 `0 <= p <= 6`,不进行时区调整。 |
-| `TIMESTAMP_NS` | 保留固定纳秒精度且不进行时区调整;值必须在 TIMESTAMP_NS 取值范围内。 |
-| `TIMESTAMPTZ(p)` | 支持 `0 <= p <= 6`,保留带时区调整的 timestamp 语义。 |
-| precision 超过 38 的 Decimal | 不支持作为 VARIANT 输入。 |
-| `TIME` | 不支持作为 VARIANT 输入。 |
+| `DECIMAL(p, s)` | 精确保留 `1 <= p <= 38` 且 `0 <= s <= p` 的值;不支持 precision 超过 38 
的 Decimal。 |
+| `DATE` | 不含时间和时区的日历日期。 |
+| `DATETIME(p)` | `0 <= p <= 6`,不做时区调整。 |
+| `TIMESTAMP_NS` | 纳秒精度,不做时区调整;值必须在 TIMESTAMP_NS 取值范围内。 |
+| `TIMESTAMPTZ(p)` | 不能 CAST 为 VARIANT。可以在 Schema Template 中把路径声明为 
`TIMESTAMPTZ`。 |

Review Comment:
    Schema Template 禁止一下



##########
i18n/zh-CN/docusaurus-plugin-content-docs/current/sql-manual/basic-element/sql-data-types/semi-structured/VARIANT.md:
##########
@@ -19,269 +17,511 @@ VARIANT 类型用于存储半结构化 JSON 数据,可包含不同基础类型
 - 关键路径可以建立路径级索引,支持全文检索,同时继续受益于 Doris 的稀疏索引裁剪能力。
 - 
面向宽列场景的存储优化,让万级子列规模的自动子列列式提取(Subcolumnization)保持可用。若参与子列列式提取(Subcolumnization)的路径接近
 10000,对硬件要求会明显提高,通常应优先评估 DOC mode。
 
-如果你还在决定默认模式、Sparse、DOC mode 还是 Schema Template,建议先阅读 [VARIANT 
使用与配置指南](./variant-workload-guide)。本页主要提供语法、类型规则、索引、限制和配置参考。
+如果你还在决定默认模式、Sparse、DOC mode 还是 Schema Template,建议先阅读 [VARIANT 
使用与配置指南](./variant-workload-guide.md)。本页提供写入与解析、类型规则、CAST、NULL 
与比较语义、ALTER、索引、限制和配置的参考。
 :::
 
-## 使用 VARIANT 类型
+:::info 版本说明
+本页描述 Doris 5.0.0 及之后版本中的 VARIANT。与 Doris 4.x 相比,最容易影响已有 SQL 的差异有:
+
+- `INSERT` 会把字符串作为 VARIANT 字符串写入,不再按 JSON 解析,从 `s3()`、`hdfs()` 等表函数执行 `INSERT 
INTO ... SELECT` 也是如此。用 `INSERT` 写入 JSON 文本时请使用 `PARSE_TO_VARIANT`;Stream Load 
等导入作业仍会解析 JSON。
+- 整个 VARIANT 值支持 `GROUP BY`、`DISTINCT` 和集合运算;两个 VARIANT 值之间可以用 `=`、`!=`、`<=>` 
比较,也可以作为 Join 键、`ORDER BY` 键和窗口键。
+- 即使路径在 Schema Template 中声明了类型,`v['path']` 仍是 `VARIANT` 类型,需要显式 CAST。会话变量 
`enable_variant_schema_auto_cast` 已不再生效。
+- `VARIANT_TYPE` 返回单个类型名称(如 `object`),不再返回从路径到类型的映射。
+- VARIANT 数组可以用整数下标访问,下标从 1 开始。
 
-### 建表语法
+Doris 4.x 的行为请参阅本页的 4.x 版本。
+:::
 
-建表时将列类型声明为 VARIANT:
+## 快速上手 {#quick-start}
 
 ```sql
-CREATE TABLE IF NOT EXISTS ${table_name} (
-    k BIGINT,
-    v VARIANT
+CREATE TABLE events (
+    id BIGINT,
+    v  VARIANT
 )
-PROPERTIES("replication_num" = "1");
+DUPLICATE KEY(id)
+DISTRIBUTED BY HASH(id) BUCKETS 1
+PROPERTIES ("replication_num" = "1");
+
+-- INSERT 会把字符串字面量保留为 VARIANT 字符串,因此 JSON 文本需要显式解析。
+INSERT INTO events VALUES
+    (1, PARSE_TO_VARIANT('{"user": {"id": 42, "name": "alice"}, "tags": 
["doris", "sql"], "score": 9.5}')),
+    (2, PARSE_TO_VARIANT('{"user": {"id": 7, "name": "bob"}, "score": 3}'));
+
+SELECT id,
+       CAST(v['user']['name'] AS STRING) AS name,
+       v['tags'][1] AS first_tag
+FROM events
+WHERE CAST(v['score'] AS DOUBLE) > 5;
+```
+
+```text
++------+-------+-----------+
+| id   | name  | first_tag |
++------+-------+-----------+
+|    1 | alice | doris     |
++------+-------+-----------+
+```
+
+- `v['user']['name']` 和 `v['tags'][1]` 返回 `VARIANT` 值。数组下标从 1 开始。
+- 对路径做比较或计算之前,先把它 CAST 为具体类型。`v['score'] > 5` 
通过[隐式转换](#implicit-conversion)也能执行,但它按 `DECIMAL(38, 9)` 比较,而且无法利用索引。
+- Stream Load 等导入作业会自动解析 JSON 文本。参见[写入数据](#write-data)。
+
+## 定义 VARIANT 列 {#define-a-variant-column}
+
+```sql
+column_name VARIANT
+column_name VARIANT< field_definition [, field_definition ...] >
+column_name VARIANT< properties('key' = 'value' [, ...]) >
+column_name VARIANT< field_definition [, ...], properties('key' = 'value' [, 
...]) >
+
+field_definition:
+    [MATCH_NAME | MATCH_NAME_GLOB] 'path_or_pattern' : data_type [COMMENT 
'comment']
 ```
 
-通过 Schema Template 约束部分 Path 的类型(更多见“扩展类型”):
+- `field_definition` 列表就是 [Schema Template](#schema-template),用于固定部分路径的存储类型。
+- `properties(...)` 用于设置列级存储属性,参见[列属性](#column-properties)。
+- VARIANT 列可以是 `NULL` 或 `NOT NULL`,默认值只能是 `NULL`。
 
 ```sql
-CREATE TABLE IF NOT EXISTS ${table_name} (
+CREATE TABLE IF NOT EXISTS example_tbl (
     k BIGINT,
-    v VARIANT <
-        'id' : INT,            -- path 为 id 的子列被限制为 INT 类型
-        'message*' : STRING,   -- 前缀匹配 message* 的子列被限制为 STRING 类型
-        'tags*' : ARRAY<TEXT>  -- 前缀匹配 tags* 的子列被限制为 ARRAY<TEXT> 类型
-    >
+    v VARIANT<
+        'id' : INT,             -- 路径 id 以 INT 存储
+        'message*' : STRING,    -- 匹配 message* 的路径以 STRING 存储
+        'tags*' : ARRAY<TEXT>,  -- 匹配 tags* 的路径以 ARRAY<TEXT> 存储
+        properties('variant_max_subcolumns_count' = '2048')
+    > NULL
 )
-PROPERTIES("replication_num" = "1");
+DUPLICATE KEY(k)
+DISTRIBUTED BY HASH(k) BUCKETS 1
+PROPERTIES ("replication_num" = "1");
 ```
 
-### 查询语法
+VARIANT 列在表中的使用范围:
+
+| 用法 | 是否支持 | 说明 |
+| --- | --- | --- |
+| Duplicate Key、Unique Key、Aggregate Key 表的 Value 列 | 支持 | 在 Aggregate Key 
表中,聚合类型必须是 `REPLACE` 或 `REPLACE_IF_NOT_NULL`。 |
+| Key 列、分区列、分桶列 | 不支持 | |
+| 在表结构中嵌套在其他类型内(`ARRAY<VARIANT>`、`MAP`、`STRUCT`) | 不支持 | 查询结果仍可以是 
`ARRAY<VARIANT>`,例如 `COLLECT_LIST(v)` 的结果。 |
+| 默认值 | 只能是 `NULL` | `DEFAULT '{}'` 等非 NULL 默认值会被拒绝。 |
+
+## 写入数据 {#write-data}
+
+### 输入如何变成 VARIANT 值 {#how-input-becomes-a-variant-value}
+
+| 写入方式 | 结果 |
+| --- | --- |
+| `INSERT ... VALUES` 或 `INSERT ... SELECT` 写入 `CHAR`、`VARCHAR`、`STRING` 表达式 | 
VARIANT **字符串**。即使内容看起来像 JSON,也不会被解析。从 `s3()`、`hdfs()`、`local()` 等表函数执行 `INSERT 
INTO ... SELECT`、带 `http_stream` SQL 语句的 Stream Load,以及以 SQL 文本发送的 Group Commit 
INSERT,都是如此。 |
+| `INSERT` 写入 `PARSE_TO_VARIANT(expr)` 或 `TRY_PARSE_TO_VARIANT(expr)` | 解析后的 
JSON 值。参见[解析错误](#parse-errors)。 |
+| `INSERT` 写入 `JSON`/`JSONB` 表达式 | 按原结构直接转换。 |
+| `INSERT` 写入其他类型的表达式 | 带类型的值,参见[其他类型 CAST 为 VARIANT](#cast-to-variant)。 |
+| 通过 JDBC 服务端预编译语句(`useServerPrepStmts=true`)执行的 Group Commit INSERT | 
按导入作业的方式执行,因此字符串会按 JSON 解析。 |
+| 导入作业(Stream Load、Broker Load、Routine Load) | 写入 VARIANT 列的字符串字段会按 JSON 
解析,与文件格式无关:CSV 文本、Parquet 的 `STRING` 列、JSON 中的字符串值都是如此。Arrow 格式不能导入 VARIANT 
列。CSV 中的 `\N` 导入为 SQL `NULL`。 |
+| JSON 格式的导入作业 | 字段对应的 JSON 值。JSON 字符串会再按 JSON 文本解析一次:`"123"` 导入为数值 
`123`,`"true"` 导入为布尔值 `true`,`"{\"a\": 1}"` 导入为对象,`"hello"` 仍是字符串 `hello`。顶层的 
JSON 布尔值导入为数值 `1` 或 `0`。JSON `null` 或缺失的字段导入为 SQL `NULL`。 |
+
+`NOT NULL` 的 VARIANT 列不接受 SQL `NULL`:严格模式下 `INSERT` 会失败,导入作业会过滤该行,文本解析失败而得到 
SQL `NULL` 的行也会被过滤。被过滤的行计入 `max_filter_ratio`,其默认值为 `0`,因此导入作业会失败。
 
 ```sql
--- 访问嵌套字段(返回类型为 VARIANT,需要显式或隐式 CAST 才能聚合/比较)
-SELECT v['properties']['title'] FROM ${table_name};
+CREATE TABLE variant_tbl (k INT, v VARIANT)
+DUPLICATE KEY(k)
+DISTRIBUTED BY HASH(k) BUCKETS 1
+PROPERTIES ("replication_num" = "1");
 
--- 聚合前显式 CAST 为确定类型
-SELECT CAST(v['properties']['title'] AS STRING) AS title
-FROM ${table_name}
-GROUP BY title;
+INSERT INTO variant_tbl VALUES
+    (1, '{"a": 1}'),                     -- 写入为字符串
+    (2, PARSE_TO_VARIANT('{"a": 1}'));   -- 写入为对象
 
--- 数组查询示例
-SELECT *
-FROM ${table_name}
-WHERE ARRAY_CONTAINS(CAST(v['tags'] AS ARRAY<TEXT>), 'Doris');
+SELECT k, v, VARIANT_TYPE(v) AS type, v['a'] FROM variant_tbl ORDER BY k;
 ```
 
-## 创建和访问值
+```text
++------+----------+--------+--------+
+| k    | v        | type   | v['a'] |
++------+----------+--------+--------+
+|    1 | {"a": 1} | string | NULL   |
+|    2 | {"a":1}  | object | 1      |
++------+----------+--------+--------+
+```
 
-:::info 版本说明
-本节所述行为适用于 Doris 4.2 及后续版本。
-:::
+字符串根值输出时不带引号,因此写入的字符串看起来可能和 JSON 一样,可以用 `VARIANT_TYPE` 区分。如需把这类字符串转成结构化值,请通过 
`PARSE_TO_VARIANT(CAST(v AS STRING))` 重新写入。
 
-VARIANT 值可以从 JSON 文本、JSON/JSONB 值或带确定类型的 SQL 表达式创建:
+分步骤的导入示例请参阅[导入 VARIANT 
数据](../../../../data-operate/import/complex-types/variant.md)。
 
-- 如果要将字符串或 JSON/JSONB 表达式解析为结构化 VARIANT 值,请使用 
[PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/parse-to-variant)。
-- 如果要将受支持的 SQL 值转换为 VARIANT,请使用 `CAST(expression AS VARIANT)`。字符串会保留为 VARIANT 
字符串值,该 CAST 不解析 JSON。
+### 解析错误 {#parse-errors}
 
-### 解析 JSON 文本
+[PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/parse-to-variant.md)、[TRY_PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/try-parse-to-variant.md)
 和导入作业使用同一个 JSON 解析器。导入作业处理错误的方式与 `TRY_PARSE_TO_VARIANT` 相同:
 
-```sql
-SELECT PARSE_TO_VARIANT('{"user": {"id": 42}, "active": true}');
-SELECT PARSE_TO_VARIANT('[10, 20, 30]');
-SELECT PARSE_TO_VARIANT(CAST('{"user": {"id": 42}}' AS JSON));
-```
+| 输入 | `PARSE_TO_VARIANT` | `TRY_PARSE_TO_VARIANT` 与导入作业 |
+| --- | --- | --- |
+| 合法 JSON | 解析后的值 | 解析后的值 |
+| 非法 JSON 文本,如 `hello` 或 `{"id":` | 保留为 VARIANT 字符串 | 保留为 VARIANT 字符串 |
+| 包含超出 [-2^63, 2^64 - 1] 的整数,或超出 `DOUBLE` 范围的数值的 JSON,如 `{"a": 1, "n": 
100000000000000000000}` | 整段文本保留为 VARIANT 字符串,因此 `v['a']` 返回 `NULL` | 整段文本保留为 
VARIANT 字符串 |
+| 空字符串 | 空对象 `{}` | 空对象 `{}` |
+| 嵌套超过 128 层 | 报错 | SQL `NULL` |
+| 对象 key 超过 `variant_max_json_key_length` 字节(BE 配置,默认 255) | 报错 | SQL `NULL` |
+| 同一对象中有重复 key | 报错 | SQL `NULL` |
+| 不是合法 UTF-8 的字符串 | 报错 | SQL `NULL` |
+
+嵌套深到 JSON 解析器本身拒绝的文档(约 1000 层)属于非法 JSON,因此会保留为字符串。
 
-如果非法 JSON 应该返回 SQL `NULL` 而不是使查询失败,请使用 
[TRY_PARSE_TO_VARIANT](../../../sql-functions/scalar-functions/variant-functions/try-parse-to-variant)。
+有两个 BE 配置会改变上述规则:
 
-### 访问对象和数组
+- `variant_throw_exeception_on_invalid_json`(默认 `false`):设置为 `true` 
后,解析器无法接受的文本(包括含超范围数值的 JSON)不再保留为 VARIANT 字符串,而是对 `PARSE_TO_VARIANT` 报错,对 
`TRY_PARSE_TO_VARIANT` 和导入作业返回 SQL `NULL`。
+- `variant_enable_duplicate_json_path_check`(默认 `false`):设置为 `true` 后,对象中重复的 
key 保留第一个值,不再报错。含 `.` 的 key 与嵌套路径重复时,写入也不再失败,只会存储其中一个值。
 
-对象字段可以使用字符串 key 访问。在 Doris 4.2 及后续版本中,VARIANT 数组的正数索引从 1 
开始,负数索引从数组末尾倒数。提取出的值仍是 `VARIANT`,如需按确定类型比较、计算或聚合,请先 CAST。
+如需让含超范围整数的文档保持结构,可以先转换为 JSON:`CAST(CAST(text AS JSON) AS VARIANT)`。不超过 38 
位的整数保持为精确的数值,更大但仍在 `LARGEINT` 范围内的整数变为字符串,再大的整数变为 
`DOUBLE`。`PARSE_TO_VARIANT(CAST(text AS JSON))` 无法解决这个问题,因为它会重新解析 JSON 文本。
 
 ```sql
-SELECT CAST(PARSE_TO_VARIANT('{"user": {"id": 42}}')['user']['id'] AS BIGINT);
-SELECT ELEMENT_AT(PARSE_TO_VARIANT('[10, 20, 30]'), 1);  -- 10
-SELECT ELEMENT_AT(PARSE_TO_VARIANT('[10, 20, 30]'), -1); -- 30
+SELECT VARIANT_TYPE(PARSE_TO_VARIANT('{"id": 1}')) AS valid_json,    -- object
+       VARIANT_TYPE(PARSE_TO_VARIANT('{"id":'))    AS invalid_json,  -- string
+       VARIANT_TYPE(CAST('{"id": 1}' AS VARIANT))  AS string_cast;   -- string
 ```
 
-对象和数组访问的详细说明请参见 
[ELEMENT_AT](../../../sql-functions/scalar-functions/variant-functions/element-at)。
+### 写入后值的规范化 {#what-storage-keeps}
+
+值写入表时会被规范化,读回的值可能与写入前计算出的值不同:
+
+| 写入前 | 从表中读回 |
+| --- | --- |
+| 值为 JSON `null` 的对象成员,如 `{"a": null, "b": 1}` | 该成员被移除:`{"b":1}`;`v['a']` 返回 
SQL `NULL`。 |
+| 值为空对象或空数组的对象成员,以及按这些规则变空的对象,如 `{"a": {}, "b": [], "c": {"d": null}}` | 
被移除:`{}` |
+| 值为只含 `null` 元素的数组的对象成员,如 `{"p": [null, null], "q": 1}` | 可能被移除:`{"q":1}` |
+| 数组内的值,如 `{"arr": [{"a": null}, {}, [], null]}` | 原样保留;`v['arr'][1]['a']` 是 
VARIANT `null`。 |
+| 根值为 JSON `null`,如 `PARSE_TO_VARIANT('null')` | 空对象 `{}` |
+| 根值为空数组 `[]` 或空对象 `{}` | 保留 |
+| 不在 Schema Template 路径上的 `DATE`、`DATETIME` 值,如 `CAST(date_col AS VARIANT)` | 
以文本形式存储,读回时是字符串 |
+| 同一路径上混有布尔值和数值 | 布尔值可能读回为 `1` 或 `0`,取决于同一次写入中值的先后顺序以及 Compaction |
+| 含 `.` 的 key,如 `{"a.b": 1}` | 按嵌套路径存储:`{"a":{"b":1}}`,`v['a.b']` 和 
`v['a']['b']` 都返回 `1`。如果一个文档同时包含 key `a.b` 和 `a` 下的 key `b`,整条 INSERT 
或整个导入作业都会失败,除非 `variant_enable_duplicate_json_path_check` 为 `true`。 |
+| 对象 key | 按字节序返回 |
 
-## CAST 规则
+这些规则会受同一次写入的其他行以及 DOC mode 影响,因此不要依赖 `null` 值或空容器在写入后仍然保留。在 Schema Template 
中声明过的路径会转换为声明的类型,参见 [Schema Template](#schema-template)。
 
-VARIANT 的 CAST 包括两个方向:把受支持的 SQL 值转换为 VARIANT,以及把 VARIANT 中兼容的值转换为具体 SQL 类型。
+## 类型推断与类型冲突 {#type-inference-and-type-conflicts}
 
-### 其他类型 CAST 为 VARIANT
+没有 Schema Template 时,Doris 在解析 JSON 时推断每个值的类型,`VARIANT_TYPE` 返回的就是这个类型:
 
-| 源类型 | 行为 |
+| JSON 值 | 类型 |
 | --- | --- |
-| `CHAR`、`VARCHAR`、`STRING` | 将输入保留为 VARIANT 字符串,不解析看起来像 JSON 的文本。 |
-| `BOOLEAN` | 保留 Boolean 值。 |
-| `TINYINT`、`SMALLINT`、`INT`、`BIGINT`、`LARGEINT` | 保留整数值。 |
-| `FLOAT`、`DOUBLE` | 保留浮点数值。 |
-| `DECIMALV2`、`DECIMAL(p, s)`(`p <= 38`) | 保留 Decimal 值,但需满足下文限制。 |
-| `DATE`、`DATETIME`、`TIMESTAMP_NS`、`TIMESTAMPTZ` | 保留对应的逻辑类型和值。 |
-| `IPV4`、`IPV6` | 保留 IP 地址值。 |
-| `JSON` / `JSONB` | 将结构化值直接转换为 VARIANT;如果输入包含 VARIANT 无法表示的 JSONB 值类型,BE 会报错。 
|
-| `ARRAY<T>` | 当 `T` 为 `VARIANT` 或也在该白名单中时递归转换每个元素,并保留 SQL NULL 元素。 |
-
-仅支持上表列出的源类型。其他源类型,包括 `MAP`、`STRUCT`、`TIME`、precision 超过 38 的 
Decimal,以及包含不支持元素类型的数组,都会由 BE 报错。
+| `BIGINT` 范围内的整数 | `tinyint`、`smallint`、`int` 或 `bigint` 中能容纳该值的最小类型 |
+| 超出 `BIGINT` 范围、不超过 18446744073709551615 的整数 | `decimal` |
+| 带小数部分或指数的数值 | `double` |
+| 超出 [-2^63, 2^64 - 1] 的整数,或超出 `DOUBLE` 范围的数值 | 不作为数值处理:整段文本都是非法 
JSON(参见[解析错误](#parse-errors)) |
+| 字符串 | `string` |
+| `true`、`false` | `bool` |
+| `null` | `null` |
+| 数组、对象 | `array`、`object` |
 
-```sql
--- 字符串会保留为 VARIANT 字符串根值,即使内容看起来像 JSON。
-SELECT CAST(CAST('{"id": 1}' AS VARIANT) AS STRING) AS string_value,
-       VARIANT_TYPE(CAST('{"id": 1}' AS VARIANT)) AS root_type;
--- string_value:{"id": 1};root_type:string
+数据存储时,每个路径只有一种存储类型:
 
--- 需要结构化 VARIANT 值时,显式解析 JSON 文本。
-SELECT PARSE_TO_VARIANT('{"id": 1}') AS parsed_object;
--- {"id":1}
+- 整数存为 `BIGINT`,需要时存为 `LARGEINT`;浮点数存为 `DOUBLE`,定点数存为 `DECIMAL`,字符串存为 
`STRING`,布尔值存为 `BOOLEAN`,由标量组成的数组存为 `ARRAY<T>`。
+- 对象数组、嵌套数组,以及值的类型不一致的路径(例如整数与浮点数、数值与字符串、标量与数组),存为 `JSONB`(`DESC` 中显示为 
`json`)。元素类型冲突的数组存为 `ARRAY<JSONB>`。
 
--- JSON/JSONB 输入按结构转换。
-SELECT CAST(CAST('{"id": 1}' AS JSON) AS VARIANT) AS parsed_object;
--- {"id":1}
+```sql
+{"a" : 12345678}
+{"a" : "HelloWorld"}
+-- a 存为 JSONB
 ```
 
-字符串 CAST 不解析 JSON,因此非法 JSON 文本仍是合法的 VARIANT 字符串。如需严格解析 JSON,请使用 
`PARSE_TO_VARIANT`;如需在解析失败时返回 SQL `NULL`,请使用 `TRY_PARSE_TO_VARIANT`。
+`JSONB` 
路径保留所有值,但失去类型化存储的能力:索引和基于类型的裁剪都不再作用于该路径。布尔值是一个特例:如果路径上的第一个值是布尔值、后续的值是数值,该路径可能按数值存储,布尔值读回为
 `1` 或 `0`。需要稳定类型的路径,请在 Schema Template 中声明。
 
-### VARIANT CAST 为其他类型
+`VARIANT_TYPE` 报告的是每个值的类型,因此从 `BIGINT` 路径读出的整数仍可能是 `tinyint`。要查看每个路径的存储类型,请执行 
`SET describe_extend_variant_column = true;` 后再执行 `DESC 
table_name;`,参见[查看子列与类型](#inspect-subcolumns-and-types)。
 
-VARIANT 可以 CAST 为兼容的标量、JSON/JSONB 或数组类型:
+## 访问路径与输出 {#access-paths-and-output}
 
-| 目标类型 | 行为 |
-| --- | --- |
-| `BOOLEAN` | 转换兼容的 Boolean 或标量根值。 |
-| `TINYINT`、`SMALLINT`、`INT`、`BIGINT`、`LARGEINT` | 将兼容的标量根值转换为指定整数类型。 |
-| `FLOAT`、`DOUBLE` | 转换兼容的数值根。 |
-| `DECIMALV2`、`DECIMAL(p, s)` | 将兼容的数值根转换为指定 Decimal 类型。 |
-| `DATE`、`DATETIME`、`TIMESTAMP_NS`、`TIMESTAMPTZ` | 转换兼容的日期时间根值。 |
-| `CHAR`、`VARCHAR`、`STRING` | 标量根值返回对应文本,对象和数组返回 JSON 文本。Variant/JSON `null` 
返回字符串 `null`,外层 SQL `NULL` 仍是 SQL `NULL`。 |
-| `IPV4`、`IPV6` | 将兼容的 IP 地址根值转换为指定的 IP 地址类型。 |
-| `JSON` / `JSONB` | 按结构转换;如果 VARIANT 值包含 JSON/JSONB 无法表示的类型,BE 会报错。 |
-| `ARRAY<T>` | 当 `T` 为 `VARIANT` 或也在该白名单中时逐元素转换;不兼容元素遵循目标类型的 CAST 规则。 |
-
-仅支持上表列出的目标类型。其他目标类型,包括 `MAP`、`STRUCT` 和 `TIME`,都会由 BE 
报错。对于受支持的目标类型,值形状不兼容、文本非法或数值越界时,按照对应 CAST 模式报错或返回 SQL `NULL`。
+- `v['key']` 和 `v['a']['b']` 读取对象成员;`v['arr'][1]` 读取数组元素,下标从 1 开始,`-1` 
表示最后一个元素。[ELEMENT_AT](../../../sql-functions/scalar-functions/variant-functions/element-at.md)
 与之等价。
+- 结果是 `VARIANT` 值。key 不存在、下标为 `0` 或越界、对数组使用字符串 key、对对象使用整数下标、对标量值使用 key,都返回 
SQL `NULL`。
+- 在计算出的值中,含 `.` 的 key 是一个整体:`v['a.b']` 读取 key `a.b`,而 `v['a']['b']` 读取 `a` 下的 
`b`。存储不保留这种区分,参见[写入后值的规范化](#what-storage-keeps)。
+- 路径不会自动展开数组。对于 `{"a": [{"b": 1}]}`,`v['a']['b']` 返回 `NULL`,应写作 
`v['a'][1]['b']`。
 
 ```sql
-SELECT CAST(PARSE_TO_VARIANT('42') AS BIGINT) AS id;
--- 42
+SELECT v['user']['id']      AS id,       -- 42
+       v['tags'][-1]        AS last_tag, -- sql
+       v['user']['missing'] AS missing   -- NULL
+FROM events
+WHERE id = 1;
+```
 
-SELECT CAST(PARSE_TO_VARIANT('[1, null, 3]') AS ARRAY<INT>) AS values;
--- [1, NULL, 3]
+常见写法是把路径 CAST 为查询需要的类型:
 
-SELECT CAST(PARSE_TO_VARIANT('{"id": 1}') AS JSON) AS json_value;
--- {"id":1}
+```sql
+SELECT * FROM tbl WHERE ARRAY_CONTAINS(CAST(v['tags'] AS ARRAY<TEXT>), 
'Doris');
+SELECT * FROM tbl WHERE CAST(v['date'] AS DATE) = '2021-01-02';
+SELECT * FROM tbl WHERE v['bool'];                -- 隐式 CAST 为 BOOLEAN
+SELECT * FROM tbl WHERE v['str'] MATCH 'Doris';   -- 使用该路径上的倒排索引
 ```
 
-### Decimal 与日期时间转换限制
+读取整个 VARIANT 值会返回 JSON 文本。对象 key 
按字节序输出,且不含空白,因此与输入文本并非按字节完全一致。字符串根值输出时不带引号。`CAST(v AS STRING)` 则按对应 SQL 
类型的格式输出标量根值,例如布尔根值变为 `1` 或 `0`。
+
+```sql
+INSERT INTO variant_tbl VALUES (3, PARSE_TO_VARIANT('{ "b": 2, "a": 1, "c": { 
"y": 20, "x": 10 } }'));
 
-| Doris 输入类型 | VARIANT 支持情况 |
+SELECT v FROM variant_tbl WHERE k = 3;
+-- {"a":1,"b":2,"c":{"x":10,"y":20}}
+```
+
+## CAST 与隐式转换 {#cast-and-implicit-conversion}
+
+### 其他类型 CAST 为 VARIANT {#cast-to-variant}
+
+| 源类型 | 结果 |
+| --- | --- |
+| `CHAR`、`VARCHAR`、`STRING` | VARIANT 字符串,不解析 JSON 文本。字符串必须是合法的 UTF-8,否则 CAST 
报错。 |
+| `BOOLEAN` | 布尔值。 |
+| `TINYINT`、`SMALLINT`、`INT`、`BIGINT` | 整数。 |
+| `LARGEINT` | 定点数。绝对值超过 10^38 - 1 的值会变成字符串。 |
+| `FLOAT`、`DOUBLE` | 浮点数。 |
+| `DECIMALV2`、`DECIMAL(p, s)`(`p <= 38`) | 定点数。 |
+| `DATE`、`DATETIME(p)`、`TIMESTAMP_NS` | 日期,或不带时区的时间戳。 |
+| `IPV4`、`IPV6` | 字符串,内容为该值的文本形式。 |
+| `JSON` / `JSONB` | 保持原结构。包含 VARIANT 无法表示的值(如 `DECIMAL256` 数值),或对象中有重复 key 
时,CAST 报错。 |
+| `ARRAY<T>` | 数组,逐个转换元素。`T` 必须是 `VARIANT` 或本表中的类型。 |
+| `MAP`、`STRUCT`、`TIME`、`TIMESTAMPTZ`、`VARBINARY` 等其他类型 | 不支持,语句报错。 |
+
+源值还必须是其类型的合法值,非法值会被拒绝,不会被自动修复。CAST 不会解析字符串:`CAST('{"id": 1}' AS VARIANT)` 
得到字符串 `{"id": 1}`。需要解析 JSON 文本时请使用 `PARSE_TO_VARIANT`。
+
+### VARIANT CAST 为其他类型 {#cast-from-variant}
+
+| 目标类型 | 结果 |
+| --- | --- |
+| `BOOLEAN` | 布尔值保持不变;数值非零即为 `true`;字符串按 `CAST(string AS BOOLEAN)` 的规则转换。 |
+| `TINYINT`、`SMALLINT`、`INT`、`BIGINT`、`LARGEINT` | 整数。定点数和浮点数的小数部分被截断(`1.5` 变为 
`1`);布尔值变为 `1` 或 `0`;`"123"` 这样的数字字符串会被转换。 |
+| `FLOAT`、`DOUBLE`、`DECIMAL(p, s)` | 数值和数字字符串。`DECIMAL` 按 scale `s` 舍入。超出 
`FLOAT` 范围的值变为 `Infinity`。 |
+| `DATE`、`DATETIME(p)`、`TIMESTAMP_NS` | 日期时间值、日期时间格式的字符串,以及 `20240102` 这样的数值。 |
+| `TIMESTAMPTZ(p)` | 日期时间值,以及日期时间格式的字符串。 |
+| `IPV4`、`IPV6` | IP 地址格式的字符串。 |
+| `CHAR`、`VARCHAR`、`STRING` | 字符串根值原样返回,不带引号;对象和数组返回 JSON 文本;其他标量按对应 SQL 
类型的格式输出:布尔根值为 `1` 或 `0`,`DATETIME` 值带 6 位小数,`TIMESTAMP_NS` 值带 9 位小数。VARIANT 
`null` 返回字符串 `null`。 |
+| `JSON` / `JSONB` | 保持原结构。没有 JSON 对应类型的值(如日期、时间戳)会变成 JSON 
字符串;带时区的时间戳按会话时区格式化。 |
+| `ARRAY<T>` | 按元素转换的数组,无法转换的元素变为 `NULL`。内容为 JSON 数组的字符串(如 `"[1, 
2]"`)也会被转换。其他值返回 `NULL`。 |
+| `MAP`、`STRUCT`、`TIME` 等其他类型 | 不支持,语句报错。 |
+
+值无法转换为目标类型时返回 SQL `NULL`,开启 `enable_strict_cast` 
时也是如此。例外是值的类型与目标类型之间根本没有转换,例如把数值 CAST 为 `TIMESTAMPTZ`、`IPV4`、`IPV6`,或把日期、时间戳 
CAST 为 `BOOLEAN`:这时语句会报错。
+
+```sql
+SELECT CAST(PARSE_TO_VARIANT('"123"') AS INT)      AS from_string,   -- 123
+       CAST(PARSE_TO_VARIANT('"abc"') AS INT)      AS not_a_number,  -- NULL
+       CAST(PARSE_TO_VARIANT('1.5') AS INT)        AS truncated,     -- 1
+       CAST(PARSE_TO_VARIANT('300') AS TINYINT)    AS overflow,      -- NULL
+       CAST(PARSE_TO_VARIANT('{"a": 1}') AS INT)   AS from_object,   -- NULL
+       CAST(PARSE_TO_VARIANT('[1, "2", null, "x"]') AS ARRAY<INT>) AS arr;  -- 
[1, 2, null, null]
+
+SELECT CAST(PARSE_TO_VARIANT('true') AS STRING)        AS bool_root,     -- 1
+       CAST(PARSE_TO_VARIANT('{"b": true}') AS STRING) AS object_text,   -- 
{"b":true}
+       CAST(PARSE_TO_VARIANT('"abc"') AS STRING)       AS string_root,   -- abc
+       CAST(PARSE_TO_VARIANT('null') AS STRING)        AS variant_null;  -- 
null
+```
+
+### Decimal 与日期时间值 {#decimal-and-datetime-values}
+
+把以下类型转换为 VARIANT 时:
+
+| Doris 类型 | 行为 |
 | --- | --- |
 | 旧版 `DECIMALV2` | 精确保留 precision 不超过 27、scale 不超过 9 的值。 |
-| `DECIMAL(p, s)` | 精确保留 `1 <= p <= 38` 且 `0 <= s <= p` 的值;不支持需要超过 38 位 
precision 的值。 |
-| `DATE` | 保留为不含时间和时区的日历日期。 |
-| 旧版 `DATETIME` | 保留到秒,不进行时区调整。 |
-| `DATETIME(p)` | 支持 `0 <= p <= 6`,不进行时区调整。 |
-| `TIMESTAMP_NS` | 保留固定纳秒精度且不进行时区调整;值必须在 TIMESTAMP_NS 取值范围内。 |
-| `TIMESTAMPTZ(p)` | 支持 `0 <= p <= 6`,保留带时区调整的 timestamp 语义。 |
-| precision 超过 38 的 Decimal | 不支持作为 VARIANT 输入。 |
-| `TIME` | 不支持作为 VARIANT 输入。 |
+| `DECIMAL(p, s)` | 精确保留 `1 <= p <= 38` 且 `0 <= s <= p` 的值;不支持 precision 超过 38 
的 Decimal。 |
+| `DATE` | 不含时间和时区的日历日期。 |
+| `DATETIME(p)` | `0 <= p <= 6`,不做时区调整。 |
+| `TIMESTAMP_NS` | 纳秒精度,不做时区调整;值必须在 TIMESTAMP_NS 取值范围内。 |
+| `TIMESTAMPTZ(p)` | 不能 CAST 为 VARIANT。可以在 Schema Template 中把路径声明为 
`TIMESTAMPTZ`。 |

Review Comment:
    Schema Template 禁止一下TIMESTAMPTZ



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to