Skip to content

Commit

Permalink
Merge #78423
Browse files Browse the repository at this point in the history
78423: sql: fix ColumnAccessExpr.Eval with NULL inner expression r=mgartner a=mgartner

#### 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.

#### 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.


Co-authored-by: Marcus Gartner <[email protected]>
  • Loading branch information
craig[bot] and mgartner committed Apr 6, 2022
2 parents 3b99ec1 + ae9a4c2 commit cf74ec6
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 1 deletion.
32 changes: 32 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/tuple
Original file line number Diff line number Diff line change
Expand Up @@ -1185,3 +1185,35 @@ 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

# 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;
3 changes: 3 additions & 0 deletions pkg/sql/sem/tree/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -4232,6 +4232,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
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/sem/tree/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -2514,7 +2514,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.
Expand Down

0 comments on commit cf74ec6

Please sign in to comment.