Thread
Commits
-
Remove obsolete prohibition on function name matching a column name.
- 45e98ee73062 11.0 landed
-
Consider syntactic form when disambiguating function vs column reference.
- b97a3465d73b 11.0 landed
-
row_to_json(), NULL values, and AS
Neil Conway <neil.conway@gmail.com> — 2018-06-14T22:57:27Z
Hi, The following behavior does not seem self-consistent to me: postgres=# select json_agg(row_to_json(x)) from (select *, null from generate_series(1, 3)) x; json_agg ----------------------------------------------------------------------------------------------------------------------- [{"generate_series":1,"?column?":null}, {"generate_series":2,"?column?":null}, {"generate_series":3,"?column?":null}] (1 row) postgres=# select json_agg(row_to_json(x)) from (select *, row_to_json(null) as jjj from generate_series(1, 3)) x; json_agg -------------------------------------------------------------------------------------------------------- [{"generate_series":1,"jjj":null}, {"generate_series":2,"jjj":null}, {"generate_series":3,"jjj":null}] (1 row) postgres=# select json_agg(row_to_json(x)) from (select *, row_to_json(null) from generate_series(1, 3)) x; json_agg -------------------- [null, null, null] (1 row) In particular, it is unclear to me why removing the targetlist alias in the subquery in the third example should change the result set of the parent query. Thanks, Neil -
Re: row_to_json(), NULL values, and AS
Tom Lane <tgl@sss.pgh.pa.us> — 2018-06-14T23:28:24Z
[ Hi Neil, long time no see ] Neil Conway <neil.conway@gmail.com> writes: > The following behavior does not seem self-consistent to me: Likewise. > In particular, it is unclear to me why removing the targetlist alias > in the subquery in the third example should change the result set of > the parent query. Looking at "explain verbose" output, it seems like it's not row_to_json's fault; rather, we seem to be mishandling expansion of the whole-row Var: regression=# explain verbose select json_agg(row_to_json(x)) from (select *, row_to_json(null) as jjj from generate_series(1, 3)) x; QUERY PLAN ----------------------------------------------------------------------------------------- Aggregate (cost=15.00..15.01 rows=1 width=32) Output: json_agg(row_to_json(ROW(generate_series.generate_series, NULL::json))) -> Function Scan on pg_catalog.generate_series (cost=0.00..10.00 rows=1000 width=4) Output: generate_series.generate_series Function Call: generate_series(1, 3) (5 rows) That's fine, but: regression=# explain verbose select json_agg(row_to_json(x)) from (select *, row_to_json(null) from generate_series(1, 3)) x; QUERY PLAN ----------------------------------------------------------------------------------------- Aggregate (cost=12.50..12.51 rows=1 width=32) Output: json_agg(NULL::json) -> Function Scan on pg_catalog.generate_series (cost=0.00..10.00 rows=1000 width=0) Output: generate_series.generate_series Function Call: generate_series(1, 3) (5 rows) That looks like it might be a bug in what we do with whole-row Vars during subquery flattening. But if you put an "offset 0" into the subquery to prevent flattening, you get different but just as weird misbehavior: regression=# explain verbose select json_agg(row_to_json(x)) from (select *, row_to_json(null) as jjj from generate_series(1, 3) offset 0) x; QUERY PLAN ------------------------------------------------------------------------------------------------ Aggregate (cost=25.00..25.02 rows=1 width=32) Output: json_agg(row_to_json(x.*)) -> Subquery Scan on x (cost=0.00..20.00 rows=1000 width=28) Output: x.* -> Function Scan on pg_catalog.generate_series (cost=0.00..10.00 rows=1000 width=36) Output: generate_series.generate_series, NULL::json Function Call: generate_series(1, 3) (7 rows) regression=# explain verbose select json_agg(row_to_json(x)) from (select *, row_to_json(null) from generate_series(1, 3) offset 0) x; QUERY PLAN ------------------------------------------------------------------------------------------ Aggregate (cost=22.50..22.52 rows=1 width=32) Output: json_agg((NULL::json)) -> Function Scan on pg_catalog.generate_series (cost=0.00..10.00 rows=1000 width=36) Output: NULL::integer, NULL::json Function Call: generate_series(1, 3) (5 rows) I'm not sure if this is just another artifact of the same problem. The extra parens in the json_agg() argument are suspicious to put it mildly, but I've not dug into it to see what the plan tree really looks like. regards, tom lane -
Re: row_to_json(), NULL values, and AS
Tom Lane <tgl@sss.pgh.pa.us> — 2018-06-15T14:53:13Z
I wrote: > Neil Conway <neil.conway@gmail.com> writes: >> The following behavior does not seem self-consistent to me: > Likewise. Oh! What is actually happening is (1) With no explicit alias, the column name of the sub-select's second output column is chosen to be "row_to_json". (2) That makes the outer query's notation row_to_json(x) ambiguous: it could be a function-syntax reference to the column x.row_to_json. (3) As it happens, the column interpretation is chosen when it's ambiguous (cf ParseComplexProjection). I'm a bit hesitant to muck with this behavior, given that it's stood for ~20 years. However, if we did want to touch it, maybe the right thing would be to give up the absolutist position that f(x) and x.f are exactly interchangeable. We could say instead that we prefer the function interpretation if function syntax is used, and the column interpretation if column syntax is used. I don't know how likely that is to break existing apps ... perhaps not very, but I wouldn't risk back-patching it in any case. regards, tom lane
-
Re: row_to_json(), NULL values, and AS
Neil Conway <neil.conway@gmail.com> — 2018-06-15T17:22:30Z
[ Hi Tom! Hope you're doing well. ] On Fri, Jun 15, 2018 at 7:53 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > Oh! What is actually happening is > > (1) With no explicit alias, the column name of the sub-select's second > output column is chosen to be "row_to_json". > > (2) That makes the outer query's notation row_to_json(x) ambiguous: it > could be a function-syntax reference to the column x.row_to_json. > > (3) As it happens, the column interpretation is chosen when it's > ambiguous (cf ParseComplexProjection). > > I'm a bit hesitant to muck with this behavior, given that it's stood > for ~20 years. However, if we did want to touch it, maybe the right > thing would be to give up the absolutist position that f(x) and x.f > are exactly interchangeable. We could say instead that we prefer the > function interpretation if function syntax is used, and the column > interpretation if column syntax is used. Interesting! Your proposed change seems quite reasonable to me. Thanks for digging into it. Neil
-
Re: row_to_json(), NULL values, and AS
Tom Lane <tgl@sss.pgh.pa.us> — 2018-06-15T19:34:58Z
Neil Conway <neil.conway@gmail.com> writes: > On Fri, Jun 15, 2018 at 7:53 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: >> (3) As it happens, the column interpretation is chosen when it's >> ambiguous (cf ParseComplexProjection). >> >> I'm a bit hesitant to muck with this behavior, given that it's stood >> for ~20 years. However, if we did want to touch it, maybe the right >> thing would be to give up the absolutist position that f(x) and x.f >> are exactly interchangeable. We could say instead that we prefer the >> function interpretation if function syntax is used, and the column >> interpretation if column syntax is used. > Interesting! Your proposed change seems quite reasonable to me. Here's a proposed patch for that. (It needs to be applied over the fixes in <14497.1529089235@sss.pgh.pa.us>, which are unrelated but touch some of the same code.) I didn't add any test cases yet, but probably it's desirable to have one. I wrote the doc change on the assumption that we'd sneak this into v11, but if anyone doesn't want to do that, I'll add it to the next commitfest instead. regards, tom lane
-
Re: row_to_json(), NULL values, and AS
Tom Lane <tgl@sss.pgh.pa.us> — 2018-06-16T14:42:26Z
I wrote: > Neil Conway <neil.conway@gmail.com> writes: >> On Fri, Jun 15, 2018 at 7:53 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: >>> I'm a bit hesitant to muck with this behavior, given that it's stood >>> for ~20 years. However, if we did want to touch it, maybe the right >>> thing would be to give up the absolutist position that f(x) and x.f >>> are exactly interchangeable. We could say instead that we prefer the >>> function interpretation if function syntax is used, and the column >>> interpretation if column syntax is used. >> Interesting! Your proposed change seems quite reasonable to me. > Here's a proposed patch for that. (It needs to be applied over the > fixes in <14497.1529089235@sss.pgh.pa.us>, which are unrelated but > touch some of the same code.) I didn't add any test cases yet, > but probably it's desirable to have one. It occurred to me this morning that there's actually a dump/reload hazard in the current behavior. Consider regression=# create table t1 (f1 int, f2 text); CREATE TABLE regression=# create view v1 as select row_to_json(t1) as j from t1; CREATE VIEW regression=# alter table t1 add column row_to_json text; ALTER TABLE regression=# \d+ v1 View "public.v1" Column | Type | Collation | Nullable | Default | Storage | Description --------+------+-----------+----------+---------+----------+------------- j | json | | | | extended | View definition: SELECT row_to_json(t1.*) AS j FROM t1; At this point, pg_dump will naively dump the view as CREATE VIEW public.v1 AS SELECT row_to_json(t1.*) AS j FROM public.t1; which, with the historical behavior, will be read as a reference to the subsequently-added column, giving a view definition that means something else entirely: regression=# \d+ v1 View "public.v1" Column | Type | Collation | Nullable | Default | Storage | Description --------+------+-----------+----------+---------+----------+------------- j | text | | | | extended | View definition: SELECT t1.row_to_json AS j FROM t1; The proposed change fixes this on the parsing side, with no need for any hacking in ruleutils.c (hence it will fix it for pre-existing dump files too). So I'm now pretty well convinced that this is a good change and we should slip it into v11. I'm still afraid to back-patch though. I vaguely recall one or two prior complaints in this area, but not so many that it's worth taking a chance of breaking applications in minor releases. regards, tom lane -
Re: row_to_json(), NULL values, and AS
Tom Lane <tgl@sss.pgh.pa.us> — 2018-06-17T17:43:26Z
I wrote: > So I'm now pretty well convinced that this is a good change and we > should slip it into v11. I wrote a test case and was about ready to commit this, when I started wondering about the behavior for fdresult == FUNCDETAIL_MULTIPLE. That is, suppose that the notation f(x) matches more than one candidate function f, and there's also a column named f. The old code would have chosen the column interpretation, and so would the patch I posted previously. But I don't really see how we can square that behavior with documenting that "we prefer the function interpretation for functional notation". I think you should get a "function is not unique" error in such a case, not silently picking the column instead. If it's the column you want, write it like a column. So the attached updated patch rearranges the error handling logic to make that happen. BTW, while trying to make a test case I was reminded that CREATE FUNCTION tries to prevent this situation from arising: regression=# create table t1 (f1 int); CREATE TABLE regression=# create function f1(t1) returns int as 'select 42' language sql; ERROR: "f1" is already an attribute of type t1 That's totally non-bulletproof, though, since you can still create the situation by renaming, or by adding the conflicting column after you create the function; not to mention Neil's original problem where the conflicting function is declared to take record or some such. Furthermore, this behavior itself creates a dump/reload hazard, since once you've done one of those things pg_dump will produce output that fails this check. While I've not done anything about it here, I think we should just remove that error check. With this patch, functions and columns can coexist reasonably peacefully, so we shouldn't need bogus hacks that try to prevent it. regards, tom lane
-
Re: row_to_json(), NULL values, and AS
Robert Haas <robertmhaas@gmail.com> — 2018-06-19T14:34:49Z
On Fri, Jun 15, 2018 at 10:53 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > I'm a bit hesitant to muck with this behavior, given that it's stood > for ~20 years. However, if we did want to touch it, maybe the right > thing would be to give up the absolutist position that f(x) and x.f > are exactly interchangeable. We could say instead that we prefer the > function interpretation if function syntax is used, and the column > interpretation if column syntax is used. I don't know how likely > that is to break existing apps ... perhaps not very, but I wouldn't > risk back-patching it in any case. For the record, I fear that this change (committed as b97a3465d73bfc2a9f5bcf5def1983dbaa0a26f8) is going to break more things than have been foreseen in this thread. I do not have a specific theory about how that's going to happen, just vague unease. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
-
Re: row_to_json(), NULL values, and AS
Tom Lane <tgl@sss.pgh.pa.us> — 2018-06-19T15:11:30Z
Robert Haas <robertmhaas@gmail.com> writes: > On Fri, Jun 15, 2018 at 10:53 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> I'm a bit hesitant to muck with this behavior, given that it's stood >> for ~20 years. However, if we did want to touch it, maybe the right >> thing would be to give up the absolutist position that f(x) and x.f >> are exactly interchangeable. We could say instead that we prefer the >> function interpretation if function syntax is used, and the column >> interpretation if column syntax is used. I don't know how likely >> that is to break existing apps ... perhaps not very, but I wouldn't >> risk back-patching it in any case. > For the record, I fear that this change (committed as > b97a3465d73bfc2a9f5bcf5def1983dbaa0a26f8) is going to break more > things than have been foreseen in this thread. I do not have a > specific theory about how that's going to happen, just vague unease. Yeah, that's why I wouldn't back-patch it. But certainly the behavior Neil complained of is pretty bad as well, and it's only going to get worse as we invent more functions taking record. I think the new behavior is a clear improvement. If it breaks any apps that were relying on interpreting f(x) as a column, they can fix it by writing x.f instead. We inflict worse upgrade pain than that on a regular basis. regards, tom lane