diff --git a/h2/src/docsrc/html/changelog.html b/h2/src/docsrc/html/changelog.html
index ae2c8dc629..4980d4c15a 100644
--- a/h2/src/docsrc/html/changelog.html
+++ b/h2/src/docsrc/html/changelog.html
@@ -21,6 +21,10 @@
Change Log
Next Version (unreleased)
+- Issue #3554: Regression in 2.1.214 when joining 2 recursive CTE containing bind values
+
+- PR #3989: Refactor CTE-related code and reduce recompilations
+
- Issue #3987: Allow empty <with column list>
- Issue #822: WITH clauses' column aliases are not maintained correctly when selecting from CTE from within a derived
diff --git a/h2/src/main/org/h2/command/ParserBase.java b/h2/src/main/org/h2/command/ParserBase.java
index c9288f43b6..f92c9afd41 100644
--- a/h2/src/main/org/h2/command/ParserBase.java
+++ b/h2/src/main/org/h2/command/ParserBase.java
@@ -217,7 +217,19 @@ public final void setLiteralsChecked(boolean literalsChecked) {
}
public final void setSuppliedParameters(ArrayList suppliedParameters) {
- this.parameters = suppliedParameters;
+ int max = Parameter.getMaxIndex(suppliedParameters);
+ if (max > suppliedParameters.size()) {
+ ArrayList 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;
+ }
}
/**
diff --git a/h2/src/main/org/h2/expression/Parameter.java b/h2/src/main/org/h2/expression/Parameter.java
index 0626255946..294d172fb2 100644
--- a/h2/src/main/org/h2/expression/Parameter.java
+++ b/h2/src/main/org/h2/expression/Parameter.java
@@ -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;
@@ -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 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;
diff --git a/h2/src/main/org/h2/table/QueryExpressionTable.java b/h2/src/main/org/h2/table/QueryExpressionTable.java
index 8c93e6be9a..0f2ccc8145 100644
--- a/h2/src/main/org/h2/table/QueryExpressionTable.java
+++ b/h2/src/main/org/h2/table/QueryExpressionTable.java
@@ -105,16 +105,6 @@ public static List createQueryColumnTemplateList(String[] cols, Query th
return columnTemplateList;
}
- static int getMaxParameterIndex(ArrayList parameters) {
- int result = -1;
- for (Parameter p : parameters) {
- if (p != null) {
- result = Math.max(result, p.getIndex());
- }
- }
- return result;
- }
-
Query viewQuery;
ArrayList
tables;
@@ -249,11 +239,11 @@ public final long getRowCountApproximation(SessionLocal session) {
*/
public final int getParameterOffset(ArrayList 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
diff --git a/h2/src/test/org/h2/test/db/TestRecursiveQueries.java b/h2/src/test/org/h2/test/db/TestRecursiveQueries.java
index 27f2db4ac3..5ffc62b542 100644
--- a/h2/src/test/org/h2/test/db/TestRecursiveQueries.java
+++ b/h2/src/test/org/h2/test/db/TestRecursiveQueries.java
@@ -32,6 +32,7 @@ public void test() throws Exception {
testWrongLinkLargeResult();
testSimpleUnionAll();
testSimpleUnion();
+ testParameters();
}
private void testWrongLinkLargeResult() throws Exception {
@@ -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");
+ }
+
}