From 5ce3daf526435a21c3ae536e6d35c04e6f1b87df Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Tue, 18 Nov 2025 22:59:20 +0000 Subject: [PATCH] Remove deprecated `WITH CONTINUATION` syntax now that `EXECUTE CONTINUATION` syntax is self-contained. --- .../src/main/antlr/RelationalParser.g4 | 7 +- .../recordlayer/query/AstNormalizer.java | 8 - .../query/visitors/BaseVisitor.java | 6 - .../query/visitors/DelegatingVisitor.java | 6 - .../query/visitors/ExpressionVisitor.java | 6 - .../query/visitors/QueryVisitor.java | 8 - .../query/visitors/TypedVisitor.java | 4 - .../relational/recordlayer/CursorTest.java | 4 +- .../recordlayer/PlanGenerationStackTest.java | 4 +- .../SimpleDirectAccessInsertionTests.java | 11 +- .../recordlayer/query/AstNormalizerTests.java | 41 -- .../query/ExecutePropertyTests.java | 7 +- .../recordlayer/query/ExplainTests.java | 34 +- .../recordlayer/query/GroupByQueryTests.java | 17 +- .../query/PreparedStatementTests.java | 14 +- .../query/QueryWithContinuationTest.java | 425 ++++++------------ .../recordlayer/query/StandardQueryTests.java | 7 +- .../recordlayer/query/UpdateTest.java | 5 +- 18 files changed, 165 insertions(+), 449 deletions(-) diff --git a/fdb-relational-core/src/main/antlr/RelationalParser.g4 b/fdb-relational-core/src/main/antlr/RelationalParser.g4 index 3bbbd614b5..8460fdd8e4 100644 --- a/fdb-relational-core/src/main/antlr/RelationalParser.g4 +++ b/fdb-relational-core/src/main/antlr/RelationalParser.g4 @@ -341,7 +341,7 @@ selectStatement ; query - : ctes? queryExpressionBody continuation? + : ctes? queryExpressionBody ; ctes @@ -369,10 +369,6 @@ tableFunctionName : fullId ; -continuation - : WITH CONTINUATION continuationAtom - ; - // done queryExpressionBody : queryTerm #queryTermDefault // done @@ -402,7 +398,6 @@ updateStatement SET updatedElement (',' updatedElement)* (WHERE whereExpr)? (RETURNING selectElements)? - (WITH CONTINUATION continuationAtom)? queryOptions? ; diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java index 57f7c67233..e61db33914 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java @@ -271,17 +271,9 @@ public Object visitQuery(@Nonnull RelationalParser.QueryContext ctx) { visit(ctx.ctes()); } ctx.queryExpressionBody().accept(this); - if (ctx.continuation() != null) { - ctx.continuation().accept(this); - } return null; } - @Override - public Object visitContinuation(@Nonnull RelationalParser.ContinuationContext ctx) { - return ctx.continuationAtom().accept(this); - } - @Override public RelationalExpression visitQueryOptions(@Nonnull RelationalParser.QueryOptionsContext ctx) { for (final var opt : ctx.queryOption()) { diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/BaseVisitor.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/BaseVisitor.java index c318a443b3..ecf6590442 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/BaseVisitor.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/BaseVisitor.java @@ -624,12 +624,6 @@ public Identifier visitTableFunctionName(final RelationalParser.TableFunctionNam return identifierVisitor.visitTableFunctionName(ctx); } - @Nonnull - @Override - public Expression visitContinuation(RelationalParser.ContinuationContext ctx) { - return expressionVisitor.visitContinuation(ctx); - } - @Nonnull @Override public Expression visitContinuationAtom(@Nonnull RelationalParser.ContinuationAtomContext ctx) { diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/DelegatingVisitor.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/DelegatingVisitor.java index 4273b248ac..d82f3613e1 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/DelegatingVisitor.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/DelegatingVisitor.java @@ -476,12 +476,6 @@ public Identifier visitTableFunctionName(@Nonnull RelationalParser.TableFunction return getDelegate().visitTableFunctionName(ctx); } - @Nonnull - @Override - public Expression visitContinuation(@Nonnull RelationalParser.ContinuationContext ctx) { - return getDelegate().visitContinuation(ctx); - } - @Nonnull @Override public Expression visitContinuationAtom(@Nonnull RelationalParser.ContinuationAtomContext ctx) { diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java index 0abe0c59db..8178bd893a 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java @@ -121,12 +121,6 @@ public Expression visitNamedFunctionArg(@Nonnull final RelationalParser.NamedFun return expression.toNamedArgument(name); } - @Nonnull - @Override - public Expression visitContinuation(@Nonnull RelationalParser.ContinuationContext ctx) { - return visitContinuationAtom(ctx.continuationAtom()); - } - @Nonnull @Override public Expression visitContinuationAtom(@Nonnull RelationalParser.ContinuationAtomContext ctx) { diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/QueryVisitor.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/QueryVisitor.java index 4989946f6e..7d358b8c7e 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/QueryVisitor.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/QueryVisitor.java @@ -31,7 +31,6 @@ import com.apple.foundationdb.record.query.plan.cascades.predicates.CompatibleTypeEvolutionPredicate; import com.apple.foundationdb.record.query.plan.cascades.typing.Type; import com.apple.foundationdb.record.query.plan.cascades.values.FieldValue; -import com.apple.foundationdb.record.query.plan.cascades.values.LiteralValue; import com.apple.foundationdb.record.query.plan.cascades.values.Value; import com.apple.foundationdb.record.util.pair.NonnullPair; import com.apple.foundationdb.relational.api.exceptions.ErrorCode; @@ -58,7 +57,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Streams; -import com.google.protobuf.ByteString; import org.antlr.v4.runtime.ParserRuleContext; import javax.annotation.Nonnull; @@ -98,12 +96,6 @@ public QueryPlan.LogicalQueryPlan visitDmlStatement(@Nonnull RelationalParser.Dm @Nonnull @Override public LogicalOperator visitQuery(@Nonnull RelationalParser.QueryContext ctx) { - if (ctx.continuation() != null) { - final var continuationExpression = visitContinuation(ctx.continuation()); - final var continuationValue = Assert.castUnchecked(continuationExpression.getUnderlying(), LiteralValue.class); - final var continuationBytes = Assert.castUnchecked(continuationValue.getLiteralValue(), ByteString.class); - getDelegate().getPlanGenerationContext().setContinuation(continuationBytes.toByteArray()); - } if (ctx.ctes() != null) { final var currentPlanFragment = getDelegate().pushPlanFragment(); visitCtes(ctx.ctes()).forEach(currentPlanFragment::addOperator); diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/TypedVisitor.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/TypedVisitor.java index 92a0ca8db9..1730c86fef 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/TypedVisitor.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/TypedVisitor.java @@ -266,10 +266,6 @@ public interface TypedVisitor extends RelationalParserVisitor { @Override Identifier visitTableFunctionName(RelationalParser.TableFunctionNameContext ctx); - @Nonnull - @Override - Expression visitContinuation(RelationalParser.ContinuationContext ctx); - @Nonnull @Override Expression visitContinuationAtom(@Nonnull RelationalParser.ContinuationAtomContext ctx); diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/CursorTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/CursorTest.java index 33f44a70a0..b4968acdda 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/CursorTest.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/CursorTest.java @@ -176,7 +176,7 @@ public void continuationWithReturnRowLimit() throws SQLException, RelationalExce } catch (SQLException e) { throw new RuntimeException(e); } - try (final var preparedStatement = conn.prepareStatement("select * from RESTAURANT with continuation ?param")) { + try (final var preparedStatement = conn.prepareStatement("EXECUTE CONTINUATION ?param")) { preparedStatement.setBytes("param", continuation.serialize()); try (final var resultSet = preparedStatement.executeQuery()) { Assertions.assertThrows(SQLException.class, resultSet::getContinuation); @@ -218,7 +218,7 @@ public void continuationWithScanRowLimit() throws SQLException, RelationalExcept // 2. Further count the rows in other execution without limits and see if total number of rows is 10 try (final var conn = DriverManager.getConnection(database.getConnectionUri().toString()).unwrap(RelationalConnection.class)) { conn.setSchema(database.getSchemaName()); - try (final var preparedStatement = conn.prepareStatement("select * from RESTAURANT with continuation ?param")) { + try (final var preparedStatement = conn.prepareStatement("EXECUTE CONTINUATION ?param")) { preparedStatement.setBytes("param", continuation.serialize()); try (final var resultSet = preparedStatement.executeQuery()) { Assertions.assertThrows(SQLException.class, resultSet::getContinuation); diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/PlanGenerationStackTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/PlanGenerationStackTest.java index 6314eb76bb..d2cb126822 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/PlanGenerationStackTest.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/PlanGenerationStackTest.java @@ -143,11 +143,11 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(57, "select * from restaurant where (((42 + 3) - 2) + 6 is not null OR ((42 + 3) - 2) + 6 > rest_no) OR (name = 'foo')", null), Arguments.of(58, "select * from restaurant where rest_no is null", null), Arguments.of(59, "select * from restaurant where rest_no is not null", null), - Arguments.of(60, "select * from restaurant with continuation b64'abc'", null), + Arguments.of(60, "select * from restaurant with continuation b64'abc'", "syntax error"), Arguments.of(61, "select * from restaurant USE INDEX (record_name_idx) where rest_no > 10 ", null), Arguments.of(62, "select * from restaurant USE INDEX (record_name_idx, reviewer_name_idx) where rest_no > 10 ", "Unknown index(es) REVIEWER_NAME_IDX"), Arguments.of(63, "select * from restaurant USE INDEX (record_name_idx), USE INDEX (reviewer_name_idx) where rest_no > 10 ", "Unknown index(es) REVIEWER_NAME_IDX"), - Arguments.of(64, "select * from restaurant with continuation", "syntax error[[]]select * from restaurant with continuation[[]] ^^"), + Arguments.of(64, "select * from restaurant with continuation", "syntax error"), Arguments.of(65, "select X.rest_no from (select rest_no from restaurant where 42 >= rest_no OR 42 > rest_no) X", null), Arguments.of( 66, "select X.UNKNOWN from (select rest_no from restaurant where 42 >= rest_no OR 42 > rest_no) X", "Attempting to query non existing column 'X.UNKNOWN'"), Arguments.of(67, "select X.rest_no from (select Y.rest_no from (select rest_no from restaurant where 42 >= rest_no OR 42 > rest_no) Y where 42 >= Y.rest_no OR 42 > Y.rest_no) X", null), diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/SimpleDirectAccessInsertionTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/SimpleDirectAccessInsertionTests.java index e5478dd1ce..089acf0dca 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/SimpleDirectAccessInsertionTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/SimpleDirectAccessInsertionTests.java @@ -40,7 +40,6 @@ import java.sql.DriverManager; import java.sql.SQLException; -import java.util.Base64; import java.util.Collections; import java.util.List; @@ -70,8 +69,9 @@ void useScanContinuationInQueryShouldNotWork() throws Exception { rrs.next(); continuation1 = rrs.getContinuation(); } - String continuationString = Base64.getEncoder().encodeToString(continuation1.serialize()); - org.junit.jupiter.api.Assertions.assertThrows(ContextualSQLException.class, () -> s.executeQuery("SELECT * FROM RESTAURANT_REVIEWER LIMIT 1 WITH CONTINUATION B64'" + continuationString + "'"), "Continuation binding does not match query"); + // Try to use scan continuation with EXECUTE CONTINUATION - should fail + String continuationString = java.util.Base64.getEncoder().encodeToString(continuation1.serialize()); + org.junit.jupiter.api.Assertions.assertThrows(ContextualSQLException.class, () -> s.executeQuery("EXECUTE CONTINUATION B64'" + continuationString + "'"), "Continuation binding does not match query"); } // get @@ -81,8 +81,9 @@ void useScanContinuationInQueryShouldNotWork() throws Exception { rrs.next(); continuation1 = rrs.getContinuation(); } - String continuationString = Base64.getEncoder().encodeToString(continuation1.serialize()); - org.junit.jupiter.api.Assertions.assertThrows(ContextualSQLException.class, () -> s.executeQuery("SELECT * FROM RESTAURANT_REVIEWER LIMIT 1 WITH CONTINUATION B64'" + continuationString + "'"), "Continuation binding does not match query"); + // Try to use get continuation with EXECUTE CONTINUATION - should fail + String continuationString = java.util.Base64.getEncoder().encodeToString(continuation1.serialize()); + org.junit.jupiter.api.Assertions.assertThrows(ContextualSQLException.class, () -> s.executeQuery("EXECUTE CONTINUATION B64'" + continuationString + "'"), "Continuation binding does not match query"); } } } diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java index 4680349d60..da5b77b23e 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java @@ -521,26 +521,6 @@ void stripBase64Literal() throws RelationalException { Map.of(constantId(1), ByteString.copyFrom(Hex.decodeHex("cafe")))); } - @Test - void continuationIsStripped() throws Exception { - validate(List.of("select * from t1 with continuation b64'yv4='", - "select * from t1 with continuation x'cafe'", - "select * from t1"), - "select * from \"T1\" "); - } - - @Test - void parseContinuation() throws Exception { - final var expectedContinuationStr = "FBUCFA=="; - validate(List.of("select * from t1 with continuation b64'" + expectedContinuationStr + "'", - "select * from t1 with continuation b64'" + expectedContinuationStr + "'"), - PreparedParams.empty(), - "select * from \"T1\" ", - List.of(Map.of(), Map.of()), - expectedContinuationStr, - -1); - } - @Test void parseInPredicateAllConstants() throws Exception { // although these queries have different number of arguments in their in-predicate @@ -886,27 +866,6 @@ void stripBase64LiteralWithPreparedParameters() throws RelationalException { constantId(5), ByteString.copyFrom(Hex.decodeHex("0B0C")))); } - @Test - void parseContinuationWithPreparedParameters() throws Exception { - final var expectedContinuationStr = "FBUCFA=="; - final var expectedContinuation = Base64.getDecoder().decode(expectedContinuationStr); - validate(List.of("select * from t1 with continuation ?", - "select * from t1 with continuation ? "), - PreparedParams.ofUnnamed(Map.of(1, expectedContinuation)), - "select * from \"T1\" ", - List.of(Map.of(), Map.of()), - expectedContinuationStr, - -1); - - validate(List.of("select * from t1 with continuation ?param", - "select * from t1 with continuation ?param "), - PreparedParams.ofNamed(Map.of("param", expectedContinuation)), - "select * from \"T1\" ", - List.of(Map.of(), Map.of()), - expectedContinuationStr, - -1); - } - @Test void parseInPredicateAllConstantsWithPreparedParameters() throws Exception { // although these queries have different number of arguments in their in-predicate diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExecutePropertyTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExecutePropertyTests.java index a3a531d8b1..195993ef93 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExecutePropertyTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExecutePropertyTests.java @@ -103,8 +103,11 @@ void hitLimitEveryRow(Options.Name optionName, Object optionValue, int expectedR try (var conn = driver.connect(database.getConnectionUri(), Options.builder().withOption(optionName, optionValue).build())) { conn.setSchema("TEST_SCHEMA"); while (!continuation.atEnd()) { - try (var ps = conn.prepareStatement("SELECT * FROM FOO WITH CONTINUATION ?")) { - ps.setBytes(1, continuation.serialize()); + String query = continuation.atBeginning() ? "SELECT * FROM FOO" : "EXECUTE CONTINUATION ?"; + try (var ps = conn.prepareStatement(query)) { + if (!continuation.atBeginning()) { + ps.setBytes(1, continuation.serialize()); + } try (final RelationalResultSet rs = ps.executeQuery()) { for (int currentRowCount = 0; currentRowCount < expectedRowCountPerQuery; currentRowCount++) { if (nextCorrectResult == 17L) { diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExplainTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExplainTests.java index 69adc7b138..4e4df8120f 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExplainTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/ExplainTests.java @@ -125,7 +125,7 @@ void explainWithContinuationSerializedPlanTest() throws Exception { continuation = consumeResultAndGetContinuation(ps, 2); } - try (RelationalPreparedStatement ps = connection.prepareStatement("EXPLAIN SELECT * FROM RestaurantComplexRecord WITH CONTINUATION ?cont")) { + try (RelationalPreparedStatement ps = connection.prepareStatement("EXPLAIN EXECUTE CONTINUATION ?cont")) { ps.setObject("cont", continuation.serialize()); try (final RelationalResultSet resultSet = ps.executeQuery()) { final var assertResult = ResultSetAssert.assertThat(resultSet); @@ -146,38 +146,6 @@ void explainWithContinuationSerializedPlanTest() throws Exception { } } - @Test - void explainWithContinuationSerializedPlanWithDifferentQueryTest() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (final var connection = ddl.setSchemaAndGetConnection()) { - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord")) { - ps.setMaxRows(2); - continuation = consumeResultAndGetContinuation(ps, 2); - } - - try (RelationalPreparedStatement ps = connection.prepareStatement("EXPLAIN SELECT rest_no FROM RestaurantComplexRecord WITH CONTINUATION ?cont")) { - ps.setObject("cont", continuation.serialize()); - try (final RelationalResultSet resultSet = ps.executeQuery()) { - final var assertResult = ResultSetAssert.assertThat(resultSet); - assertResult.hasNextRow() - .hasColumn("PLAN", "COVERING(RECORD_NAME_IDX <,> -> [NAME: KEY[0], REST_NO: KEY[2]]) | MAP (_.REST_NO AS REST_NO)") - .hasColumn("PLAN_HASH", 4759756); - final var continuationInfo = resultSet.getStruct(5); - org.junit.jupiter.api.Assertions.assertNotNull(continuationInfo); - final var assertStruct = RelationalStructAssert.assertThat(continuationInfo); - assertStruct.hasValue("EXECUTION_STATE", new byte[]{10, 5, 0, 21, 1, 21, 11, 17, -84, -51, 115, -104, -35, 66, 0, 94}); - assertStruct.hasValue("VERSION", 1); - assertStruct.hasValue("PLAN_HASH_MODE", "VC0"); - assertStruct.hasValue("PLAN_HASH", -1635569052); - assertStruct.hasValue("SERIALIZED_PLAN_COMPLEXITY", 1); - } - } - } - } - } - @Test void explainExecuteStatementTest() throws Exception { try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/GroupByQueryTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/GroupByQueryTests.java index bbaef024ce..2cd1e84930 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/GroupByQueryTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/GroupByQueryTests.java @@ -35,7 +35,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import java.net.URI; -import java.util.Base64; import static com.apple.foundationdb.relational.recordlayer.query.QueryTestUtils.insertT1Record; import static com.apple.foundationdb.relational.recordlayer.query.QueryTestUtils.insertT1RecordColAIsNull; @@ -149,13 +148,15 @@ void groupByWithRowLimit() throws Exception { .hasNoNextRow(); continuation = resultSet.getContinuation(); } - String postfix = " WITH CONTINUATION B64'" + Base64.getEncoder().encodeToString(continuation.serialize()) + "'"; - Assertions.assertTrue(statement.execute(query + postfix), "Did not return a result set from a select statement!"); - try (final RelationalResultSet resultSet = statement.getResultSet()) { - ResultSetAssert.assertThat(resultSet).hasNextRow() - .isRowExactly(9.5) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); + try (var ps = conn.prepareStatement("EXECUTE CONTINUATION ?continuation")) { + ps.setBytes("continuation", continuation.serialize()); + Assertions.assertTrue(ps.execute(), "Did not return a result set from a select statement!"); + try (final RelationalResultSet resultSet = ps.getResultSet()) { + ResultSetAssert.assertThat(resultSet).hasNextRow() + .isRowExactly(9.5) + .hasNoNextRow(); + continuation = resultSet.getContinuation(); + } } } } diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/PreparedStatementTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/PreparedStatementTests.java index 657f586dc9..6487a2afc9 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/PreparedStatementTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/PreparedStatementTests.java @@ -345,7 +345,8 @@ void continuation() throws Exception { statement.execute("INSERT INTO RestaurantComplexRecord(rest_no) VALUES (10), (11), (12), (13), (14)"); } Continuation continuation; - try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord")) { + final var connection = ddl.setSchemaAndGetConnection(); + try (var ps = connection.prepareStatement("SELECT * FROM RestaurantComplexRecord")) { ps.setMaxRows(2); try (final RelationalResultSet resultSet = ps.executeQuery()) { ResultSetAssert.assertThat(resultSet) @@ -355,7 +356,7 @@ void continuation() throws Exception { continuation = resultSet.getContinuation(); } } - try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WITH CONTINUATION ?continuation")) { + try (var ps = connection.prepareStatement("EXECUTE CONTINUATION ?continuation")) { ps.setMaxRows(2); ps.setBytes("continuation", continuation.serialize()); try (final RelationalResultSet resultSet = ps.executeQuery()) { @@ -374,7 +375,7 @@ void continuation() throws Exception { } // Same but with logs - try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord OPTIONS(LOG QUERY)")) { + try (var ps = connection.prepareStatement("SELECT * FROM RestaurantComplexRecord OPTIONS(LOG QUERY)")) { ps.setMaxRows(2); try (final RelationalResultSet resultSet = ps.executeQuery()) { ResultSetAssert.assertThat(resultSet) @@ -385,7 +386,7 @@ void continuation() throws Exception { } } Assertions.assertThat(logAppender.getLastLogEventMessage()).contains("planCache=\"hit\""); - try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord OPTIONS(LOG QUERY) WITH CONTINUATION ?continuation")) { + try (var ps = connection.prepareStatement("EXECUTE CONTINUATION ?continuation OPTIONS(LOG QUERY)")) { ps.setMaxRows(2); ps.setBytes("continuation", continuation.serialize()); try (final RelationalResultSet resultSet = ps.executeQuery()) { @@ -395,14 +396,15 @@ void continuation() throws Exception { .hasNoNextRow(); continuation = resultSet.getContinuation(); } - Assertions.assertThat(logAppender.getLastLogEventMessage()).contains("planCache=\"hit\""); + // With EXECUTE CONTINUATION, the plan is embedded in the continuation, so cache is skipped + Assertions.assertThat(logAppender.getLastLogEventMessage()).contains("planCache=\"skip\""); ps.setBytes("continuation", continuation.serialize()); try (final RelationalResultSet resultSet = ps.executeQuery()) { ResultSetAssert.assertThat(resultSet) .hasNextRow().hasColumn("REST_NO", 14L) .hasNoNextRow(); } - Assertions.assertThat(logAppender.getLastLogEventMessage()).contains("planCache=\"hit\""); + Assertions.assertThat(logAppender.getLastLogEventMessage()).contains("planCache=\"skip\""); } } } diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/QueryWithContinuationTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/QueryWithContinuationTest.java index 2e5a0f972a..53f75a069d 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/QueryWithContinuationTest.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/QueryWithContinuationTest.java @@ -27,7 +27,6 @@ import com.apple.foundationdb.relational.api.RelationalPreparedStatement; import com.apple.foundationdb.relational.api.RelationalResultSet; import com.apple.foundationdb.relational.api.RelationalStatement; -import com.apple.foundationdb.relational.api.exceptions.RelationalException; import com.apple.foundationdb.relational.api.metrics.RelationalMetric; import com.apple.foundationdb.relational.continuation.ContinuationProto; import com.apple.foundationdb.relational.recordlayer.ContinuationImpl; @@ -44,7 +43,6 @@ import java.net.URI; import java.sql.SQLException; -import java.util.Base64; import java.util.Objects; public class QueryWithContinuationTest { @@ -80,7 +78,7 @@ void preparedStatement() throws Exception { continuation = assertResult(ps, 10L, 11L); assertContinuation(continuation, false, false); } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WITH CONTINUATION ?continuation")) { + try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("EXECUTE CONTINUATION ?continuation")) { ps.setMaxRows(2); ps.setBytes("continuation", continuation.serialize()); continuation = assertResult(ps, 12L, 13L); @@ -128,7 +126,7 @@ void preparedStatementWithLimit() throws Exception { continuation = assertResult(ps, 10L, 11L); assertContinuation(continuation, false, false); } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WITH CONTINUATION ?continuation")) { + try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("EXECUTE CONTINUATION ?continuation")) { ps.setBytes("continuation", continuation.serialize()); ps.setMaxRows(2); continuation = assertResult(ps, 12L, 13L); @@ -146,22 +144,23 @@ void preparedStatementWithParam() throws Exception { try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { executeInsert(ddl); Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p")) { - ps.setMaxRows(2); - ps.setInt("p", 9); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p WITH CONTINUATION ?continuation")) { - ps.setBytes("continuation", continuation.serialize()); - ps.setMaxRows(2); - ps.setInt("p", 9); - continuation = assertResult(ps, 12L, 13L); - assertContinuation(continuation, false, false); + try (final var connection = ddl.setSchemaAndGetConnection()) { + try (var statement = connection.prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p")) { + statement.setMaxRows(2); + statement.setInt("p", 9); + continuation = assertResult(statement, 10L, 11L); + assertContinuation(continuation, false, false); + } + try (var statement = connection.prepareStatement("EXECUTE CONTINUATION ?continuation")) { + statement.setBytes("continuation", continuation.serialize()); + statement.setMaxRows(2); + continuation = assertResult(statement, 12L, 13L); + assertContinuation(continuation, false, false); - ps.setBytes("continuation", continuation.serialize()); - continuation = assertResult(ps, 14L); - assertContinuation(continuation, false, true); + statement.setBytes("continuation", continuation.serialize()); + continuation = assertResult(statement, 14L); + assertContinuation(continuation, false, true); + } } } } @@ -171,21 +170,22 @@ void preparedStatementWithLiteral() throws Exception { try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { executeInsert(ddl); Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { - ps.setMaxRows(2); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9 WITH CONTINUATION ?continuation")) { - ps.setMaxRows(2); - ps.setBytes("continuation", continuation.serialize()); - ps.setInt("l", 2); - continuation = assertResult(ps, 12L, 13L); - assertContinuation(continuation, false, false); + try (final var connection = ddl.setSchemaAndGetConnection()) { + try (var statement = connection.prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { + statement.setMaxRows(2); + continuation = assertResult(statement, 10L, 11L); + assertContinuation(continuation, false, false); + } + try (var statement = connection.prepareStatement("EXECUTE CONTINUATION ?continuation")) { + statement.setMaxRows(2); + statement.setBytes("continuation", continuation.serialize()); + continuation = assertResult(statement, 12L, 13L); + assertContinuation(continuation, false, false); - ps.setBytes("continuation", continuation.serialize()); - continuation = assertResult(ps, 14L); - assertContinuation(continuation, false, true); + statement.setBytes("continuation", continuation.serialize()); + continuation = assertResult(statement, 14L); + assertContinuation(continuation, false, true); + } } } } @@ -195,16 +195,18 @@ void preparedStatementWithDifferentLimit() throws Exception { try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { executeInsert(ddl); Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { - ps.setMaxRows(2); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9 WITH CONTINUATION ?continuation")) { - ps.setMaxRows(4); - ps.setBytes("continuation", continuation.serialize()); - continuation = assertResult(ps, 12L, 13L, 14L); - assertContinuation(continuation, false, true); + try (final var connection = ddl.setSchemaAndGetConnection()) { + try (var statement = connection.prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { + statement.setMaxRows(2); + continuation = assertResult(statement, 10L, 11L); + assertContinuation(continuation, false, false); + } + try (var statement = connection.prepareStatement("EXECUTE CONTINUATION ?continuation")) { + statement.setMaxRows(4); + statement.setBytes("continuation", continuation.serialize()); + continuation = assertResult(statement, 12L, 13L, 14L); + assertContinuation(continuation, false, true); + } } } } @@ -214,146 +216,18 @@ void preparedStatementWithDifferentLimitParam() throws Exception { try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { executeInsert(ddl); Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { - ps.setMaxRows(2); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9 WITH CONTINUATION ?continuation")) { - ps.setBytes("continuation", continuation.serialize()); - ps.setMaxRows(4); - continuation = assertResult(ps, 12L, 13L, 14L); - assertContinuation(continuation, false, true); - } - } - } - - @Test - void preparedStatementInitialContEmpty() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p WITH CONTINUATION ?continuation")) { - ps.setMaxRows(2); - ps.setBytes("continuation", new byte[0]); - ps.setInt("p", 9); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - - ps.setBytes("continuation", continuation.serialize()); - continuation = assertResult(ps, 12L, 13L); - assertContinuation(continuation, false, false); - - ps.setBytes("continuation", continuation.serialize()); - continuation = assertResult(ps, 14L); - assertContinuation(continuation, false, true); - } - } - } - - @Test - void preparedStatementWithParamChangedFails() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p")) { - ps.setMaxRows(2); - ps.setInt("p", 9); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p WITH CONTINUATION ?continuation")) { - ps.setMaxRows(2); - ps.setBytes("continuation", continuation.serialize()); - ps.setInt("p", 10); - Assertions.assertThatThrownBy(ps::executeQuery) - .hasCauseInstanceOf(RelationalException.class) - .hasMessageContaining("Continuation binding does not match query"); - } - } - } - - @Test - void preparedStatementWithLiteralChangedFails() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { - ps.setMaxRows(2); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 10 WITH CONTINUATION ?continuation")) { - ps.setMaxRows(2); - ps.setBytes("continuation", continuation.serialize()); - Assertions.assertThatThrownBy(ps::executeQuery) - .hasCauseInstanceOf(RelationalException.class) - .hasMessageContaining("Continuation binding does not match query"); - } - } - } - - @Test - void preparedStatementWithLiteralChangedToParamFails() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { - ps.setMaxRows(2); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p WITH CONTINUATION ?continuation")) { - ps.setMaxRows(2); - ps.setBytes("continuation", continuation.serialize()); - ps.setInt("p", 9); - Assertions.assertThatThrownBy(ps::executeQuery) - .hasCauseInstanceOf(RelationalException.class) - .hasMessageContaining("Continuation binding does not match query"); - } - } - } - - @Test - void preparedStatementWithParamNameChangedFails() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p")) { - ps.setMaxRows(2); - ps.setInt("p", 9); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?otherName WITH CONTINUATION ?continuation")) { - ps.setMaxRows(2); - ps.setBytes("continuation", continuation.serialize()); - ps.setInt("otherName", 9); - Assertions.assertThatThrownBy(() -> ps.executeQuery()) - .hasCauseInstanceOf(RelationalException.class) - .hasMessageContaining("Continuation binding does not match query"); - } - } - } - - @Test - void preparedStatementWithPlanChangedFails() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > ?p")) { - ps.setMaxRows(2); - ps.setInt("p", 9); - continuation = assertResult(ps, 10L, 11L); - assertContinuation(continuation, false, false); - } - try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("SELECT REST_NO FROM RestaurantComplexRecord WHERE REST_NO > ?p WITH CONTINUATION ?continuation")) { - ps.setMaxRows(2); - ps.setBytes("continuation", continuation.serialize()); - ps.setInt("p", 9); - Assertions.assertThatThrownBy(() -> ps.executeQuery()) - .hasCauseInstanceOf(RelationalException.class) - .hasMessageContaining("Continuation plan does not match query"); + try (final var connection = ddl.setSchemaAndGetConnection()) { + try (var statement = connection.prepareStatement("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { + statement.setMaxRows(2); + continuation = assertResult(statement, 10L, 11L); + assertContinuation(continuation, false, false); + } + try (var statement = connection.prepareStatement("EXECUTE CONTINUATION ?continuation")) { + statement.setBytes("continuation", continuation.serialize()); + statement.setMaxRows(4); + continuation = assertResult(statement, 12L, 13L, 14L); + assertContinuation(continuation, false, true); + } } } } @@ -363,36 +237,39 @@ void standardStatement() throws Exception { try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { executeInsert(ddl); Continuation continuation; - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 10L) - .hasNextRow().hasColumn("REST_NO", 11L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, false); - } - } - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - String continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WITH CONTINUATION B64'" + continuationString + "'")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 12L) - .hasNextRow().hasColumn("REST_NO", 13L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, false); + try (final var connection = ddl.setSchemaAndGetConnection()) { + try (RelationalStatement statement = connection.createStatement()) { + statement.setMaxRows(2); + try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord")) { + ResultSetAssert.assertThat(resultSet) + .hasNextRow().hasColumn("REST_NO", 10L) + .hasNextRow().hasColumn("REST_NO", 11L) + .hasNoNextRow(); + continuation = resultSet.getContinuation(); + assertContinuation(continuation, false, false); + } } - continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WITH CONTINUATION B64'" + continuationString + "'")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 14L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, true); + try (var statement = connection.prepareStatement("EXECUTE CONTINUATION ?continuation")) { + statement.setMaxRows(2); + statement.setBytes("continuation", continuation.serialize()); + try (final RelationalResultSet resultSet = statement.executeQuery()) { + ResultSetAssert.assertThat(resultSet) + .hasNextRow().hasColumn("REST_NO", 12L) + .hasNextRow().hasColumn("REST_NO", 13L) + .hasNoNextRow(); + continuation = resultSet.getContinuation(); + assertContinuation(continuation, false, false); + } + + statement.setBytes("continuation", continuation.serialize()); + statement.setMaxRows(2); + try (final RelationalResultSet resultSet = statement.executeQuery()) { + ResultSetAssert.assertThat(resultSet) + .hasNextRow().hasColumn("REST_NO", 14L) + .hasNoNextRow(); + continuation = resultSet.getContinuation(); + assertContinuation(continuation, false, true); + } } } } @@ -427,9 +304,9 @@ void standardStatementWithDifferentPlanHashModes() throws Exception { PlanHashable.PlanHashMode.VL0.name() + "," + PlanHashable.PlanHashMode.VC0.name()); connection.setOption(Options.Name.CURRENT_PLAN_HASH_MODE, PlanHashable.PlanHashMode.VC0.name()); try (RelationalStatement statement = connection.createStatement()) { - String continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); + String continuationString = java.util.Base64.getEncoder().encodeToString(continuation.serialize()); statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WITH CONTINUATION B64'" + continuationString + "'")) { + try (final RelationalResultSet resultSet = statement.executeQuery("EXECUTE CONTINUATION B64'" + continuationString + "'")) { ResultSetAssert.assertThat(resultSet) .hasNextRow().hasColumn("REST_NO", 12L) .hasNextRow().hasColumn("REST_NO", 13L) @@ -442,16 +319,16 @@ void standardStatementWithDifferentPlanHashModes() throws Exception { Assertions.assertThat(metricCollector.hasCounter(RelationalMetric.RelationalCount.CONTINUATION_ACCEPTED)).isTrue(); Assertions.assertThat(metricCollector.getCountsForCounter(RelationalMetric.RelationalCount.CONTINUATION_ACCEPTED)).isEqualTo(1L); Assertions.assertThat(metricCollector.hasCounter(RelationalMetric.RelationalCount.CONTINUATION_DOWN_LEVEL)).isTrue(); - Assertions.assertThat(metricCollector.getCountsForCounter(RelationalMetric.RelationalCount.CONTINUATION_DOWN_LEVEL)).isEqualTo(2L); + Assertions.assertThat(metricCollector.getCountsForCounter(RelationalMetric.RelationalCount.CONTINUATION_DOWN_LEVEL)).isEqualTo(1L); } } connection.setOption(Options.Name.VALID_PLAN_HASH_MODES, PlanHashable.PlanHashMode.VC0.name()); connection.setOption(Options.Name.CURRENT_PLAN_HASH_MODE, PlanHashable.PlanHashMode.VC0.name()); try (RelationalStatement statement = connection.createStatement()) { - String continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); + String continuationString = java.util.Base64.getEncoder().encodeToString(continuation.serialize()); statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WITH CONTINUATION B64'" + continuationString + "'")) { + try (final RelationalResultSet resultSet = statement.executeQuery("EXECUTE CONTINUATION B64'" + continuationString + "'")) { ResultSetAssert.assertThat(resultSet) .hasNextRow().hasColumn("REST_NO", 14L) .hasNoNextRow(); @@ -462,7 +339,7 @@ void standardStatementWithDifferentPlanHashModes() throws Exception { final var metricCollector = Objects.requireNonNull(embeddedRelationalConnection.getMetricCollector()); Assertions.assertThat(metricCollector.hasCounter(RelationalMetric.RelationalCount.CONTINUATION_ACCEPTED)).isTrue(); Assertions.assertThat(metricCollector.getCountsForCounter(RelationalMetric.RelationalCount.CONTINUATION_ACCEPTED)).isEqualTo(1L); - Assertions.assertThat(metricCollector.hasCounter(RelationalMetric.RelationalCount.CONTINUATION_DOWN_LEVEL)).isTrue(); + // No down-level in this execution (continuation was created with VC0, executed with VC0) } } } @@ -474,90 +351,40 @@ void standardStatementWithLiterals() throws Exception { try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { executeInsert(ddl); Continuation continuation; - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 10L) - .hasNextRow().hasColumn("REST_NO", 11L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, false); - } - } - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - String continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9 WITH CONTINUATION B64'" + continuationString + "'")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 12L) - .hasNextRow().hasColumn("REST_NO", 13L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, false); - } - continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9 WITH CONTINUATION B64'" + continuationString + "'")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 14L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, true); - } - } - } - } - - @Test - void standardStatementWithDifferentLiteralFails() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 10L) - .hasNextRow().hasColumn("REST_NO", 11L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, false); + try (final var connection = ddl.setSchemaAndGetConnection()) { + try (RelationalStatement statement = connection.createStatement()) { + statement.setMaxRows(2); + try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord WHERE REST_NO > 9")) { + ResultSetAssert.assertThat(resultSet) + .hasNextRow().hasColumn("REST_NO", 10L) + .hasNextRow().hasColumn("REST_NO", 11L) + .hasNoNextRow(); + continuation = resultSet.getContinuation(); + assertContinuation(continuation, false, false); + } } - } - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - String continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); - statement.setMaxRows(2); - Assertions.assertThatThrownBy(() -> statement.executeQuery("SELECT REST_NO FROM RestaurantComplexRecord WHERE REST_NO > 10 WITH CONTINUATION B64'" + continuationString + "'")) - .hasCauseInstanceOf(RelationalException.class) - .hasMessageContaining("Continuation binding does not match query"); - } - } - } - - @Test - void standardStatementWithDifferentPlanFails() throws Exception { - try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) { - executeInsert(ddl); - Continuation continuation; - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - statement.setMaxRows(2); - try (final RelationalResultSet resultSet = statement.executeQuery("SELECT * FROM RestaurantComplexRecord")) { - ResultSetAssert.assertThat(resultSet) - .hasNextRow().hasColumn("REST_NO", 10L) - .hasNextRow().hasColumn("REST_NO", 11L) - .hasNoNextRow(); - continuation = resultSet.getContinuation(); - assertContinuation(continuation, false, false); + try (var statement = connection.prepareStatement("EXECUTE CONTINUATION ?continuation")) { + statement.setMaxRows(2); + statement.setBytes("continuation", continuation.serialize()); + try (final RelationalResultSet resultSet = statement.executeQuery()) { + ResultSetAssert.assertThat(resultSet) + .hasNextRow().hasColumn("REST_NO", 12L) + .hasNextRow().hasColumn("REST_NO", 13L) + .hasNoNextRow(); + continuation = resultSet.getContinuation(); + assertContinuation(continuation, false, false); + } + statement.setMaxRows(2); + statement.setBytes("continuation", continuation.serialize()); + try (final RelationalResultSet resultSet = statement.executeQuery()) { + ResultSetAssert.assertThat(resultSet) + .hasNextRow().hasColumn("REST_NO", 14L) + .hasNoNextRow(); + continuation = resultSet.getContinuation(); + assertContinuation(continuation, false, true); + } } } - try (RelationalStatement statement = ddl.setSchemaAndGetConnection().createStatement()) { - String continuationString = Base64.getEncoder().encodeToString(continuation.serialize()); - statement.setMaxRows(2); - Assertions.assertThatThrownBy(() -> statement.executeQuery("SELECT REST_NO FROM RestaurantComplexRecord WITH CONTINUATION B64'" + continuationString + "'")) - .hasCauseInstanceOf(RelationalException.class) - .hasMessageContaining("Continuation plan does not match query"); - } } } diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StandardQueryTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StandardQueryTests.java index 8e3a61cc03..615bb8fb55 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StandardQueryTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StandardQueryTests.java @@ -385,15 +385,14 @@ void selectWithContinuation() throws Exception { RelationalStruct l43 = insertRestaurantComplexRecord(statement, 43L, "rest1"); RelationalStruct l44 = insertRestaurantComplexRecord(statement, 44L, "rest1"); RelationalStruct l45 = insertRestaurantComplexRecord(statement, 45L, "rest2"); - final String initialQuery = "select * from RestaurantComplexRecord where rest_no > 40"; + String query = "select * from RestaurantComplexRecord where rest_no > 40"; Continuation continuation = ContinuationImpl.BEGIN; final List expected = List.of(l42, l43, l44, l45); int i = 0; while (!continuation.atEnd()) { - String query = initialQuery; if (!continuation.atBeginning()) { - query += " WITH CONTINUATION B64'" + Base64.getEncoder().encodeToString(continuation.serialize()) + "'"; + query = "EXECUTE CONTINUATION B64'" + Base64.getEncoder().encodeToString(continuation.serialize()) + "'"; } try (final RelationalResultSet resultSet = statement.executeQuery(query)) { // assert result matches expected @@ -424,7 +423,7 @@ void selectWithContinuationBeginEndShouldFail() throws Exception { .hasErrorCode(ErrorCode.SYNTAX_ERROR); final String end = "select * from RestaurantComplexRecord where rest_no > 40 with continuation b64''"; RelationalAssertions.assertThrowsSqlException(() -> statement.executeQuery(end)) - .hasErrorCode(ErrorCode.INVALID_CONTINUATION); + .hasErrorCode(ErrorCode.SYNTAX_ERROR); } } } diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/UpdateTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/UpdateTest.java index b6fa92d4fc..a5c11bbd3a 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/UpdateTest.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/UpdateTest.java @@ -179,10 +179,9 @@ public void insertRecords(int numRecords) throws RelationalException, SQLExcepti } private static RelationalPreparedStatement prepareUpdate(RelationalConnection conn, String updateField, Object param, Continuation continuation) throws SQLException { - if (continuation.atBeginning() || !((ContinuationImpl) continuation).hasCompiledStatement()) { - final var statement = conn.prepareStatement("UPDATE RestaurantReviewer SET " + updateField + " = ?param WHERE id >= 0 RETURNING \"new\"." + updateField + ", \"new\".id WITH CONTINUATION ?cont"); + if (continuation.atBeginning()) { + final var statement = conn.prepareStatement("UPDATE RestaurantReviewer SET " + updateField + " = ?param WHERE id >= 0 RETURNING \"new\"." + updateField + ", \"new\".id"); statement.setObject("param", param); - statement.setObject("cont", continuation.serialize()); return statement; } else { final var statement = conn.prepareStatement("EXECUTE CONTINUATION ?cont");