0006-Deparser-and-DDL-replication-for-the-rest-2023_04_07-2.patch
application/octet-stream
Filename: 0006-Deparser-and-DDL-replication-for-the-rest-2023_04_07-2.patch
Type: application/octet-stream
Part: 5
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0006
Subject: Deparser and DDL replication for the rest commands.
| File | + | − |
|---|---|---|
| contrib/test_decoding/test_decoding.c | 2 | 3 |
| doc/src/sgml/logical-replication.sgml | 108 | 103 |
| src/backend/catalog/aclchk.c | 5 | 4 |
| src/backend/catalog/pg_publication.c | 1 | 0 |
| src/backend/commands/collationcmds.c | 20 | 1 |
| src/backend/commands/ddl_deparse.c | 6523 | 118 |
| src/backend/commands/event_trigger.c | 34 | 0 |
| src/backend/commands/publicationcmds.c | 44 | 19 |
| src/backend/commands/seclabel.c | 6 | 0 |
| src/backend/replication/logical/ddltrigger.c | 85 | 3 |
| src/backend/replication/pgoutput/pgoutput.c | 14 | 6 |
| src/backend/tcop/cmdtag.c | 5 | 5 |
| src/backend/tcop/utility.c | 3 | 1 |
| src/backend/utils/adt/regproc.c | 51 | 0 |
| src/backend/utils/adt/ruleutils.c | 200 | 93 |
| src/backend/utils/cache/relcache.c | 1 | 0 |
| src/bin/pg_dump/pg_dump.c | 18 | 5 |
| src/bin/pg_dump/pg_dump.h | 1 | 0 |
| src/bin/psql/describe.c | 8 | 4 |
| src/include/catalog/pg_publication.h | 4 | 0 |
| src/include/commands/collationcmds.h | 2 | 1 |
| src/include/commands/event_trigger.h | 2 | 0 |
| src/include/tcop/cmdtag.h | 1 | 1 |
| src/include/tcop/ddl_deparse.h | 2 | 0 |
| src/include/tcop/deparse_utility.h | 8 | 0 |
| src/include/utils/aclchk_internal.h | 1 | 0 |
| src/include/utils/acl.h | 2 | 0 |
| src/include/utils/builtins.h | 1 | 0 |
| src/include/utils/ruleutils.h | 10 | 0 |
| src/test/regress/expected/psql.out | 3 | 3 |
| src/test/regress/expected/publication.out | 210 | 210 |
| src/test/regress/expected/subscription.out | 72 | 72 |
From b072482d1ebafbc6d99ebd11001f1a7c66296487 Mon Sep 17 00:00:00 2001
From: Hou Zhijie <houzj.fnst@cn.fujitsu.com>
Date: Thu, 6 Apr 2023 20:33:02 +0800
Subject: [PATCH 6/6] Deparser and DDL replication for the rest commands.
---
contrib/test_decoding/test_decoding.c | 5 +-
doc/src/sgml/logical-replication.sgml | 211 +-
src/backend/catalog/aclchk.c | 9 +-
src/backend/catalog/pg_publication.c | 1 +
src/backend/commands/collationcmds.c | 21 +-
src/backend/commands/ddl_deparse.c | 6641 +++++++++++++++++-
src/backend/commands/event_trigger.c | 34 +
src/backend/commands/publicationcmds.c | 63 +-
src/backend/commands/seclabel.c | 6 +
src/backend/replication/logical/ddltrigger.c | 88 +-
src/backend/replication/pgoutput/pgoutput.c | 20 +-
src/backend/tcop/cmdtag.c | 10 +-
src/backend/tcop/utility.c | 4 +-
src/backend/utils/adt/regproc.c | 51 +
src/backend/utils/adt/ruleutils.c | 293 +-
src/backend/utils/cache/relcache.c | 1 +
src/bin/pg_dump/pg_dump.c | 23 +-
src/bin/pg_dump/pg_dump.h | 1 +
src/bin/psql/describe.c | 12 +-
src/include/catalog/pg_publication.h | 4 +
src/include/commands/collationcmds.h | 3 +-
src/include/commands/event_trigger.h | 2 +
src/include/tcop/cmdtag.h | 2 +-
src/include/tcop/ddl_deparse.h | 2 +
src/include/tcop/deparse_utility.h | 8 +
src/include/utils/acl.h | 2 +
src/include/utils/aclchk_internal.h | 1 +
src/include/utils/builtins.h | 1 +
src/include/utils/ruleutils.h | 10 +
src/test/regress/expected/psql.out | 6 +-
src/test/regress/expected/publication.out | 420 +-
src/test/regress/expected/subscription.out | 144 +-
32 files changed, 7447 insertions(+), 652 deletions(-)
diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c
index 763d5c007f..da5b41df59 100644
--- a/contrib/test_decoding/test_decoding.c
+++ b/contrib/test_decoding/test_decoding.c
@@ -774,9 +774,11 @@ pg_decode_ddl_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
appendStringInfo(ctx->out, "cmdtype: Simple, ");
break;
case DCT_TableDropStart:
+ case DCT_ObjectDropStart:
appendStringInfo(ctx->out, "cmdtype: Drop start, ");
break;
case DCT_TableDropEnd:
+ case DCT_ObjectDropEnd:
appendStringInfo(ctx->out, "cmdtype: Drop end, ");
break;
case DCT_TableAlter:
@@ -785,9 +787,6 @@ pg_decode_ddl_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
case DCT_ObjectCreate:
appendStringInfo(ctx->out, "cmdtype: Create, ");
break;
- case DCT_ObjectDrop:
- appendStringInfo(ctx->out, "cmdtype: Drop, ");
- break;
default:
appendStringInfo(ctx->out, "cmdtype: Invalid, ");
break;
diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml
index 75b395222b..10b3d5f5f1 100644
--- a/doc/src/sgml/logical-replication.sgml
+++ b/doc/src/sgml/logical-replication.sgml
@@ -1480,6 +1480,11 @@ test_sub=# SELECT * FROM t1 ORDER BY id;
</para>
<itemizedlist>
+ <listitem>
+ <para>
+ all: this option enables replication of all supported DDL commands.
+ </para>
+ </listitem>
<listitem>
<para>
table: this option enables replication of Table DDL commands,
@@ -1550,22 +1555,22 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
<tbody>
<row>
<entry align="left"><literal>ALTER AGGREGATE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER CAST</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER COLLATION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER CONVERSION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1575,12 +1580,12 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>ALTER DEFAULT PRIVILEGES</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER DOMAIN</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1590,22 +1595,22 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>ALTER EXTENSION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER FUNCTION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1615,7 +1620,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>ALTER LANGUAGE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1625,32 +1630,32 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>ALTER MATERIALIZED VIEW</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER OPERATOR</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER POLICY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER PROCEDURE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1665,32 +1670,32 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>ALTER ROUTINE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER RULER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER SCHEMA</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER SEQUENCE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER SERVER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER STATISTICS</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1715,22 +1720,22 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1740,22 +1745,22 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>ALTER TRIGGER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER TYPE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER USER MAPPING</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>ALTER VIEW</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1800,7 +1805,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>COMMENT</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1825,32 +1830,32 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>CREATE ACCESS METHOD</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE AGGREGATE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE CAST</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE COLLATION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE CONSTRAINT</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE CONVERSION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1860,7 +1865,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>CREATE DOMAIN</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1870,22 +1875,22 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>CREATE EXTENSION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE FUNCTION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1895,37 +1900,37 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>CREATE LANGUAGE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE MATERIALIZED VIEW</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE OPERATOR</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE POLICY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE PROCEDURE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1940,27 +1945,27 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>CREATE RULE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE SCHEMA</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE SEQUENCE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE SERVER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE STATISTICS</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -1985,47 +1990,47 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE TRANSFORM</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE TRIGGER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE TYPE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE USER MAPPING</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>CREATE VIEW</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2055,7 +2060,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DISCARD ALL</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2080,32 +2085,32 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DROP ACCESS METHOD</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP AGGREGATE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP CAST</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP COLLATION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP CONSTRAINT</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP CONVERSION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2115,7 +2120,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DROP DOMAIN</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2125,22 +2130,22 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DROP EXTENSION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP FOREIGN TABLE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP FUNCTION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2150,27 +2155,27 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DROP LANGUAGE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP MATERIALIZED VIEW</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP OPERATOR</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP OPERATOR CLASS</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2180,12 +2185,12 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DROP POLICY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP PROCEDURE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2200,32 +2205,32 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DROP ROUTINE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP RULE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP SCHEMA</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP SEQUENCE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP SERVER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP STATISTICS</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2245,47 +2250,47 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP TRANSFORM</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP TRIGGER</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP TYPE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP USER MAPPING</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
<entry align="left"><literal>DROP VIEW</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2305,7 +2310,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>GRANT</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2315,7 +2320,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>IMPORT FOREIGN SCHEMA</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2370,7 +2375,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>REFRESH MATERIALIZED VIEW</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2390,7 +2395,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>REVOKE</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
@@ -2415,7 +2420,7 @@ ALTER PUBLICATION mypub SET (ddl = 'table');
</row>
<row>
<entry align="left"><literal>SECURITY LABEL</literal></entry>
- <entry align="center"><literal>-</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
<entry align="left"></entry>
</row>
<row>
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 45cdcd3dc6..4b5a3ba950 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -129,7 +129,6 @@ static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
AclMode *col_privileges,
int num_col_privileges);
static AclMode string_to_privilege(const char *privname);
-static const char *privilege_to_string(AclMode privilege);
static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
bool all_privs, AclMode privileges,
Oid objectId, Oid grantorId,
@@ -385,11 +384,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
ListCell *cell;
const char *errormsg;
AclMode all_privileges;
+ Oid grantor = InvalidOid;
if (stmt->grantor)
{
- Oid grantor;
-
grantor = get_rolespec_oid(stmt->grantor, false);
/*
@@ -408,6 +406,9 @@ ExecuteGrantStmt(GrantStmt *stmt)
istmt.is_grant = stmt->is_grant;
istmt.objtype = stmt->objtype;
+ /* Copy the grantor id needed for DDL deparsing of Grant */
+ istmt.grantor_uid = grantor;
+
/* Collect the OIDs of the target objects */
switch (stmt->targtype)
{
@@ -2622,7 +2623,7 @@ string_to_privilege(const char *privname)
return 0; /* appease compiler */
}
-static const char *
+const char *
privilege_to_string(AclMode privilege)
{
switch (privilege)
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 1a3105e025..bb8ee605bd 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -1028,6 +1028,7 @@ GetPublication(Oid pubid)
pub->pubactions.pubupdate = pubform->pubupdate;
pub->pubactions.pubdelete = pubform->pubdelete;
pub->pubactions.pubtruncate = pubform->pubtruncate;
+ pub->pubactions.pubddl_all = pubform->pubddl_all;
pub->pubactions.pubddl_table = pubform->pubddl_table;
pub->pubactions.pubddl_index = pubform->pubddl_index;
pub->pubviaroot = pubform->pubviaroot;
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index c91fe66d9b..285a5dcf8a 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -50,9 +50,21 @@ typedef struct
/*
* CREATE COLLATION
+ *
+ * pstate: parse state.
+ * names: qualified collation names (a list of String).
+ * parameters: collation attributes (a list of DefElem).
+ * if_not_exists: if true, don't fail on duplicate name, just print a notice
+ * and return InvalidOid.
+ * from_existing_collid: output argument which, if not NULL, is set to the
+ * address of existing collation that was used to create in case of
+ * CREATE COLLATION any_name FROM existing_collation.
+ *
+ * If successful, returns the address of the new collation.
*/
ObjectAddress
-DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists)
+DefineCollation(ParseState *pstate, List *names, List *parameters,
+ bool if_not_exists, ObjectAddress *from_existing_collid)
{
char *collName;
Oid collNamespace;
@@ -143,6 +155,13 @@ DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_e
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for collation %u", collid);
+ /*
+ * Make from existing collationid available to callers for statements
+ * such as CREATE COLLATION any_name FROM existing_collation.
+ */
+ if (from_existing_collid && OidIsValid(collid))
+ ObjectAddressSet(*from_existing_collid, CollationRelationId, collid);
+
collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
collisdeterministic = ((Form_pg_collation) GETSTRUCT(tp))->collisdeterministic;
collencoding = ((Form_pg_collation) GETSTRUCT(tp))->collencoding;
diff --git a/src/backend/commands/ddl_deparse.c b/src/backend/commands/ddl_deparse.c
index 5d85070a85..fa4ef7b971 100644
--- a/src/backend/commands/ddl_deparse.c
+++ b/src/backend/commands/ddl_deparse.c
@@ -59,6 +59,8 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_publication_rel.h"
+#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_sequence.h"
@@ -71,6 +73,7 @@
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
+#include "commands/publicationcmds.h"
#include "commands/sequence.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
@@ -159,6 +162,7 @@ bool verbose = true;
static void append_array_object(ObjTree *tree, char *sub_fmt, List *array);
static void append_bool_object(ObjTree *tree, char *sub_fmt, bool value);
+static void append_float_object(ObjTree *tree, char *sub_fmt, float8 value);
static void append_null_object(ObjTree *tree, char *sub_fmt);
static void append_object_object(ObjTree *tree, char *sub_fmt, ObjTree *value);
static char *append_object_to_format_string(ObjTree *tree, char *sub_fmt);
@@ -168,11 +172,21 @@ static void append_string_object(ObjTree *tree, char *sub_fmt, char *name,
static void format_type_detailed(Oid type_oid, int32 typemod,
Oid *nspid, char **typname, char **typemodstr,
bool *typarray);
+static List *FunctionGetDefaults(text *proargdefaults);
static ObjElem *new_object(ObjType type, char *name);
static ObjTree *new_objtree_for_qualname_id(Oid classId, Oid objectId);
+static ObjTree *new_objtree_for_rolespec(RoleSpec *spec);
static ObjElem *new_object_object(ObjTree *value);
static ObjTree *new_objtree_VA(char *fmt, int numobjs,...);
+static ObjTree *new_objtree(char *fmt);
+static ObjElem *new_string_object(char *value);
static JsonbValue *objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state, char *owner);
+static void pg_get_indexdef_detailed(Oid indexrelid,
+ char **index_am,
+ char **definition,
+ char **reloptions,
+ char **tablespace,
+ char **whereClause);
static char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
List *dpcontext, List **exprs);
@@ -180,8 +194,18 @@ static ObjTree *deparse_ColumnDef(Relation relation, List *dpcontext, bool compo
ColumnDef *coldef, bool is_alter, List **exprs);
static ObjTree *deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table);
static ObjTree *deparse_ColumnSetOptions(AlterTableCmd *subcmd);
+static ObjTree *deparse_DefineStmt_Aggregate(Oid objectId, DefineStmt *define);
+static ObjTree *deparse_DefineStmt_Collation(Oid objectId, DefineStmt *define,
+ ObjectAddress fromCollid);
+static ObjTree *deparse_DefineStmt_Operator(Oid objectId, DefineStmt *define);
+static ObjTree *deparse_DefineStmt_Type(Oid objectId, DefineStmt *define);
+static ObjTree *deparse_DefineStmt_TSConfig(Oid objectId, DefineStmt *define, ObjectAddress copied);
+static ObjTree *deparse_DefineStmt_TSParser(Oid objectId, DefineStmt *define);
+static ObjTree *deparse_DefineStmt_TSDictionary(Oid objectId, DefineStmt *define);
+static ObjTree *deparse_DefineStmt_TSTemplate(Oid objectId, DefineStmt *define);
static ObjTree *deparse_DefElem(DefElem *elem, bool is_reset);
+static ObjTree *deparse_FunctionSet(VariableSetKind kind, char *name, char *value);
static ObjTree *deparse_OnCommitClause(OnCommitAction option);
static ObjTree *deparse_RelSetOptions(AlterTableCmd *subcmd);
@@ -190,6 +214,7 @@ static inline ObjElem *deparse_Seq_Cycle(Form_pg_sequence seqdata, bool alter_ta
static inline ObjElem *deparse_Seq_IncrementBy(Form_pg_sequence seqdata, bool alter_table);
static inline ObjElem *deparse_Seq_Minvalue(Form_pg_sequence seqdata, bool alter_table);
static inline ObjElem *deparse_Seq_Maxvalue(Form_pg_sequence seqdata, bool alter_table);
+static ObjElem *deparse_Seq_OwnedBy(Oid sequenceId, bool alter_table);
static inline ObjElem *deparse_Seq_Restart(int64 last_value);
static inline ObjElem *deparse_Seq_Startwith(Form_pg_sequence seqdata, bool alter_table);
static inline ObjElem *deparse_Seq_As(Form_pg_sequence seqdata);
@@ -201,10 +226,15 @@ static inline ObjElem *deparse_Type_Typmod_Out(Form_pg_type typForm);
static inline ObjElem *deparse_Type_Analyze(Form_pg_type typForm);
static inline ObjElem *deparse_Type_Subscript(Form_pg_type typForm);
+static ObjTree *deparse_FdwOptions(List *options, char *colname,
+ bool altercoloptions);
+
static List *deparse_InhRelations(Oid objectId);
static List *deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
bool typed, bool composite);
+static char *DomainGetDefault(HeapTuple domTup, bool missing_ok);
+
/*
* Append present as false to a tree.
*/
@@ -214,6 +244,42 @@ append_not_present(ObjTree *tree)
append_bool_object(tree, "present", false);
}
+/*
+ * Append an int32 parameter to a tree.
+ */
+static void
+append_int_object(ObjTree *tree, char *sub_fmt, int32 value)
+{
+ ObjElem *param;
+ char *object_name;
+
+ Assert(sub_fmt);
+
+ object_name = append_object_to_format_string(tree, sub_fmt);
+
+ param = new_object(ObjTypeInteger, object_name);
+ param->value.flt = value;
+ append_premade_object(tree, param);
+}
+
+/*
+ * Append a float8 parameter to a tree.
+ */
+static void
+append_float_object(ObjTree *tree, char *sub_fmt, float8 value)
+{
+ ObjElem *param;
+ char *object_name;
+
+ Assert(sub_fmt);
+
+ object_name = append_object_to_format_string(tree, sub_fmt);
+
+ param = new_object(ObjTypeFloat, object_name);
+ param->value.flt = value;
+ append_premade_object(tree, param);
+}
+
/*
* Append an array parameter to a tree.
*/
@@ -420,6 +486,34 @@ append_string_object(ObjTree *tree, char *sub_fmt, char * object_name,
append_premade_object(tree, param);
}
+/*
+ * Append a NULL-or-quoted-literal clause. Useful for COMMENT and SECURITY
+ * LABEL.
+ *
+ * Verbose syntax
+ * %{null}s %{literal}s
+ */
+static void
+append_literal_or_null(ObjTree *parent, char *elemname, char *value)
+{
+ ObjTree *top;
+ ObjTree *part;
+
+ top = new_objtree("");
+ part = new_objtree_VA("NULL", 1,
+ "present", ObjTypeBool, !value);
+ append_object_object(top, "%{null}s", part);
+
+ part = new_objtree_VA("", 1,
+ "present", ObjTypeBool, value != NULL);
+
+ if (value)
+ append_string_object(part, "%{value}L", "value", value);
+ append_object_object(top, "%{literal}s", part);
+
+ append_object_object(parent, elemname, top);
+}
+
/*
* Similar to format_type_extended, except we return each bit of information
* separately:
@@ -522,6 +616,31 @@ format_type_detailed(Oid type_oid, int32 typemod,
ReleaseSysCache(tuple);
}
+/*
+ * Return the default values of arguments to a function, as a list of
+ * deparsed expressions.
+ */
+static List *
+FunctionGetDefaults(text *proargdefaults)
+{
+ List *nodedefs;
+ List *strdefs = NIL;
+ ListCell *cell;
+
+ nodedefs = (List *) stringToNode(text_to_cstring(proargdefaults));
+ if (!IsA(nodedefs, List))
+ elog(ERROR, "proargdefaults is not a list");
+
+ foreach(cell, nodedefs)
+ {
+ Node *onedef = lfirst(cell);
+
+ strdefs = lappend(strdefs, deparse_expression(onedef, NIL, false, false));
+ }
+
+ return strdefs;
+}
+
/*
* Return the string representation of the given RELPERSISTENCE value.
*/
@@ -711,6 +830,57 @@ new_objtree_for_type(Oid typeId, int32 typmod)
"typarray", ObjTypeBool, type_array);
}
+/*
+ * Helper routine for %{}R objects, with role specified by RoleSpec node.
+ * Special values such as ROLESPEC_CURRENT_USER are expanded to their final
+ * names.
+ */
+static ObjTree *
+new_objtree_for_rolespec(RoleSpec *spec)
+{
+ char *roletype;
+
+ if (spec->roletype != ROLESPEC_PUBLIC)
+ roletype = get_rolespec_name(spec);
+ else
+ roletype = pstrdup("");
+
+ return new_objtree_VA(NULL, 2,
+ "is_public", ObjTypeBool, spec->roletype == ROLESPEC_PUBLIC,
+ "rolename", ObjTypeString, roletype);
+}
+
+/*
+ * Helper routine for %{}R objects, with role specified by OID. (ACL_ID_PUBLIC
+ * means to use "public").
+ */
+static ObjTree *
+new_objtree_for_role_id(Oid roleoid)
+{
+ ObjTree *role;
+
+ if (roleoid != ACL_ID_PUBLIC)
+ {
+ HeapTuple roltup;
+ char *role_name;
+
+ roltup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleoid));
+ if (!HeapTupleIsValid(roltup))
+ elog(ERROR, "cache lookup failed for role with OID %u", roleoid);
+
+ role_name = NameStr(((Form_pg_authid) GETSTRUCT(roltup))->rolname);
+ role = new_objtree_VA("%{rolename}I", 1,
+ "rolename", ObjTypeString, pstrdup(role_name));
+
+ ReleaseSysCache(roltup);
+ }
+ else
+ role = new_objtree_VA("%{rolename}I", 1,
+ "rolename", ObjTypeString, "public");
+
+ return role;
+}
+
/*
* Allocate a new object tree to store parameter values -- varargs version.
*
@@ -788,6 +958,22 @@ new_objtree_VA(char *fmt, int numobjs,...)
return tree;
}
+/*
+ * Allocate a new string object.
+ */
+static ObjElem *
+new_string_object(char *value)
+{
+ ObjElem *param;
+
+ Assert(value);
+
+ param = new_object(ObjTypeString, NULL);
+ param->value.string = value;
+
+ return param;
+}
+
/*
* Process the pre-built format string from the ObjTree into the output parse
* state.
@@ -1950,6 +2136,83 @@ deparse_Seq_Minvalue(Form_pg_sequence seqdata, bool alter_table)
return new_object_object(ret);
}
+/*
+ * Deparse the sequence OWNED BY command.
+ *
+ * Verbose syntax
+ * OWNED BY %{owner}D
+ */
+static ObjElem *
+deparse_Seq_OwnedBy(Oid sequenceId, bool alter_table)
+{
+ ObjTree *ret = NULL;
+ Relation depRel;
+ SysScanDesc scan;
+ ScanKeyData keys[3];
+ HeapTuple tuple;
+
+ depRel = table_open(DependRelationId, AccessShareLock);
+ ScanKeyInit(&keys[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&keys[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(sequenceId));
+ ScanKeyInit(&keys[2],
+ Anum_pg_depend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(0));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 3, keys);
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ Oid ownerId;
+ Form_pg_depend depform;
+ ObjTree *tmp_obj;
+ char *colname;
+
+ depform = (Form_pg_depend) GETSTRUCT(tuple);
+
+ /* Only consider AUTO dependencies on pg_class */
+ if (depform->deptype != DEPENDENCY_AUTO)
+ continue;
+ if (depform->refclassid != RelationRelationId)
+ continue;
+ if (depform->refobjsubid <= 0)
+ continue;
+
+ ownerId = depform->refobjid;
+ colname = get_attname(ownerId, depform->refobjsubid, false);
+ if (colname == NULL)
+ continue;
+
+ tmp_obj = new_objtree_for_qualname_id(RelationRelationId, ownerId);
+ append_string_object(tmp_obj, "attrname", "attrname", colname);
+ ret = new_objtree_VA("OWNED BY %{owner}D", 2,
+ "clause", ObjTypeString, "owned",
+ "owner", ObjTypeObject, tmp_obj);
+ }
+
+ systable_endscan(scan);
+ relation_close(depRel, AccessShareLock);
+
+ /*
+ * If there's no owner column, emit an empty OWNED BY element, set up so
+ * that it won't print anything.
+ */
+ if (!ret)
+ /* XXX this shouldn't happen ... */
+ ret = new_objtree_VA("OWNED BY %{owner}D", 3,
+ "clause", ObjTypeString, "owned",
+ "owner", ObjTypeNull,
+ "present", ObjTypeBool, false);
+
+ return new_object_object(ret);
+}
+
/*
* Deparse the sequence RESTART option.
*
@@ -2214,6 +2477,64 @@ deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
return elements;
}
+/*
+ * Deparse a CreateSeqStmt.
+ *
+ * Given a sequence OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE %{persistence}s SEQUENCE %{identity}D %{definition: }s
+ */
+static ObjTree *
+deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
+{
+ ObjTree *ret;
+ Relation relation;
+ List *elems = NIL;
+ Form_pg_sequence seqform;
+ Sequence_values *seqvalues;
+ CreateSeqStmt *createSeqStmt = (CreateSeqStmt *) parsetree;
+
+ /*
+ * Sequence for IDENTITY COLUMN output separately (via CREATE TABLE or
+ * ALTER TABLE); return empty here.
+ */
+ if (createSeqStmt->for_identity)
+ return NULL;
+
+ seqvalues = get_sequence_values(objectId);
+ seqform = seqvalues->seqform;
+
+ /* Definition elements */
+ elems = lappend(elems, deparse_Seq_Cache(seqform, false));
+ elems = lappend(elems, deparse_Seq_Cycle(seqform, false));
+ elems = lappend(elems, deparse_Seq_IncrementBy(seqform, false));
+ elems = lappend(elems, deparse_Seq_Minvalue(seqform, false));
+ elems = lappend(elems, deparse_Seq_Maxvalue(seqform, false));
+ elems = lappend(elems, deparse_Seq_Startwith(seqform, false));
+ elems = lappend(elems, deparse_Seq_Restart(seqvalues->last_value));
+ elems = lappend(elems, deparse_Seq_As(seqform));
+
+ /* We purposefully do not emit OWNED BY here */
+
+ relation = relation_open(objectId, AccessShareLock);
+
+ ret = new_objtree_VA("CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s", 4,
+ "persistence", ObjTypeString,
+ get_persistence_str(relation->rd_rel->relpersistence),
+ "if_not_exists", ObjTypeString,
+ createSeqStmt->if_not_exists ? "IF NOT EXISTS" : "",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(relation->rd_rel->relnamespace,
+ RelationGetRelationName(relation)),
+ "definition", ObjTypeArray, elems);
+
+ relation_close(relation, AccessShareLock);
+
+ return ret;
+}
+
/*
* Deparse an IndexStmt.
*
@@ -2559,7 +2880,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
Assert(cmd->type == SCT_AlterTable);
stmt = (AlterTableStmt *) cmd->parsetree;
- Assert(IsA(stmt, AlterTableStmt));
+ Assert(IsA(stmt, AlterTableStmt) || IsA(stmt, ViewStmt));
/*
* ALTER TABLE subcommands generated for TableLikeClause is processed in
@@ -2602,8 +2923,10 @@ deparse_AlterRelation(CollectedCommand *cmd)
elog(ERROR, "unexpected relkind %d", rel->rd_rel->relkind);
}
- ret = new_objtree_VA("ALTER %{objtype}s %{identity}D", 2,
+ ret = new_objtree_VA("ALTER %{objtype}s %{identity}D", 3,
"objtype", ObjTypeString, reltype,
+ "only", ObjTypeString,
+ stmt->relation->inh ? "" : "ONLY",
"identity", ObjTypeObject,
new_objtree_for_qualname(rel->rd_rel->relnamespace,
RelationGetRelationName(rel)));
@@ -3035,13 +3358,12 @@ deparse_AlterRelation(CollectedCommand *cmd)
}
break;
-#ifdef TODOLIST
case AT_AlterColumnGenericOptions:
tmp_obj = deparse_FdwOptions((List *) subcmd->def,
- subcmd->name);
+ subcmd->name, true);
subcmds = lappend(subcmds, new_object_object(tmp_obj));
break;
-#endif
+
case AT_ChangeOwner:
tmp_obj = new_objtree_VA("OWNER TO %{owner}I", 2,
"type", ObjTypeString, "change owner",
@@ -3432,6 +3754,200 @@ deparse_drop_command(const char *objidentity, const char *objecttype,
return command;
}
+/*
+ * Deparse an AlterCollationStmt (ALTER COLLATION)
+ *
+ * Given a collation OID and the parse tree that created it, return an ObjTree
+ * representing the alter command.
+ *
+ * Verbose syntax:
+ * ALTER COLLATION %{identity}O REFRESH VERSION
+ */
+static ObjTree *
+deparse_AlterCollation(Oid objectId, Node *parsetree)
+{
+ ObjTree *ret;
+ HeapTuple colTup;
+ Form_pg_collation colForm;
+
+ colTup = SearchSysCache1(COLLOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(colTup))
+ elog(ERROR, "cache lookup failed for collation with OID %u", objectId);
+ colForm = (Form_pg_collation) GETSTRUCT(colTup);
+
+ ret = new_objtree_VA("ALTER COLLATION %{identity}O REFRESH VERSION", 1,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(colForm->collnamespace,
+ NameStr(colForm->collname)));
+
+ ReleaseSysCache(colTup);
+
+ return ret;
+}
+
+/*
+ * Handle deparsing setting of Function
+ *
+ * Verbose syntax
+ * RESET ALL
+ * OR
+ * SET %{set_name}I TO %{set_value}s
+ * OR
+ * RESET %{set_name}I
+ */
+static ObjTree *
+deparse_FunctionSet(VariableSetKind kind, char *name, char *value)
+{
+ ObjTree *ret;
+
+ if (kind == VAR_RESET_ALL)
+ ret = new_objtree("RESET ALL");
+ else if (kind == VAR_SET_VALUE)
+ {
+ ret = new_objtree_VA("SET %{set_name}I", 1,
+ "set_name", ObjTypeString, name);
+
+ /*
+ * Some GUC variable names are 'LIST' type and hence must not be
+ * quoted.
+ */
+ if (GetConfigOptionFlags(name, true) & GUC_LIST_QUOTE)
+ append_string_object(ret, "TO %{set_value}s", "set_value", value);
+ else
+ append_string_object(ret, "TO %{set_value}L", "set_value", value);
+ }
+ else
+ ret = new_objtree_VA("RESET %{set_name}I", 1,
+ "set_name", ObjTypeString, name);
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterFunctionStmt (ALTER FUNCTION/ROUTINE/PROCEDURE)
+ *
+ * Given a function OID and the parse tree that created it, return an ObjTree
+ * representing the alter command.
+ *
+ * Verbose syntax:
+ * ALTER FUNCTION/ROUTINE/PROCEDURE %{signature}s %{definition: }s
+ */
+static ObjTree *
+deparse_AlterFunction(Oid objectId, Node *parsetree)
+{
+ AlterFunctionStmt *node = (AlterFunctionStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *sign;
+ HeapTuple procTup;
+ Form_pg_proc procForm;
+ List *params = NIL;
+ List *elems = NIL;
+ ListCell *cell;
+ int i;
+
+ /* Get the pg_proc tuple */
+ procTup = SearchSysCache1(PROCOID, objectId);
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for function with OID %u", objectId);
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+ if (procForm->prokind == PROKIND_PROCEDURE)
+ ret = new_objtree("ALTER PROCEDURE");
+ else
+ ret = new_objtree(node->objtype == OBJECT_ROUTINE ?
+ "ALTER ROUTINE" : "ALTER FUNCTION");
+
+ /*
+ * ALTER FUNCTION does not change signature so we can use catalog to get
+ * input type Oids.
+ */
+ for (i = 0; i < procForm->pronargs; i++)
+ {
+ ObjTree *tmp_obj;
+
+ tmp_obj = new_objtree_VA("%{type}T", 1,
+ "type", ObjTypeObject,
+ new_objtree_for_type(procForm->proargtypes.values[i], -1));
+ params = lappend(params, new_object_object(tmp_obj));
+ }
+
+ sign = new_objtree_VA("%{identity}D (%{arguments:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId, objectId),
+ "arguments", ObjTypeArray, params);
+
+ append_object_object(ret, "%{signature}s", sign);
+
+ foreach(cell, node->actions)
+ {
+ DefElem *defel = (DefElem *) lfirst(cell);
+ ObjTree *tmp_obj = NULL;
+
+ if (strcmp(defel->defname, "volatility") == 0)
+ tmp_obj = new_objtree(strVal(defel->arg));
+ else if (strcmp(defel->defname, "strict") == 0)
+ tmp_obj = new_objtree(boolVal(defel->arg) ?
+ "RETURNS NULL ON NULL INPUT" :
+ "CALLED ON NULL INPUT");
+ else if (strcmp(defel->defname, "security") == 0)
+ tmp_obj = new_objtree(boolVal(defel->arg) ?
+ "SECURITY DEFINER" : "SECURITY INVOKER");
+ else if (strcmp(defel->defname, "leakproof") == 0)
+ tmp_obj = new_objtree(boolVal(defel->arg) ?
+ "LEAKPROOF" : "NOT LEAKPROOF");
+ else if (strcmp(defel->defname, "cost") == 0)
+ tmp_obj = new_objtree_VA("COST %{cost}n", 1,
+ "cost", ObjTypeFloat,
+ defGetNumeric(defel));
+ else if (strcmp(defel->defname, "rows") == 0)
+ {
+ tmp_obj = new_objtree("ROWS");
+ if (defGetNumeric(defel) == 0)
+ append_not_present(tmp_obj);
+ else
+ append_float_object(tmp_obj, "%{rows}n",
+ defGetNumeric(defel));
+ }
+ else if (strcmp(defel->defname, "set") == 0)
+ {
+ VariableSetStmt *sstmt = (VariableSetStmt *) defel->arg;
+ char *value = ExtractSetVariableArgs(sstmt);
+
+ tmp_obj = deparse_FunctionSet(sstmt->kind, sstmt->name, value);
+ }
+ else if (strcmp(defel->defname, "support") == 0)
+ {
+ Oid argtypes[1];
+
+ tmp_obj = new_objtree("SUPPORT");
+
+ Assert(procForm->prosupport);
+
+ /*
+ * We should qualify the support function's name if it wouldn't be
+ * resolved by lookup in the current search path.
+ */
+ argtypes[0] = INTERNALOID;
+ append_string_object(tmp_obj, "%{name}s", "name",
+ generate_function_name(procForm->prosupport, 1,
+ NIL, argtypes,
+ false, NULL,
+ EXPR_KIND_NONE));
+ }
+ else if (strcmp(defel->defname, "parallel") == 0)
+ tmp_obj = new_objtree_VA("PARALLEL %{value}s", 1,
+ "value", ObjTypeString, strVal(defel->arg));
+
+ elems = lappend(elems, new_object_object(tmp_obj));
+ }
+
+ append_array_object(ret, "%{definition: }s", elems);
+
+ ReleaseSysCache(procTup);
+
+ return ret;
+}
+
/*
* Deparse an AlterObjectSchemaStmt (ALTER ... SET SCHEMA command)
*
@@ -3477,22 +3993,499 @@ deparse_AlterObjectSchemaStmt(ObjectAddress address, Node *parsetree,
}
/*
- * Deparse an AlterOwnerStmt (ALTER ... OWNER TO ...).
- *
- * Given the object address and the parse tree that created it, return an
- * ObjTree representing the alter command.
+ * Deparse a GRANT/REVOKE command.
*
* Verbose syntax
- * ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I
+ * GRANT %{privileges:, }s ON" %{objtype}s %{privtarget:, }s TO %{grantees:, }s
+ * %{grant_option}s GRANTED BY %{rolename}s
+ * OR
+ * REVOKE %{privileges:, }s ON" %{objtype}s %{privtarget:, }s
+ * FROM %{grantees:, }s %{grant_option}s %{cascade}s
*/
static ObjTree *
-deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
+deparse_GrantStmt(CollectedCommand *cmd)
{
- AlterOwnerStmt *node = (AlterOwnerStmt *) parsetree;
+ InternalGrant *istmt;
+ ObjTree *ret;
+ List *list = NIL;
+ ListCell *cell;
+ Oid classId;
+ ObjTree *tmp;
- return new_objtree_VA("ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I", 3,
- "objtype", ObjTypeString,
- stringify_objtype(node->objectType, false),
+ /* Don't deparse SQL commands generated while creating extension */
+ if (cmd->in_extension)
+ return NULL;
+
+ istmt = cmd->d.grant.istmt;
+
+ /*
+ * If there are no objects from "ALL ... IN SCHEMA" to be granted, then
+ * nothing to do.
+ */
+ if (istmt->objects == NIL)
+ return NULL;
+
+ switch (istmt->objtype)
+ {
+ case OBJECT_COLUMN:
+ case OBJECT_TABLE:
+ case OBJECT_SEQUENCE:
+ classId = RelationRelationId;
+ break;
+ case OBJECT_DOMAIN:
+ case OBJECT_TYPE:
+ classId = TypeRelationId;
+ break;
+ case OBJECT_FDW:
+ classId = ForeignDataWrapperRelationId;
+ break;
+ case OBJECT_FOREIGN_SERVER:
+ classId = ForeignServerRelationId;
+ break;
+ case OBJECT_FUNCTION:
+ case OBJECT_PROCEDURE:
+ case OBJECT_ROUTINE:
+ classId = ProcedureRelationId;
+ break;
+ case OBJECT_LANGUAGE:
+ classId = LanguageRelationId;
+ break;
+ case OBJECT_LARGEOBJECT:
+ classId = LargeObjectRelationId;
+ break;
+ case OBJECT_SCHEMA:
+ classId = NamespaceRelationId;
+ break;
+ case OBJECT_DATABASE:
+ case OBJECT_TABLESPACE:
+ classId = InvalidOid;
+ elog(ERROR, "global objects not supported");
+ break;
+ default:
+ elog(ERROR, "invalid OBJECT value %d", istmt->objtype);
+ }
+
+ /* GRANT TO or REVOKE FROM */
+ ret = new_objtree(istmt->is_grant ? "GRANT" : "REVOKE");
+
+ /* Build a list of privileges to grant/revoke */
+ if (istmt->all_privs)
+ {
+ tmp = new_objtree("ALL PRIVILEGES");
+ list = list_make1(new_object_object(tmp));
+ }
+ else
+ {
+ char *priv;
+ if (istmt->privileges & ACL_INSERT)
+ {
+ priv = (char *)privilege_to_string(ACL_INSERT);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_SELECT)
+ {
+ priv = (char *)privilege_to_string(ACL_SELECT);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_UPDATE)
+ {
+ priv = (char *)privilege_to_string(ACL_UPDATE);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_DELETE)
+ {
+ priv = (char *)privilege_to_string(ACL_DELETE);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_TRUNCATE)
+ {
+ priv = (char *)privilege_to_string(ACL_TRUNCATE);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_REFERENCES)
+ {
+ priv = (char *)privilege_to_string(ACL_REFERENCES);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_TRIGGER)
+ {
+ priv = (char *)privilege_to_string(ACL_TRIGGER);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_EXECUTE)
+ {
+ priv = (char *)privilege_to_string(ACL_EXECUTE);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_USAGE)
+ {
+ priv = (char *)privilege_to_string(ACL_USAGE);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_CREATE)
+ {
+ priv = (char *)privilege_to_string(ACL_CREATE);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_CREATE_TEMP)
+ {
+ priv = (char *)privilege_to_string(ACL_CREATE_TEMP);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_CONNECT)
+ {
+ priv = (char *)privilege_to_string(ACL_CONNECT);
+ list = lappend(list, new_string_object(priv));
+ }
+ if (istmt->privileges & ACL_MAINTAIN)
+ {
+ priv = (char *)privilege_to_string(ACL_MAINTAIN);
+ list = lappend(list, new_string_object(priv));
+ }
+
+ if (!istmt->is_grant && istmt->grant_option)
+ append_string_object(ret, "%{grant_option}s", "grant_option",
+ istmt->grant_option ? "GRANT OPTION FOR" : "");
+
+ if (istmt->col_privs != NIL)
+ {
+ ListCell *ocell;
+
+ foreach(ocell, istmt->col_privs)
+ {
+ AccessPriv *priv = lfirst(ocell);
+ List *cols = NIL;
+
+ foreach(cell, priv->cols)
+ {
+ String *colname = lfirst_node(String, cell);
+
+ cols = lappend(cols,
+ new_string_object(strVal(colname)));
+ }
+
+ tmp = new_objtree_VA("(%{cols:, }s) %{priv}s", 2,
+ "cols", ObjTypeArray, cols,
+ "priv", ObjTypeString,
+ priv->priv_name ? priv->priv_name : "ALL PRIVILEGES");
+
+ list = lappend(list, new_object_object(tmp));
+ }
+ }
+ }
+ append_array_object(ret, "%{privileges:, }s ON", list);
+
+ append_string_object(ret, "%{objtype}s", "objtype",
+ (char *)stringify_objtype(istmt->objtype, true));
+
+ /* Target objects. We use object identities here */
+ list = NIL;
+ foreach(cell, istmt->objects)
+ {
+ Oid objid = lfirst_oid(cell);
+ ObjectAddress addr;
+
+ addr.classId = classId;
+ addr.objectId = objid;
+ addr.objectSubId = 0;
+
+ tmp = new_objtree_VA("%{identity}s", 1,
+ "identity", ObjTypeString,
+ getObjectIdentity(&addr, false));
+
+ list = lappend(list, new_object_object(tmp));
+ }
+ append_array_object(ret, "%{privtarget:, }s", list);
+
+ append_format_string(ret, istmt->is_grant ? "TO" : "FROM");
+
+ /* List of grantees */
+ list = NIL;
+ foreach(cell, istmt->grantees)
+ {
+ Oid grantee = lfirst_oid(cell);
+
+ tmp = new_objtree_for_role_id(grantee);
+ list = lappend(list, new_object_object(tmp));
+ }
+
+ append_array_object(ret, "%{grantees:, }s", list);
+
+ /* The wording of the grant option is variable ... */
+ if (istmt->is_grant)
+ append_string_object(ret, "%{grant_option}s", "grant_option",
+ istmt->grant_option ? "WITH GRANT OPTION" : "");
+
+ if (istmt->grantor_uid)
+ {
+ HeapTuple roltup;
+ char *rolename;
+
+ roltup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(istmt->grantor_uid));
+ if (!HeapTupleIsValid(roltup))
+ elog(ERROR, "cache lookup failed for role with OID %u",
+ istmt->grantor_uid);
+
+ rolename = NameStr(((Form_pg_authid) GETSTRUCT(roltup))->rolname);
+ append_string_object(ret, "GRANTED BY %{rolename}s",
+ "rolename", rolename);
+ ReleaseSysCache(roltup);
+ }
+
+ if (!istmt->is_grant)
+ append_string_object(ret, "%{cascade}s", "cascade",
+ istmt->behavior == DROP_CASCADE ? "CASCADE" : "");
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterOperatorStmt (ALTER OPERATOR ... SET ...).
+ *
+ * Given an operator OID and the parse tree that created it, return an ObjTree
+ * representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER OPERATOR %{identity}O (%{left_type}T, %{right_type}T)
+ * SET (%{elems:, }s)
+ */
+static ObjTree *
+deparse_AlterOperatorStmt(Oid objectId, Node *parsetree)
+{
+ HeapTuple oprTup;
+ AlterOperatorStmt *node = (AlterOperatorStmt *) parsetree;
+ ObjTree *ret;
+ Form_pg_operator oprForm;
+ ListCell *cell;
+ List *list = NIL;
+
+ oprTup = SearchSysCache1(OPEROID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(oprTup))
+ elog(ERROR, "cache lookup failed for operator with OID %u", objectId);
+ oprForm = (Form_pg_operator) GETSTRUCT(oprTup);
+
+ ret = new_objtree_VA("ALTER OPERATOR %{identity}O", 1,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(oprForm->oprnamespace,
+ NameStr(oprForm->oprname)));
+
+ /* LEFTARG */
+ if (OidIsValid(oprForm->oprleft))
+ append_object_object(ret, "(%{left_type}T",
+ new_objtree_for_type(oprForm->oprleft, -1));
+ else
+ append_string_object(ret, "(%{left_type}s", "left_type", "NONE");
+
+ /* RIGHTARG */
+ Assert(OidIsValid(oprForm->oprleft));
+ append_object_object(ret, ", %{right_type}T)",
+ new_objtree_for_type(oprForm->oprright, -1));
+
+ foreach(cell, node->options)
+ {
+ DefElem *elem = (DefElem *) lfirst(cell);
+ ObjTree *tmp_obj = NULL;
+
+ if (strcmp(elem->defname, "restrict") == 0)
+ {
+ tmp_obj = new_objtree_VA("RESTRICT=", 1,
+ "clause", ObjTypeString, "restrict");
+ if (OidIsValid(oprForm->oprrest))
+ append_object_object(tmp_obj, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ oprForm->oprrest));
+ else
+ append_string_object(tmp_obj, "%{procedure}s", "procedure",
+ "NONE");
+ }
+ else if (strcmp(elem->defname, "join") == 0)
+ {
+ tmp_obj = new_objtree_VA("JOIN=", 1,
+ "clause", ObjTypeString, "join");
+ if (OidIsValid(oprForm->oprjoin))
+ append_object_object(tmp_obj, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ oprForm->oprjoin));
+ else
+ append_string_object(tmp_obj, "%{procedure}s", "procedure",
+ "NONE");
+ }
+
+ Assert(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+ }
+
+ append_array_object(ret, "SET (%{elems:, }s)", list);
+
+ ReleaseSysCache(oprTup);
+
+ return ret;
+}
+
+/*
+ * Deparse an ALTER OPERATOR FAMILY ADD/DROP command.
+ *
+ * Given the CollectedCommand, return an ObjTree representing the Alter
+ * Operator command.
+ *
+ * Verbose syntax
+ * ALTER OPERATOR FAMILY %{identity}D USING %{amname}I ADD/DROP %{items:,}s
+ */
+static ObjTree *
+deparse_AlterOpFamily(CollectedCommand *cmd)
+{
+ ObjTree *ret;
+ AlterOpFamilyStmt *stmt = (AlterOpFamilyStmt *) cmd->parsetree;
+ HeapTuple ftp;
+ Form_pg_opfamily opfForm;
+ List *list = NIL;
+ ListCell *cell;
+
+ /* Don't deparse SQL commands generated while creating extension */
+ if (cmd->in_extension)
+ return NULL;
+
+ ftp = SearchSysCache1(OPFAMILYOID,
+ ObjectIdGetDatum(cmd->d.opfam.address.objectId));
+ if (!HeapTupleIsValid(ftp))
+ elog(ERROR, "cache lookup failed for operator family with OID %u",
+ cmd->d.opfam.address.objectId);
+ opfForm = (Form_pg_opfamily) GETSTRUCT(ftp);
+
+ ret = new_objtree_VA("ALTER OPERATOR FAMILY %{identity}D USING %{amname}I", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(opfForm->opfnamespace,
+ NameStr(opfForm->opfname)),
+ "amname", ObjTypeString, stmt->amname);
+
+ foreach(cell, cmd->d.opfam.operators)
+ {
+ OpFamilyMember *oper = lfirst(cell);
+ ObjTree *tmp_obj;
+
+ /*
+ * Verbose syntax
+ *
+ * OPERATOR %{num}n %{operator}O(%{ltype}T, %{rtype}T) %{purpose}s
+ */
+ tmp_obj = new_objtree_VA("OPERATOR %{num}n", 1,
+ "num", ObjTypeInteger, oper->number);
+
+ /* Add the operator name; the DROP case doesn't have this */
+ if (!stmt->isDrop)
+ append_object_object(tmp_obj, "%{operator}O",
+ new_objtree_for_qualname_id(OperatorRelationId,
+ oper->object));
+
+ /* Add the types */
+ append_object_object(tmp_obj, "(%{ltype}T,",
+ new_objtree_for_type(oper->lefttype, -1));
+ append_object_object(tmp_obj, "%{rtype}T)",
+ new_objtree_for_type(oper->righttype, -1));
+
+ /* Add the FOR SEARCH / FOR ORDER BY clause; not in the DROP case */
+ if (!stmt->isDrop)
+ {
+ if (oper->sortfamily == InvalidOid)
+ append_string_object(tmp_obj, "%{purpose}s", "purpose",
+ "FOR SEARCH");
+ else
+ {
+ ObjTree *orderby_obj;
+
+ orderby_obj = new_objtree_VA("FOR ORDER BY %{opfamily}D", 1,
+ "opfamily", ObjTypeObject,
+ new_objtree_for_qualname_id(OperatorFamilyRelationId,
+ oper->sortfamily));
+ append_object_object(tmp_obj, "%{purpose}s", orderby_obj);
+ }
+ }
+
+ list = lappend(list, new_object_object(tmp_obj));
+ }
+
+ foreach(cell, cmd->d.opfam.procedures)
+ {
+ OpFamilyMember *proc = lfirst(cell);
+ ObjTree *tmp_obj;
+
+ tmp_obj = new_objtree_VA("FUNCTION %{num}n (%{ltype}T, %{rtype}T)", 3,
+ "num", ObjTypeInteger, proc->number,
+ "ltype", ObjTypeObject,
+ new_objtree_for_type(proc->lefttype, -1),
+ "rtype", ObjTypeObject,
+ new_objtree_for_type(proc->righttype, -1));
+
+ /*
+ * Add the function name and arg types; the DROP case doesn't have
+ * this
+ */
+ if (!stmt->isDrop)
+ {
+ HeapTuple procTup;
+ Form_pg_proc procForm;
+ Oid *proargtypes;
+ List *arglist = NIL;
+ int i;
+
+ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc->object));
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for procedure with OID %u",
+ proc->object);
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+ proargtypes = procForm->proargtypes.values;
+ for (i = 0; i < procForm->pronargs; i++)
+ {
+ ObjTree *arg;
+
+ arg = new_objtree_for_type(proargtypes[i], -1);
+ arglist = lappend(arglist, new_object_object(arg));
+ }
+
+ append_object_object(tmp_obj, "%{function}D",
+ new_objtree_for_qualname(procForm->pronamespace,
+ NameStr(procForm->proname)));
+
+ append_format_string(tmp_obj, "(");
+ append_array_object(tmp_obj, "%{argtypes:, }T", arglist);
+ append_format_string(tmp_obj, ")");
+
+ ReleaseSysCache(procTup);
+ }
+
+ list = lappend(list, new_object_object(tmp_obj));
+ }
+
+ if (stmt->isDrop)
+ append_format_string(ret, "DROP");
+ else
+ append_format_string(ret, "ADD");
+
+ append_array_object(ret, "%{items:, }s", list);
+
+ ReleaseSysCache(ftp);
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterOwnerStmt (ALTER ... OWNER TO ...).
+ *
+ * Given the object address and the parse tree that created it, return an
+ * ObjTree representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I
+ */
+static ObjTree *
+deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
+{
+ AlterOwnerStmt *node = (AlterOwnerStmt *) parsetree;
+
+ return new_objtree_VA("ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I", 3,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->objectType, false),
"identity", ObjTypeString,
getObjectIdentity(&address, false),
"newowner", ObjTypeString,
@@ -3500,148 +4493,5385 @@ deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
}
/*
- * Deparse a RenameStmt.
+ * Deparse an AlterSeqStmt.
+ *
+ * Given a sequence OID and a parse tree that modified it, return an ObjTree
+ * representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER SEQUENCE %{identity}D %{definition: }s
+ */
+static ObjTree *
+deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
+{
+ ObjTree *ret;
+ Relation relation;
+ List *elems = NIL;
+ ListCell *cell;
+ Form_pg_sequence seqform;
+ Sequence_values *seqvalues;
+ AlterSeqStmt *alterSeqStmt = (AlterSeqStmt *) parsetree;
+
+ /*
+ * Sequence for IDENTITY COLUMN output separately (via CREATE TABLE or
+ * ALTER TABLE); return empty here.
+ */
+ if (alterSeqStmt->for_identity)
+ return NULL;
+
+ seqvalues = get_sequence_values(objectId);
+ seqform = seqvalues->seqform;
+
+ foreach(cell, ((AlterSeqStmt *) parsetree)->options)
+ {
+ DefElem *elem = (DefElem *) lfirst(cell);
+ ObjElem *newelm;
+
+ if (strcmp(elem->defname, "cache") == 0)
+ newelm = deparse_Seq_Cache(seqform, false);
+ else if (strcmp(elem->defname, "cycle") == 0)
+ newelm = deparse_Seq_Cycle(seqform, false);
+ else if (strcmp(elem->defname, "increment") == 0)
+ newelm = deparse_Seq_IncrementBy(seqform, false);
+ else if (strcmp(elem->defname, "minvalue") == 0)
+ newelm = deparse_Seq_Minvalue(seqform, false);
+ else if (strcmp(elem->defname, "maxvalue") == 0)
+ newelm = deparse_Seq_Maxvalue(seqform, false);
+ else if (strcmp(elem->defname, "start") == 0)
+ newelm = deparse_Seq_Startwith(seqform, false);
+ else if (strcmp(elem->defname, "restart") == 0)
+ newelm = deparse_Seq_Restart(seqvalues->last_value);
+ else if (strcmp(elem->defname, "owned_by") == 0)
+ newelm = deparse_Seq_OwnedBy(objectId, false);
+ else if (strcmp(elem->defname, "as") == 0)
+ newelm = deparse_Seq_As(seqform);
+ else
+ elog(ERROR, "invalid sequence option %s", elem->defname);
+
+ elems = lappend(elems, newelm);
+ }
+
+ relation = relation_open(objectId, AccessShareLock);
+
+ ret = new_objtree_VA("ALTER SEQUENCE %{identity}D %{definition: }s", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(relation->rd_rel->relnamespace,
+ RelationGetRelationName(relation)),
+ "definition", ObjTypeArray, elems);
+
+ relation_close(relation, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterTypeStmt.
+ *
+ * Given a type OID and a parse tree that modified it, return an ObjTree
+ * representing the alter type.
+ *
+ * Verbose syntax
+ * ALTER TYPE %{identity}D SET (%{definition: }s)
+ */
+static ObjTree *
+deparse_AlterTypeSetStmt(Oid objectId, Node *cmd)
+{
+ AlterTypeStmt *alterTypeSetStmt = (AlterTypeStmt *) cmd;
+ ListCell *pl;
+ List *elems = NIL;
+ HeapTuple typTup;
+ Form_pg_type typForm;
+
+ typTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(typTup))
+ elog(ERROR, "cache lookup failed for type with OID %u", objectId);
+ typForm = (Form_pg_type) GETSTRUCT(typTup);
+
+ foreach(pl, alterTypeSetStmt->options)
+ {
+ DefElem *defel = (DefElem *) lfirst(pl);
+ ObjElem *newelm;
+
+ if (strcmp(defel->defname, "storage") == 0)
+ newelm = deparse_Type_Storage(typForm);
+ else if (strcmp(defel->defname, "receive") == 0)
+ newelm = deparse_Type_Receive(typForm);
+ else if (strcmp(defel->defname, "send") == 0)
+ newelm = deparse_Type_Send(typForm);
+ else if (strcmp(defel->defname, "typmod_in") == 0)
+ newelm = deparse_Type_Typmod_In(typForm);
+ else if (strcmp(defel->defname, "typmod_out") == 0)
+ newelm = deparse_Type_Typmod_Out(typForm);
+ else if (strcmp(defel->defname, "analyze") == 0)
+ newelm = deparse_Type_Analyze(typForm);
+ else if (strcmp(defel->defname, "subscript") == 0)
+ newelm = deparse_Type_Subscript(typForm);
+ else
+ elog(ERROR, "invalid type option %s", defel->defname);
+
+ elems = lappend(elems, newelm);
+ }
+
+ ReleaseSysCache(typTup);
+
+ return new_objtree_VA("ALTER TYPE %{identity}D SET (%{definition:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId,
+ objectId),
+ "definition", ObjTypeArray, elems);
+}
+
+/*
+ * Deparse a CompositeTypeStmt (CREATE TYPE AS)
+ *
+ * Given a Composite type OID and the parse tree that created it, return an
+ * ObjTree representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE TYPE %{identity}D AS (%{columns:, }s)
+ */
+static ObjTree *
+deparse_CompositeTypeStmt(Oid objectId, Node *parsetree)
+{
+ CompositeTypeStmt *node = (CompositeTypeStmt *) parsetree;
+ HeapTuple typtup;
+ Form_pg_type typform;
+ Relation typerel;
+ List *dpcontext;
+ List *tableelts = NIL;
+
+ /* Find the pg_type entry and open the corresponding relation */
+ typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(typtup))
+ elog(ERROR, "cache lookup failed for type with OID %u", objectId);
+ typform = (Form_pg_type) GETSTRUCT(typtup);
+ typerel = relation_open(typform->typrelid, AccessShareLock);
+
+ dpcontext = deparse_context_for(RelationGetRelationName(typerel),
+ RelationGetRelid(typerel));
+
+ tableelts = deparse_TableElements(typerel, node->coldeflist, dpcontext,
+ false, /* not typed */
+ true); /* composite type */
+
+ table_close(typerel, AccessShareLock);
+ ReleaseSysCache(typtup);
+
+ return new_objtree_VA("CREATE TYPE %{identity}D AS (%{columns:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId, objectId),
+ "columns", ObjTypeArray, tableelts);
+}
+
+/*
+ * Deparse a CreateConversionStmt
+ *
+ * Given a conversion OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE %{default}s CONVERSION %{identity}D FOR %{source}L TO %{dest}L
+ * FROM %{function}D
+ */
+static ObjTree *
+deparse_CreateConversion(Oid objectId, Node *parsetree)
+{
+ HeapTuple conTup;
+ Relation convrel;
+ Form_pg_conversion conForm;
+ ObjTree *ret;
+
+ convrel = table_open(ConversionRelationId, AccessShareLock);
+ conTup = get_catalog_object_by_oid(convrel, Anum_pg_conversion_oid, objectId);
+ if (!HeapTupleIsValid(conTup))
+ elog(ERROR, "cache lookup failed for conversion with OID %u", objectId);
+ conForm = (Form_pg_conversion) GETSTRUCT(conTup);
+
+ ret = new_objtree_VA("CREATE %{default}s CONVERSION %{identity}D FOR %{source}L TO %{dest}L FROM %{function}D", 5,
+ "default", ObjTypeString,
+ conForm->condefault ? "DEFAULT" : "",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(conForm->connamespace,
+ NameStr(conForm->conname)),
+ "source", ObjTypeString,
+ (char *)pg_encoding_to_char(conForm->conforencoding),
+ "dest", ObjTypeString,
+ (char *)pg_encoding_to_char(conForm->contoencoding),
+ "function", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ conForm->conproc));
+
+ table_close(convrel, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateEnumStmt (CREATE TYPE AS ENUM)
+ *
+ * Given a Enum type OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE TYPE %{identity}D AS ENUM (%{values:, }L)
+ */
+static ObjTree *
+deparse_CreateEnumStmt(Oid objectId, Node *parsetree)
+{
+ CreateEnumStmt *node = (CreateEnumStmt *) parsetree;
+ List *values = NIL;
+ ListCell *cell;
+
+ foreach(cell, node->vals)
+ values = lappend(values, new_string_object(strVal(lfirst_node(String, cell))));
+
+ return new_objtree_VA("CREATE TYPE %{identity}D AS ENUM (%{values:, }L)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId, objectId),
+ "values", ObjTypeArray, values);
+}
+
+/*
+ * Deparse a CreateExtensionStmt
+ *
+ * Given an extension OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE EXTENSION %{if_not_exists}s %{name}I %{options: }s
+ */
+static ObjTree *
+deparse_CreateExtensionStmt(Oid objectId, Node *parsetree)
+{
+ CreateExtensionStmt *node = (CreateExtensionStmt *) parsetree;
+ Relation pg_extension;
+ HeapTuple extTup;
+ Form_pg_extension extForm;
+ ObjTree *tmp;
+ List *list = NIL;
+ ListCell *cell;
+
+ pg_extension = table_open(ExtensionRelationId, AccessShareLock);
+ extTup = get_catalog_object_by_oid(pg_extension, Anum_pg_extension_oid, objectId);
+ if (!HeapTupleIsValid(extTup))
+ elog(ERROR, "cache lookup failed for extension with OID %u", objectId);
+ extForm = (Form_pg_extension) GETSTRUCT(extTup);
+
+ /* List of options */
+ foreach(cell, node->options)
+ {
+ DefElem *opt = (DefElem *) lfirst(cell);
+
+ if (strcmp(opt->defname, "schema") == 0)
+ {
+ /* Skip this one; we add one unconditionally below */
+ continue;
+ }
+ else if (strcmp(opt->defname, "new_version") == 0)
+ {
+ tmp = new_objtree_VA("VERSION %{version}L", 2,
+ "type", ObjTypeString, "version",
+ "version", ObjTypeString, defGetString(opt));
+ list = lappend(list, new_object_object(tmp));
+ }
+ else if (strcmp(opt->defname, "old_version") == 0)
+ {
+ tmp = new_objtree_VA("FROM %{version}L", 2,
+ "type", ObjTypeString, "from",
+ "version", ObjTypeString, defGetString(opt));
+ list = lappend(list, new_object_object(tmp));
+ }
+ else
+ elog(ERROR, "unsupported option %s", opt->defname);
+ }
+
+ /* Add the SCHEMA option */
+ tmp = new_objtree_VA("SCHEMA %{schema}I", 2,
+ "type", ObjTypeString, "schema",
+ "schema", ObjTypeString,
+ get_namespace_name(extForm->extnamespace));
+ list = lappend(list, new_object_object(tmp));
+ table_close(pg_extension, AccessShareLock);
+
+ return new_objtree_VA("CREATE EXTENSION %{if_not_exists}s %{name}I %{options: }s", 3,
+ "if_not_exists", ObjTypeString,
+ node->if_not_exists ? "IF NOT EXISTS" : "",
+ "name", ObjTypeString, node->extname,
+ "options", ObjTypeArray, list);
+}
+
+/*
+ * If a column name is specified, add an element for it; otherwise it's a
+ * table-level option.
+ */
+static ObjTree *
+deparse_FdwOptions(List *options, char *colname, bool altercoloptions)
+{
+ ObjTree *ret;
+
+ if (colname)
+ ret = new_objtree_VA("ALTER COLUMN %{column}I %{options}s", 2,
+ "column", ObjTypeString, colname,
+ "options", ObjTypeString,
+ altercoloptions ? "OPTIONS" : "");
+ else
+ ret = new_objtree("OPTIONS");
+
+ if (options != NIL)
+ {
+ List *optout = NIL;
+ ListCell *cell;
+
+ foreach(cell, options)
+ {
+ DefElem *elem;
+ ObjTree *opt;
+
+ elem = (DefElem *) lfirst(cell);
+
+ switch (elem->defaction)
+ {
+ case DEFELEM_UNSPEC:
+ opt = new_objtree_VA("%{label}I %{value}L", 2,
+ "label", ObjTypeString, elem->defname,
+ "value", ObjTypeString,
+ elem->arg ? defGetString(elem) :
+ defGetBoolean(elem) ? "TRUE" : "FALSE");
+ break;
+ case DEFELEM_SET:
+ opt = new_objtree_VA("SET %{label}I %{value}L", 2,
+ "label", ObjTypeString, elem->defname,
+ "value", ObjTypeString,
+ elem->arg ? defGetString(elem) :
+ defGetBoolean(elem) ? "TRUE" : "FALSE");
+ break;
+ case DEFELEM_ADD:
+ opt = new_objtree_VA("ADD %{label}I %{value}L", 2,
+ "label", ObjTypeString, elem->defname,
+ "value", ObjTypeString,
+ elem->arg ? defGetString(elem) :
+ defGetBoolean(elem) ? "TRUE" : "FALSE");
+ break;
+ case DEFELEM_DROP:
+ opt = new_objtree_VA("DROP %{label}I", 1,
+ "label", ObjTypeString, elem->defname);
+ break;
+ default:
+ elog(ERROR, "invalid def action %d", elem->defaction);
+ opt = NULL;
+ }
+
+ optout = lappend(optout, new_object_object(opt));
+ }
+
+ append_array_object(ret, "(%{option: ,}s)", optout);
+ }
+ else
+ append_not_present(ret);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateFdwStmt (CREATE FOREIGN DATA WRAPPER)
+ *
+ * Given an FDW OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE FOREIGN DATA WRAPPER %{identity}I %{handler}s %{validator}s %{generic_options}s
+ */
+static ObjTree *
+deparse_CreateFdwStmt(Oid objectId, Node *parsetree)
+{
+ CreateFdwStmt *node = (CreateFdwStmt *) parsetree;
+ HeapTuple fdwTup;
+ Form_pg_foreign_data_wrapper fdwForm;
+ Relation rel;
+ ObjTree *ret;
+ ObjTree *tmp;
+
+ rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ fdwTup = SearchSysCache1(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(fdwTup))
+ elog(ERROR, "cache lookup failed for foreign-data wrapper with OID %u",
+ objectId);
+
+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(fdwTup);
+
+ ret = new_objtree_VA("CREATE FOREIGN DATA WRAPPER %{identity}I", 1,
+ "identity", ObjTypeString, NameStr(fdwForm->fdwname));
+
+ /* Add HANDLER clause */
+ if (!OidIsValid(fdwForm->fdwhandler))
+ tmp = new_objtree("NO HANDLER");
+ else
+ {
+ tmp = new_objtree_VA("HANDLER %{procedure}D", 1,
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ fdwForm->fdwhandler));
+ }
+ append_object_object(ret, "%{handler}s", tmp);
+
+ /* Add VALIDATOR clause */
+ if (!OidIsValid(fdwForm->fdwvalidator))
+ tmp = new_objtree("NO VALIDATOR");
+ else
+ {
+ tmp = new_objtree_VA("VALIDATOR %{procedure}D", 1,
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ fdwForm->fdwvalidator));
+ }
+ append_object_object(ret, "%{validator}s", tmp);
+
+ /* Add an OPTIONS clause, if any */
+ append_object_object(ret, "%{generic_options}s",
+ deparse_FdwOptions(node->options, NULL, false));
+
+ ReleaseSysCache(fdwTup);
+ table_close(rel, RowExclusiveLock);
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterFdwStmt (ALTER FOREIGN DATA WRAPPER)
+ *
+ * Given an FDW OID and the parse tree that created it, return an ObjTree
+ * representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER FOREIGN DATA WRAPPER %{identity}I %{fdw_options: }s %{generic_options}D
+ */
+static ObjTree *
+deparse_AlterFdwStmt(Oid objectId, Node *parsetree)
+{
+ AlterFdwStmt *node = (AlterFdwStmt *) parsetree;
+ HeapTuple fdwTup;
+ Form_pg_foreign_data_wrapper fdwForm;
+ Relation rel;
+ ObjTree *ret;
+ List *fdw_options = NIL;
+ ListCell *cell;
+
+ rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ fdwTup = SearchSysCache1(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(fdwTup))
+ elog(ERROR, "cache lookup failed for foreign-data wrapper with OID %u",
+ objectId);
+
+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(fdwTup);
+
+ ret = new_objtree_VA("ALTER FOREIGN DATA WRAPPER %{identity}I", 1,
+ "identity", ObjTypeString, NameStr(fdwForm->fdwname));
+
+ /*
+ * Iterate through options, to see what changed, but use catalog as a
+ * basis for new values.
+ */
+ foreach(cell, node->func_options)
+ {
+ DefElem *elem;
+ ObjTree *tmp;
+
+ elem = lfirst(cell);
+
+ if (pg_strcasecmp(elem->defname, "handler") == 0)
+ {
+ /* add HANDLER clause */
+ if (!OidIsValid(fdwForm->fdwhandler))
+ tmp = new_objtree("NO HANDLER");
+ else
+ {
+ tmp = new_objtree_VA("HANDLER %{procedure}D", 1,
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ fdwForm->fdwhandler));
+ }
+ fdw_options = lappend(fdw_options, new_object_object(tmp));
+ }
+ else if (pg_strcasecmp(elem->defname, "validator") == 0)
+ {
+ /* add VALIDATOR clause */
+ if (!OidIsValid(fdwForm->fdwvalidator))
+ tmp = new_objtree("NO VALIDATOR");
+ else
+ {
+ tmp = new_objtree_VA("VALIDATOR %{procedure}D", 1,
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ fdwForm->fdwvalidator));
+ }
+ fdw_options = lappend(fdw_options, new_object_object(tmp));
+ }
+ }
+
+ /* Add HANDLER/VALIDATOR if specified */
+ append_array_object(ret, "%{fdw_options: }s", fdw_options);
+
+ /* Add an OPTIONS clause, if any */
+ append_object_object(ret, "%{generic_options}D",
+ deparse_FdwOptions(node->options, NULL, false));
+
+ ReleaseSysCache(fdwTup);
+ table_close(rel, RowExclusiveLock);
+
+ return ret;
+}
+
+/*
+ * Deparse a CREATE TYPE AS RANGE statement
+ *
+ * Given a Range type OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE TYPE %{identity}D AS RANGE (%{definition:, }s)
+ */
+static ObjTree *
+deparse_CreateRangeStmt(Oid objectId, Node *parsetree)
+{
+ ObjTree *tmp;
+ List *definition = NIL;
+ Relation pg_range;
+ HeapTuple rangeTup;
+ Form_pg_range rangeForm;
+ ScanKeyData key[1];
+ SysScanDesc scan;
+
+ pg_range = table_open(RangeRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_range_rngtypid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(pg_range, RangeTypidIndexId, true,
+ NULL, 1, key);
+
+ rangeTup = systable_getnext(scan);
+ if (!HeapTupleIsValid(rangeTup))
+ elog(ERROR, "cache lookup failed for range with type OID %u",
+ objectId);
+
+ rangeForm = (Form_pg_range) GETSTRUCT(rangeTup);
+
+ /* SUBTYPE */
+ tmp = new_objtree_VA("SUBTYPE = %{type}D", 2,
+ "clause", ObjTypeString, "subtype",
+ "type", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId, rangeForm->rngsubtype));
+ definition = lappend(definition, new_object_object(tmp));
+
+ /* SUBTYPE_OPCLASS */
+ if (OidIsValid(rangeForm->rngsubopc))
+ {
+ tmp = new_objtree_VA("SUBTYPE_OPCLASS = %{opclass}D", 2,
+ "clause", ObjTypeString, "opclass",
+ "opclass", ObjTypeObject,
+ new_objtree_for_qualname_id(OperatorClassRelationId,
+ rangeForm->rngsubopc));
+ definition = lappend(definition, new_object_object(tmp));
+ }
+
+ /* COLLATION */
+ if (OidIsValid(rangeForm->rngcollation))
+ {
+ tmp = new_objtree_VA("COLLATION = %{collation}D", 2,
+ "clause", ObjTypeString, "collation",
+ "collation", ObjTypeObject,
+ new_objtree_for_qualname_id(CollationRelationId,
+ rangeForm->rngcollation));
+ definition = lappend(definition, new_object_object(tmp));
+ }
+
+ /* CANONICAL */
+ if (OidIsValid(rangeForm->rngcanonical))
+ {
+ tmp = new_objtree_VA("CANONICAL = %{canonical}D", 2,
+ "clause", ObjTypeString, "canonical",
+ "canonical", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ rangeForm->rngcanonical));
+ definition = lappend(definition, new_object_object(tmp));
+ }
+
+ /* SUBTYPE_DIFF */
+ if (OidIsValid(rangeForm->rngsubdiff))
+ {
+ tmp = new_objtree_VA("SUBTYPE_DIFF = %{subtype_diff}D", 2,
+ "clause", ObjTypeString, "subtype_diff",
+ "subtype_diff", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ rangeForm->rngsubdiff));
+ definition = lappend(definition, new_object_object(tmp));
+ }
+
+ systable_endscan(scan);
+ table_close(pg_range, RowExclusiveLock);
+
+ return new_objtree_VA("CREATE TYPE %{identity}D AS RANGE (%{definition:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId, objectId),
+ "definition", ObjTypeArray, definition);
+}
+
+/*
+ * Deparse an AlterEnumStmt.
+ *
+ * Given an enum OID and a parse tree that modified it, return an ObjTree
+ * representing the alter type.
+ *
+ * Verbose syntax
+ * ALTER TYPE %{identity}D ADD VALUE %{if_not_exists}s %{value}L
+ * %{after_or_before}s %{neighbor}L
+ * OR
+ * ALTER TYPE %{identity}D RENAME VALUE %{value}L TO %{newvalue}L
+ */
+static ObjTree *
+deparse_AlterEnumStmt(Oid objectId, Node *parsetree)
+{
+ AlterEnumStmt *node = (AlterEnumStmt *) parsetree;
+ ObjTree *ret;
+
+ ret = new_objtree_VA("ALTER TYPE %{identity}D", 1,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId,
+ objectId));
+
+ if (!node->oldVal)
+ {
+ append_format_string(ret, "ADD VALUE");
+ append_string_object(ret, "%{if_not_exists}s", "if_not_exists",
+ node->skipIfNewValExists ? "IF NOT EXISTS" : "");
+
+ append_string_object(ret, "%{value}L", "value", node->newVal);
+
+ if (node->newValNeighbor)
+ {
+ append_string_object(ret, "%{after_or_before}s",
+ "after_or_before",
+ node->newValIsAfter ? "AFTER" : "BEFORE");
+ append_string_object(ret, "%{neighbor}L", "neighbor",
+ node->newValNeighbor);
+ }
+ }
+ else
+ {
+ append_string_object(ret, "RENAME VALUE %{value}L TO", "value",
+ node->oldVal);
+ append_string_object(ret, "%{newvalue}L", "newvalue", node->newVal);
+ }
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterExtensionStmt (ALTER EXTENSION .. UPDATE TO VERSION)
+ *
+ * Given an extension OID and a parse tree that modified it, return an ObjTree
+ * representing the alter type.
+ *
+ * Verbose syntax
+ * ALTER EXTENSION %{identity}I UPDATE %{options: }s
+ */
+static ObjTree *
+deparse_AlterExtensionStmt(Oid objectId, Node *parsetree)
+{
+ AlterExtensionStmt *node = (AlterExtensionStmt *) parsetree;
+ Relation pg_extension;
+ HeapTuple extTup;
+ Form_pg_extension extForm;
+ ObjTree *ret;
+ List *list = NIL;
+ ListCell *cell;
+ DefElem *d_new_version = NULL;
+
+ pg_extension = table_open(ExtensionRelationId, AccessShareLock);
+ extTup = get_catalog_object_by_oid(pg_extension, Anum_pg_extension_oid, objectId);
+ if (!HeapTupleIsValid(extTup))
+ elog(ERROR, "cache lookup failed for extension with OID %u",
+ objectId);
+ extForm = (Form_pg_extension) GETSTRUCT(extTup);
+
+ foreach(cell, node->options)
+ {
+ DefElem *opt = (DefElem *) lfirst(cell);
+
+ if (strcmp(opt->defname, "new_version") == 0)
+ {
+ ObjTree *tmp;
+
+ if (d_new_version)
+ elog(ERROR, "conflicting or redundant options for extension with OID %u",
+ objectId);
+
+ d_new_version = opt;
+
+ tmp = new_objtree_VA("TO %{version}L", 2,
+ "type", ObjTypeString, "version",
+ "version", ObjTypeString, defGetString(opt));
+ list = lappend(list, new_object_object(tmp));
+ }
+ else
+ elog(ERROR, "unsupported option %s", opt->defname);
+ }
+
+ ret = new_objtree_VA("ALTER EXTENSION %{identity}I UPDATE %{options: }s", 2,
+ "identity", ObjTypeString, NameStr(extForm->extname),
+ "options", ObjTypeArray, list);
+
+ table_close(pg_extension, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterExtensionContentsStmt (ALTER EXTENSION ext ADD/DROP object)
+ *
+ * Verbose syntax
+ * ALTER EXTENSION %{extidentity}I %{extoption}s %{extobjtype}s
+ * ADD/DROP %{objidentity}s
+ */
+static ObjTree *
+deparse_AlterExtensionContentsStmt(Oid objectId, Node *parsetree,
+ ObjectAddress objectAddress)
+{
+ AlterExtensionContentsStmt *node = (AlterExtensionContentsStmt *) parsetree;
+
+ Assert(node->action == +1 || node->action == -1);
+
+ return new_objtree_VA("ALTER EXTENSION %{extidentity}I %{extoption}s %{extobjtype}s %{objidentity}s", 4,
+ "extidentity", ObjTypeString, node->extname,
+ "extoption", ObjTypeString,
+ node->action == +1 ? "ADD" : "DROP",
+ "extobjtype", ObjTypeString,
+ stringify_objtype(node->objtype, false),
+ "objidentity", ObjTypeString,
+ getObjectIdentity(&objectAddress, false));
+}
+
+/*
+ * Deparse an CreateCastStmt.
+ *
+ * Given a sequence OID and a parse tree that modified it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE CAST (%{sourcetype}T AS %{targettype}T) %{mechanism}s %{context}s
+ */
+static ObjTree *
+deparse_CreateCastStmt(Oid objectId, Node *parsetree)
+{
+ CreateCastStmt *node = (CreateCastStmt *) parsetree;
+ Relation castrel;
+ HeapTuple castTup;
+ Form_pg_cast castForm;
+ ObjTree *ret;
+ char *context;
+
+ castrel = table_open(CastRelationId, AccessShareLock);
+ castTup = get_catalog_object_by_oid(castrel, Anum_pg_cast_oid, objectId);
+ if (!HeapTupleIsValid(castTup))
+ elog(ERROR, "cache lookup failed for cast with OID %u", objectId);
+ castForm = (Form_pg_cast) GETSTRUCT(castTup);
+
+ ret = new_objtree_VA("CREATE CAST (%{sourcetype}T AS %{targettype}T)", 2,
+ "sourcetype", ObjTypeObject,
+ new_objtree_for_type(castForm->castsource, -1),
+ "targettype", ObjTypeObject,
+ new_objtree_for_type(castForm->casttarget, -1));
+
+ if (node->inout)
+ append_string_object(ret, "%{mechanism}s", "mechanism",
+ "WITH INOUT");
+ else if (node->func == NULL)
+ append_string_object(ret, "%{mechanism}s", "mechanism",
+ "WITHOUT FUNCTION");
+ else
+ {
+ ObjTree *tmp_obj;
+ StringInfoData func;
+ HeapTuple funcTup;
+ Form_pg_proc funcForm;
+ int i;
+
+ funcTup = SearchSysCache1(PROCOID, castForm->castfunc);
+ funcForm = (Form_pg_proc) GETSTRUCT(funcTup);
+
+ initStringInfo(&func);
+ appendStringInfo(&func, "%s(",
+ quote_qualified_identifier(get_namespace_name(funcForm->pronamespace),
+ NameStr(funcForm->proname)));
+ for (i = 0; i < funcForm->pronargs; i++)
+ {
+ if (i != 0)
+ appendStringInfoChar(&func, ',');
+
+ appendStringInfoString(&func,
+ format_type_be_qualified(funcForm->proargtypes.values[i]));
+ }
+ appendStringInfoChar(&func, ')');
+
+ tmp_obj = new_objtree_VA("WITH FUNCTION %{castfunction}s", 1,
+ "castfunction", ObjTypeString, func.data);
+ append_object_object(ret, "%{mechanism}s", tmp_obj);
+
+ ReleaseSysCache(funcTup);
+ }
+
+ switch (node->context)
+ {
+ case COERCION_IMPLICIT:
+ context = "AS IMPLICIT";
+ break;
+ case COERCION_ASSIGNMENT:
+ context = "AS ASSIGNMENT";
+ break;
+ case COERCION_EXPLICIT:
+ context = "";
+ break;
+ default:
+ elog(ERROR, "invalid coercion code %c", node->context);
+ return NULL; /* keep compiler quiet */
+ }
+ append_string_object(ret, "%{context}s", "context", context);
+
+ table_close(castrel, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Deparse an ALTER DEFAULT PRIVILEGES statement.
+ *
+ * Verbose syntax
+ * ALTER DEFAULT PRIVILEGES %{in_schema}s %{for_roles}s %{grant}s
+ */
+static ObjTree *
+deparse_AlterDefaultPrivilegesStmt(CollectedCommand *cmd)
+{
+ ObjTree *ret;
+ AlterDefaultPrivilegesStmt *stmt = (AlterDefaultPrivilegesStmt *) cmd->parsetree;
+ List *roles = NIL;
+ List *schemas = NIL;
+ List *grantees;
+ List *privs;
+ ListCell *cell;
+ ObjTree *tmp;
+ ObjTree *grant;
+ char *objtype;
+
+ ret = new_objtree("ALTER DEFAULT PRIVILEGES");
+
+ /* Scan the parse node to dig out the FOR ROLE and IN SCHEMA clauses */
+ foreach(cell, stmt->options)
+ {
+ DefElem *opt = (DefElem *) lfirst(cell);
+ ListCell *cell2;
+
+ Assert(IsA(opt, DefElem));
+ Assert(IsA(opt->arg, List));
+ if (strcmp(opt->defname, "roles") == 0)
+ {
+ foreach(cell2, (List *) opt->arg)
+ {
+ RoleSpec *rolespec = lfirst(cell2);
+ ObjTree *obj = new_objtree_for_rolespec(rolespec);
+
+ roles = lappend(roles, new_object_object(obj));
+ }
+ }
+ else if (strcmp(opt->defname, "schemas") == 0)
+ {
+ foreach(cell2, (List *) opt->arg)
+ {
+ String *val = lfirst_node(String, cell2);
+
+ schemas = lappend(schemas, new_string_object(strVal(val)));
+ }
+ }
+ }
+
+ /* Add the IN SCHEMA clause, if any */
+ tmp = new_objtree("IN SCHEMA");
+ append_array_object(tmp, "%{schemas:, }I", schemas);
+ if (schemas == NIL)
+ append_not_present(tmp);
+ append_object_object(ret, "%{in_schema}s", tmp);
+
+ /* Add the FOR ROLE clause, if any */
+ tmp = new_objtree("FOR ROLE");
+ append_array_object(tmp, "%{roles:, }R", roles);
+ if (roles == NIL)
+ append_not_present(tmp);
+ append_object_object(ret, "%{for_roles}s", tmp);
+
+ /*
+ * Add the GRANT subcommand
+ * Verbose syntax
+ * GRANT %{privileges:, }s ON %{target}s TO %{grantees:, }R %{grant_option}s
+ * or
+ * REVOKE %{grant_option}s %{privileges:, }s ON %{target}s FROM %{grantees:, }R
+ */
+ if (stmt->action->is_grant)
+ grant = new_objtree("GRANT");
+ else
+ {
+ grant = new_objtree("REVOKE");
+
+ /* Add the GRANT OPTION clause for REVOKE subcommand */
+ tmp = new_objtree_VA("GRANT OPTION FOR", 1,
+ "present", ObjTypeBool,
+ stmt->action->grant_option);
+ append_object_object(grant, "%{grant_option}s", tmp);
+ }
+
+ /*
+ * Add the privileges list. This uses the parser struct, as opposed to
+ * the InternalGrant format used by GRANT. There are enough other
+ * differences that this doesn't seem worth improving.
+ */
+ if (stmt->action->privileges == NIL)
+ privs = list_make1(new_string_object("ALL PRIVILEGES"));
+ else
+ {
+ privs = NIL;
+
+ foreach(cell, stmt->action->privileges)
+ {
+ AccessPriv *priv = lfirst(cell);
+
+ Assert(priv->cols == NIL);
+ privs = lappend(privs, new_string_object(priv->priv_name));
+ }
+ }
+
+ append_array_object(grant, "%{privileges:, }s", privs);
+
+ switch (cmd->d.defprivs.objtype)
+ {
+ case OBJECT_TABLE:
+ objtype = "TABLES";
+ break;
+ case OBJECT_FUNCTION:
+ objtype = "FUNCTIONS";
+ break;
+ case OBJECT_ROUTINE:
+ objtype = "ROUTINES";
+ break;
+ case OBJECT_SEQUENCE:
+ objtype = "SEQUENCES";
+ break;
+ case OBJECT_TYPE:
+ objtype = "TYPES";
+ break;
+ case OBJECT_SCHEMA:
+ objtype = "SCHEMAS";
+ break;
+ default:
+ elog(ERROR, "invalid OBJECT value %d for default privileges", cmd->d.defprivs.objtype);
+ }
+
+ /* Add the target object type */
+ append_string_object(grant, "ON %{target}s", "target", objtype);
+
+ /* Add the grantee list */
+ grantees = NIL;
+ foreach(cell, stmt->action->grantees)
+ {
+ RoleSpec *spec = (RoleSpec *) lfirst(cell);
+ ObjTree *obj = new_objtree_for_rolespec(spec);
+
+ grantees = lappend(grantees, new_object_object(obj));
+ }
+
+ if (stmt->action->is_grant)
+ {
+ append_array_object(grant, "TO %{grantees:, }R", grantees);
+
+ /* Add the WITH GRANT OPTION clause for GRANT subcommand */
+ tmp = new_objtree_VA("WITH GRANT OPTION", 1,
+ "present", ObjTypeBool,
+ stmt->action->grant_option);
+ append_object_object(grant, "%{grant_option}s", tmp);
+ }
+ else
+ append_array_object(grant, "FROM %{grantees:, }R", grantees);
+
+ append_object_object(ret, "%{grant}s", grant);
+
+ return ret;
+}
+
+/*
+ * Deparse the CREATE DOMAIN
+ *
+ * Given a function OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE DOMAIN %{identity}D AS %{type}T %{not_null}s %{constraints}s
+ * %{collation}s
+ */
+static ObjTree *
+deparse_CreateDomain(Oid objectId, Node *parsetree)
+{
+ ObjTree *ret;
+ ObjTree *tmp_obj;
+ HeapTuple typTup;
+ Form_pg_type typForm;
+ List *constraints;
+ char *defval;
+
+ typTup = SearchSysCache1(TYPEOID, objectId);
+ if (!HeapTupleIsValid(typTup))
+ elog(ERROR, "cache lookup failed for domain with OID %u", objectId);
+ typForm = (Form_pg_type) GETSTRUCT(typTup);
+
+ ret = new_objtree_VA("CREATE DOMAIN %{identity}D AS %{type}T %{not_null}s", 3,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId, objectId),
+ "type", ObjTypeObject,
+ new_objtree_for_type(typForm->typbasetype,
+ typForm->typtypmod),
+ "not_null", ObjTypeString,
+ typForm->typnotnull ? "NOT NULL" : "");
+
+ constraints = obtainConstraints(NIL, InvalidOid, objectId,
+ ConstrObjDomain);
+ if (constraints == NIL)
+ {
+ tmp_obj = new_objtree("");
+ append_not_present(tmp_obj);
+ }
+ else
+ tmp_obj = new_objtree_VA("%{elements:, }s", 1,
+ "elements", ObjTypeArray, constraints);
+ append_object_object(ret, "%{constraints}s", tmp_obj);
+
+ tmp_obj = new_objtree("COLLATE");
+ if (OidIsValid(typForm->typcollation))
+ {
+ ObjTree *collname;
+
+ collname = new_objtree_for_qualname_id(CollationRelationId,
+ typForm->typcollation);
+ append_object_object(tmp_obj, "%{name}D", collname);
+ }
+ else
+ append_not_present(tmp_obj);
+ append_object_object(ret, "%{collation}s", tmp_obj);
+
+ defval = DomainGetDefault(typTup, true);
+ if (defval)
+ append_string_object(ret, "DEFAULT %{default}s", "default", defval);
+
+ ReleaseSysCache(typTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateFunctionStmt (CREATE FUNCTION)
+ *
+ * Given a function OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ *
+ * CREATE %{or_replace}s FUNCTION %{signature}s RETURNS %{return_type}s
+ * LANGUAGE %{transform_type}s %{language}I %{window}s %{volatility}s
+ * %{parallel_safety}s %{leakproof}s %{strict}s %{security_definer}s
+ * %{cost}s %{rows}s %{support}s %{set_options: }s AS %{objfile}L,
+ * %{symbol}L
+ */
+static ObjTree *
+deparse_CreateFunction(Oid objectId, Node *parsetree)
+{
+ CreateFunctionStmt *node = (CreateFunctionStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *tmp_obj;
+ Datum tmpdatum;
+ char *source;
+ char *probin = NULL;
+ List *params;
+ List *defaults;
+ List *sets = NIL;
+ List *types = NIL;
+ ListCell *cell;
+ ListCell *curdef;
+ ListCell *table_params = NULL;
+ HeapTuple procTup;
+ Form_pg_proc procForm;
+ HeapTuple langTup;
+ Oid *typarray;
+ Oid *trftypes;
+ Form_pg_language langForm;
+ int i;
+ int typnum;
+ int ntypes;
+ int paramcount = list_length(node->parameters);
+ bool isnull;
+ bool isfunction;
+
+ /* Get the pg_proc tuple */
+ procTup = SearchSysCache1(PROCOID, objectId);
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for function with OID %u",
+ objectId);
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+ /* Get the corresponding pg_language tuple */
+ langTup = SearchSysCache1(LANGOID, procForm->prolang);
+ if (!HeapTupleIsValid(langTup))
+ elog(ERROR, "cache lookup failed for language with OID %u",
+ procForm->prolang);
+ langForm = (Form_pg_language) GETSTRUCT(langTup);
+
+ /*
+ * Determine useful values for prosrc and probin. We cope with probin
+ * being either NULL or "-", but prosrc must have a valid value.
+ */
+ tmpdatum = SysCacheGetAttr(PROCOID, procTup,
+ Anum_pg_proc_prosrc, &isnull);
+ if (isnull)
+ elog(ERROR, "null prosrc in function with OID %u", objectId);
+ source = TextDatumGetCString(tmpdatum);
+
+ /* Determine a useful value for probin */
+ tmpdatum = SysCacheGetAttr(PROCOID, procTup,
+ Anum_pg_proc_probin, &isnull);
+ if (!isnull)
+ {
+ probin = TextDatumGetCString(tmpdatum);
+ if (probin[0] == '\0' || strcmp(probin, "-") == 0)
+ probin = NULL;
+ }
+
+ ret = new_objtree_VA("CREATE %{or_replace}s", 1,
+ "or_replace", ObjTypeString,
+ node->replace ? "OR REPLACE" : "");
+
+ /*
+ * To construct the arguments array, extract the type OIDs from the
+ * function's pg_proc entry. If pronargs equals the parameter list
+ * length, there are no OUT parameters and thus we can extract the type
+ * OID from proargtypes; otherwise we need to decode proallargtypes, which
+ * is a bit more involved.
+ */
+ typarray = palloc(paramcount * sizeof(Oid));
+ if (paramcount > procForm->pronargs)
+ {
+ Datum alltypes;
+ Datum *values;
+ bool *nulls;
+ int nelems;
+
+ alltypes = SysCacheGetAttr(PROCOID, procTup,
+ Anum_pg_proc_proallargtypes, &isnull);
+ if (isnull)
+ elog(ERROR, "null proallargtypes, more number of parameters than args in function with OID %u",
+ objectId);
+ deconstruct_array(DatumGetArrayTypeP(alltypes),
+ OIDOID, 4, 't', 'i',
+ &values, &nulls, &nelems);
+ if (nelems != paramcount)
+ elog(ERROR, "mismatched proallargatypes");
+ for (i = 0; i < paramcount; i++)
+ typarray[i] = values[i];
+ }
+ else
+ {
+ for (i = 0; i < paramcount; i++)
+ typarray[i] = procForm->proargtypes.values[i];
+ }
+
+ /*
+ * If there are any default expressions, we read the deparsed expression
+ * as a list so that we can attach them to each argument.
+ */
+ tmpdatum = SysCacheGetAttr(PROCOID, procTup,
+ Anum_pg_proc_proargdefaults, &isnull);
+ if (!isnull)
+ {
+ defaults = FunctionGetDefaults(DatumGetTextP(tmpdatum));
+ curdef = list_head(defaults);
+ }
+ else
+ {
+ defaults = NIL;
+ curdef = NULL;
+ }
+
+ /*
+ * Now iterate over each parameter in the parse tree to create the
+ * parameters array.
+ */
+ params = NIL;
+ typnum = 0;
+ foreach(cell, node->parameters)
+ {
+ FunctionParameter *param = (FunctionParameter *) lfirst(cell);
+ ObjTree *param_obj;
+ ObjTree *defaultval;
+ ObjTree *name;
+
+ /*
+ * A PARAM_TABLE parameter indicates the end of input arguments; the
+ * following parameters are part of the return type. We ignore them
+ * here, but keep track of the current position in the list so that we
+ * can easily produce the return type below.
+ */
+ if (param->mode == FUNC_PARAM_TABLE)
+ {
+ table_params = cell;
+ break;
+ }
+
+ /*
+ * Verbose syntax for paramater: %{mode}s %{name}s %{type}T
+ * %{default}s
+ */
+ param_obj = new_objtree_VA("%{mode}s", 1,
+ "mode", ObjTypeString,
+ param->mode == FUNC_PARAM_OUT ? "OUT" :
+ param->mode == FUNC_PARAM_INOUT ? "INOUT" :
+ param->mode == FUNC_PARAM_VARIADIC ? "VARIADIC" :
+ "IN");
+
+ /* Optional wholesale suppression of "name" occurs here */
+ name = new_objtree("");
+ if (param->name)
+ append_string_object(name, "%{name}I", "name", param->name);
+ else
+ {
+ append_null_object(name, "%{name}I");
+ append_not_present(name);
+ }
+
+ append_object_object(param_obj, "%{name}s", name);
+
+ defaultval = new_objtree("DEFAULT");
+ if (PointerIsValid(param->defexpr))
+ {
+ char *expr;
+
+ if (curdef == NULL)
+ elog(ERROR, "proargdefaults list too short");
+ expr = lfirst(curdef);
+
+ append_string_object(defaultval, "%{value}s", "value", expr);
+ curdef = lnext(defaults, curdef);
+ }
+ else
+ append_not_present(defaultval);
+
+ append_object_object(param_obj, "%{type}T",
+ new_objtree_for_type(typarray[typnum++], -1));
+
+ append_object_object(param_obj, "%{default}s", defaultval);
+
+ params = lappend(params, new_object_object(param_obj));
+ }
+
+ tmp_obj = new_objtree_VA("%{identity}D", 1,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ objectId));
+
+ append_format_string(tmp_obj, "(");
+ append_array_object(tmp_obj, "%{arguments:, }s", params);
+ append_format_string(tmp_obj, ")");
+
+ isfunction = (procForm->prokind != PROKIND_PROCEDURE);
+
+ if (isfunction)
+ append_object_object(ret, "FUNCTION %{signature}s", tmp_obj);
+ else
+ append_object_object(ret, "PROCEDURE %{signature}s", tmp_obj);
+
+ /*
+ * A return type can adopt one of two forms: either a [SETOF] some_type,
+ * or a TABLE(list-of-types). We can tell the second form because we saw
+ * a table param above while scanning the argument list.
+ */
+ if (table_params == NULL)
+ {
+ tmp_obj = new_objtree_VA("", 1,
+ "return_form", ObjTypeString, "plain");
+ append_string_object(tmp_obj, "%{setof}s", "setof",
+ procForm->proretset ? "SETOF" : "");
+ append_object_object(tmp_obj, "%{rettype}T",
+ new_objtree_for_type(procForm->prorettype, -1));
+ }
+ else
+ {
+ List *rettypes = NIL;
+ ObjTree *param_obj;
+
+ tmp_obj = new_objtree_VA("TABLE", 1,
+ "return_form", ObjTypeString, "table");
+ for (; table_params != NULL; table_params = lnext(node->parameters, table_params))
+ {
+ FunctionParameter *param = lfirst(table_params);
+
+ param_obj = new_objtree_VA("%{name}I %{type}T", 2,
+ "name", ObjTypeString, param->name,
+ "type", ObjTypeObject,
+ new_objtree_for_type(typarray[typnum++], -1));
+ rettypes = lappend(rettypes, new_object_object(param_obj));
+ }
+
+ append_array_object(tmp_obj, "(%{rettypes:, }s)", rettypes);
+ }
+
+ if (isfunction)
+ append_object_object(ret, "RETURNS %{return_type}s", tmp_obj);
+
+ /* TRANSFORM FOR TYPE */
+ tmp_obj = new_objtree("TRANSFORM");
+
+ ntypes = get_func_trftypes(procTup, &trftypes);
+ for (i = 0; i < ntypes; i++)
+ {
+ tmp_obj = new_objtree_VA("FOR TYPE %{type}T", 1,
+ "type", ObjTypeObject,
+ new_objtree_for_type(trftypes[i], -1));
+ types = lappend(types, tmp_obj);
+ }
+
+ if (types)
+ append_array_object(tmp_obj, "%{for_type:, }s", types);
+ else
+ append_not_present(tmp_obj);
+
+ append_object_object(ret, "%{transform_type}s", tmp_obj);
+
+ append_string_object(ret, "LANGUAGE %{language}I", "language",
+ NameStr(langForm->lanname));
+
+ if (isfunction)
+ {
+ append_string_object(ret, "%{window}s", "window",
+ procForm->prokind == PROKIND_WINDOW ? "WINDOW" : "");
+ append_string_object(ret, "%{volatility}s", "volatility",
+ procForm->provolatile == PROVOLATILE_VOLATILE ?
+ "VOLATILE" :
+ procForm->provolatile == PROVOLATILE_STABLE ?
+ "STABLE" : "IMMUTABLE");
+
+ append_string_object(ret, "%{parallel_safety}s",
+ "parallel_safety",
+ procForm->proparallel == PROPARALLEL_SAFE ?
+ "PARALLEL SAFE" :
+ procForm->proparallel == PROPARALLEL_RESTRICTED ?
+ "PARALLEL RESTRICTED" : "PARALLEL UNSAFE");
+
+ append_string_object(ret, "%{leakproof}s", "leakproof",
+ procForm->proleakproof ? "LEAKPROOF" : "");
+ append_string_object(ret, "%{strict}s", "strict",
+ procForm->proisstrict ?
+ "RETURNS NULL ON NULL INPUT" :
+ "CALLED ON NULL INPUT");
+
+ append_string_object(ret, "%{security_definer}s",
+ "security_definer",
+ procForm->prosecdef ?
+ "SECURITY DEFINER" : "SECURITY INVOKER");
+
+ append_object_object(ret, "%{cost}s",
+ new_objtree_VA("COST %{cost}n", 1,
+ "cost", ObjTypeFloat,
+ procForm->procost));
+
+ tmp_obj = new_objtree("ROWS");
+ if (procForm->prorows == 0)
+ append_not_present(tmp_obj);
+ else
+ append_float_object(tmp_obj, "%{rows}n", procForm->prorows);
+ append_object_object(ret, "%{rows}s", tmp_obj);
+
+ tmp_obj = new_objtree("SUPPORT %{name}s");
+ if (procForm->prosupport)
+ {
+ Oid argtypes[] = { INTERNALOID };
+
+ /*
+ * We should qualify the support function's name if it wouldn't be
+ * resolved by lookup in the current search path.
+ */
+ append_string_object(tmp_obj, "%{name}s", "name",
+ generate_function_name(procForm->prosupport, 1,
+ NIL, argtypes,
+ false, NULL,
+ EXPR_KIND_NONE));
+ }
+ else
+ append_not_present(tmp_obj);
+
+ append_object_object(ret, "%{support}s", tmp_obj);
+ }
+
+ foreach(cell, node->options)
+ {
+ DefElem *defel = (DefElem *) lfirst(cell);
+
+ if (strcmp(defel->defname, "set") == 0)
+ {
+ VariableSetStmt *sstmt = (VariableSetStmt *) defel->arg;
+ char *value = ExtractSetVariableArgs(sstmt);
+
+ tmp_obj = deparse_FunctionSet(sstmt->kind, sstmt->name, value);
+ sets = lappend(sets, new_object_object(tmp_obj));
+ }
+ }
+ append_array_object(ret, "%{set_options: }s", sets);
+
+ /* Add the function definition */
+ (void) SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosqlbody, &isnull);
+ if (procForm->prolang == SQLlanguageId && !isnull)
+ {
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+ print_function_sqlbody(&buf, procTup);
+
+ append_string_object(ret, "%{definition}s", "definition",
+ buf.data);
+ }
+ else if (probin == NULL)
+ append_string_object(ret, "AS %{definition}L", "definition", source);
+ else
+ {
+ append_string_object(ret, "AS %{objfile}L", "objfile", probin);
+ append_string_object(ret, ", %{symbol}L", "symbol", source);
+ }
+
+ ReleaseSysCache(langTup);
+ ReleaseSysCache(procTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a CREATE OPERATOR CLASS command.
+ *
+ * Verbose syntax
+ * CREATE OPERATOR CLASS %{identity}D %{default}s FOR TYPE %{type}T USING
+ * %{amname}I %{opfamily}s AS %{items:, }s
+ */
+static ObjTree *
+deparse_CreateOpClassStmt(CollectedCommand *cmd)
+{
+ Oid opcoid = cmd->d.createopc.address.objectId;
+ HeapTuple opcTup;
+ HeapTuple opfTup;
+ Form_pg_opfamily opfForm;
+ Form_pg_opclass opcForm;
+ ObjTree *ret;
+ ObjTree *tmp_obj;
+ List *list;
+ ListCell *cell;
+
+ /* Don't deparse SQL commands generated while creating extension */
+ if (cmd->in_extension)
+ return NULL;
+
+ opcTup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcoid));
+ if (!HeapTupleIsValid(opcTup))
+ elog(ERROR, "cache lookup failed for opclass with OID %u", opcoid);
+ opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
+
+ opfTup = SearchSysCache1(OPFAMILYOID, opcForm->opcfamily);
+ if (!HeapTupleIsValid(opfTup))
+ elog(ERROR, "cache lookup failed for operator family with OID %u", opcForm->opcfamily);
+ opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
+
+ ret = new_objtree_VA("CREATE OPERATOR CLASS %{identity}D %{default}s FOR TYPE %{type}T USING %{amname}I", 4,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(opcForm->opcnamespace,
+ NameStr(opcForm->opcname)),
+ "default", ObjTypeString,
+ opcForm->opcdefault ? "DEFAULT" : "",
+ "type", ObjTypeObject,
+ new_objtree_for_type(opcForm->opcintype, -1),
+ "amname", ObjTypeString, get_am_name(opcForm->opcmethod));
+
+ /*
+ * Add the FAMILY clause, but if it has the same name and namespace as the
+ * opclass, then have it expand to empty because it would cause a failure
+ * if the opfamily was created internally.
+ */
+ tmp_obj = new_objtree_VA("FAMILY %{opfamily}D", 1,
+ "opfamily", ObjTypeObject,
+ new_objtree_for_qualname(opfForm->opfnamespace,
+ NameStr(opfForm->opfname)));
+
+ if (strcmp(NameStr(opfForm->opfname), NameStr(opcForm->opcname)) == 0 &&
+ opfForm->opfnamespace == opcForm->opcnamespace)
+ append_not_present(tmp_obj);
+
+ append_object_object(ret, "%{opfamily}s", tmp_obj);
+
+ /*
+ * Add the initial item list. Note we always add the STORAGE clause, even
+ * when it is implicit in the original command.
+ */
+ tmp_obj = new_objtree("STORAGE");
+ append_object_object(tmp_obj, "%{type}T",
+ new_objtree_for_type(OidIsValid(opcForm->opckeytype) ?
+ opcForm->opckeytype : opcForm->opcintype,
+ -1));
+ list = list_make1(new_object_object(tmp_obj));
+
+ /* Add the declared operators */
+ foreach(cell, cmd->d.createopc.operators)
+ {
+ OpFamilyMember *oper = lfirst(cell);
+
+ tmp_obj = new_objtree_VA("OPERATOR %{num}n %{operator}O(%{ltype}T, %{rtype}T)", 4,
+ "num", ObjTypeInteger, oper->number,
+ "operator", ObjTypeObject,
+ new_objtree_for_qualname_id(OperatorRelationId,
+ oper->object),
+ "ltype", ObjTypeObject,
+ new_objtree_for_type(oper->lefttype, -1),
+ "rtype", ObjTypeObject,
+ new_objtree_for_type(oper->righttype, -1));
+
+ /* Add the FOR SEARCH / FOR ORDER BY clause */
+ if (oper->sortfamily == InvalidOid)
+ append_string_object(tmp_obj, "%{purpose}s", "purpose",
+ "FOR SEARCH");
+ else
+ {
+ ObjTree *tmp_obj2;
+
+ tmp_obj2 = new_objtree("FOR ORDER BY %{opfamily}D");
+ append_object_object(tmp_obj2, "opfamily",
+ new_objtree_for_qualname_id(OperatorFamilyRelationId,
+ oper->sortfamily));
+ append_object_object(tmp_obj, "%{purpose}s", tmp_obj2);
+ }
+
+ list = lappend(list, new_object_object(tmp_obj));
+ }
+
+ /* Add the declared support functions */
+ foreach(cell, cmd->d.createopc.procedures)
+ {
+ OpFamilyMember *proc = lfirst(cell);
+ HeapTuple procTup;
+ Form_pg_proc procForm;
+ Oid *proargtypes;
+ List *arglist = NIL;
+ int i;
+
+ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc->object));
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for procedure with OID %u",
+ proc->object);
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+ tmp_obj = new_objtree_VA("FUNCTION %{num}n (%{ltype}T, %{rtype}T) %{function}D", 4,
+ "num", ObjTypeInteger, proc->number,
+ "ltype", ObjTypeObject,
+ new_objtree_for_type(proc->lefttype, -1),
+ "rtype", ObjTypeObject,
+ new_objtree_for_type(proc->righttype, -1),
+ "function", ObjTypeObject,
+ new_objtree_for_qualname(procForm->pronamespace,
+ NameStr(procForm->proname)));
+
+ proargtypes = procForm->proargtypes.values;
+ for (i = 0; i < procForm->pronargs; i++)
+ {
+ ObjTree *arg;
+
+ arg = new_objtree_for_type(proargtypes[i], -1);
+ arglist = lappend(arglist, new_object_object(arg));
+ }
+
+ append_format_string(tmp_obj, "(");
+ append_array_object(tmp_obj, "%{argtypes:, }T", arglist);
+ append_format_string(tmp_obj, ")");
+
+ ReleaseSysCache(procTup);
+
+ list = lappend(list, new_object_object(tmp_obj));
+ }
+
+ append_array_object(ret, "AS %{items:, }s", list);
+
+ ReleaseSysCache(opfTup);
+ ReleaseSysCache(opcTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateOpFamilyStmt (CREATE OPERATOR FAMILY)
+ *
+ * Given a operator family OID and the parse tree that created it, return an
+ * ObjTree representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE OPERATOR FAMILY %{identity}D USING %{amname}I
+ */
+static ObjTree *
+deparse_CreateOpFamily(Oid objectId, Node *parsetree)
+{
+ HeapTuple opfTup;
+ HeapTuple amTup;
+ Form_pg_opfamily opfForm;
+ Form_pg_am amForm;
+ ObjTree *ret;
+
+ opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(opfTup))
+ elog(ERROR, "cache lookup failed for operator family with OID %u", objectId);
+ opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
+
+ amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod));
+ if (!HeapTupleIsValid(amTup))
+ elog(ERROR, "cache lookup failed for access method with OID %u",
+ opfForm->opfmethod);
+ amForm = (Form_pg_am) GETSTRUCT(amTup);
+
+ ret = new_objtree_VA("CREATE OPERATOR FAMILY %{identity}D USING %{amname}I", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(opfForm->opfnamespace,
+ NameStr(opfForm->opfname)),
+ "amname", ObjTypeString, NameStr(amForm->amname));
+
+ ReleaseSysCache(amTup);
+ ReleaseSysCache(opfTup);
+
+ return ret;
+}
+
+/*
+ * Add common clauses to CreatePolicy or AlterPolicy deparse objects.
+ */
+static void
+add_policy_clauses(ObjTree *ret, Oid policyOid, List *roles, bool do_qual,
+ bool do_with_check)
+{
+ Relation polRel = table_open(PolicyRelationId, AccessShareLock);
+ HeapTuple polTup = get_catalog_object_by_oid(polRel, Anum_pg_policy_oid, policyOid);
+ Form_pg_policy polForm;
+
+ if (!HeapTupleIsValid(polTup))
+ elog(ERROR, "cache lookup failed for policy with OID %u", policyOid);
+
+ polForm = (Form_pg_policy) GETSTRUCT(polTup);
+
+ /* Add the "ON table" clause */
+ append_object_object(ret, "ON %{table}D",
+ new_objtree_for_qualname_id(RelationRelationId,
+ polForm->polrelid));
+
+ /*
+ * Add the "TO role" clause, if any. In the CREATE case, it always
+ * contains at least PUBLIC, but in the ALTER case it might be empty.
+ */
+ if (roles)
+ {
+ List *list = NIL;
+ ListCell *cell;
+
+ foreach(cell, roles)
+ {
+ RoleSpec *spec = (RoleSpec *) lfirst(cell);
+
+ list = lappend(list,
+ new_object_object(new_objtree_for_rolespec(spec)));
+ }
+ append_array_object(ret, "TO %{role:, }R", list);
+ }
+ else
+ append_not_present(ret);
+
+ /* Add the USING clause, if any */
+ if (do_qual)
+ {
+ Datum deparsed;
+ Datum storedexpr;
+ bool isnull;
+
+ storedexpr = heap_getattr(polTup, Anum_pg_policy_polqual,
+ RelationGetDescr(polRel), &isnull);
+ if (isnull)
+ elog(ERROR, "null polqual expression in policy %u", policyOid);
+ deparsed = DirectFunctionCall2(pg_get_expr, storedexpr, polForm->polrelid);
+ append_string_object(ret, "USING (%{expression}s)", "expression",
+ TextDatumGetCString(deparsed));
+ }
+ else
+ append_not_present(ret);
+
+ /* Add the WITH CHECK clause, if any */
+ if (do_with_check)
+ {
+ Datum deparsed;
+ Datum storedexpr;
+ bool isnull;
+
+ storedexpr = heap_getattr(polTup, Anum_pg_policy_polwithcheck,
+ RelationGetDescr(polRel), &isnull);
+ if (isnull)
+ elog(ERROR, "null polwithcheck expression in policy %u", policyOid);
+ deparsed = DirectFunctionCall2(pg_get_expr, storedexpr, polForm->polrelid);
+ append_string_object(ret, "WITH CHECK (%{expression}s)",
+ "expression", TextDatumGetCString(deparsed));
+ }
+ else
+ append_not_present(ret);
+
+ relation_close(polRel, AccessShareLock);
+}
+
+/*
+ * Deparse a CreatePolicyStmt (CREATE POLICY)
+ *
+ * Given a policy OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE POLICY %{identity}I
+ */
+static ObjTree *
+deparse_CreatePolicyStmt(Oid objectId, Node *parsetree)
+{
+ CreatePolicyStmt *node = (CreatePolicyStmt *) parsetree;
+ ObjTree *ret;
+
+ ret = new_objtree_VA("CREATE POLICY %{identity}I", 1,
+ "identity", ObjTypeString, node->policy_name);
+
+ /* Add the rest of the stuff */
+ add_policy_clauses(ret, objectId, node->roles, node->qual != NULL,
+ node->with_check != NULL);
+
+ return ret;
+}
+
+/*
+ * Deparse a AlterPolicyStmt (ALTER POLICY)
+ *
+ * Given a policy OID and the parse tree of the ALTER POLICY command, return
+ * an ObjTree representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER POLICY %{identity}I
+ */
+static ObjTree *
+deparse_AlterPolicyStmt(Oid objectId, Node *parsetree)
+{
+ AlterPolicyStmt *node = (AlterPolicyStmt *) parsetree;
+ ObjTree *ret;
+
+ ret = new_objtree_VA("ALTER POLICY %{identity}I", 1,
+ "identity", ObjTypeString, node->policy_name);
+
+ /* Add the rest of the stuff */
+ add_policy_clauses(ret, objectId, node->roles, node->qual != NULL,
+ node->with_check != NULL);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateSchemaStmt.
+ *
+ * Given a schema OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE SCHEMA %{if_not_exists}s %{name}I AUTHORIZATION %{authorization}s
+*/
+static ObjTree *
+deparse_CreateSchemaStmt(Oid objectId, Node *parsetree)
+{
+ CreateSchemaStmt *node = (CreateSchemaStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *auth;
+
+ ret = new_objtree_VA("CREATE SCHEMA %{if_not_exists}s %{name}I", 2,
+ "if_not_exists", ObjTypeString,
+ node->if_not_exists ? "IF NOT EXISTS" : "",
+ "name", ObjTypeString,
+ node->schemaname ? node->schemaname : "");
+
+ auth = new_objtree("AUTHORIZATION");
+ if (node->authrole)
+ append_string_object(auth, "%{authorization_role}I",
+ "authorization_role",
+ get_rolespec_name(node->authrole));
+ else
+ {
+ append_null_object(auth, "%{authorization_role}I");
+ append_not_present(auth);
+ }
+ append_object_object(ret, "%{authorization}s", auth);
+
+ return ret;
+}
+
+/*
+ * Return the default value of a domain.
+ */
+static char *
+DomainGetDefault(HeapTuple domTup, bool missing_ok)
+{
+ Datum def;
+ Node *defval;
+ char *defstr;
+ bool isnull;
+
+ def = SysCacheGetAttr(TYPEOID, domTup, Anum_pg_type_typdefaultbin,
+ &isnull);
+ if (isnull)
+ {
+ if (!missing_ok)
+ elog(ERROR, "domain \"%s\" does not have a default value",
+ NameStr(((Form_pg_type) GETSTRUCT(domTup))->typname));
+ else
+ return NULL;
+ }
+
+ defval = stringToNode(TextDatumGetCString(def));
+ defstr = deparse_expression(defval, NULL /* dpcontext? */ ,
+ false, false);
+
+ return defstr;
+}
+
+/*
+ * Deparse a AlterDomainStmt.
+ *
+ * Verbose syntax
+ * ALTER DOMAIN %{identity}D DROP DEFAULT
+ * OR
+ * ALTER DOMAIN %{identity}D SET DEFAULT %{default}s
+ * OR
+ * ALTER DOMAIN %{identity}D DROP NOT NULL
+ * OR
+ * ALTER DOMAIN %{identity}D SET NOT NULL
+ * OR
+ * ALTER DOMAIN %{identity}D ADD CONSTRAINT %{constraint_name}s %{definition}s
+ * OR
+ * ALTER DOMAIN %{identity}D DROP CONSTRAINT IF EXISTS %{constraint_name}s %{cascade}s
+ * OR
+ * ALTER DOMAIN %{identity}D VALIDATE CONSTRAINT %{constraint_name}s
+ */
+static ObjTree *
+deparse_AlterDomainStmt(Oid objectId, Node *parsetree,
+ ObjectAddress constraintAddr)
+{
+ AlterDomainStmt *node = (AlterDomainStmt *) parsetree;
+ HeapTuple domTup;
+ Form_pg_type domForm;
+ ObjTree *ret;
+
+ domTup = SearchSysCache1(TYPEOID, objectId);
+ if (!HeapTupleIsValid(domTup))
+ elog(ERROR, "cache lookup failed for domain with OID %u", objectId);
+ domForm = (Form_pg_type) GETSTRUCT(domTup);
+
+ switch (node->subtype)
+ {
+ case 'T':
+ /* SET DEFAULT / DROP DEFAULT */
+ if (node->def == NULL)
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D DROP DEFAULT", 2,
+ "type", ObjTypeString, "drop default",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domForm->typnamespace,
+ NameStr(domForm->typname)));
+ else
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D SET DEFAULT %{default}s", 3,
+ "type", ObjTypeString, "set default",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domForm->typnamespace,
+ NameStr(domForm->typname)),
+ "default", ObjTypeString,
+ DomainGetDefault(domTup, false));
+ break;
+ case 'N':
+ /* DROP NOT NULL */
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D DROP NOT NULL", 2,
+ "type", ObjTypeString, "drop not null",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domForm->typnamespace,
+ NameStr(domForm->typname)));
+ break;
+ case 'O':
+ /* SET NOT NULL */
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D SET NOT NULL", 2,
+ "type", ObjTypeString, "set not null",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domForm->typnamespace,
+ NameStr(domForm->typname)));
+ break;
+ case 'C':
+ /*
+ * ADD CONSTRAINT. Only CHECK constraints are supported by
+ * domains
+ */
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D ADD CONSTRAINT %{constraint_name}s %{definition}s", 4,
+ "type", ObjTypeString, "add constraint",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domForm->typnamespace,
+ NameStr(domForm->typname)),
+ "constraint_name", ObjTypeString,
+ get_constraint_name(constraintAddr.objectId),
+ "definition", ObjTypeString,
+ pg_get_constraintdef_string(constraintAddr.objectId));
+ break;
+ case 'V':
+ /* VALIDATE CONSTRAINT */
+ /*
+ * Process subtype=specific options. Validation a constraint
+ * requires its name.
+ */
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D VALIDATE CONSTRAINT %{constraint_name}s", 3,
+ "type", ObjTypeString, "validate constraint",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domForm->typnamespace,
+ NameStr(domForm->typname)),
+ "constraint_name", ObjTypeString, node->name);
+ break;
+ case 'X':
+ {
+ ObjTree *tmp_obj;
+
+ /* DROP CONSTRAINT */
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D DROP CONSTRAINT IF EXISTS %{constraint_name}s", 3,
+ "type", ObjTypeString, "drop constraint",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domForm->typnamespace,
+ NameStr(domForm->typname)),
+ "constraint_name", ObjTypeString, node->name);
+
+ tmp_obj = new_objtree_VA("CASCADE", 1,
+ "present", ObjTypeBool, node->behavior == DROP_CASCADE);
+
+ append_object_object(ret, "%{cascade}s", tmp_obj);
+ }
+ break;
+ default:
+ elog(ERROR, "invalid subtype %c", node->subtype);
+ }
+
+ /* Done */
+ ReleaseSysCache(domTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateStatsStmt.
+ *
+ * Given a statistics OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE STATISTICS %{if_not_exists}s %{identity}D ON %{expr:, }s FROM %{stat_table_identity}D
+ */
+static ObjTree *
+deparse_CreateStatisticsStmt(Oid objectId, Node *parsetree)
+{
+ CreateStatsStmt *node = (CreateStatsStmt *) parsetree;
+ Form_pg_statistic_ext statform;
+ ObjTree *ret;
+ HeapTuple tup;
+ Datum datum;
+ bool isnull;
+ List *statexprs = NIL;
+
+ tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for statistic with OID %u", objectId);
+
+ statform = (Form_pg_statistic_ext) GETSTRUCT(tup);
+
+ ret = new_objtree_VA("CREATE STATISTICS %{if_not_exists}s %{identity}D", 2,
+ "if_not_exists", ObjTypeString,
+ node->if_not_exists ? "IF NOT EXISTS" : "",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(statform->stxnamespace,
+ NameStr(statform->stxname)));
+
+ datum = SysCacheGetAttr(STATEXTOID, tup, Anum_pg_statistic_ext_stxexprs,
+ &isnull);
+ if (!isnull)
+ {
+ ListCell *lc;
+ Relation statsrel;
+ List *context;
+ List *exprs = NIL;
+ char *exprsString;
+
+ statsrel = relation_open(statform->stxrelid, AccessShareLock);
+ context = deparse_context_for(RelationGetRelationName(statsrel),
+ RelationGetRelid(statsrel));
+
+ exprsString = TextDatumGetCString(datum);
+ exprs = (List *) stringToNode(exprsString);
+
+ foreach(lc, exprs)
+ {
+ Node *expr = (Node *) lfirst(lc);
+ char *statexpr;
+
+ statexpr = deparse_expression(expr, context, false, false);
+ statexprs = lappend(statexprs, new_string_object(statexpr));
+ }
+
+ append_array_object(ret, "ON %{expr:, }s", statexprs);
+ pfree(exprsString);
+ relation_close(statsrel, AccessShareLock);
+ }
+
+ datum = SysCacheGetAttr(STATEXTOID, tup, Anum_pg_statistic_ext_stxkeys,
+ &isnull);
+ if (!isnull)
+ {
+ int keyno;
+ char *attname;
+ List *statcols = NIL;
+ int2vector *indoption;
+
+ indoption = (int2vector *) DatumGetPointer(datum);
+
+ for (keyno = 0; keyno < indoption->dim1; keyno++)
+ {
+ attname = get_attname(statform->stxrelid, indoption->values[keyno],
+ false);
+ statcols = lappend(statcols, new_string_object(attname));
+ }
+
+ if (indoption->dim1)
+ {
+ /* Append a ',' if statexprs is present else append 'ON' */
+ append_string_object(ret, "%{comma}s", "comma",
+ statexprs ? "," : "ON");
+ append_array_object(ret, "%{cols:, }s", statcols);
+ }
+ }
+
+ append_object_object(ret, "FROM %{stat_table_identity}D",
+ new_objtree_for_qualname(get_rel_namespace(statform->stxrelid),
+ get_rel_name(statform->stxrelid)));
+
+ ReleaseSysCache(tup);
+
+ return ret;
+}
+
+/*
+ * Deparse an CreateForeignServerStmt (CREATE SERVER)
+ *
+ * Given a server OID and the parse tree that created it, return an ObjTree
+ * representing the alter command.
+ *
+ * Verbose syntax
+ * CREATE SERVER %{identity}I %{type}s %{version}s FOREIGN DATA WRAPPER %{fdw}I
+ * %{generic_options}s
+ */
+static ObjTree *
+deparse_CreateForeignServerStmt(Oid objectId, Node *parsetree)
+{
+ CreateForeignServerStmt *node = (CreateForeignServerStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *tmp;
+
+ ret = new_objtree_VA("CREATE SERVER %{identity}I", 1,
+ "identity", ObjTypeString, node->servername);
+
+ /* Add a TYPE clause, if any */
+ tmp = new_objtree("TYPE");
+ if (node->servertype)
+ append_string_object(tmp, "%{type}L", "type", node->servertype);
+ else
+ append_not_present(tmp);
+ append_object_object(ret, "%{type}s", tmp);
+
+ /* Add a VERSION clause, if any */
+ tmp = new_objtree("VERSION");
+ if (node->version)
+ append_string_object(tmp, "%{version}L", "version", node->version);
+ else
+ append_not_present(tmp);
+ append_object_object(ret, "%{version}s", tmp);
+
+ append_string_object(ret, "FOREIGN DATA WRAPPER %{fdw}I", "fdw",
+ node->fdwname);
+
+ /* Add an OPTIONS clause, if any */
+ append_object_object(ret, "%{generic_options}s",
+ deparse_FdwOptions(node->options, NULL, false));
+
+ return ret;
+}
+
+/*
+ * Deparse an AlterForeignServerStmt (ALTER SERVER)
+ *
+ * Given a server OID and the parse tree that created it, return an ObjTree
+ * representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER SERVER %{identity}I %{version}s %{generic_options}s
+ */
+static ObjTree *
+deparse_AlterForeignServerStmt(Oid objectId, Node *parsetree)
+{
+ AlterForeignServerStmt *node = (AlterForeignServerStmt *) parsetree;
+ ObjTree *tmp;
+
+ /* Add a VERSION clause, if any */
+ tmp = new_objtree("VERSION");
+ if (node->has_version && node->version)
+ append_string_object(tmp, "%{version}L", "version", node->version);
+ else if (node->has_version)
+ append_string_object(tmp, "version", "version", "NULL");
+ else
+ append_not_present(tmp);
+
+ return new_objtree_VA("ALTER SERVER %{identity}I %{version}s %{generic_options}s", 3,
+ "identity", ObjTypeString, node->servername,
+ "version", ObjTypeObject, tmp,
+ "generic_options", ObjTypeObject,
+ deparse_FdwOptions(node->options, NULL, false));
+}
+
+/*
+ * Deparse a CreatePLangStmt.
+ *
+ * Given a language OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE %{or_replace}s %{trusted}s LANGUAGE %{identity}s HANDLER %{handler}D
+ * %{inline}s %{validator}s
+ */
+static ObjTree *
+deparse_CreateLangStmt(Oid objectId, Node *parsetree)
+{
+ CreatePLangStmt *node = (CreatePLangStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *tmp;
+ HeapTuple langTup;
+ Form_pg_language langForm;
+
+ langTup = SearchSysCache1(LANGOID,
+ ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(langTup))
+ elog(ERROR, "cache lookup failed for language with OID %u", objectId);
+ langForm = (Form_pg_language) GETSTRUCT(langTup);
+
+ ret = new_objtree_VA("CREATE %{or_replace}s %{trusted}s LANGUAGE %{identity}s", 3,
+ "or_replace", ObjTypeString,
+ node->replace ? "OR REPLACE" : "",
+ "trusted", ObjTypeString,
+ langForm->lanpltrusted ? "TRUSTED" : "",
+ "identity", ObjTypeString, node->plname);
+
+ if (node->plhandler != NIL)
+ {
+ /* Add the HANDLER clause */
+ append_object_object(ret, "HANDLER %{handler}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ langForm->lanplcallfoid));
+
+ /* Add the INLINE clause, if any */
+ tmp = new_objtree("INLINE");
+ if (OidIsValid(langForm->laninline))
+ {
+ append_object_object(tmp, "%{handler_name}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ langForm->laninline));
+ }
+ else
+ append_not_present(tmp);
+ append_object_object(ret, "%{inline}s", tmp);
+
+ /* Add the VALIDATOR clause, if any */
+ tmp = new_objtree("VALIDATOR");
+ if (OidIsValid(langForm->lanvalidator))
+ {
+ append_object_object(tmp, "%{handler_name}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ langForm->lanvalidator));
+ }
+ else
+ append_not_present(tmp);
+ append_object_object(ret, "%{validator}s", tmp);
+ }
+
+ ReleaseSysCache(langTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateForeignTableStmt (CREATE FOREIGN TABLE).
+ *
+ * Given a table OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * CREATE FOREIGN TABLE %{if_not_exists}s %{identity}D
+ * [(%{table_elements:, }s) %{inherits}s
+ * | PARTITION OF %{parent_identity}D (%{typed_table_elements:, }s) %{partition_bound}s]
+ * SERVER %{server}I %{generic_options}s
+ */
+static ObjTree *
+deparse_CreateForeignTableStmt(Oid objectId, Node *parsetree)
+{
+ CreateForeignTableStmt *stmt = (CreateForeignTableStmt *) parsetree;
+ Relation relation = relation_open(objectId, AccessShareLock);
+ List *dpcontext;
+ ObjTree *createStmt;
+ ObjTree *tmpobj;
+ List *tableelts = NIL;
+
+ createStmt = new_objtree("CREATE FOREIGN TABLE");
+
+ append_string_object(createStmt, "%{if_not_exists}s", "if_not_exists",
+ stmt->base.if_not_exists ? "IF NOT EXISTS" : "");
+
+ tmpobj = new_objtree_for_qualname(relation->rd_rel->relnamespace,
+ RelationGetRelationName(relation));
+ append_object_object(createStmt, "%{identity}D", tmpobj);
+
+ dpcontext = deparse_context_for(RelationGetRelationName(relation),
+ objectId);
+ if (stmt->base.partbound)
+ {
+ /* Partitioned table */
+ List *parents;
+ ObjElem *elem;
+
+ parents = deparse_InhRelations(objectId);
+ elem = (ObjElem *) linitial(parents);
+
+ Assert(list_length(parents) == 1);
+
+ append_format_string(createStmt, "PARTITION OF");
+
+ append_object_object(createStmt, "%{parent_identity}D",
+ elem->value.object);
+
+ tableelts = deparse_TableElements(relation, stmt->base.tableElts, dpcontext,
+ true, /* typed table */
+ false); /* not composite */
+ tableelts = obtainConstraints(tableelts, objectId, InvalidOid,
+ ConstrObjForeignTable);
+
+ tmpobj = new_objtree("");
+ if (tableelts)
+ append_array_object(tmpobj, "(%{elements:, }s)", tableelts);
+ else
+ append_not_present(tmpobj);
+
+ append_object_object(createStmt, "%{typed_table_elements}s", tmpobj);
+
+ /*
+ * Add the partition_bound_spec, i.e. the FOR VALUES clause.
+ * Get pg_class.relpartbound. We cannot use partbound in the parsetree
+ * directly as it's the original partbound expression which haven't
+ * been transformed.
+ */
+ append_string_object(createStmt, "%{partition_bound}s", "partition_bound",
+ RelationGetPartitionBound(objectId));
+
+ /* No PARTITION BY clause for CREATE FOREIGN TABLE */
+ }
+ else
+ {
+ tableelts = deparse_TableElements(relation, stmt->base.tableElts, dpcontext,
+ false, /* not typed table */
+ false); /* not composite */
+ tableelts = obtainConstraints(tableelts, objectId, InvalidOid,
+ ConstrObjForeignTable);
+
+ if (tableelts)
+ append_array_object(createStmt, "(%{table_elements:, }s)", tableelts);
+ else
+ append_format_string(createStmt, "()");
+
+ /*
+ * Add inheritance specification. We cannot simply scan the list of
+ * parents from the parser node, because that may lack the actual
+ * qualified names of the parent relations. Rather than trying to
+ * re-resolve them from the information in the parse node, it seems
+ * more accurate and convenient to grab it from pg_inherits.
+ */
+ tmpobj = new_objtree("INHERITS");
+ if (stmt->base.inhRelations != NIL)
+ append_array_object(tmpobj, "(%{parents:, }D)", deparse_InhRelations(objectId));
+ else
+ {
+ append_null_object(tmpobj, "(%{parents:, }D)");
+ append_not_present(tmpobj);
+ }
+ append_object_object(createStmt, "%{inherits}s", tmpobj);
+ }
+
+ append_string_object(createStmt, "SERVER %{server}I", "server", stmt->servername);
+
+ /* add an OPTIONS clause, if any */
+ append_object_object(createStmt, "%{generic_options}s",
+ deparse_FdwOptions(stmt->options, NULL, false));
+
+ relation_close(relation, AccessShareLock);
+
+ return createStmt;
+}
+
+/*
+ * Deparse a DefineStmt.
+ */
+static ObjTree *
+deparse_DefineStmt(Oid objectId, Node *parsetree, ObjectAddress secondaryObj)
+{
+ DefineStmt *define = (DefineStmt *) parsetree;
+
+ switch (define->kind)
+ {
+ case OBJECT_AGGREGATE:
+ return deparse_DefineStmt_Aggregate(objectId, define);
+
+ case OBJECT_COLLATION:
+ return deparse_DefineStmt_Collation(objectId, define, secondaryObj);
+
+ case OBJECT_OPERATOR:
+ return deparse_DefineStmt_Operator(objectId, define);
+
+ case OBJECT_TSCONFIGURATION:
+ return deparse_DefineStmt_TSConfig(objectId, define, secondaryObj);
+
+ case OBJECT_TSDICTIONARY:
+ return deparse_DefineStmt_TSDictionary(objectId, define);
+
+ case OBJECT_TSPARSER:
+ return deparse_DefineStmt_TSParser(objectId, define);
+
+ case OBJECT_TSTEMPLATE:
+ return deparse_DefineStmt_TSTemplate(objectId, define);
+
+ case OBJECT_TYPE:
+ return deparse_DefineStmt_Type(objectId, define);
+
+ default:
+ elog(ERROR, "unsupported object kind: %d", define->kind);
+ }
+
+ return NULL;
+}
+
+/*
+ * Form an ObjElem to be used as a single argument in an aggregate argument
+ * list
+ */
+static ObjElem *
+form_agg_argument(int idx, char *modes, char **names, Oid *types)
+{
+ ObjTree *arg;
+
+ arg = new_objtree("");
+
+ append_string_object(arg, "%{mode}s", "mode",
+ (modes && modes[idx] == 'v') ? "VARIADIC" : "");
+ append_string_object(arg, "%{name}s", "name", names ? names[idx] : "");
+ append_object_object(arg, "%{type}T",
+ new_objtree_for_type(types[idx], -1));
+
+ return new_object_object(arg);
+}
+
+/*
+ * Deparse a DefineStmt (CREATE AGGREGATE)
+ *
+ * Given a aggregate OID, return an ObjTree representing the CREATE command.
+ *
+ * Verbose syntax
+ * CREATE %{or_replace}s AGGREGATE %{identity}D(%{types}s) (%{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_Aggregate(Oid objectId, DefineStmt *define)
+{
+ HeapTuple aggTup;
+ HeapTuple procTup;
+ ObjTree *stmt;
+ ObjTree *tmp;
+ List *list;
+ Datum initval;
+ bool isnull;
+ Form_pg_aggregate agg;
+ Form_pg_proc proc;
+ Form_pg_operator op;
+ HeapTuple tup;
+
+ aggTup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(aggTup))
+ elog(ERROR, "cache lookup failed for aggregate with OID %u", objectId);
+ agg = (Form_pg_aggregate) GETSTRUCT(aggTup);
+
+ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(agg->aggfnoid));
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for procedure with OID %u",
+ agg->aggfnoid);
+ proc = (Form_pg_proc) GETSTRUCT(procTup);
+
+ stmt = new_objtree_VA("CREATE %{or_replace}s AGGREGATE", 1,
+ "or_replace", ObjTypeString, define->replace ? "OR REPLACE" : "");
+
+ append_object_object(stmt, "%{identity}D",
+ new_objtree_for_qualname(proc->pronamespace,
+ NameStr(proc->proname)));
+
+ /*
+ * Add the argument list. There are three cases to consider:
+ *
+ * 1. no arguments, in which case the signature is (*).
+ *
+ * 2. Not an ordered-set aggregate. In this case there's one or more
+ * arguments.
+ *
+ * 3. Ordered-set aggregates. These have zero or more direct arguments, and
+ * one or more ordered arguments.
+ *
+ * We don't need to consider default values or table parameters, and the
+ * only mode that we need to consider is VARIADIC.
+ */
+
+ if (proc->pronargs == 0)
+ append_string_object(stmt, "(%{types}s)", "types", "*");
+ else if (!AGGKIND_IS_ORDERED_SET(agg->aggkind))
+ {
+ int i;
+ int nargs;
+ Oid *types;
+ char *modes;
+ char **names;
+
+ nargs = get_func_arg_info(procTup, &types, &names, &modes);
+
+ /* only direct arguments in this case */
+ list = NIL;
+ for (i = 0; i < nargs; i++)
+ {
+ list = lappend(list, form_agg_argument(i, modes, names, types));
+ }
+
+ tmp = new_objtree_VA("%{direct:, }s", 1,
+ "direct", ObjTypeArray, list);
+ append_object_object(stmt, "(%{types}s)", tmp);
+ }
+ else
+ {
+ int i;
+ int nargs;
+ Oid *types;
+ char *modes;
+ char **names;
+
+ nargs = get_func_arg_info(procTup, &types, &names, &modes);
+
+ /* direct arguments ... */
+ list = NIL;
+ for (i = 0; i < agg->aggnumdirectargs; i++)
+ {
+ list = lappend(list, form_agg_argument(i, modes, names, types));
+ }
+ tmp = new_objtree_VA("%{direct:, }s", 1,
+ "direct", ObjTypeArray, list);
+
+ /*
+ * ... and aggregated arguments. If the last direct argument is
+ * VARIADIC, we need to repeat it here rather than searching for more
+ * arguments. (See aggr_args production in gram.y for an explanation.)
+ */
+ if (modes && modes[agg->aggnumdirectargs - 1] == 'v')
+ {
+ list = list_make1(form_agg_argument(agg->aggnumdirectargs - 1,
+ modes, names, types));
+ }
+ else
+ {
+ list = NIL;
+ for (i = agg->aggnumdirectargs; i < nargs; i++)
+ {
+ list = lappend(list, form_agg_argument(i, modes, names, types));
+ }
+ }
+ append_array_object(tmp, "ORDER BY %{aggregated:, }s", list);
+
+ append_object_object(stmt, "(%{types}s)", tmp);
+ }
+
+ /* Add the definition clause */
+ list = NIL;
+
+ /* SFUNC */
+ tmp = new_objtree_VA("SFUNC=%{procedure}D", 1,
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId, agg->aggtransfn));
+ list = lappend(list, new_object_object(tmp));
+
+ /* STYPE */
+ tmp = new_objtree_VA("STYPE=%{type}T", 1,
+ "type", ObjTypeObject,
+ new_objtree_for_type(agg->aggtranstype, -1));
+ list = lappend(list, new_object_object(tmp));
+
+ /* SSPACE */
+ tmp = new_objtree("SSPACE=");
+ if (agg->aggtransspace != 0)
+ append_int_object(tmp,"%{space}n", agg->aggtransspace);
+ else
+ append_not_present(tmp);
+
+ list = lappend(list, new_object_object(tmp));
+
+ /* FINALFUNC */
+ tmp = new_objtree("FINALFUNC=");
+ if (OidIsValid(agg->aggfinalfn))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ agg->aggfinalfn));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* FINALFUNC_EXTRA */
+ tmp = new_objtree("FINALFUNC_EXTRA=");
+ if (agg->aggfinalextra)
+ append_string_object(tmp, "%{value}s", "value", "true");
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* INITCOND */
+ initval = SysCacheGetAttr(AGGFNOID, aggTup,
+ Anum_pg_aggregate_agginitval,
+ &isnull);
+ tmp = new_objtree("INITCOND=");
+ if (!isnull)
+ append_string_object(tmp,"%{initval}L", "initval", TextDatumGetCString(initval));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* MSFUNC */
+ tmp = new_objtree("MSFUNC=");
+ if (OidIsValid(agg->aggmtransfn))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId, agg->aggmtransfn));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* MSTYPE */
+ tmp = new_objtree("MSTYPE=");
+ if (OidIsValid(agg->aggmtranstype))
+ append_object_object(tmp, "%{type}T", new_objtree_for_type(agg->aggmtranstype, -1));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* MSSPACE */
+ tmp = new_objtree("MSSPACE=");
+ if (agg->aggmtransspace != 0)
+ append_int_object(tmp, "%{space}n", agg->aggmtransspace);
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* MINVFUNC */
+ tmp = new_objtree("MINVFUNC=");
+ if (OidIsValid(agg->aggminvtransfn))
+ append_object_object(tmp, "%{type}T", new_objtree_for_qualname_id(ProcedureRelationId,
+ agg->aggminvtransfn));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* MFINALFUNC */
+ tmp = new_objtree("MFINALFUNC=");
+ if (OidIsValid(agg->aggmfinalfn))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ agg->aggmfinalfn));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* MFINALFUNC_EXTRA */
+ tmp = new_objtree("MFINALFUNC_EXTRA=");
+ if (agg->aggmfinalextra)
+ append_string_object(tmp, "%{value}s", "value", "true");
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* MINITCOND */
+ initval = SysCacheGetAttr(AGGFNOID, aggTup,
+ Anum_pg_aggregate_aggminitval,
+ &isnull);
+ tmp = new_objtree("MINITCOND=");
+ if (!isnull)
+ append_string_object(tmp, "%{initval}L", "initval", TextDatumGetCString(initval));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* HYPOTHETICAL */
+ tmp = new_objtree("HYPOTHETICAL=");
+ if (agg->aggkind == AGGKIND_HYPOTHETICAL)
+ append_string_object(tmp, "%{value}s", "value", "true");
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* SORTOP */
+ tmp = new_objtree("SORTOP=");
+ if (OidIsValid(agg->aggsortop))
+ {
+ tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(agg->aggsortop));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for operator with OID %u", agg->aggsortop);
+ op = (Form_pg_operator) GETSTRUCT(tup);
+ append_object_object(tmp, "%{operator}D",
+ new_objtree_for_qualname(op->oprnamespace,
+ NameStr(op->oprname)));
+
+ ReleaseSysCache(tup);
+ }
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* Done with the definition clause */
+ append_array_object(stmt, "(%{elems:, }s)", list);
+
+ ReleaseSysCache(procTup);
+ ReleaseSysCache(aggTup);
+
+ return stmt;
+}
+
+/*
+ * Deparse a DefineStmt (CREATE COLLATION)
+ *
+ * Given a collation OID, return an ObjTree representing the CREATE command.
+ *
+ * Verbose syntax
+ * CREATE COLLATION %{if_not_exists}s %{identity}D FROM %{from_identity}D
+ * (%{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_Collation(Oid objectId, DefineStmt *define,
+ ObjectAddress fromCollid)
+{
+ ObjTree *ret;
+ HeapTuple colTup;
+ Form_pg_collation colForm;
+ Datum datum;
+ bool isnull;
+ ObjTree *tmp;
+ List *list = NIL;
+
+ colTup = SearchSysCache1(COLLOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(colTup))
+ elog(ERROR, "cache lookup failed for collation with OID %u", objectId);
+ colForm = (Form_pg_collation) GETSTRUCT(colTup);
+
+ ret = new_objtree_VA("CREATE COLLATION %{if_not_exists}s %{identity}D", 2,
+ "if_not_exists", ObjTypeString,
+ define->if_not_exists ? "IF NOT EXISTS" : "",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(colForm->collnamespace,
+ NameStr(colForm->collname)));
+
+ if (OidIsValid(fromCollid.objectId))
+ {
+ Oid collid = fromCollid.objectId;
+ HeapTuple tp;
+ Form_pg_collation fromColForm;
+
+ /*
+ * CREATE COLLATION %{identity}D FROM existing_collation;
+ */
+ tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for collation with OID %u", collid);
+
+ fromColForm = (Form_pg_collation) GETSTRUCT(tp);
+
+ append_object_object(ret, "FROM %{from_identity}D",
+ new_objtree_for_qualname(fromColForm->collnamespace,
+ NameStr(fromColForm->collname)));
+
+ ReleaseSysCache(tp);
+ ReleaseSysCache(colTup);
+ return ret;
+ }
+
+ /* LOCALE */
+ datum = SysCacheGetAttr(COLLOID, colTup, Anum_pg_collation_colliculocale, &isnull);
+ if (!isnull)
+ {
+ tmp = new_objtree_VA("LOCALE=%{locale}L", 2,
+ "clause", ObjTypeString, "locale",
+ "locale", ObjTypeString,
+ psprintf("%s", TextDatumGetCString(datum)));
+ list = lappend(list, new_object_object(tmp));
+ }
+
+ /* LC_COLLATE */
+ datum = SysCacheGetAttr(COLLOID, colTup, Anum_pg_collation_collcollate, &isnull);
+ if (!isnull)
+ {
+ tmp = new_objtree_VA("LC_COLLATE=%{collate}L", 2,
+ "clause", ObjTypeString, "collate",
+ "collate", ObjTypeString,
+ psprintf("%s", TextDatumGetCString(datum)));
+ list = lappend(list, new_object_object(tmp));
+ }
+
+ /* LC_CTYPE */
+ datum = SysCacheGetAttr(COLLOID, colTup, Anum_pg_collation_collctype, &isnull);
+ if (!isnull)
+ {
+ tmp = new_objtree_VA("LC_CTYPE=%{ctype}L", 2,
+ "clause", ObjTypeString, "ctype",
+ "ctype", ObjTypeString,
+ psprintf("%s", TextDatumGetCString(datum)));
+ list = lappend(list, new_object_object(tmp));
+ }
+
+ /* PROVIDER */
+ if (colForm->collprovider == COLLPROVIDER_ICU)
+ {
+ tmp = new_objtree_VA("PROVIDER=%{provider}L", 2,
+ "clause", ObjTypeString, "provider",
+ "provider", ObjTypeString,
+ psprintf("%s", "icu"));
+ list = lappend(list, new_object_object(tmp));
+ }
+ else if (colForm->collprovider == COLLPROVIDER_LIBC)
+ {
+ tmp = new_objtree_VA("PROVIDER=%{provider}L", 2,
+ "clause", ObjTypeString, "provider",
+ "provider", ObjTypeString,
+ psprintf("%s", "libc"));
+ list = lappend(list, new_object_object(tmp));
+ }
+
+ /* DETERMINISTIC */
+ if (colForm->collisdeterministic)
+ {
+ tmp = new_objtree_VA("DETERMINISTIC=%{deterministic}L", 2,
+ "clause", ObjTypeString, "deterministic",
+ "deterministic", ObjTypeString,
+ psprintf("%s", "true"));
+ list = lappend(list, new_object_object(tmp));
+ }
+
+ /* VERSION */
+ datum = SysCacheGetAttr(COLLOID, colTup, Anum_pg_collation_collversion, &isnull);
+ if (!isnull)
+ {
+ tmp = new_objtree_VA("VERSION=%{version}L", 2,
+ "clause", ObjTypeString, "version",
+ "version", ObjTypeString,
+ psprintf("%s", TextDatumGetCString(datum)));
+ list = lappend(list, new_object_object(tmp));
+ }
+
+ append_array_object(ret, "(%{elems:, }s)", list);
+ ReleaseSysCache(colTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a DefineStmt (CREATE OPERATOR)
+ *
+ * Given a operator OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE OPERATOR %{identity}O (%{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_Operator(Oid objectId, DefineStmt *define)
+{
+ HeapTuple oprTup;
+ ObjTree *ret;
+ ObjTree *tmp_obj;
+ List *list = NIL;
+ Form_pg_operator oprForm;
+
+ oprTup = SearchSysCache1(OPEROID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(oprTup))
+ elog(ERROR, "cache lookup failed for operator with OID %u", objectId);
+ oprForm = (Form_pg_operator) GETSTRUCT(oprTup);
+
+ ret = new_objtree_VA("CREATE OPERATOR %{identity}O", 1,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(oprForm->oprnamespace,
+ NameStr(oprForm->oprname)));
+
+ /* PROCEDURE */
+ tmp_obj = new_objtree_VA("PROCEDURE=%{procedure}D", 2,
+ "clause", ObjTypeString, "procedure",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ oprForm->oprcode));
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* LEFTARG */
+ tmp_obj = new_objtree_VA("LEFTARG=", 1,
+ "clause", ObjTypeString, "leftarg");
+ if (OidIsValid(oprForm->oprleft))
+ append_object_object(tmp_obj, "%{type}T",
+ new_objtree_for_type(oprForm->oprleft, -1));
+ else
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* RIGHTARG */
+ tmp_obj = new_objtree_VA("RIGHTARG=", 1,
+ "clause", ObjTypeString, "rightarg");
+ if (OidIsValid(oprForm->oprright))
+ append_object_object(tmp_obj, "%{type}T",
+ new_objtree_for_type(oprForm->oprright, -1));
+ else
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* COMMUTATOR */
+ tmp_obj = new_objtree_VA("COMMUTATOR=", 1,
+ "clause", ObjTypeString, "commutator");
+ if (OidIsValid(oprForm->oprcom))
+ append_object_object(tmp_obj, "%{oper}D",
+ new_objtree_for_qualname_id(OperatorRelationId,
+ oprForm->oprcom));
+ else
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* NEGATOR */
+ tmp_obj = new_objtree_VA("NEGATOR=", 1,
+ "clause", ObjTypeString, "negator");
+ if (OidIsValid(oprForm->oprnegate))
+ append_object_object(tmp_obj, "%{oper}D",
+ new_objtree_for_qualname_id(OperatorRelationId,
+ oprForm->oprnegate));
+ else
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* RESTRICT */
+ tmp_obj = new_objtree_VA("RESTRICT=", 1,
+ "clause", ObjTypeString, "restrict");
+ if (OidIsValid(oprForm->oprrest))
+ append_object_object(tmp_obj, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ oprForm->oprrest));
+ else
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* JOIN */
+ tmp_obj = new_objtree_VA("JOIN=", 1,
+ "clause", ObjTypeString, "join");
+ if (OidIsValid(oprForm->oprjoin))
+ append_object_object(tmp_obj, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ oprForm->oprjoin));
+ else
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* HASHES */
+ tmp_obj = new_objtree_VA("HASHES", 1,
+ "clause", ObjTypeString, "hashes");
+ if (!oprForm->oprcanhash)
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ /* MERGES */
+ tmp_obj = new_objtree_VA("MERGES", 1,
+ "clause", ObjTypeString, "merges");
+ if (!oprForm->oprcanmerge)
+ append_not_present(tmp_obj);
+ list = lappend(list, new_object_object(tmp_obj));
+
+ append_array_object(ret, "(%{elems:, }s)", list);
+
+ ReleaseSysCache(oprTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a CREATE TYPE statement.
+ *
+ * Verbose syntax
+ * CREATE TYPE %{identity}D %{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_Type(Oid objectId, DefineStmt *define)
+{
+ HeapTuple typTup;
+ ObjTree *ret;
+ ObjTree *tmp;
+ List *list = NIL;
+ char *str;
+ Datum dflt;
+ bool isnull;
+ Form_pg_type typForm;
+
+ typTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(typTup))
+ elog(ERROR, "cache lookup failed for type with OID %u", objectId);
+ typForm = (Form_pg_type) GETSTRUCT(typTup);
+
+ ret = new_objtree_VA("CREATE TYPE %{identity}D", 1,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(typForm->typnamespace,
+ NameStr(typForm->typname)));
+
+ /* Shell types. */
+ if (!typForm->typisdefined)
+ {
+ ReleaseSysCache(typTup);
+ return ret;
+ }
+
+ /* Add the definition clause */
+ /* INPUT */
+ tmp = new_objtree_VA("(INPUT=%{procedure}D", 2,
+ "clause", ObjTypeString, "input",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ typForm->typinput));
+ list = lappend(list, new_object_object(tmp));
+
+ /* OUTPUT */
+ tmp = new_objtree_VA("OUTPUT=%{procedure}D", 2,
+ "clause", ObjTypeString, "output",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ typForm->typoutput));
+ list = lappend(list, new_object_object(tmp));
+
+ /* RECEIVE */
+ tmp = new_objtree_VA("RECEIVE=", 1,
+ "clause", ObjTypeString, "receive");
+ if (OidIsValid(typForm->typreceive))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ typForm->typreceive));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* SEND */
+ tmp = new_objtree_VA("SEND=", 1,
+ "clause", ObjTypeString, "send");
+ if (OidIsValid(typForm->typsend))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ typForm->typsend));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* TYPMOD_IN */
+ tmp = new_objtree_VA("TYPMOD_IN=", 1,
+ "clause", ObjTypeString, "typmod_in");
+ if (OidIsValid(typForm->typmodin))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ typForm->typmodin));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* TYPMOD_OUT */
+ tmp = new_objtree_VA("TYPMOD_OUT=", 1,
+ "clause", ObjTypeString, "typmod_out");
+ if (OidIsValid(typForm->typmodout))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ typForm->typmodout));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* ANALYZE */
+ tmp = new_objtree_VA("ANALYZE=", 1,
+ "clause", ObjTypeString, "analyze");
+ if (OidIsValid(typForm->typanalyze))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ typForm->typanalyze));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* INTERNALLENGTH */
+ if (typForm->typlen == -1)
+ tmp = new_objtree("INTERNALLENGTH=VARIABLE");
+ else
+ tmp = new_objtree_VA("INTERNALLENGTH=%{typlen}n", 1,
+ "typlen", ObjTypeInteger, typForm->typlen);
+
+ list = lappend(list, new_object_object(tmp));
+
+ /* PASSEDBYVALUE */
+ tmp = new_objtree_VA("PASSEDBYVALUE", 1,
+ "clause", ObjTypeString, "passedbyvalue");
+ if (!typForm->typbyval)
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* XXX it's odd to represent alignment with schema-qualified type names */
+ switch (typForm->typalign)
+ {
+ case 'd':
+ str = "pg_catalog.float8";
+ break;
+ case 'i':
+ str = "pg_catalog.int4";
+ break;
+ case 's':
+ str = "pg_catalog.int2";
+ break;
+ case 'c':
+ str = "pg_catalog.bpchar";
+ break;
+ default:
+ elog(ERROR, "invalid alignment %c", typForm->typalign);
+ }
+
+ /* ALIGNMENT */
+ tmp = new_objtree_VA("ALIGNMENT=%{align}s", 2,
+ "clause", ObjTypeString, "alignment",
+ "align", ObjTypeString, str);
+ list = lappend(list, new_object_object(tmp));
+
+ /* STORAGE */
+ tmp = new_objtree_VA("STORAGE=%{storage}s", 2,
+ "clause", ObjTypeString, "storage",
+ "storage", ObjTypeString,
+ get_type_storage(typForm->typstorage));
+ list = lappend(list, new_object_object(tmp));
+
+ /* CATEGORY */
+ tmp = new_objtree_VA("CATEGORY=%{category}s", 2,
+ "clause", ObjTypeString, "category",
+ "category", ObjTypeString,
+ psprintf("%c", typForm->typcategory));
+ list = lappend(list, new_object_object(tmp));
+
+ /* PREFERRED */
+ tmp = new_objtree_VA("PREFERRED=%{preferred}s", 1,
+ "preferred", ObjTypeString,
+ typForm->typispreferred ? "TRUE" : "FALSE");
+ if (!typForm->typispreferred)
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* DEFAULT */
+ dflt = SysCacheGetAttr(TYPEOID, typTup,
+ Anum_pg_type_typdefault,
+ &isnull);
+ tmp = new_objtree_VA("DEFAULT=", 1,
+ "clause", ObjTypeString, "default");
+ if (!isnull)
+ append_string_object(tmp, "%{default}s", "default",
+ TextDatumGetCString(dflt));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* ELEMENT */
+ tmp = new_objtree_VA("ELEMENT=", 1,
+ "clause", ObjTypeString, "element");
+ if (OidIsValid(typForm->typelem))
+ append_object_object(tmp, "%{elem}T",
+ new_objtree_for_type(typForm->typelem, -1));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* DELIMITER */
+ tmp = new_objtree_VA("DELIMITER=", 1,
+ "clause", ObjTypeString, "delimiter");
+ append_string_object(tmp, "%{delim}L", "delim",
+ psprintf("%c", typForm->typdelim));
+ list = lappend(list, new_object_object(tmp));
+
+ /* COLLATABLE */
+ tmp = new_objtree_VA("COLLATABLE=", 1,
+ "clause", ObjTypeString, "collatable");
+ if (!OidIsValid(typForm->typcollation))
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ append_array_object(ret, "%{elems:, }s)", list);
+
+ ReleaseSysCache(typTup);
+
+ return ret;
+}
+
+/*
+ * Deparse a CREATE TEXT SEARCH CONFIGURATION statement.
+ *
+ * Verbose syntax
+ * CREATE TEXT SEARCH CONFIGURATION %{identity}D (%{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_TSConfig(Oid objectId, DefineStmt *define,
+ ObjectAddress copied)
+{
+ HeapTuple tscTup;
+ HeapTuple tspTup;
+ ObjTree *ret;
+ ObjTree *tmp;
+ Form_pg_ts_config tscForm;
+ Form_pg_ts_parser tspForm;
+ List *list = NIL;
+
+ tscTup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tscTup))
+ elog(ERROR, "cache lookup failed for text search configuration with OID %u",
+ objectId);
+ tscForm = (Form_pg_ts_config) GETSTRUCT(tscTup);
+
+ tspTup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(tscForm->cfgparser));
+ if (!HeapTupleIsValid(tspTup))
+ elog(ERROR, "cache lookup failed for text search parser %u",
+ tscForm->cfgparser);
+ tspForm = (Form_pg_ts_parser) GETSTRUCT(tspTup);
+
+ /*
+ * Add the definition clause. If we have COPY'ed an existing config, add
+ * a COPY clause; otherwise add a PARSER clause.
+ */
+ /* COPY */
+ tmp = new_objtree_VA("COPY=", 1,
+ "clause", ObjTypeString, "copy");
+ if (OidIsValid(copied.objectId))
+ append_object_object(tmp, "%{tsconfig}D",
+ new_objtree_for_qualname_id(TSConfigRelationId,
+ copied.objectId));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* PARSER */
+ tmp = new_objtree_VA("PARSER=", 1,
+ "clause", ObjTypeString, "parser");
+ if (copied.objectId == InvalidOid)
+ append_object_object(tmp, "%{parser}D",
+ new_objtree_for_qualname(tspForm->prsnamespace,
+ NameStr(tspForm->prsname)));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ ret = new_objtree_VA("CREATE TEXT SEARCH CONFIGURATION %{identity}D (%{elems:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(tscForm->cfgnamespace,
+ NameStr(tscForm->cfgname)),
+ "elems", ObjTypeArray, list);
+
+ ReleaseSysCache(tspTup);
+ ReleaseSysCache(tscTup);
+ return ret;
+}
+
+/*
+ * Deparse a CREATE TEXT SEARCH PARSER statement.
+ *
+ * Verbose syntax
+ * CREATE TEXT SEARCH PARSER %{identity}D (%{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_TSParser(Oid objectId, DefineStmt *define)
+{
+ HeapTuple tspTup;
+ ObjTree *ret;
+ ObjTree *tmp;
+ List *list = NIL;
+ Form_pg_ts_parser tspForm;
+
+ tspTup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tspTup))
+ elog(ERROR, "cache lookup failed for text search parser with OID %u",
+ objectId);
+ tspForm = (Form_pg_ts_parser) GETSTRUCT(tspTup);
+
+ /* Add the definition clause */
+ /* START */
+ tmp = new_objtree_VA("START=%{procedure}D", 2,
+ "clause", ObjTypeString, "start",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tspForm->prsstart));
+ list = lappend(list, new_object_object(tmp));
+
+ /* GETTOKEN */
+ tmp = new_objtree_VA("GETTOKEN=%{procedure}D", 2,
+ "clause", ObjTypeString, "gettoken",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tspForm->prstoken));
+ list = lappend(list, new_object_object(tmp));
+
+ /* END */
+ tmp = new_objtree_VA("END=%{procedure}D", 2,
+ "clause", ObjTypeString, "end",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tspForm->prsend));
+ list = lappend(list, new_object_object(tmp));
+
+ /* LEXTYPES */
+ tmp = new_objtree_VA("LEXTYPES=%{procedure}D", 2,
+ "clause", ObjTypeString, "lextypes",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tspForm->prslextype));
+ list = lappend(list, new_object_object(tmp));
+
+ /* HEADLINE */
+ tmp = new_objtree_VA("HEADLINE=", 1,
+ "clause", ObjTypeString, "headline");
+ if (OidIsValid(tspForm->prsheadline))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tspForm->prsheadline));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ ret = new_objtree_VA("CREATE TEXT SEARCH PARSER %{identity}D (%{elems:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(tspForm->prsnamespace,
+ NameStr(tspForm->prsname)),
+ "elems", ObjTypeArray, list);
+
+ ReleaseSysCache(tspTup);
+ return ret;
+}
+
+/*
+ * Deparse a CREATE TEXT SEARCH DICTIONARY statement.
+ *
+ * Verbose syntax
+ * CREATE TEXT SEARCH DICTIONARY %{identity}D (%{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_TSDictionary(Oid objectId, DefineStmt *define)
+{
+ HeapTuple tsdTup;
+ ObjTree *ret;
+ ObjTree *tmp;
+ List *list = NIL;
+ Datum options;
+ bool isnull;
+ Form_pg_ts_dict tsdForm;
+
+ tsdTup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tsdTup))
+ elog(ERROR, "cache lookup failed for text search dictionary with OID %u",
+ objectId);
+ tsdForm = (Form_pg_ts_dict) GETSTRUCT(tsdTup);
+
+
+
+ /* Add the definition clause */
+ /* TEMPLATE */
+ tmp = new_objtree_VA("TEMPLATE=", 1,
+ "clause", ObjTypeString, "template");
+ append_object_object(tmp, "%{template}D",
+ new_objtree_for_qualname_id(TSTemplateRelationId,
+ tsdForm->dicttemplate));
+ list = lappend(list, new_object_object(tmp));
+
+ /*
+ * options. XXX this is a pretty useless representation, but we can't do
+ * better without more ts_cache.c cooperation ...
+ */
+ options = SysCacheGetAttr(TSDICTOID, tsdTup,
+ Anum_pg_ts_dict_dictinitoption,
+ &isnull);
+ tmp = new_objtree("");
+ if (!isnull)
+ append_string_object(tmp, "%{options}s", "options",
+ TextDatumGetCString(options));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ ret = new_objtree_VA("CREATE TEXT SEARCH DICTIONARY %{identity}D (%{elems:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(tsdForm->dictnamespace,
+ NameStr(tsdForm->dictname)),
+ "elems", ObjTypeArray, list);
+
+ ReleaseSysCache(tsdTup);
+ return ret;
+}
+
+/*
+ * Deparse a CREATE TEXT SEARCH TEMPLATE statement.
+ *
+ * Verbose syntax
+ * CREATE TEXT SEARCH TEMPLATE %{identity}D (%{elems:, }s)
+ */
+static ObjTree *
+deparse_DefineStmt_TSTemplate(Oid objectId, DefineStmt *define)
+{
+ HeapTuple tstTup;
+ ObjTree *ret;
+ ObjTree *tmp;
+ List *list = NIL;
+ Form_pg_ts_template tstForm;
+
+ tstTup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tstTup))
+ elog(ERROR, "cache lookup failed for text search template with OID %u",
+ objectId);
+ tstForm = (Form_pg_ts_template) GETSTRUCT(tstTup);
+
+ /* Add the definition clause */
+ /* INIT */
+ tmp = new_objtree_VA("INIT=", 1,
+ "clause", ObjTypeString, "init");
+ if (OidIsValid(tstForm->tmplinit))
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tstForm->tmplinit));
+ else
+ append_not_present(tmp);
+ list = lappend(list, new_object_object(tmp));
+
+ /* LEXIZE */
+ tmp = new_objtree_VA("LEXIZE=%{procedure}D", 2,
+ "clause", ObjTypeString, "lexize",
+ "procedure", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tstForm->tmpllexize));
+ list = lappend(list, new_object_object(tmp));
+
+ ret = new_objtree_VA("CREATE TEXT SEARCH TEMPLATE %{identity}D (%{elems:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(tstForm->tmplnamespace,
+ NameStr(tstForm->tmplname)),
+ "elems", ObjTypeArray, list);
+
+ ReleaseSysCache(tstTup);
+ return ret;
+}
+
+/*
+ * Deparse an ALTER TEXT SEARCH CONFIGURATION statement.
+ *
+ * Verbose syntax
+ * ALTER TEXT SEARCH CONFIGURATION %{identity}D ADD MAPPING
+ * FOR %{tokentype:, }I WITH %{dictionaries:, }D
+ * OR
+ * ALTER TEXT SEARCH CONFIGURATION %{identity}D DROP MAPPING %{if_exists}s
+ * FOR %{tokentype}I
+ * OR
+ * ALTER TEXT SEARCH CONFIGURATION %{identity}D ALTER MAPPING
+ * FOR %{tokentype:, }I WITH %{dictionaries:, }D
+ * OR
+ * ALTER TEXT SEARCH CONFIGURATION %{identity}D ALTER MAPPING
+ * REPLACE %{old_dictionary}D WITH %{new_dictionary}D
+ * OR
+ * ALTER TEXT SEARCH CONFIGURATION %{identity}D ALTER MAPPING
+ * FOR %{tokentype:, }I REPLACE %{old_dictionary}D WITH %{new_dictionary}D
+ */
+static ObjTree *
+deparse_AlterTSConfigurationStmt(CollectedCommand *cmd)
+{
+ AlterTSConfigurationStmt *node = (AlterTSConfigurationStmt *) cmd->parsetree;
+ ObjTree *ret;
+ ObjTree *tmp;
+ List *list = NIL;
+ ListCell *cell;
+ int i;
+
+ ret = new_objtree("ALTER TEXT SEARCH CONFIGURATION");
+
+ /* Determine the format string appropriate to each subcommand */
+ switch (node->kind)
+ {
+ case ALTER_TSCONFIG_ADD_MAPPING:
+ append_object_object(ret, "%{identity}D ADD MAPPING",
+ new_objtree_for_qualname_id(cmd->d.atscfg.address.classId,
+ cmd->d.atscfg.address.objectId));
+ break;
+
+ case ALTER_TSCONFIG_DROP_MAPPING:
+ append_object_object(ret, "%{identity}D DROP MAPPING",
+ new_objtree_for_qualname_id(cmd->d.atscfg.address.classId,
+ cmd->d.atscfg.address.objectId));
+ tmp = new_objtree("IF EXISTS");
+ append_bool_object(tmp, "present", node->missing_ok);
+ append_object_object(ret, "%{if_exists}s", tmp);
+ break;
+
+ case ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN:
+ case ALTER_TSCONFIG_REPLACE_DICT:
+ case ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN:
+ append_object_object(ret, "%{identity}D ALTER MAPPING",
+ new_objtree_for_qualname_id(cmd->d.atscfg.address.classId,
+ cmd->d.atscfg.address.objectId));
+ break;
+ }
+
+ /* Add the affected token list, for subcommands that have one */
+ if (node->kind == ALTER_TSCONFIG_ADD_MAPPING ||
+ node->kind == ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN ||
+ node->kind == ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN ||
+ node->kind == ALTER_TSCONFIG_DROP_MAPPING)
+ {
+ foreach(cell, node->tokentype)
+ list = lappend(list, new_string_object(strVal(lfirst(cell))));
+ append_array_object(ret, "FOR %{tokentype:, }I", list);
+ }
+
+ /* Add further subcommand-specific elements */
+ if (node->kind == ALTER_TSCONFIG_ADD_MAPPING ||
+ node->kind == ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN)
+ {
+ /* ADD MAPPING and ALTER MAPPING FOR need a list of dictionaries */
+ list = NIL;
+ for (i = 0; i < cmd->d.atscfg.ndicts; i++)
+ {
+ ObjTree *dict_obj;
+
+ dict_obj = new_objtree_for_qualname_id(TSDictionaryRelationId,
+ cmd->d.atscfg.dictIds[i]);
+ list = lappend(list,
+ new_object_object(dict_obj));
+ }
+ append_array_object(ret, "WITH %{dictionaries:, }D", list);
+ }
+ else if (node->kind == ALTER_TSCONFIG_REPLACE_DICT ||
+ node->kind == ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN)
+ {
+ /* The REPLACE forms want old and new dictionaries */
+ Assert(cmd->d.atscfg.ndicts == 2);
+ append_object_object(ret, "REPLACE %{old_dictionary}D",
+ new_objtree_for_qualname_id(TSDictionaryRelationId,
+ cmd->d.atscfg.dictIds[0]));
+ append_object_object(ret, "WITH %{new_dictionary}D",
+ new_objtree_for_qualname_id(TSDictionaryRelationId,
+ cmd->d.atscfg.dictIds[1]));
+ }
+
+ return ret;
+}
+
+/*
+ * Deparse an ALTER TEXT SEARCH DICTIONARY statement.
+ *
+ * Verbose syntax
+ * ALTER TEXT SEARCH DICTIONARY %{identity}D (%{definition:, }s)
+ */
+static ObjTree *
+deparse_AlterTSDictionaryStmt(Oid objectId, Node *parsetree)
+{
+ ObjTree *ret;
+ ObjTree *tmp;
+ Datum options;
+ List *definition = NIL;
+ bool isnull;
+ HeapTuple tsdTup;
+ Form_pg_ts_dict tsdForm;
+
+ tsdTup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tsdTup))
+ elog(ERROR, "cache lookup failed for text search dictionary with OID %u",
+ objectId);
+ tsdForm = (Form_pg_ts_dict) GETSTRUCT(tsdTup);
+
+ /*
+ * Add the definition list according to the pg_ts_dict dictinitoption
+ * column
+ */
+ options = SysCacheGetAttr(TSDICTOID, tsdTup,
+ Anum_pg_ts_dict_dictinitoption,
+ &isnull);
+ tmp = new_objtree("");
+ if (!isnull)
+ append_string_object(tmp, "%{options}s", "options",
+ TextDatumGetCString(options));
+ else
+ append_not_present(tmp);
+
+ definition = lappend(definition, new_object_object(tmp));
+
+ ret = new_objtree_VA("ALTER TEXT SEARCH DICTIONARY %{identity}D (%{definition:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(tsdForm->dictnamespace,
+ NameStr(tsdForm->dictname)),
+ "definition", ObjTypeArray, definition);
+
+ ReleaseSysCache(tsdTup);
+ return ret;
+}
+
+/*
+ * deparse_ViewStmt
+ * deparse a ViewStmt
+ *
+ * Given a view OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE %{or_replace}s %{persistence}s VIEW %{identity}D AS %{query}s
+ */
+static ObjTree *
+deparse_ViewStmt(Oid objectId, Node *parsetree)
+{
+ ViewStmt *node = (ViewStmt *) parsetree;
+ ObjTree *ret;
+ Relation relation;
+
+ relation = relation_open(objectId, AccessShareLock);
+
+ ret = new_objtree_VA("CREATE %{or_replace}s %{persistence}s VIEW %{identity}D AS %{query}s", 4,
+ "or_replace", ObjTypeString,
+ node->replace ? "OR REPLACE" : "",
+ "persistence", ObjTypeString,
+ get_persistence_str(relation->rd_rel->relpersistence),
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(relation->rd_rel->relnamespace,
+ RelationGetRelationName(relation)),
+ "query", ObjTypeString,
+ pg_get_viewdef_string(objectId));
+
+ relation_close(relation, AccessShareLock);
+ return ret;
+}
+
+/*
+ * Deparse CREATE Materialized View statement, it is a variant of
+ * CreateTableAsStmt
+ *
+ * Note that CREATE TABLE AS SELECT INTO can also be deparsed by
+ * deparse_CreateTableAsStmt to remove the SELECT INTO clause.
+ *
+ * Verbose syntax
+ * CREATE %{persistence}s [MATERIALIZED VIEW | TABLE] %{if_not_exists}s
+ * %{identity}D %{columns}s [%{on_commit}s] %{tablespace}s
+ * AS %{query}s %{with_no_data}s"
+ */
+static ObjTree *
+deparse_CreateTableAsStmt_vanilla(Oid objectId, Node *parsetree)
+{
+ CreateTableAsStmt *node = (CreateTableAsStmt *) parsetree;
+ Relation relation = relation_open(objectId, AccessShareLock);
+ ObjTree *ret;
+ ObjTree *tmp;
+ ObjTree *tmp2;
+ char *fmt;
+ List *list = NIL;
+ ListCell *cell;
+
+ /* Reject unsupported case right away. */
+ if (((Query *) (node->query))->commandType == CMD_UTILITY)
+ elog(ERROR, "unimplemented deparse of CREATE TABLE AS EXECUTE");
+
+ /*
+ * Note that INSERT INTO is deparsed as CREATE TABLE AS. They are
+ * functionally equivalent synonyms so there is no harm from this.
+ */
+ if (node->objtype == OBJECT_MATVIEW)
+ fmt = "CREATE %{persistence}s MATERIALIZED VIEW %{if_not_exists}s %{identity}D";
+ else
+ fmt = "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D";
+
+ ret = new_objtree_VA(fmt, 3,
+ "persistence", ObjTypeString,
+ get_persistence_str(node->into->rel->relpersistence),
+ "if_not_exists", ObjTypeString,
+ node->if_not_exists ? "IF NOT EXISTS" : "",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId,
+ objectId));
+
+ /* COLUMNS clause */
+ if (node->into->colNames)
+ {
+ foreach(cell, node->into->colNames)
+ list = lappend(list, new_string_object(strVal(lfirst(cell))));
+
+ tmp = new_objtree_VA("(%{columns:, }I)", 1,
+ "columns", ObjTypeArray, list);
+ }
+ else
+ tmp = new_objtree_VA("", 1,
+ "present", ObjTypeBool, false);
+
+ append_object_object(ret, "%{columns}s", tmp);
+
+ /* USING clause */
+ tmp = new_objtree("USING");
+ if (node->into->accessMethod)
+ append_string_object(tmp, "%{access_method}I", "access_method",
+ node->into->accessMethod);
+ else
+ {
+ append_null_object(tmp, "%{access_method}I");
+ append_not_present(tmp);
+ }
+ append_object_object(ret, "%{access_method}s", tmp);
+
+ /* WITH clause */
+ tmp = new_objtree("WITH");
+ list = NIL;
+
+ foreach(cell, node->into->options)
+ {
+ DefElem *opt = (DefElem *) lfirst(cell);
+
+ tmp2 = deparse_DefElem(opt, false);
+ list = lappend(list, new_object_object(tmp2));
+ }
+
+ if (list)
+ append_array_object(tmp, "(%{with:, }s)", list);
+ else
+ append_not_present(tmp);
+
+ append_object_object(ret, "%{with_clause}s", tmp);
+
+ /* ON COMMIT clause. CREATE MATERIALIZED VIEW doesn't have one */
+ if (node->objtype == OBJECT_TABLE)
+ append_object_object(ret, "%{on_commit}s",
+ deparse_OnCommitClause(node->into->onCommit));
+
+ /* TABLESPACE clause */
+ tmp = new_objtree("TABLESPACE %{tablespace}I");
+ if (node->into->tableSpaceName)
+ append_string_object(tmp, "%{tablespace}s", "tablespace",
+ node->into->tableSpaceName);
+ else
+ {
+ append_null_object(tmp, "%{tablespace}I");
+ append_not_present(tmp);
+ }
+ append_object_object(ret, "%{tablespace}s", tmp);
+
+ /* Add the query */
+ Assert(IsA(node->query, Query));
+ append_string_object(ret, "AS %{query}s", "query",
+ pg_get_querydef((Query *) node->query, false));
+
+ /* Add a WITH NO DATA clause */
+ tmp = new_objtree_VA("WITH NO DATA", 1,
+ "present", ObjTypeBool,
+ node->into->skipData ? true : false);
+ append_object_object(ret, "%{with_no_data}s", tmp);
+
+ relation_close(relation, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateTrigStmt (CREATE TRIGGER)
+ *
+ * Given a trigger OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE %{or_replace}s %{constraint}s TRIGGER %{name}I %{time}s
+ * %{events: OR }s ON %{relation}D %{from_table}s %{constraint_attrs: }s
+ * %{referencing: }s FOR EACH %{for_each}s %{when}s EXECUTE PROCEDURE
+ * %{function}s
+ */
+static ObjTree *
+deparse_CreateTrigStmt(Oid objectId, Node *parsetree)
+{
+ CreateTrigStmt *node = (CreateTrigStmt *) parsetree;
+ Relation pg_trigger;
+ HeapTuple trigTup;
+ Form_pg_trigger trigForm;
+ ObjTree *ret;
+ ObjTree *tmp_obj;
+ int tgnargs;
+ List *list = NIL;
+ List *events;
+ char *trigtiming;
+ char *tgoldtable;
+ char *tgnewtable;
+ Datum value;
+ bool isnull;
+
+ pg_trigger = table_open(TriggerRelationId, AccessShareLock);
+
+ trigTup = get_catalog_object_by_oid(pg_trigger, Anum_pg_trigger_oid, objectId);
+ trigForm = (Form_pg_trigger) GETSTRUCT(trigTup);
+
+ trigtiming = node->timing == TRIGGER_TYPE_BEFORE ? "BEFORE" :
+ node->timing == TRIGGER_TYPE_AFTER ? "AFTER" :
+ node->timing == TRIGGER_TYPE_INSTEAD ? "INSTEAD OF" :
+ NULL;
+ if (!trigtiming)
+ elog(ERROR, "unrecognized trigger timing type %d", node->timing);
+
+ ret = new_objtree_VA("CREATE %{or_replace}s %{constraint}s TRIGGER %{name}I %{time}s", 4,
+ "or_replace", ObjTypeString, node->replace ? "OR REPLACE" : "",
+ "constraint", ObjTypeString, node->isconstraint ? "CONSTRAINT" : "",
+ "name", ObjTypeString, node->trigname,
+ "time", ObjTypeString, trigtiming);
+
+ /*
+ * Decode the events that the trigger fires for. The output is a list; in
+ * most cases it will just be a string with the event name, but when
+ * there's an UPDATE with a list of columns, we return a JSON object.
+ */
+ events = NIL;
+ if (node->events & TRIGGER_TYPE_INSERT)
+ events = lappend(events, new_string_object("INSERT"));
+ if (node->events & TRIGGER_TYPE_DELETE)
+ events = lappend(events, new_string_object("DELETE"));
+ if (node->events & TRIGGER_TYPE_TRUNCATE)
+ events = lappend(events, new_string_object("TRUNCATE"));
+ if (node->events & TRIGGER_TYPE_UPDATE)
+ {
+ if (node->columns == NIL)
+ {
+ events = lappend(events, new_string_object("UPDATE"));
+ }
+ else
+ {
+ ObjTree *update;
+ ListCell *cell;
+ List *cols = NIL;
+
+ /*
+ * Currently only UPDATE OF can be objects in the output JSON, but
+ * we add a "kind" element so that user code can distinguish
+ * possible future new event types.
+ */
+ update = new_objtree_VA("UPDATE OF", 1,
+ "kind", ObjTypeString, "update_of");
+
+ foreach(cell, node->columns)
+ {
+ char *colname = strVal(lfirst(cell));
+
+ cols = lappend(cols, new_string_object(colname));
+ }
+
+ append_array_object(update, "%{columns:, }I", cols);
+
+ events = lappend(events, new_object_object(update));
+ }
+ }
+ append_array_object(ret, "%{events: OR }s", events);
+
+ tmp_obj = new_objtree_for_qualname_id(RelationRelationId,
+ trigForm->tgrelid);
+ append_object_object(ret, "ON %{relation}D", tmp_obj);
+
+ tmp_obj = new_objtree("FROM");
+ if (trigForm->tgconstrrelid)
+ {
+ ObjTree *rel;
+
+ rel = new_objtree_for_qualname_id(RelationRelationId,
+ trigForm->tgconstrrelid);
+ append_object_object(tmp_obj, "%{relation}D", rel);
+ }
+ else
+ append_not_present(tmp_obj);
+ append_object_object(ret, "%{from_table}s", tmp_obj);
+
+ if (node->isconstraint)
+ {
+ if (!node->deferrable)
+ list = lappend(list, new_string_object("NOT"));
+ list = lappend(list, new_string_object("DEFERRABLE INITIALLY"));
+ if (node->initdeferred)
+ list = lappend(list, new_string_object("DEFERRED"));
+ else
+ list = lappend(list, new_string_object("IMMEDIATE"));
+ }
+ append_array_object(ret, "%{constraint_attrs: }s", list);
+
+ list = NIL;
+ value = fastgetattr(trigTup, Anum_pg_trigger_tgoldtable,
+ RelationGetDescr(pg_trigger), &isnull);
+ if (!isnull)
+ tgoldtable = NameStr(*DatumGetName(value));
+ else
+ tgoldtable = NULL;
+ value = fastgetattr(trigTup, Anum_pg_trigger_tgnewtable,
+ RelationGetDescr(pg_trigger), &isnull);
+ if (!isnull)
+ tgnewtable = NameStr(*DatumGetName(value));
+ else
+ tgnewtable = NULL;
+ if (tgoldtable != NULL || tgnewtable != NULL)
+ {
+ list = lappend(list, new_string_object("REFERENCING"));
+
+ if (tgoldtable != NULL)
+ {
+ list = lappend(list, new_string_object("OLD TABLE AS"));
+ list = lappend(list, new_string_object(tgoldtable));
+ }
+ if (tgnewtable != NULL)
+ {
+ list = lappend(list, new_string_object("NEW TABLE AS"));
+ list = lappend(list, new_string_object(tgnewtable));
+ }
+ }
+ append_array_object(ret, "%{referencing: }s", list);
+
+ append_string_object(ret, "FOR EACH %{for_each}s", "for_each",
+ node->row ? "ROW" : "STATEMENT");
+
+ tmp_obj = new_objtree("WHEN");
+ if (node->whenClause)
+ {
+ Node *whenClause;
+
+ value = fastgetattr(trigTup, Anum_pg_trigger_tgqual,
+ RelationGetDescr(pg_trigger), &isnull);
+ if (isnull)
+ elog(ERROR, "null tgqual for trigger \"%s\"",
+ NameStr(trigForm->tgname));
+
+ whenClause = stringToNode(TextDatumGetCString(value));
+ append_string_object(tmp_obj, "(%{clause}s)", "clause",
+ pg_get_trigger_whenclause(trigForm,
+ whenClause,
+ false));
+ }
+ else
+ append_not_present(tmp_obj);
+ append_object_object(ret, "%{when}s", tmp_obj);
+
+ tmp_obj = new_objtree_VA("%{funcname}D", 1,
+ "funcname", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ trigForm->tgfoid));
+ list = NIL;
+ tgnargs = trigForm->tgnargs;
+ if (tgnargs > 0)
+ {
+ char *p;
+ int i;
+
+ value = fastgetattr(trigTup, Anum_pg_trigger_tgargs,
+ RelationGetDescr(pg_trigger), &isnull);
+ if (isnull)
+ elog(ERROR, "null tgargs for trigger \"%s\"",
+ NameStr(trigForm->tgname));
+ p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
+ for (i = 0; i < tgnargs; i++)
+ {
+ list = lappend(list, new_string_object(p));
+ /* advance p to next string embedded in tgargs */
+ while (*p)
+ p++;
+ p++;
+ }
+ }
+
+ append_format_string(tmp_obj, "(");
+ append_array_object(tmp_obj, "%{args:, }L", list); /* might be NIL */
+ append_format_string(tmp_obj, ")");
+
+ append_object_object(ret, "EXECUTE FUNCTION %{function}s", tmp_obj);
+
+ table_close(pg_trigger, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateUserMappingStmt (CREATE USER MAPPING)
+ *
+ * Given a User Mapping OID and the parse tree that created it,
+ * return an ObjTree representing the CREATE USER MAPPING command.
+ *
+ * Verbose syntax
+ * CREATE USER MAPPING FOR %{role}R SERVER %{server}I
+ */
+static ObjTree *
+deparse_CreateUserMappingStmt(Oid objectId, Node *parsetree)
+{
+ CreateUserMappingStmt *node = (CreateUserMappingStmt *) parsetree;
+ ObjTree *ret;
+ Relation rel;
+ HeapTuple tp;
+ Form_pg_user_mapping form;
+ ForeignServer *server;
+
+ rel = table_open(UserMappingRelationId, RowExclusiveLock);
+
+ /*
+ * Lookup object in the catalog, so we don't have to deal with
+ * current_user and such.
+ */
+ tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for user mapping with OID %u",
+ objectId);
+
+ form = (Form_pg_user_mapping) GETSTRUCT(tp);
+
+ server = GetForeignServer(form->umserver);
+
+ ret = new_objtree_VA("CREATE USER MAPPING FOR %{role}R SERVER %{server}I %{generic_options}s", 3,
+ "role", ObjTypeObject,
+ new_objtree_for_role_id(form->umuser),
+ "server", ObjTypeString, server->servername,
+ "generic_options", ObjTypeObject,
+ deparse_FdwOptions(node->options, NULL, false));
+
+ ReleaseSysCache(tp);
+ table_close(rel, RowExclusiveLock);
+ return ret;
+}
+
+/*
+ * deparse_AlterUserMapping
+ *
+ * Given a User Mapping OID and the parse tree that created it, return an
+ * ObjTree representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER USER MAPPING FOR %{role}R SERVER %{server}I
+ */
+static ObjTree *
+deparse_AlterUserMappingStmt(Oid objectId, Node *parsetree)
+{
+ AlterUserMappingStmt *node = (AlterUserMappingStmt *) parsetree;
+ ObjTree *ret;
+ Relation rel;
+ HeapTuple tp;
+ Form_pg_user_mapping form;
+ ForeignServer *server;
+
+ rel = table_open(UserMappingRelationId, RowExclusiveLock);
+
+ /*
+ * Lookup object in the catalog, so we don't have to deal with
+ * current_user and such.
+ */
+ tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for user mapping with OID %u",
+ objectId);
+
+ form = (Form_pg_user_mapping) GETSTRUCT(tp);
+
+ server = GetForeignServer(form->umserver);
+
+ ret = new_objtree_VA("ALTER USER MAPPING FOR %{role}R SERVER %{server}I %{generic_options}s", 3,
+ "role", ObjTypeObject,
+ new_objtree_for_role_id(form->umuser),
+ "server", ObjTypeString, server->servername,
+ "generic_options", ObjTypeObject,
+ deparse_FdwOptions(node->options, NULL, false));
+
+ ReleaseSysCache(tp);
+ table_close(rel, RowExclusiveLock);
+ return ret;
+}
+
+/*
+ * Deparse an AlterStatsStmt (ALTER STATISTICS)
+ *
+ * Given a alter statistics OID and the parse tree that created it, return an
+ * ObjTree representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER STATISTICS %{identity}D SET STATISTICS %{target}n
+ */
+static ObjTree *
+deparse_AlterStatsStmt(Oid objectId, Node *parsetree)
+{
+ AlterStatsStmt *node = (AlterStatsStmt *) parsetree;
+ ObjTree *ret;
+ HeapTuple tp;
+ Form_pg_statistic_ext statform;
+
+ if (!node->stxstattarget)
+ return NULL;
+
+ /* Lookup object in the catalog */
+ tp = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for statistic with OID %u", objectId);
+
+ statform = (Form_pg_statistic_ext) GETSTRUCT(tp);
+ ret = new_objtree_VA("ALTER STATISTICS %{identity}D SET STATISTICS %{target}n", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(statform->stxnamespace,
+ NameStr(statform->stxname)),
+ "target", ObjTypeInteger, node->stxstattarget);
+
+ ReleaseSysCache(tp);
+ return ret;
+}
+
+/*
+ * Deparse a RefreshMatViewStmt (REFRESH MATERIALIZED VIEW)
+ *
+ * Given a materialized view OID and the parse tree that created it, return an
+ * ObjTree representing the refresh command.
+ *
+ * Verbose syntax
+ * REFRESH MATERIALIZED VIEW %{concurrently}s %{identity}D %{with_no_data}s
+ */
+static ObjTree *
+deparse_RefreshMatViewStmt(Oid objectId, Node *parsetree)
+{
+ RefreshMatViewStmt *node = (RefreshMatViewStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *tmp;
+
+ ret = new_objtree_VA("REFRESH MATERIALIZED VIEW %{concurrently}s %{identity}D", 2,
+ "concurrently", ObjTypeString,
+ node->concurrent ? "CONCURRENTLY" : "",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId,
+ objectId));
+
+ /* Add a WITH NO DATA clause */
+ tmp = new_objtree_VA("WITH NO DATA", 1,
+ "present", ObjTypeBool,
+ node->skipData ? true : false);
+ append_object_object(ret, "%{with_no_data}s", tmp);
+
+ return ret;
+}
+
+/*
+ * Deparse a RenameStmt.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I
+ * OR
+ * ALTER POLICY %{if_exists}s %{policyname}I ON %{identity}D RENAME TO %{newname}I
+ * OR
+ * ALTER DOMAIN %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I
+ * OR
+ * ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I
+ * OR
+ * ALTER TYPE %{identity}D RENAME ATTRIBUTE %{colname}I TO %{newname}I %{cascade}s
+ * OR
+ * ALTER %{objtype}s %{if_exists}s %{only}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I %{cascade}s
+ * OR
+ * ALTER %{objtype}s %{identity}s RENAME TO %{newname}I
+ * OR
+ * ALTER %{objtype}s %{identity}D USING %{index_method}s RENAME TO %{newname}I
+ * OR
+ * ALTER SCHEMA %{identity}I RENAME TO %{newname}I
+ * OR
+ * ALTER RULE %{rulename}I ON %{identity}D RENAME TO %{newname}I
+ * OR
+ * ALTER TRIGGER %{triggername}I ON %{identity}D RENAME TO %{newname}I
+ * OR
+ * ALTER %{objtype}s %{identity}D RENAME TO %{newname}I
+ */
+static ObjTree *
+deparse_RenameStmt(ObjectAddress address, Node *parsetree)
+{
+ RenameStmt *node = (RenameStmt *) parsetree;
+ ObjTree *ret;
+ Relation relation;
+ Oid schemaId;
+
+ /*
+ * In an ALTER .. RENAME command, we don't have the original name of the
+ * object in system catalogs: since we inspect them after the command has
+ * executed, the old name is already gone. Therefore, we extract it from
+ * the parse node. Note we still extract the schema name from the catalog
+ * (it might not be present in the parse node); it cannot possibly have
+ * changed anyway.
+ */
+ switch (node->renameType)
+ {
+ case OBJECT_TABLE:
+ case OBJECT_INDEX:
+ case OBJECT_SEQUENCE:
+ case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
+ case OBJECT_FOREIGN_TABLE:
+ relation = relation_open(address.objectId, AccessShareLock);
+ schemaId = RelationGetNamespace(relation);
+ ret = new_objtree_VA("ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I", 4,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->renameType, false),
+ "if_exists", ObjTypeString,
+ node->missing_ok ? "IF EXISTS" : "",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(schemaId,
+ node->relation->relname),
+ "newname", ObjTypeString,
+ node->newname);
+ relation_close(relation, AccessShareLock);
+ break;
+
+ case OBJECT_POLICY:
+ {
+ HeapTuple polTup;
+ Form_pg_policy polForm;
+ Relation pg_policy;
+ ScanKeyData key;
+ SysScanDesc scan;
+
+ pg_policy = relation_open(PolicyRelationId, AccessShareLock);
+ ScanKeyInit(&key, Anum_pg_policy_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(address.objectId));
+ scan = systable_beginscan(pg_policy, PolicyOidIndexId, true,
+ NULL, 1, &key);
+ polTup = systable_getnext(scan);
+ if (!HeapTupleIsValid(polTup))
+ elog(ERROR, "cache lookup failed for policy with OID %u",
+ address.objectId);
+ polForm = (Form_pg_policy) GETSTRUCT(polTup);
+
+ ret = new_objtree_VA("ALTER POLICY %{if_exists}s %{policyname}I ON %{identity}D RENAME TO %{newname}I", 4,
+ "if_exists", ObjTypeString,
+ node->missing_ok ? "IF EXISTS" : "",
+ "policyname", ObjTypeString,
+ node->subname,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId,
+ polForm->polrelid),
+ "newname", ObjTypeString,
+ node->newname);
+ systable_endscan(scan);
+ relation_close(pg_policy, AccessShareLock);
+ }
+ break;
+
+ case OBJECT_DOMCONSTRAINT:
+ {
+ HeapTuple domtup;
+ HeapTuple consttup;
+ Form_pg_constraint constform;
+ Form_pg_type domform;
+
+ /* Get domain id from the constraint */
+ consttup = SearchSysCache1(CONSTROID,
+ ObjectIdGetDatum(address.objectId));
+ if (!HeapTupleIsValid(consttup))
+ elog(ERROR, "cache lookup failed for constraint with OID %u",
+ address.objectId);
+ constform = (Form_pg_constraint) GETSTRUCT(consttup);
+
+ domtup = SearchSysCache1(TYPEOID,
+ ObjectIdGetDatum(constform->contypid));
+ if (!HeapTupleIsValid(domtup))
+ elog(ERROR, "cache lookup failed for domain with OID %u",
+ constform->contypid);
+ ReleaseSysCache(consttup);
+
+ domform = (Form_pg_type) GETSTRUCT(domtup);
+ ret = new_objtree_VA("ALTER DOMAIN %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", 3,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(domform->typnamespace,
+ NameStr(domform->typname)),
+ "oldname", ObjTypeString, node->subname,
+ "newname", ObjTypeString, node->newname);
+ ReleaseSysCache(domtup);
+ }
+ break;
+
+ case OBJECT_TABCONSTRAINT:
+ {
+ HeapTuple constrtup;
+ Form_pg_constraint constform;
+
+ constrtup = SearchSysCache1(CONSTROID,
+ ObjectIdGetDatum(address.objectId));
+ if (!HeapTupleIsValid(constrtup))
+ elog(ERROR, "cache lookup failed for constraint with OID %u",
+ address.objectId);
+ constform = (Form_pg_constraint) GETSTRUCT(constrtup);
+
+ ret = new_objtree_VA("ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", 4,
+ "only", ObjTypeString,
+ node->relation->inh ? "" : "ONLY",
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId,
+ constform->conrelid),
+ "oldname", ObjTypeString, node->subname,
+ "newname", ObjTypeString, node->newname);
+ ReleaseSysCache(constrtup);
+ }
+ break;
+
+ case OBJECT_ATTRIBUTE:
+ case OBJECT_COLUMN:
+ relation = relation_open(address.objectId, AccessShareLock);
+ schemaId = RelationGetNamespace(relation);
+
+ if (node->renameType == OBJECT_ATTRIBUTE)
+ ret = new_objtree_VA("ALTER TYPE %{identity}D RENAME ATTRIBUTE %{colname}I", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(schemaId,
+ node->relation->relname),
+ "colname", ObjTypeString, node->subname);
+ else
+ {
+ ret = new_objtree_VA("ALTER %{objtype}s", 1,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->relationType, false));
+
+ /* Composite types do not support IF EXISTS */
+ if (node->renameType == OBJECT_COLUMN)
+ append_string_object(ret, "%{if_exists}s",
+ "if_exists",
+ node->missing_ok ? "IF EXISTS" : "");
+ if (!node->relation->inh)
+ append_string_object(ret, "%{only}s",
+ "only",
+ "ONLY");
+ append_object_object(ret, "%{identity}D",
+ new_objtree_for_qualname(schemaId,
+ node->relation->relname));
+ append_string_object(ret, "RENAME COLUMN %{colname}I",
+ "colname", node->subname);
+ }
+
+ append_string_object(ret, "TO %{newname}I", "newname",
+ node->newname);
+
+ if (node->renameType == OBJECT_ATTRIBUTE)
+ append_object_object(ret, "%{cascade}s",
+ new_objtree_VA("CASCADE", 1,
+ "present", ObjTypeBool,
+ node->behavior == DROP_CASCADE));
+
+ relation_close(relation, AccessShareLock);
+ break;
+
+ case OBJECT_AGGREGATE:
+ case OBJECT_FUNCTION:
+ case OBJECT_ROUTINE:
+ {
+ char *ident;
+ HeapTuple proctup;
+ Form_pg_proc procform;
+ List *identity;
+
+ Assert(IsA(node->object, ObjectWithArgs));
+ identity = ((ObjectWithArgs *) node->object)->objname;
+
+ proctup = SearchSysCache1(PROCOID,
+ ObjectIdGetDatum(address.objectId));
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup failed for procedure with OID %u",
+ address.objectId);
+ procform = (Form_pg_proc) GETSTRUCT(proctup);
+
+ /* XXX does this work for ordered-set aggregates? */
+ ident = psprintf("%s%s",
+ quote_qualified_identifier(get_namespace_name(procform->pronamespace),
+ strVal(llast(identity))),
+ format_procedure_args(address.objectId, true));
+
+ ret = new_objtree_VA("ALTER %{objtype}s %{identity}s RENAME TO %{newname}I", 3,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->renameType, false),
+ "identity", ObjTypeString, ident,
+ "newname", ObjTypeString, node->newname);
+
+ ReleaseSysCache(proctup);
+ }
+ break;
+
+ case OBJECT_OPCLASS:
+ {
+ HeapTuple opcTup;
+ Form_pg_opclass opcForm;
+ List *oldnames;
+ char *schemaname;
+ char *opcname;
+
+ opcTup = SearchSysCache1(CLAOID, ObjectIdGetDatum(address.objectId));
+ if (!HeapTupleIsValid(opcTup))
+ elog(ERROR, "cache lookup failed for opclass with OID %u",
+ address.objectId);
+
+ opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
+
+ oldnames = list_copy_tail((List *) node->object, 1);
+
+ /* Deconstruct the name list */
+ DeconstructQualifiedName(oldnames, &schemaname, &opcname);
+
+ ret = new_objtree_VA("ALTER %{objtype}s %{identity}D USING %{index_method}s RENAME TO %{newname}I", 4,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->renameType, false),
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(opcForm->opcnamespace,
+ opcname),
+ "index_method", ObjTypeString,
+ get_am_name(opcForm->opcmethod),
+ "newname", ObjTypeString, node->newname);
+
+ ReleaseSysCache(opcTup);
+ }
+ break;
+
+ case OBJECT_OPFAMILY:
+ {
+ HeapTuple opfTup;
+ HeapTuple amTup;
+ Form_pg_opfamily opfForm;
+ Form_pg_am amForm;
+ List *oldnames;
+ char *schemaname;
+ char *opfname;
+
+ opfTup = SearchSysCache1(OPFAMILYOID, address.objectId);
+ if (!HeapTupleIsValid(opfTup))
+ elog(ERROR, "cache lookup failed for operator family with OID %u",
+ address.objectId);
+ opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
+
+ amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod));
+ if (!HeapTupleIsValid(amTup))
+ elog(ERROR, "cache lookup failed for access method with OID %u",
+ opfForm->opfmethod);
+
+ amForm = (Form_pg_am) GETSTRUCT(amTup);
+
+ oldnames = list_copy_tail((List *) node->object, 1);
+
+ /* Deconstruct the name list */
+ DeconstructQualifiedName(oldnames, &schemaname, &opfname);
+
+ ret = new_objtree_VA("ALTER %{objtype}s %{identity}D USING %{index_method}s RENAME TO %{newname}I", 4,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->renameType, false),
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(opfForm->opfnamespace,
+ opfname),
+ "index_method", ObjTypeString,
+ NameStr(amForm->amname),
+ "newname", ObjTypeString, node->newname);
+
+ ReleaseSysCache(amTup);
+ ReleaseSysCache(opfTup);
+ }
+ break;
+
+ case OBJECT_SCHEMA:
+ ret = new_objtree_VA("ALTER SCHEMA %{identity}I RENAME TO %{newname}I", 2,
+ "identity", ObjTypeString, node->subname,
+ "newname", ObjTypeString, node->newname);
+ break;
+
+ case OBJECT_FDW:
+ case OBJECT_LANGUAGE:
+ case OBJECT_FOREIGN_SERVER:
+ case OBJECT_PUBLICATION:
+ ret = new_objtree_VA("ALTER %{objtype}s %{identity}s RENAME TO %{newname}I", 3,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->renameType, false),
+ "identity", ObjTypeString,
+ strVal(castNode(String, node->object)),
+ "newname", ObjTypeString, node->newname);
+ break;
+
+ case OBJECT_RULE:
+ {
+ HeapTuple rewrTup;
+ Form_pg_rewrite rewrForm;
+ Relation pg_rewrite;
+
+ pg_rewrite = relation_open(RewriteRelationId, AccessShareLock);
+ rewrTup = get_catalog_object_by_oid(pg_rewrite, Anum_pg_rewrite_oid, address.objectId);
+ rewrForm = (Form_pg_rewrite) GETSTRUCT(rewrTup);
+
+ ret = new_objtree_VA("ALTER RULE %{rulename}I ON %{identity}D RENAME TO %{newname}I", 3,
+ "rulename", ObjTypeString, node->subname,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId,
+ rewrForm->ev_class),
+ "newname", ObjTypeString, node->newname);
+ relation_close(pg_rewrite, AccessShareLock);
+ }
+ break;
+
+ case OBJECT_TRIGGER:
+ {
+ HeapTuple trigTup;
+ Form_pg_trigger trigForm;
+ Relation pg_trigger;
+
+ pg_trigger = relation_open(TriggerRelationId, AccessShareLock);
+ trigTup = get_catalog_object_by_oid(pg_trigger, get_object_attnum_oid(address.classId), address.objectId);
+ trigForm = (Form_pg_trigger) GETSTRUCT(trigTup);
+
+ ret = new_objtree_VA("ALTER TRIGGER %{triggername}I ON %{identity}D RENAME TO %{newname}I", 3,
+ "triggername", ObjTypeString, node->subname,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId,
+ trigForm->tgrelid),
+ "newname", ObjTypeString, node->newname);
+
+ relation_close(pg_trigger, AccessShareLock);
+ }
+ break;
+
+ case OBJECT_COLLATION:
+ case OBJECT_STATISTIC_EXT:
+ case OBJECT_TYPE:
+ case OBJECT_CONVERSION:
+ case OBJECT_DOMAIN:
+ case OBJECT_TSDICTIONARY:
+ case OBJECT_TSPARSER:
+ case OBJECT_TSTEMPLATE:
+ case OBJECT_TSCONFIGURATION:
+ {
+ HeapTuple objTup;
+ Relation catalog;
+ Datum objnsp;
+ bool isnull;
+ AttrNumber Anum_namespace;
+ List *identity = castNode(List, node->object);
+
+ /* Obtain object tuple */
+ catalog = relation_open(address.classId, AccessShareLock);
+ objTup = get_catalog_object_by_oid(catalog, get_object_attnum_oid(address.classId), address.objectId);
+
+ /* Obtain namespace */
+ Anum_namespace = get_object_attnum_namespace(address.classId);
+ objnsp = heap_getattr(objTup, Anum_namespace,
+ RelationGetDescr(catalog), &isnull);
+ if (isnull)
+ elog(ERROR, "invalid NULL namespace");
+
+ ret = new_objtree_VA("ALTER %{objtype}s %{identity}D RENAME TO %{newname}I", 3,
+ "objtype", ObjTypeString,
+ stringify_objtype(node->renameType, false),
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(DatumGetObjectId(objnsp),
+ strVal(llast(identity))),
+ "newname", ObjTypeString, node->newname);
+ relation_close(catalog, AccessShareLock);
+ }
+ break;
+
+ default:
+ elog(ERROR, "unsupported object type %d", node->renameType);
+ }
+
+ return ret;
+}
+
+/*
+ * Deparse a AlterObjectDependsStmt (ALTER ... DEPENDS ON ...).
+ *
+ * Verbose syntax
+ * ALTER INDEX %{identity}D %{no}s DEPENDS ON EXTENSION %{newname}I
+ */
+static ObjTree *
+deparse_AlterDependStmt(Oid objectId, Node *parsetree)
+{
+ AlterObjectDependsStmt *node = (AlterObjectDependsStmt *) parsetree;
+ ObjTree *ret = NULL;
+
+ if (node->objectType == OBJECT_INDEX)
+ {
+ ObjTree *qualified;
+ Relation relation = relation_open(objectId, AccessShareLock);
+
+ qualified = new_objtree_for_qualname(relation->rd_rel->relnamespace,
+ node->relation->relname);
+ relation_close(relation, AccessShareLock);
+
+ ret = new_objtree_VA("ALTER INDEX %{identity}D %{no}s DEPENDS ON EXTENSION %{newname}I", 3,
+ "identity", ObjTypeObject, qualified,
+ "no", ObjTypeString,
+ node->remove ? "NO" : "",
+ "newname", ObjTypeString, strVal(node->extname));
+ }
+ else
+ elog(LOG, "unrecognized node type in deparse command: %d",
+ (int) nodeTag(parsetree));
+
+ return ret;
+}
+
+/*
+ * Deparse a RuleStmt (CREATE RULE).
+ *
+ * Given a rule OID and the parse tree that created it, return an ObjTree
+ * representing the rule command.
+ *
+ * Verbose syntax
+ * CREATE RULE %{or_replace}s %{identity}I AS ON %{event}s TO %{table}D
+ * %{where_clause}s DO %{instead}s %{actions:, }s
+ */
+static ObjTree *
+deparse_RuleStmt(Oid objectId, Node *parsetree)
+{
+ RuleStmt *node = (RuleStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *tmp;
+ Relation pg_rewrite;
+ Form_pg_rewrite rewrForm;
+ HeapTuple rewrTup;
+ SysScanDesc scan;
+ ScanKeyData key;
+ Datum ev_qual;
+ Datum ev_actions;
+ bool isnull;
+ char *qual;
+ List *actions;
+ List *list = NIL;
+ ListCell *cell;
+ char *event_str;
+
+ pg_rewrite = table_open(RewriteRelationId, AccessShareLock);
+ ScanKeyInit(&key,
+ Anum_pg_rewrite_oid,
+ BTEqualStrategyNumber,
+ F_OIDEQ, ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(pg_rewrite, RewriteOidIndexId, true,
+ NULL, 1, &key);
+ rewrTup = systable_getnext(scan);
+ if (!HeapTupleIsValid(rewrTup))
+ elog(ERROR, "cache lookup failed for rewrite rule with OID %u",
+ objectId);
+
+ rewrForm = (Form_pg_rewrite) GETSTRUCT(rewrTup);
+
+ event_str = node->event == CMD_SELECT ? "SELECT" :
+ node->event == CMD_UPDATE ? "UPDATE" :
+ node->event == CMD_DELETE ? "DELETE" :
+ node->event == CMD_INSERT ? "INSERT" : NULL;
+ Assert(event_str != NULL);
+
+ ev_qual = heap_getattr(rewrTup, Anum_pg_rewrite_ev_qual,
+ RelationGetDescr(pg_rewrite), &isnull);
+ ev_actions = heap_getattr(rewrTup, Anum_pg_rewrite_ev_action,
+ RelationGetDescr(pg_rewrite), &isnull);
+
+ pg_get_ruledef_detailed(ev_qual, ev_actions, &qual, &actions);
+
+ tmp = new_objtree("");
+ if (qual)
+ append_string_object(tmp, "WHERE %{clause}s", "clause", qual);
+ else
+ {
+ append_null_object(tmp, "WHERE %{clause}s");
+ append_not_present(tmp);
+ }
+
+ if (actions == NIL)
+ list = lappend(list, new_string_object("NOTHING"));
+ else
+ {
+ foreach(cell, actions)
+ list = lappend(list, new_string_object(lfirst(cell)));
+ }
+
+ ret = new_objtree_VA("CREATE RULE %{or_replace}s %{identity}I AS ON %{event}s TO %{table}D %{where_clause}s DO %{instead}s", 6,
+ "or_replace", ObjTypeString,
+ node->replace ? "OR REPLACE" : "",
+ "identity", ObjTypeString, node->rulename,
+ "event", ObjTypeString, event_str,
+ "table", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId,
+ rewrForm->ev_class),
+ "where_clause", ObjTypeObject, tmp,
+ "instead", ObjTypeString,
+ node->instead ? "INSTEAD" : "ALSO");
+
+ if (list_length(list) > 1)
+ append_array_object(ret, "(%{actions:; }s)", list);
+ else
+ append_array_object(ret, "%{actions:; }s", list);
+
+ systable_endscan(scan);
+ table_close(pg_rewrite, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateTransformStmt (CREATE TRANSFORM).
+ *
+ * Given a transform OID and the parse tree that created it, return an ObjTree
+ * representing the CREATE TRANSFORM command.
+ *
+ * Verbose syntax
+ * CREATE %{or_replace}s TRANSFORM FOR %{typename}D LANGUAGE %{language}I
+ * ( FROM SQL WITH FUNCTION %{signature}s, TO SQL WITH FUNCTION
+ * %{signature_tof}s )
+ */
+static ObjTree *
+deparse_CreateTransformStmt(Oid objectId, Node *parsetree)
+{
+ CreateTransformStmt *node = (CreateTransformStmt *) parsetree;
+ ObjTree *ret;
+ ObjTree *signature;
+ HeapTuple trfTup;
+ HeapTuple langTup;
+ HeapTuple procTup;
+ Form_pg_transform trfForm;
+ Form_pg_language langForm;
+ Form_pg_proc procForm;
+ int i;
+
+ /* Get the pg_transform tuple */
+ trfTup = SearchSysCache1(TRFOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(trfTup))
+ elog(ERROR, "cache lookup failed for transform with OID %u",
+ objectId);
+ trfForm = (Form_pg_transform) GETSTRUCT(trfTup);
+
+ /* Get the corresponding pg_language tuple */
+ langTup = SearchSysCache1(LANGOID, trfForm->trflang);
+ if (!HeapTupleIsValid(langTup))
+ elog(ERROR, "cache lookup failed for language with OID %u",
+ trfForm->trflang);
+ langForm = (Form_pg_language) GETSTRUCT(langTup);
+
+ ret = new_objtree_VA("CREATE %{or_replace}s TRANSFORM FOR %{typename}D LANGUAGE %{language}I", 3,
+ "or_replace", ObjTypeString,
+ node->replace ? "OR REPLACE" : "",
+ "typename", ObjTypeObject,
+ new_objtree_for_qualname_id(TypeRelationId,
+ trfForm->trftype),
+ "language", ObjTypeString,
+ NameStr(langForm->lanname));
+
+ /* Deparse the transform_element_list */
+ if (OidIsValid(trfForm->trffromsql))
+ {
+ List *params = NIL;
+
+ /* Get the pg_proc tuple for the FROM FUNCTION */
+ procTup = SearchSysCache1(PROCOID, trfForm->trffromsql);
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for function with OID %u",
+ trfForm->trffromsql);
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+ /*
+ * CREATE TRANSFORM does not change function signature so we can use
+ * catalog to get input type Oids.
+ */
+ for (i = 0; i < procForm->pronargs; i++)
+ {
+ ObjTree *param_obj;
+
+ param_obj = new_objtree_VA("%{type}T", 1,
+ "type", ObjTypeObject,
+ new_objtree_for_type(procForm->proargtypes.values[i], -1));
+ params = lappend(params, new_object_object(param_obj));
+ }
+
+ signature = new_objtree_VA("%{identity}D (%{arguments:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(procForm->pronamespace,
+ NameStr(procForm->proname)),
+ "arguments", ObjTypeArray, params);
+
+ append_object_object(ret, "(FROM SQL WITH FUNCTION %{signature}s",
+ signature);
+ ReleaseSysCache(procTup);
+ }
+ if (OidIsValid(trfForm->trftosql))
+ {
+ List *params = NIL;
+
+ /* Append a ',' if trffromsql is present, else append '(' */
+ append_format_string(ret, OidIsValid(trfForm->trffromsql) ? "," : "(");
+
+ /* Get the pg_proc tuple for the TO FUNCTION */
+ procTup = SearchSysCache1(PROCOID, trfForm->trftosql);
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for function with OID %u",
+ trfForm->trftosql);
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+ /*
+ * CREATE TRANSFORM does not change function signature so we can use
+ * catalog to get input type Oids.
+ */
+ for (i = 0; i < procForm->pronargs; i++)
+ {
+ ObjTree *param_obj = new_objtree("");
+
+ param_obj = new_objtree_VA("%{type}T", 1,
+ "type", ObjTypeObject,
+ new_objtree_for_type(procForm->proargtypes.values[i], -1));
+ params = lappend(params, new_object_object(param_obj));
+ }
+
+ signature = new_objtree_VA("%{identity}D (%{arguments:, }s)", 2,
+ "identity", ObjTypeObject,
+ new_objtree_for_qualname(procForm->pronamespace,
+ NameStr(procForm->proname)),
+ "arguments", ObjTypeArray, params);
+
+ append_object_object(ret, "TO SQL WITH FUNCTION %{signature_tof}s",
+ signature);
+ ReleaseSysCache(procTup);
+ }
+
+ append_format_string(ret, ")");
+
+ ReleaseSysCache(langTup);
+ ReleaseSysCache(trfTup);
+ return ret;
+}
+
+/*
+ * Deparse a CommentStmt when it pertains to a constraint.
+ *
+ * Verbose syntax
+ * COMMENT ON CONSTRAINT %{identity}s ON [DOMAIN] %{parentobj}s IS %{comment}s
+ */
+static ObjTree *
+deparse_CommentOnConstraintSmt(Oid objectId, Node *parsetree)
+{
+ CommentStmt *node = (CommentStmt *) parsetree;
+ ObjTree *ret;
+ HeapTuple constrTup;
+ Form_pg_constraint constrForm;
+ ObjectAddress addr;
+
+ Assert(node->objtype == OBJECT_TABCONSTRAINT || node->objtype == OBJECT_DOMCONSTRAINT);
+
+ constrTup = SearchSysCache1(CONSTROID, objectId);
+ if (!HeapTupleIsValid(constrTup))
+ elog(ERROR, "cache lookup failed for constraint with OID %u", objectId);
+ constrForm = (Form_pg_constraint) GETSTRUCT(constrTup);
+
+ if (OidIsValid(constrForm->conrelid))
+ ObjectAddressSet(addr, RelationRelationId, constrForm->conrelid);
+ else
+ ObjectAddressSet(addr, TypeRelationId, constrForm->contypid);
+
+ ret = new_objtree_VA("COMMENT ON CONSTRAINT %{identity}s ON %{domain}s %{parentobj}s", 3,
+ "identity", ObjTypeString, pstrdup(NameStr(constrForm->conname)),
+ "domain", ObjTypeString,
+ (node->objtype == OBJECT_DOMCONSTRAINT) ? "DOMAIN" : "",
+ "parentobj", ObjTypeString,
+ getObjectIdentity(&addr, false));
+
+ /* Add the comment clause */
+ append_literal_or_null(ret, "IS %{comment}s", node->comment);
+
+ ReleaseSysCache(constrTup);
+ return ret;
+}
+
+/*
+ * Deparse an CommentStmt (COMMENT ON ...).
+ *
+ * Given the object address and the parse tree that created it, return an
+ * ObjTree representing the comment command.
+ *
+ * Verbose syntax
+ * COMMENT ON %{objtype}s %{identity}s IS %{comment}s
+ */
+static ObjTree *
+deparse_CommentStmt(ObjectAddress address, Node *parsetree)
+{
+ CommentStmt *node = (CommentStmt *) parsetree;
+ ObjTree *ret;
+ char *identity;
+
+ /* Comment on subscription is not supported */
+ if (node->objtype == OBJECT_SUBSCRIPTION)
+ return NULL;
+
+ /*
+ * Constraints are sufficiently different that it is easier to handle them
+ * separately.
+ */
+ if (node->objtype == OBJECT_DOMCONSTRAINT ||
+ node->objtype == OBJECT_TABCONSTRAINT)
+ {
+ Assert(address.classId == ConstraintRelationId);
+ return deparse_CommentOnConstraintSmt(address.objectId, parsetree);
+ }
+
+ ret = new_objtree_VA("COMMENT ON %{objtype}s", 1,
+ "objtype", ObjTypeString,
+ (char *) stringify_objtype(node->objtype, false));
+
+ /*
+ * Add the object identity clause. For zero argument aggregates we need
+ * to add the (*) bit; in all other cases we can just use
+ * getObjectIdentity.
+ *
+ * XXX shouldn't we instead fix the object identities for zero-argument
+ * aggregates?
+ */
+ if (node->objtype == OBJECT_AGGREGATE)
+ {
+ HeapTuple procTup;
+ Form_pg_proc procForm;
+
+ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(address.objectId));
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for procedure with OID %u",
+ address.objectId);
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+ if (procForm->pronargs == 0)
+ identity = psprintf("%s(*)",
+ quote_qualified_identifier(get_namespace_name(procForm->pronamespace),
+ NameStr(procForm->proname)));
+ else
+ identity = getObjectIdentity(&address, false);
+ ReleaseSysCache(procTup);
+ }
+ else
+ identity = getObjectIdentity(&address, false);
+
+ append_string_object(ret, "%{identity}s", "identity", identity);
+
+ /* Add the comment clause; can be either NULL or a quoted literal. */
+ append_literal_or_null(ret, "IS %{comment}s", node->comment);
+
+ return ret;
+}
+
+/*
+ * Deparse a SECURITY LABEL command.
+ *
+ * Verbose syntax
+ * SECURITY LABEL FOR %{provider}s ON %{object_type_name}s %{identity}s IS %{label}s
+ */
+static ObjTree *
+deparse_SecLabelStmt(CollectedCommand *cmd)
+{
+ ObjTree *ret;
+ SecLabelStmt *node = (SecLabelStmt *) cmd->parsetree;
+
+ /* Don't deparse sql commands generated while creating extension */
+ if (cmd->in_extension)
+ return NULL;
+
+ Assert(cmd->d.seclabel.provider);
+
+ ret = new_objtree_VA("SECURITY LABEL FOR %{provider}s ON %{objtype}s %{identity}s", 3,
+ "provider", ObjTypeString, cmd->d.seclabel.provider,
+ "objtype", ObjTypeString, stringify_objtype(node->objtype, false),
+ "identity", ObjTypeString, getObjectIdentity(&(cmd->d.seclabel.address), false));
+
+ /* Add the label clause; can be either NULL or a quoted literal. */
+ append_literal_or_null(ret, "IS %{label}s", node->label);
+
+ return ret;
+}
+
+/*
+ * Deparse a CreateAmStmt (CREATE ACCESS METHOD).
+ *
+ * Given an access method OID and the parse tree that created it, return an
+ * ObjTree representing the CREATE ACCESS METHOD command.
+ *
+ * Verbose syntax
+ * CREATE ACCESS METHOD %{identity}I TYPE %{am_type}s HANDLER %{handler}D
+ */
+static ObjTree *
+deparse_CreateAmStmt(Oid objectId, Node *parsetree)
+{
+ ObjTree *ret;
+ HeapTuple amTup;
+ Form_pg_am amForm;
+ char *amtype;
+
+ amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(amTup))
+ elog(ERROR, "cache lookup failed for access method with OID %u",
+ objectId);
+ amForm = (Form_pg_am) GETSTRUCT(amTup);
+
+ switch (amForm->amtype)
+ {
+ case 'i':
+ amtype = "INDEX";
+ break;
+ case 't':
+ amtype = "TABLE";
+ break;
+ default:
+ elog(ERROR, "invalid type %c for access method", amForm->amtype);
+ }
+
+ ret = new_objtree_VA("CREATE ACCESS METHOD %{identity}I TYPE %{am_type}s HANDLER %{handler}D", 3,
+ "identity", ObjTypeString,
+ NameStr(amForm->amname),
+ "am_type", ObjTypeString, amtype,
+ "handler", ObjTypeObject,
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ amForm->amhandler));
+
+ ReleaseSysCache(amTup);
+
+ return ret;
+}
+
+/*
+ * Deparse the publication column list.
+ *
+ * Given a tuple of pg_publication_rel, return an objTree that represent the
+ * column names.
+ */
+static ObjTree *
+deparse_PublicationRelationColumnList(HeapTuple pubreltup)
+{
+ bool isnull;
+ List *collist = NIL;
+ ObjTree *columnlist;
+ Datum attrsdatum;
+
+ attrsdatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubreltup,
+ Anum_pg_publication_rel_prattrs,
+ &isnull);
+
+ columnlist = new_objtree("");
+ if (!isnull)
+ {
+ Form_pg_publication_rel pubrel;
+ ArrayType *arr;
+ int nelems;
+ int16 *elems;
+
+ arr = DatumGetArrayTypeP(attrsdatum);
+ nelems = ARR_DIMS(arr)[0];
+ elems = (int16 *) ARR_DATA_PTR(arr);
+
+ pubrel = (Form_pg_publication_rel) GETSTRUCT(pubreltup);
+
+ for (int i = 0; i < nelems; i++)
+ {
+ char *colname = get_attname(pubrel->prrelid, elems[i], false);
+ collist = lappend(collist, new_string_object(colname));
+ }
+
+ append_array_object(columnlist, "(%{cols:, }s)", collist);
+ }
+ else
+ append_not_present(columnlist);
+
+ return columnlist;
+}
+
+/*
+ * Deparse the publication where clause.
+ *
+ * Given a tuple of pg_publication_rel, return a objTree that represent the
+ * publication where clause.
+ */
+static ObjTree *
+deparse_PublicationRelationWhereClause(HeapTuple pubreltup)
+{
+ Datum qualdatum;
+ ObjTree *whereclause;
+ bool isnull;
+
+ qualdatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubreltup,
+ Anum_pg_publication_rel_prqual, &isnull);
+
+ whereclause = new_objtree("");
+ if (!isnull)
+ {
+ Form_pg_publication_rel pubrel;
+ Node *qualnode;
+ List *context;
+ char *qualstr;
+ Oid relid;
+
+ pubrel = (Form_pg_publication_rel) GETSTRUCT(pubreltup);
+ relid = pubrel->prrelid;
+
+ context = deparse_context_for(get_rel_name(relid), relid);
+ qualnode = stringToNode(TextDatumGetCString(qualdatum));
+ qualstr = deparse_expression(qualnode, context, false, false);
+
+ append_string_object(whereclause, "WHERE %{where}s", "where", qualstr);
+ }
+ else
+ append_not_present(whereclause);
+
+ return whereclause;
+}
+
+/*
+ * Subroutine for CREATE PUBLICATION deparsing.
+ *
+ * Deal with all the publication table information including the where clause
+ * and column list.
+ */
+static List *
+deparse_PublicationRelationDefs(Oid puboid)
+{
+ Relation pubrelsrel;
+ ScanKeyData scankey;
+ SysScanDesc scan;
+ HeapTuple tup;
+ List *reldefs = NIL;
+
+ pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
+
+ ScanKeyInit(&scankey,
+ Anum_pg_publication_rel_prpubid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(puboid));
+
+ scan = systable_beginscan(pubrelsrel, PublicationRelPrpubidIndexId,
+ true, NULL, 1, &scankey);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_publication_rel pubrel;
+ Oid relid;
+ ObjTree *columnlist;
+ ObjTree *whereclause;
+ ObjTree *reldef;
+
+ pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
+ relid = pubrel->prrelid;
+
+ reldef = new_objtree_VA("%{tablename}D", 1,
+ "tablename", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId, relid));
+
+ columnlist = deparse_PublicationRelationColumnList(tup);
+ append_object_object(reldef, "%{column_list}s", columnlist);
+
+ whereclause = deparse_PublicationRelationWhereClause(tup);
+ append_object_object(reldef, "%{where_clause}s", whereclause);
+
+ reldefs = lappend(reldefs, new_object_object(reldef));
+ }
+
+ systable_endscan(scan);
+ table_close(pubrelsrel, AccessShareLock);
+
+ return reldefs;
+}
+
+/*
+ * Deparse a CreatePublicationStmt (CREATE PUBLICATION).
+ *
+ * Given an publication OID and the parse tree that created it, return an
+ * ObjTree representing the CREATE PUBLICATION command.
*
* Verbose syntax
- * ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I
+ * CREATE PUBLICATION %{identity}I %{for_tables}s %{for_schemas}s %{with_clause}s
* OR
- * ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I
+ * CREATE PUBLICATION %{identity}I FOR ALL TABLES %{with_clause}s
+ */
+static ObjTree *
+deparse_CreatePublicationStmt(Oid objectId, Node *parsetree)
+{
+ CreatePublicationStmt *node = (CreatePublicationStmt *) parsetree;
+ ObjTree *createPub;
+ ObjTree *tmp;
+ HeapTuple pubTup;
+ Form_pg_publication pubForm;
+ List *list = NIL;
+ ListCell *cell;
+
+ pubTup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(pubTup))
+ elog(ERROR, "cache lookup failed for access method with OID %u",
+ objectId);
+ pubForm = (Form_pg_publication) GETSTRUCT(pubTup);
+
+ createPub = new_objtree_VA("CREATE PUBLICATION %{identity}s", 1,
+ "identity", ObjTypeString,
+ NameStr(pubForm->pubname));
+
+ if (node->for_all_tables)
+ append_format_string(createPub, "FOR ALL TABLES");
+ else
+ {
+ List *oldschemaids;
+ StringInfoData for_schemas;
+
+ /* FOR TABLES t1,t2,... */
+ tmp = new_objtree("FOR TABLE");
+
+ list = deparse_PublicationRelationDefs(objectId);
+ if (list != NIL)
+ append_array_object(tmp, "%{tables:, }s", list);
+ else
+ append_not_present(tmp);
+
+ append_object_object(createPub, "%{for_tables}s", tmp);
+
+ /* FOR TABLES IN SCHEMA s1,s2,... */
+ initStringInfo(&for_schemas);
+ if (list != NIL)
+ appendStringInfoString(&for_schemas, ", ");
+ else
+ appendStringInfoString(&for_schemas, "FOR ");
+ appendStringInfoString(&for_schemas, "TABLES IN SCHEMA");
+
+ tmp = new_objtree(for_schemas.data);
+ list = NIL;
+ oldschemaids = GetPublicationSchemas(pubForm->oid);
+ foreach(cell, oldschemaids)
+ {
+ Oid schemaid = lfirst_oid(cell);
+ list = lappend(list, new_string_object(get_namespace_name(schemaid)));
+ }
+
+ if (list != NIL)
+ append_array_object(tmp, "%{schemas:, }I", list);
+ else
+ append_not_present(tmp);
+
+ append_object_object(createPub, "%{for_schemas}s", tmp);
+ }
+
+ /* WITH clause */
+ tmp = new_objtree("WITH");
+
+ list = NIL;
+ foreach(cell, node->options)
+ {
+ ObjTree *tmp_obj;
+ DefElem *opt = (DefElem *) lfirst(cell);
+
+ tmp_obj = deparse_DefElem(opt, false);
+ list = lappend(list, new_object_object(tmp_obj));
+ }
+
+ if (list != NIL)
+ append_array_object(tmp, "(%{with:, }s)", list);
+ else
+ append_not_present(tmp);
+
+ append_object_object(createPub, "%{with_clause}s", tmp);
+
+ ReleaseSysCache(pubTup);
+
+ return createPub;
+}
+
+/*
+ * Deparse a AlterPublicationStmt (ALTER PUBLICATION).
+ *
+ * Given an publication relation OID or publication schema OID and the parse
+ * tree that added it, return an ObjTree representing the ALTER PUBLICATION
+ * command.
+ *
+ * Note that only ALTER PUBLICATION ADD/SET should be deparsed here, ALTER
+ * PUBLICATION DROP is deparsed in different places.
+ *
+ * XXX ALTER PUBLICATION SET publication_object is converted to ALTER
+ * PUBLICATION ADD/DROP.
+ *
+ * Verbose syntax
+ * ALTER PUBLICATION %{identity}I %{add_object}s
* OR
- * ALTER %{objtype}s %{if_exists}s %{only}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I %{cascade}s
+ * ALTER PUBLICATION %{identity}I %{set_options}s
*/
static ObjTree *
-deparse_RenameStmt(ObjectAddress address, Node *parsetree)
+deparse_AlterPublicationAddStmt(ObjectAddress object, Node *parsetree)
{
- RenameStmt *node = (RenameStmt *) parsetree;
- ObjTree *ret;
- Relation relation;
- Oid schemaId;
+ char *pubname;
+ ObjTree *alterpub;
+ ObjTree *addobject = NULL;
+ ObjTree *setoption = NULL;
+ AlterPublicationStmt *node = (AlterPublicationStmt *) parsetree;
- /*
- * In an ALTER .. RENAME command, we don't have the original name of the
- * object in system catalogs: since we inspect them after the command has
- * executed, the old name is already gone. Therefore, we extract it from
- * the parse node. Note we still extract the schema name from the catalog
- * (it might not be present in the parse node); it cannot possibly have
- * changed anyway.
- */
- switch (node->renameType)
+ switch (object.classId)
{
- case OBJECT_INDEX:
- case OBJECT_TABLE:
- relation = relation_open(address.objectId, AccessShareLock);
- schemaId = RelationGetNamespace(relation);
- ret = new_objtree_VA("ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I", 4,
- "objtype", ObjTypeString,
- stringify_objtype(node->renameType, false),
- "if_exists", ObjTypeString,
- node->missing_ok ? "IF EXISTS" : "",
- "identity", ObjTypeObject,
- new_objtree_for_qualname(schemaId,
- node->relation->relname),
- "newname", ObjTypeString,
- node->newname);
- relation_close(relation, AccessShareLock);
+ /* ADD TABLE */
+ case PublicationRelRelationId:
+ {
+ ObjTree *whereclause;
+ ObjTree *columnlist;
+ Form_pg_publication_rel pubrelform;
+ Oid relid;
+ HeapTuple tup;
+
+ tup = SearchSysCache1(PUBLICATIONREL,
+ ObjectIdGetDatum(object.objectId));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for publication table %u",
+ object.objectId);
+
+ pubrelform = (Form_pg_publication_rel) GETSTRUCT(tup);
+ relid = pubrelform->prrelid;
+
+ addobject = new_objtree_VA("ADD TABLE %{tablename}D", 1,
+ "tablename", ObjTypeObject,
+ new_objtree_for_qualname_id(RelationRelationId, relid));
+
+ columnlist = deparse_PublicationRelationColumnList(tup);
+ append_object_object(addobject, "%{column_list}s", columnlist);
+
+ whereclause = deparse_PublicationRelationWhereClause(tup);
+ append_object_object(addobject, "%{where_clause}s", whereclause);
+
+ ReleaseSysCache(tup);
+ }
break;
- case OBJECT_TABCONSTRAINT:
+ /* ADD TABLES IN SCHEMA */
+ case PublicationNamespaceRelationId:
{
- HeapTuple constrtup;
- Form_pg_constraint constform;
+ Form_pg_publication_namespace pubnspform;
+ HeapTuple tup;
- constrtup = SearchSysCache1(CONSTROID,
- ObjectIdGetDatum(address.objectId));
- if (!HeapTupleIsValid(constrtup))
- elog(ERROR, "cache lookup failed for constraint with OID %u",
- address.objectId);
- constform = (Form_pg_constraint) GETSTRUCT(constrtup);
+ tup = SearchSysCache1(PUBLICATIONNAMESPACE,
+ ObjectIdGetDatum(object.objectId));
- ret = new_objtree_VA("ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", 4,
- "only", ObjTypeString,
- node->relation->inh ? "" : "ONLY",
- "identity", ObjTypeObject,
- new_objtree_for_qualname_id(RelationRelationId,
- constform->conrelid),
- "oldname", ObjTypeString, node->subname,
- "newname", ObjTypeString, node->newname);
- ReleaseSysCache(constrtup);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for publication schema %u",
+ object.objectId);
+
+ pubnspform = (Form_pg_publication_namespace) GETSTRUCT(tup);
+
+ addobject = new_objtree_VA("ADD TABLES IN SCHEMA %{schema}I", 1,
+ "schema", ObjTypeString,
+ get_namespace_name(pubnspform->pnnspid));
+
+ ReleaseSysCache(tup);
}
break;
- case OBJECT_COLUMN:
- relation = relation_open(address.objectId, AccessShareLock);
- schemaId = RelationGetNamespace(relation);
-
- ret = new_objtree_VA("ALTER %{objtype}s", 1,
- "objtype", ObjTypeString,
- stringify_objtype(node->relationType, false));
+ /* SET option */
+ case PublicationRelationId:
+ {
+ List *optionlist = NIL;
+ ListCell *cell;
- /* Composite types do not support IF EXISTS */
- if (node->renameType == OBJECT_COLUMN)
- append_string_object(ret, "%{if_exists}s",
- "if_exists",
- node->missing_ok ? "IF EXISTS" : "");
- if (!node->relation->inh)
- append_string_object(ret, "%{only}s",
- "only",
- "ONLY");
- append_object_object(ret, "%{identity}D",
- new_objtree_for_qualname(schemaId,
- node->relation->relname));
- append_string_object(ret, "RENAME COLUMN %{colname}I",
- "colname", node->subname);
+ Assert(node->options != NIL);
- append_string_object(ret, "TO %{newname}I", "newname",
- node->newname);
+ foreach(cell, node->options)
+ {
+ ObjTree *tmp_obj;
+ DefElem *opt = (DefElem *) lfirst(cell);
- if (node->renameType == OBJECT_ATTRIBUTE)
- append_object_object(ret, "%{cascade}s",
- new_objtree_VA("CASCADE", 1,
- "present", ObjTypeBool,
- node->behavior == DROP_CASCADE));
+ tmp_obj = deparse_DefElem(opt, false);
+ optionlist = lappend(optionlist, new_object_object(tmp_obj));
+ }
- relation_close(relation, AccessShareLock);
+ setoption = new_objtree_VA("SET (%{options:, }s)", 1,
+ "options", ObjTypeArray,
+ optionlist);
+ }
break;
default:
- elog(ERROR, "unsupported object type %d", node->renameType);
+ Assert(false);
+ break;
}
- return ret;
+ pubname = pstrdup(node->pubname);
+
+ alterpub = new_objtree_VA("ALTER PUBLICATION %{identity}s", 1,
+ "identity", ObjTypeString, pubname);
+
+ Assert(addobject || setoption);
+
+ if (addobject)
+ append_object_object(alterpub, "%{add_object}s", addobject);
+ else
+ append_object_object(alterpub, "%{set_options}s", setoption);
+
+ return alterpub;
}
/*
- * Deparse a AlterObjectDependsStmt (ALTER ... DEPENDS ON ...).
+ * Handle deparsing of ALTER PUBLICATION DROP commands.
*
* Verbose syntax
- * ALTER INDEX %{identity}D %{no}s DEPENDS ON EXTENSION %{newname}I
+ * ALTER PUBLICATION %{identity}I %{drop_object}s
*/
-static ObjTree *
-deparse_AlterDependStmt(Oid objectId, Node *parsetree)
+char *
+deparse_AlterPublicationDropStmt(SQLDropObject *obj)
{
- AlterObjectDependsStmt *node = (AlterObjectDependsStmt *) parsetree;
- ObjTree *ret = NULL;
+ ObjTree *alterpub;
+ ObjTree *drop_object = NULL;
+ char objname[NAMEDATALEN];
+ char pubname[NAMEDATALEN];
+ Jsonb *jsonb;
+ char *command;
+ StringInfoData str;
- if (node->objectType == OBJECT_INDEX)
+ if (sscanf(obj->objidentity, "%s in publication %s", objname, pubname) != 2)
+ elog(ERROR, "could not parse ALTER PUBLICATION command \"%s\"",
+ obj->objidentity);
+
+ switch (getObjectClass(&obj->address))
{
- ObjTree *qualified;
- Relation relation = relation_open(objectId, AccessShareLock);
+ /* DROP TABLE */
+ case OCLASS_PUBLICATION_REL:
+ drop_object = new_objtree_VA("DROP TABLE %{tablename}s", 1,
+ "tablename", ObjTypeString, objname);
+ break;
- qualified = new_objtree_for_qualname(relation->rd_rel->relnamespace,
- node->relation->relname);
- relation_close(relation, AccessShareLock);
+ /* DROP TABLES IN SCHEMA */
+ case OCLASS_PUBLICATION_NAMESPACE:
+ drop_object = new_objtree_VA("DROP TABLES IN SCHEMA %{schemaname}s", 1,
+ "schemaname", ObjTypeString, objname);
+ break;
- ret = new_objtree_VA("ALTER INDEX %{identity}D %{no}s DEPENDS ON EXTENSION %{newname}I", 3,
- "identity", ObjTypeObject, qualified,
- "no", ObjTypeString,
- node->remove ? "NO" : "",
- "newname", ObjTypeString, strVal(node->extname));
+ default:
+ Assert(false);
+ break;
}
- else
- elog(LOG, "unrecognized node type in deparse command: %d",
- (int) nodeTag(parsetree));
- return ret;
+ alterpub = new_objtree_VA("ALTER PUBLICATION %{identity}s", 1,
+ "identity", ObjTypeString, pubname);
+
+ Assert(drop_object);
+
+ append_object_object(alterpub, "%{drop_object}s", drop_object);
+
+ initStringInfo(&str);
+ jsonb = objtree_to_jsonb(alterpub, NULL /* Owner/role can be skipped for drop command */);
+ command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
+
+ return command;
}
/*
@@ -3666,6 +9896,40 @@ deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
/* This switch needs to handle everything that ProcessUtilitySlow does */
switch (nodeTag(parsetree))
{
+ case T_AlterCollationStmt:
+ *include_owner = false;
+ return deparse_AlterCollation(objectId, parsetree);
+
+ case T_AlterDomainStmt:
+ *include_owner = false;
+ return deparse_AlterDomainStmt(objectId, parsetree,
+ cmd->d.simple.secondaryObject);
+
+ case T_AlterEnumStmt:
+ *include_owner = false;
+ return deparse_AlterEnumStmt(objectId, parsetree);
+
+ case T_AlterExtensionContentsStmt:
+ *include_owner = false;
+ return deparse_AlterExtensionContentsStmt(objectId, parsetree,
+ cmd->d.simple.secondaryObject);
+
+ case T_AlterExtensionStmt:
+ *include_owner = false;
+ return deparse_AlterExtensionStmt(objectId, parsetree);
+
+ case T_AlterFdwStmt:
+ *include_owner = false;
+ return deparse_AlterFdwStmt(objectId, parsetree);
+
+ case T_AlterForeignServerStmt:
+ *include_owner = false;
+ return deparse_AlterForeignServerStmt(objectId, parsetree);
+
+ case T_AlterFunctionStmt:
+ *include_owner = false;
+ return deparse_AlterFunction(objectId, parsetree);
+
case T_AlterObjectDependsStmt:
*include_owner = false;
return deparse_AlterDependStmt(objectId, parsetree);
@@ -3676,20 +9940,138 @@ deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
parsetree,
cmd->d.simple.secondaryObject);
+ case T_AlterOperatorStmt:
+ *include_owner = false;
+ return deparse_AlterOperatorStmt(objectId, parsetree);
+
case T_AlterOwnerStmt:
*include_owner = false;
return deparse_AlterOwnerStmt(cmd->d.simple.address, parsetree);
+ case T_AlterPolicyStmt:
+ *include_owner = false;
+ return deparse_AlterPolicyStmt(objectId, parsetree);
+
+ case T_AlterSeqStmt:
+ *include_owner = false;
+ return deparse_AlterSeqStmt(objectId, parsetree);
+
+ case T_AlterStatsStmt:
+ *include_owner = false;
+ return deparse_AlterStatsStmt(objectId, parsetree);
+
+ case T_AlterTSDictionaryStmt:
+ *include_owner = false;
+ return deparse_AlterTSDictionaryStmt(objectId, parsetree);
+
+ case T_AlterTypeStmt:
+ *include_owner = false;
+ return deparse_AlterTypeSetStmt(objectId, parsetree);
+
+ case T_AlterUserMappingStmt:
+ *include_owner = false;
+ return deparse_AlterUserMappingStmt(objectId, parsetree);
+
+ case T_CommentStmt:
+ *include_owner = false;
+ return deparse_CommentStmt(cmd->d.simple.address, parsetree);
+
+ case T_CompositeTypeStmt:
+ return deparse_CompositeTypeStmt(objectId, parsetree);
+
+ case T_CreateAmStmt:
+ return deparse_CreateAmStmt(objectId, parsetree);
+
+ case T_CreateCastStmt:
+ return deparse_CreateCastStmt(objectId, parsetree);
+
+ case T_CreateConversionStmt:
+ return deparse_CreateConversion(objectId, parsetree);
+
+ case T_CreateDomainStmt:
+ return deparse_CreateDomain(objectId, parsetree);
+
+ case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
+ return deparse_CreateEnumStmt(objectId, parsetree);
+
+ case T_CreateExtensionStmt:
+ return deparse_CreateExtensionStmt(objectId, parsetree);
+
+ case T_CreateFdwStmt:
+ return deparse_CreateFdwStmt(objectId, parsetree);
+
+ case T_CreateForeignServerStmt:
+ return deparse_CreateForeignServerStmt(objectId, parsetree);
+
+ case T_CreateFunctionStmt:
+ return deparse_CreateFunction(objectId, parsetree);
+
+ case T_CreateOpFamilyStmt:
+ return deparse_CreateOpFamily(objectId, parsetree);
+
+ case T_CreatePLangStmt:
+ return deparse_CreateLangStmt(objectId, parsetree);
+
+ case T_CreatePolicyStmt:
+ return deparse_CreatePolicyStmt(objectId, parsetree);
+
+ case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
+ return deparse_CreateRangeStmt(objectId, parsetree);
+
+ case T_CreateSchemaStmt:
+ return deparse_CreateSchemaStmt(objectId, parsetree);
+
+ case T_CreateSeqStmt:
+ return deparse_CreateSeqStmt(objectId, parsetree);
+
+ case T_CreateStatsStmt:
+ return deparse_CreateStatisticsStmt(objectId, parsetree);
+
case T_CreateStmt:
return deparse_CreateStmt(objectId, parsetree);
+ case T_CreateForeignTableStmt:
+ return deparse_CreateForeignTableStmt(objectId, parsetree);
+
+ case T_CreateTableAsStmt: /* CREATE MATERIALIZED VIEW */
+ return deparse_CreateTableAsStmt_vanilla(objectId, parsetree);
+
+ case T_CreateTransformStmt:
+ return deparse_CreateTransformStmt(objectId, parsetree);
+
+ case T_CreateTrigStmt:
+ return deparse_CreateTrigStmt(objectId, parsetree);
+
+ case T_CreateUserMappingStmt:
+ return deparse_CreateUserMappingStmt(objectId, parsetree);
+
+ case T_DefineStmt:
+ return deparse_DefineStmt(objectId, parsetree,
+ cmd->d.simple.secondaryObject);
+
case T_IndexStmt:
return deparse_IndexStmt(objectId, parsetree);
+ case T_RefreshMatViewStmt:
+ *include_owner = false;
+ return deparse_RefreshMatViewStmt(objectId, parsetree);
+
case T_RenameStmt:
*include_owner = false;
return deparse_RenameStmt(cmd->d.simple.address, parsetree);
+ case T_RuleStmt:
+ return deparse_RuleStmt(objectId, parsetree);
+
+ case T_ViewStmt: /* CREATE VIEW */
+ return deparse_ViewStmt(objectId, parsetree);
+
+ case T_CreatePublicationStmt:
+ return deparse_CreatePublicationStmt(objectId, parsetree);
+
+ case T_AlterPublicationStmt:
+ return deparse_AlterPublicationAddStmt(cmd->d.simple.address, parsetree);
+
default:
elog(LOG, "unrecognized node type in deparse command: %d",
(int) nodeTag(parsetree));
@@ -3755,9 +10137,32 @@ deparse_utility_command(CollectedCommand *cmd, bool include_owner, bool verbose_
tree = deparse_AlterRelation(cmd);
include_owner = false;
break;
+ case SCT_Grant:
+ tree = deparse_GrantStmt(cmd);
+ include_owner = false;
+ break;
case SCT_CreateTableAs:
tree = deparse_CreateTableAsStmt(cmd);
break;
+ case SCT_AlterOpFamily:
+ include_owner = false;
+ tree = deparse_AlterOpFamily(cmd);
+ break;
+ case SCT_CreateOpClass:
+ tree = deparse_CreateOpClassStmt(cmd);
+ break;
+ case SCT_AlterDefaultPrivileges:
+ include_owner = false;
+ tree = deparse_AlterDefaultPrivilegesStmt(cmd);
+ break;
+ case SCT_AlterTSConfig:
+ include_owner = false;
+ tree = deparse_AlterTSConfigurationStmt(cmd);
+ break;
+ case SCT_SecurityLabel:
+ include_owner = false;
+ tree = deparse_SecLabelStmt(cmd);
+ break;
default:
elog(ERROR, "unexpected deparse node type %d", cmd->type);
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 38c11f000c..1b75003b5f 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -2074,6 +2074,37 @@ EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
MemoryContextSwitchTo(oldcxt);
}
+/*
+ * EventTriggerCollectSecLabel
+ * Save data about an SECURITY LABEL command being executed
+ */
+void
+EventTriggerCollectSecLabel(ObjectAddress address, char *provider,
+ SecLabelStmt *stmt)
+{
+ MemoryContext oldcxt;
+ CollectedCommand *command;
+
+ /* ignore if event trigger context not set, or collection disabled */
+ if (!currentEventTriggerState ||
+ currentEventTriggerState->commandCollectionInhibited)
+ return;
+
+ oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+ command = palloc0(sizeof(CollectedCommand));
+ command->type = SCT_SecurityLabel;
+ command->in_extension = creating_extension;
+ command->role = GetUserNameFromId(GetUserId(), false);
+ command->d.seclabel.address = address;
+ command->d.seclabel.provider = provider;
+ command->parsetree = (Node *) copyObject(stmt);
+
+ currentEventTriggerState->commandList =
+ lappend(currentEventTriggerState->commandList, command);
+ MemoryContextSwitchTo(oldcxt);
+}
+
/*
* In a ddl_command_end event trigger, this function reports the DDL commands
* being run.
@@ -2125,6 +2156,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
case SCT_AlterOpFamily:
case SCT_CreateOpClass:
case SCT_AlterTSConfig:
+ case SCT_SecurityLabel:
case SCT_CreateTableAs:
{
char *identity;
@@ -2143,6 +2175,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
addr = cmd->d.createopc.address;
else if (cmd->type == SCT_AlterTSConfig)
addr = cmd->d.atscfg.address;
+ else if (cmd->type == SCT_SecurityLabel)
+ addr = cmd->d.seclabel.address;
else if (cmd->type == SCT_CreateTableAs)
addr = cmd->d.ctas.address;
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index e3aad1c883..a8a9175d15 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -104,6 +104,7 @@ parse_publication_options(ParseState *pstate,
pubactions->pubtruncate = true;
pubactions->pubddl_table = false;
pubactions->pubddl_index = false;
+ pubactions->pubddl_all = false;
*publish_via_partition_root = false;
/* Parse options */
@@ -180,6 +181,7 @@ parse_publication_options(ParseState *pstate,
*/
pubactions->pubddl_table = false;
pubactions->pubddl_index = false;
+ pubactions->pubddl_all = false;
*ddl_type_given = true;
ddl_level = defGetString(defel);
@@ -194,7 +196,9 @@ parse_publication_options(ParseState *pstate,
{
char *publish_opt = (char *) lfirst(lc3);
- if (strcmp(publish_opt, "table") == 0)
+ if (strcmp(publish_opt, "all") == 0)
+ pubactions->pubddl_all = true;
+ else if (strcmp(publish_opt, "table") == 0)
pubactions->pubddl_table = true;
else if (strcmp(publish_opt, "index") == 0)
pubactions->pubddl_index = true;
@@ -847,28 +851,39 @@ CreateDDLReplicaEventTriggers(PublicationActions pubactions, Oid puboid)
List *init_commands = NIL;
List *end_commands = NIL;
- if (pubactions.pubddl_table)
+ if (pubactions.pubddl_all)
{
- start_commands = lappend_int(start_commands, CMDTAG_DROP_TABLE);
+ start_commands = end_commands = init_commands =
+ GetCommandTagsForDDLRepl();
+
rewrite_commands = lappend_int(rewrite_commands, CMDTAG_ALTER_TABLE);
+ }
+ else
+ {
+ if (pubactions.pubddl_table)
+ {
+ start_commands = lappend_int(start_commands, CMDTAG_DROP_TABLE);
+ rewrite_commands = lappend_int(rewrite_commands, CMDTAG_ALTER_TABLE);
- init_commands = lappend_int(init_commands, CMDTAG_CREATE_TABLE_AS);
- init_commands = lappend_int(init_commands, CMDTAG_SELECT_INTO);
+ init_commands = lappend_int(init_commands, CMDTAG_CREATE_TABLE_AS);
+ init_commands = lappend_int(init_commands, CMDTAG_SELECT_INTO);
- end_commands = lappend_int(end_commands, CMDTAG_CREATE_TABLE);
- end_commands = lappend_int(end_commands, CMDTAG_ALTER_TABLE);
- end_commands = lappend_int(end_commands, CMDTAG_DROP_TABLE);
- }
+ end_commands = lappend_int(end_commands, CMDTAG_CREATE_TABLE);
+ end_commands = lappend_int(end_commands, CMDTAG_ALTER_TABLE);
+ end_commands = lappend_int(end_commands, CMDTAG_DROP_TABLE);
+ }
- if (pubactions.pubddl_index)
- {
- start_commands = lappend_int(start_commands, CMDTAG_DROP_INDEX);
+ if (pubactions.pubddl_index)
+ {
+ start_commands = lappend_int(start_commands, CMDTAG_DROP_INDEX);
- end_commands = lappend_int(end_commands, CMDTAG_CREATE_INDEX);
- end_commands = lappend_int(end_commands, CMDTAG_ALTER_INDEX);
- end_commands = lappend_int(end_commands, CMDTAG_DROP_INDEX);
+ end_commands = lappend_int(end_commands, CMDTAG_CREATE_INDEX);
+ end_commands = lappend_int(end_commands, CMDTAG_ALTER_INDEX);
+ end_commands = lappend_int(end_commands, CMDTAG_DROP_INDEX);
+ }
}
+
/* Create the ddl_command_end event trigger */
if (end_commands != NIL)
CreateDDLReplicaEventTrigger(PUB_TRIG_EVENT1, end_commands, puboid);
@@ -1007,6 +1022,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
BoolGetDatum(pubactions.pubddl_table);
values[Anum_pg_publication_pubddl_index - 1] =
BoolGetDatum(pubactions.pubddl_index);
+ values[Anum_pg_publication_pubddl_all - 1] =
+ BoolGetDatum(pubactions.pubddl_all);
values[Anum_pg_publication_pubviaroot - 1] =
BoolGetDatum(publish_via_partition_root);
@@ -1203,7 +1220,9 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
}
}
- if (ddl_type_given && (pubactions.pubddl_table || pubactions.pubddl_index))
+ if (ddl_type_given &&
+ (pubactions.pubddl_table || pubactions.pubddl_index ||
+ pubactions.pubddl_all))
{
if (root_relids == NIL)
root_relids = GetPublicationRelations(pubform->oid,
@@ -1239,7 +1258,8 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
{
/* Recreate the event triggers if the ddl option is changed. */
if (pubform->pubddl_table != pubactions.pubddl_table ||
- pubform->pubddl_index != pubactions.pubddl_index)
+ pubform->pubddl_index != pubactions.pubddl_index ||
+ pubform->pubddl_all != pubactions.pubddl_all)
{
DropDDLReplicaEventTriggers(pubform->oid);
CreateDDLReplicaEventTriggers(pubactions, pubform->oid);
@@ -1250,6 +1270,9 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
values[Anum_pg_publication_pubddl_index - 1] = BoolGetDatum(pubactions.pubddl_index);
replaces[Anum_pg_publication_pubddl_index - 1] = true;
+
+ values[Anum_pg_publication_pubddl_all - 1] = BoolGetDatum(pubactions.pubddl_all);
+ replaces[Anum_pg_publication_pubddl_all - 1] = true;
}
if (publish_via_partition_root_given)
@@ -1357,7 +1380,8 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
if (stmt->action == AP_AddObjects)
{
- if (pubform->pubddl_table || pubform->pubddl_index)
+ if (pubform->pubddl_table || pubform->pubddl_index ||
+ pubform->pubddl_all)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot add table to publication \"%s\" if DDL replication is enabled",
@@ -1381,7 +1405,8 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
List *delrels = NIL;
ListCell *oldlc;
- if (pubform->pubddl_table || pubform->pubddl_index)
+ if (pubform->pubddl_table || pubform->pubddl_index ||
+ pubform->pubddl_all)
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot add table to publication \"%s\" if DDL replication is enabled",
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7ff16e3276..7ee54cde8e 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -18,6 +18,7 @@
#include "catalog/indexing.h"
#include "catalog/pg_seclabel.h"
#include "catalog/pg_shseclabel.h"
+#include "commands/event_trigger.h"
#include "commands/seclabel.h"
#include "miscadmin.h"
#include "utils/builtins.h"
@@ -213,6 +214,11 @@ ExecSecLabelStmt(SecLabelStmt *stmt)
if (relation != NULL)
relation_close(relation, NoLock);
+ /* Pass the info to event triggers about the SECURITY LABEL. */
+ if (EventTriggerSupportsObjectType(stmt->objtype))
+ EventTriggerCollectSecLabel(address, pstrdup(provider->provider_name),
+ stmt);
+
return address;
}
diff --git a/src/backend/replication/logical/ddltrigger.c b/src/backend/replication/logical/ddltrigger.c
index 11e33b9b65..86c520ba11 100644
--- a/src/backend/replication/logical/ddltrigger.c
+++ b/src/backend/replication/logical/ddltrigger.c
@@ -226,21 +226,103 @@ publication_deparse_ddl_command_end(PG_FUNCTION_ARGS)
EventTriggerData *trigdata;
char *command;
DeparsedCommandType cmdtype;
+ const char *tmptype;
+ ObjectClass objclass;
+ ObjectAddress objaddr;
trigdata = (EventTriggerData *) fcinfo->context;
stmt = (DropStmt *) trigdata->parsetree;
obj = slist_container(SQLDropObject, next, iter.cur);
+ objaddr = obj->address;
+ objclass = getObjectClass(&objaddr);
if (strcmp(obj->objecttype, "table") == 0)
cmdtype = DCT_TableDropEnd;
- else if (strcmp(obj->objecttype, "index") == 0)
+ else if (objclass == OCLASS_SCHEMA ||
+ objclass == OCLASS_OPERATOR ||
+ objclass == OCLASS_OPCLASS ||
+ objclass == OCLASS_OPFAMILY ||
+ objclass == OCLASS_CAST ||
+ objclass == OCLASS_TYPE ||
+ objclass == OCLASS_TRIGGER ||
+ objclass == OCLASS_CONVERSION ||
+ objclass == OCLASS_POLICY ||
+ objclass == OCLASS_REWRITE ||
+ objclass == OCLASS_EXTENSION ||
+ objclass == OCLASS_FDW ||
+ objclass == OCLASS_TSCONFIG ||
+ objclass == OCLASS_TSDICT ||
+ objclass == OCLASS_TSTEMPLATE ||
+ objclass == OCLASS_TSPARSER ||
+ objclass == OCLASS_TRANSFORM ||
+ objclass == OCLASS_FOREIGN_SERVER ||
+ objclass == OCLASS_COLLATION ||
+ objclass == OCLASS_USER_MAPPING ||
+ objclass == OCLASS_LANGUAGE ||
+ objclass == OCLASS_STATISTIC_EXT ||
+ objclass == OCLASS_AM ||
+ objclass == OCLASS_PUBLICATION ||
+ objclass == OCLASS_PUBLICATION_REL ||
+ objclass == OCLASS_PUBLICATION_NAMESPACE ||
+ strcmp(obj->objecttype, "foreign table") == 0 ||
+ strcmp(obj->objecttype, "index") == 0 ||
+ strcmp(obj->objecttype, "sequence") == 0 ||
+ strcmp(obj->objecttype, "view") == 0 ||
+ strcmp(obj->objecttype, "function") == 0 ||
+ strcmp(obj->objecttype, "materialized view") == 0 ||
+ strcmp(obj->objecttype, "procedure") == 0 ||
+ strcmp(obj->objecttype, "routine") == 0 ||
+ strcmp(obj->objecttype, "aggregate") == 0)
cmdtype = DCT_ObjectDropEnd;
else
continue;
- command = deparse_drop_command(obj->objidentity, obj->objecttype,
- stmt->behavior);
+ /* Change foreign-data wrapper to foreign data wrapper */
+ if (strncmp(obj->objecttype, "foreign-data wrapper", 20) == 0)
+ {
+ tmptype = pstrdup("foreign data wrapper");
+ command = deparse_drop_command(obj->objidentity, tmptype,
+ stmt->behavior);
+ }
+
+ /* Change statistics object to statistics */
+ else if (strncmp(obj->objecttype, "statistics object",
+ strlen("statistics object")) == 0)
+ {
+ tmptype = pstrdup("statistics");
+ command = deparse_drop_command(obj->objidentity, tmptype,
+ stmt->behavior);
+ }
+
+ /*
+ * Object identity needs to be modified to make the drop work
+ *
+ * FROM: <role> on server <servername> TO : for <role> server
+ * <servername>
+ *
+ */
+ else if (strncmp(obj->objecttype, "user mapping", 12) == 0)
+ {
+ char *on_server;
+
+ tmptype = palloc(strlen(obj->objidentity) + 2);
+ on_server = strstr(obj->objidentity, "on server");
+
+ sprintf((char *) tmptype, "for ");
+ strncat((char *) tmptype, obj->objidentity, on_server - obj->objidentity);
+ strcat((char *) tmptype, on_server + 3);
+ command = deparse_drop_command(tmptype, obj->objecttype,
+ stmt->behavior);
+ }
+ else if (strncmp(obj->objecttype, "publication namespace",
+ strlen("publication namespace")) == 0 ||
+ strncmp(obj->objecttype, "publication relation",
+ strlen("publication relation")) == 0)
+ command = deparse_AlterPublicationDropStmt(obj);
+ else
+ command = deparse_drop_command(obj->objidentity, obj->objecttype,
+ stmt->behavior);
if (command)
LogLogicalDDLMessage("deparse", obj->address.objectId, cmdtype,
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 9a54cbd152..80cd6fad56 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -224,6 +224,7 @@ typedef struct DDLSyncCache
{
bool valid;
bool pubindex;
+ bool puball;
} DDLSyncCache;
static DDLSyncCache *ddlcache = NULL;
@@ -1754,6 +1755,8 @@ is_object_published(LogicalDecodingContext *ctx, Oid objid)
RelationSyncEntry *relentry;
PGOutputData *data = (PGOutputData *) ctx->output_plugin_private;
+ build_ddl_sync_cache(data);
+
/* First check the DDL command filter. */
switch (get_rel_relkind(objid))
{
@@ -1766,15 +1769,14 @@ is_object_published(LogicalDecodingContext *ctx, Oid objid)
* Skip sending this ddl if we don't publish ddl message or the ddl
* need to be published via its root relation.
*/
- if (!relentry->pubactions.pubddl_table ||
+ if ((!relentry->pubactions.pubddl_table &&
+ !ddlcache->puball) ||
relentry->publish_as_relid != objid)
return false;
break;
case RELKIND_INDEX:
- build_ddl_sync_cache(data);
-
- if (!ddlcache->pubindex)
+ if (!ddlcache->pubindex && !ddlcache->puball)
return false;
/* Get the table OID that the index is for. */
@@ -1787,7 +1789,8 @@ is_object_published(LogicalDecodingContext *ctx, Oid objid)
relentry = get_rel_sync_entry(data, relation);
RelationClose(relation);
- if (!relentry->pubactions.pubddl_table ||
+ if ((!relentry->pubactions.pubddl_table &&
+ !ddlcache->puball) ||
relentry->publish_as_relid != objid)
return false;
@@ -2240,7 +2243,8 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->streamed_txns = NIL;
entry->pubactions.pubinsert = entry->pubactions.pubupdate =
entry->pubactions.pubdelete = entry->pubactions.pubtruncate =
- entry->pubactions.pubddl_table = entry->pubactions.pubddl_index = false;
+ entry->pubactions.pubddl_table = entry->pubactions.pubddl_index =
+ entry->pubactions.pubddl_all = false;
entry->new_slot = NULL;
entry->old_slot = NULL;
memset(entry->exprstate, 0, sizeof(entry->exprstate));
@@ -2288,6 +2292,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->pubactions.pubtruncate = false;
entry->pubactions.pubddl_table = false;
entry->pubactions.pubddl_index = false;
+ entry->pubactions.pubddl_all = false;
/*
* Tuple slots cleanups. (Will be rebuilt later if needed).
@@ -2403,6 +2408,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate;
entry->pubactions.pubddl_table |= pub->pubactions.pubddl_table;
entry->pubactions.pubddl_index |= pub->pubactions.pubddl_index;
+ entry->pubactions.pubddl_all |= pub->pubactions.pubddl_all;
/*
* We want to publish the changes as the top-most ancestor
@@ -2488,6 +2494,7 @@ build_ddl_sync_cache(PGOutputData *data)
return;
ddlcache->pubindex = false;
+ ddlcache->puball = false;
reload_publications(data);
@@ -2496,6 +2503,7 @@ build_ddl_sync_cache(PGOutputData *data)
Publication *pub = lfirst(lc);
ddlcache->pubindex |= pub->pubactions.pubddl_index;
+ ddlcache->puball |= pub->pubactions.pubddl_all;
}
ddlcache->valid = true;
diff --git a/src/backend/tcop/cmdtag.c b/src/backend/tcop/cmdtag.c
index ce09c5f233..7e03782524 100644
--- a/src/backend/tcop/cmdtag.c
+++ b/src/backend/tcop/cmdtag.c
@@ -14,6 +14,7 @@
#include "postgres.h"
#include "miscadmin.h"
+#include "nodes/pg_list.h"
#include "tcop/cmdtag.h"
#include "utils/builtins.h"
@@ -58,16 +59,15 @@ GetCommandTagNameAndLen(CommandTag commandTag, Size *len)
return tag_behavior[commandTag].name;
}
-CommandTag *
-GetCommandTagsForDDLRepl(int *ncommands)
+List *
+GetCommandTagsForDDLRepl(void)
{
- CommandTag *ddlrepl_commands = palloc0(COMMAND_TAG_NEXTTAG * sizeof(CommandTag));
- *ncommands = 0;
+ List *ddlrepl_commands = NIL;
for(int i = 0; i < COMMAND_TAG_NEXTTAG; i++)
{
if (tag_behavior[i].ddl_replication_ok)
- ddlrepl_commands[(*ncommands)++] = (CommandTag) i;
+ ddlrepl_commands = lappend_int(ddlrepl_commands, i);
}
return ddlrepl_commands;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 578a229ffc..34a9c853a6 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1449,7 +1449,8 @@ ProcessUtilitySlow(ParseState *pstate,
address = DefineCollation(pstate,
stmt->defnames,
stmt->definition,
- stmt->if_not_exists);
+ stmt->if_not_exists,
+ &secondaryObject);
break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
@@ -1839,6 +1840,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_SecLabelStmt:
address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
+ commandCollected = true;
break;
case T_CreateAmStmt:
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 296930eb3b..e46c39d620 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -307,6 +307,57 @@ format_procedure_qualified(Oid procedure_oid)
return format_procedure_extended(procedure_oid, FORMAT_PROC_FORCE_QUALIFY);
}
+/*
+ * Append the parenthesized arguments of the given pg_proc row into the output
+ * buffer. force_qualify indicates whether to schema-qualify type names
+ * regardless of visibility.
+ */
+static void
+format_procedure_args_internal(Form_pg_proc procform, StringInfo buf,
+ bool force_qualify)
+{
+ int i;
+ char *(*func[2]) (Oid) = {format_type_be, format_type_be_qualified};
+
+ appendStringInfoChar(buf, '(');
+ for (i = 0; i < procform->pronargs; i++)
+ {
+ Oid thisargtype = procform->proargtypes.values[i];
+ char *argtype;
+
+ if (i > 0)
+ appendStringInfoChar(buf, ',');
+
+ argtype = func[force_qualify] (thisargtype);
+ appendStringInfoString(buf, argtype);
+ pfree(argtype);
+ }
+ appendStringInfoChar(buf, ')');
+}
+
+/*
+ * format_procedure_args - converts proc OID to "(args)"
+ */
+char *
+format_procedure_args(Oid procedure_oid, bool force_qualify)
+{
+ StringInfoData buf;
+ HeapTuple proctup;
+ Form_pg_proc procform;
+
+ proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup failed for procedure %u", procedure_oid);
+ procform = (Form_pg_proc) GETSTRUCT(proctup);
+
+ initStringInfo(&buf);
+ format_procedure_args_internal(procform, &buf, force_qualify);
+
+ ReleaseSysCache(proctup);
+
+ return buf.data;
+}
+
/*
* format_procedure_extended - converts procedure OID to "pro_name(args)"
*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c8f5471b18..c489cac8dc 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -35,6 +35,7 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -341,6 +342,8 @@ static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static int decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
+static void pg_get_rule_whereclause(char *qualstr, Query *query,
+ StringInfo buf, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
bool attrsOnly, bool keysOnly,
@@ -358,7 +361,6 @@ static int print_function_arguments(StringInfo buf, HeapTuple proctup,
bool print_table_args, bool print_defaults);
static void print_function_rettype(StringInfo buf, HeapTuple proctup);
static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
-static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
Bitmapset *rels_used);
static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
@@ -555,6 +557,80 @@ pg_get_ruledef_ext(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P(string_to_text(res));
}
+/*
+ * Given a pair of Datum corresponding to a rule's pg_rewrite.ev_qual and
+ * ev_action columns, return their text representation; ev_qual as a single
+ * string in whereClause and ev_action as a List of strings (which might be
+ * NIL, signalling NOTHING) in actions.
+ */
+void
+pg_get_ruledef_detailed(Datum ev_qual, Datum ev_action,
+ char **whereClause, List **actions)
+{
+ int prettyFlags = 0;
+ char *qualstr = TextDatumGetCString(ev_qual);
+ char *actionstr = TextDatumGetCString(ev_action);
+ List *actionNodeList = (List *) stringToNode(actionstr);
+ StringInfoData buf;
+
+ *whereClause = NULL;
+ *actions = NIL;
+ initStringInfo(&buf);
+
+ if (strcmp(qualstr, "<>") != 0)
+ {
+ Query *query = (Query *) linitial(actionNodeList);
+
+ pg_get_rule_whereclause(qualstr, query, &buf, 0);
+ *whereClause = pstrdup(buf.data);
+ }
+
+ if (list_length(actionNodeList) > 0)
+ {
+ ListCell *cell;
+
+ foreach(cell, actionNodeList)
+ {
+ Query *query = (Query *) lfirst(cell);
+
+ resetStringInfo(&buf);
+ get_query_def(query, &buf, NIL, NULL, true,
+ prettyFlags, WRAP_COLUMN_DEFAULT, 0);
+ *actions = lappend(*actions, pstrdup(buf.data));
+ }
+ }
+}
+
+/*
+ * To get the rewrite rule of a view when the CREATE VIEW command execution is
+ * still in progress: we search the system cache RULERELNAME to get the rewrite
+ * rule of the view as opposed to querying pg_rewrite as in pg_get_viewdef_worker(),
+ * which will return empty result.
+ */
+char *
+pg_get_viewdef_string(Oid viewoid)
+{
+ StringInfoData buf;
+ Relation pg_rewrite;
+ HeapTuple ruletup;
+ TupleDesc rulettc;
+
+ initStringInfo(&buf);
+ pg_rewrite = table_open(RewriteRelationId, AccessShareLock);
+
+ ruletup = SearchSysCache2(RULERELNAME,
+ ObjectIdGetDatum(viewoid),
+ PointerGetDatum(ViewSelectRuleName));
+ if (!HeapTupleIsValid(ruletup))
+ elog(ERROR, "cache lookup failed for rewrite rule for view with OID %u", viewoid);
+
+ rulettc = pg_rewrite->rd_att;
+ make_viewdef(&buf, ruletup, rulettc, 0, WRAP_COLUMN_DEFAULT);
+ ReleaseSysCache(ruletup);
+ table_close(pg_rewrite, AccessShareLock);
+
+ return buf.data;
+}
static char *
pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
@@ -1025,65 +1101,12 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
if (!isnull)
{
Node *qual;
- char relkind;
- deparse_context context;
- deparse_namespace dpns;
- RangeTblEntry *oldrte;
- RangeTblEntry *newrte;
-
- appendStringInfoString(&buf, "WHEN (");
+ char *qualstr;
qual = stringToNode(TextDatumGetCString(value));
+ qualstr = pg_get_trigger_whenclause(trigrec, qual, pretty);
- relkind = get_rel_relkind(trigrec->tgrelid);
-
- /* Build minimal OLD and NEW RTEs for the rel */
- oldrte = makeNode(RangeTblEntry);
- oldrte->rtekind = RTE_RELATION;
- oldrte->relid = trigrec->tgrelid;
- oldrte->relkind = relkind;
- oldrte->rellockmode = AccessShareLock;
- oldrte->alias = makeAlias("old", NIL);
- oldrte->eref = oldrte->alias;
- oldrte->lateral = false;
- oldrte->inh = false;
- oldrte->inFromCl = true;
-
- newrte = makeNode(RangeTblEntry);
- newrte->rtekind = RTE_RELATION;
- newrte->relid = trigrec->tgrelid;
- newrte->relkind = relkind;
- newrte->rellockmode = AccessShareLock;
- newrte->alias = makeAlias("new", NIL);
- newrte->eref = newrte->alias;
- newrte->lateral = false;
- newrte->inh = false;
- newrte->inFromCl = true;
-
- /* Build two-element rtable */
- memset(&dpns, 0, sizeof(dpns));
- dpns.rtable = list_make2(oldrte, newrte);
- dpns.subplans = NIL;
- dpns.ctes = NIL;
- dpns.appendrels = NULL;
- set_rtable_names(&dpns, NIL, NULL);
- set_simple_column_names(&dpns);
-
- /* Set up context with one-deep namespace stack */
- context.buf = &buf;
- context.namespaces = list_make1(&dpns);
- context.windowClause = NIL;
- context.windowTList = NIL;
- context.varprefix = true;
- context.prettyFlags = GET_PRETTY_FLAGS(pretty);
- context.wrapColumn = WRAP_COLUMN_DEFAULT;
- context.indentLevel = PRETTYINDENT_STD;
- context.special_exprkind = EXPR_KIND_NONE;
- context.appendparents = NULL;
-
- get_rule_expr(qual, &context, false);
-
- appendStringInfoString(&buf, ") ");
+ appendStringInfo(&buf, "WHEN (%s) ", qualstr);
}
appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
@@ -1124,6 +1147,74 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
return buf.data;
}
+/*
+ * Pass back the TriggerWhen clause of a trigger given the pg_trigger record and
+ * the expression tree (in nodeToString() representation) from pg_trigger.tgqual
+ * for the trigger's WHEN condition.
+ */
+char *
+pg_get_trigger_whenclause(Form_pg_trigger trigrec, Node *whenClause, bool pretty)
+{
+ StringInfoData buf;
+ char relkind;
+ deparse_context context;
+ deparse_namespace dpns;
+ RangeTblEntry *oldrte;
+ RangeTblEntry *newrte;
+
+ initStringInfo(&buf);
+
+ relkind = get_rel_relkind(trigrec->tgrelid);
+
+ /* Build minimal OLD and NEW RTEs for the rel */
+ oldrte = makeNode(RangeTblEntry);
+ oldrte->rtekind = RTE_RELATION;
+ oldrte->relid = trigrec->tgrelid;
+ oldrte->relkind = relkind;
+ oldrte->rellockmode = AccessShareLock;
+ oldrte->alias = makeAlias("old", NIL);
+ oldrte->eref = oldrte->alias;
+ oldrte->lateral = false;
+ oldrte->inh = false;
+ oldrte->inFromCl = true;
+
+ newrte = makeNode(RangeTblEntry);
+ newrte->rtekind = RTE_RELATION;
+ newrte->relid = trigrec->tgrelid;
+ newrte->relkind = relkind;
+ newrte->rellockmode = AccessShareLock;
+ newrte->alias = makeAlias("new", NIL);
+ newrte->eref = newrte->alias;
+ newrte->lateral = false;
+ newrte->inh = false;
+ newrte->inFromCl = true;
+
+ /* Build two-element rtable */
+ memset(&dpns, 0, sizeof(dpns));
+ dpns.rtable = list_make2(oldrte, newrte);
+ dpns.subplans = NIL;
+ dpns.ctes = NIL;
+ dpns.appendrels = NULL;
+ set_rtable_names(&dpns, NIL, NULL);
+ set_simple_column_names(&dpns);
+
+ /* Set up context with one-deep namespace stack */
+ context.buf = &buf;
+ context.namespaces = list_make1(&dpns);
+ context.windowClause = NIL;
+ context.windowTList = NIL;
+ context.varprefix = true;
+ context.prettyFlags = GET_PRETTY_FLAGS(pretty);
+ context.wrapColumn = WRAP_COLUMN_DEFAULT;
+ context.indentLevel = PRETTYINDENT_STD;
+ context.special_exprkind = EXPR_KIND_NONE;
+ context.appendparents = NULL;
+
+ get_rule_expr(whenClause, &context, false);
+
+ return buf.data;
+}
+
/* ----------
* pg_get_indexdef - Get the definition of an index
*
@@ -3486,7 +3577,12 @@ pg_get_function_arg_default(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P(string_to_text(str));
}
-static void
+/*
+ * Produce the formatted SQL body (not the whole function definition)
+ * of a function given the pg_proc tuple. Save the formatted SQL in the
+ * given StringInfo.
+ */
+void
print_function_sqlbody(StringInfo buf, HeapTuple proctup)
{
int numargs;
@@ -5231,48 +5327,18 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
/* If the rule has an event qualification, add it */
if (strcmp(ev_qual, "<>") != 0)
{
- Node *qual;
- Query *query;
- deparse_context context;
- deparse_namespace dpns;
-
- if (prettyFlags & PRETTYFLAG_INDENT)
- appendStringInfoString(buf, "\n ");
- appendStringInfoString(buf, " WHERE ");
-
- qual = stringToNode(ev_qual);
-
/*
* We need to make a context for recognizing any Vars in the qual
* (which can only be references to OLD and NEW). Use the rtable of
* the first query in the action list for this purpose.
*/
- query = (Query *) linitial(actions);
-
- /*
- * If the action is INSERT...SELECT, OLD/NEW have been pushed down
- * into the SELECT, and that's what we need to look at. (Ugly kluge
- * ... try to fix this when we redesign querytrees.)
- */
- query = getInsertSelectQuery(query, NULL);
-
- /* Must acquire locks right away; see notes in get_query_def() */
- AcquireRewriteLocks(query, false, false);
-
- context.buf = buf;
- context.namespaces = list_make1(&dpns);
- context.windowClause = NIL;
- context.windowTList = NIL;
- context.varprefix = (list_length(query->rtable) != 1);
- context.prettyFlags = prettyFlags;
- context.wrapColumn = WRAP_COLUMN_DEFAULT;
- context.indentLevel = PRETTYINDENT_STD;
- context.special_exprkind = EXPR_KIND_NONE;
- context.appendparents = NULL;
+ Query *query = (Query *) linitial(actions);
- set_deparse_for_query(&dpns, query, NIL);
+ if (prettyFlags & PRETTYFLAG_INDENT)
+ appendStringInfoString(buf, "\n ");
+ appendStringInfoString(buf, " WHERE ");
- get_rule_expr(qual, &context, false);
+ pg_get_rule_whereclause(ev_qual, query, buf, prettyFlags);
}
appendStringInfoString(buf, " DO ");
@@ -5313,6 +5379,47 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
table_close(ev_relation, AccessShareLock);
}
+/*
+ * Given a string corresponding to a rule's pg_rewrite.ev_qual and a query
+ * parsetree, append ev_qual's text representation into the output buf.
+ *
+ * Tries to pretty up the output according to prettyFlags.
+ */
+static void
+pg_get_rule_whereclause(char *qualstr, Query *query, StringInfo buf,
+ int prettyFlags)
+{
+ Node *qual;
+ deparse_context context;
+ deparse_namespace dpns;
+
+ qual = stringToNode(qualstr);
+
+ /*
+ * If the action is INSERT...SELECT, OLD/NEW have been pushed down
+ * into the SELECT, and that's what we need to look at. (Ugly kluge
+ * ... try to fix this when we redesign querytrees.)
+ */
+ query = getInsertSelectQuery(query, NULL);
+
+ /* Must acquire locks right away; see notes in get_query_def() */
+ AcquireRewriteLocks(query, false, false);
+
+ context.buf = buf;
+ context.namespaces = list_make1(&dpns);
+ context.windowClause = NIL;
+ context.windowTList = NIL;
+ context.varprefix = (list_length(query->rtable) != 1);
+ context.prettyFlags = prettyFlags;
+ context.wrapColumn = WRAP_COLUMN_DEFAULT;
+ context.indentLevel = PRETTYINDENT_STD;
+ context.special_exprkind = EXPR_KIND_NONE;
+ context.appendparents = NULL;
+
+ set_deparse_for_query(&dpns, query, NIL);
+
+ get_rule_expr(qual, &context, false);
+}
/* ----------
* make_viewdef - reconstruct the SELECT part of a
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 12259c9dd8..ce5f9b4112 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5722,6 +5722,7 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
pubdesc->pubactions.pubddl_table |= pubform->pubddl_table;
pubdesc->pubactions.pubddl_index |= pubform->pubddl_index;
+ pubdesc->pubactions.pubddl_all |= pubform->pubddl_all;
/*
* Check if all columns referenced in the filter expression are part
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fdcf27dd1e..1623abf145 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4067,6 +4067,7 @@ getPublications(Archive *fout, int *numPublications)
int i_pubtruncate;
int i_pubddl_table;
int i_pubddl_index;
+ int i_pubddl_all;
int i_pubviaroot;
int i,
ntups;
@@ -4086,25 +4087,25 @@ getPublications(Archive *fout, int *numPublications)
appendPQExpBufferStr(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
- "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubddl_table, p.pubddl_index, p.pubviaroot "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubddl_table, p.pubddl_index, p.pubddl_all, p.pubviaroot "
"FROM pg_publication p");
else if (fout->remoteVersion >= 130000)
appendPQExpBufferStr(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
- "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false as p.pubddl_table, false as p.pubddl_index, p.pubviaroot "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false as p.pubddl_table, false as p.pubddl_index, false as p.pubddl_all, p.pubviaroot "
"FROM pg_publication p");
else if (fout->remoteVersion >= 110000)
appendPQExpBufferStr(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
- "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false as p.pubddl_table, false as p.pubddl_index, false AS pubviaroot "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false as p.pubddl_table, false as p.pubddl_index, false as p.pubddl_all, false AS pubviaroot "
"FROM pg_publication p");
else
appendPQExpBufferStr(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
- "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false as p.pubddl_table, false as p.pubddl_index, false AS pubviaroot "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false as p.pubddl_table, false as p.pubddl_index, false as p.pubddl_all, false AS pubviaroot "
"FROM pg_publication p");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -4122,6 +4123,7 @@ getPublications(Archive *fout, int *numPublications)
i_pubtruncate = PQfnumber(res, "pubtruncate");
i_pubddl_table = PQfnumber(res, "pubddl_table");
i_pubddl_index = PQfnumber(res, "pubddl_index");
+ i_pubddl_all = PQfnumber(res, "pubddl_all");
i_pubviaroot = PQfnumber(res, "pubviaroot");
pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
@@ -4149,6 +4151,8 @@ getPublications(Archive *fout, int *numPublications)
(strcmp(PQgetvalue(res, i, i_pubddl_table), "t") == 0);
pubinfo[i].pubddl_index =
(strcmp(PQgetvalue(res, i, i_pubddl_index), "t") == 0);
+ pubinfo[i].pubddl_all =
+ (strcmp(PQgetvalue(res, i, i_pubddl_all), "t") == 0);
pubinfo[i].pubviaroot =
(strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
@@ -4250,6 +4254,15 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
first = false;
}
+ if (pubinfo->pubddl_all)
+ {
+ if (!first)
+ appendPQExpBufferStr(query, ", ");
+
+ appendPQExpBufferStr(query, "all");
+ first = false;
+ }
+
appendPQExpBufferStr(query, "'");
}
@@ -8040,7 +8053,7 @@ getPublicationEventTriggers(Archive *fout, SimpleStringList *skipTriggers)
appendPQExpBufferStr(query,
"SELECT oid FROM pg_publication "
- "WHERE pubddl_table");
+ "WHERE pubddl_all OR pubddl_table");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index efd63477a8..706f206c8b 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -622,6 +622,7 @@ typedef struct _PublicationInfo
bool pubtruncate;
bool pubddl_table;
bool pubddl_index;
+ bool pubddl_all;
bool pubviaroot;
} PublicationInfo;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index df79c79e8d..568fe296b3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -6208,9 +6208,11 @@ listPublications(const char *pattern)
{
appendPQExpBuffer(&buf,
",\n pubddl_table AS \"%s\",\n"
- " pubddl_index AS \"%s\"\n",
+ " pubddl_index AS \"%s\",\n"
+ " pubddl_all AS \"%s\"\n",
gettext_noop("Table DDLs"),
- gettext_noop("Index DDLs"));
+ gettext_noop("Index DDLs"),
+ gettext_noop("All DDLs"));
}
appendPQExpBuffer(&buf,
",\n pubinsert AS \"%s\",\n"
@@ -6343,7 +6345,7 @@ describePublications(const char *pattern)
" puballtables");
if (has_pubddl)
appendPQExpBufferStr(&buf,
- ", pubddl_table, pubddl_index");
+ ", pubddl_table, pubddl_index, pubddl_all");
appendPQExpBufferStr(&buf,
", pubinsert, pubupdate, pubdelete");
@@ -6405,7 +6407,7 @@ describePublications(const char *pattern)
if (has_pubviaroot)
ncols++;
if (has_pubddl)
- ncols += 2;
+ ncols += 3;
initPQExpBuffer(&title);
printfPQExpBuffer(&title, _("Publication %s"), pubname);
@@ -6417,6 +6419,7 @@ describePublications(const char *pattern)
{
printTableAddHeader(&cont, gettext_noop("Table DDLs"), true, align);
printTableAddHeader(&cont, gettext_noop("Index DDLs"), true, align);
+ printTableAddHeader(&cont, gettext_noop("All DDLs"), true, align);
}
printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
@@ -6439,6 +6442,7 @@ describePublications(const char *pattern)
{
printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false);
+ printTableAddCell(&cont, PQgetvalue(res, i, 11), false, false);
}
if (!puballtables)
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index 467731c61a..35b3ea25e5 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -69,6 +69,9 @@ CATALOG(pg_publication,6104,PublicationRelationId)
/* true if index ddls are published */
bool pubddl_index;
+
+ /* true if all supported ddls are published */
+ bool pubddl_all;
} FormData_pg_publication;
/* ----------------
@@ -89,6 +92,7 @@ typedef struct PublicationActions
bool pubtruncate;
bool pubddl_table;
bool pubddl_index;
+ bool pubddl_all;
} PublicationActions;
typedef struct PublicationDesc
diff --git a/src/include/commands/collationcmds.h b/src/include/commands/collationcmds.h
index b76c7b3dc3..53c4a1a8c2 100644
--- a/src/include/commands/collationcmds.h
+++ b/src/include/commands/collationcmds.h
@@ -18,7 +18,8 @@
#include "catalog/objectaddress.h"
#include "parser/parse_node.h"
-extern ObjectAddress DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists);
+extern ObjectAddress DefineCollation(ParseState *pstate, List *names, List *parameters,
+ bool if_not_exists, ObjectAddress *from_collid);
extern void IsThereCollationInNamespace(const char *collname, Oid nspOid);
extern ObjectAddress AlterCollation(AlterCollationStmt *stmt);
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index cba4e72455..339553d456 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -132,5 +132,7 @@ extern void EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt,
extern void EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt,
Oid cfgId, Oid *dictIds, int ndicts);
extern void EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt);
+extern void EventTriggerCollectSecLabel(ObjectAddress address, char *provider,
+ SecLabelStmt *stmt);
#endif /* EVENT_TRIGGER_H */
diff --git a/src/include/tcop/cmdtag.h b/src/include/tcop/cmdtag.h
index 076c27e642..0259a4b4f6 100644
--- a/src/include/tcop/cmdtag.h
+++ b/src/include/tcop/cmdtag.h
@@ -53,7 +53,7 @@ CopyQueryCompletion(QueryCompletion *dst, const QueryCompletion *src)
extern void InitializeQueryCompletion(QueryCompletion *qc);
extern const char *GetCommandTagName(CommandTag commandTag);
extern const char *GetCommandTagNameAndLen(CommandTag commandTag, Size *len);
-extern CommandTag *GetCommandTagsForDDLRepl(int *ncommands);
+extern List *GetCommandTagsForDDLRepl(void);
extern bool command_tag_display_rowcount(CommandTag commandTag);
extern bool command_tag_event_trigger_ok(CommandTag commandTag);
extern bool command_tag_table_rewrite_ok(CommandTag commandTag);
diff --git a/src/include/tcop/ddl_deparse.h b/src/include/tcop/ddl_deparse.h
index 2b7754bfac..581a470c09 100644
--- a/src/include/tcop/ddl_deparse.h
+++ b/src/include/tcop/ddl_deparse.h
@@ -12,11 +12,13 @@
#ifndef DDL_DEPARSE_H
#define DDL_DEPARSE_H
+#include "commands/event_trigger.h"
#include "tcop/deparse_utility.h"
extern char *deparse_utility_command(CollectedCommand *cmd, bool include_owner, bool verbose_mode);
extern char *deparse_ddl_json_to_string(char *jsonb, char** owner);
extern char *deparse_drop_command(const char *objidentity, const char *objecttype,
DropBehavior behavior);
+extern char * deparse_AlterPublicationDropStmt(SQLDropObject *obj);
#endif /* DDL_DEPARSE_H */
diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h
index 87a761bb3e..cce3c07fc0 100644
--- a/src/include/tcop/deparse_utility.h
+++ b/src/include/tcop/deparse_utility.h
@@ -30,6 +30,7 @@ typedef enum CollectedCommandType
SCT_AlterDefaultPrivileges,
SCT_CreateOpClass,
SCT_AlterTSConfig,
+ SCT_SecurityLabel,
SCT_CreateTableAs
} CollectedCommandType;
@@ -105,6 +106,13 @@ typedef struct CollectedCommand
ObjectType objtype;
} defprivs;
+ /* SECURITY LABEL */
+ struct
+ {
+ ObjectAddress address;
+ char *provider;
+ } seclabel;
+
/* CREATE TABLE AS */
struct
{
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index f8e1238fa2..f05578d322 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -209,6 +209,8 @@ extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how);
extern int aclmembers(const Acl *acl, Oid **roleids);
+extern const char *privilege_to_string(AclMode privilege);
+
extern bool has_privs_of_role(Oid member, Oid role);
extern bool member_can_set_role(Oid member, Oid role);
extern void check_can_set_role(Oid member, Oid role);
diff --git a/src/include/utils/aclchk_internal.h b/src/include/utils/aclchk_internal.h
index 55af624fb3..946545f53f 100644
--- a/src/include/utils/aclchk_internal.h
+++ b/src/include/utils/aclchk_internal.h
@@ -39,6 +39,7 @@ typedef struct
List *grantees;
bool grant_option;
DropBehavior behavior;
+ Oid grantor_uid;
} InternalGrant;
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index c90c32bff1..48b8bfd79a 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -123,6 +123,7 @@ extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS);
#define FORMAT_TYPE_FORCE_QUALIFY 0x04 /* force qualification of type */
#define FORMAT_TYPE_INVALID_AS_NULL 0x08 /* NULL if undefined */
extern char *format_type_extended(Oid type_oid, int32 typemod, bits16 flags);
+extern char *format_procedure_args(Oid procedure_oid, bool force_qualify);
extern char *format_type_be(Oid type_oid);
extern char *format_type_be_qualified(Oid type_oid);
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9adc589173..b6e20e7ee4 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -13,9 +13,12 @@
#ifndef RULEUTILS_H
#define RULEUTILS_H
+#include "access/htup.h"
+#include "catalog/pg_trigger.h"
#include "nodes/nodes.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
+#include "parser/parse_node.h"
struct Plan; /* avoid including plannodes.h here */
struct PlannedStmt;
@@ -23,7 +26,10 @@ struct PlannedStmt;
extern char *pg_get_indexdef_string(Oid indexrelid);
extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty);
+extern char *pg_get_trigger_whenclause(Form_pg_trigger trigrec,
+ Node *whenClause, bool pretty);
extern char *pg_get_querydef(Query *query, bool pretty);
+extern char *pg_get_viewdef_string(Oid viewoid);
extern char *pg_get_partkeydef_columns(Oid relid, bool pretty);
extern char *pg_get_partkeydef_string(Oid relid);
@@ -31,6 +37,9 @@ extern char *pg_get_partconstrdef_string(Oid partitionId, char *aliasname);
extern char *pg_get_constraintdef_command(Oid constraintId);
extern char *pg_get_constraintdef_string(Oid constraintId);
+extern void pg_get_ruledef_detailed(Datum ev_qual, Datum ev_action,
+ char **whereClause, List **actions);
+
extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid);
@@ -52,5 +61,6 @@ extern void get_opclass_name(Oid opclass, Oid actual_datatype,
extern char *flatten_reloptions(Oid relid);
extern char *pg_get_statisticsobjdef_string(Oid statextid);
+extern void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 3e6d2791c8..4474c3b730 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -6223,9 +6223,9 @@ List of schemas
(0 rows)
\dRp "no.such.publication"
- List of publications
- Name | Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
-------+-------+------------+------------+------------+---------+---------+---------+-----------+----------
+ List of publications
+ Name | Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+------+-------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
(0 rows)
\dRs "no.such.subscription"
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index ebcff41c0c..c54ce53e7b 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -30,20 +30,20 @@ ERROR: conflicting or redundant options
LINE 1: ...ub_xxx WITH (publish_via_partition_root = 'true', publish_vi...
^
\dRp
- List of publications
- Name | Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------+--------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f | f | f | t | f | f | f | f
- testpub_default | regress_publication_user | f | f | f | f | t | f | f | f
+ List of publications
+ Name | Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ testpib_ins_trunct | regress_publication_user | f | f | f | f | t | f | f | f | f
+ testpub_default | regress_publication_user | f | f | f | f | f | t | f | f | f
(2 rows)
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
\dRp
- List of publications
- Name | Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------+--------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f | f | f | t | f | f | f | f
- testpub_default | regress_publication_user | f | f | f | t | t | t | f | f
+ List of publications
+ Name | Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ testpib_ins_trunct | regress_publication_user | f | f | f | f | t | f | f | f | f
+ testpub_default | regress_publication_user | f | f | f | f | t | t | t | f | f
(2 rows)
--- adding tables
@@ -87,10 +87,10 @@ RESET client_min_messages;
-- should be able to add schema to 'FOR TABLE' publication
ALTER PUBLICATION testpub_fortable ADD TABLES IN SCHEMA pub_test;
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"public.testpub_tbl1"
Tables from schemas:
@@ -99,20 +99,20 @@ Tables from schemas:
-- should be able to drop schema from 'FOR TABLE' publication
ALTER PUBLICATION testpub_fortable DROP TABLES IN SCHEMA pub_test;
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"public.testpub_tbl1"
-- should be able to set schema to 'FOR TABLE' publication
ALTER PUBLICATION testpub_fortable SET TABLES IN SCHEMA pub_test;
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test"
@@ -123,10 +123,10 @@ CREATE PUBLICATION testpub_forschema FOR TABLES IN SCHEMA pub_test;
CREATE PUBLICATION testpub_for_tbl_schema FOR TABLES IN SCHEMA pub_test, TABLE pub_test.testpub_nopk;
RESET client_min_messages;
\dRp+ testpub_for_tbl_schema
- Publication testpub_for_tbl_schema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_for_tbl_schema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"pub_test.testpub_nopk"
Tables from schemas:
@@ -135,10 +135,10 @@ Tables from schemas:
-- should be able to add a table of the same schema to the schema publication
ALTER PUBLICATION testpub_forschema ADD TABLE pub_test.testpub_nopk;
\dRp+ testpub_forschema
- Publication testpub_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"pub_test.testpub_nopk"
Tables from schemas:
@@ -147,10 +147,10 @@ Tables from schemas:
-- should be able to drop the table
ALTER PUBLICATION testpub_forschema DROP TABLE pub_test.testpub_nopk;
\dRp+ testpub_forschema
- Publication testpub_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test"
@@ -161,10 +161,10 @@ ERROR: relation "testpub_nopk" is not part of the publication
-- should be able to set table to schema publication
ALTER PUBLICATION testpub_forschema SET TABLE pub_test.testpub_nopk;
\dRp+ testpub_forschema
- Publication testpub_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"pub_test.testpub_nopk"
@@ -186,10 +186,10 @@ Publications:
"testpub_foralltables"
\dRp+ testpub_foralltables
- Publication testpub_foralltables
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | t | f | f | t | t | f | f | f
+ Publication testpub_foralltables
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | t | f | f | f | t | t | f | f | f
(1 row)
DROP TABLE testpub_tbl2;
@@ -201,19 +201,19 @@ CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3;
CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3;
RESET client_min_messages;
\dRp+ testpub3
- Publication testpub3
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub3
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"public.testpub_tbl3"
"public.testpub_tbl3a"
\dRp+ testpub4
- Publication testpub4
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub4
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"public.testpub_tbl3"
@@ -234,10 +234,10 @@ UPDATE testpub_parted1 SET a = 1;
-- only parent is listed as being in publication, not the partition
ALTER PUBLICATION testpub_forparted ADD TABLE testpub_parted;
\dRp+ testpub_forparted
- Publication testpub_forparted
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_forparted
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"public.testpub_parted"
@@ -252,10 +252,10 @@ ALTER TABLE testpub_parted DETACH PARTITION testpub_parted1;
UPDATE testpub_parted1 SET a = 1;
ALTER PUBLICATION testpub_forparted SET (publish_via_partition_root = true);
\dRp+ testpub_forparted
- Publication testpub_forparted
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | t
+ Publication testpub_forparted
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | t
Tables:
"public.testpub_parted"
@@ -284,10 +284,10 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub5 FOR TABLE testpub_rf_tbl1, testpub_rf_tbl2 WHERE (c <> 'test' AND d < 5) WITH (publish = 'insert');
RESET client_min_messages;
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -300,10 +300,10 @@ Tables:
ALTER PUBLICATION testpub5 ADD TABLE testpub_rf_tbl3 WHERE (e > 1000 AND e < 2000);
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -319,10 +319,10 @@ Publications:
ALTER PUBLICATION testpub5 DROP TABLE testpub_rf_tbl2;
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl3" WHERE ((e > 1000) AND (e < 2000))
@@ -330,10 +330,10 @@ Tables:
-- remove testpub_rf_tbl1 and add testpub_rf_tbl3 again (another WHERE expression)
ALTER PUBLICATION testpub5 SET TABLE testpub_rf_tbl3 WHERE (e > 300 AND e < 500);
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | f | f | f | f
Tables:
"public.testpub_rf_tbl3" WHERE ((e > 300) AND (e < 500))
@@ -366,10 +366,10 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_syntax1 FOR TABLE testpub_rf_tbl1, ONLY testpub_rf_tbl3 WHERE (e < 999) WITH (publish = 'insert');
RESET client_min_messages;
\dRp+ testpub_syntax1
- Publication testpub_syntax1
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | f | f | f | f
+ Publication testpub_syntax1
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl3" WHERE (e < 999)
@@ -379,10 +379,10 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_syntax2 FOR TABLE testpub_rf_tbl1, testpub_rf_schema1.testpub_rf_tbl5 WHERE (h < 999) WITH (publish = 'insert');
RESET client_min_messages;
\dRp+ testpub_syntax2
- Publication testpub_syntax2
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | f | f | f | f
+ Publication testpub_syntax2
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"testpub_rf_schema1.testpub_rf_tbl5" WHERE (h < 999)
@@ -497,10 +497,10 @@ CREATE PUBLICATION testpub6 FOR TABLES IN SCHEMA testpub_rf_schema2;
ALTER PUBLICATION testpub6 SET TABLES IN SCHEMA testpub_rf_schema2, TABLE testpub_rf_schema2.testpub_rf_tbl6 WHERE (i < 99);
RESET client_min_messages;
\dRp+ testpub6
- Publication testpub6
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub6
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"testpub_rf_schema2.testpub_rf_tbl6" WHERE (i < 99)
Tables from schemas:
@@ -714,10 +714,10 @@ CREATE PUBLICATION testpub_table_ins WITH (publish = 'insert, truncate');
RESET client_min_messages;
ALTER PUBLICATION testpub_table_ins ADD TABLE testpub_tbl5 (a); -- ok
\dRp+ testpub_table_ins
- Publication testpub_table_ins
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | f | f | t | f
+ Publication testpub_table_ins
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | f | f | t | f
Tables:
"public.testpub_tbl5" (a)
@@ -891,10 +891,10 @@ CREATE TABLE testpub_tbl_both_filters (a int, b int, c int, PRIMARY KEY (a,c));
ALTER TABLE testpub_tbl_both_filters REPLICA IDENTITY USING INDEX testpub_tbl_both_filters_pkey;
ALTER PUBLICATION testpub_both_filters ADD TABLE testpub_tbl_both_filters (a,c) WHERE (c != 1);
\dRp+ testpub_both_filters
- Publication testpub_both_filters
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_both_filters
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"public.testpub_tbl_both_filters" (a, c) WHERE (c <> 1)
@@ -1099,10 +1099,10 @@ ERROR: relation "testpub_tbl1" is already member of publication "testpub_fortbl
CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1;
ERROR: publication "testpub_fortbl" already exists
\dRp+ testpub_fortbl
- Publication testpub_fortbl
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_fortbl
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"pub_test.testpub_nopk"
"public.testpub_tbl1"
@@ -1140,10 +1140,10 @@ Publications:
"testpub_fortbl"
\dRp+ testpub_default
- Publication testpub_default
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | f | f
+ Publication testpub_default
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | f | f
Tables:
"pub_test.testpub_nopk"
"public.testpub_tbl1"
@@ -1221,10 +1221,10 @@ REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
DROP TABLE testpub_parted;
DROP TABLE testpub_tbl1;
\dRp+ testpub_default
- Publication testpub_default
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | f | f
+ Publication testpub_default
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | f | f
(1 row)
-- fail - must be owner of publication
@@ -1234,20 +1234,20 @@ ERROR: must be owner of publication testpub_default
RESET ROLE;
ALTER PUBLICATION testpub_default RENAME TO testpub_foo;
\dRp testpub_foo
- List of publications
- Name | Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
--------------+--------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- testpub_foo | regress_publication_user | f | f | f | t | t | t | f | f
+ List of publications
+ Name | Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+-------------+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ testpub_foo | regress_publication_user | f | f | f | f | t | t | t | f | f
(1 row)
-- rename back to keep the rest simple
ALTER PUBLICATION testpub_foo RENAME TO testpub_default;
ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2;
\dRp testpub_default
- List of publications
- Name | Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
------------------+---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- testpub_default | regress_publication_user2 | f | f | f | t | t | t | f | f
+ List of publications
+ Name | Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+-----------------+---------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ testpub_default | regress_publication_user2 | f | f | f | f | t | t | t | f | f
(1 row)
-- adding schemas and tables
@@ -1263,19 +1263,19 @@ CREATE TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA"(id int);
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub1_forschema FOR TABLES IN SCHEMA pub_test1;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
CREATE PUBLICATION testpub2_forschema FOR TABLES IN SCHEMA pub_test1, pub_test2, pub_test3;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1289,44 +1289,44 @@ CREATE PUBLICATION testpub6_forschema FOR TABLES IN SCHEMA "CURRENT_SCHEMA", CUR
CREATE PUBLICATION testpub_fortable FOR TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA";
RESET client_min_messages;
\dRp+ testpub3_forschema
- Publication testpub3_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub3_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"public"
\dRp+ testpub4_forschema
- Publication testpub4_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub4_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"CURRENT_SCHEMA"
\dRp+ testpub5_forschema
- Publication testpub5_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub5_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"CURRENT_SCHEMA"
"public"
\dRp+ testpub6_forschema
- Publication testpub6_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub6_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"CURRENT_SCHEMA"
"public"
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"CURRENT_SCHEMA.CURRENT_SCHEMA"
@@ -1360,10 +1360,10 @@ ERROR: schema "testpub_view" does not exist
-- dropping the schema should reflect the change in publication
DROP SCHEMA pub_test3;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1371,20 +1371,20 @@ Tables from schemas:
-- renaming the schema should reflect the change in publication
ALTER SCHEMA pub_test1 RENAME to pub_test1_renamed;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1_renamed"
"pub_test2"
ALTER SCHEMA pub_test1_renamed RENAME to pub_test1;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1392,10 +1392,10 @@ Tables from schemas:
-- alter publication add schema
ALTER PUBLICATION testpub1_forschema ADD TABLES IN SCHEMA pub_test2;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1404,10 +1404,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema ADD TABLES IN SCHEMA non_existent_schema;
ERROR: schema "non_existent_schema" does not exist
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1416,10 +1416,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema ADD TABLES IN SCHEMA pub_test1;
ERROR: schema "pub_test1" is already member of publication "testpub1_forschema"
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1427,10 +1427,10 @@ Tables from schemas:
-- alter publication drop schema
ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA pub_test2;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
@@ -1438,10 +1438,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA pub_test2;
ERROR: tables from schema "pub_test2" are not part of the publication
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
@@ -1449,29 +1449,29 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA non_existent_schema;
ERROR: schema "non_existent_schema" does not exist
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
-- drop all schemas
ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA pub_test1;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
(1 row)
-- alter publication set multiple schema
ALTER PUBLICATION testpub1_forschema SET TABLES IN SCHEMA pub_test1, pub_test2;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1480,10 +1480,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema SET TABLES IN SCHEMA non_existent_schema;
ERROR: schema "non_existent_schema" does not exist
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1492,10 +1492,10 @@ Tables from schemas:
-- removing the duplicate schemas
ALTER PUBLICATION testpub1_forschema SET TABLES IN SCHEMA pub_test1, pub_test1;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
@@ -1574,18 +1574,18 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub3_forschema;
RESET client_min_messages;
\dRp+ testpub3_forschema
- Publication testpub3_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub3_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
(1 row)
ALTER PUBLICATION testpub3_forschema SET TABLES IN SCHEMA pub_test1;
\dRp+ testpub3_forschema
- Publication testpub3_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub3_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables from schemas:
"pub_test1"
@@ -1595,20 +1595,20 @@ CREATE PUBLICATION testpub_forschema_fortable FOR TABLES IN SCHEMA pub_test1, TA
CREATE PUBLICATION testpub_fortable_forschema FOR TABLE pub_test2.tbl1, TABLES IN SCHEMA pub_test1;
RESET client_min_messages;
\dRp+ testpub_forschema_fortable
- Publication testpub_forschema_fortable
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_forschema_fortable
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"pub_test2.tbl1"
Tables from schemas:
"pub_test1"
\dRp+ testpub_fortable_forschema
- Publication testpub_fortable_forschema
- Owner | All tables | Table DDLs | Index DDLs | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | f | f | t | t | t | t | f
+ Publication testpub_fortable_forschema
+ Owner | All tables | Table DDLs | Index DDLs | All DDLs | Inserts | Updates | Deletes | Truncates | Via root
+--------------------------+------------+------------+------------+----------+---------+---------+---------+-----------+----------
+ regress_publication_user | f | f | f | f | t | t | t | t | f
Tables:
"pub_test2.tbl1"
Tables from schemas:
diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out
index 9c52890f1d..c2bd4d3f62 100644
--- a/src/test/regress/expected/subscription.out
+++ b/src/test/regress/expected/subscription.out
@@ -115,18 +115,18 @@ CREATE SUBSCRIPTION regress_testsub4 CONNECTION 'dbname=regress_doesnotexist' PU
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
\dRs+ regress_testsub4
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
-------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub4 | regress_subscription_user | f | {testpub} | f | off | d | f | none | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub4 | regress_subscription_user | f | {testpub} | f | off | d | f | none | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub4 SET (origin = any);
\dRs+ regress_testsub4
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
-------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub4 | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub4 | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
DROP SUBSCRIPTION regress_testsub3;
@@ -144,10 +144,10 @@ ALTER SUBSCRIPTION regress_testsub CONNECTION 'foobar';
ERROR: invalid connection string syntax: missing "=" after "foobar" in connection info string
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET PUBLICATION testpub2, testpub3 WITH (refresh = false);
@@ -166,10 +166,10 @@ ERROR: unrecognized subscription parameter: "create_slot"
-- ok
ALTER SUBSCRIPTION regress_testsub SKIP (lsn = '0/12345');
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+------------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | off | d | f | any | f | off | dbname=regress_doesnotexist2 | 0/12345
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+------------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist2 | 0/12345
(1 row)
-- ok - with lsn = NONE
@@ -178,10 +178,10 @@ ALTER SUBSCRIPTION regress_testsub SKIP (lsn = NONE);
ALTER SUBSCRIPTION regress_testsub SKIP (lsn = '0/0');
ERROR: invalid WAL location (LSN): 0/0
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+------------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | off | d | f | any | f | off | dbname=regress_doesnotexist2 | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+------------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist2 | 0/0
(1 row)
BEGIN;
@@ -213,10 +213,10 @@ ALTER SUBSCRIPTION regress_testsub_foo SET (synchronous_commit = foobar);
ERROR: invalid value for parameter "synchronous_commit": "foobar"
HINT: Available values: local, remote_write, remote_apply, on, off.
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
----------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+------------------------------+----------
- regress_testsub_foo | regress_subscription_user | f | {testpub2,testpub3} | f | off | d | f | any | f | local | dbname=regress_doesnotexist2 | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+---------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+------------------------------+----------
+ regress_testsub_foo | regress_subscription_user | f | {testpub2,testpub3} | f | off | d | f | any | f | t | local | dbname=regress_doesnotexist2 | 0/0
(1 row)
-- rename back to keep the rest simple
@@ -245,19 +245,19 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | t | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | t | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET (binary = false);
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
DROP SUBSCRIPTION regress_testsub;
@@ -269,27 +269,27 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | on | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | on | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET (streaming = parallel);
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET (streaming = false);
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
-- fail - publication already exists
@@ -304,10 +304,10 @@ ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refr
ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refresh = false);
ERROR: publication "testpub1" is already in subscription "regress_testsub"
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-----------------------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub,testpub1,testpub2} | f | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-----------------------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub,testpub1,testpub2} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
-- fail - publication used more than once
@@ -322,10 +322,10 @@ ERROR: publication "testpub3" is not in subscription "regress_testsub"
-- ok - delete publications
ALTER SUBSCRIPTION regress_testsub DROP PUBLICATION testpub1, testpub2 WITH (refresh = false);
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
DROP SUBSCRIPTION regress_testsub;
@@ -361,10 +361,10 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | off | p | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | off | p | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
--fail - alter of two_phase option not supported.
@@ -373,10 +373,10 @@ ERROR: unrecognized subscription parameter: "two_phase"
-- but can alter streaming when two_phase enabled
ALTER SUBSCRIPTION regress_testsub SET (streaming = true);
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | on | p | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | on | p | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
@@ -386,10 +386,10 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | on | p | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | on | p | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
@@ -402,18 +402,18 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET (disable_on_error = true);
\dRs+
- List of subscriptions
- Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Synchronous commit | Conninfo | Skip LSN
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | t | any | f | off | dbname=regress_doesnotexist | 0/0
+ List of subscriptions
+ Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Run as Owner? | Match DDL owner | Synchronous commit | Conninfo | Skip LSN
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | t | any | f | t | off | dbname=regress_doesnotexist | 0/0
(1 row)
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
--
2.30.0.windows.2