Skip to content

Commit

Permalink
opt: add rule to eliminate Exists when input has zero rows
Browse files Browse the repository at this point in the history
This commit adds a new rule, EliminateExistsZeroRows, which
converts an Exists subquery to False when it's known
that the input produces zero rows.

Informs #47058

Release note (performance improvement): The optimizer can now
detect when an Exists subquery can be eliminated because the input
has zero rows. This leads to better plans in some cases.
  • Loading branch information
rytaft committed Apr 29, 2020
1 parent aa7ff4e commit 67a1d4d
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 17 deletions.
5 changes: 5 additions & 0 deletions pkg/sql/opt/norm/rules/scalar.opt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
$result
)

# EliminateExistsZeroRows converts an Exists subquery to False when it's known
# that the input produces zero rows.
[EliminateExistsZeroRows, Normalize]
(Exists $input:* & (HasZeroRows $input)) => (False)

# EliminateExistsProject discards a Project input to the Exists operator. The
# Project operator never changes the row cardinality of its input, and row
# cardinality is the only thing that Exists cares about, so Project is a no-op.
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/opt/norm/testdata/rules/join
Original file line number Diff line number Diff line change
Expand Up @@ -2165,7 +2165,7 @@ scan a
# SimplifyZeroCardinalitySemiJoin
# --------------------------------------------------
# TODO(justin): figure out if there's a good way to make this still apply.
norm disable=SimplifyZeroCardinalityGroup expect=SimplifyZeroCardinalitySemiJoin
norm disable=(SimplifyZeroCardinalityGroup,EliminateExistsZeroRows) expect=SimplifyZeroCardinalitySemiJoin
SELECT * FROM a WHERE EXISTS(SELECT * FROM (VALUES (k)) OFFSET 1)
----
values
Expand All @@ -2178,7 +2178,7 @@ values
# EliminateAntiJoin
# --------------------------------------------------
# TODO(justin): figure out if there's a good way to make this still apply.
norm disable=SimplifyZeroCardinalityGroup expect=EliminateAntiJoin
norm disable=(SimplifyZeroCardinalityGroup,EliminateExistsZeroRows) expect=EliminateAntiJoin
SELECT * FROM a WHERE NOT EXISTS(SELECT * FROM (VALUES (k)) OFFSET 1)
----
scan a
Expand Down
34 changes: 19 additions & 15 deletions pkg/sql/opt/norm/testdata/rules/scalar
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,20 @@ values
├── fd: ()-->(1)
└── (true IN (NULL, NULL, ('201.249.149.90/18' & '97a7:3650:3dd8:d4e9:35fe:6cfb:a714:1c17/61') << 'e22f:2067:2ed2:7b07:b167:206f:f17b:5b7d/82'),)

# --------------------------------------------------
# EliminateExistsZeroRows
# --------------------------------------------------

norm expect=EliminateExistsZeroRows
SELECT EXISTS(SELECT * FROM (VALUES (1)) WHERE false)
----
values
├── columns: exists:2!null
├── cardinality: [1 - 1]
├── key: ()
├── fd: ()-->(2)
└── (false,)

# --------------------------------------------------
# EliminateExistsProject
# --------------------------------------------------
Expand Down Expand Up @@ -524,21 +538,11 @@ anti-join (hash)
norm expect-not=EliminateExistsLimit
SELECT * FROM a a1 WHERE EXISTS(SELECT i FROM a a2 where a1.i = a2.i LIMIT 0)
----
select
├── columns: k:1!null i:2 f:3 s:4 j:5 arr:6
├── key: (1)
├── fd: (1)-->(2-6)
├── scan a1
│ ├── columns: a1.k:1!null a1.i:2 a1.f:3 a1.s:4 a1.j:5 a1.arr:6
│ ├── key: (1)
│ └── fd: (1)-->(2-6)
└── filters
└── exists [subquery]
└── values
├── columns: a2.i:8!null
├── cardinality: [0 - 0]
├── key: ()
└── fd: ()-->(8)
values
├── columns: k:1!null i:2!null f:3!null s:4!null j:5!null arr:6!null
├── cardinality: [0 - 0]
├── key: ()
└── fd: ()-->(1-6)

# Don't eliminate a limit from a non-correlated subquery.
norm expect-not=EliminateExistsLimit
Expand Down

0 comments on commit 67a1d4d

Please sign in to comment.