Commit b488c580 authored by Alvaro Herrera's avatar Alvaro Herrera

Allow on-the-fly capture of DDL event details

This feature lets user code inspect and take action on DDL events.
Whenever a ddl_command_end event trigger is installed, DDL actions
executed are saved to a list which can be inspected during execution of
a function attached to ddl_command_end.

The set-returning function pg_event_trigger_ddl_commands can be used to
list actions so captured; it returns data about the type of command
executed, as well as the affected object.  This is sufficient for many
uses of this feature.  For the cases where it is not, we also provide a
"command" column of a new pseudo-type pg_ddl_command, which is a
pointer to a C structure that can be accessed by C code.  The struct
contains all the info necessary to completely inspect and even
reconstruct the executed command.

There is no actual deparse code here; that's expected to come later.
What we have is enough infrastructure that the deparsing can be done in
an external extension.  The intention is that we will add some deparsing
code in a later release, as an in-core extension.

A new test module is included.  It's probably insufficient as is, but it
should be sufficient as a starting point for a more complete and
future-proof approach.

Authors: Álvaro Herrera, with some help from Andres Freund, Ian Barwick,
Abhijit Menon-Sen.

Reviews by Andres Freund, Robert Haas, Amit Kapila, Michael Paquier,
Craig Ringer, David Steele.
Additional input from Chris Browne, Dimitri Fontaine, Stephen Frost,
Petr Jelínek, Tom Lane, Jim Nasby, Steven Singer, Pavel Stěhule.

Based on original work by Dimitri Fontaine, though I didn't use his
code.

Discussion:
  https://www.postgresql.org/message-id/m2txrsdzxa.fsf@2ndQuadrant.fr
  https://www.postgresql.org/message-id/20131108153322.GU5809@eldon.alvh.no-ip.org
  https://www.postgresql.org/message-id/20150215044814.GL3391@alvh.no-ip.org
parent fa264243
......@@ -29,7 +29,8 @@
occurs in the database in which it is defined. Currently, the only
supported events are
<literal>ddl_command_start</>,
<literal>ddl_command_end</>
<literal>ddl_command_end</>,
<literal>table_rewrite</>
and <literal>sql_drop</>.
Support for additional events may be added in future releases.
</para>
......@@ -52,7 +53,13 @@
<para>
The <literal>ddl_command_end</> event occurs just after the execution of
this same set of commands.
this same set of commands. To obtain more details on the <acronym>DDL</>
operations that took place, use the set-returning function
<literal>pg_event_trigger_ddl_commands()</> from the
<literal>ddl_command_end</> event trigger code (see
<xref linkend="functions-event-triggers">). Note that the trigger fires
after the actions have taken place (but before the transaction commits),
and thus the system catalogs can be read as already changed.
</para>
<para>
......
......@@ -18066,8 +18066,99 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
see <xref linkend="event-triggers">.
</para>
<sect2 id="pg-event-trigger-ddl-command-end-functions">
<title>Capturing Changes at Command End</title>
<indexterm>
<primary>pg_event_trigger_ddl_commands</primary>
</indexterm>
<para>
<function>pg_event_trigger_ddl_commands</> returns a list of
<acronym>DDL</acronym> commands executed by each user action,
when invoked in a function attached to a
<literal>ddl_command_end</> event trigger. If called in any other
context, an error is raised.
<function>pg_event_trigger_ddl_commands</> returns one row for each
base command executed; some commands that are a single SQL sentence
may return more than one row. This function returns the following
columns:
<informaltable>
<tgroup cols="3">
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>classid</literal></entry>
<entry><type>Oid</type></entry>
<entry>OID of catalog the object belongs in</entry>
</row>
<row>
<entry><literal>objid</literal></entry>
<entry><type>Oid</type></entry>
<entry>OID of the object in the catalog</entry>
</row>
<row>
<entry><literal>objsubid</literal></entry>
<entry><type>integer</type></entry>
<entry>Object sub-id (e.g. attribute number for columns)</entry>
</row>
<row>
<entry><literal>command_tag</literal></entry>
<entry><type>text</type></entry>
<entry>command tag</entry>
</row>
<row>
<entry><literal>object_type</literal></entry>
<entry><type>text</type></entry>
<entry>Type of the object</entry>
</row>
<row>
<entry><literal>schema_name</literal></entry>
<entry><type>text</type></entry>
<entry>
Name of the schema the object belongs in, if any; otherwise <literal>NULL</>.
No quoting is applied.
</entry>
</row>
<row>
<entry><literal>object_identity</literal></entry>
<entry><type>text</type></entry>
<entry>
Text rendering of the object identity, schema-qualified. Each and every
identifier present in the identity is quoted if necessary.
</entry>
</row>
<row>
<entry><literal>in_extension</literal></entry>
<entry><type>bool</type></entry>
<entry>whether the command is part of an extension script</entry>
</row>
<row>
<entry><literal>command</literal></entry>
<entry><type>pg_ddl_command</type></entry>
<entry>
A complete representation of the command, in internal format.
This cannot be output directly, but it can be passed to other
functions to obtain different pieces of information about the
command.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
</sect2>
<sect2 id="pg-event-trigger-sql-drop-functions">
<title>Processing objects dropped by a DDL command.</title>
<title>Processing Objects Dropped by a DDL Command</title>
<indexterm>
<primary>pg_event_trigger_dropped_objects</primary>
......
......@@ -48,6 +48,7 @@
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
......@@ -56,6 +57,7 @@
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/aclchk_internal.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
......@@ -64,32 +66,6 @@
#include "utils/tqual.h"
/*
* The information about one Grant/Revoke statement, in internal format: object
* and grantees names have been turned into Oids, the privilege list is an
* AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
* all_privs is true, 'privileges' will be internally set to the right kind of
* ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
* InternalGrant struct!)
*
* Note: 'all_privs' and 'privileges' represent object-level privileges only.
* There might also be column-level privilege specifications, which are
* represented in col_privs (this is a list of untransformed AccessPriv nodes).
* Column privileges are only valid for objtype ACL_OBJECT_RELATION.
*/
typedef struct
{
bool is_grant;
GrantObjectType objtype;
List *objects;
bool all_privs;
AclMode privileges;
List *col_privs;
List *grantees;
bool grant_option;
DropBehavior behavior;
} InternalGrant;
/*
* Internal format used by ALTER DEFAULT PRIVILEGES.
*/
......@@ -605,6 +581,15 @@ ExecGrantStmt_oids(InternalGrant *istmt)
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
}
/*
* Pass the info to event triggers about the just-executed GRANT. Note
* that we prefer to do it after actually executing it, because that gives
* the functions a chance to adjust the istmt with privileges actually
* granted.
*/
if (EventTriggerSupportsGrantObjectType(istmt->objtype))
EventTriggerCollectGrant(istmt);
}
/*
......
This diff is collapsed.
......@@ -25,6 +25,7 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/opfam_internal.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_namespace.h"
......@@ -35,6 +36,7 @@
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
......@@ -47,24 +49,12 @@
#include "utils/tqual.h"
/*
* We use lists of this struct type to keep track of both operators and
* procedures while building or adding to an opfamily.
*/
typedef struct
{
Oid object; /* operator or support proc's OID */
int number; /* strategy or support proc number */
Oid lefttype; /* lefttype */
Oid righttype; /* righttype */
Oid sortfamily; /* ordering operator's sort opfamily, or 0 */
} OpFamilyMember;
static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt,
Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber,
List *items);
static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt,
Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber,
List *items);
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
......@@ -675,6 +665,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
opclassoid, procedures, false);
/* let event triggers know what happened */
EventTriggerCollectCreateOpClass(stmt, opclassoid, operators, procedures);
/*
* Create dependencies for the opclass proper. Note: we do not create a
* dependency link to the AM, because we don't currently support DROP
......@@ -822,13 +815,11 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
* ADD and DROP cases need separate code from here on down.
*/
if (stmt->isDrop)
AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid,
maxOpNumber, maxProcNumber,
stmt->items);
AlterOpFamilyDrop(stmt, amoid, opfamilyoid,
maxOpNumber, maxProcNumber, stmt->items);
else
AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
maxOpNumber, maxProcNumber,
stmt->items);
AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
maxOpNumber, maxProcNumber, stmt->items);
return opfamilyoid;
}
......@@ -837,9 +828,8 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
* ADD part of ALTER OP FAMILY
*/
static void
AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber,
List *items)
AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber, List *items)
{
List *operators; /* OpFamilyMember list for operators */
List *procedures; /* OpFamilyMember list for support procs */
......@@ -958,19 +948,22 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
* Add tuples to pg_amop and pg_amproc tying in the operators and
* functions. Dependencies on them are inserted, too.
*/
storeOperators(opfamilyname, amoid, opfamilyoid,
storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
InvalidOid, operators, true);
storeProcedures(opfamilyname, amoid, opfamilyoid,
storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
InvalidOid, procedures, true);
/* make information available to event triggers */
EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
operators, procedures);
}
/*
* DROP part of ALTER OP FAMILY
*/
static void
AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber,
List *items)
AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber, List *items)
{
List *operators; /* OpFamilyMember list for operators */
List *procedures; /* OpFamilyMember list for support procs */
......@@ -1033,8 +1026,12 @@ AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
/*
* Remove tuples from pg_amop and pg_amproc.
*/
dropOperators(opfamilyname, amoid, opfamilyoid, operators);
dropProcedures(opfamilyname, amoid, opfamilyoid, procedures);
dropOperators(stmt->opfamilyname, amoid, opfamilyoid, operators);
dropProcedures(stmt->opfamilyname, amoid, opfamilyoid, procedures);
/* make information available to event triggers */
EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
operators, procedures);
}
......@@ -1673,7 +1670,7 @@ RemoveAmProcEntryById(Oid entryOid)
heap_close(rel, RowExclusiveLock);
}
static char *
char *
get_am_name(Oid amOid)
{
HeapTuple tup;
......
......@@ -25,6 +25,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/schemacmds.h"
#include "miscadmin.h"
#include "parser/parse_utilcmd.h"
......@@ -52,6 +53,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
Oid saved_uid;
int save_sec_context;
AclResult aclresult;
ObjectAddress address;
GetUserIdAndSecContext(&saved_uid, &save_sec_context);
......@@ -142,6 +144,16 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
/* XXX should we clear overridePath->useTemp? */
PushOverrideSearchPath(overridePath);
/*
* Report the new schema to possibly interested event triggers. Note we
* must do this here and not in ProcessUtilitySlow because otherwise the
* objects created below are reported before the schema, which would be
* wrong.
*/
ObjectAddressSet(address, NamespaceRelationId, namespaceId);
EventTriggerCollectSimpleCommand(address, InvalidObjectAddress,
(Node *) stmt);
/*
* Examine the list of commands embedded in the CREATE SCHEMA command, and
* reorganize them into a sequentially executable order with no forward
......
......@@ -2789,6 +2789,8 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
rel = relation_open(relid, lockmode);
EventTriggerAlterTableRelid(relid);
ATController(NULL, rel, cmds, recurse, lockmode);
}
......@@ -3672,8 +3674,10 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
break;
}
/* supress compiler warning until we have some use for the address */
(void) address;
/*
* Report the subcommand to interested event triggers.
*/
EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
/*
* Bump the command counter to ensure the next subcommand in the sequence
......@@ -9728,7 +9732,10 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
cmds = lappend(cmds, cmd);
EventTriggerAlterTableStart((Node *) stmt);
/* OID is set by AlterTableInternal */
AlterTableInternal(lfirst_oid(l), cmds, false);
EventTriggerAlterTableEnd();
}
return new_tablespaceoid;
......
......@@ -34,6 +34,7 @@
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
......@@ -1442,6 +1443,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
}
}
}
EventTriggerCollectAlterTSConfig(stmt, cfgId, dictIds, ndict);
}
/*
......@@ -1509,6 +1512,8 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
i++;
}
EventTriggerCollectAlterTSConfig(stmt, cfgId, NULL, 0);
}
......
......@@ -3972,6 +3972,7 @@ _copyAlterTSConfigurationStmt(const AlterTSConfigurationStmt *from)
{
AlterTSConfigurationStmt *newnode = makeNode(AlterTSConfigurationStmt);
COPY_SCALAR_FIELD(kind);
COPY_NODE_FIELD(cfgname);
COPY_NODE_FIELD(tokentype);
COPY_NODE_FIELD(dicts);
......
......@@ -2032,6 +2032,7 @@ static bool
_equalAlterTSConfigurationStmt(const AlterTSConfigurationStmt *a,
const AlterTSConfigurationStmt *b)
{
COMPARE_SCALAR_FIELD(kind);
COMPARE_NODE_FIELD(cfgname);
COMPARE_NODE_FIELD(tokentype);
COMPARE_NODE_FIELD(dicts);
......
......@@ -8998,6 +8998,7 @@ AlterTSConfigurationStmt:
ALTER TEXT_P SEARCH CONFIGURATION any_name ADD_P MAPPING FOR name_list any_with any_name_list
{
AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt);
n->kind = ALTER_TSCONFIG_ADD_MAPPING;
n->cfgname = $5;
n->tokentype = $9;
n->dicts = $11;
......@@ -9008,6 +9009,7 @@ AlterTSConfigurationStmt:
| ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list any_with any_name_list
{
AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt);
n->kind = ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN;
n->cfgname = $5;
n->tokentype = $9;
n->dicts = $11;
......@@ -9018,6 +9020,7 @@ AlterTSConfigurationStmt:
| ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING REPLACE any_name any_with any_name
{
AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt);
n->kind = ALTER_TSCONFIG_REPLACE_DICT;
n->cfgname = $5;
n->tokentype = NIL;
n->dicts = list_make2($9,$11);
......@@ -9028,6 +9031,7 @@ AlterTSConfigurationStmt:
| ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list REPLACE any_name any_with any_name
{
AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt);
n->kind = ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN;
n->cfgname = $5;
n->tokentype = $9;
n->dicts = list_make2($11,$13);
......@@ -9038,6 +9042,7 @@ AlterTSConfigurationStmt:
| ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING FOR name_list
{
AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt);
n->kind = ALTER_TSCONFIG_DROP_MAPPING;
n->cfgname = $5;
n->tokentype = $9;
n->missing_ok = false;
......@@ -9046,6 +9051,7 @@ AlterTSConfigurationStmt:
| ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING IF_P EXISTS FOR name_list
{
AlterTSConfigurationStmt *n = makeNode(AlterTSConfigurationStmt);
n->kind = ALTER_TSCONFIG_DROP_MAPPING;
n->cfgname = $5;
n->tokentype = $11;
n->missing_ok = true;
......
This diff is collapsed.
......@@ -96,6 +96,9 @@ format_type_be(Oid type_oid)
return format_type_internal(type_oid, -1, false, false, false);
}
/*
* This version returns a name which is always qualified.
*/
char *
format_type_be_qualified(Oid type_oid)
{
......
......@@ -522,11 +522,12 @@ pg_node_tree_in(PG_FUNCTION_ARGS)
*/
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type pg_node_tree")));
errmsg("cannot accept a value of type %s", "pg_node_tree")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* pg_node_tree_out - output routine for type PG_NODE_TREE.
*
......@@ -546,7 +547,7 @@ pg_node_tree_recv(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type pg_node_tree")));
errmsg("cannot accept a value of type %s", "pg_node_tree")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
......@@ -559,3 +560,63 @@ pg_node_tree_send(PG_FUNCTION_ARGS)
{
return textsend(fcinfo);
}
/*
* pg_ddl_command_in - input routine for type PG_DDL_COMMAND.
*
* Like pg_node_tree, pg_ddl_command isn't really a pseudotype; it's here for
* the same reasons as that one.
*/
Datum
pg_ddl_command_in(PG_FUNCTION_ARGS)
{
/*
* Disallow input of pg_ddl_command value.
*/
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type %s", "pg_ddl_command")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* pg_ddl_command_out - output routine for type PG_DDL_COMMAND.
*
* We don't have any good way to output this type directly, so punt.
*/
Datum
pg_ddl_command_out(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot output a value of type %s", "pg_ddl_command")));
PG_RETURN_VOID();
}
/*
* pg_ddl_command_recv - binary input routine for type PG_DDL_COMMAND.
*/
Datum
pg_ddl_command_recv(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type %s", "pg_ddl_command")));
PG_RETURN_VOID();
}
/*
* pg_ddl_command_send - binary output routine for type PG_DDL_COMMAND.
*/
Datum
pg_ddl_command_send(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot output a value of type %s", "pg_ddl_command")));
PG_RETURN_VOID();
}
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201505091
#define CATALOG_VERSION_NO 201505111
#endif
/*-------------------------------------------------------------------------
*
* opfam_internal.h
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/catalog/opfam_internal.h
*
*-------------------------------------------------------------------------
*/
#ifndef OPFAM_INTERNAL_H
#define OPFAM_INTERNAL_H
/*
* We use lists of this struct type to keep track of both operators and
* procedures while building or adding to an opfamily.
*/
typedef struct
{
Oid object; /* operator or support proc's OID */
int number; /* strategy or support proc number */
Oid lefttype; /* lefttype */
Oid righttype; /* righttype */
Oid sortfamily; /* ordering operator's sort opfamily, or 0 */
} OpFamilyMember;
#endif /* OPFAM_INTERNAL_H */
......@@ -233,6 +233,15 @@ DATA(insert OID = 84 ( boolne PGNSP PGUID 12 1 0 0 0 f f f t t f i 2 0 16
DATA(insert OID = 89 ( version PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 25 "" _null_ _null_ _null_ _null_ _null_ pgsql_version _null_ _null_ _null_ ));
DESCR("PostgreSQL version string");
DATA(insert OID = 86 ( pg_ddl_command_in PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 32 "2275" _null_ _null_ _null_ _null_ _null_ pg_ddl_command_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 87 ( pg_ddl_command_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "32" _null_ _null_ _null_ _null_ _null_ pg_ddl_command_out _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 88 ( pg_ddl_command_recv PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 32 "2281" _null_ _null_ _null_ _null_ _null_ pg_ddl_command_recv _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 90 ( pg_ddl_command_send PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "32" _null_ _null_ _null_ _null_ _null_ pg_ddl_command_send _null_ _null_ _null_ ));
DESCR("I/O");
/* OIDS 100 - 199 */
DATA(insert OID = 101 ( eqsel PGNSP PGUID 12 1 0 0 0 f f f f t f s 4 0 701 "2281 26 2281 23" _null_ _null_ _null_ _null_ _null_ eqsel _null_ _null_ _null_ ));
......@@ -5163,6 +5172,8 @@ DATA(insert OID = 4566 ( pg_event_trigger_table_rewrite_oid PGNSP PGUID 12 1 0
DESCR("return Oid of the table getting rewritten");
DATA(insert OID = 4567 ( pg_event_trigger_table_rewrite_reason PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ _null_ pg_event_trigger_table_rewrite_reason _null_ _null_ _null_ ));
DESCR("return reason code for table getting rewritten");
DATA(insert OID = 4568 ( pg_event_trigger_ddl_commands PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25,16,32}" "{o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, command_tag, object_type, schema_name, object_identity, in_extension, command}" _null_ _null_ pg_event_trigger_ddl_commands _null_ _null_ _null_ ));
DESCR("list DDL actions being executed by the current command");
/* generic transition functions for ordered-set aggregates */
DATA(insert OID = 3970 ( ordered_set_transition PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2276" _null_ _null_ _null_ _null_ _null_ ordered_set_transition _null_ _null_ _null_ ));
......
......@@ -364,6 +364,10 @@ DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
......
......@@ -90,6 +90,7 @@ extern void IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
Oid opfnamespace);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern char *get_am_name(Oid amOid);
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
......
......@@ -17,6 +17,8 @@
#include "catalog/objectaddress.h"
#include "catalog/pg_event_trigger.h"
#include "nodes/parsenodes.h"
#include "utils/aclchk_internal.h"
#include "tcop/deparse_utility.h"
typedef struct EventTriggerData
{
......@@ -60,4 +62,28 @@ extern bool trackDroppedObjectsNeeded(void);
extern void EventTriggerSQLDropAddObject(const ObjectAddress *object,
bool original, bool normal);
extern void EventTriggerInhibitCommandCollection(void);
extern void EventTriggerUndoInhibitCommandCollection(void);
extern void EventTriggerCollectSimpleCommand(ObjectAddress address,
ObjectAddress secondaryObject,
Node *parsetree);
extern void EventTriggerAlterTableStart(Node *parsetree);
extern void EventTriggerAlterTableRelid(Oid objectId);
extern void EventTriggerCollectAlterTableSubcmd(Node *subcmd,
ObjectAddress address);
extern void EventTriggerAlterTableEnd(void);
extern void EventTriggerCollectGrant(InternalGrant *istmt);
extern void EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt,
Oid opfamoid, List *operators,
List *procedures);
extern void EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt,
Oid opcoid, List *operators,
List *procedures);
extern void EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt,
Oid cfgId, Oid *dictIds, int ndicts);
extern void EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt);
#endif /* EVENT_TRIGGER_H */
......@@ -24,7 +24,7 @@
* on the current pg_extension object for each SQL object created by its
* installation script.
*/
extern bool creating_extension;
extern PGDLLIMPORT bool creating_extension;
extern Oid CurrentExtensionObject;
......
......@@ -2911,9 +2911,19 @@ typedef struct AlterTSDictionaryStmt
/*
* TS Configuration stmts: DefineStmt, RenameStmt and DropStmt are default
*/
typedef enum AlterTSConfigType
{
ALTER_TSCONFIG_ADD_MAPPING,
ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN,
ALTER_TSCONFIG_REPLACE_DICT,
ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN,
ALTER_TSCONFIG_DROP_MAPPING
} AlterTSConfigType;
typedef struct AlterTSConfigurationStmt
{
NodeTag type;
AlterTSConfigType kind; /* ALTER_TSCONFIG_ADD_MAPPING, etc */
List *cfgname; /* qualified name (list of Value strings) */
/*
......
/*-------------------------------------------------------------------------
*
* deparse_utility.h
*
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/tcop/deparse_utility.h
*
*-------------------------------------------------------------------------
*/
#ifndef DEPARSE_UTILITY_H
#define DEPARSE_UTILITY_H
#include "access/attnum.h"
#include "catalog/objectaddress.h"
#include "nodes/nodes.h"
#include "utils/aclchk_internal.h"
/*
* Support for keeping track of collected commands.
*/
typedef enum CollectedCommandType
{
SCT_Simple,
SCT_AlterTable,
SCT_Grant,
SCT_AlterOpFamily,
SCT_AlterDefaultPrivileges,
SCT_CreateOpClass,
SCT_AlterTSConfig
} CollectedCommandType;
/*
* For ALTER TABLE commands, we keep a list of the subcommands therein.
*/
typedef struct CollectedATSubcmd
{
ObjectAddress address; /* affected column, constraint, index, ... */
Node *parsetree;
} CollectedATSubcmd;
typedef struct CollectedCommand
{
CollectedCommandType type;
bool in_extension;
Node *parsetree;
union
{
/* most commands */
struct
{
ObjectAddress address;
ObjectAddress secondaryObject;
} simple;
/* ALTER TABLE, and internal uses thereof */
struct
{
Oid objectId;
Oid classId;
List *subcmds;
} alterTable;
/* GRANT / REVOKE */
struct
{
InternalGrant *istmt;
} grant;
/* ALTER OPERATOR FAMILY */
struct
{
ObjectAddress address;
List *operators;
List *procedures;
} opfam;
/* CREATE OPERATOR CLASS */
struct
{
ObjectAddress address;
List *operators;
List *procedures;
} createopc;
/* ALTER TEXT SEARCH CONFIGURATION ADD/ALTER/DROP MAPPING */
struct
{
ObjectAddress address;
Oid *dictIds;
int ndicts;
} atscfg;
/* ALTER DEFAULT PRIVILEGES */
struct
{
GrantObjectType objtype;
} defprivs;
} d;
} CollectedCommand;
#endif /* DEPARSE_UTILITY_H */
/*-------------------------------------------------------------------------
*
* aclchk_internal.h
*
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/aclchk_internal.h
*
*-------------------------------------------------------------------------
*/
#ifndef ACLCHK_INTERNAL_H
#define ACLCHK_INTERNAL_H
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
/*
* The information about one Grant/Revoke statement, in internal format: object
* and grantees names have been turned into Oids, the privilege list is an
* AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
* all_privs is true, 'privileges' will be internally set to the right kind of
* ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
* InternalGrant struct!)
*
* Note: 'all_privs' and 'privileges' represent object-level privileges only.
* There might also be column-level privilege specifications, which are
* represented in col_privs (this is a list of untransformed AccessPriv nodes).
* Column privileges are only valid for objtype ACL_OBJECT_RELATION.
*/
typedef struct
{
bool is_grant;
GrantObjectType objtype;
List *objects;
bool all_privs;
AclMode privileges;
List *col_privs;
List *grantees;
bool grant_option;
DropBehavior behavior;
} InternalGrant;
#endif /* ACLCHK_INTERNAL_H */
......@@ -576,6 +576,10 @@ extern Datum pg_node_tree_in(PG_FUNCTION_ARGS);
extern Datum pg_node_tree_out(PG_FUNCTION_ARGS);
extern Datum pg_node_tree_recv(PG_FUNCTION_ARGS);
extern Datum pg_node_tree_send(PG_FUNCTION_ARGS);
extern Datum pg_ddl_command_in(PG_FUNCTION_ARGS);
extern Datum pg_ddl_command_out(PG_FUNCTION_ARGS);
extern Datum pg_ddl_command_recv(PG_FUNCTION_ARGS);
extern Datum pg_ddl_command_send(PG_FUNCTION_ARGS);
/* regexp.c */
extern Datum nameregexeq(PG_FUNCTION_ARGS);
......@@ -1231,6 +1235,7 @@ extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
extern Datum pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS);
extern Datum pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS);
extern Datum pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS);
extern Datum pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS);
/* commands/extension.c */
extern Datum pg_available_extensions(PG_FUNCTION_ARGS);
......
......@@ -6,11 +6,12 @@ include $(top_builddir)/src/Makefile.global
SUBDIRS = \
commit_ts \
worker_spi \
dummy_seclabel \
test_ddl_deparse \
test_parser \
test_rls_hooks \
test_shm_mq \
test_parser
worker_spi
all: submake-errcodes
......
MODULES = test_ddl_deparse
PGFILEDESC = "test_ddl_deparse - regression testing for DDL deparsing"
EXTENSION = test_ddl_deparse
DATA = test_ddl_deparse--1.0.sql
REGRESS = --schedule=$(srcdir)/regress_schedule
EXTRA_INSTALL = contrib/pg_stat_statements
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = src/test/modules/test_ddl_deparse
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
--
-- ALTER_FUNCTION
--
ALTER FUNCTION plpgsql_function_trigger_1 ()
SET SCHEMA foo;
NOTICE: DDL test: type simple, tag ALTER FUNCTION
ALTER FUNCTION foo.plpgsql_function_trigger_1()
COST 10;
NOTICE: DDL test: type simple, tag ALTER FUNCTION
CREATE ROLE tmprole;
ALTER FUNCTION plpgsql_function_trigger_2()
OWNER TO tmprole;
ERROR: function plpgsql_function_trigger_2() does not exist
DROP OWNED BY tmprole;
DROP ROLE tmprole;
--
-- ALTER_SEQUENCE
--
ALTER SEQUENCE fkey_table_seq
MINVALUE 10
START 20
CACHE 1
NO CYCLE;
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
ALTER SEQUENCE fkey_table_seq
RENAME TO fkey_table_seq_renamed;
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
ALTER SEQUENCE fkey_table_seq_renamed
SET SCHEMA foo;
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
CREATE TABLE parent (
a int
);
NOTICE: DDL test: type simple, tag CREATE TABLE
CREATE TABLE child () INHERITS (parent);
NOTICE: DDL test: type simple, tag CREATE TABLE
CREATE TABLE grandchild () INHERITS (child);
NOTICE: DDL test: type simple, tag CREATE TABLE
ALTER TABLE parent ADD COLUMN b serial;
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type alter table, tag ALTER TABLE
NOTICE: subcommand: ADD COLUMN (and recurse)
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
ALTER TABLE parent RENAME COLUMN b TO c;
NOTICE: DDL test: type simple, tag ALTER TABLE
ALTER TABLE parent ADD CONSTRAINT a_pos CHECK (a > 0);
NOTICE: DDL test: type alter table, tag ALTER TABLE
NOTICE: subcommand: ADD CONSTRAINT (and recurse)
---
--- ALTER_TYPE_ENUM
---
ALTER TYPE enum_test ADD VALUE 'zzz' AFTER 'baz';
NOTICE: DDL test: type simple, tag ALTER TYPE
ALTER TYPE enum_test ADD VALUE 'aaa' BEFORE 'foo';
NOTICE: DDL test: type simple, tag ALTER TYPE
--
-- COMMENT_ON
--
COMMENT ON SCHEMA foo IS 'This is schema foo';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON TYPE enum_test IS 'ENUM test';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON TYPE int2range IS 'RANGE test';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON DOMAIN japanese_postal_code IS 'DOMAIN test';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON SEQUENCE fkey_table_seq IS 'SEQUENCE test';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON TABLE datatype_table IS 'This table should contain all native datatypes';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON VIEW datatype_view IS 'This is a view';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON FUNCTION c_function_test() IS 'FUNCTION test';
ERROR: function c_function_test() does not exist
COMMENT ON TRIGGER trigger_1 ON datatype_table IS 'TRIGGER test';
NOTICE: DDL test: type simple, tag COMMENT
COMMENT ON RULE rule_1 IS 'RULE test';
NOTICE: DDL test: type simple, tag COMMENT
-- should not fire
COMMENT ON DATABASE contrib_regression IS 'contrib regression';
---
--- CREATE_CONVERSION
---
-- Simple test should suffice for this
CREATE CONVERSION myconv FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8;
NOTICE: DDL test: type simple, tag CREATE CONVERSION
---
--- CREATE_DOMAIN
---
CREATE DOMAIN domainvarchar VARCHAR(5);
NOTICE: DDL test: type simple, tag CREATE DOMAIN
CREATE DOMAIN japanese_postal_code AS TEXT
CHECK(
VALUE ~ '^\d{3}$'
OR VALUE ~ '^\d{3}-\d{4}$'
);
NOTICE: DDL test: type simple, tag CREATE DOMAIN
---
--- CREATE_EXTENSION
---
CREATE EXTENSION pg_stat_statements;
NOTICE: DDL test: type simple, tag CREATE EXTENSION
---
--- CREATE_RULE
---
CREATE RULE rule_1 AS
ON INSERT
TO datatype_table
DO NOTHING;
NOTICE: DDL test: type simple, tag CREATE RULE
CREATE RULE rule_2 AS
ON UPDATE
TO datatype_table
DO INSERT INTO unlogged_table (id) VALUES(NEW.id);
NOTICE: DDL test: type simple, tag CREATE RULE
CREATE RULE rule_3 AS
ON DELETE
TO datatype_table
DO ALSO NOTHING;
NOTICE: DDL test: type simple, tag CREATE RULE
CREATE RULE "_RETURN" AS
ON SELECT
TO like_datatype_table
DO INSTEAD
SELECT * FROM datatype_view;
NOTICE: DDL test: type simple, tag CREATE RULE
CREATE RULE rule_3 AS
ON DELETE
TO like_datatype_table
WHERE id < 100
DO ALSO NOTHING;
NOTICE: DDL test: type simple, tag CREATE RULE
--
-- CREATE_SCHEMA
--
CREATE SCHEMA foo;
NOTICE: DDL test: type simple, tag CREATE SCHEMA
CREATE SCHEMA IF NOT EXISTS bar;
NOTICE: DDL test: type simple, tag CREATE SCHEMA
CREATE SCHEMA baz;
NOTICE: DDL test: type simple, tag CREATE SCHEMA
-- Will not be created, and will not be handled by the
-- event trigger
CREATE SCHEMA IF NOT EXISTS baz;
NOTICE: schema "baz" already exists, skipping
CREATE SCHEMA element_test
CREATE TABLE foo (id int)
CREATE VIEW bar AS SELECT * FROM foo;
NOTICE: DDL test: type simple, tag CREATE SCHEMA
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE VIEW
--
-- CREATE_SEQUENCE
--
CREATE SEQUENCE fkey_table_seq
INCREMENT BY 1
MINVALUE 0
MAXVALUE 1000000
START 10
CACHE 10
CYCLE;
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
--
-- CREATE_TABLE
--
-- Datatypes
CREATE TABLE datatype_table (
id SERIAL,
id_big BIGSERIAL,
is_small SMALLSERIAL,
v_bytea BYTEA,
v_smallint SMALLINT,
v_int INT,
v_bigint BIGINT,
v_char CHAR(1),
v_varchar VARCHAR(10),
v_text TEXT,
v_bool BOOLEAN,
v_inet INET,
v_cidr CIDR,
v_macaddr MACADDR,
v_numeric NUMERIC(1,0),
v_real REAL,
v_float FLOAT(1),
v_float8 FLOAT8,
v_money MONEY,
v_tsquery TSQUERY,
v_tsvector TSVECTOR,
v_date DATE,
v_time TIME,
v_time_tz TIME WITH TIME ZONE,
v_timestamp TIMESTAMP,
v_timestamp_tz TIMESTAMP WITH TIME ZONE,
v_interval INTERVAL,
v_bit BIT,
v_bit4 BIT(4),
v_varbit VARBIT,
v_varbit4 VARBIT(4),
v_box BOX,
v_circle CIRCLE,
v_lseg LSEG,
v_path PATH,
v_point POINT,
v_polygon POLYGON,
v_json JSON,
v_xml XML,
v_uuid UUID,
v_txid_snapshot txid_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,
PRIMARY KEY (id),
UNIQUE (id_big)
);
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
-- Constraint definitions
CREATE TABLE IF NOT EXISTS fkey_table (
id INT NOT NULL DEFAULT nextval('fkey_table_seq'::REGCLASS),
datatype_id INT NOT NULL REFERENCES datatype_table(id),
big_id BIGINT NOT NULL,
sometext TEXT COLLATE "POSIX",
check_col_1 INT NOT NULL CHECK(check_col_1 < 10),
check_col_2 INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT fkey_big_id
FOREIGN KEY (big_id)
REFERENCES datatype_table(id_big),
EXCLUDE USING btree (check_col_2 WITH =)
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type alter table, tag ALTER TABLE
NOTICE: subcommand: ADD CONSTRAINT (and recurse)
NOTICE: subcommand: ADD CONSTRAINT (and recurse)
-- Typed table
CREATE TABLE employees OF employee_type (
PRIMARY KEY (name),
salary WITH OPTIONS DEFAULT 1000
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
-- Inheritance
CREATE TABLE person (
id INT NOT NULL PRIMARY KEY,
name text,
age int4,
location point
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TABLE emp (
salary int4,
manager name
) INHERITS (person) WITH OIDS;
NOTICE: DDL test: type simple, tag CREATE TABLE
CREATE TABLE student (
gpa float8
) INHERITS (person);
NOTICE: DDL test: type simple, tag CREATE TABLE
CREATE TABLE stud_emp (
percent int4
) INHERITS (emp, student);
NOTICE: merging multiple inherited definitions of column "id"
NOTICE: merging multiple inherited definitions of column "name"
NOTICE: merging multiple inherited definitions of column "age"
NOTICE: merging multiple inherited definitions of column "location"
NOTICE: DDL test: type simple, tag CREATE TABLE
-- Storage parameters
CREATE TABLE storage (
id INT
) WITH (
fillfactor = 10,
autovacuum_enabled = FALSE
);
NOTICE: DDL test: type simple, tag CREATE TABLE
-- LIKE
CREATE TABLE like_datatype_table (
LIKE datatype_table
EXCLUDING ALL
);
NOTICE: DDL test: type simple, tag CREATE TABLE
CREATE TABLE like_fkey_table (
LIKE fkey_table
INCLUDING DEFAULTS
INCLUDING INDEXES
INCLUDING STORAGE
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag CREATE INDEX
-- Volatile table types
CREATE UNLOGGED TABLE unlogged_table (
id INT PRIMARY KEY
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TEMP TABLE temp_table (
id INT PRIMARY KEY
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TEMP TABLE temp_table_commit_delete (
id INT PRIMARY KEY
)
ON COMMIT DELETE ROWS;
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TEMP TABLE temp_table_commit_drop (
id INT PRIMARY KEY
)
ON COMMIT DROP;
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type simple, tag CREATE INDEX
---
--- CREATE_TRIGGER
---
CREATE FUNCTION plpgsql_function_trigger_1()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
RETURN NEW;
END;
$$;
NOTICE: DDL test: type simple, tag CREATE FUNCTION
CREATE TRIGGER trigger_1
BEFORE INSERT OR UPDATE
ON datatype_table
FOR EACH ROW
EXECUTE PROCEDURE plpgsql_function_trigger_1();
NOTICE: DDL test: type simple, tag CREATE TRIGGER
---
--- CREATE_TYPE
---
CREATE FUNCTION text_w_default_in(cstring)
RETURNS text_w_default
AS 'textin'
LANGUAGE internal STABLE STRICT;
NOTICE: type "text_w_default" is not yet defined
DETAIL: Creating a shell type definition.
NOTICE: DDL test: type simple, tag CREATE FUNCTION
CREATE FUNCTION text_w_default_out(text_w_default)
RETURNS cstring
AS 'textout'
LANGUAGE internal STABLE STRICT ;
NOTICE: argument type text_w_default is only a shell
NOTICE: DDL test: type simple, tag CREATE FUNCTION
CREATE TYPE employee_type AS (name TEXT, salary NUMERIC);
NOTICE: DDL test: type simple, tag CREATE TYPE
CREATE TYPE enum_test AS ENUM ('foo', 'bar', 'baz');
NOTICE: DDL test: type simple, tag CREATE TYPE
CREATE TYPE int2range AS RANGE (
SUBTYPE = int2
);
NOTICE: DDL test: type simple, tag CREATE TYPE
--
-- CREATE_VIEW
--
CREATE VIEW static_view AS
SELECT 'foo'::TEXT AS col;
NOTICE: DDL test: type simple, tag CREATE VIEW
CREATE OR REPLACE VIEW static_view AS
SELECT 'bar'::TEXT AS col;
NOTICE: DDL test: type simple, tag CREATE VIEW
NOTICE: DDL test: type alter table, tag CREATE VIEW
NOTICE: subcommand: REPLACE RELOPTIONS
CREATE VIEW datatype_view AS
SELECT * FROM datatype_table;
NOTICE: DDL test: type simple, tag CREATE VIEW
CREATE RECURSIVE VIEW nums_1_100 (n) AS
VALUES (1)
UNION ALL
SELECT n+1 FROM nums_1_100 WHERE n < 100;
NOTICE: DDL test: type simple, tag CREATE VIEW
--
-- ALTER DEFAULT PRIVILEGES
--
ALTER DEFAULT PRIVILEGES IN SCHEMA public
REVOKE ALL PRIVILEGES ON TABLES FROM public;
NOTICE: DDL test: type alter default privileges, tag ALTER DEFAULT PRIVILEGES
--
-- Materialized views
--
CREATE MATERIALIZED VIEW pg_class_mv AS
SELECT * FROM pg_class LIMIT 1 WITH NO DATA;
NOTICE: DDL test: type simple, tag CREATE MATERIALIZED VIEW
REFRESH MATERIALIZED VIEW pg_class_mv;
NOTICE: DDL test: type simple, tag REFRESH MATERIALIZED VIEW
-- copied from equivclass.sql
create type int8alias1;
NOTICE: DDL test: type simple, tag CREATE TYPE
create function int8alias1in(cstring) returns int8alias1
strict immutable language internal as 'int8in';
NOTICE: return type int8alias1 is only a shell
NOTICE: DDL test: type simple, tag CREATE FUNCTION
create function int8alias1out(int8alias1) returns cstring
strict immutable language internal as 'int8out';
NOTICE: argument type int8alias1 is only a shell
NOTICE: DDL test: type simple, tag CREATE FUNCTION
create type int8alias1 (
input = int8alias1in,
output = int8alias1out,
like = int8
);
NOTICE: DDL test: type simple, tag CREATE TYPE
create type int8alias2;
NOTICE: DDL test: type simple, tag CREATE TYPE
create function int8alias2in(cstring) returns int8alias2
strict immutable language internal as 'int8in';
NOTICE: return type int8alias2 is only a shell
NOTICE: DDL test: type simple, tag CREATE FUNCTION
create function int8alias2out(int8alias2) returns cstring
strict immutable language internal as 'int8out';
NOTICE: argument type int8alias2 is only a shell
NOTICE: DDL test: type simple, tag CREATE FUNCTION
create type int8alias2 (
input = int8alias2in,
output = int8alias2out,
like = int8
);
NOTICE: DDL test: type simple, tag CREATE TYPE
create cast (int8 as int8alias1) without function;
NOTICE: DDL test: type simple, tag CREATE CAST
create cast (int8 as int8alias2) without function;
NOTICE: DDL test: type simple, tag CREATE CAST
create cast (int8alias1 as int8) without function;
NOTICE: DDL test: type simple, tag CREATE CAST
create cast (int8alias2 as int8) without function;
NOTICE: DDL test: type simple, tag CREATE CAST
create function int8alias1eq(int8alias1, int8alias1) returns bool
strict immutable language internal as 'int8eq';
NOTICE: DDL test: type simple, tag CREATE FUNCTION
create operator = (
procedure = int8alias1eq,
leftarg = int8alias1, rightarg = int8alias1,
commutator = =,
restrict = eqsel, join = eqjoinsel,
merges
);
NOTICE: DDL test: type simple, tag CREATE OPERATOR
alter operator family integer_ops using btree add
operator 3 = (int8alias1, int8alias1);
NOTICE: DDL test: type alter operator family, tag ALTER OPERATOR FAMILY
-- copied from alter_table.sql
create type ctype as (f1 int, f2 text);
NOTICE: DDL test: type simple, tag CREATE TYPE
create function same(ctype, ctype) returns boolean language sql
as 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2';
NOTICE: DDL test: type simple, tag CREATE FUNCTION
create operator =(procedure = same, leftarg = ctype, rightarg = ctype);
NOTICE: DDL test: type simple, tag CREATE OPERATOR
create operator class ctype_hash_ops
default for type ctype using hash as
operator 1 =(ctype, ctype);
NOTICE: DDL test: type create operator class, tag CREATE OPERATOR CLASS
CREATE EXTENSION test_ddl_deparse;
CREATE OR REPLACE FUNCTION test_ddl_deparse()
RETURNS event_trigger LANGUAGE plpgsql AS
$$
DECLARE
r record;
r2 record;
cmdtype text;
objtype text;
tag text;
BEGIN
FOR r IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
-- verify that tags match
tag = get_command_tag(r.command);
IF tag <> r.command_tag THEN
RAISE NOTICE 'tag % doesn''t match %', tag, r.command_tag;
END IF;
-- log the operation
cmdtype = get_command_type(r.command);
IF cmdtype <> 'grant' THEN
RAISE NOTICE 'DDL test: type %, tag %', cmdtype, tag;
ELSE
RAISE NOTICE 'DDL test: type %, object type %', cmdtype, r.object_type;
END IF;
-- if alter table, log more
IF cmdtype = 'alter table' THEN
FOR r2 IN SELECT *
FROM unnest(get_altertable_subcmdtypes(r.command))
LOOP
RAISE NOTICE ' subcommand: %', r2.unnest;
END LOOP;
END IF;
END LOOP;
END;
$$;
CREATE EVENT TRIGGER test_ddl_deparse
ON ddl_command_end EXECUTE PROCEDURE test_ddl_deparse();
# must be first
test: test_ddl_deparse
test: create_extension
test: create_schema
test: create_type
test: create_conversion
test: create_domain
test: create_sequence_1
test: create_table
test: alter_table
test: create_view
test: create_trigger
test: create_rule
test: comment_on
test: alter_function
test: alter_sequence
test: alter_type_enum
test: opfamily
test: defprivs
test: matviews
--
-- ALTER_FUNCTION
--
ALTER FUNCTION plpgsql_function_trigger_1 ()
SET SCHEMA foo;
ALTER FUNCTION foo.plpgsql_function_trigger_1()
COST 10;
CREATE ROLE tmprole;
ALTER FUNCTION plpgsql_function_trigger_2()
OWNER TO tmprole;
DROP OWNED BY tmprole;
DROP ROLE tmprole;
--
-- ALTER_SEQUENCE
--
ALTER SEQUENCE fkey_table_seq
MINVALUE 10
START 20
CACHE 1
NO CYCLE;
ALTER SEQUENCE fkey_table_seq
RENAME TO fkey_table_seq_renamed;
ALTER SEQUENCE fkey_table_seq_renamed
SET SCHEMA foo;
CREATE TABLE parent (
a int
);
CREATE TABLE child () INHERITS (parent);
CREATE TABLE grandchild () INHERITS (child);
ALTER TABLE parent ADD COLUMN b serial;
ALTER TABLE parent RENAME COLUMN b TO c;
ALTER TABLE parent ADD CONSTRAINT a_pos CHECK (a > 0);
---
--- ALTER_TYPE_ENUM
---
ALTER TYPE enum_test ADD VALUE 'zzz' AFTER 'baz';
ALTER TYPE enum_test ADD VALUE 'aaa' BEFORE 'foo';
--
-- COMMENT_ON
--
COMMENT ON SCHEMA foo IS 'This is schema foo';
COMMENT ON TYPE enum_test IS 'ENUM test';
COMMENT ON TYPE int2range IS 'RANGE test';
COMMENT ON DOMAIN japanese_postal_code IS 'DOMAIN test';
COMMENT ON SEQUENCE fkey_table_seq IS 'SEQUENCE test';
COMMENT ON TABLE datatype_table IS 'This table should contain all native datatypes';
COMMENT ON VIEW datatype_view IS 'This is a view';
COMMENT ON FUNCTION c_function_test() IS 'FUNCTION test';
COMMENT ON TRIGGER trigger_1 ON datatype_table IS 'TRIGGER test';
COMMENT ON RULE rule_1 IS 'RULE test';
-- should not fire
COMMENT ON DATABASE contrib_regression IS 'contrib regression';
---
--- CREATE_CONVERSION
---
-- Simple test should suffice for this
CREATE CONVERSION myconv FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8;
---
--- CREATE_DOMAIN
---
CREATE DOMAIN domainvarchar VARCHAR(5);
CREATE DOMAIN japanese_postal_code AS TEXT
CHECK(
VALUE ~ '^\d{3}$'
OR VALUE ~ '^\d{3}-\d{4}$'
);
---
--- CREATE_EXTENSION
---
CREATE EXTENSION pg_stat_statements;
---
--- CREATE_RULE
---
CREATE RULE rule_1 AS
ON INSERT
TO datatype_table
DO NOTHING;
CREATE RULE rule_2 AS
ON UPDATE
TO datatype_table
DO INSERT INTO unlogged_table (id) VALUES(NEW.id);
CREATE RULE rule_3 AS
ON DELETE
TO datatype_table
DO ALSO NOTHING;
CREATE RULE "_RETURN" AS
ON SELECT
TO like_datatype_table
DO INSTEAD
SELECT * FROM datatype_view;
CREATE RULE rule_3 AS
ON DELETE
TO like_datatype_table
WHERE id < 100
DO ALSO NOTHING;
--
-- CREATE_SCHEMA
--
CREATE SCHEMA foo;
CREATE SCHEMA IF NOT EXISTS bar;
CREATE SCHEMA baz;
-- Will not be created, and will not be handled by the
-- event trigger
CREATE SCHEMA IF NOT EXISTS baz;
CREATE SCHEMA element_test
CREATE TABLE foo (id int)
CREATE VIEW bar AS SELECT * FROM foo;
--
-- CREATE_SEQUENCE
--
CREATE SEQUENCE fkey_table_seq
INCREMENT BY 1
MINVALUE 0
MAXVALUE 1000000
START 10
CACHE 10
CYCLE;
--
-- CREATE_TABLE
--
-- Datatypes
CREATE TABLE datatype_table (
id SERIAL,
id_big BIGSERIAL,
is_small SMALLSERIAL,
v_bytea BYTEA,
v_smallint SMALLINT,
v_int INT,
v_bigint BIGINT,
v_char CHAR(1),
v_varchar VARCHAR(10),
v_text TEXT,
v_bool BOOLEAN,
v_inet INET,
v_cidr CIDR,
v_macaddr MACADDR,
v_numeric NUMERIC(1,0),
v_real REAL,
v_float FLOAT(1),
v_float8 FLOAT8,
v_money MONEY,
v_tsquery TSQUERY,
v_tsvector TSVECTOR,
v_date DATE,
v_time TIME,
v_time_tz TIME WITH TIME ZONE,
v_timestamp TIMESTAMP,
v_timestamp_tz TIMESTAMP WITH TIME ZONE,
v_interval INTERVAL,
v_bit BIT,
v_bit4 BIT(4),
v_varbit VARBIT,
v_varbit4 VARBIT(4),
v_box BOX,
v_circle CIRCLE,
v_lseg LSEG,
v_path PATH,
v_point POINT,
v_polygon POLYGON,
v_json JSON,
v_xml XML,
v_uuid UUID,
v_txid_snapshot txid_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,
PRIMARY KEY (id),
UNIQUE (id_big)
);
-- Constraint definitions
CREATE TABLE IF NOT EXISTS fkey_table (
id INT NOT NULL DEFAULT nextval('fkey_table_seq'::REGCLASS),
datatype_id INT NOT NULL REFERENCES datatype_table(id),
big_id BIGINT NOT NULL,
sometext TEXT COLLATE "POSIX",
check_col_1 INT NOT NULL CHECK(check_col_1 < 10),
check_col_2 INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT fkey_big_id
FOREIGN KEY (big_id)
REFERENCES datatype_table(id_big),
EXCLUDE USING btree (check_col_2 WITH =)
);
-- Typed table
CREATE TABLE employees OF employee_type (
PRIMARY KEY (name),
salary WITH OPTIONS DEFAULT 1000
);
-- Inheritance
CREATE TABLE person (
id INT NOT NULL PRIMARY KEY,
name text,
age int4,
location point
);
CREATE TABLE emp (
salary int4,
manager name
) INHERITS (person) WITH OIDS;
CREATE TABLE student (
gpa float8
) INHERITS (person);
CREATE TABLE stud_emp (
percent int4
) INHERITS (emp, student);
-- Storage parameters
CREATE TABLE storage (
id INT
) WITH (
fillfactor = 10,
autovacuum_enabled = FALSE
);
-- LIKE
CREATE TABLE like_datatype_table (
LIKE datatype_table
EXCLUDING ALL
);
CREATE TABLE like_fkey_table (
LIKE fkey_table
INCLUDING DEFAULTS
INCLUDING INDEXES
INCLUDING STORAGE
);
-- Volatile table types
CREATE UNLOGGED TABLE unlogged_table (
id INT PRIMARY KEY
);
CREATE TEMP TABLE temp_table (
id INT PRIMARY KEY
);
CREATE TEMP TABLE temp_table_commit_delete (
id INT PRIMARY KEY
)
ON COMMIT DELETE ROWS;
CREATE TEMP TABLE temp_table_commit_drop (
id INT PRIMARY KEY
)
ON COMMIT DROP;
---
--- CREATE_TRIGGER
---
CREATE FUNCTION plpgsql_function_trigger_1()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
RETURN NEW;
END;
$$;
CREATE TRIGGER trigger_1
BEFORE INSERT OR UPDATE
ON datatype_table
FOR EACH ROW
EXECUTE PROCEDURE plpgsql_function_trigger_1();
---
--- CREATE_TYPE
---
CREATE FUNCTION text_w_default_in(cstring)
RETURNS text_w_default
AS 'textin'
LANGUAGE internal STABLE STRICT;
CREATE FUNCTION text_w_default_out(text_w_default)
RETURNS cstring
AS 'textout'
LANGUAGE internal STABLE STRICT ;
CREATE TYPE employee_type AS (name TEXT, salary NUMERIC);
CREATE TYPE enum_test AS ENUM ('foo', 'bar', 'baz');
CREATE TYPE int2range AS RANGE (
SUBTYPE = int2
);
--
-- CREATE_VIEW
--
CREATE VIEW static_view AS
SELECT 'foo'::TEXT AS col;
CREATE OR REPLACE VIEW static_view AS
SELECT 'bar'::TEXT AS col;
CREATE VIEW datatype_view AS
SELECT * FROM datatype_table;
CREATE RECURSIVE VIEW nums_1_100 (n) AS
VALUES (1)
UNION ALL
SELECT n+1 FROM nums_1_100 WHERE n < 100;
--
-- ALTER DEFAULT PRIVILEGES
--
ALTER DEFAULT PRIVILEGES IN SCHEMA public
REVOKE ALL PRIVILEGES ON TABLES FROM public;
--
-- Materialized views
--
CREATE MATERIALIZED VIEW pg_class_mv AS
SELECT * FROM pg_class LIMIT 1 WITH NO DATA;
REFRESH MATERIALIZED VIEW pg_class_mv;
-- copied from equivclass.sql
create type int8alias1;
create function int8alias1in(cstring) returns int8alias1
strict immutable language internal as 'int8in';
create function int8alias1out(int8alias1) returns cstring
strict immutable language internal as 'int8out';
create type int8alias1 (
input = int8alias1in,
output = int8alias1out,
like = int8
);
create type int8alias2;
create function int8alias2in(cstring) returns int8alias2
strict immutable language internal as 'int8in';
create function int8alias2out(int8alias2) returns cstring
strict immutable language internal as 'int8out';
create type int8alias2 (
input = int8alias2in,
output = int8alias2out,
like = int8
);
create cast (int8 as int8alias1) without function;
create cast (int8 as int8alias2) without function;
create cast (int8alias1 as int8) without function;
create cast (int8alias2 as int8) without function;
create function int8alias1eq(int8alias1, int8alias1) returns bool
strict immutable language internal as 'int8eq';
create operator = (
procedure = int8alias1eq,
leftarg = int8alias1, rightarg = int8alias1,
commutator = =,
restrict = eqsel, join = eqjoinsel,
merges
);
alter operator family integer_ops using btree add
operator 3 = (int8alias1, int8alias1);
-- copied from alter_table.sql
create type ctype as (f1 int, f2 text);
create function same(ctype, ctype) returns boolean language sql
as 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2';
create operator =(procedure = same, leftarg = ctype, rightarg = ctype);
create operator class ctype_hash_ops
default for type ctype using hash as
operator 1 =(ctype, ctype);
CREATE EXTENSION test_ddl_deparse;
CREATE OR REPLACE FUNCTION test_ddl_deparse()
RETURNS event_trigger LANGUAGE plpgsql AS
$$
DECLARE
r record;
r2 record;
cmdtype text;
objtype text;
tag text;
BEGIN
FOR r IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
-- verify that tags match
tag = get_command_tag(r.command);
IF tag <> r.command_tag THEN
RAISE NOTICE 'tag % doesn''t match %', tag, r.command_tag;
END IF;
-- log the operation
cmdtype = get_command_type(r.command);
IF cmdtype <> 'grant' THEN
RAISE NOTICE 'DDL test: type %, tag %', cmdtype, tag;
ELSE
RAISE NOTICE 'DDL test: type %, object type %', cmdtype, r.object_type;
END IF;
-- if alter table, log more
IF cmdtype = 'alter table' THEN
FOR r2 IN SELECT *
FROM unnest(get_altertable_subcmdtypes(r.command))
LOOP
RAISE NOTICE ' subcommand: %', r2.unnest;
END LOOP;
END IF;
END LOOP;
END;
$$;
CREATE EVENT TRIGGER test_ddl_deparse
ON ddl_command_end EXECUTE PROCEDURE test_ddl_deparse();
/* src/test/modules/test_ddl_deparse/test_ddl_deparse--1.0.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION test_ddl_deparse" to load this file. \quit
CREATE FUNCTION get_command_type(pg_ddl_command)
RETURNS text IMMUTABLE STRICT
AS 'MODULE_PATHNAME' LANGUAGE C;
CREATE FUNCTION get_command_tag(pg_ddl_command)
RETURNS text IMMUTABLE STRICT
AS 'MODULE_PATHNAME' LANGUAGE C;
CREATE FUNCTION get_altertable_subcmdtypes(pg_ddl_command)
RETURNS text[] IMMUTABLE STRICT
AS 'MODULE_PATHNAME' LANGUAGE C;
#include "postgres.h"
#include "catalog/pg_type.h"
#include "tcop/deparse_utility.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(get_command_type);
PG_FUNCTION_INFO_V1(get_command_tag);
PG_FUNCTION_INFO_V1(get_altertable_subcmdtypes);
Datum
get_command_type(PG_FUNCTION_ARGS)
{
CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
const char *type;
switch (cmd->type)
{
case SCT_Simple:
type = "simple";
break;
case SCT_AlterTable:
type = "alter table";
break;
case SCT_Grant:
type = "grant";
break;
case SCT_AlterOpFamily:
type = "alter operator family";
break;
case SCT_AlterDefaultPrivileges:
type = "alter default privileges";
break;
case SCT_CreateOpClass:
type = "create operator class";
break;
case SCT_AlterTSConfig:
type = "alter text search configuration";
break;
default:
type = "unknown command type";
break;
}
PG_RETURN_TEXT_P(cstring_to_text(type));
}
Datum
get_command_tag(PG_FUNCTION_ARGS)
{
CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
if (!cmd->parsetree)
PG_RETURN_NULL();
PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
}
Datum
get_altertable_subcmdtypes(PG_FUNCTION_ARGS)
{
CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
ArrayBuildState *astate = NULL;
ListCell *cell;
if (cmd->type != SCT_AlterTable)
elog(ERROR, "command is not ALTER TABLE");
foreach(cell, cmd->d.alterTable.subcmds)
{
CollectedATSubcmd *sub = lfirst(cell);
AlterTableCmd *subcmd = (AlterTableCmd *) sub->parsetree;
const char *strtype;
Assert(IsA(subcmd, AlterTableCmd));
switch (subcmd->subtype)
{
case AT_AddColumn:
strtype = "ADD COLUMN";
break;
case AT_AddColumnRecurse:
strtype = "ADD COLUMN (and recurse)";
break;
case AT_AddColumnToView:
strtype = "ADD COLUMN TO VIEW";
break;
case AT_ColumnDefault:
strtype = "ALTER COLUMN SET DEFAULT";
break;
case AT_DropNotNull:
strtype = "DROP NOT NULL";
break;
case AT_SetNotNull:
strtype = "SET NOT NULL";
break;
case AT_SetStatistics:
strtype = "SET STATS";
break;
case AT_SetOptions:
strtype = "SET OPTIONS";
break;
case AT_ResetOptions:
strtype = "RESET OPTIONS";
break;
case AT_SetStorage:
strtype = "SET STORAGE";
break;
case AT_DropColumn:
strtype = "DROP COLUMN";
break;
case AT_DropColumnRecurse:
strtype = "DROP COLUMN (and recurse)";
break;
case AT_AddIndex:
strtype = "ADD INDEX";
break;
case AT_ReAddIndex:
strtype = "(re) ADD INDEX";
break;
case AT_AddConstraint:
strtype = "ADD CONSTRAINT";
break;
case AT_AddConstraintRecurse:
strtype = "ADD CONSTRAINT (and recurse)";
break;
case AT_ReAddConstraint:
strtype = "(re) ADD CONSTRAINT";
break;
case AT_AlterConstraint:
strtype = "ALTER CONSTRAINT";
break;
case AT_ValidateConstraint:
strtype = "VALIDATE CONSTRAINT";
break;
case AT_ValidateConstraintRecurse:
strtype = "VALIDATE CONSTRAINT (and recurse)";
break;
case AT_ProcessedConstraint:
strtype = "ADD (processed) CONSTRAINT";
break;
case AT_AddIndexConstraint:
strtype = "ADD CONSTRAINT (using index)";
break;
case AT_DropConstraint:
strtype = "DROP CONSTRAINT";
break;
case AT_DropConstraintRecurse:
strtype = "DROP CONSTRAINT (and recurse)";
break;
case AT_AlterColumnType:
strtype = "ALTER COLUMN SET TYPE";
break;
case AT_AlterColumnGenericOptions:
strtype = "ALTER COLUMN SET OPTIONS";
break;
case AT_ChangeOwner:
strtype = "CHANGE OWNER";
break;
case AT_ClusterOn:
strtype = "CLUSTER";
break;
case AT_DropCluster:
strtype = "DROP CLUSTER";
break;
case AT_SetLogged:
strtype = "SET LOGGED";
break;
case AT_SetUnLogged:
strtype = "SET UNLOGGED";
break;
case AT_AddOids:
strtype = "ADD OIDS";
break;
case AT_AddOidsRecurse:
strtype = "ADD OIDS (and recurse)";
break;
case AT_DropOids:
strtype = "DROP OIDS";
break;
case AT_SetTableSpace:
strtype = "SET TABLESPACE";
break;
case AT_SetRelOptions:
strtype = "SET RELOPTIONS";
break;
case AT_ResetRelOptions:
strtype = "RESET RELOPTIONS";
break;
case AT_ReplaceRelOptions:
strtype = "REPLACE RELOPTIONS";
break;
case AT_EnableTrig:
strtype = "ENABLE TRIGGER";
break;
case AT_EnableAlwaysTrig:
strtype = "ENABLE TRIGGER (always)";
break;
case AT_EnableReplicaTrig:
strtype = "ENABLE TRIGGER (replica)";
break;
case AT_DisableTrig:
strtype = "DISABLE TRIGGER";
break;
case AT_EnableTrigAll:
strtype = "ENABLE TRIGGER (all)";
break;
case AT_DisableTrigAll:
strtype = "DISABLE TRIGGER (all)";
break;
case AT_EnableTrigUser:
strtype = "ENABLE TRIGGER (user)";
break;
case AT_DisableTrigUser:
strtype = "DISABLE TRIGGER (user)";
break;
case AT_EnableRule:
strtype = "ENABLE RULE";
break;
case AT_EnableAlwaysRule:
strtype = "ENABLE RULE (always)";
break;
case AT_EnableReplicaRule:
strtype = "ENABLE RULE (replica)";
break;
case AT_DisableRule:
strtype = "DISABLE RULE";
break;
case AT_AddInherit:
strtype = "ADD INHERIT";
break;
case AT_DropInherit:
strtype = "DROP INHERIT";
break;
case AT_AddOf:
strtype = "OF";
break;
case AT_DropOf:
strtype = "NOT OF";
break;
case AT_ReplicaIdentity:
strtype = "REPLICA IDENTITY";
break;
case AT_EnableRowSecurity:
strtype = "ENABLE ROW SECURITY";
break;
case AT_DisableRowSecurity:
strtype = "DISABLE ROW SECURITY";
break;
case AT_GenericOptions:
strtype = "SET OPTIONS";
break;
}
astate =
accumArrayResult(astate, CStringGetTextDatum(strtype),
false, TEXTOID, CurrentMemoryContext);
}
if (astate == NULL)
elog(ERROR, "empty alter table subcommand list");
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
}
comment = 'Test code for DDL deparse feature'
default_version = '1.0'
module_pathname = '$libdir/test_ddl_deparse'
relocatable = true
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment