Skip to content

Commit

Permalink
opt/optbuilder: support using table name as projection
Browse files Browse the repository at this point in the history
* Fix projection code to be able to use the column name as a table name
  by catching undefined columns error and evaluating it as a TupleStar
* Fix scope VisitPre to do something similar to above, to make this work
  for functions.

Release note (sql change): Using the table name as a projection now
works, e.g. SELECT table_name FROM table_name or SELECT
row_to_json(table_name) FROM table_name.
  • Loading branch information
otan committed May 6, 2021
1 parent 684e753 commit 4c66b7b
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 9 deletions.
19 changes: 16 additions & 3 deletions pkg/sql/logictest/testdata/logic_test/tuple
Original file line number Diff line number Diff line change
Expand Up @@ -914,10 +914,23 @@ a b
1 one
2 two


# Pending #26719
query error pq: column "t" does not exist
query I rowsort
SELECT (t).a FROM t
----
1
2

query T rowsort
SELECT t FROM t
----
(1,one)
(2,two)

query T rowsort
SELECT row_to_json(t) FROM t
----
{"a": 1, "b": "one"}
{"a": 2, "b": "two"}

statement ok
DROP TABLE t
Expand Down
15 changes: 12 additions & 3 deletions pkg/sql/opt/optbuilder/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sqlerrors"
"github.com/cockroachdb/cockroach/pkg/sql/types"
)

Expand Down Expand Up @@ -186,9 +187,17 @@ func (b *Builder) resolveColRef(e tree.Expr, inScope *scope) tree.TypedExpr {
unresolved, ok := e.(*tree.UnresolvedName)
if ok && !unresolved.Star && unresolved.NumParts == 1 {
colName := unresolved.Parts[0]
_, srcMeta, _, err := inScope.FindSourceProvidingColumn(b.ctx, tree.Name(colName))
if err != nil {
panic(err)
_, srcMeta, _, resolveErr := inScope.FindSourceProvidingColumn(b.ctx, tree.Name(colName))
if resolveErr != nil {
if sqlerrors.IsUndefinedColumnError(resolveErr) {
// It may be a reference to a table, e.g. SELECT tbl FROM tbl.
// Attempt to resolve as a TupleStar.
return func() tree.TypedExpr {
defer wrapColTupleStarPanic(resolveErr)
return inScope.resolveType(columnNameAsTupleStar(colName), types.Any)
}()
}
panic(resolveErr)
}
return srcMeta.(tree.TypedExpr)
}
Expand Down
38 changes: 35 additions & 3 deletions pkg/sql/opt/optbuilder/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -925,9 +925,17 @@ func (s *scope) VisitPre(expr tree.Expr) (recurse bool, newExpr tree.Expr) {
return s.VisitPre(vn)

case *tree.ColumnItem:
colI, err := t.Resolve(s.builder.ctx, s)
if err != nil {
panic(err)
colI, resolveErr := t.Resolve(s.builder.ctx, s)
if resolveErr != nil {
if sqlerrors.IsUndefinedColumnError(resolveErr) {
// Attempt to resolve as columnname.*, which allows items
// such as SELECT row_to_json(tbl_name) FROM tbl_name to work.
return func() (bool, tree.Expr) {
defer wrapColTupleStarPanic(resolveErr)
return s.VisitPre(columnNameAsTupleStar(string(t.ColumnName)))
}()
}
panic(resolveErr)
}
return false, colI.(*scopeColumn)

Expand Down Expand Up @@ -1593,3 +1601,27 @@ func (s *scope) String() string {

return buf.String()
}

func columnNameAsTupleStar(colName string) *tree.TupleStar {
return &tree.TupleStar{
Expr: &tree.UnresolvedName{
Star: true,
NumParts: 2,
Parts: tree.NameParts{"", colName},
},
}
}

// wrapColTupleStarPanic checks for panics and if the pgcode is
// UndefinedTable panics with the originalError.
// Otherwise, it will panic with the recovered error.
func wrapColTupleStarPanic(originalError error) {
if r := recover(); r != nil {
if err, ok := r.(error); ok {
if sqlerrors.IsUndefinedRelationError(err) {
panic(originalError)
}
}
panic(r)
}
}
62 changes: 62 additions & 0 deletions pkg/sql/opt/optbuilder/testdata/select
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,31 @@ project
└── scan kv
└── columns: k:1!null v:2 crdb_internal_mvcc_timestamp:3

build
SELECT row_to_json(kv) FROM kv
----
project
├── columns: row_to_json:4
├── scan kv
│ └── columns: k:1!null v:2 crdb_internal_mvcc_timestamp:3
└── projections
└── row_to_json(((k:1, v:2) AS k, v)) [as=row_to_json:4]

build
SELECT kv FROM kv
----
project
├── columns: kv:4
├── scan kv
│ └── columns: k:1!null v:2 crdb_internal_mvcc_timestamp:3
└── projections
└── ((k:1, v:2) AS k, v) [as=kv:4]

build
SELECT log(kv) FROM kv
----
error (42883): unknown signature: log(tuple{char AS k, char AS v})

build
SELECT (kv.*) AS r FROM kv
----
Expand Down Expand Up @@ -1478,3 +1503,40 @@ build
DELETE FROM inaccessible RETURNING v
----
error (42703): column "v" does not exist

exec-ddl
CREATE TABLE same_name (
same_name TEXT PRIMARY KEY,
b INT
)
----

build
SELECT same_name FROM same_name
----
project
├── columns: same_name:1!null
└── scan same_name
└── columns: same_name:1!null b:2 crdb_internal_mvcc_timestamp:3

build
SELECT row_to_json(same_name) FROM same_name
----
error (42883): unknown signature: row_to_json(string)

build
SELECT log(same_name) FROM same_name
----
error (42883): unknown signature: log(string)

build
SELECT t FROM (VALUES (1,2), (3,4)) t(x, y);
----
project
├── columns: t:3!null
├── values
│ ├── columns: column1:1!null column2:2!null
│ ├── (1, 2)
│ └── (3, 4)
└── projections
└── ((column1:1, column2:2) AS x, y) [as=t:3]

0 comments on commit 4c66b7b

Please sign in to comment.