You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Calcite执行含NULL值多列排序查询时触发NullPointerException

问题描述

执行以下SQL查询时触发NullPointerException:

select
    distinct tbl.col1 ,
    tbl.col2  
from
    infoset.SUBEKT tbl   
order by
    col1 asc nulls last ,
    col2 asc nulls last

异常堆栈信息:

java.lang.NullPointerException
    at org.apache.calcite.runtime.Utilities.compare(Utilities.java:202)
    at Baz$2.compare(ANONYMOUS.java:15)
    at Baz$2.compare(ANONYMOUS.java:27)
    at java.util.TreeMap.getEntryUsingComparator(TreeMap.java:376)
    at java.util.TreeMap.getEntry(TreeMap.java:345)
    at java.util.TreeMap.get(TreeMap.java:278)
    at org.apache.calcite.linq4j.EnumerableDefaults.toLookup_(EnumerableDefaults.java:3608)
    at org.apache.calcite.linq4j.EnumerableDefaults$14.enumerator(EnumerableDefaults.java:2660)
    at org.apache.calcite.linq4j.AbstractEnumerable.iterator(AbstractEnumerable.java:33)
    at org.apache.calcite.avatica.MetaImpl.createCursor(MetaImpl.java:90)
    at org.apache.calcite.avatica.AvaticaResultSet.execute(AvaticaResultSet.java:186)
    at org.apache.calcite.jdbc.CalciteResultSet.execute(CalciteResultSet.java:64)
    at org.apache.calcite.jdbc.CalciteResultSet.execute(CalciteResultSet.java:43)
    at org.apache.calcite.avatica.AvaticaConnection$1.execute(AvaticaConnection.java:669)
    at org.apache.calcite.jdbc.CalciteMetaImpl.prepareAndExecute(CalciteMetaImpl.java:636)
    at org.apache.calcite.avatica.AvaticaConnection.prepareAndExecuteInternal(AvaticaConnection.java:677)
    at org.apache.calcite.avatica.AvaticaStatement.executeInternal(AvaticaStatement.java:157)
    at org.apache.calcite.avatica.AvaticaStatement.executeQuery(AvaticaStatement.java:228)
    at ru.spi2.modules.infosetquery.InfosetQueryPlugin.executeQuery(InfosetQueryPlugin.java:101)

经排查,问题由col1存在NULL值导致,仅按col1或col2单一列排序时可正常执行。

解决方案

1. 升级Calcite版本

该NPE是Calcite的已知bug,在1.28.0及以上版本已修复。升级到对应版本可直接解决多列排序含NULL值时的比较逻辑漏洞。

2. 临时规避方案(无法升级时)

  • 为可能为NULL的列添加非空默认值,确保排序时无NULL参与比较:
select
    distinct coalesce(tbl.col1, 'DEFAULT_MARKER') as col1,
    tbl.col2  
from
    infoset.SUBEKT tbl   
order by
    coalesce(tbl.col1, 'DEFAULT_MARKER') asc nulls last ,
    col2 asc nulls last

注意:DEFAULT_MARKER需与col1数据类型匹配,比如字符串类型用空串,数值类型用0或业务允许的默认值。

  • 拆分去重与排序逻辑,调整执行计划:
select col1, col2
from (
    select distinct tbl.col1, tbl.col2
    from infoset.SUBEKT tbl
) t
order by col1 asc nulls last, col2 asc nulls last

这种写法会让Calcite先完成去重再执行排序,绕过原bug触发的执行路径。

3. 检查自定义比较器(若有)

如果项目中自定义了Calcite的比较逻辑,需确认是否正确处理NULL值场景:在调用比较方法前先判断NULL,避免直接对NULL值执行比较操作导致NPE。

内容的提问来源于stack exchange,提问作者Kirill Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:53:19