Skip to content

Commit

Permalink
sql: Support tuple column access and tuple stars
Browse files Browse the repository at this point in the history
This work is yet another step towards cockroachdb#16971.

The labeled tuples introduced in cockroachdb#25283 can now be accessed using
their labels or using a star, e.g. `(E).*`.

Release note (sql change): Labeled tuples can now be accessed using
their labels (e.g. `SELECT (x).word FROM (SELECT pg_expand_keywords()
AS x)` or a star (e.g. `SELECT (x).* FROM (SELECT pg_expand_keywords()
AS x)`).

Co-authored-by: Raphael 'kena' Poss <knz@cockroachlabs.com>
  • Loading branch information
BramGruneir and knz committed Jun 13, 2018
1 parent 5947420 commit 0ea0d96
Show file tree
Hide file tree
Showing 22 changed files with 434 additions and 219 deletions.
2 changes: 1 addition & 1 deletion pkg/sql/logictest/testdata/logic_test/builtin_function
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,7 @@ SELECT OVERLAY(foo.* PLACING 'string' FROM 'string') FROM (VALUES (1)) AS foo(x)
query error cannot use "nonexistent.\*" without a FROM clause
SELECT nonexistent.* IS NOT TRUE

query error unsupported comparison operator: <tuple{int}> IS DISTINCT FROM <bool>
query error unsupported comparison operator: <tuple{int AS x}> IS DISTINCT FROM <bool>
SELECT foo.* IS NOT TRUE FROM (VALUES (1)) AS foo(x)

query T
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/logictest/testdata/logic_test/select
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ query error no data source matches pattern: bar.kv.\*
SELECT bar.kv.* FROM kv

# Don't panic with invalid names (#8024)
query error cannot subscript type tuple\{string, string\} because it is not an array
query error cannot subscript type tuple\{string AS k, string AS v\} because it is not an array
SELECT kv.*[1] FROM kv

query T colnames
Expand Down
124 changes: 91 additions & 33 deletions pkg/sql/logictest/testdata/logic_test/srfs
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,10 @@ SELECT (('a')).x
query error pq: unnest\(\): cannot determine type of empty array. Consider annotating with the desired type, for example ARRAY\[\]:::int\[\]
SELECT (unnest(ARRAY[])).*

query I colnames
query error type int is not composite
SELECT (unnest(ARRAY[]:::INT[])).*
----
unnest

subtest multi_column

query TI colnames
SELECT (information_schema._pg_expandarray(ARRAY['c', 'b', 'a'])).*
Expand All @@ -465,7 +465,7 @@ n
2
3

query error pq: could not identify column "other" in record data type
query error pq: could not identify column "other" in tuple{string AS x, int AS n}
SELECT (information_schema._pg_expandarray(ARRAY['c', 'b', 'a'])).other

query T colnames
Expand Down Expand Up @@ -503,16 +503,28 @@ c 1
b 2
a 3

subtest 24866
query I colnames
SELECT (i.keys).n FROM (SELECT information_schema._pg_expandarray(ARRAY[3,2,1]) AS keys) AS i
----
n
1
2
3

# TODO(bram): #24866
# query I colnames
# SELECT (i.keys).n FROM (SELECT information_schema._pg_expandarray(ARRAY[3,2,1]) AS keys) AS i;
# ----
# n
# 3
# 2
# 1
query II colnames
SELECT (i.keys).* FROM (SELECT information_schema._pg_expandarray(ARRAY[3,2,1]) AS keys) AS i
----
x n
3 1
2 2
1 3

query T
SELECT ((i.keys).*, 123) FROM (SELECT information_schema._pg_expandarray(ARRAY[3,2,1]) AS keys) AS i
----
((3, 1),123)
((2, 2),123)
((1, 3),123)

subtest generate_subscripts

Expand Down Expand Up @@ -778,8 +790,10 @@ SELECT information_schema._pg_expandarray(indkey) FROM pg_index ORDER BY x, n
# pg_index
# ----

subtest correlated_json_object_keys

statement ok
CREATE TABLE j(x INT, y JSON);
CREATE TABLE j(x INT PRIMARY KEY, y JSON);
INSERT INTO j VALUES
(1, '{"a":123,"b":456}'),
(2, '{"c":111,"d":222}')
Expand All @@ -792,26 +806,70 @@ SELECT x, y->>json_object_keys(y) FROM j
2 111
2 222

subtest correlated_multi_column

# The following needs some more tuple support to work. #24866
# query TTI
# SELECT a.attname, a.atttypid, atttypmod
# FROM pg_catalog.pg_class ct
# JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid)
# JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid)
# JOIN (
# SELECT i.indexrelid, i.indrelid, i.indisprimary,
# information_schema._pg_expandarray(i.indkey) AS keys
# FROM pg_catalog.pg_index i
# ) i ON (a.attnum = (i.keys).x AND a.attrelid = i.indrelid)
# WHERE true
# AND n.nspname = 'information_schema'
# AND ct.relname = 'columns'
# AND i.indisprimary
# ORDER BY a.attnum
# ----
query I colnames
SELECT (i.keys).n FROM (SELECT information_schema._pg_expandarray(indkey) AS keys FROM pg_index) AS i
----
n
1
1
1
1
2
1

# Ditto #24866
subtest dbviz_example_query

# DbVisualizer query from #24649 listed in #16971.
query TTI
SELECT a.attname, a.atttypid, atttypmod
FROM pg_catalog.pg_class ct
JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid)
JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid)
JOIN (
SELECT i.indexrelid, i.indrelid, i.indisprimary,
information_schema._pg_expandarray(i.indkey) AS keys
FROM pg_catalog.pg_index i
) i ON (a.attnum = (i.keys).x AND a.attrelid = i.indrelid)
WHERE true
AND n.nspname = 'public'
AND ct.relname = 'j'
AND i.indisprimary
ORDER BY a.attnum
----
x 20 -1

subtest metabase_confluent_example_query

# Test from metabase listed on #16971.
# Also Kafka Confluent sink query from #25854.
query TTTTIT
SELECT NULL AS TABLE_CAT,
n.nspname AS TABLE_SCHEM,
ct.relname AS TABLE_NAME,
a.attname AS COLUMN_NAME,
(i.keys).n AS KEY_SEQ,
ci.relname AS PK_NAME
FROM pg_catalog.pg_class ct
JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid)
JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid)
JOIN (SELECT i.indexrelid,
i.indrelid,
i.indisprimary,
information_schema._pg_expandarray(i.indkey) AS keys
FROM pg_catalog.pg_index i) i ON (a.attnum = (i.keys).x AND a.attrelid = i.indrelid)
JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid)
WHERE true AND ct.relname = 'j' AND i.indisprimary
ORDER BY table_name, pk_name, key_seq
----
NULL public j x 1 primary

subtest liquibase_example_query

# # Test from #24713 (Liquibase) listed on #16971.
# # TODO(knz) Needs support for pg_get_indexdef with 3 arguments,
# # see #26629.
# query TTTBTTIITTTTT
# SELECT NULL AS table_cat,
# n.nspname AS table_schem,
Expand Down Expand Up @@ -856,7 +914,7 @@ SELECT x, y->>json_object_keys(y) FROM j
# JOIN pg_catalog.pg_am am ON (ci.relam = am.oid)
# WHERE TRUE
# AND n.nspname = 'public'
# AND ct.relname = 'act_de_databasechangelog'
# AND ct.relname = 'j'
# ORDER BY non_unique,
# TYPE,
# index_name,
Expand Down
86 changes: 71 additions & 15 deletions pkg/sql/logictest/testdata/logic_test/tuple
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,8 @@ SELECT (((ROW(pow(1, 10.0) + 9) AS t1), 'a' || 'b') AS t2, t3) = (((ROW(sqrt(100
a b
true true

subtest labeled_tuple_errors

query error pq: tuples \(\(\(\(1, 2\) AS a, b\), 'equal'\) AS c, d\), \(\(\(\(1, 'huh'\) AS e, f\), 'equal'\) AS g, h\) are not comparable at index 1: tuples \(\(1, 2\) AS a, b\), \(\(1, 'huh'\) AS e, f\) are not comparable at index 2: unsupported comparison operator: <int> = <string>
SELECT ((((1, 2) AS a, b), 'equal') AS c, d) = ((((1, 'huh') AS e, f), 'equal') AS g, h)

Expand Down Expand Up @@ -709,10 +711,14 @@ SELECT ((((((1, '2', 3) AS a, b, c), ((4,'5') AS a, b), (ROW(6) AS a)) AS a, b,
r
(((1, '2', 3), (4, '5'), (6)),(7, 8),('9'))

### Accessing a specific column
subtest labeled_tuple_column_access

## base working case

# Accessing a specific column
query error pq: could not identify column "x" in tuple{int AS a, int AS b, int AS c}
SELECT (((1,2,3) AS a,b,c)).x

query ITBITB colnames
SELECT (((1,'2',true) AS a,b,c)).a
,(((1,'2',true) AS a,b,c)).b
Expand All @@ -724,7 +730,7 @@ SELECT (((1,'2',true) AS a,b,c)).a
a b c a b c
1 2 true 1 2 true

## A collection of error cases
subtest labeled_tuple_column_access_errors

# column doesn't exist
query error pq: could not identify column "x" in tuple{int AS a, int AS b, int AS c}
Expand All @@ -744,33 +750,83 @@ SELECT ((1,2,3)).x
query error pq: type tuple{int, int, int} is not composite
SELECT ((1,2,3)).*

# Non unique labels
query error pq: found duplicate tuple label: "a"
SELECT (((1,'2',true) AS a,b,a)).a

## The following are all cases that will be addressed in future work

# Accessing all the columns
query error pq: star expansion of tuples is not supported

query ITB colnames
SELECT (((1,'2',true) AS a,b,c)).*
----
a b c
1 2 true

query error pq: star expansion of tuples is not supported
query ITB colnames
SELECT ((ROW(1,'2',true) AS a,b,c)).*
----
a b c
1 2 true

query T
SELECT (((ROW(1,'2',true) AS a,b,c)).*, 456)
----
((1, '2', true),456)

query I colnames
SELECT ((ROW(1) AS a)).*
----
a
1


# From a Subquery
query error pq: unimplemented: cannot access column "e" in non-tuple expression
SELECT ((t)).e, ((t)).f, ((t)).g
subtest literal_labeled_tuple_in_subquery

query ITB colnames
SELECT (x).e, (x).f, (x).g
FROM (
SELECT ((1,'2',true) AS e,f,g) AS x
)
----
e f g
1 2 true

query ITB colnames
SELECT (x).*
FROM (
SELECT ((1,'2',true) AS e,f,g) AS t
SELECT ((1,'2',true) AS e,f,g) AS x
)
----
e f g
1 2 true

subtest labeled_tuples_derived_from_relational_subquery_schema

# Needed until #26627 is resolved.
statement ok
set distsql=off

query IT
SELECT (x).a, (x).b
FROM (SELECT (ROW(a, b) AS a, b) AS x FROM (VALUES (1, 'one')) AS t(a, b))
----
1 one

# From a table directly
statement ok
CREATE TABLE t (a int, b string)

statement ok
INSERT INTO t VALUES (1, 'one'), (2, 'two')

query IT
SELECT (x).a, (x).b
FROM (SELECT (ROW(a, b) AS a, b) AS x FROM t)
ORDER BY 1
LIMIT 1
----
1 one

statement ok
set distsql=auto

subtest labeled_column_access_from_table

query error pq: column "t" does not exist
SELECT (t).a FROM t

Expand Down
24 changes: 12 additions & 12 deletions pkg/sql/opt/exec/execbuilder/testdata/aggregate
Original file line number Diff line number Diff line change
Expand Up @@ -171,18 +171,18 @@ group · · (count int, sum decimal, max int) ·
query TTTTT
EXPLAIN (VERBOSE) SELECT count(DISTINCT a.*) FROM kv a, kv b
----
group · · (count) ·
│ aggregate 0 count(DISTINCT (k, v, w, s)) · ·
└── render · · ("(k, v, w, s)") ·
│ render 0 (a.k, a.v, a.w, a.s) · ·
└── join · · (k, v, w, s, k[omitted], v[omitted], w[omitted], s[omitted]) ·
│ type cross · ·
├── scan · · (k, v, w, s) k!=NULL; key(k)
│ table kv@primary · ·
│ spans ALL · ·
└── scan · · (k[omitted], v[omitted], w[omitted], s[omitted]) k!=NULL; key(k)
· table kv@primary · ·
· spans ALL · ·
group · · (count) ·
│ aggregate 0 count(DISTINCT ((k, v, w, s) AS k, v, w, s)) · ·
└── render · · ("((k, v, w, s) AS k, v, w, s)") ·
│ render 0 ((a.k, a.v, a.w, a.s) AS k, v, w, s) · ·
└── join · · (k, v, w, s, k[omitted], v[omitted], w[omitted], s[omitted]) ·
│ type cross · ·
├── scan · · (k, v, w, s) k!=NULL; key(k)
│ table kv@primary · ·
│ spans ALL · ·
└── scan · · (k[omitted], v[omitted], w[omitted], s[omitted]) k!=NULL; key(k)
· table kv@primary · ·
· spans ALL · ·

query TTT
SELECT "Tree", "Field", "Description" FROM [
Expand Down
10 changes: 5 additions & 5 deletions pkg/sql/opt/optbuilder/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,18 @@ func (b *Builder) buildProjectionList(selects tree.SelectExprs, inScope *scope,
panic(builderError{err})
}

// Special handling for "*" and "<table>.*".
// Special handling for "*", "<table>.*" and "(E).*".
if v, ok := e.Expr.(tree.VarName); ok {
switch v.(type) {
case tree.UnqualifiedStar, *tree.AllColumnsSelector:
case tree.UnqualifiedStar, *tree.AllColumnsSelector, *tree.TupleStar:
if e.As != "" {
panic(builderError{pgerror.NewErrorf(pgerror.CodeSyntaxError,
"%q cannot be aliased", tree.ErrString(v))})
}

exprs := b.expandStar(e.Expr, inScope)
for _, e := range exprs {
b.buildScalarProjection(e, "", inScope, outScope)
labels, exprs := b.expandStar(e.Expr, inScope)
for i, e := range exprs {
b.buildScalarProjection(e, labels[i], inScope, outScope)
}
continue
}
Expand Down
Loading

0 comments on commit 0ea0d96

Please sign in to comment.