+++ /dev/null
--- ----------------------------------------------------------------------\r
--- slony1_funcs.sql\r
---\r
--- Declaration of replication support functions.\r
---\r
--- Copyright (c) 2003-2004, PostgreSQL Global Development Group\r
--- Author: Jan Wieck, Afilias USA INC.\r
---\r
--- $Id: slony1_funcs.sql,v 1.2 2005/06/16 14:40:14 chriskl Exp $\r
--- ----------------------------------------------------------------------\r
-\r
-\r
--- **********************************************************************\r
--- * C functions in src/backend/slony1_base.c\r
--- **********************************************************************\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]])\r
---\r
--- Create an sl_event entry\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.createEvent (name, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text, text, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text, text, text, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text, text, text, text, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text, text, text, text, text, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-create or replace function @NAMESPACE@.createEvent (name, text, text, text, text, text, text, text, text, text)\r
- returns bigint\r
- as '$libdir/slony1_funcs', '_Slony_I_createEvent'\r
- language C\r
- called on null input;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION denyAccess (cluster_name)\r
---\r
--- Trigger function to prevent modifications to a table on\r
--- a subscriber.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.denyAccess ()\r
- returns trigger\r
- as '$libdir/slony1_funcs', '_Slony_I_denyAccess'\r
- language C\r
- security definer;\r
-grant execute on function @NAMESPACE@.denyAccess () to public;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION lockedSet (cluster_name)\r
---\r
--- Trigger function to prevent modifications to a table before\r
--- and after a moveSet().\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.lockedSet ()\r
- returns trigger\r
- as '$libdir/slony1_funcs', '_Slony_I_lockedSet'\r
- language C;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION getLocalNodeId (name)\r
---\r
--- \r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.getLocalNodeId (name) returns int4\r
- as '$libdir/slony1_funcs', '_Slony_I_getLocalNodeId'\r
- language C\r
- security definer;\r
-grant execute on function @NAMESPACE@.getLocalNodeId (name) to public;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION getModuleVersion ()\r
---\r
--- Returns the compiled in version number of the Slony-I shared\r
--- object.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.getModuleVersion () returns text\r
- as '$libdir/slony1_funcs', '_Slony_I_getModuleVersion'\r
- language C\r
- security definer;\r
-grant execute on function @NAMESPACE@.getModuleVersion () to public;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setSessionRole (name, role)\r
---\r
--- \r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setSessionRole (name, text) returns text\r
- as '$libdir/slony1_funcs', '_Slony_I_setSessionRole'\r
- language C\r
- security definer;\r
-grant execute on function @NAMESPACE@.setSessionRole (name, text) to public;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION getSessionRole (name, role)\r
---\r
--- \r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.getSessionRole (name) returns text\r
- as '$libdir/slony1_funcs', '_Slony_I_getSessionRole'\r
- language C\r
- security definer;\r
-grant execute on function @NAMESPACE@.getSessionRole (name) to public;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION logTrigger ()\r
---\r
--- \r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.logTrigger () returns trigger\r
- as '$libdir/slony1_funcs', '_Slony_I_logTrigger'\r
- language C\r
- security definer;\r
-grant execute on function @NAMESPACE@.logTrigger () to public;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION terminateNodeConnections (name)\r
---\r
--- \r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.terminateNodeConnections (name) returns int4\r
- as '$libdir/slony1_funcs', '_Slony_I_terminateNodeConnections'\r
- language C;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION cleanupListener ()\r
---\r
--- \r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.cleanupListener () returns int4\r
- as '$libdir/slony1_funcs', '_Slony_I_cleanupListener'\r
- language C;\r
-\r
-\r
--- **********************************************************************\r
--- * PL/pgSQL functions for administrative tasks\r
--- **********************************************************************\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION slonyVersionMajor()\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.slonyVersionMajor()\r
-returns int4\r
-as '\r
-begin\r
- return 1;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION slonyVersionMinor()\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.slonyVersionMinor()\r
-returns int4\r
-as '\r
-begin\r
- return 0;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION slonyVersionPatchlevel()\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.slonyVersionPatchlevel()\r
-returns int4\r
-as '\r
-begin\r
- return 5;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION slonyVersion()\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.slonyVersion()\r
-returns text\r
-as '\r
-begin\r
- return '''' || @NAMESPACE@.slonyVersionMajor() || ''.''\r
- || @NAMESPACE@.slonyVersionMinor() || ''.''\r
- || @NAMESPACE@.slonyVersionPatchlevel();\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION initializeLocalNode (no_id, no_comment)\r
---\r
--- Initializes a new node.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.initializeLocalNode (int4, text)\r
-returns int4\r
-as '\r
-declare\r
- p_local_node_id alias for $1;\r
- p_comment alias for $2;\r
- v_old_node_id int4;\r
- v_first_log_no int4;\r
- v_event_seq int8;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Make sure this node is uninitialized or got reset\r
- -- ----\r
- select last_value::int4 into v_old_node_id from @NAMESPACE@.sl_local_node_id;\r
- if v_old_node_id != -1 then\r
- raise exception ''Slony-I: This node is already initialized'';\r
- end if;\r
-\r
- -- ----\r
- -- Set sl_local_node_id to the requested value and add our\r
- -- own system to sl_node.\r
- -- ----\r
- perform setval(''@NAMESPACE@.sl_local_node_id'', p_local_node_id);\r
- perform setval(''@NAMESPACE@.sl_rowid_seq'', \r
- p_local_node_id::int8 * ''1000000000000000''::int8);\r
- perform @NAMESPACE@.storeNode_int (p_local_node_id, p_comment);\r
- \r
- return p_local_node_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeNode (no_id, no_comment)\r
---\r
--- Generate the STORE_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeNode (int4, text)\r
-returns bigint\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
- p_no_comment alias for $2;\r
-begin\r
- perform @NAMESPACE@.storeNode_int (p_no_id, p_no_comment);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''STORE_NODE'',\r
- p_no_id, p_no_comment);\r
-end;\r
-' language plpgsql\r
- called on null input;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeNode_int (no_id, no_comment)\r
---\r
--- Process the STORE_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeNode_int (int4, text)\r
-returns int4\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
- p_no_comment alias for $2;\r
- v_old_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check if the node exists\r
- -- ----\r
- select * into v_old_row\r
- from @NAMESPACE@.sl_node\r
- where no_id = p_no_id\r
- for update;\r
- if found then \r
- -- ----\r
- -- Node exists, update the existing row.\r
- -- ----\r
- update @NAMESPACE@.sl_node\r
- set no_comment = p_no_comment\r
- where no_id = p_no_id;\r
- else\r
- -- ----\r
- -- New node, insert the sl_node row\r
- -- ----\r
- insert into @NAMESPACE@.sl_node\r
- (no_id, no_active, no_comment) values\r
- (p_no_id, ''f'', p_no_comment);\r
- end if;\r
-\r
- return p_no_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION enableNode (no_id)\r
---\r
--- Generate the ENABLE_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.enableNode (int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
- v_local_node_id int4;\r
- v_node_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that we are the node to activate and that we are\r
- -- currently disabled.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select * into v_node_row\r
- from @NAMESPACE@.sl_node\r
- where no_id = p_no_id\r
- for update;\r
- if not found then \r
- raise exception ''Slony-I: node % not found'', p_no_id;\r
- end if;\r
- if v_node_row.no_active then\r
- raise exception ''Slony-I: node % is already active'', p_no_id;\r
- end if;\r
-\r
- -- ----\r
- -- Activate this node and generate the ENABLE_NODE event\r
- -- ----\r
- perform @NAMESPACE@.enableNode_int (p_no_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''ENABLE_NODE'',\r
- p_no_id);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION enableNode_int (no_id)\r
---\r
--- Process the ENABLE_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.enableNode_int (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
- v_local_node_id int4;\r
- v_node_row record;\r
- v_sub_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that the node is inactive\r
- -- ----\r
- select * into v_node_row\r
- from @NAMESPACE@.sl_node\r
- where no_id = p_no_id\r
- for update;\r
- if not found then \r
- raise exception ''Slony-I: node % not found'', p_no_id;\r
- end if;\r
- if v_node_row.no_active then\r
- return p_no_id;\r
- end if;\r
-\r
- -- ----\r
- -- Activate the node and generate sl_confirm status rows for it.\r
- -- ----\r
- update @NAMESPACE@.sl_node\r
- set no_active = ''t''\r
- where no_id = p_no_id;\r
- insert into @NAMESPACE@.sl_confirm\r
- (con_origin, con_received, con_seqno)\r
- select no_id, p_no_id, 0 from @NAMESPACE@.sl_node\r
- where no_id != p_no_id\r
- and no_active;\r
- insert into @NAMESPACE@.sl_confirm\r
- (con_origin, con_received, con_seqno)\r
- select p_no_id, no_id, 0 from @NAMESPACE@.sl_node\r
- where no_id != p_no_id\r
- and no_active;\r
-\r
- -- ----\r
- -- Generate ENABLE_SUBSCRIPTION events for all sets that\r
- -- origin here and are subscribed by the just enabled node.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- for v_sub_row in select SUB.sub_set, SUB.sub_provider from\r
- @NAMESPACE@.sl_set S,\r
- @NAMESPACE@.sl_subscribe SUB\r
- where S.set_origin = v_local_node_id\r
- and S.set_id = SUB.sub_set\r
- and SUB.sub_receiver = p_no_id\r
- for update of S\r
- loop\r
- perform @NAMESPACE@.enableSubscription (v_sub_row.sub_set,,\r
- v_sub_row.sub_provider, p_no_id);\r
- end loop;\r
-\r
- return p_no_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION disableNode (no_id)\r
---\r
--- Generate the DISABLE_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.disableNode (int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
-begin\r
- -- **** TODO ****\r
- raise exception ''Slony-I: disableNode() not implemented'';\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION disableNode_int (no_id)\r
---\r
--- Process the DISABLE_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.disableNode_int (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
-begin\r
- -- **** TODO ****\r
- raise exception ''Slony-I: disableNode_int() not implemented'';\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropNode (no_id)\r
---\r
--- Generate the DROP_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropNode (int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
- v_node_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that this got called on a different node\r
- -- ----\r
- if p_no_id = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: DROP_NODE cannot initiate on the dropped node'';\r
- end if;\r
-\r
- select * into v_node_row from @NAMESPACE@.sl_node\r
- where no_id = p_no_id\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: unknown node ID %'', p_no_id;\r
- end if;\r
-\r
- -- ----\r
- -- Make sure we do not break other nodes subscriptions with this\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_subscribe\r
- where sub_provider = p_no_id)\r
- then\r
- raise exception ''Slony-I: Node % is still configured as data provider'',\r
- p_no_id;\r
- end if;\r
-\r
- -- ----\r
- -- Make sure no set originates there any more\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_set\r
- where set_origin = p_no_id)\r
- then\r
- raise exception ''Slony-I: Node % is still origin of one or more sets'',\r
- p_no_id;\r
- end if;\r
-\r
- -- ----\r
- -- Call the internal drop functionality and generate the event\r
- -- ----\r
- perform @NAMESPACE@.dropNode_int(p_no_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''DROP_NODE'',\r
- p_no_id);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropNode_int (no_id)\r
---\r
--- Process the DROP_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropNode_int (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_no_id alias for $1;\r
- v_tab_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- If the dropped node is a remote node, clean the configuration\r
- -- from all traces for it.\r
- -- ----\r
- if p_no_id <> @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_receiver = p_no_id;\r
- delete from @NAMESPACE@.sl_listen\r
- where li_origin = p_no_id\r
- or li_provider = p_no_id\r
- or li_receiver = p_no_id;\r
- delete from @NAMESPACE@.sl_path\r
- where pa_server = p_no_id\r
- or pa_client = p_no_id;\r
- delete from @NAMESPACE@.sl_confirm\r
- where con_origin = p_no_id\r
- or con_received = p_no_id;\r
- delete from @NAMESPACE@.sl_event\r
- where ev_origin = p_no_id;\r
- delete from @NAMESPACE@.sl_node\r
- where no_id = p_no_id;\r
-\r
- return p_no_id;\r
- end if;\r
-\r
- -- ----\r
- -- This is us ... deactivate the node for now, the daemon\r
- -- will call uninstallNode() in a separate transaction.\r
- -- ----\r
- update @NAMESPACE@.sl_node\r
- set no_active = false\r
- where no_id = p_no_id;\r
-\r
- return p_no_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION failedNode (failed_node, backup_node)\r
---\r
--- Initiate a failover. This function must be called on all nodes\r
--- and then waited for the restart of all node deamons.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.failedNode(int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_failed_node alias for $1;\r
- p_backup_node alias for $2;\r
- v_row record;\r
- v_row2 record;\r
- v_n int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- All consistency checks first\r
- -- Check that every system that has a path to the failed node\r
- -- also has a path to the backup node.\r
- -- ----\r
- for v_row in select P.pa_client\r
- from @NAMESPACE@.sl_path P\r
- where P.pa_server = p_failed_node\r
- and P.pa_client <> p_backup_node\r
- and not exists (select true from @NAMESPACE@.sl_path PP\r
- where PP.pa_server = p_backup_node\r
- and PP.pa_client = P.pa_client)\r
- loop\r
- raise exception ''Slony-I: cannot failover - node % has no path to the backup node'',\r
- v_row.pa_client;\r
- end loop;\r
-\r
- -- ----\r
- -- Check all sets originating on the failed node\r
- -- ----\r
- for v_row in select set_id\r
- from @NAMESPACE@.sl_set\r
- where set_origin = p_failed_node\r
- loop\r
- -- ----\r
- -- Check that the backup node is subscribed to all sets\r
- -- that origin on the failed node\r
- -- ----\r
- select into v_row2 sub_forward, sub_active\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_set = v_row.set_id\r
- and sub_receiver = p_backup_node;\r
- if not found then\r
- raise exception ''Slony-I: cannot failover - node % is not subscribed to set %'',\r
- p_backup_node, v_row.set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Check that the subscription is active\r
- -- ----\r
- if not v_row2.sub_active then\r
- raise exception ''Slony-I: cannot failover - subscription for set % is not active'',\r
- v_row.set_id;\r
- end if;\r
-\r
- -- ----\r
- -- If there are other subscribers, the backup node needs to\r
- -- be a forwarder too.\r
- -- ----\r
- select into v_n count(*)\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_set = v_row.set_id\r
- and sub_receiver <> p_backup_node;\r
- if v_n > 0 and not v_row2.sub_forward then\r
- raise exception ''Slony-I: cannot failover - node % is not a forwarder of set %'',\r
- p_backup_node, v_row.set_id;\r
- end if;\r
- end loop;\r
-\r
- -- ----\r
- -- Terminate all connections of the failed node the hard way\r
- -- ----\r
- perform @NAMESPACE@.terminateNodeConnections(\r
- ''_@CLUSTERNAME@_Node_'' || p_failed_node);\r
-\r
- -- ----\r
- -- Let every node that listens for something on the failed node\r
- -- listen for that on the backup node instead.\r
- -- ----\r
- for v_row in select * from @NAMESPACE@.sl_listen\r
- where li_provider = p_failed_node\r
- and li_receiver <> p_backup_node\r
- loop\r
- perform @NAMESPACE@.storeListen_int(v_row.li_origin,\r
- p_backup_node, v_row.li_receiver);\r
- end loop;\r
-\r
- -- ----\r
- -- Let the backup node listen for all events where the\r
- -- failed node did listen for it.\r
- -- ----\r
- for v_row in select li_origin, li_provider\r
- from @NAMESPACE@.sl_listen\r
- where li_receiver = p_failed_node\r
- and li_provider <> p_backup_node\r
- loop\r
- perform @NAMESPACE@.storeListen_int(v_row.li_origin,\r
- v_row.li_provider, p_backup_node);\r
- end loop;\r
-\r
- -- ----\r
- -- Remove all sl_listen entries that receive anything from the\r
- -- failed node.\r
- -- ----\r
- delete from @NAMESPACE@.sl_listen\r
- where li_provider = p_failed_node\r
- or li_receiver = p_failed_node;\r
-\r
- -- ----\r
- -- Move the sets\r
- -- ----\r
- for v_row in select S.set_id, (select count(*)\r
- from @NAMESPACE@.sl_subscribe SUB\r
- where S.set_id = SUB.sub_set\r
- and SUB.sub_receiver <> p_backup_node\r
- and SUB.sub_provider = p_failed_node)\r
- as num_direct_receivers \r
- from @NAMESPACE@.sl_set S\r
- where S.set_origin = p_failed_node\r
- for update\r
- loop\r
- -- ----\r
- -- If the backup node is the only direct subscriber ...\r
- -- ----\r
- if v_row.num_direct_receivers = 0 then\r
-raise notice ''failedNode: set % has no other direct receivers - move now'', v_row.set_id;\r
- -- ----\r
- -- backup_node is the only direct subscriber, move the set\r
- -- right now. On the backup node itself that includes restoring\r
- -- all user mode triggers, removing the protection trigger,\r
- -- adding the log trigger, removing the subscription and the\r
- -- obsolete setsync status.\r
- -- ----\r
- if p_backup_node = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- for v_row2 in select * from @NAMESPACE@.sl_table\r
- where tab_set = v_row.set_id\r
- loop\r
- perform @NAMESPACE@.alterTableRestore(v_row2.tab_id);\r
- end loop;\r
- end if;\r
-\r
- update @NAMESPACE@.sl_set set set_origin = p_backup_node\r
- where set_id = v_row.set_id;\r
-\r
- if p_backup_node = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = v_row.set_id;\r
-\r
- for v_row2 in select * from @NAMESPACE@.sl_table\r
- where tab_set = v_row.set_id\r
- loop\r
- perform @NAMESPACE@.alterTableForReplication(v_row2.tab_id);\r
- end loop;\r
- end if;\r
-\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_set = v_row.set_id\r
- and sub_receiver = p_backup_node;\r
- else\r
-raise notice ''failedNode: set % has other direct receivers - change providers only'', v_row.set_id;\r
- -- ----\r
- -- Backup node is not the only direct subscriber. This\r
- -- means that at this moment, we redirect all direct\r
- -- subscribers to receive from the backup node, and the\r
- -- backup node itself to receive from another one.\r
- -- The admin utility will wait for the slon engine to\r
- -- restart and then call failedNode2() on the node with\r
- -- the highest SYNC and redirect this to it on\r
- -- backup node later.\r
- -- ----\r
- update @NAMESPACE@.sl_subscribe\r
- set sub_provider = (select min(SS.sub_receiver)\r
- from @NAMESPACE@.sl_subscribe SS\r
- where SS.sub_set = v_row.set_id\r
- and SS.sub_provider = p_failed_node\r
- and SS.sub_receiver <> p_backup_node\r
- and SS.sub_forward)\r
- where sub_set = v_row.set_id\r
- and sub_receiver = p_backup_node;\r
- update @NAMESPACE@.sl_subscribe\r
- set sub_provider = p_backup_node\r
- where sub_set = v_row.set_id\r
- and sub_provider = p_failed_node\r
- and sub_receiver <> p_backup_node;\r
- end if;\r
- end loop;\r
-\r
- -- ----\r
- -- Make sure the node daemon will restart\r
- -- ----\r
- notify "_@CLUSTERNAME@_Restart";\r
-\r
- -- ----\r
- -- That is it - so far.\r
- -- ----\r
- return p_failed_node;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION failedNode2 (failed_node, backup_node, set_id, ev_seqno, ev_seqfake)\r
---\r
--- On the node that has the highest sequence number of the failed node,\r
--- fake the FAILED_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.failedNode2 (int4, int4, int4, int8, int8)\r
-returns bigint\r
-as '\r
-declare\r
- p_failed_node alias for $1;\r
- p_backup_node alias for $2;\r
- p_set_id alias for $3;\r
- p_ev_seqno alias for $4;\r
- p_ev_seqfake alias for $5;\r
- v_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- select * into v_row\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = p_failed_node\r
- and ev_seqno = p_ev_seqno;\r
- if not found then\r
- raise exception ''Slony-I: event %,% not found'',\r
- p_failed_node, p_ev_seqno;\r
- end if;\r
-\r
- insert into @NAMESPACE@.sl_event\r
- (ev_origin, ev_seqno, ev_timestamp,\r
- ev_minxid, ev_maxxid, ev_xip,\r
- ev_type, ev_data1, ev_data2, ev_data3)\r
- values \r
- (p_failed_node, p_ev_seqfake, CURRENT_TIMESTAMP,\r
- v_row.ev_minxid, v_row.ev_maxxid, v_row.ev_xip,\r
- ''FAILOVER_SET'', p_failed_node::text, p_backup_node::text,\r
- p_set_id::text);\r
- insert into @NAMESPACE@.sl_confirm\r
- (con_origin, con_received, con_seqno, con_timestamp)\r
- values\r
- (p_failed_node, @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@''),\r
- p_ev_seqfake, CURRENT_TIMESTAMP);\r
- notify "_@CLUSTERNAME@_Event";\r
- notify "_@CLUSTERNAME@_Confirm";\r
- notify "_@CLUSTERNAME@_Restart";\r
-\r
- perform @NAMESPACE@.failoverSet_int(p_failed_node,\r
- p_backup_node, p_set_id);\r
-\r
- return p_ev_seqfake;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION failoverSet_int (failed_node, backup_node, set_id)\r
---\r
--- Finish failover for one set.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.failoverSet_int (int4, int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_failed_node alias for $1;\r
- p_backup_node alias for $2;\r
- p_set_id alias for $3;\r
- v_row record;\r
- v_last_sync int8;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Change the origin of the set now to the backup node.\r
- -- On the backup node this includes changing all the\r
- -- trigger and protection stuff\r
- -- ----\r
- if p_backup_node = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- for v_row in select * from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id\r
- loop\r
- perform @NAMESPACE@.alterTableRestore(v_row.tab_id);\r
- end loop;\r
-\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = p_set_id;\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = p_backup_node;\r
- update @NAMESPACE@.sl_set\r
- set set_origin = p_backup_node\r
- where set_id = p_set_id;\r
-\r
- for v_row in select * from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id\r
- loop\r
- perform @NAMESPACE@.alterTableForReplication(v_row.tab_id);\r
- end loop;\r
- else\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = p_backup_node;\r
- update @NAMESPACE@.sl_set\r
- set set_origin = p_backup_node\r
- where set_id = p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- If we are a subscriber of the set ourself, change our\r
- -- setsync status to reflect the new set origin.\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = @NAMESPACE@.getLocalNodeId(\r
- ''_@CLUSTERNAME@''))\r
- then\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = p_set_id;\r
-\r
- select coalesce(max(ev_seqno), 0) into v_last_sync\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = p_backup_node\r
- and ev_type = ''SYNC'';\r
- if v_last_sync > 0 then\r
- insert into @NAMESPACE@.sl_setsync\r
- (ssy_setid, ssy_origin, ssy_seqno,\r
- ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)\r
- select p_set_id, p_backup_node, v_last_sync,\r
- ev_minxid, ev_maxxid, ev_xip, NULL\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = p_backup_node\r
- and ev_seqno = v_last_sync;\r
- else\r
- insert into @NAMESPACE@.sl_setsync\r
- (ssy_setid, ssy_origin, ssy_seqno,\r
- ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)\r
- values (p_set_id, p_backup_node, ''0'',\r
- ''0'', ''0'', '''', NULL);\r
- end if;\r
- \r
- end if;\r
-\r
- return p_failed_node;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION uninstallNode ()\r
---\r
--- Reset the whole database to standalone by removing the whole\r
--- replication system.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.uninstallNode ()\r
-returns int4\r
-as '\r
-declare\r
- v_tab_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- This is us ... time for suicide! Restore all tables to\r
- -- their original status.\r
- -- ----\r
- for v_tab_row in select * from @NAMESPACE@.sl_table loop\r
- perform @NAMESPACE@.alterTableRestore(v_tab_row.tab_id);\r
- perform @NAMESPACE@.tableDropKey(v_tab_row.tab_id);\r
- end loop;\r
-\r
- raise notice ''Slony-I: Please drop schema "_@CLUSTERNAME@"'';\r
- return 0;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storePath (pa_server, pa_client, pa_conninfo, pa_connretry)\r
---\r
--- Generate the STORE_PATH event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storePath (int4, int4, text, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_pa_server alias for $1;\r
- p_pa_client alias for $2;\r
- p_pa_conninfo alias for $3;\r
- p_pa_connretry alias for $4;\r
-begin\r
- perform @NAMESPACE@.storePath_int(p_pa_server, p_pa_client,\r
- p_pa_conninfo, p_pa_connretry);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''STORE_PATH'', \r
- p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storePath_int (pa_server, pa_client, pa_conninfo, pa_connretry)\r
---\r
--- Process the STORE_PATH event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storePath_int (int4, int4, text, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_pa_server alias for $1;\r
- p_pa_client alias for $2;\r
- p_pa_conninfo alias for $3;\r
- p_pa_connretry alias for $4;\r
- v_dummy int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check if the path already exists\r
- -- ----\r
- select 1 into v_dummy\r
- from @NAMESPACE@.sl_path\r
- where pa_server = p_pa_server\r
- and pa_client = p_pa_client\r
- for update;\r
- if found then\r
- -- ----\r
- -- Path exists, update pa_conninfo\r
- -- ----\r
- update @NAMESPACE@.sl_path\r
- set pa_conninfo = p_pa_conninfo,\r
- pa_connretry = p_pa_connretry\r
- where pa_server = p_pa_server\r
- and pa_client = p_pa_client;\r
- else\r
- -- ----\r
- -- New path\r
- --\r
- -- In case we receive STORE_PATH events before we know\r
- -- about the nodes involved in this, we generate those nodes\r
- -- as pending.\r
- -- ----\r
- if not exists (select 1 from @NAMESPACE@.sl_node\r
- where no_id = p_pa_server) then\r
- perform @NAMESPACE@.storeNode_int (p_pa_server, ''<event pending>'');\r
- end if;\r
- if not exists (select 1 from @NAMESPACE@.sl_node\r
- where no_id = p_pa_client) then\r
- perform @NAMESPACE@.storeNode_int (p_pa_client, ''<event pending>'');\r
- end if;\r
- insert into @NAMESPACE@.sl_path\r
- (pa_server, pa_client, pa_conninfo, pa_connretry) values\r
- (p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry);\r
- end if;\r
-\r
- return 0;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropPath (pa_server, pa_client)\r
---\r
--- Generate the DROP_PATH event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropPath (int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_pa_server alias for $1;\r
- p_pa_client alias for $2;\r
- v_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- There should be no existing subscriptions. Auto unsubscribing\r
- -- is considered too dangerous. \r
- -- ----\r
- for v_row in select sub_set, sub_provider, sub_receiver\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_provider = p_pa_server\r
- and sub_receiver = p_pa_client\r
- loop\r
- raise exception \r
- ''Slony-I: Path cannot be dropped, subscription of set % needs it'',\r
- v_row.sub_set;\r
- end loop;\r
-\r
- -- ----\r
- -- Drop all sl_listen entries that depend on this path\r
- -- ----\r
- for v_row in select li_origin, li_provider, li_receiver\r
- from @NAMESPACE@.sl_listen\r
- where li_provider = p_pa_server\r
- and li_receiver = p_pa_client\r
- loop\r
- perform @NAMESPACE@.dropListen(\r
- v_row.li_origin, v_row.li_provider, v_row.li_receiver);\r
- end loop;\r
-\r
- -- ----\r
- -- Now drop the path and create the event\r
- -- ----\r
- perform @NAMESPACE@.dropPath_int(p_pa_server, p_pa_client);\r
- return @NAMESPACE@.createEvent (''_@CLUSTERNAME@'', ''DROP_PATH'',\r
- p_pa_server, p_pa_client);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropPath_int (pa_server, pa_client)\r
---\r
--- Process the DROP_NODE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropPath_int (int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_pa_server alias for $1;\r
- p_pa_client alias for $2;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Remove any dangling sl_listen entries with the server\r
- -- as provider and the client as receiver. This must have\r
- -- been cleared out before, but obviously was not.\r
- -- ----\r
- delete from @NAMESPACE@.sl_listen\r
- where li_provider = p_pa_server\r
- and li_receiver = p_pa_client;\r
-\r
- delete from @NAMESPACE@.sl_path\r
- where pa_server = p_pa_server\r
- and pa_client = p_pa_client;\r
-\r
- if found then\r
- return 1;\r
- else\r
- return 0;\r
- end if;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeListen (li_origin, li_provider, li_receiver)\r
---\r
--- Generate the STORE_LISTEN event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeListen (int4, int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_li_origin alias for $1;\r
- p_li_provider alias for $2;\r
- p_li_receiver alias for $3;\r
-begin\r
- perform @NAMESPACE@.storeListen_int (p_li_origin, p_li_provider, p_li_receiver);\r
- return @NAMESPACE@.createEvent (''_@CLUSTERNAME@'', ''STORE_LISTEN'',\r
- p_li_origin, p_li_provider, p_li_receiver);\r
-end;\r
-' language plpgsql\r
- called on null input;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeListen_int (li_origin, li_provider, li_receiver)\r
---\r
--- Process the STORE_LISTEN event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeListen_int (int4, int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_li_origin alias for $1;\r
- p_li_provider alias for $2;\r
- p_li_receiver alias for $3;\r
- v_exists int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- select 1 into v_exists\r
- from @NAMESPACE@.sl_listen\r
- where li_origin = p_li_origin\r
- and li_provider = p_li_provider\r
- and li_receiver = p_li_receiver;\r
- if not found then\r
- -- ----\r
- -- In case we receive STORE_LISTEN events before we know\r
- -- about the nodes involved in this, we generate those nodes\r
- -- as pending.\r
- -- ----\r
- if not exists (select 1 from @NAMESPACE@.sl_node\r
- where no_id = p_li_origin) then\r
- perform @NAMESPACE@.storeNode_int (p_li_origin, ''<event pending>'');\r
- end if;\r
- if not exists (select 1 from @NAMESPACE@.sl_node\r
- where no_id = p_li_provider) then\r
- perform @NAMESPACE@.storeNode_int (p_li_provider, ''<event pending>'');\r
- end if;\r
- if not exists (select 1 from @NAMESPACE@.sl_node\r
- where no_id = p_li_receiver) then\r
- perform @NAMESPACE@.storeNode_int (p_li_receiver, ''<event pending>'');\r
- end if;\r
-\r
- insert into @NAMESPACE@.sl_listen\r
- (li_origin, li_provider, li_receiver) values\r
- (p_li_origin, p_li_provider, p_li_receiver);\r
- end if;\r
-\r
- return 0;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropListen (li_origin, li_provider, li_receiver)\r
---\r
--- Generate the DROP_LISTEN event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropListen (int4, int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_li_origin alias for $1;\r
- p_li_provider alias for $2;\r
- p_li_receiver alias for $3;\r
-begin\r
- perform @NAMESPACE@.dropListen_int(p_li_origin, \r
- p_li_provider, p_li_receiver);\r
- \r
- return @NAMESPACE@.createEvent (''_@CLUSTERNAME@'', ''DROP_LISTEN'',\r
- p_li_origin, p_li_provider, p_li_receiver);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropListen_int (li_origin, li_provider, li_receiver)\r
---\r
--- Process the DROP_LISTEN event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropListen_int (int4, int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_li_origin alias for $1;\r
- p_li_provider alias for $2;\r
- p_li_receiver alias for $3;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- delete from @NAMESPACE@.sl_listen\r
- where li_origin = p_li_origin\r
- and li_provider = p_li_provider\r
- and li_receiver = p_li_receiver;\r
- if found then\r
- return 1;\r
- else\r
- return 0;\r
- end if;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeSet (set_id, set_comment)\r
---\r
--- Generate the STORE_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeSet (int4, text)\r
-returns bigint\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_set_comment alias for $2;\r
- v_local_node_id int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
-\r
- insert into @NAMESPACE@.sl_set\r
- (set_id, set_origin, set_comment) values\r
- (p_set_id, v_local_node_id, p_set_comment);\r
-\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''STORE_SET'', \r
- p_set_id, v_local_node_id, p_set_comment);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeSet_int (set_id, set_origin, set_comment)\r
---\r
--- Process the STORE_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeSet_int (int4, int4, text)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_set_origin alias for $2;\r
- p_set_comment alias for $3;\r
- v_dummy int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- select 1 into v_dummy\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_set_id\r
- for update;\r
- if found then \r
- update @NAMESPACE@.sl_set\r
- set set_comment = p_set_comment\r
- where set_id = p_set_id;\r
- else\r
- if not exists (select 1 from @NAMESPACE@.sl_node\r
- where no_id = p_set_origin) then\r
- perform @NAMESPACE@.storeNode_int (p_set_origin, ''<event pending>'');\r
- end if;\r
- insert into @NAMESPACE@.sl_set\r
- (set_id, set_origin, set_comment) values\r
- (p_set_id, p_set_origin, p_set_comment);\r
- end if;\r
-\r
- return p_set_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION lockSet (set_id)\r
---\r
--- Add a special trigger to all tables of a set that disables\r
--- access to it.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.lockSet (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- v_local_node_id int4;\r
- v_set_row record;\r
- v_tab_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that the set exists and that we are the origin\r
- -- and that it is not already locked.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select * into v_set_row from @NAMESPACE@.sl_set\r
- where set_id = p_set_id\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_set_id;\r
- end if;\r
- if v_set_row.set_origin <> v_local_node_id then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_set_id;\r
- end if;\r
- if v_set_row.set_locked notnull then\r
- raise exception ''Slony-I: set % is already locked'', p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Place the lockedSet trigger on all tables in the set.\r
- -- ----\r
- for v_tab_row in select T.tab_id,\r
- "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) as tab_fqname\r
- from @NAMESPACE@.sl_table T,\r
- "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN\r
- where T.tab_set = p_set_id\r
- and T.tab_reloid = PGC.oid\r
- and PGC.relnamespace = PGN.oid\r
- order by tab_id\r
- loop\r
- execute ''create trigger "_@CLUSTERNAME@_lockedset_'' || \r
- v_tab_row.tab_id || \r
- ''" before insert or update or delete on '' ||\r
- v_tab_row.tab_fqname || '' for each row execute procedure\r
- @NAMESPACE@.lockedSet (''''_@CLUSTERNAME@'''');'';\r
- end loop;\r
-\r
- -- ----\r
- -- Remember our snapshots xmax as for the set locking\r
- -- ----\r
- update @NAMESPACE@.sl_set\r
- set set_locked = @NAMESPACE@.getMaxXid()\r
- where set_id = p_set_id;\r
-\r
- return p_set_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION unlockSet (set_id)\r
---\r
--- Remove the special trigger from all tables of a set that disables\r
--- access to it.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.unlockSet (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- v_local_node_id int4;\r
- v_set_row record;\r
- v_tab_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that the set exists and that we are the origin\r
- -- and that it is not already locked.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select * into v_set_row from @NAMESPACE@.sl_set\r
- where set_id = p_set_id\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_set_id;\r
- end if;\r
- if v_set_row.set_origin <> v_local_node_id then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_set_id;\r
- end if;\r
- if v_set_row.set_locked isnull then\r
- raise exception ''Slony-I: set % is not locked'', p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Drop the lockedSet trigger from all tables in the set.\r
- -- ----\r
- for v_tab_row in select T.tab_id,\r
- "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) as tab_fqname\r
- from @NAMESPACE@.sl_table T,\r
- "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN\r
- where T.tab_set = p_set_id\r
- and T.tab_reloid = PGC.oid\r
- and PGC.relnamespace = PGN.oid\r
- order by tab_id\r
- loop\r
- execute ''drop trigger "_@CLUSTERNAME@_lockedset_'' || \r
- v_tab_row.tab_id || ''" on '' || v_tab_row.tab_fqname;\r
- end loop;\r
-\r
- -- ----\r
- -- Clear out the set_locked field\r
- -- ----\r
- update @NAMESPACE@.sl_set\r
- set set_locked = NULL\r
- where set_id = p_set_id;\r
-\r
- return p_set_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION moveSet (set_id, new_origin)\r
---\r
--- Generate the MOVE_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.moveSet (int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_new_origin alias for $2;\r
- v_local_node_id int4;\r
- v_set_row record;\r
- v_sub_row record;\r
- v_sync_seqno int8;\r
- v_lv_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that the set is locked and that this locking\r
- -- happened long enough ago.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select * into v_set_row from @NAMESPACE@.sl_set\r
- where set_id = p_set_id\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_set_id;\r
- end if;\r
- if v_set_row.set_origin <> v_local_node_id then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_set_id;\r
- end if;\r
- if v_set_row.set_locked isnull then\r
- raise exception ''Slony-I: set % is not locked'', p_set_id;\r
- end if;\r
- if v_set_row.set_locked > @NAMESPACE@.getMinXid() then\r
- raise exception ''Slony-I: cannot move set % yet, transactions < % are still in progress'',\r
- p_set_id, v_set_row.set_locked;\r
- end if;\r
-\r
- -- ----\r
- -- Unlock the set\r
- -- ----\r
- perform @NAMESPACE@.unlockSet(p_set_id);\r
-\r
- -- ----\r
- -- Check that the new_origin is an active subscriber of the set\r
- -- ----\r
- select * into v_sub_row from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = p_new_origin;\r
- if not found then\r
- raise exception ''Slony-I: set % is not subscribed by node %'',\r
- p_set_id, p_new_origin;\r
- end if;\r
- if not v_sub_row.sub_active then\r
- raise exception ''Slony-I: subsctiption of node % for set % is inactive'',\r
- p_new_origin, p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Reconfigure everything\r
- -- ----\r
- perform @NAMESPACE@.moveSet_int(p_set_id, v_local_node_id,\r
- p_new_origin);\r
-\r
- -- ----\r
- -- At this time we hold access exclusive locks for every table\r
- -- in the set. But we did move the set to the new origin, so the\r
- -- createEvent() we are doing now will not record the sequences.\r
- -- ----\r
- v_sync_seqno := @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SYNC'');\r
- insert into @NAMESPACE@.sl_seqlog \r
- (seql_seqid, seql_origin, seql_ev_seqno, seql_last_value)\r
- select seq_id, v_local_node_id, v_sync_seqno, seq_last_value\r
- from @NAMESPACE@.sl_seqlastvalue\r
- where seq_set = p_set_id;\r
- \r
- -- ----\r
- -- Finally we generate the real event\r
- -- ----\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''MOVE_SET'', \r
- p_set_id, v_local_node_id, p_new_origin);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION moveSet_int (set_id, old_origin, new_origin)\r
---\r
--- Process the MOVE_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.moveSet_int (int4, int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_old_origin alias for $2;\r
- p_new_origin alias for $3;\r
- v_local_node_id int4;\r
- v_tab_row record;\r
- v_sub_row record;\r
- v_sub_node int4;\r
- v_sub_last int4;\r
- v_sub_next int4;\r
- v_last_sync int8;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Get our local node ID\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
-\r
- -- ----\r
- -- If we are the old or new origin of the set, we need to\r
- -- remove the log trigger from all tables first.\r
- -- ----\r
- if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then\r
- for v_tab_row in select tab_id from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id\r
- order by tab_id\r
- loop\r
- perform @NAMESPACE@.alterTableRestore(v_tab_row.tab_id);\r
- end loop;\r
- end if;\r
-\r
- -- ----\r
- -- Next we have to reverse the subscription path\r
- -- ----\r
- v_sub_last = p_new_origin;\r
- select sub_provider into v_sub_node\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_receiver = p_new_origin;\r
- if not found then\r
- raise exception ''Slony-I: subscription path broken in moveSet_int'';\r
- end if;\r
- while v_sub_node <> p_old_origin loop\r
- -- ----\r
- -- Tracing node by node, the old receiver is now in\r
- -- v_sub_last and the old provider is in v_sub_node.\r
- -- ----\r
-\r
- -- ----\r
- -- Get the current provider of this node as next\r
- -- and change the provider to the previous one in\r
- -- the reverse chain.\r
- -- ----\r
- select sub_provider into v_sub_next\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = v_sub_node\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: subscription path broken in moveSet_int'';\r
- end if;\r
- update @NAMESPACE@.sl_subscribe\r
- set sub_provider = v_sub_last\r
- where sub_set = p_set_id\r
- and sub_receiver = v_sub_node;\r
-\r
- v_sub_last = v_sub_node;\r
- v_sub_node = v_sub_next;\r
- end loop;\r
-\r
- -- ----\r
- -- This includes creating a subscription for the old origin\r
- -- ----\r
- insert into @NAMESPACE@.sl_subscribe\r
- (sub_set, sub_provider, sub_receiver,\r
- sub_forward, sub_active)\r
- values (p_set_id, v_sub_last, p_old_origin, true, true);\r
- if v_local_node_id = p_old_origin then\r
- select coalesce(max(ev_seqno), 0) into v_last_sync \r
- from @NAMESPACE@.sl_event\r
- where ev_origin = p_new_origin\r
- and ev_type = ''SYNC'';\r
- if v_last_sync > 0 then\r
- insert into @NAMESPACE@.sl_setsync\r
- (ssy_setid, ssy_origin, ssy_seqno,\r
- ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)\r
- select p_set_id, p_new_origin, v_last_sync,\r
- ev_minxid, ev_maxxid, ev_xip, NULL\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = p_new_origin\r
- and ev_seqno = v_last_sync;\r
- else\r
- insert into @NAMESPACE@.sl_setsync\r
- (ssy_setid, ssy_origin, ssy_seqno,\r
- ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)\r
- values (p_set_id, p_new_origin, ''0'',\r
- ''0'', ''0'', '''', NULL);\r
- end if;\r
- end if;\r
-\r
- -- ----\r
- -- Now change the ownership of the set.\r
- -- ----\r
- update @NAMESPACE@.sl_set\r
- set set_origin = p_new_origin\r
- where set_id = p_set_id;\r
-\r
- -- ----\r
- -- On the new origin, delete the obsolete setsync information\r
- -- and the subscription.\r
- -- ----\r
- if v_local_node_id = p_new_origin then\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = p_set_id;\r
- else\r
- if v_local_node_id <> p_old_origin then\r
- --\r
- -- On every other node, change the setsync so that it will\r
- -- pick up from the new origins last known sync.\r
- --\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = p_set_id;\r
- select coalesce(max(ev_seqno), 0) into v_last_sync\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = p_new_origin\r
- and ev_type = ''SYNC'';\r
- if v_last_sync > 0 then\r
- insert into @NAMESPACE@.sl_setsync\r
- (ssy_setid, ssy_origin, ssy_seqno,\r
- ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)\r
- select p_set_id, p_new_origin, v_last_sync,\r
- ev_minxid, ev_maxxid, ev_xip, NULL\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = p_new_origin\r
- and ev_seqno = v_last_sync;\r
- else\r
- insert into @NAMESPACE@.sl_setsync\r
- (ssy_setid, ssy_origin, ssy_seqno,\r
- ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list)\r
- values (p_set_id, p_new_origin, ''0'',\r
- ''0'', ''0'', '''', NULL);\r
- end if;\r
- end if;\r
- end if;\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = p_new_origin;\r
-\r
- -- ----\r
- -- If we are the new or old origin, we have to\r
- -- put all the tables into altered state again.\r
- -- ----\r
- if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then\r
- for v_tab_row in select tab_id from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id\r
- order by tab_id\r
- loop\r
- perform @NAMESPACE@.alterTableForReplication(v_tab_row.tab_id);\r
- end loop;\r
- end if;\r
-\r
- return p_set_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropSet (set_id)\r
---\r
--- Generate the DROP_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropSet (int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- v_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
- \r
- -- ----\r
- -- Check that the set exists and originates here\r
- -- ----\r
- select set_origin into v_origin from @NAMESPACE@.sl_set\r
- where set_id = p_set_id;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_set_id;\r
- end if;\r
- if v_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Call the internal drop set functionality and generate the event\r
- -- ----\r
- perform @NAMESPACE@.dropSet_int(p_set_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''DROP_SET'', \r
- p_set_id);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropSet_int (set_id)\r
---\r
--- Process the DROP_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropSet_int (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- v_tab_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
- \r
- -- ----\r
- -- Restore all tables original triggers and rules and remove\r
- -- our replication stuff.\r
- -- ----\r
- for v_tab_row in select tab_id from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id\r
- order by tab_id\r
- loop\r
- perform @NAMESPACE@.alterTableRestore(v_tab_row.tab_id);\r
- perform @NAMESPACE@.tableDropKey(v_tab_row.tab_id);\r
- end loop;\r
-\r
- -- ----\r
- -- Remove all traces of the set configuration\r
- -- ----\r
- delete from @NAMESPACE@.sl_sequence\r
- where seq_set = p_set_id;\r
- delete from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id;\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id;\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = p_set_id;\r
- delete from @NAMESPACE@.sl_set\r
- where set_id = p_set_id;\r
-\r
- return p_set_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION mergeSet (set_id, add_id)\r
---\r
--- Generate the MERGE_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.mergeSet (int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_add_id alias for $2;\r
- v_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
- \r
- -- ----\r
- -- Check that both sets exist and originate here\r
- -- ----\r
- if p_set_id = p_add_id then\r
- raise exception ''Slony-I: merged set ids cannot be identical'';\r
- end if;\r
- select set_origin into v_origin from @NAMESPACE@.sl_set\r
- where set_id = p_set_id;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_set_id;\r
- end if;\r
- if v_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_set_id;\r
- end if;\r
-\r
- select set_origin into v_origin from @NAMESPACE@.sl_set\r
- where set_id = p_add_id;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_add_id;\r
- end if;\r
- if v_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_add_id;\r
- end if;\r
-\r
- -- ----\r
- -- Check that both sets are subscribed by the same set of nodes\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_subscribe SUB1\r
- where SUB1.sub_set = p_set_id\r
- and SUB1.sub_receiver not in (select SUB2.sub_receiver\r
- from @NAMESPACE@.sl_subscribe SUB2\r
- where SUB2.sub_set = p_add_id))\r
- then\r
- raise exception ''Slony-I: subscriber lists of set % and % are different'',\r
- p_set_id, p_add_id;\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_subscribe SUB1\r
- where SUB1.sub_set = p_add_id\r
- and SUB1.sub_receiver not in (select SUB2.sub_receiver\r
- from @NAMESPACE@.sl_subscribe SUB2\r
- where SUB2.sub_set = p_set_id))\r
- then\r
- raise exception ''Slony-I: subscriber lists of set % and % are different'',\r
- p_add_id, p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Also check that there are no unconfirmed enable subscriptions\r
- -- still lingering (prevents bug 896)\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_event\r
- where ev_origin = v_origin\r
- and ev_type = ''ENABLE_SUBSCRIPTION''\r
- and ev_data1 = p_set_id\r
- and ev_seqno > (select min(max_con_seqno) from\r
- (select con_received, max(con_seqno) as max_con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = v_origin\r
- group by con_received) as CON\r
- )\r
- )\r
- then\r
- raise exception ''Slony-I: set % cannot be merged because of pending subscription'',\r
- p_set_id;\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_event\r
- where ev_origin = v_origin\r
- and ev_type = ''ENABLE_SUBSCRIPTION''\r
- and ev_data1 = p_add_id\r
- and ev_seqno > (select min(max_con_seqno) from\r
- (select con_received, max(con_seqno) as max_con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = v_origin\r
- group by con_received) as CON\r
- )\r
- )\r
- then\r
- raise exception ''Slony-I: set % cannot be merged because of pending subscription'',\r
- p_add_id;\r
- end if;\r
-\r
- -- ----\r
- -- Create a SYNC event, merge the sets, create a MERGE_SET event\r
- -- ----\r
- perform @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SYNC'', NULL);\r
- perform @NAMESPACE@.mergeSet_int(p_set_id, p_add_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''MERGE_SET'', \r
- p_set_id, p_add_id);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION mergeSet_int (set_id, add_id)\r
---\r
--- Process the MERGE_SET event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.mergeSet_int (int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_add_id alias for $2;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
- \r
- update @NAMESPACE@.sl_sequence\r
- set seq_set = p_set_id\r
- where seq_set = p_add_id;\r
- update @NAMESPACE@.sl_table\r
- set tab_set = p_set_id\r
- where tab_set = p_add_id;\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_add_id;\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = p_add_id;\r
- delete from @NAMESPACE@.sl_set\r
- where set_id = p_add_id;\r
-\r
- return p_set_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setAddTable (set_id, tab_id, tab_fqname, tab_idxname,\r
--- tab_comment)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setAddTable(int4, int4, text, name, text)\r
-returns bigint\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_tab_id alias for $2;\r
- p_fqname alias for $3;\r
- p_tab_idxname alias for $4;\r
- p_tab_comment alias for $5;\r
- v_set_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that we are the origin of the set\r
- -- ----\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setAddTable(): set % not found'', p_set_id;\r
- end if;\r
- if v_set_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: setAddTable(): set % has remote origin'', p_set_id;\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id)\r
- then\r
- raise exception ''Slony-I: cannot add table to currently subscribed set %'',\r
- p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Add the table to the set and generate the SET_ADD_TABLE event\r
- -- ----\r
- perform @NAMESPACE@.setAddTable_int(p_set_id, p_tab_id, p_fqname,\r
- p_tab_idxname, p_tab_comment);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SET_ADD_TABLE'',\r
- p_set_id, p_tab_id, p_fqname,\r
- p_tab_idxname, p_tab_comment);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setAddTable_int (set_id, tab_id, tab_fqname, tab_idxname,\r
--- tab_comment)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setAddTable_int(int4, int4, text, name, text)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_tab_id alias for $2;\r
- p_fqname alias for $3;\r
- p_tab_idxname alias for $4;\r
- p_tab_comment alias for $5;\r
- v_local_node_id int4;\r
- v_set_origin int4;\r
- v_sub_provider int4;\r
- v_relkind char;\r
- v_tab_reloid oid;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- For sets with a remote origin, check that we are subscribed \r
- -- to that set. Otherwise we ignore the table because it might \r
- -- not even exist in our database.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setAddTable_int(): set % not found'',\r
- p_set_id;\r
- end if;\r
- if v_set_origin != v_local_node_id then\r
- select sub_provider into v_sub_provider\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- if not found then\r
- return 0;\r
- end if;\r
- end if;\r
- \r
- -- ----\r
- -- Get the tables OID and check that it is a real table\r
- -- ----\r
- select PGC.oid, PGC.relkind into v_tab_reloid, v_relkind\r
- from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN\r
- where PGC.relnamespace = PGN.oid\r
- and p_fqname = "pg_catalog".quote_ident(PGN.nspname) ||\r
- ''.'' || "pg_catalog".quote_ident(PGC.relname);\r
- if not found then\r
- raise exception ''Slony-I: setAddTable(): table % not found'', \r
- p_fqname;\r
- end if;\r
- if v_relkind != ''r'' then\r
- raise exception ''Slony-I: setAddTable(): % is not a regular table'',\r
- p_fqname;\r
- end if;\r
-\r
- if not exists (select indexrelid\r
- from "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGC\r
- where PGX.indrelid = v_tab_reloid\r
- and PGX.indexrelid = PGC.oid\r
- and PGC.relname = p_tab_idxname)\r
- then\r
- raise exception ''Slony-I: setAddTable(): table % has no index %'',\r
- p_fqname, p_tab_idxname;\r
- end if;\r
-\r
- -- ----\r
- -- Add the table to sl_table and create the trigger on it.\r
- -- ----\r
- insert into @NAMESPACE@.sl_table\r
- (tab_id, tab_reloid, tab_set, tab_idxname, \r
- tab_altered, tab_comment) values\r
- (p_tab_id, v_tab_reloid, p_set_id, p_tab_idxname,\r
- false, p_tab_comment);\r
- perform @NAMESPACE@.alterTableForReplication(p_tab_id);\r
-\r
- return p_tab_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setDropTable (tab_id)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setDropTable(int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_tab_id alias for $1;\r
- v_set_id int4;\r
- v_set_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Determine the set_id\r
- -- ----\r
- select tab_set into v_set_id from @NAMESPACE@.sl_table where tab_id = p_tab_id;\r
-\r
- -- ----\r
- -- Ensure table exists\r
- -- ----\r
- if not found then\r
- raise exception ''Slony-I: setDropTable_int(): table % not found'',\r
- p_tab_id;\r
- end if;\r
-\r
- -- ----\r
- -- Check that we are the origin of the set\r
- -- ----\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = v_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setDropTable(): set % not found'', v_set_id;\r
- end if;\r
- if v_set_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: setDropTable(): set % has remote origin'', v_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Drop the table from the set and generate the SET_ADD_TABLE event\r
- -- ----\r
- perform @NAMESPACE@.setDropTable_int(p_tab_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SET_DROP_TABLE'', p_tab_id);\r
-end;\r
-' language plpgsql;\r
-comment on function @NAMESPACE@.setDropTable(int4) is\r
-'setDropTable (tab_id)\r
-\r
-Drop table tab_id from set on origin node, and generate SET_DROP_TABLE\r
-event to allow this to propagate to other nodes.';\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setDropTable_int (tab_id)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setDropTable_int(int4)\r
-returns int4\r
-as '\r
-declare\r
- p_tab_id alias for $1;\r
- v_set_id int4;\r
- v_local_node_id int4;\r
- v_set_origin int4;\r
- v_sub_provider int4;\r
- v_tab_reloid oid;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Determine the set_id\r
- -- ----\r
- select tab_set into v_set_id from @NAMESPACE@.sl_table where tab_id = p_tab_id;\r
-\r
- -- ----\r
- -- Ensure table exists\r
- -- ----\r
- if not found then\r
- return 0;\r
- end if;\r
-\r
- -- ----\r
- -- For sets with a remote origin, check that we are subscribed \r
- -- to that set. Otherwise we ignore the table because it might \r
- -- not even exist in our database.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = v_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setDropTable_int(): set % not found'',\r
- v_set_id;\r
- end if;\r
- if v_set_origin != v_local_node_id then\r
- select sub_provider into v_sub_provider\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_set = v_set_id\r
- and sub_receiver = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- if not found then\r
- return 0;\r
- end if;\r
- end if;\r
- \r
- -- ----\r
- -- Drop the table from sl_table and drop trigger from it.\r
- -- ----\r
- perform @NAMESPACE@.alterTableRestore(p_tab_id);\r
- perform @NAMESPACE@.tableDropKey(p_tab_id);\r
- delete from @NAMESPACE@.sl_table where tab_id = p_tab_id;\r
- return p_tab_id;\r
-end;\r
-' language plpgsql;\r
-comment on function @NAMESPACE@.setDropTable_int(int4) is\r
-'setDropTable_int (tab_id)\r
-\r
-This function processes the SET_DROP_TABLE event on remote nodes,\r
-dropping a table from replication if the remote node is subscribing to\r
-its replication set.';\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setAddSequence (set_id, seq_id, seq_fqname, seq_comment)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setAddSequence (int4, int4, text, text)\r
-returns bigint\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_seq_id alias for $2;\r
- p_fqname alias for $3;\r
- p_seq_comment alias for $4;\r
- v_set_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that we are the origin of the set\r
- -- ----\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setAddSequence(): set % not found'', p_set_id;\r
- end if;\r
- if v_set_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: setAddSequence(): set % has remote origin'', p_set_id;\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id)\r
- then\r
- raise exception ''Slony-I: cannot add sequence to currently subscribed set %'',\r
- p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Add the sequence to the set and generate the SET_ADD_SEQUENCE event\r
- -- ----\r
- perform @NAMESPACE@.setAddSequence_int(p_set_id, p_seq_id, p_fqname,\r
- p_seq_comment);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SET_ADD_SEQUENCE'',\r
- p_set_id, p_seq_id, p_fqname, p_seq_comment);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setAddSequence_int (set_id, seq_id, seq_fqname, seq_comment\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setAddSequence_int(int4, int4, text, text)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_seq_id alias for $2;\r
- p_fqname alias for $3;\r
- p_seq_comment alias for $4;\r
- v_local_node_id int4;\r
- v_set_origin int4;\r
- v_sub_provider int4;\r
- v_relkind char;\r
- v_seq_reloid oid;\r
- v_sync_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- For sets with a remote origin, check that we are subscribed \r
- -- to that set. Otherwise we ignore the sequence because it might \r
- -- not even exist in our database.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setAddSequence_int(): set % not found'',\r
- p_set_id;\r
- end if;\r
- if v_set_origin != v_local_node_id then\r
- select sub_provider into v_sub_provider\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- if not found then\r
- return 0;\r
- end if;\r
- end if;\r
- \r
- -- ----\r
- -- Get the sequences OID and check that it is a sequence\r
- -- ----\r
- select PGC.oid, PGC.relkind into v_seq_reloid, v_relkind\r
- from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN\r
- where PGC.relnamespace = PGN.oid\r
- and p_fqname = "pg_catalog".quote_ident(PGN.nspname) ||\r
- ''.'' || "pg_catalog".quote_ident(PGC.relname);\r
- if not found then\r
- raise exception ''Slony-I: setAddSequence_int(): sequence % not found'', \r
- p_fqname;\r
- end if;\r
- if v_relkind != ''S'' then\r
- raise exception ''Slony-I: setAddSequence_int(): % is not a sequence'',\r
- p_fqname;\r
- end if;\r
-\r
- -- ----\r
- -- Add the sequence to sl_sequence\r
- -- ----\r
- insert into @NAMESPACE@.sl_sequence\r
- (seq_id, seq_reloid, seq_set, seq_comment) values\r
- (p_seq_id, v_seq_reloid, p_set_id, p_seq_comment);\r
-\r
- -- ----\r
- -- On the set origin, fake a sl_seqlog row for the last sync event\r
- -- ----\r
- if v_set_origin = v_local_node_id then\r
- for v_sync_row in select coalesce (max(ev_seqno), 0) as ev_seqno\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = v_local_node_id\r
- and ev_type = ''SYNC''\r
- loop\r
- insert into @NAMESPACE@.sl_seqlog\r
- (seql_seqid, seql_origin, seql_ev_seqno, \r
- seql_last_value) values\r
- (p_seq_id, v_local_node_id, v_sync_row.ev_seqno,\r
- @NAMESPACE@.sequenceLastValue(p_fqname));\r
- end loop;\r
- end if;\r
-\r
- return p_seq_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setDropSequence (seq_id)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setDropSequence (int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_seq_id alias for $1;\r
- v_set_id int4;\r
- v_set_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Determine set id for this sequence\r
- -- ----\r
- select seq_set into v_set_id from @NAMESPACE@.sl_sequence where seq_id = p_seq_id;\r
-\r
- -- ----\r
- -- Ensure sequence exists\r
- -- ----\r
- if not found then\r
- raise exception ''Slony-I: setDropSequence_int(): sequence % not found'',\r
- p_seq_id;\r
- end if;\r
-\r
- -- ----\r
- -- Check that we are the origin of the set\r
- -- ----\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = v_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setDropSequence(): set % not found'', v_set_id;\r
- end if;\r
- if v_set_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: setDropSequence(): set % has remote origin'', v_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Add the sequence to the set and generate the SET_ADD_SEQUENCE event\r
- -- ----\r
- perform @NAMESPACE@.setDropSequence_int(p_seq_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SET_DROP_SEQUENCE'',\r
- p_seq_id);\r
-end;\r
-' language plpgsql;\r
-comment on function @NAMESPACE@.setDropSequence (int4) is\r
-'setDropSequence (seq_id)\r
-\r
-On the origin node for the set, drop sequence seq_id from replication\r
-set, and raise SET_DROP_SEQUENCE to cause this to replicate to\r
-subscriber nodes.';\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setDropSequence_int (seq_id)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setDropSequence_int(int4)\r
-returns int4\r
-as '\r
-declare\r
- p_seq_id alias for $1;\r
- v_set_id int4;\r
- v_local_node_id int4;\r
- v_set_origin int4;\r
- v_sub_provider int4;\r
- v_relkind char;\r
- v_sync_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Determine set id for this sequence\r
- -- ----\r
- select seq_set into v_set_id from @NAMESPACE@.sl_sequence where seq_id = p_seq_id;\r
-\r
- -- ----\r
- -- Ensure sequence exists\r
- -- ----\r
- if not found then\r
- return 0;\r
- end if;\r
-\r
- -- ----\r
- -- For sets with a remote origin, check that we are subscribed \r
- -- to that set. Otherwise we ignore the sequence because it might \r
- -- not even exist in our database.\r
- -- ----\r
- v_local_node_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = v_set_id;\r
- if not found then\r
- raise exception ''Slony-I: setDropSequence_int(): set % not found'',\r
- v_set_id;\r
- end if;\r
- if v_set_origin != v_local_node_id then\r
- select sub_provider into v_sub_provider\r
- from @NAMESPACE@.sl_subscribe\r
- where sub_set = v_set_id\r
- and sub_receiver = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- if not found then\r
- return 0;\r
- end if;\r
- end if;\r
-\r
- -- ----\r
- -- drop the sequence from sl_sequence, sl_seqlog\r
- -- ----\r
- delete from @NAMESPACE@.sl_seqlog where seql_seqid = p_seq_id;\r
- delete from @NAMESPACE@.sl_sequence where seq_id = p_seq_id;\r
-\r
- return p_seq_id;\r
-end;\r
-' language plpgsql;\r
-comment on function @NAMESPACE@.setDropSequence_int(int4) is\r
-'setDropSequence_int (seq_id)\r
-\r
-This processes the SET_DROP_SEQUENCE event. On remote nodes that\r
-subscribe to the set containing sequence seq_id, drop the sequence\r
-from the replication set.';\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setMoveTable (tab_id, new_set_id)\r
---\r
--- Generate the SET_MOVE_TABLE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setMoveTable (int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_tab_id alias for $1;\r
- p_new_set_id alias for $2;\r
- v_old_set_id int4;\r
- v_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Get the tables current set\r
- -- ----\r
- select tab_set into v_old_set_id from @NAMESPACE@.sl_table\r
- where tab_id = p_tab_id;\r
- if not found then\r
- raise exception ''Slony-I: table %d not found'', p_tab_id;\r
- end if;\r
- \r
- -- ----\r
- -- Check that both sets exist and originate here\r
- -- ----\r
- if p_new_set_id = v_old_set_id then\r
- raise exception ''Slony-I: set ids cannot be identical'';\r
- end if;\r
- select set_origin into v_origin from @NAMESPACE@.sl_set\r
- where set_id = p_new_set_id;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_new_set_id;\r
- end if;\r
- if v_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_new_set_id;\r
- end if;\r
-\r
- select set_origin into v_origin from @NAMESPACE@.sl_set\r
- where set_id = v_old_set_id;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', v_old_set_id;\r
- end if;\r
- if v_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- v_old_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Check that both sets are subscribed by the same set of nodes\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_subscribe SUB1\r
- where SUB1.sub_set = p_new_set_id\r
- and SUB1.sub_receiver not in (select SUB2.sub_receiver\r
- from @NAMESPACE@.sl_subscribe SUB2\r
- where SUB2.sub_set = v_old_set_id))\r
- then\r
- raise exception ''Slony-I: subscriber lists of set % and % are different'',\r
- p_new_set_id, v_old_set_id;\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_subscribe SUB1\r
- where SUB1.sub_set = v_old_set_id\r
- and SUB1.sub_receiver not in (select SUB2.sub_receiver\r
- from @NAMESPACE@.sl_subscribe SUB2\r
- where SUB2.sub_set = p_new_set_id))\r
- then\r
- raise exception ''Slony-I: subscriber lists of set % and % are different'',\r
- v_old_set_id, p_new_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Also check that there are no unconfirmed enable subscriptions\r
- -- still lingering (prevents bug 896)\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_event\r
- where ev_origin = v_origin\r
- and ev_type = ''ENABLE_SUBSCRIPTION''\r
- and ev_data1 = v_old_set_id\r
- and ev_seqno > (select min(max_con_seqno) from\r
- (select con_received, max(con_seqno) as max_con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = v_origin\r
- group by con_received) as CON\r
- )\r
- )\r
- then\r
- raise exception ''Slony-I: table cannot be moved because of pending subscription'';\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_event\r
- where ev_origin = v_origin\r
- and ev_type = ''ENABLE_SUBSCRIPTION''\r
- and ev_data1 = p_new_set_id\r
- and ev_seqno > (select min(max_con_seqno) from\r
- (select con_received, max(con_seqno) as max_con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = v_origin\r
- group by con_received) as CON\r
- )\r
- )\r
- then\r
- raise exception ''Slony-I: table cannot be moved because of pending subscription'';\r
- end if;\r
-\r
- -- ----\r
- -- Change the set the table belongs to\r
- -- ----\r
- perform @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SYNC'', NULL);\r
- perform @NAMESPACE@.setMoveTable_int(p_tab_id, p_new_set_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SET_MOVE_TABLE'', \r
- p_tab_id, p_new_set_id);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setMoveTable_int (tab_id, new_set_id)\r
---\r
--- Process the SET_MOVE_TABLE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setMoveTable_int (int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_tab_id alias for $1;\r
- p_new_set_id alias for $2;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
- \r
- -- ----\r
- -- Move the table to the new set\r
- -- ----\r
- update @NAMESPACE@.sl_table\r
- set tab_set = p_new_set_id\r
- where tab_id = p_tab_id;\r
-\r
- return p_tab_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setMoveSequence (seq_id, new_set_id)\r
---\r
--- Generate the SET_MOVE_SEQUENCE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setMoveSequence (int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_seq_id alias for $1;\r
- p_new_set_id alias for $2;\r
- v_old_set_id int4;\r
- v_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Get the sequences current set\r
- -- ----\r
- select seq_set into v_old_set_id from @NAMESPACE@.sl_sequence\r
- where seq_id = p_seq_id;\r
- if not found then\r
- raise exception ''Slony-I: sequence %d not found'', p_seq_id;\r
- end if;\r
- \r
- -- ----\r
- -- Check that both sets exist and originate here\r
- -- ----\r
- if p_new_set_id = v_old_set_id then\r
- raise exception ''Slony-I: set ids cannot be identical'';\r
- end if;\r
- select set_origin into v_origin from @NAMESPACE@.sl_set\r
- where set_id = p_new_set_id;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_new_set_id;\r
- end if;\r
- if v_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_new_set_id;\r
- end if;\r
-\r
- select set_origin into v_origin from @NAMESPACE@.sl_set\r
- where set_id = v_old_set_id;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', v_old_set_id;\r
- end if;\r
- if v_origin != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- v_old_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Check that both sets are subscribed by the same set of nodes\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_subscribe SUB1\r
- where SUB1.sub_set = p_new_set_id\r
- and SUB1.sub_receiver not in (select SUB2.sub_receiver\r
- from @NAMESPACE@.sl_subscribe SUB2\r
- where SUB2.sub_set = v_old_set_id))\r
- then\r
- raise exception ''Slony-I: subscriber lists of set % and % are different'',\r
- p_new_set_id, v_old_set_id;\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_subscribe SUB1\r
- where SUB1.sub_set = v_old_set_id\r
- and SUB1.sub_receiver not in (select SUB2.sub_receiver\r
- from @NAMESPACE@.sl_subscribe SUB2\r
- where SUB2.sub_set = p_new_set_id))\r
- then\r
- raise exception ''Slony-I: subscriber lists of set % and % are different'',\r
- v_old_set_id, p_new_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Also check that there are no unconfirmed enable subscriptions\r
- -- still lingering (prevents bug 896)\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_event\r
- where ev_origin = v_origin\r
- and ev_type = ''ENABLE_SUBSCRIPTION''\r
- and ev_data1 = v_old_set_id\r
- and ev_seqno > (select min(max_con_seqno) from\r
- (select con_received, max(con_seqno) as max_con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = v_origin\r
- group by con_received) as CON\r
- )\r
- )\r
- then\r
- raise exception ''Slony-I: sequence cannot be moved because of pending subscription'';\r
- end if;\r
-\r
- if exists (select true from @NAMESPACE@.sl_event\r
- where ev_origin = v_origin\r
- and ev_type = ''ENABLE_SUBSCRIPTION''\r
- and ev_data1 = p_new_set_id\r
- and ev_seqno > (select min(max_con_seqno) from\r
- (select con_received, max(con_seqno) as max_con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = v_origin\r
- group by con_received) as CON\r
- )\r
- )\r
- then\r
- raise exception ''Slony-I: sequence cannot be moved because of pending subscription'';\r
- end if;\r
-\r
- -- ----\r
- -- Change the set the sequence belongs to\r
- -- ----\r
- perform @NAMESPACE@.setMoveSequence_int(p_seq_id, p_new_set_id);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SET_MOVE_SEQUENCE'', \r
- p_seq_id, p_new_set_id);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION setMoveSequence_int (seq_id, new_set_id)\r
---\r
--- Process the SET_MOVE_SEQUENCE event.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.setMoveSequence_int (int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_seq_id alias for $1;\r
- p_new_set_id alias for $2;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
- \r
- -- ----\r
- -- Move the sequence to the new set\r
- -- ----\r
- update @NAMESPACE@.sl_sequence\r
- set seq_set = p_new_set_id\r
- where seq_id = p_seq_id;\r
-\r
- return p_seq_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION sequenceSetValue (seq_id, seq_origin, ev_seqno, last_value)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.sequenceSetValue(int4, int4, int8, int8) returns int4\r
-as '\r
-declare\r
- p_seq_id alias for $1;\r
- p_seq_origin alias for $2;\r
- p_ev_seqno alias for $3;\r
- p_last_value alias for $4;\r
- v_fqname text;\r
-begin\r
- -- ----\r
- -- Get the sequences fully qualified name\r
- -- ----\r
- select "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) into v_fqname\r
- from @NAMESPACE@.sl_sequence SQ,\r
- "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN\r
- where SQ.seq_id = p_seq_id\r
- and SQ.seq_reloid = PGC.oid\r
- and PGC.relnamespace = PGN.oid;\r
- if not found then\r
- raise exception ''Slony-I: sequence % not found'', p_seq_id;\r
- end if;\r
-\r
- -- ----\r
- -- Update it to the new value\r
- -- ----\r
- execute ''select setval('''''' || v_fqname ||\r
- '''''', '''''' || p_last_value || '''''')'';\r
-\r
- insert into @NAMESPACE@.sl_seqlog\r
- (seql_seqid, seql_origin, seql_ev_seqno, seql_last_value)\r
- values (p_seq_id, p_seq_origin, p_ev_seqno, p_last_value);\r
-\r
- return p_seq_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeTrigger (trig_tabid, trig_tgname)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeTrigger (int4, name)\r
-returns bigint\r
-as '\r
-declare\r
- p_trig_tabid alias for $1;\r
- p_trig_tgname alias for $2;\r
-begin\r
- perform @NAMESPACE@.storeTrigger_int(p_trig_tabid, p_trig_tgname);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''STORE_TRIGGER'',\r
- p_trig_tabid, p_trig_tgname);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION storeTrigger_int (trig_tabid, trig_tgname)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.storeTrigger_int (int4, name)\r
-returns int4\r
-as '\r
-declare\r
- p_trig_tabid alias for $1;\r
- p_trig_tgname alias for $2;\r
- v_tab_altered boolean;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Get the current table status (altered or not)\r
- -- ----\r
- select tab_altered into v_tab_altered\r
- from @NAMESPACE@.sl_table where tab_id = p_trig_tabid;\r
- if not found then\r
- -- ----\r
- -- Not found is no hard error here, because that might\r
- -- mean that we are not subscribed to that set\r
- -- ----\r
- return 0;\r
- end if;\r
-\r
- -- ----\r
- -- If the table is modified for replication, restore the original state\r
- -- ----\r
- if v_tab_altered then\r
- perform @NAMESPACE@.alterTableRestore(p_trig_tabid);\r
- end if;\r
-\r
- -- ----\r
- -- Make sure that an entry for this trigger exists\r
- -- ----\r
- delete from @NAMESPACE@.sl_trigger\r
- where trig_tabid = p_trig_tabid\r
- and trig_tgname = p_trig_tgname;\r
- insert into @NAMESPACE@.sl_trigger (\r
- trig_tabid, trig_tgname\r
- ) values (\r
- p_trig_tabid, p_trig_tgname\r
- );\r
-\r
- -- ----\r
- -- Put the table back into replicated state if it was\r
- -- ----\r
- if v_tab_altered then\r
- perform @NAMESPACE@.alterTableForReplication(p_trig_tabid);\r
- end if;\r
-\r
- return p_trig_tabid;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropTrigger (trig_tabid, trig_tgname)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropTrigger (int4, name)\r
-returns bigint\r
-as '\r
-declare\r
- p_trig_tabid alias for $1;\r
- p_trig_tgname alias for $2;\r
-begin\r
- perform @NAMESPACE@.dropTrigger_int(p_trig_tabid, p_trig_tgname);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''DROP_TRIGGER'',\r
- p_trig_tabid, p_trig_tgname);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION dropTrigger_int (trig_tabid, trig_tgname)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.dropTrigger_int (int4, name)\r
-returns int4\r
-as '\r
-declare\r
- p_trig_tabid alias for $1;\r
- p_trig_tgname alias for $2;\r
- v_tab_altered boolean;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Get the current table status (altered or not)\r
- -- ----\r
- select tab_altered into v_tab_altered\r
- from @NAMESPACE@.sl_table where tab_id = p_trig_tabid;\r
- if not found then\r
- -- ----\r
- -- Not found is no hard error here, because that might\r
- -- mean that we are not subscribed to that set\r
- -- ----\r
- return 0;\r
- end if;\r
-\r
- -- ----\r
- -- If the table is modified for replication, restore the original state\r
- -- ----\r
- if v_tab_altered then\r
- perform @NAMESPACE@.alterTableRestore(p_trig_tabid);\r
- end if;\r
-\r
- -- ----\r
- -- Remove the entry from sl_trigger\r
- -- ----\r
- delete from @NAMESPACE@.sl_trigger\r
- where trig_tabid = p_trig_tabid\r
- and trig_tgname = p_trig_tgname;\r
-\r
- -- ----\r
- -- Put the table back into replicated state if it was\r
- -- ----\r
- if v_tab_altered then\r
- perform @NAMESPACE@.alterTableForReplication(p_trig_tabid);\r
- end if;\r
-\r
- return p_trig_tabid;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION ddlScript (set_id, script)\r
---\r
--- Generate the DDL_SCRIPT event\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.ddlScript (int4, text)\r
-returns bigint\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_script alias for $2;\r
- v_set_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that the set exists and originates here\r
- -- ----\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_set_id\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_set_id;\r
- end if;\r
- if v_set_origin <> @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: set % does not originate on local node'',\r
- p_set_id;\r
- end if;\r
-\r
- -- ----\r
- -- Create a SYNC event, run the script and generate the DDL_SCRIPT event\r
- -- ----\r
- perform @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SYNC'', NULL);\r
- perform @NAMESPACE@.ddlScript_int(p_set_id, p_script);\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''DDL_SCRIPT'', \r
- p_set_id, p_script);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION ddlScript_int (set_id, script)\r
---\r
--- Process the DDL_SCRIPT event\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.ddlScript_int (int4, text)\r
-returns int4\r
-as '\r
-declare\r
- p_set_id alias for $1;\r
- p_script alias for $2;\r
- v_set_origin int4;\r
- v_no_id int4;\r
- v_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that we either are the set origin or a current\r
- -- subscriber of the set.\r
- -- ----\r
- v_no_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_set_id\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_set_id;\r
- end if;\r
- if v_set_origin <> v_no_id\r
- and not exists (select 1 from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_set_id\r
- and sub_receiver = v_no_id)\r
- then\r
- return 0;\r
- end if;\r
-\r
- -- ----\r
- -- Restore all original triggers and rules\r
- -- ----\r
- for v_row in select * from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id\r
- loop\r
- perform @NAMESPACE@.alterTableRestore(v_row.tab_id);\r
- end loop;\r
-\r
- -- ----\r
- -- Run the script\r
- -- ----\r
- execute p_script;\r
-\r
- -- ----\r
- -- Put all tables back into replicated mode\r
- -- ----\r
- for v_row in select * from @NAMESPACE@.sl_table\r
- where tab_set = p_set_id\r
- loop\r
- perform @NAMESPACE@.alterTableForReplication(v_row.tab_id);\r
- end loop;\r
-\r
- return p_set_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION alterTableForReplication (tab_id)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.alterTableForReplication (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_tab_id alias for $1;\r
- v_no_id int4;\r
- v_tab_row record;\r
- v_tab_fqname text;\r
- v_tab_attkind text;\r
- v_n int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Get our local node ID\r
- -- ----\r
- v_no_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
-\r
- -- ----\r
- -- Get the sl_table row and the current tables origin. Check\r
- -- that the table currently is NOT in altered state.\r
- -- ----\r
- select T.tab_reloid, T.tab_set, T.tab_idxname, T.tab_altered,\r
- S.set_origin, PGX.indexrelid,\r
- "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) as tab_fqname\r
- into v_tab_row\r
- from @NAMESPACE@.sl_table T, @NAMESPACE@.sl_set S,\r
- "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGXC\r
- where T.tab_id = p_tab_id\r
- and T.tab_set = S.set_id\r
- and T.tab_reloid = PGC.oid\r
- and PGC.relnamespace = PGN.oid\r
- and PGX.indrelid = T.tab_reloid\r
- and PGX.indexrelid = PGXC.oid\r
- and PGXC.relname = T.tab_idxname\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: Table with id % not found'', p_tab_id;\r
- end if;\r
- v_tab_fqname = v_tab_row.tab_fqname;\r
- if v_tab_row.tab_altered then\r
- raise exception ''Slony-I: Table % is already in altered state'',\r
- v_tab_fqname;\r
- end if;\r
-\r
- v_tab_attkind := @NAMESPACE@.determineAttKindUnique(v_tab_row.tab_fqname, \r
- v_tab_row.tab_idxname);\r
-\r
- execute ''lock table '' || v_tab_fqname || '' in access exclusive mode'';\r
-\r
- -- ----\r
- -- Procedures are different on origin and subscriber\r
- -- ----\r
- if v_no_id = v_tab_row.set_origin then\r
- -- ----\r
- -- On the Origin we add the log trigger to the table and done\r
- -- ----\r
- execute ''create trigger "_@CLUSTERNAME@_logtrigger_'' || \r
- p_tab_id || ''" after insert or update or delete on '' ||\r
- v_tab_fqname || '' for each row execute procedure\r
- @NAMESPACE@.logTrigger (''''_@CLUSTERNAME@'''', '''''' || \r
- p_tab_id || '''''', '''''' || \r
- v_tab_attkind || '''''');'';\r
- else\r
- -- ----\r
- -- On the subscriber the thing is a bit more difficult. We want\r
- -- to disable all user- and foreign key triggers and rules.\r
- -- ----\r
-\r
-\r
- -- ----\r
- -- Disable all existing triggers\r
- -- ----\r
- update "pg_catalog".pg_trigger\r
- set tgrelid = v_tab_row.indexrelid\r
- where tgrelid = v_tab_row.tab_reloid\r
- and not exists (\r
- select true from @NAMESPACE@.sl_table TAB,\r
- @NAMESPACE@.sl_trigger TRIG\r
- where TAB.tab_reloid = tgrelid\r
- and TAB.tab_id = TRIG.trig_tabid\r
- and TRIG.trig_tgname = tgname\r
- );\r
- get diagnostics v_n = row_count;\r
- if v_n > 0 then\r
- update "pg_catalog".pg_class\r
- set reltriggers = reltriggers - v_n\r
- where oid = v_tab_row.tab_reloid;\r
- end if;\r
-\r
- -- ----\r
- -- Disable all existing rules\r
- -- ----\r
- update "pg_catalog".pg_rewrite\r
- set ev_class = v_tab_row.indexrelid\r
- where ev_class = v_tab_row.tab_reloid;\r
- get diagnostics v_n = row_count;\r
- if v_n > 0 then\r
- update "pg_catalog".pg_class\r
- set relhasrules = false\r
- where oid = v_tab_row.tab_reloid;\r
- end if;\r
-\r
- -- ----\r
- -- Add the trigger that denies write access to replicated tables\r
- -- ----\r
- execute ''create trigger "_@CLUSTERNAME@_denyaccess_'' || \r
- p_tab_id || ''" before insert or update or delete on '' ||\r
- v_tab_fqname || '' for each row execute procedure\r
- @NAMESPACE@.denyAccess (''''_@CLUSTERNAME@'''');'';\r
- end if;\r
-\r
- -- ----\r
- -- Mark the table altered in our configuration\r
- -- ----\r
- update @NAMESPACE@.sl_table\r
- set tab_altered = true where tab_id = p_tab_id;\r
-\r
- return p_tab_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION alterTableRestore (tab_id)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.alterTableRestore (int4)\r
-returns int4\r
-as '\r
-declare\r
- p_tab_id alias for $1;\r
- v_no_id int4;\r
- v_tab_row record;\r
- v_tab_fqname text;\r
- v_n int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Get our local node ID\r
- -- ----\r
- v_no_id := @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'');\r
-\r
- -- ----\r
- -- Get the sl_table row and the current tables origin. Check\r
- -- that the table currently IS in altered state.\r
- -- ----\r
- select T.tab_reloid, T.tab_set, T.tab_altered,\r
- S.set_origin, PGX.indexrelid,\r
- "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) as tab_fqname\r
- into v_tab_row\r
- from @NAMESPACE@.sl_table T, @NAMESPACE@.sl_set S,\r
- "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGXC\r
- where T.tab_id = p_tab_id\r
- and T.tab_set = S.set_id\r
- and T.tab_reloid = PGC.oid\r
- and PGC.relnamespace = PGN.oid\r
- and PGX.indrelid = T.tab_reloid\r
- and PGX.indexrelid = PGXC.oid\r
- and PGXC.relname = T.tab_idxname\r
- for update;\r
- if not found then\r
- raise exception ''Slony-I: Table with id % not found'', p_tab_id;\r
- end if;\r
- v_tab_fqname = v_tab_row.tab_fqname;\r
- if not v_tab_row.tab_altered then\r
- raise exception ''Slony-I: Table % is not in altered state'',\r
- v_tab_fqname;\r
- end if;\r
-\r
- execute ''lock table '' || v_tab_fqname || '' in access exclusive mode'';\r
-\r
- -- ----\r
- -- Procedures are different on origin and subscriber\r
- -- ----\r
- if v_no_id = v_tab_row.set_origin then\r
- -- ----\r
- -- On the Origin we just drop the trigger we originally added\r
- -- ----\r
- execute ''drop trigger "_@CLUSTERNAME@_logtrigger_'' || \r
- p_tab_id || ''" on '' || v_tab_fqname;\r
- else\r
- -- ----\r
- -- On the subscriber drop the denyAccess trigger\r
- -- ----\r
- execute ''drop trigger "_@CLUSTERNAME@_denyaccess_'' || \r
- p_tab_id || ''" on '' || v_tab_fqname;\r
- \r
- -- ----\r
- -- Restore all original triggers\r
- -- ----\r
- update "pg_catalog".pg_trigger\r
- set tgrelid = v_tab_row.tab_reloid\r
- where tgrelid = v_tab_row.indexrelid;\r
- get diagnostics v_n = row_count;\r
- if v_n > 0 then\r
- update "pg_catalog".pg_class\r
- set reltriggers = reltriggers + v_n\r
- where oid = v_tab_row.tab_reloid;\r
- end if;\r
-\r
- -- ----\r
- -- Restore all original rewrite rules\r
- -- ----\r
- update "pg_catalog".pg_rewrite\r
- set ev_class = v_tab_row.tab_reloid\r
- where ev_class = v_tab_row.indexrelid;\r
- get diagnostics v_n = row_count;\r
- if v_n > 0 then\r
- update "pg_catalog".pg_class\r
- set relhasrules = true\r
- where oid = v_tab_row.tab_reloid;\r
- end if;\r
-\r
- end if;\r
-\r
- -- ----\r
- -- Mark the table not altered in our configuration\r
- -- ----\r
- update @NAMESPACE@.sl_table\r
- set tab_altered = false where tab_id = p_tab_id;\r
-\r
- return p_tab_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION subscribeSet (sub_set, sub_provider, sub_receiver, sub_forward)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.subscribeSet (int4, int4, int4, bool)\r
-returns bigint\r
-as '\r
-declare\r
- p_sub_set alias for $1;\r
- p_sub_provider alias for $2;\r
- p_sub_receiver alias for $3;\r
- p_sub_forward alias for $4;\r
- v_set_origin int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that this is called on the receiver node\r
- -- ----\r
- if p_sub_receiver != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: subscribeSet() must be called on receiver'';\r
- end if;\r
-\r
- -- ----\r
- -- Check that the origin and provider of the set are remote\r
- -- ----\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_sub_set;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_sub_set;\r
- end if;\r
- if v_set_origin = p_sub_receiver then\r
- raise exception \r
- ''Slony-I: set origin and receiver cannot be identical'';\r
- end if;\r
- if p_sub_receiver = p_sub_provider then\r
- raise exception \r
- ''Slony-I: set provider and receiver cannot be identical'';\r
- end if;\r
-\r
- -- ----\r
- -- Call the internal procedure to store the subscription\r
- -- ----\r
- perform @NAMESPACE@.subscribeSet_int(p_sub_set, p_sub_provider,\r
- p_sub_receiver, p_sub_forward);\r
-\r
- -- ----\r
- -- Create the SUBSCRIBE_SET event\r
- -- ----\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''SUBSCRIBE_SET'', \r
- p_sub_set, p_sub_provider, p_sub_receiver, \r
- case p_sub_forward when true then ''t'' else ''f'' end);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION subscribeSet_int (sub_set, sub_provider, sub_receiver, sub_forward)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.subscribeSet_int (int4, int4, int4, bool)\r
-returns int4\r
-as '\r
-declare\r
- p_sub_set alias for $1;\r
- p_sub_provider alias for $2;\r
- p_sub_receiver alias for $3;\r
- p_sub_forward alias for $4;\r
- v_set_origin int4;\r
- v_sub_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Provider change is only allowed for active sets\r
- -- ----\r
- if p_sub_receiver = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- select sub_active into v_sub_row from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_sub_set\r
- and sub_receiver = p_sub_receiver;\r
- if found then\r
- if not v_sub_row.sub_active then\r
- raise exception ''Slony-I: set % is not active, cannot change provider'',\r
- p_sub_set;\r
- end if;\r
- end if;\r
- end if;\r
-\r
- -- ----\r
- -- Try to change provider and/or forward for an existing subscription\r
- -- ----\r
- update @NAMESPACE@.sl_subscribe\r
- set sub_provider = p_sub_provider,\r
- sub_forward = p_sub_forward\r
- where sub_set = p_sub_set\r
- and sub_receiver = p_sub_receiver;\r
- if found then\r
- return p_sub_set;\r
- end if;\r
-\r
- -- ----\r
- -- Not found, insert a new one\r
- -- ----\r
- if not exists (select true from @NAMESPACE@.sl_path\r
- where pa_server = p_sub_provider\r
- and pa_client = p_sub_receiver)\r
- then\r
- insert into @NAMESPACE@.sl_path\r
- (pa_server, pa_client, pa_conninfo, pa_connretry)\r
- values \r
- (p_sub_provider, p_sub_receiver, \r
- ''<event pending>'', 10);\r
- end if;\r
- insert into @NAMESPACE@.sl_subscribe\r
- (sub_set, sub_provider, sub_receiver, sub_forward, sub_active)\r
- values (p_sub_set, p_sub_provider, p_sub_receiver,\r
- p_sub_forward, false);\r
-\r
- -- ----\r
- -- If the set origin is here, then enable the subscription\r
- -- ----\r
- select set_origin into v_set_origin\r
- from @NAMESPACE@.sl_set\r
- where set_id = p_sub_set;\r
- if not found then\r
- raise exception ''Slony-I: set % not found'', p_sub_set;\r
- end if;\r
-\r
- if v_set_origin = @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- perform @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''ENABLE_SUBSCRIPTION'', \r
- p_sub_set, p_sub_provider, p_sub_receiver, \r
- case p_sub_forward when true then ''t'' else ''f'' end);\r
- perform @NAMESPACE@.enableSubscription(p_sub_set, \r
- p_sub_provider, p_sub_receiver);\r
- end if;\r
-\r
- return p_sub_set;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION unsubscribeSet (sub_set, sub_receiver)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.unsubscribeSet (int4, int4)\r
-returns bigint\r
-as '\r
-declare\r
- p_sub_set alias for $1;\r
- p_sub_receiver alias for $2;\r
- v_tab_row record;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Check that this is called on the receiver node\r
- -- ----\r
- if p_sub_receiver != @NAMESPACE@.getLocalNodeId(''_@CLUSTERNAME@'') then\r
- raise exception ''Slony-I: unsubscribeSet() must be called on receiver'';\r
- end if;\r
-\r
- -- ----\r
- -- Check that this does not break any chains\r
- -- ----\r
- if exists (select true from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_sub_set\r
- and sub_provider = p_sub_receiver)\r
- then\r
- raise exception ''Slony-I: Cannot unsubscibe set % while being provider'',\r
- p_sub_set;\r
- end if;\r
-\r
- -- ----\r
- -- Restore all tables original triggers and rules and remove\r
- -- our replication stuff.\r
- -- ----\r
- for v_tab_row in select tab_id from @NAMESPACE@.sl_table\r
- where tab_set = p_sub_set\r
- order by tab_id\r
- loop\r
- perform @NAMESPACE@.alterTableRestore(v_tab_row.tab_id);\r
- perform @NAMESPACE@.tableDropKey(v_tab_row.tab_id);\r
- end loop;\r
-\r
- -- ----\r
- -- Remove the setsync status. This will also cause the\r
- -- worker thread to ignore the set and stop replicating\r
- -- right now.\r
- -- ----\r
- delete from @NAMESPACE@.sl_setsync\r
- where ssy_setid = p_sub_set;\r
-\r
- -- ----\r
- -- Remove all sl_table and sl_sequence entries for this set.\r
- -- Should we ever subscribe again, the initial data\r
- -- copy process will create new ones.\r
- -- ----\r
- delete from @NAMESPACE@.sl_table\r
- where tab_set = p_sub_set;\r
- delete from @NAMESPACE@.sl_sequence\r
- where seq_set = p_sub_set;\r
-\r
- -- ----\r
- -- Call the internal procedure to drop the subscription\r
- -- ----\r
- perform @NAMESPACE@.unsubscribeSet_int(p_sub_set, p_sub_receiver);\r
-\r
- -- ----\r
- -- Create the UNSUBSCRIBE_SET event\r
- -- ----\r
- return @NAMESPACE@.createEvent(''_@CLUSTERNAME@'', ''UNSUBSCRIBE_SET'', \r
- p_sub_set, p_sub_receiver);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION unsubscribeSet_int (sub_set, sub_receiver)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.unsubscribeSet_int (int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_sub_set alias for $1;\r
- p_sub_receiver alias for $2;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- All the real work is done before event generation on the\r
- -- subscriber.\r
- -- ----\r
- delete from @NAMESPACE@.sl_subscribe\r
- where sub_set = p_sub_set\r
- and sub_receiver = p_sub_receiver;\r
-\r
- return p_sub_set;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION enableSubscription (sub_set, sub_provider, sub_receiver)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.enableSubscription (int4, int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_sub_set alias for $1;\r
- p_sub_provider alias for $2;\r
- p_sub_receiver alias for $3;\r
-begin\r
- return @NAMESPACE@.enableSubscription_int (p_sub_set, \r
- p_sub_provider, p_sub_receiver);\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION enableSubscription_int (sub_set, sub_provider, sub_receiver)\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.enableSubscription_int (int4, int4, int4)\r
-returns int4\r
-as '\r
-declare\r
- p_sub_set alias for $1;\r
- p_sub_provider alias for $2;\r
- p_sub_receiver alias for $3;\r
- v_n int4;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- The real work is done in the replication engine. All\r
- -- we have to do here is remembering that it happened.\r
- -- ----\r
- update @NAMESPACE@.sl_subscribe\r
- set sub_active = ''t''\r
- where sub_set = p_sub_set\r
- and sub_receiver = p_sub_receiver;\r
- get diagnostics v_n = row_count;\r
- if v_n = 0 then\r
- insert into @NAMESPACE@.sl_subscribe\r
- (sub_set, sub_provider, sub_receiver,\r
- sub_forward, sub_active)\r
- values\r
- (p_sub_set, p_sub_provider, p_sub_receiver,\r
- false, true);\r
- end if;\r
-\r
- return p_sub_set;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION forwardConfirm ()\r
---\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.forwardConfirm (int4, int4, int8, timestamp)\r
-returns bigint\r
-as '\r
-declare\r
- p_con_origin alias for $1;\r
- p_con_received alias for $2;\r
- p_con_seqno alias for $3;\r
- p_con_timestamp alias for $4;\r
- v_max_seqno bigint;\r
-begin\r
- select into v_max_seqno coalesce(max(con_seqno), 0)\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = p_con_origin\r
- and con_received = p_con_received;\r
- if v_max_seqno < p_con_seqno then\r
- insert into @NAMESPACE@.sl_confirm \r
- (con_origin, con_received, con_seqno, con_timestamp)\r
- values (p_con_origin, p_con_received, p_con_seqno,\r
- p_con_timestamp);\r
- notify "_@CLUSTERNAME@_Confirm";\r
- v_max_seqno = p_con_seqno;\r
- end if;\r
-\r
- return v_max_seqno;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION cleanupEvent ()\r
---\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.cleanupEvent ()\r
-returns int4\r
-as '\r
-declare\r
- v_max_row record;\r
- v_min_row record;\r
- v_max_sync int8;\r
-begin\r
- -- ----\r
- -- First remove all but the oldest confirm row per origin,receiver pair\r
- -- ----\r
- delete from @NAMESPACE@.sl_confirm\r
- where con_origin not in (select no_id from @NAMESPACE@.sl_node);\r
- delete from @NAMESPACE@.sl_confirm\r
- where con_received not in (select no_id from @NAMESPACE@.sl_node);\r
- -- ----\r
- -- Next remove all but the oldest confirm row per origin,receiver pair.\r
- -- Ignore confirmations that are younger than 10 minutes. We currently\r
- -- have an not confirmed suspicion that a possibly lost transaction due\r
- -- to a server crash might have been visible to another session, and\r
- -- that this led to log data that is needed again got removed.\r
- -- ----\r
- for v_max_row in select con_origin, con_received, max(con_seqno) as con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- where con_timestamp < (CURRENT_TIMESTAMP - ''10 min''::interval)\r
- group by con_origin, con_received\r
- loop\r
- delete from @NAMESPACE@.sl_confirm\r
- where con_origin = v_max_row.con_origin\r
- and con_received = v_max_row.con_received\r
- and con_seqno < v_max_row.con_seqno;\r
- end loop;\r
-\r
- -- ----\r
- -- Then remove all events that are confirmed by all nodes in the\r
- -- whole cluster up to the last SYNC\r
- -- ----\r
- for v_min_row in select con_origin, min(con_seqno) as con_seqno\r
- from @NAMESPACE@.sl_confirm\r
- group by con_origin\r
- loop\r
- select coalesce(max(ev_seqno), 0) into v_max_sync\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = v_min_row.con_origin\r
- and ev_seqno <= v_min_row.con_seqno\r
- and ev_type = ''SYNC'';\r
- if v_max_sync > 0 then\r
- delete from @NAMESPACE@.sl_event\r
- where ev_origin = v_min_row.con_origin\r
- and ev_seqno < v_max_sync;\r
- end if;\r
- end loop;\r
-\r
- return 0;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION tableAddKey (tab_fqname)\r
---\r
--- If the specified table does not have a column \r
--- "_Slony-I_<clustername>_rowID", then add it as a bigint\r
--- with default nextval('"_<clustername>".sl_rowid_seq').\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.tableAddKey(text) returns text\r
-as '\r
-declare\r
- p_tab_fqname alias for $1;\r
- v_attkind text default '''';\r
- v_attrow record;\r
- v_have_serial bool default ''f'';\r
-begin\r
- --\r
- -- Loop over the attributes of this relation\r
- -- and add a "v" for every user column, and a "k"\r
- -- if we find the Slony-I special serial column.\r
- --\r
- for v_attrow in select PGA.attnum, PGA.attname\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_attribute PGA\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGN.oid = PGC.relnamespace\r
- and PGA.attrelid = PGC.oid\r
- and not PGA.attisdropped\r
- and PGA.attnum > 0\r
- order by attnum\r
- loop\r
- if v_attrow.attname = ''_Slony-I_@CLUSTERNAME@_rowID'' then\r
- v_attkind := v_attkind || ''k'';\r
- v_have_serial := ''t'';\r
- else\r
- v_attkind := v_attkind || ''v'';\r
- end if;\r
- end loop;\r
- \r
- --\r
- -- A table must have at least one attribute, so not finding\r
- -- anything means the table does not exist.\r
- --\r
- if not found then\r
- raise exception ''Slony-I: table % not found'', p_tab_fqname;\r
- end if;\r
-\r
- --\r
- -- If it does not have the special serial column, we\r
- -- have to add it. This will be only half way done.\r
- -- The function to add the table to the set must finish\r
- -- these definitions with NOT NULL and UNIQUE after\r
- -- updating all existing rows.\r
- --\r
- if not v_have_serial then\r
- execute ''lock table '' || p_tab_fqname ||\r
- '' in access exclusive mode'';\r
- execute ''alter table only '' || p_tab_fqname ||\r
- '' add column "_Slony-I_@CLUSTERNAME@_rowID" bigint;'';\r
- execute ''alter table only '' || p_tab_fqname ||\r
- '' alter column "_Slony-I_@CLUSTERNAME@_rowID" '' ||\r
- '' set default "pg_catalog".nextval(''''@NAMESPACE@.sl_rowid_seq'''');'';\r
-\r
- v_attkind := v_attkind || ''k'';\r
- end if;\r
-\r
- --\r
- -- Return the resulting Slony-I attkind\r
- --\r
- return v_attkind;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION tableDropKey (tab_id)\r
---\r
--- If the specified table does not have a column \r
--- "_Slony-I_<clustername>_rowID", then add it as a bigint\r
--- with default nextval('"_<clustername>".sl_rowid_seq').\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.tableDropKey(int4) returns int4\r
-as '\r
-declare\r
- p_tab_id alias for $1;\r
- v_tab_fqname text;\r
- v_tab_oid oid;\r
-begin\r
- -- ----\r
- -- Grab the central configuration lock\r
- -- ----\r
- lock table @NAMESPACE@.sl_config_lock;\r
-\r
- -- ----\r
- -- Construct the tables fully qualified name and get its oid\r
- -- ----\r
- select "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname),\r
- PGC.oid into v_tab_fqname, v_tab_oid\r
- from @NAMESPACE@.sl_table T,\r
- "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN\r
- where T.tab_id = p_tab_id\r
- and T.tab_reloid = PGC.oid\r
- and PGC.relnamespace = PGN.oid;\r
- if not found then\r
- raise exception ''Slony-I: table with ID % not found'', p_tab_id;\r
- end if;\r
-\r
- -- ----\r
- -- Drop the special serial ID column if the table has it\r
- -- ----\r
- if exists (select true from "pg_catalog".pg_attribute\r
- where attrelid = v_tab_oid\r
- and attname = ''_Slony-I_@CLUSTERNAME@_rowID'')\r
- then\r
- execute ''lock table '' || v_tab_fqname ||\r
- '' in access exclusive mode'';\r
- execute ''alter table '' || v_tab_fqname ||\r
- '' drop column "_Slony-I_@CLUSTERNAME@_rowID"'';\r
- end if;\r
-\r
- return p_tab_id;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION determineIdxnameUnique (tab_fqname, indexname)\r
---\r
--- Given a tablename, check that a unique index exists or return\r
--- the tables primary key index name.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.determineIdxnameUnique(text, name) returns name\r
-as '\r
-declare\r
- p_tab_fqname alias for $1;\r
- p_idx_name alias for $2;\r
- v_idxrow record;\r
-begin\r
- --\r
- -- Lookup the tables primary key or the specified unique index\r
- --\r
- if p_idx_name isnull then\r
- select PGXC.relname\r
- into v_idxrow\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_index PGX,\r
- "pg_catalog".pg_class PGXC\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGN.oid = PGC.relnamespace\r
- and PGX.indrelid = PGC.oid\r
- and PGX.indexrelid = PGXC.oid\r
- and PGX.indisprimary;\r
- if not found then\r
- raise exception ''Slony-I: table % has no primary key'',\r
- p_tab_fqname;\r
- end if;\r
- else\r
- select PGXC.relname\r
- into v_idxrow\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_index PGX,\r
- "pg_catalog".pg_class PGXC\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGN.oid = PGC.relnamespace\r
- and PGX.indrelid = PGC.oid\r
- and PGX.indexrelid = PGXC.oid\r
- and PGX.indisunique\r
- and PGXC.relname = p_idx_name;\r
- if not found then\r
- raise exception ''Slony-I: table % has no unique index %'',\r
- p_tab_fqname, p_idx_name;\r
- end if;\r
- end if;\r
-\r
- --\r
- -- Return the found index name\r
- --\r
- return v_idxrow.relname;\r
-end;\r
-' language plpgsql called on null input;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION determineIdxnameSerial (tab_fqname)\r
---\r
--- Given a tablename, construct the serial columns index name\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.determineIdxnameSerial(text) returns name\r
-as '\r
-declare\r
- p_tab_fqname alias for $1;\r
- v_row record;\r
-begin\r
- --\r
- -- Lookup the table name alone\r
- --\r
- select PGC.relname\r
- into v_row\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGN.oid = PGC.relnamespace;\r
- if not found then\r
- raise exception ''Slony-I: table % not found'',\r
- p_tab_fqname;\r
- end if;\r
-\r
- --\r
- -- Return the found index name\r
- --\r
- return v_row.relname || ''__Slony-I_@CLUSTERNAME@_rowID_key'';\r
-end;\r
-' language plpgsql called on null input;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION determineAttKindUnique (tab_fqname, indexname)\r
---\r
--- Given a tablename, return the Slony-I specific attkind (used for\r
--- the log trigger) of the table. Use the specified unique index or\r
--- the primary key (if indexname is NULL).\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.determineAttkindUnique(text, name) returns text\r
-as '\r
-declare\r
- p_tab_fqname alias for $1;\r
- p_idx_name alias for $2;\r
- v_idxrow record;\r
- v_attrow record;\r
- v_i integer;\r
- v_attno int2;\r
- v_attkind text default '''';\r
- v_attfound bool;\r
-begin\r
- --\r
- -- Lookup the tables primary key or the specified unique index\r
- --\r
- if p_idx_name isnull then\r
- raise exception ''Slony-I: index name must be specified'';\r
- else\r
- select PGXC.relname, PGX.indexrelid, PGX.indkey\r
- into v_idxrow\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_index PGX,\r
- "pg_catalog".pg_class PGXC\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGN.oid = PGC.relnamespace\r
- and PGX.indrelid = PGC.oid\r
- and PGX.indexrelid = PGXC.oid\r
- and PGX.indisunique\r
- and PGXC.relname = p_idx_name;\r
- if not found then\r
- raise exception ''Slony-I: table % has no unique index %'',\r
- p_tab_fqname, p_idx_name;\r
- end if;\r
- end if;\r
-\r
- --\r
- -- Loop over the tables attributes and check if they are\r
- -- index attributes. If so, add a "k" to the return value,\r
- -- otherwise add a "v".\r
- --\r
- for v_attrow in select PGA.attnum, PGA.attname\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_attribute PGA\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGN.oid = PGC.relnamespace\r
- and PGA.attrelid = PGC.oid\r
- and not PGA.attisdropped\r
- and PGA.attnum > 0\r
- order by attnum\r
- loop\r
- v_attfound = ''f'';\r
-\r
- v_i := 0;\r
- loop\r
- select indkey[v_i] into v_attno from "pg_catalog".pg_index\r
- where indexrelid = v_idxrow.indexrelid;\r
- if v_attno = 0 then\r
- exit;\r
- end if;\r
- if v_attrow.attnum = v_attno then\r
- v_attfound = ''t'';\r
- exit;\r
- end if;\r
- v_i := v_i + 1;\r
- end loop;\r
-\r
- if v_attfound then\r
- v_attkind := v_attkind || ''k'';\r
- else\r
- v_attkind := v_attkind || ''v'';\r
- end if;\r
- end loop;\r
-\r
- --\r
- -- Return the resulting attkind\r
- --\r
- return v_attkind;\r
-end;\r
-' language plpgsql called on null input;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION determineAttKindSerial (tab_fqname)\r
---\r
--- A table was that was specified without a primary key is added\r
--- to the replication. Assume that tableAddKey() was called before\r
--- and finish the creation of the serial column. The return an\r
--- attkind according to that.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.determineAttkindSerial(text)\r
-returns text\r
-as '\r
-declare\r
- p_tab_fqname alias for $1;\r
- v_attkind text default '''';\r
- v_attrow record;\r
- v_have_serial bool default ''f'';\r
-begin\r
- --\r
- -- Loop over the attributes of this relation\r
- -- and add a "v" for every user column, and a "k"\r
- -- if we find the Slony-I special serial column.\r
- --\r
- for v_attrow in select PGA.attnum, PGA.attname\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_attribute PGA\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGN.oid = PGC.relnamespace\r
- and PGA.attrelid = PGC.oid\r
- and not PGA.attisdropped\r
- and PGA.attnum > 0\r
- order by attnum\r
- loop\r
- if v_attrow.attname = ''_Slony-I_@CLUSTERNAME@_rowID'' then\r
- v_attkind := v_attkind || ''k'';\r
- v_have_serial := ''t'';\r
- else\r
- v_attkind := v_attkind || ''v'';\r
- end if;\r
- end loop;\r
- \r
- --\r
- -- A table must have at least one attribute, so not finding\r
- -- anything means the table does not exist.\r
- --\r
- if not found then\r
- raise exception ''Slony-I: table % not found'', p_tab_fqname;\r
- end if;\r
-\r
- --\r
- -- If it does not have the special serial column, we\r
- -- should not have been called in the first place.\r
- --\r
- if not v_have_serial then\r
- raise exception ''Slony-I: table % does not have the serial key'',\r
- p_tab_fqname;\r
- end if;\r
-\r
- execute ''update '' || p_tab_fqname ||\r
- '' set "_Slony-I_@CLUSTERNAME@_rowID" ='' ||\r
- '' "pg_catalog".nextval(''''@NAMESPACE@.sl_rowid_seq'''');'';\r
- execute ''alter table only '' || p_tab_fqname ||\r
- '' add unique ("_Slony-I_@CLUSTERNAME@_rowID");'';\r
- execute ''alter table only '' || p_tab_fqname ||\r
- '' alter column "_Slony-I_@CLUSTERNAME@_rowID" '' ||\r
- '' set not null;'';\r
-\r
- --\r
- -- Return the resulting Slony-I attkind\r
- --\r
- return v_attkind;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- FUNCTION tableHasSerialKey (tab_fqname)\r
---\r
--- Checks if a table has our special serial key column that is\r
--- used if the table has no natural unique constraint.\r
--- ----------------------------------------------------------------------\r
-create or replace function @NAMESPACE@.tableHasSerialKey(text) \r
-returns bool\r
-as '\r
-declare\r
- p_tab_fqname alias for $1;\r
- v_attnum int2;\r
-begin\r
- select PGA.attnum into v_attnum\r
- from "pg_catalog".pg_class PGC,\r
- "pg_catalog".pg_namespace PGN,\r
- "pg_catalog".pg_attribute PGA\r
- where "pg_catalog".quote_ident(PGN.nspname) || ''.'' ||\r
- "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname\r
- and PGC.relnamespace = PGN.oid\r
- and PGA.attrelid = PGC.oid\r
- and PGA.attname = ''_Slony-I_@CLUSTERNAME@_rowID''\r
- and not PGA.attisdropped;\r
- return found;\r
-end;\r
-' language plpgsql;\r
-\r
-\r
--- **********************************************************************\r
--- * Views\r
--- **********************************************************************\r
-\r
-\r
--- ----------------------------------------------------------------------\r
--- VIEW sl_status\r
---\r
--- This view shows the local nodes last event sequence number\r
--- and how far all remote nodes have processed events.\r
--- ----------------------------------------------------------------------\r
-create or replace view @NAMESPACE@.sl_status as select\r
- E.ev_origin as st_origin,\r
- C.con_received as st_received,\r
- E.ev_seqno as st_last_event,\r
- E.ev_timestamp as st_last_event_ts,\r
- C.con_seqno as st_last_received,\r
- C.con_timestamp as st_last_received_ts,\r
- CE.ev_timestamp as st_last_received_event_ts,\r
- E.ev_seqno - C.con_seqno as st_lag_num_events,\r
- current_timestamp - CE.ev_timestamp as st_lag_time\r
- from @NAMESPACE@.sl_event E, @NAMESPACE@.sl_confirm C,\r
- @NAMESPACE@.sl_event CE\r
- where E.ev_origin = C.con_origin\r
- and CE.ev_origin = E.ev_origin\r
- and CE.ev_seqno = C.con_seqno\r
- and (E.ev_origin, E.ev_seqno) in \r
- (select ev_origin, max(ev_seqno)\r
- from @NAMESPACE@.sl_event\r
- where ev_origin = @NAMESPACE@.getLocalNodeId('_@CLUSTERNAME@')\r
- group by 1\r
- )\r
- and (C.con_origin, C.con_received, C.con_seqno) in\r
- (select con_origin, con_received, max(con_seqno)\r
- from @NAMESPACE@.sl_confirm\r
- where con_origin = @NAMESPACE@.getLocalNodeId('_@CLUSTERNAME@')\r
- group by 1, 2\r
- );\r
-\r
-\r