From 698d8c5ae4cb82b03384aa86e74a1df419129031 Mon Sep 17 00:00:00 2001 From: Andrew Kimball Date: Sat, 26 May 2018 22:16:16 -0700 Subject: [PATCH] opt: Add execbuilder support for ANY operator The optbuilder maps the SQL ANY, ALL, and IN operators that use a subquery into the opt.Any operator. This commit adds execution support for this operator. Release note: None --- .../opt/exec/execbuilder/scalar_builder.go | 26 +++ pkg/sql/opt/exec/execbuilder/testdata/select | 186 ++++++++++++++++++ pkg/sql/opt/exec/factory.go | 5 + pkg/sql/opt_exec_factory.go | 2 + 4 files changed, 219 insertions(+) diff --git a/pkg/sql/opt/exec/execbuilder/scalar_builder.go b/pkg/sql/opt/exec/execbuilder/scalar_builder.go index bc45cef3b2d2..854a3a675292 100644 --- a/pkg/sql/opt/exec/execbuilder/scalar_builder.go +++ b/pkg/sql/opt/exec/execbuilder/scalar_builder.go @@ -53,6 +53,7 @@ func init() { opt.CastOp: (*Builder).buildCast, opt.CoalesceOp: (*Builder).buildCoalesce, opt.ArrayOp: (*Builder).buildArray, + opt.AnyOp: (*Builder).buildAny, opt.UnsupportedExprOp: (*Builder).buildUnsupportedExpr, // Subquery operators. @@ -301,6 +302,31 @@ func (b *Builder) buildArray(ctx *buildScalarCtx, ev memo.ExprView) (tree.TypedE return tree.NewTypedArray(exprs, ev.Logical().Scalar.Type), nil } +func (b *Builder) buildAny(ctx *buildScalarCtx, ev memo.ExprView) (tree.TypedExpr, error) { + // Build the execution plan for the input subquery. + plan, err := b.buildRelational(ev.Child(0)) + if err != nil { + return nil, err + } + + // Construct tuple type of columns in the row. + types := types.TTuple{Types: make([]types.T, plan.outputCols.Len())} + plan.outputCols.ForEach(func(key, val int) { + types.Types[val] = ev.Metadata().ColumnType(opt.ColumnID(key)) + }) + + input := b.addSubquery(exec.SubqueryAnyRows, types, plan.root) + + // Build the scalar value that is compared against each row. + scalar, err := b.buildScalar(ctx, ev.Child(1)) + if err != nil { + return nil, err + } + + cmp := opt.ComparisonOpReverseMap[ev.Private().(opt.Operator)] + return tree.NewTypedComparisonExprWithSubOp(tree.Any, cmp, scalar, input), nil +} + func (b *Builder) buildUnsupportedExpr( ctx *buildScalarCtx, ev memo.ExprView, ) (tree.TypedExpr, error) { diff --git a/pkg/sql/opt/exec/execbuilder/testdata/select b/pkg/sql/opt/exec/execbuilder/testdata/select index 58a615467fea..4eff7454fcba 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/select +++ b/pkg/sql/opt/exec/execbuilder/testdata/select @@ -276,3 +276,189 @@ filter · · (f float) · └── scan · · (f float) · · table flt@primary · · · spans ALL · · + +# ------------------------------------------------------------------------------ +# ANY, ALL tests. +# ------------------------------------------------------------------------------ +statement ok +CREATE TABLE abc (a INT, b INT, C INT) + +statement ok +INSERT INTO abc VALUES (1, 10, 100), (2, 20, 200), (3, 30, 300) + +query III +SELECT * FROM abc WHERE a = ANY(SELECT a FROM abc WHERE b = 10) +---- +1 10 100 + +query III +SELECT * FROM abc WHERE a < ANY(SELECT a FROM abc WHERE b = 30) ORDER BY a +---- +1 10 100 +2 20 200 + +query III +SELECT * FROM abc WHERE a > ANY(SELECT a FROM abc WHERE b = 30) +---- + +query III +SELECT * FROM abc WHERE a < ALL(SELECT b FROM abc) ORDER BY a +---- +1 10 100 +2 20 200 +3 30 300 + +query III +SELECT * FROM abc WHERE a < ALL(SELECT a FROM abc WHERE a >= 2) +---- +1 10 100 + +query III +SELECT * FROM abc WHERE a < ALL(SELECT a FROM abc) +---- + +# ------------------------------------------------------------------------------ +# IN tests. +# ------------------------------------------------------------------------------ +# Regression tests for #22670. +query B +SELECT 1 IN (1, 2) +---- +true + +query B +SELECT NULL IN (1, 2) +---- +NULL + +query B +SELECT 1 IN (1, NULL) +---- +true + +query B +SELECT 1 IN (NULL, 2) +---- +NULL + +query B +SELECT (1, NULL) IN ((1, 1)) +---- +NULL + +query B +SELECT (2, NULL) IN ((1, 1)) +---- +false + +query B +SELECT (1, 1) IN ((1, NULL)) +---- +NULL + +query B +SELECT (1, 1) IN ((2, NULL)) +---- +false + +# Tests with a tuple coming from a subquery. +query B +SELECT NULL::int IN (SELECT * FROM (VALUES (1)) AS t(a)) +---- +NULL + +query B +SELECT (1, NULL::int) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +NULL + +query B +SELECT (2, NULL::int) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +false + +query B +SELECT (NULL::int, 1) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +NULL + +query B +SELECT (NULL::int, 2) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +false + +query B +SELECT (NULL::int, NULL::int) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +NULL + +query B +SELECT NULL::int NOT IN (SELECT * FROM (VALUES (1)) AS t(a)) +---- +NULL + +query B +SELECT (1, NULL::int) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +NULL + +query B +SELECT (2, NULL::int) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +true + +query B +SELECT (NULL::int, 1) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +NULL + +query B +SELECT (NULL::int, 2) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +true + +query B +SELECT (NULL::int, NULL::int) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b)) +---- +NULL + +# Tests with an empty IN tuple. +query B +SELECT NULL::int IN (SELECT * FROM (VALUES (1)) AS t(a) WHERE a > 1) +---- +false + +query B +SELECT (1, NULL::int) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b) WHERE a > 1) +---- +false + +query B +SELECT (NULL::int, 1) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b) WHERE a > 1) +---- +false + +query B +SELECT (NULL::int, NULL::int) IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b) WHERE a > 1) +---- +false + +query B +SELECT NULL::int NOT IN (SELECT * FROM (VALUES (1)) AS t(a) WHERE a > 1) +---- +true + +query B +SELECT (1, NULL::int) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b) WHERE a > 1) +---- +true + +query B +SELECT (NULL::int, 1) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b) WHERE a > 1) +---- +true + +query B +SELECT (NULL::int, NULL::int) NOT IN (SELECT * FROM (VALUES (1, 1)) AS t(a, b) WHERE a > 1) +---- +true diff --git a/pkg/sql/opt/exec/factory.go b/pkg/sql/opt/exec/factory.go index c9eba9954f16..af3b479160f0 100644 --- a/pkg/sql/opt/exec/factory.go +++ b/pkg/sql/opt/exec/factory.go @@ -148,6 +148,11 @@ const ( // SubqueryOneRow - the subquery expects at most one row; the result is that // row (as a single value or a tuple), or NULL if there were no rows. SubqueryOneRow + // SubqueryAnyRows - the subquery is an argument to ANY. Any number of rows + // expected; the result is a sorted, distinct tuple of rows (i.e. it has been + // normalized). As a special case, if there is only one column selected, the + // result is a tuple of the selected values (instead of a tuple of 1-tuples). + SubqueryAnyRows ) // ColumnOrdinal is the 0-based ordinal index of a column produced by a Node. diff --git a/pkg/sql/opt_exec_factory.go b/pkg/sql/opt_exec_factory.go index 7560f8042f04..356584f90106 100644 --- a/pkg/sql/opt_exec_factory.go +++ b/pkg/sql/opt_exec_factory.go @@ -378,6 +378,8 @@ func (ef *execFactory) ConstructPlan( out.execMode = execModeExists case exec.SubqueryOneRow: out.execMode = execModeOneRow + case exec.SubqueryAnyRows: + out.execMode = execModeAllRowsNormalized default: return nil, errors.Errorf("invalid SubqueryMode %d", in.Mode) }