Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 79acf9e

Browse files
committed
create_hash_partitions
1 parent 05a0f09 commit 79acf9e

File tree

6 files changed

+306
-18
lines changed

6 files changed

+306
-18
lines changed

bin/shardman_init.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ done
3535

3636
restart_nodes
3737

38+
psql -p 5433 -c "drop table if exists partitioned_table cascade;"
3839
psql -p 5433 -c "CREATE TABLE partitioned_table(id INT NOT NULL, payload REAL);"
3940
psql -p 5433 -c "INSERT INTO partitioned_table SELECT generate_series(1, 1000), random();"
4041
psql -c "select shardman.add_node('port=5433');"
42+
psql -c "select shardman.add_node('port=5434');"
4143

4244
psql

bin/shardman_start.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@ done
1515
# to restart master bgw
1616
restart_nodes
1717

18+
psql -p 5433 -c "drop table if exists partitioned_table cascade;"
19+
psql -p 5433 -c "CREATE TABLE partitioned_table(id INT NOT NULL, payload REAL);"
20+
psql -p 5433 -c "INSERT INTO partitioned_table SELECT generate_series(1, 1000), random();"
21+
psql -c "select shardman.add_node('port=5433');"
22+
psql -c "select shardman.add_node('port=5434');"
23+
1824
psql

pg_shardman--0.0.1.sql

Lines changed: 126 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,20 @@ CREATE TABLE tables (
5757
initial_node int NOT NULL REFERENCES nodes(id)
5858
);
5959

60-
-- On adding new table, create it on non-owner nodes using provided sql and
61-
-- partition
60+
-- On adding new table, create this table on non-owner nodes using provided sql
61+
-- and partition it.
6262
CREATE FUNCTION new_table_worker_side() RETURNS TRIGGER AS $$
6363
BEGIN
6464
IF NEW.initial_node != (SELECT shardman.get_node_id()) THEN
65+
EXECUTE format ('DROP TABLE IF EXISTS %I CASCADE;', NEW.relation);
6566
EXECUTE format('%s', NEW.create_sql);
66-
EXECUTE format('select create_hash_partitions(%L, %L, %L);',
67-
NEW.relation, NEW.expr, NEW.partitions_count);
67+
-- We are adding '_tmp' to default names, because
68+
-- these partitions will be immediately replaced with foreign tables
69+
-- having conventional names.
70+
EXECUTE format('select create_hash_partitions(%L, %L, %L, true, %L);',
71+
NEW.relation, NEW.expr, NEW.partitions_count,
72+
(SELECT ARRAY(SELECT part_name FROM shardman.gen_part_names(
73+
NEW.relation, NEW.partitions_count, '_tmp'))));
6874
END IF;
6975
RETURN NULL;
7076
END
@@ -77,13 +83,10 @@ ALTER TABLE shardman.tables ENABLE REPLICA TRIGGER new_table_worker_side;
7783
CREATE FUNCTION new_table_master_side() RETURNS TRIGGER AS $$
7884
BEGIN
7985
INSERT INTO shardman.partitions
80-
-- part names look like tablename_partnum, partnums start from 0
81-
SELECT NEW.relation || '_' || range.num AS part_name,
82-
NEW.relation AS relation,
83-
NEW.initial_node AS owner
84-
FROM
85-
(SELECT num FROM generate_series(0, NEW.partitions_count, 1)
86-
AS range(num)) AS range;
86+
SELECT part_name, NEW.relation AS relation, NEW.initial_node AS owner
87+
FROM (SELECT part_name FROM shardman.gen_part_names(
88+
NEW.relation, NEW.partitions_count))
89+
AS partnames;
8790
RETURN NULL;
8891
END
8992
$$ LANGUAGE plpgsql;
@@ -96,6 +99,99 @@ CREATE TABLE partitions (
9699
owner int REFERENCES nodes(id) -- node on which partition lies
97100
);
98101

102+
-- On adding new partition, create proper foreign server & foreign table and
103+
-- replace tmp (empty) partition with it.
104+
CREATE FUNCTION new_partition() RETURNS TRIGGER AS $$
105+
DECLARE
106+
connstring text;
107+
connstring_keywords text[];
108+
connstring_vals text[];
109+
server_opts text default '';
110+
um_opts text default '';
111+
server_opts_first_time_through bool DEFAULT true;
112+
um_opts_first_time_through bool DEFAULT true;
113+
BEGIN
114+
IF NEW.owner != (SELECT shardman.get_node_id()) THEN
115+
raise info 'creating foreign table';
116+
SELECT nodes.connstring FROM shardman.nodes WHERE id = NEW.owner
117+
INTO connstring;
118+
EXECUTE format('DROP SERVER IF EXISTS %I CASCADE;', NEW.part_name);
119+
-- Options to postgres_fdw are specified in two places: user & password
120+
-- in user mapping and everything else in create server. The problem is
121+
-- that we use single connstring, however user mapping and server
122+
-- doesn't understand this format, i.e. we can't say create server
123+
-- ... options (dbname 'port=4848 host=blabla.org'). So we have to parse
124+
-- the opts and pass them manually. libpq knows how to do it, but
125+
-- doesn't expose that. On the other hand, quote_literal (which is
126+
-- neccessary here) doesn't have handy C API. I resorted to have C
127+
-- function which parses the opts and returns them in two parallel
128+
-- arrays, and here we join them with quoting.
129+
SELECT * FROM shardman.pq_conninfo_parse(connstring)
130+
INTO connstring_keywords, connstring_vals;
131+
FOR i IN 1..(SELECT array_upper(connstring_keywords, 1)) LOOP
132+
IF connstring_keywords[i] = 'client_encoding' OR
133+
connstring_keywords[i] = 'fallback_application_name' THEN
134+
CONTINUE; /* not allowed in postgres_fdw */
135+
ELSIF connstring_keywords[i] = 'user' OR
136+
connstring_keywords[i] = 'password' THEN -- user mapping option
137+
IF NOT um_opts_first_time_through THEN
138+
um_opts := um_opts || ', ';
139+
END IF;
140+
um_opts_first_time_through := false;
141+
um_opts := um_opts ||
142+
format('%s %L', connstring_keywords[i], connstring_vals[i]);
143+
ELSE -- server option
144+
IF NOT server_opts_first_time_through THEN
145+
server_opts := server_opts || ', ';
146+
END IF;
147+
server_opts_first_time_through := false;
148+
server_opts := server_opts ||
149+
format('%s %L', connstring_keywords[i], connstring_vals[i]);
150+
END IF;
151+
END LOOP;
152+
-- OPTIONS () is syntax error, so add OPTIONS only if we really have opts
153+
IF server_opts != '' THEN
154+
server_opts := format(' OPTIONS (%s)', server_opts);
155+
END IF;
156+
IF um_opts != '' THEN
157+
um_opts := format(' OPTIONS (%s)', um_opts);
158+
END IF;
159+
raise log 'serv opts are %, um opts are %', server_opts, um_opts;
160+
EXECUTE format('CREATE SERVER %I FOREIGN DATA WRAPPER
161+
postgres_fdw %s;', NEW.part_name, server_opts);
162+
EXECUTE format('DROP USER MAPPING IF EXISTS FOR CURRENT_USER SERVER %I;',
163+
NEW.part_name);
164+
EXECUTE format('CREATE USER MAPPING FOR CURRENT_USER SERVER %I
165+
%s;', NEW.part_name, um_opts);
166+
EXECUTE format('DROP FOREIGN TABLE IF EXISTS %I;', NEW.part_name);
167+
168+
-- Generate and execute CREATE FOREIGN TABLE sql statement which will
169+
-- clone the existing local table schema. In constrast to
170+
-- gen_create_table_sql, here we need only the header of the table,
171+
-- i.e. its attributes. CHECK constraint for partition will be added
172+
-- during the attachment, and other stuff doesn't seem to have much
173+
-- sense on foreign table.
174+
-- In fact, we should have CREATE FOREIGN TABLE (LIKE ...) to make this
175+
-- sane. We could also used here IMPORT FOREIGN SCHEMA, but it
176+
-- unneccessary involves network (we already have this schema locally)
177+
-- and dangerous: what if table was created and dropped before this
178+
-- change reached us?
179+
EXECUTE format('CREATE FOREIGN TABLE %I %s SERVER %I',
180+
NEW.part_name,
181+
(select
182+
shardman.reconstruct_table_attrs('partitioned_table_0_tmp')),
183+
NEW.part_name);
184+
-- Finally, replace empty local tmp partition with foreign table
185+
EXECUTE format('SELECT replace_hash_partition(%L, %L)',
186+
format('%s_tmp', NEW.part_name), NEW.part_name);
187+
END IF;
188+
RETURN NULL;
189+
END
190+
$$ LANGUAGE plpgsql;
191+
CREATE TRIGGER new_partition AFTER INSERT ON shardman.partitions
192+
FOR EACH ROW EXECUTE PROCEDURE new_partition();
193+
-- fire trigger only on worker nodes
194+
ALTER TABLE shardman.partitions ENABLE REPLICA TRIGGER new_partition;
99195

100196
-- Currently it is used just to store node id, in general we can keep any local
101197
-- node metadata here. If is ever used extensively, probably hstore suits better.
@@ -132,7 +228,6 @@ CREATE TRIGGER cmd_log_inserts
132228
FOR EACH STATEMENT
133229
EXECUTE PROCEDURE notify_shardmaster();
134230

135-
136231
-- probably better to keep opts in an array field, but working with arrays from
137232
-- libpq is not very handy
138233
-- opts must be inserted sequentially, we order by them by id
@@ -182,7 +277,7 @@ CREATE FUNCTION create_meta_pub() RETURNS void AS $$
182277
BEGIN
183278
IF NOT EXISTS (SELECT * FROM pg_publication WHERE pubname = 'shardman_meta_pub') THEN
184279
CREATE PUBLICATION shardman_meta_pub FOR TABLE
185-
shardman.nodes, shardman.tables;
280+
shardman.nodes, shardman.tables, shardman.partitions;
186281
END IF;
187282
END;
188283
$$ LANGUAGE plpgsql;
@@ -302,9 +397,27 @@ BEGIN
302397
END
303398
$$ LANGUAGE plpgsql;
304399

400+
-- generate one-column table with partition names as 'tablename'_'partnum''suffix'
401+
CREATE FUNCTION gen_part_names(relation text, partitions_count int,
402+
suffix text DEFAULT '')
403+
RETURNS TABLE(part_name text) AS $$
404+
BEGIN
405+
RETURN QUERY SELECT relation || '_' || range.num || suffix AS partname
406+
FROM
407+
(SELECT num FROM generate_series(0, partitions_count - 1, 1)
408+
AS range(num)) AS range;
409+
END
410+
$$ LANGUAGE plpgsql;
411+
305412
CREATE FUNCTION gen_create_table_sql(relation text, connstring text) RETURNS text
306413
AS 'pg_shardman' LANGUAGE C;
307414

415+
CREATE FUNCTION reconstruct_table_attrs(relation regclass)
416+
RETURNS text AS 'pg_shardman' LANGUAGE C STRICT;
417+
418+
CREATE FUNCTION pq_conninfo_parse(IN conninfo text, OUT keys text[], OUT vals text[])
419+
RETURNS record AS 'pg_shardman' LANGUAGE C STRICT;
420+
308421
-- Interface functions
309422

310423
-- Add a node. Its state will be reset, all shardman data lost.

readme.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ create extension pg_shardman;
1616

1717
The master itself can't be worker node for now, because it requires special
1818
handling of LR channels setup.
19+
20+
ALTER TABLE for sharded tables is not supported for now.

src/pg_shardman.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -732,9 +732,11 @@ static void create_hash_partitions(Cmd *cmd)
732732

733733
/* Note that we have to run statements in separate transactions, otherwise
734734
* we have a deadlock between pathman and pg_dump */
735-
sql = psprintf("begin; select create_hash_partitions('%s', '%s', %d); end;"
736-
"select shardman.gen_create_table_sql('%s', '%s');",
737-
relation, expr, partitions_count, relation, connstring);
735+
sql = psprintf(
736+
"begin; select create_hash_partitions('%s', '%s', %d); end;"
737+
"select shardman.gen_create_table_sql('%s', '%s');",
738+
relation, expr, partitions_count,
739+
relation, connstring);
738740

739741
/* Try to execute command indefinitely until it succeeded or canceled */
740742
while (!got_sigusr1 && !got_sigterm)

0 commit comments

Comments
 (0)