Skip to content

Fix NPE in ALIGN BY DEVICE queries when a device's predicate is always true - #19009

Merged
JackieTien97 merged 1 commit into
apache:masterfrom
JackieTien97:fix-abd-always-true-predicate-npe
Oct 24, 2026
Merged

JackieTien97 merged 1 commit into
apache:masterfrom
JackieTien97:fix-abd-always-true-predicate-npe

Conversation

@JackieTien97

Copy link
Copy Markdown
Contributor

Description

In a tree-model ALIGN BY DEVICE query, the WHERE predicate is bound and simplified per device.
When the predicate of a device simplifies to TRUE, AnalyzeVisitor#analyzeDeviceToWhere stores
null as that device's where expression, which means "no value filter". This happens, for example,
when the predicate references a series that the device does not have: the series is bound to
NullOperand, so s2 IS NULL becomes null IS NULL (TRUE) and NOT (s2 > 5) becomes
NOT (null > 5) (TRUE).

AnalyzeVisitor#analyzeDeviceToSource did not skip these null entries and passed them to
ExpressionAnalyzer.searchSourceExpressions, so the query failed:

305: Cannot invoke "org.apache.iotdb.db.queryengine.plan.expression.Expression.accept(...)" because "expression" is null

Reproduce:

CREATE DATABASE root.rv;
CREATE TIMESERIES root.rv.d2.s1 WITH DATATYPE=INT32, ENCODING=PLAIN;
INSERT INTO root.rv.d2(time, s1) VALUES (1, 2);
INSERT INTO root.rv.d2(time, s1) VALUES (2, 7);
SELECT s1 FROM root.rv.d2 WHERE s2 IS NULL ALIGN BY DEVICE;    -- 305 NPE
SELECT s1 FROM root.rv.d2 WHERE NOT (s2 > 5) ALIGN BY DEVICE;  -- 305 NPE
SELECT s1 FROM root.rv.d2 WHERE NOT (s2 > 5);                  -- returns both rows

The null convention came with predicate simplification in b93cd04, which made the
non-ALIGN BY DEVICE counterpart (analyzeSource) skip a null where expression but missed
analyzeDeviceToSource. The other consumers of the per-device where expressions already handle
null: LogicalPlanVisitor plans it as "no filter", and the removal in analyzeSelect is
null-safe.

Found while reviewing #18982, but independent of it: it also fails on master with a predicate that
contains no NOT, and the two changes merge cleanly.

Changes

  • AnalyzeVisitor#analyzeDeviceToSource: skip devices whose where expression is null, as
    analyzeSource does, so their source expressions come only from the selected expressions.
  • Tests:
    • AnalyzeTest: a device whose IS NULL / NOT predicate becomes TRUE, and a mix of devices
      where one device keeps its real filter.
    • DataQueryLogicalPlannerTest: only the device with a real predicate gets a FilterNode.
    • IoTDBNullOperandIT: raw, time-filtered and aggregation ALIGN BY DEVICE queries on a series
      that does not exist.
    • IoTDBAlignByDeviceIT (also runs as 2IT/3IT/4IT): one device filtered by its own series
      while the other device's predicate is always true, for raw and aggregation queries.

Verification

  • Before the fix, the three new unit tests and the two new IT methods fail with the NPE above, thrown
    from AnalyzeVisitor.analyzeDeviceToSource.
  • After the fix:
    mvn verify -pl iotdb-core/datanode,integration-test -am -P with-integration-tests -Dtest='org/apache/iotdb/db/queryengine/plan/**/*Test.java, !org/apache/iotdb/db/queryengine/plan/relational/**' -Dit.test=IoTDBNullOperandIT,IoTDBAlignByDeviceIT,IoTDBAlignByDevice2IT,IoTDBAlignByDevice3IT,IoTDBAlignByDevice4IT
    • AnalyzeTest 30/30, DataQueryLogicalPlannerTest 14/14
    • IoTDBNullOperandIT 8/8, IoTDBAlignByDeviceIT / 2IT / 3IT / 4IT 22/22 each
    • The other non-relational queryengine.plan unit tests in that run show no new failures. Four
      PowerMock load test classes fail locally only because user_encrypt_token is set in my shell;
      they fail the same way on master, and that is fixed separately in Fix datanode PowerMock tests failing when user_encrypt_token is set #19008.
    • Spotless, Checkstyle and RAT passed.

🤖 Generated with Claude Code

…s true

In a tree-model ALIGN BY DEVICE query, the WHERE predicate is bound and
simplified per device. When it becomes TRUE for a device, for example
because it references a series that the device does not have
(`s2 IS NULL`, `NOT (s2 > 5)`), analyzeDeviceToWhere stores null as that
device's where expression to mean "no value filter".
analyzeDeviceToSource passed that null to searchSourceExpressions, so the
query failed with a NullPointerException (305).

Skip null where expressions in analyzeDeviceToSource, as analyzeSource
already does for queries without ALIGN BY DEVICE. LogicalPlanVisitor
already plans a null where expression as no filter.
@JackieTien97
JackieTien97 merged commit 321bba7 into apache:master Oct 24, 2026
37 of 39 checks passed
@JackieTien97
JackieTien97 deleted the fix-abd-always-true-predicate-npe branch October 24, 2026 00:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant