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