Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parameters in CTEs #3990

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions h2/src/docsrc/html/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ <h1>Change Log</h1>

<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #3554: Regression in 2.1.214 when joining 2 recursive CTE containing bind values
</li>
<li>PR #3989: Refactor CTE-related code and reduce recompilations
</li>
<li>Issue #3987: Allow empty &lt;with column list&gt;
</li>
<li>Issue #822: WITH clauses' column aliases are not maintained correctly when selecting from CTE from within a derived
Expand Down
14 changes: 13 additions & 1 deletion h2/src/main/org/h2/command/ParserBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,19 @@ public final void setLiteralsChecked(boolean literalsChecked) {
}

public final void setSuppliedParameters(ArrayList<Parameter> suppliedParameters) {
this.parameters = suppliedParameters;
int max = Parameter.getMaxIndex(suppliedParameters);
if (max > suppliedParameters.size()) {
ArrayList<Parameter> parameters = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
parameters.add(null);
}
for (Parameter p : suppliedParameters) {
parameters.set(p.getIndex(), p);
}
this.parameters = parameters;
} else {
this.parameters = suppliedParameters;
}
}

/**
Expand Down
22 changes: 22 additions & 0 deletions h2/src/main/org/h2/expression/Parameter.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/
package org.h2.expression;

import java.util.ArrayList;

import org.h2.api.ErrorCode;
import org.h2.engine.SessionLocal;
import org.h2.expression.condition.Comparison;
Expand All @@ -20,6 +22,26 @@
*/
public final class Parameter extends Operation0 implements ParameterInterface {

/**
* Returns the maximum 1-based index.
*
* @param parameters
* parameters
* @return the maximum 1-based index, or {@code -1}
*/
public static int getMaxIndex(ArrayList<Parameter> parameters) {
int result = 0;
for (Parameter p : parameters) {
if (p != null) {
int index = p.getIndex() + 1;
if (index > result) {
result = index;
}
}
}
return result;
}

private Value value;
private Column column;
private final int index;
Expand Down
16 changes: 3 additions & 13 deletions h2/src/main/org/h2/table/QueryExpressionTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,6 @@ public static List<Column> createQueryColumnTemplateList(String[] cols, Query th
return columnTemplateList;
}

static int getMaxParameterIndex(ArrayList<Parameter> parameters) {
int result = -1;
for (Parameter p : parameters) {
if (p != null) {
result = Math.max(result, p.getIndex());
}
}
return result;
}

Query viewQuery;

ArrayList<Table> tables;
Expand Down Expand Up @@ -249,11 +239,11 @@ public final long getRowCountApproximation(SessionLocal session) {
*/
public final int getParameterOffset(ArrayList<Parameter> additionalParameters) {
Query topQuery = getTopQuery();
int result = topQuery == null ? -1 : getMaxParameterIndex(topQuery.getParameters());
int result = topQuery == null ? 0 : Parameter.getMaxIndex(topQuery.getParameters());
if (additionalParameters != null) {
result = Math.max(result, getMaxParameterIndex(additionalParameters));
result = Math.max(result, Parameter.getMaxIndex(additionalParameters));
}
return result + 1;
return result;
}

@Override
Expand Down
39 changes: 39 additions & 0 deletions h2/src/test/org/h2/test/db/TestRecursiveQueries.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void test() throws Exception {
testWrongLinkLargeResult();
testSimpleUnionAll();
testSimpleUnion();
testParameters();
}

private void testWrongLinkLargeResult() throws Exception {
Expand Down Expand Up @@ -180,4 +181,42 @@ private void testSimpleUnion() throws Exception {
deleteDb("recursiveQueries");
}

private void testParameters() throws Exception {
deleteDb("recursiveQueries");
Connection conn = getConnection("recursiveQueries");
PreparedStatement prep = conn.prepareStatement("WITH RECURSIVE T1(F1, F2) AS (\n" //
+ " SELECT CAST(? AS INT), CAST(? AS VARCHAR(15))\n" //
+ " UNION ALL\n" //
+ " SELECT (T1.F1 + CAST(? AS INT)), CAST((CAST(? AS VARCHAR) || T1.F2) AS VARCHAR(15))\n" //
+ " FROM T1 WHERE T1.F1 < 10\n" //
+ " ),\n" //
+ "T2(G1, G2) AS (\n" //
+ " SELECT CAST(? AS INT), CAST(? AS VARCHAR(15))\n" //
+ " UNION ALL\n" //
+ " SELECT (T2.G1 + 1), CAST(('b' || T2.G2) AS VARCHAR(15))\n" //
+ " FROM T2 WHERE T2.G1 < 10\n" //
+ " )\n" //
+ "SELECT T1.F1, T1.F2, T2.G1, T2.G2 FROM T1 JOIN T2 ON T1.F1 = T2.G1");
prep.setInt(1, 1);
prep.setString(2, "a");
prep.setInt(3, 1);
prep.setString(4, "a");
prep.setInt(5, 1);
prep.setString(6, "b");
ResultSet rs = prep.executeQuery();
StringBuilder a = new StringBuilder(10), b = new StringBuilder(10);
for (int i = 1; i <= 10; i++) {
a.append('a');
b.append('b');
assertTrue(rs.next());
assertEquals(i, rs.getInt(1));
assertEquals(a.toString(), rs.getString(2));
assertEquals(i, rs.getInt(3));
assertEquals(b.toString(), rs.getString(4));
}
assertFalse(rs.next());
conn.close();
deleteDb("recursiveQueries");
}

}