diff --git a/scripts/YAML-SQL.xml b/scripts/YAML-SQL.xml index dee2702b8f..554b8391b9 100644 --- a/scripts/YAML-SQL.xml +++ b/scripts/YAML-SQL.xml @@ -53,8 +53,8 @@ multi_repetition_parallelized" /> + maxRows:;initialVersionAtLeast:;initialVersionLessThan:;;mode:;repetition:;seed:;setup:;transaction_setups:; + setupReference:;check_cache:;connection_lifecycle:;steps:;preset:;statement_type:;!r;!in;!a;" /> diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/CustomYamlConstructor.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/CustomYamlConstructor.java index 121c141122..07a12ef04c 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/CustomYamlConstructor.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/CustomYamlConstructor.java @@ -25,6 +25,7 @@ import com.apple.foundationdb.relational.yamltests.block.FileOptions; import com.apple.foundationdb.relational.yamltests.block.SetupBlock; import com.apple.foundationdb.relational.yamltests.block.TestBlock; +import com.apple.foundationdb.relational.yamltests.block.TransactionSetupsBlock; import com.apple.foundationdb.relational.yamltests.command.Command; import com.apple.foundationdb.relational.yamltests.command.QueryConfig; import org.yaml.snakeyaml.LoaderOptions; @@ -61,6 +62,7 @@ public CustomYamlConstructor(LoaderOptions loaderOptions) { requireLineNumber.add(FileOptions.OPTIONS); requireLineNumber.add(SetupBlock.SETUP_BLOCK); requireLineNumber.add(SetupBlock.SchemaTemplateBlock.SCHEMA_TEMPLATE_BLOCK); + requireLineNumber.add(TransactionSetupsBlock.TRANSACTION_SETUP); requireLineNumber.add(TestBlock.TEST_BLOCK); // commands requireLineNumber.add(Command.COMMAND_LOAD_SCHEMA_TEMPLATE); @@ -78,6 +80,8 @@ public CustomYamlConstructor(LoaderOptions loaderOptions) { requireLineNumber.add(QueryConfig.QUERY_CONFIG_SUPPORTED_VERSION); requireLineNumber.add(QueryConfig.QUERY_CONFIG_INITIAL_VERSION_LESS_THAN); requireLineNumber.add(QueryConfig.QUERY_CONFIG_INITIAL_VERSION_AT_LEAST); + requireLineNumber.add(QueryConfig.QUERY_CONFIG_SETUP); + requireLineNumber.add(QueryConfig.QUERY_CONFIG_SETUP_REFERENCE); requireLineNumber.add(QueryConfig.QUERY_CONFIG_DEBUGGER); } diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java index 9ad65058a4..8f40d6bff5 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/SimpleYamlConnection.java @@ -24,8 +24,10 @@ import com.apple.foundationdb.relational.api.RelationalConnection; import com.apple.foundationdb.relational.api.RelationalPreparedStatement; import com.apple.foundationdb.relational.api.RelationalStatement; +import com.apple.foundationdb.relational.api.exceptions.RelationalException; import com.apple.foundationdb.relational.api.metrics.MetricCollector; import com.apple.foundationdb.relational.recordlayer.EmbeddedRelationalConnection; +import com.apple.foundationdb.relational.yamltests.command.SQLFunction; import com.apple.foundationdb.relational.yamltests.server.SemanticVersion; import com.google.common.collect.Iterables; import org.junit.jupiter.api.Assumptions; @@ -117,6 +119,24 @@ public SemanticVersion getInitialVersion() { return versions.get(0); } + @Override + public T executeTransactionally(final SQLFunction transactionalWork) throws SQLException, RelationalException { + underlying.setAutoCommit(false); + T result; + try { + result = transactionalWork.apply(this); + } catch (final SQLException | RelationalException e) { + underlying.rollback(); + throw e; + } finally { + // enabling autoCommit will commit if there is outstanding work + // It would probably be good to commit earlier, but https://github.com/FoundationDB/fdb-record-layer/pull/3477 + // causes the setAutoCommit to fail if you do that + underlying.setAutoCommit(true); + } + return result; + } + @Override public String toString() { return connectionLabel; diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java index bb0c1c41dd..e380edaacc 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlConnection.java @@ -24,8 +24,10 @@ import com.apple.foundationdb.relational.api.RelationalConnection; import com.apple.foundationdb.relational.api.RelationalPreparedStatement; import com.apple.foundationdb.relational.api.RelationalStatement; +import com.apple.foundationdb.relational.api.exceptions.RelationalException; import com.apple.foundationdb.relational.api.metrics.MetricCollector; import com.apple.foundationdb.relational.recordlayer.EmbeddedRelationalConnection; +import com.apple.foundationdb.relational.yamltests.command.SQLFunction; import com.apple.foundationdb.relational.yamltests.server.SemanticVersion; import javax.annotation.Nonnull; @@ -104,4 +106,6 @@ public interface YamlConnection extends AutoCloseable { */ @Nonnull SemanticVersion getInitialVersion(); + + T executeTransactionally(SQLFunction transactionalWork) throws SQLException, RelationalException; } diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlExecutionContext.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlExecutionContext.java index 30cd2c84ea..c0c82e4c30 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlExecutionContext.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlExecutionContext.java @@ -84,6 +84,7 @@ public final class YamlExecutionContext { private final List connectionURIs = new ArrayList<>(); // Additional options that can be set by the runners to impact test execution private final ContextOptions additionalOptions; + private final Map transactionSetups = new HashMap<>(); public static class YamlExecutionError extends RuntimeException { @@ -248,6 +249,20 @@ public URI inferConnectionURI(@Nullable Object connectObject) { } } + public void registerTransactionSetup(final String name, final String command) { + // Note: at the time of writing, this is only called by code that is iterating over a Map from yaml, so it will + // not prevent two entries in the yaml file itself + Assert.thatUnchecked(!transactionSetups.containsKey(name), ErrorCode.INTERNAL_ERROR, + () -> "Transaction setup " + name + " is defined multiple times."); + transactionSetups.put(name, command); + } + + public String getTransactionSetup(final Object name) { + return Matchers.notNull( + transactionSetups.get(Matchers.string(name, "setup reference")), + "transaction setup " + name + " is not defined"); + } + @Nonnull public List getFinalizeBlocks() { return finalizeBlocks; diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlRunner.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlRunner.java index af5ad3f1d3..73a0b95c5a 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlRunner.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlRunner.java @@ -72,7 +72,8 @@ public void run() throws Exception { LoaderOptions loaderOptions = new LoaderOptions(); loaderOptions.setAllowDuplicateKeys(true); DumperOptions dumperOptions = new DumperOptions(); - final var yaml = new Yaml(new CustomYamlConstructor(loaderOptions), new Representer(dumperOptions), new DumperOptions(), loaderOptions, new Resolver()); + final var yaml = new Yaml(new CustomYamlConstructor(loaderOptions), new Representer(dumperOptions), + new DumperOptions(), loaderOptions, new Resolver()); final var testBlocks = new ArrayList(); int blockNumber = 0; diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/Block.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/Block.java index 442e03c584..58a05e362a 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/Block.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/Block.java @@ -41,6 +41,7 @@ * */ public interface Block { + /** * Looks at the block to determine if its one of the valid blocks. If it is a valid one, parses it to that. This * method dispatches the execution to the right block which takes care of reading from the block and initializing @@ -62,12 +63,14 @@ static Block parse(@Nonnull Object document, int blockNumber, @Nonnull YamlExecu switch (blockKey) { case SetupBlock.SETUP_BLOCK: return SetupBlock.ManualSetupBlock.parse(lineNumber, entry.getValue(), executionContext); + case TransactionSetupsBlock.TRANSACTION_SETUP: + return TransactionSetupsBlock.parse(lineNumber, entry.getValue(), executionContext); case TestBlock.TEST_BLOCK: return TestBlock.parse(blockNumber, lineNumber, entry.getValue(), executionContext); case SetupBlock.SchemaTemplateBlock.SCHEMA_TEMPLATE_BLOCK: return SetupBlock.SchemaTemplateBlock.parse(lineNumber, entry.getValue(), executionContext); case FileOptions.OPTIONS: - Assert.thatUnchecked(blockNumber == 0, + Assert.that(blockNumber == 0, "File level options must be the first block, but found one at line " + lineNumber); return FileOptions.parse(lineNumber, entry.getValue(), executionContext); default: diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/TransactionSetupsBlock.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/TransactionSetupsBlock.java new file mode 100644 index 0000000000..cbe631c3a7 --- /dev/null +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/TransactionSetupsBlock.java @@ -0,0 +1,43 @@ +/* + * TransactionSetupsBlock.java + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2015-2025 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.apple.foundationdb.relational.yamltests.block; + +import com.apple.foundationdb.relational.yamltests.Matchers; +import com.apple.foundationdb.relational.yamltests.YamlExecutionContext; + +import java.util.Map; + +public class TransactionSetupsBlock { + public static final String TRANSACTION_SETUP = "transaction_setups"; + + public static Block parse(final int lineNumber, + final Object document, + final YamlExecutionContext executionContext) { + final Map map = Matchers.map(document); + for (final Map.Entry entry : map.entrySet()) { + final String transactionSetupName = Matchers.string(entry.getKey(), "transaction setup name"); + final String transactionSetupCommand = Matchers.string(entry.getValue(), "transaction setup command"); + executionContext.registerTransactionSetup(transactionSetupName, transactionSetupCommand); + } + return new NoOpBlock(lineNumber); + } + +} diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryCommand.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryCommand.java index fdd2cdbc64..dba46b7e96 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryCommand.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryCommand.java @@ -207,6 +207,18 @@ private void executeInternal(@Nonnull final YamlConnection connection, boolean c } else if (QueryConfig.QUERY_CONFIG_NO_OP.equals(queryConfig.getConfigName())) { // Do nothing for noop execution. continue; + } else if (QueryConfig.QUERY_CONFIG_SETUP.equals(queryConfig.getConfigName())) { + Assert.that(!queryIsRunning, "Transaction setup should not be intermingled with query results"); + final String setupStatement = Matchers.notNull(Matchers.string(Matchers.notNull(queryConfig.getVal(), + "Setup Config Val"), "Transaction setup"), "Transaction setup"); + // we restrict transaction setups to CREATE TEMPORARY FUNCTION, because other mutations could be hard + // to reason about when running in a parallel world. It's possible the right answer is that we shouldn't + // commit after the query, or that we shouldn't allow things that modify state in the database. This will + // become clearer as we have more related tests, and for now, just try to stop people from being confused. + final String allowedStatement = "CREATE TEMPORARY FUNCTION"; + Assert.that(setupStatement.regionMatches(true, 0, allowedStatement, 0, allowedStatement.length()), + "Only \"CREATE TEMPORARY FUNCTION\" is allowed for transaction setups"); + executor.addSetup(setupStatement); } else if (!QueryConfig.QUERY_CONFIG_SUPPORTED_VERSION.equals(queryConfig.getConfigName()) && !QueryConfig.QUERY_CONFIG_DEBUGGER.equals(queryConfig.getConfigName())) { if (QueryConfig.QUERY_CONFIG_ERROR.equals(queryConfig.getConfigName())) { diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryConfig.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryConfig.java index 5e80e5f713..d26b5c152c 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryConfig.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryConfig.java @@ -85,6 +85,8 @@ public abstract class QueryConfig { public static final String QUERY_CONFIG_INITIAL_VERSION_AT_LEAST = "initialVersionAtLeast"; public static final String QUERY_CONFIG_INITIAL_VERSION_LESS_THAN = "initialVersionLessThan"; public static final String QUERY_CONFIG_NO_OP = "noOp"; + public static final String QUERY_CONFIG_SETUP = "setup"; + public static final String QUERY_CONFIG_SETUP_REFERENCE = "setupReference"; public static final String QUERY_CONFIG_DEBUGGER = "debugger"; private static final Set RESULT_CONFIGS = ImmutableSet.of(QUERY_CONFIG_ERROR, QUERY_CONFIG_COUNT, QUERY_CONFIG_RESULT, QUERY_CONFIG_UNORDERED_RESULT); @@ -466,6 +468,16 @@ void checkResultInternal(@Nonnull String currentQuery, @Nonnull Object actual, @ }; } + private static QueryConfig getSetupConfig(final Object value, final int lineNumber, final YamlExecutionContext executionContext) { + return new QueryConfig(QUERY_CONFIG_SETUP, value, lineNumber, executionContext) { + @Override + void checkResultInternal(@Nonnull final String currentQuery, @Nonnull final Object actual, + @Nonnull final String queryDescription) throws SQLException { + Assert.failUnchecked("No results to check on a setup config"); + } + }; + } + private static QueryConfig getDebuggerConfig(@Nonnull Object value, int lineNumber, @Nonnull YamlExecutionContext executionContext) { return new QueryConfig(QUERY_CONFIG_DEBUGGER, DebuggerImplementation.valueOf(((String)value).toUpperCase(Locale.ROOT)), lineNumber, executionContext) { @@ -590,10 +602,14 @@ private static QueryConfig parseConfig(String blockName, String key, Object valu return getCheckResultConfig(false, key, value, lineNumber, executionContext); } else if (QUERY_CONFIG_MAX_ROWS.equals(key)) { return getMaxRowConfig(value, lineNumber, executionContext); + } else if (QUERY_CONFIG_SETUP.equals(key)) { + return getSetupConfig(value, lineNumber, executionContext); + } else if (QUERY_CONFIG_SETUP_REFERENCE.equals(key)) { + return getSetupConfig(executionContext.getTransactionSetup(value), lineNumber, executionContext); } else if (QUERY_CONFIG_DEBUGGER.equals(key)) { return getDebuggerConfig(value, lineNumber, executionContext); } else { - throw Assert.failUnchecked("‼️ '%s' is not a valid configuration"); + throw Assert.failUnchecked("‼️ '" + key + "' is not a valid configuration"); } } diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryExecutor.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryExecutor.java index 4e5e05bb8c..88174ba9af 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryExecutor.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/QueryExecutor.java @@ -24,6 +24,7 @@ import com.apple.foundationdb.relational.api.RelationalPreparedStatement; import com.apple.foundationdb.relational.api.RelationalResultSet; import com.apple.foundationdb.relational.api.RelationalResultSetMetaData; +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.recordlayer.ContinuationImpl; @@ -69,6 +70,8 @@ public class QueryExecutor { */ @Nullable private final List parameters; + @Nonnull + private final List setup = new ArrayList<>(); QueryExecutor(@Nonnull String query, int lineNumber) { this(query, lineNumber, null); @@ -163,23 +166,29 @@ private Continuation executeQuery(@Nonnull YamlConnection connection, @Nonnull Q try { if (parameters == null) { logger.debug("⏳ Executing query '{}'", this.toString()); - try (var s = connection.createStatement()) { - final var queryResult = executeStatementAndCheckCacheIfNeeded(s, false, connection, currentQuery, checkCache, maxRows); - config.checkResult(currentQuery, queryResult, this.toString(), connection); - if (queryResult instanceof RelationalResultSet) { - continuationAfter = ((RelationalResultSet) queryResult).getContinuation(); + continuationAfter = executeWithSetup(connection, singleConnection -> { + try (var s = singleConnection.createStatement()) { + final var queryResult = executeStatementAndCheckCacheIfNeeded(s, false, singleConnection, currentQuery, checkCache, maxRows); + config.checkResult(currentQuery, queryResult, this.toString(), singleConnection); + if (queryResult instanceof RelationalResultSet) { + return ((RelationalResultSet) queryResult).getContinuation(); + } } - } + return null; + }); } else { logger.debug("⏳ Executing query '{}'", this.toString()); - try (var s = connection.prepareStatement(currentQuery)) { - setParametersInPreparedStatement(s); - final var queryResult = executeStatementAndCheckCacheIfNeeded(s, true, connection, currentQuery, checkCache, maxRows); - config.checkResult(currentQuery, queryResult, this.toString(), connection); - if (queryResult instanceof RelationalResultSet) { - continuationAfter = ((RelationalResultSet) queryResult).getContinuation(); + continuationAfter = executeWithSetup(connection, singleConnection -> { + try (var s = singleConnection.prepareStatement(currentQuery)) { + setParametersInPreparedStatement(s); + final var queryResult = executeStatementAndCheckCacheIfNeeded(s, true, singleConnection, currentQuery, checkCache, maxRows); + config.checkResult(currentQuery, queryResult, this.toString(), singleConnection); + if (queryResult instanceof RelationalResultSet) { + return ((RelationalResultSet)queryResult).getContinuation(); + } } - } + return null; + }); } logger.debug("👍 Finished executing query '{}'", this.toString()); } catch (SQLException sqle) { @@ -188,6 +197,22 @@ private Continuation executeQuery(@Nonnull YamlConnection connection, @Nonnull Q return continuationAfter; } + @Nullable + private Continuation executeWithSetup(final @Nonnull YamlConnection connection, + SQLFunction execute) throws SQLException, RelationalException { + if (!setup.isEmpty()) { + return connection.executeTransactionally(singleConnection -> { + for (var setupStatement : setup) { + final RelationalStatement statement = singleConnection.createStatement(); + statement.execute(setupStatement); + } + return execute.apply(singleConnection); + }); + } else { + return execute.apply(connection); + } + } + @Nullable private Continuation executeContinuation(@Nonnull YamlConnection connection, @Nonnull Continuation continuation, @Nonnull QueryConfig config, final @Nonnull String currentQuery, @Nullable Integer maxRows) { @@ -336,4 +361,8 @@ public String toString() { return query + " with parameters (" + String.join(", ", paramList) + ")"; } } + + public void addSetup(final String setupStatement) { + setup.add(setupStatement); + } } diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/SQLFunction.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/SQLFunction.java new file mode 100644 index 0000000000..6420642f15 --- /dev/null +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/SQLFunction.java @@ -0,0 +1,30 @@ +/* + * SQLFunction.java + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2015-2025 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.apple.foundationdb.relational.yamltests.command; + +import com.apple.foundationdb.relational.api.exceptions.RelationalException; + +import java.sql.SQLException; + +@FunctionalInterface +public interface SQLFunction { + R apply(T t) throws SQLException, RelationalException; +} diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/ExternalMultiServerConfig.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/ExternalMultiServerConfig.java index bb44ea18ac..05849cd1ee 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/ExternalMultiServerConfig.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/configs/ExternalMultiServerConfig.java @@ -1,5 +1,5 @@ /* - * MultiServerConfig.java + * ExternalMultiServerConfig.java * * This source file is part of the FoundationDB open source project * @@ -31,6 +31,7 @@ /** * Run against multiple external servers, alternating commands that go against each. + * Notably, even if the versions are the same, you can catch issues where something is jvm specific (e.g. planHash). */ public class ExternalMultiServerConfig implements YamlTestConfig { diff --git a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java index 9821261295..02b3d382f7 100644 --- a/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java +++ b/yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/connectionfactory/MultiServerConnectionFactory.java @@ -23,11 +23,13 @@ import com.apple.foundationdb.relational.api.Options; import com.apple.foundationdb.relational.api.RelationalPreparedStatement; import com.apple.foundationdb.relational.api.RelationalStatement; +import com.apple.foundationdb.relational.api.exceptions.RelationalException; import com.apple.foundationdb.relational.api.metrics.MetricCollector; import com.apple.foundationdb.relational.recordlayer.EmbeddedRelationalConnection; import com.apple.foundationdb.relational.util.Assert; import com.apple.foundationdb.relational.yamltests.YamlConnection; import com.apple.foundationdb.relational.yamltests.YamlConnectionFactory; +import com.apple.foundationdb.relational.yamltests.command.SQLFunction; import com.apple.foundationdb.relational.yamltests.server.SemanticVersion; import com.google.common.collect.Iterables; import org.apache.logging.log4j.LogManager; @@ -204,6 +206,12 @@ public SemanticVersion getInitialVersion() { return versions.get(0); } + @Override + public T executeTransactionally(final SQLFunction transactionalWork) + throws SQLException, RelationalException { + return getCurrentConnection(true, "transactional work").executeTransactionally(transactionalWork); + } + @Override public void close() throws SQLException { logger.info("Sending operation {} to all connections", "close"); diff --git a/yaml-tests/src/test/java/TransactionSetupTest.java b/yaml-tests/src/test/java/TransactionSetupTest.java new file mode 100644 index 0000000000..47eeae28ab --- /dev/null +++ b/yaml-tests/src/test/java/TransactionSetupTest.java @@ -0,0 +1,121 @@ +/* + * SupportedVersionTest.java + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2015-2025 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.apple.foundationdb.relational.yamltests.SimpleYamlConnection; +import com.apple.foundationdb.relational.yamltests.YamlConnection; +import com.apple.foundationdb.relational.yamltests.YamlConnectionFactory; +import com.apple.foundationdb.relational.yamltests.YamlExecutionContext; +import com.apple.foundationdb.relational.yamltests.YamlRunner; +import com.apple.foundationdb.relational.yamltests.configs.EmbeddedConfig; +import com.apple.foundationdb.relational.yamltests.server.SemanticVersion; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import javax.annotation.Nonnull; +import java.net.URI; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Tests that {@code transaction_setups}, {@code setup} and {@code setupReference} work correctly. + */ +public class TransactionSetupTest { + + private static final SemanticVersion VERSION = SemanticVersion.parse("4.4.8.0"); + private static final EmbeddedConfig config = new EmbeddedConfig(null); + + @BeforeAll + static void beforeAll() throws Exception { + config.beforeAll(); + } + + @AfterAll + static void afterAll() throws Exception { + config.afterAll(); + } + + private void doRun(String fileName) throws Exception { + new YamlRunner(fileName, createConnectionFactory(), YamlExecutionContext.ContextOptions.EMPTY_OPTIONS).run(); + } + + YamlConnectionFactory createConnectionFactory() { + return new YamlConnectionFactory() { + @Override + public YamlConnection getNewConnection(@Nonnull URI connectPath) throws SQLException { + return new SimpleYamlConnection(DriverManager.getConnection(connectPath.toString()), VERSION); + } + + @Override + public Set getVersionsUnderTest() { + return Set.of(VERSION); + } + }; + } + + static Stream shouldFail() { + return Stream.of( + "double-setup-reference-wrong-order", + "double-setup-wrong-order", + "duplicate-setup-reference-name", + "future-reference", + "setup-after-results", + "setup-before-query", + "setup-reference-after-results", + "setup-reference-before-query", + "unsupported-setup", + "unsupported-setup-reference" + ); + } + + @ParameterizedTest + @MethodSource("shouldFail") + void shouldFail(String filename) { + assertThrows(YamlExecutionContext.YamlExecutionError.class, () -> + doRun("transaction-setup/shouldFail/" + filename + ".yamsql")); + } + + + static Stream shouldPass() { + return Stream.of( + "double-reference", + "double-setup", + "duplicate-setup-reference-name", + "multiple-transaction-setups", + "reference-and-setup", + "setup-and-reference", + "single-setup", + "single-setup-reference", + "transaction-setup-after-setup", + "transaction-setup-after-tests" + ); + } + + @ParameterizedTest + @MethodSource("shouldPass") + void shouldPass(String filename) throws Exception { + doRun("transaction-setup/shouldPass/" + filename + ".yamsql"); + } +} diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/double-setup-reference-wrong-order.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/double-setup-reference-wrong-order.yamsql new file mode 100644 index 0000000000..d3563099bb --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/double-setup-reference-wrong-order.yamsql @@ -0,0 +1,44 @@ +# +# double-setup-reference-wrong-order.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + t1_greater_than_10: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_greater_than_10 + - setupReference: t1_less_than_50 + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/double-setup-wrong-order.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/double-setup-wrong-order.yamsql new file mode 100644 index 0000000000..3ee4dbc0ea --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/double-setup-wrong-order.yamsql @@ -0,0 +1,40 @@ +# +# double-setup-wrong-order.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t2; + - setup: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; + - setup: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/duplicate-setup-reference-name.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/duplicate-setup-reference-name.yamsql new file mode 100644 index 0000000000..c423ccd7d1 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/duplicate-setup-reference-name.yamsql @@ -0,0 +1,45 @@ +# +# duplicate-setup-reference-name.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +# Yaml itself will manage the map, and remove duplicates, keeping the last one, so this test +# will fail, because we reference the one that was replaced +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + temp_func: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + temp_func: create temporary function t2() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: temp_func + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/future-reference.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/future-reference.yamsql new file mode 100644 index 0000000000..0ab34b05c2 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/future-reference.yamsql @@ -0,0 +1,41 @@ +# +# future-reference.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_less_than_50 + - result: [{id: 30, col1: 40}] +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-after-results.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-after-results.yamsql new file mode 100644 index 0000000000..0df729eb7b --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-after-results.yamsql @@ -0,0 +1,38 @@ +# +# setup-after-results.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - result: [{id: 30, col1: 40}] + - setup: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-before-query.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-before-query.yamsql new file mode 100644 index 0000000000..c252799ec3 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-before-query.yamsql @@ -0,0 +1,42 @@ +# +# setup-before-query.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +# The way parsing works, the first item in a list is the "command" +# so the first thing has to be "query", or it will get confused. +# Changing this is more work than desired when adding support for transaction +# setups. +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - setup: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + - query: select * from t1 where id > 10; + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-reference-after-results.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-reference-after-results.yamsql new file mode 100644 index 0000000000..7b697d7bb3 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-reference-after-results.yamsql @@ -0,0 +1,41 @@ +# +# setup-reference-after-results.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - result: [{id: 30, col1: 40}] + - setupReference: t1_less_than_50 +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-reference-before-query.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-reference-before-query.yamsql new file mode 100644 index 0000000000..6521a460e6 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/setup-reference-before-query.yamsql @@ -0,0 +1,45 @@ +# +# setup-reference-before-query.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +# The way parsing works, the first item in a list is the "command" +# so the first thing has to be "query", or it will get confused. +# Changing this is more work than desired when adding support for transaction +# setups. +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - setupReference: t1_less_than_50 + - query: select * from t1 where id > 10; + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/unsupported-setup-reference.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/unsupported-setup-reference.yamsql new file mode 100644 index 0000000000..79356c3a5d --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/unsupported-setup-reference.yamsql @@ -0,0 +1,43 @@ +# +# unsupported-setup-reference.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + insert_extras: INSERT INTO table_T1 VALUES (25, 35) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + # We currently need this, or it would otherwise execute multiple concurrent inserts and fail because of conflicts, but + # we want to ensure it fails because it doesn't allow inserts + preset: single_repetition_ordered + tests: + - + - query: select * from table_t1 where id > 10 and id < 50; + - setupReference: insert_extras + - result: [{id: 25, col1: 35}, {id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldFail/unsupported-setup.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldFail/unsupported-setup.yamsql new file mode 100644 index 0000000000..9793c75875 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldFail/unsupported-setup.yamsql @@ -0,0 +1,40 @@ +# +# unsupported-setup.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should fail +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + # We currently need this, or it would otherwise execute multiple concurrent inserts and fail because of conflicts, but + # we want to ensure it fails because it doesn't allow inserts + preset: single_repetition_ordered + tests: + - + - query: select * from table_T1 where id > 10 and id < 40; + - setup: INSERT INTO table_T1 VALUES (25, 35) + - result: [{id: 25, col1: 35}, {id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/double-reference.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/double-reference.yamsql new file mode 100644 index 0000000000..f8eb8fac49 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/double-reference.yamsql @@ -0,0 +1,44 @@ +# +# double-reference.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + t1_greater_than_10: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_less_than_50 + - setupReference: t1_greater_than_10 + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/double-setup.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/double-setup.yamsql new file mode 100644 index 0000000000..8ffb7e0fe9 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/double-setup.yamsql @@ -0,0 +1,40 @@ +# +# double-setup.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t2; + - setup: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + - setup: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/duplicate-setup-reference-name.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/duplicate-setup-reference-name.yamsql new file mode 100644 index 0000000000..7c390f9ada --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/duplicate-setup-reference-name.yamsql @@ -0,0 +1,45 @@ +# +# duplicate-setup-reference-name.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +# Yaml itself will manage the map, and remove duplicates, keeping the last one, so this test +# will pass, because we reference the one that was kept +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + temp_func: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + temp_func: create temporary function t2() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t2 where id > 10; + - setupReference: temp_func + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/multiple-transaction-setups.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/multiple-transaction-setups.yamsql new file mode 100644 index 0000000000..d2f5330e84 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/multiple-transaction-setups.yamsql @@ -0,0 +1,48 @@ +# +# multiple-transaction-setups.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +# This toy example might not make a lot of sense, but you could imagine having some tests or setup +# in between. +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +transaction_setups: + t1_greater_than_10: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_less_than_50 + - setupReference: t1_greater_than_10 + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/reference-and-setup.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/reference-and-setup.yamsql new file mode 100644 index 0000000000..799b54c7ba --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/reference-and-setup.yamsql @@ -0,0 +1,43 @@ +# +# reference-and-setup.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_less_than_50 + - setup: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/setup-and-reference.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/setup-and-reference.yamsql new file mode 100644 index 0000000000..41adfc15a8 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/setup-and-reference.yamsql @@ -0,0 +1,43 @@ +# +# setup-and-reference.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_greater_than_10: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setup: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + - setupReference: t1_greater_than_10 + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/single-setup-reference.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/single-setup-reference.yamsql new file mode 100644 index 0000000000..3d8cc854b0 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/single-setup-reference.yamsql @@ -0,0 +1,41 @@ +# +# single-setup-reference.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_less_than_50 + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/single-setup.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/single-setup.yamsql new file mode 100644 index 0000000000..0991b442ca --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/single-setup.yamsql @@ -0,0 +1,38 @@ +# +# single-setup.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setup: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/transaction-setup-after-setup.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/transaction-setup-after-setup.yamsql new file mode 100644 index 0000000000..e744c763f2 --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/transaction-setup-after-setup.yamsql @@ -0,0 +1,46 @@ +# +# transaction-setup-after-setup.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +# Non-test blocks are executed as they are read, and setup may contain schema install +# which is nice to have before transaction_setups, even if it is unnecessary +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; + t1_greater_than_10: create temporary function t2() on commit drop function AS + SELECT * FROM t1 where id > 10; +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_less_than_50 + - setupReference: t1_greater_than_10 + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transaction-setup/shouldPass/transaction-setup-after-tests.yamsql b/yaml-tests/src/test/resources/transaction-setup/shouldPass/transaction-setup-after-tests.yamsql new file mode 100644 index 0000000000..54c5822d7c --- /dev/null +++ b/yaml-tests/src/test/resources/transaction-setup/shouldPass/transaction-setup-after-tests.yamsql @@ -0,0 +1,49 @@ +# +# transaction-setup-after-tests.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This test should pass +# Test blocks after creating the references can access it, test blocks before cannot +--- +schema_template: + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +setup: + connect: 1 + steps: + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60), (70, 80) +--- +test_block: + name: transactions-tests-before-setup + tests: + - + - query: select * from table_t1 where id > 10 and id < 50; + - result: [{id: 30, col1: 40}] +--- +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50; +--- +test_block: + name: transactions-tests + tests: + - + - query: select * from t1 where id > 10; + - setupReference: t1_less_than_50 + - result: [{id: 30, col1: 40}] +... diff --git a/yaml-tests/src/test/resources/transactions-tests.yamsql b/yaml-tests/src/test/resources/transactions-tests.yamsql index 839964b0fc..7a429f6150 100644 --- a/yaml-tests/src/test/resources/transactions-tests.yamsql +++ b/yaml-tests/src/test/resources/transactions-tests.yamsql @@ -1,5 +1,5 @@ # -# transacitons-tests.yamsql +# transactions-tests.yamsql # # This source file is part of the FoundationDB open source project # @@ -17,41 +17,45 @@ # See the License for the specific language governing permissions and # limitations under the License. -# you can add comments like this one to your scripts. -# -# The file is split into "documents" wherein each document is describes a block. A type of block exhibits a unique -# functional behavior. At the top-level, there are 2 types of blocks: -# - setup_block: These blocks are used to control the environment needed to run the test blocks. For instance, they -# are used to "setup", "populate" and "destruct" the databases and tables. A file can have multiple of these to -# `tweak` the environment as needed by the testing block. -# - test_block: Defines a scope for a group of tests by setting the knobs that determines how those tests are run. -# -# The config block can be of the following types: -# - schema_template: This block carries the definition of the template based on which the database and schemas are setup -# automatically on the execution for the following `test_block` to use. Note that a file can have multiple of these -# blocks, each creating a database and schema with a different name scheme. Consequently, cleaning up of multiple -# of these blocks happens after all the blocks in the file has been executed, in the order of their definition. -# That is, 1st `schema_template` gets cleaned first, then second, then third and so on. -# - setup: This block gives more flexibility to the tester to setup the tests. Compared to `schema_template`, which are -# opaque as to how and where schemas are created, `setup` setup block gives the tester the option to execute -# whatever statements they want in whichever location. +# This test serves as an example of using transaction setup, both via references and inline. --- options: - supported_version: 4.3.5.0 + supported_version: 4.4.7.0 --- schema_template: - create table t1(id bigint, col1 bigint, primary key(id)) + create table table_t1(id bigint, col1 bigint, primary key(id)) +--- +# This document type creates a map of transaction setups that can be referenced in individual queries +# This helps to reduce duplication. +# The map is basically from a name (e.g. t1_less_than_50) to a statement to run at the start of every query. +# They're referenced in the query using `setupReference`. Right now it only supports a single statement as a string, but +# it could be expanded to support an array in the future. +transaction_setups: + t1_less_than_50: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 50 --- setup: - # `connect` is default and defaults to 1. Value 0 has a special meaning and depicts connection to the catalog. connect: 1 steps: - - query: INSERT INTO T1 VALUES (10, 20), (30, 40), (50, 60) + - query: INSERT INTO table_T1 VALUES (10, 20), (30, 40), (50, 60) --- test_block: name: transactions-tests - preset: single_repetition_ordered tests: - + # This query uses an inline transaction setup. It will execute the given statement when it executes the initial + # query. If continuations are used (either explicitly, or via force-continuations), the setup will only + # run on the initial query, not when executing the continuation. + # Right now it only supports a single statement as a string, but it could be expanded to support an arra in the + # future. + - query: select * from t1 where id > 15; + - setup: create temporary function t1() on commit drop function AS + SELECT * FROM table_t1 where id < 30; + - result: [] + - + # This query references the transaction setup from above. This behaves exactly the same as the inline version + # it just allows easier use of the same setup for many queries. - query: select * from t1 where id > 15; + - setupReference: t1_less_than_50 + - result: [{id: 30, col1: 40}] ...