diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain b/pkg/sql/opt/exec/execbuilder/testdata/explain index 22c3964d350a..cbb616712c98 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain @@ -454,7 +454,7 @@ vectorized: true │ order: +grantee,+privilege_type │ └── • filter - │ filter: (table_catalog, table_schema, table_name) IN (('test', 'public', 'foo'),) + │ filter: ((table_catalog = 'test') AND (table_schema = 'public')) AND (table_name = 'foo') │ └── • virtual table table: table_privileges@primary diff --git a/pkg/sql/opt/exec/execbuilder/testdata/scalar b/pkg/sql/opt/exec/execbuilder/testdata/scalar index dcadf6730c50..f98bb91723eb 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/scalar +++ b/pkg/sql/opt/exec/execbuilder/testdata/scalar @@ -1201,7 +1201,7 @@ vectorized: true • filter │ columns: (c0) │ estimated row count: 333 (missing stats) -│ filter: CASE WHEN c0 IN (c0,) THEN ARRAY[NULL] ELSE ARRAY[] END IS NULL +│ filter: CASE WHEN (c0 IS DISTINCT FROM CAST(NULL AS DECIMAL)) OR CAST(NULL AS BOOL) THEN ARRAY[NULL] ELSE ARRAY[] END IS NULL │ └── • scan columns: (c0) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/select_index b/pkg/sql/opt/exec/execbuilder/testdata/select_index index b05bdd938251..f60976202e18 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/select_index +++ b/pkg/sql/opt/exec/execbuilder/testdata/select_index @@ -823,7 +823,7 @@ vectorized: true └── • filter │ columns: (k, a, b) │ estimated row count: 333 (missing stats) - │ filter: ((a IN (6,)) AND (a > 6)) OR (b >= 4) + │ filter: ((a = 6) AND (a > 6)) OR (b >= 4) │ └── • scan columns: (k, a, b) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/virtual b/pkg/sql/opt/exec/execbuilder/testdata/virtual index 10256c5834f4..c88e6e12aa3e 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/virtual +++ b/pkg/sql/opt/exec/execbuilder/testdata/virtual @@ -139,7 +139,7 @@ vectorized: true │ equality: (oid) = (connamespace) │ ├── • filter - │ │ filter: nspname IN ('public',) + │ │ filter: nspname = 'public' │ │ │ └── • virtual table │ table: pg_namespace@primary diff --git a/pkg/sql/opt/memo/testdata/stats/scan b/pkg/sql/opt/memo/testdata/stats/scan index be10bfbd7b9e..509ccab962bb 100644 --- a/pkg/sql/opt/memo/testdata/stats/scan +++ b/pkg/sql/opt/memo/testdata/stats/scan @@ -537,18 +537,20 @@ project │ ├── project │ │ ├── columns: col1:27(bool) │ │ ├── immutable - │ │ ├── stats: [rows=333333.333, distinct(27)=333333.333, null(27)=0] - │ │ ├── inner-join (cross) + │ │ ├── stats: [rows=10000, distinct(27)=10000, null(27)=0] + │ │ ├── inner-join (hash) │ │ │ ├── columns: tab0.e:5(varchar) tab0.f:6("char") tab0.h:8(varchar) tab0.j:10(float!null) tab1.e:17(varchar) tab1.f:18("char") tab1.j:22(float!null) - │ │ │ ├── stats: [rows=333333.333, distinct(5,6,8,17,18)=333333.333, null(5,6,8,17,18)=3.33333333e-05] + │ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(one-or-more) + │ │ │ ├── stats: [rows=10000, distinct(10)=100, null(10)=0, distinct(22)=100, null(22)=0, distinct(5,6,8,17,18)=10000, null(5,6,8,17,18)=1e-06] + │ │ │ ├── fd: (10)==(22), (22)==(10) │ │ │ ├── scan t37953 [as=tab0] │ │ │ │ ├── columns: tab0.e:5(varchar) tab0.f:6("char") tab0.h:8(varchar) tab0.j:10(float!null) - │ │ │ │ └── stats: [rows=1000, distinct(5,6,8)=1000, null(5,6,8)=0.001] + │ │ │ │ └── stats: [rows=1000, distinct(10)=100, null(10)=0, distinct(5,6,8)=1000, null(5,6,8)=0.001] │ │ │ ├── scan t37953 [as=tab1] │ │ │ │ ├── columns: tab1.e:17(varchar) tab1.f:18("char") tab1.j:22(float!null) - │ │ │ │ └── stats: [rows=1000, distinct(17,18)=1000, null(17,18)=0.1] + │ │ │ │ └── stats: [rows=1000, distinct(22)=100, null(22)=0, distinct(17,18)=1000, null(17,18)=0.1] │ │ │ └── filters - │ │ │ └── tab0.j:10 IN (tab1.j:22,) [type=bool, outer=(10,22)] + │ │ │ └── tab0.j:10 = tab1.j:22 [type=bool, outer=(10,22), constraints=(/10: (/NULL - ]; /22: (/NULL - ]), fd=(10)==(22), (22)==(10)] │ │ └── projections │ │ └── CASE WHEN ilike_escape(regexp_replace(tab0.h:8, tab1.e:17, tab0.f:6, tab0.e:5::STRING), tab1.f:18, '') THEN true ELSE false END [as=col1:27, type=bool, outer=(5,6,8,17,18), immutable] │ └── filters diff --git a/pkg/sql/opt/norm/rules/scalar.opt b/pkg/sql/opt/norm/rules/scalar.opt index e72005f6e873..71b012b776fd 100644 --- a/pkg/sql/opt/norm/rules/scalar.opt +++ b/pkg/sql/opt/norm/rules/scalar.opt @@ -86,6 +86,16 @@ $input => (Null (BoolType)) +[SimplifyInSingleElement, Normalize] +(In $left:* (Tuple [ $right:* ])) +=> +(Eq $left $right) + +[SimplifyNotInSingleElement, Normalize] +(NotIn $left:* (Tuple [ $right:* ])) +=> +(Ne $left $right) + # UnifyComparisonTypes takes a mixed-type comparison between a non-constant and # a constant and, if appropriate, converts the constant to the type of the # non-constant to allow constraints to be generated. diff --git a/pkg/sql/opt/norm/testdata/rules/assign_placeholders b/pkg/sql/opt/norm/testdata/rules/assign_placeholders index 8d27ff305c64..fa57bef072f3 100644 --- a/pkg/sql/opt/norm/testdata/rules/assign_placeholders +++ b/pkg/sql/opt/norm/testdata/rules/assign_placeholders @@ -58,7 +58,8 @@ select │ ├── key: (1-3) │ └── fd: (1-3)-->(4) └── filters - └── (a:1, b:2) IN ((1, 2),) [outer=(1,2), constraints=(/1/2: [/1/2 - /1/2]; /2: [/2 - /2]; tight), fd=()-->(1,2)] + ├── a:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)] + └── b:2 = 2 [outer=(2), constraints=(/2: [/2 - /2]; tight), fd=()-->(2)] # The normalized expression above can be explored into a constrained scan. opt diff --git a/pkg/sql/opt/norm/testdata/rules/scalar b/pkg/sql/opt/norm/testdata/rules/scalar index 297087215077..bf95270fcc81 100644 --- a/pkg/sql/opt/norm/testdata/rules/scalar +++ b/pkg/sql/opt/norm/testdata/rules/scalar @@ -239,7 +239,7 @@ project ├── scan a │ └── columns: s:4 └── projections - └── s:4 NOT IN ('foo',) [as=r:7, outer=(4)] + └── s:4 != 'foo' [as=r:7, outer=(4)] # Don't sort, since the list is not constant. norm expect-not=NormalizeInConst @@ -275,6 +275,111 @@ 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'),) +# -------------------------------------------------- +# SimplifyInSingleElement +# -------------------------------------------------- +norm expect=SimplifyInSingleElement +SELECT * FROM a WHERE k IN (1) +---- +select + ├── columns: k:1!null i:2 f:3 s:4 arr:5 + ├── cardinality: [0 - 1] + ├── key: () + ├── fd: ()-->(1-5) + ├── scan a + │ ├── columns: k:1!null i:2 f:3 s:4 arr:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── k:1 = 1 [outer=(1), constraints=(/1: [/1 - /1]; tight), fd=()-->(1)] + +norm expect=SimplifyInSingleElement +SELECT 1 IN (k) FROM a +---- +project + ├── columns: "?column?":7!null + ├── scan a + │ ├── columns: k:1!null + │ └── key: (1) + └── projections + └── k:1 = 1 [as="?column?":7, outer=(1)] + +norm expect=SimplifyInSingleElement +SELECT k+1 IN (i*2) FROM a +---- +project + ├── columns: "?column?":7 + ├── immutable + ├── scan a + │ ├── columns: k:1!null i:2 + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── projections + └── (k:1 + 1) = (i:2 * 2) [as="?column?":7, outer=(1,2), immutable] + +norm expect-not=SimplifyInSingleElement +SELECT k IN (1,2) FROM a +---- +project + ├── columns: "?column?":7!null + ├── scan a + │ ├── columns: k:1!null + │ └── key: (1) + └── projections + └── k:1 IN (1, 2) [as="?column?":7, outer=(1)] + +# -------------------------------------------------- +# SimplifyNotInSingleElement +# -------------------------------------------------- +norm expect=SimplifyNotInSingleElement +SELECT * FROM a WHERE k NOT IN (1) +---- +select + ├── columns: k:1!null i:2 f:3 s:4 arr:5 + ├── key: (1) + ├── fd: (1)-->(2-5) + ├── scan a + │ ├── columns: k:1!null i:2 f:3 s:4 arr:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── k:1 != 1 [outer=(1), constraints=(/1: (/NULL - /0] [/2 - ]; tight)] + +norm expect=SimplifyNotInSingleElement +SELECT 1 NOT IN (k) FROM a +---- +project + ├── columns: "?column?":7!null + ├── scan a + │ ├── columns: k:1!null + │ └── key: (1) + └── projections + └── k:1 != 1 [as="?column?":7, outer=(1)] + +norm expect=SimplifyNotInSingleElement +SELECT k+1 NOT IN (i*2) FROM a +---- +project + ├── columns: "?column?":7 + ├── immutable + ├── scan a + │ ├── columns: k:1!null i:2 + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── projections + └── (k:1 + 1) != (i:2 * 2) [as="?column?":7, outer=(1,2), immutable] + +norm expect-not=SimplifyNotInSingleElement +SELECT k NOT IN (1,2) FROM a +---- +project + ├── columns: "?column?":7!null + ├── scan a + │ ├── columns: k:1!null + │ └── key: (1) + └── projections + └── k:1 NOT IN (1, 2) [as="?column?":7, outer=(1)] + # -------------------------------------------------- # EliminateExistsZeroRows # -------------------------------------------------- diff --git a/pkg/sql/opt/partialidx/testdata/implicator/or-expr b/pkg/sql/opt/partialidx/testdata/implicator/or-expr index c27595e8075f..028f3155f115 100644 --- a/pkg/sql/opt/partialidx/testdata/implicator/or-expr +++ b/pkg/sql/opt/partialidx/testdata/implicator/or-expr @@ -55,7 +55,7 @@ a IN (1) a = 1 OR a = 2 ---- true -└── remaining filters: a IN (1,) +└── remaining filters: a = 1 predtest vars=(a int) a IN (1, 2, 3) @@ -146,7 +146,7 @@ a IN (1) OR a IN (2) a = 1 OR a = 2 ---- true -└── remaining filters: (a IN (1,)) OR (a IN (2,)) +└── remaining filters: none predtest vars=(a int, b int) a IN (1, 2)