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}]
...