Dumped on 2004-09-29
This table exists solely to prevent overlapping execution of configuration change procedures and the resulting possible deadlocks.
| F-Key | Name | Type | Description |
|---|---|---|---|
| dummy | integer |
Holds confirmation of replication events. After a period of time, Slony removes old confirmed events from both this table and the sl_event table.
| 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 |
Holds information about replication events. After a period of time, Slony removes old confirmed events from both this table and the sl_confirm table.
| 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 |
Indicates how nodes listen to events from other nodes in the Slony-I network.
| 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 |
Stores each change to be propagated to subscriber nodes
| 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 |
Stores each change to be propagated to subscriber nodes
| 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 |
Holds the list of nodes associated with this namespace.
| 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:
Holds connection information for the paths between nodes, and the synchronisation delay
| 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:
| 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)
);
Log of Sequence updates
| 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 |
Similar to sl_table, each entry identifies a sequence being replicated.
| 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 |
Holds definitions of replication sets.
| 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:
Not documented yet
| 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 |
Holds a list of subscriptions on sets
| 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 |
Holds information about the tables being replicated.
| 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:
Holds information about triggers on tables managed using Slony-I
| 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 |
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;
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;
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;
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;
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;
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;
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;
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 (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;
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;
declare p_no_id alias for $1; begin -- **** TODO **** raise exception 'Slony-I: disableNode_int() not implemented'; end;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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 (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 (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;
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;
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;
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;
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;
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;
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;
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;
declare p_seqname alias for $1; v_seq_row record; begin for v_seq_row in execute 'select last_value from ' || p_seqname loop return v_seq_row.last_value; end loop; -- not reached end;
sequenceSetValue (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;
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;
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;
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;
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;
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;
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;
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;
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;
Returns the version number of the slony schema
begin return '' || schemadoc.slonyVersionMajor() || '.' || schemadoc.slonyVersionMinor() || '.' || schemadoc.slonyVersionPatchlevel(); end;
Returns the major version number of the slony schema
begin return 1; end;
Returns the minor version number of the slony schema
begin return 1; end;
Returns the version patch level of the slony schema
begin return 0; end;
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 (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;
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;
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 (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 (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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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