Ruben Q L created CALCITE-7904:
----------------------------------
Summary: SingleValuesOptimizationRules give wrong results for ASOF
and SEMI joins and fail for LEFT ASOF join
Key: CALCITE-7904
URL: https://issues.apache.org/jira/browse/CALCITE-7904
Project: Calcite
Issue Type: Bug
Components: core
Reporter: Ruben Q L
Assignee: Ruben Q L
Fix For: 1.43.0
The rules in SingleValuesOptimizationRules (JOIN_LEFT_INSTANCE,
JOIN_RIGHT_INSTANCE, JOIN_LEFT_PROJECT_INSTANCE, JOIN_RIGHT_PROJECT_INSTANCE)
remove a Join when one of its inputs is a single-row Values. They replace it
with a Filter and Project on the other input. isJoinTransformable does not
handle three cases correctly:
1. ASOF JOIN (single-row Values on either side). The rewrite uses only the ON
condition and ignores the MATCH_CONDITION. Instead of the single closest match,
it returns every row that matches on the key.
Example:
{code:sql}
SELECT *
FROM (VALUES (1, 3), (1, 5), (2, 5)) AS t1(k, t)
ASOF JOIN (VALUES (1, 4)) AS t2(k, t)
MATCH_CONDITION t2.t < t1.t
ON t1.k = t2.k;
-- expected: (1, 5, 1, 4)
-- actual: (1, 3, 1, 4), (1, 5, 1, 4)
{code}
2. LEFT ASOF JOIN (single-row Values on either side). LEFT_ASOF counts as an
outer join, so no filter is applied. getRexTransformer has no LEFT_ASOF case,
so the right-side columns are not NULL-padded and keep their NOT NULL type. The
rewritten expression's row type then differs from the join's, whose right-side
columns are nullable, and planning fails.
Example:
{code:sql}
SELECT *
FROM (VALUES (1, 3), (1, 5), (2, 5)) AS t1(k, t)
LEFT ASOF JOIN (VALUES (1, 4)) AS t2(k, t)
MATCH_CONDITION t2.t < t1.t
ON t1.k = t2.k;
-- expected: (1, 3, null, null), (1, 5, 1, 4), (2, 5, null, null)
-- actual: java.lang.IllegalArgumentException: Type mismatch:
-- rel rowtype: RecordType(INTEGER NOT NULL K, INTEGER NOT NULL T, INTEGER NOT
NULL $f2, INTEGER NOT NULL $f3) NOT NULL
-- equiv rowtype: RecordType(INTEGER NOT NULL K, INTEGER NOT NULL T, INTEGER
K0, INTEGER T0) NOT NULL
-- at
org.apache.calcite.plan.volcano.VolcanoPlanner.register(VolcanoPlanner.java:594)
{code}
Note: even if we fixed the nullability issue, then the LEFT ASOF would still
fall into the "wrong result" scenario of the ASOF case above (due to
MATCH_CONDITION being ignore).
3. SEMI JOIN with the single-row Values as the left input. The rewrite emits
the Values row once for each matching row on the right, but a semi-join should
emit it at most once. Single-row Values on the right of a SEMI join is handled
correctly. For example, with the hr schema and
topDownGeneralDecorrelationEnabled=true (which produces a SEMI join for EXISTS).
Example:
{code:sql}
SELECT * FROM (VALUES (10)) AS t(x)
WHERE EXISTS (SELECT 1 FROM "hr"."emps" AS e WHERE e."deptno" = t.x);
-- expected: 10
-- actual: 10, 10, 10 (one row per employee in department 10)
{code}
The fix is to make isJoinTransformable exclude ASOF and LEFT ASOF joins on both
sides, and SEMI joins when the Values is the left input.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)