# name: test/sql/functions/cast_function.test
# description: duck_cast_function 的三种返回形式、入参可空性、CAST/TRY_CAST 错误分流、panic、implicit_cost、复杂类型(LIST)与注册控制
# group: [functions]
require duckfn
# cast 函数注册后接管 `CAST(源 AS 目标)`;下面大部分期望值都与 DuckDB 内置转换不同,
# 以此证明走的确实是扩展里的回调。每个 .test 文件跑在独立的内存库里,覆盖只影响本文件。
# ============================================================================
# 返回形式 1/3:-> DuckOptionResult<T>(可 NULL、可报错)
# ============================================================================
query I
SELECT CAST('42' AS INTEGER);
----
42
# 前后空白由函数自己 trim
query I
SELECT CAST(' 7 ' AS INTEGER);
----
7
# 覆盖内置行为:空串 -> NULL(内置会报错)
query I
SELECT CAST('' AS INTEGER);
----
NULL
# TRY_CAST:这一行失败 -> NULL,查询照常返回
query I
SELECT TRY_CAST('abc' AS INTEGER);
----
NULL
# CAST:失败让整条查询失败
statement error
SELECT CAST('abc' AS INTEGER);
----
dfn_cast_str_to_int: not an integer: "abc"
# 逐行批量转换:NULL 值直接短路成 NULL,不进入函数体
query I
SELECT CAST(s AS INTEGER) FROM (VALUES ('1'), (NULL), (''), (' 3 ')) t(s);
----
1
NULL
NULL
3
# TRY_CAST 在列上:坏值行变 NULL,其他行照常
query I
SELECT TRY_CAST(s AS INTEGER) FROM (VALUES ('1'), ('x'), (NULL)) t(s);
----
1
NULL
NULL
# 目标类型来自返回类型
query T
SELECT typeof(CAST('1' AS INTEGER));
----
INTEGER
# ============================================================================
# 返回形式 2/3:-> T(永不为 NULL,panic 变成查询错误)
# ============================================================================
query I
SELECT CAST(2.4::DOUBLE AS BIGINT);
----
2
# 覆盖内置行为:内置是截断(2),这里是四舍五入
query I
SELECT CAST(2.5::DOUBLE AS BIGINT);
----
3
query I
SELECT CAST(-2.5::DOUBLE AS BIGINT);
----
-3
# 非 Option 入参:NULL 短路成 NULL,函数体不执行
query I
SELECT CAST(NULL::DOUBLE AS BIGINT);
----
NULL
# 函数体 panic:适配层 catch_unwind 转成查询错误,不跨 FFI 展开
statement error
SELECT CAST('NaN'::DOUBLE AS BIGINT);
----
dfn_cast_double_to_bigint: not a finite number: NaN
# ============================================================================
# 返回形式 3/3 + Option 入参:NULL 以 None 进入函数体
# ============================================================================
query R
SELECT CAST(3::BIGINT AS DOUBLE);
----
1.5
# 入参写成 Option<i64>:NULL 不再短路,函数体收到 None 并返回哨兵值 -1.0
query R
SELECT CAST(NULL::BIGINT AS DOUBLE);
----
-1.0
query R
SELECT TRY_CAST(NULL::BIGINT AS DOUBLE);
----
-1.0
# ============================================================================
# implicit_cost:允许 DuckDB 自动插入这个转换
# ============================================================================
# 显式 CAST 照常
query I
SELECT CAST('41' AS HUGEINT);
----
41
# 内置没有 VARCHAR -> HUGEINT 的隐式转换,注册 implicit_cost 后 `+` 才能解析
query I
SELECT CAST('41' AS VARCHAR) + 1::HUGEINT;
----
42
query T
SELECT typeof(CAST('41' AS VARCHAR) + 1::HUGEINT);
----
HUGEINT
statement ok
CREATE TABLE dfn_cast_implicit_t(s VARCHAR);
statement ok
INSERT INTO dfn_cast_implicit_t VALUES ('41'), ('1');
query I
SELECT sum(s + 1::HUGEINT) FROM dfn_cast_implicit_t;
----
44
# 隐式路径上的转换失败同样冒泡成查询错误
statement error
SELECT CAST('abc' AS VARCHAR) + 1::HUGEINT;
----
dfn_cast_str_to_hugeint: not an integer: "abc"
# 隐式转换只作用于本文件选定的类型对:给 VARCHAR -> BIGINT 加隐式代价会顺带
# 改掉「无匹配函数」这类绑定结果,所以这里故意只挂在 HUGEINT 上
statement error
SELECT dfn_agg_sum('x');
----
No function matches
# ============================================================================
# 复杂类型:LIST(VARCHAR) -> LIST(INTEGER)
#
# 源/目标类型统一取 DuckValueType::logical_type(),Vec<Option<T>> 这类嵌套类型
# 也用同一个宏注册(LIST 的输出统一 CAST 成 VARCHAR 后按文本比对)。
# ============================================================================
query T
SELECT CAST(CAST(['1', '2'] AS INTEGER[]) AS VARCHAR);
----
[1, 2]
# NULL 元素保持 NULL
query T
SELECT CAST(CAST([NULL, '2'] AS INTEGER[]) AS VARCHAR);
----
[NULL, 2]
query T
SELECT CAST(CAST([] AS INTEGER[]) AS VARCHAR);
----
[]
query T
SELECT TRY_CAST(['1', 'x'] AS INTEGER[]);
----
NULL
statement error
SELECT CAST(['1', 'x'] AS INTEGER[]);
----
dfn_cast_list_str_to_int: not an integer: "x"
query T
SELECT CAST(CAST(v AS INTEGER[]) AS VARCHAR) FROM (VALUES (['1', '2']), (['3'])) t(v);
----
[1, 2]
[3]
# ============================================================================
# 注册控制:auto_register = false 与手动注册
# ============================================================================
# 只声明、不注册:TINYINT -> BIGINT 仍走内置转换
query I
SELECT CAST(3::TINYINT AS BIGINT);
----
3
# #[duck_custom_register] + cast_function_register() 手动注册后替换成 ×10
query I
SELECT CAST(3::SMALLINT AS BIGINT);
----
30
# ============================================================================
# 没有注册过的转换不受影响
# ============================================================================
query R
SELECT CAST('1.5' AS DOUBLE);
----
1.5