diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 9a9f9b55bef5378e34f297aae5fde5b4b5e4c03e..31db6521c5efc270a99a12f960292046faf94cda 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.145 2007/02/14 01:58:55 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.146 2007/03/19 23:38:28 wieck Exp $ --> <!-- Documentation of the system catalogs, directed toward PostgreSQL developers --> @@ -3639,6 +3639,20 @@ </entry> </row> + <row> + <entry><structfield>ev_enabled</structfield></entry> + <entry><type>char</type></entry> + <entry></entry> + <entry> + Controls in which <xref linkend="guc-session-replication-role"> modes + the rule fires. + <literal>O</> = rule fires in <quote>origin</> and <quote>local</> modes, + <literal>D</> = rule is disabled, + <literal>R</> = rule fires in <quote>replica</> mode, + <literal>A</> = rule fires always. + </entry> + </row> + <row> <entry><structfield>is_instead</structfield></entry> <entry><type>bool</type></entry> @@ -4178,9 +4192,16 @@ <row> <entry><structfield>tgenabled</structfield></entry> - <entry><type>bool</type></entry> + <entry><type>char</type></entry> <entry></entry> - <entry>True if trigger is enabled</entry> + <entry> + Controls in which <xref linkend="guc-session-replication-role"> modes + the trigger fires. + <literal>O</> = trigger fires in <quote>origin</> and <quote>local</> modes, + <literal>D</> = trigger is disabled, + <literal>R</> = trigger fires in <quote>replica</> mode, + <literal>A</> = trigger fires always. + </entry> </row> <row> diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 17504b3d5e8a00aa358303284b92dd89ad6da16d..1fe39db2af7db0cad0759d29d6dfca043f4dfca9 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.115 2007/03/06 02:06:12 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.116 2007/03/19 23:38:28 wieck Exp $ --> <chapter Id="runtime-config"> <title>Server Configuration</title> @@ -3523,6 +3523,23 @@ SELECT * FROM parent WHERE key = 2400; </listitem> </varlistentry> + <varlistentry id="guc-session-replication-role" xreflabel="session_replication_role"> + <term><varname>session_replication_role</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>session_replication_role</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Controls the trigger and rule firing for the current session. + See <xref linkend="sql-altertable"> for the different options to + enable or disable triggers and rules. Setting the variable requires + superuser privilege and can only be done before any query plans have + been cached. Possible values are <literal>origin</>, + <literal>replica</> and <literal>local</>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-vacuum-freeze-min-age" xreflabel="vacuum_freeze_min_age"> <term><varname>vacuum_freeze_min_age</varname> (<type>integer</type>)</term> <indexterm> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 6ff289b21a40910e8f86c63d177f5a5aa49b1f14..9b34c4fb9934f3f60d2f99a963785e0efd79369a 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.94 2007/02/01 00:28:18 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.95 2007/03/19 23:38:29 wieck Exp $ PostgreSQL documentation --> @@ -43,6 +43,12 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ] DISABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ] ENABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ] + ENABLE REPLICA TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> + ENABLE ALWAYS TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> + DISABLE RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable> + ENABLE RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable> + ENABLE REPLICA RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable> + ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable> CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable> SET WITHOUT CLUSTER SET WITHOUT OIDS @@ -193,10 +199,10 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: </varlistentry> <varlistentry> - <term><literal>DISABLE</literal>/<literal>ENABLE TRIGGER</literal></term> + <term><literal>DISABLE</literal>/<literal>ENABLE [ REPLICA | ALWAYS ] TRIGGER</literal></term> <listitem> <para> - These forms disable or enable trigger(s) belonging to the table. + These forms configure the firing of trigger(s) belonging to the table. A disabled trigger is still known to the system, but is not executed when its triggering event occurs. For a deferred trigger, the enable status is checked when the event occurs, not when the trigger function @@ -207,6 +213,27 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: requires superuser privileges; it should be done with caution since of course the integrity of the constraint cannot be guaranteed if the triggers are not executed. + The trigger firing mechanism is also affected by the configuration + variable <xref linkend="guc-session-replication-role">. Simply ENABLEd + triggers will fire when the replication role is <quote>origin</> + (the default) or <quote>local</>. Triggers configured ENABLE REPLICA + will only fire if the session is in <quote>replica</> mode and triggers + configured ENABLE ALWAYS will fire regardless of the current replication + mode. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>DISABLE</literal>/<literal>ENABLE [ REPLICA | ALWAYS ] RULE</literal></term> + <listitem> + <para> + These forms configure the firing of rewrite rules belonging to the table. + A disabled rule is still known to the system, but is not applied + during query rewriting. The semantics are as for disabled/enabled + triggers. This configuration is ignored for ON SELECT rules, which + are always applied in order to keep views working even if the current + session is in a non-default replication role. </para> </listitem> </varlistentry> diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ddc62086f51a8c6928d646715005a34d06878c5d..25e53a3dc43d10861fcff64e62a1e92c1da5b727 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.217 2007/03/13 00:33:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.218 2007/03/19 23:38:29 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ #include "parser/parse_relation.h" #include "parser/parse_type.h" #include "parser/parser.h" +#include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "storage/smgr.h" #include "utils/acl.h" @@ -253,7 +254,9 @@ static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace); static void ATExecSetRelOptions(Relation rel, List *defList, bool isReset); static void ATExecEnableDisableTrigger(Relation rel, char *trigname, - bool enable, bool skip_system); + char fires_when, bool skip_system); +static void ATExecEnableDisableRule(Relation rel, char *rulename, + char fires_when); static void ATExecAddInherit(Relation rel, RangeVar *parent); static void ATExecDropInherit(Relation rel, RangeVar *parent); static void copy_relation_data(Relation rel, SMgrRelation dst); @@ -1955,11 +1958,17 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_MISC; break; case AT_EnableTrig: /* ENABLE TRIGGER variants */ + case AT_EnableAlwaysTrig: + case AT_EnableReplicaTrig: case AT_EnableTrigAll: case AT_EnableTrigUser: case AT_DisableTrig: /* DISABLE TRIGGER variants */ case AT_DisableTrigAll: case AT_DisableTrigUser: + case AT_EnableRule: /* ENABLE/DISABLE RULE variants */ + case AT_EnableAlwaysRule: + case AT_EnableReplicaRule: + case AT_DisableRule: case AT_AddInherit: /* INHERIT / NO INHERIT */ case AT_DropInherit: ATSimplePermissions(rel, false); @@ -2127,24 +2136,57 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd) case AT_ResetRelOptions: /* RESET (...) */ ATExecSetRelOptions(rel, (List *) cmd->def, true); break; - case AT_EnableTrig: /* ENABLE TRIGGER name */ - ATExecEnableDisableTrigger(rel, cmd->name, true, false); + + case AT_EnableTrig: /* ENABLE TRIGGER name */ + ATExecEnableDisableTrigger(rel, cmd->name, + TRIGGER_FIRES_ON_ORIGIN, false); + break; + case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */ + ATExecEnableDisableTrigger(rel, cmd->name, + TRIGGER_FIRES_ALWAYS, false); + break; + case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */ + ATExecEnableDisableTrigger(rel, cmd->name, + TRIGGER_FIRES_ON_REPLICA, false); break; case AT_DisableTrig: /* DISABLE TRIGGER name */ - ATExecEnableDisableTrigger(rel, cmd->name, false, false); + ATExecEnableDisableTrigger(rel, cmd->name, + TRIGGER_DISABLED, false); break; case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */ - ATExecEnableDisableTrigger(rel, NULL, true, false); + ATExecEnableDisableTrigger(rel, NULL, + TRIGGER_FIRES_ON_ORIGIN, false); break; case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */ - ATExecEnableDisableTrigger(rel, NULL, false, false); + ATExecEnableDisableTrigger(rel, NULL, + TRIGGER_DISABLED, false); break; case AT_EnableTrigUser: /* ENABLE TRIGGER USER */ - ATExecEnableDisableTrigger(rel, NULL, true, true); + ATExecEnableDisableTrigger(rel, NULL, + TRIGGER_FIRES_ON_ORIGIN, true); break; case AT_DisableTrigUser: /* DISABLE TRIGGER USER */ - ATExecEnableDisableTrigger(rel, NULL, false, true); + ATExecEnableDisableTrigger(rel, NULL, + TRIGGER_DISABLED, true); + break; + + case AT_EnableRule: /* ENABLE RULE name */ + ATExecEnableDisableRule(rel, cmd->name, + RULE_FIRES_ON_ORIGIN); + break; + case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */ + ATExecEnableDisableRule(rel, cmd->name, + RULE_FIRES_ALWAYS); + break; + case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */ + ATExecEnableDisableRule(rel, cmd->name, + RULE_FIRES_ON_REPLICA); + break; + case AT_DisableRule: /* DISABLE RULE name */ + ATExecEnableDisableRule(rel, cmd->name, + RULE_DISABLED); break; + case AT_AddInherit: ATExecAddInherit(rel, (RangeVar *) cmd->def); break; @@ -4380,7 +4422,7 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint, MemSet(&trig, 0, sizeof(trig)); trig.tgoid = InvalidOid; trig.tgname = fkconstraint->constr_name; - trig.tgenabled = TRUE; + trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN; trig.tgisconstraint = TRUE; trig.tgconstrrelid = RelationGetRelid(pkrel); trig.tgconstraint = constraintOid; @@ -5877,9 +5919,21 @@ copy_relation_data(Relation rel, SMgrRelation dst) */ static void ATExecEnableDisableTrigger(Relation rel, char *trigname, - bool enable, bool skip_system) + char fires_when, bool skip_system) +{ + EnableDisableTrigger(rel, trigname, fires_when, skip_system); +} + +/* + * ALTER TABLE ENABLE/DISABLE RULE + * + * We just pass this off to rewriteDefine.c. + */ +static void +ATExecEnableDisableRule(Relation rel, char *trigname, + char fires_when) { - EnableDisableTrigger(rel, trigname, enable, skip_system); + EnableDisableRule(rel, trigname, fires_when); } /* diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index c08525c2e040ffb41b44ce4fa9a060b4713c2cbe..e2dadb7a7e283c9c34d11d44414d6974cb4fd06c 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.213 2007/02/14 01:58:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.214 2007/03/19 23:38:29 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -54,6 +54,13 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, HeapTuple oldtup, HeapTuple newtup); +/* + * SessionReplicationRole - + * + * Global variable that controls the trigger firing behaviour based + * on pg_trigger.tgenabled. This is maintained from misc/guc.c. + */ +int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN; /* * Create a trigger. Returns the OID of the created trigger. @@ -270,7 +277,7 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid) CStringGetDatum(trigname)); values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid); values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); - values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true); + values[Anum_pg_trigger_tgenabled - 1] = CharGetDatum(TRIGGER_FIRES_ON_ORIGIN); values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint); values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein, CStringGetDatum(constrname)); @@ -701,11 +708,11 @@ renametrig(Oid relid, * EnableDisableTrigger() * * Called by ALTER TABLE ENABLE/DISABLE TRIGGER - * to change 'tgenabled' flag for the specified trigger(s) + * to change 'tgenabled' field for the specified trigger(s) * * rel: relation to process (caller must hold suitable lock on it) * tgname: trigger to process, or NULL to scan all triggers - * enable: new value for tgenabled flag + * enable: new value for tgenabled field * skip_system: if true, skip "system" triggers (constraint triggers) * * Caller should have checked permissions for the table; here we also @@ -714,7 +721,7 @@ renametrig(Oid relid, */ void EnableDisableTrigger(Relation rel, const char *tgname, - bool enable, bool skip_system) + char fires_when, bool skip_system) { Relation tgrel; int nkeys; @@ -765,13 +772,13 @@ EnableDisableTrigger(Relation rel, const char *tgname, found = true; - if (oldtrig->tgenabled != enable) + if (oldtrig->tgenabled != fires_when) { /* need to change this one ... make a copy to scribble on */ HeapTuple newtup = heap_copytuple(tuple); Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup); - newtrig->tgenabled = enable; + newtrig->tgenabled = fires_when; simple_heap_update(tgrel, &newtup->t_self, newtup); @@ -1333,8 +1340,18 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo) Trigger *trigger = &trigdesc->triggers[tgindx[i]]; HeapTuple newtuple; - if (!trigger->tgenabled) - continue; + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, tgindx[i], @@ -1382,8 +1399,18 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; - if (!trigger->tgenabled) - continue; + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } LocTriggerData.tg_trigtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; @@ -1444,8 +1471,18 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo) Trigger *trigger = &trigdesc->triggers[tgindx[i]]; HeapTuple newtuple; - if (!trigger->tgenabled) - continue; + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, tgindx[i], @@ -1500,8 +1537,18 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; - if (!trigger->tgenabled) - continue; + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; @@ -1575,8 +1622,18 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) Trigger *trigger = &trigdesc->triggers[tgindx[i]]; HeapTuple newtuple; - if (!trigger->tgenabled) - continue; + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, tgindx[i], @@ -1636,8 +1693,18 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; - if (!trigger->tgenabled) - continue; + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; @@ -3267,8 +3334,18 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, Trigger *trigger = &trigdesc->triggers[tgindx[i]]; /* Ignore disabled triggers */ - if (!trigger->tgenabled) - continue; + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } /* * If this is an UPDATE of a PK table or FK table that does not change diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 22d03f0b23461227ca331bbaa9220b5a4eb3fa8f..75404716d5a1f82a6a70d46188484e22fbe88ab4 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.582 2007/03/17 19:27:12 meskes Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.583 2007/03/19 23:38:29 wieck Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -365,7 +365,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) /* ordinary key words in alphabetical order */ %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER - AGGREGATE ALL ALSO ALTER ANALYSE ANALYZE AND ANY ARRAY AS ASC + AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT @@ -422,8 +422,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) QUOTE READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME - REPEATABLE REPLACE RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT - ROLE ROLLBACK ROW ROWS RULE + REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURNING RETURNS REVOKE + RIGHT ROLE ROLLBACK ROW ROWS RULE SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE @@ -1480,6 +1480,22 @@ alter_table_cmd: n->name = $3; $$ = (Node *)n; } + /* ALTER TABLE <name> ENABLE ALWAYS TRIGGER <trig> */ + | ENABLE_P ALWAYS TRIGGER name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableAlwaysTrig; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE REPLICA TRIGGER <trig> */ + | ENABLE_P REPLICA TRIGGER name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableReplicaTrig; + n->name = $4; + $$ = (Node *)n; + } /* ALTER TABLE <name> ENABLE TRIGGER ALL */ | ENABLE_P TRIGGER ALL { @@ -1516,6 +1532,38 @@ alter_table_cmd: n->subtype = AT_DisableTrigUser; $$ = (Node *)n; } + /* ALTER TABLE <name> ENABLE RULE <rule> */ + | ENABLE_P RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableRule; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE ALWAYS RULE <rule> */ + | ENABLE_P ALWAYS RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableAlwaysRule; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ENABLE REPLICA RULE <rule> */ + | ENABLE_P REPLICA RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableReplicaRule; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> DISABLE RULE <rule> */ + | DISABLE_P RULE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DisableRule; + n->name = $3; + $$ = (Node *)n; + } /* ALTER TABLE <name> INHERIT <parent> */ | INHERIT qualified_name { @@ -8651,6 +8699,7 @@ unreserved_keyword: | AGGREGATE | ALSO | ALTER + | ALWAYS | ASSERTION | ASSIGNMENT | AT @@ -8796,6 +8845,7 @@ unreserved_keyword: | RENAME | REPEATABLE | REPLACE + | REPLICA | RESET | RESTART | RESTRICT diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 368f3e06947972e98cc00c34508431f6b6b40b3f..9be91e7e6e62a43cb0b176d491669a56c93568c8 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.184 2007/01/25 11:53:51 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.185 2007/03/19 23:38:29 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -42,6 +42,7 @@ static const ScanKeyword ScanKeywords[] = { {"all", ALL}, {"also", ALSO}, {"alter", ALTER}, + {"always", ALWAYS}, {"analyse", ANALYSE}, /* British spelling */ {"analyze", ANALYZE}, {"and", AND}, @@ -289,6 +290,7 @@ static const ScanKeyword ScanKeywords[] = { {"rename", RENAME}, {"repeatable", REPEATABLE}, {"replace", REPLACE}, + {"replica", REPLICA}, {"reset", RESET}, {"restart", RESTART}, {"restrict", RESTRICT}, diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 64a2a96f0e9a8d7b1056c183f62fdeabaa698848..864b00f1e73554c208d4e37afb061fa9c21aca37 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.118 2007/03/13 00:33:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.119 2007/03/19 23:38:29 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -30,6 +30,7 @@ #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" +#include "utils/inval.h" static void checkRuleResultList(List *targetList, TupleDesc resultDesc, @@ -79,6 +80,7 @@ InsertRule(char *rulname, values[i++] = ObjectIdGetDatum(eventrel_oid); /* ev_class */ values[i++] = Int16GetDatum(evslot_index); /* ev_attr */ values[i++] = CharGetDatum(evtype + '0'); /* ev_type */ + values[i++] = CharGetDatum(RULE_FIRES_ON_ORIGIN); /* ev_enabled */ values[i++] = BoolGetDatum(evinstead); /* is_instead */ values[i++] = DirectFunctionCall1(textin, CStringGetDatum(evqual)); /* ev_qual */ values[i++] = DirectFunctionCall1(textin, CStringGetDatum(actiontree)); /* ev_action */ @@ -628,6 +630,72 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) } +/* + * Change the firing semantics of an existing rule. + * + */ +void +EnableDisableRule(Relation rel, const char *rulename, + char fires_when) +{ + Relation pg_rewrite_desc; + Oid owningRel = RelationGetRelid(rel); + Oid eventRelationOid; + HeapTuple ruletup; + bool changed = false; + + /* + * Find the rule tuple to change. + */ + pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock); + ruletup = SearchSysCacheCopy(RULERELNAME, + ObjectIdGetDatum(owningRel), + PointerGetDatum(rulename), + 0, 0); + if (!HeapTupleIsValid(ruletup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("rule \"%s\" for relation \"%s\" does not exist", + rulename, get_rel_name(owningRel)))); + + /* + * Verify that the user has appropriate permissions. + */ + eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_class; + Assert(eventRelationOid == owningRel); + if (!pg_class_ownercheck(eventRelationOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(eventRelationOid)); + + /* + * Change ev_enabled if it is different from the desired new state. + */ + if (DatumGetChar(((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_enabled) != + fires_when) + { + ((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_enabled = + CharGetDatum(fires_when); + simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup); + + /* keep system catalog indexes current */ + CatalogUpdateIndexes(pg_rewrite_desc, ruletup); + + changed = true; + } + + heap_freetuple(ruletup); + heap_close(pg_rewrite_desc, RowExclusiveLock); + + /* + * If we changed anything, broadcast a SI inval message to force each + * backend (including our own!) to rebuild relation's relcache entry. + * Otherwise they will fail to apply the change promptly. + */ + if (changed) + CacheInvalidateRelcache(rel); +} + + /* * Rename an existing rewrite rule. * diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index e5a1d4c74725f172e826eb917f683fef9a9d8693..cd1cb54e9af35d5b69658e89e4276cd79af6b8c1 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.172 2007/03/17 00:11:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.173 2007/03/19 23:38:29 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -21,10 +21,12 @@ #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parsetree.h" +#include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +#include "commands/trigger.h" /* We use a list of these to detect recursion in RewriteQuery */ @@ -1035,6 +1037,29 @@ matchLocks(CmdType event, { RewriteRule *oneLock = rulelocks->rules[i]; + /* + * Suppress ON INSERT/UPDATE/DELETE rules that are disabled + * or configured to not fire during the current sessions + * replication role. ON SELECT rules will always be applied + * in order to keep views working even in LOCAL or REPLICA + * role. + */ + if (oneLock->event != CMD_SELECT) + { + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (oneLock->enabled == RULE_FIRES_ON_ORIGIN || + oneLock->enabled == RULE_DISABLED) + continue; + } + else /* ORIGIN or LOCAL ROLE */ + { + if (oneLock->enabled == RULE_FIRES_ON_REPLICA || + oneLock->enabled == RULE_DISABLED) + continue; + } + } + if (oneLock->event == event) { if (parsetree->commandType != CMD_SELECT || diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 61a576d35d981928d9baade9ec64955729cdb8f9..4648b05803e13c4fb5065247a20f5f0b8e964260 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -33,7 +33,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.2 2007/03/15 23:12:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.3 2007/03/19 23:38:29 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -860,3 +860,14 @@ InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context) if (relid == context->inval_relid) context->plan->dead = true; } + +/* + * HaveCachedPlans + * Check if the plancache has stored any plans at all. + */ +bool +HaveCachedPlans(void) +{ + return (cached_plans_list != NIL); +} + diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 4ce955917a54a958001945faf2c2cc6b62d614e9..91b7f146b43abd96b9a8d1dcf2d9e0e169908856 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.257 2007/03/03 20:08:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.258 2007/03/19 23:38:29 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -651,6 +651,7 @@ RelationBuildRuleLock(Relation relation) rule->event = rewrite_form->ev_type - '0'; rule->attrno = rewrite_form->ev_attr; + rule->enabled = rewrite_form->ev_enabled; rule->isInstead = rewrite_form->is_instead; /* diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 8088432bac869555cd8373183ed3a97e623bcdd6..f921c75a60b6997e1acd18f131b10b70432e5944 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.382 2007/03/13 14:32:25 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.383 2007/03/19 23:38:30 wieck Exp $ * *-------------------------------------------------------------------- */ @@ -34,6 +34,7 @@ #include "commands/async.h" #include "commands/vacuum.h" #include "commands/variable.h" +#include "commands/trigger.h" #include "funcapi.h" #include "libpq/auth.h" #include "libpq/pqformat.h" @@ -59,6 +60,7 @@ #include "utils/guc_tables.h" #include "utils/memutils.h" #include "utils/pg_locale.h" +#include "utils/plancache.h" #include "utils/ps_status.h" #include "utils/tzparser.h" #include "utils/xml.h" @@ -124,6 +126,8 @@ static const char *assign_syslog_ident(const char *ident, static const char *assign_defaultxactisolevel(const char *newval, bool doit, GucSource source); +static const char *assign_session_replication_role(const char *newval, bool doit, + GucSource source); static const char *assign_log_min_messages(const char *newval, bool doit, GucSource source); static const char *assign_client_min_messages(const char *newval, @@ -226,6 +230,7 @@ static char *backslash_quote_string; static char *client_encoding_string; static char *datestyle_string; static char *default_iso_level_string; +static char *session_replication_role_string; static char *locale_collate; static char *locale_ctype; static char *regex_flavor_string; @@ -1928,6 +1933,16 @@ static struct config_string ConfigureNamesString[] = "read committed", assign_defaultxactisolevel, NULL }, + { + {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the sessions behaviour for triggers and rewrite rules."), + gettext_noop("Each session can be either" + " \"origin\", \"replica\" or \"local\".") + }, + &session_replication_role_string, + "origin", assign_session_replication_role, NULL + }, + { {"dynamic_library_path", PGC_SUSET, CLIENT_CONN_OTHER, gettext_noop("Sets the path for dynamically loadable modules."), @@ -6115,6 +6130,33 @@ assign_defaultxactisolevel(const char *newval, bool doit, GucSource source) return newval; } +static const char * +assign_session_replication_role(const char *newval, bool doit, GucSource source) +{ + if (HaveCachedPlans()) + elog(ERROR, "session_replication_role cannot be changed " + "after prepared plans have been cached"); + + if (pg_strcasecmp(newval, "origin") == 0) + { + if (doit) + SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN; + } + else if (pg_strcasecmp(newval, "replica") == 0) + { + if (doit) + SessionReplicationRole = SESSION_REPLICATION_ROLE_REPLICA; + } + else if (pg_strcasecmp(newval, "local") == 0) + { + if (doit) + SessionReplicationRole = SESSION_REPLICATION_ROLE_LOCAL; + } + else + return NULL; + return newval; +} + static const char * assign_log_min_messages(const char *newval, bool doit, GucSource source) diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index cf2dfdc099e07211e7bc1e48be80c9ce924ce5e2..22f9685bbdce04a4995c988cb0a6976f2191a7a0 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -408,6 +408,7 @@ #default_transaction_isolation = 'read committed' #default_transaction_read_only = off #statement_timeout = 0 # 0 is disabled +#session_replication_role = "origin" #vacuum_freeze_min_age = 100000000 #xmlbinary = 'base64' #xmloption = 'content' diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index eef9a4c875a13a7861fc634124283a09348e0a30..90e78c36d15ff50396ebdfbc33aa01a7d4cd5549 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.460 2007/02/14 01:58:57 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.461 2007/03/19 23:38:30 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -3699,15 +3699,26 @@ getRules(int *numRules) int i_ruletable; int i_ev_type; int i_is_instead; + int i_ev_enabled; /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); - if (g_fout->remoteVersion >= 70100) + if (g_fout->remoteVersion >= 80300) { appendPQExpBuffer(query, "SELECT " "tableoid, oid, rulename, " - "ev_class as ruletable, ev_type, is_instead " + "ev_class as ruletable, ev_type, is_instead, " + "ev_enabled " + "FROM pg_rewrite " + "ORDER BY oid"); + } + else if (g_fout->remoteVersion >= 70100) + { + appendPQExpBuffer(query, "SELECT " + "tableoid, oid, rulename, " + "ev_class as ruletable, ev_type, is_instead, " + "'O'::char as ev_enabled " "FROM pg_rewrite " "ORDER BY oid"); } @@ -3716,7 +3727,8 @@ getRules(int *numRules) appendPQExpBuffer(query, "SELECT " "(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, " "oid, rulename, " - "ev_class as ruletable, ev_type, is_instead " + "ev_class as ruletable, ev_type, is_instead, " + "'O'::char as ev_enabled " "FROM pg_rewrite " "ORDER BY oid"); } @@ -3736,6 +3748,7 @@ getRules(int *numRules) i_ruletable = PQfnumber(res, "ruletable"); i_ev_type = PQfnumber(res, "ev_type"); i_is_instead = PQfnumber(res, "is_instead"); + i_ev_enabled = PQfnumber(res, "ev_enabled"); for (i = 0; i < ntups; i++) { @@ -3759,6 +3772,7 @@ getRules(int *numRules) ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump; ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type)); ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't'; + ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled)); if (ruleinfo[i].ruletable) { /* @@ -3956,7 +3970,7 @@ getTriggers(TableInfo tblinfo[], int numTables) 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)) == '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'; @@ -8824,11 +8838,27 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) } appendPQExpBuffer(query, ");\n"); - if (!tginfo->tgenabled) + if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O') { appendPQExpBuffer(query, "\nALTER TABLE %s ", fmtId(tbinfo->dobj.name)); - appendPQExpBuffer(query, "DISABLE TRIGGER %s;\n", + switch (tginfo->tgenabled) + { + case 'D': + case 'f': + appendPQExpBuffer(query, "DISABLE"); + break; + case 'A': + appendPQExpBuffer(query, "ENABLE ALWAYS"); + break; + case 'R': + appendPQExpBuffer(query, "ENABLE REPLICA"); + break; + default: + appendPQExpBuffer(query, "ENABLE"); + break; + } + appendPQExpBuffer(query, " TRIGGER %s;\n", fmtId(tginfo->dobj.name)); } @@ -8914,6 +8944,33 @@ dumpRule(Archive *fout, RuleInfo *rinfo) printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0)); + /* + * Add the command to alter the rules replication firing semantics + * if it differs from the default. + */ + if (rinfo->ev_enabled != 'O') + { + appendPQExpBuffer(cmd, "ALTER TABLE %s.", + fmtId(tbinfo->dobj.namespace->dobj.name)); + appendPQExpBuffer(cmd, "%s ", + fmtId(tbinfo->dobj.name)); + switch (rinfo->ev_enabled) + { + case 'A': + appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n", + fmtId(rinfo->dobj.name)); + break; + case 'R': + appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n", + fmtId(rinfo->dobj.name)); + break; + case 'D': + appendPQExpBuffer(cmd, "DISABLE RULE %s;\n", + fmtId(rinfo->dobj.name)); + break; + } + } + /* * DROP must be fully qualified in case same name appears in pg_catalog */ diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index b584ab1fe017ef9c88a4d7f442966d1428ef0c67..8694376b5299c42a29a5d04e3effb472fa2cffe5 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-2007, 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.133 2007/02/19 15:05:06 mha Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.134 2007/03/19 23:38:30 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -314,6 +314,7 @@ typedef struct _ruleInfo TableInfo *ruletable; /* link to table the rule is for */ char ev_type; bool is_instead; + char ev_enabled; bool separate; /* TRUE if must dump as separate item */ /* separate is always true for non-ON SELECT rules */ } RuleInfo; @@ -330,7 +331,7 @@ typedef struct _triggerInfo char *tgconstrname; Oid tgconstrrelid; char *tgconstrrelname; - bool tgenabled; + char tgenabled; bool tgdeferrable; bool tginitdeferred; } TriggerInfo; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 5dfaf9367a1da18d127b6bdd3323a3ccac21531e..13d3be678e84f6a22c9d5fb2c30004321ff4d6cd 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.154 2007/03/18 16:50:44 neilc Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.155 2007/03/19 23:38:31 wieck Exp $ */ #include "postgres_fe.h" #include "describe.h" @@ -1055,14 +1055,12 @@ describeOneTableDetails(const char *schemaname, *result3 = NULL, *result4 = NULL, *result5 = NULL, - *result6 = NULL, - *result7 = NULL; + *result6 = NULL; int check_count = 0, index_count = 0, foreignkey_count = 0, rule_count = 0, trigger_count = 0, - disabled_trigger_count = 0, inherits_count = 0; int count_footers = 0; @@ -1105,11 +1103,24 @@ describeOneTableDetails(const char *schemaname, /* count rules */ if (tableinfo.hasrules) { - printfPQExpBuffer(&buf, - "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n" + if (pset.sversion < 80300) + { + printfPQExpBuffer(&buf, + "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " + "'O'::char AS ev_enabled\n" "FROM pg_catalog.pg_rewrite r\n" "WHERE r.ev_class = '%s' ORDER BY 1", oid); + } + else + { + printfPQExpBuffer(&buf, + "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " + "ev_enabled\n" + "FROM pg_catalog.pg_rewrite r\n" + "WHERE r.ev_class = '%s' ORDER BY 1", + oid); + } result3 = PSQLexec(buf.data, false); if (!result3) { @@ -1125,10 +1136,10 @@ describeOneTableDetails(const char *schemaname, if (tableinfo.triggers) { printfPQExpBuffer(&buf, - "SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid)\n" + "SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid), " + "t.tgenabled\n" "FROM pg_catalog.pg_trigger t\n" "WHERE t.tgrelid = '%s' " - "AND t.tgenabled " "AND t.tgconstraint = 0\n" "ORDER BY 1", oid); @@ -1142,27 +1153,6 @@ describeOneTableDetails(const char *schemaname, } else trigger_count = PQntuples(result4); - - /* acquire disabled triggers as a separate list */ - printfPQExpBuffer(&buf, - "SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid)\n" - "FROM pg_catalog.pg_trigger t\n" - "WHERE t.tgrelid = '%s' " - "AND NOT t.tgenabled " - "AND t.tgconstraint = 0\n" - "ORDER BY 1", - oid); - result7 = PSQLexec(buf.data, false); - if (!result7) - { - PQclear(result1); - PQclear(result2); - PQclear(result3); - PQclear(result4); - goto error_return; - } - else - disabled_trigger_count = PQntuples(result7); } /* count foreign-key constraints (there are none if no triggers) */ @@ -1181,7 +1171,6 @@ describeOneTableDetails(const char *schemaname, PQclear(result2); PQclear(result3); PQclear(result4); - PQclear(result7); goto error_return; } else @@ -1199,7 +1188,6 @@ describeOneTableDetails(const char *schemaname, PQclear(result3); PQclear(result4); PQclear(result5); - PQclear(result7); goto error_return; } else @@ -1297,63 +1285,143 @@ describeOneTableDetails(const char *schemaname, /* print rules */ if (rule_count > 0) { - printfPQExpBuffer(&buf, _("Rules:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < rule_count; i++) - { - const char *ruledef; - - /* Everything after "CREATE RULE" is echoed verbatim */ - ruledef = PQgetvalue(result3, i, 1); - ruledef += 12; + bool have_heading; + int category; - printfPQExpBuffer(&buf, " %s", ruledef); + for (category = 0; category < 4; category++) + { + have_heading = false; - footers[count_footers++] = pg_strdup(buf.data); + for (i = 0; i < rule_count; i++) + { + const char *ruledef; + bool list_rule = false; + + switch (category) + { + case 0: + if (*PQgetvalue(result3, i, 2) == 'O') + list_rule = true; + break; + case 1: + if (*PQgetvalue(result3, i, 2) == 'D') + list_rule = true; + break; + case 2: + if (*PQgetvalue(result3, i, 2) == 'A') + list_rule = true; + break; + case 3: + if (*PQgetvalue(result3, i, 2) == 'R') + list_rule = true; + break; + } + if (!list_rule) + continue; + + if (!have_heading) + { + switch (category) + { + case 0: + printfPQExpBuffer(&buf, _("Rules:")); + break; + case 1: + printfPQExpBuffer(&buf, _("Disabled Rules:")); + break; + case 2: + printfPQExpBuffer(&buf, _("Rules firing always:")); + break; + case 3: + printfPQExpBuffer(&buf, _("Rules firing on replica only:")); + break; + } + footers[count_footers++] = pg_strdup(buf.data); + have_heading = true; + } + + /* Everything after "CREATE RULE" is echoed verbatim */ + ruledef = PQgetvalue(result3, i, 1); + ruledef += 12; + printfPQExpBuffer(&buf, " %s", ruledef); + footers[count_footers++] = pg_strdup(buf.data); + } } } /* print triggers */ if (trigger_count > 0) { - printfPQExpBuffer(&buf, _("Triggers:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < trigger_count; i++) - { - const char *tgdef; - const char *usingpos; - - /* Everything after "TRIGGER" is echoed verbatim */ - tgdef = PQgetvalue(result4, i, 1); - usingpos = strstr(tgdef, " TRIGGER "); - if (usingpos) - tgdef = usingpos + 9; - - printfPQExpBuffer(&buf, " %s", tgdef); - - footers[count_footers++] = pg_strdup(buf.data); - } - } - - /* print disabled triggers */ - if (disabled_trigger_count > 0) - { - printfPQExpBuffer(&buf, _("Disabled triggers:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < disabled_trigger_count; i++) + bool have_heading; + int category; + + /* split the output into 4 different categories. + * Enabled triggers, disabled triggers and the two + * special ALWAYS and REPLICA configurations. + */ + for (category = 0; category < 4; category++) { - const char *tgdef; - const char *usingpos; - - /* Everything after "TRIGGER" is echoed verbatim */ - tgdef = PQgetvalue(result7, i, 1); - usingpos = strstr(tgdef, " TRIGGER "); - if (usingpos) - tgdef = usingpos + 9; - - printfPQExpBuffer(&buf, " %s", tgdef); - - footers[count_footers++] = pg_strdup(buf.data); + have_heading = false; + for (i = 0; i < trigger_count; i++) + { + bool list_trigger; + const char *tgdef; + const char *usingpos; + const char *tgenabled; + + /* Check if this trigger falls into the current category */ + tgenabled = PQgetvalue(result4, i, 2); + list_trigger = false; + switch (category) + { + case 0: if (*tgenabled == 'O' || *tgenabled == 't') + list_trigger = true; + break; + case 1: if (*tgenabled == 'D' || *tgenabled == 'f') + list_trigger = true; + break; + case 2: if (*tgenabled == 'A') + list_trigger = true; + break; + case 3: if (*tgenabled == 'R') + list_trigger = true; + break; + } + if (list_trigger == false) + continue; + + /* Print the category heading once */ + if (have_heading == false) + { + switch (category) + { + case 0: + printfPQExpBuffer(&buf, _("Triggers:")); + break; + case 1: + printfPQExpBuffer(&buf, _("Disabled Triggers:")); + break; + case 2: + printfPQExpBuffer(&buf, _("Triggers firing always:")); + break; + case 3: + printfPQExpBuffer(&buf, _("Triggers firing on replica only:")); + break; + + } + footers[count_footers++] = pg_strdup(buf.data); + have_heading = true; + } + + /* Everything after "TRIGGER" is echoed verbatim */ + tgdef = PQgetvalue(result4, i, 1); + usingpos = strstr(tgdef, " TRIGGER "); + if (usingpos) + tgdef = usingpos + 9; + + printfPQExpBuffer(&buf, " %s", tgdef); + footers[count_footers++] = pg_strdup(buf.data); + } } } @@ -1392,7 +1460,6 @@ describeOneTableDetails(const char *schemaname, PQclear(result4); PQclear(result5); PQclear(result6); - PQclear(result7); } printTable(title.data, headers, diff --git a/src/include/catalog/pg_rewrite.h b/src/include/catalog/pg_rewrite.h index 91afb1419901fa5aa24ac61917470a5849030667..9996a67e4078aa8a039c5b15133be03d7573811d 100644 --- a/src/include/catalog/pg_rewrite.h +++ b/src/include/catalog/pg_rewrite.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_rewrite.h,v 1.27 2007/01/05 22:19:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_rewrite.h,v 1.28 2007/03/19 23:38:31 wieck Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -42,6 +42,7 @@ CATALOG(pg_rewrite,2618) Oid ev_class; int2 ev_attr; char ev_type; + char ev_enabled; bool is_instead; /* NB: remaining fields must be accessed via heap_getattr */ @@ -60,13 +61,14 @@ typedef FormData_pg_rewrite *Form_pg_rewrite; * compiler constants for pg_rewrite * ---------------- */ -#define Natts_pg_rewrite 7 +#define Natts_pg_rewrite 8 #define Anum_pg_rewrite_rulename 1 #define Anum_pg_rewrite_ev_class 2 #define Anum_pg_rewrite_ev_attr 3 #define Anum_pg_rewrite_ev_type 4 -#define Anum_pg_rewrite_is_instead 5 -#define Anum_pg_rewrite_ev_qual 6 -#define Anum_pg_rewrite_ev_action 7 +#define Anum_pg_rewrite_ev_enabled 5 +#define Anum_pg_rewrite_is_instead 6 +#define Anum_pg_rewrite_ev_qual 7 +#define Anum_pg_rewrite_ev_action 8 #endif /* PG_REWRITE_H */ diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h index df22089e3107677cae40a8dede04fbef63e3e11e..b7a71b2f0709ae65d502e2820bbf3a7cd033e9a3 100644 --- a/src/include/catalog/pg_trigger.h +++ b/src/include/catalog/pg_trigger.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.27 2007/02/14 01:58:58 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.28 2007/03/19 23:38:31 wieck Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -46,7 +46,8 @@ CATALOG(pg_trigger,2620) Oid tgfoid; /* OID of function to be called */ int2 tgtype; /* BEFORE/AFTER UPDATE/DELETE/INSERT * ROW/STATEMENT; see below */ - bool tgenabled; /* trigger is enabled/disabled */ + char tgenabled; /* trigger's firing configuration + * WRT session_replication_role */ bool tgisconstraint; /* trigger is a constraint trigger */ NameData tgconstrname; /* constraint name */ Oid tgconstrrelid; /* constraint's FROM table, if any */ diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index ac9eb7296d12e7bf5ab75c9db20ebaac58849c55..8fcd0bb6aa7e8144afd33fbdc20bd4ae1c38b318 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.61 2007/02/14 01:58:58 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.62 2007/03/19 23:38:31 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -78,6 +78,18 @@ typedef struct TriggerData #define TRIGGER_FIRED_AFTER(event) \ (!TRIGGER_FIRED_BEFORE (event)) +/* + * Definitions for the replication role based firing. + */ +#define SESSION_REPLICATION_ROLE_ORIGIN 0 +#define SESSION_REPLICATION_ROLE_REPLICA 1 +#define SESSION_REPLICATION_ROLE_LOCAL 2 +extern int SessionReplicationRole; + +#define TRIGGER_FIRES_ON_ORIGIN 'O' +#define TRIGGER_FIRES_ALWAYS 'A' +#define TRIGGER_FIRES_ON_REPLICA 'R' +#define TRIGGER_DISABLED 'D' extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid); @@ -88,7 +100,7 @@ extern void RemoveTriggerById(Oid trigOid); extern void renametrig(Oid relid, const char *oldname, const char *newname); extern void EnableDisableTrigger(Relation rel, const char *tgname, - bool enable, bool skip_system); + char fires_when, bool skip_system); extern void RelationBuildTriggers(Relation relation); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index e24b57e8a2326a1c4c4017104eef8923401193b2..c3a2bebca68b54e3e3d333fe83ea1dca21d98674 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.342 2007/03/13 00:33:43 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.343 2007/03/19 23:38:32 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -897,11 +897,17 @@ typedef enum AlterTableType AT_SetRelOptions, /* SET (...) -- AM specific parameters */ AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */ AT_EnableTrig, /* ENABLE TRIGGER name */ + AT_EnableAlwaysTrig, /* ENABLE ALWAYS TRIGGER name */ + AT_EnableReplicaTrig, /* ENABLE REPLICA TRIGGER name */ AT_DisableTrig, /* DISABLE TRIGGER name */ AT_EnableTrigAll, /* ENABLE TRIGGER ALL */ AT_DisableTrigAll, /* DISABLE TRIGGER ALL */ AT_EnableTrigUser, /* ENABLE TRIGGER USER */ AT_DisableTrigUser, /* DISABLE TRIGGER USER */ + AT_EnableRule, /* ENABLE RULE name */ + AT_EnableAlwaysRule, /* ENABLE ALWAYS RULE name */ + AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */ + AT_DisableRule, /* DISABLE RULE name */ AT_AddInherit, /* INHERIT parent */ AT_DropInherit /* NO INHERIT parent */ } AlterTableType; diff --git a/src/include/rewrite/prs2lock.h b/src/include/rewrite/prs2lock.h index d7ebbbe6bc107d46fd0901d1d1e1961a8e9bd754..17d045053e6ef19293f2fb70a656d0d9ae98092d 100644 --- a/src/include/rewrite/prs2lock.h +++ b/src/include/rewrite/prs2lock.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/rewrite/prs2lock.h,v 1.22 2007/01/05 22:19:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/rewrite/prs2lock.h,v 1.23 2007/03/19 23:38:32 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ typedef struct RewriteRule AttrNumber attrno; Node *qual; List *actions; + char enabled; bool isInstead; } RewriteRule; diff --git a/src/include/rewrite/rewriteDefine.h b/src/include/rewrite/rewriteDefine.h index d4673f82a6c048098900eccd91a2485169a6618e..5b21cf8e797b9970d1e8537b1f3186dfe24f913d 100644 --- a/src/include/rewrite/rewriteDefine.h +++ b/src/include/rewrite/rewriteDefine.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/rewrite/rewriteDefine.h,v 1.24 2007/03/13 00:33:43 tgl Exp $ + * $PostgreSQL: pgsql/src/include/rewrite/rewriteDefine.h,v 1.25 2007/03/19 23:38:32 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,11 @@ #include "nodes/parsenodes.h" +#define RULE_FIRES_ON_ORIGIN 'O' +#define RULE_FIRES_ALWAYS 'A' +#define RULE_FIRES_ON_REPLICA 'R' +#define RULE_DISABLED 'D' + extern void DefineRule(RuleStmt *stmt, const char *queryString); extern void DefineQueryRewrite(char *rulename, @@ -31,4 +36,7 @@ extern void RenameRewriteRule(Oid owningRel, const char *oldName, extern void setRuleCheckAsUser(Node *node, Oid userid); +extern void EnableDisableRule(Relation rel, const char *rulename, + char fires_when); + #endif /* REWRITEDEFINE_H */ diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index 4f03cd9e0a2c5fa36f963432487664499d330739..91101a318d719fc1b81d82e182fbd16bd023e113 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.2 2007/03/15 23:12:07 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.3 2007/03/19 23:38:32 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -102,5 +102,6 @@ extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner); extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner); extern TupleDesc PlanCacheComputeResultDesc(List *stmt_list); +extern bool HaveCachedPlans(void); #endif /* PLANCACHE_H */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 4065eced8211d896e1d7836d9253fbffc0689846..2963cc6616aae31030224a7fdf17869ed80aab71 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.98 2007/02/27 23:48:10 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.99 2007/03/19 23:38:32 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -53,7 +53,7 @@ typedef struct Trigger char *tgname; Oid tgfoid; int16 tgtype; - bool tgenabled; + char tgenabled; bool tgisconstraint; Oid tgconstrrelid; Oid tgconstraint;