Skip to content

Commit

Permalink
Merge pull request h2database#4103 from katzyn/map_columns
Browse files Browse the repository at this point in the history
Pass mapped columns to table filters of subqueries
  • Loading branch information
katzyn authored Aug 4, 2024
2 parents 74ed2b5 + 64f2fbe commit bd9ac2f
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 17 deletions.
12 changes: 12 additions & 0 deletions h2/src/docsrc/html/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ <h1>Change Log</h1>

<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #3945: Column not found in correlated subquery, when referencing outer column from LEFT JOIN .. ON clause
</li>
<li>Issue #4097: StackOverflowException when using multiple SELECT statements in one query (2.3.230)
</li>
<li>Issue #3982: Potential issue when using ROUND
</li>
<li>Issue #3894: Race condition causing stale data in query last result cache
</li>
<li>Issue #4075: infinite loop in compact
</li>
<li>Issue #4091: Wrong case with linked table to postgresql
</li>
<li>Issue #4088: BadGrammarException when the same alias is used within two different CTEs
</li>
<li>Issue #4079: [2.3.230] Regression in ORDER BY ... DESC on dates
Expand Down
4 changes: 3 additions & 1 deletion h2/src/main/org/h2/command/query/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,10 @@ public TypeInfo getRowDataType() {
* @param level
* the subquery level (0 is the top level query, 1 is the first
* subquery level)
* @param outer
* whether this method was called from the outer query
*/
public abstract void mapColumns(ColumnResolver resolver, int level);
public abstract void mapColumns(ColumnResolver resolver, int level, boolean outer);

/**
* Change the evaluatable flag. This is used when building the execution
Expand Down
7 changes: 5 additions & 2 deletions h2/src/main/org/h2/command/query/Select.java
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,7 @@ public void init() {
}
// map columns in select list and condition
for (TableFilter f : filters) {
mapColumns(f, 0);
mapColumns(f, 0, false);
}
mapCondition(havingIndex);
mapCondition(qualifyIndex);
Expand Down Expand Up @@ -1596,13 +1596,16 @@ public void setForUpdate(ForUpdate b) {
}

@Override
public void mapColumns(ColumnResolver resolver, int level) {
public void mapColumns(ColumnResolver resolver, int level, boolean outer) {
for (Expression e : expressions) {
e.mapColumns(resolver, level, Expression.MAP_INITIAL);
}
if (condition != null) {
condition.mapColumns(resolver, level, Expression.MAP_INITIAL);
}
for (TableFilter tableFilter : topFilters) {
tableFilter.mapColumns(resolver, level, outer);
}
}

@Override
Expand Down
6 changes: 3 additions & 3 deletions h2/src/main/org/h2/command/query/SelectUnion.java
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ public void setForUpdate(ForUpdate forUpdate) {
}

@Override
public void mapColumns(ColumnResolver resolver, int level) {
left.mapColumns(resolver, level);
right.mapColumns(resolver, level);
public void mapColumns(ColumnResolver resolver, int level, boolean outer) {
left.mapColumns(resolver, level, outer);
right.mapColumns(resolver, level, outer);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public void setForUpdate(ForUpdate forUpdate) {
}

@Override
public void mapColumns(ColumnResolver resolver, int level) {
public void mapColumns(ColumnResolver resolver, int level, boolean outer) {
int columnCount = visibleColumnCount;
for (ArrayList<Expression> row : rows) {
for (int i = 0; i < columnCount; i++) {
Expand Down
2 changes: 1 addition & 1 deletion h2/src/main/org/h2/expression/ArrayConstructorByQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public TypeInfo getType() {

@Override
public void mapColumns(ColumnResolver resolver, int level, int state) {
query.mapColumns(resolver, level + 1);
query.mapColumns(resolver, level + 1, true);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion h2/src/main/org/h2/expression/Subquery.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public TypeInfo getType() {
@Override
public void mapColumns(ColumnResolver resolver, int level, int state) {
outerResolvers.add(resolver);
query.mapColumns(resolver, level + 1);
query.mapColumns(resolver, level + 1, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ abstract class PredicateWithSubquery extends Condition {

@Override
public void mapColumns(ColumnResolver resolver, int level, int state) {
query.mapColumns(resolver, level + 1);
query.mapColumns(resolver, level + 1, true);
}

@Override
Expand Down
35 changes: 28 additions & 7 deletions h2/src/main/org/h2/table/TableFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,6 @@ public void addFilterCondition(Expression condition, boolean isJoin) {
*/
public void addJoin(TableFilter filter, boolean outer, Expression on) {
if (on != null) {
on.mapColumns(this, 0, Expression.MAP_INITIAL);
TableFilterVisitor visitor = new MapColumnsVisitor(on);
visit(visitor);
filter.visit(visitor);
Expand All @@ -647,7 +646,7 @@ public void addJoin(TableFilter filter, boolean outer, Expression on) {
filter.visit(JOI_VISITOR);
}
if (on != null) {
filter.mapAndAddFilter(on);
filter.addFilter(on);
}
} else {
join.addJoin(filter, outer, on);
Expand All @@ -664,18 +663,40 @@ public void setNestedJoin(TableFilter filter) {
}

/**
* Map the columns and add the join condition.
* Add the join condition.
*
* @param on the condition
*/
public void mapAndAddFilter(Expression on) {
on.mapColumns(this, 0, Expression.MAP_INITIAL);
public void addFilter(Expression on) {
addFilterCondition(on, true);
if (join != null) {
join.addFilter(on);
}
}

/**
* Map the columns to the given column resolver.
*
* @param resolver
* the resolver
* @param level
* the subquery level (0 is the top level query, 1 is the first
* subquery level)
* @param outer
* whether this method was called from the outer query
*/
public void mapColumns(ColumnResolver resolver, int level, boolean outer) {
if (!outer && joinOuter) {
return;
}
if (joinCondition != null) {
joinCondition.mapColumns(resolver, level, Expression.MAP_INITIAL);
}
if (nestedJoin != null) {
on.mapColumns(nestedJoin, 0, Expression.MAP_INITIAL);
nestedJoin.mapColumns(resolver, level, outer);
}
if (join != null) {
join.mapAndAddFilter(on);
join.mapColumns(resolver, level, outer);
}
}

Expand Down
52 changes: 52 additions & 0 deletions h2/src/test/org/h2/test/scripts/queries/joins.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1044,3 +1044,55 @@ FROM (SELECT 1 A) X JOIN (
> - - -
> 1 1 1
> rows: 1

EXPLAIN
WITH TEST(ID) AS (VALUES 1)
SELECT * FROM TEST A INNER JOIN TEST B ON TRUE LEFT OUTER JOIN TEST C ON C.ID = A.ID;
> PLAN
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> WITH "TEST"("ID") AS ( VALUES (1) ) SELECT "A"."ID", "B"."ID", "C"."ID" FROM "TEST" "A" /* VALUES (1) */ INNER JOIN "TEST" "B" /* VALUES (1) */ ON 1=1 LEFT OUTER JOIN "TEST" "C" /* VALUES (1) */ ON "C"."ID" = "A"."ID"
> rows: 1

-- Column A.ID cannot be referenced from this part of the query
EXPLAIN
WITH TEST(ID) AS (VALUES 1)
SELECT * FROM TEST A INNER JOIN TEST B LEFT OUTER JOIN TEST C ON C.ID = A.ID ON TRUE;
> exception COLUMN_NOT_FOUND_1

WITH
A(A) AS (VALUES (1)),
B(B) AS (VALUES (1)),
C(C) AS (VALUES (1))
SELECT
A.A,
(
SELECT B.B
FROM B
JOIN C
ON B.B = A.A
AND C.C = B.B
)
FROM A;
> A (SELECT B.B FROM B B INNER JOIN C C ON 1=1 WHERE (B.B = A.A) AND (C.C = B.B))
> - -----------------------------------------------------------------------------
> 1 1
> rows: 1

WITH
A(A) AS (VALUES (1)),
B(B) AS (VALUES (1)),
C(C) AS (VALUES (1))
SELECT
A.A,
(
SELECT B.B
FROM B
LEFT JOIN C
ON B.B = A.A
AND C.C = B.B
)
FROM A;
> A (SELECT B.B FROM B B LEFT OUTER JOIN C C ON (B.B = A.A) AND (C.C = B.B))
> - ------------------------------------------------------------------------
> 1 1
> rows: 1

0 comments on commit bd9ac2f

Please sign in to comment.