As proposed the complete changes to pg_trigger and pg_rewrite - Mailing list pgsql-patches

From Jan Wieck
Subject As proposed the complete changes to pg_trigger and pg_rewrite
Date
Msg-id 45F9FD9D.8040505@Yahoo.com
Whole thread Raw
Responses Re: As proposed the complete changes to pg_trigger and pg_rewrite
List pgsql-patches
For discussion:

Attached is the completed patch that changes pg_trigger and extends
pg_rewrite in order to allow triggers and rules to be defined with
different, per session controllable, behaviors for replication purposes.

This will allow replication systems like Slony-I and, as has been stated
on pgsql-hackers, other products to control the firing mechanism of
triggers and rewrite rules without modifying the system catalog directly.

The firing mechanisms are controlled by a new superuser-only GUC
variable, session_replication_role, together with a change to
pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both
columns are a single char data type now (tgenabled was a bool before).
The possible values in these attributes are:

      'O' - Trigger/Rule fires when session_replication_role is "origin"
            (default) or "local". This is the default behavior.

      'D' - Trigger/Rule is disabled and fires never

      'A' - Trigger/Rule fires always regardless of the setting of
            session_replication_role

      'R' - Trigger/Rule fires when session_replication_role is "replica"

The GUC variable can only be changed as long as the system does not have
any saved SPI plans. This will prevent changing the session role and
accidentally executing stored procedures or functions that have plans
cached that expand to the wrong query set due to differences in the rule
firing semantics.

The SQL syntax for changing a triggers/rules firing semantics is

      ALTER TABLE <tabname> <when> TRIGGER|RULE <name>;

      <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE

psql's \d command as well as pg_dump are extended in a backward
compatible fashion.

Any volunteers to do the corresponding documentation changes should this
patch be accepted?


Jan

--
#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me.                                  #
#================================================== JanWieck@Yahoo.com #

Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.217
diff -c -r1.217 tablecmds.c
*** src/backend/commands/tablecmds.c    13 Mar 2007 00:33:39 -0000    1.217
--- src/backend/commands/tablecmds.c    15 Mar 2007 15:44:16 -0000
***************
*** 53,58 ****
--- 53,59 ----
  #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,259 ****
  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);
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
--- 254,262 ----
  static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace);
  static void ATExecSetRelOptions(Relation rel, List *defList, bool isReset);
  static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
!                            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,1965 ****
--- 1958,1974 ----
              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,2150 ****
          case AT_ResetRelOptions:        /* RESET (...) */
              ATExecSetRelOptions(rel, (List *) cmd->def, true);
              break;
!         case AT_EnableTrig:        /* ENABLE TRIGGER name */
!             ATExecEnableDisableTrigger(rel, cmd->name, true, false);
              break;
          case AT_DisableTrig:    /* DISABLE TRIGGER name */
!             ATExecEnableDisableTrigger(rel, cmd->name, false, false);
              break;
          case AT_EnableTrigAll:    /* ENABLE TRIGGER ALL */
!             ATExecEnableDisableTrigger(rel, NULL, true, false);
              break;
          case AT_DisableTrigAll:    /* DISABLE TRIGGER ALL */
!             ATExecEnableDisableTrigger(rel, NULL, false, false);
              break;
          case AT_EnableTrigUser:    /* ENABLE TRIGGER USER */
!             ATExecEnableDisableTrigger(rel, NULL, true, true);
              break;
          case AT_DisableTrigUser:        /* DISABLE TRIGGER USER */
!             ATExecEnableDisableTrigger(rel, NULL, false, true);
              break;
          case AT_AddInherit:
              ATExecAddInherit(rel, (RangeVar *) cmd->def);
              break;
--- 2136,2192 ----
          case AT_ResetRelOptions:        /* RESET (...) */
              ATExecSetRelOptions(rel, (List *) cmd->def, true);
              break;
!
!         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,
!                     TRIGGER_DISABLED, false);
              break;
          case AT_EnableTrigAll:    /* ENABLE TRIGGER ALL */
!             ATExecEnableDisableTrigger(rel, NULL,
!                     TRIGGER_FIRES_ON_ORIGIN, false);
              break;
          case AT_DisableTrigAll:    /* DISABLE TRIGGER ALL */
!             ATExecEnableDisableTrigger(rel, NULL,
!                     TRIGGER_DISABLED, false);
              break;
          case AT_EnableTrigUser:    /* ENABLE TRIGGER USER */
!             ATExecEnableDisableTrigger(rel, NULL,
!                     TRIGGER_FIRES_ON_ORIGIN, true);
              break;
          case AT_DisableTrigUser:        /* DISABLE TRIGGER USER */
!             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,4386 ****
      MemSet(&trig, 0, sizeof(trig));
      trig.tgoid = InvalidOid;
      trig.tgname = fkconstraint->constr_name;
!     trig.tgenabled = TRUE;
      trig.tgisconstraint = TRUE;
      trig.tgconstrrelid = RelationGetRelid(pkrel);
      trig.tgconstraint = constraintOid;
--- 4422,4428 ----
      MemSet(&trig, 0, sizeof(trig));
      trig.tgoid = InvalidOid;
      trig.tgname = fkconstraint->constr_name;
!     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
      trig.tgisconstraint = TRUE;
      trig.tgconstrrelid = RelationGetRelid(pkrel);
      trig.tgconstraint = constraintOid;
***************
*** 5877,5885 ****
   */
  static void
  ATExecEnableDisableTrigger(Relation rel, char *trigname,
!                            bool enable, bool skip_system)
  {
!     EnableDisableTrigger(rel, trigname, enable, skip_system);
  }

  /*
--- 5919,5939 ----
   */
  static void
  ATExecEnableDisableTrigger(Relation rel, char *trigname,
!                            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)
  {
!     EnableDisableRule(rel, trigname, fires_when);
  }

  /*
Index: src/backend/commands/trigger.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/commands/trigger.c,v
retrieving revision 1.213
diff -c -r1.213 trigger.c
*** src/backend/commands/trigger.c    14 Feb 2007 01:58:56 -0000    1.213
--- src/backend/commands/trigger.c    26 Feb 2007 13:38:54 -0000
***************
*** 54,59 ****
--- 54,66 ----
  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,276 ****
                                                    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_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
      values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
                                                  CStringGetDatum(constrname));
--- 277,283 ----
                                                    CStringGetDatum(trigname));
      values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
      values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
!     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,711 ****
   * EnableDisableTrigger()
   *
   *    Called by ALTER TABLE ENABLE/DISABLE TRIGGER
!  *    to change 'tgenabled' flag 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
   * skip_system: if true, skip "system" triggers (constraint triggers)
   *
   * Caller should have checked permissions for the table; here we also
--- 708,718 ----
   * EnableDisableTrigger()
   *
   *    Called by ALTER TABLE ENABLE/DISABLE TRIGGER
!  *    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 field
   * skip_system: if true, skip "system" triggers (constraint triggers)
   *
   * Caller should have checked permissions for the table; here we also
***************
*** 714,720 ****
   */
  void
  EnableDisableTrigger(Relation rel, const char *tgname,
!                      bool enable, bool skip_system)
  {
      Relation    tgrel;
      int            nkeys;
--- 721,727 ----
   */
  void
  EnableDisableTrigger(Relation rel, const char *tgname,
!                      char fires_when, bool skip_system)
  {
      Relation    tgrel;
      int            nkeys;
***************
*** 765,777 ****

          found = true;

!         if (oldtrig->tgenabled != enable)
          {
              /* 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;

              simple_heap_update(tgrel, &newtup->t_self, newtup);

--- 772,784 ----

          found = true;

!         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 = fires_when;

              simple_heap_update(tgrel, &newtup->t_self, newtup);

***************
*** 1333,1340 ****
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
          HeapTuple    newtuple;

!         if (!trigger->tgenabled)
!             continue;
          LocTriggerData.tg_trigger = trigger;
          newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                         tgindx[i],
--- 1340,1357 ----
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
          HeapTuple    newtuple;

!         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,1389 ****
      {
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

!         if (!trigger->tgenabled)
!             continue;
          LocTriggerData.tg_trigtuple = oldtuple = newtuple;
          LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
          LocTriggerData.tg_trigger = trigger;
--- 1399,1416 ----
      {
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

!         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,1451 ****
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
          HeapTuple    newtuple;

!         if (!trigger->tgenabled)
!             continue;
          LocTriggerData.tg_trigger = trigger;
          newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                         tgindx[i],
--- 1471,1488 ----
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
          HeapTuple    newtuple;

!         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,1507 ****
      {
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

!         if (!trigger->tgenabled)
!             continue;
          LocTriggerData.tg_trigtuple = trigtuple;
          LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
          LocTriggerData.tg_trigger = trigger;
--- 1537,1554 ----
      {
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

!         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,1582 ****
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
          HeapTuple    newtuple;

!         if (!trigger->tgenabled)
!             continue;
          LocTriggerData.tg_trigger = trigger;
          newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                         tgindx[i],
--- 1622,1639 ----
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
          HeapTuple    newtuple;

!         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,1643 ****
      {
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

!         if (!trigger->tgenabled)
!             continue;
          LocTriggerData.tg_trigtuple = trigtuple;
          LocTriggerData.tg_newtuple = oldtuple = newtuple;
          LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
--- 1693,1710 ----
      {
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

!         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,3274 ****
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

          /* Ignore disabled triggers */
!         if (!trigger->tgenabled)
!             continue;

          /*
           * If this is an UPDATE of a PK table or FK table that does not change
--- 3334,3351 ----
          Trigger    *trigger = &trigdesc->triggers[tgindx[i]];

          /* Ignore disabled triggers */
!         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
Index: src/backend/executor/spi.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.171
diff -c -r1.171 spi.c
*** src/backend/executor/spi.c    13 Mar 2007 00:33:40 -0000    1.171
--- src/backend/executor/spi.c    15 Mar 2007 17:19:31 -0000
***************
*** 27,32 ****
--- 27,33 ----
  Oid            SPI_lastoid = InvalidOid;
  SPITupleTable *SPI_tuptable = NULL;
  int            SPI_result;
+ int            SPI_savedplans = 0;

  static _SPI_connection *_SPI_stack = NULL;
  static _SPI_connection *_SPI_current = NULL;
***************
*** 448,453 ****
--- 449,459 ----
      _SPI_curid--;
      SPI_result = 0;

+     /*
+      * Keep track of how many saved SPI plans exist in this backend
+      */
+     SPI_savedplans++;
+
      return (void *) newplan;
  }

***************
*** 459,464 ****
--- 465,476 ----
      if (plan == NULL)
          return SPI_ERROR_ARGUMENT;

+     /*
+      * Keep track of how many saved SPI plans exist in this backend
+      */
+     if (spiplan->plancxt->parent == TopMemoryContext)
+         SPI_savedplans--;
+
      MemoryContextDelete(spiplan->plancxt);
      return 0;
  }
Index: src/backend/parser/gram.y
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.581
diff -c -r2.581 gram.y
*** src/backend/parser/gram.y    13 Mar 2007 00:33:41 -0000    2.581
--- src/backend/parser/gram.y    15 Mar 2007 15:32:44 -0000
***************
*** 365,371 ****

  /* 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
      ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION

      BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
--- 365,371 ----

  /* ordinary key words in alphabetical order */
  %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
!     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,429 ****
      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

      SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
      SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
--- 422,429 ----
      QUOTE

      READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
!     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,1485 ****
--- 1480,1501 ----
                      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,1521 ****
--- 1532,1569 ----
                      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
                  {
***************
*** 8667,8672 ****
--- 8715,8721 ----
              | AGGREGATE
              | ALSO
              | ALTER
+             | ALWAYS
              | ASSERTION
              | ASSIGNMENT
              | AT
***************
*** 8812,8817 ****
--- 8861,8867 ----
              | RENAME
              | REPEATABLE
              | REPLACE
+             | REPLICA
              | RESET
              | RESTART
              | RESTRICT
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.184
diff -c -r1.184 keywords.c
*** src/backend/parser/keywords.c    25 Jan 2007 11:53:51 -0000    1.184
--- src/backend/parser/keywords.c    2 Feb 2007 20:22:24 -0000
***************
*** 42,47 ****
--- 42,48 ----
      {"all", ALL},
      {"also", ALSO},
      {"alter", ALTER},
+     {"always", ALWAYS},
      {"analyse", ANALYSE},        /* British spelling */
      {"analyze", ANALYZE},
      {"and", AND},
***************
*** 289,294 ****
--- 290,296 ----
      {"rename", RENAME},
      {"repeatable", REPEATABLE},
      {"replace", REPLACE},
+     {"replica", REPLICA},
      {"reset", RESET},
      {"restart", RESTART},
      {"restrict", RESTRICT},
Index: src/backend/rewrite/rewriteDefine.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/rewrite/rewriteDefine.c,v
retrieving revision 1.118
diff -c -r1.118 rewriteDefine.c
*** src/backend/rewrite/rewriteDefine.c    13 Mar 2007 00:33:42 -0000    1.118
--- src/backend/rewrite/rewriteDefine.c    15 Mar 2007 16:58:33 -0000
***************
*** 30,35 ****
--- 30,36 ----
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
+ #include "utils/inval.h"


  static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
***************
*** 79,84 ****
--- 80,86 ----
      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 */
***************
*** 629,634 ****
--- 631,702 ----


  /*
+  * 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.
   *
   * This is unused code at the moment.
Index: src/backend/rewrite/rewriteHandler.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/rewrite/rewriteHandler.c,v
retrieving revision 1.171
diff -c -r1.171 rewriteHandler.c
*** src/backend/rewrite/rewriteHandler.c    1 Mar 2007 18:50:28 -0000    1.171
--- src/backend/rewrite/rewriteHandler.c    7 Mar 2007 18:46:02 -0000
***************
*** 21,30 ****
--- 21,32 ----
  #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 */
***************
*** 1033,1038 ****
--- 1035,1063 ----
      {
          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 ||
Index: src/backend/utils/cache/relcache.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/utils/cache/relcache.c,v
retrieving revision 1.257
diff -c -r1.257 relcache.c
*** src/backend/utils/cache/relcache.c    3 Mar 2007 20:08:41 -0000    1.257
--- src/backend/utils/cache/relcache.c    7 Mar 2007 17:21:18 -0000
***************
*** 651,656 ****
--- 651,657 ----

          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;

          /*
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.382
diff -c -r1.382 guc.c
*** src/backend/utils/misc/guc.c    13 Mar 2007 14:32:25 -0000    1.382
--- src/backend/utils/misc/guc.c    15 Mar 2007 17:12:26 -0000
***************
*** 34,39 ****
--- 34,41 ----
  #include "commands/async.h"
  #include "commands/vacuum.h"
  #include "commands/variable.h"
+ #include "commands/trigger.h"
+ #include "executor/spi.h"
  #include "funcapi.h"
  #include "libpq/auth.h"
  #include "libpq/pqformat.h"
***************
*** 124,129 ****
--- 126,133 ----

  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,231 ****
--- 230,236 ----
  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;
***************
*** 1929,1934 ****
--- 1934,1949 ----
      },

      {
+         {"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."),
              gettext_noop("If a dynamically loadable module needs to be opened and "
***************
*** 6116,6121 ****
--- 6131,6163 ----
  }

  static const char *
+ assign_session_replication_role(const char *newval, bool doit, GucSource source)
+ {
+     if (SPI_savedplans > 0)
+         elog(ERROR, "session_replication_role cannot be changed "
+                     "after prepared SPI plans have been saved permanently");
+
+     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)
  {
Index: src/bin/pg_dump/pg_dump.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/bin/pg_dump/pg_dump.c,v
retrieving revision 1.460
diff -c -r1.460 pg_dump.c
*** src/bin/pg_dump/pg_dump.c    14 Feb 2007 01:58:57 -0000    1.460
--- src/bin/pg_dump/pg_dump.c    15 Mar 2007 19:05:23 -0000
***************
*** 3699,3713 ****
      int            i_ruletable;
      int            i_ev_type;
      int            i_is_instead;

      /* Make sure we are in proper schema */
      selectSourceSchema("pg_catalog");

!     if (g_fout->remoteVersion >= 70100)
      {
          appendPQExpBuffer(query, "SELECT "
                            "tableoid, oid, rulename, "
!                           "ev_class as ruletable, ev_type, is_instead "
                            "FROM pg_rewrite "
                            "ORDER BY oid");
      }
--- 3699,3724 ----
      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 >= 80300)
!     {
!         appendPQExpBuffer(query, "SELECT "
!                           "tableoid, oid, rulename, "
!                           "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,3722 ****
          appendPQExpBuffer(query, "SELECT "
                            "(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, "
                            "oid, rulename, "
!                           "ev_class as ruletable, ev_type, is_instead "
                            "FROM pg_rewrite "
                            "ORDER BY oid");
      }
--- 3727,3734 ----
          appendPQExpBuffer(query, "SELECT "
                            "(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, "
                            "oid, rulename, "
!                           "ev_class as ruletable, ev_type, is_instead, "
!                           "'O'::char as ev_enabled "
                            "FROM pg_rewrite "
                            "ORDER BY oid");
      }
***************
*** 3736,3741 ****
--- 3748,3754 ----
      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,3764 ****
--- 3772,3778 ----
          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,3962 ****
              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].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
              tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';

--- 3970,3976 ----
              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';

***************
*** 8824,8834 ****
      }
      appendPQExpBuffer(query, ");\n");

!     if (!tginfo->tgenabled)
      {
          appendPQExpBuffer(query, "\nALTER TABLE %s ",
                            fmtId(tbinfo->dobj.name));
!         appendPQExpBuffer(query, "DISABLE TRIGGER %s;\n",
                            fmtId(tginfo->dobj.name));
      }

--- 8838,8864 ----
      }
      appendPQExpBuffer(query, ");\n");

!     if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
      {
          appendPQExpBuffer(query, "\nALTER TABLE %s ",
                            fmtId(tbinfo->dobj.name));
!         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));
      }

***************
*** 8915,8920 ****
--- 8945,8977 ----
      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
       */
      appendPQExpBuffer(delcmd, "DROP RULE %s ",
Index: src/bin/pg_dump/pg_dump.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/bin/pg_dump/pg_dump.h,v
retrieving revision 1.133
diff -c -r1.133 pg_dump.h
*** src/bin/pg_dump/pg_dump.h    19 Feb 2007 15:05:06 -0000    1.133
--- src/bin/pg_dump/pg_dump.h    15 Mar 2007 18:43:57 -0000
***************
*** 314,319 ****
--- 314,320 ----
      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,336 ****
      char       *tgconstrname;
      Oid            tgconstrrelid;
      char       *tgconstrrelname;
!     bool        tgenabled;
      bool        tgdeferrable;
      bool        tginitdeferred;
  } TriggerInfo;
--- 331,337 ----
      char       *tgconstrname;
      Oid            tgconstrrelid;
      char       *tgconstrrelname;
!     char        tgenabled;
      bool        tgdeferrable;
      bool        tginitdeferred;
  } TriggerInfo;
Index: src/bin/psql/describe.c
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/bin/psql/describe.c,v
retrieving revision 1.152
diff -c -r1.152 describe.c
*** src/bin/psql/describe.c    20 Feb 2007 10:23:38 -0000    1.152
--- src/bin/psql/describe.c    15 Mar 2007 21:37:32 -0000
***************
*** 1054,1067 ****
                     *result3 = NULL,
                     *result4 = NULL,
                     *result5 = NULL,
!                    *result6 = NULL,
!                    *result7 = 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;

--- 1054,1065 ----
                     *result3 = NULL,
                     *result4 = NULL,
                     *result5 = NULL,
!                    *result6 = NULL;
          int            check_count = 0,
                      index_count = 0,
                      foreignkey_count = 0,
                      rule_count = 0,
                      trigger_count = 0,
                      inherits_count = 0;
          int            count_footers = 0;

***************
*** 1104,1114 ****
          /* count rules */
          if (tableinfo.hasrules)
          {
!             printfPQExpBuffer(&buf,
!                               "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
                                "FROM pg_catalog.pg_rewrite r\n"
                                "WHERE r.ev_class = '%s' ORDER BY 1",
                                oid);
              result3 = PSQLexec(buf.data, false);
              if (!result3)
              {
--- 1102,1125 ----
          /* count rules */
          if (tableinfo.hasrules)
          {
!             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)
              {
***************
*** 1124,1133 ****
          if (tableinfo.triggers)
          {
              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 t.tgenabled "
                                "AND t.tgconstraint = 0\n"
                                "ORDER BY 1",
                                oid);
--- 1135,1144 ----
          if (tableinfo.triggers)
          {
              printfPQExpBuffer(&buf,
!                      "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.tgconstraint = 0\n"
                                "ORDER BY 1",
                                oid);
***************
*** 1141,1167 ****
              }
              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) */
--- 1152,1157 ----
***************
*** 1180,1186 ****
                  PQclear(result2);
                  PQclear(result3);
                  PQclear(result4);
-                 PQclear(result7);
                  goto error_return;
              }
              else
--- 1170,1175 ----
***************
*** 1198,1204 ****
              PQclear(result3);
              PQclear(result4);
              PQclear(result5);
-             PQclear(result7);
              goto error_return;
          }
          else
--- 1187,1192 ----
***************
*** 1296,1358 ****
          /* 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;

!                 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++)
              {
!                 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);
              }
          }

--- 1284,1426 ----
          /* print rules */
          if (rule_count > 0)
          {
!             bool    have_heading;
!             int        category;

!             for (category = 0; category < 4; category++)
!             {
!                 have_heading = false;

!                 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)
          {
!             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++)
              {
!                 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);
!                 }
              }
          }

***************
*** 1391,1397 ****
          PQclear(result4);
          PQclear(result5);
          PQclear(result6);
-         PQclear(result7);
      }

      printTable(title.data, headers,
--- 1459,1464 ----
Index: src/include/catalog/pg_rewrite.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/catalog/pg_rewrite.h,v
retrieving revision 1.27
diff -c -r1.27 pg_rewrite.h
*** src/include/catalog/pg_rewrite.h    5 Jan 2007 22:19:53 -0000    1.27
--- src/include/catalog/pg_rewrite.h    7 Mar 2007 16:30:14 -0000
***************
*** 42,47 ****
--- 42,48 ----
      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,72 ****
   *        compiler constants for pg_rewrite
   * ----------------
   */
! #define Natts_pg_rewrite                7
  #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

  #endif   /* PG_REWRITE_H */
--- 61,74 ----
   *        compiler constants for pg_rewrite
   * ----------------
   */
! #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_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 */
Index: src/include/catalog/pg_trigger.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/catalog/pg_trigger.h,v
retrieving revision 1.27
diff -c -r1.27 pg_trigger.h
*** src/include/catalog/pg_trigger.h    14 Feb 2007 01:58:58 -0000    1.27
--- src/include/catalog/pg_trigger.h    26 Feb 2007 13:44:14 -0000
***************
*** 46,52 ****
      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 */
      bool        tgisconstraint; /* trigger is a constraint trigger */
      NameData    tgconstrname;    /* constraint name */
      Oid            tgconstrrelid;    /* constraint's FROM table, if any */
--- 46,53 ----
      Oid            tgfoid;            /* OID of function to be called */
      int2        tgtype;            /* BEFORE/AFTER UPDATE/DELETE/INSERT
                                   * ROW/STATEMENT; see below */
!     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 */
Index: src/include/commands/trigger.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/commands/trigger.h,v
retrieving revision 1.61
diff -c -r1.61 trigger.h
*** src/include/commands/trigger.h    14 Feb 2007 01:58:58 -0000    1.61
--- src/include/commands/trigger.h    26 Feb 2007 13:49:15 -0000
***************
*** 78,83 ****
--- 78,95 ----
  #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,94 ****
  extern void renametrig(Oid relid, const char *oldname, const char *newname);

  extern void EnableDisableTrigger(Relation rel, const char *tgname,
!                      bool enable, bool skip_system);

  extern void RelationBuildTriggers(Relation relation);

--- 100,106 ----
  extern void renametrig(Oid relid, const char *oldname, const char *newname);

  extern void EnableDisableTrigger(Relation rel, const char *tgname,
!                      char fires_when, bool skip_system);

  extern void RelationBuildTriggers(Relation relation);

Index: src/include/executor/spi.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/executor/spi.h,v
retrieving revision 1.58
diff -c -r1.58 spi.h
*** src/include/executor/spi.h    4 Oct 2006 00:30:08 -0000    1.58
--- src/include/executor/spi.h    15 Mar 2007 17:12:09 -0000
***************
*** 79,84 ****
--- 79,85 ----
  extern DLLIMPORT Oid SPI_lastoid;
  extern DLLIMPORT SPITupleTable *SPI_tuptable;
  extern DLLIMPORT int SPI_result;
+ extern DLLIMPORT int SPI_savedplans;

  extern int    SPI_connect(void);
  extern int    SPI_finish(void);
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.342
diff -c -r1.342 parsenodes.h
*** src/include/nodes/parsenodes.h    13 Mar 2007 00:33:43 -0000    1.342
--- src/include/nodes/parsenodes.h    15 Mar 2007 15:13:03 -0000
***************
*** 897,907 ****
--- 897,913 ----
      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;
Index: src/include/rewrite/prs2lock.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/rewrite/prs2lock.h,v
retrieving revision 1.22
diff -c -r1.22 prs2lock.h
*** src/include/rewrite/prs2lock.h    5 Jan 2007 22:19:57 -0000    1.22
--- src/include/rewrite/prs2lock.h    7 Mar 2007 17:21:19 -0000
***************
*** 28,33 ****
--- 28,34 ----
      AttrNumber    attrno;
      Node       *qual;
      List       *actions;
+     char        enabled;
      bool        isInstead;
  } RewriteRule;

Index: src/include/rewrite/rewriteDefine.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/rewrite/rewriteDefine.h,v
retrieving revision 1.24
diff -c -r1.24 rewriteDefine.h
*** src/include/rewrite/rewriteDefine.h    13 Mar 2007 00:33:43 -0000    1.24
--- src/include/rewrite/rewriteDefine.h    15 Mar 2007 15:33:07 -0000
***************
*** 16,21 ****
--- 16,26 ----

  #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,34 ****
--- 36,42 ----

  extern void setRuleCheckAsUser(Node *node, Oid userid);

+ extern void EnableDisableRule(Relation rel, const char *rulename,
+                   char fires_when);
+
  #endif   /* REWRITEDEFINE_H */
Index: src/include/utils/rel.h
===================================================================
RCS file: /usr/local/pgsql/CvsRoot/pgsql/src/include/utils/rel.h,v
retrieving revision 1.98
diff -c -r1.98 rel.h
*** src/include/utils/rel.h    27 Feb 2007 23:48:10 -0000    1.98
--- src/include/utils/rel.h    5 Mar 2007 17:05:24 -0000
***************
*** 53,59 ****
      char       *tgname;
      Oid            tgfoid;
      int16        tgtype;
!     bool        tgenabled;
      bool        tgisconstraint;
      Oid            tgconstrrelid;
      Oid            tgconstraint;
--- 53,59 ----
      char       *tgname;
      Oid            tgfoid;
      int16        tgtype;
!     char        tgenabled;
      bool        tgisconstraint;
      Oid            tgconstrrelid;
      Oid            tgconstraint;


pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: remove psql support for ancient copy syntax
Next
From: Tom Lane
Date:
Subject: Re: As proposed the complete changes to pg_trigger and pg_rewrite