From 3f7e9ebf898ecc8199774724d10e9f23a25fff9c Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Wed, 20 May 2026 00:49:11 +0100 Subject: [PATCH 1/7] Name struct columns from parenthesised-star expressions after their source SELECT (T.*) now names the outer column after T; SELECT (*) from a single table names it after the table/alias. With multiple tables in scope the column remains anonymous. --- .../query/visitors/ExpressionVisitor.java | 13 +++- .../test/java/CheckResultMetadataTest.java | 3 +- .../star-expression-metadata.yamsql | 69 +++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java index cfc3ae9f01..c07f0fdddc 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java @@ -60,6 +60,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.common.collect.Streams; import com.google.protobuf.ZeroCopyByteString; import org.antlr.v4.runtime.ParserRuleContext; @@ -891,12 +892,22 @@ public Expression visitRecordConstructor(@Nonnull RelationalParser.RecordConstru } else { final var star = getDelegate().getSemanticAnalyzer().expandStar(Optional.of(id), getDelegate().getLogicalOperators()); final var resultValue = star.getUnderlying(); - return Expression.ofUnnamed(resultValue); + // Name the column after the qualifier (table name or alias) that was expanded. + return Expression.of(resultValue, id); } } if (ctx.STAR() != null) { final var star = getDelegate().getSemanticAnalyzer().expandStar(Optional.empty(), getDelegate().getLogicalOperators()); final var resultValue = star.getUnderlying(); + // When there is exactly one table in scope, name the column after that table/alias. + // With multiple tables the expansion is ambiguous, so fall back to an unnamed column. + final var forEachOps = getDelegate().getLogicalOperators().forEachOnly(); + if (Iterables.size(forEachOps) == 1) { + final Optional tableName = Iterables.getOnlyElement(forEachOps).getName(); + if (tableName.isPresent()) { + return Expression.of(resultValue, tableName.get()); + } + } return Expression.ofUnnamed(resultValue); } final var expressions = parseRecordFieldsUnderReorderings(ctx.expressionWithOptionalName()); diff --git a/yaml-tests/src/test/java/CheckResultMetadataTest.java b/yaml-tests/src/test/java/CheckResultMetadataTest.java index df4e85a87c..4a6e73dd56 100644 --- a/yaml-tests/src/test/java/CheckResultMetadataTest.java +++ b/yaml-tests/src/test/java/CheckResultMetadataTest.java @@ -116,7 +116,8 @@ static Stream shouldPass() { "array-of-struct-column", // array-of-struct with nested element field descriptors "struct-type-name", // struct type name as optional prefix in field list "field-named-array", // struct field named "array" — no clash with {array: ...} map syntax - "type-named-array" // struct type named "array" — no clash with {array: ...} map syntax + "type-named-array", // struct type named "array" — no clash with {array: ...} map syntax + "star-expression-metadata" // column names from SELECT (*) / SELECT (T.*) / SELECT (*) AS alias ); } diff --git a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql new file mode 100644 index 0000000000..cb8f63b9f5 --- /dev/null +++ b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql @@ -0,0 +1,69 @@ +# +# star-expression-metadata.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 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. + +# Tests the result-set column names produced by parenthesised-star SELECT expressions. +# +# Parenthesised-star forms — SELECT (*) and SELECT (T.*) — pack the entire row into a +# single struct-typed column. The outer column name is inferred as follows: +# +# SELECT (T.*) → column named T (explicit qualifier used as name) +# SELECT (*) single tbl → column named after the sole table/alias in scope +# SELECT (*) / (T.*) AS X → explicit alias overrides inferred name +# SELECT (*) multi-table → column is anonymous ("_0") — ambiguous source +--- +schema_template: + create table foo(id bigint, val bigint, primary key(id)) +--- +setup: + steps: + - query: INSERT INTO foo VALUES (1, 10) +--- +test_block: + tests: + - + # Case 1: SELECT (*) FROM FOO → single table in scope → column named FOO + - query: SELECT (*) FROM foo + - resultMetadata: [{FOO: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{FOO: {ID: 1, VAL: 10}}] + - + # Case 2: SELECT (FOO.*) FROM FOO → qualifier is used as column name + - query: SELECT (foo.*) FROM foo + - resultMetadata: [{FOO: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{FOO: {ID: 1, VAL: 10}}] + - + # Case 3: SELECT (*) FROM FOO AS BAR → single table alias in scope → column named BAR + - query: SELECT (*) FROM foo AS bar + - resultMetadata: [{BAR: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{BAR: {ID: 1, VAL: 10}}] + - + # Case 4: SELECT (BAR.*) FROM FOO AS BAR → qualifier (the alias) is used as column name + - query: SELECT (bar.*) FROM foo AS bar + - resultMetadata: [{BAR: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{BAR: {ID: 1, VAL: 10}}] + - + # Case 5: SELECT (*) AS BAR FROM FOO → explicit alias overrides inferred name + - query: SELECT (*) AS bar FROM foo + - resultMetadata: [{BAR: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{BAR: {ID: 1, VAL: 10}}] + - + # Case 6: SELECT (FOO.*) AS BAR FROM FOO → explicit alias overrides qualifier name + - query: SELECT (foo.*) AS bar FROM foo + - resultMetadata: [{BAR: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{BAR: {ID: 1, VAL: 10}}] +... From 13909cf5b03581127217478b61d862f6dce79864 Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Wed, 20 May 2026 01:12:34 +0100 Subject: [PATCH 2/7] Extend star-expression-metadata tests for subquery, TVF, and join sources --- .../star-expression-metadata.yamsql | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql index cb8f63b9f5..b139d79d4f 100644 --- a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql +++ b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql @@ -22,19 +22,26 @@ # Parenthesised-star forms — SELECT (*) and SELECT (T.*) — pack the entire row into a # single struct-typed column. The outer column name is inferred as follows: # -# SELECT (T.*) → column named T (explicit qualifier used as name) -# SELECT (*) single tbl → column named after the sole table/alias in scope -# SELECT (*) / (T.*) AS X → explicit alias overrides inferred name -# SELECT (*) multi-table → column is anonymous ("_0") — ambiguous source +# SELECT (T.*) → column named T (explicit qualifier used as name) +# SELECT (*) single source → column named after the sole table/alias/function in scope +# SELECT (*) / (T.*) AS X → explicit alias overrides inferred name +# SELECT (*) multi-table → column is anonymous ("_0") — source is ambiguous --- schema_template: create table foo(id bigint, val bigint, primary key(id)) + create table bar(bid bigint, bval bigint, primary key(bid)) +--- +transaction_setups: + tvf_sq: create temporary function sq(in x bigint) on commit drop function AS + SELECT id, val FROM foo WHERE id > x --- setup: steps: - query: INSERT INTO foo VALUES (1, 10) + - query: INSERT INTO bar VALUES (1, 20) --- test_block: + name: table-source tests: - # Case 1: SELECT (*) FROM FOO → single table in scope → column named FOO @@ -47,7 +54,7 @@ test_block: - resultMetadata: [{FOO: [{ID: BIGINT}, {VAL: BIGINT}]}] - result: [{FOO: {ID: 1, VAL: 10}}] - - # Case 3: SELECT (*) FROM FOO AS BAR → single table alias in scope → column named BAR + # Case 3: SELECT (*) FROM FOO AS BAR → table alias in scope → column named BAR - query: SELECT (*) FROM foo AS bar - resultMetadata: [{BAR: [{ID: BIGINT}, {VAL: BIGINT}]}] - result: [{BAR: {ID: 1, VAL: 10}}] @@ -66,4 +73,43 @@ test_block: - query: SELECT (foo.*) AS bar FROM foo - resultMetadata: [{BAR: [{ID: BIGINT}, {VAL: BIGINT}]}] - result: [{BAR: {ID: 1, VAL: 10}}] +--- +test_block: + name: subquery-source + tests: + - + # Subquery with alias → column named after the alias + - query: SELECT (*) FROM (SELECT id, val FROM foo) AS sub + - resultMetadata: [{SUB: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{SUB: {ID: 1, VAL: 10}}] + - + # (T.*) where T is a subquery alias → same rule as for a table alias + - query: SELECT (sub.*) FROM (SELECT id, val FROM foo) AS sub + - resultMetadata: [{SUB: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{SUB: {ID: 1, VAL: 10}}] +--- +test_block: + name: function-source + tests: + - + # TVF without alias → column named after the function + - query: SELECT (*) FROM sq(0) + - setupReference: tvf_sq + - resultMetadata: [{SQ: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{SQ: {ID: 1, VAL: 10}}] + - + # TVF with alias → alias wins + - query: SELECT (*) FROM sq(0) AS f + - setupReference: tvf_sq + - resultMetadata: [{F: [{ID: BIGINT}, {VAL: BIGINT}]}] + - result: [{F: {ID: 1, VAL: 10}}] +--- +test_block: + name: ambiguous-join-source + tests: + - + # Two tables in scope → source is ambiguous, column is anonymous + - query: SELECT (*) FROM foo, bar WHERE foo.id = bar.bid + - resultMetadata: [{_0: [{ID: BIGINT}, {VAL: BIGINT}, {BID: BIGINT}, {BVAL: BIGINT}]}] + - result: [{_0: {ID: 1, VAL: 10, BID: 1, BVAL: 20}}] ... From ecfea2936640150658e45cdc40bab6165675b12a Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Wed, 20 May 2026 12:47:13 +0100 Subject: [PATCH 3/7] Fix expected plans --- .../arrays-cardinality.metrics.binpb | 107 ++++++++++++++ .../resources/arrays-cardinality.metrics.yaml | 138 +++++++++++++++--- .../src/test/resources/join-tests.yamsql | 4 +- .../resources/join-with-order-by-tests.yamsql | 52 +++---- yaml-tests/src/test/resources/orderby.yamsql | 4 +- .../test/resources/pseudo-field-clash.yamsql | 2 +- .../test/resources/valid-identifiers.yamsql | 2 +- .../src/test/resources/versions-tests.yamsql | 20 +-- 8 files changed, 268 insertions(+), 61 deletions(-) diff --git a/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb b/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb index f6e7d491b2..147ceed64a 100644 --- a/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb +++ b/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb @@ -212,6 +212,113 @@ digraph G { 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 2 -> 1 [ label=< q53> label="q53" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +n +arrays-cardinality-index-testsLEXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS NULL +c ^(0 8&@PCOVERING(tab1_indexed_index [[null],[null]] -> [id: KEY:[2]]) | MAP (_.id AS id) +digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Covering Index Scan
range: [(null), (null)]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +r +arrays-cardinality-index-testsPEXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS NOT NULL +c W(0 +8&@JCOVERING(tab1_indexed_index ([null],> -> [id: KEY:[2]]) | MAP (_.id AS id) +digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Covering Index Scan
range: ((null), ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +q +arrays-cardinality-index-testsOEXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") IS NULL +c a(0 8&@SCOVERING(tab1_indexed_nn_index [[null],[null]] -> [id: KEY:[2]]) | MAP (_.id AS id) +digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Covering Index Scan
range: [(null), (null)]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Index
tab1_indexed_nn_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +u +arrays-cardinality-index-testsSEXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") IS NOT NULL +c (08&@MCOVERING(tab1_indexed_nn_index ([null],> -> [id: KEY:[2]]) | MAP (_.id AS id) +digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Covering Index Scan
range: ((null), ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Index
tab1_indexed_nn_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +e +arrays-cardinality-index-testsCEXPLAIN SELECT "id" FROM "tab1" WHERE CARDINALITY("int_arr") = NULL +ā@ :(08@0SCAN([IS tab1]) | FILTER null | MAP (_.id AS id)digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q23.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Predicate Filter
WHERE null
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Scan
comparisons: [IS tab1]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 4 [ label=<
Primary Storage
record types: [dummy, tab1, tab1_indexed_nn, tab2, tab1_nn, tab1_indexed]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +h +arrays-cardinality-index-testsFEXPLAIN SELECT "id" FROM "tab1_nn" WHERE CARDINALITY("int_arr") = NULL +c@ '(08@3SCAN([IS tab1_nn]) | FILTER null | MAP (_.id AS id)digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q23.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Predicate Filter
WHERE null
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Scan
comparisons: [IS tab1_nn]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 4 [ label=<
Primary Storage
record types: [dummy, tab1, tab1_indexed_nn, tab2, tab1_nn, tab1_indexed]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +m +arrays-cardinality-index-testsKEXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") = NULL +h ^(!0 8*@NCOVERING(tab1_indexed_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP (_.id AS id) +digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q53.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Covering Index Scan
comparisons: [EQUALS NULL]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q53> label="q53" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +p +arrays-cardinality-index-testsNEXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = NULL +h d(!0 8*@QCOVERING(tab1_indexed_nn_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP (_.id AS id) +digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q53.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; + 2 [ label=<
Covering Index Scan
comparisons: [EQUALS NULL]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 [ label=<
Index
tab1_indexed_nn_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q53> label="q53" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; }  arrays-cardinality-index-testsEXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = 1 ORDER BY CARDINALITY("int_arr") diff --git a/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml b/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml index 592c09feb8..1bfd63c655 100644 --- a/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml +++ b/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml @@ -1,6 +1,6 @@ arrays-cardinality-tests: - query: EXPLAIN SELECT CARDINALITY("int_arr") FROM "tab1_nn" - ref: arrays_cardinality.yamsql:72 + ref: arrays-cardinality.yamsql:76 explain: SCAN([IS tab1_nn]) | MAP (cardinality(_.int_arr) AS _0) task_count: 146 task_total_time_ms: 71 @@ -11,7 +11,7 @@ arrays-cardinality-tests: insert_new_count: 12 insert_reused_count: 2 - query: EXPLAIN SELECT CARDINALITY("int_arr") FROM "tab1" - ref: arrays_cardinality.yamsql:76 + ref: arrays-cardinality.yamsql:81 explain: SCAN([IS tab1]) | MAP (cardinality(_.int_arr) AS _0) task_count: 146 task_total_time_ms: 9 @@ -23,7 +23,7 @@ arrays-cardinality-tests: insert_reused_count: 2 arrays-cardinality-index-tests: - query: EXPLAIN SELECT * FROM "tab1" - ref: arrays_cardinality.yamsql:86 + ref: arrays-cardinality.yamsql:109 explain: SCAN([IS tab1]) task_count: 140 task_total_time_ms: 6 @@ -34,7 +34,7 @@ arrays-cardinality-index-tests: insert_new_count: 10 insert_reused_count: 2 - query: EXPLAIN SELECT * FROM "tab1_indexed_nn" - ref: arrays_cardinality.yamsql:88 + ref: arrays-cardinality.yamsql:111 explain: ISCAN(tab1_indexed_nn_index <,>) task_count: 221 task_total_time_ms: 22 @@ -45,7 +45,7 @@ arrays-cardinality-index-tests: insert_new_count: 19 insert_reused_count: 6 - query: EXPLAIN SELECT * FROM "tab1_indexed" - ref: arrays_cardinality.yamsql:90 + ref: arrays-cardinality.yamsql:113 explain: ISCAN(tab1_indexed_index <,>) task_count: 221 task_total_time_ms: 12 @@ -56,7 +56,7 @@ arrays-cardinality-index-tests: insert_new_count: 19 insert_reused_count: 6 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" - ref: arrays_cardinality.yamsql:93 + ref: arrays-cardinality.yamsql:116 explain: 'COVERING(tab1_indexed_nn_index <,> -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 241 @@ -68,7 +68,7 @@ arrays-cardinality-index-tests: insert_new_count: 25 insert_reused_count: 4 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" - ref: arrays_cardinality.yamsql:96 + ref: arrays-cardinality.yamsql:119 explain: 'COVERING(tab1_indexed_index <,> -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 241 task_total_time_ms: 11 @@ -79,7 +79,7 @@ arrays-cardinality-index-tests: insert_new_count: 25 insert_reused_count: 4 - query: EXPLAIN SELECT "id", CARDINALITY("int_arr") AS "card" FROM "tab1_indexed_nn" - ref: arrays_cardinality.yamsql:100 + ref: arrays-cardinality.yamsql:125 explain: ISCAN(tab1_indexed_nn_index <,>) | MAP (_.id AS id, cardinality(_.int_arr) AS card) task_count: 213 @@ -91,7 +91,7 @@ arrays-cardinality-index-tests: insert_new_count: 22 insert_reused_count: 4 - query: EXPLAIN SELECT "id", CARDINALITY("int_arr") AS "card" FROM "tab1_indexed" - ref: arrays_cardinality.yamsql:103 + ref: arrays-cardinality.yamsql:129 explain: ISCAN(tab1_indexed_index <,>) | MAP (_.id AS id, cardinality(_.int_arr) AS card) task_count: 213 @@ -103,7 +103,7 @@ arrays-cardinality-index-tests: insert_new_count: 22 insert_reused_count: 4 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" ORDER BY "id" DESC - ref: arrays_cardinality.yamsql:107 + ref: arrays-cardinality.yamsql:133 explain: SCAN([IS tab1_indexed_nn]) | MAP (_.id AS id) task_count: 158 task_total_time_ms: 8 @@ -114,7 +114,7 @@ arrays-cardinality-index-tests: insert_new_count: 12 insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" ORDER BY "id" DESC - ref: arrays_cardinality.yamsql:110 + ref: arrays-cardinality.yamsql:136 explain: SCAN([IS tab1_indexed]) | MAP (_.id AS id) task_count: 158 task_total_time_ms: 8 @@ -126,7 +126,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" ORDER BY CARDINALITY("int_arr") DESC - ref: arrays_cardinality.yamsql:115 + ref: arrays-cardinality.yamsql:140 explain: ISCAN(tab1_indexed_nn_index <,> REVERSE) | MAP (_.id AS id, cardinality(_.int_arr) AS _1) | MAP (_.id AS id) task_count: 244 @@ -139,7 +139,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" ORDER BY CARDINALITY("int_arr") DESC - ref: arrays_cardinality.yamsql:118 + ref: arrays-cardinality.yamsql:143 explain: ISCAN(tab1_indexed_index <,> REVERSE) | MAP (_.id AS id, cardinality(_.int_arr) AS _1) | MAP (_.id AS id) task_count: 244 @@ -152,7 +152,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed_nn" ORDER BY CARDINALITY("int_arr") - ref: arrays_cardinality.yamsql:123 + ref: arrays-cardinality.yamsql:147 explain: ISCAN(tab1_indexed_nn_index <,>) | MAP (cardinality(_.int_arr) AS card, _.id AS id) task_count: 182 @@ -165,7 +165,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed" ORDER BY CARDINALITY("int_arr") - ref: arrays_cardinality.yamsql:126 + ref: arrays-cardinality.yamsql:152 explain: ISCAN(tab1_indexed_index <,>) | MAP (cardinality(_.int_arr) AS card, _.id AS id) task_count: 182 @@ -178,7 +178,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = 1 - ref: arrays_cardinality.yamsql:130 + ref: arrays-cardinality.yamsql:156 explain: 'COVERING(tab1_indexed_nn_index [EQUALS promote(@c11 AS INT)] -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 395 @@ -191,7 +191,7 @@ arrays-cardinality-index-tests: insert_reused_count: 3 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") = 1 - ref: arrays_cardinality.yamsql:133 + ref: arrays-cardinality.yamsql:159 explain: 'COVERING(tab1_indexed_index [EQUALS promote(@c11 AS INT)] -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 395 @@ -202,9 +202,109 @@ arrays-cardinality-index-tests: insert_time_ms: 1 insert_new_count: 42 insert_reused_count: 3 +- query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS + NULL + ref: arrays-cardinality.yamsql:163 + explain: 'COVERING(tab1_indexed_index [[null],[null]] -> [id: KEY:[2]]) | MAP + (_.id AS id)' + task_count: 379 + task_total_time_ms: 3 + transform_count: 99 + transform_time_ms: 1 + transform_yield_count: 31 + insert_time_ms: 0 + insert_new_count: 38 + insert_reused_count: 3 +- query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS + NOT NULL + ref: arrays-cardinality.yamsql:166 + explain: 'COVERING(tab1_indexed_index ([null],> -> [id: KEY:[2]]) | MAP (_.id + AS id)' + task_count: 379 + task_total_time_ms: 3 + transform_count: 99 + transform_time_ms: 1 + transform_yield_count: 31 + insert_time_ms: 0 + insert_new_count: 38 + insert_reused_count: 3 +- query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") + IS NULL + ref: arrays-cardinality.yamsql:171 + explain: 'COVERING(tab1_indexed_nn_index [[null],[null]] -> [id: KEY:[2]]) | MAP + (_.id AS id)' + task_count: 379 + task_total_time_ms: 5 + transform_count: 99 + transform_time_ms: 1 + transform_yield_count: 31 + insert_time_ms: 0 + insert_new_count: 38 + insert_reused_count: 3 +- query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") + IS NOT NULL + ref: arrays-cardinality.yamsql:174 + explain: 'COVERING(tab1_indexed_nn_index ([null],> -> [id: KEY:[2]]) | MAP (_.id + AS id)' + task_count: 379 + task_total_time_ms: 5 + transform_count: 99 + transform_time_ms: 2 + transform_yield_count: 31 + insert_time_ms: 0 + insert_new_count: 38 + insert_reused_count: 3 +- query: EXPLAIN SELECT "id" FROM "tab1" WHERE CARDINALITY("int_arr") = NULL + ref: arrays-cardinality.yamsql:181 + explain: SCAN([IS tab1]) | FILTER null | MAP (_.id AS id) + task_count: 222 + task_total_time_ms: 2 + transform_count: 64 + transform_time_ms: 0 + transform_yield_count: 17 + insert_time_ms: 0 + insert_new_count: 22 + insert_reused_count: 2 +- query: EXPLAIN SELECT "id" FROM "tab1_nn" WHERE CARDINALITY("int_arr") = NULL + ref: arrays-cardinality.yamsql:184 + explain: SCAN([IS tab1_nn]) | FILTER null | MAP (_.id AS id) + task_count: 222 + task_total_time_ms: 1 + transform_count: 64 + transform_time_ms: 0 + transform_yield_count: 17 + insert_time_ms: 0 + insert_new_count: 22 + insert_reused_count: 2 +- query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") = + NULL + ref: arrays-cardinality.yamsql:188 + explain: 'COVERING(tab1_indexed_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP (_.id + AS id)' + task_count: 395 + task_total_time_ms: 3 + transform_count: 104 + transform_time_ms: 1 + transform_yield_count: 33 + insert_time_ms: 0 + insert_new_count: 42 + insert_reused_count: 3 +- query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") + = NULL + ref: arrays-cardinality.yamsql:191 + explain: 'COVERING(tab1_indexed_nn_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP + (_.id AS id)' + task_count: 395 + task_total_time_ms: 3 + transform_count: 104 + transform_time_ms: 1 + transform_yield_count: 33 + insert_time_ms: 0 + insert_new_count: 42 + insert_reused_count: 3 - query: EXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = 1 ORDER BY CARDINALITY("int_arr") - ref: arrays_cardinality.yamsql:138 + ref: arrays-cardinality.yamsql:195 explain: ISCAN(tab1_indexed_nn_index [EQUALS promote(@c18 AS INT)]) | MAP (cardinality(_.int_arr) AS card, _.id AS id) task_count: 276 @@ -217,7 +317,7 @@ arrays-cardinality-index-tests: insert_reused_count: 1 - query: EXPLAIN SELECT CARDINALITY("struct"."int_arr") AS "card", "id" FROM "tab2" WHERE CARDINALITY("struct"."int_arr") = 1 ORDER BY CARDINALITY("struct"."int_arr") - ref: arrays_cardinality.yamsql:143 + ref: arrays-cardinality.yamsql:200 explain: ISCAN(tab2_index [EQUALS promote(@c22 AS INT)]) | MAP (cardinality(_.struct.int_arr) AS card, _.id AS id) task_count: 276 diff --git a/yaml-tests/src/test/resources/join-tests.yamsql b/yaml-tests/src/test/resources/join-tests.yamsql index 8dbe88e7ff..4398b6e613 100644 --- a/yaml-tests/src/test/resources/join-tests.yamsql +++ b/yaml-tests/src/test/resources/join-tests.yamsql @@ -200,7 +200,7 @@ test_block: - # ambiguous sub select is fine if the top-level query does not specify column names - query: select (*) from (select dept.name, project.name from emp, dept, project where emp.dept_id = dept.id and project.emp_id = emp.id) X; - - explain: "SCAN([IS DEPT]) | FLATMAP q0 -> { SCAN([IS PROJECT]) | FLATMAP q1 -> { SCAN([IS EMP]) | FILTER _.DEPT_ID EQUALS q0.ID AND q1.EMP_ID EQUALS _.ID AS q2 RETURN q1 } AS q1 RETURN ((q0.NAME AS _0, q1.NAME AS _1) AS _0) }" + - explain: "SCAN([IS DEPT]) | FLATMAP q0 -> { SCAN([IS PROJECT]) | FLATMAP q1 -> { SCAN([IS EMP]) | FILTER _.DEPT_ID EQUALS q0.ID AND q1.EMP_ID EQUALS _.ID AS q2 RETURN q1 } AS q1 RETURN ((q0.NAME AS _0, q1.NAME AS _1) AS X) }" - unorderedResult: [{{"Engineering", "OLAP"}}, {{"Sales", "Feedback"}}, {{"Marketing", "SEO"}}] @@ -456,7 +456,7 @@ test_block: - # ambiguous sub-select is fine if the top-level query does not specify column names (select (*)) - query: select (*) from (select dept.name, project.name from emp, dept, project where emp.dept_id = dept.id and project.emp_id = emp.id) X; - - explain: "SCAN([IS DEPT]) | FLATMAP q0 -> { SCAN([IS PROJECT]) | FLATMAP q1 -> { SCAN([IS EMP]) | FILTER _.DEPT_ID EQUALS q0.ID AND q1.EMP_ID EQUALS _.ID AS q2 RETURN q1 } AS q1 RETURN ((q0.NAME AS _0, q1.NAME AS _1) AS _0) }" + - explain: "SCAN([IS DEPT]) | FLATMAP q0 -> { SCAN([IS PROJECT]) | FLATMAP q1 -> { SCAN([IS EMP]) | FILTER _.DEPT_ID EQUALS q0.ID AND q1.EMP_ID EQUALS _.ID AS q2 RETURN q1 } AS q1 RETURN ((q0.NAME AS _0, q1.NAME AS _1) AS X) }" - unorderedResult: [{{"Engineering", "OLAP"}}, {{"Sales", "Feedback"}}, {{"Marketing", "SEO"}}] diff --git a/yaml-tests/src/test/resources/join-with-order-by-tests.yamsql b/yaml-tests/src/test/resources/join-with-order-by-tests.yamsql index 33f517e2db..858492a4cb 100644 --- a/yaml-tests/src/test/resources/join-with-order-by-tests.yamsql +++ b/yaml-tests/src/test/resources/join-with-order-by-tests.yamsql @@ -92,7 +92,7 @@ test_block: - query: select (t1.*), (t2.*) from t1, t2 where t1.a1 = 1 and t2.b1 = 1 and t1.a3 = t2.b3 - - explain: "ISCAN(t2$b1 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS _1) }" + - explain: "ISCAN(t2$b1 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS t2) }" - unorderedResult: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 2001, b1: 1, b2: 20, b3: "a" } }, { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 2002, b1: 1, b2: 19, b3: "a" } }, @@ -108,7 +108,7 @@ test_block: from t1, t2 where t1.a1 = 1 and t2.b1 = 1 and t1.a3 = t2.b3 order by t1.a2 - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c21 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c21 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS t1, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS t2) }" - result: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 2001, b1: 1, b2: 20, b3: "a" } }, { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 2002, b1: 1, b2: 19, b3: "a" } }, @@ -124,7 +124,7 @@ test_block: from t1, t2 where t1.a1 = 1 and t2.b1 = 1 and t1.a3 = t2.b3 order by t1.a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c21 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c21 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS t1, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS t2) }" - result: [ { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 2003, b1: 1, b2: 18, b3: "b" } }, { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 2004, b1: 1, b2: 17, b3: "b" } }, @@ -141,7 +141,7 @@ test_block: from t1, t2 where t1.a1 = 1 and t2.b1 = 1 and t1.a3 = t2.b3 order by t1.a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _0, q0.a2 AS a2) } | MAP (_._0 AS _0)" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS t2, q0.a2 AS a2) } | MAP (_.t2 AS t2)" - result: [ { { id: 2003, b1: 1, b2: 18, b3: "b" } }, { { id: 2004, b1: 1, b2: 17, b3: "b" } }, @@ -192,7 +192,7 @@ test_block: from t1_f(1) as x, t2_f(1) as y where x.a3 = y.b3 order by x.a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS x, (q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS y) }" - result: [ { { '__ROW_VERSION': !not_null _, id: 1004, a1: 1, a2: 13, a3: "b" }, { '__ROW_VERSION': !not_null _, id: 2003, b1: 1, b2: 18, b3: "b" } }, { { '__ROW_VERSION': !not_null _, id: 1004, a1: 1, a2: 13, a3: "b" }, { '__ROW_VERSION': !not_null _, id: 2004, b1: 1, b2: 17, b3: "b" } }, @@ -209,7 +209,7 @@ test_block: from t1_f(1) as x, t2_f(1) as y where x.a3 = y.b3 order by x.a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c9 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c9 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _0, q0.a2 AS a2) } | MAP (_._0 AS _0)" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c9 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c9 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS y, q0.a2 AS a2) } | MAP (_.y AS y)" - initialVersionLessThan: 4.10.5.0 - error: 'XX000' - initialVersionAtLeast: 4.10.5.0 @@ -229,7 +229,7 @@ test_block: from t1_g(1) as x, t2_g(1) as y where x.a3 = y.b3 order by x.the_a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3, q0.a2 AS the_a2) AS _0, (q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3, q1.b2 AS the_b2) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3, q0.a2 AS the_a2) AS x, (q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3, q1.b2 AS the_b2) AS y) }" - result: [ { { '__ROW_VERSION': !not_null _, id: 1004, a1: 1, a2: 13, a3: "b", the_a2: 13 }, { '__ROW_VERSION': !not_null _, id: 2003, b1: 1, b2: 18, b3: "b", the_b2: 18 } }, { { '__ROW_VERSION': !not_null _, id: 1004, a1: 1, a2: 13, a3: "b", the_a2: 13 }, { '__ROW_VERSION': !not_null _, id: 2004, b1: 1, b2: 17, b3: "b", the_b2: 17 } }, @@ -246,7 +246,7 @@ test_block: from t1_g(1) as x, t2_g(1) as y where x.a3 = y.b3 order by x.the_a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c9 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c9 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3, q1.b2 AS the_b2) AS _0, q0.a2 AS the_a2) } | MAP (_._0 AS _0)" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c9 AS LONG)] REVERSE) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c9 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3, q1.b2 AS the_b2) AS y, q0.a2 AS the_a2) } | MAP (_.y AS y)" - initialVersionLessThan: 4.10.5.0 - error: 'XX000' - initialVersionAtLeast: 4.10.5.0 @@ -282,7 +282,7 @@ test_block: from t1, t2 where t1.a1 = 1 and t2.b1 = 1 and t2.b3 = t1.a3 order by t1.a1 asc, t1.a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t2$b1_b3 [EQUALS promote(@c21 AS LONG), EQUALS q0.a3]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t2$b1_b3 [EQUALS promote(@c21 AS LONG), EQUALS q0.a3]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS t1, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS t2) }" - result: [ { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 2003, b1: 1, b2: 18, b3: "b" } }, { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 2004, b1: 1, b2: 17, b3: "b" } }, @@ -298,7 +298,7 @@ test_block: from t1, t2 where t1.a1 = 1 and t2.b1 = 1 and t1.a3 = t2.b3 order by t2.b3 asc - - explain: "ISCAN(t2$b1_b3 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS _1) }" + - explain: "ISCAN(t2$b1_b3 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS t2) }" - result: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 2001, b1: 1, b2: 20, b3: "a" } }, { { id: 1003, a1: 1, a2: 12, a3: "a" }, { id: 2001, b1: 1, b2: 20, b3: "a" } }, @@ -314,7 +314,7 @@ test_block: from t1, t2 where t1.a1 = 1 and t2.b1 = 1 and t1.a3 = t2.b3 order by t2.b3 desc - - explain: "ISCAN(t2$b1_b3 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS _1) }" + - explain: "ISCAN(t2$b1_b3 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS t2) }" - result: [ { { id: 1002, a1: 1, a2: 11, a3: "b" }, { id: 2004, b1: 1, b2: 17, b3: "b" } }, { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 2004, b1: 1, b2: 17, b3: "b" } }, @@ -330,7 +330,7 @@ test_block: from t1, t2 where t1.a1 = 1 and t2.b1 = 1 order by t1.a2 - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t2$b1 [EQUALS promote(@c21 AS LONG)]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t2$b1 [EQUALS promote(@c21 AS LONG)]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS t1, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS t2) }" - result: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 2001, b1: 1, b2: 20, b3: "a" } }, { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 2002, b1: 1, b2: 19, b3: "a" } }, @@ -373,7 +373,7 @@ test_block: - query: select (t1.*), (t3.*) from t1, t3 where t1.a1 = 1 and t3.aRef = t1.id - - explain: "ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t3$aRef [EQUALS q0.id]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.id AS id, q1.c1 AS c1, q1.c2 AS c2, q1.c3 AS c3, q1.aRef AS aRef) AS _1) }" + - explain: "ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG)]) | FLATMAP q0 -> { ISCAN(t3$aRef [EQUALS q0.id]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS t1, (q1.id AS id, q1.c1 AS c1, q1.c2 AS c2, q1.c3 AS c3, q1.aRef AS aRef) AS t3) }" - unorderedResult: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 3001, c1: 1, c2: 10, c3: "a", aRef: 1001 } }, { { id: 1002, a1: 1, a2: 11, a3: "b" }, { id: 3002, c1: 1, c2: 11, c3: "a", aRef: 1002 } }, @@ -385,7 +385,7 @@ test_block: from t1, t3 where t1.a1 = 1 and t3.aRef = t1.id order by t1.a2 desc - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t3$aRef [EQUALS q0.id]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.id AS id, q1.c1 AS c1, q1.c2 AS c2, q1.c3 AS c3, q1.aRef AS aRef) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t3$aRef [EQUALS q0.id]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS t1, (q1.id AS id, q1.c1 AS c1, q1.c2 AS c2, q1.c3 AS c3, q1.aRef AS aRef) AS t3) }" - result: [ { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 3004, c1: 1, c2: 13, c3: "b", aRef: 1004 } }, { { id: 1003, a1: 1, a2: 12, a3: "a" }, { id: 3003, c1: 1, c2: 12, c3: "b", aRef: 1003 } }, @@ -397,7 +397,7 @@ test_block: from t1, t3 where t1.a1 = 1 and t3.aRef = t1.id order by t3.c1, t3.c2 - - explain: "ISCAN(t3$c1_c2 <,>) | FLATMAP q0 -> { COVERING(t1$a1 [EQUALS promote(@c21 AS LONG)] -> [a1: KEY:[0], id: KEY:[1]]) | FILTER q0.aRef EQUALS _.id | FETCH AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS _1) }" + - explain: "ISCAN(t3$c1_c2 <,>) | FLATMAP q0 -> { COVERING(t1$a1 [EQUALS promote(@c21 AS LONG)] -> [a1: KEY:[0], id: KEY:[1]]) | FILTER q0.aRef EQUALS _.id | FETCH AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS t3) }" - result: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 3001, c1: 1, c2: 10, c3: "a", aRef: 1001 } }, { { id: 1002, a1: 1, a2: 11, a3: "b" }, { id: 3002, c1: 1, c2: 11, c3: "a", aRef: 1002 } }, @@ -410,7 +410,7 @@ test_block: from t1, t3 where t1.a1 = 1 and t1.id = t3.aRef order by t3.c1, t3.c2 - - explain: "ISCAN(t3$c1_c2 <,>) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG), EQUALS q0.aRef]) AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS _1) }" + - explain: "ISCAN(t3$c1_c2 <,>) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c21 AS LONG), EQUALS q0.aRef]) AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS t3) }" - result: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 3001, c1: 1, c2: 10, c3: "a", aRef: 1001 } }, { { id: 1002, a1: 1, a2: 11, a3: "b" }, { id: 3002, c1: 1, c2: 11, c3: "a", aRef: 1002 } }, @@ -531,7 +531,7 @@ test_block: where t1.id = 1003 and t2.b3 >= t1.a3 and t2.b1 = 1 order by t2.b3 # The predicate on t1's primary key means that we can plan the t2 as the inner - - explain: "SCAN([EQUALS promote(@c21 AS LONG)]) | TFILTER t1 | FLATMAP q0 -> { ISCAN(t2$b1_b3 [EQUALS promote(@c36 AS LONG), [GREATER_THAN_OR_EQUALS q0.a3]]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _1) }" + - explain: "SCAN([EQUALS promote(@c21 AS LONG)]) | TFILTER t1 | FLATMAP q0 -> { ISCAN(t2$b1_b3 [EQUALS promote(@c36 AS LONG), [GREATER_THAN_OR_EQUALS q0.a3]]) AS q1 RETURN ((q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS t1, (q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS t2) }" - result: [ { { id: 1003, a1: 1, a2: 12, a3: "a" }, { id: 2001, b1: 1, b2: 20, b3: "a" } }, { { id: 1003, a1: 1, a2: 12, a3: "a" }, { id: 2002, b1: 1, b2: 19, b3: "a" } }, @@ -563,7 +563,7 @@ test_block: and t1.a1 = t3.c1 and t1.a2 >= t3.c2 order by t1.a2 desc # Uniqueness constraint on t3.(c1, c2) means that we can ensure the max cardinality of the t3 quantifier is 1, so it can be planned as the outer - - explain: "ISCAN(t3$c1_c2 [EQUALS promote(@c21 AS LONG), EQUALS promote(@c27 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1_a2 [EQUALS q0.c1, [GREATER_THAN_OR_EQUALS q0.c2]] REVERSE) AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS _1) }" + - explain: "ISCAN(t3$c1_c2 [EQUALS promote(@c21 AS LONG), EQUALS promote(@c27 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1_a2 [EQUALS q0.c1, [GREATER_THAN_OR_EQUALS q0.c2]] REVERSE) AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS t3) }" - result: [ { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 3003, c1: 1, c2: 12, c3: "b", aRef: 1003 } }, { { id: 1003, a1: 1, a2: 12, a3: "a" }, { id: 3003, c1: 1, c2: 12, c3: "b", aRef: 1003 } }, @@ -574,7 +574,7 @@ test_block: where t3.c1 = 1 and t1.a1 = t3.c1 and t1.a2 >= t3.c2 order by t3.c2 desc - - explain: "ISCAN(t3$c1_c2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t1$a1_a2 [EQUALS q0.c1, [GREATER_THAN_OR_EQUALS q0.c2]]) AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS _1) }" + - explain: "ISCAN(t3$c1_c2 [EQUALS promote(@c21 AS LONG)] REVERSE) | FLATMAP q0 -> { ISCAN(t1$a1_a2 [EQUALS q0.c1, [GREATER_THAN_OR_EQUALS q0.c2]]) AS q1 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q0.id AS id, q0.c1 AS c1, q0.c2 AS c2, q0.c3 AS c3, q0.aRef AS aRef) AS t3) }" - result: [ { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 3004, c1: 1, c2: 13, c3: "b", aRef: 1004 } }, @@ -704,7 +704,7 @@ test_block: where t1.a1 IN (1, 2) and t3.aRef = t1.id order by t1.a2 # Order by just a2 gives us an in-union - - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG)))] INUNION q0 -> { COVERING(t1$a1_a2 [EQUALS q0] -> [a1: KEY:[0], a2: KEY:[1], id: KEY:[2]]) } COMPARE BY (_.a2, _.id, _.a1) | FETCH | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS _1) }" + - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG)))] INUNION q0 -> { COVERING(t1$a1_a2 [EQUALS q0] -> [a1: KEY:[0], a2: KEY:[1], id: KEY:[2]]) } COMPARE BY (_.a2, _.id, _.a1) | FETCH | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS t3) }" - result: [ { { id: 1101, a1: 2, a2: 5, a3: "a" }, { id: 3101, c1: 2, c2: 7, c3: "c", aRef: 1101 } }, { { id: 1102, a1: 2, a2: 5, a3: "b" }, { id: 3102, c1: 2, c2: 8, c3: "d", aRef: 1102 } }, @@ -721,7 +721,7 @@ test_block: where t1.a1 IN (1, 2) and t3.aRef = t1.id order by t1.a1, t1.a2 # Order by a1, a2 also gives us an in join. - - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG))) SORTED] | INJOIN q0 -> { ISCAN(t1$a1_a2 [EQUALS q0]) } | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS _1) }" + - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG))) SORTED] | INJOIN q0 -> { ISCAN(t1$a1_a2 [EQUALS q0]) } | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS t3) }" - result: [ { { id: 1001, a1: 1, a2: 10, a3: "a" }, { id: 3001, c1: 1, c2: 10, c3: "a", aRef: 1001 } }, { { id: 1002, a1: 1, a2: 11, a3: "b" }, { id: 3002, c1: 1, c2: 11, c3: "a", aRef: 1002 } }, @@ -738,7 +738,7 @@ test_block: where t1.a1 IN (1, 2) and t3.aRef = t1.id order by t1.a1 asc, t1.a2 desc # Order by a1, a2 also gives us an in join. - - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG))) SORTED] | INJOIN q0 -> { ISCAN(t1$a1_a2 [EQUALS q0] REVERSE) } | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS _1) }" + - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG))) SORTED] | INJOIN q0 -> { ISCAN(t1$a1_a2 [EQUALS q0] REVERSE) } | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS t3) }" - result: [ { { id: 1004, a1: 1, a2: 13, a3: "b" }, { id: 3004, c1: 1, c2: 13, c3: "b", aRef: 1004 } }, { { id: 1003, a1: 1, a2: 12, a3: "a" }, { id: 3003, c1: 1, c2: 12, c3: "b", aRef: 1003 } }, @@ -755,7 +755,7 @@ test_block: where t1.a1 IN (1, 2) and t3.aRef = t1.id order by t1.a1 desc, t1.a2 asc # Order by a1, a2 also gives us an in join. - - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG))) SORTED DESC] | INJOIN q0 -> { ISCAN(t1$a1_a2 [EQUALS q0]) } | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS _1) }" + - explain: "[IN arrayDistinct(promote(@c21 AS ARRAY(LONG))) SORTED DESC] | INJOIN q0 -> { ISCAN(t1$a1_a2 [EQUALS q0]) } | FLATMAP q1 -> { ISCAN(t3$aRef [EQUALS q1.id]) AS q2 RETURN ((q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS t1, (q2.id AS id, q2.c1 AS c1, q2.c2 AS c2, q2.c3 AS c3, q2.aRef AS aRef) AS t3) }" - result: [ { { id: 1101, a1: 2, a2: 5, a3: "a" }, { id: 3101, c1: 2, c2: 7, c3: "c", aRef: 1101 } }, { { id: 1102, a1: 2, a2: 5, a3: "b" }, { id: 3102, c1: 2, c2: 8, c3: "d", aRef: 1102 } }, @@ -788,7 +788,7 @@ test_block: - query: select (a.*), (b.*) from t1_f(1) as a, t2_f(1) as b where a.a3 = b.b3 - - explain: "ISCAN(t2$b1 [EQUALS promote(@c15 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c15 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS _1) }" + - explain: "ISCAN(t2$b1 [EQUALS promote(@c15 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c15 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS a, (q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS b) }" - unorderedResult: [ { { '__ROW_VERSION': !not_null _, id: 1001, a1: 1, a2: 10, a3: "a" }, { '__ROW_VERSION': !not_null _, id: 2001, b1: 1, b2: 20, b3: "a" } }, { { '__ROW_VERSION': !not_null _, id: 1001, a1: 1, a2: 10, a3: "a" }, { '__ROW_VERSION': !not_null _, id: 2002, b1: 1, b2: 19, b3: "a" } }, @@ -804,7 +804,7 @@ test_block: from t1_f(1) as a, t2_f(1) as b where a.a3 = b.b3 order by a.a2 - - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)]) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS _0, (q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS _1) }" + - explain: "ISCAN(t1$a1_a2 [EQUALS promote(@c15 AS LONG)]) | FLATMAP q0 -> { COVERING(t2$b1_b3 [EQUALS promote(@c15 AS LONG)] -> [b1: KEY:[0], b3: KEY:[1], id: KEY:[2]]) | FILTER q0.a3 EQUALS _.b3 | FETCH AS q1 RETURN ((q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.a1 AS a1, q0.a2 AS a2, q0.a3 AS a3) AS a, (q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.b1 AS b1, q1.b2 AS b2, q1.b3 AS b3) AS b) }" - result: [ { { '__ROW_VERSION': !not_null _, id: 1001, a1: 1, a2: 10, a3: "a" }, { '__ROW_VERSION': !not_null _, id: 2001, b1: 1, b2: 20, b3: "a" } }, { { '__ROW_VERSION': !not_null _, id: 1001, a1: 1, a2: 10, a3: "a" }, { '__ROW_VERSION': !not_null _, id: 2002, b1: 1, b2: 19, b3: "a" } }, @@ -820,7 +820,7 @@ test_block: from t1_f(1) as a, t2_f(1) as b where a.a3 = b.b3 order by b.b3 - - explain: "ISCAN(t2$b1_b3 [EQUALS promote(@c15 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c15 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS _0, (q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS _1) }" + - explain: "ISCAN(t2$b1_b3 [EQUALS promote(@c15 AS LONG)]) | FLATMAP q0 -> { ISCAN(t1$a1 [EQUALS promote(@c15 AS LONG)]) | FILTER _.a3 EQUALS q0.b3 AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.id AS id, q1.a1 AS a1, q1.a2 AS a2, q1.a3 AS a3) AS a, (q0.__ROW_VERSION AS __ROW_VERSION, q0.id AS id, q0.b1 AS b1, q0.b2 AS b2, q0.b3 AS b3) AS b) }" - result: [ { { '__ROW_VERSION': !not_null _, id: 1001, a1: 1, a2: 10, a3: "a" }, { '__ROW_VERSION': !not_null _, id: 2001, b1: 1, b2: 20, b3: "a" } }, { { '__ROW_VERSION': !not_null _, id: 1003, a1: 1, a2: 12, a3: "a" }, { '__ROW_VERSION': !not_null _, id: 2001, b1: 1, b2: 20, b3: "a" } }, diff --git a/yaml-tests/src/test/resources/orderby.yamsql b/yaml-tests/src/test/resources/orderby.yamsql index f68730d413..54613f7652 100644 --- a/yaml-tests/src/test/resources/orderby.yamsql +++ b/yaml-tests/src/test/resources/orderby.yamsql @@ -279,13 +279,13 @@ test_block: # Ordering by a ambiguous projected column in subquery - query: select (T.*) from (select q as "nested", q.a as "ordered" from t2) as T order by "ordered" - supported_version: 4.10.1.0 - - explain: "ISCAN(I4 <,>) | MAP ((_.Q AS nested, _.Q.A AS ordered) AS _0)" + - explain: "ISCAN(I4 <,>) | MAP ((_.Q AS nested, _.Q.A AS ordered) AS T)" - result: [ {{{1, 2}, 1}}, {{{2, 1}, 2}}, {{{3, 2}, 3}}, {{{4, 1}, 4}}, {{{5, 2}, 5}}, {{{6, 1}, 6}}, {{{7, 2}, 7}}, {{{8, 1}, 8}} ] - # Ordering by a column in table function that has been projected in multiple ways - query: select (*) from t2_func() order by "ordered" - supported_version: 4.10.1.0 - - explain: "ISCAN(I4 <,>) | MAP ((_.Q AS nesting, _.Q.A AS ordered) AS _0)" + - explain: "ISCAN(I4 <,>) | MAP ((_.Q AS nesting, _.Q.A AS ordered) AS T2_FUNC)" - result: [ {{{1, 2}, 1}}, {{{2, 1}, 2}}, {{{3, 2}, 3}}, {{{4, 1}, 4}}, {{{5, 2}, 5}}, {{{6, 1}, 6}}, {{{7, 2}, 7}}, {{{8, 1}, 8}} ] - # Ordering by a column in table function that has been projected in multiple ways diff --git a/yaml-tests/src/test/resources/pseudo-field-clash.yamsql b/yaml-tests/src/test/resources/pseudo-field-clash.yamsql index a8533397c7..9f65a41bc3 100644 --- a/yaml-tests/src/test/resources/pseudo-field-clash.yamsql +++ b/yaml-tests/src/test/resources/pseudo-field-clash.yamsql @@ -243,7 +243,7 @@ test_block: - query: SELECT (t1.*), (t2.*), (t3.*) FROM t1, t2, t3 WHERE t2.id = t1.id AND t3.id = t1.id - - explain: "SCAN([IS T1]) | FLATMAP q0 -> { SCAN([IS T3, EQUALS q0.ID]) | FLATMAP q1 -> { SCAN([IS T2, EQUALS q0.ID]) AS q2 RETURN (q2 AS _0, q1 AS _1) } AS q3 RETURN (q0 AS _0, q3._0 AS _1, (q3._1.ID AS ID, q3._1.COL1 AS COL1, q3._1.COL2 AS COL2) AS _2) }" + - explain: "SCAN([IS T1]) | FLATMAP q0 -> { SCAN([IS T3, EQUALS q0.ID]) | FLATMAP q1 -> { SCAN([IS T2, EQUALS q0.ID]) AS q2 RETURN (q2 AS _0, q1 AS _1) } AS q3 RETURN (q0 AS T1, q3._0 AS T2, (q3._1.ID AS ID, q3._1.COL1 AS COL1, q3._1.COL2 AS COL2) AS T3) }" - unorderedResult: [ { { ID: 1, COL1: "a", '__ROW_VERSION': x'0000' }, { ID: 1, COL1: 10, '__ROW_VERSION': "aa" }, { ID: 1, COL1: 10, COL2: "aa" } }, { { ID: 2, COL1: "b", '__ROW_VERSION': x'0001' }, { ID: 2, COL1: 20, '__ROW_VERSION': "ab" }, { ID: 2, COL1: 20, COL2: "ab" } }, diff --git a/yaml-tests/src/test/resources/valid-identifiers.yamsql b/yaml-tests/src/test/resources/valid-identifiers.yamsql index 2d29a98006..8e71d2f205 100644 --- a/yaml-tests/src/test/resources/valid-identifiers.yamsql +++ b/yaml-tests/src/test/resources/valid-identifiers.yamsql @@ -460,7 +460,7 @@ test_block: - # named record construction with uid star - query: select struct "x$$" ("foo.tableA".*) from "foo.tableA" - - explain: "ISCAN(foo.tableA.idx3 <,>) | MAP (_ AS _0)" + - explain: "ISCAN(foo.tableA.idx3 <,>) | MAP (_ AS foo.tableA)" - result: [{{"foo.tableA.A1": 1 , "foo.tableA.A2": 10, "foo.tableA.A3": 1}}, {{"foo.tableA.A1": 2, "foo.tableA.A2": 10, "foo.tableA.A3": 2}}, {{"foo.tableA.A1": 3, "foo.tableA.A2": 10, "foo.tableA.A3": 3}}] - # named record construction with aliased expressions diff --git a/yaml-tests/src/test/resources/versions-tests.yamsql b/yaml-tests/src/test/resources/versions-tests.yamsql index 59ed76260c..91221b274b 100644 --- a/yaml-tests/src/test/resources/versions-tests.yamsql +++ b/yaml-tests/src/test/resources/versions-tests.yamsql @@ -228,7 +228,7 @@ test_block: - # Do not include __ROW_VERSION (as a pseudo-column) in nested (*) with identifier - query: select (t1.*) from t1 where col1 = 10; - - explain: "ISCAN(I1 [EQUALS promote(@c11 AS LONG)]) | MAP ((_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS _0)" + - explain: "ISCAN(I1 [EQUALS promote(@c11 AS LONG)]) | MAP ((_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS T1)" - result: [ { {ID: 1, COL1: 10, COL2: 1} }, { {ID: 2, COL1: 10, COL2: 2} }, @@ -250,7 +250,7 @@ test_block: - # Do not include __ROW_VERSION (as a pseudo-column) in a nested element when there is a type alias - query: select (a.*) from t1 as a where a.col1 = 10; - - explain: "ISCAN(I1 [EQUALS promote(@c15 AS LONG)]) | MAP ((_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS _0)" + - explain: "ISCAN(I1 [EQUALS promote(@c15 AS LONG)]) | MAP ((_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS A)" - result: [ { { ID: 1, COL1: 10, COL2: 1 } }, { { ID: 2, COL1: 10, COL2: 2 } }, @@ -291,7 +291,7 @@ test_block: - # Do not include __ROW_VERSION (as a pseudo-column) in nested (*) without identifier - query: select (*) from t1 where col1 = 10; - - explain: "ISCAN(I1 [EQUALS promote(@c9 AS LONG)]) | MAP ((_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS _0)" + - explain: "ISCAN(I1 [EQUALS promote(@c9 AS LONG)]) | MAP ((_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS T1)" - result: [ { {ID: 1, COL1: 10, COL2: 1} }, { {ID: 2, COL1: 10, COL2: 2} }, @@ -325,7 +325,7 @@ test_block: - result: [{VERSION: !not_null _, ID: 6, COL1: 20, COL2: 6}, {VERSION: !not_null _, ID: 7, COL1: 20, COL2: 7}, {VERSION: !not_null _, ID: 8, COL1: 20, COL2: 8}, {VERSION: !not_null _, ID: 9, COL1: 20, COL2: 9}, {VERSION: !not_null _, ID: 10, COL1: 20, COL2: 10}, {VERSION: !not_null _, ID: 11, COL1: 20, COL2: 11}, {VERSION: !not_null _, ID: 12, COL1: 20, COL2: 12}, {VERSION: !not_null _, ID: 13, COL1: 20, COL2: 13}] - - query: select "__ROW_VERSION" as version, (t1.*) from t1 where col1 = 20; - - explain: "ISCAN(I1 [EQUALS promote(@c15 AS LONG)]) | MAP (_.__ROW_VERSION AS VERSION, (_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS _1)" + - explain: "ISCAN(I1 [EQUALS promote(@c15 AS LONG)]) | MAP (_.__ROW_VERSION AS VERSION, (_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS T1)" - result: [{VERSION: !not_null _, {ID: 6, COL1: 20, COL2: 6}}, {VERSION: !not_null _, {ID: 7, COL1: 20, COL2: 7}}, {VERSION: !not_null _, {ID: 8, COL1: 20, COL2: 8}}, {VERSION: !not_null _, {ID: 9, COL1: 20, COL2: 9}}, {VERSION: !not_null _, {ID: 10, COL1: 20, COL2: 10}}, {VERSION: !not_null _, {ID: 11, COL1: 20, COL2: 11}}, {VERSION: !not_null _, {ID: 12, COL1: 20, COL2: 12}}, {VERSION: !not_null _, {ID: 13, COL1: 20, COL2: 13}}] - - query: select "__ROW_VERSION", t1.* from t1 where col1 = 20; @@ -333,7 +333,7 @@ test_block: - result: [{__ROW_VERSION: !not_null _, ID: 6, COL1: 20, COL2: 6}, {__ROW_VERSION: !not_null _, ID: 7, COL1: 20, COL2: 7}, {__ROW_VERSION: !not_null _, ID: 8, COL1: 20, COL2: 8}, {__ROW_VERSION: !not_null _, ID: 9, COL1: 20, COL2: 9}, {__ROW_VERSION: !not_null _, ID: 10, COL1: 20, COL2: 10}, {__ROW_VERSION: !not_null _, ID: 11, COL1: 20, COL2: 11}, {__ROW_VERSION: !not_null _, ID: 12, COL1: 20, COL2: 12}, {__ROW_VERSION: !not_null _, ID: 13, COL1: 20, COL2: 13}] - - query: select "__ROW_VERSION", (t1.*) from t1 where col1 = 20; - - explain: "ISCAN(I1 [EQUALS promote(@c13 AS LONG)]) | MAP (_.__ROW_VERSION AS __ROW_VERSION, (_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS _1)" + - explain: "ISCAN(I1 [EQUALS promote(@c13 AS LONG)]) | MAP (_.__ROW_VERSION AS __ROW_VERSION, (_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS T1)" - result: [{__ROW_VERSION: !not_null _, {ID: 6, COL1: 20, COL2: 6}}, {__ROW_VERSION: !not_null _, {ID: 7, COL1: 20, COL2: 7}}, {__ROW_VERSION: !not_null _, {ID: 8, COL1: 20, COL2: 8}}, {__ROW_VERSION: !not_null _, {ID: 9, COL1: 20, COL2: 9}}, {__ROW_VERSION: !not_null _, {ID: 10, COL1: 20, COL2: 10}}, {__ROW_VERSION: !not_null _, {ID: 11, COL1: 20, COL2: 11}}, {__ROW_VERSION: !not_null _, {ID: 12, COL1: 20, COL2: 12}}, {__ROW_VERSION: !not_null _, {ID: 13, COL1: 20, COL2: 13}}] - # Non-version query that should be able to make use of the covering index @@ -439,7 +439,7 @@ test_block: - # Simple star select ordered by newest first. The row version and data are packaged up. The row version should not be in the nested star - query: select (t1."__ROW_VERSION", (t1.*)) from t1 where col1 = 10 order by "__ROW_VERSION" desc; - - explain: "ISCAN(GROUPED_VERSION_INDEX [EQUALS promote(@c17 AS LONG)] REVERSE) | MAP ((_.__ROW_VERSION AS __ROW_VERSION, (_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS _1) AS _0)" + - explain: "ISCAN(GROUPED_VERSION_INDEX [EQUALS promote(@c17 AS LONG)] REVERSE) | MAP ((_.__ROW_VERSION AS __ROW_VERSION, (_.ID AS ID, _.COL1 AS COL1, _.COL2 AS COL2) AS T1) AS _0)" - result: [ { { '__ROW_VERSION': !not_null _, { ID: 4, COL1: 10, COL2: 4 }, }, }, { { '__ROW_VERSION': !not_null _, { ID: 2, COL1: 10, COL2: 2 }, }, }, @@ -581,7 +581,7 @@ test_block: - query: select "__ROW_VERSION", (a.*), (b.*) from t1 as a, (select * from t2) b where a.col2 = b.col1 - - explain: "ISCAN(I1 <,>) | FLATMAP q0 -> { ISCAN(T2_COL2 <,>) | FILTER q0.COL2 EQUALS _.COL1 AS q1 RETURN (q0.__ROW_VERSION AS __ROW_VERSION, (q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS _1, (q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS _2) }" + - explain: "ISCAN(I1 <,>) | FLATMAP q0 -> { ISCAN(T2_COL2 <,>) | FILTER q0.COL2 EQUALS _.COL1 AS q1 RETURN (q0.__ROW_VERSION AS __ROW_VERSION, (q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS A, (q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS B) }" - initialVersionLessThan: 4.10.1.0 # Prior to 4.10.1.0, we did not keep track of which quantifiers actually had the version, so this would default to # assuming that the row version column was ambiguous @@ -704,7 +704,7 @@ test_block: - query: select (t2.*), (t3.*) from t2, t3 where t2.col2 = 'b' and t3.col1 = 'b' - - explain: "ISCAN(T2_COL2 [EQUALS promote(@c21 AS STRING)]) | FLATMAP q0 -> { COVERING(T3_VERSION_WITH_COL1 <,> -> [COL1: VALUE:[0], ID: KEY:[2]]) | FILTER _.COL1 EQUALS promote(@c21 AS STRING) | FETCH AS q1 RETURN ((q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS _0, (q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS _1) }" + - explain: "ISCAN(T2_COL2 [EQUALS promote(@c21 AS STRING)]) | FLATMAP q0 -> { COVERING(T3_VERSION_WITH_COL1 <,> -> [COL1: VALUE:[0], ID: KEY:[2]]) | FILTER _.COL1 EQUALS promote(@c21 AS STRING) | FETCH AS q1 RETURN ((q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS T2, (q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS T3) }" - unorderedResult: [ { { ID: 3, COL1: 1, COL2: 'b' }, { ID: 3, COL1: 'b', COL2: 1 } }, { { ID: 4, COL1: 2, COL2: 'b' }, { ID: 3, COL1: 'b', COL2: 1 } }, @@ -748,7 +748,7 @@ test_block: from t2, t3 where t2.col2 = 'b' and t3.col1 = 'b' and t2."__ROW_VERSION" > t3."__ROW_VERSION" - - explain: "ISCAN(T2_COL2 [EQUALS promote(@c21 AS STRING)]) | FLATMAP q0 -> { ISCAN(T3_VERSION_WITH_COL1 <,>) | FILTER _.COL1 EQUALS promote(@c21 AS STRING) AND q0.__ROW_VERSION GREATER_THAN _.__ROW_VERSION AS q1 RETURN ((q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS _0, (q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS _1) }" + - explain: "ISCAN(T2_COL2 [EQUALS promote(@c21 AS STRING)]) | FLATMAP q0 -> { ISCAN(T3_VERSION_WITH_COL1 <,>) | FILTER _.COL1 EQUALS promote(@c21 AS STRING) AND q0.__ROW_VERSION GREATER_THAN _.__ROW_VERSION AS q1 RETURN ((q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS T2, (q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS T3) }" - initialVersionLessThan: 4.10.1.0 # Prior to 4.10.1.0, the two row version predicates would be transformed incorrectly so that they were # evaluated twice on the same record (i.e., `version([_]) > version([_])`) which is a contradiction, so we @@ -771,7 +771,7 @@ test_block: - query: select (A.*), (B.*) from t2_v(2) as A, t3_v(2) as B where A.col2 = B.col1 - - explain: "ISCAN(T3_VERSION_WITH_COL1 <,>) | FILTER _.COL2 EQUALS promote(@c15 AS LONG) | FLATMAP q0 -> { ISCAN(T2_COL2 [EQUALS q0.COL1]) | FILTER _.COL1 EQUALS promote(@c15 AS LONG) AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS _0, (q0.__ROW_VERSION AS __ROW_VERSION, q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS _1) }" + - explain: "ISCAN(T3_VERSION_WITH_COL1 <,>) | FILTER _.COL2 EQUALS promote(@c15 AS LONG) | FLATMAP q0 -> { ISCAN(T2_COL2 [EQUALS q0.COL1]) | FILTER _.COL1 EQUALS promote(@c15 AS LONG) AS q1 RETURN ((q1.__ROW_VERSION AS __ROW_VERSION, q1.ID AS ID, q1.COL1 AS COL1, q1.COL2 AS COL2) AS A, (q0.__ROW_VERSION AS __ROW_VERSION, q0.ID AS ID, q0.COL1 AS COL1, q0.COL2 AS COL2) AS B) }" - unorderedResult: [ { { '__ROW_VERSION': !not_null _, ID: 4, COL1: 2, COL2: 'b' }, { '__ROW_VERSION': !not_null _, ID: 4, COL1: 'b', COL2: 2 } }, { { '__ROW_VERSION': !not_null _, ID: 2, COL1: 2, COL2: 'a' }, { '__ROW_VERSION': !not_null _, ID: 1, COL1: 'a', COL2: 2 } }, From b6e3c30b5a1b966f169f17e8d356f339e88a97c7 Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Fri, 22 May 2026 21:53:19 +0100 Subject: [PATCH 4/7] Address comments --- .../arrays-cardinality.metrics.binpb | 107 -------------- .../resources/arrays-cardinality.metrics.yaml | 138 +++--------------- .../star-expression-metadata.yamsql | 9 ++ 3 files changed, 28 insertions(+), 226 deletions(-) diff --git a/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb b/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb index 147ceed64a..f6e7d491b2 100644 --- a/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb +++ b/yaml-tests/src/test/resources/arrays-cardinality.metrics.binpb @@ -212,113 +212,6 @@ digraph G { 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; 2 -> 1 [ label=< q53> label="q53" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -n -arrays-cardinality-index-testsLEXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS NULL -c ^(0 8&@PCOVERING(tab1_indexed_index [[null],[null]] -> [id: KEY:[2]]) | MAP (_.id AS id) -digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Covering Index Scan
range: [(null), (null)]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -r -arrays-cardinality-index-testsPEXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS NOT NULL -c W(0 -8&@JCOVERING(tab1_indexed_index ([null],> -> [id: KEY:[2]]) | MAP (_.id AS id) -digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Covering Index Scan
range: ((null), ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -q -arrays-cardinality-index-testsOEXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") IS NULL -c a(0 8&@SCOVERING(tab1_indexed_nn_index [[null],[null]] -> [id: KEY:[2]]) | MAP (_.id AS id) -digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Covering Index Scan
range: [(null), (null)]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Index
tab1_indexed_nn_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -u -arrays-cardinality-index-testsSEXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") IS NOT NULL -c (08&@MCOVERING(tab1_indexed_nn_index ([null],> -> [id: KEY:[2]]) | MAP (_.id AS id) -digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q52.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Covering Index Scan
range: ((null), ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Index
tab1_indexed_nn_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q52> label="q52" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -e -arrays-cardinality-index-testsCEXPLAIN SELECT "id" FROM "tab1" WHERE CARDINALITY("int_arr") = NULL -ā@ :(08@0SCAN([IS tab1]) | FILTER null | MAP (_.id AS id)digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q23.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Predicate Filter
WHERE null
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Scan
comparisons: [IS tab1]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 4 [ label=<
Primary Storage
record types: [dummy, tab1, tab1_indexed_nn, tab2, tab1_nn, tab1_indexed]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -h -arrays-cardinality-index-testsFEXPLAIN SELECT "id" FROM "tab1_nn" WHERE CARDINALITY("int_arr") = NULL -c@ '(08@3SCAN([IS tab1_nn]) | FILTER null | MAP (_.id AS id)digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q23.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Predicate Filter
WHERE null
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Scan
comparisons: [IS tab1_nn]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 4 [ label=<
Primary Storage
record types: [dummy, tab1, tab1_indexed_nn, tab2, tab1_nn, tab1_indexed]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q23> label="q23" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -m -arrays-cardinality-index-testsKEXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") = NULL -h ^(!0 8*@NCOVERING(tab1_indexed_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP (_.id AS id) -digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q53.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Covering Index Scan
comparisons: [EQUALS NULL]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Index
tab1_indexed_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q53> label="q53" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; -} -p -arrays-cardinality-index-testsNEXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = NULL -h d(!0 8*@QCOVERING(tab1_indexed_nn_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP (_.id AS id) -digraph G { - fontname=courier; - rankdir=BT; - splines=line; - 1 [ label=<
Value Computation
MAP (q53.id AS id)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id)" ]; - 2 [ label=<
Covering Index Scan
comparisons: [EQUALS NULL]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 [ label=<
Index
tab1_indexed_nn_index
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS id, ARRAY(INT) AS int_arr)" ]; - 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; - 2 -> 1 [ label=< q53> label="q53" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; }  arrays-cardinality-index-testsEXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = 1 ORDER BY CARDINALITY("int_arr") diff --git a/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml b/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml index 1bfd63c655..592c09feb8 100644 --- a/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml +++ b/yaml-tests/src/test/resources/arrays-cardinality.metrics.yaml @@ -1,6 +1,6 @@ arrays-cardinality-tests: - query: EXPLAIN SELECT CARDINALITY("int_arr") FROM "tab1_nn" - ref: arrays-cardinality.yamsql:76 + ref: arrays_cardinality.yamsql:72 explain: SCAN([IS tab1_nn]) | MAP (cardinality(_.int_arr) AS _0) task_count: 146 task_total_time_ms: 71 @@ -11,7 +11,7 @@ arrays-cardinality-tests: insert_new_count: 12 insert_reused_count: 2 - query: EXPLAIN SELECT CARDINALITY("int_arr") FROM "tab1" - ref: arrays-cardinality.yamsql:81 + ref: arrays_cardinality.yamsql:76 explain: SCAN([IS tab1]) | MAP (cardinality(_.int_arr) AS _0) task_count: 146 task_total_time_ms: 9 @@ -23,7 +23,7 @@ arrays-cardinality-tests: insert_reused_count: 2 arrays-cardinality-index-tests: - query: EXPLAIN SELECT * FROM "tab1" - ref: arrays-cardinality.yamsql:109 + ref: arrays_cardinality.yamsql:86 explain: SCAN([IS tab1]) task_count: 140 task_total_time_ms: 6 @@ -34,7 +34,7 @@ arrays-cardinality-index-tests: insert_new_count: 10 insert_reused_count: 2 - query: EXPLAIN SELECT * FROM "tab1_indexed_nn" - ref: arrays-cardinality.yamsql:111 + ref: arrays_cardinality.yamsql:88 explain: ISCAN(tab1_indexed_nn_index <,>) task_count: 221 task_total_time_ms: 22 @@ -45,7 +45,7 @@ arrays-cardinality-index-tests: insert_new_count: 19 insert_reused_count: 6 - query: EXPLAIN SELECT * FROM "tab1_indexed" - ref: arrays-cardinality.yamsql:113 + ref: arrays_cardinality.yamsql:90 explain: ISCAN(tab1_indexed_index <,>) task_count: 221 task_total_time_ms: 12 @@ -56,7 +56,7 @@ arrays-cardinality-index-tests: insert_new_count: 19 insert_reused_count: 6 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" - ref: arrays-cardinality.yamsql:116 + ref: arrays_cardinality.yamsql:93 explain: 'COVERING(tab1_indexed_nn_index <,> -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 241 @@ -68,7 +68,7 @@ arrays-cardinality-index-tests: insert_new_count: 25 insert_reused_count: 4 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" - ref: arrays-cardinality.yamsql:119 + ref: arrays_cardinality.yamsql:96 explain: 'COVERING(tab1_indexed_index <,> -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 241 task_total_time_ms: 11 @@ -79,7 +79,7 @@ arrays-cardinality-index-tests: insert_new_count: 25 insert_reused_count: 4 - query: EXPLAIN SELECT "id", CARDINALITY("int_arr") AS "card" FROM "tab1_indexed_nn" - ref: arrays-cardinality.yamsql:125 + ref: arrays_cardinality.yamsql:100 explain: ISCAN(tab1_indexed_nn_index <,>) | MAP (_.id AS id, cardinality(_.int_arr) AS card) task_count: 213 @@ -91,7 +91,7 @@ arrays-cardinality-index-tests: insert_new_count: 22 insert_reused_count: 4 - query: EXPLAIN SELECT "id", CARDINALITY("int_arr") AS "card" FROM "tab1_indexed" - ref: arrays-cardinality.yamsql:129 + ref: arrays_cardinality.yamsql:103 explain: ISCAN(tab1_indexed_index <,>) | MAP (_.id AS id, cardinality(_.int_arr) AS card) task_count: 213 @@ -103,7 +103,7 @@ arrays-cardinality-index-tests: insert_new_count: 22 insert_reused_count: 4 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" ORDER BY "id" DESC - ref: arrays-cardinality.yamsql:133 + ref: arrays_cardinality.yamsql:107 explain: SCAN([IS tab1_indexed_nn]) | MAP (_.id AS id) task_count: 158 task_total_time_ms: 8 @@ -114,7 +114,7 @@ arrays-cardinality-index-tests: insert_new_count: 12 insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" ORDER BY "id" DESC - ref: arrays-cardinality.yamsql:136 + ref: arrays_cardinality.yamsql:110 explain: SCAN([IS tab1_indexed]) | MAP (_.id AS id) task_count: 158 task_total_time_ms: 8 @@ -126,7 +126,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" ORDER BY CARDINALITY("int_arr") DESC - ref: arrays-cardinality.yamsql:140 + ref: arrays_cardinality.yamsql:115 explain: ISCAN(tab1_indexed_nn_index <,> REVERSE) | MAP (_.id AS id, cardinality(_.int_arr) AS _1) | MAP (_.id AS id) task_count: 244 @@ -139,7 +139,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" ORDER BY CARDINALITY("int_arr") DESC - ref: arrays-cardinality.yamsql:143 + ref: arrays_cardinality.yamsql:118 explain: ISCAN(tab1_indexed_index <,> REVERSE) | MAP (_.id AS id, cardinality(_.int_arr) AS _1) | MAP (_.id AS id) task_count: 244 @@ -152,7 +152,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed_nn" ORDER BY CARDINALITY("int_arr") - ref: arrays-cardinality.yamsql:147 + ref: arrays_cardinality.yamsql:123 explain: ISCAN(tab1_indexed_nn_index <,>) | MAP (cardinality(_.int_arr) AS card, _.id AS id) task_count: 182 @@ -165,7 +165,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed" ORDER BY CARDINALITY("int_arr") - ref: arrays-cardinality.yamsql:152 + ref: arrays_cardinality.yamsql:126 explain: ISCAN(tab1_indexed_index <,>) | MAP (cardinality(_.int_arr) AS card, _.id AS id) task_count: 182 @@ -178,7 +178,7 @@ arrays-cardinality-index-tests: insert_reused_count: 2 - query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = 1 - ref: arrays-cardinality.yamsql:156 + ref: arrays_cardinality.yamsql:130 explain: 'COVERING(tab1_indexed_nn_index [EQUALS promote(@c11 AS INT)] -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 395 @@ -191,7 +191,7 @@ arrays-cardinality-index-tests: insert_reused_count: 3 - query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") = 1 - ref: arrays-cardinality.yamsql:159 + ref: arrays_cardinality.yamsql:133 explain: 'COVERING(tab1_indexed_index [EQUALS promote(@c11 AS INT)] -> [id: KEY:[2]]) | MAP (_.id AS id)' task_count: 395 @@ -202,109 +202,9 @@ arrays-cardinality-index-tests: insert_time_ms: 1 insert_new_count: 42 insert_reused_count: 3 -- query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS - NULL - ref: arrays-cardinality.yamsql:163 - explain: 'COVERING(tab1_indexed_index [[null],[null]] -> [id: KEY:[2]]) | MAP - (_.id AS id)' - task_count: 379 - task_total_time_ms: 3 - transform_count: 99 - transform_time_ms: 1 - transform_yield_count: 31 - insert_time_ms: 0 - insert_new_count: 38 - insert_reused_count: 3 -- query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") IS - NOT NULL - ref: arrays-cardinality.yamsql:166 - explain: 'COVERING(tab1_indexed_index ([null],> -> [id: KEY:[2]]) | MAP (_.id - AS id)' - task_count: 379 - task_total_time_ms: 3 - transform_count: 99 - transform_time_ms: 1 - transform_yield_count: 31 - insert_time_ms: 0 - insert_new_count: 38 - insert_reused_count: 3 -- query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") - IS NULL - ref: arrays-cardinality.yamsql:171 - explain: 'COVERING(tab1_indexed_nn_index [[null],[null]] -> [id: KEY:[2]]) | MAP - (_.id AS id)' - task_count: 379 - task_total_time_ms: 5 - transform_count: 99 - transform_time_ms: 1 - transform_yield_count: 31 - insert_time_ms: 0 - insert_new_count: 38 - insert_reused_count: 3 -- query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") - IS NOT NULL - ref: arrays-cardinality.yamsql:174 - explain: 'COVERING(tab1_indexed_nn_index ([null],> -> [id: KEY:[2]]) | MAP (_.id - AS id)' - task_count: 379 - task_total_time_ms: 5 - transform_count: 99 - transform_time_ms: 2 - transform_yield_count: 31 - insert_time_ms: 0 - insert_new_count: 38 - insert_reused_count: 3 -- query: EXPLAIN SELECT "id" FROM "tab1" WHERE CARDINALITY("int_arr") = NULL - ref: arrays-cardinality.yamsql:181 - explain: SCAN([IS tab1]) | FILTER null | MAP (_.id AS id) - task_count: 222 - task_total_time_ms: 2 - transform_count: 64 - transform_time_ms: 0 - transform_yield_count: 17 - insert_time_ms: 0 - insert_new_count: 22 - insert_reused_count: 2 -- query: EXPLAIN SELECT "id" FROM "tab1_nn" WHERE CARDINALITY("int_arr") = NULL - ref: arrays-cardinality.yamsql:184 - explain: SCAN([IS tab1_nn]) | FILTER null | MAP (_.id AS id) - task_count: 222 - task_total_time_ms: 1 - transform_count: 64 - transform_time_ms: 0 - transform_yield_count: 17 - insert_time_ms: 0 - insert_new_count: 22 - insert_reused_count: 2 -- query: EXPLAIN SELECT "id" FROM "tab1_indexed" WHERE CARDINALITY("int_arr") = - NULL - ref: arrays-cardinality.yamsql:188 - explain: 'COVERING(tab1_indexed_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP (_.id - AS id)' - task_count: 395 - task_total_time_ms: 3 - transform_count: 104 - transform_time_ms: 1 - transform_yield_count: 33 - insert_time_ms: 0 - insert_new_count: 42 - insert_reused_count: 3 -- query: EXPLAIN SELECT "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") - = NULL - ref: arrays-cardinality.yamsql:191 - explain: 'COVERING(tab1_indexed_nn_index [EQUALS NULL] -> [id: KEY:[2]]) | MAP - (_.id AS id)' - task_count: 395 - task_total_time_ms: 3 - transform_count: 104 - transform_time_ms: 1 - transform_yield_count: 33 - insert_time_ms: 0 - insert_new_count: 42 - insert_reused_count: 3 - query: EXPLAIN SELECT CARDINALITY("int_arr") AS "card", "id" FROM "tab1_indexed_nn" WHERE CARDINALITY("int_arr") = 1 ORDER BY CARDINALITY("int_arr") - ref: arrays-cardinality.yamsql:195 + ref: arrays_cardinality.yamsql:138 explain: ISCAN(tab1_indexed_nn_index [EQUALS promote(@c18 AS INT)]) | MAP (cardinality(_.int_arr) AS card, _.id AS id) task_count: 276 @@ -317,7 +217,7 @@ arrays-cardinality-index-tests: insert_reused_count: 1 - query: EXPLAIN SELECT CARDINALITY("struct"."int_arr") AS "card", "id" FROM "tab2" WHERE CARDINALITY("struct"."int_arr") = 1 ORDER BY CARDINALITY("struct"."int_arr") - ref: arrays-cardinality.yamsql:200 + ref: arrays_cardinality.yamsql:143 explain: ISCAN(tab2_index [EQUALS promote(@c22 AS INT)]) | MAP (cardinality(_.struct.int_arr) AS card, _.id AS id) task_count: 276 diff --git a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql index b139d79d4f..83efa7599b 100644 --- a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql +++ b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql @@ -104,6 +104,15 @@ test_block: - resultMetadata: [{F: [{ID: BIGINT}, {VAL: BIGINT}]}] - result: [{F: {ID: 1, VAL: 10}}] --- +test_block: + name: values-source + tests: + - + # VALUES without alias has no name → source is anonymous, column is _0 + - query: SELECT (*) FROM VALUES (1, 2), (3, 4) + - resultMetadata: [{_0: [{_0: INTEGER}, {_1: INTEGER}]}] + - unorderedResult: [{_0: {_0: 1, _1: 2}}, {_0: {_0: 3, _1: 4}}] +--- test_block: name: ambiguous-join-source tests: From 3a9d640f977dc441b458ca300f70adebccc025d8 Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Tue, 26 May 2026 14:16:22 +0100 Subject: [PATCH 5/7] Address comments --- .../query/visitors/ExpressionVisitor.java | 13 ++-- .../star-expression-metadata.yamsql | 62 +++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java index c07f0fdddc..802ed2eac3 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/ExpressionVisitor.java @@ -899,14 +899,15 @@ public Expression visitRecordConstructor(@Nonnull RelationalParser.RecordConstru if (ctx.STAR() != null) { final var star = getDelegate().getSemanticAnalyzer().expandStar(Optional.empty(), getDelegate().getLogicalOperators()); final var resultValue = star.getUnderlying(); - // When there is exactly one table in scope, name the column after that table/alias. - // With multiple tables the expansion is ambiguous, so fall back to an unnamed column. + // Name the column after the sole for-each quantifier in scope. + // Both standard joins and PartiQL unnest expansions introduce multiple for-each + // quantifiers (attributes originating from different sources), so fall back to + // an unnamed column whenever there is more than one. final var forEachOps = getDelegate().getLogicalOperators().forEachOnly(); if (Iterables.size(forEachOps) == 1) { - final Optional tableName = Iterables.getOnlyElement(forEachOps).getName(); - if (tableName.isPresent()) { - return Expression.of(resultValue, tableName.get()); - } + return Iterables.getOnlyElement(forEachOps).getName() + .map(name -> Expression.of(resultValue, name)) + .orElse(Expression.ofUnnamed(resultValue)); } return Expression.ofUnnamed(resultValue); } diff --git a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql index 83efa7599b..1b07d2067d 100644 --- a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql +++ b/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql @@ -30,6 +30,9 @@ schema_template: create table foo(id bigint, val bigint, primary key(id)) create table bar(bid bigint, bval bigint, primary key(bid)) + create type as struct s1(a bigint, b bigint) + create type as struct s2(c bigint, d s1 array) + create table t1(col1 bigint, col2 s2 array, primary key(col1)) --- transaction_setups: tvf_sq: create temporary function sq(in x bigint) on commit drop function AS @@ -39,6 +42,7 @@ setup: steps: - query: INSERT INTO foo VALUES (1, 10) - query: INSERT INTO bar VALUES (1, 20) + - query: INSERT INTO t1 VALUES (1, [(10, [(100, 200)])]) --- test_block: name: table-source @@ -112,6 +116,11 @@ test_block: - query: SELECT (*) FROM VALUES (1, 2), (3, 4) - resultMetadata: [{_0: [{_0: INTEGER}, {_1: INTEGER}]}] - unorderedResult: [{_0: {_0: 1, _1: 2}}, {_0: {_0: 3, _1: 4}}] + - + # VALUES with in-line table definition → column named after the table alias + - query: SELECT (*) FROM VALUES (1, 2), (3, 4) AS X(A, B) + - resultMetadata: [{X: [{A: INTEGER}, {B: INTEGER}]}] + - unorderedResult: [{X: {A: 1, B: 2}}, {X: {A: 3, B: 4}}] --- test_block: name: ambiguous-join-source @@ -121,4 +130,57 @@ test_block: - query: SELECT (*) FROM foo, bar WHERE foo.id = bar.bid - resultMetadata: [{_0: [{ID: BIGINT}, {VAL: BIGINT}, {BID: BIGINT}, {BVAL: BIGINT}]}] - result: [{_0: {ID: 1, VAL: 10, BID: 1, BVAL: 20}}] +--- +test_block: + name: partiql-unnest-source + tests: + - + # PartiQL unnest introduces two for-each quantifiers (t1 and k) → source is ambiguous, + # column is anonymous even though only one base table is in scope + - query: SELECT (*) FROM t1, t1.col2 AS k + - resultMetadata: + - _0: + - {COL1: BIGINT} + - COL2: {array: [{C: BIGINT}, {D: {array: [{A: BIGINT}, {B: BIGINT}]}}]} + - {C: BIGINT} + - D: {array: [{A: BIGINT}, {B: BIGINT}]} + - result: [{_0: {COL1: 1, COL2: [{C: 10, D: [{A: 100, B: 200}]}], C: 10, D: [{A: 100, B: 200}]}}] + - + # Wrapping the multi-level unnest in a subquery alias gives exactly one for-each + # quantifier in the outer scope → column named after the alias + - query: SELECT (*) FROM (SELECT * FROM t1, t1.col2 AS k, k.d AS m) AS p + - resultMetadata: + - P: + - {COL1: BIGINT} + - COL2: {array: [{C: BIGINT}, {D: {array: [{A: BIGINT}, {B: BIGINT}]}}]} + - {C: BIGINT} + - D: {array: [{A: BIGINT}, {B: BIGINT}]} + - {A: BIGINT} + - {B: BIGINT} + - result: [{P: {COL1: 1, COL2: [{C: 10, D: [{A: 100, B: 200}]}], C: 10, D: [{A: 100, B: 200}], A: 100, B: 200}}] + - + # Explicit (p.*) qualifier gives the same column name as the inferred case above + - query: SELECT (p.*) FROM (SELECT * FROM t1, t1.col2 AS k, k.d AS m) AS p + - resultMetadata: + - P: + - {COL1: BIGINT} + - COL2: {array: [{C: BIGINT}, {D: {array: [{A: BIGINT}, {B: BIGINT}]}}]} + - {C: BIGINT} + - D: {array: [{A: BIGINT}, {B: BIGINT}]} + - {A: BIGINT} + - {B: BIGINT} + - result: [{P: {COL1: 1, COL2: [{C: 10, D: [{A: 100, B: 200}]}], C: 10, D: [{A: 100, B: 200}], A: 100, B: 200}}] + - + # Nesting: inner (*) names the column after alias h; outer (*) names the column after alias q + - query: SELECT (*) FROM (SELECT (*) FROM (SELECT * FROM t1, t1.col2 AS k, k.d AS m) AS h) AS q + - resultMetadata: + - Q: + - H: + - {COL1: BIGINT} + - COL2: {array: [{C: BIGINT}, {D: {array: [{A: BIGINT}, {B: BIGINT}]}}]} + - {C: BIGINT} + - D: {array: [{A: BIGINT}, {B: BIGINT}]} + - {A: BIGINT} + - {B: BIGINT} + - result: [{Q: {H: {COL1: 1, COL2: [{C: 10, D: [{A: 100, B: 200}]}], C: 10, D: [{A: 100, B: 200}], A: 100, B: 200}}}] ... From 2e4e5d9a859078f5bfd5ce7ca4a487fcfa5c59bd Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Tue, 26 May 2026 16:31:42 +0100 Subject: [PATCH 6/7] Move star-expression-metadata test to YamlIntegrationTests --- yaml-tests/src/test/java/CheckResultMetadataTest.java | 3 +-- yaml-tests/src/test/java/YamlIntegrationTests.java | 5 +++++ .../shouldPass => }/star-expression-metadata.yamsql | 0 3 files changed, 6 insertions(+), 2 deletions(-) rename yaml-tests/src/test/resources/{check-result-metadata/shouldPass => }/star-expression-metadata.yamsql (100%) diff --git a/yaml-tests/src/test/java/CheckResultMetadataTest.java b/yaml-tests/src/test/java/CheckResultMetadataTest.java index 4a6e73dd56..df4e85a87c 100644 --- a/yaml-tests/src/test/java/CheckResultMetadataTest.java +++ b/yaml-tests/src/test/java/CheckResultMetadataTest.java @@ -116,8 +116,7 @@ static Stream shouldPass() { "array-of-struct-column", // array-of-struct with nested element field descriptors "struct-type-name", // struct type name as optional prefix in field list "field-named-array", // struct field named "array" — no clash with {array: ...} map syntax - "type-named-array", // struct type named "array" — no clash with {array: ...} map syntax - "star-expression-metadata" // column names from SELECT (*) / SELECT (T.*) / SELECT (*) AS alias + "type-named-array" // struct type named "array" — no clash with {array: ...} map syntax ); } diff --git a/yaml-tests/src/test/java/YamlIntegrationTests.java b/yaml-tests/src/test/java/YamlIntegrationTests.java index f42b76bf15..eaaad34e19 100644 --- a/yaml-tests/src/test/java/YamlIntegrationTests.java +++ b/yaml-tests/src/test/java/YamlIntegrationTests.java @@ -387,6 +387,11 @@ public void standardTestsWithProto(YamlTest.Runner runner) throws Exception { runner.runYamsql("standard-tests-proto.yamsql"); } + @TestTemplate + public void starExpressionColumnNames(YamlTest.Runner runner) throws Exception { + runner.runYamsql("star-expression-metadata.yamsql"); + } + @TestTemplate public void structTypeNullabilityVariants(YamlTest.Runner runner) throws Exception { runner.runYamsql("struct-type-nullability-variants.yamsql"); diff --git a/yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql b/yaml-tests/src/test/resources/star-expression-metadata.yamsql similarity index 100% rename from yaml-tests/src/test/resources/check-result-metadata/shouldPass/star-expression-metadata.yamsql rename to yaml-tests/src/test/resources/star-expression-metadata.yamsql From 40f2473a06578bf38f10a2cb1cda1f41c17f4a5f Mon Sep 17 00:00:00 2001 From: Arnaud Lacurie Date: Tue, 26 May 2026 17:22:59 +0100 Subject: [PATCH 7/7] !current_version --- yaml-tests/src/test/resources/star-expression-metadata.yamsql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yaml-tests/src/test/resources/star-expression-metadata.yamsql b/yaml-tests/src/test/resources/star-expression-metadata.yamsql index 1b07d2067d..1fd22a1b4e 100644 --- a/yaml-tests/src/test/resources/star-expression-metadata.yamsql +++ b/yaml-tests/src/test/resources/star-expression-metadata.yamsql @@ -27,6 +27,9 @@ # SELECT (*) / (T.*) AS X → explicit alias overrides inferred name # SELECT (*) multi-table → column is anonymous ("_0") — source is ambiguous --- +options: + supported_version: !current_version +--- schema_template: create table foo(id bigint, val bigint, primary key(id)) create table bar(bid bigint, bval bigint, primary key(bid))