# Schema schemadoc

## Table: sl_config_lock

This table exists solely to prevent overlapping execution of configuration change procedures and the resulting possible deadlocks.

### Structure of sl_config_lock

dummy

  integer

## Table: sl_confirm

Holds confirmation of replication events. After a period of time, Slony removes old confirmed events from both this table and the sl_event table.

### Structure of sl_confirm

con_origin

  integer

  The ID # (from sl_node.no_id) of the source node for this event

con_received

  integer

con_seqno

  bigint

  The ID # for the event

con_timestamp

  timestamp without time zone

  `DEFAULT (timeofday())::timestamp without time zone`

  When this event was confirmed

### Indexes on sl_confirm

sl_confirm_idx1

  con_origin, con_received, con_seqno

sl_confirm_idx2

  con_received, con_seqno

## Table: sl_event

Holds information about replication events. After a period of time, Slony removes old confirmed events from both this table and the sl_confirm table.

### Structure of sl_event

ev_origin

  integer

  `PRIMARY KEY`

  The ID # (from sl_node.no_id) of the source node for this event

ev_seqno

  bigint

  `PRIMARY KEY`

  The ID # for the event

ev_timestamp

  timestamp without time zone

  When this event record was created

ev_minxid

  xxid

  Earliest XID on provider node for this event

ev_maxxid

  xxid

  Latest XID on provider node for this event

ev_xip

  text

  List of XIDs, in order, that are part of this event

ev_type

  text

  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 = SET_DROP_TABLE = SET_DROP_SEQUENCE = SET_MOVE_TABLE = SET_MOVE_SEQUENCE = FAILOVER_SET = SUBSCRIBE_SET = ENABLE_SUBSCRIPTION = UNSUBSCRIBE_SET = DDL_SCRIPT = ADJUST_SEQ = RESET_CONFIG =

ev_data1

  text

  Data field containing an argument needed to process the event

ev_data2

  text

  Data field containing an argument needed to process the event

ev_data3

  text

  Data field containing an argument needed to process the event

ev_data4

  text

  Data field containing an argument needed to process the event

ev_data5

  text

  Data field containing an argument needed to process the event

ev_data6

  text

  Data field containing an argument needed to process the event

ev_data7

  text

  Data field containing an argument needed to process the event

ev_data8

  text

  Data field containing an argument needed to process the event

## Table: sl_listen

Indicates how nodes listen to events from other nodes in the Slony-I network.

### Structure of sl_listen

li_origin

  integer

  `PRIMARY KEY`

  `REFERENCES`

  The ID # (from sl_node.no_id) of the node this listener is operating on

li_provider

  integer

  `PRIMARY KEY`

  `REFERENCES`

  The ID # (from sl_node.no_id) of the source node for this listening event

li_receiver

  integer

  `PRIMARY KEY`

  `REFERENCES`

  The ID # (from sl_node.no_id) of the target node for this listening event

## Table: sl_log_1

Stores each change to be propagated to subscriber nodes

### Structure of sl_log_1

log_origin

  integer

  Origin node from which the change came

log_xid

  xxid

  Transaction ID on the origin node

log_tableid

  integer

  The table ID (from sl_table.tab_id) that this log entry is to affect

log_actionseq

  bigint

log_cmdtype

  character(1)

  Replication action to take. U = Update, I = Insert, D = DELETE

log_cmddata

  text

  The data needed to perform the log action

## Table: sl_log_2

Stores each change to be propagated to subscriber nodes

### Structure of sl_log_2

log_origin

  integer

  Origin node from which the change came

log_xid

  xxid

  Transaction ID on the origin node

log_tableid

  integer

  The table ID (from sl_table.tab_id) that this log entry is to affect

log_actionseq

  bigint

log_cmdtype

  character(1)

  Replication action to take. U = Update, I = Insert, D = DELETE

log_cmddata

  text

  The data needed to perform the log action

## Table: sl_node

Holds the list of nodes associated with this namespace.

### Structure of sl_node

no_id

  integer

  `PRIMARY KEY`

  The unique ID number for the node

no_active

  boolean

  Is the node active in replication yet?

no_comment

  text

  A human-oriented description of the node

no_spool

  boolean

### Tables referencing sl_listen via Foreign Key Constraints

## Table: sl_path

Holds connection information for the paths between nodes, and the synchronisation delay

### Structure of sl_path

pa_server

  integer

  `PRIMARY KEY`

  `REFERENCES`

  The Node ID # (from sl_node.no_id) of the data source

pa_client

  integer

  `PRIMARY KEY`

  `REFERENCES`

  The Node ID # (from sl_node.no_id) of the data target

pa_conninfo

  text

  `NOT NULL`

  The PostgreSQL connection string used to connect to the source node.

pa_connretry

  integer

  The synchronisation delay, in seconds

### Tables referencing sl_listen via Foreign Key Constraints

## View: sl_seqlastvalue

### Structure of sl_seqlastvalue

seq_id

  integer

seq_set

  integer

seq_reloid

  oid

seq_origin

  integer

seq_last_value

  bigint

### Definition of view sl_seqlastvalue

    SELECT sq.seq_id
    , sq.seq_set
    , sq.seq_reloid
    , s.set_origin AS seq_origin
    , sequencelastvalue
    (
         (
               (quote_ident
                     (
                           (pgn.nspname)::text
                     ) || '.'::text
               ) || quote_ident
               (
                     (pgc.relname)::text
               )
         )
    ) AS seq_last_value
    FROM sl_sequence sq
    , 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)
    );

## Table: sl_seqlog

Log of Sequence updates

### Structure of sl_seqlog

seql_seqid

  integer

  Sequence ID

seql_origin

  integer

  Publisher node at which the sequence originates

seql_ev_seqno

  bigint

  Slony-I Event with which this sequence update is associated

seql_last_value

  bigint

  Last value published for this sequence

### Indexes on sl_seqlog

sl_seqlog_idx

  seql_origin, seql_ev_seqno, seql_seqid

## Table: sl_sequence

Similar to sl_table, each entry identifies a sequence being replicated.

### Structure of sl_sequence

seq_id

  integer

  `PRIMARY KEY`

  An internally-used ID for Slony-I to use in its sequencing of updates

seq_reloid

  oid

  `UNIQUE`

  `NOT NULL`

  The OID of the sequence object

seq_relname

  name

  `NOT NULL`

  The name of the sdequence in pg_catalog.pg_class.relname used to recover from a dump/restore cycle

seq_nspname

  name

  `NOT NULL`

  The name of the schema in pg_catalog.pg_namespace.nspname used to recover from a dump/restore cycle

seq_set

  integer

  `REFERENCES`

  Indicates which replication set the object is in

seq_comment

  text

  A human-oriented comment

## Table: sl_set

Holds definitions of replication sets.

### Structure of sl_set

set_id

  integer

  `PRIMARY KEY`

  A unique ID number for the set.

set_origin

  integer

  `REFERENCES`

  The ID number of the source node for the replication set.

set_locked

  xxid

  Indicates whether or not the set is locked.

set_comment

  text

  A human-oriented description of the set.

### Tables referencing sl_sequence via Foreign Key Constraints

## Table: sl_setsync

SYNC information

### Structure of sl_setsync

ssy_setid

  integer

  `PRIMARY KEY`

  `REFERENCES`

  ID number of the replication set

ssy_origin

  integer

  `REFERENCES`

  ID number of the node

ssy_seqno

  bigint

  Slony-I sequence number

ssy_minxid

  xxid

  Earliest XID in provider system affected by SYNC

ssy_maxxid

  xxid

  Latest XID in provider system affected by SYNC

ssy_xip

  text

  Contains the list of XIDs in progress at SYNC time

ssy_action_list

  text

  action list used during the subscription process. At the time a subscriber copies over data from the origin, it sees all tables in a state somewhere between two SYNC events. Therefore this list must contains all XIDs that are visible at that time, whose operations have therefore already been included in the data copied at the time the initial data copy is done. Those actions may therefore be filtered out of the first SYNC done after subscribing.

## Table: sl_subscribe

Holds a list of subscriptions on sets

### Structure of sl_subscribe

sub_set

  integer

  `PRIMARY KEY`

  `REFERENCES`

  ID # (from sl_set) of the set being subscribed to

sub_provider

  integer

  `REFERENCES`

  ID# (from sl_node) of the node providing data

sub_receiver

  integer

  `PRIMARY KEY`

  `REFERENCES`

  ID# (from sl_node) of the node receiving data from the provider

sub_forward

  boolean

  Does this provider keep data in sl_log_1/sl_log_2 to allow it to be a provider for other nodes?

sub_active

  boolean

  Has this subscription been activated? This is not set until the subscriber has received COPY data from the provider

## Table: sl_table

Holds information about the tables being replicated.

### Structure of sl_table

tab_id

  integer

  `PRIMARY KEY`

  Unique key for Slony-I to use to identify the table

tab_reloid

  oid

  `UNIQUE`

  `NOT NULL`

  The OID of the table in pg_catalog.pg_class.oid

tab_relname

  name

  `NOT NULL`

  The name of the table in pg_catalog.pg_class.relname used to recover from a dump/restore cycle

tab_nspname

  name

  `NOT NULL`

  The name of the schema in pg_catalog.pg_namespace.nspname used to recover from a dump/restore cycle

tab_set

  integer

  `REFERENCES`

  ID of the replication set the table is in

tab_idxname

  name

  `NOT NULL`

  The name of the primary index of the table

tab_altered

  boolean

  `NOT NULL`

  Has the table been modified for replication?

tab_comment

  text

  Human-oriented description of the table

### Tables referencing sl_trigger via Foreign Key Constraints

## Table: sl_trigger

Holds information about triggers on tables managed using Slony-I

### Structure of sl_trigger

trig_tabid

  integer

  `PRIMARY KEY`

  `REFERENCES`

  Slony-I ID number of table the trigger is on

trig_tgname

  name

  `PRIMARY KEY`

  Indicates the name of a trigger

## add_missing_table_field( text, text, text, text )

add_missing_table_field( text, text, text, text )

### Function Properties

Language Return Type PLPGSQL boolean

Add a column of a given type to a table if it is missing

    DECLARE
      p_namespace alias for $1;
      p_table     alias for $2;
      p_field     alias for $3;
      p_type      alias for $4;
      v_row       record;
      v_query     text;
    BEGIN
      select 1 into v_row from pg_namespace n, pg_class c, pg_attribute a
         where slon_quote_brute(n.nspname) = p_namespace and
             c.relnamespace = n.oid and
             slon_quote_brute(c.relname) = p_table and
             a.attrelid = c.oid and
             slon_quote_brute(a.attname) = p_field;
      if not found then
        raise notice 'Upgrade table %.% - add field %', p_namespace, p_table, p_field;
        v_query := 'alter table ' || p_namespace || '.' || p_table || ' add column ';
        v_query := v_query || p_field || ' ' || p_type || ';';
        execute v_query;
        return 't';
      else
        return 'f';
      end if;
    END;

## altertableforreplication( integer )

altertableforreplication( integer )

### Function Properties

Language Return Type PLPGSQL integer

alterTableForReplication(tab_id)

Sets up a table for replication. On the origin, this involves adding the "logTrigger()" 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.

    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 sl_config_lock;

    	-- ----
    	-- Get our local node ID
    	-- ----
    	v_no_id := getLocalNodeId('_schemadoc');

    	-- ----
    	-- 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,
    			slon_quote_brute(PGN.nspname) || '.' ||
    			slon_quote_brute(PGC.relname) as tab_fqname
    			into v_tab_row
    			from sl_table T, sl_set S,
    				"pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN,
    				"pg_catalog".pg_index PGX, "pg_catalog".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 'Slony-I: Table with id % not found', p_tab_id;
    	end if;
    	v_tab_fqname = v_tab_row.tab_fqname;
    	if v_tab_row.tab_altered then
    		raise exception 'Slony-I: Table % is already in altered state',
    				v_tab_fqname;
    	end if;

    	v_tab_attkind := determineAttKindUnique(v_tab_row.tab_fqname,
    						v_tab_row.tab_idxname);

    	execute 'lock table ' || v_tab_fqname || ' in access exclusive mode';

    	-- ----
    	-- 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 'create trigger "_schemadoc_logtrigger_' ||
    				p_tab_id || '" after insert or update or delete on ' ||
    				v_tab_fqname || ' for each row execute procedure
    				logTrigger (''_schemadoc'', ''' ||
    					p_tab_id || ''', ''' ||
    					v_tab_attkind || ''');';
    	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 "pg_catalog".pg_trigger
    				set tgrelid = v_tab_row.indexrelid
    				where tgrelid = v_tab_row.tab_reloid
    				and not exists (
    						select true from sl_table TAB,
    								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 > 0 then
    			update "pg_catalog".pg_class
    					set reltriggers = reltriggers - v_n
    					where oid = v_tab_row.tab_reloid;
    		end if;

    		-- ----
    		-- Disable all existing rules
    		-- ----
    		update "pg_catalog".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 > 0 then
    			update "pg_catalog".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 'create trigger "_schemadoc_denyaccess_' ||
    				p_tab_id || '" before insert or update or delete on ' ||
    				v_tab_fqname || ' for each row execute procedure
    				denyAccess (''_schemadoc'');';
    	end if;

    	-- ----
    	-- Mark the table altered in our configuration
    	-- ----
    	update sl_table
    			set tab_altered = true where tab_id = p_tab_id;

    	return p_tab_id;
    end;

## altertablerestore( integer )

altertablerestore( integer )

### Function Properties

Language Return Type PLPGSQL integer

alterTableRestore (tab_id)

Restores table tab_id from being replicated.

On the origin, this simply involves dropping the "logtrigger" trigger.

On subscriber nodes, this involves dropping the "denyaccess" trigger, and restoring user triggers and rules.

    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 sl_config_lock;

    	-- ----
    	-- Get our local node ID
    	-- ----
    	v_no_id := getLocalNodeId('_schemadoc');

    	-- ----
    	-- 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,
    			slon_quote_brute(PGN.nspname) || '.' ||
    			slon_quote_brute(PGC.relname) as tab_fqname
    			into v_tab_row
    			from sl_table T, sl_set S,
    				"pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN,
    				"pg_catalog".pg_index PGX, "pg_catalog".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 'Slony-I: Table with id % not found', p_tab_id;
    	end if;
    	v_tab_fqname = v_tab_row.tab_fqname;
    	if not v_tab_row.tab_altered then
    		raise exception 'Slony-I: Table % is not in altered state',
    				v_tab_fqname;
    	end if;

    	execute 'lock table ' || v_tab_fqname || ' in access exclusive mode';

    	-- ----
    	-- 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 'drop trigger "_schemadoc_logtrigger_' ||
    				p_tab_id || '" on ' || v_tab_fqname;
    	else
    		-- ----
    		-- On the subscriber drop the denyAccess trigger
    		-- ----
    		execute 'drop trigger "_schemadoc_denyaccess_' ||
    				p_tab_id || '" on ' || v_tab_fqname;

    		-- ----
    		-- Restore all original triggers
    		-- ----
    		update "pg_catalog".pg_trigger
    				set tgrelid = v_tab_row.tab_reloid
    				where tgrelid = v_tab_row.indexrelid;
    		get diagnostics v_n = row_count;
    		if v_n > 0 then
    			update "pg_catalog".pg_class
    					set reltriggers = reltriggers + v_n
    					where oid = v_tab_row.tab_reloid;
    		end if;

    		-- ----
    		-- Restore all original rewrite rules
    		-- ----
    		update "pg_catalog".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 > 0 then
    			update "pg_catalog".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 sl_table
    			set tab_altered = false where tab_id = p_tab_id;

    	return p_tab_id;
    end;

## cleanupevent( )

cleanupevent( )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_confirm
    				where con_origin not in (select no_id from sl_node);
    	delete from sl_confirm
    				where con_received not in (select no_id from 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 sl_confirm
    				where con_timestamp < (CURRENT_TIMESTAMP - '10 min'::interval)
    				group by con_origin, con_received
    	loop
    		delete from sl_confirm
    				where con_origin = v_max_row.con_origin
    				and con_received = v_max_row.con_received
    				and con_seqno < 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 sl_confirm
    				group by con_origin
    	loop
    		select coalesce(max(ev_seqno), 0) into v_max_sync
    				from sl_event
    				where ev_origin = v_min_row.con_origin
    				and ev_seqno <= v_min_row.con_seqno
    				and ev_type = 'SYNC';
    		if v_max_sync > 0 then
    			delete from sl_event
    					where ev_origin = v_min_row.con_origin
    					and ev_seqno < v_max_sync;
    		end if;
    	end loop;

    	return 0;
    end;

## cleanuplistener( )

cleanuplistener( )

### Function Properties

Language Return Type C integer

look for stale pg_listener entries and submit Async_Unlisten() to them

    _Slony_I_cleanupListener

## createevent( name, text )

createevent( name, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text )

createevent( name, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text, text )

createevent( name, text, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text, text, text )

createevent( name, text, text, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text, text, text, text )

createevent( name, text, text, text, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text, text, text, text, text )

createevent( name, text, text, text, text, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text, text, text, text, text, text )

createevent( name, text, text, text, text, text, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text, text, text, text, text, text, text )

createevent( name, text, text, text, text, text, text, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## createevent( name, text, text, text, text, text, text, text, text, text )

createevent( name, text, text, text, text, text, text, text, text, text )

### Function Properties

Language Return Type C bigint

FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])

Create an sl_event entry

    _Slony_I_createEvent

## ddlscript( integer, text, integer )

ddlscript( integer, text, integer )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    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 sl_config_lock;

    	-- ----
    	-- Check that the set exists and originates here
    	-- ----
    	select set_origin into v_set_origin
    			from sl_set
    			where set_id = p_set_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_set_id;
    	end if;
    	if v_set_origin <> getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_set_id;
    	end if;

    	-- ----
    	-- Create a SYNC event, run the script and generate the DDL_SCRIPT event
    	-- ----
    	perform createEvent('_schemadoc', 'SYNC', NULL);
    	perform ddlScript_int(p_set_id, p_script, p_only_on_node);
    	perform updateRelname(p_set_id, p_only_on_node);
    	return  createEvent('_schemadoc', 'DDL_SCRIPT',
    			p_set_id, p_script, p_only_on_node);
    end;

## ddlscript_int( integer, text, integer )

ddlscript_int( integer, text, integer )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_config_lock;

    	-- ----
    	-- Check that we either are the set origin or a current
    	-- subscriber of the set.
    	-- ----
    	v_no_id := getLocalNodeId('_schemadoc');
    	select set_origin into v_set_origin
    			from sl_set
    			where set_id = p_set_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_set_id;
    	end if;
    	if v_set_origin <> v_no_id
    			and not exists (select 1 from 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 > 0 and p_only_on_node <> v_no_id then
    		return 0;
    	end if;

    	-- ----
    	-- Restore all original triggers and rules
    	-- ----
    	for v_row in select * from sl_table
    			where tab_set = p_set_id
    	loop
    		perform 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 sl_table
    			where tab_set = p_set_id
    	loop
    		perform alterTableForReplication(v_row.tab_id);
    	end loop;

    	return p_set_id;
    end;

## denyaccess( )

denyaccess( )

### Function Properties

Language Return Type C "trigger"

Trigger function to prevent modifications to a table on a subscriber

    _Slony_I_denyAccess

## determineattkindserial( text )

determineattkindserial( text )

### Function Properties

Language Return Type PLPGSQL text

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.

    declare
    	p_tab_fqname	alias for $1;
    	v_tab_fqname_quoted	text default '';
    	v_attkind		text default '';
    	v_attrow		record;
    	v_have_serial	bool default 'f';
    begin
    	v_tab_fqname_quoted := slon_quote_input(p_tab_fqname);
    	--
    	-- Loop over the attributes of this relation
    	-- and add a "v" for every user column, and a "k"
    	-- if we find the Slony-I special serial column.
    	--
    	for v_attrow in select PGA.attnum, PGA.attname
    			from "pg_catalog".pg_class PGC,
    			    "pg_catalog".pg_namespace PGN,
    				"pg_catalog".pg_attribute PGA
    			where slon_quote_brute(PGN.nspname) || '.' ||
    			    slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    				and PGN.oid = PGC.relnamespace
    				and PGA.attrelid = PGC.oid
    				and not PGA.attisdropped
    				and PGA.attnum > 0
    			order by attnum
    	loop
    		if v_attrow.attname = '_Slony-I_schemadoc_rowID' then
    		    v_attkind := v_attkind || 'k';
    			v_have_serial := 't';
    		else
    			v_attkind := v_attkind || 'v';
    		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 'Slony-I: table % not found', v_tab_fqname_quoted;
    	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 'Slony-I: table % does not have the serial key',
    				v_tab_fqname_quoted;
    	end if;

    	execute 'update ' || v_tab_fqname_quoted ||
    		' set "_Slony-I_schemadoc_rowID" =' ||
    		' "pg_catalog".nextval(''sl_rowid_seq'');';
    	execute 'alter table only ' || v_tab_fqname_quoted ||
    		' add unique ("_Slony-I_schemadoc_rowID");';
    	execute 'alter table only ' || v_tab_fqname_quoted ||
    		' alter column "_Slony-I_schemadoc_rowID" ' ||
    		' set not null;';

    	--
    	-- Return the resulting Slony-I attkind
    	--
    	return v_attkind;
    end;

## determineattkindunique( text, name )

determineattkindunique( text, name )

### Function Properties

Language Return Type PLPGSQL text

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).

    declare
    	p_tab_fqname	alias for $1;
    	v_tab_fqname_quoted	text default '';
    	p_idx_name		alias for $2;
    	v_idx_name_quoted	text;
    	v_idxrow		record;
    	v_attrow		record;
    	v_i				integer;
    	v_attno			int2;
    	v_attkind		text default '';
    	v_attfound		bool;
    begin
    	v_tab_fqname_quoted := slon_quote_input(p_tab_fqname);
    	v_idx_name_quoted := slon_quote_brute(p_idx_name);
    	--
    	-- Ensure that the table exists
    	--
    	if (select PGC.relname
    				from "pg_catalog".pg_class PGC,
    					"pg_catalog".pg_namespace PGN
    				where slon_quote_brute(PGN.nspname) || '.' ||
    					slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    					and PGN.oid = PGC.relnamespace) is null then
    		raise exception 'Slony-I: table % not found', v_tab_fqname_quoted;
    	end if;

    	--
    	-- Lookup the tables primary key or the specified unique index
    	--
    	if p_idx_name isnull then
    		raise exception 'Slony-I: index name must be specified';
    	else
    		select PGXC.relname, PGX.indexrelid, PGX.indkey
    				into v_idxrow
    				from "pg_catalog".pg_class PGC,
    					"pg_catalog".pg_namespace PGN,
    					"pg_catalog".pg_index PGX,
    					"pg_catalog".pg_class PGXC
    				where slon_quote_brute(PGN.nspname) || '.' ||
    					slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    					and PGN.oid = PGC.relnamespace
    					and PGX.indrelid = PGC.oid
    					and PGX.indexrelid = PGXC.oid
    					and PGX.indisunique
    					and slon_quote_brute(PGXC.relname) = v_idx_name_quoted;
    		if not found then
    			raise exception 'Slony-I: table % has no unique index %',
    					v_tab_fqname_quoted, v_idx_name_quoted;
    		end if;
    	end if;

    	--
    	-- Loop over the tables attributes and check if they are
    	-- index attributes. If so, add a "k" to the return value,
    	-- otherwise add a "v".
    	--
    	for v_attrow in select PGA.attnum, PGA.attname
    			from "pg_catalog".pg_class PGC,
    			    "pg_catalog".pg_namespace PGN,
    				"pg_catalog".pg_attribute PGA
    			where slon_quote_brute(PGN.nspname) || '.' ||
    			    slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    				and PGN.oid = PGC.relnamespace
    				and PGA.attrelid = PGC.oid
    				and not PGA.attisdropped
    				and PGA.attnum > 0
    			order by attnum
    	loop
    		v_attfound = 'f';

    		v_i := 0;
    		loop
    			select indkey[v_i] into v_attno from "pg_catalog".pg_index
    					where indexrelid = v_idxrow.indexrelid;
    			if v_attno isnull or v_attno = 0 then
    				exit;
    			end if;
    			if v_attrow.attnum = v_attno then
    				v_attfound = 't';
    				exit;
    			end if;
    			v_i := v_i + 1;
    		end loop;

    		if v_attfound then
    			v_attkind := v_attkind || 'k';
    		else
    			v_attkind := v_attkind || 'v';
    		end if;
    	end loop;

    	--
    	-- Return the resulting attkind
    	--
    	return v_attkind;
    end;

## determineidxnameserial( text )

determineidxnameserial( text )

### Function Properties

Language Return Type PLPGSQL name

determineIdxnameSerial (tab_fqname)

Given a tablename, construct the index name of the serial column.

    declare
    	p_tab_fqname	alias for $1;
    	v_tab_fqname_quoted	text default '';
    	v_row			record;
    begin
    	v_tab_fqname_quoted := slon_quote_input(p_tab_fqname);
    	--
    	-- Lookup the table name alone
    	--
    	select PGC.relname
    			into v_row
    			from "pg_catalog".pg_class PGC,
    				"pg_catalog".pg_namespace PGN
    			where slon_quote_brute(PGN.nspname) || '.' ||
    				slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    				and PGN.oid = PGC.relnamespace;
    	if not found then
    		raise exception 'Slony-I: table % not found',
    				v_tab_fqname_quoted;
    	end if;

    	--
    	-- Return the found index name
    	--
    	return v_row.relname || '__Slony-I_schemadoc_rowID_key';
    end;

## determineidxnameunique( text, name )

determineidxnameunique( text, name )

### Function Properties

Language Return Type PLPGSQL name

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.

    declare
    	p_tab_fqname	alias for $1;
    	v_tab_fqname_quoted	text default '';
    	p_idx_name		alias for $2;
    	v_idxrow		record;
    begin
    	v_tab_fqname_quoted := slon_quote_input(p_tab_fqname);
    	--
    	-- Ensure that the table exists
    	--
    	if (select PGC.relname
    				from "pg_catalog".pg_class PGC,
    					"pg_catalog".pg_namespace PGN
    				where slon_quote_brute(PGN.nspname) || '.' ||
    					slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    					and PGN.oid = PGC.relnamespace) is null then
    		raise exception 'Slony-I: table % not found', v_tab_fqname_quoted;
    	end if;

    	--
    	-- Lookup the tables primary key or the specified unique index
    	--
    	if p_idx_name isnull then
    		select PGXC.relname
    				into v_idxrow
    				from "pg_catalog".pg_class PGC,
    					"pg_catalog".pg_namespace PGN,
    					"pg_catalog".pg_index PGX,
    					"pg_catalog".pg_class PGXC
    				where slon_quote_brute(PGN.nspname) || '.' ||
    					slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    					and PGN.oid = PGC.relnamespace
    					and PGX.indrelid = PGC.oid
    					and PGX.indexrelid = PGXC.oid
    					and PGX.indisprimary;
    		if not found then
    			raise exception 'Slony-I: table % has no primary key',
    					v_tab_fqname_quoted;
    		end if;
    	else
    		select PGXC.relname
    				into v_idxrow
    				from "pg_catalog".pg_class PGC,
    					"pg_catalog".pg_namespace PGN,
    					"pg_catalog".pg_index PGX,
    					"pg_catalog".pg_class PGXC
    				where slon_quote_brute(PGN.nspname) || '.' ||
    					slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    					and PGN.oid = PGC.relnamespace
    					and PGX.indrelid = PGC.oid
    					and PGX.indexrelid = PGXC.oid
    					and PGX.indisunique
    					and slon_quote_brute(PGXC.relname) = slon_quote_input(p_idx_name);
    		if not found then
    			raise exception 'Slony-I: table % has no unique index %',
    					v_tab_fqname_quoted, p_idx_name;
    		end if;
    	end if;

    	--
    	-- Return the found index name
    	--
    	return v_idxrow.relname;
    end;

## disablenode( integer )

disablenode( integer )

### Function Properties

Language Return Type PLPGSQL bigint

process DISABLE_NODE event for node no_id

NOTE: This is not yet implemented!

    declare
    	p_no_id			alias for $1;
    begin
    	-- **** TODO ****
    	raise exception 'Slony-I: disableNode() not implemented';
    end;

## disablenode_int( integer )

disablenode_int( integer )

### Function Properties

Language Return Type PLPGSQL integer

    declare
    	p_no_id			alias for $1;
    begin
    	-- **** TODO ****
    	raise exception 'Slony-I: disableNode_int() not implemented';
    end;

## droplisten( integer, integer, integer )

droplisten( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

dropListen (li_origin, li_provider, li_receiver)

Generate the DROP_LISTEN event.

    declare
    	p_li_origin		alias for $1;
    	p_li_provider	alias for $2;
    	p_li_receiver	alias for $3;
    begin
    	return -1;

    	perform dropListen_int(p_li_origin,
    			p_li_provider, p_li_receiver);

    	return  createEvent ('_schemadoc', 'DROP_LISTEN',
    			p_li_origin, p_li_provider, p_li_receiver);
    end;

## droplisten_int( integer, integer, integer )

droplisten_int( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

dropListen (li_origin, li_provider, li_receiver)

Process the DROP_LISTEN event, deleting the sl_listen entry for the indicated (origin,provider,receiver) combination.

    declare
    	p_li_origin		alias for $1;
    	p_li_provider	alias for $2;
    	p_li_receiver	alias for $3;
    begin
    	return -1;

    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	delete from 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;

## dropnode( integer )

dropnode( integer )

### Function Properties

Language Return Type PLPGSQL bigint

generate DROP_NODE event to drop node node_id from replication

    declare
    	p_no_id			alias for $1;
    	v_node_row		record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Check that this got called on a different node
    	-- ----
    	if p_no_id = getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: DROP_NODE cannot initiate on the dropped node';
    	end if;

    	select * into v_node_row from sl_node
    			where no_id = p_no_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: unknown node ID %', p_no_id;
    	end if;

    	-- ----
    	-- Make sure we do not break other nodes subscriptions with this
    	-- ----
    	if exists (select true from sl_subscribe
    			where sub_provider = p_no_id)
    	then
    		raise exception 'Slony-I: Node % is still configured as data provider',
    				p_no_id;
    	end if;

    	-- ----
    	-- Make sure no set originates there any more
    	-- ----
    	if exists (select true from sl_set
    			where set_origin = p_no_id)
    	then
    		raise exception 'Slony-I: Node % is still origin of one or more sets',
    				p_no_id;
    	end if;

    	-- ----
    	-- Call the internal drop functionality and generate the event
    	-- ----
    	perform dropNode_int(p_no_id);
    	return  createEvent('_schemadoc', 'DROP_NODE',
    									p_no_id);
    end;

## dropnode_int( integer )

dropnode_int( integer )

### Function Properties

Language Return Type PLPGSQL integer

internal function to process DROP_NODE event to drop node node_id from replication

    declare
    	p_no_id			alias for $1;
    	v_tab_row		record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- If the dropped node is a remote node, clean the configuration
    	-- from all traces for it.
    	-- ----
    	if p_no_id <> getLocalNodeId('_schemadoc') then
    		delete from sl_subscribe
    				where sub_receiver = p_no_id;
    		delete from sl_listen
    				where li_origin = p_no_id
    					or li_provider = p_no_id
    					or li_receiver = p_no_id;
    		delete from sl_path
    				where pa_server = p_no_id
    					or pa_client = p_no_id;
    		delete from sl_confirm
    				where con_origin = p_no_id
    					or con_received = p_no_id;
    		delete from sl_event
    				where ev_origin = p_no_id;
    		delete from 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 sl_node
    			set no_active = false
    			where no_id = p_no_id;

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	return p_no_id;
    end;

## droppath( integer, integer )

droppath( integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

Generate DROP_PATH event to drop path from pa_server to pa_client

    declare
    	p_pa_server		alias for $1;
    	p_pa_client		alias for $2;
    	v_row			record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table 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 sl_subscribe
    			where sub_provider = p_pa_server
    			and sub_receiver = p_pa_client
    	loop
    		raise exception
    			'Slony-I: Path cannot be dropped, subscription of set % needs it',
    			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 sl_listen
    			where li_provider = p_pa_server
    			and li_receiver = p_pa_client
    	loop
    		perform dropListen(
    				v_row.li_origin, v_row.li_provider, v_row.li_receiver);
    	end loop;

    	-- ----
    	-- Now drop the path and create the event
    	-- ----
    	perform dropPath_int(p_pa_server, p_pa_client);

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	return  createEvent ('_schemadoc', 'DROP_PATH',
    			p_pa_server, p_pa_client);
    end;

## droppath_int( integer, integer )

droppath_int( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

Process DROP_PATH event to drop path from pa_server to pa_client

    declare
    	p_pa_server		alias for $1;
    	p_pa_client		alias for $2;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table 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 sl_listen
    			where li_provider = p_pa_server
    			and li_receiver = p_pa_client;

    	delete from sl_path
    			where pa_server = p_pa_server
    			and pa_client = p_pa_client;

    	if found then
    		-- Rewrite sl_listen table
    		perform RebuildListenEntries();

    		return 1;
    	else
    		-- Rewrite sl_listen table
    		perform RebuildListenEntries();

    		return 0;
    	end if;
    end;

## dropset( integer )

dropset( integer )

### Function Properties

Language Return Type PLPGSQL bigint

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

    declare
    	p_set_id			alias for $1;
    	v_origin			int4;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Check that the set exists and originates here
    	-- ----
    	select set_origin into v_origin from sl_set
    			where set_id = p_set_id;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_set_id;
    	end if;
    	if v_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_set_id;
    	end if;

    	-- ----
    	-- Call the internal drop set functionality and generate the event
    	-- ----
    	perform dropSet_int(p_set_id);
    	return  createEvent('_schemadoc', 'DROP_SET',
    			p_set_id);
    end;

## dropset_int( integer )

dropset_int( integer )

### Function Properties

Language Return Type PLPGSQL integer

    declare
    	p_set_id			alias for $1;
    	v_tab_row			record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Restore all tables original triggers and rules and remove
    	-- our replication stuff.
    	-- ----
    	for v_tab_row in select tab_id from sl_table
    			where tab_set = p_set_id
    			order by tab_id
    	loop
    		perform alterTableRestore(v_tab_row.tab_id);
    		perform tableDropKey(v_tab_row.tab_id);
    	end loop;

    	-- ----
    	-- Remove all traces of the set configuration
    	-- ----
    	delete from sl_sequence
    			where seq_set = p_set_id;
    	delete from sl_table
    			where tab_set = p_set_id;
    	delete from sl_subscribe
    			where sub_set = p_set_id;
    	delete from sl_setsync
    			where ssy_setid = p_set_id;
    	delete from sl_set
    			where set_id = p_set_id;

    	-- Regenerate sl_listen since we revised the subscriptions
    	perform RebuildListenEntries();

    	return p_set_id;
    end;

## droptrigger( integer, name )

droptrigger( integer, name )

### Function Properties

Language Return Type PLPGSQL bigint

dropTrigger (trig_tabid, trig_tgname)

Submits DROP_TRIGGER event to indicate that trigger trig_tgname on replicated table trig_tabid WILL be disabled.

    declare
    	p_trig_tabid		alias for $1;
    	p_trig_tgname		alias for $2;
    begin
    	perform dropTrigger_int(p_trig_tabid, p_trig_tgname);
    	return  createEvent('_schemadoc', 'DROP_TRIGGER',
    			p_trig_tabid, p_trig_tgname);
    end;

## droptrigger_int( integer, name )

droptrigger_int( integer, name )

### Function Properties

Language Return Type PLPGSQL integer

dropTrigger_int (trig_tabid, trig_tgname)

Processes DROP_TRIGGER event to make sure that trigger trig_tgname on replicated table trig_tabid IS disabled.

    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 sl_config_lock;

    	-- ----
    	-- Get the current table status (altered or not)
    	-- ----
    	select tab_altered into v_tab_altered
    			from 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 alterTableRestore(p_trig_tabid);
    	end if;

    	-- ----
    	-- Remove the entry from sl_trigger
    	-- ----
    	delete from 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 alterTableForReplication(p_trig_tabid);
    	end if;

    	return p_trig_tabid;
    end;

## enablenode( integer )

enablenode( integer )

### Function Properties

Language Return Type PLPGSQL bigint

no_id - Node ID #

Generate the ENABLE_NODE event for node no_id

    declare
    	p_no_id			alias for $1;
    	v_local_node_id	int4;
    	v_node_row		record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Check that we are the node to activate and that we are
    	-- currently disabled.
    	-- ----
    	v_local_node_id := getLocalNodeId('_schemadoc');
    	select * into v_node_row
    			from sl_node
    			where no_id = p_no_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: node % not found', p_no_id;
    	end if;
    	if v_node_row.no_active then
    		raise exception 'Slony-I: node % is already active', p_no_id;
    	end if;

    	-- ----
    	-- Activate this node and generate the ENABLE_NODE event
    	-- ----
    	perform enableNode_int (p_no_id);
    	return  createEvent('_schemadoc', 'ENABLE_NODE',
    									p_no_id);
    end;

## enablenode_int( integer )

enablenode_int( integer )

### Function Properties

Language Return Type PLPGSQL integer

no_id - Node ID #

Internal function to process the ENABLE_NODE event for node no_id

    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 sl_config_lock;

    	-- ----
    	-- Check that the node is inactive
    	-- ----
    	select * into v_node_row
    			from sl_node
    			where no_id = p_no_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: node % not found', 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 sl_node
    			set no_active = 't'
    			where no_id = p_no_id;
    	insert into sl_confirm
    			(con_origin, con_received, con_seqno)
    			select no_id, p_no_id, 0 from sl_node
    				where no_id != p_no_id
    				and no_active;
    	insert into sl_confirm
    			(con_origin, con_received, con_seqno)
    			select p_no_id, no_id, 0 from 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 := getLocalNodeId('_schemadoc');
    	for v_sub_row in select SUB.sub_set, SUB.sub_provider from
    			sl_set S,
    			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 enableSubscription (v_sub_row.sub_set,
    				v_sub_row.sub_provider, p_no_id);
    	end loop;

    	return p_no_id;
    end;

## enablesubscription( integer, integer, integer )

enablesubscription( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

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).

    declare
    	p_sub_set			alias for $1;
    	p_sub_provider		alias for $2;
    	p_sub_receiver		alias for $3;
    begin
    	return  enableSubscription_int (p_sub_set,
    			p_sub_provider, p_sub_receiver);
    end;

## enablesubscription_int( integer, integer, integer )

enablesubscription_int( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_config_lock;

    	-- ----
    	-- The real work is done in the replication engine. All
    	-- we have to do here is remembering that it happened.
    	-- ----

    	-- ----
    	-- Well, not only ... we might be missing an important event here
    	-- ----
    	if not exists (select true from sl_path
    			where pa_server = p_sub_provider
    			and pa_client = p_sub_receiver)
    	then
    		insert into sl_path
    				(pa_server, pa_client, pa_conninfo, pa_connretry)
    				values
    				(p_sub_provider, p_sub_receiver,
    				'<event pending>', 10);
    	end if;

    	update sl_subscribe
    			set sub_active = 't'
    			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 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;

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	return p_sub_set;
    end;

## failednode( integer, integer )

failednode( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 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 sl_path P
    			where P.pa_server = p_failed_node
    				and P.pa_client <> p_backup_node
    				and not exists (select true from sl_path PP
    							where PP.pa_server = p_backup_node
    								and PP.pa_client = P.pa_client)
    	loop
    		raise exception 'Slony-I: cannot failover - node % has no path to the backup node',
    				v_row.pa_client;
    	end loop;

    	-- ----
    	-- Check all sets originating on the failed node
    	-- ----
    	for v_row in select set_id
    			from 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 sl_subscribe
    				where sub_set = v_row.set_id
    					and sub_receiver = p_backup_node;
    		if not found then
    			raise exception 'Slony-I: cannot failover - node % is not subscribed to set %',
    					p_backup_node, v_row.set_id;
    		end if;

    		-- ----
    		-- Check that the subscription is active
    		-- ----
    		if not v_row2.sub_active then
    			raise exception 'Slony-I: cannot failover - subscription for set % is not active',
    					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 sl_subscribe
    				where sub_set = v_row.set_id
    					and sub_receiver <> p_backup_node;
    		if v_n > 0 and not v_row2.sub_forward then
    			raise exception 'Slony-I: cannot failover - node % is not a forwarder of set %',
    					p_backup_node, v_row.set_id;
    		end if;
    	end loop;

    	-- ----
    	-- Terminate all connections of the failed node the hard way
    	-- ----
    	perform terminateNodeConnections(
    			'_schemadoc_Node_' || p_failed_node);

    -- Note that the following code should all become obsolete in the wake
    -- of the availability of RebuildListenEntries()...

    if false then
    	-- ----
    	-- 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 sl_listen
    			where li_provider = p_failed_node
    				and li_receiver <> p_backup_node
    	loop
    		perform 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 sl_listen
    			where li_receiver = p_failed_node
    				and li_provider <> p_backup_node
    	loop
    		perform 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 sl_listen
    			where li_provider = p_failed_node
    				or li_receiver = p_failed_node;
    end if;

    	-- ----
    	-- Move the sets
    	-- ----
    	for v_row in select S.set_id, (select count(*)
    					from sl_subscribe SUB
    					where S.set_id = SUB.sub_set
    						and SUB.sub_receiver <> p_backup_node
    						and SUB.sub_provider = p_failed_node)
    					as num_direct_receivers
    			from 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 'failedNode: set % has no other direct receivers - move now', 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 = getLocalNodeId('_schemadoc') then
    				for v_row2 in select * from sl_table
    						where tab_set = v_row.set_id
    				loop
    					perform alterTableRestore(v_row2.tab_id);
    				end loop;
    			end if;

    			update sl_set set set_origin = p_backup_node
    					where set_id = v_row.set_id;

    			if p_backup_node = getLocalNodeId('_schemadoc') then
    				delete from sl_setsync
    						where ssy_setid = v_row.set_id;

    				for v_row2 in select * from sl_table
    						where tab_set = v_row.set_id
    				loop
    					perform alterTableForReplication(v_row2.tab_id);
    				end loop;
    			end if;

    			delete from sl_subscribe
    					where sub_set = v_row.set_id
    						and sub_receiver = p_backup_node;
    		else
    raise notice 'failedNode: set % has other direct receivers - change providers only', 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 sl_subscribe
    					set sub_provider = (select min(SS.sub_receiver)
    							from sl_subscribe SS
    							where SS.sub_set = v_row.set_id
    								and SS.sub_provider = p_failed_node
    								and SS.sub_receiver <> p_backup_node
    								and SS.sub_forward)
    					where sub_set = v_row.set_id
    						and sub_receiver = p_backup_node;
    			update sl_subscribe
    					set sub_provider = p_backup_node
    					where sub_set = v_row.set_id
    						and sub_provider = p_failed_node
    						and sub_receiver <> p_backup_node;
    		end if;
    	end loop;

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	-- ----
    	-- Make sure the node daemon will restart
    	-- ----
    	notify "_schemadoc_Restart";

    	-- ----
    	-- That is it - so far.
    	-- ----
    	return p_failed_node;
    end;

## failednode2( integer, integer, integer, bigint, bigint )

failednode2( integer, integer, integer, bigint, bigint )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    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 sl_config_lock;

    	select * into v_row
    			from sl_event
    			where ev_origin = p_failed_node
    			and ev_seqno = p_ev_seqno;
    	if not found then
    		raise exception 'Slony-I: event %,% not found',
    				p_failed_node, p_ev_seqno;
    	end if;

    	insert into 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,
    			'FAILOVER_SET', p_failed_node::text, p_backup_node::text,
    			p_set_id::text);
    	insert into sl_confirm
    			(con_origin, con_received, con_seqno, con_timestamp)
    			values
    			(p_failed_node, getLocalNodeId('_schemadoc'),
    			p_ev_seqfake, CURRENT_TIMESTAMP);
    	notify "_schemadoc_Event";
    	notify "_schemadoc_Confirm";
    	notify "_schemadoc_Restart";

    	perform failoverSet_int(p_failed_node,
    			p_backup_node, p_set_id);

    	return p_ev_seqfake;
    end;

## failoverset_int( integer, integer, integer )

failoverset_int( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

FUNCTION failoverSet_int (failed_node, backup_node, set_id)

Finish failover for one set.

    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 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 = getLocalNodeId('_schemadoc') then
    		for v_row in select * from sl_table
    				where tab_set = p_set_id
    		loop
    			perform alterTableRestore(v_row.tab_id);
    		end loop;

    		delete from sl_setsync
    				where ssy_setid = p_set_id;
    		delete from sl_subscribe
    				where sub_set = p_set_id
    					and sub_receiver = p_backup_node;
    		update sl_set
    				set set_origin = p_backup_node
    				where set_id = p_set_id;

    		for v_row in select * from sl_table
    				where tab_set = p_set_id
    		loop
    			perform alterTableForReplication(v_row.tab_id);
    		end loop;
    	else
    		delete from sl_subscribe
    				where sub_set = p_set_id
    					and sub_receiver = p_backup_node;
    		update sl_set
    				set set_origin = p_backup_node
    				where set_id = p_set_id;
    	end if;

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	-- ----
    	-- If we are a subscriber of the set ourself, change our
    	-- setsync status to reflect the new set origin.
    	-- ----
    	if exists (select true from sl_subscribe
    			where sub_set = p_set_id
    				and sub_receiver = getLocalNodeId(
    						'_schemadoc'))
    	then
    		delete from sl_setsync
    				where ssy_setid = p_set_id;

    		select coalesce(max(ev_seqno), 0) into v_last_sync
    				from sl_event
    				where ev_origin = p_backup_node
    					and ev_type = 'SYNC';
    		if v_last_sync > 0 then
    			insert into 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 sl_event
    					where ev_origin = p_backup_node
    						and ev_seqno = v_last_sync;
    		else
    			insert into sl_setsync
    					(ssy_setid, ssy_origin, ssy_seqno,
    					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
    					values (p_set_id, p_backup_node, '0',
    					'0', '0', '', NULL);
    		end if;

    	end if;

    	return p_failed_node;
    end;

## forwardconfirm( integer, integer, bigint, timestamp without time zone )

forwardconfirm( integer, integer, bigint, timestamp without time zone )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    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 sl_confirm
    			where con_origin = p_con_origin
    			and con_received = p_con_received;
    	if v_max_seqno < p_con_seqno then
    		insert into sl_confirm
    				(con_origin, con_received, con_seqno, con_timestamp)
    				values (p_con_origin, p_con_received, p_con_seqno,
    					p_con_timestamp);
    		notify "_schemadoc_Confirm";
    		v_max_seqno = p_con_seqno;
    	end if;

    	return v_max_seqno;
    end;

## generate_sync_event( interval )

generate_sync_event( interval )

### Function Properties

Language Return Type PLPGSQL integer

Generate a sync event if there has not been one in 30 seconds.

    declare
    	p_interval     alias for $1;
    	v_node_row     record;

    BEGIN
    	select 1 into v_node_row from sl_event
           	  where ev_type = 'SYNC' and ev_origin = getLocalNodeId('schemadoc')
              and ev_timestamp > now() - p_interval limit 1;
    	if not found then
    		-- If there has been no SYNC in the last interval, then push one
    		perform createEvent('schemadoc', 'SYNC', NULL);
    		return 1;
    	else
    		return 0;
    	end if;
    end;

## getlocalnodeid( name )

getlocalnodeid( name )

### Function Properties

Language Return Type C integer

Returns the node ID of the node being serviced on the local database

    _Slony_I_getLocalNodeId

## getmoduleversion( )

getmoduleversion( )

### Function Properties

Language Return Type C text

Returns the compiled-in version number of the Slony-I shared object

    _Slony_I_getModuleVersion

## getsessionrole( name )

getsessionrole( name )

### Function Properties

Language Return Type C text

not yet documented

    _Slony_I_getSessionRole

## initializelocalnode( integer, text )

initializelocalnode( integer, text )

### Function Properties

Language Return Type PLPGSQL integer

no_id - Node ID # no_comment - Human-oriented comment

Initializes the new node, no_id

    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 sl_config_lock;

    	-- ----
    	-- Make sure this node is uninitialized or got reset
    	-- ----
    	select last_value::int4 into v_old_node_id from sl_local_node_id;
    	if v_old_node_id != -1 then
    		raise exception 'Slony-I: This node is already initialized';
    	end if;

    	-- ----
    	-- Set sl_local_node_id to the requested value and add our
    	-- own system to sl_node.
    	-- ----
    	perform setval('sl_local_node_id', p_local_node_id);
    	perform setval('sl_rowid_seq',
    			p_local_node_id::int8 * '1000000000000000'::int8);
    	perform storeNode_int (p_local_node_id, p_comment, false);

    	return p_local_node_id;
    end;

## lockedset( )

lockedset( )

### Function Properties

Language Return Type C "trigger"

Trigger function to prevent modifications to a table before and after a moveSet()

    _Slony_I_lockedSet

## lockset( integer )

lockset( integer )

### Function Properties

Language Return Type PLPGSQL integer

lockSet(set_id)

Add a special trigger to all tables of a set that disables access to it.

    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 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 := getLocalNodeId('_schemadoc');
    	select * into v_set_row from sl_set
    			where set_id = p_set_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_set_id;
    	end if;
    	if v_set_row.set_origin <> v_local_node_id then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_set_id;
    	end if;
    	if v_set_row.set_locked notnull then
    		raise exception 'Slony-I: set % is already locked', p_set_id;
    	end if;

    	-- ----
    	-- Place the lockedSet trigger on all tables in the set.
    	-- ----
    	for v_tab_row in select T.tab_id,
    			slon_quote_brute(PGN.nspname) || '.' ||
    			slon_quote_brute(PGC.relname) as tab_fqname
    			from sl_table T,
    				"pg_catalog".pg_class PGC, "pg_catalog".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 'create trigger "_schemadoc_lockedset_' ||
    				v_tab_row.tab_id ||
    				'" before insert or update or delete on ' ||
    				v_tab_row.tab_fqname || ' for each row execute procedure
    				lockedSet (''_schemadoc'');';
    	end loop;

    	-- ----
    	-- Remember our snapshots xmax as for the set locking
    	-- ----
    	update sl_set
    			set set_locked = getMaxXid()
    			where set_id = p_set_id;

    	return p_set_id;
    end;

## logtrigger( )

logtrigger( )

### Function Properties

Language Return Type C "trigger"

This is the trigger that is executed on the origin node that causes updates to be recorded in sl_log_1/sl_log_2.

    _Slony_I_logTrigger

## mergeset( integer, integer )

mergeset( integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    declare
    	p_set_id			alias for $1;
    	p_add_id			alias for $2;
    	v_origin			int4;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Check that both sets exist and originate here
    	-- ----
    	if p_set_id = p_add_id then
    		raise exception 'Slony-I: merged set ids cannot be identical';
    	end if;
    	select set_origin into v_origin from sl_set
    			where set_id = p_set_id;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_set_id;
    	end if;
    	if v_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_set_id;
    	end if;

    	select set_origin into v_origin from sl_set
    			where set_id = p_add_id;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_add_id;
    	end if;
    	if v_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_add_id;
    	end if;

    	-- ----
    	-- Check that both sets are subscribed by the same set of nodes
    	-- ----
    	if exists (select true from sl_subscribe SUB1
    				where SUB1.sub_set = p_set_id
    				and SUB1.sub_receiver not in (select SUB2.sub_receiver
    						from sl_subscribe SUB2
    						where SUB2.sub_set = p_add_id))
    	then
    		raise exception 'Slony-I: subscriber lists of set % and % are different',
    				p_set_id, p_add_id;
    	end if;

    	if exists (select true from sl_subscribe SUB1
    				where SUB1.sub_set = p_add_id
    				and SUB1.sub_receiver not in (select SUB2.sub_receiver
    						from sl_subscribe SUB2
    						where SUB2.sub_set = p_set_id))
    	then
    		raise exception 'Slony-I: subscriber lists of set % and % are different',
    				p_add_id, p_set_id;
    	end if;

    	-- ----
    	-- Create a SYNC event, merge the sets, create a MERGE_SET event
    	-- ----
    	perform createEvent('_schemadoc', 'SYNC', NULL);
    	perform mergeSet_int(p_set_id, p_add_id);
    	return  createEvent('_schemadoc', 'MERGE_SET',
    			p_set_id, p_add_id);
    end;

## mergeset_int( integer, integer )

mergeset_int( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

mergeSet_int(set_id, add_id) - Perform MERGE_SET event, merging all objects from set add_id into set set_id.

    declare
    	p_set_id			alias for $1;
    	p_add_id			alias for $2;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	update sl_sequence
    			set seq_set = p_set_id
    			where seq_set = p_add_id;
    	update sl_table
    			set tab_set = p_set_id
    			where tab_set = p_add_id;
    	delete from sl_subscribe
    			where sub_set = p_add_id;
    	delete from sl_setsync
    			where ssy_setid = p_add_id;
    	delete from sl_set
    			where set_id = p_add_id;

    	return p_set_id;
    end;

## moveset( integer, integer )

moveset( integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

moveSet(set_id, new_origin)

Generate MOVE_SET event to request that the origin for set set_id be moved to node new_origin

    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 sl_config_lock;

    	-- ----
    	-- Check that the set is locked and that this locking
    	-- happened long enough ago.
    	-- ----
    	v_local_node_id := getLocalNodeId('_schemadoc');
    	select * into v_set_row from sl_set
    			where set_id = p_set_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_set_id;
    	end if;
    	if v_set_row.set_origin <> v_local_node_id then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_set_id;
    	end if;
    	if v_set_row.set_locked isnull then
    		raise exception 'Slony-I: set % is not locked', p_set_id;
    	end if;
    	if v_set_row.set_locked > getMinXid() then
    		raise exception 'Slony-I: cannot move set % yet, transactions < % are still in progress',
    				p_set_id, v_set_row.set_locked;
    	end if;

    	-- ----
    	-- Unlock the set
    	-- ----
    	perform unlockSet(p_set_id);

    	-- ----
    	-- Check that the new_origin is an active subscriber of the set
    	-- ----
    	select * into v_sub_row from sl_subscribe
    			where sub_set = p_set_id
    			and sub_receiver = p_new_origin;
    	if not found then
    		raise exception 'Slony-I: set % is not subscribed by node %',
    				p_set_id, p_new_origin;
    	end if;
    	if not v_sub_row.sub_active then
    		raise exception 'Slony-I: subsctiption of node % for set % is inactive',
    				p_new_origin, p_set_id;
    	end if;

    	-- ----
    	-- Reconfigure everything
    	-- ----
    	perform moveSet_int(p_set_id, v_local_node_id,
    			p_new_origin);

    	perform RebuildListenEntries();

    	-- ----
    	-- 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 := createEvent('_schemadoc', 'SYNC');
    	insert into 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 sl_seqlastvalue
    			where seq_set = p_set_id;

    	-- ----
    	-- Finally we generate the real event
    	-- ----
    	return createEvent('_schemadoc', 'MOVE_SET',
    			p_set_id, v_local_node_id, p_new_origin);
    end;

## moveset_int( integer, integer, integer )

moveset_int( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

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

    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 sl_config_lock;

    	-- ----
    	-- Get our local node ID
    	-- ----
    	v_local_node_id := getLocalNodeId('_schemadoc');

    	-- ----
    	-- 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 sl_table
    				where tab_set = p_set_id
    				order by tab_id
    		loop
    			perform alterTableRestore(v_tab_row.tab_id);
    		end loop;
    	end if;

    	-- On the new origin, raise an event - ACCEPT_SET
    	if v_local_node_id = p_new_origin then
    		-- Find the event number from the origin
    		select max(ev_seqno) as seqno into v_sub_row
    			from sl_event
    			where ev_type = 'MOVE_SET' and
    			  ev_data1 = p_set_id and
    			  ev_data2 = p_old_origin and
    			  ev_data3 = p_new_origin and
    			  ev_origin = p_old_origin;

    		perform createEvent('_schemadoc', 'ACCEPT_SET',
    			p_set_id, p_old_origin, p_new_origin, v_sub_row.seqno);
    	end if;

    	-- ----
    	-- Next we have to reverse the subscription path
    	-- ----
    	v_sub_last = p_new_origin;
    	select sub_provider into v_sub_node
    			from sl_subscribe
    			where sub_set = p_set_id
    			and sub_receiver = p_new_origin;
    	if not found then
    		raise exception 'Slony-I: subscription path broken in moveSet_int';
    	end if;
    	while v_sub_node <> 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 sl_subscribe
    				where sub_set = p_set_id
    					and sub_receiver = v_sub_node
    				for update;
    		if not found then
    			raise exception 'Slony-I: subscription path broken in moveSet_int';
    		end if;
    		update 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 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 sl_event
    				where ev_origin = p_new_origin
    					and ev_type = 'SYNC';
    		if v_last_sync > 0 then
    			insert into 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 sl_event
    					where ev_origin = p_new_origin
    						and ev_seqno = v_last_sync;
    		else
    			insert into sl_setsync
    					(ssy_setid, ssy_origin, ssy_seqno,
    					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
    					values (p_set_id, p_new_origin, '0',
    					'0', '0', '', NULL);
    		end if;
    	end if;

    	-- ----
    	-- Now change the ownership of the set.
    	-- ----
    	update 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 sl_setsync
    				where ssy_setid = p_set_id;
    	else
    		if v_local_node_id <> 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 sl_setsync
    					where ssy_setid = p_set_id;
    			select coalesce(max(ev_seqno), 0) into v_last_sync
    					from sl_event
    					where ev_origin = p_new_origin
    						and ev_type = 'SYNC';
    			if v_last_sync > 0 then
    				insert into 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 sl_event
    						where ev_origin = p_new_origin
    							and ev_seqno = v_last_sync;
    			else
    				insert into sl_setsync
    						(ssy_setid, ssy_origin, ssy_seqno,
    						ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
    						values (p_set_id, p_new_origin, '0',
    						'0', '0', '', NULL);
    			end if;
    		end if;
    	end if;
    	delete from sl_subscribe
    			where sub_set = p_set_id
    			and sub_receiver = p_new_origin;

    	-- Regenerate sl_listen since we revised the subscriptions
    	perform RebuildListenEntries();

    	-- ----
    	-- 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 sl_table
    				where tab_set = p_set_id
    				order by tab_id
    		loop
    			perform alterTableForReplication(v_tab_row.tab_id);
    		end loop;
    	end if;

    	return p_set_id;
    end;

## rebuildlistenentries( )

rebuildlistenentries( )

### Function Properties

Language Return Type PLPGSQL integer

RebuildListenEntries(p_provider, p_receiver)

Invoked by various subscription and path modifying functions, this rewrites the sl_listen entries, adding in all the ones required to allow communications between nodes in the Slony-I cluster.

    declare
    	v_row			record;
    begin
    	-- First remove the entire configuration
    	delete from sl_listen;

    	-- The loop over every possible pair of origin, receiver
    	for v_row in select N1.no_id as origin, N2.no_id as receiver
    			from sl_node N1, sl_node N2
    			where N1.no_id <> N2.no_id
    	loop
    		perform RebuildListenEntriesOne(v_row.origin, v_row.receiver);
    	end loop;

    	return 0;
    end;

## rebuildlistenentriesone( integer, integer )

rebuildlistenentriesone( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

RebuildListenEntriesOne(p_origin, p_receiver)

Rebuilding of sl_listen entries for one origin, receiver pair.

    declare
    	p_origin		alias for $1;
    	p_receiver		alias for $2;
    	v_row			record;
    begin
    	-- 1. If the receiver is subscribed to any set from the origin,
    	--    listen on the same provider(s).
    	for v_row in select distinct sub_provider
    			from sl_subscribe, sl_set,
    				sl_path
    			where sub_set = set_id
    			and set_origin = p_origin
    			and sub_receiver = p_receiver
    			and sub_provider = pa_server
    			and sub_receiver = pa_client
    	loop
    		perform storeListen_int(p_origin,
    				v_row.sub_provider, p_receiver);
    	end loop;
    	if found then
    		return 1;
    	end if;

    	-- 2. If the receiver has a direct path to the provider,
    	--    use that.
    	if exists (select true
    			from sl_path
    			where pa_server = p_origin
    			and pa_client = p_receiver)
    	then
    		perform storeListen_int(p_origin, p_origin, p_receiver);
    		return 1;
    	end if;

    	-- 3. Listen on every node that is either provider for the
    	--    receiver or is using the receiver as provider (follow the
    	--    normal subscription routes).
    	for v_row in select distinct provider from (
    			select sub_provider as provider
    					from sl_subscribe
    					where sub_receiver = p_receiver
    			union
    			select sub_receiver as provider
    					from sl_subscribe
    					where sub_provider = p_receiver
    					and exists (select true from sl_path
    								where pa_server = sub_receiver
    								and pa_client = sub_provider)
    			) as S
    	loop
    		perform storeListen_int(p_origin,
    				v_row.provider, p_receiver);
    	end loop;
    	if found then
    		return 1;
    	end if;

    	-- 4. If all else fails - meaning there are no subscriptions to
    	--    guide us to the right path - use every node we have a path
    	--    to as provider. This normally only happens when the cluster
    	--    is built or a new node added. This brute force fallback
    	--    ensures that events will propagate if possible at all.
    	for v_row in select pa_server as provider
    			from sl_path
    			where pa_client = p_receiver
    	loop
    		perform storeListen_int(p_origin,
    				v_row.provider, p_receiver);
    	end loop;
    	if found then
    		return 1;
    	end if;

    	return 0;
    end;

## sequencelastvalue( text )

sequencelastvalue( text )

### Function Properties

Language Return Type PLPGSQL bigint

    declare
    	p_seqname	alias for $1;
    	v_seq_row	record;
    begin
    	for v_seq_row in execute 'select last_value from ' || p_seqname
    	loop
    		return v_seq_row.last_value;
    	end loop;

    	-- not reached
    end;

## sequencesetvalue( integer, integer, bigint, bigint )

sequencesetvalue( integer, integer, bigint, bigint )

### Function Properties

Language Return Type PLPGSQL integer

sequenceSetValue (seq_id, seq_origin, ev_seqno, last_value) Set sequence seq_id to have new value last_value.

    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 slon_quote_brute(PGN.nspname) || '.' ||
    			slon_quote_brute(PGC.relname) into v_fqname
    		from sl_sequence SQ,
    			"pg_catalog".pg_class PGC, "pg_catalog".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 'Slony-I: sequence % not found', p_seq_id;
    	end if;

    	-- ----
    	-- Update it to the new value
    	-- ----
    	execute 'select setval(''' || v_fqname ||
    			''', ''' || p_last_value || ''')';

    	insert into 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;

## setaddsequence( integer, integer, text, text )

setaddsequence( integer, integer, text, text )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    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 sl_config_lock;

    	-- ----
    	-- Check that we are the origin of the set
    	-- ----
    	select set_origin into v_set_origin
    			from sl_set
    			where set_id = p_set_id;
    	if not found then
    		raise exception 'Slony-I: setAddSequence(): set % not found', p_set_id;
    	end if;
    	if v_set_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: setAddSequence(): set % has remote origin', p_set_id;
    	end if;

    	if exists (select true from sl_subscribe
    			where sub_set = p_set_id)
    	then
    		raise exception 'Slony-I: cannot add sequence to currently subscribed set %',
    				p_set_id;
    	end if;

    	-- ----
    	-- Add the sequence to the set and generate the SET_ADD_SEQUENCE event
    	-- ----
    	perform setAddSequence_int(p_set_id, p_seq_id, p_fqname,
    			p_seq_comment);
    	return  createEvent('_schemadoc', 'SET_ADD_SEQUENCE',
    			p_set_id, p_seq_id, p_fqname, p_seq_comment);
    end;

## setaddsequence_int( integer, integer, text, text )

setaddsequence_int( integer, integer, text, text )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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_seq_relname		name;
    	v_seq_nspname		name;
    	v_sync_row			record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table 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 := getLocalNodeId('_schemadoc');
    	select set_origin into v_set_origin
    			from sl_set
    			where set_id = p_set_id;
    	if not found then
    		raise exception 'Slony-I: setAddSequence_int(): set % not found',
    				p_set_id;
    	end if;
    	if v_set_origin != v_local_node_id then
    		select sub_provider into v_sub_provider
    				from sl_subscribe
    				where sub_set = p_set_id
    				and sub_receiver = getLocalNodeId('_schemadoc');
    		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, PGC.relname, PGN.nspname
    		into v_seq_reloid, v_relkind, v_seq_relname, v_seq_nspname
    			from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN
    			where PGC.relnamespace = PGN.oid
    			and slon_quote_input(p_fqname) = slon_quote_brute(PGN.nspname) ||
    					'.' || slon_quote_brute(PGC.relname);
    	if not found then
    		raise exception 'Slony-I: setAddSequence_int(): sequence % not found',
    				p_fqname;
    	end if;
    	if v_relkind != 'S' then
    		raise exception 'Slony-I: setAddSequence_int(): % is not a sequence',
    				p_fqname;
    	end if;

    	-- ----
    	-- Add the sequence to sl_sequence
    	-- ----
    	insert into sl_sequence
    		(seq_id, seq_reloid, seq_relname, seq_nspname, seq_set, seq_comment)
    		values
    		(p_seq_id, v_seq_reloid, v_seq_relname, v_seq_nspname,  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 sl_event
    				where ev_origin = v_local_node_id
    					and ev_type = 'SYNC'
    		loop
    			insert into 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,
    					sequenceLastValue(p_fqname));
    		end loop;
    	end if;

    	return p_seq_id;
    end;

## setaddtable( integer, integer, text, name, text )

setaddtable( integer, integer, text, name, text )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    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 sl_config_lock;

    	-- ----
    	-- Check that we are the origin of the set
    	-- ----
    	select set_origin into v_set_origin
    			from sl_set
    			where set_id = p_set_id;
    	if not found then
    		raise exception 'Slony-I: setAddTable(): set % not found', p_set_id;
    	end if;
    	if v_set_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: setAddTable(): set % has remote origin', p_set_id;
    	end if;

    	if exists (select true from sl_subscribe
    			where sub_set = p_set_id)
    	then
    		raise exception 'Slony-I: cannot add table to currently subscribed set %',
    				p_set_id;
    	end if;

    	-- ----
    	-- Add the table to the set and generate the SET_ADD_TABLE event
    	-- ----
    	perform setAddTable_int(p_set_id, p_tab_id, p_fqname,
    			p_tab_idxname, p_tab_comment);
    	return  createEvent('_schemadoc', 'SET_ADD_TABLE',
    			p_set_id, p_tab_id, p_fqname,
    			p_tab_idxname, p_tab_comment);
    end;

## setaddtable_int( integer, integer, text, name, text )

setaddtable_int( integer, integer, text, name, text )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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_tab_relname		name;
    	v_tab_nspname		name;
    	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 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 := getLocalNodeId('_schemadoc');
    	select set_origin into v_set_origin
    			from sl_set
    			where set_id = p_set_id;
    	if not found then
    		raise exception 'Slony-I: setAddTable_int(): set % not found',
    				p_set_id;
    	end if;
    	if v_set_origin != v_local_node_id then
    		select sub_provider into v_sub_provider
    				from sl_subscribe
    				where sub_set = p_set_id
    				and sub_receiver = getLocalNodeId('_schemadoc');
    		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, PGC.relname, PGN.nspname into v_tab_reloid, v_relkind, v_tab_relname, v_tab_nspname
    			from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN
    			where PGC.relnamespace = PGN.oid
    			and slon_quote_input(p_fqname) = slon_quote_brute(PGN.nspname) ||
    					'.' || slon_quote_brute(PGC.relname);
    	if not found then
    		raise exception 'Slony-I: setAddTable(): table % not found',
    				p_fqname;
    	end if;
    	if v_relkind != 'r' then
    		raise exception 'Slony-I: setAddTable(): % is not a regular table',
    				p_fqname;
    	end if;

    	if not exists (select indexrelid
    			from "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGC
    			where PGX.indrelid = v_tab_reloid
    				and PGX.indexrelid = PGC.oid
    				and PGC.relname = p_tab_idxname)
    	then
    		raise exception 'Slony-I: setAddTable(): table % has no index %',
    				p_fqname, p_tab_idxname;
    	end if;

    	-- ----
    	-- Add the table to sl_table and create the trigger on it.
    	-- ----
    	insert into sl_table
    			(tab_id, tab_reloid, tab_relname, tab_nspname,
    			tab_set, tab_idxname, tab_altered, tab_comment)
    			values
    			(p_tab_id, v_tab_reloid, v_tab_relname, v_tab_nspname,
    			p_set_id, p_tab_idxname, false, p_tab_comment);
    	perform alterTableForReplication(p_tab_id);

    	return p_tab_id;
    end;

## setdropsequence( integer )

setdropsequence( integer )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    declare
    	p_seq_id		alias for $1;
    	v_set_id		int4;
    	v_set_origin		int4;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Determine set id for this sequence
    	-- ----
    	select seq_set into v_set_id from 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 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 != getLocalNodeId('_schemadoc') 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 setDropSequence_int(p_seq_id);
    	return  createEvent('_schemadoc', 'SET_DROP_SEQUENCE',
    			p_seq_id);
    end;

## setdropsequence_int( integer )

setdropsequence_int( integer )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_config_lock;

    	-- ----
    	-- Determine set id for this sequence
    	-- ----
    	select seq_set into v_set_id from 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 := getLocalNodeId('_schemadoc');
    	select set_origin into v_set_origin
    			from 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 sl_subscribe
    				where sub_set = v_set_id
    				and sub_receiver = getLocalNodeId('_schemadoc');
    		if not found then
    			return 0;
    		end if;
    	end if;

    	-- ----
    	-- drop the sequence from sl_sequence, sl_seqlog
    	-- ----
    	delete from sl_seqlog where seql_seqid = p_seq_id;
    	delete from sl_sequence where seq_id = p_seq_id;

    	return p_seq_id;
    end;

## setdroptable( integer )

setdroptable( integer )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    declare
    	p_tab_id		alias for $1;
    	v_set_id		int4;
    	v_set_origin		int4;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

            -- ----
    	-- Determine the set_id
            -- ----
    	select tab_set into v_set_id from 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 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 != getLocalNodeId('_schemadoc') 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 setDropTable_int(p_tab_id);
    	return  createEvent('_schemadoc', 'SET_DROP_TABLE', p_tab_id);
    end;

## setdroptable_int( integer )

setdroptable_int( integer )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_config_lock;

            -- ----
    	-- Determine the set_id
            -- ----
    	select tab_set into v_set_id from 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 := getLocalNodeId('_schemadoc');
    	select set_origin into v_set_origin
    			from 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 sl_subscribe
    				where sub_set = v_set_id
    				and sub_receiver = getLocalNodeId('_schemadoc');
    		if not found then
    			return 0;
    		end if;
    	end if;

    	-- ----
    	-- Drop the table from sl_table and drop trigger from it.
    	-- ----
    	perform alterTableRestore(p_tab_id);
    	perform tableDropKey(p_tab_id);
    	delete from sl_table where tab_id = p_tab_id;
    	return p_tab_id;
    end;

## setmovesequence( integer, integer )

setmovesequence( integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

    declare
    	p_seq_id			alias for $1;
    	p_new_set_id		alias for $2;
    	v_old_set_id		int4;
    	v_origin			int4;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Get the sequences current set
    	-- ----
    	select seq_set into v_old_set_id from sl_sequence
    			where seq_id = p_seq_id;
    	if not found then
    		raise exception 'Slony-I: sequence %d not found', p_seq_id;
    	end if;

    	-- ----
    	-- Check that both sets exist and originate here
    	-- ----
    	if p_new_set_id = v_old_set_id then
    		raise exception 'Slony-I: set ids cannot be identical';
    	end if;
    	select set_origin into v_origin from sl_set
    			where set_id = p_new_set_id;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_new_set_id;
    	end if;
    	if v_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_new_set_id;
    	end if;

    	select set_origin into v_origin from sl_set
    			where set_id = v_old_set_id;
    	if not found then
    		raise exception 'Slony-I: set % not found', v_old_set_id;
    	end if;
    	if v_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				v_old_set_id;
    	end if;

    	-- ----
    	-- Check that both sets are subscribed by the same set of nodes
    	-- ----
    	if exists (select true from sl_subscribe SUB1
    				where SUB1.sub_set = p_new_set_id
    				and SUB1.sub_receiver not in (select SUB2.sub_receiver
    						from sl_subscribe SUB2
    						where SUB2.sub_set = v_old_set_id))
    	then
    		raise exception 'Slony-I: subscriber lists of set % and % are different',
    				p_new_set_id, v_old_set_id;
    	end if;

    	if exists (select true from sl_subscribe SUB1
    				where SUB1.sub_set = v_old_set_id
    				and SUB1.sub_receiver not in (select SUB2.sub_receiver
    						from sl_subscribe SUB2
    						where SUB2.sub_set = p_new_set_id))
    	then
    		raise exception 'Slony-I: subscriber lists of set % and % are different',
    				v_old_set_id, p_new_set_id;
    	end if;

    	-- ----
    	-- Change the set the sequence belongs to
    	-- ----
    	perform setMoveSequence_int(p_seq_id, p_new_set_id);
    	return  createEvent('_schemadoc', 'SET_MOVE_SEQUENCE',
    			p_seq_id, p_new_set_id);
    end;

## setmovesequence_int( integer, integer )

setmovesequence_int( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

    declare
    	p_seq_id			alias for $1;
    	p_new_set_id		alias for $2;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Move the sequence to the new set
    	-- ----
    	update sl_sequence
    			set seq_set = p_new_set_id
    			where seq_id = p_seq_id;

    	return p_seq_id;
    end;

## setmovetable( integer, integer )

setmovetable( integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

This processes the SET_MOVE_TABLE event. The table is moved to the destination set.

    declare
    	p_tab_id			alias for $1;
    	p_new_set_id		alias for $2;
    	v_old_set_id		int4;
    	v_origin			int4;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Get the tables current set
    	-- ----
    	select tab_set into v_old_set_id from sl_table
    			where tab_id = p_tab_id;
    	if not found then
    		raise exception 'Slony-I: table %d not found', p_tab_id;
    	end if;

    	-- ----
    	-- Check that both sets exist and originate here
    	-- ----
    	if p_new_set_id = v_old_set_id then
    		raise exception 'Slony-I: set ids cannot be identical';
    	end if;
    	select set_origin into v_origin from sl_set
    			where set_id = p_new_set_id;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_new_set_id;
    	end if;
    	if v_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_new_set_id;
    	end if;

    	select set_origin into v_origin from sl_set
    			where set_id = v_old_set_id;
    	if not found then
    		raise exception 'Slony-I: set % not found', v_old_set_id;
    	end if;
    	if v_origin != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: set % does not originate on local node',
    				v_old_set_id;
    	end if;

    	-- ----
    	-- Check that both sets are subscribed by the same set of nodes
    	-- ----
    	if exists (select true from sl_subscribe SUB1
    				where SUB1.sub_set = p_new_set_id
    				and SUB1.sub_receiver not in (select SUB2.sub_receiver
    						from sl_subscribe SUB2
    						where SUB2.sub_set = v_old_set_id))
    	then
    		raise exception 'Slony-I: subscriber lists of set % and % are different',
    				p_new_set_id, v_old_set_id;
    	end if;

    	if exists (select true from sl_subscribe SUB1
    				where SUB1.sub_set = v_old_set_id
    				and SUB1.sub_receiver not in (select SUB2.sub_receiver
    						from sl_subscribe SUB2
    						where SUB2.sub_set = p_new_set_id))
    	then
    		raise exception 'Slony-I: subscriber lists of set % and % are different',
    				v_old_set_id, p_new_set_id;
    	end if;

    	-- ----
    	-- Change the set the table belongs to
    	-- ----
    	perform createEvent('_schemadoc', 'SYNC', NULL);
    	perform setMoveTable_int(p_tab_id, p_new_set_id);
    	return  createEvent('_schemadoc', 'SET_MOVE_TABLE',
    			p_tab_id, p_new_set_id);
    end;

## setmovetable_int( integer, integer )

setmovetable_int( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

    declare
    	p_tab_id			alias for $1;
    	p_new_set_id		alias for $2;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Move the table to the new set
    	-- ----
    	update sl_table
    			set tab_set = p_new_set_id
    			where tab_id = p_tab_id;

    	return p_tab_id;
    end;

## setsessionrole( name, text )

setsessionrole( name, text )

### Function Properties

Language Return Type C text

not yet documented

    _Slony_I_setSessionRole

## slon_quote_brute( text )

slon_quote_brute( text )

### Function Properties

Language Return Type PLPGSQL text

Brutally quote the given text

    declare
        p_tab_fqname alias for $1;
        v_fqname text default '';
    begin
        v_fqname := '"' || replace(p_tab_fqname,'"','\\"') || '"';
        return v_fqname;
    end;

## slon_quote_input( text )

slon_quote_input( text )

### Function Properties

Language Return Type PLPGSQL text

quote all words that aren't quoted yet

    declare
        p_tab_fqname alias for $1;
        v_temp_fqname text default '';
        v_pre_quoted text[] default '{}';
        v_pre_quote_counter smallint default 0;
        v_count_fqname smallint default 0;
        v_fqname_split text[];
        v_quoted_fqname text default '';
    begin
        v_temp_fqname := p_tab_fqname;

        LOOP
    	v_pre_quote_counter := v_pre_quote_counter + 1;
    	v_pre_quoted[v_pre_quote_counter] :=
    	    substring(v_temp_fqname from '%#""%"#"%' for '#');
    	IF v_pre_quoted[v_pre_quote_counter] <> '' THEN
    	    v_temp_fqname := replace(v_temp_fqname,
    	        v_pre_quoted[v_pre_quote_counter], '@' ||
    		v_pre_quote_counter);
    	ELSE
    	    EXIT;
    	END IF;
        END LOOP;

        v_fqname_split := string_to_array(v_temp_fqname , '.');
        v_count_fqname := array_upper (v_fqname_split, 1);

        FOR i in 1..v_count_fqname LOOP
            IF substring(v_fqname_split[i],1,1) = '@' THEN
                v_quoted_fqname := v_quoted_fqname ||
    		v_pre_quoted[substring (v_fqname_split[i] from 2)::int];
            ELSE
                v_quoted_fqname := v_quoted_fqname || '"' ||
    		v_fqname_split[i] || '"';
            END IF;

            IF i < v_count_fqname THEN
                v_quoted_fqname := v_quoted_fqname || '.' ;
            END IF;
        END LOOP;

        return v_quoted_fqname;
    end;

## slonyversion( )

slonyversion( )

### Function Properties

Language Return Type PLPGSQL text

Returns the version number of the slony schema

    begin
    	return ''	|| slonyVersionMajor() || '.'
    				|| slonyVersionMinor() || '.'
    				|| slonyVersionPatchlevel();
    end;

## slonyversionmajor( )

slonyversionmajor( )

### Function Properties

Language Return Type PLPGSQL integer

Returns the major version number of the slony schema

    begin
    	return 1;
    end;

## slonyversionminor( )

slonyversionminor( )

### Function Properties

Language Return Type PLPGSQL integer

Returns the minor version number of the slony schema

    begin
    	return 1;
    end;

## slonyversionpatchlevel( )

slonyversionpatchlevel( )

### Function Properties

Language Return Type PLPGSQL integer

Returns the version patch level of the slony schema

    begin
    	return 0;
    end;

## storelisten( integer, integer, integer )

storelisten( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    declare
    	p_origin		alias for $1;
    	p_provider	alias for $2;
    	p_receiver	alias for $3;
    begin
    	return -1;

    	perform storeListen_int (p_origin, p_provider, p_receiver);
    	return  createEvent ('_schemadoc', 'STORE_LISTEN',
    			p_origin, p_provider, p_receiver);
    end;

## storelisten_int( integer, integer, integer )

storelisten_int( integer, integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_config_lock;

    	select 1 into v_exists
    			from 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 sl_node
    						where no_id = p_li_origin) then
    			perform storeNode_int (p_li_origin, '<event pending>', 'f');
    		end if;
    		if not exists (select 1 from sl_node
    						where no_id = p_li_provider) then
    			perform storeNode_int (p_li_provider, '<event pending>', 'f');
    		end if;
    		if not exists (select 1 from sl_node
    						where no_id = p_li_receiver) then
    			perform storeNode_int (p_li_receiver, '<event pending>', 'f');
    		end if;

    		insert into sl_listen
    				(li_origin, li_provider, li_receiver) values
    				(p_li_origin, p_li_provider, p_li_receiver);
    	end if;

    	return 0;
    end;

## storenode( integer, text, boolean )

storenode( integer, text, boolean )

### Function Properties

Language Return Type PLPGSQL bigint

no_id - Node ID # no_comment - Human-oriented comment no_spool - Flag for virtual spool nodes

Generate the STORE_NODE event for node no_id

    declare
    	p_no_id			alias for $1;
    	p_no_comment	alias for $2;
    	p_no_spool		alias for $3;
    	v_no_spool_txt	text;
    begin
    	if p_no_spool then
    		v_no_spool_txt = 't';
    	else
    		v_no_spool_txt = 'f';
    	end if;
    	perform storeNode_int (p_no_id, p_no_comment, p_no_spool);
    	return  createEvent('_schemadoc', 'STORE_NODE',
    									p_no_id, p_no_comment, v_no_spool_txt);
    end;

## storenode_int( integer, text, boolean )

storenode_int( integer, text, boolean )

### Function Properties

Language Return Type PLPGSQL integer

no_id - Node ID # no_comment - Human-oriented comment no_spool - Flag for virtual spool nodes

Internal function to process the STORE_NODE event for node no_id

    declare
    	p_no_id			alias for $1;
    	p_no_comment	alias for $2;
    	p_no_spool		alias for $3;
    	v_old_row		record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Check if the node exists
    	-- ----
    	select * into v_old_row
    			from sl_node
    			where no_id = p_no_id
    			for update;
    	if found then
    		-- ----
    		-- Node exists, update the existing row.
    		-- ----
    		update sl_node
    				set no_comment = p_no_comment,
    				no_spool = p_no_spool
    				where no_id = p_no_id;
    	else
    		-- ----
    		-- New node, insert the sl_node row
    		-- ----
    		insert into sl_node
    				(no_id, no_active, no_comment, no_spool) values
    				(p_no_id, 'f', p_no_comment, p_no_spool);
    	end if;

    	return p_no_id;
    end;

## storepath( integer, integer, text, integer )

storepath( integer, integer, text, integer )

### Function Properties

Language Return Type PLPGSQL bigint

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

    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 storePath_int(p_pa_server, p_pa_client,
    			p_pa_conninfo, p_pa_connretry);
    	return  createEvent('_schemadoc', 'STORE_PATH',
    			p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry);
    end;

## storepath_int( integer, integer, text, integer )

storepath_int( integer, integer, text, integer )

### Function Properties

Language Return Type PLPGSQL integer

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

    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 sl_config_lock;

    	-- ----
    	-- Check if the path already exists
    	-- ----
    	select 1 into v_dummy
    			from 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 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 sl_node
    						where no_id = p_pa_server) then
    			perform storeNode_int (p_pa_server, '<event pending>', 'f');
    		end if;
    		if not exists (select 1 from sl_node
    						where no_id = p_pa_client) then
    			perform storeNode_int (p_pa_client, '<event pending>', 'f');
    		end if;
    		insert into 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;

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	return 0;
    end;

## storeset( integer, text )

storeset( integer, text )

### Function Properties

Language Return Type PLPGSQL bigint

Generate STORE_SET event for set set_id with human readable comment set_comment

    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 sl_config_lock;

    	v_local_node_id := getLocalNodeId('_schemadoc');

    	insert into sl_set
    			(set_id, set_origin, set_comment) values
    			(p_set_id, v_local_node_id, p_set_comment);

    	return createEvent('_schemadoc', 'STORE_SET',
    			p_set_id, v_local_node_id, p_set_comment);
    end;

## storeset_int( integer, integer, text )

storeset_int( integer, integer, text )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_config_lock;

    	select 1 into v_dummy
    			from sl_set
    			where set_id = p_set_id
    			for update;
    	if found then
    		update sl_set
    				set set_comment = p_set_comment
    				where set_id = p_set_id;
    	else
    		if not exists (select 1 from sl_node
    						where no_id = p_set_origin) then
    			perform storeNode_int (p_set_origin, '<event pending>', 'f');
    		end if;
    		insert into 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;

## storetrigger( integer, name )

storetrigger( integer, name )

### Function Properties

Language Return Type PLPGSQL bigint

storeTrigger (trig_tabid, trig_tgname)

Submits STORE_TRIGGER event to indicate that trigger trig_tgname on replicated table trig_tabid will NOT be disabled.

    declare
    	p_trig_tabid		alias for $1;
    	p_trig_tgname		alias for $2;
    begin
    	perform storeTrigger_int(p_trig_tabid, p_trig_tgname);
    	return  createEvent('_schemadoc', 'STORE_TRIGGER',
    			p_trig_tabid, p_trig_tgname);
    end;

## storetrigger_int( integer, name )

storetrigger_int( integer, name )

### Function Properties

Language Return Type PLPGSQL integer

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.

    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 sl_config_lock;

    	-- ----
    	-- Get the current table status (altered or not)
    	-- ----
    	select tab_altered into v_tab_altered
    			from 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 alterTableRestore(p_trig_tabid);
    	end if;

    	-- ----
    	-- Make sure that an entry for this trigger exists
    	-- ----
    	delete from sl_trigger
    			where trig_tabid = p_trig_tabid
    			  and trig_tgname = p_trig_tgname;
    	insert into 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 alterTableForReplication(p_trig_tabid);
    	end if;

    	return p_trig_tabid;
    end;

## subscribeset( integer, integer, integer, boolean )

subscribeset( integer, integer, integer, boolean )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    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_ev_seqno			int8;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Check that this is called on the receiver node
    	-- ----
    	if p_sub_provider != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: subscribeSet() must be called on provider';
    	end if;

    	-- ----
    	-- Check that the origin and provider of the set are remote
    	-- ----
    	select set_origin into v_set_origin
    			from sl_set
    			where set_id = p_sub_set;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_sub_set;
    	end if;
    	if v_set_origin = p_sub_receiver then
    		raise exception
    				'Slony-I: set origin and receiver cannot be identical';
    	end if;
    	if p_sub_receiver = p_sub_provider then
    		raise exception
    				'Slony-I: set provider and receiver cannot be identical';
    	end if;

    	-- ---
    	-- Check to see if the set contains any tables - gripe if not - bug #1226
    	-- ---
    	if not exists (select true
    		from sl_table
    		where tab_set = p_sub_set) then
    		raise notice 'subscribeSet:: set % has no tables - risk of problems - see bug 1226', p_sub_set;
    		raise notice 'http://gborg.postgresql.org/project/slony1/bugs/bugupdate.php?1226';
    	end if;

    	-- ----
    	-- Create the SUBSCRIBE_SET event
    	-- ----
    	v_ev_seqno :=  createEvent('_schemadoc', 'SUBSCRIBE_SET',
    			p_sub_set, p_sub_provider, p_sub_receiver,
    			case p_sub_forward when true then 't' else 'f' end);

    	-- ----
    	-- Call the internal procedure to store the subscription
    	-- ----
    	perform subscribeSet_int(p_sub_set, p_sub_provider,
    			p_sub_receiver, p_sub_forward);

    	-- ----
    	-- Submit listen management events
    	-- ----
    	perform RebuildListenEntries();

    	return v_ev_seqno;
    end;

## subscribeset_int( integer, integer, integer, boolean )

subscribeset_int( integer, integer, integer, boolean )

### Function Properties

Language Return Type PLPGSQL integer

subscribeSet_int (sub_set, sub_provider, sub_receiver, sub_forward)

Internal actions for subscribing receiver sub_receiver to subscription set sub_set.

    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 sl_config_lock;

    	-- ----
    	-- Provider change is only allowed for active sets
    	-- ----
    	if p_sub_receiver = getLocalNodeId('_schemadoc') then
    		select sub_active into v_sub_row from 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 'Slony-I: set % is not active, cannot change provider',
    						p_sub_set;
    			end if;
    		end if;
    	end if;

    	-- ----
    	-- Try to change provider and/or forward for an existing subscription
    	-- ----
    	update 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 sl_path
    			where pa_server = p_sub_provider
    			and pa_client = p_sub_receiver)
    	then
    		insert into sl_path
    				(pa_server, pa_client, pa_conninfo, pa_connretry)
    				values
    				(p_sub_provider, p_sub_receiver,
    				'<event pending>', 10);
    	end if;
    	insert into 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 sl_set
    			where set_id = p_sub_set;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_sub_set;
    	end if;

    	if v_set_origin = getLocalNodeId('_schemadoc') then
    		perform createEvent('_schemadoc', 'ENABLE_SUBSCRIPTION',
    				p_sub_set, p_sub_provider, p_sub_receiver,
    				case p_sub_forward when true then 't' else 'f' end);
    		perform enableSubscription(p_sub_set,
    				p_sub_provider, p_sub_receiver);
    	end if;

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	return p_sub_set;
    end;

## tableaddkey( text )

tableaddkey( text )

### Function Properties

Language Return Type PLPGSQL text

tableAddKey (tab_fqname) - if the table has not got a column of the form _Slony-I_<clustername>_rowID, then add it as a bigint, defaulted to nextval() for a sequence created for the cluster.

    declare
    	p_tab_fqname	alias for $1;
    	v_tab_fqname_quoted	text default '';
    	v_attkind		text default '';
    	v_attrow		record;
    	v_have_serial	bool default 'f';
    begin
    	v_tab_fqname_quoted := slon_quote_input(p_tab_fqname);
    	--
    	-- Loop over the attributes of this relation
    	-- and add a "v" for every user column, and a "k"
    	-- if we find the Slony-I special serial column.
    	--
    	for v_attrow in select PGA.attnum, PGA.attname
    			from "pg_catalog".pg_class PGC,
    			    "pg_catalog".pg_namespace PGN,
    				"pg_catalog".pg_attribute PGA
    			where slon_quote_brute(PGN.nspname) || '.' ||
    			    slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    				and PGN.oid = PGC.relnamespace
    				and PGA.attrelid = PGC.oid
    				and not PGA.attisdropped
    				and PGA.attnum > 0
    			order by attnum
    	loop
    		if v_attrow.attname = '_Slony-I_schemadoc_rowID' then
    		    v_attkind := v_attkind || 'k';
    			v_have_serial := 't';
    		else
    			v_attkind := v_attkind || 'v';
    		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 'Slony-I: table % not found', v_tab_fqname_quoted;
    	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 'lock table ' || v_tab_fqname_quoted ||
    			' in access exclusive mode';
    		execute 'alter table only ' || v_tab_fqname_quoted ||
    			' add column "_Slony-I_schemadoc_rowID" bigint;';
    		execute 'alter table only ' || v_tab_fqname_quoted ||
    			' alter column "_Slony-I_schemadoc_rowID" ' ||
    			' set default "pg_catalog".nextval(''sl_rowid_seq'');';

    		v_attkind := v_attkind || 'k';
    	end if;

    	--
    	-- Return the resulting Slony-I attkind
    	--
    	return v_attkind;
    end;

## tabledropkey( integer )

tabledropkey( integer )

### Function Properties

Language Return Type PLPGSQL integer

tableDropKey (tab_id)

If the specified table has a column "_Slony-I_<clustername>_rowID", then drop it.

    declare
    	p_tab_id		alias for $1;
    	v_tab_fqname	text;
    	v_tab_oid		oid;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- Construct the tables fully qualified name and get its oid
    	-- ----
    	select slon_quote_brute(PGN.nspname) || '.' ||
    				slon_quote_brute(PGC.relname),
    				PGC.oid into v_tab_fqname, v_tab_oid
    			from sl_table T,
    				"pg_catalog".pg_class PGC,
    				"pg_catalog".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 'Slony-I: table with ID % not found', p_tab_id;
    	end if;

    	-- ----
    	-- Drop the special serial ID column if the table has it
    	-- ----
    	if exists (select true from "pg_catalog".pg_attribute
    			where attrelid = v_tab_oid
    				and attname = '_Slony-I_schemadoc_rowID')
    	then
    		execute 'lock table ' || v_tab_fqname ||
    				' in access exclusive mode';
    		execute 'alter table ' || v_tab_fqname ||
    				' drop column "_Slony-I_schemadoc_rowID"';
    	end if;

    	return p_tab_id;
    end;

## tablehasserialkey( text )

tablehasserialkey( text )

### Function Properties

Language Return Type PLPGSQL boolean

tableHasSerialKey (tab_fqname)

Checks if a table has our special serial key column that is used if the table has no natural unique constraint.

    declare
    	p_tab_fqname	alias for $1;
    	v_tab_fqname_quoted	text default '';
    	v_attnum		int2;
    begin
    	v_tab_fqname_quoted := slon_quote_input(p_tab_fqname);
    	select PGA.attnum into v_attnum
    			from "pg_catalog".pg_class PGC,
    				"pg_catalog".pg_namespace PGN,
    				"pg_catalog".pg_attribute PGA
    			where slon_quote_brute(PGN.nspname) || '.' ||
    				slon_quote_brute(PGC.relname) = v_tab_fqname_quoted
    				and PGC.relnamespace = PGN.oid
    				and PGA.attrelid = PGC.oid
    				and PGA.attname = '_Slony-I_schemadoc_rowID'
    				and not PGA.attisdropped;
    	return found;
    end;

## terminatenodeconnections( name )

terminatenodeconnections( name )

### Function Properties

Language Return Type C integer

terminates connections to the node and terminates the process

    _Slony_I_terminateNodeConnections

## uninstallnode( )

uninstallnode( )

### Function Properties

Language Return Type PLPGSQL integer

Reset the whole database to standalone by removing the whole replication system.

    declare
    	v_tab_row		record;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- This is us ... time for suicide! Restore all tables to
    	-- their original status.
    	-- ----
    	for v_tab_row in select * from sl_table loop
    		perform alterTableRestore(v_tab_row.tab_id);
    		perform tableDropKey(v_tab_row.tab_id);
    	end loop;

    	raise notice 'Slony-I: Please drop schema "_schemadoc"';
    	return 0;
    end;

## unlockset( integer )

unlockset( integer )

### Function Properties

Language Return Type PLPGSQL integer

Remove the special trigger from all tables of a set that disables access to it.

    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 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 := getLocalNodeId('_schemadoc');
    	select * into v_set_row from sl_set
    			where set_id = p_set_id
    			for update;
    	if not found then
    		raise exception 'Slony-I: set % not found', p_set_id;
    	end if;
    	if v_set_row.set_origin <> v_local_node_id then
    		raise exception 'Slony-I: set % does not originate on local node',
    				p_set_id;
    	end if;
    	if v_set_row.set_locked isnull then
    		raise exception 'Slony-I: set % is not locked', p_set_id;
    	end if;

    	-- ----
    	-- Drop the lockedSet trigger from all tables in the set.
    	-- ----
    	for v_tab_row in select T.tab_id,
    			slon_quote_brute(PGN.nspname) || '.' ||
    			slon_quote_brute(PGC.relname) as tab_fqname
    			from sl_table T,
    				"pg_catalog".pg_class PGC, "pg_catalog".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 'drop trigger "_schemadoc_lockedset_' ||
    				v_tab_row.tab_id || '" on ' || v_tab_row.tab_fqname;
    	end loop;

    	-- ----
    	-- Clear out the set_locked field
    	-- ----
    	update sl_set
    			set set_locked = NULL
    			where set_id = p_set_id;

    	return p_set_id;
    end;

## unsubscribeset( integer, integer )

unsubscribeset( integer, integer )

### Function Properties

Language Return Type PLPGSQL bigint

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.

    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 sl_config_lock;

    	-- ----
    	-- Check that this is called on the receiver node
    	-- ----
    	if p_sub_receiver != getLocalNodeId('_schemadoc') then
    		raise exception 'Slony-I: unsubscribeSet() must be called on receiver';
    	end if;

    	-- ----
    	-- Check that this does not break any chains
    	-- ----
    	if exists (select true from sl_subscribe
    			where sub_set = p_sub_set
    				and sub_provider = p_sub_receiver)
    	then
    		raise exception 'Slony-I: Cannot unsubscibe set % while being provider',
    				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 sl_table
    			where tab_set = p_sub_set
    			order by tab_id
    	loop
    		perform alterTableRestore(v_tab_row.tab_id);
    		perform 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 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 sl_table
    			where tab_set = p_sub_set;
    	delete from sl_sequence
    			where seq_set = p_sub_set;

    	-- ----
    	-- Call the internal procedure to drop the subscription
    	-- ----
    	perform unsubscribeSet_int(p_sub_set, p_sub_receiver);

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	-- ----
    	-- Create the UNSUBSCRIBE_SET event
    	-- ----
    	return  createEvent('_schemadoc', 'UNSUBSCRIBE_SET',
    			p_sub_set, p_sub_receiver);
    end;

## unsubscribeset_int( integer, integer )

unsubscribeset_int( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

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.

    declare
    	p_sub_set			alias for $1;
    	p_sub_receiver		alias for $2;
    begin
    	-- ----
    	-- Grab the central configuration lock
    	-- ----
    	lock table sl_config_lock;

    	-- ----
    	-- All the real work is done before event generation on the
    	-- subscriber.
    	-- ----
    	delete from sl_subscribe
    			where sub_set = p_sub_set
    				and sub_receiver = p_sub_receiver;

    	-- Rewrite sl_listen table
    	perform RebuildListenEntries();

    	return p_sub_set;
    end;

## updaterelname( integer, integer )

updaterelname( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

updateRelname(set_id, only_on_node)

    declare
            p_set_id                alias for $1;
            p_only_on_node          alias for $2;
            v_no_id                 int4;
            v_set_origin            int4;
    begin
            -- ----
            -- Grab the central configuration lock
            -- ----
            lock table sl_config_lock;

            -- ----
            -- Check that we either are the set origin or a current
            -- subscriber of the set.
            -- ----
            v_no_id := getLocalNodeId('_schemadoc');
            select set_origin into v_set_origin
                            from sl_set
                            where set_id = p_set_id
                            for update;
            if not found then
                    raise exception 'Slony-I: set % not found', p_set_id;
            end if;
            if v_set_origin <> v_no_id
                    and not exists (select 1 from 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 > 0 and p_only_on_node <> v_no_id then
                    return 0;
            end if;
            update sl_table set
                    tab_relname = PGC.relname, tab_nspname = PGN.nspname
                    from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN
                    where sl_table.tab_reloid = PGC.oid
                            and PGC.relnamespace = PGN.oid;
            update sl_sequence set
                    seq_relname = PGC.relname, seq_nspname = PGN.nspname
                    from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN
                    where sl_sequence.seq_reloid = PGC.oid
                    and PGC.relnamespace = PGN.oid;
            return p_set_id;
    end;

## updatereloid( integer, integer )

updatereloid( integer, integer )

### Function Properties

Language Return Type PLPGSQL integer

updateReloid(set_id, only_on_node)

Updates the respective reloids in sl_table and sl_seqeunce based on their respective FQN

    declare
            p_set_id                alias for $1;
            p_only_on_node          alias for $2;
            v_no_id                 int4;
            v_set_origin            int4;
    begin
            -- ----
            -- Grab the central configuration lock
            -- ----
            lock table sl_config_lock;

            -- ----
            -- Check that we either are the set origin or a current
            -- subscriber of the set.
            -- ----
            v_no_id := getLocalNodeId('_schemadoc');
            select set_origin into v_set_origin
                            from sl_set
                            where set_id = p_set_id
                            for update;
            if not found then
                    raise exception 'Slony-I: set % not found', p_set_id;
            end if;
            if v_set_origin <> v_no_id
                    and not exists (select 1 from 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 > 0 and p_only_on_node <> v_no_id then
                    return 0;
            end if;
            update sl_table set
                    tab_reloid = PGC.oid
                    from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN
                    where slon_quote_brute(sl_table.tab_relname) = slon_quote_brute(PGC.relname)
                            and PGC.relnamespace = PGN.oid
    			and slon_quote_brute(PGN.nspname) = slon_quote_brute(sl_table.tab_nspname);

            update sl_sequence set
                    seq_reloid = PGC.oid
                    from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN
                    where slon_quote_brute(sl_sequence.seq_relname) = slon_quote_brute(PGC.relname)
                    	and PGC.relnamespace = PGN.oid
    			and slon_quote_brute(PGN.nspname) = slon_quote_brute(sl_sequence.seq_nspname);

            return  createEvent('_schemadoc', 'RESET_CONFIG',
                            p_set_id, p_only_on_node);
    end;

## upgradeschema( text )

upgradeschema( text )

### Function Properties

Language Return Type PLPGSQL text

Called during "update functions" by slonik to perform schema changes

    declare
            p_old   alias for $1;
    begin
    	-- upgrade sl_table
    	if p_old = '1.0.2' or p_old = '1.0.5' then
    		-- Add new column(s) sl_table.tab_relname, sl_table.tab_nspname
    		execute 'alter table sl_table add column tab_relname name';
    		execute 'alter table sl_table add column tab_nspname name';

    		-- populate the colums with data
    		update sl_table set
    			tab_relname = PGC.relname, tab_nspname = PGN.nspname
    			from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN
    			where sl_table.tab_reloid = PGC.oid
    			and PGC.relnamespace = PGN.oid;

    		-- constrain the colums
    		execute 'alter table sl_table alter column tab_relname set NOT NULL';
    		execute 'alter table sl_table alter column tab_nspname set NOT NULL';

    	end if;

    	-- upgrade sl_sequence
    	if p_old = '1.0.2' or p_old = '1.0.5' then
    		-- Add new column(s) sl_sequence.seq_relname, sl_sequence.seq_nspname
    		execute 'alter table sl_sequence add column seq_relname name';
    		execute 'alter table sl_sequence add column seq_nspname name';

    		-- populate the columns with data
    		update sl_sequence set
    			seq_relname = PGC.relname, seq_nspname = PGN.nspname
    			from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN
    			where sl_sequence.seq_reloid = PGC.oid
    			and PGC.relnamespace = PGN.oid;

    		-- constrain the data
    		execute 'alter table sl_sequence alter column seq_relname set NOT NULL';
    		execute 'alter table sl_sequence alter column seq_nspname set NOT NULL';
    	end if;

    	-- ----
    	-- Changes from 1.0.x to 1.1.0
    	-- ----
    	if p_old = '1.0.2' or p_old = '1.0.5' then
    		-- Add new column sl_node.no_spool for virtual spool nodes
    		execute 'alter table sl_node add column no_spool boolean';
    		update sl_node set no_spool = false;
    	end if;
    	return p_old;
    end;