【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 的灵活性和可扩展性。

相关推荐
爱可生开源社区1 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1771 天前
《从零搭建NestJS项目》
数据库·typescript
加号32 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏2 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐2 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再2 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip
tryCbest2 天前
数据库SQL学习
数据库·sql
jnrjian2 天前
ORA-01017 查找机器名 用户名 以及library cache lock 参数含义
数据库·oracle
十月南城2 天前
数据湖技术对比——Iceberg、Hudi、Delta的表格格式与维护策略
大数据·数据库·数据仓库·hive·hadoop·spark
Henry Zhu1232 天前
数据库:并发控制基本概念
服务器·数据库