From 84db765a5ca1283c7ee3862db94b151a0146f04d Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Thu, 24 Mar 2022 14:27:01 +0000 Subject: [PATCH 1/2] sql: fix ColumnAccessExpr.Eval with NULL inner expression Previously, `ColumnAccessExpr.Eval` would panic if the `ColumnAccessExpr`'s inner expression evaluated to `NULL`, because it attempted to cast this `NULL` to a `DTuple`. Now, if the inner expression is `NULL`, `ColumnAccessExpr.Eval` returns `NULL`. Fixes #78159 Release note (bug fix): A bug has been fixed that caused an internal error when the inner expression of a column access expression evaluated to `NULL`. For example, evaluation of the expression `(CASE WHEN b THEN ((ROW(1) AS a)) ELSE NULL END).a` would error when `b` is `false`. This bug has been present since version 19.1 or earlier. --- pkg/sql/logictest/testdata/logic_test/tuple | 15 +++++++++++++++ pkg/sql/sem/tree/eval.go | 3 +++ 2 files changed, 18 insertions(+) diff --git a/pkg/sql/logictest/testdata/logic_test/tuple b/pkg/sql/logictest/testdata/logic_test/tuple index 3672a5a4ff07..3d5fc327c27f 100644 --- a/pkg/sql/logictest/testdata/logic_test/tuple +++ b/pkg/sql/logictest/testdata/logic_test/tuple @@ -978,3 +978,18 @@ CREATE TABLE t74729 AS SELECT g % 2 = 1 AS _bool FROM generate_series(1, 5) AS g statement ok SELECT CASE WHEN _bool THEN (1, ('a', 2)) ELSE (3, NULL) END FROM t74729 + +# Regression test for #78159. Column access of NULL should not cause an internal +# error. +subtest 78159 + +statement ok +CREATE TABLE t78159 (b BOOL) + +statement ok +INSERT INTO t78159 VALUES (false) + +query B +SELECT (CASE WHEN b THEN ((ROW(1) AS a)) ELSE NULL END).a from t78159 +---- +NULL diff --git a/pkg/sql/sem/tree/eval.go b/pkg/sql/sem/tree/eval.go index f2427c32aeb5..ee2250754458 100644 --- a/pkg/sql/sem/tree/eval.go +++ b/pkg/sql/sem/tree/eval.go @@ -3927,6 +3927,9 @@ func (expr *ColumnAccessExpr) Eval(ctx *EvalContext) (Datum, error) { if err != nil { return nil, err } + if d == DNull { + return d, nil + } return d.(*DTuple).D[expr.ColIndex], nil } From d3fbe5f92cfe2c615034a926915efb34ce277b25 Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Fri, 25 Mar 2022 18:13:30 +0000 Subject: [PATCH 2/2] sql: include tuple labels in types returned from typeCheckSameTypedTupleExprs Previously, an expression that produced a tuple from several potential values was typed as a tuple without any labels. This prevented a tuple's column to be accessed by a label name. For example, the expression below would result in the error "could not identify column 'a' in record data type". SELECT (CASE WHEN true THEN (ROW(1) AS a) ELSE (ROW(2) AS a) END).a Now, the labels of the first tuple are used in the type of the outer expression. Fixes #78515 Release note (bug fix): A bug has been fixed that caused an error when accessing a named column of a labelled tuple. The bug only occurred when an expression could produce one of several different tuples. For example, `(CASE WHEN true THEN (ROW(1) AS a) ELSE (ROW(2) AS a) END).a` would fail to evaluate. This bug was present since version 22.1.0. Although present in previous versions, it was impossible to encounter due to limitations that prevented using tuples in this way. --- pkg/sql/logictest/testdata/logic_test/tuple | 17 +++++++++++++++++ pkg/sql/sem/tree/type_check.go | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pkg/sql/logictest/testdata/logic_test/tuple b/pkg/sql/logictest/testdata/logic_test/tuple index 3d5fc327c27f..27c219f0fedc 100644 --- a/pkg/sql/logictest/testdata/logic_test/tuple +++ b/pkg/sql/logictest/testdata/logic_test/tuple @@ -993,3 +993,20 @@ query B SELECT (CASE WHEN b THEN ((ROW(1) AS a)) ELSE NULL END).a from t78159 ---- NULL + +# Regression test for #78515. Propagate tuple labels when type-checking +# expressions with multiple matching tuple types. +subtest 78515 + +statement ok +SELECT (CASE WHEN false THEN (ROW(1) AS a) ELSE (ROW(2) AS a) END).a; + +# The label of the first tuple is used in the type of the CASE expression. This +# is similar to Postgres, but not exactly the same - Postgres uses the labels of +# the last tuple. This difference should be addressed in the future when we try +# to adhere more closely to Postgres's type conversion behavior (see #75101). +statement ok +SELECT (CASE WHEN false THEN (ROW(1) AS a) ELSE (ROW(2) AS b) END).a; + +statement error could not identify column \"b\" in tuple{int AS a} +SELECT (CASE WHEN false THEN (ROW(1) AS a) ELSE (ROW(2) AS b) END).b; diff --git a/pkg/sql/sem/tree/type_check.go b/pkg/sql/sem/tree/type_check.go index 1b6ebc13502f..8892bb52081f 100644 --- a/pkg/sql/sem/tree/type_check.go +++ b/pkg/sql/sem/tree/type_check.go @@ -2397,7 +2397,7 @@ func typeCheckSameTypedTupleExprs( } // All expressions within tuples at the same indexes must be the same type. - resTypes := types.MakeTuple(make([]*types.T, firstLen)) + resTypes := types.MakeLabeledTuple(make([]*types.T, firstLen), first.Labels) sameTypeExprs := make([]Expr, 0, len(exprs)) // We will be skipping nulls, so we need to keep track at which indices in // exprs are the non-null tuples.