diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java index 8248d49cdea..7cde20d1e32 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java @@ -38,7 +38,9 @@ import com.apple.foundationdb.relational.recordlayer.metadata.DataTypeUtils; import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerInvokedRoutine; import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerSchemaTemplate; +import com.apple.foundationdb.relational.recordlayer.query.cache.PlanCacheSchemaKey; import com.apple.foundationdb.relational.recordlayer.query.cache.QueryCacheKey; +import com.google.common.collect.ImmutableSortedMap; import com.apple.foundationdb.relational.recordlayer.util.ExceptionUtil; import com.apple.foundationdb.relational.util.Assert; import com.google.common.annotations.VisibleForTesting; @@ -639,10 +641,10 @@ public static NormalizationResult normalizeAst(@Nonnull final SchemaTemplate sch } } return new NormalizationResult( - recordLayerSchemaTemplate.getName(), + PlanCacheSchemaKey.of(recordLayerSchemaTemplate.getName()), QueryCacheKey.of(astNormalizer.getCanonicalSqlString(), getQuerySpecificPlannerConfig(plannerConfiguration, astNormalizer.getQueryOptions()), recordLayerSchemaTemplate.getTransactionBoundMetadataAsString(), - recordLayerSchemaTemplate.getVersion()), + ImmutableSortedMap.of(recordLayerSchemaTemplate.getName(), recordLayerSchemaTemplate.getVersion())), astNormalizer.getQueryExecutionParameters(), parseTreeInfo.getRootContext(), astNormalizer.getQueryCachingFlags(), @@ -686,7 +688,7 @@ public enum QueryCachingFlags { } @Nonnull - private final String schemaTemplateName; + private final PlanCacheSchemaKey schemaKey; @Nonnull private final QueryCacheKey queryCacheKey; @@ -706,14 +708,14 @@ public enum QueryCachingFlags { @Nonnull private final String query; - public NormalizationResult(@Nonnull final String schemaTemplateName, + public NormalizationResult(@Nonnull final PlanCacheSchemaKey schemaKey, @Nonnull final QueryCacheKey queryCacheKey, @Nonnull final QueryExecutionContext queryExecutionContext, @Nonnull final ParseTree parseTree, @Nonnull final Set queryCachingFlags, @Nonnull final Options queryOptions, @Nonnull final String query) { - this.schemaTemplateName = schemaTemplateName; + this.schemaKey = schemaKey; this.queryCacheKey = queryCacheKey; this.queryExecutionContext = queryExecutionContext; this.parseTree = parseTree; @@ -723,8 +725,8 @@ public NormalizationResult(@Nonnull final String schemaTemplateName, } @Nonnull - public String getSchemaTemplateName() { - return schemaTemplateName; + public PlanCacheSchemaKey getSchemaKey() { + return schemaKey; } @Nonnull diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/PlanGenerator.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/PlanGenerator.java index 047c2a81420..08cc21a067d 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/PlanGenerator.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/PlanGenerator.java @@ -186,7 +186,7 @@ private Plan getPlanInternal(@Nonnull String query, @Nonnull KeyValueLogMessa final var planEquivalence = PhysicalPlanEquivalence.of(astHashResult.getQueryExecutionContext().getEvaluationContext()); return planContext.getMetricsCollector().clock(RelationalMetric.RelationalEvent.CACHE_LOOKUP, () -> cache.get().reduce( - astHashResult.getSchemaTemplateName(), + astHashResult.getSchemaKey(), astHashResult.getQueryCacheKey(), planEquivalence, () -> { diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/PlanCacheSchemaKey.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/PlanCacheSchemaKey.java new file mode 100644 index 00000000000..0fcfe6cfd33 --- /dev/null +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/PlanCacheSchemaKey.java @@ -0,0 +1,84 @@ +/* + * PlanCacheSchemaKey.java + * + * 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. + */ + +package com.apple.foundationdb.relational.recordlayer.query.cache; + +import com.apple.foundationdb.annotation.API; +import com.google.common.collect.ImmutableSortedSet; + +import javax.annotation.Nonnull; +import java.util.Collection; + +/** + * Primary key for {@link RelationalPlanCache}. Represents the set of schema template names that a + * cached plan was compiled against. Single-schema queries produce a one-element set; cross-schema + * queries produce a multi-element set. + *

+ * The set is always sorted so that {@code {"a","b"}} and {@code {"b","a"}} map to the same bucket. + */ +@API(API.Status.EXPERIMENTAL) +public final class PlanCacheSchemaKey { + + @Nonnull + private final ImmutableSortedSet schemaNames; + + private final int memoizedHashCode; + + private PlanCacheSchemaKey(@Nonnull final ImmutableSortedSet schemaNames) { + this.schemaNames = schemaNames; + this.memoizedHashCode = schemaNames.hashCode(); + } + + @Nonnull + public ImmutableSortedSet getSchemaNames() { + return schemaNames; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + return schemaNames.equals(((PlanCacheSchemaKey) other).schemaNames); + } + + @Override + public int hashCode() { + return memoizedHashCode; + } + + @Override + public String toString() { + return String.join("|", schemaNames); + } + + @Nonnull + public static PlanCacheSchemaKey of(@Nonnull final String schemaName) { + return new PlanCacheSchemaKey(ImmutableSortedSet.of(schemaName)); + } + + @Nonnull + public static PlanCacheSchemaKey of(@Nonnull final Collection schemaNames) { + return new PlanCacheSchemaKey(ImmutableSortedSet.copyOf(schemaNames)); + } +} diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/QueryCacheKey.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/QueryCacheKey.java index d42b8a174ad..38c1520c9fc 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/QueryCacheKey.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/QueryCacheKey.java @@ -24,6 +24,7 @@ import com.apple.foundationdb.relational.recordlayer.query.AstNormalizer; import com.apple.foundationdb.relational.recordlayer.query.PlannerConfiguration; +import com.google.common.collect.ImmutableSortedMap; import javax.annotation.Nonnull; import java.util.Objects; @@ -32,53 +33,13 @@ * This is used to look up a plan in the primary cache (see {@link MultiStageCache} for more information). * It comprises the following fields: *

- * Example1 - *
- * Let us assume we have two schema templates ({@code s1} and {@code s2}) defined as the following: - *
- * {@code
- * create schema template s1
- * create table t1(id bigint, col1 bigint, primary key(id))
- *
- * create schema template s2
- * create table t1(id bigint, col1 bigint, col3 bigint, primary key(id))
- * }
- * 
- * If we run a query like this: - *
- * {@code
- * create schema /FRL/YOUSSEF/s1s with s1
- * connect: "jdbc:embed:/FRL/YOUSSEF?schema=S1S"
- * select * from t1 where col1 > 42;
- * }
- * 
- * we would compile the query and return a result set comprising two columns {@code id, col1}. - * we will also cache this query using a {@link QueryCacheKey} key of something like {@code "s1", "select * from t1 where col1 > ? ", 123456789} - *
- * if we run the same query, however after connecting to a schema that uses a {@code s2} instead: - *
- * {@code
- * create schema /FRL/YOUSSEF/s2s with s2
- * connect: "jdbc:embed:/FRL/YOUSSEF?schema=S2S"
- * select * from t1 where col1 > 53;
- * }
- * 
- * we would correctly compile this query and return a result set comprising three columns {@code id, col1, col2}. - * we will also cache this query using {@link QueryCacheKey} key of something like {@code "s2", "select * from t1 where col1 > ? ", 123456789} - *
- * Note that without having the schema template name as part of the {@link QueryCacheKey} both keys would be identical. - * Therefore, we might incorrectly choose the compiled plan of the first query to execute the second query (because we find - * a match in the cache) causing an error since the result set structure is different because table {@code T1} is defined - * differently in {@code S1} and {@code S2}. - *
- *
- * Example 2 + * Example *
* Although these queries appear different, their canonical representation is the same, and will end up using the same * compiled plan. @@ -108,23 +69,25 @@ public final class QueryCacheKey { @Nonnull private final String auxiliaryMetadata; - private final int schemaTemplateVersion; + /** + * Maps each participating schema template name to its version. A cache miss is triggered when + * any entry in this map changes, which is the correct behaviour for cross-schema queries. + */ + @Nonnull + private final ImmutableSortedMap schemaVersions; private final int memoizedHashCode; private QueryCacheKey(@Nonnull final String canonicalQueryString, @Nonnull final PlannerConfiguration plannerConfiguration, @Nonnull final String auxiliaryMetadata, - int schemaTemplateVersion) { + @Nonnull final ImmutableSortedMap schemaVersions) { this.canonicalQueryString = canonicalQueryString; - this.schemaTemplateVersion = schemaTemplateVersion; + this.schemaVersions = schemaVersions; this.auxiliaryMetadata = auxiliaryMetadata; this.plannerConfiguration = plannerConfiguration; - // Memoize the hash code. Because this object is used as a key in a hash map, it is important that - // hashCode() be quick. Note that this includes information about the query (canonicalQueryString is like the query hash), - // the schema template version, and the schema (like the set of readable indexes) - this.memoizedHashCode = Objects.hash(canonicalQueryString, schemaTemplateVersion, plannerConfiguration, auxiliaryMetadata); + this.memoizedHashCode = Objects.hash(canonicalQueryString, schemaVersions, plannerConfiguration, auxiliaryMetadata); } @Override @@ -136,10 +99,10 @@ public boolean equals(Object other) { return false; } final var that = (QueryCacheKey) other; - return schemaTemplateVersion == that.schemaTemplateVersion && - Objects.equals(canonicalQueryString, that.canonicalQueryString) && + return Objects.equals(canonicalQueryString, that.canonicalQueryString) && Objects.equals(auxiliaryMetadata, that.auxiliaryMetadata) && - Objects.equals(plannerConfiguration, that.plannerConfiguration); + Objects.equals(plannerConfiguration, that.plannerConfiguration) && + Objects.equals(schemaVersions, that.schemaVersions); } @Override @@ -152,8 +115,9 @@ public String getCanonicalQueryString() { return canonicalQueryString; } - public int getSchemaTemplateVersion() { - return schemaTemplateVersion; + @Nonnull + public ImmutableSortedMap getSchemaVersions() { + return schemaVersions; } @Nonnull @@ -168,14 +132,14 @@ public String getAuxiliaryMetadata() { @Override public String toString() { - return "(" + schemaTemplateVersion + " || " + auxiliaryMetadata + ")" + "||" + canonicalQueryString + "||" + memoizedHashCode; + return "(" + schemaVersions + " || " + auxiliaryMetadata + ")" + "||" + canonicalQueryString + "||" + memoizedHashCode; } @Nonnull public static QueryCacheKey of(@Nonnull final String query, @Nonnull final PlannerConfiguration plannerConfiguration, @Nonnull final String auxiliaryMetadata, - int schemaTemplateVersion) { - return new QueryCacheKey(query, plannerConfiguration, auxiliaryMetadata, schemaTemplateVersion); + @Nonnull final ImmutableSortedMap schemaVersions) { + return new QueryCacheKey(query, plannerConfiguration, auxiliaryMetadata, schemaVersions); } } diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCache.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCache.java index 13e8192d8a8..d93fc8e43e2 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCache.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCache.java @@ -34,7 +34,7 @@ * This is just a specialization of {@link MultiStageCache} with concrete types specific to plan caching. */ @API(API.Status.EXPERIMENTAL) -public final class RelationalPlanCache extends MultiStageCache> { +public final class RelationalPlanCache extends MultiStageCache> { @Nonnull private static final TimeUnit DEFAULT_TTL_TIME_UNIT = TimeUnit.MILLISECONDS; @@ -61,7 +61,7 @@ private RelationalPlanCache(int size, super(size, secondarySize, tertiarySize, ttl, ttlTimeUnit, secondaryTtl, secondaryTtlTimeUnit, tertiaryTtl, tertiaryTtlTimeUnit, executor, secondaryExecutor, tertiaryExecutor, ticker); } - public static final class RelationalCacheBuilder extends MultiStageCache.Builder, RelationalCacheBuilder> { + public static final class RelationalCacheBuilder extends MultiStageCache.Builder, RelationalCacheBuilder> { public RelationalCacheBuilder() { size = (Integer) (Options.defaultOptions().get(Options.Name.PLAN_CACHE_PRIMARY_MAX_ENTRIES)); diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java index ac4df6a9b77..745e2b16f2c 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java @@ -39,6 +39,7 @@ import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerSchemaTemplate; import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerTable; import com.apple.foundationdb.relational.recordlayer.query.cache.QueryCacheKey; +import com.google.common.collect.ImmutableSortedMap; import com.apple.foundationdb.relational.recordlayer.query.functions.CompiledSqlFunction; import com.apple.foundationdb.relational.recordlayer.util.Hex; import com.apple.foundationdb.relational.util.Assert; @@ -1635,8 +1636,9 @@ void visitFullDescribeStatementThrows() throws ReflectiveOperationException { @Test void queryCacheKeyToString() { - final var key = QueryCacheKey.of("select ? from testTable", plannerConfiguration, "someAuxiliaryMetadata", 3); - final var expected = "(3 || someAuxiliaryMetadata)||select ? from testTable||" + key.hashCode(); + final var key = QueryCacheKey.of("select ? from testTable", plannerConfiguration, "someAuxiliaryMetadata", + ImmutableSortedMap.of("testSchema", 3)); + final var expected = "({testSchema=3} || someAuxiliaryMetadata)||select ? from testTable||" + key.hashCode(); Assertions.assertThat(key).hasToString(expected); } } diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StoredQueriesTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StoredQueriesTest.java index a288deb9183..977c20b350e 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StoredQueriesTest.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/StoredQueriesTest.java @@ -27,6 +27,7 @@ import com.apple.foundationdb.relational.recordlayer.EmbeddedRelationalConnection; import com.apple.foundationdb.relational.recordlayer.EmbeddedRelationalExtension; import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerSchemaTemplate; +import com.apple.foundationdb.relational.recordlayer.query.cache.PlanCacheSchemaKey; import com.apple.foundationdb.relational.recordlayer.query.cache.QueryCacheKey; import com.apple.foundationdb.relational.recordlayer.query.cache.RelationalPlanCache; import com.apple.foundationdb.relational.utils.ConnectionUtils; @@ -130,8 +131,9 @@ private long countCachedPlans(RelationalConnection connection, String templateNa return 0; } long total = 0; - for (QueryCacheKey secondaryKey : cache.getStats().getAllSecondaryKeys(templateName)) { - total += cache.getStats().getAllTertiaryMappings(templateName, secondaryKey).size(); + final var schemaKey = PlanCacheSchemaKey.of(templateName); + for (QueryCacheKey secondaryKey : cache.getStats().getAllSecondaryKeys(schemaKey)) { + total += cache.getStats().getAllTertiaryMappings(schemaKey, secondaryKey).size(); } return total; } diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/cache/PlanCacheSchemaKeyTest.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/cache/PlanCacheSchemaKeyTest.java new file mode 100644 index 00000000000..66debd3a4d1 --- /dev/null +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/cache/PlanCacheSchemaKeyTest.java @@ -0,0 +1,71 @@ +/* + * PlanCacheSchemaKeyTest.java + * + * 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. + */ + +package com.apple.foundationdb.relational.recordlayer.query.cache; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class PlanCacheSchemaKeyTest { + + @Test + void ofCollectionProducesSortedKey() { + final List names = Arrays.asList("schema_b", "schema_a", "schema_c"); + final PlanCacheSchemaKey key = PlanCacheSchemaKey.of(names); + assertEquals(List.of("schema_a", "schema_b", "schema_c"), List.copyOf(key.getSchemaNames())); + } + + @Test + void ofCollectionEqualsSingletonOfForSingleElement() { + final PlanCacheSchemaKey fromCollection = PlanCacheSchemaKey.of(List.of("my_schema")); + final PlanCacheSchemaKey fromSingle = PlanCacheSchemaKey.of("my_schema"); + assertEquals(fromSingle, fromCollection); + assertEquals(fromSingle.hashCode(), fromCollection.hashCode()); + } + + @Test + void ofCollectionOrderIndependent() { + final PlanCacheSchemaKey k1 = PlanCacheSchemaKey.of(Arrays.asList("alpha", "beta")); + final PlanCacheSchemaKey k2 = PlanCacheSchemaKey.of(Arrays.asList("beta", "alpha")); + assertEquals(k1, k2); + assertEquals(k1.hashCode(), k2.hashCode()); + } + + @Test + void toStringSingleSchema() { + assertEquals("my_schema", PlanCacheSchemaKey.of("my_schema").toString()); + } + + @Test + void toStringMultiSchemaJoinedWithPipe() { + final PlanCacheSchemaKey key = PlanCacheSchemaKey.of(Arrays.asList("schema_b", "schema_a")); + assertEquals("schema_a|schema_b", key.toString()); + } + + @Test + void differentSchemasDontMatch() { + assertNotEquals(PlanCacheSchemaKey.of("schema_x"), PlanCacheSchemaKey.of("schema_y")); + } +} diff --git a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCacheTests.java b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCacheTests.java index 579b303e599..a791b3c04e9 100644 --- a/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCacheTests.java +++ b/fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/cache/RelationalPlanCacheTests.java @@ -55,6 +55,7 @@ import com.apple.foundationdb.relational.utils.TestSchemas; import com.apple.test.BooleanSource; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSortedMap; import com.google.common.testing.FakeTicker; import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.Assertions; @@ -405,12 +406,12 @@ private static QueryPlanConstraint cons(@Nonnull final QueryPlanConstraint... co private static void shouldBe(@Nonnull final RelationalPlanCache cache, @Nonnull final Map> expectedLayout) { Map> result = new HashMap<>(); - for (String key : cache.getStats().getAllKeys()) { + for (PlanCacheSchemaKey key : cache.getStats().getAllKeys()) { for (QueryCacheKey secondaryKey : cache.getStats().getAllSecondaryKeys(key)) { final var resMap = result.computeIfAbsent( new Tuple(secondaryKey.getCanonicalQueryString(), key, - secondaryKey.getSchemaTemplateVersion(), + secondaryKey.getSchemaVersions(), secondaryKey.getPlannerConfiguration(), secondaryKey.getAuxiliaryMetadata()), k -> new HashMap<>()); @@ -508,13 +509,13 @@ void testCachingDifferentQueries() throws Exception { // adding an entry to the cache to warm it up. planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // let's add an identical query with different boundaries, so we can use a different index (i1980). // however, we end up with in the same primary cache bucket because the primary key is identical. planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1980 AND YEAR < 1985", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1980); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of( ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(cons(c1980Cp0(7), c1980Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1980))); @@ -522,11 +523,11 @@ void testCachingDifferentQueries() throws Exception { // let's try another query, now the query itself is different -> must be a different entry in the _primary_ cache. planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1980 OR YEAR < 1985", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), Scan); shouldBe(cache, Map.of( - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of( ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(cons(c1980Cp0(7), c1980Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1980), - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? OR \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? OR \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), // tautology is expected since a primary scan accepts everything. Map.of(ppe(tautology, cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), Scan) )); @@ -544,7 +545,7 @@ void testKeywordCaseSharesCacheEntry() throws Exception { // keyword case is normalized in the canonical form. The cache therefore still has a single entry. planQuery(cache, "sELEct * frOm BOOKS wHeRe YEAR > 1970 aNd YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); } @@ -555,16 +556,16 @@ void testCachingDifferentSchemaTemplateNames() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues exactly the same query, the environment is identical to first query, but the schema template is different ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_2", 10, Set.of(i1970, i1980), i1970); shouldBe(cache, Map.of( - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970), // so it is added as a separate entry in the main cache - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_2", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_2"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_2", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); } @@ -575,16 +576,16 @@ void testCachingDifferentSchemaTemplateVersions() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues exactly the same query, the environment is identical to first query, but the schema template version is different ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 11, Set.of(i1970, i1980), i1970); shouldBe(cache, Map.of( - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970), // so it is added as a separate entry in the main cache - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 11, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 11), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); } @@ -595,7 +596,7 @@ void testCachingDifferentReadableIndexes() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues exactly the same query, the environment is identical to first query, but the readable indexes set is different ... @@ -603,10 +604,10 @@ void testCachingDifferentReadableIndexes() throws Exception { // in that case, we want to make sure that we re-plan the query giving the optimizer a chance to consider the newly created index for producing a better plan. planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1970); shouldBe(cache, Map.of( - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970), // so it is added as a separate entry in the main cache - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); } @@ -617,12 +618,12 @@ void testCachingDifferentConstraints() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues exactly the same query, the environment is identical to first query, but which predicates that fall outside the ranges of the chosen index of the first query planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1980 AND YEAR < 1983", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1980); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of( ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(cons(c1980Cp0(7), c1980Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1980))); @@ -635,7 +636,7 @@ void testEvictionFromPrimaryCache() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // 10 MS TTL for primary cache, if we pass 11 -> item must be evicted from primary cache. @@ -650,24 +651,24 @@ void testEvictionFromPrimaryCacheWithLru() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues a different query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 OR YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), Scan); shouldBe(cache, Map.of( - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970), // ... which is added as a separate entry in the main cache - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? OR \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? OR \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(tautology, cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), Scan))); // customer 3 issues yet a different query, cache size is two, evicts an item ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), Scan); shouldBe(cache, Map.of( - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(tautology, cons(ofTypeIntCp0(7), isNotNullInt(7))), Scan), - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); } @@ -678,7 +679,7 @@ void testEvictionFromSecondaryCacheRemovesPrimaryKeyWhenEmpty() throws Exception // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // 10 MS TTL for primary cache, 5 MS TTL for secondary cache, if we pass 7 -> item must be evicted from secondary cache @@ -699,7 +700,7 @@ void testEvictionFromTertiaryCache() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // pass some time, so we have some jitter between secondary cache items necessary for producing deterministic test results. @@ -707,7 +708,7 @@ void testEvictionFromTertiaryCache() throws Exception { // customer 2 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1980 AND YEAR < 1985", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1980); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of( ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(cons(c1980Cp0(7), c1980Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1980))); @@ -715,7 +716,7 @@ void testEvictionFromTertiaryCache() throws Exception { // 10 MS TTL for primary cache, 5 MS TTL for secondary cache, we already passed 2, now if pass 3 more we'll // evict the first cached item in the secondary cache, but _not_ the more recent one. ticker.advance(Duration.of(3, ChronoUnit.MILLIS)); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1980Cp0(7), c1980Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1980))); } @@ -726,12 +727,12 @@ void testEvictionFromSecondaryCacheWithLru() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1980 AND YEAR < 1985", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1980); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), ""), Map.of( ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(cons(c1980Cp0(7), c1980Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1980))); @@ -739,7 +740,7 @@ void testEvictionFromSecondaryCacheWithLru() throws Exception { // secondary cache has capacity of two, attempting to add a third item causes an eviction (LRU). // customer 3 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1990 AND YEAR < 1992", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1990); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), ""), Map.of( ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(cons(c1990Cp0(7), c1990Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1990))); @@ -752,7 +753,7 @@ void testEvictionFromTertiaryCacheRemovesSecondaryKeyWhenEmpty() throws Exceptio // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); @@ -767,7 +768,7 @@ void testEvictionFromTertiaryCacheRemovesSecondaryKeyWhenEmpty() throws Exceptio cache.cleanUp(); // Only one secondary key, the first one was removed - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(tautology, cons(ofTypeIntCp0(7), isNotNullInt(7))), Scan))); } @@ -778,14 +779,14 @@ void testPlanReductionViaCosting() throws Exception { // customer 1 issues a query ... planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues a query that forces a "bad" plan within the same secondary cache entry of the plan above // because of scan boundaries that fall outside the range of the filtered index planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 2005 AND YEAR < 2010", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), Scan); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(tautology, cons(ofTypeIntCp0(7), ofTypeIntCp1(11), @@ -793,7 +794,7 @@ void testPlanReductionViaCosting() throws Exception { // we should still get back the "good" plan (scanning i1970) planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970, ppe(tautology, cons(ofTypeIntCp0(7), ofTypeIntCp1(11), @@ -810,7 +811,7 @@ void testConstraintsWithTemporaryFunction() throws Exception { "CREATE TEMPORARY FUNCTION SCI_FI_BOOKS_OF_80S() ON COMMIT DROP FUNCTION AS SELECT * FROM SCI_FI_BOOKS() WHERE YEAR > 1980 AND YEAR < 1989"), "SELECT * FROM SCI_FI_BOOKS_OF_80S()", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1980); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"SCI_FI_BOOKS_OF_80S\" ( ) ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), "CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS\" ( ) " + + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"SCI_FI_BOOKS_OF_80S\" ( ) ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), "CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"BOOKS\" WHERE \"TITLE\" LIKE ? ||CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS_OF_80S\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"SCI_FI_BOOKS\" ( ) WHERE \"YEAR\" > ? AND \"YEAR\" < ? "), Map.of(ppe(cons( @@ -837,7 +838,7 @@ void testConstraintsWithTemporaryFunctionsIncludingUnusedOnes() throws Exception "CREATE TEMPORARY FUNCTION SCI_FI_BOOKS_OF_80S() ON COMMIT DROP FUNCTION AS SELECT * FROM SCI_FI_BOOKS() WHERE YEAR > 1980 AND YEAR < 1989"), "SELECT * FROM SCI_FI_BOOKS_OF_80S()", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1980); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"SCI_FI_BOOKS_OF_80S\" ( ) ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), "CREATE TEMPORARY FUNCTION \"OTHER_BOOKS\" ( ) " + + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"SCI_FI_BOOKS_OF_80S\" ( ) ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), "CREATE TEMPORARY FUNCTION \"OTHER_BOOKS\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"BOOKS\" WHERE \"TITLE\" LIKE ? ||CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"BOOKS\" WHERE \"TITLE\" LIKE ? ||CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS_OF_80S\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"SCI_FI_BOOKS\" ( ) WHERE \"YEAR\" > ? AND \"YEAR\" < ? "), @@ -862,7 +863,7 @@ void testConstraintsWithTemporaryFunctionsMultipleReferences() throws Exception "CREATE TEMPORARY FUNCTION SCI_FI_BOOKS_OF_80S() ON COMMIT DROP FUNCTION AS SELECT * FROM SCI_FI_BOOKS() WHERE YEAR > 1980 AND YEAR < 1989"), "SELECT * FROM SCI_FI_BOOKS_OF_80S(), SCI_FI_BOOKS_OF_80S()", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1980); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"SCI_FI_BOOKS_OF_80S\" ( ) , \"SCI_FI_BOOKS_OF_80S\" ( ) ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"SCI_FI_BOOKS_OF_80S\" ( ) , \"SCI_FI_BOOKS_OF_80S\" ( ) ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), "CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"BOOKS\" WHERE \"TITLE\" LIKE ? ||CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS_OF_80S\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"SCI_FI_BOOKS\" ( ) WHERE \"YEAR\" > ? AND \"YEAR\" < ? "), @@ -889,7 +890,7 @@ void testConstraintsWithTemporaryFunctionsMultipleLiterals() throws Exception { "SELECT * FROM SCI_FI_BOOKS_OF_80S() AS A, OTHER_BOOKS() AS B WHERE A.YEAR > 1985 AND A.TITLE = 'OTHER'", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980, i1990), i1980); shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"SCI_FI_BOOKS_OF_80S\" ( ) AS \"A\" , \"OTHER_BOOKS\" ( ) AS \"B\" WHERE \"A\" . \"YEAR\" > ? AND \"A\" . \"TITLE\" = ? ", - "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980, i1990)), "CREATE TEMPORARY FUNCTION \"OTHER_BOOKS\" ( ) " + + PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980, i1990)), "CREATE TEMPORARY FUNCTION \"OTHER_BOOKS\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"BOOKS\" WHERE \"TITLE\" LIKE ? ||CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"BOOKS\" WHERE \"TITLE\" LIKE ? ||CREATE TEMPORARY FUNCTION \"SCI_FI_BOOKS_OF_80S\" ( ) " + "ON COMMIT DROP FUNCTION AS SELECT * FROM \"SCI_FI_BOOKS\" ( ) WHERE \"YEAR\" > ? AND \"YEAR\" < ? "), @@ -918,7 +919,7 @@ void testPlanningQueryWithAndWithoutDisabledPlannerRewriteRules() throws Excepti // customer 1 issues a query with disabled planner rules planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), disabledRulesOption, i1970); shouldBe(cache, Map.of( - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980), disabledRulesOption), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980), disabledRulesOption), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // customer 2 issues the same query, with enabled planner rules. @@ -927,10 +928,10 @@ void testPlanningQueryWithAndWithoutDisabledPlannerRewriteRules() throws Excepti // cache should contain two entries shouldBe(cache, Map.of( // ... one with disabled planner rules. - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980), disabledRulesOption), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980), disabledRulesOption), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970), // ... another one with enabled planner rules. - new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // this is needed because expiration is done passively for better performance. @@ -955,15 +956,15 @@ void testPlanningQueryWithAndWithoutPlanRightDeepOption(boolean specifyInQuery) planQuery(cache, queryWithOption, "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), optionsWithOption, i1970); shouldBe(cache, Map.of( - new Tuple(expectedCononicalString, "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980), rightDeepOption), ""), + new Tuple(expectedCononicalString, PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980), rightDeepOption), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); // now run the plain query (no option either way) planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), Options.none(), i1970); shouldBe(cache, Map.of( - new Tuple(expectedCononicalString, "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980), rightDeepOption), ""), + new Tuple(expectedCononicalString, PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980), rightDeepOption), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970), - new Tuple(expectedCononicalString, "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + new Tuple(expectedCononicalString, PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); cache.cleanUp(); @@ -975,15 +976,15 @@ void testExplainReusesCachedPlan() throws Exception { final var cache = getCache(ticker); planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); planQuery(cache, "SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); planQuery(cache, "EXPLAIN SELECT * FROM BOOKS WHERE YEAR > 1970 AND YEAR < 1979", "SCHEMA_TEMPLATE_1", 10, Set.of(i1970, i1980), i1970); - shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", "SCHEMA_TEMPLATE_1", 10, configOf(Set.of(i1970, i1980)), ""), + shouldBe(cache, Map.of(new Tuple("SELECT * FROM \"BOOKS\" WHERE \"YEAR\" > ? AND \"YEAR\" < ? ", PlanCacheSchemaKey.of("SCHEMA_TEMPLATE_1"), ImmutableSortedMap.of("SCHEMA_TEMPLATE_1", 10), configOf(Set.of(i1970, i1980)), ""), Map.of(ppe(cons(c1970Cp0(7), c1970Cp1(11)), cons(ofTypeIntCp0(7), ofTypeIntCp1(11), isNotNullInt(7), isNotNullInt(11))), i1970))); } }