CVS User Account cvsuser
Thu Sep 30 18:45:42 PDT 2004
Log Message:
-----------
1.  Add in 'helpitsbroken.txt', 'randomfacts.txt', and 'schemadoc.html'
    to STABLE

2.  Modified README to point to additional new documentation

3.  Implemented SET DROP TABLE and SET DROP SEQUENCE, modifying
    slonik, slon events, Slony-I functions, as well as the documentation.

Tags:
----
REL_1_0_STABLE

Modified Files:
--------------
    slony1-engine:
        README (r1.4.2.1 -> r1.4.2.2)
    slony1-engine/doc/howto:
        slonik_commands.html (r1.1 -> r1.1.2.1)
    slony1-engine/src/backend:
        slony1_funcs.sql (r1.15.2.4 -> r1.15.2.5)
        slony1_funcs.v73.sql (r1.4.2.1 -> r1.4.2.2)
    slony1-engine/src/slon:
        cleanup_thread.c (r1.13.2.3 -> r1.13.2.4)
        dbutils.c (r1.9.2.1 -> r1.9.2.2)
        local_listen.c (r1.23.2.1 -> r1.23.2.2)
        remote_listen.c (r1.15 -> r1.15.2.1)
        remote_worker.c (r1.55.2.5 -> r1.55.2.6)
        runtime_config.c (r1.19 -> r1.19.2.1)
        scheduler.c (r1.15 -> r1.15.2.1)
        slon.c (r1.27 -> r1.27.2.1)
        slon.h (r1.36 -> r1.36.2.1)
        sync_thread.c (r1.11 -> r1.11.2.1)
    slony1-engine/src/slonik:
        parser.y (r1.16.2.1 -> r1.16.2.2)
        slonik.c (r1.27.2.2 -> r1.27.2.3)
        slonik.h (r1.17 -> r1.17.2.1)

Added Files:
-----------
    slony1-engine/doc/howto:
        helpitsbroken.txt (r1.12.2.1)
        randomfacts.txt (r1.2.2.1)
        schemadoc.html (r1.6.2.1)

-------------- next part --------------
Index: slony1_funcs.sql
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/backend/slony1_funcs.sql,v
retrieving revision 1.15.2.4
retrieving revision 1.15.2.5
diff -Lsrc/backend/slony1_funcs.sql -Lsrc/backend/slony1_funcs.sql -u -w -r1.15.2.4 -r1.15.2.5
--- src/backend/slony1_funcs.sql
+++ src/backend/slony1_funcs.sql
@@ -2121,6 +2121,131 @@
 
 
 -- ----------------------------------------------------------------------
+-- FUNCTION setDropTable (tab_id)
+-- ----------------------------------------------------------------------
+create or replace function @NAMESPACE at .setDropTable(int4)
+returns bigint
+as '
+declare
+	p_tab_id		alias for $1;
+	v_set_id		int4;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table @NAMESPACE at .sl_config_lock;
+
+        -- ----
+	-- Determine the set_id
+        -- ----
+	select tab_set into v_set_id from @NAMESPACE at .sl_table where tab_id = p_tab_id;
+
+	-- ----
+	-- Ensure table exists
+	-- ----
+	if not found then
+		raise exception ''Slony-I: setDropTable_int(): table % not found'',
+			p_tab_id;
+	end if;
+
+	-- ----
+	-- Check that we are the origin of the set
+	-- ----
+	select set_origin into v_set_origin
+			from @NAMESPACE at .sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception ''Slony-I: setDropTable(): set % not found'', v_set_id;
+	end if;
+	if v_set_origin != @NAMESPACE at .getLocalNodeId(''_ at CLUSTERNAME@'') then
+		raise exception ''Slony-I: setDropTable(): set % has remote origin'', v_set_id;
+	end if;
+
+	-- ----
+	-- Drop the table from the set and generate the SET_ADD_TABLE event
+	-- ----
+	perform @NAMESPACE at .setDropTable_int(p_tab_id);
+	return  @NAMESPACE at .createEvent(''_ at CLUSTERNAME@'', ''SET_DROP_TABLE'', p_tab_id);
+end;
+' language plpgsql;
+comment on function @NAMESPACE at .setDropTable(int4) is
+'setDropTable (tab_id)
+
+Drop table tab_id from set on origin node, and generate SET_DROP_TABLE
+event to allow this to propagate to other nodes.';
+
+-- ----------------------------------------------------------------------
+-- FUNCTION setDropTable_int (tab_id)
+-- ----------------------------------------------------------------------
+create or replace function @NAMESPACE at .setDropTable_int(int4)
+returns int4
+as '
+declare
+	p_tab_id		alias for $1;
+	v_set_id		int4;
+	v_local_node_id		int4;
+	v_set_origin		int4;
+	v_sub_provider		int4;
+	v_tab_reloid		oid;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table @NAMESPACE at .sl_config_lock;
+
+        -- ----
+	-- Determine the set_id
+        -- ----
+	select tab_set into v_set_id from @NAMESPACE at .sl_table where tab_id = p_tab_id;
+
+	-- ----
+	-- Ensure table exists
+	-- ----
+	if not found then
+		return 0;
+	end if;
+
+	-- ----
+	-- For sets with a remote origin, check that we are subscribed 
+	-- to that set. Otherwise we ignore the table because it might 
+	-- not even exist in our database.
+	-- ----
+	v_local_node_id := @NAMESPACE at .getLocalNodeId(''_ at CLUSTERNAME@'');
+	select set_origin into v_set_origin
+			from @NAMESPACE at .sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception ''Slony-I: setDropTable_int(): set % not found'',
+				v_set_id;
+	end if;
+	if v_set_origin != v_local_node_id then
+		select sub_provider into v_sub_provider
+				from @NAMESPACE at .sl_subscribe
+				where sub_set = v_set_id
+				and sub_receiver = @NAMESPACE at .getLocalNodeId(''_ at CLUSTERNAME@'');
+		if not found then
+			return 0;
+		end if;
+	end if;
+	
+	-- ----
+	-- Drop the table from sl_table and drop trigger from it.
+	-- ----
+	perform @NAMESPACE at .alterTableRestore(p_tab_id);
+	perform @NAMESPACE at .tableDropKey(p_tab_id);
+	delete from @NAMESPACE at .sl_table where tab_id = p_tab_id;
+	return p_tab_id;
+end;
+' language plpgsql;
+comment on function @NAMESPACE at .setDropTable_int(int4) is
+'setDropTable_int (tab_id)
+
+This function processes the SET_DROP_TABLE event on remote nodes,
+dropping a table from replication if the remote node is subscribing to
+its replication set.';
+
+-- ----------------------------------------------------------------------
 -- FUNCTION setAddSequence (set_id, seq_id, seq_fqname, seq_comment)
 -- ----------------------------------------------------------------------
 create or replace function @NAMESPACE at .setAddSequence (int4, int4, text, text)
@@ -2262,6 +2387,134 @@
 
 
 -- ----------------------------------------------------------------------
+-- FUNCTION setDropSequence (seq_id)
+-- ----------------------------------------------------------------------
+create or replace function @NAMESPACE at .setDropSequence (int4)
+returns bigint
+as '
+declare
+	p_seq_id		alias for $1;
+	v_set_id		int4;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table @NAMESPACE at .sl_config_lock;
+
+	-- ----
+	-- Determine set id for this sequence
+	-- ----
+	select seq_set into v_set_id from @NAMESPACE at .sl_sequence where seq_id = p_seq_id;
+
+	-- ----
+	-- Ensure sequence exists
+	-- ----
+	if not found then
+		raise exception ''Slony-I: setDropSequence_int(): sequence % not found'',
+			p_seq_id;
+	end if;
+
+	-- ----
+	-- Check that we are the origin of the set
+	-- ----
+	select set_origin into v_set_origin
+			from @NAMESPACE at .sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception ''Slony-I: setDropSequence(): set % not found'', v_set_id;
+	end if;
+	if v_set_origin != @NAMESPACE at .getLocalNodeId(''_ at CLUSTERNAME@'') then
+		raise exception ''Slony-I: setDropSequence(): set % has remote origin'', v_set_id;
+	end if;
+
+	-- ----
+	-- Add the sequence to the set and generate the SET_ADD_SEQUENCE event
+	-- ----
+	perform @NAMESPACE at .setDropSequence_int(p_seq_id);
+	return  @NAMESPACE at .createEvent(''_ at CLUSTERNAME@'', ''SET_DROP_SEQUENCE'',
+			p_seq_id);
+end;
+' language plpgsql;
+comment on function @NAMESPACE at .setDropSequence (int4) is
+'setDropSequence (seq_id)
+
+On the origin node for the set, drop sequence seq_id from replication
+set, and raise SET_DROP_SEQUENCE to cause this to replicate to
+subscriber nodes.';
+
+-- ----------------------------------------------------------------------
+-- FUNCTION setDropSequence_int (seq_id)
+-- ----------------------------------------------------------------------
+create or replace function @NAMESPACE at .setDropSequence_int(int4)
+returns int4
+as '
+declare
+	p_seq_id		alias for $1;
+	v_set_id		int4;
+	v_local_node_id		int4;
+	v_set_origin		int4;
+	v_sub_provider		int4;
+	v_relkind			char;
+	v_sync_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table @NAMESPACE at .sl_config_lock;
+
+	-- ----
+	-- Determine set id for this sequence
+	-- ----
+	select seq_set into v_set_id from @NAMESPACE at .sl_sequence where seq_id = p_seq_id;
+
+	-- ----
+	-- Ensure sequence exists
+	-- ----
+	if not found then
+		return 0;
+	end if;
+
+	-- ----
+	-- For sets with a remote origin, check that we are subscribed 
+	-- to that set. Otherwise we ignore the sequence because it might 
+	-- not even exist in our database.
+	-- ----
+	v_local_node_id := @NAMESPACE at .getLocalNodeId(''_ at CLUSTERNAME@'');
+	select set_origin into v_set_origin
+			from @NAMESPACE at .sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception ''Slony-I: setDropSequence_int(): set % not found'',
+				v_set_id;
+	end if;
+	if v_set_origin != v_local_node_id then
+		select sub_provider into v_sub_provider
+				from @NAMESPACE at .sl_subscribe
+				where sub_set = v_set_id
+				and sub_receiver = @NAMESPACE at .getLocalNodeId(''_ at CLUSTERNAME@'');
+		if not found then
+			return 0;
+		end if;
+	end if;
+
+	-- ----
+	-- drop the sequence from sl_sequence, sl_seqlog
+	-- ----
+	delete from @NAMESPACE at .sl_seqlog where seql_seqid = p_seq_id;
+	delete from @NAMESPACE at .sl_sequence where seq_id = p_seq_id;
+
+	return p_seq_id;
+end;
+' language plpgsql;
+comment on function @NAMESPACE at .setDropSequence_int(int4) is
+'setDropSequence_int (seq_id)
+
+This processes the SET_DROP_SEQUENCE event.  On remote nodes that
+subscribe to the set containing sequence seq_id, drop the sequence
+from the replication set.';
+
+-- ----------------------------------------------------------------------
 -- FUNCTION sequenceSetValue (seq_id, seq_origin, ev_seqno, last_value)
 -- ----------------------------------------------------------------------
 create or replace function @NAMESPACE at .sequenceSetValue(int4, int4, int8, int8) returns int4
Index: slony1_funcs.v73.sql
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/backend/slony1_funcs.v73.sql,v
retrieving revision 1.4.2.1
retrieving revision 1.4.2.2
diff -Lsrc/backend/slony1_funcs.v73.sql -Lsrc/backend/slony1_funcs.v73.sql -u -w -r1.4.2.1 -r1.4.2.2
--- src/backend/slony1_funcs.v73.sql
+++ src/backend/slony1_funcs.v73.sql
@@ -25,4 +25,3 @@
 	return 1;
 end;
 ' language plpgsql;
-
--- /dev/null
+++ doc/howto/helpitsbroken.txt
@@ -0,0 +1,389 @@
+Help!  It's broken!
+------------------------------
+
+You're having trouble getting it to work, and are scratching your head
+as to what might be wrong.
+
+Here are some idiosyncracies that other people have stumbled over that
+might help you to "stumble more quickly."
+
+1.  I looked for the _clustername namespace, and it wasn't there.
+
+If the DSNs are wrong, then slon instances can't connect to the nodes.
+
+This will generally lead to nodes remaining entirely untouched.
+
+Recheck the connection configuration.  By the way, since slon links to
+libpq, you could have password information stored in $HOME/.pgpass,
+partially filling in right/wrong authentication information there.
+
+2.  Everything in my script _looks_ OK, and some data is getting
+pushed around, but not all of it.
+
+Slony logs might look like the following:
+
+DEBUG1 remoteListenThread_1: connected to 'host=host004 dbname=pgbenchrep user=postgres port=5432'
+ERROR  remoteListenThread_1: "select ev_origin, ev_seqno, ev_timestamp,        ev_minxid, ev_maxxid, ev_xip,        ev_type,        ev_data1, ev_data2,        ev_data3, ev_data4,        ev_data5, ev_data6,        ev_data7, ev_data8 from "_pgbenchtest".sl_event e where (e.ev_origin = '1' and e.ev_seqno > '1') order by e.ev_origin, e.ev_seqno" - could not receive data from server: Operation now in progress
+
+On AIX and Solaris (and possibly elsewhere), both Slony-I _and
+PostgreSQL_ must be compiled with the --enable-thread-safety option.
+The above results when PostgreSQL isn't so compiled.
+
+What happens is that the libc (threadsafe) and libpq (non-threadsafe)
+use different memory locations for errno, thereby leading to the
+request failing.
+
+Problems like this crop up with disadmirable regularity on AIX and
+Solaris; it may take something of an "object code audit" to make sure
+that ALL of the necessary components have been compiled and linked
+with --enable-thread-safety.
+
+For instance, I ran into the problem one that LD_LIBRARY_PATH had been
+set, on Solaris, to point to libraries from an old PostgreSQL compile.
+That meant that even though the database had been compiled with
+--enable-thread-safety, and slon had been compiled against that, slon
+was being dynamically linked to the "bad old thread-unsafe version,"
+so slon didn't work.  It wasn't clear until I ran "ldd" against slon.
+
+3.  I tried creating a CLUSTER NAME with a "-" in it.  That didn't work.
+
+Slony-I uses the same rules for unquoted identifiers as the PostgreSQL
+main parser, so no, you probably shouldn't put a "-" in your
+identifier name.
+
+You may be able to defeat this by putting "quotes" around identifier
+names, but it's liable to bite you some, so this is something that is
+probably not worth working around.
+
+4.  After an immediate stop of postgresql (simulation of system crash)
+in pg_catalog.pg_listener a tuple with
+relname='_${cluster_name}_Restart' exists. slon doesn't start cause it
+thinks another process is serving the cluster on this node.  What can
+I do? The tuples can't be dropped from this relation.
+
+The logs claim that "Another slon daemon is serving this node already"
+
+It's handy to keep a slonik script like the following one around to
+run in such cases:
+================================================================================
+twcsds004[/opt/twcsds004/OXRS/slony-scripts]$ cat restart_org.slonik 
+cluster name = oxrsorg ;
+node 1 admin conninfo = 'host=32.85.68.220 dbname=oxrsorg user=postgres port=5532';
+node 2 admin conninfo = 'host=32.85.68.216 dbname=oxrsorg user=postgres port=5532';
+node 3 admin conninfo = 'host=32.85.68.244 dbname=oxrsorg user=postgres port=5532';
+node 4 admin conninfo = 'host=10.28.103.132 dbname=oxrsorg user=postgres port=5532';
+restart node 1;
+restart node 2;
+restart node 3;
+restart node 4;
+================================================================================
+
+'restart node n' cleans this stuff up so that you can restart the
+node.
+
+5.  If I run a "ps" command, I, and everyone else, can see passwords
+on the command line.
+
+Take the passwords out of the Slony configuration, and put them into
+$(HOME)/.pgpass.
+
+6.  When I run the sample setup script I get an error message similar
+to:
+
+<stdin>:64: PGRES_FATAL_ERROR load '$libdir/xxid';  - ERROR:  LOAD:
+could not open file '$libdir/xxid': No such file or directory
+
+Evidently, you haven't got the xxid.so library in the $libdir
+directory that the PostgreSQL instance is using.  Note that the Slony
+components need to be installed on EACH ONE of the nodes, not just on
+the "master."
+
+This may also point to there being some other mismatch between the
+PostgreSQL binary instance and the Slony-I instance.  If you compiled
+Slony-I yourself, on a machine that may have multiple PostgreSQL
+builds "lying around," it's possible that the slon or slonik binaries
+are asking to load something that isn't actually in the library
+directory for the PostgreSQL database cluster that it's hitting.
+
+Long and short: This points to a need to "audit" what installations of
+PostgreSQL and Slony you have in place on the machine(s).
+Unfortunately, just about any mismatch will cause things not to link
+up quite right.  Look back at #2...
+
+7.  An oddity - no need for Fully Qualified Name for table keys...
+
+set add table (set id = 1, origin = 1, id = 27, full qualified name = 'nspace.some_table', key = 'key_on_whatever', 
+    comment = 'Table some_table in namespace nspace with a candidate primary key');
+
+If you have
+   key = 'nspace.key_on_whatever'
+the request will FAIL.
+
+8.  I'm trying to get a slave subscribed, and get the following
+messages in the logs:
+
+DEBUG1 copy_set 1
+DEBUG1 remoteWorkerThread_1: connected to provider DB
+WARN   remoteWorkerThread_1: transactions earlier than XID 127314958 are still in progress
+WARN   remoteWorkerThread_1: data copy for set 1 failed - sleep 60 seconds
+
+Oops.  What I forgot to mention, as well, was that I was trying to add
+TWO subscribers, concurrently.
+
+That doesn't work out: Slony-I won't work on the COPY commands
+concurrently.  See src/slon/remote_worker.c, function copy_set()
+
+This has the (perhaps unfortunate) implication that you cannot
+populate two slaves concurrently.  You have to subscribe one to the
+set, and only once it has completed setting up the subscription
+(copying table contents and such) can the second subscriber start
+setting up the subscription.
+
+It could also be possible for there to be an old outstanding
+transaction blocking Slony-I from processing the sync.  You might want
+to take a look at pg_locks to see what's up:
+
+sampledb=# select * from pg_locks where transaction is not null order by transaction;
+ relation | database | transaction |   pid   |     mode      | granted 
+----------+----------+-------------+---------+---------------+---------
+          |          |   127314921 | 2605100 | ExclusiveLock | t
+          |          |   127326504 | 5660904 | ExclusiveLock | t
+(2 rows)
+
+See?  127314921 is indeed older than 127314958, and it's still running.
+
+$ ps -aef | egrep '[2]605100'
+postgres 2605100  205018   0 18:53:43  pts/3  3:13 postgres: postgres sampledb localhost COPY 
+
+This happens to be a COPY transaction involved in setting up the
+subscription for one of the nodes.  All is well; the system is busy
+setting up the first subscriber; it won't start on the second one
+until the first one has completed subscribing.
+
+By the way, if there is more than one database on the PostgreSQL
+cluster, and activity is taking place on the OTHER database, that will
+lead to there being "transactions earlier than XID whatever" being
+found to be still in progress.  The fact that it's a separate database
+on the cluster is irrelevant; Slony-I will wait until those old
+transactions terminate.
+
+9.  I tried setting up a second replication set, and got the following error:
+
+<stdin>:9: Could not create subscription set 2 for oxrslive!
+<stdin>:11: PGRES_FATAL_ERROR select "_oxrslive".setAddTable(2, 1, 'public.replic_test', 'replic_test__Slony-I_oxrslive_rowID_key', 'Table public.replic_test without primary key');  - ERROR:  duplicate key violates unique constraint "sl_table-pkey"
+CONTEXT:  PL/pgSQL function "setaddtable_int" line 71 at SQL statement
+
+The table IDs used in SET ADD TABLE are required to be unique ACROSS
+ALL SETS.  Thus, you can't restart numbering at 1 for a second set; if
+you are numbering them consecutively, a subsequent set has to start
+with IDs after where the previous set(s) left off.
+
+10.  I need to drop a table from a replication set, and I can't see
+how to do that.
+
+This can be accomplished several ways, not all equally desirable ;-).
+
+a) You could drop the whole replication set, and recreate it with just
+the tables that you need.  Alas, that means recopying a whole lot of
+data, and kills the usability of the cluster on the rest of the set
+while that's happening.
+
+b) If you are running 1.0.3 or later, there is the command SET DROP
+TABLE, which will "do the trick."
+
+c) If you are still using 1.0.1 or 1.0.2, the _essential_
+functionality of SET DROP TABLE involves the functionality in
+droptable_int().  You can fiddle this by hand by finding the table ID
+for the table you want to get rid of, which you can find in sl_table,
+and then run the following three queries, on each host:
+
+  select _slonyschema.alterTableRestore(40);
+  select _slonyschema.tableDropKey(40);
+  delete from _slonyschema.sl_table where tab_id = 40;
+
+The schema will obviously depend on how you defined the Slony-I
+cluster.  The table ID, in this case, 40, will need to change to the
+ID of the table you want to have go away.
+
+You'll have to run these three queries on all of the nodes, preferably
+firstly on the "master" node, so that the dropping of this propagates
+properly.  Implementing this via a SLONIK statement with a new Slony
+event would do that.  Submitting the three queries using EXECUTE
+SCRIPT could do that.  Also possible would be to connect to each
+database and submit the queries by hand.
+
+11.  I want to stop replicating a sequence
+
+If you are running 1.0.3 or later, there is a SET DROP SEQUENCE
+command in Slonik to allow you to do this, parallelling SET DROP
+TABLE.
+
+If you are running 1.0.2 or earlier, the process is a bit more manual.
+
+Supposing I want to get rid of the two sequences listed below,
+whois_cachemgmt_seq and epp_whoi_cach_seq_, we start by needing the
+seq_id values.
+
+oxrsorg=# select * from _oxrsorg.sl_sequence  where seq_id in (93,59);
+ seq_id | seq_reloid | seq_set |             seq_comment             
+--------+------------+---------+-------------------------------------
+     93 |  107451516 |       1 | Sequence public.whois_cachemgmt_seq
+     59 |  107451860 |       1 | Sequence public.epp_whoi_cach_seq_
+(2 rows)
+
+
+The data that needs to be deleted to stop Slony from continuing to
+replicate these are thus:
+
+delete from _oxrsorg.sl_seqlog where seql_seqid in (93, 59);
+delete from _oxrsorg.sl_sequence where seq_id in (93,59);
+
+Those two queries could be submitted to all of the nodes via
+ddlscript() / EXECUTE SCRIPT, thus eliminating the sequence everywhere
+"at once."  Or they may be applied by hand to each of the nodes.
+
+Similarly to SET DROP TABLE, this should be in place for Slony-I version
+1.0.3 as SET DROP SEQUENCE.
+
+12.  I tried to add a table to a set, and got the following message:
+
+   Slony-I: cannot add table to currently subscribed set 1
+
+You cannot add tables to sets that already have subscribers.
+
+The workaround to this is to create ANOTHER set, add the new tables to
+that new set, subscribe the same nodes subscribing to "set 1" to the
+new set, and then merge the sets together.
+
+13.  Some nodes start consistently falling behind
+
+I have been running Slony-I on a node for a while, and am seeing
+system performance suffering.
+
+I'm seeing long running queries of the form:
+
+   fetch 100 from LOG;
+
+This is characteristic of pg_listener (which is the table containing
+NOTIFY data) having plenty of dead tuples in it.  That makes NOTIFY
+events take a long time, and causes the affected node to gradually
+fall further and further behind.
+
+You quite likely need to do a VACUUM FULL on pg_listener, to
+vigorously clean it out, and need to vacuum pg_listener really
+frequently.  Once every five minutes would likely be AOK.
+
+Slon daemons already vacuum a bunch of tables, and cleanup_thread.c
+contains a list of tables that are frequently vacuumed automatically.
+In Slony-I 1.0.2, pg_listener is not included.  In 1.0.3 and later, it
+is regularly vacuumed, so this should cease to be an issue.
+
+14.  I started doing a backup using pg_dump, and suddenly Slony stops
+replicating anything.
+
+Ouch.  What happens here is a conflict between:
+
+ a) pg_dump, which has taken out an AccessShareLock on all of the
+    tables in the database, including the Slony-I ones, and
+
+ b) A Slony-I sync event, which wants to grab a AccessExclusiveLock on
+    the table sl_event.
+
+The initial query that will be blocked is thus:
+
+    select "_slonyschema".createEvent('_slonyschema, 'SYNC', NULL);     
+
+(You can see this in pg_stat_activity, if you have query display
+turned on in postgresql.conf)
+
+The actual query combination that is causing the lock is from the
+function Slony_I_ClusterStatus(), found in slony1_funcs.c, and is
+localized in the code that does:
+
+  LOCK TABLE %s.sl_event;
+  INSERT INTO %s.sl_event (...stuff...)
+  SELECT currval('%s.sl_event_seq');
+
+The LOCK statement will sit there and wait until pg_dump (or whatever
+else has pretty much any kind of access lock on sl_event) completes.  
+
+Every subsequent query submitted that touches sl_event will block
+behind the createEvent call.
+
+There are a number of possible answers to this:
+
+ a) Have pg_dump specify the schema dumped using --schema=whatever,
+    and don't try dumping the cluster's schema.
+
+ b) It would be nice to add an "--exclude-schema" option to pg_dump to
+    exclude the Slony cluster schema.  Maybe in 8.0 or 8.1...
+
+Note that 1.0.3 uses a more precise lock that is less exclusive that
+should relieve this problem.
+
+15.  The slons spent the weekend out of commission [for some reason],
+and it's taking a long time to get a sync through.
+
+You might want to take a look at the sl_log_1/sl_log_2 tables, and do
+a summary to see if there are any really enormous Slony-I transactions
+in there.  Up until at least 1.0.2, there needs to be a slon connected
+to the master in order for SYNC events to be generated.
+
+If none are being generated, then all of the updates until the next
+one is generated will collect into one rather enormous Slony-I
+transaction.
+
+Conclusion: Even if there is not going to be a subscriber around, you
+_really_ want to have a slon running to service the "master" node.
+
+Some future version (probably 1.1) may provide a way for SYNC counts
+to be updated on the master by the stored function that is invoked by
+the table triggers.
+
+16.  I pointed a subscribing node to a different parent and it stopped
+replicating
+
+We noticed this happening when we wanted to re-initialize a node,
+where we had configuration thus:
+
+ Node 1 - master
+ Node 2 - child of node 1 - the node we're reinitializing
+ Node 3 - child of node 3 - node that should keep replicating
+
+The subscription for node 3 was changed to have node 1 as provider,
+and we did DROP SET/SUBSCRIBE SET for node 2 to get it repopulating.
+
+Unfortunately, replication suddenly stopped to node 3.
+
+The problem was that there was not a suitable set of "listener paths"
+in sl_listen to allow the events from node 1 to propagate to node 3.
+The events were going through node 2, and blocking behind the
+SUBSCRIBE SET event that node 2 was working on.
+
+The following slonik script dropped out the listen paths where node 3
+had to go through node 2, and added in direct listens between nodes 1
+and 3.
+
+cluster name = oxrslive;
+ node 1 admin conninfo='host=32.85.68.220 dbname=oxrslive user=postgres port=5432';
+ node 2 admin conninfo='host=32.85.68.216 dbname=oxrslive user=postgres port=5432';
+ node 3 admin conninfo='host=32.85.68.244 dbname=oxrslive user=postgres port=5432';
+ node 4 admin conninfo='host=10.28.103.132 dbname=oxrslive user=postgres port=5432';
+try {
+      store listen (origin = 1, receiver = 3, provider = 1);
+      store listen (origin = 3, receiver = 1, provider = 3);
+      drop listen (origin = 1, receiver = 3, provider = 2);
+      drop listen (origin = 3, receiver = 1, provider = 2);
+}
+
+Immediately after this script was run, SYNC events started propagating
+again to node 3.
+
+This points out two principles:
+
+ 1.  If you have multiple nodes, and cascaded subscribers, you need to
+     be quite careful in populating the STORE LISTEN entries, and in
+     modifying them if the structure of the replication "tree" changes.
+
+ 2.  Version 1.1 probably ought to provide better tools to help manage
+     this.
--- /dev/null
+++ doc/howto/schemadoc.html
@@ -0,0 +1,6273 @@
+<!-- $Header: /usr/local/cvsroot/slony1/slony1-engine/doc/howto/schemadoc.html,v 1.6.2.1 2004/09/30 17:45:05 cbbrowne Exp $ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+   "http://www.w3.org/TR/html4/strict.dtd">
+
+<html>
+  <head>
+    <title>Index for schemadoc</title>
+    <style type="text/css">
+	BODY {
+		color:	#000000; 
+		background-color: #FFFFFF;
+		font-family: Helvetica, sans-serif; 
+	}
+
+	P {
+		margin-top: 5px;
+		margin-bottom: 5px;
+	}
+
+	P.w3ref {
+		font-size: 8pt;
+		font-style: italic;
+		text-align: right;
+	}
+
+	P.detail {
+		font-size: 10pt;
+	}
+
+	.error {
+		color: #FFFFFF;
+		background-color: #FF0000;
+	}
+
+	H1, H2, H3, H4, H5, H6 {
+	}
+
+	OL {
+		list-style-type: upper-alpha;
+	}
+
+	UL.topic {
+		list-style-type: upper-alpha;
+	}
+
+	LI.topic {
+		font-weight : bold;
+	}
+
+	HR {
+		color: #00FF00;
+		background-color: #808080;
+	}
+
+	TABLE {
+		border-width: medium;
+		padding: 3px;
+		background-color: #000000;
+		width: 90%;
+	}
+
+	CAPTION {
+		text-transform: capitalize;
+		font-weight : bold;
+		font-size: 14pt;
+	}
+
+	TH {
+		color: #FFFFFF;
+		background-color: #000000;
+		text-align: left;
+	}
+
+	TR {
+		color: #000000;
+		background-color: #000000;
+		vertical-align: top;
+	}
+
+	TR.tr0 {
+		background-color: #F0F0F0;
+	}
+
+	TR.tr1 {
+		background-color: #D8D8D8;
+	}
+
+	TD {
+		font-size: 12pt;
+	}
+
+	TD.col0 {
+		font-weight : bold;
+		width: 20%;
+	}
+
+	TD.col1 {
+		font-style: italic;
+		width: 15%;
+	}
+
+	TD.col2 {
+		font-size: 12px;
+	}
+    </style>
+    <link rel="stylesheet" type="text/css" media="all" href="all.css">
+    <link rel="stylesheet" type="text/css" media="screen" href="screen.css">
+    <link rel="stylesheet" type="text/css" media="print" href="print.css">
+    <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+  </head>
+  <body>
+
+    <!-- Primary Index -->
+	<p><br><br>Dumped on 2004-09-29</p>
+<h1><a name="index">Index of database - schemadoc</a></h1>
+<ul>
+    
+    <li><a name="schemadoc.schema">schemadoc</a></li><ul>
+    	<li><a href="#schemadoc.table.sl-config-lock">sl_config_lock</a></li><li><a href="#schemadoc.table.sl-confirm">sl_confirm</a></li><li><a href="#schemadoc.table.sl-event">sl_event</a></li><li><a href="#schemadoc.table.sl-listen">sl_listen</a></li><li><a href="#schemadoc.table.sl-log-1">sl_log_1</a></li><li><a href="#schemadoc.table.sl-log-2">sl_log_2</a></li><li><a href="#schemadoc.table.sl-node">sl_node</a></li><li><a href="#schemadoc.table.sl-path">sl_path</a></li><li><a href="#schemadoc.view.sl-seqlastvalue">sl_seqlastvalue</a></li><li><a href="#schemadoc.table.sl-seqlog">sl_seqlog</a></li><li><a href="#schemadoc.table.sl-sequence">sl_sequence</a></li><li><a href="#schemadoc.table.sl-set">sl_set</a></li><li><a href="#schemadoc.table.sl-setsync">sl_setsync</a></li><li><a href="#schemadoc.table.sl-subscribe">sl_subscribe</a></li><li><a href="#schemadoc.table.sl-table">sl_table</a></li><li><a href="#schemadoc.table.sl-trigger">sl_trigger</a></li>
+  	<li><a href="#schemadoc.function.altertableforreplication-integer">altertableforreplication( integer )</a></li><li><a href="#schemadoc.function.altertablerestore-integer">altertablerestore( integer )</a></li><li><a href="#schemadoc.function.cleanupevent">cleanupevent(  )</a></li><li><a href="#schemadoc.function.ddlscript-integer-text-integer">ddlscript( integer, text, integer )</a></li><li><a href="#schemadoc.function.ddlscript-int-integer-text-integer">ddlscript_int( integer, text, integer )</a></li><li><a href="#schemadoc.function.determineattkindserial-text">determineattkindserial( text )</a></li><li><a href="#schemadoc.function.determineattkindunique-text-name">determineattkindunique( text, name )</a></li><li><a href="#schemadoc.function.determineidxnameserial-text">determineidxnameserial( text )</a></li><li><a href="#schemadoc.function.determineidxnameunique-text-name">determineidxnameunique( text, name )</a></li><li><a href="#schemadoc.function.disablenode-integer">disablenode( integer )</a></li><li><a href="#schemadoc.function.disablenode-int-integer">disablenode_int( integer )</a></li><li><a href="#schemadoc.function.droplisten-integer-integer-integer">droplisten( integer, integer, integer )</a></li><li><a href="#schemadoc.function.droplisten-int-integer-integer-integer">droplisten_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.dropnode-integer">dropnode( integer )</a></li><li><a href="#schemadoc.function.dropnode-int-integer">dropnode_int( integer )</a></li><li><a href="#schemadoc.function.droppath-integer-integer">droppath( integer, integer )</a></li><li><a href="#schemadoc.function.droppath-int-integer-integer">droppath_int( integer, integer )</a></li><li><a href="#schemadoc.function.dropset-integer">dropset( integer )</a></li><li><a href="#schemadoc.function.dropset-int-integer">dropset_int( integer )</a></li><li><a href="#schemadoc.function.droptrigger-integer-name">droptrigger( integer, name )</a></li><li><a href="#schemadoc.function.droptrigger-int-integer-name">droptrigger_int( integer, name )</a></li><li><a href="#schemadoc.function.enablenode-integer">enablenode( integer )</a></li><li><a href="#schemadoc.function.enablenode-int-integer">enablenode_int( integer )</a></li><li><a href="#schemadoc.function.enablesubscription-integer-integer-integer">enablesubscription( integer, integer, integer )</a></li><li><a href="#schemadoc.function.enablesubscription-int-integer-integer-integer">enablesubscription_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.failednode-integer-integer">failednode( integer, integer )</a></li><li><a href="#schemadoc.function.failednode2-integer-integer-integer-bigint-bigint">failednode2( integer, integer, integer, bigint, bigint )</a></li><li><a href="#schemadoc.function.failoverset-int-integer-integer-integer">failoverset_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.forwardconfirm-integer-integer-bigint-timestamp-without-time-zone">forwardconfirm( integer, integer, bigint, timestamp without time zone )</a></li><li><a href="#schemadoc.function.initializelocalnode-integer-text">initializelocalnode( integer, text )</a></li><li><a href="#schemadoc.function.lockset-integer">lockset( integer )</a></li><li><a href="#schemadoc.function.mergeset-integer-integer">mergeset( integer, integer )</a></li><li><a href="#schemadoc.function.mergeset-int-integer-integer">mergeset_int( integer, integer )</a></li><li><a href="#schemadoc.function.moveset-integer-integer">moveset( integer, integer )</a></li><li><a href="#schemadoc.function.moveset-int-integer-integer-integer">moveset_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.sequencelastvalue-text">sequencelastvalue( text )</a></li><li><a href="#schemadoc.function.sequencesetvalue-integer-integer-bigint-bigint">sequencesetvalue( integer, integer, bigint, bigint )</a></li><li><a href="#schemadoc.function.setaddsequence-integer-integer-text-text">setaddsequence( integer, integer, text, text )</a></li><li><a href="#schemadoc.function.setaddsequence-int-integer-integer-text-text">setaddsequence_int( integer, integer, text, text )</a></li><li><a href="#schemadoc.function.setaddtable-integer-integer-text-name-text">setaddtable( integer, integer, text, name, text )</a></li><li><a href="#schemadoc.function.setaddtable-int-integer-integer-text-name-text">setaddtable_int( integer, integer, text, name, text )</a></li><li><a href="#schemadoc.function.setdropsequence-integer">setdropsequence( integer )</a></li><li><a href="#schemadoc.function.setdropsequence-int-integer">setdropsequence_int( integer )</a></li><li><a href="#schemadoc.function.setdroptable-integer">setdroptable( integer )</a></li><li><a href="#schemadoc.function.setdroptable-int-integer">setdroptable_int( integer )</a></li><li><a href="#schemadoc.function.slonyversion">slonyversion(  )</a></li><li><a href="#schemadoc.function.slonyversionmajor">slonyversionmajor(  )</a></li><li><a href="#schemadoc.function.slonyversionminor">slonyversionminor(  )</a></li><li><a href="#schemadoc.function.slonyversionpatchlevel">slonyversionpatchlevel(  )</a></li><li><a href="#schemadoc.function.storelisten-integer-integer-integer">storelisten( integer, integer, integer )</a></li><li><a href="#schemadoc.function.storelisten-int-integer-integer-integer">storelisten_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.storenode-integer-text">storenode( integer, text )</a></li><li><a href="#schemadoc.function.storenode-int-integer-text">storenode_int( integer, text )</a></li><li><a href="#schemadoc.function.storepath-integer-integer-text-integer">storepath( integer, integer, text, integer )</a></li><li><a href="#schemadoc.function.storepath-int-integer-integer-text-integer">storepath_int( integer, integer, text, integer )</a></li><li><a href="#schemadoc.function.storeset-integer-text">storeset( integer, text )</a></li><li><a href="#schemadoc.function.storeset-int-integer-integer-text">storeset_int( integer, integer, text )</a></li><li><a href="#schemadoc.function.storetrigger-integer-name">storetrigger( integer, name )</a></li><li><a href="#schemadoc.function.storetrigger-int-integer-name">storetrigger_int( integer, name )</a></li><li><a href="#schemadoc.function.subscribeset-integer-integer-integer-boolean">subscribeset( integer, integer, integer, boolean )</a></li><li><a href="#schemadoc.function.subscribeset-int-integer-integer-integer-boolean">subscribeset_int( integer, integer, integer, boolean )</a></li><li><a href="#schemadoc.function.tableaddkey-text">tableaddkey( text )</a></li><li><a href="#schemadoc.function.tabledropkey-integer">tabledropkey( integer )</a></li><li><a href="#schemadoc.function.tablehasserialkey-text">tablehasserialkey( text )</a></li><li><a href="#schemadoc.function.uninstallnode">uninstallnode(  )</a></li><li><a href="#schemadoc.function.unlockset-integer">unlockset( integer )</a></li><li><a href="#schemadoc.function.unsubscribeset-integer-integer">unsubscribeset( integer, integer )</a></li><li><a href="#schemadoc.function.unsubscribeset-int-integer-integer">unsubscribeset_int( integer, integer )</a></li>
+    </ul>
+    
+</ul>
+
+    <!-- Schema Creation -->
+    <!-- schemadocschemadoc -->
+
+		
+		
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-config-lock">sl_config_lock</a>
+		</h2>
+        
+         <p>This table exists solely to prevent overlapping execution of configuration change procedures and the resulting possible deadlocks.
+</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_config_lock Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>dummy</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-confirm">sl_confirm</a>
+		</h2>
+        
+         <p>Holds confirmation of replication events.  After a period of time, Slony removes old confirmed events from both this table and the sl_event table.</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_confirm Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>con_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The ID # (from sl_node.no_id) of the source node for this event
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>con_received</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>con_seqno</td>
+            	<td>bigint</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The ID # for the event
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>con_timestamp</td>
+            	<td>timestamp without time zone</td>
+                <td><i>
+				
+
+				
+				DEFAULT (timeofday())::timestamp without time zone
+				</i>
+				<br><br>When this event was confirmed
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-event">sl_event</a>
+		</h2>
+        
+         <p>Holds information about replication events.  After a period of time, Slony removes old confirmed events from both this table and the sl_confirm table.</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_event Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>ev_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The ID # (from sl_node.no_id) of the source node for this event
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>ev_seqno</td>
+            	<td>bigint</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The ID # for the event
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ev_timestamp</td>
+            	<td>timestamp without time zone</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>When this event record was created
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ev_minxid</td>
+            	<td>schemadoc.xxid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Earliest XID on provider node for this event
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ev_maxxid</td>
+            	<td>schemadoc.xxid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Latest XID on provider node for this event
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ev_xip</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>TBD
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ev_type</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The type of event this record is for.  
+				SYNC				= Synchronise
+				STORE_NODE			=
+				ENABLE_NODE			=
+				DROP_NODE			=
+				STORE_PATH			=
+				DROP_PATH			=
+				STORE_LISTEN		=
+				DROP_LISTEN			=
+				STORE_SET			=
+				DROP_SET			=
+				MERGE_SET			=
+				SET_ADD_TABLE		=
+				SET_ADD_SEQUENCE	=
+				STORE_TRIGGER		=
+				DROP_TRIGGER		=
+				MOVE_SET			=
+				FAILOVER_SET		=
+				SUBSCRIBE_SET		=
+				ENABLE_SUBSCRIPTION	=
+				UNSUBSCRIBE_SET		=
+				DDL_SCRIPT			=
+				ADJUST_SEQ			=
+
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ev_data1</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ev_data2</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ev_data3</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ev_data4</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ev_data5</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ev_data6</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ev_data7</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ev_data8</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Data field containing an argument needed to process the event
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-listen">sl_listen</a>
+		</h2>
+        
+         <p>Indicates how nodes listen to events from other nodes in the Slony-I network.</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_listen Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-node">sl_node.no_id</a>
+                  
+                
+                  
+                
+                </td>
+            	<td>li_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The ID # (from sl_node.no_id) of the node this listener is operating on
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                
+                  
+                  <a href="#schemadoc.table.sl-path">sl_path.pa_server#1</a>
+                  
+                
+                </td>
+            	<td>li_provider</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The ID # (from sl_node.no_id) of the source node for this listening event
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                  
+                  <a href="#schemadoc.table.sl-path">sl_path.pa_client#1</a>
+                  
+                
+                </td>
+            	<td>li_receiver</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The ID # (from sl_node.no_id) of the target node for this listening event
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-log-1">sl_log_1</a>
+		</h2>
+        
+         <p>Stores each change to be propagated to subscriber nodes</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_log_1 Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>log_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Origin node from which the change came
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>log_xid</td>
+            	<td>schemadoc.xxid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Transaction ID on the origin node
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>log_tableid</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The table ID (from sl_table.tab_id) that this log entry is to affect
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>log_actionseq</td>
+            	<td>bigint</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>log_cmdtype</td>
+            	<td>character(1)</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Replication action to take. U = Update, I = Insert, D = DELETE
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>log_cmddata</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The data needed to perform the log action
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-log-2">sl_log_2</a>
+		</h2>
+        
+         <p>Stores each change to be propagated to subscriber nodes</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_log_2 Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>log_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Origin node from which the change came
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>log_xid</td>
+            	<td>schemadoc.xxid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Transaction ID on the origin node
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>log_tableid</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The table ID (from sl_table.tab_id) that this log entry is to affect
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>log_actionseq</td>
+            	<td>bigint</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>log_cmdtype</td>
+            	<td>character(1)</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Replication action to take. U = Update, I = Insert, D = DELETE
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>log_cmddata</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The data needed to perform the log action
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-node">sl_node</a>
+		</h2>
+        
+         <p>Holds the list of nodes associated with this namespace.</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_node Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>no_id</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The unique ID number for the node
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>no_active</td>
+            	<td>boolean</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>no_comment</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>A human-oriented description of the node
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+			<p>Tables referencing this one via Foreign Key Constraints:</p>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-listen">sl_listen</a></li>
+			</ul>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-path">sl_path</a></li>
+			</ul>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-set">sl_set</a></li>
+			</ul>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-setsync">sl_setsync</a></li>
+			</ul>
+		
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-path">sl_path</a>
+		</h2>
+        
+         <p>Holds connection information for the paths between nodes, and the synchronisation delay</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_path Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-node">sl_node.no_id</a>
+                  
+                
+                  
+                
+                </td>
+            	<td>pa_server</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The Node ID # (from sl_node.no_id) of the data source
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-node">sl_node.no_id</a>
+                  
+                
+                  
+                
+                </td>
+            	<td>pa_client</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The Node ID # (from sl_node.no_id) of the data target
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>pa_conninfo</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				NOT NULL
+				
+				</i>
+				<br><br>The PostgreSQL connection string used to connect to the source node.
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>pa_connretry</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>The synchronisation delay, in seconds
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+			<p>Tables referencing this one via Foreign Key Constraints:</p>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-listen">sl_listen</a></li>
+			</ul>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-subscribe">sl_subscribe</a></li>
+			</ul>
+		
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>View:
+			<a name="schemadoc.view.sl-seqlastvalue">sl_seqlastvalue</a>
+		</h2>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_seqlastvalue Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>seq_id</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>seq_set</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>seq_reloid</td>
+            	<td>oid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>seq_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>seq_last_value</td>
+            	<td>bigint</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+	<pre>
+SELECT sq.seq_id
+, sq.seq_set
+, sq.seq_reloid
+, s.set_origin AS seq_origin
+, schemadoc.sequencelastvalue
+(
+     (
+           (quote_ident
+                 (
+                       (pgn.nspname)::text
+                 ) || &#39;.&#39;::text
+           ) || quote_ident
+           (
+                 (pgc.relname)::text
+           )
+     )
+) AS seq_last_value 
+FROM schemadoc.sl_sequence sq
+, schemadoc.sl_set s
+, pg_class pgc
+, pg_namespace pgn 
+WHERE (
+     (
+           (s.set_id = sq.seq_set)
+         AND (pgc.oid = sq.seq_reloid)
+     )
+   AND (pgn.oid = pgc.relnamespace)
+);</pre>
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-seqlog">sl_seqlog</a>
+		</h2>
+        
+         <p>Log of Sequence updates</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_seqlog Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>seql_seqid</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Sequence ID
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>seql_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Publisher node at which the sequence originates
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>seql_ev_seqno</td>
+            	<td>bigint</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>TBD
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>seql_last_value</td>
+            	<td>bigint</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Last value published for this sequence
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-sequence">sl_sequence</a>
+		</h2>
+        
+         <p>Similar to sl_table, each entry identifies a sequence being replicated.</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_sequence Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>seq_id</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>An internally-used ID for Slony-I to use in its sequencing of updates
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>seq_reloid</td>
+            	<td>oid</td>
+                <td><i>
+				
+					
+
+					
+                       UNIQUE
+                    
+				
+
+				NOT NULL
+				
+				</i>
+				<br><br>The OID of the sequence object
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-set">sl_set.set_id</a>
+                  
+                
+                </td>
+            	<td>seq_set</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>Indicates which replication set the object is in
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>seq_comment</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>A human-oriented comment
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-set">sl_set</a>
+		</h2>
+        
+         <p>Holds definitions of replication sets.</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_set Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>set_id</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>A unique ID number for the set.
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-node">sl_node.no_id</a>
+                  
+                
+                </td>
+            	<td>set_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>The ID number of the source node for the replication set.
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>set_locked</td>
+            	<td>schemadoc.xxid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Indicates whether or not the set is locked.
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>set_comment</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>A human-oriented description of the set.
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+			<p>Tables referencing this one via Foreign Key Constraints:</p>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-sequence">sl_sequence</a></li>
+			</ul>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-setsync">sl_setsync</a></li>
+			</ul>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-subscribe">sl_subscribe</a></li>
+			</ul>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-table">sl_table</a></li>
+			</ul>
+		
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-setsync">sl_setsync</a>
+		</h2>
+        
+         <p>Not documented yet</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_setsync Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                  
+                  <a href="#schemadoc.table.sl-set">sl_set.set_id</a>
+                  
+                
+                </td>
+            	<td>ssy_setid</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>ID number of the replication set
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-node">sl_node.no_id</a>
+                  
+                
+                </td>
+            	<td>ssy_origin</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>ID number of the node
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ssy_seqno</td>
+            	<td>bigint</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Slony-I sequence number
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ssy_minxid</td>
+            	<td>schemadoc.xxid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Earliest XID in provider system affected by SYNC
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ssy_maxxid</td>
+            	<td>schemadoc.xxid</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Latest XID in provider system affected by SYNC
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>ssy_xip</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>TBD
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>ssy_action_list</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>TBD
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-subscribe">sl_subscribe</a>
+		</h2>
+        
+         <p>Holds a list of subscriptions on sets</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_subscribe Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                  
+                  <a href="#schemadoc.table.sl-set">sl_set.set_id</a>
+                  
+                
+                </td>
+            	<td>sub_set</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>ID # (from sl_set) of the set being subscribed to
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-path">sl_path.pa_server#1</a>
+                  
+                
+                </td>
+            	<td>sub_provider</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>ID# (from sl_node) of the node providing data
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                  
+                  <a href="#schemadoc.table.sl-path">sl_path.pa_client#1</a>
+                  
+                
+                </td>
+            	<td>sub_receiver</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>ID# (from sl_node) of the node receiving data from the provider
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>sub_forward</td>
+            	<td>boolean</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Does this provider keep data in sl_log_1/sl_log_2 to allow it to be a provider for other nodes?
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>sub_active</td>
+            	<td>boolean</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Has this subscription been activated?  This is not set until the subscriber has received COPY data from the provider
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-table">sl_table</a>
+		</h2>
+        
+         <p>Holds information about the tables being replicated.</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_table Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>tab_id</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>Unique key for Slony-I to use to identify the table
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>tab_reloid</td>
+            	<td>oid</td>
+                <td><i>
+				
+					
+
+					
+                       UNIQUE
+                    
+				
+
+				NOT NULL
+				
+				</i>
+				<br><br>The OID of the table in pg_catalog.pg_class.oid
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                  <a href="#schemadoc.table.sl-set">sl_set.set_id</a>
+                  
+                
+                </td>
+            	<td>tab_set</td>
+            	<td>integer</td>
+                <td><i>
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>tab_idxname</td>
+            	<td>name</td>
+                <td><i>
+				
+
+				NOT NULL
+				
+				</i>
+				<br><br>The name of the primary index of the table
+				</td>
+			 </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                </td>
+            	<td>tab_altered</td>
+            	<td>boolean</td>
+                <td><i>
+				
+
+				NOT NULL
+				
+				</i>
+				
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                </td>
+            	<td>tab_comment</td>
+            	<td>text</td>
+                <td><i>
+				
+
+				
+				
+				</i>
+				<br><br>Human-oriented description of the table
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+			<p>Tables referencing this one via Foreign Key Constraints:</p>
+		
+			<ul>	
+				<li><a href="#schemadoc.table.sl-trigger">sl_trigger</a></li>
+			</ul>
+		
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+        <hr>
+		<h2>Table:
+			<a name="schemadoc.table.sl-trigger">sl_trigger</a>
+		</h2>
+        
+         <p>Holds information about triggers on tables managed using Slony-I</p>
+        
+
+
+        <table width="100%" cellspacing="0" cellpadding="3">
+                <caption>sl_trigger Structure</caption>
+                <tr>
+                <th>F-Key</th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Description</th>
+                </tr>
+            
+            <tr class="tr0">
+				<td>
+                
+                  
+                
+                  
+                  <a href="#schemadoc.table.sl-table">sl_table.tab_id</a>
+                  
+                
+                </td>
+            	<td>trig_tabid</td>
+            	<td>integer</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>Slony-I ID number of table the trigger is on
+				</td>
+			 </tr>
+            
+            <tr class="tr1">
+				<td>
+                
+                  
+                
+                </td>
+            	<td>trig_tgname</td>
+            	<td>name</td>
+                <td><i>
+				
+					PRIMARY KEY
+					
+
+					
+				
+
+				
+				
+				</i>
+				<br><br>Indicates the name of a trigger
+				</td>
+			 </tr>
+            
+        </table>
+
+		
+		
+		
+
+        <!-- Constraint List -->
+		
+
+        <!-- Foreign Key Discovery -->
+		
+
+	<!-- View Definition -->
+	
+
+	<!-- List off permissions -->
+	
+
+	<p>
+		<a href="#index">Index</a> -
+		<a href="#schemadoc.schema">Schema schemadoc</a>
+    </p>
+	
+
+	<!-- We've gone through the table structure, now lets take a look at user functions -->
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.altertableforreplication-integer">altertableforreplication( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>alterTableForReplication(tab_id)
+
+Sets up a table for replication.
+On the origin, this involves adding the &quot;logTrigger()&quot; trigger to the
+table.
+
+On a subscriber node, this involves disabling triggers and rules, and
+adding in the trigger that denies write access to replicated tables.</p>
+        <pre>
+declare
+	p_tab_id			alias for $1;
+	v_no_id				int4;
+	v_tab_row			record;
+	v_tab_fqname		text;
+	v_tab_attkind		text;
+	v_n					int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Get our local node ID
+	-- ----
+	v_no_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+
+	-- ----
+	-- Get the sl_table row and the current origin of the table. 
+	-- Verify that the table currently is NOT in altered state.
+	-- ----
+	select T.tab_reloid, T.tab_set, T.tab_idxname, T.tab_altered,
+			S.set_origin, PGX.indexrelid,
+			&quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			&quot;pg_catalog&quot;.quote_ident(PGC.relname) as tab_fqname
+			into v_tab_row
+			from schemadoc.sl_table T, schemadoc.sl_set S,
+				&quot;pg_catalog&quot;.pg_class PGC, &quot;pg_catalog&quot;.pg_namespace PGN,
+				&quot;pg_catalog&quot;.pg_index PGX, &quot;pg_catalog&quot;.pg_class PGXC
+			where T.tab_id = p_tab_id
+				and T.tab_set = S.set_id
+				and T.tab_reloid = PGC.oid
+				and PGC.relnamespace = PGN.oid
+				and PGX.indrelid = T.tab_reloid
+				and PGX.indexrelid = PGXC.oid
+				and PGXC.relname = T.tab_idxname
+				for update;
+	if not found then
+		raise exception &#39;Slony-I: Table with id % not found&#39;, p_tab_id;
+	end if;
+	v_tab_fqname = v_tab_row.tab_fqname;
+	if v_tab_row.tab_altered then
+		raise exception &#39;Slony-I: Table % is already in altered state&#39;,
+				v_tab_fqname;
+	end if;
+
+	v_tab_attkind := schemadoc.determineAttKindUnique(v_tab_row.tab_fqname, 
+						v_tab_row.tab_idxname);
+
+	execute &#39;lock table &#39; || v_tab_fqname || &#39; in access exclusive mode&#39;;
+
+	-- ----
+	-- Procedures are different on origin and subscriber
+	-- ----
+	if v_no_id = v_tab_row.set_origin then
+		-- ----
+		-- On the Origin we add the log trigger to the table and done
+		-- ----
+		execute &#39;create trigger &quot;_schemadoc_logtrigger_&#39; || 
+				p_tab_id || &#39;&quot; after insert or update or delete on &#39; ||
+				v_tab_fqname || &#39; for each row execute procedure
+				schemadoc.logTrigger (&#39;&#39;_schemadoc&#39;&#39;, &#39;&#39;&#39; || 
+					p_tab_id || &#39;&#39;&#39;, &#39;&#39;&#39; || 
+					v_tab_attkind || &#39;&#39;&#39;);&#39;;
+	else
+		-- ----
+		-- On the subscriber the thing is a bit more difficult. We want
+		-- to disable all user- and foreign key triggers and rules.
+		-- ----
+
+
+		-- ----
+		-- Disable all existing triggers
+		-- ----
+		update &quot;pg_catalog&quot;.pg_trigger
+				set tgrelid = v_tab_row.indexrelid
+				where tgrelid = v_tab_row.tab_reloid
+				and not exists (
+						select true from schemadoc.sl_table TAB,
+								schemadoc.sl_trigger TRIG
+								where TAB.tab_reloid = tgrelid
+								and TAB.tab_id = TRIG.trig_tabid
+								and TRIG.trig_tgname = tgname
+					);
+		get diagnostics v_n = row_count;
+		if v_n &gt; 0 then
+			update &quot;pg_catalog&quot;.pg_class
+					set reltriggers = reltriggers - v_n
+					where oid = v_tab_row.tab_reloid;
+		end if;
+
+		-- ----
+		-- Disable all existing rules
+		-- ----
+		update &quot;pg_catalog&quot;.pg_rewrite
+				set ev_class = v_tab_row.indexrelid
+				where ev_class = v_tab_row.tab_reloid;
+		get diagnostics v_n = row_count;
+		if v_n &gt; 0 then
+			update &quot;pg_catalog&quot;.pg_class
+					set relhasrules = false
+					where oid = v_tab_row.tab_reloid;
+		end if;
+
+		-- ----
+		-- Add the trigger that denies write access to replicated tables
+		-- ----
+		execute &#39;create trigger &quot;_schemadoc_denyaccess_&#39; || 
+				p_tab_id || &#39;&quot; before insert or update or delete on &#39; ||
+				v_tab_fqname || &#39; for each row execute procedure
+				schemadoc.denyAccess (&#39;&#39;_schemadoc&#39;&#39;);&#39;;
+	end if;
+
+	-- ----
+	-- Mark the table altered in our configuration
+	-- ----
+	update schemadoc.sl_table
+			set tab_altered = true where tab_id = p_tab_id;
+
+	return p_tab_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.altertablerestore-integer">altertablerestore( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>alterTableRestore (tab_id)
+
+Restores table tab_id from being replicated.
+
+On the origin, this simply involves dropping the &quot;logtrigger&quot; trigger.
+
+On subscriber nodes, this involves dropping the &quot;denyaccess&quot; trigger,
+and restoring user triggers and rules.</p>
+        <pre>
+declare
+	p_tab_id			alias for $1;
+	v_no_id				int4;
+	v_tab_row			record;
+	v_tab_fqname		text;
+	v_n					int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Get our local node ID
+	-- ----
+	v_no_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+
+	-- ----
+	-- Get the sl_table row and the current tables origin. Check
+	-- that the table currently IS in altered state.
+	-- ----
+	select T.tab_reloid, T.tab_set, T.tab_altered,
+			S.set_origin, PGX.indexrelid,
+			&quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			&quot;pg_catalog&quot;.quote_ident(PGC.relname) as tab_fqname
+			into v_tab_row
+			from schemadoc.sl_table T, schemadoc.sl_set S,
+				&quot;pg_catalog&quot;.pg_class PGC, &quot;pg_catalog&quot;.pg_namespace PGN,
+				&quot;pg_catalog&quot;.pg_index PGX, &quot;pg_catalog&quot;.pg_class PGXC
+			where T.tab_id = p_tab_id
+				and T.tab_set = S.set_id
+				and T.tab_reloid = PGC.oid
+				and PGC.relnamespace = PGN.oid
+				and PGX.indrelid = T.tab_reloid
+				and PGX.indexrelid = PGXC.oid
+				and PGXC.relname = T.tab_idxname
+				for update;
+	if not found then
+		raise exception &#39;Slony-I: Table with id % not found&#39;, p_tab_id;
+	end if;
+	v_tab_fqname = v_tab_row.tab_fqname;
+	if not v_tab_row.tab_altered then
+		raise exception &#39;Slony-I: Table % is not in altered state&#39;,
+				v_tab_fqname;
+	end if;
+
+	execute &#39;lock table &#39; || v_tab_fqname || &#39; in access exclusive mode&#39;;
+
+	-- ----
+	-- Procedures are different on origin and subscriber
+	-- ----
+	if v_no_id = v_tab_row.set_origin then
+		-- ----
+		-- On the Origin we just drop the trigger we originally added
+		-- ----
+		execute &#39;drop trigger &quot;_schemadoc_logtrigger_&#39; || 
+				p_tab_id || &#39;&quot; on &#39; || v_tab_fqname;
+	else
+		-- ----
+		-- On the subscriber drop the denyAccess trigger
+		-- ----
+		execute &#39;drop trigger &quot;_schemadoc_denyaccess_&#39; || 
+				p_tab_id || &#39;&quot; on &#39; || v_tab_fqname;
+				
+		-- ----
+		-- Restore all original triggers
+		-- ----
+		update &quot;pg_catalog&quot;.pg_trigger
+				set tgrelid = v_tab_row.tab_reloid
+				where tgrelid = v_tab_row.indexrelid;
+		get diagnostics v_n = row_count;
+		if v_n &gt; 0 then
+			update &quot;pg_catalog&quot;.pg_class
+					set reltriggers = reltriggers + v_n
+					where oid = v_tab_row.tab_reloid;
+		end if;
+
+		-- ----
+		-- Restore all original rewrite rules
+		-- ----
+		update &quot;pg_catalog&quot;.pg_rewrite
+				set ev_class = v_tab_row.tab_reloid
+				where ev_class = v_tab_row.indexrelid;
+		get diagnostics v_n = row_count;
+		if v_n &gt; 0 then
+			update &quot;pg_catalog&quot;.pg_class
+					set relhasrules = true
+					where oid = v_tab_row.tab_reloid;
+		end if;
+
+	end if;
+
+	-- ----
+	-- Mark the table not altered in our configuration
+	-- ----
+	update schemadoc.sl_table
+			set tab_altered = false where tab_id = p_tab_id;
+
+	return p_tab_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.cleanupevent">cleanupevent(  )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>cleaning old data out of sl_confirm, sl_event.  Removes all but the
+last sl_confirm row per (origin,receiver), and then removes all events
+that are confirmed by all nodes in the whole cluster up to the last
+SYNC.  </p>
+        <pre>
+declare
+	v_max_row	record;
+	v_min_row	record;
+	v_max_sync	int8;
+begin
+	-- ----
+	-- First remove all but the oldest confirm row per origin,receiver pair
+	-- ----
+	delete from schemadoc.sl_confirm
+				where con_origin not in (select no_id from schemadoc.sl_node);
+	delete from schemadoc.sl_confirm
+				where con_received not in (select no_id from schemadoc.sl_node);
+	-- ----
+	-- Next remove all but the oldest confirm row per origin,receiver pair.
+	-- Ignore confirmations that are younger than 10 minutes. We currently
+	-- have an not confirmed suspicion that a possibly lost transaction due
+	-- to a server crash might have been visible to another session, and
+	-- that this led to log data that is needed again got removed.
+	-- ----
+	for v_max_row in select con_origin, con_received, max(con_seqno) as con_seqno
+				from schemadoc.sl_confirm
+				where con_timestamp &lt; (CURRENT_TIMESTAMP - &#39;10 min&#39;::interval)
+				group by con_origin, con_received
+	loop
+		delete from schemadoc.sl_confirm
+				where con_origin = v_max_row.con_origin
+				and con_received = v_max_row.con_received
+				and con_seqno &lt; v_max_row.con_seqno;
+	end loop;
+
+	-- ----
+	-- Then remove all events that are confirmed by all nodes in the
+	-- whole cluster up to the last SYNC
+	-- ----
+	for v_min_row in select con_origin, min(con_seqno) as con_seqno
+				from schemadoc.sl_confirm
+				group by con_origin
+	loop
+		select coalesce(max(ev_seqno), 0) into v_max_sync
+				from schemadoc.sl_event
+				where ev_origin = v_min_row.con_origin
+				and ev_seqno &lt;= v_min_row.con_seqno
+				and ev_type = &#39;SYNC&#39;;
+		if v_max_sync &gt; 0 then
+			delete from schemadoc.sl_event
+					where ev_origin = v_min_row.con_origin
+					and ev_seqno &lt; v_max_sync;
+		end if;
+	end loop;
+
+	return 0;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.ddlscript-integer-text-integer">ddlscript( integer, text, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>ddlScript(set_id, script, only_on_node)
+
+Generates a SYNC event, runs the script on the origin, and then
+generates a DDL_SCRIPT event to request it to be run on replicated
+slaves.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_script			alias for $2;
+	p_only_on_node		alias for $3;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that the set exists and originates here
+	-- ----
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_set_id
+			for update;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_set_id;
+	end if;
+	if v_set_origin &lt;&gt; schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: set % does not originate on local node&#39;,
+				p_set_id;
+	end if;
+
+	-- ----
+	-- Create a SYNC event, run the script and generate the DDL_SCRIPT event
+	-- ----
+	perform schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SYNC&#39;, NULL);
+	perform schemadoc.ddlScript_int(p_set_id, p_script, p_only_on_node);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;DDL_SCRIPT&#39;, 
+			p_set_id, p_script, p_only_on_node);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.ddlscript-int-integer-text-integer">ddlscript_int( integer, text, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>ddlScript_int(set_id, script, only_on_node)
+
+Processes the DDL_SCRIPT event.  On slave nodes, this restores
+original triggers/rules, runs the script, and then puts tables back
+into replicated mode.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_script			alias for $2;
+	p_only_on_node		alias for $3;
+	v_set_origin		int4;
+	v_no_id				int4;
+	v_row				record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that we either are the set origin or a current
+	-- subscriber of the set.
+	-- ----
+	v_no_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_set_id
+			for update;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_set_id;
+	end if;
+	if v_set_origin &lt;&gt; v_no_id
+			and not exists (select 1 from schemadoc.sl_subscribe
+						where sub_set = p_set_id
+						and sub_receiver = v_no_id)
+	then
+		return 0;
+	end if;
+
+	-- ----
+	-- If execution on only one node is requested, check that
+	-- we are that node.
+	-- ----
+	if p_only_on_node &gt; 0 and p_only_on_node &lt;&gt; v_no_id then
+		return 0;
+	end if;
+
+	-- ----
+	-- Restore all original triggers and rules
+	-- ----
+	for v_row in select * from schemadoc.sl_table
+			where tab_set = p_set_id
+	loop
+		perform schemadoc.alterTableRestore(v_row.tab_id);
+	end loop;
+
+	-- ----
+	-- Run the script
+	-- ----
+	execute p_script;
+
+	-- ----
+	-- Put all tables back into replicated mode
+	-- ----
+	for v_row in select * from schemadoc.sl_table
+			where tab_set = p_set_id
+	loop
+		perform schemadoc.alterTableForReplication(v_row.tab_id);
+	end loop;
+
+	return p_set_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.determineattkindserial-text">determineattkindserial( text )</a>
+		</h2>
+<h3>Returns: text</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>determineAttKindSerial (tab_fqname)
+
+A table was that was specified without a primary key is added to the
+replication. Assume that tableAddKey() was called before and finish
+the creation of the serial column. The return an attkind according to
+that.</p>
+        <pre>
+declare
+	p_tab_fqname	alias for $1;
+	v_attkind		text default &#39;&#39;;
+	v_attrow		record;
+	v_have_serial	bool default &#39;f&#39;;
+begin
+	--
+	-- Loop over the attributes of this relation
+	-- and add a &quot;v&quot; for every user column, and a &quot;k&quot;
+	-- if we find the Slony-I special serial column.
+	--
+	for v_attrow in select PGA.attnum, PGA.attname
+			from &quot;pg_catalog&quot;.pg_class PGC,
+			    &quot;pg_catalog&quot;.pg_namespace PGN,
+				&quot;pg_catalog&quot;.pg_attribute PGA
+			where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			    &quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+				and PGN.oid = PGC.relnamespace
+				and PGA.attrelid = PGC.oid
+				and not PGA.attisdropped
+				and PGA.attnum &gt; 0
+			order by attnum
+	loop
+		if v_attrow.attname = &#39;_Slony-I_schemadoc_rowID&#39; then
+		    v_attkind := v_attkind || &#39;k&#39;;
+			v_have_serial := &#39;t&#39;;
+		else
+			v_attkind := v_attkind || &#39;v&#39;;
+		end if;
+	end loop;
+	
+	--
+	-- A table must have at least one attribute, so not finding
+	-- anything means the table does not exist.
+	--
+	if not found then
+		raise exception &#39;Slony-I: table % not found&#39;, p_tab_fqname;
+	end if;
+
+	--
+	-- If it does not have the special serial column, we
+	-- should not have been called in the first place.
+	--
+	if not v_have_serial then
+		raise exception &#39;Slony-I: table % does not have the serial key&#39;,
+				p_tab_fqname;
+	end if;
+
+	execute &#39;update &#39; || p_tab_fqname ||
+		&#39; set &quot;_Slony-I_schemadoc_rowID&quot; =&#39; ||
+		&#39; &quot;pg_catalog&quot;.nextval(&#39;&#39;schemadoc.sl_rowid_seq&#39;&#39;);&#39;;
+	execute &#39;alter table only &#39; || p_tab_fqname ||
+		&#39; add unique (&quot;_Slony-I_schemadoc_rowID&quot;);&#39;;
+	execute &#39;alter table only &#39; || p_tab_fqname ||
+		&#39; alter column &quot;_Slony-I_schemadoc_rowID&quot; &#39; ||
+		&#39; set not null;&#39;;
+
+	--
+	-- Return the resulting Slony-I attkind
+	--
+	return v_attkind;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.determineattkindunique-text-name">determineattkindunique( text, name )</a>
+		</h2>
+<h3>Returns: text</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>determineAttKindUnique (tab_fqname, indexname)
+
+Given a tablename, return the Slony-I specific attkind (used for the
+log trigger) of the table. Use the specified unique index or the
+primary key (if indexname is NULL).</p>
+        <pre>
+declare
+	p_tab_fqname	alias for $1;
+	p_idx_name		alias for $2;
+	v_idxrow		record;
+	v_attrow		record;
+	v_i				integer;
+	v_attno			int2;
+	v_attkind		text default &#39;&#39;;
+	v_attfound		bool;
+begin
+	--
+	-- Lookup the tables primary key or the specified unique index
+	--
+	if p_idx_name isnull then
+		raise exception &#39;Slony-I: index name must be specified&#39;;
+	else
+		select PGXC.relname, PGX.indexrelid, PGX.indkey
+				into v_idxrow
+				from &quot;pg_catalog&quot;.pg_class PGC,
+					&quot;pg_catalog&quot;.pg_namespace PGN,
+					&quot;pg_catalog&quot;.pg_index PGX,
+					&quot;pg_catalog&quot;.pg_class PGXC
+				where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+					&quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+					and PGN.oid = PGC.relnamespace
+					and PGX.indrelid = PGC.oid
+					and PGX.indexrelid = PGXC.oid
+					and PGX.indisunique
+					and PGXC.relname = p_idx_name;
+		if not found then
+			raise exception &#39;Slony-I: table % has no unique index %&#39;,
+					p_tab_fqname, p_idx_name;
+		end if;
+	end if;
+
+	--
+	-- Loop over the tables attributes and check if they are
+	-- index attributes. If so, add a &quot;k&quot; to the return value,
+	-- otherwise add a &quot;v&quot;.
+	--
+	for v_attrow in select PGA.attnum, PGA.attname
+			from &quot;pg_catalog&quot;.pg_class PGC,
+			    &quot;pg_catalog&quot;.pg_namespace PGN,
+				&quot;pg_catalog&quot;.pg_attribute PGA
+			where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			    &quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+				and PGN.oid = PGC.relnamespace
+				and PGA.attrelid = PGC.oid
+				and not PGA.attisdropped
+				and PGA.attnum &gt; 0
+			order by attnum
+	loop
+		v_attfound = &#39;f&#39;;
+
+		v_i := 0;
+		loop
+			select indkey[v_i] into v_attno from &quot;pg_catalog&quot;.pg_index
+					where indexrelid = v_idxrow.indexrelid;
+			if v_attno = 0 then
+				exit;
+			end if;
+			if v_attrow.attnum = v_attno then
+				v_attfound = &#39;t&#39;;
+				exit;
+			end if;
+			v_i := v_i + 1;
+		end loop;
+
+		if v_attfound then
+			v_attkind := v_attkind || &#39;k&#39;;
+		else
+			v_attkind := v_attkind || &#39;v&#39;;
+		end if;
+	end loop;
+
+	--
+	-- Return the resulting attkind
+	--
+	return v_attkind;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.determineidxnameserial-text">determineidxnameserial( text )</a>
+		</h2>
+<h3>Returns: name</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>determineIdxnameSerial (tab_fqname)
+
+Given a tablename, construct the index name of the serial column.</p>
+        <pre>
+declare
+	p_tab_fqname	alias for $1;
+	v_row			record;
+begin
+	--
+	-- Lookup the table name alone
+	--
+	select PGC.relname
+			into v_row
+			from &quot;pg_catalog&quot;.pg_class PGC,
+				&quot;pg_catalog&quot;.pg_namespace PGN
+			where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+				&quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+				and PGN.oid = PGC.relnamespace;
+	if not found then
+		raise exception &#39;Slony-I: table % not found&#39;,
+				p_tab_fqname;
+	end if;
+
+	--
+	-- Return the found index name
+	--
+	return v_row.relname || &#39;__Slony-I_schemadoc_rowID_key&#39;;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.determineidxnameunique-text-name">determineidxnameunique( text, name )</a>
+		</h2>
+<h3>Returns: name</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>FUNCTION determineIdxnameUnique (tab_fqname, indexname)
+
+Given a tablename, tab_fqname, check that the unique index, indexname,
+exists or return the primary key index name for the table.  If there
+is no unique index, it raises an exception.</p>
+        <pre>
+declare
+	p_tab_fqname	alias for $1;
+	p_idx_name		alias for $2;
+	v_idxrow		record;
+begin
+	--
+	-- Lookup the tables primary key or the specified unique index
+	--
+	if p_idx_name isnull then
+		select PGXC.relname
+				into v_idxrow
+				from &quot;pg_catalog&quot;.pg_class PGC,
+					&quot;pg_catalog&quot;.pg_namespace PGN,
+					&quot;pg_catalog&quot;.pg_index PGX,
+					&quot;pg_catalog&quot;.pg_class PGXC
+				where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+					&quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+					and PGN.oid = PGC.relnamespace
+					and PGX.indrelid = PGC.oid
+					and PGX.indexrelid = PGXC.oid
+					and PGX.indisprimary;
+		if not found then
+			raise exception &#39;Slony-I: table % has no primary key&#39;,
+					p_tab_fqname;
+		end if;
+	else
+		select PGXC.relname
+				into v_idxrow
+				from &quot;pg_catalog&quot;.pg_class PGC,
+					&quot;pg_catalog&quot;.pg_namespace PGN,
+					&quot;pg_catalog&quot;.pg_index PGX,
+					&quot;pg_catalog&quot;.pg_class PGXC
+				where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+					&quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+					and PGN.oid = PGC.relnamespace
+					and PGX.indrelid = PGC.oid
+					and PGX.indexrelid = PGXC.oid
+					and PGX.indisunique
+					and PGXC.relname = p_idx_name;
+		if not found then
+			raise exception &#39;Slony-I: table % has no unique index %&#39;,
+					p_tab_fqname, p_idx_name;
+		end if;
+	end if;
+
+	--
+	-- Return the found index name
+	--
+	return v_idxrow.relname;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.disablenode-integer">disablenode( integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>process DISABLE_NODE event for node no_id
+
+NOTE: This is not yet implemented!</p>
+        <pre>
+declare
+	p_no_id			alias for $1;
+begin
+	-- **** TODO ****
+	raise exception &#39;Slony-I: disableNode() not implemented&#39;;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.disablenode-int-integer">disablenode_int( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        
+        <pre>
+declare
+	p_no_id			alias for $1;
+begin
+	-- **** TODO ****
+	raise exception &#39;Slony-I: disableNode_int() not implemented&#39;;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.droplisten-integer-integer-integer">droplisten( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>dropListen (li_origin, li_provider, li_receiver)
+
+Generate the DROP_LISTEN event.</p>
+        <pre>
+declare
+	p_li_origin		alias for $1;
+	p_li_provider	alias for $2;
+	p_li_receiver	alias for $3;
+begin
+	perform schemadoc.dropListen_int(p_li_origin, 
+			p_li_provider, p_li_receiver);
+	
+	return  schemadoc.createEvent (&#39;_schemadoc&#39;, &#39;DROP_LISTEN&#39;,
+			p_li_origin, p_li_provider, p_li_receiver);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.droplisten-int-integer-integer-integer">droplisten_int( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>dropListen (li_origin, li_provider, li_receiver)
+
+Process the DROP_LISTEN event, deleting the sl_listen entry for
+the indicated (origin,provider,receiver) combination.</p>
+        <pre>
+declare
+	p_li_origin		alias for $1;
+	p_li_provider	alias for $2;
+	p_li_receiver	alias for $3;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	delete from schemadoc.sl_listen
+			where li_origin = p_li_origin
+			and li_provider = p_li_provider
+			and li_receiver = p_li_receiver;
+	if found then
+		return 1;
+	else
+		return 0;
+	end if;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.dropnode-integer">dropnode( integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>generate DROP_NODE event to drop node node_id from replication</p>
+        <pre>
+declare
+	p_no_id			alias for $1;
+	v_node_row		record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that this got called on a different node
+	-- ----
+	if p_no_id = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: DROP_NODE cannot initiate on the dropped node&#39;;
+	end if;
+
+	select * into v_node_row from schemadoc.sl_node
+			where no_id = p_no_id
+			for update;
+	if not found then
+		raise exception &#39;Slony-I: unknown node ID %&#39;, p_no_id;
+	end if;
+
+	-- ----
+	-- Make sure we do not break other nodes subscriptions with this
+	-- ----
+	if exists (select true from schemadoc.sl_subscribe
+			where sub_provider = p_no_id)
+	then
+		raise exception &#39;Slony-I: Node % is still configured as data provider&#39;,
+				p_no_id;
+	end if;
+
+	-- ----
+	-- Make sure no set originates there any more
+	-- ----
+	if exists (select true from schemadoc.sl_set
+			where set_origin = p_no_id)
+	then
+		raise exception &#39;Slony-I: Node % is still origin of one or more sets&#39;,
+				p_no_id;
+	end if;
+
+	-- ----
+	-- Call the internal drop functionality and generate the event
+	-- ----
+	perform schemadoc.dropNode_int(p_no_id);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;DROP_NODE&#39;,
+									p_no_id);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.dropnode-int-integer">dropnode_int( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>internal function to process DROP_NODE event to drop node node_id from replication</p>
+        <pre>
+declare
+	p_no_id			alias for $1;
+	v_tab_row		record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- If the dropped node is a remote node, clean the configuration
+	-- from all traces for it.
+	-- ----
+	if p_no_id &lt;&gt; schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		delete from schemadoc.sl_subscribe
+				where sub_receiver = p_no_id;
+		delete from schemadoc.sl_listen
+				where li_origin = p_no_id
+					or li_provider = p_no_id
+					or li_receiver = p_no_id;
+		delete from schemadoc.sl_path
+				where pa_server = p_no_id
+					or pa_client = p_no_id;
+		delete from schemadoc.sl_confirm
+				where con_origin = p_no_id
+					or con_received = p_no_id;
+		delete from schemadoc.sl_event
+				where ev_origin = p_no_id;
+		delete from schemadoc.sl_node
+				where no_id = p_no_id;
+
+		return p_no_id;
+	end if;
+
+	-- ----
+	-- This is us ... deactivate the node for now, the daemon
+	-- will call uninstallNode() in a separate transaction.
+	-- ----
+	update schemadoc.sl_node
+			set no_active = false
+			where no_id = p_no_id;
+
+	return p_no_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.droppath-integer-integer">droppath( integer, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Generate DROP_PATH event to drop path from pa_server to pa_client</p>
+        <pre>
+declare
+	p_pa_server		alias for $1;
+	p_pa_client		alias for $2;
+	v_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- There should be no existing subscriptions. Auto unsubscribing
+	-- is considered too dangerous. 
+	-- ----
+	for v_row in select sub_set, sub_provider, sub_receiver
+			from schemadoc.sl_subscribe
+			where sub_provider = p_pa_server
+			and sub_receiver = p_pa_client
+	loop
+		raise exception 
+			&#39;Slony-I: Path cannot be dropped, subscription of set % needs it&#39;,
+			v_row.sub_set;
+	end loop;
+
+	-- ----
+	-- Drop all sl_listen entries that depend on this path
+	-- ----
+	for v_row in select li_origin, li_provider, li_receiver
+			from schemadoc.sl_listen
+			where li_provider = p_pa_server
+			and li_receiver = p_pa_client
+	loop
+		perform schemadoc.dropListen(
+				v_row.li_origin, v_row.li_provider, v_row.li_receiver);
+	end loop;
+
+	-- ----
+	-- Now drop the path and create the event
+	-- ----
+	perform schemadoc.dropPath_int(p_pa_server, p_pa_client);
+	return  schemadoc.createEvent (&#39;_schemadoc&#39;, &#39;DROP_PATH&#39;,
+			p_pa_server, p_pa_client);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.droppath-int-integer-integer">droppath_int( integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Process DROP_PATH event to drop path from pa_server to pa_client</p>
+        <pre>
+declare
+	p_pa_server		alias for $1;
+	p_pa_client		alias for $2;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Remove any dangling sl_listen entries with the server
+	-- as provider and the client as receiver. This must have
+	-- been cleared out before, but obviously was not.
+	-- ----
+	delete from schemadoc.sl_listen
+			where li_provider = p_pa_server
+			and li_receiver = p_pa_client;
+
+	delete from schemadoc.sl_path
+			where pa_server = p_pa_server
+			and pa_client = p_pa_client;
+
+	if found then
+		return 1;
+	else
+		return 0;
+	end if;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.dropset-integer">dropset( integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Process DROP_SET event to drop replication of set set_id.  This involves:
+- Restoring original triggers and rules
+- Removing all traces of the set configuration, including sequences, tables, subscribers, syncs, and the set itself</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	v_origin			int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+	
+	-- ----
+	-- Check that the set exists and originates here
+	-- ----
+	select set_origin into v_origin from schemadoc.sl_set
+			where set_id = p_set_id;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_set_id;
+	end if;
+	if v_origin != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: set % does not originate on local node&#39;,
+				p_set_id;
+	end if;
+
+	-- ----
+	-- Call the internal drop set functionality and generate the event
+	-- ----
+	perform schemadoc.dropSet_int(p_set_id);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;DROP_SET&#39;, 
+			p_set_id);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.dropset-int-integer">dropset_int( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        
+        <pre>
+declare
+	p_set_id			alias for $1;
+	v_tab_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+	
+	-- ----
+	-- Restore all tables original triggers and rules and remove
+	-- our replication stuff.
+	-- ----
+	for v_tab_row in select tab_id from schemadoc.sl_table
+			where tab_set = p_set_id
+			order by tab_id
+	loop
+		perform schemadoc.alterTableRestore(v_tab_row.tab_id);
+		perform schemadoc.tableDropKey(v_tab_row.tab_id);
+	end loop;
+
+	-- ----
+	-- Remove all traces of the set configuration
+	-- ----
+	delete from schemadoc.sl_sequence
+			where seq_set = p_set_id;
+	delete from schemadoc.sl_table
+			where tab_set = p_set_id;
+	delete from schemadoc.sl_subscribe
+			where sub_set = p_set_id;
+	delete from schemadoc.sl_setsync
+			where ssy_setid = p_set_id;
+	delete from schemadoc.sl_set
+			where set_id = p_set_id;
+
+	return p_set_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.droptrigger-integer-name">droptrigger( integer, name )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>dropTrigger (trig_tabid, trig_tgname)
+
+Submits DROP_TRIGGER event to indicate that trigger trig_tgname on
+replicated table trig_tabid WILL be disabled.</p>
+        <pre>
+declare
+	p_trig_tabid		alias for $1;
+	p_trig_tgname		alias for $2;
+begin
+	perform schemadoc.dropTrigger_int(p_trig_tabid, p_trig_tgname);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;DROP_TRIGGER&#39;,
+			p_trig_tabid, p_trig_tgname);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.droptrigger-int-integer-name">droptrigger_int( integer, name )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>dropTrigger_int (trig_tabid, trig_tgname)
+
+Processes DROP_TRIGGER event to make sure that trigger trig_tgname on
+replicated table trig_tabid IS disabled.</p>
+        <pre>
+declare
+	p_trig_tabid		alias for $1;
+	p_trig_tgname		alias for $2;
+	v_tab_altered		boolean;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Get the current table status (altered or not)
+	-- ----
+	select tab_altered into v_tab_altered
+			from schemadoc.sl_table where tab_id = p_trig_tabid;
+	if not found then
+		-- ----
+		-- Not found is no hard error here, because that might
+		-- mean that we are not subscribed to that set
+		-- ----
+		return 0;
+	end if;
+
+	-- ----
+	-- If the table is modified for replication, restore the original state
+	-- ----
+	if v_tab_altered then
+		perform schemadoc.alterTableRestore(p_trig_tabid);
+	end if;
+
+	-- ----
+	-- Remove the entry from sl_trigger
+	-- ----
+	delete from schemadoc.sl_trigger
+			where trig_tabid = p_trig_tabid
+			  and trig_tgname = p_trig_tgname;
+
+	-- ----
+	-- Put the table back into replicated state if it was
+	-- ----
+	if v_tab_altered then
+		perform schemadoc.alterTableForReplication(p_trig_tabid);
+	end if;
+
+	return p_trig_tabid;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.enablenode-integer">enablenode( integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>no_id - Node ID #
+
+Generate the ENABLE_NODE event for node no_id</p>
+        <pre>
+declare
+	p_no_id			alias for $1;
+	v_local_node_id	int4;
+	v_node_row		record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that we are the node to activate and that we are
+	-- currently disabled.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select * into v_node_row
+			from schemadoc.sl_node
+			where no_id = p_no_id
+			for update;
+	if not found then 
+		raise exception &#39;Slony-I: node % not found&#39;, p_no_id;
+	end if;
+	if v_node_row.no_active then
+		raise exception &#39;Slony-I: node % is already active&#39;, p_no_id;
+	end if;
+
+	-- ----
+	-- Activate this node and generate the ENABLE_NODE event
+	-- ----
+	perform schemadoc.enableNode_int (p_no_id);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;ENABLE_NODE&#39;,
+									p_no_id);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.enablenode-int-integer">enablenode_int( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>no_id - Node ID #
+
+Internal function to process the ENABLE_NODE event for node no_id</p>
+        <pre>
+declare
+	p_no_id			alias for $1;
+	v_local_node_id	int4;
+	v_node_row		record;
+	v_sub_row		record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that the node is inactive
+	-- ----
+	select * into v_node_row
+			from schemadoc.sl_node
+			where no_id = p_no_id
+			for update;
+	if not found then 
+		raise exception &#39;Slony-I: node % not found&#39;, p_no_id;
+	end if;
+	if v_node_row.no_active then
+		return p_no_id;
+	end if;
+
+	-- ----
+	-- Activate the node and generate sl_confirm status rows for it.
+	-- ----
+	update schemadoc.sl_node
+			set no_active = &#39;t&#39;
+			where no_id = p_no_id;
+	insert into schemadoc.sl_confirm
+			(con_origin, con_received, con_seqno)
+			select no_id, p_no_id, 0 from schemadoc.sl_node
+				where no_id != p_no_id
+				and no_active;
+	insert into schemadoc.sl_confirm
+			(con_origin, con_received, con_seqno)
+			select p_no_id, no_id, 0 from schemadoc.sl_node
+				where no_id != p_no_id
+				and no_active;
+
+	-- ----
+	-- Generate ENABLE_SUBSCRIPTION events for all sets that
+	-- origin here and are subscribed by the just enabled node.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	for v_sub_row in select SUB.sub_set, SUB.sub_provider from
+			schemadoc.sl_set S,
+			schemadoc.sl_subscribe SUB
+			where S.set_origin = v_local_node_id
+			and S.set_id = SUB.sub_set
+			and SUB.sub_receiver = p_no_id
+			for update of S
+	loop
+		perform schemadoc.enableSubscription (v_sub_row.sub_set,,
+				v_sub_row.sub_provider, p_no_id);
+	end loop;
+
+	return p_no_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.enablesubscription-integer-integer-integer">enablesubscription( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>enableSubscription (sub_set, sub_provider, sub_receiver)
+
+Indicates that sub_receiver intends subscribing to set sub_set from
+sub_provider.  Work is all done by the internal function
+enableSubscription_int (sub_set, sub_provider, sub_receiver).</p>
+        <pre>
+declare
+	p_sub_set			alias for $1;
+	p_sub_provider		alias for $2;
+	p_sub_receiver		alias for $3;
+begin
+	return  schemadoc.enableSubscription_int (p_sub_set, 
+			p_sub_provider, p_sub_receiver);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.enablesubscription-int-integer-integer-integer">enablesubscription_int( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>enableSubscription_int (sub_set, sub_provider, sub_receiver)
+
+Internal function to enable subscription of node sub_receiver to set
+sub_set via node sub_provider.
+
+slon does most of the work; all we need do here is to remember that it
+happened.  The function updates sl_subscribe, indicating that the
+subscription has become active.</p>
+        <pre>
+declare
+	p_sub_set			alias for $1;
+	p_sub_provider		alias for $2;
+	p_sub_receiver		alias for $3;
+	v_n					int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- The real work is done in the replication engine. All
+	-- we have to do here is remembering that it happened.
+	-- ----
+	update schemadoc.sl_subscribe
+			set sub_active = &#39;t&#39;
+			where sub_set = p_sub_set
+			and sub_receiver = p_sub_receiver;
+	get diagnostics v_n = row_count;
+	if v_n = 0 then
+		insert into schemadoc.sl_subscribe
+				(sub_set, sub_provider, sub_receiver,
+				sub_forward, sub_active)
+				values
+				(p_sub_set, p_sub_provider, p_sub_receiver,
+				false, true);
+	end if;
+
+	return p_sub_set;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.failednode-integer-integer">failednode( integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Initiate failover from failed_node to backup_node.  This function must be called on all nodes, 
+and then waited for the restart of all node daemons.</p>
+        <pre>
+declare
+	p_failed_node		alias for $1;
+	p_backup_node		alias for $2;
+	v_row				record;
+	v_row2				record;
+	v_n					int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- All consistency checks first
+	-- Check that every system that has a path to the failed node
+	-- also has a path to the backup node.
+	-- ----
+	for v_row in select P.pa_client
+			from schemadoc.sl_path P
+			where P.pa_server = p_failed_node
+				and P.pa_client &lt;&gt; p_backup_node
+				and not exists (select true from schemadoc.sl_path PP
+							where PP.pa_server = p_backup_node
+								and PP.pa_client = P.pa_client)
+	loop
+		raise exception &#39;Slony-I: cannot failover - node % has no path to the backup node&#39;,
+				v_row.pa_client;
+	end loop;
+
+	-- ----
+	-- Check all sets originating on the failed node
+	-- ----
+	for v_row in select set_id
+			from schemadoc.sl_set
+			where set_origin = p_failed_node
+	loop
+		-- ----
+		-- Check that the backup node is subscribed to all sets
+		-- that origin on the failed node
+		-- ----
+		select into v_row2 sub_forward, sub_active
+				from schemadoc.sl_subscribe
+				where sub_set = v_row.set_id
+					and sub_receiver = p_backup_node;
+		if not found then
+			raise exception &#39;Slony-I: cannot failover - node % is not subscribed to set %&#39;,
+					p_backup_node, v_row.set_id;
+		end if;
+
+		-- ----
+		-- Check that the subscription is active
+		-- ----
+		if not v_row2.sub_active then
+			raise exception &#39;Slony-I: cannot failover - subscription for set % is not active&#39;,
+					v_row.set_id;
+		end if;
+
+		-- ----
+		-- If there are other subscribers, the backup node needs to
+		-- be a forwarder too.
+		-- ----
+		select into v_n count(*)
+				from schemadoc.sl_subscribe
+				where sub_set = v_row.set_id
+					and sub_receiver &lt;&gt; p_backup_node;
+		if v_n &gt; 0 and not v_row2.sub_forward then
+			raise exception &#39;Slony-I: cannot failover - node % is not a forwarder of set %&#39;,
+					p_backup_node, v_row.set_id;
+		end if;
+	end loop;
+
+	-- ----
+	-- Terminate all connections of the failed node the hard way
+	-- ----
+	perform schemadoc.terminateNodeConnections(
+			&#39;_schemadoc_Node_&#39; || p_failed_node);
+
+	-- ----
+	-- Let every node that listens for something on the failed node
+	-- listen for that on the backup node instead.
+	-- ----
+	for v_row in select * from schemadoc.sl_listen
+			where li_provider = p_failed_node
+				and li_receiver &lt;&gt; p_backup_node
+	loop
+		perform schemadoc.storeListen_int(v_row.li_origin,
+				p_backup_node, v_row.li_receiver);
+	end loop;
+
+	-- ----
+	-- Let the backup node listen for all events where the
+	-- failed node did listen for it.
+	-- ----
+	for v_row in select li_origin, li_provider
+			from schemadoc.sl_listen
+			where li_receiver = p_failed_node
+				and li_provider &lt;&gt; p_backup_node
+	loop
+		perform schemadoc.storeListen_int(v_row.li_origin,
+				v_row.li_provider, p_backup_node);
+	end loop;
+
+	-- ----
+	-- Remove all sl_listen entries that receive anything from the
+	-- failed node.
+	-- ----
+	delete from schemadoc.sl_listen
+			where li_provider = p_failed_node
+				or li_receiver = p_failed_node;
+
+	-- ----
+	-- Move the sets
+	-- ----
+	for v_row in select S.set_id, (select count(*)
+					from schemadoc.sl_subscribe SUB
+					where S.set_id = SUB.sub_set
+						and SUB.sub_receiver &lt;&gt; p_backup_node
+						and SUB.sub_provider = p_failed_node)
+					as num_direct_receivers 
+			from schemadoc.sl_set S
+			where S.set_origin = p_failed_node
+			for update
+	loop
+		-- ----
+		-- If the backup node is the only direct subscriber ...
+		-- ----
+		if v_row.num_direct_receivers = 0 then
+raise notice &#39;failedNode: set % has no other direct receivers - move now&#39;, v_row.set_id;
+			-- ----
+			-- backup_node is the only direct subscriber, move the set
+			-- right now. On the backup node itself that includes restoring
+			-- all user mode triggers, removing the protection trigger,
+			-- adding the log trigger, removing the subscription and the
+			-- obsolete setsync status.
+			-- ----
+			if p_backup_node = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+				for v_row2 in select * from schemadoc.sl_table
+						where tab_set = v_row.set_id
+				loop
+					perform schemadoc.alterTableRestore(v_row2.tab_id);
+				end loop;
+			end if;
+
+			update schemadoc.sl_set set set_origin = p_backup_node
+					where set_id = v_row.set_id;
+
+			if p_backup_node = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+				delete from schemadoc.sl_setsync
+						where ssy_setid = v_row.set_id;
+
+				for v_row2 in select * from schemadoc.sl_table
+						where tab_set = v_row.set_id
+				loop
+					perform schemadoc.alterTableForReplication(v_row2.tab_id);
+				end loop;
+			end if;
+
+			delete from schemadoc.sl_subscribe
+					where sub_set = v_row.set_id
+						and sub_receiver = p_backup_node;
+		else
+raise notice &#39;failedNode: set % has other direct receivers - change providers only&#39;, v_row.set_id;
+			-- ----
+			-- Backup node is not the only direct subscriber. This
+			-- means that at this moment, we redirect all direct
+			-- subscribers to receive from the backup node, and the
+			-- backup node itself to receive from another one.
+			-- The admin utility will wait for the slon engine to
+			-- restart and then call failedNode2() on the node with
+			-- the highest SYNC and redirect this to it on
+			-- backup node later.
+			-- ----
+			update schemadoc.sl_subscribe
+					set sub_provider = (select min(SS.sub_receiver)
+							from schemadoc.sl_subscribe SS
+							where SS.sub_set = v_row.set_id
+								and SS.sub_provider = p_failed_node
+								and SS.sub_receiver &lt;&gt; p_backup_node
+								and SS.sub_forward)
+					where sub_set = v_row.set_id
+						and sub_receiver = p_backup_node;
+			update schemadoc.sl_subscribe
+					set sub_provider = p_backup_node
+					where sub_set = v_row.set_id
+						and sub_provider = p_failed_node
+						and sub_receiver &lt;&gt; p_backup_node;
+		end if;
+	end loop;
+
+	-- ----
+	-- Make sure the node daemon will restart
+	-- ----
+	notify &quot;_schemadoc_Restart&quot;;
+
+	-- ----
+	-- That is it - so far.
+	-- ----
+	return p_failed_node;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.failednode2-integer-integer-integer-bigint-bigint">failednode2( integer, integer, integer, bigint, bigint )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>FUNCTION failedNode2 (failed_node, backup_node, set_id, ev_seqno, ev_seqfake)
+
+On the node that has the highest sequence number of the failed node,
+fake the FAILED_NODE event.</p>
+        <pre>
+declare
+	p_failed_node		alias for $1;
+	p_backup_node		alias for $2;
+	p_set_id			alias for $3;
+	p_ev_seqno			alias for $4;
+	p_ev_seqfake		alias for $5;
+	v_row				record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	select * into v_row
+			from schemadoc.sl_event
+			where ev_origin = p_failed_node
+			and ev_seqno = p_ev_seqno;
+	if not found then
+		raise exception &#39;Slony-I: event %,% not found&#39;,
+				p_failed_node, p_ev_seqno;
+	end if;
+
+	insert into schemadoc.sl_event
+			(ev_origin, ev_seqno, ev_timestamp,
+			ev_minxid, ev_maxxid, ev_xip,
+			ev_type, ev_data1, ev_data2, ev_data3)
+			values 
+			(p_failed_node, p_ev_seqfake, CURRENT_TIMESTAMP,
+			v_row.ev_minxid, v_row.ev_maxxid, v_row.ev_xip,
+			&#39;FAILOVER_SET&#39;, p_failed_node::text, p_backup_node::text,
+			p_set_id::text);
+	insert into schemadoc.sl_confirm
+			(con_origin, con_received, con_seqno, con_timestamp)
+			values
+			(p_failed_node, schemadoc.getLocalNodeId(&#39;_schemadoc&#39;),
+			p_ev_seqfake, CURRENT_TIMESTAMP);
+	notify &quot;_schemadoc_Event&quot;;
+	notify &quot;_schemadoc_Confirm&quot;;
+	notify &quot;_schemadoc_Restart&quot;;
+
+	perform schemadoc.failoverSet_int(p_failed_node,
+			p_backup_node, p_set_id);
+
+	return p_ev_seqfake;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.failoverset-int-integer-integer-integer">failoverset_int( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>FUNCTION failoverSet_int (failed_node, backup_node, set_id)
+
+Finish failover for one set.</p>
+        <pre>
+declare
+	p_failed_node		alias for $1;
+	p_backup_node		alias for $2;
+	p_set_id			alias for $3;
+	v_row				record;
+	v_last_sync			int8;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Change the origin of the set now to the backup node.
+	-- On the backup node this includes changing all the
+	-- trigger and protection stuff
+	-- ----
+	if p_backup_node = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		for v_row in select * from schemadoc.sl_table
+				where tab_set = p_set_id
+		loop
+			perform schemadoc.alterTableRestore(v_row.tab_id);
+		end loop;
+
+		delete from schemadoc.sl_setsync
+				where ssy_setid = p_set_id;
+		delete from schemadoc.sl_subscribe
+				where sub_set = p_set_id
+					and sub_receiver = p_backup_node;
+		update schemadoc.sl_set
+				set set_origin = p_backup_node
+				where set_id = p_set_id;
+
+		for v_row in select * from schemadoc.sl_table
+				where tab_set = p_set_id
+		loop
+			perform schemadoc.alterTableForReplication(v_row.tab_id);
+		end loop;
+	else
+		delete from schemadoc.sl_subscribe
+				where sub_set = p_set_id
+					and sub_receiver = p_backup_node;
+		update schemadoc.sl_set
+				set set_origin = p_backup_node
+				where set_id = p_set_id;
+	end if;
+
+	-- ----
+	-- If we are a subscriber of the set ourself, change our
+	-- setsync status to reflect the new set origin.
+	-- ----
+	if exists (select true from schemadoc.sl_subscribe
+			where sub_set = p_set_id
+				and sub_receiver = schemadoc.getLocalNodeId(
+						&#39;_schemadoc&#39;))
+	then
+		delete from schemadoc.sl_setsync
+				where ssy_setid = p_set_id;
+
+		select coalesce(max(ev_seqno), 0) into v_last_sync
+				from schemadoc.sl_event
+				where ev_origin = p_backup_node
+					and ev_type = &#39;SYNC&#39;;
+		if v_last_sync &gt; 0 then
+			insert into schemadoc.sl_setsync
+					(ssy_setid, ssy_origin, ssy_seqno,
+					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
+					select p_set_id, p_backup_node, v_last_sync,
+					ev_minxid, ev_maxxid, ev_xip, NULL
+					from schemadoc.sl_event
+					where ev_origin = p_backup_node
+						and ev_seqno = v_last_sync;
+		else
+			insert into schemadoc.sl_setsync
+					(ssy_setid, ssy_origin, ssy_seqno,
+					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
+					values (p_set_id, p_backup_node, &#39;0&#39;,
+					&#39;0&#39;, &#39;0&#39;, &#39;&#39;, NULL);
+		end if;
+				
+	end if;
+
+	return p_failed_node;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.forwardconfirm-integer-integer-bigint-timestamp-without-time-zone">forwardconfirm( integer, integer, bigint, timestamp without time zone )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>forwardConfirm (p_con_origin, p_con_received, p_con_seqno, p_con_timestamp)
+
+Confirms (recorded in sl_confirm) that items from p_con_origin up to
+p_con_seqno have been received by node p_con_received as of
+p_con_timestamp, and raises an event to forward this confirmation.</p>
+        <pre>
+declare
+	p_con_origin	alias for $1;
+	p_con_received	alias for $2;
+	p_con_seqno		alias for $3;
+	p_con_timestamp	alias for $4;
+	v_max_seqno		bigint;
+begin
+	select into v_max_seqno coalesce(max(con_seqno), 0)
+			from schemadoc.sl_confirm
+			where con_origin = p_con_origin
+			and con_received = p_con_received;
+	if v_max_seqno &lt; p_con_seqno then
+		insert into schemadoc.sl_confirm 
+				(con_origin, con_received, con_seqno, con_timestamp)
+				values (p_con_origin, p_con_received, p_con_seqno,
+					p_con_timestamp);
+		notify &quot;_schemadoc_Confirm&quot;;
+		v_max_seqno = p_con_seqno;
+	end if;
+
+	return v_max_seqno;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.initializelocalnode-integer-text">initializelocalnode( integer, text )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>no_id - Node ID #
+no_comment - Human-oriented comment
+
+Initializes the new node, no_id</p>
+        <pre>
+declare
+	p_local_node_id		alias for $1;
+	p_comment			alias for $2;
+	v_old_node_id		int4;
+	v_first_log_no		int4;
+	v_event_seq			int8;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Make sure this node is uninitialized or got reset
+	-- ----
+	select last_value::int4 into v_old_node_id from schemadoc.sl_local_node_id;
+	if v_old_node_id != -1 then
+		raise exception &#39;Slony-I: This node is already initialized&#39;;
+	end if;
+
+	-- ----
+	-- Set sl_local_node_id to the requested value and add our
+	-- own system to sl_node.
+	-- ----
+	perform setval(&#39;schemadoc.sl_local_node_id&#39;, p_local_node_id);
+	perform setval(&#39;schemadoc.sl_rowid_seq&#39;, 
+			p_local_node_id::int8 * &#39;1000000000000000&#39;::int8);
+	perform schemadoc.storeNode_int (p_local_node_id, p_comment);
+	
+	return p_local_node_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.lockset-integer">lockset( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>lockSet(set_id)
+
+Add a special trigger to all tables of a set that disables access to
+it.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	v_local_node_id		int4;
+	v_set_row			record;
+	v_tab_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that the set exists and that we are the origin
+	-- and that it is not already locked.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select * into v_set_row from schemadoc.sl_set
+			where set_id = p_set_id
+			for update;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_set_id;
+	end if;
+	if v_set_row.set_origin &lt;&gt; v_local_node_id then
+		raise exception &#39;Slony-I: set % does not originate on local node&#39;,
+				p_set_id;
+	end if;
+	if v_set_row.set_locked notnull then
+		raise exception &#39;Slony-I: set % is already locked&#39;, p_set_id;
+	end if;
+
+	-- ----
+	-- Place the lockedSet trigger on all tables in the set.
+	-- ----
+	for v_tab_row in select T.tab_id,
+			&quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			&quot;pg_catalog&quot;.quote_ident(PGC.relname) as tab_fqname
+			from schemadoc.sl_table T,
+				&quot;pg_catalog&quot;.pg_class PGC, &quot;pg_catalog&quot;.pg_namespace PGN
+			where T.tab_set = p_set_id
+				and T.tab_reloid = PGC.oid
+				and PGC.relnamespace = PGN.oid
+			order by tab_id
+	loop
+		execute &#39;create trigger &quot;_schemadoc_lockedset_&#39; || 
+				v_tab_row.tab_id || 
+				&#39;&quot; before insert or update or delete on &#39; ||
+				v_tab_row.tab_fqname || &#39; for each row execute procedure
+				schemadoc.lockedSet (&#39;&#39;_schemadoc&#39;&#39;);&#39;;
+	end loop;
+
+	-- ----
+	-- Remember our snapshots xmax as for the set locking
+	-- ----
+	update schemadoc.sl_set
+			set set_locked = schemadoc.getMaxXid()
+			where set_id = p_set_id;
+
+	return p_set_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.mergeset-integer-integer">mergeset( integer, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Generate MERGE_SET event to request that sets be merged together.
+
+Both sets must exist, and originate on the same node.  They must be
+subscribed by the same set of nodes.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_add_id			alias for $2;
+	v_origin			int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+	
+	-- ----
+	-- Check that both sets exist and originate here
+	-- ----
+	if p_set_id = p_add_id then
+		raise exception &#39;Slony-I: merged set ids cannot be identical&#39;;
+	end if;
+	select set_origin into v_origin from schemadoc.sl_set
+			where set_id = p_set_id;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_set_id;
+	end if;
+	if v_origin != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: set % does not originate on local node&#39;,
+				p_set_id;
+	end if;
+
+	select set_origin into v_origin from schemadoc.sl_set
+			where set_id = p_add_id;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_add_id;
+	end if;
+	if v_origin != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: set % does not originate on local node&#39;,
+				p_add_id;
+	end if;
+
+	-- ----
+	-- Check that both sets are subscribed by the same set of nodes
+	-- ----
+	if exists (select true from schemadoc.sl_subscribe SUB1
+				where SUB1.sub_set = p_set_id
+				and SUB1.sub_receiver not in (select SUB2.sub_receiver
+						from schemadoc.sl_subscribe SUB2
+						where SUB2.sub_set = p_add_id))
+	then
+		raise exception &#39;Slony-I: subscriber lists of set % and % are different&#39;,
+				p_set_id, p_add_id;
+	end if;
+
+	if exists (select true from schemadoc.sl_subscribe SUB1
+				where SUB1.sub_set = p_add_id
+				and SUB1.sub_receiver not in (select SUB2.sub_receiver
+						from schemadoc.sl_subscribe SUB2
+						where SUB2.sub_set = p_set_id))
+	then
+		raise exception &#39;Slony-I: subscriber lists of set % and % are different&#39;,
+				p_add_id, p_set_id;
+	end if;
+
+	-- ----
+	-- Create a SYNC event, merge the sets, create a MERGE_SET event
+	-- ----
+	perform schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SYNC&#39;, NULL);
+	perform schemadoc.mergeSet_int(p_set_id, p_add_id);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;MERGE_SET&#39;, 
+			p_set_id, p_add_id);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.mergeset-int-integer-integer">mergeset_int( integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>mergeSet_int(set_id, add_id) - Perform MERGE_SET event, merging all objects from 
+set add_id into set set_id.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_add_id			alias for $2;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+	
+	update schemadoc.sl_sequence
+			set seq_set = p_set_id
+			where seq_set = p_add_id;
+	update schemadoc.sl_table
+			set tab_set = p_set_id
+			where tab_set = p_add_id;
+	delete from schemadoc.sl_subscribe
+			where sub_set = p_add_id;
+	delete from schemadoc.sl_setsync
+			where ssy_setid = p_add_id;
+	delete from schemadoc.sl_set
+			where set_id = p_add_id;
+
+	return p_set_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.moveset-integer-integer">moveset( integer, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>moveSet(set_id, new_origin)
+
+Generate MOVE_SET event to request that the origin for set set_id be moved to node new_origin</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_new_origin		alias for $2;
+	v_local_node_id		int4;
+	v_set_row			record;
+	v_sub_row			record;
+	v_sync_seqno		int8;
+	v_lv_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that the set is locked and that this locking
+	-- happened long enough ago.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select * into v_set_row from schemadoc.sl_set
+			where set_id = p_set_id
+			for update;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_set_id;
+	end if;
+	if v_set_row.set_origin &lt;&gt; v_local_node_id then
+		raise exception &#39;Slony-I: set % does not originate on local node&#39;,
+				p_set_id;
+	end if;
+	if v_set_row.set_locked isnull then
+		raise exception &#39;Slony-I: set % is not locked&#39;, p_set_id;
+	end if;
+	if v_set_row.set_locked &gt; schemadoc.getMinXid() then
+		raise exception &#39;Slony-I: cannot move set % yet, transactions &lt; % are still in progress&#39;,
+				p_set_id, v_set_row.set_locked;
+	end if;
+
+	-- ----
+	-- Unlock the set
+	-- ----
+	perform schemadoc.unlockSet(p_set_id);
+
+	-- ----
+	-- Check that the new_origin is an active subscriber of the set
+	-- ----
+	select * into v_sub_row from schemadoc.sl_subscribe
+			where sub_set = p_set_id
+			and sub_receiver = p_new_origin;
+	if not found then
+		raise exception &#39;Slony-I: set % is not subscribed by node %&#39;,
+				p_set_id, p_new_origin;
+	end if;
+	if not v_sub_row.sub_active then
+		raise exception &#39;Slony-I: subsctiption of node % for set % is inactive&#39;,
+				p_new_origin, p_set_id;
+	end if;
+
+	-- ----
+	-- Reconfigure everything
+	-- ----
+	perform schemadoc.moveSet_int(p_set_id, v_local_node_id,
+			p_new_origin);
+
+	-- ----
+	-- At this time we hold access exclusive locks for every table
+	-- in the set. But we did move the set to the new origin, so the
+	-- createEvent() we are doing now will not record the sequences.
+	-- ----
+	v_sync_seqno := schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SYNC&#39;);
+	insert into schemadoc.sl_seqlog 
+			(seql_seqid, seql_origin, seql_ev_seqno, seql_last_value)
+			select seq_id, v_local_node_id, v_sync_seqno, seq_last_value
+			from schemadoc.sl_seqlastvalue
+			where seq_set = p_set_id;
+					
+	-- ----
+	-- Finally we generate the real event
+	-- ----
+	return schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;MOVE_SET&#39;, 
+			p_set_id, v_local_node_id, p_new_origin);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.moveset-int-integer-integer-integer">moveset_int( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>moveSet(set_id, old_origin, new_origin)
+
+Process MOVE_SET event to request that the origin for set set_id be
+moved from old_origin to node new_origin</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_old_origin		alias for $2;
+	p_new_origin		alias for $3;
+	v_local_node_id		int4;
+	v_tab_row			record;
+	v_sub_row			record;
+	v_sub_node			int4;
+	v_sub_last			int4;
+	v_sub_next			int4;
+	v_last_sync			int8;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Get our local node ID
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+
+	-- ----
+	-- If we are the old or new origin of the set, we need to
+	-- remove the log trigger from all tables first.
+	-- ----
+	if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then
+		for v_tab_row in select tab_id from schemadoc.sl_table
+				where tab_set = p_set_id
+				order by tab_id
+		loop
+			perform schemadoc.alterTableRestore(v_tab_row.tab_id);
+		end loop;
+	end if;
+
+	-- ----
+	-- Next we have to reverse the subscription path
+	-- ----
+	v_sub_last = p_new_origin;
+	select sub_provider into v_sub_node
+			from schemadoc.sl_subscribe
+			where sub_receiver = p_new_origin;
+	if not found then
+		raise exception &#39;Slony-I: subscription path broken in moveSet_int&#39;;
+	end if;
+	while v_sub_node &lt;&gt; p_old_origin loop
+		-- ----
+		-- Tracing node by node, the old receiver is now in
+		-- v_sub_last and the old provider is in v_sub_node.
+		-- ----
+
+		-- ----
+		-- Get the current provider of this node as next
+		-- and change the provider to the previous one in
+		-- the reverse chain.
+		-- ----
+		select sub_provider into v_sub_next
+				from schemadoc.sl_subscribe
+				where sub_set = p_set_id
+					and sub_receiver = v_sub_node
+				for update;
+		if not found then
+			raise exception &#39;Slony-I: subscription path broken in moveSet_int&#39;;
+		end if;
+		update schemadoc.sl_subscribe
+				set sub_provider = v_sub_last
+				where sub_set = p_set_id
+					and sub_receiver = v_sub_node;
+
+		v_sub_last = v_sub_node;
+		v_sub_node = v_sub_next;
+	end loop;
+
+	-- ----
+	-- This includes creating a subscription for the old origin
+	-- ----
+	insert into schemadoc.sl_subscribe
+			(sub_set, sub_provider, sub_receiver,
+			sub_forward, sub_active)
+			values (p_set_id, v_sub_last, p_old_origin, true, true);
+	if v_local_node_id = p_old_origin then
+		select coalesce(max(ev_seqno), 0) into v_last_sync 
+				from schemadoc.sl_event
+				where ev_origin = p_new_origin
+					and ev_type = &#39;SYNC&#39;;
+		if v_last_sync &gt; 0 then
+			insert into schemadoc.sl_setsync
+					(ssy_setid, ssy_origin, ssy_seqno,
+					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
+					select p_set_id, p_new_origin, v_last_sync,
+					ev_minxid, ev_maxxid, ev_xip, NULL
+					from schemadoc.sl_event
+					where ev_origin = p_new_origin
+						and ev_seqno = v_last_sync;
+		else
+			insert into schemadoc.sl_setsync
+					(ssy_setid, ssy_origin, ssy_seqno,
+					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
+					values (p_set_id, p_new_origin, &#39;0&#39;,
+					&#39;0&#39;, &#39;0&#39;, &#39;&#39;, NULL);
+		end if;
+	end if;
+
+	-- ----
+	-- Now change the ownership of the set.
+	-- ----
+	update schemadoc.sl_set
+			set set_origin = p_new_origin
+			where set_id = p_set_id;
+
+	-- ----
+	-- On the new origin, delete the obsolete setsync information
+	-- and the subscription.
+	-- ----
+	if v_local_node_id = p_new_origin then
+		delete from schemadoc.sl_setsync
+				where ssy_setid = p_set_id;
+	else
+		if v_local_node_id &lt;&gt; p_old_origin then
+			--
+			-- On every other node, change the setsync so that it will
+			-- pick up from the new origins last known sync.
+			--
+			delete from schemadoc.sl_setsync
+					where ssy_setid = p_set_id;
+			select coalesce(max(ev_seqno), 0) into v_last_sync
+					from schemadoc.sl_event
+					where ev_origin = p_new_origin
+						and ev_type = &#39;SYNC&#39;;
+			if v_last_sync &gt; 0 then
+				insert into schemadoc.sl_setsync
+						(ssy_setid, ssy_origin, ssy_seqno,
+						ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
+						select p_set_id, p_new_origin, v_last_sync,
+						ev_minxid, ev_maxxid, ev_xip, NULL
+						from schemadoc.sl_event
+						where ev_origin = p_new_origin
+							and ev_seqno = v_last_sync;
+			else
+				insert into schemadoc.sl_setsync
+						(ssy_setid, ssy_origin, ssy_seqno,
+						ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
+						values (p_set_id, p_new_origin, &#39;0&#39;,
+						&#39;0&#39;, &#39;0&#39;, &#39;&#39;, NULL);
+			end if;
+		end if;
+	end if;
+	delete from schemadoc.sl_subscribe
+			where sub_set = p_set_id
+			and sub_receiver = p_new_origin;
+
+	-- ----
+	-- If we are the new or old origin, we have to
+	-- put all the tables into altered state again.
+	-- ----
+	if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then
+		for v_tab_row in select tab_id from schemadoc.sl_table
+				where tab_set = p_set_id
+				order by tab_id
+		loop
+			perform schemadoc.alterTableForReplication(v_tab_row.tab_id);
+		end loop;
+	end if;
+
+	return p_set_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.sequencelastvalue-text">sequencelastvalue( text )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        
+        <pre>
+declare
+	p_seqname	alias for $1;
+	v_seq_row	record;
+begin
+	for v_seq_row in execute &#39;select last_value from &#39; || p_seqname
+	loop
+		return v_seq_row.last_value;
+	end loop;
+
+	-- not reached
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.sequencesetvalue-integer-integer-bigint-bigint">sequencesetvalue( integer, integer, bigint, bigint )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>sequenceSetValue (seq_id, seq_origin, ev_seqno, last_value)
+Set sequence seq_id to have new value last_value.
+</p>
+        <pre>
+declare
+	p_seq_id			alias for $1;
+	p_seq_origin		alias for $2;
+	p_ev_seqno			alias for $3;
+	p_last_value		alias for $4;
+	v_fqname			text;
+begin
+	-- ----
+	-- Get the sequences fully qualified name
+	-- ----
+	select &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			&quot;pg_catalog&quot;.quote_ident(PGC.relname) into v_fqname
+		from schemadoc.sl_sequence SQ,
+			&quot;pg_catalog&quot;.pg_class PGC, &quot;pg_catalog&quot;.pg_namespace PGN
+		where SQ.seq_id = p_seq_id
+			and SQ.seq_reloid = PGC.oid
+			and PGC.relnamespace = PGN.oid;
+	if not found then
+		raise exception &#39;Slony-I: sequence % not found&#39;, p_seq_id;
+	end if;
+
+	-- ----
+	-- Update it to the new value
+	-- ----
+	execute &#39;select setval(&#39;&#39;&#39; || v_fqname ||
+			&#39;&#39;&#39;, &#39;&#39;&#39; || p_last_value || &#39;&#39;&#39;)&#39;;
+
+	insert into schemadoc.sl_seqlog
+			(seql_seqid, seql_origin, seql_ev_seqno, seql_last_value)
+			values (p_seq_id, p_seq_origin, p_ev_seqno, p_last_value);
+
+	return p_seq_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setaddsequence-integer-integer-text-text">setaddsequence( integer, integer, text, text )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setAddSequence (set_id, seq_id, seq_fqname, seq_comment)
+
+On the origin node for set set_id, add sequence seq_fqname to the
+replication set, and raise SET_ADD_SEQUENCE to cause this to replicate
+to subscriber nodes.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_seq_id			alias for $2;
+	p_fqname			alias for $3;
+	p_seq_comment		alias for $4;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that we are the origin of the set
+	-- ----
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setAddSequence(): set % not found&#39;, p_set_id;
+	end if;
+	if v_set_origin != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: setAddSequence(): set % has remote origin&#39;, p_set_id;
+	end if;
+
+	if exists (select true from schemadoc.sl_subscribe
+			where sub_set = p_set_id)
+	then
+		raise exception &#39;Slony-I: cannot add sequence to currently subscribed set %&#39;,
+				p_set_id;
+	end if;
+
+	-- ----
+	-- Add the sequence to the set and generate the SET_ADD_SEQUENCE event
+	-- ----
+	perform schemadoc.setAddSequence_int(p_set_id, p_seq_id, p_fqname,
+			p_seq_comment);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SET_ADD_SEQUENCE&#39;,
+			p_set_id, p_seq_id, p_fqname, p_seq_comment);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setaddsequence-int-integer-integer-text-text">setaddsequence_int( integer, integer, text, text )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setAddSequence_int (set_id, seq_id, seq_fqname, seq_comment)
+
+This processes the SET_ADD_SEQUENCE event.  On remote nodes that
+subscribe to set_id, add the sequence to the replication set.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_seq_id			alias for $2;
+	p_fqname			alias for $3;
+	p_seq_comment		alias for $4;
+	v_local_node_id		int4;
+	v_set_origin		int4;
+	v_sub_provider		int4;
+	v_relkind			char;
+	v_seq_reloid		oid;
+	v_sync_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- For sets with a remote origin, check that we are subscribed 
+	-- to that set. Otherwise we ignore the sequence because it might 
+	-- not even exist in our database.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setAddSequence_int(): set % not found&#39;,
+				p_set_id;
+	end if;
+	if v_set_origin != v_local_node_id then
+		select sub_provider into v_sub_provider
+				from schemadoc.sl_subscribe
+				where sub_set = p_set_id
+				and sub_receiver = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+		if not found then
+			return 0;
+		end if;
+	end if;
+	
+	-- ----
+	-- Get the sequences OID and check that it is a sequence
+	-- ----
+	select PGC.oid, PGC.relkind into v_seq_reloid, v_relkind
+			from &quot;pg_catalog&quot;.pg_class PGC, &quot;pg_catalog&quot;.pg_namespace PGN
+			where PGC.relnamespace = PGN.oid
+			and p_fqname = &quot;pg_catalog&quot;.quote_ident(PGN.nspname) ||
+					&#39;.&#39; || &quot;pg_catalog&quot;.quote_ident(PGC.relname);
+	if not found then
+		raise exception &#39;Slony-I: setAddSequence_int(): sequence % not found&#39;, 
+				p_fqname;
+	end if;
+	if v_relkind != &#39;S&#39; then
+		raise exception &#39;Slony-I: setAddSequence_int(): % is not a sequence&#39;,
+				p_fqname;
+	end if;
+
+	-- ----
+	-- Add the sequence to sl_sequence
+	-- ----
+	insert into schemadoc.sl_sequence
+			(seq_id, seq_reloid, seq_set, seq_comment) values
+			(p_seq_id, v_seq_reloid, p_set_id, p_seq_comment);
+
+	-- ----
+	-- On the set origin, fake a sl_seqlog row for the last sync event
+	-- ----
+	if v_set_origin = v_local_node_id then
+		for v_sync_row in select coalesce (max(ev_seqno), 0) as ev_seqno
+				from schemadoc.sl_event
+				where ev_origin = v_local_node_id
+					and ev_type = &#39;SYNC&#39;
+		loop
+			insert into schemadoc.sl_seqlog
+					(seql_seqid, seql_origin, seql_ev_seqno, 
+					seql_last_value) values
+					(p_seq_id, v_local_node_id, v_sync_row.ev_seqno,
+					schemadoc.sequenceLastValue(p_fqname));
+		end loop;
+	end if;
+
+	return p_seq_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setaddtable-integer-integer-text-name-text">setaddtable( integer, integer, text, name, text )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setAddTable (set_id, tab_id, tab_fqname, tab_idxname, tab_comment)
+
+Add table tab_fqname to replication set on origin node, and generate
+SET_ADD_TABLE event to allow this to propagate to other nodes.
+
+Note that the table id, tab_id, must be unique ACROSS ALL SETS.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_tab_id			alias for $2;
+	p_fqname			alias for $3;
+	p_tab_idxname		alias for $4;
+	p_tab_comment		alias for $5;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that we are the origin of the set
+	-- ----
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setAddTable(): set % not found&#39;, p_set_id;
+	end if;
+	if v_set_origin != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: setAddTable(): set % has remote origin&#39;, p_set_id;
+	end if;
+
+	if exists (select true from schemadoc.sl_subscribe
+			where sub_set = p_set_id)
+	then
+		raise exception &#39;Slony-I: cannot add table to currently subscribed set %&#39;,
+				p_set_id;
+	end if;
+
+	-- ----
+	-- Add the table to the set and generate the SET_ADD_TABLE event
+	-- ----
+	perform schemadoc.setAddTable_int(p_set_id, p_tab_id, p_fqname,
+			p_tab_idxname, p_tab_comment);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SET_ADD_TABLE&#39;,
+			p_set_id, p_tab_id, p_fqname,
+			p_tab_idxname, p_tab_comment);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setaddtable-int-integer-integer-text-name-text">setaddtable_int( integer, integer, text, name, text )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setAddTable_int (set_id, tab_id, tab_fqname, tab_idxname, tab_comment)
+
+This function processes the SET_ADD_TABLE event on remote nodes,
+adding a table to replication if the remote node is subscribing to its
+replication set.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_tab_id			alias for $2;
+	p_fqname			alias for $3;
+	p_tab_idxname		alias for $4;
+	p_tab_comment		alias for $5;
+	v_local_node_id		int4;
+	v_set_origin		int4;
+	v_sub_provider		int4;
+	v_relkind			char;
+	v_tab_reloid		oid;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- For sets with a remote origin, check that we are subscribed 
+	-- to that set. Otherwise we ignore the table because it might 
+	-- not even exist in our database.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setAddTable_int(): set % not found&#39;,
+				p_set_id;
+	end if;
+	if v_set_origin != v_local_node_id then
+		select sub_provider into v_sub_provider
+				from schemadoc.sl_subscribe
+				where sub_set = p_set_id
+				and sub_receiver = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+		if not found then
+			return 0;
+		end if;
+	end if;
+	
+	-- ----
+	-- Get the tables OID and check that it is a real table
+	-- ----
+	select PGC.oid, PGC.relkind into v_tab_reloid, v_relkind
+			from &quot;pg_catalog&quot;.pg_class PGC, &quot;pg_catalog&quot;.pg_namespace PGN
+			where PGC.relnamespace = PGN.oid
+			and p_fqname = &quot;pg_catalog&quot;.quote_ident(PGN.nspname) ||
+					&#39;.&#39; || &quot;pg_catalog&quot;.quote_ident(PGC.relname);
+	if not found then
+		raise exception &#39;Slony-I: setAddTable(): table % not found&#39;, 
+				p_fqname;
+	end if;
+	if v_relkind != &#39;r&#39; then
+		raise exception &#39;Slony-I: setAddTable(): % is not a regular table&#39;,
+				p_fqname;
+	end if;
+
+	if not exists (select indexrelid
+			from &quot;pg_catalog&quot;.pg_index PGX, &quot;pg_catalog&quot;.pg_class PGC
+			where PGX.indrelid = v_tab_reloid
+				and PGX.indexrelid = PGC.oid
+				and PGC.relname = p_tab_idxname)
+	then
+		raise exception &#39;Slony-I: setAddTable(): table % has no index %&#39;,
+				p_fqname, p_tab_idxname;
+	end if;
+
+	-- ----
+	-- Add the table to sl_table and create the trigger on it.
+	-- ----
+	insert into schemadoc.sl_table
+			(tab_id, tab_reloid, tab_set, tab_idxname, 
+			tab_altered, tab_comment) values
+			(p_tab_id, v_tab_reloid, p_set_id, p_tab_idxname,
+			false, p_tab_comment);
+	perform schemadoc.alterTableForReplication(p_tab_id);
+
+	return p_tab_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setdropsequence-integer">setdropsequence( integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setDropSequence (seq_id)
+
+On the origin node for the set, drop sequence seq_id from replication
+set, and raise SET_DROP_SEQUENCE to cause this to replicate to
+subscriber nodes.</p>
+        <pre>
+declare
+	p_seq_id		alias for $1;
+	v_set_id		int4;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Determine set id for this sequence
+	-- ----
+	select seq_set into v_set_id from schemadoc.sl_sequence where seq_id = p_seq_id;
+
+	-- ----
+	-- Ensure sequence exists
+	-- ----
+	if not found then
+		raise exception &#39;Slony-I: setDropSequence_int(): sequence % not found&#39;,
+			p_seq_id;
+	end if;
+
+	-- ----
+	-- Check that we are the origin of the set
+	-- ----
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setDropSequence(): set % not found&#39;, v_set_id;
+	end if;
+	if v_set_origin != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: setDropSequence(): set % has remote origin&#39;, v_set_id;
+	end if;
+
+	-- ----
+	-- Add the sequence to the set and generate the SET_ADD_SEQUENCE event
+	-- ----
+	perform schemadoc.setDropSequence_int(p_seq_id);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SET_DROP_SEQUENCE&#39;,
+			p_seq_id);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setdropsequence-int-integer">setdropsequence_int( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setDropSequence_int (seq_id)
+
+This processes the SET_DROP_SEQUENCE event.  On remote nodes that
+subscribe to the set containing sequence seq_id, drop the sequence
+from the replication set.</p>
+        <pre>
+declare
+	p_seq_id		alias for $1;
+	v_set_id		int4;
+	v_local_node_id		int4;
+	v_set_origin		int4;
+	v_sub_provider		int4;
+	v_relkind			char;
+	v_sync_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Determine set id for this sequence
+	-- ----
+	select seq_set into v_set_id from schemadoc.sl_sequence where seq_id = p_seq_id;
+
+	-- ----
+	-- Ensure sequence exists
+	-- ----
+	if not found then
+		return 0;
+	end if;
+
+	-- ----
+	-- For sets with a remote origin, check that we are subscribed 
+	-- to that set. Otherwise we ignore the sequence because it might 
+	-- not even exist in our database.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setDropSequence_int(): set % not found&#39;,
+				v_set_id;
+	end if;
+	if v_set_origin != v_local_node_id then
+		select sub_provider into v_sub_provider
+				from schemadoc.sl_subscribe
+				where sub_set = v_set_id
+				and sub_receiver = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+		if not found then
+			return 0;
+		end if;
+	end if;
+
+	-- ----
+	-- drop the sequence from sl_sequence, sl_seqlog
+	-- ----
+	delete from schemadoc.sl_seqlog where seql_seqid = p_seq_id;
+	delete from schemadoc.sl_sequence where seq_id = p_seq_id;
+
+	return p_seq_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setdroptable-integer">setdroptable( integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setDropTable (tab_id)
+
+Drop table tab_id from set on origin node, and generate SET_DROP_TABLE
+event to allow this to propagate to other nodes.</p>
+        <pre>
+declare
+	p_tab_id		alias for $1;
+	v_set_id		int4;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+        -- ----
+	-- Determine the set_id
+        -- ----
+	select tab_set into v_set_id from schemadoc.sl_table where tab_id = p_tab_id;
+
+	-- ----
+	-- Ensure table exists
+	-- ----
+	if not found then
+		raise exception &#39;Slony-I: setDropTable_int(): table % not found&#39;,
+			p_tab_id;
+	end if;
+
+	-- ----
+	-- Check that we are the origin of the set
+	-- ----
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setDropTable(): set % not found&#39;, v_set_id;
+	end if;
+	if v_set_origin != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: setDropTable(): set % has remote origin&#39;, v_set_id;
+	end if;
+
+	-- ----
+	-- Drop the table from the set and generate the SET_ADD_TABLE event
+	-- ----
+	perform schemadoc.setDropTable_int(p_tab_id);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SET_DROP_TABLE&#39;, p_tab_id);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.setdroptable-int-integer">setdroptable_int( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>setDropTable_int (tab_id)
+
+This function processes the SET_DROP_TABLE event on remote nodes,
+dropping a table from replication if the remote node is subscribing to
+its replication set.</p>
+        <pre>
+declare
+	p_tab_id		alias for $1;
+	v_set_id		int4;
+	v_local_node_id		int4;
+	v_set_origin		int4;
+	v_sub_provider		int4;
+	v_tab_reloid		oid;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+        -- ----
+	-- Determine the set_id
+        -- ----
+	select tab_set into v_set_id from schemadoc.sl_table where tab_id = p_tab_id;
+
+	-- ----
+	-- Ensure table exists
+	-- ----
+	if not found then
+		return 0;
+	end if;
+
+	-- ----
+	-- For sets with a remote origin, check that we are subscribed 
+	-- to that set. Otherwise we ignore the table because it might 
+	-- not even exist in our database.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = v_set_id;
+	if not found then
+		raise exception &#39;Slony-I: setDropTable_int(): set % not found&#39;,
+				v_set_id;
+	end if;
+	if v_set_origin != v_local_node_id then
+		select sub_provider into v_sub_provider
+				from schemadoc.sl_subscribe
+				where sub_set = v_set_id
+				and sub_receiver = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+		if not found then
+			return 0;
+		end if;
+	end if;
+	
+	-- ----
+	-- Drop the table from sl_table and drop trigger from it.
+	-- ----
+	perform schemadoc.alterTableRestore(p_tab_id);
+	perform schemadoc.tableDropKey(p_tab_id);
+	delete from schemadoc.sl_table where tab_id = p_tab_id;
+	return p_tab_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.slonyversion">slonyversion(  )</a>
+		</h2>
+<h3>Returns: text</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Returns the version number of the slony schema</p>
+        <pre>
+begin
+	return &#39;&#39;	|| schemadoc.slonyVersionMajor() || &#39;.&#39;
+				|| schemadoc.slonyVersionMinor() || &#39;.&#39;
+				|| schemadoc.slonyVersionPatchlevel();
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.slonyversionmajor">slonyversionmajor(  )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Returns the major version number of the slony schema</p>
+        <pre>
+begin
+	return 1;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.slonyversionminor">slonyversionminor(  )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Returns the minor version number of the slony schema</p>
+        <pre>
+begin
+	return 1;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.slonyversionpatchlevel">slonyversionpatchlevel(  )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Returns the version patch level of the slony schema</p>
+        <pre>
+begin
+	return 0;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storelisten-integer-integer-integer">storelisten( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>FUNCTION storeListen (li_origin, li_provider, li_receiver)
+
+generate STORE_LISTEN event, indicating that receiver node li_receiver
+listens to node li_provider in order to get messages coming from node
+li_origin.</p>
+        <pre>
+declare
+	p_li_origin		alias for $1;
+	p_li_provider	alias for $2;
+	p_li_receiver	alias for $3;
+begin
+	perform schemadoc.storeListen_int (p_li_origin, p_li_provider, p_li_receiver);
+	return  schemadoc.createEvent (&#39;_schemadoc&#39;, &#39;STORE_LISTEN&#39;,
+			p_li_origin, p_li_provider, p_li_receiver);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storelisten-int-integer-integer-integer">storelisten_int( integer, integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>FUNCTION storeListen_int (li_origin, li_provider, li_receiver)
+
+Process STORE_LISTEN event, indicating that receiver node li_receiver
+listens to node li_provider in order to get messages coming from node
+li_origin.</p>
+        <pre>
+declare
+	p_li_origin		alias for $1;
+	p_li_provider	alias for $2;
+	p_li_receiver	alias for $3;
+	v_exists		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	select 1 into v_exists
+			from schemadoc.sl_listen
+			where li_origin = p_li_origin
+			and li_provider = p_li_provider
+			and li_receiver = p_li_receiver;
+	if not found then
+		-- ----
+		-- In case we receive STORE_LISTEN events before we know
+		-- about the nodes involved in this, we generate those nodes
+		-- as pending.
+		-- ----
+		if not exists (select 1 from schemadoc.sl_node
+						where no_id = p_li_origin) then
+			perform schemadoc.storeNode_int (p_li_origin, &#39;&lt;event pending&gt;&#39;);
+		end if;
+		if not exists (select 1 from schemadoc.sl_node
+						where no_id = p_li_provider) then
+			perform schemadoc.storeNode_int (p_li_provider, &#39;&lt;event pending&gt;&#39;);
+		end if;
+		if not exists (select 1 from schemadoc.sl_node
+						where no_id = p_li_receiver) then
+			perform schemadoc.storeNode_int (p_li_receiver, &#39;&lt;event pending&gt;&#39;);
+		end if;
+
+		insert into schemadoc.sl_listen
+				(li_origin, li_provider, li_receiver) values
+				(p_li_origin, p_li_provider, p_li_receiver);
+	end if;
+
+	return 0;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storenode-integer-text">storenode( integer, text )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>no_id - Node ID #
+no_comment - Human-oriented comment
+
+Generate the STORE_NODE event for node no_id</p>
+        <pre>
+declare
+	p_no_id			alias for $1;
+	p_no_comment	alias for $2;
+begin
+	perform schemadoc.storeNode_int (p_no_id, p_no_comment);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;STORE_NODE&#39;,
+									p_no_id, p_no_comment);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storenode-int-integer-text">storenode_int( integer, text )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>no_id - Node ID #
+no_comment - Human-oriented comment
+
+Internal function to process the STORE_NODE event for node no_id</p>
+        <pre>
+declare
+	p_no_id			alias for $1;
+	p_no_comment	alias for $2;
+	v_old_row		record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check if the node exists
+	-- ----
+	select * into v_old_row
+			from schemadoc.sl_node
+			where no_id = p_no_id
+			for update;
+	if found then 
+		-- ----
+		-- Node exists, update the existing row.
+		-- ----
+		update schemadoc.sl_node
+				set no_comment = p_no_comment
+				where no_id = p_no_id;
+	else
+		-- ----
+		-- New node, insert the sl_node row
+		-- ----
+		insert into schemadoc.sl_node
+				(no_id, no_active, no_comment) values
+				(p_no_id, &#39;f&#39;, p_no_comment);
+	end if;
+
+	return p_no_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storepath-integer-integer-text-integer">storepath( integer, integer, text, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>FUNCTION storePath (pa_server, pa_client, pa_conninfo, pa_connretry)
+
+Generate the STORE_PATH event indicating that node pa_client can
+access node pa_server using DSN pa_conninfo</p>
+        <pre>
+declare
+	p_pa_server		alias for $1;
+	p_pa_client		alias for $2;
+	p_pa_conninfo	alias for $3;
+	p_pa_connretry	alias for $4;
+begin
+	perform schemadoc.storePath_int(p_pa_server, p_pa_client,
+			p_pa_conninfo, p_pa_connretry);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;STORE_PATH&#39;, 
+			p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storepath-int-integer-integer-text-integer">storepath_int( integer, integer, text, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>FUNCTION storePath (pa_server, pa_client, pa_conninfo, pa_connretry)
+
+Process the STORE_PATH event indicating that node pa_client can
+access node pa_server using DSN pa_conninfo</p>
+        <pre>
+declare
+	p_pa_server		alias for $1;
+	p_pa_client		alias for $2;
+	p_pa_conninfo	alias for $3;
+	p_pa_connretry	alias for $4;
+	v_dummy			int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check if the path already exists
+	-- ----
+	select 1 into v_dummy
+			from schemadoc.sl_path
+			where pa_server = p_pa_server
+			and pa_client = p_pa_client
+			for update;
+	if found then
+		-- ----
+		-- Path exists, update pa_conninfo
+		-- ----
+		update schemadoc.sl_path
+				set pa_conninfo = p_pa_conninfo,
+					pa_connretry = p_pa_connretry
+				where pa_server = p_pa_server
+				and pa_client = p_pa_client;
+	else
+		-- ----
+		-- New path
+		--
+		-- In case we receive STORE_PATH events before we know
+		-- about the nodes involved in this, we generate those nodes
+		-- as pending.
+		-- ----
+		if not exists (select 1 from schemadoc.sl_node
+						where no_id = p_pa_server) then
+			perform schemadoc.storeNode_int (p_pa_server, &#39;&lt;event pending&gt;&#39;);
+		end if;
+		if not exists (select 1 from schemadoc.sl_node
+						where no_id = p_pa_client) then
+			perform schemadoc.storeNode_int (p_pa_client, &#39;&lt;event pending&gt;&#39;);
+		end if;
+		insert into schemadoc.sl_path
+				(pa_server, pa_client, pa_conninfo, pa_connretry) values
+				(p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry);
+	end if;
+
+	return 0;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storeset-integer-text">storeset( integer, text )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Generate STORE_SET event for set set_id with human readable comment set_comment</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_set_comment		alias for $2;
+	v_local_node_id		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+
+	insert into schemadoc.sl_set
+			(set_id, set_origin, set_comment) values
+			(p_set_id, v_local_node_id, p_set_comment);
+
+	return schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;STORE_SET&#39;, 
+			p_set_id, v_local_node_id, p_set_comment);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storeset-int-integer-integer-text">storeset_int( integer, integer, text )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>storeSet_int (set_id, set_origin, set_comment)
+
+Process the STORE_SET event, indicating the new set with given ID,
+origin node, and human readable comment.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	p_set_origin		alias for $2;
+	p_set_comment		alias for $3;
+	v_dummy				int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	select 1 into v_dummy
+			from schemadoc.sl_set
+			where set_id = p_set_id
+			for update;
+	if found then 
+		update schemadoc.sl_set
+				set set_comment = p_set_comment
+				where set_id = p_set_id;
+	else
+		if not exists (select 1 from schemadoc.sl_node
+						where no_id = p_set_origin) then
+			perform schemadoc.storeNode_int (p_set_origin, &#39;&lt;event pending&gt;&#39;);
+		end if;
+		insert into schemadoc.sl_set
+				(set_id, set_origin, set_comment) values
+				(p_set_id, p_set_origin, p_set_comment);
+	end if;
+
+	return p_set_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storetrigger-integer-name">storetrigger( integer, name )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>storeTrigger (trig_tabid, trig_tgname)
+
+Submits STORE_TRIGGER event to indicate that trigger trig_tgname on
+replicated table trig_tabid will NOT be disabled.</p>
+        <pre>
+declare
+	p_trig_tabid		alias for $1;
+	p_trig_tgname		alias for $2;
+begin
+	perform schemadoc.storeTrigger_int(p_trig_tabid, p_trig_tgname);
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;STORE_TRIGGER&#39;,
+			p_trig_tabid, p_trig_tgname);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.storetrigger-int-integer-name">storetrigger_int( integer, name )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>storeTrigger_int (trig_tabid, trig_tgname)
+
+Processes STORE_TRIGGER event to make sure that trigger trig_tgname on
+replicated table trig_tabid is NOT disabled.</p>
+        <pre>
+declare
+	p_trig_tabid		alias for $1;
+	p_trig_tgname		alias for $2;
+	v_tab_altered		boolean;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Get the current table status (altered or not)
+	-- ----
+	select tab_altered into v_tab_altered
+			from schemadoc.sl_table where tab_id = p_trig_tabid;
+	if not found then
+		-- ----
+		-- Not found is no hard error here, because that might
+		-- mean that we are not subscribed to that set
+		-- ----
+		return 0;
+	end if;
+
+	-- ----
+	-- If the table is modified for replication, restore the original state
+	-- ----
+	if v_tab_altered then
+		perform schemadoc.alterTableRestore(p_trig_tabid);
+	end if;
+
+	-- ----
+	-- Make sure that an entry for this trigger exists
+	-- ----
+	delete from schemadoc.sl_trigger
+			where trig_tabid = p_trig_tabid
+			  and trig_tgname = p_trig_tgname;
+	insert into schemadoc.sl_trigger (
+				trig_tabid, trig_tgname
+			) values (
+				p_trig_tabid, p_trig_tgname
+			);
+
+	-- ----
+	-- Put the table back into replicated state if it was
+	-- ----
+	if v_tab_altered then
+		perform schemadoc.alterTableForReplication(p_trig_tabid);
+	end if;
+
+	return p_trig_tabid;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.subscribeset-integer-integer-integer-boolean">subscribeset( integer, integer, integer, boolean )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>subscribeSet (sub_set, sub_provider, sub_receiver, sub_forward)
+
+Makes sure that the receiver is not the provider, then stores the
+subscription, and publishes the SUBSCRIBE_SET event to other nodes.</p>
+        <pre>
+declare
+	p_sub_set			alias for $1;
+	p_sub_provider		alias for $2;
+	p_sub_receiver		alias for $3;
+	p_sub_forward		alias for $4;
+	v_set_origin		int4;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that this is called on the receiver node
+	-- ----
+	if p_sub_receiver != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: subscribeSet() must be called on receiver&#39;;
+	end if;
+
+	-- ----
+	-- Check that the origin and provider of the set are remote
+	-- ----
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_sub_set;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_sub_set;
+	end if;
+	if v_set_origin = p_sub_receiver then
+		raise exception 
+				&#39;Slony-I: set origin and receiver cannot be identical&#39;;
+	end if;
+	if p_sub_receiver = p_sub_provider then
+		raise exception 
+				&#39;Slony-I: set provider and receiver cannot be identical&#39;;
+	end if;
+
+	-- ----
+	-- Call the internal procedure to store the subscription
+	-- ----
+	perform schemadoc.subscribeSet_int(p_sub_set, p_sub_provider,
+			p_sub_receiver, p_sub_forward);
+
+	-- ----
+	-- Create the SUBSCRIBE_SET event
+	-- ----
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;SUBSCRIBE_SET&#39;, 
+			p_sub_set, p_sub_provider, p_sub_receiver, 
+			case p_sub_forward when true then &#39;t&#39; else &#39;f&#39; end);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.subscribeset-int-integer-integer-integer-boolean">subscribeset_int( integer, integer, integer, boolean )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>subscribeSet_int (sub_set, sub_provider, sub_receiver, sub_forward)
+
+Internal actions for subscribing receiver sub_receiver to subscription
+set sub_set.</p>
+        <pre>
+declare
+	p_sub_set			alias for $1;
+	p_sub_provider		alias for $2;
+	p_sub_receiver		alias for $3;
+	p_sub_forward		alias for $4;
+	v_set_origin		int4;
+	v_sub_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Provider change is only allowed for active sets
+	-- ----
+	if p_sub_receiver = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		select sub_active into v_sub_row from schemadoc.sl_subscribe
+				where sub_set = p_sub_set
+				and sub_receiver = p_sub_receiver;
+		if found then
+			if not v_sub_row.sub_active then
+				raise exception &#39;Slony-I: set % is not active, cannot change provider&#39;,
+						p_sub_set;
+			end if;
+		end if;
+	end if;
+
+	-- ----
+	-- Try to change provider and/or forward for an existing subscription
+	-- ----
+	update schemadoc.sl_subscribe
+			set sub_provider = p_sub_provider,
+				sub_forward = p_sub_forward
+			where sub_set = p_sub_set
+			and sub_receiver = p_sub_receiver;
+	if found then
+		return p_sub_set;
+	end if;
+
+	-- ----
+	-- Not found, insert a new one
+	-- ----
+	if not exists (select true from schemadoc.sl_path
+			where pa_server = p_sub_provider
+			and pa_client = p_sub_receiver)
+	then
+		insert into schemadoc.sl_path
+				(pa_server, pa_client, pa_conninfo, pa_connretry)
+				values 
+				(p_sub_provider, p_sub_receiver, 
+				&#39;&lt;event pending&gt;&#39;, 10);
+	end if;
+	insert into schemadoc.sl_subscribe
+			(sub_set, sub_provider, sub_receiver, sub_forward, sub_active)
+			values (p_sub_set, p_sub_provider, p_sub_receiver,
+				p_sub_forward, false);
+
+	-- ----
+	-- If the set origin is here, then enable the subscription
+	-- ----
+	select set_origin into v_set_origin
+			from schemadoc.sl_set
+			where set_id = p_sub_set;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_sub_set;
+	end if;
+
+	if v_set_origin = schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		perform schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;ENABLE_SUBSCRIPTION&#39;, 
+				p_sub_set, p_sub_provider, p_sub_receiver, 
+				case p_sub_forward when true then &#39;t&#39; else &#39;f&#39; end);
+		perform schemadoc.enableSubscription(p_sub_set, 
+				p_sub_provider, p_sub_receiver);
+	end if;
+
+	return p_sub_set;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.tableaddkey-text">tableaddkey( text )</a>
+		</h2>
+<h3>Returns: text</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>tableAddKey (tab_fqname) - if the table has not got a column of the
+form _Slony-I_&lt;clustername&gt;_rowID, then add it as a bigint, defaulted
+to nextval() for a sequence created for the cluster.</p>
+        <pre>
+declare
+	p_tab_fqname	alias for $1;
+	v_attkind		text default &#39;&#39;;
+	v_attrow		record;
+	v_have_serial	bool default &#39;f&#39;;
+begin
+	--
+	-- Loop over the attributes of this relation
+	-- and add a &quot;v&quot; for every user column, and a &quot;k&quot;
+	-- if we find the Slony-I special serial column.
+	--
+	for v_attrow in select PGA.attnum, PGA.attname
+			from &quot;pg_catalog&quot;.pg_class PGC,
+			    &quot;pg_catalog&quot;.pg_namespace PGN,
+				&quot;pg_catalog&quot;.pg_attribute PGA
+			where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			    &quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+				and PGN.oid = PGC.relnamespace
+				and PGA.attrelid = PGC.oid
+				and not PGA.attisdropped
+				and PGA.attnum &gt; 0
+			order by attnum
+	loop
+		if v_attrow.attname = &#39;_Slony-I_schemadoc_rowID&#39; then
+		    v_attkind := v_attkind || &#39;k&#39;;
+			v_have_serial := &#39;t&#39;;
+		else
+			v_attkind := v_attkind || &#39;v&#39;;
+		end if;
+	end loop;
+	
+	--
+	-- A table must have at least one attribute, so not finding
+	-- anything means the table does not exist.
+	--
+	if not found then
+		raise exception &#39;Slony-I: table % not found&#39;, p_tab_fqname;
+	end if;
+
+	--
+	-- If it does not have the special serial column, we
+	-- have to add it. This will be only half way done.
+	-- The function to add the table to the set must finish
+	-- these definitions with NOT NULL and UNIQUE after
+	-- updating all existing rows.
+	--
+	if not v_have_serial then
+		execute &#39;lock table &#39; || p_tab_fqname ||
+			&#39; in access exclusive mode&#39;;
+		execute &#39;alter table only &#39; || p_tab_fqname ||
+			&#39; add column &quot;_Slony-I_schemadoc_rowID&quot; bigint;&#39;;
+		execute &#39;alter table only &#39; || p_tab_fqname ||
+			&#39; alter column &quot;_Slony-I_schemadoc_rowID&quot; &#39; ||
+			&#39; set default &quot;pg_catalog&quot;.nextval(&#39;&#39;schemadoc.sl_rowid_seq&#39;&#39;);&#39;;
+
+		v_attkind := v_attkind || &#39;k&#39;;
+	end if;
+
+	--
+	-- Return the resulting Slony-I attkind
+	--
+	return v_attkind;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.tabledropkey-integer">tabledropkey( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>tableDropKey (tab_id)
+
+If the specified table has a column &quot;_Slony-I_&lt;clustername&gt;_rowID&quot;,
+then drop it.</p>
+        <pre>
+declare
+	p_tab_id		alias for $1;
+	v_tab_fqname	text;
+	v_tab_oid		oid;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Construct the tables fully qualified name and get its oid
+	-- ----
+	select &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+				&quot;pg_catalog&quot;.quote_ident(PGC.relname),
+				PGC.oid into v_tab_fqname, v_tab_oid
+			from schemadoc.sl_table T,
+				&quot;pg_catalog&quot;.pg_class PGC,
+				&quot;pg_catalog&quot;.pg_namespace PGN
+			where T.tab_id = p_tab_id
+				and T.tab_reloid = PGC.oid
+				and PGC.relnamespace = PGN.oid;
+	if not found then
+		raise exception &#39;Slony-I: table with ID % not found&#39;, p_tab_id;
+	end if;
+
+	-- ----
+	-- Drop the special serial ID column if the table has it
+	-- ----
+	if exists (select true from &quot;pg_catalog&quot;.pg_attribute
+			where attrelid = v_tab_oid
+				and attname = &#39;_Slony-I_schemadoc_rowID&#39;)
+	then
+		execute &#39;lock table &#39; || v_tab_fqname ||
+				&#39; in access exclusive mode&#39;;
+		execute &#39;alter table &#39; || v_tab_fqname ||
+				&#39; drop column &quot;_Slony-I_schemadoc_rowID&quot;&#39;;
+	end if;
+
+	return p_tab_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.tablehasserialkey-text">tablehasserialkey( text )</a>
+		</h2>
+<h3>Returns: boolean</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>tableHasSerialKey (tab_fqname)
+
+Checks if a table has our special serial key column that is used if
+the table has no natural unique constraint.</p>
+        <pre>
+declare
+	p_tab_fqname	alias for $1;
+	v_attnum		int2;
+begin
+	select PGA.attnum into v_attnum
+			from &quot;pg_catalog&quot;.pg_class PGC,
+				&quot;pg_catalog&quot;.pg_namespace PGN,
+				&quot;pg_catalog&quot;.pg_attribute PGA
+			where &quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+				&quot;pg_catalog&quot;.quote_ident(PGC.relname) = p_tab_fqname
+				and PGC.relnamespace = PGN.oid
+				and PGA.attrelid = PGC.oid
+				and PGA.attname = &#39;_Slony-I_schemadoc_rowID&#39;
+				and not PGA.attisdropped;
+	return found;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.uninstallnode">uninstallnode(  )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Reset the whole database to standalone by removing the whole
+replication system.</p>
+        <pre>
+declare
+	v_tab_row		record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- This is us ... time for suicide! Restore all tables to
+	-- their original status.
+	-- ----
+	for v_tab_row in select * from schemadoc.sl_table loop
+		perform schemadoc.alterTableRestore(v_tab_row.tab_id);
+		perform schemadoc.tableDropKey(v_tab_row.tab_id);
+	end loop;
+
+	raise notice &#39;Slony-I: Please drop schema &quot;_schemadoc&quot;&#39;;
+	return 0;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.unlockset-integer">unlockset( integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>Remove the special trigger from all tables of a set that disables access to it.</p>
+        <pre>
+declare
+	p_set_id			alias for $1;
+	v_local_node_id		int4;
+	v_set_row			record;
+	v_tab_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that the set exists and that we are the origin
+	-- and that it is not already locked.
+	-- ----
+	v_local_node_id := schemadoc.getLocalNodeId(&#39;_schemadoc&#39;);
+	select * into v_set_row from schemadoc.sl_set
+			where set_id = p_set_id
+			for update;
+	if not found then
+		raise exception &#39;Slony-I: set % not found&#39;, p_set_id;
+	end if;
+	if v_set_row.set_origin &lt;&gt; v_local_node_id then
+		raise exception &#39;Slony-I: set % does not originate on local node&#39;,
+				p_set_id;
+	end if;
+	if v_set_row.set_locked isnull then
+		raise exception &#39;Slony-I: set % is not locked&#39;, p_set_id;
+	end if;
+
+	-- ----
+	-- Drop the lockedSet trigger from all tables in the set.
+	-- ----
+	for v_tab_row in select T.tab_id,
+			&quot;pg_catalog&quot;.quote_ident(PGN.nspname) || &#39;.&#39; ||
+			&quot;pg_catalog&quot;.quote_ident(PGC.relname) as tab_fqname
+			from schemadoc.sl_table T,
+				&quot;pg_catalog&quot;.pg_class PGC, &quot;pg_catalog&quot;.pg_namespace PGN
+			where T.tab_set = p_set_id
+				and T.tab_reloid = PGC.oid
+				and PGC.relnamespace = PGN.oid
+			order by tab_id
+	loop
+		execute &#39;drop trigger &quot;_schemadoc_lockedset_&#39; || 
+				v_tab_row.tab_id || &#39;&quot; on &#39; || v_tab_row.tab_fqname;
+	end loop;
+
+	-- ----
+	-- Clear out the set_locked field
+	-- ----
+	update schemadoc.sl_set
+			set set_locked = NULL
+			where set_id = p_set_id;
+
+	return p_set_id;
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.unsubscribeset-integer-integer">unsubscribeset( integer, integer )</a>
+		</h2>
+<h3>Returns: bigint</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>unsubscribeSet (sub_set, sub_receiver) 
+
+Unsubscribe node sub_receiver from subscription set sub_set.  This is
+invoked on the receiver node.  It verifies that this does not break
+any chains (e.g. - where sub_receiver is a provider for another node),
+then restores tables, drops Slony-specific keys, drops table entries
+for the set, drops the subscription, and generates an UNSUBSCRIBE_SET
+node to publish that the node is being dropped.</p>
+        <pre>
+declare
+	p_sub_set			alias for $1;
+	p_sub_receiver		alias for $2;
+	v_tab_row			record;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- Check that this is called on the receiver node
+	-- ----
+	if p_sub_receiver != schemadoc.getLocalNodeId(&#39;_schemadoc&#39;) then
+		raise exception &#39;Slony-I: unsubscribeSet() must be called on receiver&#39;;
+	end if;
+
+	-- ----
+	-- Check that this does not break any chains
+	-- ----
+	if exists (select true from schemadoc.sl_subscribe
+			where sub_set = p_sub_set
+				and sub_provider = p_sub_receiver)
+	then
+		raise exception &#39;Slony-I: Cannot unsubscibe set % while being provider&#39;,
+				p_sub_set;
+	end if;
+
+	-- ----
+	-- Restore all tables original triggers and rules and remove
+	-- our replication stuff.
+	-- ----
+	for v_tab_row in select tab_id from schemadoc.sl_table
+			where tab_set = p_sub_set
+			order by tab_id
+	loop
+		perform schemadoc.alterTableRestore(v_tab_row.tab_id);
+		perform schemadoc.tableDropKey(v_tab_row.tab_id);
+	end loop;
+
+	-- ----
+	-- Remove the setsync status. This will also cause the
+	-- worker thread to ignore the set and stop replicating
+	-- right now.
+	-- ----
+	delete from schemadoc.sl_setsync
+			where ssy_setid = p_sub_set;
+
+	-- ----
+	-- Remove all sl_table and sl_sequence entries for this set.
+	-- Should we ever subscribe again, the initial data
+	-- copy process will create new ones.
+	-- ----
+	delete from schemadoc.sl_table
+			where tab_set = p_sub_set;
+	delete from schemadoc.sl_sequence
+			where seq_set = p_sub_set;
+
+	-- ----
+	-- Call the internal procedure to drop the subscription
+	-- ----
+	perform schemadoc.unsubscribeSet_int(p_sub_set, p_sub_receiver);
+
+	-- ----
+	-- Create the UNSUBSCRIBE_SET event
+	-- ----
+	return  schemadoc.createEvent(&#39;_schemadoc&#39;, &#39;UNSUBSCRIBE_SET&#39;, 
+			p_sub_set, p_sub_receiver);
+end;
+</pre>
+	
+		<hr>
+		<h2>Function: 
+			<a href="#schemadoc.schema"><a name="schemadoc.function.unsubscribeset-int-integer-integer">unsubscribeset_int( integer, integer )</a>
+		</h2>
+<h3>Returns: integer</h3>
+<h3>Language: PLPGSQL</h3>
+        <p>unsubscribeSet_int (sub_set, sub_receiver)
+
+All the REAL work of removing the subscriber is done before the event
+is generated, so this function just has to drop the references to the
+subscription in sl_subscribe.</p>
+        <pre>
+declare
+	p_sub_set			alias for $1;
+	p_sub_receiver		alias for $2;
+begin
+	-- ----
+	-- Grab the central configuration lock
+	-- ----
+	lock table schemadoc.sl_config_lock;
+
+	-- ----
+	-- All the real work is done before event generation on the
+	-- subscriber.
+	-- ----
+	delete from schemadoc.sl_subscribe
+			where sub_set = p_sub_set
+				and sub_receiver = p_sub_receiver;
+
+	return p_sub_set;
+end;
+</pre>
+	
+
+
+<p class="w3ref">Generated by <a href="http://www.rbt.ca/autodoc/">PostgreSQL Autodoc</a></p>
+<p class="w3ref"><a href="http://validator.w3.org/check/referer">W3C HTML 4.01 Strict</a></p>
+</body></html>
--- /dev/null
+++ doc/howto/randomfacts.txt
@@ -0,0 +1,57 @@
+Random Things That Ought To Be Documented Somewhere
+---------------------------------------------------------------------------------------------
+
+$Id: randomfacts.txt,v 1.2.2.1 2004/09/30 17:45:05 cbbrowne Exp $
+
+These are things that have been noticed that need to be documented.
+There should probably be a "How To Set Up a Simple Slony-I Replication
+System" document as well as "Advanced Slonying for Master DBAs," and
+these should be added to those sorts of documents...
+
+1.  Yes, you CAN "kill -9" slon processes
+
+2.  You can subscribe a node without having started the "slon" process
+for that node.
+
+Nothing will start replicating until the "slon" starts up.
+
+3.  No, you don't really need a "node 1".  
+
+In many places, slonik defaults kind of assume that there is one, but
+it doesn't HAVE to be there.
+
+4.  A little more about primary keys.
+
+Slony-I NEEDS to have a primary key candidate to work with in order to
+uniquely specify tuples that are to be replicated.  This can work out
+three ways:
+
+ - If the table has a primary key defined, then you can do a SET ADD
+   TABLE on the table, and it'll "just replicate."
+
+ - If the table has NO "unique, not NULL" key, you need to add one.
+
+   There's a slonik command to do that; TABLE ADD KEY.
+
+ - The _third_ case is where the table has one or more _candidate_
+    primary keys, none of which are formally defined to be THE primary
+    key.
+
+    In that case, you must pick one of them, and specify it in the SET
+    ADD TABLE statement.
+
+5 I want to update data on any of my servers, and have it propagate
+  around.
+
+  That case is specifically NOT addressed by Slony-I.  Slony-I _locks_
+  all the replicated tables on the subscribers; updates are only
+  permitted on the "master" node.
+
+  There are plans for a later Slony-II project to address distributed
+  updates; part of the point of Slony-I is to provide the
+  "bootstrapping" system needed to get multiple databases "in sync,"
+  which is a prerequisite for being able to do distributed updates.
+
+  That still means that distributed updates (e.g. - doing updates at
+  other than One Single Master Server Node) is NOT part of the Slony-I
+  design.
Index: slonik_commands.html
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/doc/howto/slonik_commands.html,v
retrieving revision 1.1
retrieving revision 1.1.2.1
diff -Ldoc/howto/slonik_commands.html -Ldoc/howto/slonik_commands.html -u -w -r1.1 -r1.1.2.1
--- doc/howto/slonik_commands.html
+++ doc/howto/slonik_commands.html
@@ -37,6 +37,8 @@
 		<li><a href="#stmt_merge_set">MERGE SET</a>
 		<li><a href="#stmt_set_add_table">SET ADD TABLE</a>
 		<li><a href="#stmt_set_add_sequence">SET ADD SEQUENCE</a>
+		<li><a href="#stmt_set_drop_table">SET DROP TABLE</a>
+		<li><a href="#stmt_set_drop_sequence">SET DROP SEQUENCE</a>
 		<li><a href="#stmt_store_trigger">STORE TRIGGER</a>
 		<li><a href="#stmt_drop_trigger">DROP TRIGGER</a>
 		<li><a href="#stmt_subscribe_set">SUBSCRIBE SET</a>
@@ -57,11 +59,12 @@
 </a>
 <div style="margin-left:40px; margin-right:80px;">
 <p>
-	Slonik is a commandline utility designed specifically to
-	setup and modify configurations of the Slony-I replication
-	system. 
-	This document is meant as a developer syntax-booklet, not
-	as a manual how to configure and setup a replication system.
+	Slonik is a commandline utility designed specifically to setup
+	and modify configurations of the Slony-I replication system.
+</p>
+
+<p> This document is meant as a developer syntax-booklet, not as a
+manual on how to configure and set up a replication system.
 </p>
 <p align="right">Back to <a href="#index">Index</a></p>
 </div>
@@ -72,16 +75,29 @@
 </a>
 <div style="margin-left:40px; margin-right:80px;">
 <p>
-	The slonik commandline utility is supposed to be embedded into
-	shell scripts and reads commands from files or stdin (via
-	here documents for example). Nearly all of the real configuration
-	work is done by calling stored procedures after loading the 
-	Slony-I support base into a database. Slonik was created because
-	these stored procedures have special requirements as to on which
-	particular node in the replication system they are called, the
-	lack of named parameters for stored procedures makes it rather
-	hard to do this from the psql prompt, and psql lacks the ability
-	to maintain multiple connections with open transactions.
+	The slonik commandline utility is supposed to be used embedded
+	into shell scripts and reads commands from files or stdin (via
+	here documents for example). Nearly all of the <i>real</i>
+	configuration work is done by calling stored procedures after
+	loading the Slony-I support base into a database.  You may
+	find documentation for those procedures in the <a
+	href="schemadoc.html"> Slony-I Schema Documentation </a>, as
+	well as in comments associated with them in the database.
+</p>
+      <p>
+	Slonik was created because:
+      <ul>
+
+	<li> The stored procedures have special requirements as to on
+	which particular node in the replication system they are
+	called,
+
+	<li> the lack of named parameters for stored procedures makes
+	it rather hard to do this from the psql prompt, and
+
+	<li> psql lacks the ability to maintain multiple connections
+	with open transactions.
+      </ul>
 </p>
 <p>
 	
@@ -95,10 +111,10 @@
 	parentheses. Each option consists of one or more keywords, followed
 	by an equal sign, followed by a value. Multiple options inside the
 	parentheses are separated by commas. All keywords are case
-	insensitive.
+	insensitive.  The language should remind the reader of SQL.
 </p>
 <p>
-	Option values are:
+	Option values may be:
 	<ul>
 		<li>integer values
 		<li>string literals enclosed in single quotes
@@ -135,9 +151,10 @@
 </a>
 <div style="margin-left:40px; margin-right:80px;">
 <p>
-	The following commands must appear at the very top of every slonik
-	command script. They do not cause any direct action on any of the
-	nodes in the replication system, but affect the entire script.
+	The following commands must appear at the very top of every
+	slonik command script. They do not cause any direct action on
+	any of the nodes in the replication system, but affect the
+	execution of the entire script.
 </p>
 
 <!-- **************************************** -->
@@ -149,14 +166,23 @@
 	CLUSTER NAME = &lt;string&gt;;
 <h3>Description:</h3>
 <p>
-	Must be the very first command in every slonik script. Defines the
-	namespace in which all Slony-I specific functions, procedures,
-	tables and sequences are defined. The namespace name is built by
-	prefixing the given string literal with an underscore. This
-	namespace will be identical in all databases that participate in
-	the same replication group. No user objects are supposed to live
-	in this namespace and the namespace is not allowed to exist prior
-	to adding a database to the replication system.
+	Must be the very first command in every slonik script. Defines
+	the namespace in which all Slony-I specific functions,
+	procedures, tables and sequences are defined. The namespace
+	name is built by prefixing the given string literal with an
+	underscore. This namespace will be identical in all databases
+	that participate in the same replication group. 
+</p>
+
+<p>
+                  No user objects are supposed to live in this
+                  namespace and the namespace is not allowed to exist
+                  prior to adding a database to the replication
+                  system.  Thus, if you add a new node using <tt>
+                  pg_dump -s </tt> on a database that is already in
+                  the cluster of replicated databases, you will need
+                  to drop the namespace via the SQL command <tt> DROP
+                  SCHEMA _testcluster CASCADE; </tt>.
 </p>
 <h3>Example:</h3>
 <p>
@@ -174,17 +200,17 @@
 	NODE &lt;ival&gt ADMIN CONNINFO = &lt;string&gt;;
 <h3>Description:</h3>
 <p>
-	Describes how the slonik utility can reach a nodes database in the cluster
-	from where it is run (usually the DBA's workstation). The conninfo
-	string is the string agrument given to the PQconnectdb() libpq
-	function. The user as to connect must be the special replication
-	superuser, as some of the actions performed later may include
-	operations that are strictly reserved for database superusers by
-	PostgreSQL.
+	Describes how the slonik utility can reach a nodes database in
+	the cluster from where it is run (likely the DBA's
+	workstation). The conninfo string is the string agrument given
+	to the PQconnectdb() libpq function. The user as to connect
+	must be the special replication superuser, as some of the
+	actions performed later may include operations that are
+	strictly reserved for database superusers by PostgreSQL.
 </p>
 <p>
 	The slonik utility will not try to connect to the databases
-	unless any subsequent command requires the connection. 
+	unless some subsequent command requires the connection.
 </p>
 <p>
 	Note: As mentioned in the original documents, Slony-I is designed as an
@@ -192,7 +218,17 @@
 	throughout the entire development that the database servers and
 	administrative workstations involved in replication and/or setup
 	and configuration activities can use simple authentication schemes
-	like trust. 
+	like <tt>trust</tt>.   Alternatively, libpq can read passwords from
+                 <tt> .pgpass </tt>.
+</p>
+
+<p>
+	Note: If you need to change the DSN information for a node, as
+	would happen if the IP address for a host were to change, you
+	may submit the new information using this command, and that
+	configuration will be propagated.  Existing <tt> slon </tt>
+	processes will need to be restarted in order to become aware
+	of the configuration change.
 </p>
 <h3>Example:</h3>
 <p>
@@ -306,6 +342,8 @@
 <p>
 	Initialize a new node and add it to the configuration of
 	an existing cluster.
+</p>
+<p>
 	The initialization process consists of creating the cluster
 	namespace in the new node (the database itself must already
 	exist), loading all the base tables, functions, procedures
@@ -431,7 +469,7 @@
 	Causes an eventually running replication daemon on the specified
 	node to shutdown and restart itself. Theoretically this command
 	should be obsolete. In practice, TCP timeouts can delay critical
-	configuration changes to actually happen in the case, a former
+	configuration changes to actually happen in the case where a former
 	forwarding node failed and needs to be bypassed by subscribers.
 </p>
 <h3>Example:</h3>
@@ -568,7 +606,7 @@
 	A <b>listen</b> entry causes a node (receiver) to query an event
 	provider for events that originate from a specific node, as well
 	as confirmations from every existing node. It requires a <b>path</b>
-	to exist so that the receiver (as client> can connect to the provider
+	to exist so that the receiver (as client) can connect to the provider
 	(as server).
 </p>
 <p>
@@ -806,11 +844,16 @@
 	MERGE SET ( &lt;options&gt; );
 <h3>Description:</h3>
 <p>
-	Merge a set of tables and sequences into another one. This function
-	is a workaround for the problem that it is not possible to add
-	tables/sequences to subscribed sets. One can create a temporary set,
-	add the new objects to that, subscribe all nodes, currently subscribed
-	to the other set to this new one and then merge the two together.
+	Merge a set of tables and sequences into another one. This
+	function is a workaround for the problem that it is not
+	possible to add tables/sequences to already-subscribed
+	sets. One may create a temporary set, add the new objects to
+	that, subscribe all nodes currently subscribed to the other
+	set to this new one, and then merge the two together.
+</p>
+<p>
+	This request will fail if the two sets do not have exactly the
+	same set of subscribers.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -853,8 +896,8 @@
 <h3>Description:</h3>
 <p>
 	Add an existing user table to a replication set. The set
-	cannot currently be subscribed by any other node (this
-	functionality will soon be implemented with a MERGE SET command).
+	cannot currently be subscribed by any other node - that
+	functionality is supported by the<tt>MERGE SET</tt> command.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -898,7 +941,8 @@
 		to be used as the row identifier for replication purposes. Or the
 		keyword SERIAL to use the special column added with a previous
 		<a href="#table_add_key">TABLE ADD KEY</a> command. Default
-		is to use the tables primary key.
+		is to use the table's primary key.  The index name is <i> not </i> 
+		fully qualified; you must omit the namespace.
 	</p></td>
 </tr>
 <tr>
@@ -931,8 +975,8 @@
 <h3>Description:</h3>
 <p>
 	Add an existing user sequence to a replication set. The set
-	cannot currently be subscribed by any other node (this
-	functionality will soon be implemented with a MERGE SET command).
+	cannot currently be subscribed by any other node - that
+	functionality is supported by the MERGE SET command.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -982,6 +1026,77 @@
 <p align="right">Back to <a href="#index">Index</a></p>
 
 <!-- **************************************** -->
+<a name="stmt_set_drop_table">
+<h3>SET DROP TABLE</h3>
+</a>
+<div style="margin-left:40px; margin-right:0px;">
+<h3>Synopsis:</h3>
+	SET DROP TABLE ( &lt;options&gt; );
+<h3>Description:</h3>
+<p>
+	Drop an existing user table to a replication set.
+</p>
+<table border="0" cellpadding="10">
+<tr>
+	<td align="left" valign="top" nowrap><b>ORIGIN = &lt;ival&gt;</b></td>
+	<td align="left" valign="top"><p>
+		The current origin of the set. A future version of slonik
+		might figure out this information by itself.
+	</p></td>
+</tr>
+<tr>
+	<td align="left" valign="top" nowrap><b>ID = &lt;ival&gt;</b></td>
+	<td align="left" valign="top"><p>
+		Unique ID of the table. 
+	</p></td>
+</tr>
+</table>
+<h3>Example:</h3>
+<p>
+	SET DROP TABLE (
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;ORIGIN = 1,
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;ID = 20,
+	<br>);
+</p>
+</div>
+<p align="right">Back to <a href="#index">Index</a></p>
+
+<!-- **************************************** -->
+<a name="stmt_set_drop_sequence">
+<h3>SET DROP SEQUENCE</h3>
+</a>
+<div style="margin-left:40px; margin-right:0px;">
+<h3>Synopsis:</h3>
+	SET DROP SEQUENCE ( &lt;options&gt; );
+<h3>Description:</h3>
+<p>
+	Drops an existing user sequence from a replication set.
+</p>
+<table border="0" cellpadding="10">
+<tr>
+	<td align="left" valign="top" nowrap><b>ORIGIN = &lt;ival&gt;</b></td>
+	<td align="left" valign="top"><p>
+		The current origin of the set.
+	</p></td>
+</tr>
+<tr>
+	<td align="left" valign="top" nowrap><b>ID = &lt;ival&gt;</b></td>
+	<td align="left" valign="top"><p>
+		Unique ID of the sequence.
+	</p></td>
+</tr>
+</table>
+<h3>Example:</h3>
+<p>
+	SET DROP SEQUENCE (
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;ORIGIN = 1,
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;ID = 21,
+	<br>);
+</p>
+</div>
+<p align="right">Back to <a href="#index">Index</a></p>
+
+<!-- **************************************** -->
 <a name="stmt_store_trigger">
 <h3>STORE TRIGGER</h3>
 </a>
@@ -1083,18 +1198,31 @@
 <h3>Description:</h3>
 <p>
 	Causes a node (subscriber) to start replicating a set of
-	tables either from the origin or from another provider node, which
-	must be a currently forwarding subscriber itself.
+	tables either from the origin or from another provider node,
+	which must be a currently forwarding subscriber itself.
 </p>
 <p>
-	The application tables contained in the set must already exist and
-	should ideally be currently empty. The current version of Slony-I
-	will not attempt to copy the schema of the set. The replication daemon
-	will start copying the current content of the set from the given
-	provider and then try to catch up with any update activity that
-	happened during that copy process. After successfull subscription,
-	the tables are guarded on the subscriber against accidential updates
-	by the application.
+	The application tables contained in the set must already exist
+	and should ideally be currently empty. The current version of
+	Slony-I will not attempt to copy the schema of the set. The
+	replication daemon will start copying the current content of
+	the set from the given provider and then try to catch up with
+	any update activity that happened during that copy
+	process. After successful subscription, the tables are guarded
+	on the subscriber using triggers against accidental updates by
+	the application.
+</p>
+
+<p>
+	Note: If you need to revise subscription information for a
+	node, you may submit the new information using this command,
+	and the new configuration will be propagated throughout the
+	replication network.  The normal reason to revise this
+	information is that you want a node to subscribe to a <i>
+	different </i> provider node, or for a node to become a
+	"forwarding" subscriber so it may later become the provider
+	for a later subscriber.
+
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -1118,9 +1246,10 @@
 <tr>
 	<td align="left" valign="top" nowrap><b>FORWARD = &lt;boolean&gt;</b></td>
 	<td align="left" valign="top"><p>
-		Flag if the new subscriber should store the log information
-		during replication to become a possible candidate for the
-		provider role of future nodes.
+		Flag whether or not the new subscriber should store
+		the log information during replication to make it
+		possible candidate for the provider role for future
+		nodes.
 	</p></td>
 </tr>
 </table>
@@ -1152,9 +1281,10 @@
 	restored.
 </p>
 <p>
-	<b>Warning!</b> Resubscribing an unsubscribed set requires a full
-	new copy from the provider to be transferred since the tables have
-	been subject to possible independant modifications.
+	<b>Warning!</b> Resubscribing an unsubscribed set requires a
+	<i>complete fresh copy</i> of data from the provider to be
+	transferred since the tables have been subject to possible
+	independent modifications.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -1194,12 +1324,13 @@
 	command.
 </p>
 <p>
-	This command must be the first in a possible statement group (try).
-	The reason for this is that it needs to commit the changes made
-	to the tables (adding a special trigger function) before it can
-	wait for every concurrent transaction to finish. At the same time
-	it cannot hold an open transaction to the same database itself since
-	this would result in blocking itself forever.
+	This command must be the first in a possible statement group
+	(<tt>try</tt>).  The reason for this is that it needs to
+	commit the changes made to the tables (adding a special
+	trigger function) before it can wait for every concurrent
+	transaction to finish. At the same time it cannot hold an open
+	transaction to the same database itself since this would
+	result in blocking itself forever.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -1274,17 +1405,19 @@
 	locked on the old origin. 
 </p>
 <p>
-	After this command, the set cannot be unlocked on the old origin
-	any more. The old origin will continue as a forwarding subscriber
-	of the set and the subscription chain from the old origin to the
-	new origin will get hop by hop reversed. As soon as the new origin
-	has finished processing the event (that includes any outstanding
-	sync events that happened before, i.e. fully catching up), the
-	new origin will take over and open all tables in the set for
-	client application update activity.
+	After this command, the set cannot be unlocked on the old
+	origin any more. The old origin will continue as a forwarding
+	subscriber of the set and the subscription chain from the old
+	origin to the new origin will be reversed, hop by hop. As soon
+	as the new origin has finished processing the event (that
+	includes any outstanding sync events that happened before,
+	<i>i.e.</i> fully catching up), the new origin will take over
+	and open all tables in the set for client application update
+	activity.
 </p>
 <p>
-	This is not failover, as it requires a fully functional old origin.
+	This is not failover, as it requires a fully functional old
+	origin node.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -1326,25 +1459,26 @@
 	FAILOVER ( &lt;options&gt; );
 <h3>Description:</h3>
 <p>
-	WARNING: This command will abandon the status of the failed node.
-	There is no other possibility to let the failed node join the
-	cluster again than rebuilding it from scratch as a slave.
-</p>
-<p>
-	The failover command causes the backup node to take over all sets
-	that currently originate on the failed node. Slonik will contact
-	all other direct subscribers of the failed node to determine which
-	node has the highest sync status for each set. If another node has
-	a higher sync status than the backup node, the replication will
-	first be redirected so that the backup node replicates against that
-	other node, before assuming the origin role and allowing update
-	activity.
-</p>
-<p>
-	After successfull failover, all former direct subscribers of the
-	failed node are direct subscribers of the backup node. The failed
-	node can and should be removed from the configuration with
-	DROP NODE.
+	WARNING: This command will abandon the status of the failed
+	node.  There is no possibility to let the failed node join the
+	cluster again without rebuilding it from scratch as a slave
+	slave.
+</p>
+<p>
+	The failover command causes the backup node to take over all
+	sets that currently originate on the failed node. Slonik will
+	contact all other direct subscribers of the failed node to
+	determine which node has the highest sync status for each
+	set. If another node has a higher sync status than the backup
+	node, the replication will first be redirected so that the
+	backup node replicates against that other node, before
+	assuming the origin role and allowing update activity.
+</p>
+<p>
+	After successful failover, all former direct subscribers of
+	the failed node become direct subscribers of the backup
+	node. The failed node can and should be removed from the
+	configuration with <tt>DROP NODE</tt>.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -1380,15 +1514,17 @@
 <h3>Description:</h3>
 <p>
 	Executes a script containing arbitrary SQL statements on all
-	nodes that are subscribed to a set at a controlled point within
-	the replication stream.
+	nodes that are subscribed to a set at a common controlled
+	point within the replication transaction stream.
 </p>
 <p>
 	The specified event origin must be the origin of the set. The
 	script file must not contain any START or COMMIT TRANSACTION
-	calls. Also any non-deterministic DML statements (like updating
-	a field with CURRENT_TIMESTAMP) must be avoided, since the
-	data changes done by the script are explicitly not replicated. 
+	calls.  (This may change in PostgreSQL 8.0 once nested
+	transactions, aka savepoints, are supported)  In addition,
+	non-deterministic DML statements (like updating a field with
+	CURRENT_TIMESTAMP) must be avoided, since the data changes
+	done by the script are explicitly not replicated.
 </p>
 <table border="0" cellpadding="10">
 <tr>
@@ -1411,6 +1547,17 @@
 		The default value is 1.
 	</p></td>
 </tr>
+<tr>
+	<td align="left" valign="top" nowrap><b>EXECUTE ONLY ON = &lt;ival&gt;</b></td>
+	<td align="left" valign="top"><p>
+		<b>(Optional)</b>
+		The ID of the only node to actually execute the script.
+		This option causes the script to be propagated by all nodes
+		but executed only by one.
+		The default is to execute the script on all nodes that are
+		subscribed to the set.
+	</p></td>
+</tr>
 </table>
 <h3>Example:</h3>
 <p>
@@ -1436,10 +1583,14 @@
 </p>
 <p>
 	Slonik remembers the last event generated on every node during
-	script execution (events generated by earlier calls are currently
-	non checked). In certain situations it is necessary that events
-	generated on one node (like CREATE SET) are processed on another
-	node before issuing more commands (like SUBSCRIBE SET).
+	script execution (events generated by earlier calls are
+	currently not checked). In certain situations it is necessary
+	that events generated on one node (such as <tt>CREATE
+	SET</tt>) are processed on another node before issuing more
+	commands (for instance, <a
+	href="#stmt_subscribe_set">SUBSCRIBE SET</a>).  <tt>WAIT FOR
+	EVENT</tt> may be used to cause the slonik script to wait
+	until the subscriber node is ready for the next action.
 </p>
 <p>
 	WAIT FOR EVENT must be called outside of any try block to
@@ -1502,5 +1653,15 @@
 
 -->
 
+<h1> Stored Procedures </h1>
+
+<P> The commands used in Slonik invoke stored procedures in the
+namespace created for the replication instance.  Slonik provides one
+convenient way to invoke these procedures; it is just as possible to
+invoke them directly to manage the Slony-I instances. </p>
+
+<P> See the <a href= "schemadoc.html"> Schema Documentation </a> for
+more details. </p>
+
 </body>
 </html>
Index: scheduler.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/scheduler.c,v
retrieving revision 1.15
retrieving revision 1.15.2.1
diff -Lsrc/slon/scheduler.c -Lsrc/slon/scheduler.c -u -w -r1.15 -r1.15.2.1
--- src/slon/scheduler.c
+++ src/slon/scheduler.c
@@ -800,3 +800,11 @@
 }
 
 
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
+
Index: remote_worker.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/remote_worker.c,v
retrieving revision 1.55.2.5
retrieving revision 1.55.2.6
diff -Lsrc/slon/remote_worker.c -Lsrc/slon/remote_worker.c -u -w -r1.55.2.5 -r1.55.2.6
--- src/slon/remote_worker.c
+++ src/slon/remote_worker.c
@@ -689,6 +689,18 @@
 				 * sequences information is not maintained in
 				 * the runtime configuration.
 				 */
+ 			} else if (strcmp(event->ev_type, "SET_DROP_TABLE") == 0) 
+ 			{
+ 			  int tab_id = (int) strtol(event->ev_data1, NULL, 10);
+ 			  slon_appendquery(&query1, "select %s.setDropTable_int(%d);", 
+ 					   rtcfg_namespace,
+ 					   tab_id);
+ 			} else if (strcmp(event->ev_type, "SET_DROP_SEQUENCE") == 0)
+ 			{
+ 			  int seq_id = (int) strtol(event->ev_data1, NULL, 10);
+ 			  slon_appendquery(&query1, "select %s.setDropSequence_int(%d);", 
+ 					   rtcfg_namespace,
+ 					   seq_id);
 			}
 			else if (strcmp(event->ev_type, "STORE_TRIGGER") == 0)
 			{
@@ -3909,4 +3921,10 @@
 	}
 }
 
-
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
Index: remote_listen.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/remote_listen.c,v
retrieving revision 1.15
retrieving revision 1.15.2.1
diff -Lsrc/slon/remote_listen.c -Lsrc/slon/remote_listen.c -u -w -r1.15 -r1.15.2.1
--- src/slon/remote_listen.c
+++ src/slon/remote_listen.c
@@ -727,4 +727,10 @@
 	return 0;
 }
 
-
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
Index: sync_thread.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/sync_thread.c,v
retrieving revision 1.11
retrieving revision 1.11.2.1
diff -Lsrc/slon/sync_thread.c -Lsrc/slon/sync_thread.c -u -w -r1.11 -r1.11.2.1
--- src/slon/sync_thread.c
+++ src/slon/sync_thread.c
@@ -188,3 +188,11 @@
 }
 
 
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
+
Index: slon.h
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/slon.h,v
retrieving revision 1.36
retrieving revision 1.36.2.1
diff -Lsrc/slon/slon.h -Lsrc/slon/slon.h -u -w -r1.36 -r1.36.2.1
--- src/slon/slon.h
+++ src/slon/slon.h
@@ -510,3 +510,11 @@
 
 #endif /*  SLON_H_INCLUDED */
 
+
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
Index: runtime_config.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/runtime_config.c,v
retrieving revision 1.19
retrieving revision 1.19.2.1
diff -Lsrc/slon/runtime_config.c -Lsrc/slon/runtime_config.c -u -w -r1.19 -r1.19.2.1
--- src/slon/runtime_config.c
+++ src/slon/runtime_config.c
@@ -637,7 +637,6 @@
 	rtcfg_unlock();
 }
 
-
 void
 rtcfg_moveSet(int set_id, int old_origin, int new_origin, int sub_provider)
 {
@@ -1064,5 +1063,10 @@
 	return retval;
 }
 
-
-
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
Index: local_listen.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/local_listen.c,v
retrieving revision 1.23.2.1
retrieving revision 1.23.2.2
diff -Lsrc/slon/local_listen.c -Lsrc/slon/local_listen.c -u -w -r1.23.2.1 -r1.23.2.2
--- src/slon/local_listen.c
+++ src/slon/local_listen.c
@@ -402,6 +402,28 @@
 				 * the runtime configuration.
 				 */
 			}
+			else if (strcmp(ev_type, "SET_DROP_TABLE") == 0)
+			{
+				/*
+				 * SET_DROP_TABLE
+				 */
+				/*
+				 * Nothing to do ATM ... 
+				 * table information is not maintained in
+				 * the runtime configuration.
+				 */
+			}
+			else if (strcmp(ev_type, "SET_DROP_SEQUENCE") == 0)
+			{
+				/*
+				 * SET_DROP_SEQUENCE
+				 */
+				/*
+				 * Nothing to do ATM ... 
+				 * table information is not maintained in
+				 * the runtime configuration.
+				 */
+			}
 			else if (strcmp(ev_type, "ADJUST_SEQ") == 0)
 			{
 				/*
@@ -620,3 +642,11 @@
 }
 
 
+
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
Index: cleanup_thread.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/cleanup_thread.c,v
retrieving revision 1.13.2.3
retrieving revision 1.13.2.4
diff -Lsrc/slon/cleanup_thread.c -Lsrc/slon/cleanup_thread.c -u -w -r1.13.2.3 -r1.13.2.4
--- src/slon/cleanup_thread.c
+++ src/slon/cleanup_thread.c
@@ -222,3 +222,11 @@
 }
 
 
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
+
Index: dbutils.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/dbutils.c,v
retrieving revision 1.9.2.1
retrieving revision 1.9.2.2
diff -Lsrc/slon/dbutils.c -Lsrc/slon/dbutils.c -u -w -r1.9.2.1 -r1.9.2.2
--- src/slon/dbutils.c
+++ src/slon/dbutils.c
@@ -336,3 +336,11 @@
 }
 
 
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
+
Index: slon.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slon/slon.c,v
retrieving revision 1.27
retrieving revision 1.27.2.1
diff -Lsrc/slon/slon.c -Lsrc/slon/slon.c -u -w -r1.27 -r1.27.2.1
--- src/slon/slon.c
+++ src/slon/slon.c
@@ -562,4 +562,12 @@
 }
 
 
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
+
 
Index: README
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/README,v
retrieving revision 1.4.2.1
retrieving revision 1.4.2.2
diff -LREADME -LREADME -u -w -r1.4.2.1 -r1.4.2.2
--- README
+++ README
@@ -7,6 +7,7 @@
 3.  Configuring the databases for replication
 4.  Time to replicate
 5.  Checking the result
+6.  If I run into problems...
 
 And now, the contents.
 
@@ -328,8 +329,19 @@
     echo "FAILED - see test_1.diff for database differences"
 fi
 
-If this script reports any differences, I would be glad to hear how
-this happened.  Please contact me as JanWieck at Yahoo.com or on the IRC
-channel #slony on freenode.net.
+If this script reports any differences, I would appreciate hearing how
+this happened.  
+
+6.  Help!  I ran into a problem.
+
+The file in the documentation area named "helpitsbroken.txt" contains
+a variety of notes describing problems that people have run into in
+setting up Slony-I instances.  
+
+It may be worth checking there to see if the problem you are having
+has already been documented and diagnosed by someone else.
+
+Please contact me as JanWieck at Yahoo.com or on the IRC channel #slony
+on freenode.net.
 
 Jan Wieck
Index: slonik.h
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slonik/slonik.h,v
retrieving revision 1.17
retrieving revision 1.17.2.1
diff -Lsrc/slonik/slonik.h -Lsrc/slonik/slonik.h -u -w -r1.17 -r1.17.2.1
--- src/slonik/slonik.h
+++ src/slonik/slonik.h
@@ -32,6 +32,8 @@
 typedef struct SlonikStmt_merge_set_s			SlonikStmt_merge_set;
 typedef struct SlonikStmt_set_add_table_s		SlonikStmt_set_add_table;
 typedef struct SlonikStmt_set_add_sequence_s	SlonikStmt_set_add_sequence;
+typedef struct SlonikStmt_set_drop_table_s		SlonikStmt_set_drop_table;
+typedef struct SlonikStmt_set_drop_sequence_s	SlonikStmt_set_drop_sequence;
 typedef struct SlonikStmt_table_add_key_s		SlonikStmt_table_add_key;
 typedef struct SlonikStmt_store_trigger_s		SlonikStmt_store_trigger;
 typedef struct SlonikStmt_drop_trigger_s		SlonikStmt_drop_trigger;
@@ -63,6 +65,8 @@
 	STMT_RESTART_NODE,
 	STMT_SET_ADD_SEQUENCE,
 	STMT_SET_ADD_TABLE,
+	STMT_SET_DROP_SEQUENCE,
+	STMT_SET_DROP_TABLE,
 	STMT_STORE_LISTEN,
 	STMT_STORE_NODE,
 	STMT_STORE_PATH,
@@ -250,6 +254,19 @@
 	char			   *seq_comment;
 };
 
+struct SlonikStmt_set_drop_table_s {
+	SlonikStmt			hdr;
+	int					set_origin;
+	int					tab_id;
+};
+
+
+struct SlonikStmt_set_drop_sequence_s {
+	SlonikStmt			hdr;
+	int					set_origin;
+	int					seq_id;
+};
+
 
 struct SlonikStmt_table_add_key_s {
 	SlonikStmt			hdr;
@@ -442,6 +459,8 @@
 extern int		slonik_merge_set(SlonikStmt_merge_set *stmt);
 extern int		slonik_set_add_table(SlonikStmt_set_add_table *stmt);
 extern int		slonik_set_add_sequence(SlonikStmt_set_add_sequence *stmt);
+extern int		slonik_set_drop_table(SlonikStmt_set_drop_table *stmt);
+extern int		slonik_set_drop_sequence(SlonikStmt_set_drop_sequence *stmt);
 extern int		slonik_table_add_key(SlonikStmt_table_add_key *stmt);
 extern int		slonik_store_trigger(SlonikStmt_store_trigger *stmt);
 extern int		slonik_drop_trigger(SlonikStmt_drop_trigger *stmt);
@@ -505,3 +524,11 @@
 extern int		yyparse(void);
 extern int		yylex(void);
 
+
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
Index: parser.y
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slonik/parser.y,v
retrieving revision 1.16.2.1
retrieving revision 1.16.2.2
diff -Lsrc/slonik/parser.y -Lsrc/slonik/parser.y -u -w -r1.16.2.1 -r1.16.2.2
--- src/slonik/parser.y
+++ src/slonik/parser.y
@@ -139,6 +139,8 @@
 %type <statement>	stmt_merge_set
 %type <statement>	stmt_set_add_table
 %type <statement>	stmt_set_add_sequence
+%type <statement>	stmt_set_drop_table
+%type <statement>	stmt_set_drop_sequence
 %type <statement>	stmt_table_add_key
 %type <statement>	stmt_store_trigger
 %type <statement>	stmt_drop_trigger
@@ -432,6 +434,10 @@
 						{ $$ = $1; }
 					| stmt_set_add_sequence
 						{ $$ = $1; }
+					| stmt_set_drop_table
+						{ $$ = $1; }
+					| stmt_set_drop_sequence
+						{ $$ = $1; }
 					| stmt_store_trigger
 						{ $$ = $1; }
 					| stmt_drop_trigger
@@ -979,6 +985,60 @@
 					}
 					;
 
+stmt_set_drop_table	: lno K_SET K_DROP K_TABLE option_list
+					{
+						SlonikStmt_set_drop_table *new;
+						statement_option opt[] = {
+							STMT_OPTION_INT( O_ORIGIN, -1 ),
+							STMT_OPTION_INT( O_ID, -1 ),
+							STMT_OPTION_END
+						};
+						new = (SlonikStmt_set_drop_table *)
+							malloc(sizeof(SlonikStmt_set_drop_table));
+						memset(new, 0, sizeof(SlonikStmt_set_drop_table));
+						new->hdr.stmt_type		= STMT_SET_DROP_TABLE;
+						new->hdr.stmt_filename	= current_file;
+						new->hdr.stmt_lno		= $1;
+
+						if (assign_options(opt, $5) == 0) {
+							new->set_origin		= opt[0].ival;
+							new->tab_id			= opt[1].ival;
+						}
+						else
+							parser_errors++;
+
+						$$ = (SlonikStmt *)new;
+					}
+					;
+
+stmt_set_drop_sequence : lno K_SET K_DROP K_SEQUENCE option_list
+					{
+						SlonikStmt_set_drop_sequence *new;
+						statement_option opt[] = {
+							STMT_OPTION_INT( O_ORIGIN, -1 ),
+							STMT_OPTION_INT( O_ID, -1 ),
+							STMT_OPTION_END
+						};
+
+						new = (SlonikStmt_set_drop_sequence *)
+								malloc(sizeof(SlonikStmt_set_drop_sequence));
+						memset(new, 0, sizeof(SlonikStmt_set_drop_sequence));
+						new->hdr.stmt_type		= STMT_SET_DROP_SEQUENCE;
+						new->hdr.stmt_filename	= current_file;
+						new->hdr.stmt_lno		= $1;
+
+						if (assign_options(opt, $5) == 0)
+						{
+							new->set_origin		= opt[0].ival;
+							new->seq_id			= opt[1].ival;
+						}
+						else
+							parser_errors++;
+
+						$$ = (SlonikStmt *)new;
+					}
+					;
+
 stmt_store_trigger	: lno K_STORE K_TRIGGER option_list
 					{
 						SlonikStmt_store_trigger *new;
@@ -1659,3 +1719,11 @@
 #include "scan.c"
 
 
+
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
Index: slonik.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/slonik/slonik.c,v
retrieving revision 1.27.2.2
retrieving revision 1.27.2.3
diff -Lsrc/slonik/slonik.c -Lsrc/slonik/slonik.c -u -w -r1.27.2.2 -r1.27.2.3
--- src/slonik/slonik.c
+++ src/slonik/slonik.c
@@ -561,6 +561,78 @@
 				}
 				break;
 
+			case STMT_SET_DROP_TABLE:
+				{
+					SlonikStmt_set_drop_table *stmt =
+							(SlonikStmt_set_drop_table *)hdr;
+
+					/*
+					 * Check that we have the set_id and set_origin
+					 * and that we can reach the origin.
+					 */
+					if (stmt->set_origin < 0)
+					{
+						printf("%s:%d: Error: "
+								"origin must be specified\n",
+								hdr->stmt_filename, hdr->stmt_lno);
+						errors++;
+					}
+					else
+					{
+						if (script_check_adminfo(hdr, stmt->set_origin) < 0)
+							errors++;
+					}
+
+					/*
+					 * Check that we have the table id, name
+					 * and what to use for the key.
+					 */
+					if (stmt->tab_id < 0)
+					{
+						printf("%s:%d: Error: "
+								"table id must be specified\n",
+								hdr->stmt_filename, hdr->stmt_lno);
+						errors++;
+					}
+				}
+				break;
+
+			case STMT_SET_DROP_SEQUENCE:
+				{
+					SlonikStmt_set_drop_sequence *stmt =
+							(SlonikStmt_set_drop_sequence *)hdr;
+
+					/*
+					 * Check that we have the set_id and set_origin
+					 * and that we can reach the origin.
+					 */
+					if (stmt->set_origin < 0)
+					{
+						printf("%s:%d: Error: "
+								"origin must be specified\n",
+								hdr->stmt_filename, hdr->stmt_lno);
+						errors++;
+					}
+					else
+					{
+						if (script_check_adminfo(hdr, stmt->set_origin) < 0)
+							errors++;
+					}
+
+					/*
+					 * Check that we have the table id, name
+					 * and what to use for the key.
+					 */
+					if (stmt->seq_id < 0)
+					{
+						printf("%s:%d: Error: "
+								"sequence id must be specified\n",
+								hdr->stmt_filename, hdr->stmt_lno);
+						errors++;
+					}
+				}
+				break;
+
 			case STMT_TABLE_ADD_KEY:
 				{
 					SlonikStmt_table_add_key *stmt =
@@ -1127,6 +1199,25 @@
 						errors++;
 				}
 				break;
+			case STMT_SET_DROP_TABLE:
+				{
+					SlonikStmt_set_drop_table *stmt =
+							(SlonikStmt_set_drop_table *)hdr;
+
+					if (slonik_set_drop_table(stmt) < 0)
+						errors++;
+				}
+				break;
+
+			case STMT_SET_DROP_SEQUENCE:
+				{
+					SlonikStmt_set_drop_sequence *stmt =
+							(SlonikStmt_set_drop_sequence *)hdr;
+
+					if (slonik_set_drop_sequence(stmt) < 0)
+						errors++;
+				}
+				break;
 
 			case STMT_TABLE_ADD_KEY:
 				{
@@ -2964,7 +3055,72 @@
 		return -1;
 	}
 	db_notice_silent = false;
+	dstring_free(&query);
+	return 0;
+}
+
+
+int
+slonik_set_drop_table(SlonikStmt_set_drop_table *stmt)
+{
+	SlonikAdmInfo  *adminfo1;
+	SlonDString		query;
+	char		   *idxname;
+	PGresult	   *res;
+	
+	adminfo1 = get_active_adminfo((SlonikStmt *)stmt, stmt->set_origin);
+	if (adminfo1 == NULL)
+		return -1;
+	
+	if (db_begin_xact((SlonikStmt *)stmt, adminfo1) < 0)
+		return -1;
+	
+	dstring_init(&query);
+	
+	slon_mkquery(&query,
+				 "select \"_%s\".setDropTable(%d); ",
+				 stmt->hdr.script->clustername,		     
+				 stmt->tab_id);
+	if (db_exec_evcommand((SlonikStmt *)stmt, adminfo1, &query) < 0) {
+		PQclear(res);
+		dstring_free(&query);
+		return -1;
+	}
+	dstring_free(&query);
+	return 0;
+}
+
+
+int
+slonik_set_drop_sequence(SlonikStmt_set_drop_sequence *stmt)
+{
+	SlonikAdmInfo  *adminfo1;
+	SlonDString		query;
 
+	adminfo1 = get_active_adminfo((SlonikStmt *)stmt, stmt->set_origin);
+	if (adminfo1 == NULL)
+		return -1;
+
+	if (db_begin_xact((SlonikStmt *)stmt, adminfo1) < 0)
+		return -1;
+
+	dstring_init(&query);
+
+	/*
+	 * call setDropSequence()
+	 */
+	db_notice_silent = true;
+	slon_mkquery(&query,
+		     "select \"_%s\".setDropSequence(%d); ",
+		     stmt->hdr.script->clustername,		     
+		     stmt->seq_id);
+	if (db_exec_evcommand((SlonikStmt *)stmt, adminfo1, &query) < 0)
+	{
+		db_notice_silent = false;
+		dstring_free(&query);
+		return -1;
+	}
+	db_notice_silent = false;
 	dstring_free(&query);
 	return 0;
 }
@@ -3578,3 +3734,10 @@
 	return true;
 }
 
+/*
+ * Local Variables:
+ *  tab-width: 4
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */


More information about the Slony1-commit mailing list