1
1
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
2
2
\echo Use " CREATE EXTENSION pg_shardman" to load this file. \quit
3
3
4
+ -- Functions here use some gucs defined in .so, so we have to ensure that the
5
+ -- library is actually loaded.
6
+ DO $$
7
+ BEGIN
8
+ -- Yes, malicious user might have another extension containing 'pg_shardman'...
9
+ -- Probably better just call no-op func from the library
10
+ IF strpos(current_setting(' shared_preload_libraries' ), ' pg_shardman' ) = 0 THEN
11
+ RAISE EXCEPTION ' pg_shardman must be loaded via shared_preload_libraries. Refusing to proceed.' ;
12
+ END IF;
13
+ END
14
+ $$;
15
+
16
+ -- list of nodes present in the cluster
17
+ CREATE TABLE nodes (
18
+ id serial PRIMARY KEY ,
19
+ connstring text ,
20
+ active bool NOT NULL -- if false, we haven't yet finished adding it
21
+ );
22
+
23
+ -- Currently it is used just to store node id, in general we can keep any local
24
+ -- node metadata here. If is ever used extensively, probably hstore suits better.
25
+ CREATE TABLE local_meta (
26
+ k text NOT NULL , -- key
27
+ v text -- value
28
+ );
29
+ INSERT INTO @extschema@.local_meta VALUES (' node_id' , NULL );
30
+
4
31
-- available commands
5
32
CREATE TYPE cmd AS ENUM (' add_node' , ' remove_node' );
6
33
-- command status
@@ -9,7 +36,10 @@ CREATE TYPE cmd_status AS ENUM ('waiting', 'canceled', 'failed', 'in progress',
9
36
CREATE TABLE cmd_log (
10
37
id bigserial PRIMARY KEY ,
11
38
cmd_type cmd NOT NULL ,
12
- status cmd_status DEFAULT ' waiting' NOT NULL
39
+ status cmd_status DEFAULT ' waiting' NOT NULL ,
40
+ -- only for add_node cmd -- generated id for newly added node. Cleaner
41
+ -- to keep that is separate table...
42
+ node_id int REFERENCES nodes(id)
13
43
);
14
44
15
45
-- Notify shardman master bgw about new commands
@@ -34,19 +64,6 @@ CREATE TABLE cmd_opts (
34
64
opt text NOT NULL
35
65
);
36
66
37
- -- list of nodes present in the cluster
38
- CREATE TABLE nodes (
39
- id serial PRIMARY KEY ,
40
- connstring text
41
- );
42
-
43
- -- Currently it is used just to store node id, in general we can keep any local
44
- -- node metadata here. If is ever used extensively, probably hstore suits better.
45
- CREATE TABLE local_meta (
46
- k text NOT NULL , -- key
47
- v text -- value
48
- );
49
- INSERT INTO @extschema@.local_meta VALUES (' node_id' , NULL );
50
67
51
68
-- Internal functions
52
69
@@ -63,13 +80,47 @@ $$ LANGUAGE plpgsql;
63
80
-- These tables will be replicated to worker nodes, notifying them about changes.
64
81
-- Called on worker nodes.
65
82
CREATE FUNCTION create_meta_sub () RETURNS void AS $$
83
+ DECLARE
84
+ master_connstring text ;
66
85
BEGIN
67
- IF NOT EXISTS (SELECT * FROM pg_publication WHERE pubname = ' shardman_meta_pub' ) THEN
68
- CREATE PUBLICATION shardman_meta_pub FOR TABLE shardman .nodes ;
69
- END IF;
86
+ SELECT pg_settings .setting into master_connstring from pg_settings
87
+ WHERE NAME = ' shardman.master_connstring' ;
88
+ -- Note that 'CONNECTION $1...' USING master_connstring won't work here
89
+ EXECUTE format(' CREATE SUBSCRIPTION shardman_meta_sub CONNECTION %L PUBLICATION shardman_meta_pub' , master_connstring);
70
90
END;
71
91
$$ LANGUAGE plpgsql;
72
92
93
+ -- If for cmd cmd_id we haven't yet inserted new node, do that; mark it as passive
94
+ -- for now, we still need to setup lr and set its id on the node itself
95
+ -- Return generated or existing node id
96
+ CREATE FUNCTION insert_node (connstring text , cmd_id bigint ) RETURNS int AS $$
97
+ DECLARE
98
+ n_id int ;
99
+ BEGIN
100
+ SELECT node_id FROM @extschema@.cmd_log INTO n_id WHERE id = cmd_id;
101
+ IF n_id IS NULL THEN
102
+ INSERT INTO @extschema@.nodes VALUES (DEFAULT, quote_literal(connstring), false)
103
+ RETURNING id INTO n_id;
104
+ UPDATE @extschema@.cmd_log SET node_id = n_id WHERE id = cmd_id;
105
+ END IF;
106
+ RETURN n_id;
107
+ END
108
+ $$ LANGUAGE plpgsql;
109
+
110
+ -- Create logical pgoutput replication slot, if not exists
111
+ CREATE FUNCTION create_repslot (slot_name text ) RETURNS void AS $$
112
+ DECLARE
113
+ slot_exists bool;
114
+ BEGIN
115
+ EXECUTE format(' SELECT EXISTS (SELECT * FROM pg_replication_slots
116
+ WHERE slot_name=%L)' , slot_name) INTO slot_exists;
117
+ IF NOT slot_exists THEN
118
+ EXECUTE format(' SELECT * FROM pg_create_logical_replication_slot(%L, %L)' ,
119
+ slot_name, ' pgoutput' );
120
+ END IF;
121
+ END
122
+ $$ LANGUAGE plpgsql;
123
+
73
124
-- Remove all our logical replication stuff in case of drop extension.
74
125
-- Dropping extension cleanup is not that easy:
75
126
-- - pg offers event triggers sql_drop, dd_command_end and ddl_command_start
@@ -84,10 +135,17 @@ $$ LANGUAGE plpgsql;
84
135
-- it is our extension is deleting, it calls plpgsql cleanup func
85
136
CREATE OR REPLACE FUNCTION pg_shardman_cleanup () RETURNS void AS $$
86
137
DECLARE
87
- pub RECORD;
138
+ pub record;
139
+ sub record;
88
140
BEGIN
89
141
FOR pub IN SELECT pubname FROM pg_publication WHERE pubname LIKE ' shardman_%' LOOP
90
- EXECUTE ' DROP PUBLICATION ' || quote_ident(pub .pubname );
142
+ EXECUTE format(' DROP PUBLICATION %I' , pub .pubname );
143
+ END LOOP;
144
+ FOR sub IN SELECT subname FROM pg_subscription WHERE subname LIKE ' shardman_%' LOOP
145
+ -- we are managing rep slots manually, so we need to detach it beforehand
146
+ EXECUTE format(' ALTER SUBSCRIPTION %I DISABLE' , sub .subname );
147
+ EXECUTE format(' ALTER SUBSCRIPTION %I SET (slot_name = NONE)' , sub .subname );
148
+ EXECUTE format(' DROP SUBSCRIPTION %I' , sub .subname );
91
149
END LOOP;
92
150
END;
93
151
$$ LANGUAGE plpgsql;
@@ -112,6 +170,7 @@ CREATE FUNCTION set_node_id(node_id int) RETURNS void AS $$
112
170
UPDATE @extschema@.local_meta SET v = node_id WHERE k = ' node_id' ;
113
171
$$ LANGUAGE sql;
114
172
173
+
115
174
-- Interface functions
116
175
117
176
-- TODO: during the initial connection, ensure that nodes id (if any) is not
0 commit comments