☰
Apache Calcite 1.38.0 版本特性深度解析:AS MEASURE、ASOF Join 与类型系统演进
2026/10/13 2:16:31 网站建设 项目流程
  • 后端
  • 数据库

【免费下载链接】calcite

Apache Calcite

项目地址:https://gitcode.com/gh_mirrors/ca/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):

  1. CAST到DECIMAL不再是 no-op(CALCITE-6322):此前对DECIMAL的 cast 会被当作空操作忽略,修复后涉及DECIMAL的计算结果可能产生细微差异;同时修复了字面量 cast 到DECIMAL结果错误(CALCITE-4871)与 INTERVAL/STRING 到 DECIMAL 的 cast 错误(CALCITE-6380)。
  2. 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

项目地址:https://gitcode.com/gh_mirrors/ca/calcite
点击查看免费下载
上一篇:Wand-Enhancer:如何安全解锁WeMod完整功能的技术方案
下一篇:终极Wand-Enhancer完整指南:如何免费解锁WeMod专业版功能

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询