Dumped on 2004-09-29

Index of database - schemadoc


Table: sl_config_lock

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

sl_config_lock Structure
F-Key Name Type Description
dummy integer

Index - Schema schemadoc


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.

sl_confirm Structure
F-Key Name Type Description
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

Index - Schema schemadoc


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.

sl_event Structure
F-Key Name Type Description
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 schemadoc.xxid

Earliest XID on provider node for this event
ev_maxxid schemadoc.xxid

Latest XID on provider node for this event
ev_xip text

TBD
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 = FAILOVER_SET = SUBSCRIBE_SET = ENABLE_SUBSCRIPTION = UNSUBSCRIBE_SET = DDL_SCRIPT = ADJUST_SEQ =
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

Index - Schema schemadoc


Table: sl_listen

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

sl_listen Structure
F-Key Name Type Description
sl_node.no_id li_origin integer PRIMARY KEY

The ID # (from sl_node.no_id) of the node this listener is operating on
sl_path.pa_server#1 li_provider integer PRIMARY KEY

The ID # (from sl_node.no_id) of the source node for this listening event
sl_path.pa_client#1 li_receiver integer PRIMARY KEY

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

Index - Schema schemadoc


Table: sl_log_1

Stores each change to be propagated to subscriber nodes

sl_log_1 Structure
F-Key Name Type Description
log_origin integer

Origin node from which the change came
log_xid schemadoc.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

Index - Schema schemadoc


Table: sl_log_2

Stores each change to be propagated to subscriber nodes

sl_log_2 Structure
F-Key Name Type Description
log_origin integer

Origin node from which the change came
log_xid schemadoc.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

Index - Schema schemadoc


Table: sl_node

Holds the list of nodes associated with this namespace.

sl_node Structure
F-Key Name Type Description
no_id integer PRIMARY KEY

The unique ID number for the node
no_active boolean
no_comment text

A human-oriented description of the node

Tables referencing this one via Foreign Key Constraints:

Index - Schema schemadoc


Table: sl_path

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

sl_path Structure
F-Key Name Type Description
sl_node.no_id pa_server integer PRIMARY KEY

The Node ID # (from sl_node.no_id) of the data source
sl_node.no_id pa_client integer PRIMARY KEY

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 this one via Foreign Key Constraints:

Index - Schema schemadoc


View: sl_seqlastvalue

sl_seqlastvalue Structure
F-Key Name Type Description
seq_id integer
seq_set integer
seq_reloid oid
seq_origin integer
seq_last_value bigint
SELECT sq.seq_id
, sq.seq_set
, sq.seq_reloid
, s.set_origin AS seq_origin
, schemadoc.sequencelastvalue
(
     (
           (quote_ident
                 (
                       (pgn.nspname)::text
                 ) || '.'::text
           ) || quote_ident
           (
                 (pgc.relname)::text
           )
     )
) AS seq_last_value 
FROM schemadoc.sl_sequence sq
, schemadoc.sl_set s
, pg_class pgc
, pg_namespace pgn 
WHERE (
     (
           (s.set_id = sq.seq_set)
         AND (pgc.oid = sq.seq_reloid)
     )
   AND (pgn.oid = pgc.relnamespace)
);

Index - Schema schemadoc


Table: sl_seqlog

Log of Sequence updates

sl_seqlog Structure
F-Key Name Type Description
seql_seqid integer

Sequence ID
seql_origin integer

Publisher node at which the sequence originates
seql_ev_seqno bigint

TBD
seql_last_value bigint

Last value published for this sequence

Index - Schema schemadoc


Table: sl_sequence

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

sl_sequence Structure
F-Key Name Type Description
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
sl_set.set_id seq_set integer

Indicates which replication set the object is in
seq_comment text

A human-oriented comment

Index - Schema schemadoc


Table: sl_set

Holds definitions of replication sets.

sl_set Structure
F-Key Name Type Description
set_id integer PRIMARY KEY

A unique ID number for the set.
sl_node.no_id set_origin integer

The ID number of the source node for the replication set.
set_locked schemadoc.xxid

Indicates whether or not the set is locked.
set_comment text

A human-oriented description of the set.

Tables referencing this one via Foreign Key Constraints:

Index - Schema schemadoc


Table: sl_setsync

Not documented yet

sl_setsync Structure
F-Key Name Type Description
sl_set.set_id ssy_setid integer PRIMARY KEY

ID number of the replication set
sl_node.no_id ssy_origin integer

ID number of the node
ssy_seqno bigint

Slony-I sequence number
ssy_minxid schemadoc.xxid

Earliest XID in provider system affected by SYNC
ssy_maxxid schemadoc.xxid

Latest XID in provider system affected by SYNC
ssy_xip text

TBD
ssy_action_list text

TBD

Index - Schema schemadoc


Table: sl_subscribe

Holds a list of subscriptions on sets

sl_subscribe Structure
F-Key Name Type Description
sl_set.set_id sub_set integer PRIMARY KEY

ID # (from sl_set) of the set being subscribed to
sl_path.pa_server#1 sub_provider integer

ID# (from sl_node) of the node providing data
sl_path.pa_client#1 sub_receiver integer PRIMARY KEY

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

Index - Schema schemadoc


Table: sl_table

Holds information about the tables being replicated.

sl_table Structure
F-Key Name Type Description
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
sl_set.set_id tab_set integer
tab_idxname name NOT NULL

The name of the primary index of the table
tab_altered boolean NOT NULL
tab_comment text

Human-oriented description of the table

Tables referencing this one via Foreign Key Constraints:

Index - Schema schemadoc


Table: sl_trigger

Holds information about triggers on tables managed using Slony-I

sl_trigger Structure
F-Key Name Type Description
sl_table.tab_id trig_tabid integer PRIMARY KEY

Slony-I ID number of table the trigger is on
trig_tgname name PRIMARY KEY

Indicates the name of a trigger

Index - Schema schemadoc


Function: altertableforreplication( integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Get our local node ID
	-- ----
	v_no_id := schemadoc.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,
			"pg_catalog".quote_ident(PGN.nspname) || '.' ||
			"pg_catalog".quote_ident(PGC.relname) as tab_fqname
			into v_tab_row
			from schemadoc.sl_table T, schemadoc.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 := schemadoc.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
				schemadoc.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 schemadoc.sl_table TAB,
								schemadoc.sl_trigger TRIG
								where TAB.tab_reloid = tgrelid
								and TAB.tab_id = TRIG.trig_tabid
								and TRIG.trig_tgname = tgname
					);
		get diagnostics v_n = row_count;
		if v_n > 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
				schemadoc.denyAccess (''_schemadoc'');';
	end if;

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

	return p_tab_id;
end;

Function: altertablerestore( integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Get our local node ID
	-- ----
	v_no_id := schemadoc.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,
			"pg_catalog".quote_ident(PGN.nspname) || '.' ||
			"pg_catalog".quote_ident(PGC.relname) as tab_fqname
			into v_tab_row
			from schemadoc.sl_table T, schemadoc.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 schemadoc.sl_table
			set tab_altered = false where tab_id = p_tab_id;

	return p_tab_id;
end;

Function: cleanupevent( )

Returns: integer

Language: PLPGSQL

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 schemadoc.sl_confirm
				where con_origin not in (select no_id from schemadoc.sl_node);
	delete from schemadoc.sl_confirm
				where con_received not in (select no_id from schemadoc.sl_node);
	-- ----
	-- Next remove all but the oldest confirm row per origin,receiver pair.
	-- Ignore confirmations that are younger than 10 minutes. We currently
	-- have an not confirmed suspicion that a possibly lost transaction due
	-- to a server crash might have been visible to another session, and
	-- that this led to log data that is needed again got removed.
	-- ----
	for v_max_row in select con_origin, con_received, max(con_seqno) as con_seqno
				from schemadoc.sl_confirm
				where con_timestamp < (CURRENT_TIMESTAMP - '10 min'::interval)
				group by con_origin, con_received
	loop
		delete from schemadoc.sl_confirm
				where con_origin = v_max_row.con_origin
				and con_received = v_max_row.con_received
				and con_seqno < v_max_row.con_seqno;
	end loop;

	-- ----
	-- Then remove all events that are confirmed by all nodes in the
	-- whole cluster up to the last SYNC
	-- ----
	for v_min_row in select con_origin, min(con_seqno) as con_seqno
				from schemadoc.sl_confirm
				group by con_origin
	loop
		select coalesce(max(ev_seqno), 0) into v_max_sync
				from schemadoc.sl_event
				where ev_origin = v_min_row.con_origin
				and ev_seqno <= v_min_row.con_seqno
				and ev_type = 'SYNC';
		if v_max_sync > 0 then
			delete from schemadoc.sl_event
					where ev_origin = v_min_row.con_origin
					and ev_seqno < v_max_sync;
		end if;
	end loop;

	return 0;
end;

Function: ddlscript( integer, text, integer )

Returns: bigint

Language: PLPGSQL

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

	-- ----
	-- Check that the set exists and originates here
	-- ----
	select set_origin into v_set_origin
			from schemadoc.sl_set
			where set_id = p_set_id
			for update;
	if not found then
		raise exception 'Slony-I: set % not found', p_set_id;
	end if;
	if v_set_origin <> schemadoc.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 schemadoc.createEvent('_schemadoc', 'SYNC', NULL);
	perform schemadoc.ddlScript_int(p_set_id, p_script, p_only_on_node);
	return  schemadoc.createEvent('_schemadoc', 'DDL_SCRIPT', 
			p_set_id, p_script, p_only_on_node);
end;

Function: ddlscript_int( integer, text, integer )

Returns: integer

Language: PLPGSQL

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

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

	-- ----
	-- If execution on only one node is requested, check that
	-- we are that node.
	-- ----
	if p_only_on_node > 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 schemadoc.sl_table
			where tab_set = p_set_id
	loop
		perform schemadoc.alterTableRestore(v_row.tab_id);
	end loop;

	-- ----
	-- Run the script
	-- ----
	execute p_script;

	-- ----
	-- Put all tables back into replicated mode
	-- ----
	for v_row in select * from schemadoc.sl_table
			where tab_set = p_set_id
	loop
		perform schemadoc.alterTableForReplication(v_row.tab_id);
	end loop;

	return p_set_id;
end;

Function: determineattkindserial( text )

Returns: text

Language: PLPGSQL

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_attkind		text default '';
	v_attrow		record;
	v_have_serial	bool default 'f';
begin
	--
	-- 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 "pg_catalog".quote_ident(PGN.nspname) || '.' ||
			    "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname
				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', p_tab_fqname;
	end if;

	--
	-- If it does not have the special serial column, we
	-- should not have been called in the first place.
	--
	if not v_have_serial then
		raise exception 'Slony-I: table % does not have the serial key',
				p_tab_fqname;
	end if;

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

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

Function: determineattkindunique( text, name )

Returns: text

Language: PLPGSQL

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;
	p_idx_name		alias for $2;
	v_idxrow		record;
	v_attrow		record;
	v_i				integer;
	v_attno			int2;
	v_attkind		text default '';
	v_attfound		bool;
begin
	--
	-- 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 "pg_catalog".quote_ident(PGN.nspname) || '.' ||
					"pg_catalog".quote_ident(PGC.relname) = p_tab_fqname
					and PGN.oid = PGC.relnamespace
					and PGX.indrelid = PGC.oid
					and PGX.indexrelid = PGXC.oid
					and PGX.indisunique
					and PGXC.relname = p_idx_name;
		if not found then
			raise exception 'Slony-I: table % has no unique index %',
					p_tab_fqname, p_idx_name;
		end if;
	end if;

	--
	-- Loop over the tables attributes and check if they are
	-- index attributes. If so, add a "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 "pg_catalog".quote_ident(PGN.nspname) || '.' ||
			    "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname
				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 = 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;

Function: determineidxnameserial( text )

Returns: name

Language: PLPGSQL

determineIdxnameSerial (tab_fqname) Given a tablename, construct the index name of the serial column.

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

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

Function: determineidxnameunique( text, name )

Returns: name

Language: PLPGSQL

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;
	p_idx_name		alias for $2;
	v_idxrow		record;
begin
	--
	-- Lookup the tables primary key or the specified unique index
	--
	if p_idx_name isnull then
		select PGXC.relname
				into v_idxrow
				from "pg_catalog".pg_class PGC,
					"pg_catalog".pg_namespace PGN,
					"pg_catalog".pg_index PGX,
					"pg_catalog".pg_class PGXC
				where "pg_catalog".quote_ident(PGN.nspname) || '.' ||
					"pg_catalog".quote_ident(PGC.relname) = p_tab_fqname
					and PGN.oid = PGC.relnamespace
					and PGX.indrelid = PGC.oid
					and PGX.indexrelid = PGXC.oid
					and PGX.indisprimary;
		if not found then
			raise exception 'Slony-I: table % has no primary key',
					p_tab_fqname;
		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 "pg_catalog".quote_ident(PGN.nspname) || '.' ||
					"pg_catalog".quote_ident(PGC.relname) = p_tab_fqname
					and PGN.oid = PGC.relnamespace
					and PGX.indrelid = PGC.oid
					and PGX.indexrelid = PGXC.oid
					and PGX.indisunique
					and PGXC.relname = p_idx_name;
		if not found then
			raise exception 'Slony-I: table % has no unique index %',
					p_tab_fqname, p_idx_name;
		end if;
	end if;

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

Function: disablenode( integer )

Returns: bigint

Language: PLPGSQL

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;

Function: disablenode_int( integer )

Returns: integer

Language: PLPGSQL

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

Function: droplisten( integer, integer, integer )

Returns: bigint

Language: PLPGSQL

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
	perform schemadoc.dropListen_int(p_li_origin, 
			p_li_provider, p_li_receiver);
	
	return  schemadoc.createEvent ('_schemadoc', 'DROP_LISTEN',
			p_li_origin, p_li_provider, p_li_receiver);
end;

Function: droplisten_int( integer, integer, integer )

Returns: integer

Language: PLPGSQL

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
	-- ----
	-- Grab the central configuration lock
	-- ----
	lock table schemadoc.sl_config_lock;

	delete from schemadoc.sl_listen
			where li_origin = p_li_origin
			and li_provider = p_li_provider
			and li_receiver = p_li_receiver;
	if found then
		return 1;
	else
		return 0;
	end if;
end;

Function: dropnode( integer )

Returns: bigint

Language: PLPGSQL

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

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

	select * into v_node_row from schemadoc.sl_node
			where no_id = p_no_id
			for update;
	if not found then
		raise exception '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 schemadoc.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 schemadoc.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 schemadoc.dropNode_int(p_no_id);
	return  schemadoc.createEvent('_schemadoc', 'DROP_NODE',
									p_no_id);
end;

Function: dropnode_int( integer )

Returns: integer

Language: PLPGSQL

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

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

		return p_no_id;
	end if;

	-- ----
	-- This is us ... deactivate the node for now, the daemon
	-- will call uninstallNode() in a separate transaction.
	-- ----
	update schemadoc.sl_node
			set no_active = false
			where no_id = p_no_id;

	return p_no_id;
end;

Function: droppath( integer, integer )

Returns: bigint

Language: PLPGSQL

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

	-- ----
	-- There should be no existing subscriptions. Auto unsubscribing
	-- is considered too dangerous. 
	-- ----
	for v_row in select sub_set, sub_provider, sub_receiver
			from schemadoc.sl_subscribe
			where sub_provider = p_pa_server
			and sub_receiver = p_pa_client
	loop
		raise exception 
			'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 schemadoc.sl_listen
			where li_provider = p_pa_server
			and li_receiver = p_pa_client
	loop
		perform schemadoc.dropListen(
				v_row.li_origin, v_row.li_provider, v_row.li_receiver);
	end loop;

	-- ----
	-- Now drop the path and create the event
	-- ----
	perform schemadoc.dropPath_int(p_pa_server, p_pa_client);
	return  schemadoc.createEvent ('_schemadoc', 'DROP_PATH',
			p_pa_server, p_pa_client);
end;

Function: droppath_int( integer, integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Remove any dangling sl_listen entries with the server
	-- as provider and the client as receiver. This must have
	-- been cleared out before, but obviously was not.
	-- ----
	delete from schemadoc.sl_listen
			where li_provider = p_pa_server
			and li_receiver = p_pa_client;

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

	if found then
		return 1;
	else
		return 0;
	end if;
end;

Function: dropset( integer )

Returns: bigint

Language: PLPGSQL

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 schemadoc.sl_config_lock;
	
	-- ----
	-- Check that the set exists and originates here
	-- ----
	select set_origin into v_origin from schemadoc.sl_set
			where set_id = p_set_id;
	if not found then
		raise exception 'Slony-I: set % not found', p_set_id;
	end if;
	if v_origin != schemadoc.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 schemadoc.dropSet_int(p_set_id);
	return  schemadoc.createEvent('_schemadoc', 'DROP_SET', 
			p_set_id);
end;

Function: dropset_int( integer )

Returns: integer

Language: PLPGSQL

declare
	p_set_id			alias for $1;
	v_tab_row			record;
begin
	-- ----
	-- Grab the central configuration lock
	-- ----
	lock table schemadoc.sl_config_lock;
	
	-- ----
	-- Restore all tables original triggers and rules and remove
	-- our replication stuff.
	-- ----
	for v_tab_row in select tab_id from schemadoc.sl_table
			where tab_set = p_set_id
			order by tab_id
	loop
		perform schemadoc.alterTableRestore(v_tab_row.tab_id);
		perform schemadoc.tableDropKey(v_tab_row.tab_id);
	end loop;

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

	return p_set_id;
end;

Function: droptrigger( integer, name )

Returns: bigint

Language: PLPGSQL

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 schemadoc.dropTrigger_int(p_trig_tabid, p_trig_tgname);
	return  schemadoc.createEvent('_schemadoc', 'DROP_TRIGGER',
			p_trig_tabid, p_trig_tgname);
end;

Function: droptrigger_int( integer, name )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Get the current table status (altered or not)
	-- ----
	select tab_altered into v_tab_altered
			from schemadoc.sl_table where tab_id = p_trig_tabid;
	if not found then
		-- ----
		-- Not found is no hard error here, because that might
		-- mean that we are not subscribed to that set
		-- ----
		return 0;
	end if;

	-- ----
	-- If the table is modified for replication, restore the original state
	-- ----
	if v_tab_altered then
		perform schemadoc.alterTableRestore(p_trig_tabid);
	end if;

	-- ----
	-- Remove the entry from sl_trigger
	-- ----
	delete from schemadoc.sl_trigger
			where trig_tabid = p_trig_tabid
			  and trig_tgname = p_trig_tgname;

	-- ----
	-- Put the table back into replicated state if it was
	-- ----
	if v_tab_altered then
		perform schemadoc.alterTableForReplication(p_trig_tabid);
	end if;

	return p_trig_tabid;
end;

Function: enablenode( integer )

Returns: bigint

Language: PLPGSQL

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

	-- ----
	-- Check that we are the node to activate and that we are
	-- currently disabled.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select * into v_node_row
			from schemadoc.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 schemadoc.enableNode_int (p_no_id);
	return  schemadoc.createEvent('_schemadoc', 'ENABLE_NODE',
									p_no_id);
end;

Function: enablenode_int( integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Check that the node is inactive
	-- ----
	select * into v_node_row
			from schemadoc.sl_node
			where no_id = p_no_id
			for update;
	if not found then 
		raise exception '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 schemadoc.sl_node
			set no_active = 't'
			where no_id = p_no_id;
	insert into schemadoc.sl_confirm
			(con_origin, con_received, con_seqno)
			select no_id, p_no_id, 0 from schemadoc.sl_node
				where no_id != p_no_id
				and no_active;
	insert into schemadoc.sl_confirm
			(con_origin, con_received, con_seqno)
			select p_no_id, no_id, 0 from schemadoc.sl_node
				where no_id != p_no_id
				and no_active;

	-- ----
	-- Generate ENABLE_SUBSCRIPTION events for all sets that
	-- origin here and are subscribed by the just enabled node.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	for v_sub_row in select SUB.sub_set, SUB.sub_provider from
			schemadoc.sl_set S,
			schemadoc.sl_subscribe SUB
			where S.set_origin = v_local_node_id
			and S.set_id = SUB.sub_set
			and SUB.sub_receiver = p_no_id
			for update of S
	loop
		perform schemadoc.enableSubscription (v_sub_row.sub_set,,
				v_sub_row.sub_provider, p_no_id);
	end loop;

	return p_no_id;
end;

Function: enablesubscription( integer, integer, integer )

Returns: integer

Language: PLPGSQL

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  schemadoc.enableSubscription_int (p_sub_set, 
			p_sub_provider, p_sub_receiver);
end;

Function: enablesubscription_int( integer, integer, integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- The real work is done in the replication engine. All
	-- we have to do here is remembering that it happened.
	-- ----
	update schemadoc.sl_subscribe
			set sub_active = '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 schemadoc.sl_subscribe
				(sub_set, sub_provider, sub_receiver,
				sub_forward, sub_active)
				values
				(p_sub_set, p_sub_provider, p_sub_receiver,
				false, true);
	end if;

	return p_sub_set;
end;

Function: failednode( integer, integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- All consistency checks first
	-- Check that every system that has a path to the failed node
	-- also has a path to the backup node.
	-- ----
	for v_row in select P.pa_client
			from schemadoc.sl_path P
			where P.pa_server = p_failed_node
				and P.pa_client <> p_backup_node
				and not exists (select true from schemadoc.sl_path PP
							where PP.pa_server = p_backup_node
								and PP.pa_client = P.pa_client)
	loop
		raise exception '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 schemadoc.sl_set
			where set_origin = p_failed_node
	loop
		-- ----
		-- Check that the backup node is subscribed to all sets
		-- that origin on the failed node
		-- ----
		select into v_row2 sub_forward, sub_active
				from schemadoc.sl_subscribe
				where sub_set = v_row.set_id
					and sub_receiver = p_backup_node;
		if not found then
			raise exception '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 schemadoc.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 schemadoc.terminateNodeConnections(
			'_schemadoc_Node_' || p_failed_node);

	-- ----
	-- Let every node that listens for something on the failed node
	-- listen for that on the backup node instead.
	-- ----
	for v_row in select * from schemadoc.sl_listen
			where li_provider = p_failed_node
				and li_receiver <> p_backup_node
	loop
		perform schemadoc.storeListen_int(v_row.li_origin,
				p_backup_node, v_row.li_receiver);
	end loop;

	-- ----
	-- Let the backup node listen for all events where the
	-- failed node did listen for it.
	-- ----
	for v_row in select li_origin, li_provider
			from schemadoc.sl_listen
			where li_receiver = p_failed_node
				and li_provider <> p_backup_node
	loop
		perform schemadoc.storeListen_int(v_row.li_origin,
				v_row.li_provider, p_backup_node);
	end loop;

	-- ----
	-- Remove all sl_listen entries that receive anything from the
	-- failed node.
	-- ----
	delete from schemadoc.sl_listen
			where li_provider = p_failed_node
				or li_receiver = p_failed_node;

	-- ----
	-- Move the sets
	-- ----
	for v_row in select S.set_id, (select count(*)
					from schemadoc.sl_subscribe SUB
					where S.set_id = SUB.sub_set
						and SUB.sub_receiver <> p_backup_node
						and SUB.sub_provider = p_failed_node)
					as num_direct_receivers 
			from schemadoc.sl_set S
			where S.set_origin = p_failed_node
			for update
	loop
		-- ----
		-- If the backup node is the only direct subscriber ...
		-- ----
		if v_row.num_direct_receivers = 0 then
raise notice '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 = schemadoc.getLocalNodeId('_schemadoc') then
				for v_row2 in select * from schemadoc.sl_table
						where tab_set = v_row.set_id
				loop
					perform schemadoc.alterTableRestore(v_row2.tab_id);
				end loop;
			end if;

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

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

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

			delete from schemadoc.sl_subscribe
					where sub_set = v_row.set_id
						and sub_receiver = p_backup_node;
		else
raise notice '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 schemadoc.sl_subscribe
					set sub_provider = (select min(SS.sub_receiver)
							from schemadoc.sl_subscribe SS
							where SS.sub_set = v_row.set_id
								and SS.sub_provider = p_failed_node
								and SS.sub_receiver <> p_backup_node
								and SS.sub_forward)
					where sub_set = v_row.set_id
						and sub_receiver = p_backup_node;
			update schemadoc.sl_subscribe
					set sub_provider = p_backup_node
					where sub_set = v_row.set_id
						and sub_provider = p_failed_node
						and sub_receiver <> p_backup_node;
		end if;
	end loop;

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

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

Function: failednode2( integer, integer, integer, bigint, bigint )

Returns: bigint

Language: PLPGSQL

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

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

	insert into schemadoc.sl_event
			(ev_origin, ev_seqno, ev_timestamp,
			ev_minxid, ev_maxxid, ev_xip,
			ev_type, ev_data1, ev_data2, ev_data3)
			values 
			(p_failed_node, p_ev_seqfake, CURRENT_TIMESTAMP,
			v_row.ev_minxid, v_row.ev_maxxid, v_row.ev_xip,
			'FAILOVER_SET', p_failed_node::text, p_backup_node::text,
			p_set_id::text);
	insert into schemadoc.sl_confirm
			(con_origin, con_received, con_seqno, con_timestamp)
			values
			(p_failed_node, schemadoc.getLocalNodeId('_schemadoc'),
			p_ev_seqfake, CURRENT_TIMESTAMP);
	notify "_schemadoc_Event";
	notify "_schemadoc_Confirm";
	notify "_schemadoc_Restart";

	perform schemadoc.failoverSet_int(p_failed_node,
			p_backup_node, p_set_id);

	return p_ev_seqfake;
end;

Function: failoverset_int( integer, integer, integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Change the origin of the set now to the backup node.
	-- On the backup node this includes changing all the
	-- trigger and protection stuff
	-- ----
	if p_backup_node = schemadoc.getLocalNodeId('_schemadoc') then
		for v_row in select * from schemadoc.sl_table
				where tab_set = p_set_id
		loop
			perform schemadoc.alterTableRestore(v_row.tab_id);
		end loop;

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

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

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

		select coalesce(max(ev_seqno), 0) into v_last_sync
				from schemadoc.sl_event
				where ev_origin = p_backup_node
					and ev_type = 'SYNC';
		if v_last_sync > 0 then
			insert into schemadoc.sl_setsync
					(ssy_setid, ssy_origin, ssy_seqno,
					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
					select p_set_id, p_backup_node, v_last_sync,
					ev_minxid, ev_maxxid, ev_xip, NULL
					from schemadoc.sl_event
					where ev_origin = p_backup_node
						and ev_seqno = v_last_sync;
		else
			insert into schemadoc.sl_setsync
					(ssy_setid, ssy_origin, ssy_seqno,
					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
					values (p_set_id, p_backup_node, '0',
					'0', '0', '', NULL);
		end if;
				
	end if;

	return p_failed_node;
end;

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

Returns: bigint

Language: PLPGSQL

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

	return v_max_seqno;
end;

Function: initializelocalnode( integer, text )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Make sure this node is uninitialized or got reset
	-- ----
	select last_value::int4 into v_old_node_id from schemadoc.sl_local_node_id;
	if v_old_node_id != -1 then
		raise exception '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('schemadoc.sl_local_node_id', p_local_node_id);
	perform setval('schemadoc.sl_rowid_seq', 
			p_local_node_id::int8 * '1000000000000000'::int8);
	perform schemadoc.storeNode_int (p_local_node_id, p_comment);
	
	return p_local_node_id;
end;

Function: lockset( integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Check that the set exists and that we are the origin
	-- and that it is not already locked.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select * into v_set_row from schemadoc.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,
			"pg_catalog".quote_ident(PGN.nspname) || '.' ||
			"pg_catalog".quote_ident(PGC.relname) as tab_fqname
			from schemadoc.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
				schemadoc.lockedSet (''_schemadoc'');';
	end loop;

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

	return p_set_id;
end;

Function: mergeset( integer, integer )

Returns: bigint

Language: PLPGSQL

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 schemadoc.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 schemadoc.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 != schemadoc.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 schemadoc.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 != schemadoc.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 schemadoc.sl_subscribe SUB1
				where SUB1.sub_set = p_set_id
				and SUB1.sub_receiver not in (select SUB2.sub_receiver
						from schemadoc.sl_subscribe SUB2
						where SUB2.sub_set = p_add_id))
	then
		raise exception 'Slony-I: subscriber lists of set % and % are different',
				p_set_id, p_add_id;
	end if;

	if exists (select true from schemadoc.sl_subscribe SUB1
				where SUB1.sub_set = p_add_id
				and SUB1.sub_receiver not in (select SUB2.sub_receiver
						from schemadoc.sl_subscribe SUB2
						where SUB2.sub_set = p_set_id))
	then
		raise exception '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 schemadoc.createEvent('_schemadoc', 'SYNC', NULL);
	perform schemadoc.mergeSet_int(p_set_id, p_add_id);
	return  schemadoc.createEvent('_schemadoc', 'MERGE_SET', 
			p_set_id, p_add_id);
end;

Function: mergeset_int( integer, integer )

Returns: integer

Language: PLPGSQL

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 schemadoc.sl_config_lock;
	
	update schemadoc.sl_sequence
			set seq_set = p_set_id
			where seq_set = p_add_id;
	update schemadoc.sl_table
			set tab_set = p_set_id
			where tab_set = p_add_id;
	delete from schemadoc.sl_subscribe
			where sub_set = p_add_id;
	delete from schemadoc.sl_setsync
			where ssy_setid = p_add_id;
	delete from schemadoc.sl_set
			where set_id = p_add_id;

	return p_set_id;
end;

Function: moveset( integer, integer )

Returns: bigint

Language: PLPGSQL

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

	-- ----
	-- Check that the set is locked and that this locking
	-- happened long enough ago.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select * into v_set_row from schemadoc.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 > schemadoc.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 schemadoc.unlockSet(p_set_id);

	-- ----
	-- Check that the new_origin is an active subscriber of the set
	-- ----
	select * into v_sub_row from schemadoc.sl_subscribe
			where sub_set = p_set_id
			and sub_receiver = p_new_origin;
	if not found then
		raise exception '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 schemadoc.moveSet_int(p_set_id, v_local_node_id,
			p_new_origin);

	-- ----
	-- At this time we hold access exclusive locks for every table
	-- in the set. But we did move the set to the new origin, so the
	-- createEvent() we are doing now will not record the sequences.
	-- ----
	v_sync_seqno := schemadoc.createEvent('_schemadoc', 'SYNC');
	insert into schemadoc.sl_seqlog 
			(seql_seqid, seql_origin, seql_ev_seqno, seql_last_value)
			select seq_id, v_local_node_id, v_sync_seqno, seq_last_value
			from schemadoc.sl_seqlastvalue
			where seq_set = p_set_id;
					
	-- ----
	-- Finally we generate the real event
	-- ----
	return schemadoc.createEvent('_schemadoc', 'MOVE_SET', 
			p_set_id, v_local_node_id, p_new_origin);
end;

Function: moveset_int( integer, integer, integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Get our local node ID
	-- ----
	v_local_node_id := schemadoc.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 schemadoc.sl_table
				where tab_set = p_set_id
				order by tab_id
		loop
			perform schemadoc.alterTableRestore(v_tab_row.tab_id);
		end loop;
	end if;

	-- ----
	-- Next we have to reverse the subscription path
	-- ----
	v_sub_last = p_new_origin;
	select sub_provider into v_sub_node
			from schemadoc.sl_subscribe
			where sub_receiver = p_new_origin;
	if not found then
		raise exception '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 schemadoc.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 schemadoc.sl_subscribe
				set sub_provider = v_sub_last
				where sub_set = p_set_id
					and sub_receiver = v_sub_node;

		v_sub_last = v_sub_node;
		v_sub_node = v_sub_next;
	end loop;

	-- ----
	-- This includes creating a subscription for the old origin
	-- ----
	insert into schemadoc.sl_subscribe
			(sub_set, sub_provider, sub_receiver,
			sub_forward, sub_active)
			values (p_set_id, v_sub_last, p_old_origin, true, true);
	if v_local_node_id = p_old_origin then
		select coalesce(max(ev_seqno), 0) into v_last_sync 
				from schemadoc.sl_event
				where ev_origin = p_new_origin
					and ev_type = 'SYNC';
		if v_last_sync > 0 then
			insert into schemadoc.sl_setsync
					(ssy_setid, ssy_origin, ssy_seqno,
					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
					select p_set_id, p_new_origin, v_last_sync,
					ev_minxid, ev_maxxid, ev_xip, NULL
					from schemadoc.sl_event
					where ev_origin = p_new_origin
						and ev_seqno = v_last_sync;
		else
			insert into schemadoc.sl_setsync
					(ssy_setid, ssy_origin, ssy_seqno,
					ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
					values (p_set_id, p_new_origin, '0',
					'0', '0', '', NULL);
		end if;
	end if;

	-- ----
	-- Now change the ownership of the set.
	-- ----
	update schemadoc.sl_set
			set set_origin = p_new_origin
			where set_id = p_set_id;

	-- ----
	-- On the new origin, delete the obsolete setsync information
	-- and the subscription.
	-- ----
	if v_local_node_id = p_new_origin then
		delete from schemadoc.sl_setsync
				where ssy_setid = p_set_id;
	else
		if v_local_node_id <> p_old_origin then
			--
			-- On every other node, change the setsync so that it will
			-- pick up from the new origins last known sync.
			--
			delete from schemadoc.sl_setsync
					where ssy_setid = p_set_id;
			select coalesce(max(ev_seqno), 0) into v_last_sync
					from schemadoc.sl_event
					where ev_origin = p_new_origin
						and ev_type = 'SYNC';
			if v_last_sync > 0 then
				insert into schemadoc.sl_setsync
						(ssy_setid, ssy_origin, ssy_seqno,
						ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
						select p_set_id, p_new_origin, v_last_sync,
						ev_minxid, ev_maxxid, ev_xip, NULL
						from schemadoc.sl_event
						where ev_origin = p_new_origin
							and ev_seqno = v_last_sync;
			else
				insert into schemadoc.sl_setsync
						(ssy_setid, ssy_origin, ssy_seqno,
						ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)
						values (p_set_id, p_new_origin, '0',
						'0', '0', '', NULL);
			end if;
		end if;
	end if;
	delete from schemadoc.sl_subscribe
			where sub_set = p_set_id
			and sub_receiver = p_new_origin;

	-- ----
	-- If we are the new or old origin, we have to
	-- put all the tables into altered state again.
	-- ----
	if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then
		for v_tab_row in select tab_id from schemadoc.sl_table
				where tab_set = p_set_id
				order by tab_id
		loop
			perform schemadoc.alterTableForReplication(v_tab_row.tab_id);
		end loop;
	end if;

	return p_set_id;
end;

Function: sequencelastvalue( text )

Returns: bigint

Language: PLPGSQL

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;

Function: sequencesetvalue( integer, integer, bigint, bigint )

Returns: integer

Language: PLPGSQL

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 "pg_catalog".quote_ident(PGN.nspname) || '.' ||
			"pg_catalog".quote_ident(PGC.relname) into v_fqname
		from schemadoc.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 schemadoc.sl_seqlog
			(seql_seqid, seql_origin, seql_ev_seqno, seql_last_value)
			values (p_seq_id, p_seq_origin, p_ev_seqno, p_last_value);

	return p_seq_id;
end;

Function: setaddsequence( integer, integer, text, text )

Returns: bigint

Language: PLPGSQL

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

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

	if exists (select true from schemadoc.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 schemadoc.setAddSequence_int(p_set_id, p_seq_id, p_fqname,
			p_seq_comment);
	return  schemadoc.createEvent('_schemadoc', 'SET_ADD_SEQUENCE',
			p_set_id, p_seq_id, p_fqname, p_seq_comment);
end;

Function: setaddsequence_int( integer, integer, text, text )

Returns: integer

Language: PLPGSQL

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_sync_row			record;
begin
	-- ----
	-- Grab the central configuration lock
	-- ----
	lock table schemadoc.sl_config_lock;

	-- ----
	-- For sets with a remote origin, check that we are subscribed 
	-- to that set. Otherwise we ignore the sequence because it might 
	-- not even exist in our database.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select set_origin into v_set_origin
			from schemadoc.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 schemadoc.sl_subscribe
				where sub_set = p_set_id
				and sub_receiver = schemadoc.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 into v_seq_reloid, v_relkind
			from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN
			where PGC.relnamespace = PGN.oid
			and p_fqname = "pg_catalog".quote_ident(PGN.nspname) ||
					'.' || "pg_catalog".quote_ident(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 schemadoc.sl_sequence
			(seq_id, seq_reloid, seq_set, seq_comment) values
			(p_seq_id, v_seq_reloid, p_set_id, p_seq_comment);

	-- ----
	-- On the set origin, fake a sl_seqlog row for the last sync event
	-- ----
	if v_set_origin = v_local_node_id then
		for v_sync_row in select coalesce (max(ev_seqno), 0) as ev_seqno
				from schemadoc.sl_event
				where ev_origin = v_local_node_id
					and ev_type = 'SYNC'
		loop
			insert into schemadoc.sl_seqlog
					(seql_seqid, seql_origin, seql_ev_seqno, 
					seql_last_value) values
					(p_seq_id, v_local_node_id, v_sync_row.ev_seqno,
					schemadoc.sequenceLastValue(p_fqname));
		end loop;
	end if;

	return p_seq_id;
end;

Function: setaddtable( integer, integer, text, name, text )

Returns: bigint

Language: PLPGSQL

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

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

	if exists (select true from schemadoc.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 schemadoc.setAddTable_int(p_set_id, p_tab_id, p_fqname,
			p_tab_idxname, p_tab_comment);
	return  schemadoc.createEvent('_schemadoc', 'SET_ADD_TABLE',
			p_set_id, p_tab_id, p_fqname,
			p_tab_idxname, p_tab_comment);
end;

Function: setaddtable_int( integer, integer, text, name, text )

Returns: integer

Language: PLPGSQL

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_local_node_id		int4;
	v_set_origin		int4;
	v_sub_provider		int4;
	v_relkind			char;
	v_tab_reloid		oid;
begin
	-- ----
	-- Grab the central configuration lock
	-- ----
	lock table schemadoc.sl_config_lock;

	-- ----
	-- For sets with a remote origin, check that we are subscribed 
	-- to that set. Otherwise we ignore the table because it might 
	-- not even exist in our database.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select set_origin into v_set_origin
			from schemadoc.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 schemadoc.sl_subscribe
				where sub_set = p_set_id
				and sub_receiver = schemadoc.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 into v_tab_reloid, v_relkind
			from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN
			where PGC.relnamespace = PGN.oid
			and p_fqname = "pg_catalog".quote_ident(PGN.nspname) ||
					'.' || "pg_catalog".quote_ident(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 schemadoc.sl_table
			(tab_id, tab_reloid, tab_set, tab_idxname, 
			tab_altered, tab_comment) values
			(p_tab_id, v_tab_reloid, p_set_id, p_tab_idxname,
			false, p_tab_comment);
	perform schemadoc.alterTableForReplication(p_tab_id);

	return p_tab_id;
end;

Function: setdropsequence( integer )

Returns: bigint

Language: PLPGSQL

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

	-- ----
	-- Determine set id for this sequence
	-- ----
	select seq_set into v_set_id from schemadoc.sl_sequence where seq_id = p_seq_id;

	-- ----
	-- Ensure sequence exists
	-- ----
	if not found then
		raise exception '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 schemadoc.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 != schemadoc.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 schemadoc.setDropSequence_int(p_seq_id);
	return  schemadoc.createEvent('_schemadoc', 'SET_DROP_SEQUENCE',
			p_seq_id);
end;

Function: setdropsequence_int( integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Determine set id for this sequence
	-- ----
	select seq_set into v_set_id from schemadoc.sl_sequence where seq_id = p_seq_id;

	-- ----
	-- Ensure sequence exists
	-- ----
	if not found then
		return 0;
	end if;

	-- ----
	-- For sets with a remote origin, check that we are subscribed 
	-- to that set. Otherwise we ignore the sequence because it might 
	-- not even exist in our database.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select set_origin into v_set_origin
			from schemadoc.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 schemadoc.sl_subscribe
				where sub_set = v_set_id
				and sub_receiver = schemadoc.getLocalNodeId('_schemadoc');
		if not found then
			return 0;
		end if;
	end if;

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

	return p_seq_id;
end;

Function: setdroptable( integer )

Returns: bigint

Language: PLPGSQL

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

        -- ----
	-- Determine the set_id
        -- ----
	select tab_set into v_set_id from schemadoc.sl_table where tab_id = p_tab_id;

	-- ----
	-- Ensure table exists
	-- ----
	if not found then
		raise exception '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 schemadoc.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 != schemadoc.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 schemadoc.setDropTable_int(p_tab_id);
	return  schemadoc.createEvent('_schemadoc', 'SET_DROP_TABLE', p_tab_id);
end;

Function: setdroptable_int( integer )

Returns: integer

Language: PLPGSQL

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

        -- ----
	-- Determine the set_id
        -- ----
	select tab_set into v_set_id from schemadoc.sl_table where tab_id = p_tab_id;

	-- ----
	-- Ensure table exists
	-- ----
	if not found then
		return 0;
	end if;

	-- ----
	-- For sets with a remote origin, check that we are subscribed 
	-- to that set. Otherwise we ignore the table because it might 
	-- not even exist in our database.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select set_origin into v_set_origin
			from schemadoc.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 schemadoc.sl_subscribe
				where sub_set = v_set_id
				and sub_receiver = schemadoc.getLocalNodeId('_schemadoc');
		if not found then
			return 0;
		end if;
	end if;
	
	-- ----
	-- Drop the table from sl_table and drop trigger from it.
	-- ----
	perform schemadoc.alterTableRestore(p_tab_id);
	perform schemadoc.tableDropKey(p_tab_id);
	delete from schemadoc.sl_table where tab_id = p_tab_id;
	return p_tab_id;
end;

Function: slonyversion( )

Returns: text

Language: PLPGSQL

Returns the version number of the slony schema

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

Function: slonyversionmajor( )

Returns: integer

Language: PLPGSQL

Returns the major version number of the slony schema

begin
	return 1;
end;

Function: slonyversionminor( )

Returns: integer

Language: PLPGSQL

Returns the minor version number of the slony schema

begin
	return 1;
end;

Function: slonyversionpatchlevel( )

Returns: integer

Language: PLPGSQL

Returns the version patch level of the slony schema

begin
	return 0;
end;

Function: storelisten( integer, integer, integer )

Returns: bigint

Language: PLPGSQL

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_li_origin		alias for $1;
	p_li_provider	alias for $2;
	p_li_receiver	alias for $3;
begin
	perform schemadoc.storeListen_int (p_li_origin, p_li_provider, p_li_receiver);
	return  schemadoc.createEvent ('_schemadoc', 'STORE_LISTEN',
			p_li_origin, p_li_provider, p_li_receiver);
end;

Function: storelisten_int( integer, integer, integer )

Returns: integer

Language: PLPGSQL

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

	select 1 into v_exists
			from schemadoc.sl_listen
			where li_origin = p_li_origin
			and li_provider = p_li_provider
			and li_receiver = p_li_receiver;
	if not found then
		-- ----
		-- In case we receive STORE_LISTEN events before we know
		-- about the nodes involved in this, we generate those nodes
		-- as pending.
		-- ----
		if not exists (select 1 from schemadoc.sl_node
						where no_id = p_li_origin) then
			perform schemadoc.storeNode_int (p_li_origin, '<event pending>');
		end if;
		if not exists (select 1 from schemadoc.sl_node
						where no_id = p_li_provider) then
			perform schemadoc.storeNode_int (p_li_provider, '<event pending>');
		end if;
		if not exists (select 1 from schemadoc.sl_node
						where no_id = p_li_receiver) then
			perform schemadoc.storeNode_int (p_li_receiver, '<event pending>');
		end if;

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

	return 0;
end;

Function: storenode( integer, text )

Returns: bigint

Language: PLPGSQL

no_id - Node ID # no_comment - Human-oriented comment Generate the STORE_NODE event for node no_id

declare
	p_no_id			alias for $1;
	p_no_comment	alias for $2;
begin
	perform schemadoc.storeNode_int (p_no_id, p_no_comment);
	return  schemadoc.createEvent('_schemadoc', 'STORE_NODE',
									p_no_id, p_no_comment);
end;

Function: storenode_int( integer, text )

Returns: integer

Language: PLPGSQL

no_id - Node ID # no_comment - Human-oriented comment 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;
	v_old_row		record;
begin
	-- ----
	-- Grab the central configuration lock
	-- ----
	lock table schemadoc.sl_config_lock;

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

	return p_no_id;
end;

Function: storepath( integer, integer, text, integer )

Returns: bigint

Language: PLPGSQL

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

Function: storepath_int( integer, integer, text, integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Check if the path already exists
	-- ----
	select 1 into v_dummy
			from schemadoc.sl_path
			where pa_server = p_pa_server
			and pa_client = p_pa_client
			for update;
	if found then
		-- ----
		-- Path exists, update pa_conninfo
		-- ----
		update schemadoc.sl_path
				set pa_conninfo = p_pa_conninfo,
					pa_connretry = p_pa_connretry
				where pa_server = p_pa_server
				and pa_client = p_pa_client;
	else
		-- ----
		-- New path
		--
		-- In case we receive STORE_PATH events before we know
		-- about the nodes involved in this, we generate those nodes
		-- as pending.
		-- ----
		if not exists (select 1 from schemadoc.sl_node
						where no_id = p_pa_server) then
			perform schemadoc.storeNode_int (p_pa_server, '<event pending>');
		end if;
		if not exists (select 1 from schemadoc.sl_node
						where no_id = p_pa_client) then
			perform schemadoc.storeNode_int (p_pa_client, '<event pending>');
		end if;
		insert into schemadoc.sl_path
				(pa_server, pa_client, pa_conninfo, pa_connretry) values
				(p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry);
	end if;

	return 0;
end;

Function: storeset( integer, text )

Returns: bigint

Language: PLPGSQL

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

	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');

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

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

Function: storeset_int( integer, integer, text )

Returns: integer

Language: PLPGSQL

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

	select 1 into v_dummy
			from schemadoc.sl_set
			where set_id = p_set_id
			for update;
	if found then 
		update schemadoc.sl_set
				set set_comment = p_set_comment
				where set_id = p_set_id;
	else
		if not exists (select 1 from schemadoc.sl_node
						where no_id = p_set_origin) then
			perform schemadoc.storeNode_int (p_set_origin, '<event pending>');
		end if;
		insert into schemadoc.sl_set
				(set_id, set_origin, set_comment) values
				(p_set_id, p_set_origin, p_set_comment);
	end if;

	return p_set_id;
end;

Function: storetrigger( integer, name )

Returns: bigint

Language: PLPGSQL

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 schemadoc.storeTrigger_int(p_trig_tabid, p_trig_tgname);
	return  schemadoc.createEvent('_schemadoc', 'STORE_TRIGGER',
			p_trig_tabid, p_trig_tgname);
end;

Function: storetrigger_int( integer, name )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Get the current table status (altered or not)
	-- ----
	select tab_altered into v_tab_altered
			from schemadoc.sl_table where tab_id = p_trig_tabid;
	if not found then
		-- ----
		-- Not found is no hard error here, because that might
		-- mean that we are not subscribed to that set
		-- ----
		return 0;
	end if;

	-- ----
	-- If the table is modified for replication, restore the original state
	-- ----
	if v_tab_altered then
		perform schemadoc.alterTableRestore(p_trig_tabid);
	end if;

	-- ----
	-- Make sure that an entry for this trigger exists
	-- ----
	delete from schemadoc.sl_trigger
			where trig_tabid = p_trig_tabid
			  and trig_tgname = p_trig_tgname;
	insert into schemadoc.sl_trigger (
				trig_tabid, trig_tgname
			) values (
				p_trig_tabid, p_trig_tgname
			);

	-- ----
	-- Put the table back into replicated state if it was
	-- ----
	if v_tab_altered then
		perform schemadoc.alterTableForReplication(p_trig_tabid);
	end if;

	return p_trig_tabid;
end;

Function: subscribeset( integer, integer, integer, boolean )

Returns: bigint

Language: PLPGSQL

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

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

	-- ----
	-- Check that the origin and provider of the set are remote
	-- ----
	select set_origin into v_set_origin
			from schemadoc.sl_set
			where set_id = p_sub_set;
	if not found then
		raise exception '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;

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

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

Function: subscribeset_int( integer, integer, integer, boolean )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Provider change is only allowed for active sets
	-- ----
	if p_sub_receiver = schemadoc.getLocalNodeId('_schemadoc') then
		select sub_active into v_sub_row from schemadoc.sl_subscribe
				where sub_set = p_sub_set
				and sub_receiver = p_sub_receiver;
		if found then
			if not v_sub_row.sub_active then
				raise exception '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 schemadoc.sl_subscribe
			set sub_provider = p_sub_provider,
				sub_forward = p_sub_forward
			where sub_set = p_sub_set
			and sub_receiver = p_sub_receiver;
	if found then
		return p_sub_set;
	end if;

	-- ----
	-- Not found, insert a new one
	-- ----
	if not exists (select true from schemadoc.sl_path
			where pa_server = p_sub_provider
			and pa_client = p_sub_receiver)
	then
		insert into schemadoc.sl_path
				(pa_server, pa_client, pa_conninfo, pa_connretry)
				values 
				(p_sub_provider, p_sub_receiver, 
				'<event pending>', 10);
	end if;
	insert into schemadoc.sl_subscribe
			(sub_set, sub_provider, sub_receiver, sub_forward, sub_active)
			values (p_sub_set, p_sub_provider, p_sub_receiver,
				p_sub_forward, false);

	-- ----
	-- If the set origin is here, then enable the subscription
	-- ----
	select set_origin into v_set_origin
			from schemadoc.sl_set
			where set_id = p_sub_set;
	if not found then
		raise exception 'Slony-I: set % not found', p_sub_set;
	end if;

	if v_set_origin = schemadoc.getLocalNodeId('_schemadoc') then
		perform schemadoc.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 schemadoc.enableSubscription(p_sub_set, 
				p_sub_provider, p_sub_receiver);
	end if;

	return p_sub_set;
end;

Function: tableaddkey( text )

Returns: text

Language: PLPGSQL

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_attkind		text default '';
	v_attrow		record;
	v_have_serial	bool default 'f';
begin
	--
	-- 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 "pg_catalog".quote_ident(PGN.nspname) || '.' ||
			    "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname
				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', p_tab_fqname;
	end if;

	--
	-- If it does not have the special serial column, we
	-- have to add it. This will be only half way done.
	-- The function to add the table to the set must finish
	-- these definitions with NOT NULL and UNIQUE after
	-- updating all existing rows.
	--
	if not v_have_serial then
		execute 'lock table ' || p_tab_fqname ||
			' in access exclusive mode';
		execute 'alter table only ' || p_tab_fqname ||
			' add column "_Slony-I_schemadoc_rowID" bigint;';
		execute 'alter table only ' || p_tab_fqname ||
			' alter column "_Slony-I_schemadoc_rowID" ' ||
			' set default "pg_catalog".nextval(''schemadoc.sl_rowid_seq'');';

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

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

Function: tabledropkey( integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Construct the tables fully qualified name and get its oid
	-- ----
	select "pg_catalog".quote_ident(PGN.nspname) || '.' ||
				"pg_catalog".quote_ident(PGC.relname),
				PGC.oid into v_tab_fqname, v_tab_oid
			from schemadoc.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;

Function: tablehasserialkey( text )

Returns: boolean

Language: PLPGSQL

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_attnum		int2;
begin
	select PGA.attnum into v_attnum
			from "pg_catalog".pg_class PGC,
				"pg_catalog".pg_namespace PGN,
				"pg_catalog".pg_attribute PGA
			where "pg_catalog".quote_ident(PGN.nspname) || '.' ||
				"pg_catalog".quote_ident(PGC.relname) = p_tab_fqname
				and PGC.relnamespace = PGN.oid
				and PGA.attrelid = PGC.oid
				and PGA.attname = '_Slony-I_schemadoc_rowID'
				and not PGA.attisdropped;
	return found;
end;

Function: uninstallnode( )

Returns: integer

Language: PLPGSQL

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

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

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

Function: unlockset( integer )

Returns: integer

Language: PLPGSQL

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

	-- ----
	-- Check that the set exists and that we are the origin
	-- and that it is not already locked.
	-- ----
	v_local_node_id := schemadoc.getLocalNodeId('_schemadoc');
	select * into v_set_row from schemadoc.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,
			"pg_catalog".quote_ident(PGN.nspname) || '.' ||
			"pg_catalog".quote_ident(PGC.relname) as tab_fqname
			from schemadoc.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 schemadoc.sl_set
			set set_locked = NULL
			where set_id = p_set_id;

	return p_set_id;
end;

Function: unsubscribeset( integer, integer )

Returns: bigint

Language: PLPGSQL

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

	-- ----
	-- Check that this is called on the receiver node
	-- ----
	if p_sub_receiver != schemadoc.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 schemadoc.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 schemadoc.sl_table
			where tab_set = p_sub_set
			order by tab_id
	loop
		perform schemadoc.alterTableRestore(v_tab_row.tab_id);
		perform schemadoc.tableDropKey(v_tab_row.tab_id);
	end loop;

	-- ----
	-- Remove the setsync status. This will also cause the
	-- worker thread to ignore the set and stop replicating
	-- right now.
	-- ----
	delete from schemadoc.sl_setsync
			where ssy_setid = p_sub_set;

	-- ----
	-- Remove all sl_table and sl_sequence entries for this set.
	-- Should we ever subscribe again, the initial data
	-- copy process will create new ones.
	-- ----
	delete from schemadoc.sl_table
			where tab_set = p_sub_set;
	delete from schemadoc.sl_sequence
			where seq_set = p_sub_set;

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

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

Function: unsubscribeset_int( integer, integer )

Returns: integer

Language: PLPGSQL

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

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

	return p_sub_set;
end;

Generated by PostgreSQL Autodoc

W3C HTML 4.01 Strict