Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(binder): enhance support of struct.* (and table.*) #10847

Open
xiangjinwu opened this issue Jul 10, 2023 · 0 comments
Open

feat(binder): enhance support of struct.* (and table.*) #10847

xiangjinwu opened this issue Jul 10, 2023 · 0 comments
Assignees
Labels

Comments

@xiangjinwu
Copy link
Contributor

xiangjinwu commented Jul 10, 2023

https://www.postgresql.org/docs/15/rowtypes.html#ROWTYPES-USAGE

In PostgreSQL, a reference to a table name (or alias) in a query is effectively a reference to the composite value of the table's current row. For example, if we had a table inventory_item as shown above, we could write:

SELECT c FROM inventory_item c;

In addition, the interpretation of .* can be different in different context:

The composite_value.* syntax results in column expansion of this kind when it appears at the top level of a SELECT output list, a RETURNING list in INSERT/UPDATE/DELETE, a VALUES clause, or a row constructor. In all other contexts (including when nested inside one of those constructs), attaching .* to a composite value does not change the value, since it means “all columns” and so the same composite value is produced again. For example, if somefunc() accepts a composite-valued argument, these queries are the same:

SELECT somefunc(c.*) FROM inventory_item c;
SELECT somefunc(c) FROM inventory_item c;

In practice, builtin functions that accept a composite-valued argument are rare: most are the ones accept any types, for example count.

This results in the following behavior in PostgreSQL:

test=# create table a(a1 int); insert into a values (2), (null), (3);
CREATE TABLE
INSERT 0 3

test=# create table b(b1 int); insert into b values (3), (null);
CREATE TABLE
INSERT 0 2

test=# select count(*), count(b.*), count(b1) from a left join b on a1 = b1;
 count | count | count 
-------+-------+-------
     3 |     1 |     1
(1 row)

test=# select count(*), count(b.*), count(b1) from a left join b on a1 is not distinct from b1;
 count | count | count 
-------+-------+-------
     3 |     2 |     1
(1 row)

test=# select a.*, b.*, a, b, b is null from a left join b on a1 = b1 order by a1;
 a1 | b1 |  a  |  b  | ?column? 
----+----+-----+-----+----------
  2 |    | (2) |     | t
  3 |  3 | (3) | (3) | f
    |    | ()  |     | t
(3 rows)

test=# select a.*, b.*, a, b, b is null from a left join b on a1 is not distinct from b1 order by a1;
 a1 | b1 |  a  |  b  | ?column? 
----+----+-----+-----+----------
  2 |    | (2) |     | t
  3 |  3 | (3) | (3) | f
    |    | ()  | ()  | t
(3 rows)

Notes:

  • count(b.*) is not same as count(*) or count(b1) during outer join.
  • CockroachDB v22.2.6 and Duckdb v0.7.1 both return 3, 3, 1 for the counts, because their join did not return NULL (but () a struct with a null field) for the composite value b.
  • PG-incompatibility: IS NULL of all-null row/struct #10150

Tentatively assigning low priority due to its complexity and rare usage. We do see one query using count(t.*) #10838 but it is an inner join.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants