diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000000..bd98446bc0 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +fdb-record-layer \ No newline at end of file diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/expressions/UpdateExpression.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/expressions/UpdateExpression.java index 5202d2ff0f..c0a1e7843b 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/expressions/UpdateExpression.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/expressions/UpdateExpression.java @@ -58,8 +58,8 @@ */ public class UpdateExpression extends AbstractRelationalExpressionWithChildren implements PlannerGraphRewritable { - private static final String OLD_FIELD_NAME = "old"; - private static final String NEW_FIELD_NAME = "new"; + private static final String OLD_FIELD_NAME = "OLD"; + private static final String NEW_FIELD_NAME = "NEW"; @Nonnull private final Quantifier.ForEach inner; diff --git a/fdb-relational-core/src/main/antlr/RelationalLexer.g4 b/fdb-relational-core/src/main/antlr/RelationalLexer.g4 index 77a34e26f3..a8392ff3ac 100644 --- a/fdb-relational-core/src/main/antlr/RelationalLexer.g4 +++ b/fdb-relational-core/src/main/antlr/RelationalLexer.g4 @@ -583,6 +583,7 @@ NAME: 'NAME'; NAMES: 'NAMES'; NCHAR: 'NCHAR'; NEVER: 'NEVER'; +NEW: 'NEW'; NEXT: 'NEXT'; NO: 'NO'; NOCOPY: 'NOCOPY'; @@ -596,6 +597,7 @@ OFFLINE: 'OFFLINE'; OFFSET: 'OFFSET'; OF: 'OF'; OJ: 'OJ'; +OLD: 'OLD'; OLD_PASSWORD: 'OLD_PASSWORD'; ONE: 'ONE'; ONLINE: 'ONLINE'; diff --git a/fdb-relational-core/src/main/antlr/RelationalParser.g4 b/fdb-relational-core/src/main/antlr/RelationalParser.g4 index 8460fdd8e4..067ad23573 100644 --- a/fdb-relational-core/src/main/antlr/RelationalParser.g4 +++ b/fdb-relational-core/src/main/antlr/RelationalParser.g4 @@ -1252,8 +1252,8 @@ keywordsCanBeId | MAX_USER_CONNECTIONS | MEDIUM | MEMBER | MEMORY | MERGE | MESSAGE | MESSAGE_TEXT | MID | MIGRATE | MIN | MIN_ROWS | MODE | MODIFY | MUTEX | MYSQL | MYSQL_ERRNO | NAME | NAMES - | NCHAR | NDB_STORED_USER | NEVER | NEXT | NO | NOCOPY | NODEGROUP | NOCACHE | NONE | NOWAIT | NUMBER | ODBC | OFFLINE | OFFSET - | OF | OJ | OLD_PASSWORD | ONE | ONLINE | ONLY | OPEN | OPTIMIZER_COSTS + | NCHAR | NDB_STORED_USER | NEVER | NEW | NEXT | NO | NOCOPY | NODEGROUP | NOCACHE | NONE | NOWAIT | NUMBER | ODBC | OFFLINE | OFFSET + | OF | OJ | OLD | OLD_PASSWORD | ONE | ONLINE | ONLY | OPEN | OPTIMIZER_COSTS | OPTIONAL | OPTIONS | ORDER | OWNER | PACK_KEYS | PAGE | PARSER | PARTIAL | PARTITIONING | PARTITIONS | PASSWORD | PERSIST_RO_VARIABLES_ADMIN | PHASE | PLUGINS | PLUGIN_DIR | PLUGIN | PORT | PRECEDES | PREPARE | PRESERVE | PREV diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/LogicalPlanFragment.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/LogicalPlanFragment.java index 1975007f03..c9dea866a2 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/LogicalPlanFragment.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/LogicalPlanFragment.java @@ -23,6 +23,7 @@ import com.apple.foundationdb.annotation.API; import com.apple.foundationdb.record.query.plan.cascades.CorrelationIdentifier; +import com.apple.foundationdb.record.query.plan.cascades.Quantifier; import com.apple.foundationdb.record.query.plan.cascades.typing.Type; import com.apple.foundationdb.relational.util.Assert; @@ -151,10 +152,15 @@ public static final class State { @Nonnull private final Optional targetTypeReorderings; + @Nonnull + private final Optional updateQuantifier; + private State(@Nonnull Optional targetType, - @Nonnull Optional targetTypeReorderings) { + @Nonnull Optional targetTypeReorderings, + @Nonnull Optional updateQuantifier) { this.targetType = targetType; this.targetTypeReorderings = targetTypeReorderings; + this.updateQuantifier = updateQuantifier; } @Nonnull @@ -172,6 +178,11 @@ public Optional getTargetTypeReorderings() { return targetTypeReorderings; } + @Nonnull + public Optional getUpdateQuantifier() { + return updateQuantifier; + } + public static final class Builder { @Nullable @@ -180,6 +191,9 @@ public static final class Builder { @Nullable StringTrieNode targetTypeReorderings; + @Nullable + private Quantifier.ForEach updateQuantifier; + private Builder() { } @@ -195,9 +209,15 @@ public Builder withTargetTypeReorderings(@Nonnull StringTrieNode targetTypeReord return this; } + @Nonnull + public Builder withUpdateQuantifier(@Nonnull Quantifier.ForEach updateQuantifier) { + this.updateQuantifier = updateQuantifier; + return this; + } + @Nonnull public State build() { - return new State(Optional.ofNullable(targetType), Optional.ofNullable(targetTypeReorderings)); + return new State(Optional.ofNullable(targetType), Optional.ofNullable(targetTypeReorderings), Optional.ofNullable(updateQuantifier)); } } } diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/SemanticAnalyzer.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/SemanticAnalyzer.java index f8d99a9cd0..09eca9c2d2 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/SemanticAnalyzer.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/SemanticAnalyzer.java @@ -292,6 +292,23 @@ public Expression resolveCorrelatedIdentifier(@Nonnull Identifier identifier, @Nonnull public Star expandStar(@Nonnull Optional optionalQualifier, @Nonnull LogicalOperators operators) { + return expandStar(optionalQualifier, operators, Optional.empty()); + } + + @Nonnull + public Star expandStar(@Nonnull Optional optionalQualifier, + @Nonnull LogicalOperators operators, + @Nonnull Optional planFragmentMaybe) { + // In UPDATE RETURNING context, unqualified * should expand to NEW.* + if (planFragmentMaybe.isPresent() && optionalQualifier.isEmpty()) { + final var stateMaybe = planFragmentMaybe.get().getStateMaybe(); + final var updateQuantifierMaybe = stateMaybe.flatMap(LogicalPlanFragment.State::getUpdateQuantifier); + if (updateQuantifierMaybe.isPresent()) { + // Treat unqualified * as NEW.* in UPDATE RETURNING + return expandStar(Optional.of(Identifier.of("NEW")), operators, planFragmentMaybe); + } + } + final var forEachOperators = operators.forEachOnly(); // Case 1: no qualifier, e.g. SELECT * FROM T, R; if (optionalQualifier.isEmpty()) { @@ -331,6 +348,32 @@ public Star expandStar(@Nonnull Optional optionalQualifier, @Nonnull public Expression resolveIdentifier(@Nonnull Identifier identifier, @Nonnull LogicalPlanFragment planFragment) { + // Check if we're in UPDATE RETURNING context + final var stateMaybe = planFragment.getStateMaybe(); + final var updateQuantifierMaybe = stateMaybe.flatMap(LogicalPlanFragment.State::getUpdateQuantifier); + + if (updateQuantifierMaybe.isPresent() && !identifier.isQualified()) { + // Unqualified identifier in UPDATE RETURNING - automatically access NEW. + final var updateQuantifier = updateQuantifierMaybe.get(); + final var updateValue = updateQuantifier.getFlowedObjectValue(); + final var fieldValue = com.apple.foundationdb.record.query.plan.cascades.values.FieldValue.ofFieldNames( + updateValue, ImmutableList.of("NEW", identifier.getName())); + return Expression.fromUnderlying(fieldValue).withName(identifier); + } + + if (updateQuantifierMaybe.isPresent() && identifier.isQualified() && identifier.getQualifier().size() == 1) { + // Check if qualified with NEW or OLD + final var qualifier = identifier.getQualifier().get(0); + if ("NEW".equals(qualifier) || "OLD".equals(qualifier)) { + final var updateQuantifier = updateQuantifierMaybe.get(); + final var updateValue = updateQuantifier.getFlowedObjectValue(); + final var fieldValue = com.apple.foundationdb.record.query.plan.cascades.values.FieldValue.ofFieldNames( + updateValue, ImmutableList.of(qualifier, identifier.getName())); + return Expression.fromUnderlying(fieldValue).withName(Identifier.of(identifier.getName())); + } + } + + // Normal identifier resolution // search throw all visible plan fragments: // - in each plan fragment, search operators left to right. // - if identifier is not resolve, go to parent plan fragment. 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 8178bd893a..ab1615ab9b 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 @@ -134,7 +134,8 @@ public Expression visitContinuationAtom(@Nonnull RelationalParser.ContinuationAt @Nonnull @Override public Expression visitSelectStarElement(@Nonnull RelationalParser.SelectStarElementContext ignored) { - return getDelegate().getSemanticAnalyzer().expandStar(Optional.empty(), getDelegate().getLogicalOperators()); + return getDelegate().getSemanticAnalyzer().expandStar(Optional.empty(), getDelegate().getLogicalOperators(), + getDelegate().getCurrentPlanFragmentMaybe()); } @Nonnull 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 901d606abd..945b7edd3c 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 @@ -489,6 +489,10 @@ public LogicalOperator visitUpdateStatement(@Nonnull RelationalParser.UpdateStat // } if (ctx.RETURNING() != null) { + // Store the updateQuantifier in State so SemanticAnalyzer can access it + final var stateBuilder = LogicalPlanFragment.State.newBuilder().withUpdateQuantifier(updateQuantifier); + getDelegate().getCurrentPlanFragment().setState(stateBuilder.build()); + final var selectExpressions = visitSelectElements(ctx.selectElements()); final var result = LogicalOperator.generateSelect(selectExpressions, getDelegate().getLogicalOperators(), Optional.empty(), List.of(), Optional.empty(), diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/AutoCommitTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/AutoCommitTests.java index f3ab88316d..1c2d85fba2 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/AutoCommitTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/AutoCommitTests.java @@ -210,7 +210,7 @@ public void simpleUpdateWithReturningExhaustedResultSet(TransactionType transact } setAutoCommit(conn, transactionType); try (final var statement = conn.createStatement()) { - final var hasResultSet = statement.execute("UPDATE RESTAURANT SET NAME = 'aa' WHERE REST_NO = 1 RETURNING \"new\".* "); + final var hasResultSet = statement.execute("UPDATE RESTAURANT SET NAME = 'aa' WHERE REST_NO = 1 RETURNING NEW.* "); Assertions.assertTrue(hasResultSet); try (final var resultSet = statement.getResultSet()) { ResultSetAssert.assertThat(resultSet) @@ -244,7 +244,7 @@ public void simpleUpdateWithReturningNonExhaustedResultSet(TransactionType trans } setAutoCommit(conn, transactionType); try (final var statement = conn.createStatement()) { - final var hasResultSet = statement.execute("UPDATE RESTAURANT SET NAME = 'aa' WHERE REST_NO < 3 RETURNING \"new\".* "); + final var hasResultSet = statement.execute("UPDATE RESTAURANT SET NAME = 'aa' WHERE REST_NO < 3 RETURNING NEW.* "); Assertions.assertTrue(hasResultSet); // resultSet not retrieved, hence it is still owned by the statement and will be closed. } @@ -530,7 +530,7 @@ public void switchOffAutoCommitBetweenOngoingTransaction() throws SQLException { Assertions.assertFalse(conn.inActiveTransaction()); Assertions.assertTrue(conn.getAutoCommit()); try (final var statement = conn.createStatement()) { - try (final var rs = statement.executeQuery("UPDATE RESTAURANT SET name = 'aa' WHERE REST_NO < 3 RETURNING \"new\".* ")) { + try (final var rs = statement.executeQuery("UPDATE RESTAURANT SET name = 'aa' WHERE REST_NO < 3 RETURNING NEW.* ")) { Assertions.assertTrue(conn.inActiveTransaction()); ResultSetAssert.assertThat(rs) .hasNextRow() @@ -571,7 +571,7 @@ public void switchOnAutoCommitBetweenOngoingTransaction() throws SQLException { conn.setAutoCommit(false); Assertions.assertFalse(conn.getAutoCommit()); try (final var statement = conn.createStatement()) { - try (final var rs = statement.executeQuery("UPDATE RESTAURANT SET name = 'aa' WHERE REST_NO < 3 RETURNING \"new\".* ")) { + try (final var rs = statement.executeQuery("UPDATE RESTAURANT SET name = 'aa' WHERE REST_NO < 3 RETURNING NEW.* ")) { Assertions.assertTrue(conn.inActiveTransaction()); ResultSetAssert.assertThat(rs) .hasNextRow() @@ -613,7 +613,7 @@ public void transactionClosesWithStatement() throws SQLException { RelationalResultSet rs; try (final var statement = conn.createStatement()) { - rs = statement.executeQuery("UPDATE RESTAURANT SET name = 'aa' WHERE REST_NO < 3 RETURNING \"new\".* "); + rs = statement.executeQuery("UPDATE RESTAURANT SET name = 'aa' WHERE REST_NO < 3 RETURNING NEW.* "); Assertions.assertTrue(conn.inActiveTransaction()); ResultSetAssert.assertThat(rs).hasNextRow(); 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 6487a2afc9..3571092f87 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 @@ -416,8 +416,7 @@ void setArrayTypeOfByte() throws Exception { final var count = statement.executeUpdate("INSERT INTO RestaurantReviewer(id) VALUES (1)"); Assertions.assertThat(count).isEqualTo(1); } - // "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("UPDATE RestaurantReviewer SET secrets = ?param WHERE id = 1 RETURNING \"new\".*")) { + try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("UPDATE RestaurantReviewer SET secrets = ?param WHERE id = 1 RETURNING NEW.*")) { final var array = List.of(new byte[]{1, 2, 3, 4}, new byte[]{5, 6, 7, 8}); final var arrayObject = ddl.getConnection().createArrayOf("BINARY", array.toArray()); ps.setArray("param", arrayObject); @@ -442,8 +441,7 @@ void setByteType() throws Exception { final var count = statement.executeUpdate("INSERT INTO RestaurantComplexRecord(rest_no) VALUES (1)"); Assertions.assertThat(count).isEqualTo(1); } - // "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("UPDATE RestaurantComplexRecord SET key = ?param WHERE rest_no = 1 RETURNING \"new\".*")) { + try (var ps = ddl.setSchemaAndGetConnection().prepareStatement("UPDATE RestaurantComplexRecord SET key = ?param WHERE rest_no = 1 RETURNING NEW.*")) { ps.setBytes("param", new byte[]{1, 2, 3, 4}); try (final var resultSet = ps.executeQuery()) { ResultSetAssert.assertThat(resultSet) @@ -668,8 +666,7 @@ void prepareUpdateWithStruct() throws Exception { try (var statement = ddl.setSchemaAndGetConnection().createStatement()) { statement.execute("INSERT INTO RestaurantReviewer(id, stats) VALUES (1, (2, 'a', 'b')), (2, (3, 'b', 'c')), (3, (4, 'c', 'd')), (4, (5, 'd', 'e')), (5, (6, 'e', 'f'))"); } - // "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - final var query = "UPDATE RestaurantReviewer SET stats = ?param WHERE id = 1 RETURNING \"new\".stats"; + final var query = "UPDATE RestaurantReviewer SET stats = ?param WHERE id = 1 RETURNING NEW.stats"; try (var ps = ddl.setSchemaAndGetConnection().prepareStatement(query)) { ps.setObject("param", ddl.getConnection().createStruct("blah", statsAttributes)); final var expectedStats = EmbeddedRelationalStruct.newBuilder() @@ -705,8 +702,7 @@ void prepareUpdateWithNestedStruct(Object[] attributes, boolean succeed) throws try (var statement = ddl.setSchemaAndGetConnection().createStatement()) { statement.execute("INSERT INTO RestaurantComplexRecord(rest_no, name) VALUES (1, 'mango & miso'), (2, 'basil & brawn'), (3, 'peach & pepper'), (4, 'smoky skillet'), (5, 'the tin pot')"); } - // "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - final var query = "UPDATE RestaurantComplexRecord SET location = ?param WHERE rest_no = 1 RETURNING \"new\".location"; + final var query = "UPDATE RestaurantComplexRecord SET location = ?param WHERE rest_no = 1 RETURNING NEW.location"; try (var ps = ddl.setSchemaAndGetConnection().prepareStatement(query)) { final var latLong = ddl.getConnection().createStruct("LATLONG", attributes); final var location = ddl.getConnection().createStruct("LOCATION", new Object[]{"next door", 217, latLong}); @@ -740,8 +736,7 @@ void prepareUpdateWithArrayOfPrimitives() throws Exception { try (var statement = ddl.setSchemaAndGetConnection().createStatement()) { statement.execute("INSERT INTO RestaurantComplexRecord(rest_no, name) VALUES (1, 'mango & miso'), (2, 'basil & brawn'), (3, 'peach & pepper'), (4, 'smoky skillet'), (5, 'the tin pot')"); } - // "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - final var query = "UPDATE RestaurantComplexRecord SET customer = ?param WHERE rest_no = 1 RETURNING \"new\".customer"; + final var query = "UPDATE RestaurantComplexRecord SET customer = ?param WHERE rest_no = 1 RETURNING NEW.customer"; try (var ps = ddl.setSchemaAndGetConnection().prepareStatement(query)) { final var customer = ddl.getConnection().createArrayOf("STRING", customerAttributes); @@ -767,8 +762,7 @@ void prepareUpdateWithArrayOfStructs() throws Exception { try (var statement = ddl.setSchemaAndGetConnection().createStatement()) { statement.execute("INSERT INTO RestaurantComplexRecord(rest_no, name) VALUES (1, 'mango & miso'), (2, 'basil & brawn'), (3, 'peach & pepper'), (4, 'smoky skillet'), (5, 'the tin pot')"); } - // "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - final var query = "UPDATE RestaurantComplexRecord SET tags = ?param WHERE rest_no = 1 RETURNING \"new\".tags OPTIONS (LOG QUERY)"; + final var query = "UPDATE RestaurantComplexRecord SET tags = ?param WHERE rest_no = 1 RETURNING NEW.tags OPTIONS (LOG QUERY)"; final var restaurantTagAttributes = new Object[][]{{"chinese", 343}, {"top-rated", 2356}, {"exotic", 10}}; try (var ps = ddl.setSchemaAndGetConnection().prepareStatement(query)) { ps.setArray("param", createTagArray(ddl.getConnection(), restaurantTagAttributes)); 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 a5c11bbd3a..471757c7c9 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 @@ -180,7 +180,7 @@ 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()) { - final var statement = conn.prepareStatement("UPDATE RestaurantReviewer SET " + updateField + " = ?param WHERE id >= 0 RETURNING \"new\"." + updateField + ", \"new\".id"); + final var statement = conn.prepareStatement("UPDATE RestaurantReviewer SET " + updateField + " = ?param WHERE id >= 0 RETURNING NEW." + updateField + ", NEW.id"); statement.setObject("param", param); return statement; } else { diff --git a/yaml-tests/src/test/resources/arrays.yamsql b/yaml-tests/src/test/resources/arrays.yamsql index 3212bd9fb3..002c0cc7ed 100644 --- a/yaml-tests/src/test/resources/arrays.yamsql +++ b/yaml-tests/src/test/resources/arrays.yamsql @@ -66,12 +66,12 @@ test_block: - supported_version: 4.5.13.0 - count: 1 - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: UPDATE A SET X = [11, 12, 13] WHERE PK = 1 RETURNING "new".X + - query: UPDATE A SET X = [11, 12, 13] WHERE PK = 1 RETURNING NEW.X + - supported_version: !current_version - result: [{[11, 12, 13]}] - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: UPDATE D SET X = [(11), (12), (13)] WHERE PK = 1 RETURNING "new".X + - query: UPDATE D SET X = [(11), (12), (13)] WHERE PK = 1 RETURNING NEW.X + - supported_version: !current_version - result: [{[{F: 11}, {F: 12}, {F: 13}]}] - - query: select X[3] from A where PK = 1 diff --git a/yaml-tests/src/test/resources/functions.yamsql b/yaml-tests/src/test/resources/functions.yamsql index 20834dc437..d07fe78144 100644 --- a/yaml-tests/src/test/resources/functions.yamsql +++ b/yaml-tests/src/test/resources/functions.yamsql @@ -139,13 +139,11 @@ test_block: - unorderedResult: [ {{ _0: 5, _1: 'e', _2: 5.0}}] - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: update C set st = coalesce(st, null) where c1 = 4 returning "new".st + - query: update C set st = coalesce(st, null) where c1 = 4 returning NEW.st - unorderedResult: [ {!null _}] - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: update C set st = coalesce(st, (5, 'e', 5.0)) where c1 = 4 returning "new".st + - query: update C set st = coalesce(st, (5, 'e', 5.0)) where c1 = 4 returning NEW.st - unorderedResult: [ {{ T1: 5, A: 'e', B: 5.0}}] ... diff --git a/yaml-tests/src/test/resources/prepared.yamsql b/yaml-tests/src/test/resources/prepared.yamsql index d41917ef73..6062cdf830 100644 --- a/yaml-tests/src/test/resources/prepared.yamsql +++ b/yaml-tests/src/test/resources/prepared.yamsql @@ -144,6 +144,7 @@ test_block: preset: multi_repetition_ordered tests: - - - query: update ta set e = !! [10, 100, 1000] !! where a = 1 returning "new".e + - query: update ta set e = !! [10, 100, 1000] !! where a = 1 returning NEW.e + - supported_version: !current_version - result: [{[10, 100, 1000]}] ... diff --git a/yaml-tests/src/test/resources/update-delete-returning.metrics.binpb b/yaml-tests/src/test/resources/update-delete-returning.metrics.binpb index 130545e674..602bef826f 100644 --- a/yaml-tests/src/test/resources/update-delete-returning.metrics.binpb +++ b/yaml-tests/src/test/resources/update-delete-returning.metrics.binpb @@ -1,39 +1,39 @@  -e - unnamed-1XEXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 OPTIONS(DRY RUN); -~G 9(0’8@tSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 AS A3)digraph G { +c + unnamed-2VEXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 OPTIONS(DRY RUN); +څG Ç(0j8@tSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 AS A3)digraph G { fontname=courier; rankdir=BT; splines=polyline; - 1 [ label=<
Value Computation
MAP (q6.new.A3 AS A3)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A3)" ]; - 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; - 3 [ label=<
Predicate Filter
WHERE q37.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; - 4 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 1 [ label=<
Value Computation
MAP (q6.NEW.A3 AS A3)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A3)" ]; + 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; + 3 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; + 4 [ label=<
Predicate Filter
WHERE q37.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 5 [ label=<
Unordered Primary Key Distinct
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 6 [ label=<
Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 7 [ label=<
Primary Storage
record types: [A]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; - 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 4 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="none" arrowtail="normal" dir="both" ]; - 5 -> 3 [ label=< q37> label="q37" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="none" arrowtail="normal" dir="both" ]; + 4 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ label=< q37> label="q37" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 6 -> 5 [ label=< q35> label="q35" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 7 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 2 -> 1 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; { rank=same; rankDir=LR; - 3 -> 4 [ color="red" style="invis" ]; + 4 -> 3 [ color="red" style="invis" ]; } } -T - unnamed-1GEXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3; -~G 9(0’8@tSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 AS A3)digraph G { +R + unnamed-2EEXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3; +څG Ç(0j8@tSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 AS A3)digraph G { fontname=courier; rankdir=BT; splines=polyline; - 1 [ label=<
Value Computation
MAP (q6.new.A3 AS A3)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A3)" ]; - 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 1 [ label=<
Value Computation
MAP (q6.NEW.A3 AS A3)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A3)" ]; + 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 3 [ label=<
Predicate Filter
WHERE q37.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; - 4 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 4 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 5 [ label=<
Unordered Primary Key Distinct
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 6 [ label=<
Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 7 [ label=<
Primary Storage
record types: [A]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; @@ -48,17 +48,17 @@ T rankDir=LR; 3 -> 4 [ color="red" style="invis" ]; } -} -p - unnamed-1cEXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 + "new".A3 OPTIONS(DRY RUN); -sG 3(0έ8@SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 + _.new.A3 AS _0)digraph G { +} +l + unnamed-2_EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 + NEW.A3 OPTIONS(DRY RUN); +G (0)8@SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 + _.NEW.A3 AS _0)digraph G { fontname=courier; rankdir=BT; splines=polyline; - 1 [ label=<
Value Computation
MAP (q6.new.A3 + q6.new.A3 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0)" ]; - 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 1 [ label=<
Value Computation
MAP (q6.NEW.A3 + q6.NEW.A3 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0)" ]; + 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 3 [ label=<
Predicate Filter
WHERE q37.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; - 4 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 4 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 5 [ label=<
Unordered Primary Key Distinct
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 6 [ label=<
Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 7 [ label=<
Primary Storage
record types: [A]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; @@ -73,17 +73,17 @@ p rankDir=LR; 3 -> 4 [ color="red" style="invis" ]; } -} -_ - unnamed-1REXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 + "new".A3; -sG 3(0έ8@SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 + _.new.A3 AS _0)digraph G { +} +[ + unnamed-2NEXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 + NEW.A3; +G (0)8@SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 + _.NEW.A3 AS _0)digraph G { fontname=courier; rankdir=BT; splines=polyline; - 1 [ label=<
Value Computation
MAP (q6.new.A3 + q6.new.A3 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0)" ]; - 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 1 [ label=<
Value Computation
MAP (q6.NEW.A3 + q6.NEW.A3 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0)" ]; + 2 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 3 [ label=<
Predicate Filter
WHERE q37.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; - 4 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 4 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 5 [ label=<
Unordered Primary Key Distinct
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 6 [ label=<
Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 7 [ label=<
Primary Storage
record types: [A]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; @@ -100,17 +100,17 @@ _ } } H - unnamed-1;EXPLAIN update A set A2 = 52 where A1 > 2 OPTIONS(DRY RUN); -7 ҟ(08@VSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 GREATER_THAN promote(@c10 AS LONG) | UPDATE Adigraph G { + unnamed-2;EXPLAIN update A set A2 = 52 where A1 > 2 OPTIONS(DRY RUN); +7 (08@VSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 GREATER_THAN promote(@c10 AS LONG) | UPDATE Adigraph G { fontname=courier; rankdir=BT; splines=polyline; - 1 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 1 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 2 [ label=<
Predicate Filter
WHERE q34.A1 GREATER_THAN promote(@c10 AS LONG)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 3 [ label=<
Unordered Primary Key Distinct
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 4 [ label=<
Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 5 [ label=<
Primary Storage
record types: [A]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; - 6 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 6 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 3 -> 2 [ label=< q34> label="q34" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 4 -> 3 [ label=< q32> label="q32" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; @@ -123,17 +123,17 @@ H } } 7 - unnamed-1*EXPLAIN update A set A2 = 52 where A1 > 2; -7 ҟ(08@VSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 GREATER_THAN promote(@c10 AS LONG) | UPDATE Adigraph G { + unnamed-2*EXPLAIN update A set A2 = 52 where A1 > 2; +7 (08@VSCAN(<,>) | DISTINCT BY PK | FILTER _.A1 GREATER_THAN promote(@c10 AS LONG) | UPDATE Adigraph G { fontname=courier; rankdir=BT; splines=polyline; - 1 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 1 [ label=<
Modification
UPDATE
> color="black" shape="plain" style="filled" fillcolor="lightcoral" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 2 [ label=<
Predicate Filter
WHERE q34.A1 GREATER_THAN promote(@c10 AS LONG)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 3 [ label=<
Unordered Primary Key Distinct
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 4 [ label=<
Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; 5 [ label=<
Primary Storage
record types: [A]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3)" ]; - 6 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS old, LONG AS A1, LONG AS A2, LONG AS A3 AS new)" ]; + 6 [ label=<
Primary Storage
A
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS A1, LONG AS A2, LONG AS A3 AS OLD, LONG AS A1, LONG AS A2, LONG AS A3 AS NEW)" ]; 3 -> 2 [ label=< q34> label="q34" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 4 -> 3 [ label=< q32> label="q32" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; diff --git a/yaml-tests/src/test/resources/update-delete-returning.metrics.yaml b/yaml-tests/src/test/resources/update-delete-returning.metrics.yaml index 510d953bb4..de37bfe95b 100644 --- a/yaml-tests/src/test/resources/update-delete-returning.metrics.yaml +++ b/yaml-tests/src/test/resources/update-delete-returning.metrics.yaml @@ -1,47 +1,47 @@ -unnamed-1: -- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 - OPTIONS(DRY RUN); +unnamed-2: +- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 OPTIONS(DRY + RUN); explain: SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 - AS LONG) | UPDATE A | MAP (_.new.A3 AS A3) + AS LONG) | UPDATE A | MAP (_.NEW.A3 AS A3) task_count: 288 - task_total_time_ms: 2 + task_total_time_ms: 48 transform_count: 71 - transform_time_ms: 0 + transform_time_ms: 29 transform_yield_count: 19 - insert_time_ms: 0 + insert_time_ms: 1 insert_new_count: 25 insert_reused_count: 2 -- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3; +- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3; explain: SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 - AS LONG) | UPDATE A | MAP (_.new.A3 AS A3) + AS LONG) | UPDATE A | MAP (_.NEW.A3 AS A3) task_count: 288 - task_total_time_ms: 2 + task_total_time_ms: 48 transform_count: 71 - transform_time_ms: 0 + transform_time_ms: 29 transform_yield_count: 19 - insert_time_ms: 0 + insert_time_ms: 1 insert_new_count: 25 insert_reused_count: 2 -- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 - + "new".A3 OPTIONS(DRY RUN); +- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 + + NEW.A3 OPTIONS(DRY RUN); explain: SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 - AS LONG) | UPDATE A | MAP (_.new.A3 + _.new.A3 AS _0) + AS LONG) | UPDATE A | MAP (_.NEW.A3 + _.NEW.A3 AS _0) task_count: 288 - task_total_time_ms: 1 + task_total_time_ms: 11 transform_count: 71 - transform_time_ms: 0 + transform_time_ms: 4 transform_yield_count: 19 insert_time_ms: 0 insert_new_count: 25 insert_reused_count: 2 -- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 - + "new".A3; +- query: EXPLAIN update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 + + NEW.A3; explain: SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 - AS LONG) | UPDATE A | MAP (_.new.A3 + _.new.A3 AS _0) + AS LONG) | UPDATE A | MAP (_.NEW.A3 + _.NEW.A3 AS _0) task_count: 288 - task_total_time_ms: 1 + task_total_time_ms: 11 transform_count: 71 - transform_time_ms: 0 + transform_time_ms: 4 transform_yield_count: 19 insert_time_ms: 0 insert_new_count: 25 @@ -50,9 +50,9 @@ unnamed-1: explain: SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 GREATER_THAN promote(@c10 AS LONG) | UPDATE A task_count: 225 - task_total_time_ms: 3 + task_total_time_ms: 7 transform_count: 55 - transform_time_ms: 2 + transform_time_ms: 3 transform_yield_count: 17 insert_time_ms: 0 insert_new_count: 21 @@ -61,9 +61,9 @@ unnamed-1: explain: SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 GREATER_THAN promote(@c10 AS LONG) | UPDATE A task_count: 225 - task_total_time_ms: 3 + task_total_time_ms: 7 transform_count: 55 - transform_time_ms: 2 + transform_time_ms: 3 transform_yield_count: 17 insert_time_ms: 0 insert_new_count: 21 diff --git a/yaml-tests/src/test/resources/update-delete-returning.yamsql b/yaml-tests/src/test/resources/update-delete-returning.yamsql index 5f237bf748..bbfed02c3f 100644 --- a/yaml-tests/src/test/resources/update-delete-returning.yamsql +++ b/yaml-tests/src/test/resources/update-delete-returning.yamsql @@ -17,6 +17,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +--- +options: + supported_version: !current_version --- schema_template: create type as struct S(S1 bigint, S2 bigint) @@ -40,8 +43,8 @@ test_block: { A1: 3, A2: 12, A3: 30 } ] - # update DRY RUN, assert that plan and returning result are the same, but nothing is updated - - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 OPTIONS(DRY RUN); - - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 AS A3)" + - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 OPTIONS(DRY RUN); + - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 AS A3)" - result: [ { 44 }, { 44 } ] - - query: select * from A; @@ -49,28 +52,41 @@ test_block: { A1: 2, A2: 11, A3: 20 }, { A1: 3, A2: 12, A3: 30 } ] - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3; - - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 AS A3)" + - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3; + - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 AS A3)" - result: [ { 44 }, { 44 } ] -# - -# # TODO ([POST] UPDATE/DELETE RETURNING * does not work) -# - query: update A set A2 = 42, A3 = 45 where A1 <= 2 returning *; -# - result: [ { { old: { A1: 1, A2: 42, A3: 44 }, new: { A1: 1, A2: 42, A3: 45 } } }, -# { { old: { A1: 2, A2: 42, A3: 44 }, new: { A1: 2, A2: 42, A3: 45 } } } ] - - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: update A set A2 = 45, A3 = 46 where A1 <= 2 returning "new".*; + - + # unqualified RETURNING - should access NEW values by default (SQL standard) + - query: update A set A2 = 100, A3 = 200 where A1 = 1 returning A2, A3; + - result: [ { A2: 100, A3: 200 } ] + - + # OLD values in RETURNING + - query: update A set A2 = 101, A3 = 201 where A1 = 1 returning OLD.A2, OLD.A3; + - result: [ { A2: 100, A3: 200 } ] + - + # Mix of OLD and NEW in RETURNING + - query: update A set A2 = 102, A3 = 202 where A1 = 1 returning OLD.A2, NEW.A3; + - result: [ { A2: 101, A3: 202 } ] + - + # Reset data for next tests + - query: update A set A2 = 42, A3 = 44 where A1 = 1; + - + # UPDATE RETURNING * should expand to NEW.* (SQL standard) + - query: update A set A2 = 42, A3 = 45 where A1 <= 2 returning *; + - supported_version: !current_version + - result: [ { A1: 1, A2: 42, A3: 45 }, + { A1: 2, A2: 42, A3: 45 } ] + - + - query: update A set A2 = 45, A3 = 46 where A1 <= 2 returning NEW.*; - result: [ { A1: 1, A2: 45, A3: 46 }, { A1: 2, A2: 45, A3: 46 } ] - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: update A set A2 = 45, A3 = 46 where A1 <= 2 returning ( "new".* ); + - query: update A set A2 = 45, A3 = 46 where A1 <= 2 returning ( NEW.* ); - result: [ { { A1: 1, A2: 45, A3: 46 } }, { { A1: 2, A2: 45, A3: 46 } } ] - - - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 + "new".A3 OPTIONS(DRY RUN); - - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 + _.new.A3 AS _0)" + - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 + NEW.A3 OPTIONS(DRY RUN); + - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 + _.NEW.A3 AS _0)" - result: [ { 88 }, { 88 } ] - - query: select * from A; @@ -78,9 +94,8 @@ test_block: { A1: 2, A2: 45, A3: 46 }, { A1: 3, A2: 12, A3: 30 } ] - - # "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator) - - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning "new".A3 + "new".A3; - - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.new.A3 + _.new.A3 AS _0)" + - query: update A set A2 = 42, A3 = 44 where A1 <= 2 returning NEW.A3 + NEW.A3; + - explain: "SCAN(<,>) | DISTINCT BY PK | FILTER _.A1 LESS_THAN_OR_EQUALS promote(@c15 AS LONG) | UPDATE A | MAP (_.NEW.A3 + _.NEW.A3 AS _0)" - result: [ { 88 }, { 88 } ] - - query: select * from A; @@ -105,16 +120,15 @@ test_block: { A1: 2, A2: 42, A3: 44 }, { A1: 3, A2: 52, A3: 30 } ] - - - query: delete from A where A1 <= 1 returning A2, A3; + - query: delete from A where A1 = 1 returning A2, A3; - result: [{ 42, 44 }] - - query: select * from A; - result: [ { A1: 2, A2: 42, A3: 44 }, { A1: 3, A2: 52, A3: 30 } ] - - - query: delete from A where A1 = 2; #returning *; -# # TODO ([POST] UPDATE/DELETE RETURNING * does not work) -# - result: [{ A1: 2, A2: 42, A3: 44 }] + - query: delete from A where A1 = 2 returning A2, A3; + - result: [{ 42, 44 }] - - query: select * from A; - result: [ { A1: 3, A2: 52, A3: 30 } ] @@ -154,4 +168,69 @@ test_block: - result: [ { A1: 1, A2: 1000, A3: 10 }, { A1: 2, A2: 1500, A3: 20 }, { A1: 3, A2: 2000, A3: 30 } ] + - + # Complex RETURNING expressions tests + - query: delete from A + - + - query: insert into A values (1, 10, 20), (2, 30, 40), (3, 50, 60); + - count: 3 + - + # RETURNING with parenthesized star - should return the full UPDATE record (OLD and NEW) + - query: update A set A2 = 100 where A1 = 1 returning (*); + - supported_version: !current_version + - result: [ { { OLD: { A1: 1, A2: 10, A3: 20 }, NEW: { A1: 1, A2: 100, A3: 20 } } } ] + - + # RETURNING with arithmetic expression on NEW values + - query: update A set A2 = 200, A3 = 300 where A1 = 1 returning A2 + A3; + - supported_version: !current_version + - result: [ { 500 } ] + - + # RETURNING with unqualified column names (should use NEW by default) + - query: update A set A2 = 15, A3 = 25 where A1 = 1 returning A2 + A3; + - supported_version: !current_version + - result: [ { 40 } ] + - + # RETURNING with mixing NEW and OLD values in arithmetic + - query: update A set A2 = 100 where A1 = 1 returning NEW.A2 + OLD.A2; + - supported_version: !current_version + - result: [ { 115 } ] + - + # RETURNING with mixing NEW and OLD in multiple expressions + - query: update A set A2 = 50, A3 = 60 where A1 = 1 returning NEW.A2 * 2, OLD.A3, NEW.A3 - OLD.A3; + - supported_version: !current_version + - result: [ { 100, 25, 35 } ] + - + # RETURNING with CASE expression + - query: update A set A2 = 75 where A1 <= 2 returning case when NEW.A2 > 50 then 'HIGH' else 'LOW' end; + - supported_version: !current_version + - result: [ { 'HIGH' }, { 'HIGH' } ] + - + # RETURNING with complex nested expression + - query: update A set A3 = 80 where A1 = 1 returning (NEW.A2 + NEW.A3) * 2; + - supported_version: !current_version + - result: [ { 310 } ] + - + # RETURNING with comparison in expression + - query: update A set A2 = 90 where A1 = 1 returning NEW.A2 > OLD.A2; + - supported_version: !current_version + - result: [ { true } ] + - + # DELETE with arithmetic expression + - query: delete from A where A1 = 3 returning A2 + A3; + - supported_version: !current_version + - result: [ { 110 } ] + - + # DELETE with CASE expression + - query: delete from A where A1 = 2 returning case when A2 > 50 then A2 else A3 end; + - supported_version: !current_version + - result: [ { 75 } ] + - + # DELETE with multiple complex expressions + - query: delete from A where A1 = 1 returning A2 * 2, A3 + 10, A1; + - supported_version: !current_version + - result: [ { 180, 90, 1 } ] + - + # Verify table is empty after deletions + - query: select count(*) from A; + - result: [ { 0 } ] ...