【DuckDB】探索函数调用新范式:点操作符链式调用

【DuckDB】探索函数调用新范式:点操作符链式调用

在数据库操作的日常实践中,函数调用的简洁性与直观性直接影响着开发效率与代码可读性。DuckDB 提供的点操作符函数链式调用功能,为我们打开了全新的函数调用视角,让数据处理逻辑更加流畅自然。

基础使用方法:从传统到创新

基本语法解析

在 DuckDB 中,我们可以通过点操作符 . 来实现函数的链式调用。例如:

sql 复制代码
select cast('A' as varchar).lower();

这条语句的本质与传统的 lower(cast('A' as varchar)) 完全一致,点操作符将前面表达式的结果作为后面函数的入参。这一特性要求点操作符后的函数必须仅支持一个入参。

常见误区与解决方案

在使用点操作符时,有两种常见的错误用法需要避免:

  1. 标量直接调用
sql 复制代码
select 'A'.lower(); -- 错误用法

由于标量 'A' 未被正确识别为表达式,无法直接使用点操作符调用函数。

  1. 双冒号强转后调用
sql 复制代码
select 'A'::VARCHAR.lower(); -- 错误用法

双冒号进行类型强转后,同样无法直接使用点操作符调用函数。

解决方法非常简单,只需为标量或强转后的表达式添加括号:

sql 复制代码
select ('A').lower(); -- 正确用法
select ('A'::VARCHAR).lower(); -- 正确用法

高级用法:链式调用的无限可能

连续链式调用

我们可以通过连续使用点操作符,实现多个函数的链式调用:

sql 复制代码
select cast('A' as varchar).lower().ascii();

这条语句会先将 'A' 转换为 varchar 类型,然后将其转换为小写 'a',最后获取其 ASCII 码值 97。

字段别名的作用域扩展

在 DuckDB 中,字段别名的作用域可以扩展到当前 select 列表中:

sql 复制代码
select 97 a,chr(a) b,b.ascii();
select 97 a,a.chr() b, b.ascii() c ;

这意味着我们可以在同一个 select 语句中,直接使用前面定义的字段别名进行后续的函数调用。

自定义类型的点操作符应用

自定义类型的创建与使用

我们可以通过创建自定义类型,进一步扩展点操作符的应用场景:

sql 复制代码
create table t_1(c1 numeric,c2 varchar);
create type ty_1 as struct(c1 numeric,c2 varchar);
create type tyt_1 as ty_1[];
insert into t_1 values (1,'a');
insert into t_1 values (2,'b');

自定义类型的点操作符调用

通过点操作符,我们可以方便地访问自定义类型的成员:

sql 复制代码
select array[(1,'a')::ty_1,(2,'b')::ty_1][1].c2;
select typeof( array[(1,'a'),(2,'b')] ::tyt_1 )

自定义变量的点操作符使用

自定义变量的创建与访问

我们可以创建自定义变量,并通过点操作符访问其成员:

sql 复制代码
-- 创建自定义结构体变量
SET VARIABLE l_ty =(1,'a')::ty_1;
-- 访问结构体变量的成员
select getvariable('l_ty').c1;

-- 创建带点的变量
SET VARIABLE l_ty.c1 =1;
select getvariable('l_ty.c1') ;

-- 创建数组变量
set VARIABLE l_tyt=array[(1,'a')::ty_1,(2,'b')::ty_1];
-- 访问数组变量的成员
select getvariable('l_tyt')[1].c2 ;

自定义类型的函数调用

我们还可以为自定义类型创建函数,并通过点操作符进行调用:

sql 复制代码
create function struct2json(i) as to_json(i);
select ((1,'a')::ty_1).struct2json();
select getvariable('l_ty').struct2json();

源码解析:点操作符的实现原理

语法分析流程

在 DuckDB 的语法分析中,a_expr 必须带括号才能使用点操作符:

sql 复制代码
indirection_expr_or_a_expr: '(' a_expr ')' { $$ = $2; } 
| indirection_expr { $$ = $1; } 
| row { PGFuncCall *n = makeFuncCall(SystemFuncName("row"), $1, @1); $$ = (PGNode *) n; } ;

标量 'A' 会走 d_expr 路线,而不是 a_expr 路线:

sql 复制代码
c_expr: d_expr 
| indirection_expr_or_a_expr opt_extended_indirection { 
    if ($2) { 
        PGAIndirection *n = makeNode(PGAIndirection); 
        n->arg = (PGNode *) $1; 
        n->indirection = check_indirection($2, yyscanner); 
        $$ = (PGNode *) n; 
    } else $$ = (PGNode *) $1; 
} ;

扩展的挑战

虽然我们可以尝试修改源码以支持更多场景,比如字符串常量不加括号直接使用点操作符,但经过分析,这并不是一两行代码就能搞定的事情,需要对 DuckDB 的语法分析和表达式处理逻辑进行深入的修改,还是不整这个活了。

总结

DuckDB 的点操作符函数链式调用功能为我们提供了一种更加简洁、直观的函数调用方式。

传统的函数调用 func1(func2(func3(func4(param)))),阅读的时候,要从左到右找到最里层,然后再从里往外,才能知道最后返回了什么;

而链式调用func4(param).func3().func2().func1(),直接从左往右读一次就行。

通过合理使用这一特性,我们可以编写出更加优雅、易读的 SQL 代码,提高开发效率。同时,自定义类型和自定义变量的点操作符应用,进一步扩展了 DuckDB 的灵活性和可扩展性。

相关推荐
爱学习的阿磊11 分钟前
使用Fabric自动化你的部署流程
jvm·数据库·python
枷锁—sha16 分钟前
【SRC】SQL注入快速判定与应对策略(一)
网络·数据库·sql·安全·网络安全·系统安全
惜分飞29 分钟前
ORA-600 kcratr_nab_less_than_odr和ORA-600 4193故障处理--惜分飞
数据库·oracle
chian-ocean29 分钟前
CANN 生态进阶:利用 `profiling-tools` 优化模型性能
数据库·mysql
m0_5500246333 分钟前
持续集成/持续部署(CI/CD) for Python
jvm·数据库·python
AC赳赳老秦34 分钟前
代码生成超越 GPT-4:DeepSeek-V4 编程任务实战与 2026 开发者效率提升指南
数据库·数据仓库·人工智能·科技·rabbitmq·memcache·deepseek
啦啦啦_99991 小时前
Redis-2-queryFormat()方法
数据库·redis·缓存
玄同7652 小时前
SQLite + LLM:大模型应用落地的轻量级数据存储方案
jvm·数据库·人工智能·python·语言模型·sqlite·知识图谱
吾日三省吾码2 小时前
别只会“加索引”了!这 3 个 PostgreSQL 反常识优化,能把性能和成本一起打下来
数据库·postgresql
chian-ocean2 小时前
百万级图文检索实战:`ops-transformer` + 向量数据库构建语义搜索引擎
数据库·搜索引擎·transformer