diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2020-11-04 12:34:50 -0500 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2020-11-04 12:34:50 -0500 |
| commit | 40c24bfef92530bd846e111c1742c2a54441c62c (patch) | |
| tree | 853a59e852f2414965dcb242473d738f5b29deb3 /src/test | |
| parent | f21636e5d5b8394ed076e18ddc5f4ba710c69c99 (diff) | |
Improve our ability to regurgitate SQL-syntax function calls.
The SQL spec calls out nonstandard syntax for certain function calls,
for example substring() with numeric position info is supposed to be
spelled "SUBSTRING(string FROM start FOR count)". We accept many
of these things, but up to now would not print them in the same format,
instead simplifying down to "substring"(string, start, count).
That's long annoyed me because it creates an interoperability
problem: we're gratuitously injecting Postgres-specific syntax into
what might otherwise be a perfectly spec-compliant view definition.
However, the real reason for addressing it right now is to support
a planned change in the semantics of EXTRACT() a/k/a date_part().
When we switch that to returning numeric, we'll have the parser
translate EXTRACT() to some new function name (might as well be
"extract" if you ask me) and then teach ruleutils.c to reverse-list
that per SQL spec. In this way existing calls to date_part() will
continue to have the old semantics.
To implement this, invent a new CoercionForm value COERCE_SQL_SYNTAX,
and make the parser insert that rather than COERCE_EXPLICIT_CALL when
the input has SQL-spec decoration. (But if the input has the form of
a plain function call, continue to mark it COERCE_EXPLICIT_CALL, even
if it's calling one of these functions.) Then ruleutils.c recognizes
COERCE_SQL_SYNTAX as a cue to emit SQL call syntax. It can know
which decoration to emit using hard-wired knowledge about the
functions that could be called this way. (While this solution isn't
extensible without manual additions, neither is the grammar, so this
doesn't seem unmaintainable.) Notice that this solution will
reverse-list a function call with SQL decoration only if it was
entered that way; so dump-and-reload will not by itself produce any
changes in the appearance of views.
This requires adding a CoercionForm field to struct FuncCall.
(I couldn't resist the temptation to rearrange that struct's
field order a tad while I was at it.) FuncCall doesn't appear
in stored rules, so that change isn't a reason for a catversion
bump, but I did one anyway because the new enum value for
CoercionForm fields could confuse old backend code.
Possible future work:
* Perhaps CoercionForm should now be renamed to DisplayForm,
or something like that, to reflect its more general meaning.
This'd require touching a couple hundred places, so it's not
clear it's worth the code churn.
* The SQLValueFunction node type, which was invented partly for
the same goal of improving SQL-compatibility of view output,
could perhaps be replaced with regular function calls marked
with COERCE_SQL_SYNTAX. It's unclear if this would be a net
code savings, however.
Discussion: https://postgr.es/m/42b73d2d-da12-ba9f-570a-420e0cce19d9@phystech.edu
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/modules/test_rls_hooks/test_rls_hooks.c | 10 | ||||
| -rw-r--r-- | src/test/regress/expected/create_view.out | 44 | ||||
| -rw-r--r-- | src/test/regress/expected/timestamptz.out | 6 | ||||
| -rw-r--r-- | src/test/regress/sql/create_view.sql | 22 |
4 files changed, 75 insertions, 7 deletions
diff --git a/src/test/modules/test_rls_hooks/test_rls_hooks.c b/src/test/modules/test_rls_hooks/test_rls_hooks.c index 0bfa878a253..c0aaabdcdb8 100644 --- a/src/test/modules/test_rls_hooks/test_rls_hooks.c +++ b/src/test/modules/test_rls_hooks/test_rls_hooks.c @@ -95,7 +95,10 @@ test_rls_hooks_permissive(CmdType cmdtype, Relation relation) */ n = makeFuncCall(list_make2(makeString("pg_catalog"), - makeString("current_user")), NIL, 0); + makeString("current_user")), + NIL, + COERCE_EXPLICIT_CALL, + -1); c = makeNode(ColumnRef); c->fields = list_make1(makeString("username")); @@ -155,7 +158,10 @@ test_rls_hooks_restrictive(CmdType cmdtype, Relation relation) policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true, TYPALIGN_INT); n = makeFuncCall(list_make2(makeString("pg_catalog"), - makeString("current_user")), NIL, 0); + makeString("current_user")), + NIL, + COERCE_EXPLICIT_CALL, + -1); c = makeNode(ColumnRef); c->fields = list_make1(makeString("supervisor")); diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index f10a3a7a127..b234d2d4f9f 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -1710,13 +1710,52 @@ select pg_get_viewdef('tt20v', true); i4.i4, + i8.i8 + FROM COALESCE(1, 2) c(c), + - pg_collation_for('x'::text) col(col), + + COLLATION FOR ('x'::text) col(col), + CURRENT_DATE d(d), + LOCALTIMESTAMP(3) t(t), + CAST(1 + 2 AS integer) i4(i4), + CAST((1 + 2)::bigint AS bigint) i8(i8); (1 row) +-- reverse-listing of various special function syntaxes required by SQL +create view tt201v as +select + extract(day from now()) as extr, + (now(), '1 day'::interval) overlaps + (current_timestamp(2), '1 day'::interval) as o, + 'foo' is normalized isn, + 'foo' is nfkc normalized isnn, + normalize('foo') as n, + normalize('foo', nfkd) as nfkd, + overlay('foo' placing 'bar' from 2) as ovl, + overlay('foo' placing 'bar' from 2 for 3) as ovl2, + position('foo' in 'foobar') as p, + substring('foo' from 2 for 3) as s, + substring('foo' similar 'f' escape '#') as ss, + substring('foo' from 'oo') as ssf, -- historically-permitted abuse + trim(' ' from ' foo ') as bt, + trim(leading ' ' from ' foo ') as lt, + trim(trailing ' foo ') as rt; +select pg_get_viewdef('tt201v', true); + pg_get_viewdef +----------------------------------------------------------------------------------------------- + SELECT date_part('day'::text, now()) AS extr, + + ((now(), '@ 1 day'::interval) OVERLAPS (CURRENT_TIMESTAMP(2), '@ 1 day'::interval)) AS o,+ + (('foo'::text) IS NORMALIZED) AS isn, + + (('foo'::text) IS NFKC NORMALIZED) AS isnn, + + NORMALIZE('foo'::text) AS n, + + NORMALIZE('foo'::text, NFKD) AS nfkd, + + OVERLAY('foo'::text PLACING 'bar'::text FROM 2) AS ovl, + + OVERLAY('foo'::text PLACING 'bar'::text FROM 2 FOR 3) AS ovl2, + + POSITION(('foo'::text) IN ('foobar'::text)) AS p, + + SUBSTRING('foo'::text FROM 2 FOR 3) AS s, + + SUBSTRING('foo'::text SIMILAR 'f'::text ESCAPE '#'::text) AS ss, + + "substring"('foo'::text, 'oo'::text) AS ssf, + + TRIM(BOTH ' '::text FROM ' foo '::text) AS bt, + + TRIM(LEADING ' '::text FROM ' foo '::text) AS lt, + + TRIM(TRAILING FROM ' foo '::text) AS rt; +(1 row) + -- corner cases with empty join conditions create view tt21v as select * from tt5 natural inner join tt6; @@ -1904,7 +1943,7 @@ drop cascades to view aliased_view_2 drop cascades to view aliased_view_3 drop cascades to view aliased_view_4 DROP SCHEMA testviewschm2 CASCADE; -NOTICE: drop cascades to 67 other objects +NOTICE: drop cascades to 68 other objects DETAIL: drop cascades to table t1 drop cascades to view temporal1 drop cascades to view temporal2 @@ -1966,6 +2005,7 @@ drop cascades to view tt17v drop cascades to view tt18v drop cascades to view tt19v drop cascades to view tt20v +drop cascades to view tt201v drop cascades to view tt21v drop cascades to view tt22v drop cascades to view tt23v diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 639b50308e4..c3009655541 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -2726,10 +2726,10 @@ create temp table tmptz (f1 timestamptz primary key); insert into tmptz values ('2017-01-18 00:00+00'); explain (costs off) select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00'; - QUERY PLAN -------------------------------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Seq Scan on tmptz - Filter: (timezone('utc'::text, f1) = 'Wed Jan 18 00:00:00 2017'::timestamp without time zone) + Filter: ((f1 AT TIME ZONE 'utc'::text) = 'Wed Jan 18 00:00:00 2017'::timestamp without time zone) (2 rows) select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00'; diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql index e7af0bf2fa4..6d4dd539655 100644 --- a/src/test/regress/sql/create_view.sql +++ b/src/test/regress/sql/create_view.sql @@ -586,6 +586,28 @@ select * from cast(1+2 as int8) as i8; select pg_get_viewdef('tt20v', true); +-- reverse-listing of various special function syntaxes required by SQL + +create view tt201v as +select + extract(day from now()) as extr, + (now(), '1 day'::interval) overlaps + (current_timestamp(2), '1 day'::interval) as o, + 'foo' is normalized isn, + 'foo' is nfkc normalized isnn, + normalize('foo') as n, + normalize('foo', nfkd) as nfkd, + overlay('foo' placing 'bar' from 2) as ovl, + overlay('foo' placing 'bar' from 2 for 3) as ovl2, + position('foo' in 'foobar') as p, + substring('foo' from 2 for 3) as s, + substring('foo' similar 'f' escape '#') as ss, + substring('foo' from 'oo') as ssf, -- historically-permitted abuse + trim(' ' from ' foo ') as bt, + trim(leading ' ' from ' foo ') as lt, + trim(trailing ' foo ') as rt; +select pg_get_viewdef('tt201v', true); + -- corner cases with empty join conditions create view tt21v as |
