Skip to content

Commit 4bd715c

Browse files
committed
Merge branch 'rel_1_2_beta' of https://github.com/postgrespro/pg_pathman into rel_1_2_beta
2 parents 9575107 + 2a31969 commit 4bd715c

10 files changed

+490
-165
lines changed

expected/pathman_calamity.out

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,44 @@ SELECT debug_capture();
1212
set client_min_messages = NOTICE;
1313
/* create table to be partitioned */
1414
CREATE TABLE calamity.part_test(val serial);
15+
/* check function build_hash_condition() */
16+
SELECT build_hash_condition('int4', 'val', 10, 1);
17+
build_hash_condition
18+
-------------------------------------------------
19+
public.get_hash_part_idx(hashint4(val), 10) = 1
20+
(1 row)
21+
22+
SELECT build_hash_condition('text', 'val', 10, 1);
23+
build_hash_condition
24+
-------------------------------------------------
25+
public.get_hash_part_idx(hashtext(val), 10) = 1
26+
(1 row)
27+
28+
SELECT build_hash_condition('int4', 'val', 1, 1);
29+
ERROR: 'partition_index' must be lower than 'partitions_count'
30+
SELECT build_hash_condition('int4', 'val', 10, 20);
31+
ERROR: 'partition_index' must be lower than 'partitions_count'
32+
SELECT build_hash_condition('text', 'val', 10, NULL) IS NULL;
33+
?column?
34+
----------
35+
t
36+
(1 row)
37+
38+
SELECT build_hash_condition('calamity.part_test', 'val', 10, 1);
39+
ERROR: no hash function for type calamity.part_test
40+
/* check function build_range_condition() */
41+
SELECT build_range_condition('val', 10, 20);
42+
build_range_condition
43+
----------------------------
44+
val >= '10' AND val < '20'
45+
(1 row)
46+
47+
SELECT build_range_condition('val', 10, NULL) IS NULL;
48+
?column?
49+
----------
50+
t
51+
(1 row)
52+
1553
/* check function validate_relname() */
1654
SELECT validate_relname('calamity.part_test');
1755
validate_relname

expected/pathman_permissions.out

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ CREATE ROLE user1 LOGIN;
55
CREATE ROLE user2 LOGIN;
66
GRANT USAGE, CREATE ON SCHEMA permissions TO user1;
77
GRANT USAGE, CREATE ON SCHEMA permissions TO user2;
8-
ALTER DEFAULT PRIVILEGES FOR ROLE user1
9-
IN SCHEMA permissions
10-
GRANT SELECT, INSERT ON TABLES
11-
TO user2;
128
/* Switch to #1 */
139
SET ROLE user1;
1410
CREATE TABLE permissions.user1_table(id serial, a int);
1511
INSERT INTO permissions.user1_table SELECT g, g FROM generate_series(1, 20) as g;
16-
/* Should fail */
12+
/* Should fail (can't SELECT) */
13+
SET ROLE user2;
14+
SELECT create_range_partitions('permissions.user1_table', 'id', 1, 10, 2);
15+
ERROR: permission denied for relation user1_table
16+
/* Grant SELECT to user2 */
17+
SET ROLE user1;
18+
GRANT SELECT ON permissions.user1_table TO user2;
19+
/* Should fail (don't own parent) */
1720
SET ROLE user2;
1821
SELECT create_range_partitions('permissions.user1_table', 'id', 1, 10, 2);
19-
NOTICE: sequence "user1_table_seq" does not exist, skipping
2022
WARNING: only the owner or superuser can change partitioning configuration of table "user1_table"
2123
ERROR: new row violates row-level security policy for table "pathman_config"
2224
/* Should be ok */
@@ -58,21 +60,61 @@ WARNING: only the owner or superuser can change partitioning configuration of t
5860
/* No rights to insert, should fail */
5961
SET ROLE user2;
6062
INSERT INTO permissions.user1_table (id, a) VALUES (35, 0);
61-
/* Have rights, should be ok (bgw connects as user1) */
63+
ERROR: permission denied for relation user1_table
64+
/* No rights to create partitions (need INSERT privilege) */
65+
SET ROLE user2;
66+
SELECT prepend_range_partition('permissions.user1_table');
67+
ERROR: permission denied for parent relation "user1_table"
68+
/* Allow user2 to create partitions */
6269
SET ROLE user1;
6370
GRANT INSERT ON permissions.user1_table TO user2;
71+
GRANT UPDATE(a) ON permissions.user1_table TO user2; /* per-column ACL */
72+
/* Should be able to prepend a partition */
73+
SET ROLE user2;
74+
SELECT prepend_range_partition('permissions.user1_table');
75+
prepend_range_partition
76+
---------------------------
77+
permissions.user1_table_4
78+
(1 row)
79+
80+
SELECT attname, attacl from pg_attribute
81+
WHERE attrelid = (SELECT partition FROM pathman_partition_list
82+
WHERE parent = 'permissions.user1_table'::REGCLASS
83+
ORDER BY range_min::int ASC /* prepend */
84+
LIMIT 1)
85+
ORDER BY attname; /* check ACL for each column */
86+
attname | attacl
87+
----------+-----------------
88+
a | {user2=w/user1}
89+
cmax |
90+
cmin |
91+
ctid |
92+
id |
93+
tableoid |
94+
xmax |
95+
xmin |
96+
(8 rows)
97+
98+
/* Have rights, should be ok (parent's ACL is shared by new children) */
6499
SET ROLE user2;
65100
INSERT INTO permissions.user1_table (id, a) VALUES (35, 0) RETURNING *;
66101
id | a
67102
----+---
68103
35 | 0
69104
(1 row)
70105

71-
SELECT relacl FROM pg_class WHERE oid = 'permissions.user1_table_4'::regclass;
72-
relacl
73-
--------------------------------------
74-
{user1=arwdDxt/user1,user2=ar/user1}
75-
(1 row)
106+
SELECT relname, relacl FROM pg_class
107+
WHERE oid = ANY (SELECT partition FROM pathman_partition_list
108+
WHERE parent = 'permissions.user1_table'::REGCLASS
109+
ORDER BY range_max::int DESC /* append */
110+
LIMIT 3)
111+
ORDER BY relname; /* we also check ACL for "user1_table_2" */
112+
relname | relacl
113+
---------------+--------------------------------------
114+
user1_table_2 | {user1=arwdDxt/user1,user2=r/user1}
115+
user1_table_5 | {user1=arwdDxt/user1,user2=ar/user1}
116+
user1_table_6 | {user1=arwdDxt/user1,user2=ar/user1}
117+
(3 rows)
76118

77119
/* Try to drop partition, should fail */
78120
SELECT drop_range_partition('permissions.user1_table_4');
@@ -95,11 +137,12 @@ SELECT drop_partitions('permissions.user1_table');
95137
NOTICE: function permissions.user1_table_upd_trig_func() does not exist, skipping
96138
NOTICE: 10 rows copied from permissions.user1_table_1
97139
NOTICE: 10 rows copied from permissions.user1_table_2
98-
NOTICE: 0 rows copied from permissions.user1_table_3
99-
NOTICE: 2 rows copied from permissions.user1_table_4
140+
NOTICE: 0 rows copied from permissions.user1_table_4
141+
NOTICE: 0 rows copied from permissions.user1_table_5
142+
NOTICE: 1 rows copied from permissions.user1_table_6
100143
drop_partitions
101144
-----------------
102-
4
145+
5
103146
(1 row)
104147

105148
/* Switch to #2 */

hash.sql

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,6 @@ CREATE OR REPLACE FUNCTION @extschema@.create_hash_partitions(
1818
partition_data BOOLEAN DEFAULT TRUE)
1919
RETURNS INTEGER AS
2020
$$
21-
DECLARE
22-
v_child_relname TEXT;
23-
v_plain_schema TEXT;
24-
v_plain_relname TEXT;
25-
-- v_atttype REGTYPE;
26-
-- v_hashfunc REGPROC;
27-
v_init_callback REGPROCEDURE;
28-
2921
BEGIN
3022
PERFORM @extschema@.validate_relname(parent_relid);
3123

@@ -40,13 +32,6 @@ BEGIN
4032
attribute := lower(attribute);
4133
PERFORM @extschema@.common_relation_checks(parent_relid, attribute);
4234

43-
/* Fetch atttype and its hash function */
44-
-- v_atttype := @extschema@.get_attribute_type(parent_relid, attribute);
45-
-- v_hashfunc := @extschema@.get_type_hash_func(v_atttype);
46-
47-
SELECT * INTO v_plain_schema, v_plain_relname
48-
FROM @extschema@.get_plain_schema_and_relname(parent_relid);
49-
5035
/* Insert new entry to pathman config */
5136
INSERT INTO @extschema@.pathman_config (partrel, attname, parttype)
5237
VALUES (parent_relid, attribute, 1);
@@ -74,29 +59,42 @@ SET client_min_messages = WARNING;
7459

7560
/*
7661
* Replace hash partition with another one. It could be useful in case when
77-
* someone wants to attach foreign table as a partition
62+
* someone wants to attach foreign table as a partition.
63+
*
64+
* lock_parent - should we take an exclusive lock?
7865
*/
7966
CREATE OR REPLACE FUNCTION @extschema@.replace_hash_partition(
8067
old_partition REGCLASS,
81-
new_partition REGCLASS)
68+
new_partition REGCLASS,
69+
lock_parent BOOL DEFAULT TRUE)
8270
RETURNS REGCLASS AS
8371
$$
8472
DECLARE
85-
v_attname TEXT;
73+
parent_relid REGCLASS;
74+
part_attname TEXT; /* partitioned column */
75+
old_constr_name TEXT; /* name of old_partition's constraint */
76+
old_constr_def TEXT; /* definition of old_partition's constraint */
8677
rel_persistence CHAR;
87-
v_init_callback REGPROCEDURE;
88-
v_parent_relid REGCLASS;
89-
v_part_count INT;
90-
v_part_num INT;
78+
p_init_callback REGPROCEDURE;
79+
9180
BEGIN
9281
PERFORM @extschema@.validate_relname(old_partition);
9382
PERFORM @extschema@.validate_relname(new_partition);
9483

9584
/* Parent relation */
96-
v_parent_relid := @extschema@.get_parent_of_partition(old_partition);
85+
parent_relid := @extschema@.get_parent_of_partition(old_partition);
86+
87+
IF lock_parent THEN
88+
/* Acquire data modification lock (prevent further modifications) */
89+
PERFORM @extschema@.prevent_relation_modification(parent_relid);
90+
ELSE
91+
/* Acquire lock on parent */
92+
PERFORM @extschema@.lock_partitioned_relation(parent_relid);
93+
END IF;
9794

98-
/* Acquire lock on parent */
99-
PERFORM @extschema@.lock_partitioned_relation(v_parent_relid);
95+
/* Acquire data modification lock (prevent further modifications) */
96+
PERFORM @extschema@.prevent_relation_modification(old_partition);
97+
PERFORM @extschema@.prevent_relation_modification(new_partition);
10098

10199
/* Ignore temporary tables */
102100
SELECT relpersistence FROM pg_catalog.pg_class
@@ -108,52 +106,54 @@ BEGIN
108106
END IF;
109107

110108
/* Check that new partition has an equal structure as parent does */
111-
IF NOT @extschema@.validate_relations_equality(v_parent_relid, new_partition) THEN
109+
IF NOT @extschema@.validate_relations_equality(parent_relid, new_partition) THEN
112110
RAISE EXCEPTION 'partition must have the exact same structure as parent';
113111
END IF;
114112

115113
/* Get partitioning key */
116-
v_attname := attname FROM @extschema@.pathman_config WHERE partrel = v_parent_relid;
117-
IF v_attname IS NULL THEN
118-
RAISE EXCEPTION 'table "%" is not partitioned', v_parent_relid::TEXT;
114+
part_attname := attname FROM @extschema@.pathman_config WHERE partrel = parent_relid;
115+
IF part_attname IS NULL THEN
116+
RAISE EXCEPTION 'table "%" is not partitioned', parent_relid::TEXT;
119117
END IF;
120118

121-
/* Calculate partitions count and old partition's number */
122-
v_part_count := count(*) FROM @extschema@.pathman_partition_list WHERE parent = v_parent_relid;
123-
v_part_num := @extschema@.get_partition_hash(v_parent_relid, old_partition);
119+
/* Fetch name of old_partition's HASH constraint */
120+
old_constr_name = @extschema@.build_check_constraint_name(old_partition::REGCLASS,
121+
part_attname);
122+
123+
/* Fetch definition of old_partition's HASH constraint */
124+
SELECT pg_catalog.pg_get_constraintdef(oid) FROM pg_catalog.pg_constraint
125+
WHERE conrelid = old_partition AND conname = old_constr_name
126+
INTO old_constr_def;
124127

125128
/* Detach old partition */
126-
EXECUTE format('ALTER TABLE %s NO INHERIT %s', old_partition, v_parent_relid);
127-
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s',
128-
old_partition,
129-
@extschema@.build_check_constraint_name(old_partition::REGCLASS,
130-
v_attname));
131-
132-
/* Attach new one */
133-
EXECUTE format('ALTER TABLE %s INHERIT %s', new_partition, v_parent_relid);
134-
EXECUTE format('ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s)',
129+
EXECUTE format('ALTER TABLE %s NO INHERIT %s', old_partition, parent_relid);
130+
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT %s',
131+
old_partition,
132+
old_constr_name);
133+
134+
/* Attach the new one */
135+
EXECUTE format('ALTER TABLE %s INHERIT %s', new_partition, parent_relid);
136+
EXECUTE format('ALTER TABLE %s ADD CONSTRAINT %s %s',
135137
new_partition,
136-
@extschema@.build_check_constraint_name(new_partition::regclass,
137-
v_attname),
138-
@extschema@.build_hash_condition(new_partition::regclass,
139-
v_attname,
140-
v_part_count,
141-
v_part_num));
138+
@extschema@.build_check_constraint_name(new_partition::REGCLASS,
139+
part_attname),
140+
old_constr_def);
142141

143142
/* Fetch init_callback from 'params' table */
144143
WITH stub_callback(stub) as (values (0))
145144
SELECT coalesce(init_callback, 0::REGPROCEDURE)
146145
FROM stub_callback
147146
LEFT JOIN @extschema@.pathman_config_params AS params
148-
ON params.partrel = v_parent_relid
149-
INTO v_init_callback;
147+
ON params.partrel = parent_relid
148+
INTO p_init_callback;
150149

151-
PERFORM @extschema@.invoke_on_partition_created_callback(v_parent_relid,
150+
/* Finally invoke init_callback */
151+
PERFORM @extschema@.invoke_on_partition_created_callback(parent_relid,
152152
new_partition,
153-
v_init_callback);
153+
p_init_callback);
154154

155155
/* Invalidate cache */
156-
PERFORM @extschema@.on_update_partitions(v_parent_relid);
156+
PERFORM @extschema@.on_update_partitions(parent_relid);
157157

158158
RETURN new_partition;
159159
END
@@ -292,3 +292,14 @@ LANGUAGE C STRICT;
292292
CREATE OR REPLACE FUNCTION @extschema@.get_hash_part_idx(INTEGER, INTEGER)
293293
RETURNS INTEGER AS 'pg_pathman', 'get_hash_part_idx'
294294
LANGUAGE C STRICT;
295+
296+
/*
297+
* Build hash condition for a CHECK CONSTRAINT
298+
*/
299+
CREATE OR REPLACE FUNCTION @extschema@.build_hash_condition(
300+
attribute_type REGTYPE,
301+
attribute TEXT,
302+
partitions_count INT4,
303+
partitions_index INT4)
304+
RETURNS TEXT AS 'pg_pathman', 'build_hash_condition'
305+
LANGUAGE C STRICT;

init.sql

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -783,23 +783,3 @@ CREATE OR REPLACE FUNCTION @extschema@.invoke_on_partition_created_callback(
783783
init_callback REGPROCEDURE)
784784
RETURNS VOID AS 'pg_pathman', 'invoke_on_partition_created_callback'
785785
LANGUAGE C;
786-
787-
/*
788-
* Build hash condition for a CHECK CONSTRAINT
789-
*/
790-
CREATE OR REPLACE FUNCTION @extschema@.build_hash_condition(
791-
parent_relid REGCLASS,
792-
attname TEXT,
793-
partitions_count INT,
794-
partition_number INT)
795-
RETURNS TEXT AS 'pg_pathman', 'build_hash_condition'
796-
LANGUAGE C;
797-
798-
/*
799-
* Returns hash value for specified partition (0..N)
800-
*/
801-
CREATE OR REPLACE FUNCTION @extschema@.get_partition_hash(
802-
parent_relid REGCLASS,
803-
partition REGCLASS)
804-
RETURNS INT AS 'pg_pathman', 'get_partition_hash'
805-
LANGUAGE C;

0 commit comments

Comments
 (0)