diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 29d821e01a2..da558668c8a 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ - + Functions and Operators @@ -12369,6 +12369,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); text get CREATE [ CONSTRAINT ] TRIGGER command for trigger + + pg_get_triggerdef(trigger_oid, pretty_bool) + text + get CREATE [ CONSTRAINT ] TRIGGER command for trigger + pg_get_userbyid(role_oid) name diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 4c04bafd7c6..d88d8f22f32 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.307 2009/10/08 02:39:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.308 2009/10/09 21:02:55 petere Exp $ * *------------------------------------------------------------------------- */ @@ -139,6 +139,7 @@ static char *deparse_expression_pretty(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit, int prettyFlags, int startIndent); static char *pg_get_viewdef_worker(Oid viewoid, int prettyFlags); +static char *pg_get_triggerdef_worker(Oid trigid, bool pretty); static void decompile_column_index_array(Datum column_index_array, Oid relId, StringInfo buf); static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags); @@ -462,6 +463,22 @@ Datum pg_get_triggerdef(PG_FUNCTION_ARGS) { Oid trigid = PG_GETARG_OID(0); + + PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, false))); +} + +Datum +pg_get_triggerdef_ext(PG_FUNCTION_ARGS) +{ + Oid trigid = PG_GETARG_OID(0); + bool pretty = PG_GETARG_BOOL(1); + + PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, pretty))); +} + +static char * +pg_get_triggerdef_worker(Oid trigid, bool pretty) +{ HeapTuple ht_trig; Form_pg_trigger trigrec; StringInfoData buf; @@ -498,9 +515,10 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) initStringInfo(&buf); tgname = NameStr(trigrec->tgname); - appendStringInfo(&buf, "CREATE %sTRIGGER %s ", + appendStringInfo(&buf, "CREATE %sTRIGGER %s", trigrec->tgisconstraint ? "CONSTRAINT " : "", quote_identifier(tgname)); + appendStringInfoString(&buf, pretty ? "\n " : " "); if (TRIGGER_FOR_BEFORE(trigrec->tgtype)) appendStringInfo(&buf, "BEFORE"); @@ -533,29 +551,33 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) else appendStringInfo(&buf, " TRUNCATE"); } - appendStringInfo(&buf, " ON %s ", + appendStringInfo(&buf, " ON %s", generate_relation_name(trigrec->tgrelid, NIL)); + appendStringInfoString(&buf, pretty ? "\n " : " "); if (trigrec->tgisconstraint) { if (trigrec->tgconstrrelid != InvalidOid) - appendStringInfo(&buf, "FROM %s ", - generate_relation_name(trigrec->tgconstrrelid, - NIL)); + { + appendStringInfo(&buf, "FROM %s", + generate_relation_name(trigrec->tgconstrrelid, NIL)); + appendStringInfoString(&buf, pretty ? "\n " : " "); + } if (!trigrec->tgdeferrable) appendStringInfo(&buf, "NOT "); appendStringInfo(&buf, "DEFERRABLE INITIALLY "); if (trigrec->tginitdeferred) - appendStringInfo(&buf, "DEFERRED "); + appendStringInfo(&buf, "DEFERRED"); else - appendStringInfo(&buf, "IMMEDIATE "); - + appendStringInfo(&buf, "IMMEDIATE"); + appendStringInfoString(&buf, pretty ? "\n " : " "); } if (TRIGGER_FOR_ROW(trigrec->tgtype)) - appendStringInfo(&buf, "FOR EACH ROW "); + appendStringInfo(&buf, "FOR EACH ROW"); else - appendStringInfo(&buf, "FOR EACH STATEMENT "); + appendStringInfo(&buf, "FOR EACH STATEMENT"); + appendStringInfoString(&buf, pretty ? "\n " : " "); appendStringInfo(&buf, "EXECUTE PROCEDURE %s(", generate_function_name(trigrec->tgfoid, 0, @@ -594,7 +616,7 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) heap_close(tgrel, AccessShareLock); - PG_RETURN_TEXT_P(string_to_text(buf.data)); + return buf.data; } /* ---------- diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index d1715eccce8..18c14081226 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.549 2009/10/05 19:24:45 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.550 2009/10/09 21:02:56 petere Exp $ * *------------------------------------------------------------------------- */ @@ -4282,7 +4282,8 @@ getTriggers(TableInfo tblinfo[], int numTables) i_tgconstrrelname, i_tgenabled, i_tgdeferrable, - i_tginitdeferred; + i_tginitdeferred, + i_tgdef; int ntups; for (i = 0; i < numTables; i++) @@ -4302,7 +4303,19 @@ getTriggers(TableInfo tblinfo[], int numTables) selectSourceSchema(tbinfo->dobj.namespace->dobj.name); resetPQExpBuffer(query); - if (g_fout->remoteVersion >= 80300) + if (g_fout->remoteVersion >= 80500) + { + appendPQExpBuffer(query, + "SELECT tgname, " + "tgfoid::pg_catalog.regproc AS tgfname, " + "pg_catalog.pg_get_triggerdef(oid, true) AS tgdef, " + "tgenabled, tableoid, oid " + "FROM pg_catalog.pg_trigger t " + "WHERE tgrelid = '%u'::pg_catalog.oid " + "AND tgconstraint = 0", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80300) { /* * We ignore triggers that are tied to a foreign-key constraint @@ -4389,6 +4402,7 @@ getTriggers(TableInfo tblinfo[], int numTables) i_tgenabled = PQfnumber(res, "tgenabled"); i_tgdeferrable = PQfnumber(res, "tgdeferrable"); i_tginitdeferred = PQfnumber(res, "tginitdeferred"); + i_tgdef = PQfnumber(res, "tgdef"); tginfo = (TriggerInfo *) malloc(ntups * sizeof(TriggerInfo)); @@ -4401,38 +4415,47 @@ getTriggers(TableInfo tblinfo[], int numTables) tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname)); tginfo[j].dobj.namespace = tbinfo->dobj.namespace; tginfo[j].tgtable = tbinfo; - tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname)); - tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype)); - tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs)); - tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs)); - tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't'; tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled)); - tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't'; - tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't'; - - if (tginfo[j].tgisconstraint) + if (i_tgdef >= 0) { - tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname)); - tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid)); - if (OidIsValid(tginfo[j].tgconstrrelid)) - { - if (PQgetisnull(res, j, i_tgconstrrelname)) - { - write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n", - tginfo[j].dobj.name, tbinfo->dobj.name, - tginfo[j].tgconstrrelid); - exit_nicely(); - } - tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname)); - } - else - tginfo[j].tgconstrrelname = NULL; + tginfo[j].tgdef = strdup(PQgetvalue(res, j, i_tgdef)); } else { - tginfo[j].tgconstrname = NULL; - tginfo[j].tgconstrrelid = InvalidOid; - tginfo[j].tgconstrrelname = NULL; + tginfo[j].tgdef = NULL; + + tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname)); + tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype)); + tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs)); + tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs)); + tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't'; + tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't'; + tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't'; + + if (tginfo[j].tgisconstraint) + { + tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname)); + tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid)); + if (OidIsValid(tginfo[j].tgconstrrelid)) + { + if (PQgetisnull(res, j, i_tgconstrrelname)) + { + write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n", + tginfo[j].dobj.name, tbinfo->dobj.name, + tginfo[j].tgconstrrelid); + exit_nicely(); + } + tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname)); + } + else + tginfo[j].tgconstrrelname = NULL; + } + else + { + tginfo[j].tgconstrname = NULL; + tginfo[j].tgconstrrelid = InvalidOid; + tginfo[j].tgconstrrelname = NULL; + } } } @@ -11245,113 +11268,120 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) appendPQExpBuffer(delqry, "%s;\n", fmtId(tbinfo->dobj.name)); - if (tginfo->tgisconstraint) + if (tginfo->tgdef) { - appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER "); - appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname)); + appendPQExpBuffer(query, "%s;\n", tginfo->tgdef); } else { - appendPQExpBuffer(query, "CREATE TRIGGER "); - appendPQExpBufferStr(query, fmtId(tginfo->dobj.name)); - } - appendPQExpBuffer(query, "\n "); - - /* Trigger type */ - findx = 0; - if (TRIGGER_FOR_BEFORE(tginfo->tgtype)) - appendPQExpBuffer(query, "BEFORE"); - else - appendPQExpBuffer(query, "AFTER"); - if (TRIGGER_FOR_INSERT(tginfo->tgtype)) - { - appendPQExpBuffer(query, " INSERT"); - findx++; - } - if (TRIGGER_FOR_DELETE(tginfo->tgtype)) - { - if (findx > 0) - appendPQExpBuffer(query, " OR DELETE"); - else - appendPQExpBuffer(query, " DELETE"); - findx++; - } - if (TRIGGER_FOR_UPDATE(tginfo->tgtype)) - { - if (findx > 0) - appendPQExpBuffer(query, " OR UPDATE"); - else - appendPQExpBuffer(query, " UPDATE"); - } - if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype)) - { - if (findx > 0) - appendPQExpBuffer(query, " OR TRUNCATE"); - else - appendPQExpBuffer(query, " TRUNCATE"); - } - appendPQExpBuffer(query, " ON %s\n", - fmtId(tbinfo->dobj.name)); - - if (tginfo->tgisconstraint) - { - if (OidIsValid(tginfo->tgconstrrelid)) + if (tginfo->tgisconstraint) { - /* If we are using regclass, name is already quoted */ - if (g_fout->remoteVersion >= 70300) - appendPQExpBuffer(query, " FROM %s\n ", - tginfo->tgconstrrelname); + appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER "); + appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname)); + } + else + { + appendPQExpBuffer(query, "CREATE TRIGGER "); + appendPQExpBufferStr(query, fmtId(tginfo->dobj.name)); + } + appendPQExpBuffer(query, "\n "); + + /* Trigger type */ + findx = 0; + if (TRIGGER_FOR_BEFORE(tginfo->tgtype)) + appendPQExpBuffer(query, "BEFORE"); + else + appendPQExpBuffer(query, "AFTER"); + if (TRIGGER_FOR_INSERT(tginfo->tgtype)) + { + appendPQExpBuffer(query, " INSERT"); + findx++; + } + if (TRIGGER_FOR_DELETE(tginfo->tgtype)) + { + if (findx > 0) + appendPQExpBuffer(query, " OR DELETE"); else - appendPQExpBuffer(query, " FROM %s\n ", - fmtId(tginfo->tgconstrrelname)); + appendPQExpBuffer(query, " DELETE"); + findx++; } - if (!tginfo->tgdeferrable) - appendPQExpBuffer(query, "NOT "); - appendPQExpBuffer(query, "DEFERRABLE INITIALLY "); - if (tginfo->tginitdeferred) - appendPQExpBuffer(query, "DEFERRED\n"); - else - appendPQExpBuffer(query, "IMMEDIATE\n"); - } - - if (TRIGGER_FOR_ROW(tginfo->tgtype)) - appendPQExpBuffer(query, " FOR EACH ROW\n "); - else - appendPQExpBuffer(query, " FOR EACH STATEMENT\n "); - - /* In 7.3, result of regproc is already quoted */ - if (g_fout->remoteVersion >= 70300) - appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(", - tginfo->tgfname); - else - appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(", - fmtId(tginfo->tgfname)); - - tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs, - &lentgargs); - p = tgargs; - for (findx = 0; findx < tginfo->tgnargs; findx++) - { - /* find the embedded null that terminates this trigger argument */ - size_t tlen = strlen(p); - - if (p + tlen >= tgargs + lentgargs) + if (TRIGGER_FOR_UPDATE(tginfo->tgtype)) { - /* hm, not found before end of bytea value... */ - write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n", - tginfo->tgargs, - tginfo->dobj.name, - tbinfo->dobj.name); - exit_nicely(); + if (findx > 0) + appendPQExpBuffer(query, " OR UPDATE"); + else + appendPQExpBuffer(query, " UPDATE"); + } + if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype)) + { + if (findx > 0) + appendPQExpBuffer(query, " OR TRUNCATE"); + else + appendPQExpBuffer(query, " TRUNCATE"); + } + appendPQExpBuffer(query, " ON %s\n", + fmtId(tbinfo->dobj.name)); + + if (tginfo->tgisconstraint) + { + if (OidIsValid(tginfo->tgconstrrelid)) + { + /* If we are using regclass, name is already quoted */ + if (g_fout->remoteVersion >= 70300) + appendPQExpBuffer(query, " FROM %s\n ", + tginfo->tgconstrrelname); + else + appendPQExpBuffer(query, " FROM %s\n ", + fmtId(tginfo->tgconstrrelname)); + } + if (!tginfo->tgdeferrable) + appendPQExpBuffer(query, "NOT "); + appendPQExpBuffer(query, "DEFERRABLE INITIALLY "); + if (tginfo->tginitdeferred) + appendPQExpBuffer(query, "DEFERRED\n"); + else + appendPQExpBuffer(query, "IMMEDIATE\n"); } - if (findx > 0) - appendPQExpBuffer(query, ", "); - appendStringLiteralAH(query, p, fout); - p += tlen + 1; + if (TRIGGER_FOR_ROW(tginfo->tgtype)) + appendPQExpBuffer(query, " FOR EACH ROW\n "); + else + appendPQExpBuffer(query, " FOR EACH STATEMENT\n "); + + /* In 7.3, result of regproc is already quoted */ + if (g_fout->remoteVersion >= 70300) + appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(", + tginfo->tgfname); + else + appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(", + fmtId(tginfo->tgfname)); + + tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs, + &lentgargs); + p = tgargs; + for (findx = 0; findx < tginfo->tgnargs; findx++) + { + /* find the embedded null that terminates this trigger argument */ + size_t tlen = strlen(p); + + if (p + tlen >= tgargs + lentgargs) + { + /* hm, not found before end of bytea value... */ + write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n", + tginfo->tgargs, + tginfo->dobj.name, + tbinfo->dobj.name); + exit_nicely(); + } + + if (findx > 0) + appendPQExpBuffer(query, ", "); + appendStringLiteralAH(query, p, fout); + p += tlen + 1; + } + free(tgargs); + appendPQExpBuffer(query, ");\n"); } - free(tgargs); - appendPQExpBuffer(query, ");\n"); if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O') { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 5b2c3d1e6bf..b6ee4bb2fc5 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.158 2009/10/05 19:24:46 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.159 2009/10/09 21:02:56 petere Exp $ * *------------------------------------------------------------------------- */ @@ -328,6 +328,7 @@ typedef struct _triggerInfo char tgenabled; bool tgdeferrable; bool tginitdeferred; + char *tgdef; } TriggerInfo; /* diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 8959997ea96..d7c24791584 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.543 2009/10/08 02:39:23 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.544 2009/10/09 21:02:56 petere Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200910072 +#define CATALOG_VERSION_NO 200910101 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 0ea894854b7..cd249d841b8 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.551 2009/09/26 22:42:02 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.552 2009/10/09 21:02:56 petere Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -4083,6 +4083,8 @@ DATA(insert OID = 2599 ( pg_timezone_abbrevs PGNSP PGUID 12 1 1000 0 f f f t t DESCR("get the available time zone abbreviations"); DATA(insert OID = 2856 ( pg_timezone_names PGNSP PGUID 12 1 1000 0 f f f t t s 0 0 2249 "" "{25,25,1186,16}" "{o,o,o,o}" "{name,abbrev,utc_offset,is_dst}" _null_ pg_timezone_names _null_ _null_ _null_ )); DESCR("get the available time zone names"); +DATA(insert OID = 2730 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 f f f t f s 2 0 25 "26 16" _null_ _null_ _null_ _null_ pg_get_triggerdef_ext _null_ _null_ _null_ )); +DESCR("trigger description with pretty-print option"); /* non-persistent series generator */ DATA(insert OID = 1066 ( generate_series PGNSP PGUID 12 1 1000 0 f f f t t i 3 0 23 "23 23 23" _null_ _null_ _null_ _null_ generate_series_step_int4 _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index bcf027ccee9..d77f4b6ecd9 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.339 2009/09/09 19:00:09 petere Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.340 2009/10/09 21:02:56 petere Exp $ * *------------------------------------------------------------------------- */ @@ -590,6 +590,7 @@ extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern char *pg_get_indexdef_string(Oid indexrelid); extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); +extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_constraintdef(PG_FUNCTION_ARGS); extern Datum pg_get_constraintdef_ext(PG_FUNCTION_ARGS); extern char *pg_get_constraintdef_string(Oid constraintId);