Skip to content

Commit

Permalink
opt: add EliminateConstValueSubquery normalization rule
Browse files Browse the repository at this point in the history
The EliminateConstValueSubquery normalization rule replaces a subquery
with a constant value when the subquery's input is a single-rows,
single-column Values expression with a constant value. This enables
further optimization of the query.

Fixes #104218

Release note (performance improvement): The optimizer now produces more
efficient query plans in some cases for queries with  subqueries and
user-defined functions.
  • Loading branch information
mgartner committed Jul 19, 2023
1 parent cd0381f commit 2f700fb
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 116 deletions.
2 changes: 1 addition & 1 deletion pkg/sql/logictest/testdata/logic_test/apply_join
Expand Up @@ -467,7 +467,7 @@ VALUES
FROM
(VALUES (tab_54747.col_95055)) AS tab_54752 (col_95061)
WHERE
(SELECT 0) < tab_54752.col_95061
(SELECT random()::INT) < tab_54752.col_95061
)
FROM
(VALUES (0:::OID), (3790322641:::OID)) AS tab_54747 (col_95055)
Expand Down
105 changes: 47 additions & 58 deletions pkg/sql/opt/exec/execbuilder/testdata/subquery
Expand Up @@ -4,7 +4,7 @@
# Uncorrelated subqueries.
# ------------------------------------------------------------------------------
statement ok
CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT);
CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT, FAMILY (a, b, c));
CREATE TABLE abc2 (a INT PRIMARY KEY, b INT, c INT)

query T
Expand All @@ -13,22 +13,12 @@ EXPLAIN ALTER TABLE abc SPLIT AT VALUES ((SELECT 42))
distribution: local
vectorized: true
·
• root
├── • split
│ │ index: abc@abc_pkey
│ │ expiry: CAST(NULL AS STRING)
│ │
│ └── • values
│ size: 1 column, 1 row
• split
│ index: abc@abc_pkey
│ expiry: CAST(NULL AS STRING)
└── • subquery
│ id: @S1
│ original sql: (SELECT 42)
│ exec mode: one row
└── • values
size: 1 column, 1 row
└── • values
size: 1 column, 1 row

statement ok
ALTER TABLE abc SPLIT AT VALUES ((SELECT 1))
Expand All @@ -39,55 +29,27 @@ EXPLAIN ALTER RANGE RELOCATE FROM 11 TO 22 FOR VALUES ((SELECT 1))
distribution: local
vectorized: true
·
• root
├── • relocate range
│ │ replicas: VOTERS
│ │ to: 22
│ │ from: 11
│ │
│ └── • values
│ size: 1 column, 1 row
• relocate range
│ replicas: VOTERS
│ to: 22
│ from: 11
└── • subquery
│ id: @S1
│ original sql: (SELECT 1)
│ exec mode: one row
└── • values
size: 1 column, 1 row
└── • values
size: 1 column, 1 row

query T
EXPLAIN ALTER RANGE 22 RELOCATE FROM ((SELECT 1)) TO ((SELECT 2))
----
distribution: local
vectorized: true
·
• root
├── • relocate range
│ │ replicas: VOTERS
│ │ to: @S1
│ │ from: @S2
│ │
│ └── • values
│ size: 1 column, 1 row
├── • subquery
│ │ id: @S1
│ │ original sql: ((SELECT 2))
│ │ exec mode: one row
│ │
│ └── • values
│ size: 1 column, 1 row
• relocate range
│ replicas: VOTERS
│ to: 2
│ from: 1
└── • subquery
│ id: @S2
│ original sql: ((SELECT 1))
│ exec mode: one row
└── • values
size: 1 column, 1 row
└── • values
size: 1 column, 1 row

query T
EXPLAIN SELECT EXISTS (SELECT a FROM abc)
Expand Down Expand Up @@ -228,7 +190,7 @@ vectorized: true

# the subquery's plan must be visible in EXPLAIN
query T
EXPLAIN VALUES (1), ((SELECT 2))
EXPLAIN VALUES (1), ((SELECT random()::INT))
----
distribution: local
vectorized: true
Expand All @@ -240,7 +202,7 @@ vectorized: true
└── • subquery
│ id: @S1
│ original sql: (SELECT 2)
│ original sql: (SELECT random()::INT8)
│ exec mode: one row
└── • values
Expand Down Expand Up @@ -291,6 +253,33 @@ vectorized: true
table: tab4@tab4_pkey
spans: FULL SCAN

# Subqueries with single, constant values can be inlined for index-acceleration.
query T
EXPLAIN (VERBOSE)
SELECT * FROM abc WHERE a = (SELECT 1)
----
distribution: local
vectorized: true
·
• scan
columns: (a, b, c)
estimated row count: 1 (missing stats)
table: abc@abc_pkey
spans: /1/0

query T
EXPLAIN (VERBOSE)
SELECT * FROM abc WHERE a >= (SELECT 1)
----
distribution: local
vectorized: true
·
• scan
columns: (a, b, c)
estimated row count: 333 (missing stats)
table: abc@abc_pkey
spans: /1-

# ------------------------------------------------------------------------------
# Correlated subqueries.
# ------------------------------------------------------------------------------
Expand Down
49 changes: 47 additions & 2 deletions pkg/sql/opt/exec/execbuilder/testdata/udf
Expand Up @@ -9,7 +9,7 @@ CREATE TABLE t (
INSERT INTO t VALUES (1, 1), (2, 2), (3, 3), (4, 1), (5, 1);

statement ok
CREATE FUNCTION one() RETURNS INT LANGUAGE SQL AS 'SELECT 1';
CREATE FUNCTION one() RETURNS INT IMMUTABLE LANGUAGE SQL AS 'SELECT 1';

query T
EXPLAIN SELECT one()
Expand All @@ -27,7 +27,7 @@ distribution: local
vectorized: true
·
• filter
│ filter: a = one()
│ filter: a = 1
└── • scan
missing stats
Expand Down Expand Up @@ -259,6 +259,51 @@ vectorized: true
└── • emptyrow
columns: ()

# Immutable UDFs can be inlined for index-acceleration.
query T
EXPLAIN (VERBOSE)
SELECT * FROM t WHERE k = one()
----
distribution: local
vectorized: true
·
• scan
columns: (k, a)
estimated row count: 1 (missing stats)
table: t@t_pkey
spans: /1/0

statement ok
CREATE FUNCTION num(n INT) RETURNS INT IMMUTABLE LANGUAGE SQL AS $$
SELECT n
$$

query T
EXPLAIN (VERBOSE)
SELECT * FROM t WHERE k = num(2)
----
distribution: local
vectorized: true
·
• scan
columns: (k, a)
estimated row count: 1 (missing stats)
table: t@t_pkey
spans: /2/0

query T
EXPLAIN (VERBOSE)
SELECT * FROM t WHERE k >= num(2)
----
distribution: local
vectorized: true
·
• scan
columns: (k, a)
estimated row count: 333 (missing stats)
table: t@t_pkey
spans: /2-


subtest regressions

Expand Down
11 changes: 11 additions & 0 deletions pkg/sql/opt/norm/rules/subquery.opt
@@ -0,0 +1,11 @@
# =============================================================================
# subquery.opt contains normalization patterns that try to simplify subqueries.
# =============================================================================

# EliminateConstValuesSubquery replaces a subquery with a constant value if the
# subquery's input is a single-row, single-column Values expression with a
# constant value.
[EliminateConstValueSubquery, Normalize]
(Subquery (Values [ (Tuple [ $value:(Const) ]) ]))
=>
$value
11 changes: 3 additions & 8 deletions pkg/sql/opt/norm/testdata/rules/inline
Expand Up @@ -1251,21 +1251,16 @@ norm expect=InlineUDF
SELECT one_strict() FROM (VALUES (1), (2), (3)) v(i)
----
project
├── columns: one_strict:3
├── columns: one_strict:3!null
├── cardinality: [3 - 3]
├── fd: ()-->(3)
├── values
│ ├── cardinality: [3 - 3]
│ ├── ()
│ ├── ()
│ └── ()
└── projections
└── subquery [as=one_strict:3, subquery]
└── values
├── columns: "?column?":2!null
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(2)
└── (1,)
└── 1 [as=one_strict:3]

# A UDF is not inlined when the arguments are not constants or either Variable
# or Const expressions.
Expand Down
105 changes: 105 additions & 0 deletions pkg/sql/opt/norm/testdata/rules/subquery
@@ -0,0 +1,105 @@
exec-ddl
CREATE TABLE t (
a INT,
b INT
)
----

# --------------------------------------------------
# EliminateConstValueSubquery
# --------------------------------------------------

norm expect=EliminateConstValueSubquery
SELECT (SELECT 1)
----
values
├── columns: "?column?":2!null
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(2)
└── (1,)

norm expect=EliminateConstValueSubquery
SELECT * FROM t WHERE a = (SELECT 1)
----
select
├── columns: a:1!null b:2
├── fd: ()-->(1)
├── scan t
│ └── columns: a:1 b:2
└── filters
└── a:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)]

norm expect=EliminateConstValueSubquery
SELECT * FROM t WHERE a >= (SELECT 1)
----
select
├── columns: a:1!null b:2
├── scan t
│ └── columns: a:1 b:2
└── filters
└── a:1 >= 1 [outer=(1), constraints=(/1: [/1 - ]; tight)]

# Cannot eliminate multi-row values subquery.
norm expect-not=EliminateConstValueSubquery
SELECT (VALUES (1), (2))
----
values
├── columns: column1:2
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(2)
└── tuple
└── subquery
└── max1-row
├── columns: column1:1!null
├── error: "more than one row returned by a subquery used as an expression"
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(1)
└── values
├── columns: column1:1!null
├── cardinality: [2 - 2]
├── (1,)
└── (2,)

# Cannot eliminate multi-column values subquery.
norm expect-not=EliminateConstValueSubquery
SELECT (1, 2) = (SELECT 1, 2)
----
values
├── columns: "?column?":4
├── cardinality: [1 - 1]
├── immutable
├── key: ()
├── fd: ()-->(4)
└── tuple
└── eq
├── (1, 2)
└── subquery
└── values
├── columns: column3:3
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(3)
└── ((1, 2),)

# Cannot eliminate non-constant values subquery.
norm expect-not=EliminateConstValueSubquery
SELECT (SELECT gen_random_uuid())
----
values
├── columns: gen_random_uuid:2
├── cardinality: [1 - 1]
├── volatile
├── key: ()
├── fd: ()-->(2)
└── tuple
└── subquery
└── values
├── columns: gen_random_uuid:1
├── cardinality: [1 - 1]
├── volatile
├── key: ()
├── fd: ()-->(1)
└── (gen_random_uuid(),)
11 changes: 2 additions & 9 deletions pkg/sql/opt/norm/testdata/rules/udf
Expand Up @@ -46,16 +46,9 @@ norm format=show-scalars
SELECT strict_fn(1, 'foo', true)
----
values
├── columns: strict_fn:5
├── columns: strict_fn:5!null
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(5)
└── tuple
└── subquery
└── values
├── columns: i:4!null
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(4)
└── tuple
└── const: 1
└── const: 1

0 comments on commit 2f700fb

Please sign in to comment.