Skip to content

Commit b6776a7

Browse files
authored
Merge pull request #3031 from ScottDugas/mixed-handle-new-features
Ability to prevent tests from running against old versions. With the new mixed-mode tests being run as part of PRB, we'll need a way to say that a test should not run against old versions. This adds support for disabling an entire yaml file. Future steps would be to allow disabling a specific query, providing a specific supported version, and also having the build update the `!current_version` to the version being released.
2 parents 66e0ca4 + 1a1df29 commit b6776a7

16 files changed

+545
-159
lines changed

scripts/YAML-SQL.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@
4747
timezone_;timezone_minute;to;trailing;transaction;translate;translation;trim;type;union;unique;unknown;update;
4848
upper;usage;user;using;value;values;varying;view;when;whenever;where;with;work;write;year;zone" ignore_case="true" />
4949
<keywords2 keywords="bigint;bit;boolean;bytes;char;char_;character;date;dec;decimal;double;float;int;integer;nchar;
50-
numeric;real;smallint;string;time;timestamp;varchar;!!;!r;!in;!a;" />
50+
numeric;real;smallint;string;time;timestamp;varchar;!!;!r;!in;!a;!current_version" />
5151
<keywords3 keywords="false;null;true;ordered;randomized;parallelized;test;block;single_repetition_ordered;
5252
single_repetition_randomized;single_repetition_parallelized;multi_repetition_ordered;multi_repetition_randomized;
5353
multi_repetition_parallelized" />
5454
<keywords4 keywords="connect:;query:;load schema template:;set schema state:;result:;unorderedresult:;explain:;
55-
explaincontains:;count:;error:;planhash:;setup:;schema_template:;test_block:;options:;tests:;mode:;repetition:;seed:;
55+
explaincontains:;count:;error:;planhash:;setup:;schema_template:;supported_version:;test_block:;options:;tests:;mode:;repetition:;seed:;
5656
check_cache:;connection_lifecycle:;steps:;preset:;statement_type:;!r;!in;!a;" />
5757
</highlighting>
5858
<extensionMap>
5959
<mapping ext="yamsql" />
6060
</extensionMap>
61-
</filetype>
61+
</filetype>

yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/CustomYamlConstructor.java

+33-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.apple.foundationdb.relational.api.exceptions.ErrorCode;
2424
import com.apple.foundationdb.relational.util.Assert;
25+
import com.apple.foundationdb.relational.yamltests.block.FileOptions;
2526
import com.apple.foundationdb.relational.yamltests.block.SetupBlock;
2627
import com.apple.foundationdb.relational.yamltests.block.TestBlock;
2728
import com.apple.foundationdb.relational.yamltests.command.Command;
@@ -34,10 +35,13 @@
3435
import org.yaml.snakeyaml.nodes.ScalarNode;
3536
import org.yaml.snakeyaml.nodes.Tag;
3637

37-
import javax.annotation.Nonnull;
3838
import java.util.ArrayList;
3939
import java.util.List;
40+
import java.util.Map;
4041
import java.util.function.Supplier;
42+
import java.util.stream.Collectors;
43+
44+
import javax.annotation.Nonnull;
4145

4246
public class CustomYamlConstructor extends SafeConstructor {
4347

@@ -51,8 +55,10 @@ public CustomYamlConstructor(LoaderOptions loaderOptions) {
5155
yamlConstructors.put(new Tag("!sc"), new ConstructStringContains());
5256
yamlConstructors.put(new Tag("!null"), new ConstructNullPlaceholder());
5357
yamlConstructors.put(new Tag("!not_null"), new ConstructNotNull());
58+
yamlConstructors.put(new Tag("!current_version"), new ConstructCurrentVersion());
5459

5560
//blocks
61+
requireLineNumber.add(FileOptions.OPTIONS);
5662
requireLineNumber.add(SetupBlock.SETUP_BLOCK);
5763
requireLineNumber.add(SetupBlock.SchemaTemplateBlock.SCHEMA_TEMPLATE_BLOCK);
5864
requireLineNumber.add(TestBlock.TEST_BLOCK);
@@ -93,6 +99,24 @@ private LinedObject(final Object object, final int lineNumber) {
9399
this.lineNumber = lineNumber;
94100
}
95101

102+
/**
103+
* Remove the {@link LinedObject} wrappers from keys and create a new map.
104+
* @param blockMap a map from a yaml file that may have keys that had lines added
105+
* @return a new map where the keys do not have lines added
106+
*/
107+
public static Map<?, ?> unlineKeys(Map<?, ?> blockMap) {
108+
return blockMap.entrySet().stream()
109+
.collect(Collectors.toMap(
110+
entry -> {
111+
if (entry.getKey() instanceof LinedObject) {
112+
return ((LinedObject)entry.getKey()).getObject();
113+
} else {
114+
return entry.getKey();
115+
}
116+
},
117+
Map.Entry::getValue));
118+
}
119+
96120
@Nonnull
97121
public Object getObject() {
98122
return object;
@@ -148,4 +172,12 @@ public Object construct(Node node) {
148172
return CustomTag.NotNull.INSTANCE;
149173
}
150174
}
175+
176+
private static class ConstructCurrentVersion extends AbstractConstruct {
177+
178+
@Override
179+
public Object construct(Node node) {
180+
return FileOptions.CurrentVersion.INSTANCE;
181+
}
182+
}
151183
}

yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/MultiServerConnectionFactory.java

+25-20
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,16 @@
2828
import org.apache.logging.log4j.Logger;
2929

3030
import javax.annotation.Nonnull;
31-
import javax.annotation.Nullable;
3231
import java.net.URI;
3332
import java.sql.Array;
3433
import java.sql.DatabaseMetaData;
3534
import java.sql.SQLException;
3635
import java.sql.Struct;
3736
import java.util.ArrayList;
3837
import java.util.List;
38+
import java.util.Set;
3939
import java.util.stream.Collectors;
40+
import java.util.stream.Stream;
4041

4142
/**
4243
* A connection factory that creates a connection that can be used with multiple servers.
@@ -47,6 +48,7 @@
4748
public class MultiServerConnectionFactory implements YamlRunner.YamlConnectionFactory {
4849
// The fixed index of the default connection
4950
public static final int DEFAULT_CONNECTION = 0;
51+
private final Set<String> versionsUnderTest;
5052

5153
/**
5254
* Server selection policy.
@@ -60,42 +62,47 @@ public enum ConnectionSelectionPolicy { DEFAULT, ALTERNATE }
6062
private final int initialConnection;
6163
@Nonnull
6264
private final YamlRunner.YamlConnectionFactory defaultFactory;
63-
@Nullable
65+
@Nonnull
6466
private final List<YamlRunner.YamlConnectionFactory> alternateFactories;
6567

6668
public MultiServerConnectionFactory(@Nonnull final YamlRunner.YamlConnectionFactory defaultFactory,
67-
@Nullable final List<YamlRunner.YamlConnectionFactory> alternateFactories) {
69+
@Nonnull final List<YamlRunner.YamlConnectionFactory> alternateFactories) {
6870
this(ConnectionSelectionPolicy.DEFAULT, 0, defaultFactory, alternateFactories);
6971
}
7072

7173
public MultiServerConnectionFactory(@Nonnull final ConnectionSelectionPolicy connectionSelectionPolicy,
7274
final int initialConnection,
7375
@Nonnull final YamlRunner.YamlConnectionFactory defaultFactory,
74-
@Nullable final List<YamlRunner.YamlConnectionFactory> alternateFactories) {
76+
@Nonnull final List<YamlRunner.YamlConnectionFactory> alternateFactories) {
7577
this.connectionSelectionPolicy = connectionSelectionPolicy;
7678
this.initialConnection = initialConnection;
7779
this.defaultFactory = defaultFactory;
7880
this.alternateFactories = alternateFactories;
81+
this.versionsUnderTest =
82+
Stream.concat(Stream.of(defaultFactory), alternateFactories.stream())
83+
.flatMap(yamlConnectionFactory -> yamlConnectionFactory.getVersionsUnderTest().stream())
84+
.collect(Collectors.toSet());
7985
}
8086

8187
@Override
8288
public RelationalConnection getNewConnection(@Nonnull URI connectPath) throws SQLException {
8389
return new MultiServerRelationalConnection(connectionSelectionPolicy, initialConnection, defaultFactory.getNewConnection(connectPath), alternateConnections(connectPath));
8490
}
8591

86-
@Nullable
92+
@Override
93+
public Set<String> getVersionsUnderTest() {
94+
return versionsUnderTest;
95+
}
96+
97+
@Nonnull
8798
private List<RelationalConnection> alternateConnections(URI connectPath) {
88-
if (alternateFactories == null) {
89-
return null;
90-
} else {
91-
return alternateFactories.stream().map(factory -> {
92-
try {
93-
return factory.getNewConnection(connectPath);
94-
} catch (SQLException e) {
95-
throw new IllegalStateException("Failed to create a connection", e);
96-
}
97-
}).collect(Collectors.toList());
98-
}
99+
return alternateFactories.stream().map(factory -> {
100+
try {
101+
return factory.getNewConnection(connectPath);
102+
} catch (SQLException e) {
103+
throw new IllegalStateException("Failed to create a connection", e);
104+
}
105+
}).collect(Collectors.toList());
99106
}
100107

101108
/**
@@ -115,15 +122,13 @@ public static class MultiServerRelationalConnection implements RelationalConnect
115122
public MultiServerRelationalConnection(@Nonnull ConnectionSelectionPolicy connectionSelectionPolicy,
116123
final int initialConnecion,
117124
@Nonnull final RelationalConnection defaultConnection,
118-
@Nullable List<RelationalConnection> alternateConnections) {
125+
@Nonnull List<RelationalConnection> alternateConnections) {
119126
this.connectionSelectionPolicy = connectionSelectionPolicy;
120127
this.currentConnectionSelector = initialConnecion;
121128
allConnections = new ArrayList<>();
122129
// The default connection is always the one at location 0
123130
allConnections.add(defaultConnection);
124-
if (alternateConnections != null) {
125-
allConnections.addAll(alternateConnections);
126-
}
131+
allConnections.addAll(alternateConnections);
127132
}
128133

129134
@Override

yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlExecutionContext.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
import org.apache.logging.log4j.LogManager;
2929
import org.apache.logging.log4j.Logger;
3030

31-
import javax.annotation.Nonnull;
32-
import javax.annotation.Nullable;
3331
import java.io.BufferedReader;
3432
import java.io.IOException;
3533
import java.io.InputStreamReader;
@@ -40,6 +38,9 @@
4038
import java.util.Optional;
4139
import java.util.function.Supplier;
4240

41+
import javax.annotation.Nonnull;
42+
import javax.annotation.Nullable;
43+
4344
@SuppressWarnings({"PMD.GuardLogStatement"}) // It already is, but PMD is confused and reporting error in unrelated locations.
4445
public final class YamlExecutionContext {
4546

yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/YamlRunner.java

+19
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import java.util.ArrayList;
4848
import java.util.List;
4949
import java.util.Optional;
50+
import java.util.Set;
5051

5152
@SuppressWarnings({"PMD.GuardLogStatement"}) // It already is, but PMD is confused and reporting error in unrelated locations.
5253
public final class YamlRunner {
@@ -65,7 +66,25 @@ public final class YamlRunner {
6566
private final YamlExecutionContext executionContext;
6667

6768
public interface YamlConnectionFactory {
69+
/**
70+
* Convert a connection uri into an actual connection.
71+
* @param connectPath the path to connect to
72+
* @return A new {@link RelationalConnection} for the given path appropriate for this test class
73+
* @throws SQLException if we cannot connect
74+
*/
6875
RelationalConnection getNewConnection(@Nonnull URI connectPath) throws SQLException;
76+
77+
/**
78+
* The versions that the connection has, other than the current code.
79+
* <p>
80+
* If we are just testing against the current code, this will be empty, but otherwise it will include the
81+
* versions that we're testing. In the future we may want to support tests that don't run against the
82+
* current version, but that's not currently needed, so not supported.
83+
* </p>
84+
* @return A set of versions that we are testing against, or an empty set if just testing against the current
85+
* version
86+
*/
87+
Set<String> getVersionsUnderTest();
6988
}
7089

7190
public YamlRunner(@Nonnull String resourcePath, @Nonnull YamlConnectionFactory factory, boolean correctExplain) throws RelationalException {

yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/block/Block.java

+16-69
Original file line numberDiff line numberDiff line change
@@ -20,98 +20,36 @@
2020

2121
package com.apple.foundationdb.relational.yamltests.block;
2222

23-
import com.apple.foundationdb.relational.api.RelationalConnection;
2423
import com.apple.foundationdb.relational.util.Assert;
2524
import com.apple.foundationdb.relational.yamltests.CustomYamlConstructor;
2625
import com.apple.foundationdb.relational.yamltests.Matchers;
2726
import com.apple.foundationdb.relational.yamltests.YamlExecutionContext;
2827

29-
import org.apache.logging.log4j.LogManager;
30-
import org.apache.logging.log4j.Logger;
31-
3228
import javax.annotation.Nonnull;
33-
import java.net.URI;
34-
import java.sql.SQLException;
35-
import java.util.Collection;
36-
import java.util.List;
37-
import java.util.function.Consumer;
3829

3930
/**
40-
* Block is a single region in the YAMSQL file that can either be a {@link SetupBlock} or {@link TestBlock}.
31+
* Block is a single region in the YAMSQL file that can either be a
32+
* {@link FileOptions}, {@link SetupBlock} or {@link TestBlock}.
4133
* <ul>
34+
* <li> {@link FileOptions}: Controls whether a file should be run at all, and other configuration that runs before
35+
* creating the connection.</li>
4236
* <li> {@link SetupBlock}: It can be either a `setup` block or a `destruct` block. The motive of these block
4337
* is to "setup" and "clean" the environment needed to run the `test-block`s. A Setup block consist of a list
4438
* of commands.</li>
4539
* <li> {@link TestBlock}: Defines a scope for a group of tests by setting the knobs that determines how those
4640
* tests are run.</li>
4741
* </ul>
48-
* <p>
49-
* Each block needs to be associated with a `connectionURI` which provides it with the address to the database to which
50-
* the block connects to for running its executables. The block does so through the
51-
* {@link com.apple.foundationdb.relational.yamltests.YamlRunner.YamlConnectionFactory}. A block is free to implement `how` and `when`
52-
* it wants to use the factory to create a connection and also manages the lifecycle of the established connection.
5342
*/
54-
@SuppressWarnings({"PMD.GuardLogStatement"})
55-
public abstract class Block {
56-
57-
private static final Logger logger = LogManager.getLogger(Block.class);
58-
59-
static final String BLOCK_CONNECT = "connect";
60-
61-
int lineNumber;
62-
@Nonnull
63-
YamlExecutionContext executionContext;
64-
@Nonnull
65-
private final URI connectionURI;
66-
@Nonnull
67-
final List<Consumer<RelationalConnection>> executables;
68-
69-
Block(int lineNumber, @Nonnull List<Consumer<RelationalConnection>> executables, @Nonnull URI connectionURI, @Nonnull YamlExecutionContext executionContext) {
70-
this.lineNumber = lineNumber;
71-
this.executables = executables;
72-
this.connectionURI = connectionURI;
73-
this.executionContext = executionContext;
74-
}
75-
76-
public int getLineNumber() {
77-
return lineNumber;
78-
}
79-
80-
/**
81-
* Executes the executables from the parsed block in a single connection.
82-
*/
83-
public abstract void execute();
84-
85-
protected final void executeExecutables(@Nonnull Collection<Consumer<RelationalConnection>> list) {
86-
connectToDatabaseAndExecute(connection -> list.forEach(t -> t.accept(connection)));
87-
}
88-
89-
/**
90-
* Tries connecting to a database and execute the consumer using the established connection.
91-
*
92-
* @param consumer operations to be performed on the database using the established connection.
93-
*/
94-
void connectToDatabaseAndExecute(Consumer<RelationalConnection> consumer) {
95-
logger.debug("🚠 Connecting to database: `{}`", connectionURI);
96-
try (var connection = executionContext.getConnectionFactory().getNewConnection(connectionURI)) {
97-
logger.debug("✅ Connected to database: `{}`", connectionURI);
98-
consumer.accept(connection);
99-
} catch (SQLException sqle) {
100-
throw executionContext.wrapContext(sqle,
101-
() -> String.format("‼️ Error connecting to the database `%s` in block at line %d", connectionURI, lineNumber),
102-
"connection [" + connectionURI + "]", getLineNumber());
103-
}
104-
}
105-
43+
public interface Block {
10644
/**
10745
* Looks at the block to determine if its one of the valid blocks. If it is a valid one, parses it to that. This
10846
* method dispatches the execution to the right block which takes care of reading from the block and initializing
109-
* the correctly configured {@link Block} for execution.
47+
* the correctly configured {@link ConnectedBlock} for execution.
11048
*
11149
* @param document a region in the file
11250
* @param executionContext information needed to carry out the execution
11351
*/
114-
public static Block parse(@Nonnull Object document, @Nonnull YamlExecutionContext executionContext) {
52+
static Block parse(@Nonnull Object document, @Nonnull YamlExecutionContext executionContext) {
11553
final var blockObject = Matchers.map(document, "block");
11654
Assert.thatUnchecked(blockObject.size() == 1, "Illegal Format: A block is expected to be a map of size 1");
11755
final var entry = Matchers.firstEntry(blockObject, "block key-value");
@@ -124,8 +62,17 @@ public static Block parse(@Nonnull Object document, @Nonnull YamlExecutionContex
12462
return TestBlock.parse(lineNumber, entry.getValue(), executionContext);
12563
case SetupBlock.SchemaTemplateBlock.SCHEMA_TEMPLATE_BLOCK:
12664
return SetupBlock.SchemaTemplateBlock.parse(lineNumber, entry.getValue(), executionContext);
65+
case FileOptions.OPTIONS:
66+
return FileOptions.parse(lineNumber, entry.getValue(), executionContext);
12767
default:
12868
throw new RuntimeException("Cannot recognize the type of block");
12969
}
13070
}
71+
72+
int getLineNumber();
73+
74+
/**
75+
* Executes the executables from the parsed block in a single connection.
76+
*/
77+
void execute();
13178
}

0 commit comments

Comments
 (0)