diff --git a/pkg/sql/logictest/testdata/logic_test/srfs b/pkg/sql/logictest/testdata/logic_test/srfs index ba0031396388..53b5f48f4b98 100644 --- a/pkg/sql/logictest/testdata/logic_test/srfs +++ b/pkg/sql/logictest/testdata/logic_test/srfs @@ -1087,8 +1087,8 @@ query II colnames SELECT (unnest(ARRAY[(1,2),(3,4)])).* ---- ?column? ?column? -1 1 -3 3 +1 2 +3 4 query T colnames SELECT * FROM unnest(ARRAY[(1,2),(3,4)]) diff --git a/pkg/sql/opt/optbuilder/testdata/project b/pkg/sql/opt/optbuilder/testdata/project index 0e6650e0eb22..f84f77b3d0ef 100644 --- a/pkg/sql/opt/optbuilder/testdata/project +++ b/pkg/sql/opt/optbuilder/testdata/project @@ -229,3 +229,17 @@ project │ └── columns: k:1!null v:2 └── projections └── ((v:2, v:2, v:2) AS a, b, c) [as=x:3] + +# Regression test for #48179. Star expansion of un-labeled tuple must project +# all columns from the tuple. +build +SELECT (b).* FROM (VALUES (((1, 2)))) as a(b) +---- +project + ├── columns: "?column?":2 "?column?":3 + ├── values + │ ├── columns: column1:1 + │ └── ((1, 2),) + └── projections + ├── (column1:1).@1 [as="?column?":2] + └── (column1:1).@2 [as="?column?":3] diff --git a/pkg/sql/opt/optbuilder/util.go b/pkg/sql/opt/optbuilder/util.go index 8a5f565e3dcf..0f7db0568326 100644 --- a/pkg/sql/opt/optbuilder/util.go +++ b/pkg/sql/opt/optbuilder/util.go @@ -81,10 +81,6 @@ func (b *Builder) expandStar( tTuple, isTuple := texpr.(*tree.Tuple) aliases = typ.TupleLabels() - for i := len(aliases); i < len(typ.TupleContents()); i++ { - // Add aliases for all the non-named columns in the tuple. - aliases = append(aliases, "?column?") - } exprs = make([]tree.TypedExpr, len(typ.TupleContents())) for i := range typ.TupleContents() { if isTuple { @@ -92,18 +88,25 @@ func (b *Builder) expandStar( exprs[i] = tTuple.Exprs[i].(tree.TypedExpr) } else { // Can't de-tuplify: - // either (Expr).* -> (Expr).a, (Expr).b, (Expr).c if there are enough labels, - // or (Expr).* -> (Expr).@1, (Expr).@2, (Expr).@3 if labels are missing. + // either (Expr).* -> (Expr).a, (Expr).b, (Expr).c if there are enough + // labels, or (Expr).* -> (Expr).@1, (Expr).@2, (Expr).@3 if labels are + // missing. // // We keep the labels if available so that the column name // generation still produces column label "x" for, e.g. (E).x. colName := "" - if i < len(typ.TupleContents()) { + if i < len(aliases) { colName = aliases[i] } + // NewTypedColumnAccessExpr expects colName to be empty if the tuple + // should be accessed by index. exprs[i] = tree.NewTypedColumnAccessExpr(texpr, colName, i) } } + for i := len(aliases); i < len(typ.TupleContents()); i++ { + // Add aliases for all the non-named columns in the tuple. + aliases = append(aliases, "?column?") + } case *tree.AllColumnsSelector: src, srcMeta, err := t.Resolve(b.ctx, inScope)