From 33babf15fdee0dcd8266d27a61c5d2164e2b30b8 Mon Sep 17 00:00:00 2001 From: Rebecca Taft Date: Mon, 31 May 2021 20:12:34 -0500 Subject: [PATCH] opt: normalize x=True, x=False, x != True, and x != False to x or NOT x This commit adds four normalization rules, FoldEqTrue, FoldEqFalse, FoldNeTrue, and FoldNeFalse, which normalize x=True to x, x=False to NOT x, x != True to NOT x, and x != False to x. These rules are important since they can unlock other types of optimizations, such as constrained index scans. Fixes #65684 Release note (performance improvement): Fixed an issue in the optimizer that prevented spatial predicates of the form `(column && value) = true` from being index-accelerated. These queries can now use a spatial index if one is available. --- .../opt/idxconstraint/testdata/single-column | 8 +- .../opt/memo/testdata/logprops/constraints | 11 +- pkg/sql/opt/memo/testdata/stats/scan | 12 +- pkg/sql/opt/memo/testdata/stats/select | 2 +- pkg/sql/opt/norm/rules/comp.opt | 24 +++ pkg/sql/opt/norm/testdata/rules/comp | 151 ++++++++++++++++++ pkg/sql/opt/norm/testdata/rules/select | 2 +- pkg/sql/opt/xform/testdata/external/liquibase | 2 +- pkg/sql/opt/xform/testdata/external/navicat | 2 +- pkg/sql/opt/xform/testdata/external/nova | 44 ++--- 10 files changed, 216 insertions(+), 42 deletions(-) diff --git a/pkg/sql/opt/idxconstraint/testdata/single-column b/pkg/sql/opt/idxconstraint/testdata/single-column index 7e95892e12e4..8c21da04af37 100644 --- a/pkg/sql/opt/idxconstraint/testdata/single-column +++ b/pkg/sql/opt/idxconstraint/testdata/single-column @@ -153,12 +153,12 @@ NOT a index-constraints vars=(a bool) index=(a) a != true ---- -(/NULL - /false] +[/false - /false] index-constraints vars=(a bool) index=(a) a != false ---- -[/true - ] +[/true - /true] index-constraints vars=(a bool) index=(a) a IS TRUE @@ -195,12 +195,12 @@ a IS DISTINCT FROM 5 index-constraints vars=(a bool) index=(a desc) a != true ---- -[/false - /NULL) +[/false - /false] index-constraints vars=(a bool) index=(a desc) a != false ---- -[ - /true] +[/true - /true] index-constraints vars=(a bool) index=(a desc) a IS TRUE diff --git a/pkg/sql/opt/memo/testdata/logprops/constraints b/pkg/sql/opt/memo/testdata/logprops/constraints index 6b9219830f80..6354cd090444 100644 --- a/pkg/sql/opt/memo/testdata/logprops/constraints +++ b/pkg/sql/opt/memo/testdata/logprops/constraints @@ -545,28 +545,27 @@ SELECT * FROM abc WHERE b != true ---- select ├── columns: a:1(int) b:2(bool!null) c:3(string) + ├── fd: ()-->(2) ├── prune: (1,3) ├── scan abc │ ├── columns: a:1(int) b:2(bool) c:3(string) │ └── prune: (1-3) └── filters - └── ne [type=bool, outer=(2), constraints=(/2: (/NULL - /false]; tight)] - ├── variable: b:2 [type=bool] - └── true [type=bool] + └── not [type=bool, outer=(2), constraints=(/2: [/false - /false]; tight), fd=()-->(2)] + └── variable: b:2 [type=bool] opt SELECT * FROM abc WHERE b != false ---- select ├── columns: a:1(int) b:2(bool!null) c:3(string) + ├── fd: ()-->(2) ├── prune: (1,3) ├── scan abc │ ├── columns: a:1(int) b:2(bool) c:3(string) │ └── prune: (1-3) └── filters - └── ne [type=bool, outer=(2), constraints=(/2: [/true - ]; tight)] - ├── variable: b:2 [type=bool] - └── false [type=bool] + └── variable: b:2 [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] opt SELECT * FROM abc WHERE b IS NOT true diff --git a/pkg/sql/opt/memo/testdata/stats/scan b/pkg/sql/opt/memo/testdata/stats/scan index 509ccab962bb..43266dc33a9f 100644 --- a/pkg/sql/opt/memo/testdata/stats/scan +++ b/pkg/sql/opt/memo/testdata/stats/scan @@ -1128,7 +1128,7 @@ select │ └── fd: ()-->(3), (7)-->(5) └── filters ├── a:1 = '37685f26-4b07-40ba-9bbf-42916ed9bc61' [type=bool, outer=(1), constraints=(/1: [/'37685f26-4b07-40ba-9bbf-42916ed9bc61' - /'37685f26-4b07-40ba-9bbf-42916ed9bc61']; tight), fd=()-->(1)] - ├── b:2 = true [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + ├── b:2 [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] ├── d:4 = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)] └── f:6 > 0.0 [type=bool, outer=(6), constraints=(/6: [/5e-324 - ]; tight)] @@ -1194,7 +1194,7 @@ select │ └── f:6 > 0.0 [type=bool, outer=(6), constraints=(/6: [/5e-324 - ]; tight)] └── filters ├── a:1 = '37685f26-4b07-40ba-9bbf-42916ed9bc61' [type=bool, outer=(1), constraints=(/1: [/'37685f26-4b07-40ba-9bbf-42916ed9bc61' - /'37685f26-4b07-40ba-9bbf-42916ed9bc61']; tight), fd=()-->(1)] - ├── b:2 = true [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + ├── b:2 [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] └── c:3 = 5 [type=bool, outer=(3), constraints=(/3: [/5 - /5]; tight), fd=()-->(3)] # A different combination of predicates. @@ -1225,7 +1225,7 @@ select │ ├── key: (7) │ └── fd: ()-->(3), (7)-->(5) └── filters - ├── b:2 = true [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + ├── b:2 [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] └── f:6 > 0.0 [type=bool, outer=(6), constraints=(/6: [/5e-324 - ]; tight)] # Force the alternate index. @@ -1514,7 +1514,7 @@ select │ ├── key: (7) │ └── fd: ()-->(3), (7)-->(5) └── filters - ├── b:2 = true [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + ├── b:2 [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] └── f:6 > 0.0 [type=bool, outer=(6), constraints=(/6: [/5e-324 - ]; tight)] # Force the alternate index. @@ -1855,7 +1855,7 @@ select │ ├── key: (7) │ └── fd: ()-->(3), (7)-->(5) └── filters - ├── b:2 = true [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + ├── b:2 [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] └── f:6 > 0.0 [type=bool, outer=(6), constraints=(/6: [/5e-324 - ]; tight)] # Force the alternate index. @@ -1959,7 +1959,7 @@ select │ └── fd: ()-->(4-6) └── filters ├── a:1 = '37685f26-4b07-40ba-9bbf-42916ed9bc61' [type=bool, outer=(1), constraints=(/1: [/'37685f26-4b07-40ba-9bbf-42916ed9bc61' - /'37685f26-4b07-40ba-9bbf-42916ed9bc61']; tight), fd=()-->(1)] - └── b:2 = true [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + └── b:2 [type=bool, outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] opt SELECT * FROM multi_col diff --git a/pkg/sql/opt/memo/testdata/stats/select b/pkg/sql/opt/memo/testdata/stats/select index 4b98c9a6a9ad..62e694fee5b8 100644 --- a/pkg/sql/opt/memo/testdata/stats/select +++ b/pkg/sql/opt/memo/testdata/stats/select @@ -1730,7 +1730,7 @@ select │ histogram(2)= 0 900 0 100 │ <--- false --- true └── filters - └── b:2 = false [type=bool, outer=(2), constraints=(/2: [/false - /false]; tight), fd=()-->(2)] + └── NOT b:2 [type=bool, outer=(2), constraints=(/2: [/false - /false]; tight), fd=()-->(2)] exec-ddl CREATE TABLE t0(c0 INT) diff --git a/pkg/sql/opt/norm/rules/comp.opt b/pkg/sql/opt/norm/rules/comp.opt index 053050fe58a2..7567658efa9f 100644 --- a/pkg/sql/opt/norm/rules/comp.opt +++ b/pkg/sql/opt/norm/rules/comp.opt @@ -295,3 +295,27 @@ ) => (MakeSTDFullyWithinRight (OpName) $args $left) + +# FoldEqTrue replaces x = True with x. +[FoldEqTrue, Normalize] +(Eq $left:* (True)) +=> +$left + +# FoldEqFalse replaces x = False with NOT x. +[FoldEqFalse, Normalize] +(Eq $left:* (False)) +=> +(Not $left) + +# FoldNeTrue replaces x != True with NOT x. +[FoldNeTrue, Normalize] +(Ne $left:* (True)) +=> +(Not $left) + +# FoldNeFalse replaces x != False with x. +[FoldNeFalse, Normalize] +(Ne $left:* (False)) +=> +$left diff --git a/pkg/sql/opt/norm/testdata/rules/comp b/pkg/sql/opt/norm/testdata/rules/comp index 46ec2ab4db0d..4db9715d2a3b 100644 --- a/pkg/sql/opt/norm/testdata/rules/comp +++ b/pkg/sql/opt/norm/testdata/rules/comp @@ -1114,3 +1114,154 @@ select │ └── columns: geom:1 geog:2 val:3 └── filters └── st_dfullywithinexclusive(geom:1, '010100000000000000000000000000000000000000', val:3) [outer=(1,3), immutable, constraints=(/1: (/NULL - ]; /3: (/NULL - ])] + +# -------------------------------------------------- +# FoldEqTrue + FoldEqFalse +# -------------------------------------------------- + +exec-ddl +CREATE TABLE tbl (k INT PRIMARY KEY, b BOOL) +---- + +norm expect=FoldEqTrue +SELECT * FROM tbl WHERE b=TRUE +---- +select + ├── columns: k:1!null b:2!null + ├── key: (1) + ├── fd: ()-->(2) + ├── scan tbl + │ ├── columns: k:1!null b:2 + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── filters + └── b:2 [outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + +norm expect=FoldEqTrue +SELECT b=TRUE FROM tbl +---- +project + ├── columns: "?column?":4 + ├── scan tbl + │ └── columns: b:2 + └── projections + └── b:2 [as="?column?":4, outer=(2)] + +norm expect=FoldEqFalse +SELECT * FROM tbl WHERE b=FALSE +---- +select + ├── columns: k:1!null b:2!null + ├── key: (1) + ├── fd: ()-->(2) + ├── scan tbl + │ ├── columns: k:1!null b:2 + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── filters + └── NOT b:2 [outer=(2), constraints=(/2: [/false - /false]; tight), fd=()-->(2)] + +norm expect=FoldEqFalse +SELECT b=FALSE FROM tbl +---- +project + ├── columns: "?column?":4 + ├── scan tbl + │ └── columns: b:2 + └── projections + └── NOT b:2 [as="?column?":4, outer=(2)] + +exec-ddl +CREATE INVERTED INDEX ON geom_geog(geom) +---- + +# Regression test for #65684. +# We use opt here to show that the inverted index is used. +opt expect=FoldEqTrue +SELECT count(*) FROM geom_geog WHERE (geom && st_geomfromewkt('SRID=4326;POLYGON((0 0,0 100,100 100,100 0,0 0))'))=TRUE; +---- +scalar-group-by + ├── columns: count:7!null + ├── cardinality: [1 - 1] + ├── immutable + ├── key: () + ├── fd: ()-->(7) + ├── select + │ ├── columns: geom:1!null + │ ├── immutable + │ ├── index-join geom_geog + │ │ ├── columns: geom:1 + │ │ └── inverted-filter + │ │ ├── columns: rowid:4!null + │ │ ├── inverted expression: /6 + │ │ │ ├── tight: false, unique: false + │ │ │ └── union spans + │ │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ ├── pre-filterer expression + │ │ │ └── st_intersects('0103000020E610000001000000050000000000000000000000000000000000000000000000000000000000000000005940000000000000594000000000000059400000000000005940000000000000000000000000000000000000000000000000', geom:1) + │ │ ├── key: (4) + │ │ └── scan geom_geog@secondary + │ │ ├── columns: rowid:4!null geom_inverted_key:6!null + │ │ ├── inverted constraint: /6/4 + │ │ │ └── spans + │ │ │ ├── ["B\x89", "B\xfd \x00\x00\x00\x00\x00\x00\x00") + │ │ │ └── ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] + │ │ ├── key: (4) + │ │ └── fd: (4)-->(6) + │ └── filters + │ └── geom:1 && '0103000020E610000001000000050000000000000000000000000000000000000000000000000000000000000000005940000000000000594000000000000059400000000000005940000000000000000000000000000000000000000000000000' [outer=(1), immutable, constraints=(/1: (/NULL - ])] + └── aggregations + └── count-rows [as=count_rows:7] + +# -------------------------------------------------- +# FoldNeTrue + FoldNeFalse +# -------------------------------------------------- + +norm expect=FoldNeTrue +SELECT * FROM tbl WHERE b != TRUE +---- +select + ├── columns: k:1!null b:2!null + ├── key: (1) + ├── fd: ()-->(2) + ├── scan tbl + │ ├── columns: k:1!null b:2 + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── filters + └── NOT b:2 [outer=(2), constraints=(/2: [/false - /false]; tight), fd=()-->(2)] + +norm expect=FoldNeTrue +SELECT b != TRUE FROM tbl +---- +project + ├── columns: "?column?":4 + ├── scan tbl + │ └── columns: b:2 + └── projections + └── NOT b:2 [as="?column?":4, outer=(2)] + +norm expect=FoldNeFalse +SELECT * FROM tbl WHERE b != FALSE +---- +select + ├── columns: k:1!null b:2!null + ├── key: (1) + ├── fd: ()-->(2) + ├── scan tbl + │ ├── columns: k:1!null b:2 + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── filters + └── b:2 [outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] + +norm expect=FoldNeFalse +SELECT b != FALSE FROM tbl +---- +project + ├── columns: "?column?":4 + ├── scan tbl + │ └── columns: b:2 + └── projections + └── b:2 [as="?column?":4, outer=(2)] diff --git a/pkg/sql/opt/norm/testdata/rules/select b/pkg/sql/opt/norm/testdata/rules/select index 484589d67c6f..eb76646e7d0f 100644 --- a/pkg/sql/opt/norm/testdata/rules/select +++ b/pkg/sql/opt/norm/testdata/rules/select @@ -215,7 +215,7 @@ select ├── scan c │ └── columns: a:1 b:2 c:3 d:4 e:5 └── filters - ├── a:1 AND (a:1 = true) [outer=(1), constraints=(/1: [/true - /true]; tight), fd=()-->(1)] + ├── a:1 [outer=(1), constraints=(/1: [/true - /true]; tight), fd=()-->(1)] ├── b:2 [outer=(2), constraints=(/2: [/true - /true]; tight), fd=()-->(2)] └── b:2 = c:3 [outer=(2,3), constraints=(/2: (/NULL - ]; /3: (/NULL - ]), fd=(2)==(3), (3)==(2)] diff --git a/pkg/sql/opt/xform/testdata/external/liquibase b/pkg/sql/opt/xform/testdata/external/liquibase index 039fbebde068..9d4889faee76 100644 --- a/pkg/sql/opt/xform/testdata/external/liquibase +++ b/pkg/sql/opt/xform/testdata/external/liquibase @@ -217,7 +217,7 @@ project │ │ │ │ │ │ │ │ ├── key: (78) │ │ │ │ │ │ │ │ └── fd: (78)-->(79,85) │ │ │ │ │ │ │ └── filters - │ │ │ │ │ │ │ └── indisclustered:85 = true [outer=(85), constraints=(/85: [/true - /true]; tight), fd=()-->(85)] + │ │ │ │ │ │ │ └── indisclustered:85 [outer=(85), constraints=(/85: [/true - /true]; tight), fd=()-->(85)] │ │ │ │ │ │ ├── left-join (lookup pg_tablespace [as=t]) │ │ │ │ │ │ │ ├── columns: c.oid:1!null c.relname:2!null c.relnamespace:3!null c.relowner:5!null c.reltablespace:8!null c.reltuples:10!null c.relhasindex:13!null c.relpersistence:15!null c.relkind:17!null c.relhasoids:20!null c.relhasrules:22!null c.relhastriggers:23!null c.relacl:26 c.reloptions:27 n.oid:29!null n.nspname:30!null t.oid:34 spcname:35 ftrelid:126 ftserver:127 ftoptions:128 fs.oid:130 srvname:131 │ │ │ │ │ │ │ ├── key columns: [8] = [34] diff --git a/pkg/sql/opt/xform/testdata/external/navicat b/pkg/sql/opt/xform/testdata/external/navicat index c3a6bb8176a9..7d64ff5a2b08 100644 --- a/pkg/sql/opt/xform/testdata/external/navicat +++ b/pkg/sql/opt/xform/testdata/external/navicat @@ -221,7 +221,7 @@ sort │ │ │ │ │ │ │ │ ├── key: (78) │ │ │ │ │ │ │ │ └── fd: (78)-->(79,85) │ │ │ │ │ │ │ └── filters - │ │ │ │ │ │ │ └── indisclustered:85 = true [outer=(85), constraints=(/85: [/true - /true]; tight), fd=()-->(85)] + │ │ │ │ │ │ │ └── indisclustered:85 [outer=(85), constraints=(/85: [/true - /true]; tight), fd=()-->(85)] │ │ │ │ │ │ ├── left-join (lookup pg_tablespace [as=t]) │ │ │ │ │ │ │ ├── columns: c.oid:1!null c.relname:2!null c.relnamespace:3!null c.relowner:5!null c.reltablespace:8!null c.reltuples:10!null c.relhasindex:13!null c.relpersistence:15!null c.relkind:17!null c.relhasoids:20!null c.relhasrules:22!null c.relhastriggers:23!null c.relacl:26 c.reloptions:27 n.oid:29!null n.nspname:30!null t.oid:34 spcname:35 ftrelid:126 ftserver:127 ftoptions:128 fs.oid:130 srvname:131 │ │ │ │ │ │ │ ├── key columns: [8] = [34] diff --git a/pkg/sql/opt/xform/testdata/external/nova b/pkg/sql/opt/xform/testdata/external/nova index fa7823db761b..5ee61f86f756 100644 --- a/pkg/sql/opt/xform/testdata/external/nova +++ b/pkg/sql/opt/xform/testdata/external/nova @@ -260,7 +260,7 @@ project │ │ │ │ │ └── const-agg [as=flavors.id:1, outer=(1)] │ │ │ │ │ └── flavors.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:25 IS NOT NULL) [outer=(12,25)] + │ │ │ │ └── is_public:12 OR (true_agg:25 IS NOT NULL) [outer=(12,25)] │ │ │ └── $3 │ │ └── $4 │ └── filters (true) @@ -412,7 +412,7 @@ sort │ │ │ │ │ │ │ │ │ │ ├── fd: (1)-->(2-12,14,15), (7)-->(1-6,8-12,14,15), (2)-->(1,3-12,14,15) │ │ │ │ │ │ │ │ │ │ └── ordering: +1 │ │ │ │ │ │ │ │ │ └── filters - │ │ │ │ │ │ │ │ │ └── disabled:11 = false [outer=(11), constraints=(/11: [/false - /false]; tight), fd=()-->(11)] + │ │ │ │ │ │ │ │ │ └── NOT disabled:11 [outer=(11), constraints=(/11: [/false - /false]; tight), fd=()-->(11)] │ │ │ │ │ │ │ │ ├── project │ │ │ │ │ │ │ │ │ ├── columns: true:31!null flavor_projects.flavor_id:18!null │ │ │ │ │ │ │ │ │ ├── has-placeholder @@ -464,7 +464,7 @@ sort │ │ │ │ │ │ │ └── const-agg [as=flavors.updated_at:15, outer=(15)] │ │ │ │ │ │ │ └── flavors.updated_at:15 │ │ │ │ │ │ └── filters - │ │ │ │ │ │ └── (is_public:12 = true) OR (true_agg:32 IS NOT NULL) [outer=(12,32)] + │ │ │ │ │ │ └── is_public:12 OR (true_agg:32 IS NOT NULL) [outer=(12,32)] │ │ │ │ │ ├── project │ │ │ │ │ │ ├── columns: true:34!null flavor_projects.flavor_id:25!null │ │ │ │ │ │ ├── has-placeholder @@ -516,7 +516,7 @@ sort │ │ │ │ └── const-agg [as=flavors.updated_at:15, outer=(15)] │ │ │ │ └── flavors.updated_at:15 │ │ │ └── filters - │ │ │ └── (is_public:12 = true) OR (true_agg:35 IS NOT NULL) [outer=(12,35)] + │ │ │ └── is_public:12 OR (true_agg:35 IS NOT NULL) [outer=(12,35)] │ │ └── $3 │ └── $4 └── filters @@ -708,7 +708,7 @@ sort │ │ │ │ └── const-agg [as=instance_types.updated_at:16, outer=(16)] │ │ │ │ └── instance_types.updated_at:16 │ │ │ └── filters - │ │ │ └── (is_public:12 = true) OR (true_agg:28 IS NOT NULL) [outer=(12,28)] + │ │ │ └── is_public:12 OR (true_agg:28 IS NOT NULL) [outer=(12,28)] │ │ └── $5 │ └── $6 ├── select @@ -869,7 +869,7 @@ sort │ │ └── const-agg [as=instance_types.updated_at:16, outer=(16)] │ │ └── instance_types.updated_at:16 │ └── filters - │ └── (is_public:12 = true) OR (true_agg:37 IS NOT NULL) [outer=(12,37)] + │ └── is_public:12 OR (true_agg:37 IS NOT NULL) [outer=(12,37)] ├── select │ ├── columns: instance_type_extra_specs_1.id:18!null key:19 value:20 instance_type_extra_specs_1.instance_type_id:21!null instance_type_extra_specs_1.deleted:22!null instance_type_extra_specs_1.deleted_at:23 instance_type_extra_specs_1.created_at:24 instance_type_extra_specs_1.updated_at:25 │ ├── has-placeholder @@ -1057,7 +1057,7 @@ project │ │ │ │ │ └── const-agg [as=instance_types.id:1, outer=(1)] │ │ │ │ │ └── instance_types.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:28 IS NOT NULL) [outer=(12,28)] + │ │ │ │ └── is_public:12 OR (true_agg:28 IS NOT NULL) [outer=(12,28)] │ │ │ └── $5 │ │ └── $6 │ └── filters @@ -1231,7 +1231,7 @@ project │ │ │ │ │ └── const-agg [as=instance_types.id:1, outer=(1)] │ │ │ │ │ └── instance_types.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:28 IS NOT NULL) [outer=(12,28)] + │ │ │ │ └── is_public:12 OR (true_agg:28 IS NOT NULL) [outer=(12,28)] │ │ │ └── $5 │ │ └── $6 │ └── filters @@ -1389,7 +1389,7 @@ project │ │ │ │ │ └── const-agg [as=flavors.id:1, outer=(1)] │ │ │ │ │ └── flavors.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:25 IS NOT NULL) [outer=(12,25)] + │ │ │ │ └── is_public:12 OR (true_agg:25 IS NOT NULL) [outer=(12,25)] │ │ │ └── $3 │ │ └── $4 │ └── filters (true) @@ -1546,7 +1546,7 @@ project │ │ │ │ │ └── const-agg [as=flavors.id:1, outer=(1)] │ │ │ │ │ └── flavors.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:25 IS NOT NULL) [outer=(12,25)] + │ │ │ │ └── is_public:12 OR (true_agg:25 IS NOT NULL) [outer=(12,25)] │ │ │ └── $3 │ │ └── $4 │ └── filters (true) @@ -1727,7 +1727,7 @@ sort │ │ │ │ └── const-agg [as=flavors.updated_at:15, outer=(15)] │ │ │ │ └── flavors.updated_at:15 │ │ │ └── filters - │ │ │ └── (is_public:12 = true) OR (true_agg:25 IS NOT NULL) [outer=(12,25)] + │ │ │ └── is_public:12 OR (true_agg:25 IS NOT NULL) [outer=(12,25)] │ │ └── $5 │ └── $6 └── filters @@ -1917,7 +1917,7 @@ sort │ │ │ │ └── const-agg [as=instance_types.updated_at:16, outer=(16)] │ │ │ │ └── instance_types.updated_at:16 │ │ │ └── filters - │ │ │ └── (is_public:12 = true) OR (true_agg:28 IS NOT NULL) [outer=(12,28)] + │ │ │ └── is_public:12 OR (true_agg:28 IS NOT NULL) [outer=(12,28)] │ │ └── $4 │ └── $5 ├── select @@ -2107,7 +2107,7 @@ project │ │ │ │ │ └── const-agg [as=instance_types.id:1, outer=(1)] │ │ │ │ │ └── instance_types.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:28 IS NOT NULL) [outer=(12,28)] + │ │ │ │ └── is_public:12 OR (true_agg:28 IS NOT NULL) [outer=(12,28)] │ │ │ └── $5 │ │ └── $6 │ └── filters @@ -2238,7 +2238,7 @@ sort │ │ └── const-agg [as=flavors.updated_at:15, outer=(15)] │ │ └── flavors.updated_at:15 │ └── filters - │ └── (is_public:12 = true) OR (true_agg:32 IS NOT NULL) [outer=(12,32)] + │ └── is_public:12 OR (true_agg:32 IS NOT NULL) [outer=(12,32)] ├── scan flavor_extra_specs [as=flavor_extra_specs_1] │ ├── columns: flavor_extra_specs_1.id:17!null key:18!null value:19 flavor_extra_specs_1.flavor_id:20!null flavor_extra_specs_1.created_at:21 flavor_extra_specs_1.updated_at:22 │ ├── key: (17) @@ -2444,7 +2444,7 @@ sort │ │ │ │ └── const-agg [as=instance_types.updated_at:16, outer=(16)] │ │ │ │ └── instance_types.updated_at:16 │ │ │ └── filters - │ │ │ └── (is_public:12 = true) OR (true_agg:28 IS NOT NULL) [outer=(12,28)] + │ │ │ └── is_public:12 OR (true_agg:28 IS NOT NULL) [outer=(12,28)] │ │ └── $7 │ └── $8 └── filters @@ -2611,7 +2611,7 @@ sort │ │ │ │ └── const-agg [as=flavors.updated_at:15, outer=(15)] │ │ │ │ └── flavors.updated_at:15 │ │ │ └── filters - │ │ │ └── (is_public:12 = true) OR (true_agg:25 IS NOT NULL) [outer=(12,25)] + │ │ │ └── is_public:12 OR (true_agg:25 IS NOT NULL) [outer=(12,25)] │ │ └── $2 │ └── $3 ├── scan flavor_extra_specs [as=flavor_extra_specs_1] @@ -2788,7 +2788,7 @@ sort │ │ │ │ │ │ │ │ │ │ └── ordering: +1 │ │ │ │ │ │ │ │ │ └── filters │ │ │ │ │ │ │ │ │ ├── instance_types.deleted:13 = $1 [outer=(13), constraints=(/13: (/NULL - ]), fd=()-->(13)] - │ │ │ │ │ │ │ │ │ └── disabled:11 = false [outer=(11), constraints=(/11: [/false - /false]; tight), fd=()-->(11)] + │ │ │ │ │ │ │ │ │ └── NOT disabled:11 [outer=(11), constraints=(/11: [/false - /false]; tight), fd=()-->(11)] │ │ │ │ │ │ │ │ ├── project │ │ │ │ │ │ │ │ │ ├── columns: true:36!null instance_type_projects.instance_type_id:19!null │ │ │ │ │ │ │ │ │ ├── has-placeholder @@ -2845,7 +2845,7 @@ sort │ │ │ │ │ │ │ └── const-agg [as=instance_types.updated_at:16, outer=(16)] │ │ │ │ │ │ │ └── instance_types.updated_at:16 │ │ │ │ │ │ └── filters - │ │ │ │ │ │ └── (is_public:12 = true) OR (true_agg:37 IS NOT NULL) [outer=(12,37)] + │ │ │ │ │ │ └── is_public:12 OR (true_agg:37 IS NOT NULL) [outer=(12,37)] │ │ │ │ │ ├── project │ │ │ │ │ │ ├── columns: true:39!null instance_type_projects.instance_type_id:28!null │ │ │ │ │ │ ├── has-placeholder @@ -2903,7 +2903,7 @@ sort │ │ │ │ └── const-agg [as=instance_types.updated_at:16, outer=(16)] │ │ │ │ └── instance_types.updated_at:16 │ │ │ └── filters - │ │ │ └── (is_public:12 = true) OR (true_agg:40 IS NOT NULL) [outer=(12,40)] + │ │ │ └── is_public:12 OR (true_agg:40 IS NOT NULL) [outer=(12,40)] │ │ └── $7 │ └── $8 └── filters @@ -3085,7 +3085,7 @@ project │ │ │ │ │ └── const-agg [as=instance_types.id:1, outer=(1)] │ │ │ │ │ └── instance_types.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:28 IS NOT NULL) [outer=(12,28)] + │ │ │ │ └── is_public:12 OR (true_agg:28 IS NOT NULL) [outer=(12,28)] │ │ │ └── $5 │ │ └── $6 │ └── filters @@ -3244,7 +3244,7 @@ project │ │ │ │ │ └── const-agg [as=flavors.id:1, outer=(1)] │ │ │ │ │ └── flavors.id:1 │ │ │ │ └── filters - │ │ │ │ └── (is_public:12 = true) OR (true_agg:25 IS NOT NULL) [outer=(12,25)] + │ │ │ │ └── is_public:12 OR (true_agg:25 IS NOT NULL) [outer=(12,25)] │ │ │ └── $3 │ │ └── $4 │ └── filters (true) @@ -3407,7 +3407,7 @@ sort │ │ │ └── const-agg [as=flavors.updated_at:15, outer=(15)] │ │ │ └── flavors.updated_at:15 │ │ └── filters - │ │ └── (is_public:12 = true) OR (true_agg:25 IS NOT NULL) [outer=(12,25)] + │ │ └── is_public:12 OR (true_agg:25 IS NOT NULL) [outer=(12,25)] │ └── $2 ├── scan flavor_extra_specs [as=flavor_extra_specs_1] │ ├── columns: flavor_extra_specs_1.id:27!null key:28!null value:29 flavor_extra_specs_1.flavor_id:30!null flavor_extra_specs_1.created_at:31 flavor_extra_specs_1.updated_at:32