- 后端
- 数据库
【免费下载链接】calcite
Apache Calcite
Apache Calcite 1.38.0 于 2024-10-15 正式发布,是继 1.37.0 之后时隔 5 个月的重要版本,由 39 位贡献者共同完成,共解决 165 个 Issue。本篇文章基于仓库内发布公告(site/_posts/2024-10-15-release-1.38.0.md)与完整变更记录(site/_docs/history.md),并结合 core 模块源码,系统解析该版本带来的AS MEASURE度量列子句、ASOF JOIN时间匹配连接、窗口函数EXCLUDE帧排除子句、Postgres 兼容日期时间函数以及支持负 scale 的类型系统扩展等核心能力,帮助你理解这些特性如何在解析器、类型系统与执行引擎中落地,并为升级到 1.38.0 提供迁移指引。
版本概览:发布节奏与兼容性
根据发布公告,1.38.0 距上一版本 1.37.0 约 5 个月,共合并了 39 位贡献者的提交,解决了 165 个问题。版本发布者(release manager)为 Julian Hyde。
官方兼容性声明(见 site/_docs/history.md)明确该版本在以下环境中完成测试:
- 操作系统:Linux、macOS、Microsoft Windows;
- JDK/OpenJDK 版本:8 到 23;
- Guava 版本:21.0 到 33.3.0-jre;
- 其余软件版本以 gradle.properties 中声明的为准。
亮点一:AS MEASURE子句与度量(Measure)类型体系
1.38.0 最大的语言特性是AS MEASURE子句(CALCITE-4496),允许在查询中定义度量列,并在简单查询(如ORDER BY)中使用(CALCITE-6519)。
语法层面的落地
解析器在词法层面新增了MEASURE与MEASURES关键字,见 core/src/main/codegen/templates/Parser.jj:
< MEASURE: "MEASURE" > < MEASURES: "MEASURES" >在 select 子句解析中,MEASURE关键字用于把某个聚合表达式包装为度量列,相关解析逻辑位于 Parser.jj 附近,内部通过SqlInternalOperators.MEASURE.createCall(...)构造调用节点:
[ <MEASURE> { e = SqlInternalOperators.MEASURE.createCall(类型系统层的支撑:MEASURE<T>
SqlMeasureOperator是SqlInternalOperator的子类(见 core/src/main/java/org/apache/calcite/sql/SqlMeasureOperator.java),其返回类型推断将表达式的类型T包装为MEASURE<T>。对应的类型变换定义在 core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java:
TO_MEASURE:把类型T变换为MEASURE<T>,实现即createMeasureType(typeToTransform);FROM_MEASURE:把MEASURE<T>还原为T,是其逆变换;FROM_MEASURE_IF:仅当输入是 measure 类型时剥离包装,否则原样返回,用于在普通运算上下文中透明地消费度量列。
度量列在参与普通非聚合运算时通过FROM_MEASURE系列变换自动还原为底层类型,这保证SELECT SUM(x) AS MEASURE ... ORDER BY 度量列这类"简单查询中使用度量"的能力(CALCITE-6519)在类型检查层面即可成立。本版本还修复了AS运算符改变度量返回类型的问题(CALCITE-6343),确保包装语义不被破坏。
亮点二:ASOF JOIN时间匹配连接
ASOF JOIN(CALCITE-6372)是金融与事件流场景中的经典连接类型:对于左表的每一行,找到右表中"时间戳不大于/不小于当前行"的最近匹配行。
语法与连接类型
解析器在 join 语法中新增了ASOF关键字,见 Parser.jj:
<ASOF> <JOIN> { joinType = JoinType.ASOF; } <LEFT> [ <OUTER> | <ASOF> { asof = true; } ] <JOIN> { joinType = asof ? JoinType.LEFT_ASOF : JoinType.LEFT; }即支持ASOF JOIN与LEFT ASOF JOIN(等价于LEFT OUTER ASOF JOIN)两种形式。关系代数层新增了ASOF与LEFT_ASOF两种JoinRelType枚举值,见 core/src/main/java/org/apache/calcite/rel/core/JoinRelType.java,并新增AsofJoin关系表达式基类(core/src/main/java/org/apache/calcite/rel/core/AsofJoin.java)。
执行与优化细节
- 物理执行由
EnumerableAsofJoin承担(core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAsofJoin.java),注册规则为ENUMERABLE_ASOFJOIN_RULE(见 core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRules.java)。 - ASOF join 的条件被限定为等值条件 + 时间比较条件(源码注释明确 "ASOF joins conditions are restricted to equalities")。
- 优化器配套修复了
RelOptUtil.pushDownJoinConditions未能正确调整 ASOF join 匹配条件的问题(CALCITE-6540),见 core/src/main/java/org/apache/calcite/plan/RelOptUtil.java。
测试示例
core/src/test/java/org/apache/calcite/test/enumerable/EnumerableJoinTest.java 中的asofJoinTest给出了可对照执行的语义,其等效 SQL 为:
-- LEFT ASOF JOIN:保留左表全部行,右表只保留时间上"最近且满足条件"的匹配 SELECT d.deptno, e.empid FROM emps e LEFT ASOF JOIN depts d MATCH_CONDITION d.name <= e.name ON d.deptno = e.deptno;对于没有匹配行的左表记录,右表字段输出 NULL,这与LEFT JOIN的保留语义一致。
亮点三:窗口聚合的EXCLUDE帧排除子句
窗口函数框架支持了帧排除(frame exclusion)语法(CALCITE-5855),例如OVER (... EXCLUDE CURRENT ROW)。解析器在窗口 frame 定义中新增EXCLUDE关键字支持,见 Parser.jj 与 Parser.jj:
<EXCLUDE> <EXCLUDE> <NULLS> { includeNulls = false; }它允许在滑动窗口计算中显式剔除当前行、前后行或 NULL 值,是 SQL:2016 标准中窗口帧排除能力的补齐,适用于自相关计算、同比环比等需要"排除自身"的统计场景。同版本还修复了无ORDER BY时ROWS窗口聚合忽略帧的问题(CALCITE-5883),保证帧语义在不同排序条件下一致。
亮点四:Postgres 兼容的TO_DATE、TO_TIMESTAMP、TO_CHAR
1.38.0 启用了 Postgres 实现版本的三个日期时间格式化函数(CALCITE-6449、CALCITE-6358),并支持 Postgres 14 的全部日期/时间格式模式(CALCITE-6392)。
在 core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 中可以看到这些函数的 Postgres 变体定义,例如TO_DATE_PG返回DATE_NULLABLE,TO_CHAR_PG返回VARCHAR_NULLABLE,它们被注册到 Postgres 函数库中,仅在使用 Postgres 方言(Lex/函数库选择)时才可见:
-- 以 Postgres 函数库运行时的典型用法 SELECT TO_DATE('2024-10-15', 'YYYY-MM-DD'); SELECT TO_TIMESTAMP('15 Oct 2024 10:30:00', 'DD Mon YYYY HH24:MI:SS'); SELECT TO_CHAR(TIMESTAMP '2024-10-15 10:30:00', 'YYYY-MM-DD HH24:MI:SS');配套改进包括:TO_CHAR缓存优化(CALCITE-6585)以及针对TO_CHARNULL 参数的补充测试(CALCITE-6572)。此外 Postgres 函数库还同步新增了DATE_PART(CALCITE-6311)、REGEXP_REPLACE(CALCITE-6310)、LOG(CALCITE-6312)、GREATEST/LEAST(CALCITE-5634)及基于角度的三角函数族(CALCITE-6472)。
亮点五:类型系统支持负 scale 的 DECIMAL
CALCITE-6560扩展了类型系统,允许DECIMAL类型使用负的 scale(例如DECIMAL(10, -2),表示精确到百位)。为此RelDataTypeSystem新增了getMinScale(SqlTypeName)方法,并废弃了getMaxNumericScale()与getMaxNumericPrecision()(CALCITE-6598),以统一 scale/precision 的取值边界管理,见 core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java。
迁移注意(Breaking Change)
1.38.0 会继续调用被废弃的getMaxNumericScale/getMaxNumericPrecision覆写方法,但从 1.39 起将停止调用。如果你在自定义RelDataTypeSystem中覆写了这两个方法,应在 1.38 版本期间把逻辑迁移到新的getMaxScale(DECIMAL)与getMaxPrecision(DECIMAL),同时为getMinScale提供负 scale 场景下的下界。
其他值得关注的新特性
新函数与函数库扩充
本版本新增了大量函数,按函数库划分(详细清单见 site/_docs/history.md):
- 通用标量函数:
BITNOT(CALCITE-3592)、BITAND/BITOR/BITXOR(CALCITE-3779)、BITCOUNT(CALCITE-3697)、数组比较运算符(CALCITE-6454)、BOOLAGG_AND/BOOLAGG_OR(Snowflake 库,CALCITE-6441); - Spark 库:
DATE_ADD(CALCITE-6527)、LOG1P(CALCITE-6549)、SUBSTRING_INDEX(CALCITE-5807)、REVERSE(CALCITE-6445)、CONCAT_WS(CALCITE-6446)、NVL2(CALCITE-6397,Oracle/Spark); - MySQL 库:
LOG(CALCITE-6325)、RLIKE(CALCITE-6424); - 同时新增了完整的 Amazon Redshift 函数库(CALCITE-6444)。
RelBuilder 与规则层增强
RelBuilder新增aggregateRex方法,支持对复杂表达式(如1 + SUM(x + 2))进行聚合(CALCITE-5802),实现位于 core/src/main/java/org/apache/calcite/tools/RelBuilder.java;- 新增规划规则
ProjectOverSumToSum0Rule,把SUM转换为SUM0以支持投影下推(CALCITE-6020); RelDataTypeSystem增加RoundingMode配置以指定取整行为(CALCITE-4838);- Lattice SQL 支持生成指定方言(CALCITE-6603);
UPDATE语句SET子句支持表别名前缀的列标识符及其校验(CALCITE-6576、CALCITE-6584);JSON_QUERY支持RETURNING子句(CALCITE-6365);STDDEV等聚合函数的DECIMAL中间结果使用更高精度(CALCITE-6427)。
依赖与构建升级
- 支持 Java 23 与 Guava 33.3.0,Hadoop 由 2.7.5 升级至 2.10.2(CALCITE-6587);
- 支持 JDK 21/22,并升级 asm 7.2→9.6、byte-buddy 1.9.3→1.14.15、forbiddenapis 3.5.1→3.7、mockito 2.23.4→3.12.4(CALCITE-5737);
- Gradle 由 7.6.1 升级到 8.7(CALCITE-6174);
- 构建基础设施从 Gradle Enterprise 插件迁移到 Develocity 插件(CALCITE-6511)。
其余 Breaking Changes 提醒
除RelDataTypeSystem方法废弃外,1.38.0 还有两处行为变更需要升级时注意(详见 site/_docs/history.md):
CAST到DECIMAL不再是 no-op(CALCITE-6322):此前对DECIMAL的 cast 会被当作空操作忽略,修复后涉及DECIMAL的计算结果可能产生细微差异;同时修复了字面量 cast 到DECIMAL结果错误(CALCITE-4871)与 INTERVAL/STRING 到 DECIMAL 的 cast 错误(CALCITE-6380)。RexLiteral中浮点值的表示方式改变(CALCITE-2067):此前DOUBLE/FLOAT/REAL字面量编码为BigDecimal会造成表达式简化时的精度损失;1.38 起改为使用 Javadouble存储,RexLiteral.getValue()的返回类型相应变化。若你的代码依赖浮点字面量的BigDecimal返回类型,需要适配。
精选 Bug 修复
- 浮点字面量精度丢失:
REAL字面量表示精度显著提升(CALCITE-6395); - 聚合与窗口:
AggregateJoinTransposeRule在SemiJoin上的越界异常(CALCITE-6600)、AggregateMergeRule的 "type mismatch"(CALCITE-6557)、窗口函数FILTER校验被误拒(CALCITE-6442); - JDBC 适配器:Oracle 不再生成
BOOLEAN的CASE WHEN(CALCITE-6480)、StarRocks 的INT/BIGINTSQL(CALCITE-6426)、Hive/Spark 无精度VARCHAR与含$标识符的引用问题(CALCITE-6419、CALCITE-6434); - Arrow 适配器:补齐
IS NULL/IS NOT NULL支持(CALCITE-6296、CALCITE-6295),并默认对不支持的过滤回退到 Enumerable 约定(CALCITE-6429); - 其他:随机函数被错误视为确定性函数(CALCITE-6507)、
HAVING误拒窗口函数(CALCITE-6473)。
总结与升级建议
Apache Calcite 1.38.0 是一次"语言特性 + 类型系统 + 生态兼容"并重的版本:AS MEASURE与MEASURE<T>类型把语义层度量正式引入查询语言,ASOF JOIN为流式与金融场景补齐了时间最近匹配的联合能力,窗口EXCLUDE与 Postgres 日期时间函数则进一步缩小了与主流数据库 SQL 方言的差距,负 scale 类型与RelDataTypeSystem的重构则为其后更复杂的数值类型推导铺路。
升级到 1.38.0 前建议重点检查三类影响:DECIMALcast 语义变化对数值计算结果的扰动、RexLiteral.getValue()对浮点返回类型的依赖,以及自定义RelDataTypeSystem中已废弃方法的迁移。若需要查看完整变更清单,可继续阅读 site/_docs/history.md,并对照各特性在 core/src/main/codegen/templates/Parser.jj 与 core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 中的实现细节。
- 后端
- 数据库
【免费下载链接】calcite
Apache Calcite
相关推荐
Apache Calcite 1.33.0 版本发布解析:BigQuery 方言增强、MEASURE 类型与自定义时间帧
Apache Calcite 1.33.0 版本发布解析:BigQuery 方言增强、MEASURE 类型与自定义时间帧 Apache Calcite 1.33
后端数据库BadgerDB 版本演进与技术特性深度解析
BadgerDB 版本演进与技术特性深度解析 前言 BadgerDB 是一个高性能的键值存储数据库,采用纯 Go 语言编写,特别优化了 SSD 存储性能。本文将
数据存储KV存储嵌入式数据库CommonMark.js 版本演进与技术特性深度解析
CommonMark.js 版本演进与技术特性深度解析 项目概述 CommonMark.js 是一个严格遵循 CommonMark 规范的 Markdown 解
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考