# name: test/sql/functions/dynamic_table_function.test
# description: 动态列表函数:bind 阶段由外部元数据决定 schema(宏层 dynamic_columns 与底层手动 impl),标量/LIST/STRUCT/MAP 列、NULL 单元格、分批流式输出与注册控制
# group: [functions]
require duckfn
# ============================================================================
# 宏层:#[duck_table_function(dynamic_columns = true)]
# 列名与列类型在 bind 阶段由「外部元数据」给出,而不是来自编译期结构体
# ============================================================================
# DESCRIBE 校验动态列名与列类型(标量 + LIST + STRUCT + MAP)
query TT
SELECT column_name, column_type FROM (DESCRIBE SELECT * FROM dfn_table_dynamic('sales', 1));
----
id BIGINT
region VARCHAR
amount DOUBLE
tags VARCHAR[]
info STRUCT(host VARCHAR, code BIGINT)
attrs MAP(VARCHAR, BIGINT)
# 标量列 + 可空标量列(每两行一个 NULL)
query ITR
SELECT id, region, amount FROM dfn_table_dynamic('sales', 3);
----
0 r0 0.0
1 r1 NULL
2 r2 1.0
# LIST / STRUCT / MAP 列:len(NULL) 是 NULL,STRUCT 子字段与 MAP 下标读取照常
query IIT
SELECT id, len(tags), info.host FROM dfn_table_dynamic('sales', 6) ORDER BY id;
----
0 0 host-0
1 1 host-1
2 2 NULL
3 3 host-0
4 NULL host-1
5 1 NULL
query II
SELECT id, attrs['k0'] FROM dfn_table_dynamic('sales', 4) ORDER BY id;
----
0 NULL
1 0
2 NULL
3 0
# LIST 元素本身按序写出:每 5 行整个 LIST 为 NULL
query IT
SELECT id, CAST(tags AS VARCHAR) FROM dfn_table_dynamic('sales', 5) ORDER BY id;
----
0 []
1 [t0]
2 [t0, t1]
3 [t0, t1, t2]
4 NULL
# 零行也必须声明出列:schema 只来自外部元数据
query I
SELECT count(*) FROM dfn_table_dynamic_empty('sales');
----
0
query T
SELECT column_type FROM (DESCRIBE SELECT * FROM dfn_table_dynamic_empty('sales')) WHERE column_name = 'tags';
----
VARCHAR[]
# bind 阶段读元数据失败 -> 整条查询失败
statement error
SELECT * FROM dfn_table_dynamic('nope', 1);
----
dfn_table_dynamic: unknown source `nope`, valid sources are: sales
# ============================================================================
# 数据规模:跨 chunk(vector_size 默认 2048)时动态列也不能丢行或串位
# ============================================================================
query III
SELECT count(*), min(id), max(id) FROM dfn_table_dynamic('sales', 5000);
----
5000 0 4999
query IIT
SELECT id, len(tags), info.host FROM dfn_table_dynamic('sales', 5001) WHERE id >= 2047 ORDER BY id LIMIT 3;
----
2047 3 host-1
2048 0 NULL
2049 NULL host-0
# 动态列能正常参与 WHERE / ORDER BY / 聚合
query I
SELECT sum(amount)::BIGINT FROM dfn_table_dynamic('sales', 10);
----
10
# ============================================================================
# 底层:手写 DynamicTableFunctionAdapter + #[duck_custom_register]
# 列名与列数都由 bind 阶段的参数算出来
# ============================================================================
query TT
SELECT column_name, column_type FROM (DESCRIBE SELECT * FROM dfn_table_dynamic_manual(start=1, label='step'));
----
step BIGINT
squared BIGINT
query II
SELECT * FROM dfn_table_dynamic_manual(start=3, label='step');
----
3 9
2 4
1 1
# label 是可空命名参数,缺省时列名回落为 n
query T
SELECT column_name FROM (DESCRIBE SELECT * FROM dfn_table_dynamic_manual(start=1)) WHERE column_name = 'n';
----
n
# ============================================================================
# 底层:手写 DuckBindArgs + DuckDynamicValue::from_duck_value
# 显式 `NULL` 必须被读成 None —— 只判 `Value::is_null()` 会漏掉,触发 FFI foreign exception
# ============================================================================
query I
SELECT * FROM dfn_table_dynamic_echo_arg(42);
----
42
query I
SELECT * FROM dfn_table_dynamic_echo_arg(NULL);
----
NULL