5
5
use strict;
6
6
use warnings;
7
7
use PostgreSQL::Test::Cluster;
8
- use Test::More tests => 100 ;
8
+ use Test::More tests => 14 ;
9
9
10
10
my ($node_publisher , $node_subscriber , $publisher_connstr , $result , $offset );
11
11
$offset = 0;
@@ -95,19 +95,6 @@ sub grant_bypassrls
95
95
$node_publisher -> start;
96
96
$node_subscriber -> start;
97
97
$publisher_connstr = $node_publisher -> connstr . ' dbname=postgres' ;
98
- my %range_a = (
99
- publisher => ' FROM (0) TO (15)' ,
100
- subscriber => ' FROM (0) TO (5)' );
101
- my %range_b = (
102
- publisher => ' FROM (15) TO (30)' ,
103
- subscriber => ' FROM (5) TO (30)' );
104
- my %list_a = (
105
- publisher => ' IN (1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29)' ,
106
- subscriber => ' IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)' ,
107
- );
108
- my %list_b = (
109
- publisher => ' IN (2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30)' ,
110
- subscriber => ' IN (17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30)' );
111
98
my %remainder_a = (
112
99
publisher => 0,
113
100
subscriber => 1);
@@ -117,10 +104,6 @@ sub grant_bypassrls
117
104
118
105
for my $node ($node_publisher , $node_subscriber )
119
106
{
120
- my $range_a = $range_a {$node -> name};
121
- my $range_b = $range_b {$node -> name};
122
- my $list_a = $list_a {$node -> name};
123
- my $list_b = $list_b {$node -> name};
124
107
my $remainder_a = $remainder_a {$node -> name};
125
108
my $remainder_b = $remainder_b {$node -> name};
126
109
$node -> safe_psql(' postgres' , qq(
@@ -135,26 +118,6 @@ sub grant_bypassrls
135
118
ALTER TABLE alice.unpartitioned REPLICA IDENTITY FULL;
136
119
GRANT SELECT ON TABLE alice.unpartitioned TO regress_admin;
137
120
138
- CREATE TABLE alice.rangepart (i INTEGER) PARTITION BY RANGE (i);
139
- ALTER TABLE alice.rangepart REPLICA IDENTITY FULL;
140
- GRANT SELECT ON TABLE alice.rangepart TO regress_admin;
141
- CREATE TABLE alice.rangepart_a PARTITION OF alice.rangepart
142
- FOR VALUES $range_a ;
143
- ALTER TABLE alice.rangepart_a REPLICA IDENTITY FULL;
144
- CREATE TABLE alice.rangepart_b PARTITION OF alice.rangepart
145
- FOR VALUES $range_b ;
146
- ALTER TABLE alice.rangepart_b REPLICA IDENTITY FULL;
147
-
148
- CREATE TABLE alice.listpart (i INTEGER) PARTITION BY LIST (i);
149
- ALTER TABLE alice.listpart REPLICA IDENTITY FULL;
150
- GRANT SELECT ON TABLE alice.listpart TO regress_admin;
151
- CREATE TABLE alice.listpart_a PARTITION OF alice.listpart
152
- FOR VALUES $list_a ;
153
- ALTER TABLE alice.listpart_a REPLICA IDENTITY FULL;
154
- CREATE TABLE alice.listpart_b PARTITION OF alice.listpart
155
- FOR VALUES $list_b ;
156
- ALTER TABLE alice.listpart_b REPLICA IDENTITY FULL;
157
-
158
121
CREATE TABLE alice.hashpart (i INTEGER) PARTITION BY HASH (i);
159
122
ALTER TABLE alice.hashpart REPLICA IDENTITY FULL;
160
123
GRANT SELECT ON TABLE alice.hashpart TO regress_admin;
@@ -170,171 +133,133 @@ sub grant_bypassrls
170
133
SET SESSION AUTHORIZATION regress_alice;
171
134
172
135
CREATE PUBLICATION alice
173
- FOR TABLE alice.unpartitioned, alice.rangepart, alice.listpart, alice. hashpart
136
+ FOR TABLE alice.unpartitioned, alice.hashpart
174
137
WITH (publish_via_partition_root = true);
175
138
) );
176
139
$node_subscriber -> safe_psql(' postgres' , qq(
177
140
SET SESSION AUTHORIZATION regress_admin;
178
141
CREATE SUBSCRIPTION admin_sub CONNECTION '$publisher_connstr ' PUBLICATION alice;
179
142
) );
180
143
144
+ $node_publisher -> wait_for_catchup(' admin_sub' );
145
+
146
+ # Wait for initial sync to finish as well
147
+ my $synced_query =
148
+ " SELECT count(1) = 0 FROM pg_subscription_rel WHERE srsubstate NOT IN ('s', 'r');" ;
149
+ $node_subscriber -> poll_query_until(' postgres' , $synced_query )
150
+ or die " Timed out while waiting for subscriber to synchronize data" ;
151
+
181
152
# Verify that "regress_admin" can replicate into the tables
182
153
#
183
- my @tbl = (qw( unpartitioned rangepart listpart hashpart) );
184
- for my $tbl (@tbl )
185
- {
186
- publish_insert(" alice.$tbl " , 1);
187
- publish_insert(" alice.$tbl " , 3);
188
- publish_insert(" alice.$tbl " , 5);
189
- expect_replication(
190
- " alice.$tbl " , 3, 1, 5,
191
- " superuser admin replicates insert into $tbl " );
192
- publish_update(" alice.$tbl " , 1 => 7);
193
- expect_replication(
194
- " alice.$tbl " , 3, 3, 7,
195
- " superuser admin replicates update into $tbl " );
196
- publish_delete(" alice.$tbl " , 3);
197
- expect_replication(
198
- " alice.$tbl " , 2, 5, 7,
199
- " superuser admin replicates delete into $tbl " );
200
- }
201
-
202
- # Repeatedly revoke and restore superuser privilege for "regress_admin", verifying
203
- # that replication fails while superuser privilege is missing, but works again and
204
- # catches up once superuser is restored.
154
+ publish_insert(" alice.unpartitioned" , 1);
155
+ publish_insert(" alice.unpartitioned" , 3);
156
+ publish_insert(" alice.unpartitioned" , 5);
157
+ publish_update(" alice.unpartitioned" , 1 => 7);
158
+ publish_delete(" alice.unpartitioned" , 3);
159
+ expect_replication(
160
+ " alice.unpartitioned" , 2, 5, 7,
161
+ " superuser admin replicates into unpartitioned" );
162
+
163
+ # Revoke and restore superuser privilege for "regress_admin",
164
+ # verifying that replication fails while superuser privilege is
165
+ # missing, but works again and catches up once superuser is restored.
205
166
#
206
- for my $tbl (@tbl )
207
- {
208
- revoke_superuser(" regress_admin" );
209
- publish_insert(" alice.$tbl " , 3);
210
- expect_failure(" alice.$tbl " , 2, 5, 7,
211
- qr / ERROR: permission denied for table $tbl / msi ,
212
- " non-superuser admin fails to replicate insert" );
213
- grant_superuser(" regress_admin" );
214
- expect_replication(" alice.$tbl " , 3, 3, 7,
215
- " admin with restored superuser privilege replicates insert" );
216
-
217
- revoke_superuser(" regress_admin" );
218
- publish_update(" alice.$tbl " , 3 => 9);
219
- expect_failure(" alice.$tbl " , 3, 3, 7,
220
- qr / ERROR: permission denied for table $tbl / msi ,
221
- " non-superuser admin fails to replicate update" );
222
- grant_superuser(" regress_admin" );
223
- expect_replication(" alice.$tbl " , 3, 5, 9,
224
- " admin with restored superuser privilege replicates update" );
225
-
226
- revoke_superuser(" regress_admin" );
227
- publish_delete(" alice.$tbl " , 5);
228
- expect_failure(" alice.$tbl " , 3, 5, 9,
229
- qr / ERROR: permission denied for table $tbl / msi ,
230
- " non-superuser admin fails to replicate delete" );
231
- grant_superuser(" regress_admin" );
232
- expect_replication(" alice.$tbl " , 2, 7, 9,
233
- " admin with restored superuser privilege replicates delete" );
234
- }
235
-
236
- # Grant privileges on the target tables to "regress_admin" so that superuser
237
- # privileges are not necessary for replication.
167
+ revoke_superuser(" regress_admin" );
168
+ publish_update(" alice.unpartitioned" , 5 => 9);
169
+ expect_failure(" alice.unpartitioned" , 2, 5, 7,
170
+ qr / ERROR: permission denied for table unpartitioned/ msi ,
171
+ " non-superuser admin fails to replicate update" );
172
+ grant_superuser(" regress_admin" );
173
+ expect_replication(" alice.unpartitioned" , 2, 7, 9,
174
+ " admin with restored superuser privilege replicates update" );
175
+
176
+ # Grant INSERT, UPDATE, DELETE privileges on the target tables to
177
+ # "regress_admin" so that superuser privileges are not necessary for
178
+ # replication.
179
+ #
180
+ # Note that UPDATE and DELETE also require SELECT privileges, which
181
+ # will be granted in subsequent test.
238
182
#
239
183
$node_subscriber -> safe_psql(' postgres' , qq(
240
184
ALTER ROLE regress_admin NOSUPERUSER;
241
185
SET SESSION AUTHORIZATION regress_alice;
242
- GRANT ALL PRIVILEGES ON
186
+ GRANT INSERT,UPDATE,DELETE ON
243
187
alice.unpartitioned,
244
- alice.rangepart, alice.rangepart_a, alice.rangepart_b,
245
- alice.listpart, alice.listpart_a, alice.listpart_b,
246
188
alice.hashpart, alice.hashpart_a, alice.hashpart_b
247
189
TO regress_admin;
190
+ REVOKE SELECT ON alice.unpartitioned FROM regress_admin;
248
191
) );
249
- for my $tbl (@tbl )
250
- {
251
- publish_insert(" alice.$tbl " , 11);
252
- publish_update(" alice.$tbl " , 7 => 13);
253
- publish_delete(" alice.$tbl " , 9);
254
- expect_replication(" alice.$tbl " , 2, 11, 13,
255
- " nosuperuser admin with all table privileges can replicate into $tbl " );
256
- }
257
192
258
- # Enable RLS on the target tables and check that "regress_admin" can only
259
- # replicate into them when superuser. Note that RLS must be enabled on the
260
- # partitions, not the partitioned tables, since the partitions are the targets
261
- # of the replication.
193
+ publish_insert(" alice.unpartitioned" , 11);
194
+ expect_replication(" alice.unpartitioned" , 3, 7, 11,
195
+ " nosuperuser admin with INSERT privileges can replicate into unpartitioned" );
196
+
197
+ publish_update(" alice.unpartitioned" , 7 => 13);
198
+ expect_failure(" alice.unpartitioned" , 3, 7, 11,
199
+ qr / ERROR: permission denied for table unpartitioned/ msi ,
200
+ " non-superuser admin without SELECT privileges fails to replicate update" );
201
+
202
+ # Now grant SELECT
262
203
#
263
204
$node_subscriber -> safe_psql(' postgres' , qq(
264
205
SET SESSION AUTHORIZATION regress_alice;
265
- ALTER TABLE alice.unpartitioned ENABLE ROW LEVEL SECURITY;
266
- ALTER TABLE alice.rangepart_a ENABLE ROW LEVEL SECURITY;
267
- ALTER TABLE alice.rangepart_b ENABLE ROW LEVEL SECURITY;
268
- ALTER TABLE alice.listpart_a ENABLE ROW LEVEL SECURITY;
269
- ALTER TABLE alice.listpart_b ENABLE ROW LEVEL SECURITY;
270
- ALTER TABLE alice.hashpart_a ENABLE ROW LEVEL SECURITY;
271
- ALTER TABLE alice.hashpart_b ENABLE ROW LEVEL SECURITY;
206
+ GRANT SELECT ON
207
+ alice.unpartitioned,
208
+ alice.hashpart, alice.hashpart_a, alice.hashpart_b
209
+ TO regress_admin;
272
210
) );
273
- for my $tbl (@tbl )
274
- {
275
- revoke_superuser(" regress_admin" );
276
- publish_insert(" alice.$tbl " , 15);
277
- expect_failure(" alice.$tbl " , 2, 11, 13,
278
- qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "$tbl \w *"/ msi ,
279
- " non-superuser admin fails to replicate insert into rls enabled table" );
280
- grant_superuser(" regress_admin" );
281
- expect_replication(" alice.$tbl " , 3, 11, 15,
282
- " admin with restored superuser privilege replicates insert into rls enabled $tbl " );
283
-
284
- revoke_superuser(" regress_admin" );
285
- publish_update(" alice.$tbl " , 11 => 17);
286
- expect_failure(" alice.$tbl " , 3, 11, 15,
287
- qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "$tbl \w *"/ msi ,
288
- " non-superuser admin fails to replicate update into rls enabled $tbl " );
289
-
290
- grant_superuser(" regress_admin" );
291
- expect_replication(" alice.$tbl " , 3, 13, 17,
292
- " admin with restored superuser privilege replicates update into rls enabled $tbl " );
293
-
294
- revoke_superuser(" regress_admin" );
295
- publish_delete(" alice.$tbl " , 13);
296
- expect_failure(" alice.$tbl " , 3, 13, 17,
297
- qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "$tbl \w *"/ msi ,
298
- " non-superuser admin fails to replicate delete into rls enabled $tbl " );
299
- grant_superuser(" regress_admin" );
300
- expect_replication(" alice.$tbl " , 2, 15, 17,
301
- " admin with restored superuser privilege replicates delete into rls enabled $tbl " );
302
- }
303
211
304
- # Revoke superuser from "regress_admin". Check that the admin can now only
305
- # replicate into alice's table when admin has the bypassrls privilege.
212
+ publish_delete(" alice.unpartitioned" , 9);
213
+ expect_replication(" alice.unpartitioned" , 2, 11, 13,
214
+ " nosuperuser admin with all table privileges can replicate into unpartitioned" );
215
+
216
+ # Test partitioning
306
217
#
307
- for my $tbl (@tbl )
308
- {
309
- revoke_superuser(" regress_admin" );
310
- revoke_bypassrls(" regress_admin" );
311
- publish_insert(" alice.$tbl " , 19);
312
- expect_failure(" alice.$tbl " , 2, 15, 17,
313
- qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "$tbl \w *"/ msi ,
314
- " nobypassrls admin fails to replicate insert into rls enabled $tbl " );
315
- grant_bypassrls(" regress_admin" );
316
- expect_replication(" alice.$tbl " , 3, 15, 19,
317
- " admin with bypassrls privilege replicates insert into rls enabled $tbl " );
318
-
319
- revoke_bypassrls(" regress_admin" );
320
- publish_update(" alice.$tbl " , 15 => 21);
321
- expect_failure(" alice.$tbl " , 3, 15, 19,
322
- qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "$tbl \w *"/ msi ,
323
- " nobypassrls admin fails to replicate update into rls enabled $tbl " );
324
-
325
- grant_bypassrls(" regress_admin" );
326
- expect_replication(" alice.$tbl " , 3, 17, 21,
327
- " admin with restored bypassrls privilege replicates update into rls enabled $tbl " );
328
-
329
- revoke_bypassrls(" regress_admin" );
330
- publish_delete(" alice.$tbl " , 17);
331
- expect_failure(" alice.$tbl " , 3, 17, 21,
332
- qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "$tbl \w *"/ msi ,
333
- " nobypassrls admin fails to replicate delete into rls enabled $tbl " );
334
- grant_bypassrls(" regress_admin" );
335
- expect_replication(" alice.$tbl " , 2, 19, 21,
336
- " admin with restored bypassrls privilege replicates delete into rls enabled $tbl " );
337
- }
218
+ publish_insert(" alice.hashpart" , 101);
219
+ publish_insert(" alice.hashpart" , 102);
220
+ publish_insert(" alice.hashpart" , 103);
221
+ publish_update(" alice.hashpart" , 102 => 120);
222
+ publish_delete(" alice.hashpart" , 101);
223
+ expect_replication(" alice.hashpart" , 2, 103, 120,
224
+ " nosuperuser admin with all table privileges can replicate into hashpart" );
225
+
226
+
227
+ # Enable RLS on the target table and check that "regress_admin" can
228
+ # only replicate into it when superuser or bypassrls.
229
+ #
230
+ $node_subscriber -> safe_psql(' postgres' , qq(
231
+ SET SESSION AUTHORIZATION regress_alice;
232
+ ALTER TABLE alice.unpartitioned ENABLE ROW LEVEL SECURITY;
233
+ ) );
234
+
235
+ revoke_superuser(" regress_admin" );
236
+ publish_insert(" alice.unpartitioned" , 15);
237
+ expect_failure(" alice.unpartitioned" , 2, 11, 13,
238
+ qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "unpartitioned\w *"/ msi ,
239
+ " non-superuser admin fails to replicate insert into rls enabled table" );
240
+ grant_superuser(" regress_admin" );
241
+ expect_replication(" alice.unpartitioned" , 3, 11, 15,
242
+ " admin with restored superuser privilege replicates insert into rls enabled unpartitioned" );
243
+
244
+ revoke_superuser(" regress_admin" );
245
+ publish_update(" alice.unpartitioned" , 11 => 17);
246
+ expect_failure(" alice.unpartitioned" , 3, 11, 15,
247
+ qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "unpartitioned\w *"/ msi ,
248
+ " non-superuser admin fails to replicate update into rls enabled unpartitioned" );
249
+
250
+ grant_bypassrls(" regress_admin" );
251
+ expect_replication(" alice.unpartitioned" , 3, 13, 17,
252
+ " admin with bypassrls replicates update into rls enabled unpartitioned" );
253
+
254
+ revoke_bypassrls(" regress_admin" );
255
+ publish_delete(" alice.unpartitioned" , 13);
256
+ expect_failure(" alice.unpartitioned" , 3, 13, 17,
257
+ qr / ERROR: "regress_admin" cannot replicate into relation with row-level security enabled: "unpartitioned\w *"/ msi ,
258
+ " non-superuser admin without bypassrls fails to replicate delete into rls enabled unpartitioned" );
259
+ grant_bypassrls(" regress_admin" );
260
+ expect_replication(" alice.unpartitioned" , 2, 15, 17,
261
+ " admin with bypassrls replicates delete into rls enabled unpartitioned" );
262
+ grant_superuser(" regress_admin" );
338
263
339
264
# Alter the subscription owner to "regress_alice". She has neither superuser
340
265
# nor bypassrls, but as the table owner should be able to replicate.
@@ -346,18 +271,10 @@ sub grant_bypassrls
346
271
ALTER ROLE regress_alice NOSUPERUSER;
347
272
ALTER SUBSCRIPTION admin_sub ENABLE;
348
273
) );
349
- for my $tbl (@tbl )
350
- {
351
- publish_insert(" alice.$tbl " , 23);
352
- expect_replication(
353
- " alice.$tbl " , 3, 19, 23,
354
- " nosuperuser nobypassrls table owner can replicate insert into $tbl despite rls" );
355
- publish_update(" alice.$tbl " , 19 => 25);
356
- expect_replication(
357
- " alice.$tbl " , 3, 21, 25,
358
- " nosuperuser nobypassrls table owner can replicate update into $tbl despite rls" );
359
- publish_delete(" alice.$tbl " , 21);
360
- expect_replication(
361
- " alice.$tbl " , 2, 23, 25,
362
- " nosuperuser nobypassrls table owner can replicate delete into $tbl despite rls" );
363
- }
274
+
275
+ publish_insert(" alice.unpartitioned" , 23);
276
+ publish_update(" alice.unpartitioned" , 15 => 25);
277
+ publish_delete(" alice.unpartitioned" , 17);
278
+ expect_replication(
279
+ " alice.unpartitioned" , 2, 23, 25,
280
+ " nosuperuser nobypassrls table owner can replicate delete into unpartitioned despite rls" );
0 commit comments