bdr: Initial DDL replication
authorAlvaro Herrera <[email protected]>
Thu, 20 Feb 2014 17:47:55 +0000 (14:47 -0300)
committerAndres Freund <[email protected]>
Thu, 3 Jul 2014 15:55:18 +0000 (17:55 +0200)
This simply accumulates DDL commands in a table, and has the bdr_apply
background worker execute it.

contrib/bdr/bdr--0.5.sql
contrib/bdr/bdr.c
contrib/bdr/bdr.h
contrib/bdr/bdr_apply.c

index 0128eb534b1b153d5d796b75105ed3e5c84a8447..96022372e6df9e84b4f2c25f788327e1bf895406 100644 (file)
@@ -145,4 +145,36 @@ VALUES (
     'bdr_sequence_options'
 );
 
+CREATE TABLE bdr_queued_commands (
+    obj_type text,
+   obj_identity text,
+   command text,
+   executed bool
+);
+
+CREATE OR REPLACE FUNCTION bdr.queue_commands()
+ RETURNS event_trigger
+ LANGUAGE plpgsql
+AS $function$
+DECLARE
+        r RECORD;
+BEGIN
+        FOR r IN SELECT * FROM pg_event_trigger_get_creation_commands()
+        LOOP
+                INSERT INTO bdr.bdr_queued_commands
+                   (obj_type, obj_identity, command, executed)
+                   VALUES
+                        (r.object_type,
+                       r.identity,
+                        pg_catalog.pg_event_trigger_expand_command(r.command),
+                       'false');
+        END LOOP;
+END;
+$function$;
+
+CREATE EVENT TRIGGER queue_commands
+ON ddl_command_end
+WHEN tag IN ('create table', 'create index', 'create sequence')
+EXECUTE PROCEDURE bdr.queue_commands();
+
 RESET search_path;
index 94c5afc8b328dfa58ca126ce0a51a2c1b71d6d1a..07421cac374da58186e0daf69f6fd693532e4a29 100644 (file)
@@ -420,6 +420,9 @@ bdr_apply_main(Datum main_arg)
 
    replication_origin_id = replication_identifier;
 
+   /* setup initial queued_cmds OID */
+   setup_queuedcmds_relid();
+
    while (!got_sigterm)
    {
        /* int       ret; */
index 595d1b447bd23b6c5d35a01c39046a834acc0313..a6f4089a065ef212b32646cabbbdfdb11294d7b9 100644 (file)
@@ -68,6 +68,9 @@ extern void bdr_sequence_alloc(PG_FUNCTION_ARGS);
 extern void bdr_sequence_setval(PG_FUNCTION_ARGS);
 extern Datum bdr_sequence_options(PG_FUNCTION_ARGS);
 
+/* DDL replication support */
+extern void setup_queuedcmds_relid(void);
+
 /* statistic functions */
 extern void bdr_count_shmem_init(size_t nnodes);
 extern void bdr_count_set_current_node(RepNodeId node_id);
index 5d099089d50f744a1dfdad4632f144b81f55b95f..2485d4d7c5fe940deb770f46a0236016a0d2f81f 100644 (file)
@@ -53,6 +53,7 @@ static void tuple_to_stringinfo(StringInfo s, TupleDesc tupdesc, HeapTuple tuple
 static void check_sequencer_wakeup(Relation rel);
 
 bool request_sequencer_wakeup = false;
+static Oid     QueuedDDLCommandsRelid = InvalidOid;
 
 void
 process_remote_begin(char *data, size_t r)
@@ -153,6 +154,83 @@ process_remote_commit(char *data, size_t r)
    }
 }
 
+static HeapTuple
+process_queued_ddl_command(HeapTuple cmdtup)
+{
+   Relation    cmdsrel;
+   HeapTuple   newtup;
+   Datum       datum;
+   char       *type;
+   char       *identstr;
+   char       *cmdstr;
+   bool        isnull;
+
+   cmdsrel = heap_open(QueuedDDLCommandsRelid, AccessShareLock);
+
+   /* fetch the object type */
+   datum = heap_getattr(cmdtup, 1,
+                        RelationGetDescr(cmdsrel),
+                        &isnull);
+   if (isnull)
+   {
+       elog(LOG, "null object type in command tuple in \"%s\"",
+            RelationGetRelationName(cmdsrel));
+       return cmdtup;
+   }
+   type = TextDatumGetCString(datum);
+
+   /* fetch the object identity */
+   datum = heap_getattr(cmdtup, 2,
+                        RelationGetDescr(cmdsrel),
+                        &isnull);
+   if (isnull)
+   {
+       elog(WARNING, "null identity in command tuple for object of type %s",
+            RelationGetRelationName(cmdsrel));
+       return cmdtup;
+   }
+   identstr = TextDatumGetCString(datum);
+   elog(LOG, "got queued command for %s: \"%s\"", type, identstr);
+
+   /* finally fetch and execute the command */
+   datum = heap_getattr(cmdtup, 3,
+                        RelationGetDescr(cmdsrel),
+                        &isnull);
+   if (isnull)
+   {
+       elog(LOG, "null command in tuple for %s \"%s\"", type, identstr);
+       return cmdtup;
+   }
+   cmdstr = TextDatumGetCString(datum);
+
+   /* do the SPI dance */
+   {
+       int     ret;
+
+       /*
+        * XXX it might be wise to establish a savepoint here, to avoid
+        * a larger problem in case the command fails; at the very least
+        * we still need to process the original insertion.
+        */
+       SPI_connect();
+       PushActiveSnapshot(GetTransactionSnapshot());
+       ret = SPI_execute(cmdstr, false, 0);
+       if (ret != SPI_OK_UTILITY)
+           elog(LOG, "SPI_execute failed");
+
+       SPI_finish();
+       PopActiveSnapshot();
+   }
+
+   /* set "executed" true */
+   // newtup = heap_modify_tuple( .. );
+   newtup = cmdtup;
+
+   pfree(identstr);
+   heap_close(cmdsrel, AccessShareLock);
+
+   return newtup;
+}
 
 void
 process_remote_insert(char *data, size_t r)
@@ -174,6 +252,9 @@ process_remote_insert(char *data, size_t r)
 
    data = read_tuple(data, r, &tup, &reloid);
 
+   if (reloid == QueuedDDLCommandsRelid)
+       tup = *process_queued_ddl_command(&tup);
+
    rel = heap_open(reloid, RowExclusiveLock);
 
    if (rel->rd_rel->relkind != RELKIND_RELATION)
@@ -908,3 +989,45 @@ find_pkey_tuple(ScanKey skey, Relation rel, Relation idxrel,
    }
    return found;
 }
+
+void
+setup_queuedcmds_relid(void)
+{
+   Datum   oid;
+   int     ret;
+   bool    isnull;
+
+   StartTransactionCommand();
+   SPI_connect();
+   PushActiveSnapshot(GetTransactionSnapshot());
+
+   ret = SPI_execute("SELECT oid FROM pg_class "
+                     "WHERE oid = 'bdr.bdr_queued_commands'::regclass",
+                     true, 0);
+   if (ret != SPI_OK_SELECT)
+       goto failed;
+
+   if (SPI_processed != 1)
+       goto failed;
+
+   oid = SPI_getbinval(SPI_tuptable->vals[0],
+                       SPI_tuptable->tupdesc,
+                       1, &isnull);
+   if (isnull)
+       goto failed;
+
+   if (true)
+       QueuedDDLCommandsRelid = DatumGetObjectId(oid);
+   else
+   {
+failed:
+       QueuedDDLCommandsRelid = InvalidOid;
+   }
+
+   SPI_finish();
+   PopActiveSnapshot();
+   CommitTransactionCommand();
+   pgstat_report_activity(STATE_IDLE, NULL);
+
+   elog(LOG, "bdr.bdr_queued_commands OID set to %u", QueuedDDLCommandsRelid);
+}