Tue Aug 3 06:38:14 PDT 2004
- Previous message: [Slony1-commit] By darcyb: We prefer the use of slony.info over slony.org
- Next message: [Slony1-commit] By cbbrowne: Added localxid.sql, that generates a domain for the "XXID"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Log Message: ----------- Add in (clunky) rules to generate documentation for tables and functions using postgresql-autodoc (Rod Taylor's tool) Modified Files: -------------- slony1-engine/doc/howto: Makefile (r1.2 -> r1.3) Added Files: ----------- slony1-engine/doc/howto: schemadoc.html (r1.1) -------------- next part -------------- --- /dev/null +++ doc/howto/schemadoc.html @@ -0,0 +1,4147 @@ +<!-- $Header: /usr/local/cvsroot/slony1/slony1-engine/doc/howto/schemadoc.html,v 1.1 2004/07/30 19:35:46 cbbrowne Exp $ --> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html> + <head> + <title>Index for schemadoc</title> + <style type="text/css"> + BODY { + color: #000000; + background-color: #FFFFFF; + font-family: Helvetica, sans-serif; + } + + P { + margin-top: 5px; + margin-bottom: 5px; + } + + P.w3ref { + font-size: 8pt; + font-style: italic; + text-align: right; + } + + P.detail { + font-size: 10pt; + } + + .error { + color: #FFFFFF; + background-color: #FF0000; + } + + H1, H2, H3, H4, H5, H6 { + } + + OL { + list-style-type: upper-alpha; + } + + UL.topic { + list-style-type: upper-alpha; + } + + LI.topic { + font-weight : bold; + } + + HR { + color: #00FF00; + background-color: #808080; + } + + TABLE { + border-width: medium; + padding: 3px; + background-color: #000000; + width: 90%; + } + + CAPTION { + text-transform: capitalize; + font-weight : bold; + font-size: 14pt; + } + + TH { + color: #FFFFFF; + background-color: #000000; + text-align: left; + } + + TR { + color: #000000; + background-color: #000000; + vertical-align: top; + } + + TR.tr0 { + background-color: #F0F0F0; + } + + TR.tr1 { + background-color: #D8D8D8; + } + + TD { + font-size: 12pt; + } + + TD.col0 { + font-weight : bold; + width: 20%; + } + + TD.col1 { + font-style: italic; + width: 15%; + } + + TD.col2 { + font-size: 12px; + } + </style> + <link rel="stylesheet" type="text/css" media="all" href="all.css"> + <link rel="stylesheet" type="text/css" media="screen" href="screen.css"> + <link rel="stylesheet" type="text/css" media="print" href="print.css"> + <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + </head> + <body> + + <!-- Primary Index --> + <p><br><br>Dumped on 2004-07-30</p> +<h1><a name="index">Index of database - schemadoc</a></h1> +<ul> + + <li><a name="schemadoc.schema">schemadoc</a></li><ul> + <li><a href="#schemadoc.table.sl-config-lock">sl_config_lock</a></li><li><a href="#schemadoc.table.sl-confirm">sl_confirm</a></li><li><a href="#schemadoc.table.sl-listen">sl_listen</a></li><li><a href="#schemadoc.table.sl-node">sl_node</a></li><li><a href="#schemadoc.table.sl-path">sl_path</a></li><li><a href="#schemadoc.table.sl-seqlog">sl_seqlog</a></li> + <li><a href="#schemadoc.function.altertableforreplication-integer">altertableforreplication( integer )</a></li><li><a href="#schemadoc.function.altertablerestore-integer">altertablerestore( integer )</a></li><li><a href="#schemadoc.function.cleanupevent">cleanupevent( )</a></li><li><a href="#schemadoc.function.ddlscript-integer-text">ddlscript( integer, text )</a></li><li><a href="#schemadoc.function.ddlscript-int-integer-text">ddlscript_int( integer, text )</a></li><li><a href="#schemadoc.function.determineattkindserial-text">determineattkindserial( text )</a></li><li><a href="#schemadoc.function.determineattkindunique-text-name">determineattkindunique( text, name )</a></li><li><a href="#schemadoc.function.determineidxnameserial-text">determineidxnameserial( text )</a></li><li><a href="#schemadoc.function.determineidxnameunique-text-name">determineidxnameunique( text, name )</a></li><li><a href="#schemadoc.function.disablenode-integer">disablenode( integer )</a></li><li><a href="#schemadoc.function.disablenode-int-integer">disablenode_int( integer )</a></li><li><a href="#schemadoc.function.droplisten-integer-integer-integer">droplisten( integer, integer, integer )</a></li><li><a href="#schemadoc.function.droplisten-int-integer-integer-integer">droplisten_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.dropnode-integer">dropnode( integer )</a></li><li><a href="#schemadoc.function.dropnode-int-integer">dropnode_int( integer )</a></li><li><a href="#schemadoc.function.droppath-integer-integer">droppath( integer, integer )</a></li><li><a href="#schemadoc.function.droppath-int-integer-integer">droppath_int( integer, integer )</a></li><li><a href="#schemadoc.function.dropset-integer">dropset( integer )</a></li><li><a href="#schemadoc.function.dropset-int-integer">dropset_int( integer )</a></li><li><a href="#schemadoc.function.droptrigger-integer-name">droptrigger( integer, name )</a></li><li><a href="#schemadoc.function.droptrigger-int-integer-name">droptrigger_int( integer, name )</a></li><li><a href="#schemadoc.function.enablenode-integer">enablenode( integer )</a></li><li><a href="#schemadoc.function.enablenode-int-integer">enablenode_int( integer )</a></li><li><a href="#schemadoc.function.enablesubscription-integer-integer-integer">enablesubscription( integer, integer, integer )</a></li><li><a href="#schemadoc.function.enablesubscription-int-integer-integer-integer">enablesubscription_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.failednode-integer-integer">failednode( integer, integer )</a></li><li><a href="#schemadoc.function.failoverset-int-integer-integer-integer">failoverset_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.forwardconfirm-integer-integer-bigint-timestamp-without-time-zone">forwardconfirm( integer, integer, bigint, timestamp without time zone )</a></li><li><a href="#schemadoc.function.initializelocalnode-integer-text">initializelocalnode( integer, text )</a></li><li><a href="#schemadoc.function.lockset-integer">lockset( integer )</a></li><li><a href="#schemadoc.function.mergeset-integer-integer">mergeset( integer, integer )</a></li><li><a href="#schemadoc.function.mergeset-int-integer-integer">mergeset_int( integer, integer )</a></li><li><a href="#schemadoc.function.moveset-integer-integer">moveset( integer, integer )</a></li><li><a href="#schemadoc.function.moveset-int-integer-integer-integer">moveset_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.sequencelastvalue-text">sequencelastvalue( text )</a></li><li><a href="#schemadoc.function.sequencesetvalue-integer-integer-bigint-bigint">sequencesetvalue( integer, integer, bigint, bigint )</a></li><li><a href="#schemadoc.function.setaddsequence-integer-integer-text-text">setaddsequence( integer, integer, text, text )</a></li><li><a href="#schemadoc.function.setaddsequence-int-integer-integer-text-text">setaddsequence_int( integer, integer, text, text )</a></li><li><a href="#schemadoc.function.setaddtable-integer-integer-text-name-text">setaddtable( integer, integer, text, name, text )</a></li><li><a href="#schemadoc.function.setaddtable-int-integer-integer-text-name-text">setaddtable_int( integer, integer, text, name, text )</a></li><li><a href="#schemadoc.function.storelisten-integer-integer-integer">storelisten( integer, integer, integer )</a></li><li><a href="#schemadoc.function.storelisten-int-integer-integer-integer">storelisten_int( integer, integer, integer )</a></li><li><a href="#schemadoc.function.storenode-integer-text">storenode( integer, text )</a></li><li><a href="#schemadoc.function.storenode-int-integer-text">storenode_int( integer, text )</a></li><li><a href="#schemadoc.function.storepath-integer-integer-text-integer">storepath( integer, integer, text, integer )</a></li><li><a href="#schemadoc.function.storepath-int-integer-integer-text-integer">storepath_int( integer, integer, text, integer )</a></li><li><a href="#schemadoc.function.storeset-integer-text">storeset( integer, text )</a></li><li><a href="#schemadoc.function.storeset-int-integer-integer-text">storeset_int( integer, integer, text )</a></li><li><a href="#schemadoc.function.storetrigger-integer-name">storetrigger( integer, name )</a></li><li><a href="#schemadoc.function.storetrigger-int-integer-name">storetrigger_int( integer, name )</a></li><li><a href="#schemadoc.function.subscribeset-integer-integer-integer-boolean">subscribeset( integer, integer, integer, boolean )</a></li><li><a href="#schemadoc.function.subscribeset-int-integer-integer-integer-boolean">subscribeset_int( integer, integer, integer, boolean )</a></li><li><a href="#schemadoc.function.tableaddkey-text">tableaddkey( text )</a></li><li><a href="#schemadoc.function.tabledropkey-integer">tabledropkey( integer )</a></li><li><a href="#schemadoc.function.tablehasserialkey-text">tablehasserialkey( text )</a></li><li><a href="#schemadoc.function.uninstallnode">uninstallnode( )</a></li><li><a href="#schemadoc.function.unlockset-integer">unlockset( integer )</a></li><li><a href="#schemadoc.function.unsubscribeset-integer-integer">unsubscribeset( integer, integer )</a></li><li><a href="#schemadoc.function.unsubscribeset-int-integer-integer">unsubscribeset_int( integer, integer )</a></li> + </ul> + +</ul> + + <!-- Schema Creation --> + <!-- schemadocschemadoc --> + + + + <hr> + <h2>Table: + <a name="schemadoc.table.sl-config-lock">sl_config_lock</a> + </h2> + + <p>This table exists solely to prevent overlapping execution of configuration change procedures and the resulting possible deadlocks. +</p> + + + + <table width="100%" cellspacing="0" cellpadding="3"> + <caption>sl_config_lock Structure</caption> + <tr> + <th>F-Key</th> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> + + <tr class="tr0"> + <td> + + </td> + <td>dummy</td> + <td>integer</td> + <td><i> + + + + + </i> + + </td> + </tr> + + </table> + + + + + + <!-- Constraint List --> + + + <!-- Foreign Key Discovery --> + + + <!-- View Definition --> + + + <!-- List off permissions --> + + + <p> + <a href="#index">Index</a> - + <a href="#schemadoc.schema">Schema schemadoc</a> + </p> + + <hr> + <h2>Table: + <a name="schemadoc.table.sl-confirm">sl_confirm</a> + </h2> + + <p>Holds confirmation of replication events. + +After a period of time, Slony removes old confirmed events from both this table and the sl_event table. + +con_origin : Integer. The ID # (from sl_node.no_id) of the source node for this event +con_received : Integer. +con_seqno : Integer. The ID # for the event +con_timestamp : Timestamp. When this event was confirmed +</p> + + + + <table width="100%" cellspacing="0" cellpadding="3"> + <caption>sl_confirm Structure</caption> + <tr> + <th>F-Key</th> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> + + <tr class="tr0"> + <td> + + </td> + <td>con_origin</td> + <td>integer</td> + <td><i> + + + + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + </td> + <td>con_received</td> + <td>integer</td> + <td><i> + + + + + </i> + + </td> + </tr> + + <tr class="tr0"> + <td> + + </td> + <td>con_seqno</td> + <td>bigint</td> + <td><i> + + + + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + </td> + <td>con_timestamp</td> + <td>timestamp without time zone</td> + <td><i> + + + + DEFAULT (timeofday())::timestamp without time zone + </i> + + </td> + </tr> + + </table> + + + + + + <!-- Constraint List --> + + + <!-- Foreign Key Discovery --> + + + <!-- View Definition --> + + + <!-- List off permissions --> + + + <p> + <a href="#index">Index</a> - + <a href="#schemadoc.schema">Schema schemadoc</a> + </p> + + <hr> + <h2>Table: + <a name="schemadoc.table.sl-listen">sl_listen</a> + </h2> + + <p>Indicates how nodes listen to events from other nodes in the Slony-I network. + +li_origin : Integer. The ID # (from sl_node.no_id) of the node this listener is operating on +li_provider : Integer. The ID # (from sl_node.no_id) of the source node for this listening event +li_receiver : Integer. The ID # (from sl_node.no_id) of the target node for this listening event +</p> + + + + <table width="100%" cellspacing="0" cellpadding="3"> + <caption>sl_listen Structure</caption> + <tr> + <th>F-Key</th> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> + + <tr class="tr0"> + <td> + + + <a href="#schemadoc.table.sl-node">sl_node.no_id</a> + + + + + </td> + <td>li_origin</td> + <td>integer</td> + <td><i> + + + + + + PRIMARY KEY + + + + + + + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + + + + <a href="#schemadoc.table.sl-path">sl_path.pa_server#1</a> + + + </td> + <td>li_provider</td> + <td>integer</td> + <td><i> + + PRIMARY KEY + + + + + + + + + + + + </i> + + </td> + </tr> + + <tr class="tr0"> + <td> + + + + + <a href="#schemadoc.table.sl-path">sl_path.pa_client#1</a> + + + </td> + <td>li_receiver</td> + <td>integer</td> + <td><i> + + PRIMARY KEY + + + + + + + + + + + + </i> + + </td> + </tr> + + </table> + + + + + + <!-- Constraint List --> + + + <!-- Foreign Key Discovery --> + + + <!-- View Definition --> + + + <!-- List off permissions --> + + + <p> + <a href="#index">Index</a> - + <a href="#schemadoc.schema">Schema schemadoc</a> + </p> + + <hr> + <h2>Table: + <a name="schemadoc.table.sl-node">sl_node</a> + </h2> + + <p>Holds the list of nodes associated with this namespace. no_id is the unique ID number for the node; no_comment is a human-oriented description of the node</p> + + + + <table width="100%" cellspacing="0" cellpadding="3"> + <caption>sl_node Structure</caption> + <tr> + <th>F-Key</th> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> + + <tr class="tr0"> + <td> + + + + </td> + <td>no_id</td> + <td>integer</td> + <td><i> + + PRIMARY KEY + + + + + + + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + </td> + <td>no_active</td> + <td>boolean</td> + <td><i> + + + + + </i> + + </td> + </tr> + + <tr class="tr0"> + <td> + + </td> + <td>no_comment</td> + <td>text</td> + <td><i> + + + + + </i> + + </td> + </tr> + + </table> + + + + + + <!-- Constraint List --> + + + <!-- Foreign Key Discovery --> + + <p>Tables referencing this one via Foreign Key Constraints:</p> + + <ul> + <li><a href="#schemadoc.table.sl-listen">sl_listen</a></li> + </ul> + + <ul> + <li><a href="#schemadoc.table.sl-path">sl_path</a></li> + </ul> + + + + <!-- View Definition --> + + + <!-- List off permissions --> + + + <p> + <a href="#index">Index</a> - + <a href="#schemadoc.schema">Schema schemadoc</a> + </p> + + <hr> + <h2>Table: + <a name="schemadoc.table.sl-path">sl_path</a> + </h2> + + <p>Holds connection information for the paths between nodes, and the synchronisation delay + +pa_server - The Node ID # (from sl_node.no_id) of the data source +pa_client - The Node ID # (from sl_node.no_id) of the data target +pa_conninfo - The PostgreSQL connection string used to connect to the source node. +pa_connretry - The synchronisation delay, in seconds +</p> + + + + <table width="100%" cellspacing="0" cellpadding="3"> + <caption>sl_path Structure</caption> + <tr> + <th>F-Key</th> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> + + <tr class="tr0"> + <td> + + + <a href="#schemadoc.table.sl-node">sl_node.no_id</a> + + + + + </td> + <td>pa_server</td> + <td>integer</td> + <td><i> + + + + + + PRIMARY KEY + + + + + + + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + + <a href="#schemadoc.table.sl-node">sl_node.no_id</a> + + + + + </td> + <td>pa_client</td> + <td>integer</td> + <td><i> + + + + + + PRIMARY KEY + + + + + + + + </i> + + </td> + </tr> + + <tr class="tr0"> + <td> + + </td> + <td>pa_conninfo</td> + <td>text</td> + <td><i> + + + NOT NULL + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + </td> + <td>pa_connretry</td> + <td>integer</td> + <td><i> + + + + + </i> + + </td> + </tr> + + </table> + + + + + + <!-- Constraint List --> + + + <!-- Foreign Key Discovery --> + + <p>Tables referencing this one via Foreign Key Constraints:</p> + + <ul> + <li><a href="#schemadoc.table.sl-listen">sl_listen</a></li> + </ul> + + + + <!-- View Definition --> + + + <!-- List off permissions --> + + + <p> + <a href="#index">Index</a> - + <a href="#schemadoc.schema">Schema schemadoc</a> + </p> + + <hr> + <h2>Table: + <a name="schemadoc.table.sl-seqlog">sl_seqlog</a> + </h2> + + <p>Not documented yet</p> + + + + <table width="100%" cellspacing="0" cellpadding="3"> + <caption>sl_seqlog Structure</caption> + <tr> + <th>F-Key</th> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> + + <tr class="tr0"> + <td> + + </td> + <td>seql_seqid</td> + <td>integer</td> + <td><i> + + + + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + </td> + <td>seql_origin</td> + <td>integer</td> + <td><i> + + + + + </i> + + </td> + </tr> + + <tr class="tr0"> + <td> + + </td> + <td>seql_ev_seqno</td> + <td>bigint</td> + <td><i> + + + + + </i> + + </td> + </tr> + + <tr class="tr1"> + <td> + + </td> + <td>seql_last_value</td> + <td>bigint</td> + <td><i> + + + + + </i> + + </td> + </tr> + + </table> + + + + + + <!-- Constraint List --> + + + <!-- Foreign Key Discovery --> + + + <!-- View Definition --> + + + <!-- List off permissions --> + + + <p> + <a href="#index">Index</a> - + <a href="#schemadoc.schema">Schema schemadoc</a> + </p> + + + <!-- We've gone through the table structure, now lets take a look at user functions --> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.altertableforreplication-integer">altertableforreplication( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_id alias for $1; + v_no_id int4; + v_tab_row record; + v_tab_fqname text; + v_tab_attkind text; + v_n int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Get our local node ID + -- ---- + v_no_id := schemadoc.getLocalNodeId('_schemadoc'); + + -- ---- + -- Get the sl_table row and the current tables origin. Check + -- that the table currently is NOT in altered state. + -- ---- + select T.tab_reloid, T.tab_set, T.tab_idxname, T.tab_altered, + S.set_origin, PGX.indexrelid, + "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) as tab_fqname + into v_tab_row + from schemadoc.sl_table T, schemadoc.sl_set S, + "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGXC + where T.tab_id = p_tab_id + and T.tab_set = S.set_id + and T.tab_reloid = PGC.oid + and PGC.relnamespace = PGN.oid + and PGX.indrelid = T.tab_reloid + and PGX.indexrelid = PGXC.oid + and PGXC.relname = T.tab_idxname + for update; + if not found then + raise exception 'Slony-I: Table with id % not found', p_tab_id; + end if; + v_tab_fqname = v_tab_row.tab_fqname; + if v_tab_row.tab_altered then + raise exception 'Slony-I: Table % is already in altered state', + v_tab_fqname; + end if; + + v_tab_attkind := schemadoc.determineAttKindUnique(v_tab_row.tab_fqname, + v_tab_row.tab_idxname); + + execute 'lock table ' || v_tab_fqname || ' in access exclusive mode'; + + -- ---- + -- Procedures are different on origin and subscriber + -- ---- + if v_no_id = v_tab_row.set_origin then + -- ---- + -- On the Origin we add the log trigger to the table and done + -- ---- + execute 'create trigger "_schemadoc_logtrigger_' || + p_tab_id || '" after insert or update or delete on ' || + v_tab_fqname || ' for each row execute procedure + schemadoc.logTrigger (''_schemadoc'', ''' || + p_tab_id || ''', ''' || + v_tab_attkind || ''');'; + else + -- ---- + -- On the subscriber the thing is a bit more difficult. We want + -- to disable all user- and foreign key triggers and rules. + -- ---- + + + -- ---- + -- Disable all existing triggers + -- ---- + update "pg_catalog".pg_trigger + set tgrelid = v_tab_row.indexrelid + where tgrelid = v_tab_row.tab_reloid + and not exists ( + select true from schemadoc.sl_table TAB, + schemadoc.sl_trigger TRIG + where TAB.tab_reloid = tgrelid + and TAB.tab_id = TRIG.trig_tabid + and TRIG.trig_tgname = tgname + ); + get diagnostics v_n = row_count; + if v_n > 0 then + update "pg_catalog".pg_class + set reltriggers = reltriggers - v_n + where oid = v_tab_row.tab_reloid; + end if; + + -- ---- + -- Disable all existing rules + -- ---- + update "pg_catalog".pg_rewrite + set ev_class = v_tab_row.indexrelid + where ev_class = v_tab_row.tab_reloid; + get diagnostics v_n = row_count; + if v_n > 0 then + update "pg_catalog".pg_class + set relhasrules = false + where oid = v_tab_row.tab_reloid; + end if; + + -- ---- + -- Add the trigger that denies write access to replicated tables + -- ---- + execute 'create trigger "_schemadoc_denyaccess_' || + p_tab_id || '" before insert or update or delete on ' || + v_tab_fqname || ' for each row execute procedure + schemadoc.denyAccess (''_schemadoc'');'; + end if; + + -- ---- + -- Mark the table altered in our configuration + -- ---- + update schemadoc.sl_table + set tab_altered = true where tab_id = p_tab_id; + + return p_tab_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.altertablerestore-integer">altertablerestore( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_id alias for $1; + v_no_id int4; + v_tab_row record; + v_tab_fqname text; + v_n int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Get our local node ID + -- ---- + v_no_id := schemadoc.getLocalNodeId('_schemadoc'); + + -- ---- + -- Get the sl_table row and the current tables origin. Check + -- that the table currently IS in altered state. + -- ---- + select T.tab_reloid, T.tab_set, T.tab_altered, + S.set_origin, PGX.indexrelid, + "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) as tab_fqname + into v_tab_row + from schemadoc.sl_table T, schemadoc.sl_set S, + "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGXC + where T.tab_id = p_tab_id + and T.tab_set = S.set_id + and T.tab_reloid = PGC.oid + and PGC.relnamespace = PGN.oid + and PGX.indrelid = T.tab_reloid + and PGX.indexrelid = PGXC.oid + and PGXC.relname = T.tab_idxname + for update; + if not found then + raise exception 'Slony-I: Table with id % not found', p_tab_id; + end if; + v_tab_fqname = v_tab_row.tab_fqname; + if not v_tab_row.tab_altered then + raise exception 'Slony-I: Table % is not in altered state', + v_tab_fqname; + end if; + + execute 'lock table ' || v_tab_fqname || ' in access exclusive mode'; + + -- ---- + -- Procedures are different on origin and subscriber + -- ---- + if v_no_id = v_tab_row.set_origin then + -- ---- + -- On the Origin we just drop the trigger we originally added + -- ---- + execute 'drop trigger "_schemadoc_logtrigger_' || + p_tab_id || '" on ' || v_tab_fqname; + else + -- ---- + -- On the subscriber drop the denyAccess trigger + -- ---- + execute 'drop trigger "_schemadoc_denyaccess_' || + p_tab_id || '" on ' || v_tab_fqname; + + -- ---- + -- Restore all original triggers + -- ---- + update "pg_catalog".pg_trigger + set tgrelid = v_tab_row.tab_reloid + where tgrelid = v_tab_row.indexrelid; + get diagnostics v_n = row_count; + if v_n > 0 then + update "pg_catalog".pg_class + set reltriggers = reltriggers + v_n + where oid = v_tab_row.tab_reloid; + end if; + + -- ---- + -- Restore all original rewrite rules + -- ---- + update "pg_catalog".pg_rewrite + set ev_class = v_tab_row.tab_reloid + where ev_class = v_tab_row.indexrelid; + get diagnostics v_n = row_count; + if v_n > 0 then + update "pg_catalog".pg_class + set relhasrules = true + where oid = v_tab_row.tab_reloid; + end if; + + end if; + + -- ---- + -- Mark the table not altered in our configuration + -- ---- + update schemadoc.sl_table + set tab_altered = false where tab_id = p_tab_id; + + return p_tab_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.cleanupevent">cleanupevent( )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + v_max_row record; + v_min_row record; + v_max_sync int8; +begin + -- ---- + -- First remove all but the oldest confirm row per origin,receiver pair + -- ---- + for v_max_row in select con_origin, con_received, max(con_seqno) as con_seqno + from schemadoc.sl_confirm + group by con_origin, con_received + loop + delete from schemadoc.sl_confirm + where con_origin = v_max_row.con_origin + and con_received = v_max_row.con_received + and con_seqno < v_max_row.con_seqno; + end loop; + + -- ---- + -- Then remove all events that are confirmed by all nodes in the + -- whole cluster up to the last SYNC + -- ---- + for v_min_row in select con_origin, min(con_seqno) as con_seqno + from schemadoc.sl_confirm + group by con_origin + loop + select coalesce(max(ev_seqno), 0) into v_max_sync + from schemadoc.sl_event + where ev_origin = v_min_row.con_origin + and ev_seqno <= v_min_row.con_seqno + and ev_type = 'SYNC'; + if v_max_sync > 0 then + delete from schemadoc.sl_event + where ev_origin = v_min_row.con_origin + and ev_seqno < v_max_sync; + end if; + end loop; + + return 0; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.ddlscript-integer-text">ddlscript( integer, text )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_script alias for $2; + v_set_origin int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that the set exists and originates here + -- ---- + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_set_id + for update; + if not found then + raise exception 'Slony-I: set % not found', p_set_id; + end if; + if v_set_origin <> schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: set % does not originate on local node', + p_set_id; + end if; + + -- ---- + -- Create a SYNC event, run the script and generate the DDL_SCRIPT event + -- ---- + perform schemadoc.createEvent('_schemadoc', 'SYNC', NULL); + perform schemadoc.ddlScript_int(p_set_id, p_script); + return schemadoc.createEvent('_schemadoc', 'DDL_SCRIPT', + p_set_id, p_script); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.ddlscript-int-integer-text">ddlscript_int( integer, text )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_script alias for $2; + v_set_origin int4; + v_no_id int4; + v_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that we either are the set origin or a current + -- subscriber of the set. + -- ---- + v_no_id := schemadoc.getLocalNodeId('_schemadoc'); + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_set_id + for update; + if not found then + raise exception 'Slony-I: set % not found', p_set_id; + end if; + if v_set_origin <> v_no_id + and not exists (select 1 from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = v_no_id) + then + return 0; + end if; + + -- ---- + -- Restore all original triggers and rules + -- ---- + for v_row in select * from schemadoc.sl_table + where tab_set = p_set_id + loop + perform schemadoc.alterTableRestore(v_row.tab_id); + end loop; + + -- ---- + -- Run the script + -- ---- + execute p_script; + + -- ---- + -- Put all tables back into replicated mode + -- ---- + for v_row in select * from schemadoc.sl_table + where tab_set = p_set_id + loop + perform schemadoc.alterTableForReplication(v_row.tab_id); + end loop; + + return p_set_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.determineattkindserial-text">determineattkindserial( text )</a> + </h2> +<h3>Returns: text</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_fqname alias for $1; + v_attkind text default ''; + v_attrow record; + v_have_serial bool default 'f'; +begin + -- + -- Loop over the attributes of this relation + -- and add a "v" for every user column, and a "k" + -- if we find the Slony-I special serial column. + -- + for v_attrow in select PGA.attnum, PGA.attname + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_attribute PGA + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGN.oid = PGC.relnamespace + and PGA.attrelid = PGC.oid + and not PGA.attisdropped + and PGA.attnum > 0 + order by attnum + loop + if v_attrow.attname = '_Slony-I_schemadoc_rowID' then + v_attkind := v_attkind || 'k'; + v_have_serial := 't'; + else + v_attkind := v_attkind || 'v'; + end if; + end loop; + + -- + -- A table must have at least one attribute, so not finding + -- anything means the table does not exist. + -- + if not found then + raise exception 'Slony-I: table % not found', p_tab_fqname; + end if; + + -- + -- If it does not have the special serial column, we + -- should not have been called in the first place. + -- + if not v_have_serial then + raise exception 'Slony-I: table % does not have the serial key', + p_tab_fqname; + end if; + + execute 'update ' || p_tab_fqname || + ' set "_Slony-I_schemadoc_rowID" =' || + ' "pg_catalog".nextval(''schemadoc.sl_rowid_seq'');'; + execute 'alter table only ' || p_tab_fqname || + ' add unique ("_Slony-I_schemadoc_rowID");'; + execute 'alter table only ' || p_tab_fqname || + ' alter column "_Slony-I_schemadoc_rowID" ' || + ' set not null;'; + + -- + -- Return the resulting Slony-I attkind + -- + return v_attkind; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.determineattkindunique-text-name">determineattkindunique( text, name )</a> + </h2> +<h3>Returns: text</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_fqname alias for $1; + p_idx_name alias for $2; + v_idxrow record; + v_attrow record; + v_i integer; + v_attno int2; + v_attkind text default ''; + v_attfound bool; +begin + -- + -- Lookup the tables primary key or the specified unique index + -- + if p_idx_name isnull then + raise exception 'Slony-I: index name must be specified'; + else + select PGXC.relname, PGX.indexrelid, PGX.indkey + into v_idxrow + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_index PGX, + "pg_catalog".pg_class PGXC + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGN.oid = PGC.relnamespace + and PGX.indrelid = PGC.oid + and PGX.indexrelid = PGXC.oid + and PGX.indisunique + and PGXC.relname = p_idx_name; + if not found then + raise exception 'Slony-I: table % has no unique index %', + p_tab_fqname, p_idx_name; + end if; + end if; + + -- + -- Loop over the tables attributes and check if they are + -- index attributes. If so, add a "k" to the return value, + -- otherwise add a "v". + -- + for v_attrow in select PGA.attnum, PGA.attname + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_attribute PGA + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGN.oid = PGC.relnamespace + and PGA.attrelid = PGC.oid + and not PGA.attisdropped + and PGA.attnum > 0 + order by attnum + loop + v_attfound = 'f'; + + v_i := 0; + loop + select indkey[v_i] into v_attno from "pg_catalog".pg_index + where indexrelid = v_idxrow.indexrelid; + if v_attno = 0 then + exit; + end if; + if v_attrow.attnum = v_attno then + v_attfound = 't'; + exit; + end if; + v_i := v_i + 1; + end loop; + + if v_attfound then + v_attkind := v_attkind || 'k'; + else + v_attkind := v_attkind || 'v'; + end if; + end loop; + + -- + -- Return the resulting attkind + -- + return v_attkind; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.determineidxnameserial-text">determineidxnameserial( text )</a> + </h2> +<h3>Returns: name</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_fqname alias for $1; + v_row record; +begin + -- + -- Lookup the table name alone + -- + select PGC.relname + into v_row + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGN.oid = PGC.relnamespace; + if not found then + raise exception 'Slony-I: table % not found', + p_tab_fqname; + end if; + + -- + -- Return the found index name + -- + return v_row.relname || '__Slony-I_schemadoc_rowID_key'; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.determineidxnameunique-text-name">determineidxnameunique( text, name )</a> + </h2> +<h3>Returns: name</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_fqname alias for $1; + p_idx_name alias for $2; + v_idxrow record; +begin + -- + -- Lookup the tables primary key or the specified unique index + -- + if p_idx_name isnull then + select PGXC.relname + into v_idxrow + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_index PGX, + "pg_catalog".pg_class PGXC + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGN.oid = PGC.relnamespace + and PGX.indrelid = PGC.oid + and PGX.indexrelid = PGXC.oid + and PGX.indisprimary; + if not found then + raise exception 'Slony-I: table % has no primary key', + p_tab_fqname; + end if; + else + select PGXC.relname + into v_idxrow + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_index PGX, + "pg_catalog".pg_class PGXC + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGN.oid = PGC.relnamespace + and PGX.indrelid = PGC.oid + and PGX.indexrelid = PGXC.oid + and PGX.indisunique + and PGXC.relname = p_idx_name; + if not found then + raise exception 'Slony-I: table % has no unique index %', + p_tab_fqname, p_idx_name; + end if; + end if; + + -- + -- Return the found index name + -- + return v_idxrow.relname; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.disablenode-integer">disablenode( integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + <p>process DISABLE_NODE event for node no_id + +NOTE: This is not yet implemented!</p> + <pre> +declare + p_no_id alias for $1; +begin + -- **** TODO **** + raise exception 'Slony-I: disableNode() not implemented'; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.disablenode-int-integer">disablenode_int( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_no_id alias for $1; +begin + -- **** TODO **** + raise exception 'Slony-I: disableNode_int() not implemented'; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.droplisten-integer-integer-integer">droplisten( integer, integer, integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_li_origin alias for $1; + p_li_provider alias for $2; + p_li_receiver alias for $3; +begin + perform schemadoc.dropListen_int(p_li_origin, + p_li_provider, p_li_receiver); + + return schemadoc.createEvent ('_schemadoc', 'DROP_LISTEN', + p_li_origin, p_li_provider, p_li_receiver); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.droplisten-int-integer-integer-integer">droplisten_int( integer, integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_li_origin alias for $1; + p_li_provider alias for $2; + p_li_receiver alias for $3; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + delete from schemadoc.sl_listen + where li_origin = p_li_origin + and li_provider = p_li_provider + and li_receiver = p_li_receiver; + if found then + return 1; + else + return 0; + end if; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.dropnode-integer">dropnode( integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + <p>generate DROP_NODE event to drop node node_id from replication</p> + <pre> +declare + p_no_id alias for $1; + v_node_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that this got called on a different node + -- ---- + if p_no_id = schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: DROP_NODE cannot initiate on the dropped node'; + end if; + + select * into v_node_row from schemadoc.sl_node + where no_id = p_no_id + for update; + if not found then + raise exception 'Slony-I: unknown node ID %', p_no_id; + end if; + + -- ---- + -- Make sure we do not break other nodes subscriptions with this + -- ---- + if exists (select true from schemadoc.sl_subscribe + where sub_provider = p_no_id) + then + raise exception 'Slony-I: Node % is still configured as data provider', + p_no_id; + end if; + + -- ---- + -- Make sure no set originates there any more + -- ---- + if exists (select true from schemadoc.sl_set + where set_origin = p_no_id) + then + raise exception 'Slony-I: Node % is still origin of one or more sets', + p_no_id; + end if; + + -- ---- + -- Call the internal drop functionality and generate the event + -- ---- + perform schemadoc.dropNode_int(p_no_id); + return schemadoc.createEvent('_schemadoc', 'DROP_NODE', + p_no_id); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.dropnode-int-integer">dropnode_int( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>internal function to process DROP_NODE event to drop node node_id from replication</p> + <pre> +declare + p_no_id alias for $1; + v_tab_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- If the dropped node is a remote node, clean the configuration + -- from all traces for it. + -- ---- + if p_no_id <> schemadoc.getLocalNodeId('_schemadoc') then + delete from schemadoc.sl_subscribe + where sub_receiver = p_no_id; + delete from schemadoc.sl_listen + where li_origin = p_no_id + or li_provider = p_no_id + or li_receiver = p_no_id; + delete from schemadoc.sl_path + where pa_server = p_no_id + or pa_client = p_no_id; + delete from schemadoc.sl_confirm + where con_origin = p_no_id + or con_received = p_no_id; + delete from schemadoc.sl_event + where ev_origin = p_no_id; + delete from schemadoc.sl_node + where no_id = p_no_id; + + return p_no_id; + end if; + + -- ---- + -- This is us ... deactivate the node for now, the daemon + -- will call uninstallNode() in a separate transaction. + -- ---- + update schemadoc.sl_node + set no_active = false + where no_id = p_no_id; + + return p_no_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.droppath-integer-integer">droppath( integer, integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + <p>Generate DROP_PATH event to drop path from pa_server to pa_client</p> + <pre> +declare + p_pa_server alias for $1; + p_pa_client alias for $2; + v_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- There should be no existing subscriptions. Auto unsubscribing + -- is considered too dangerous. + -- ---- + for v_row in select sub_set, sub_provider, sub_receiver + from schemadoc.sl_subscribe + where sub_provider = p_pa_server + and sub_receiver = p_pa_client + loop + raise exception + 'Slony-I: Path cannot be dropped, subscription of set % needs it', + v_row.sub_set; + end loop; + + -- ---- + -- Drop all sl_listen entries that depend on this path + -- ---- + for v_row in select li_origin, li_provider, li_receiver + from schemadoc.sl_listen + where li_provider = p_pa_server + and li_receiver = p_pa_client + loop + perform schemadoc.dropListen( + v_row.li_origin, v_row.li_provider, v_row.li_receiver); + end loop; + + -- ---- + -- Now drop the path and create the event + -- ---- + perform schemadoc.dropPath_int(p_pa_server, p_pa_client); + return schemadoc.createEvent ('_schemadoc', 'DROP_PATH', + p_pa_server, p_pa_client); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.droppath-int-integer-integer">droppath_int( integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>Process DROP_PATH event to drop path from pa_server to pa_client</p> + <pre> +declare + p_pa_server alias for $1; + p_pa_client alias for $2; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Remove any dangling sl_listen entries with the server + -- as provider and the client as receiver. This must have + -- been cleared out before, but obviously was not. + -- ---- + delete from schemadoc.sl_listen + where li_provider = p_pa_server + and li_receiver = p_pa_client; + + delete from schemadoc.sl_path + where pa_server = p_pa_server + and pa_client = p_pa_client; + + if found then + return 1; + else + return 0; + end if; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.dropset-integer">dropset( integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + v_origin int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that the set exists and originates here + -- ---- + select set_origin into v_origin from schemadoc.sl_set + where set_id = p_set_id; + if not found then + raise exception 'Slony-I: set % not found', p_set_id; + end if; + if v_origin != schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: set % does not originate on local node', + p_set_id; + end if; + + -- ---- + -- Call the internal drop set functionality and generate the event + -- ---- + perform schemadoc.dropSet_int(p_set_id); + return schemadoc.createEvent('_schemadoc', 'DROP_SET', + p_set_id); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.dropset-int-integer">dropset_int( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + v_tab_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Restore all tables original triggers and rules and remove + -- our replication stuff. + -- ---- + for v_tab_row in select tab_id from schemadoc.sl_table + where tab_set = p_set_id + order by tab_id + loop + perform schemadoc.alterTableRestore(v_tab_row.tab_id); + perform schemadoc.tableDropKey(v_tab_row.tab_id); + end loop; + + -- ---- + -- Remove all traces of the set configuration + -- ---- + delete from schemadoc.sl_sequence + where seq_set = p_set_id; + delete from schemadoc.sl_table + where tab_set = p_set_id; + delete from schemadoc.sl_subscribe + where sub_set = p_set_id; + delete from schemadoc.sl_setsync + where ssy_setid = p_set_id; + delete from schemadoc.sl_set + where set_id = p_set_id; + + return p_set_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.droptrigger-integer-name">droptrigger( integer, name )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_trig_tabid alias for $1; + p_trig_tgname alias for $2; +begin + perform schemadoc.dropTrigger_int(p_trig_tabid, p_trig_tgname); + return schemadoc.createEvent('_schemadoc', 'DROP_TRIGGER', + p_trig_tabid, p_trig_tgname); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.droptrigger-int-integer-name">droptrigger_int( integer, name )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_trig_tabid alias for $1; + p_trig_tgname alias for $2; + v_tab_altered boolean; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Get the current table status (altered or not) + -- ---- + select tab_altered into v_tab_altered + from schemadoc.sl_table where tab_id = p_trig_tabid; + if not found then + -- ---- + -- Not found is no hard error here, because that might + -- mean that we are not subscribed to that set + -- ---- + return 0; + end if; + + -- ---- + -- If the table is modified for replication, restore the original state + -- ---- + if v_tab_altered then + perform schemadoc.alterTableRestore(p_trig_tabid); + end if; + + -- ---- + -- Remove the entry from sl_trigger + -- ---- + delete from schemadoc.sl_trigger + where trig_tabid = p_trig_tabid + and trig_tgname = p_trig_tgname; + + -- ---- + -- Put the table back into replicated state if it was + -- ---- + if v_tab_altered then + perform schemadoc.alterTableForReplication(p_trig_tabid); + end if; + + return p_trig_tabid; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.enablenode-integer">enablenode( integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + <p>no_id - Node ID # + +Generate the ENABLE_NODE event for node no_id</p> + <pre> +declare + p_no_id alias for $1; + v_local_node_id int4; + v_node_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that we are the node to activate and that we are + -- currently disabled. + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + select * into v_node_row + from schemadoc.sl_node + where no_id = p_no_id + for update; + if not found then + raise exception 'Slony-I: node % not found', p_no_id; + end if; + if v_node_row.no_active then + raise exception 'Slony-I: node % is already active', p_no_id; + end if; + + -- ---- + -- Activate this node and generate the ENABLE_NODE event + -- ---- + perform schemadoc.enableNode_int (p_no_id); + return schemadoc.createEvent('_schemadoc', 'ENABLE_NODE', + p_no_id); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.enablenode-int-integer">enablenode_int( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>no_id - Node ID # + +Internal funciton to process the ENABLE_NODE event for node no_id</p> + <pre> +declare + p_no_id alias for $1; + v_local_node_id int4; + v_node_row record; + v_sub_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that the node is inactive + -- ---- + select * into v_node_row + from schemadoc.sl_node + where no_id = p_no_id + for update; + if not found then + raise exception 'Slony-I: node % not found', p_no_id; + end if; + if v_node_row.no_active then + return p_no_id; + end if; + + -- ---- + -- Activate the node and generate sl_confirm status rows for it. + -- ---- + update schemadoc.sl_node + set no_active = 't' + where no_id = p_no_id; + insert into schemadoc.sl_confirm + (con_origin, con_received, con_seqno) + select no_id, p_no_id, 0 from schemadoc.sl_node + where no_id != p_no_id + and no_active; + insert into schemadoc.sl_confirm + (con_origin, con_received, con_seqno) + select p_no_id, no_id, 0 from schemadoc.sl_node + where no_id != p_no_id + and no_active; + + -- ---- + -- Generate ENABLE_SUBSCRIPTION events for all sets that + -- origin here and are subscribed by the just enabled node. + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + for v_sub_row in select SUB.sub_set, SUB.sub_provider from + schemadoc.sl_set S, + schemadoc.sl_subscribe SUB + where S.set_origin = v_local_node_id + and S.set_id = SUB.sub_set + and SUB.sub_receiver = p_no_id + for update of S + loop + perform schemadoc.enableSubscription (v_sub_row.sub_set,, + v_sub_row.sub_provider, p_no_id); + end loop; + + return p_no_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.enablesubscription-integer-integer-integer">enablesubscription( integer, integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_sub_set alias for $1; + p_sub_provider alias for $2; + p_sub_receiver alias for $3; +begin + return schemadoc.enableSubscription_int (p_sub_set, + p_sub_provider, p_sub_receiver); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.enablesubscription-int-integer-integer-integer">enablesubscription_int( integer, integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_sub_set alias for $1; + p_sub_provider alias for $2; + p_sub_receiver alias for $3; + v_n int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- The real work is done in the replication engine. All + -- we have to do here is remembering that it happened. + -- ---- + update schemadoc.sl_subscribe + set sub_active = 't' + where sub_set = p_sub_set + and sub_receiver = p_sub_receiver; + get diagnostics v_n = row_count; + if v_n = 0 then + insert into schemadoc.sl_subscribe + (sub_set, sub_provider, sub_receiver, + sub_forward, sub_active) + values + (p_sub_set, p_sub_provider, p_sub_receiver, + false, true); + end if; + + return p_sub_set; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.failednode-integer-integer">failednode( integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_failed_node alias for $1; + p_backup_node alias for $2; + v_row record; + v_row2 record; + v_n int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- All consistency checks first + -- Check that every system that has a path to the failed node + -- also has a path to the backup node. + -- ---- + for v_row in select P.pa_client + from schemadoc.sl_path P + where P.pa_server = p_failed_node + and P.pa_client <> p_backup_node + and not exists (select true from schemadoc.sl_path PP + where PP.pa_server = p_backup_node + and PP.pa_client = P.pa_client) + loop + raise exception 'Slony-I: cannot failover - node % has no path to the backup node', + v_row.pa_client; + end loop; + + -- ---- + -- Check all sets originating on the failed node + -- ---- + for v_row in select set_id + from schemadoc.sl_set + where set_origin = p_failed_node + loop + -- ---- + -- Check that the backup node is subscribed to all sets + -- that origin on the failed node + -- ---- + select into v_row2 sub_forward, sub_active + from schemadoc.sl_subscribe + where sub_set = v_row.set_id + and sub_receiver = p_backup_node; + if not found then + raise exception 'Slony-I: cannot failover - node % is not subscribed to set %', + p_backup_node, v_row.set_id; + end if; + + -- ---- + -- Check that the subscription is active + -- ---- + if not v_row2.sub_active then + raise exception 'Slony-I: cannot failover - subscription for set % is not active', + v_row.set_id; + end if; + + -- ---- + -- If there are other subscribers, the backup node needs to + -- be a forwarder too. + -- ---- + select into v_n count(*) + from schemadoc.sl_subscribe + where sub_set = v_row.set_id + and sub_receiver <> p_backup_node; + if v_n > 0 and not v_row2.sub_forward then + raise exception 'Slony-I: cannot failover - node % is not a forwarder of set %', + p_backup_node, v_row.set_id; + end if; + end loop; + + -- ---- + -- Terminate all connections of the failed node the hard way + -- ---- + perform schemadoc.terminateNodeConnections( + '_schemadoc_Node_' || p_failed_node); + + -- ---- + -- Let every node that listens for something on the failed node + -- listen for that on the backup node instead. + -- ---- + for v_row in select * from schemadoc.sl_listen + where li_provider = p_failed_node + and li_receiver <> p_backup_node + loop + perform schemadoc.storeListen_int(v_row.li_origin, + p_backup_node, v_row.li_receiver); + end loop; + + -- ---- + -- Let the backup node listen for all events where the + -- failed node did listen for it. + -- ---- + for v_row in select li_origin, li_provider + from schemadoc.sl_listen + where li_receiver = p_failed_node + and li_provider <> p_backup_node + loop + perform schemadoc.storeListen_int(v_row.li_origin, + v_row.li_provider, p_backup_node); + end loop; + + -- ---- + -- Remove all sl_listen entries that receive anything from the + -- failed node. + -- ---- + delete from schemadoc.sl_listen + where li_provider = p_failed_node + or li_receiver = p_failed_node; + + -- ---- + -- Move the sets + -- ---- + for v_row in select S.set_id, (select count(*) + from schemadoc.sl_subscribe SUB + where S.set_id = SUB.sub_set + and SUB.sub_receiver <> p_backup_node + and SUB.sub_provider = p_failed_node) + as num_direct_receivers + from schemadoc.sl_set S + where S.set_origin = p_failed_node + for update + loop + -- ---- + -- If the backup node is the only direct subscriber ... + -- ---- + if v_row.num_direct_receivers = 0 then +raise notice 'failedNode: set % has no other direct receivers - move now', v_row.set_id; + -- ---- + -- backup_node is the only direct subscriber, move the set + -- right now. On the backup node itself that includes restoring + -- all user mode triggers, removing the protection trigger, + -- adding the log trigger, removing the subscription and the + -- obsolete setsync status. + -- ---- + if p_backup_node = schemadoc.getLocalNodeId('_schemadoc') then + for v_row2 in select * from schemadoc.sl_table + where tab_set = v_row.set_id + loop + perform schemadoc.alterTableRestore(v_row2.tab_id); + end loop; + end if; + + update schemadoc.sl_set set set_origin = p_backup_node + where set_id = v_row.set_id; + + if p_backup_node = schemadoc.getLocalNodeId('_schemadoc') then + delete from schemadoc.sl_setsync + where ssy_setid = v_row.set_id; + + for v_row2 in select * from schemadoc.sl_table + where tab_set = v_row.set_id + loop + perform schemadoc.alterTableForReplication(v_row2.tab_id); + end loop; + end if; + + delete from schemadoc.sl_subscribe + where sub_set = v_row.set_id + and sub_receiver = p_backup_node; + else +raise notice 'failedNode: set % has other direct receivers - change providers only', v_row.set_id; + -- ---- + -- Backup node is not the only direct subscriber. This + -- means that at this moment, we redirect all direct + -- subscribers to receive from the backup node, and the + -- backup node itself to receive from another one. + -- The admin utility will wait for the slon engine to + -- restart and then call failedNode2() on the node with + -- the highest SYNC and redirect this to it on + -- backup node later. + -- ---- + update schemadoc.sl_subscribe + set sub_provider = (select min(SS.sub_receiver) + from schemadoc.sl_subscribe SS + where SS.sub_set = v_row.set_id + and SS.sub_provider = p_failed_node + and SS.sub_receiver <> p_backup_node + and SS.sub_forward) + where sub_set = v_row.set_id + and sub_receiver = p_backup_node; + update schemadoc.sl_subscribe + set sub_provider = p_backup_node + where sub_set = v_row.set_id + and sub_provider = p_failed_node + and sub_receiver <> p_backup_node; + end if; + end loop; + + -- ---- + -- Make sure the node daemon will restart + -- ---- + notify "_schemadoc_Restart"; + + -- ---- + -- That is it - so far. + -- ---- + return p_failed_node; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.failoverset-int-integer-integer-integer">failoverset_int( integer, integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>FUNCTION failoverSet_int (failed_node, backup_node, set_id) + +Finish failover for one set.</p> + <pre> +declare + p_failed_node alias for $1; + p_backup_node alias for $2; + p_set_id alias for $3; + v_row record; + v_last_sync int8; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Change the origin of the set now to the backup node. + -- On the backup node this includes changing all the + -- trigger and protection stuff + -- ---- + if p_backup_node = schemadoc.getLocalNodeId('_schemadoc') then + for v_row in select * from schemadoc.sl_table + where tab_set = p_set_id + loop + perform schemadoc.alterTableRestore(v_row.tab_id); + end loop; + + delete from schemadoc.sl_setsync + where ssy_setid = p_set_id; + delete from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = p_backup_node; + update schemadoc.sl_set + set set_origin = p_backup_node + where set_id = p_set_id; + + for v_row in select * from schemadoc.sl_table + where tab_set = p_set_id + loop + perform schemadoc.alterTableForReplication(v_row.tab_id); + end loop; + else + delete from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = p_backup_node; + update schemadoc.sl_set + set set_origin = p_backup_node + where set_id = p_set_id; + end if; + + -- ---- + -- If we are a subscriber of the set ourself, change our + -- setsync status to reflect the new set origin. + -- ---- + if exists (select true from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = schemadoc.getLocalNodeId( + '_schemadoc')) + then + delete from schemadoc.sl_setsync + where ssy_setid = p_set_id; + + select coalesce(max(ev_seqno), 0) into v_last_sync + from schemadoc.sl_event + where ev_origin = p_backup_node + and ev_type = 'SYNC'; + if v_last_sync > 0 then + insert into schemadoc.sl_setsync + (ssy_setid, ssy_origin, ssy_seqno, + ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list) + select p_set_id, p_backup_node, v_last_sync, + ev_minxid, ev_maxxid, ev_xip, NULL + from schemadoc.sl_event + where ev_origin = p_backup_node + and ev_seqno = v_last_sync; + else + insert into schemadoc.sl_setsync + (ssy_setid, ssy_origin, ssy_seqno, + ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list) + values (p_set_id, p_backup_node, '0', + '0', '0', '', NULL); + end if; + + end if; + + return p_failed_node; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.forwardconfirm-integer-integer-bigint-timestamp-without-time-zone">forwardconfirm( integer, integer, bigint, timestamp without time zone )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_con_origin alias for $1; + p_con_received alias for $2; + p_con_seqno alias for $3; + p_con_timestamp alias for $4; + v_max_seqno bigint; +begin + select into v_max_seqno coalesce(max(con_seqno), 0) + from schemadoc.sl_confirm + where con_origin = p_con_origin + and con_received = p_con_received; + if v_max_seqno < p_con_seqno then + insert into schemadoc.sl_confirm + (con_origin, con_received, con_seqno, con_timestamp) + values (p_con_origin, p_con_received, p_con_seqno, + p_con_timestamp); + notify "_schemadoc_Confirm"; + v_max_seqno = p_con_seqno; + end if; + + return v_max_seqno; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.initializelocalnode-integer-text">initializelocalnode( integer, text )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>no_id - Node ID # +no_comment - Human-oriented comment + +Initializes the new node, no_id</p> + <pre> +declare + p_local_node_id alias for $1; + p_comment alias for $2; + v_old_node_id int4; + v_first_log_no int4; + v_event_seq int8; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Make sure this node is uninitialized or got reset + -- ---- + select last_value::int4 into v_old_node_id from schemadoc.sl_local_node_id; + if v_old_node_id != -1 then + raise exception 'Slony-I: This node is already initialized'; + end if; + + -- ---- + -- Set sl_local_node_id to the requested value and add our + -- own system to sl_node. + -- ---- + perform setval('schemadoc.sl_local_node_id', p_local_node_id); + perform setval('schemadoc.sl_rowid_seq', + p_local_node_id::int8 * '1000000000000000'::int8); + perform schemadoc.storeNode_int (p_local_node_id, p_comment); + + return p_local_node_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.lockset-integer">lockset( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + v_local_node_id int4; + v_set_row record; + v_tab_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that the set exists and that we are the origin + -- and that it is not already locked. + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + select * into v_set_row from schemadoc.sl_set + where set_id = p_set_id + for update; + if not found then + raise exception 'Slony-I: set % not found', p_set_id; + end if; + if v_set_row.set_origin <> v_local_node_id then + raise exception 'Slony-I: set % does not originate on local node', + p_set_id; + end if; + if v_set_row.set_locked notnull then + raise exception 'Slony-I: set % is already locked', p_set_id; + end if; + + -- ---- + -- Place the lockedSet trigger on all tables in the set. + -- ---- + for v_tab_row in select T.tab_id, + "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) as tab_fqname + from schemadoc.sl_table T, + "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN + where T.tab_set = p_set_id + and T.tab_reloid = PGC.oid + and PGC.relnamespace = PGN.oid + order by tab_id + loop + execute 'create trigger "_schemadoc_lockedset_' || + v_tab_row.tab_id || + '" before insert or update or delete on ' || + v_tab_row.tab_fqname || ' for each row execute procedure + schemadoc.lockedSet (''_schemadoc'');'; + end loop; + + -- ---- + -- Remember our snapshots xmax as for the set locking + -- ---- + update schemadoc.sl_set + set set_locked = schemadoc.getMaxXid() + where set_id = p_set_id; + + return p_set_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.mergeset-integer-integer">mergeset( integer, integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_add_id alias for $2; + v_origin int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that both sets exist and originate here + -- ---- + if p_set_id = p_add_id then + raise exception 'Slony-I: merged set ids cannot be identical'; + end if; + select set_origin into v_origin from schemadoc.sl_set + where set_id = p_set_id; + if not found then + raise exception 'Slony-I: set % not found', p_set_id; + end if; + if v_origin != schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: set % does not originate on local node', + p_set_id; + end if; + + select set_origin into v_origin from schemadoc.sl_set + where set_id = p_add_id; + if not found then + raise exception 'Slony-I: set % not found', p_add_id; + end if; + if v_origin != schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: set % does not originate on local node', + p_add_id; + end if; + + -- ---- + -- Check that both sets are subscribed by the same set of nodes + -- ---- + if exists (select true from schemadoc.sl_subscribe SUB1 + where SUB1.sub_set = p_set_id + and SUB1.sub_receiver not in (select SUB2.sub_receiver + from schemadoc.sl_subscribe SUB2 + where SUB2.sub_set = p_add_id)) + then + raise exception 'Slony-I: subscriber lists of set % and % are different', + p_set_id, p_add_id; + end if; + + if exists (select true from schemadoc.sl_subscribe SUB1 + where SUB1.sub_set = p_add_id + and SUB1.sub_receiver not in (select SUB2.sub_receiver + from schemadoc.sl_subscribe SUB2 + where SUB2.sub_set = p_set_id)) + then + raise exception 'Slony-I: subscriber lists of set % and % are different', + p_add_id, p_set_id; + end if; + + -- ---- + -- Create a SYNC event, merge the sets, create a MERGE_SET event + -- ---- + perform schemadoc.createEvent('_schemadoc', 'SYNC', NULL); + perform schemadoc.mergeSet_int(p_set_id, p_add_id); + return schemadoc.createEvent('_schemadoc', 'MERGE_SET', + p_set_id, p_add_id); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.mergeset-int-integer-integer">mergeset_int( integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_add_id alias for $2; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + update schemadoc.sl_sequence + set seq_set = p_set_id + where seq_set = p_add_id; + update schemadoc.sl_table + set tab_set = p_set_id + where tab_set = p_add_id; + delete from schemadoc.sl_subscribe + where sub_set = p_add_id; + delete from schemadoc.sl_setsync + where ssy_setid = p_add_id; + delete from schemadoc.sl_set + where set_id = p_add_id; + + return p_set_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.moveset-integer-integer">moveset( integer, integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_new_origin alias for $2; + v_local_node_id int4; + v_set_row record; + v_sub_row record; + v_sync_seqno int8; + v_lv_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that the set is locked and that this locking + -- happened long enough ago. + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + select * into v_set_row from schemadoc.sl_set + where set_id = p_set_id + for update; + if not found then + raise exception 'Slony-I: set % not found', p_set_id; + end if; + if v_set_row.set_origin <> v_local_node_id then + raise exception 'Slony-I: set % does not originate on local node', + p_set_id; + end if; + if v_set_row.set_locked isnull then + raise exception 'Slony-I: set % is not locked', p_set_id; + end if; + if v_set_row.set_locked > schemadoc.getMinXid() then + raise exception 'Slony-I: cannot move set % yet, transactions < % are still in progress', + p_set_id, v_set_row.set_locked; + end if; + + -- ---- + -- Unlock the set + -- ---- + perform schemadoc.unlockSet(p_set_id); + + -- ---- + -- Check that the new_origin is an active subscriber of the set + -- ---- + select * into v_sub_row from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = p_new_origin; + if not found then + raise exception 'Slony-I: set % is not subscribed by node %', + p_set_id, p_new_origin; + end if; + if not v_sub_row.sub_active then + raise exception 'Slony-I: subsctiption of node % for set % is inactive', + p_new_origin, p_set_id; + end if; + + -- ---- + -- Reconfigure everything + -- ---- + perform schemadoc.moveSet_int(p_set_id, v_local_node_id, + p_new_origin); + + -- ---- + -- At this time we hold access exclusive locks for every table + -- in the set. But we did move the set to the new origin, so the + -- createEvent() we are doing now will not record the sequences. + -- ---- + v_sync_seqno := schemadoc.createEvent('_schemadoc', 'SYNC'); + insert into schemadoc.sl_seqlog + (seql_seqid, seql_origin, seql_ev_seqno, seql_last_value) + select seq_id, v_local_node_id, v_sync_seqno, seq_last_value + from schemadoc.sl_seqlastvalue + where seq_set = p_set_id; + + -- ---- + -- Finally we generate the real event + -- ---- + return schemadoc.createEvent('_schemadoc', 'MOVE_SET', + p_set_id, v_local_node_id, p_new_origin); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.moveset-int-integer-integer-integer">moveset_int( integer, integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_old_origin alias for $2; + p_new_origin alias for $3; + v_local_node_id int4; + v_tab_row record; + v_sub_row record; + v_sub_node int4; + v_sub_last int4; + v_sub_next int4; + v_last_sync int8; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Get our local node ID + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + + -- ---- + -- If we are the old or new origin of the set, we need to + -- remove the log trigger from all tables first. + -- ---- + if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then + for v_tab_row in select tab_id from schemadoc.sl_table + where tab_set = p_set_id + order by tab_id + loop + perform schemadoc.alterTableRestore(v_tab_row.tab_id); + end loop; + end if; + + -- ---- + -- Next we have to reverse the subscription path + -- ---- + v_sub_last = p_new_origin; + select sub_provider into v_sub_node + from schemadoc.sl_subscribe + where sub_receiver = p_new_origin; + if not found then + raise exception 'Slony-I: subscription path broken in moveSet_int'; + end if; + while v_sub_node <> p_old_origin loop + -- ---- + -- Tracing node by node, the old receiver is now in + -- v_sub_last and the old provider is in v_sub_node. + -- ---- + + -- ---- + -- Get the current provider of this node as next + -- and change the provider to the previous one in + -- the reverse chain. + -- ---- + select sub_provider into v_sub_next + from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = v_sub_node + for update; + if not found then + raise exception 'Slony-I: subscription path broken in moveSet_int'; + end if; + update schemadoc.sl_subscribe + set sub_provider = v_sub_last + where sub_set = p_set_id + and sub_receiver = v_sub_node; + + v_sub_last = v_sub_node; + v_sub_node = v_sub_next; + end loop; + + -- ---- + -- This includes creating a subscription for the old origin + -- ---- + insert into schemadoc.sl_subscribe + (sub_set, sub_provider, sub_receiver, + sub_forward, sub_active) + values (p_set_id, v_sub_last, p_old_origin, true, true); + if v_local_node_id = p_old_origin then + select coalesce(max(ev_seqno), 0) into v_last_sync + from schemadoc.sl_event + where ev_origin = p_new_origin + and ev_type = 'SYNC'; + if v_last_sync > 0 then + insert into schemadoc.sl_setsync + (ssy_setid, ssy_origin, ssy_seqno, + ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list) + select p_set_id, p_new_origin, v_last_sync, + ev_minxid, ev_maxxid, ev_xip, NULL + from schemadoc.sl_event + where ev_origin = p_new_origin + and ev_seqno = v_last_sync; + else + insert into schemadoc.sl_setsync + (ssy_setid, ssy_origin, ssy_seqno, + ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list) + values (p_set_id, p_new_origin, '0', + '0', '0', '', NULL); + end if; + end if; + + -- ---- + -- Now change the ownership of the set. + -- ---- + update schemadoc.sl_set + set set_origin = p_new_origin + where set_id = p_set_id; + + -- ---- + -- On the new origin, delete the obsolete setsync information + -- and the subscription. + -- ---- + if v_local_node_id = p_new_origin then + delete from schemadoc.sl_setsync + where ssy_setid = p_set_id; + else + if v_local_node_id <> p_old_origin then + -- + -- On every other node, change the setsync so that it will + -- pick up from the new origins last known sync. + -- + delete from schemadoc.sl_setsync + where ssy_setid = p_set_id; + select coalesce(max(ev_seqno), 0) into v_last_sync + from schemadoc.sl_event + where ev_origin = p_new_origin + and ev_type = 'SYNC'; + if v_last_sync > 0 then + insert into schemadoc.sl_setsync + (ssy_setid, ssy_origin, ssy_seqno, + ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list) + select p_set_id, p_new_origin, v_last_sync, + ev_minxid, ev_maxxid, ev_xip, NULL + from schemadoc.sl_event + where ev_origin = p_new_origin + and ev_seqno = v_last_sync; + else + insert into schemadoc.sl_setsync + (ssy_setid, ssy_origin, ssy_seqno, + ssy_minxid, ssy_maxxid, ssy_xip, ssy_action_list) + values (p_set_id, p_new_origin, '0', + '0', '0', '', NULL); + end if; + end if; + end if; + delete from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = p_new_origin; + + -- ---- + -- If we are the new or old origin, we have to + -- put all the tables into altered state again. + -- ---- + if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then + for v_tab_row in select tab_id from schemadoc.sl_table + where tab_set = p_set_id + order by tab_id + loop + perform schemadoc.alterTableForReplication(v_tab_row.tab_id); + end loop; + end if; + + return p_set_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.sequencelastvalue-text">sequencelastvalue( text )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_seqname alias for $1; + v_seq_row record; +begin + for v_seq_row in execute 'select last_value from ' || p_seqname + loop + return v_seq_row.last_value; + end loop; + + -- not reached +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.sequencesetvalue-integer-integer-bigint-bigint">sequencesetvalue( integer, integer, bigint, bigint )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_seq_id alias for $1; + p_seq_origin alias for $2; + p_ev_seqno alias for $3; + p_last_value alias for $4; + v_fqname text; +begin + -- ---- + -- Get the sequences fully qualified name + -- ---- + select "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) into v_fqname + from schemadoc.sl_sequence SQ, + "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN + where SQ.seq_id = p_seq_id + and SQ.seq_reloid = PGC.oid + and PGC.relnamespace = PGN.oid; + if not found then + raise exception 'Slony-I: sequence % not found', p_seq_id; + end if; + + -- ---- + -- Update it to the new value + -- ---- + execute 'select setval(''' || v_fqname || + ''', ''' || p_last_value || ''')'; + + insert into schemadoc.sl_seqlog + (seql_seqid, seql_origin, seql_ev_seqno, seql_last_value) + values (p_seq_id, p_seq_origin, p_ev_seqno, p_last_value); + + return p_seq_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.setaddsequence-integer-integer-text-text">setaddsequence( integer, integer, text, text )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_seq_id alias for $2; + p_fqname alias for $3; + p_seq_comment alias for $4; + v_set_origin int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that we are the origin of the set + -- ---- + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_set_id; + if not found then + raise exception 'Slony-I: setAddSequence(): set % not found', p_set_id; + end if; + if v_set_origin != schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: setAddSequence(): set % has remote origin', p_set_id; + end if; + + if exists (select true from schemadoc.sl_subscribe + where sub_set = p_set_id) + then + raise exception 'Slony-I: cannot add sequence to currently subscribed set %', + p_set_id; + end if; + + -- ---- + -- Add the sequence to the set and generate the SET_ADD_SEQUENCE event + -- ---- + perform schemadoc.setAddSequence_int(p_set_id, p_seq_id, p_fqname, + p_seq_comment); + return schemadoc.createEvent('_schemadoc', 'SET_ADD_SEQUENCE', + p_set_id, p_seq_id, p_fqname, p_seq_comment); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.setaddsequence-int-integer-integer-text-text">setaddsequence_int( integer, integer, text, text )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_seq_id alias for $2; + p_fqname alias for $3; + p_seq_comment alias for $4; + v_local_node_id int4; + v_set_origin int4; + v_sub_provider int4; + v_relkind char; + v_seq_reloid oid; + v_sync_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- For sets with a remote origin, check that we are subscribed + -- to that set. Otherwise we ignore the sequence because it might + -- not even exist in our database. + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_set_id; + if not found then + raise exception 'Slony-I: setAddSequence_int(): set % not found', + p_set_id; + end if; + if v_set_origin != v_local_node_id then + select sub_provider into v_sub_provider + from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = schemadoc.getLocalNodeId('_schemadoc'); + if not found then + return 0; + end if; + end if; + + -- ---- + -- Get the sequences OID and check that it is a sequence + -- ---- + select PGC.oid, PGC.relkind into v_seq_reloid, v_relkind + from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN + where PGC.relnamespace = PGN.oid + and p_fqname = "pg_catalog".quote_ident(PGN.nspname) || + '.' || "pg_catalog".quote_ident(PGC.relname); + if not found then + raise exception 'Slony-I: setAddSequence_int(): sequence % not found', + p_fqname; + end if; + if v_relkind != 'S' then + raise exception 'Slony-I: setAddSequence_int(): % is not a sequence', + p_fqname; + end if; + + -- ---- + -- Add the sequence to sl_sequence + -- ---- + insert into schemadoc.sl_sequence + (seq_id, seq_reloid, seq_set, seq_comment) values + (p_seq_id, v_seq_reloid, p_set_id, p_seq_comment); + + -- ---- + -- On the set origin, fake a sl_seqlog row for the last sync event + -- ---- + if v_set_origin = v_local_node_id then + for v_sync_row in select coalesce (max(ev_seqno), 0) as ev_seqno + from schemadoc.sl_event + where ev_origin = v_local_node_id + and ev_type = 'SYNC' + loop + insert into schemadoc.sl_seqlog + (seql_seqid, seql_origin, seql_ev_seqno, + seql_last_value) values + (p_seq_id, v_local_node_id, v_sync_row.ev_seqno, + schemadoc.sequenceLastValue(p_fqname)); + end loop; + end if; + + return p_seq_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.setaddtable-integer-integer-text-name-text">setaddtable( integer, integer, text, name, text )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_tab_id alias for $2; + p_fqname alias for $3; + p_tab_idxname alias for $4; + p_tab_comment alias for $5; + v_set_origin int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that we are the origin of the set + -- ---- + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_set_id; + if not found then + raise exception 'Slony-I: setAddTable(): set % not found', p_set_id; + end if; + if v_set_origin != schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: setAddTable(): set % has remote origin', p_set_id; + end if; + + if exists (select true from schemadoc.sl_subscribe + where sub_set = p_set_id) + then + raise exception 'Slony-I: cannot add table to currently subscribed set %', + p_set_id; + end if; + + -- ---- + -- Add the table to the set and generate the SET_ADD_TABLE event + -- ---- + perform schemadoc.setAddTable_int(p_set_id, p_tab_id, p_fqname, + p_tab_idxname, p_tab_comment); + return schemadoc.createEvent('_schemadoc', 'SET_ADD_TABLE', + p_set_id, p_tab_id, p_fqname, + p_tab_idxname, p_tab_comment); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.setaddtable-int-integer-integer-text-name-text">setaddtable_int( integer, integer, text, name, text )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_tab_id alias for $2; + p_fqname alias for $3; + p_tab_idxname alias for $4; + p_tab_comment alias for $5; + v_local_node_id int4; + v_set_origin int4; + v_sub_provider int4; + v_relkind char; + v_tab_reloid oid; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- For sets with a remote origin, check that we are subscribed + -- to that set. Otherwise we ignore the table because it might + -- not even exist in our database. + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_set_id; + if not found then + raise exception 'Slony-I: setAddTable_int(): set % not found', + p_set_id; + end if; + if v_set_origin != v_local_node_id then + select sub_provider into v_sub_provider + from schemadoc.sl_subscribe + where sub_set = p_set_id + and sub_receiver = schemadoc.getLocalNodeId('_schemadoc'); + if not found then + return 0; + end if; + end if; + + -- ---- + -- Get the tables OID and check that it is a real table + -- ---- + select PGC.oid, PGC.relkind into v_tab_reloid, v_relkind + from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN + where PGC.relnamespace = PGN.oid + and p_fqname = "pg_catalog".quote_ident(PGN.nspname) || + '.' || "pg_catalog".quote_ident(PGC.relname); + if not found then + raise exception 'Slony-I: setAddTable(): table % not found', + p_fqname; + end if; + if v_relkind != 'r' then + raise exception 'Slony-I: setAddTable(): % is not a regular table', + p_fqname; + end if; + + if not exists (select indexrelid + from "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGC + where PGX.indrelid = v_tab_reloid + and PGX.indexrelid = PGC.oid + and PGC.relname = p_tab_idxname) + then + raise exception 'Slony-I: setAddTable(): table % has no index %', + p_fqname, p_tab_idxname; + end if; + + -- ---- + -- Add the table to sl_table and create the trigger on it. + -- ---- + insert into schemadoc.sl_table + (tab_id, tab_reloid, tab_set, tab_idxname, + tab_altered, tab_comment) values + (p_tab_id, v_tab_reloid, p_set_id, p_tab_idxname, + false, p_tab_comment); + perform schemadoc.alterTableForReplication(p_tab_id); + + return p_tab_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storelisten-integer-integer-integer">storelisten( integer, integer, integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_li_origin alias for $1; + p_li_provider alias for $2; + p_li_receiver alias for $3; +begin + perform schemadoc.storeListen_int (p_li_origin, p_li_provider, p_li_receiver); + return schemadoc.createEvent ('_schemadoc', 'STORE_LISTEN', + p_li_origin, p_li_provider, p_li_receiver); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storelisten-int-integer-integer-integer">storelisten_int( integer, integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_li_origin alias for $1; + p_li_provider alias for $2; + p_li_receiver alias for $3; + v_exists int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + select 1 into v_exists + from schemadoc.sl_listen + where li_origin = p_li_origin + and li_provider = p_li_provider + and li_receiver = p_li_receiver; + if not found then + -- ---- + -- In case we receive STORE_LISTEN events before we know + -- about the nodes involved in this, we generate those nodes + -- as pending. + -- ---- + if not exists (select 1 from schemadoc.sl_node + where no_id = p_li_origin) then + perform schemadoc.storeNode_int (p_li_origin, '<event pending>'); + end if; + if not exists (select 1 from schemadoc.sl_node + where no_id = p_li_provider) then + perform schemadoc.storeNode_int (p_li_provider, '<event pending>'); + end if; + if not exists (select 1 from schemadoc.sl_node + where no_id = p_li_receiver) then + perform schemadoc.storeNode_int (p_li_receiver, '<event pending>'); + end if; + + insert into schemadoc.sl_listen + (li_origin, li_provider, li_receiver) values + (p_li_origin, p_li_provider, p_li_receiver); + end if; + + return 0; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storenode-integer-text">storenode( integer, text )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + <p>no_id - Node ID # +no_comment - Human-oriented comment + +Generate the STORE_NODE event for node no_id</p> + <pre> +declare + p_no_id alias for $1; + p_no_comment alias for $2; +begin + perform schemadoc.storeNode_int (p_no_id, p_no_comment); + return schemadoc.createEvent('_schemadoc', 'STORE_NODE', + p_no_id, p_no_comment); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storenode-int-integer-text">storenode_int( integer, text )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>no_id - Node ID # +no_comment - Human-oriented comment + +Internal function to process the STORE_NODE event for node no_id</p> + <pre> +declare + p_no_id alias for $1; + p_no_comment alias for $2; + v_old_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check if the node exists + -- ---- + select * into v_old_row + from schemadoc.sl_node + where no_id = p_no_id + for update; + if found then + -- ---- + -- Node exists, update the existing row. + -- ---- + update schemadoc.sl_node + set no_comment = p_no_comment + where no_id = p_no_id; + else + -- ---- + -- New node, insert the sl_node row + -- ---- + insert into schemadoc.sl_node + (no_id, no_active, no_comment) values + (p_no_id, 'f', p_no_comment); + end if; + + return p_no_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storepath-integer-integer-text-integer">storepath( integer, integer, text, integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + <p>FUNCTION storePath (pa_server, pa_client, pa_conninfo, pa_connretry) + +Generate the STORE_PATH event indicating that node pa_client can +access node pa_server using DSN pa_conninfo</p> + <pre> +declare + p_pa_server alias for $1; + p_pa_client alias for $2; + p_pa_conninfo alias for $3; + p_pa_connretry alias for $4; +begin + perform schemadoc.storePath_int(p_pa_server, p_pa_client, + p_pa_conninfo, p_pa_connretry); + return schemadoc.createEvent('_schemadoc', 'STORE_PATH', + p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storepath-int-integer-integer-text-integer">storepath_int( integer, integer, text, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>FUNCTION storePath (pa_server, pa_client, pa_conninfo, pa_connretry) + +Process the STORE_PATH event indicating that node pa_client can +access node pa_server using DSN pa_conninfo</p> + <pre> +declare + p_pa_server alias for $1; + p_pa_client alias for $2; + p_pa_conninfo alias for $3; + p_pa_connretry alias for $4; + v_dummy int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check if the path already exists + -- ---- + select 1 into v_dummy + from schemadoc.sl_path + where pa_server = p_pa_server + and pa_client = p_pa_client + for update; + if found then + -- ---- + -- Path exists, update pa_conninfo + -- ---- + update schemadoc.sl_path + set pa_conninfo = p_pa_conninfo, + pa_connretry = p_pa_connretry + where pa_server = p_pa_server + and pa_client = p_pa_client; + else + -- ---- + -- New path + -- + -- In case we receive STORE_PATH events before we know + -- about the nodes involved in this, we generate those nodes + -- as pending. + -- ---- + if not exists (select 1 from schemadoc.sl_node + where no_id = p_pa_server) then + perform schemadoc.storeNode_int (p_pa_server, '<event pending>'); + end if; + if not exists (select 1 from schemadoc.sl_node + where no_id = p_pa_client) then + perform schemadoc.storeNode_int (p_pa_client, '<event pending>'); + end if; + insert into schemadoc.sl_path + (pa_server, pa_client, pa_conninfo, pa_connretry) values + (p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry); + end if; + + return 0; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storeset-integer-text">storeset( integer, text )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_set_comment alias for $2; + v_local_node_id int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + + insert into schemadoc.sl_set + (set_id, set_origin, set_comment) values + (p_set_id, v_local_node_id, p_set_comment); + + return schemadoc.createEvent('_schemadoc', 'STORE_SET', + p_set_id, v_local_node_id, p_set_comment); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storeset-int-integer-integer-text">storeset_int( integer, integer, text )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + p_set_origin alias for $2; + p_set_comment alias for $3; + v_dummy int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + select 1 into v_dummy + from schemadoc.sl_set + where set_id = p_set_id + for update; + if found then + update schemadoc.sl_set + set set_comment = p_set_comment + where set_id = p_set_id; + else + if not exists (select 1 from schemadoc.sl_node + where no_id = p_set_origin) then + perform schemadoc.storeNode_int (p_set_origin, '<event pending>'); + end if; + insert into schemadoc.sl_set + (set_id, set_origin, set_comment) values + (p_set_id, p_set_origin, p_set_comment); + end if; + + return p_set_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storetrigger-integer-name">storetrigger( integer, name )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_trig_tabid alias for $1; + p_trig_tgname alias for $2; +begin + perform schemadoc.storeTrigger_int(p_trig_tabid, p_trig_tgname); + return schemadoc.createEvent('_schemadoc', 'STORE_TRIGGER', + p_trig_tabid, p_trig_tgname); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.storetrigger-int-integer-name">storetrigger_int( integer, name )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_trig_tabid alias for $1; + p_trig_tgname alias for $2; + v_tab_altered boolean; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Get the current table status (altered or not) + -- ---- + select tab_altered into v_tab_altered + from schemadoc.sl_table where tab_id = p_trig_tabid; + if not found then + -- ---- + -- Not found is no hard error here, because that might + -- mean that we are not subscribed to that set + -- ---- + return 0; + end if; + + -- ---- + -- If the table is modified for replication, restore the original state + -- ---- + if v_tab_altered then + perform schemadoc.alterTableRestore(p_trig_tabid); + end if; + + -- ---- + -- Make sure that an entry for this trigger exists + -- ---- + delete from schemadoc.sl_trigger + where trig_tabid = p_trig_tabid + and trig_tgname = p_trig_tgname; + insert into schemadoc.sl_trigger ( + trig_tabid, trig_tgname + ) values ( + p_trig_tabid, p_trig_tgname + ); + + -- ---- + -- Put the table back into replicated state if it was + -- ---- + if v_tab_altered then + perform schemadoc.alterTableForReplication(p_trig_tabid); + end if; + + return p_trig_tabid; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.subscribeset-integer-integer-integer-boolean">subscribeset( integer, integer, integer, boolean )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_sub_set alias for $1; + p_sub_provider alias for $2; + p_sub_receiver alias for $3; + p_sub_forward alias for $4; + v_set_origin int4; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that this is called on the receiver node + -- ---- + if p_sub_receiver != schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: subscribeSet() must be called on receiver'; + end if; + + -- ---- + -- Check that the origin and provider of the set are remote + -- ---- + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_sub_set; + if not found then + raise exception 'Slony-I: set % not found', p_sub_set; + end if; + if v_set_origin = p_sub_receiver then + raise exception + 'Slony-I: set origin and receiver cannot be identical'; + end if; + if p_sub_receiver = p_sub_provider then + raise exception + 'Slony-I: set provider and receiver cannot be identical'; + end if; + + -- ---- + -- Call the internal procedure to store the subscription + -- ---- + perform schemadoc.subscribeSet_int(p_sub_set, p_sub_provider, + p_sub_receiver, p_sub_forward); + + -- ---- + -- Create the SUBSCRIBE_SET event + -- ---- + return schemadoc.createEvent('_schemadoc', 'SUBSCRIBE_SET', + p_sub_set, p_sub_provider, p_sub_receiver, + case p_sub_forward when true then 't' else 'f' end); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.subscribeset-int-integer-integer-integer-boolean">subscribeset_int( integer, integer, integer, boolean )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_sub_set alias for $1; + p_sub_provider alias for $2; + p_sub_receiver alias for $3; + p_sub_forward alias for $4; + v_set_origin int4; + v_sub_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Provider change is only allowed for active sets + -- ---- + if p_sub_receiver = schemadoc.getLocalNodeId('_schemadoc') then + select sub_active into v_sub_row from schemadoc.sl_subscribe + where sub_set = p_sub_set + and sub_receiver = p_sub_receiver; + if found then + if not v_sub_row.sub_active then + raise exception 'Slony-I: set % is not active, cannot change provider', + p_sub_set; + end if; + end if; + end if; + + -- ---- + -- Try to change provider and/or forward for an existing subscription + -- ---- + update schemadoc.sl_subscribe + set sub_provider = p_sub_provider, + sub_forward = p_sub_forward + where sub_set = p_sub_set + and sub_receiver = p_sub_receiver; + if found then + return p_sub_set; + end if; + + -- ---- + -- Not found, insert a new one + -- ---- + if not exists (select true from schemadoc.sl_path + where pa_server = p_sub_provider + and pa_client = p_sub_receiver) + then + insert into schemadoc.sl_path + (pa_server, pa_client, pa_conninfo, pa_connretry) + values + (p_sub_provider, p_sub_receiver, + '<event pending>', 10); + end if; + insert into schemadoc.sl_subscribe + (sub_set, sub_provider, sub_receiver, sub_forward, sub_active) + values (p_sub_set, p_sub_provider, p_sub_receiver, + p_sub_forward, false); + + -- ---- + -- If the set origin is here, then enable the subscription + -- ---- + select set_origin into v_set_origin + from schemadoc.sl_set + where set_id = p_sub_set; + if not found then + raise exception 'Slony-I: set % not found', p_sub_set; + end if; + + if v_set_origin = schemadoc.getLocalNodeId('_schemadoc') then + perform schemadoc.createEvent('_schemadoc', 'ENABLE_SUBSCRIPTION', + p_sub_set, p_sub_provider, p_sub_receiver, + case p_sub_forward when true then 't' else 'f' end); + perform schemadoc.enableSubscription(p_sub_set, + p_sub_provider, p_sub_receiver); + end if; + + return p_sub_set; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.tableaddkey-text">tableaddkey( text )</a> + </h2> +<h3>Returns: text</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_fqname alias for $1; + v_attkind text default ''; + v_attrow record; + v_have_serial bool default 'f'; +begin + -- + -- Loop over the attributes of this relation + -- and add a "v" for every user column, and a "k" + -- if we find the Slony-I special serial column. + -- + for v_attrow in select PGA.attnum, PGA.attname + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_attribute PGA + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGN.oid = PGC.relnamespace + and PGA.attrelid = PGC.oid + and not PGA.attisdropped + and PGA.attnum > 0 + order by attnum + loop + if v_attrow.attname = '_Slony-I_schemadoc_rowID' then + v_attkind := v_attkind || 'k'; + v_have_serial := 't'; + else + v_attkind := v_attkind || 'v'; + end if; + end loop; + + -- + -- A table must have at least one attribute, so not finding + -- anything means the table does not exist. + -- + if not found then + raise exception 'Slony-I: table % not found', p_tab_fqname; + end if; + + -- + -- If it does not have the special serial column, we + -- have to add it. This will be only half way done. + -- The function to add the table to the set must finish + -- these definitions with NOT NULL and UNIQUE after + -- updating all existing rows. + -- + if not v_have_serial then + execute 'lock table ' || p_tab_fqname || + ' in access exclusive mode'; + execute 'alter table only ' || p_tab_fqname || + ' add column "_Slony-I_schemadoc_rowID" bigint;'; + execute 'alter table only ' || p_tab_fqname || + ' alter column "_Slony-I_schemadoc_rowID" ' || + ' set default "pg_catalog".nextval(''schemadoc.sl_rowid_seq'');'; + + v_attkind := v_attkind || 'k'; + end if; + + -- + -- Return the resulting Slony-I attkind + -- + return v_attkind; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.tabledropkey-integer">tabledropkey( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_id alias for $1; + v_tab_fqname text; + v_tab_oid oid; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Construct the tables fully qualified name and get its oid + -- ---- + select "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname), + PGC.oid into v_tab_fqname, v_tab_oid + from schemadoc.sl_table T, + "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN + where T.tab_id = p_tab_id + and T.tab_reloid = PGC.oid + and PGC.relnamespace = PGN.oid; + if not found then + raise exception 'Slony-I: table with ID % not found', p_tab_id; + end if; + + -- ---- + -- Drop the special serial ID column if the table has it + -- ---- + if exists (select true from "pg_catalog".pg_attribute + where attrelid = v_tab_oid + and attname = '_Slony-I_schemadoc_rowID') + then + execute 'lock table ' || v_tab_fqname || + ' in access exclusive mode'; + execute 'alter table ' || v_tab_fqname || + ' drop column "_Slony-I_schemadoc_rowID"'; + end if; + + return p_tab_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.tablehasserialkey-text">tablehasserialkey( text )</a> + </h2> +<h3>Returns: boolean</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_tab_fqname alias for $1; + v_attnum int2; +begin + select PGA.attnum into v_attnum + from "pg_catalog".pg_class PGC, + "pg_catalog".pg_namespace PGN, + "pg_catalog".pg_attribute PGA + where "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) = p_tab_fqname + and PGC.relnamespace = PGN.oid + and PGA.attrelid = PGC.oid + and PGA.attname = '_Slony-I_schemadoc_rowID' + and not PGA.attisdropped; + return found; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.uninstallnode">uninstallnode( )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + <p>Reset the whole database to standalone by removing the whole +replication system.</p> + <pre> +declare + v_tab_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- This is us ... time for suicide! Restore all tables to + -- their original status. + -- ---- + for v_tab_row in select * from schemadoc.sl_table loop + perform schemadoc.alterTableRestore(v_tab_row.tab_id); + perform schemadoc.tableDropKey(v_tab_row.tab_id); + end loop; + + raise notice 'Slony-I: Please drop schema "_schemadoc"'; + return 0; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.unlockset-integer">unlockset( integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_set_id alias for $1; + v_local_node_id int4; + v_set_row record; + v_tab_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that the set exists and that we are the origin + -- and that it is not already locked. + -- ---- + v_local_node_id := schemadoc.getLocalNodeId('_schemadoc'); + select * into v_set_row from schemadoc.sl_set + where set_id = p_set_id + for update; + if not found then + raise exception 'Slony-I: set % not found', p_set_id; + end if; + if v_set_row.set_origin <> v_local_node_id then + raise exception 'Slony-I: set % does not originate on local node', + p_set_id; + end if; + if v_set_row.set_locked isnull then + raise exception 'Slony-I: set % is not locked', p_set_id; + end if; + + -- ---- + -- Drop the lockedSet trigger from all tables in the set. + -- ---- + for v_tab_row in select T.tab_id, + "pg_catalog".quote_ident(PGN.nspname) || '.' || + "pg_catalog".quote_ident(PGC.relname) as tab_fqname + from schemadoc.sl_table T, + "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN + where T.tab_set = p_set_id + and T.tab_reloid = PGC.oid + and PGC.relnamespace = PGN.oid + order by tab_id + loop + execute 'drop trigger "_schemadoc_lockedset_' || + v_tab_row.tab_id || '" on ' || v_tab_row.tab_fqname; + end loop; + + -- ---- + -- Clear out the set_locked field + -- ---- + update schemadoc.sl_set + set set_locked = NULL + where set_id = p_set_id; + + return p_set_id; +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.unsubscribeset-integer-integer">unsubscribeset( integer, integer )</a> + </h2> +<h3>Returns: bigint</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_sub_set alias for $1; + p_sub_receiver alias for $2; + v_tab_row record; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- Check that this is called on the receiver node + -- ---- + if p_sub_receiver != schemadoc.getLocalNodeId('_schemadoc') then + raise exception 'Slony-I: unsubscribeSet() must be called on receiver'; + end if; + + -- ---- + -- Check that this does not break any chains + -- ---- + if exists (select true from schemadoc.sl_subscribe + where sub_set = p_sub_set + and sub_provider = p_sub_receiver) + then + raise exception 'Slony-I: Cannot unsubscibe set % while being provider', + p_sub_set; + end if; + + -- ---- + -- Restore all tables original triggers and rules and remove + -- our replication stuff. + -- ---- + for v_tab_row in select tab_id from schemadoc.sl_table + where tab_set = p_sub_set + order by tab_id + loop + perform schemadoc.alterTableRestore(v_tab_row.tab_id); + perform schemadoc.tableDropKey(v_tab_row.tab_id); + end loop; + + -- ---- + -- Remove the setsync status. This will also cause the + -- worker thread to ignore the set and stop replicating + -- right now. + -- ---- + delete from schemadoc.sl_setsync + where ssy_setid = p_sub_set; + + -- ---- + -- Remove all sl_table entries for this set. + -- Should we ever subscribe again, the initial data + -- copy process will create new ones. + -- ---- + delete from schemadoc.sl_table + where tab_set = p_sub_set; + + -- ---- + -- Call the internal procedure to drop the subscription + -- ---- + perform schemadoc.unsubscribeSet_int(p_sub_set, p_sub_receiver); + + -- ---- + -- Create the UNSUBSCRIBE_SET event + -- ---- + return schemadoc.createEvent('_schemadoc', 'UNSUBSCRIBE_SET', + p_sub_set, p_sub_receiver); +end; +</pre> + + <hr> + <h2>Function: + <a href="#schemadoc.schema"><a name="schemadoc.function.unsubscribeset-int-integer-integer">unsubscribeset_int( integer, integer )</a> + </h2> +<h3>Returns: integer</h3> +<h3>Language: PLPGSQL</h3> + + <pre> +declare + p_sub_set alias for $1; + p_sub_receiver alias for $2; +begin + -- ---- + -- Grab the central configuration lock + -- ---- + lock table schemadoc.sl_config_lock; + + -- ---- + -- All the real work is done before event generation on the + -- subscriber. + -- ---- + delete from schemadoc.sl_subscribe + where sub_set = p_sub_set + and sub_receiver = p_sub_receiver; + + return p_sub_set; +end; +</pre> + + + +<p class="w3ref">Generated by <a href="http://www.rbt.ca/autodoc/">PostgreSQL Autodoc</a></p> +<p class="w3ref"><a href="http://validator.w3.org/check/referer">W3C HTML 4.01 Strict</a></p> +</body></html> Index: Makefile =================================================================== RCS file: /usr/local/cvsroot/slony1/slony1-engine/doc/howto/Makefile,v retrieving revision 1.2 retrieving revision 1.3 diff -Ldoc/howto/Makefile -Ldoc/howto/Makefile -u -w -r1.2 -r1.3 --- doc/howto/Makefile +++ doc/howto/Makefile @@ -9,7 +9,7 @@ slony_top_builddir = ../.. include $(slony_top_builddir)/Makefile.global -DISTFILES = Makefile $(wildcard *.txt*) $(wildcard *.html*) +DISTFILES = Makefile $(wildcard *.txt*) $(wildcard *.html*) schemadoc.html distdir: $(DISTFILES) mkdir $(distdir)/$(subdir) @@ -18,5 +18,32 @@ cp $$file $(distdir)/$(subdir)/$$file ; \ done +# Here's a somewhat fiddly way of generating documentation for the set +# of functions and tables using Rod Taylor's postgresql_autodoc tool +# schemadoc is actually likely to be checked into CVS, so you don't +# _always_ want to recreate it + +# Assumptions: +# - it's safe to create database "schemadoc" on a local database +# - my "createlang" hides in a bit of an odd place +# - "make clean" should really drop the database +# - you need to manually drop the database before regenning the docs + +BASEDIR=../../src/backend +BASESQL=$(BASEDIR)/slony1_base.sql +BASEFUNS=$(BASEDIR)/slony1_funcs.sql +# Might want to add version-specific functions, too... +TEMPDB=schemadoc +TEMPSCHEMA=schemadoc +CREATELANG=/usr/lib/postgresql/bin/createlang # That's how it is for me... +AUTODOC=postgresql_autodoc + +schemadoc.html: $(BASESQL) $(BASEFUNS) + createdb $(TEMPDB) + $(CREATELANG) plpgsql $(TEMPDB) + echo "drop schema $(TEMPSCHEMA);create schema $(TEMPSCHEMA);" | psql $(TEMPDB) + cat $(BASESQL) $(BASEFUNS) | sed "s/@NAMESPACE@/$(TEMPSCHEMA)/g" | sed "s/@CLUSTERNAME@/$(TEMPSCHEMA)/g" | psql $(TEMPDB) + $(AUTODOC) -d $(TEMPDB) -s $(TEMPSCHEMA) -t html + clean:
- Previous message: [Slony1-commit] By darcyb: We prefer the use of slony.info over slony.org
- Next message: [Slony1-commit] By cbbrowne: Added localxid.sql, that generates a domain for the "XXID"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Slony1-commit mailing list