@oliphaunt/extension-pgtap-linux-arm64-gnu 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/THIRD_PARTY_NOTICES.md +20 -0
  4. package/extension-contract.json +31 -0
  5. package/package.json +49 -0
  6. package/runtime/share/licenses/pgtap/README.md +153 -0
  7. package/runtime/share/postgresql/extension/pgtap--0.90.0--0.91.0.sql +29 -0
  8. package/runtime/share/postgresql/extension/pgtap--0.91.0--0.92.0.sql +1650 -0
  9. package/runtime/share/postgresql/extension/pgtap--0.92.0--0.93.0.sql +679 -0
  10. package/runtime/share/postgresql/extension/pgtap--0.93.0--0.94.0.sql +193 -0
  11. package/runtime/share/postgresql/extension/pgtap--0.94.0--0.95.0.sql +639 -0
  12. package/runtime/share/postgresql/extension/pgtap--0.95.0--0.96.0.sql +415 -0
  13. package/runtime/share/postgresql/extension/pgtap--0.96.0--0.97.0.sql +375 -0
  14. package/runtime/share/postgresql/extension/pgtap--0.97.0--0.98.0.sql +441 -0
  15. package/runtime/share/postgresql/extension/pgtap--0.98.0--0.99.0.sql +209 -0
  16. package/runtime/share/postgresql/extension/pgtap--0.99.0--1.0.0.sql +737 -0
  17. package/runtime/share/postgresql/extension/pgtap--1.0.0--1.1.0.sql +186 -0
  18. package/runtime/share/postgresql/extension/pgtap--1.1.0--1.2.0.sql +889 -0
  19. package/runtime/share/postgresql/extension/pgtap--1.2.0--1.3.0.sql +495 -0
  20. package/runtime/share/postgresql/extension/pgtap--1.3.0--1.3.1.sql +229 -0
  21. package/runtime/share/postgresql/extension/pgtap--1.3.1--1.3.2.sql +34 -0
  22. package/runtime/share/postgresql/extension/pgtap--1.3.2--1.3.3.sql +42 -0
  23. package/runtime/share/postgresql/extension/pgtap--1.3.3--1.3.4.sql +89 -0
  24. package/runtime/share/postgresql/extension/pgtap--1.3.4--1.3.5.sql +1 -0
  25. package/runtime/share/postgresql/extension/pgtap--1.3.5.sql +11520 -0
  26. package/runtime/share/postgresql/extension/pgtap--unpackaged--0.91.0.sql +714 -0
  27. package/runtime/share/postgresql/extension/pgtap-core--1.3.5.sql +4390 -0
  28. package/runtime/share/postgresql/extension/pgtap-core.sql +4390 -0
  29. package/runtime/share/postgresql/extension/pgtap-schema--1.3.5.sql +6764 -0
  30. package/runtime/share/postgresql/extension/pgtap-schema.sql +6764 -0
  31. package/runtime/share/postgresql/extension/pgtap.control +6 -0
  32. package/runtime/share/postgresql/extension/pgtap.sql +11520 -0
  33. package/runtime/share/postgresql/extension/uninstall_pgtap.sql +1088 -0
  34. package/share/licenses/pgtap/README.md +153 -0
@@ -0,0 +1,1650 @@
1
+ CREATE OR REPLACE FUNCTION pgtap_version()
2
+ RETURNS NUMERIC AS 'SELECT 0.92;'
3
+ LANGUAGE SQL IMMUTABLE;
4
+
5
+ CREATE OR REPLACE FUNCTION _cexists ( NAME, NAME )
6
+ RETURNS BOOLEAN AS $$
7
+ SELECT EXISTS(
8
+ SELECT true
9
+ FROM pg_catalog.pg_class c
10
+ JOIN pg_catalog.pg_attribute a ON c.oid = a.attrelid
11
+ WHERE c.relname = $1
12
+ AND pg_catalog.pg_table_is_visible(c.oid)
13
+ AND a.attnum > 0
14
+ AND NOT a.attisdropped
15
+ AND a.attname = $2
16
+ );
17
+ $$ LANGUAGE SQL;
18
+
19
+ -- has_foreign_table( schema, table, description )
20
+ CREATE OR REPLACE FUNCTION has_foreign_table ( NAME, NAME, TEXT )
21
+ RETURNS TEXT AS $$
22
+ SELECT ok( _rexists( 'f', $1, $2 ), $3 );
23
+ $$ LANGUAGE SQL;
24
+
25
+ -- has_foreign_table( table, description )
26
+ CREATE OR REPLACE FUNCTION has_foreign_table ( NAME, TEXT )
27
+ RETURNS TEXT AS $$
28
+ SELECT ok( _rexists( 'f', $1 ), $2 );
29
+ $$ LANGUAGE SQL;
30
+
31
+ -- has_foreign_table( table )
32
+ CREATE OR REPLACE FUNCTION has_foreign_table ( NAME )
33
+ RETURNS TEXT AS $$
34
+ SELECT has_foreign_table( $1, 'Foreign table ' || quote_ident($1) || ' should exist' );
35
+ $$ LANGUAGE SQL;
36
+
37
+ -- hasnt_foreign_table( schema, table, description )
38
+ CREATE OR REPLACE FUNCTION hasnt_foreign_table ( NAME, NAME, TEXT )
39
+ RETURNS TEXT AS $$
40
+ SELECT ok( NOT _rexists( 'f', $1, $2 ), $3 );
41
+ $$ LANGUAGE SQL;
42
+
43
+ -- hasnt_foreign_table( table, description )
44
+ CREATE OR REPLACE FUNCTION hasnt_foreign_table ( NAME, TEXT )
45
+ RETURNS TEXT AS $$
46
+ SELECT ok( NOT _rexists( 'f', $1 ), $2 );
47
+ $$ LANGUAGE SQL;
48
+
49
+ -- hasnt_foreign_table( table )
50
+ CREATE OR REPLACE FUNCTION hasnt_foreign_table ( NAME )
51
+ RETURNS TEXT AS $$
52
+ SELECT hasnt_foreign_table( $1, 'Foreign table ' || quote_ident($1) || ' should not exist' );
53
+ $$ LANGUAGE SQL;
54
+
55
+
56
+ -- has_composite( schema, type, description )
57
+ CREATE OR REPLACE FUNCTION has_composite ( NAME, NAME, TEXT )
58
+ RETURNS TEXT AS $$
59
+ SELECT ok( _rexists( 'c', $1, $2 ), $3 );
60
+ $$ LANGUAGE SQL;
61
+
62
+ -- has_composite( type, description )
63
+ CREATE OR REPLACE FUNCTION has_composite ( NAME, TEXT )
64
+ RETURNS TEXT AS $$
65
+ SELECT ok( _rexists( 'c', $1 ), $2 );
66
+ $$ LANGUAGE SQL;
67
+
68
+ -- has_composite( type )
69
+ CREATE OR REPLACE FUNCTION has_composite ( NAME )
70
+ RETURNS TEXT AS $$
71
+ SELECT has_composite( $1, 'Composite type ' || quote_ident($1) || ' should exist' );
72
+ $$ LANGUAGE SQL;
73
+
74
+ -- hasnt_composite( schema, type, description )
75
+ CREATE OR REPLACE FUNCTION hasnt_composite ( NAME, NAME, TEXT )
76
+ RETURNS TEXT AS $$
77
+ SELECT ok( NOT _rexists( 'c', $1, $2 ), $3 );
78
+ $$ LANGUAGE SQL;
79
+
80
+ -- hasnt_composite( type, description )
81
+ CREATE OR REPLACE FUNCTION hasnt_composite ( NAME, TEXT )
82
+ RETURNS TEXT AS $$
83
+ SELECT ok( NOT _rexists( 'c', $1 ), $2 );
84
+ $$ LANGUAGE SQL;
85
+
86
+ -- hasnt_composite( type )
87
+ CREATE OR REPLACE FUNCTION hasnt_composite ( NAME )
88
+ RETURNS TEXT AS $$
89
+ SELECT hasnt_composite( $1, 'Composite type ' || quote_ident($1) || ' should not exist' );
90
+ $$ LANGUAGE SQL;
91
+
92
+ CREATE OR REPLACE FUNCTION _get_rel_owner ( NAME, NAME )
93
+ RETURNS NAME AS $$
94
+ SELECT pg_catalog.pg_get_userbyid(c.relowner)
95
+ FROM pg_catalog.pg_class c
96
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
97
+ WHERE n.nspname = $1
98
+ AND c.relname = $2
99
+ $$ LANGUAGE SQL;
100
+
101
+ CREATE OR REPLACE FUNCTION _get_rel_owner ( NAME )
102
+ RETURNS NAME AS $$
103
+ SELECT pg_catalog.pg_get_userbyid(c.relowner)
104
+ FROM pg_catalog.pg_class c
105
+ WHERE c.relname = $1
106
+ AND pg_catalog.pg_table_is_visible(c.oid)
107
+ $$ LANGUAGE SQL;
108
+
109
+ -- relation_owner_is ( schema, relation, user, description )
110
+ CREATE OR REPLACE FUNCTION relation_owner_is ( NAME, NAME, NAME, TEXT )
111
+ RETURNS TEXT AS $$
112
+ DECLARE
113
+ owner NAME := _get_rel_owner($1, $2);
114
+ BEGIN
115
+ -- Make sure the relation exists.
116
+ IF owner IS NULL THEN
117
+ RETURN ok(FALSE, $4) || E'\n' || diag(
118
+ E' Relation ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
119
+ );
120
+ END IF;
121
+
122
+ RETURN is(owner, $3, $4);
123
+ END;
124
+ $$ LANGUAGE plpgsql;
125
+
126
+ -- relation_owner_is ( schema, relation, user )
127
+ CREATE OR REPLACE FUNCTION relation_owner_is ( NAME, NAME, NAME )
128
+ RETURNS TEXT AS $$
129
+ SELECT relation_owner_is(
130
+ $1, $2, $3,
131
+ 'Relation ' || quote_ident($1) || '.' || quote_ident($2) || ' should be owned by ' || quote_ident($3)
132
+ );
133
+ $$ LANGUAGE sql;
134
+
135
+ -- relation_owner_is ( relation, user, description )
136
+ CREATE OR REPLACE FUNCTION relation_owner_is ( NAME, NAME, TEXT )
137
+ RETURNS TEXT AS $$
138
+ DECLARE
139
+ owner NAME := _get_rel_owner($1);
140
+ BEGIN
141
+ -- Make sure the relation exists.
142
+ IF owner IS NULL THEN
143
+ RETURN ok(FALSE, $3) || E'\n' || diag(
144
+ E' Relation ' || quote_ident($1) || ' does not exist'
145
+ );
146
+ END IF;
147
+
148
+ RETURN is(owner, $2, $3);
149
+ END;
150
+ $$ LANGUAGE plpgsql;
151
+
152
+ -- relation_owner_is ( relation, user )
153
+ CREATE OR REPLACE FUNCTION relation_owner_is ( NAME, NAME )
154
+ RETURNS TEXT AS $$
155
+ SELECT relation_owner_is(
156
+ $1, $2,
157
+ 'Relation ' || quote_ident($1) || ' should be owned by ' || quote_ident($2)
158
+ );
159
+ $$ LANGUAGE sql;
160
+
161
+ CREATE OR REPLACE FUNCTION _get_rel_owner ( CHAR, NAME, NAME )
162
+ RETURNS NAME AS $$
163
+ SELECT pg_catalog.pg_get_userbyid(c.relowner)
164
+ FROM pg_catalog.pg_class c
165
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
166
+ WHERE c.relkind = $1
167
+ AND n.nspname = $2
168
+ AND c.relname = $3
169
+ $$ LANGUAGE SQL;
170
+
171
+ CREATE OR REPLACE FUNCTION _get_rel_owner ( CHAR, NAME )
172
+ RETURNS NAME AS $$
173
+ SELECT pg_catalog.pg_get_userbyid(c.relowner)
174
+ FROM pg_catalog.pg_class c
175
+ WHERE c.relkind = $1
176
+ AND c.relname = $2
177
+ AND pg_catalog.pg_table_is_visible(c.oid)
178
+ $$ LANGUAGE SQL;
179
+
180
+ -- table_owner_is ( schema, table, user, description )
181
+ CREATE OR REPLACE FUNCTION table_owner_is ( NAME, NAME, NAME, TEXT )
182
+ RETURNS TEXT AS $$
183
+ DECLARE
184
+ owner NAME := _get_rel_owner('r'::char, $1, $2);
185
+ BEGIN
186
+ -- Make sure the table exists.
187
+ IF owner IS NULL THEN
188
+ RETURN ok(FALSE, $4) || E'\n' || diag(
189
+ E' Table ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
190
+ );
191
+ END IF;
192
+
193
+ RETURN is(owner, $3, $4);
194
+ END;
195
+ $$ LANGUAGE plpgsql;
196
+
197
+ -- table_owner_is ( schema, table, user )
198
+ CREATE OR REPLACE FUNCTION table_owner_is ( NAME, NAME, NAME )
199
+ RETURNS TEXT AS $$
200
+ SELECT table_owner_is(
201
+ $1, $2, $3,
202
+ 'Table ' || quote_ident($1) || '.' || quote_ident($2) || ' should be owned by ' || quote_ident($3)
203
+ );
204
+ $$ LANGUAGE sql;
205
+
206
+ -- table_owner_is ( table, user, description )
207
+ CREATE OR REPLACE FUNCTION table_owner_is ( NAME, NAME, TEXT )
208
+ RETURNS TEXT AS $$
209
+ DECLARE
210
+ owner NAME := _get_rel_owner('r'::char, $1);
211
+ BEGIN
212
+ -- Make sure the table exists.
213
+ IF owner IS NULL THEN
214
+ RETURN ok(FALSE, $3) || E'\n' || diag(
215
+ E' Table ' || quote_ident($1) || ' does not exist'
216
+ );
217
+ END IF;
218
+
219
+ RETURN is(owner, $2, $3);
220
+ END;
221
+ $$ LANGUAGE plpgsql;
222
+
223
+ -- table_owner_is ( table, user )
224
+ CREATE OR REPLACE FUNCTION table_owner_is ( NAME, NAME )
225
+ RETURNS TEXT AS $$
226
+ SELECT table_owner_is(
227
+ $1, $2,
228
+ 'Table ' || quote_ident($1) || ' should be owned by ' || quote_ident($2)
229
+ );
230
+ $$ LANGUAGE sql;
231
+
232
+ -- view_owner_is ( schema, view, user, description )
233
+ CREATE OR REPLACE FUNCTION view_owner_is ( NAME, NAME, NAME, TEXT )
234
+ RETURNS TEXT AS $$
235
+ DECLARE
236
+ owner NAME := _get_rel_owner('v'::char, $1, $2);
237
+ BEGIN
238
+ -- Make sure the view exists.
239
+ IF owner IS NULL THEN
240
+ RETURN ok(FALSE, $4) || E'\n' || diag(
241
+ E' View ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
242
+ );
243
+ END IF;
244
+
245
+ RETURN is(owner, $3, $4);
246
+ END;
247
+ $$ LANGUAGE plpgsql;
248
+
249
+ -- view_owner_is ( schema, view, user )
250
+ CREATE OR REPLACE FUNCTION view_owner_is ( NAME, NAME, NAME )
251
+ RETURNS TEXT AS $$
252
+ SELECT view_owner_is(
253
+ $1, $2, $3,
254
+ 'View ' || quote_ident($1) || '.' || quote_ident($2) || ' should be owned by ' || quote_ident($3)
255
+ );
256
+ $$ LANGUAGE sql;
257
+
258
+ -- view_owner_is ( view, user, description )
259
+ CREATE OR REPLACE FUNCTION view_owner_is ( NAME, NAME, TEXT )
260
+ RETURNS TEXT AS $$
261
+ DECLARE
262
+ owner NAME := _get_rel_owner('v'::char, $1);
263
+ BEGIN
264
+ -- Make sure the view exists.
265
+ IF owner IS NULL THEN
266
+ RETURN ok(FALSE, $3) || E'\n' || diag(
267
+ E' View ' || quote_ident($1) || ' does not exist'
268
+ );
269
+ END IF;
270
+
271
+ RETURN is(owner, $2, $3);
272
+ END;
273
+ $$ LANGUAGE plpgsql;
274
+
275
+ -- view_owner_is ( view, user )
276
+ CREATE OR REPLACE FUNCTION view_owner_is ( NAME, NAME )
277
+ RETURNS TEXT AS $$
278
+ SELECT view_owner_is(
279
+ $1, $2,
280
+ 'View ' || quote_ident($1) || ' should be owned by ' || quote_ident($2)
281
+ );
282
+ $$ LANGUAGE sql;
283
+
284
+ -- sequence_owner_is ( schema, sequence, user, description )
285
+ CREATE OR REPLACE FUNCTION sequence_owner_is ( NAME, NAME, NAME, TEXT )
286
+ RETURNS TEXT AS $$
287
+ DECLARE
288
+ owner NAME := _get_rel_owner('S'::char, $1, $2);
289
+ BEGIN
290
+ -- Make sure the sequence exists.
291
+ IF owner IS NULL THEN
292
+ RETURN ok(FALSE, $4) || E'\n' || diag(
293
+ E' Sequence ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
294
+ );
295
+ END IF;
296
+
297
+ RETURN is(owner, $3, $4);
298
+ END;
299
+ $$ LANGUAGE plpgsql;
300
+
301
+ -- sequence_owner_is ( schema, sequence, user )
302
+ CREATE OR REPLACE FUNCTION sequence_owner_is ( NAME, NAME, NAME )
303
+ RETURNS TEXT AS $$
304
+ SELECT sequence_owner_is(
305
+ $1, $2, $3,
306
+ 'Sequence ' || quote_ident($1) || '.' || quote_ident($2) || ' should be owned by ' || quote_ident($3)
307
+ );
308
+ $$ LANGUAGE sql;
309
+
310
+ -- sequence_owner_is ( sequence, user, description )
311
+ CREATE OR REPLACE FUNCTION sequence_owner_is ( NAME, NAME, TEXT )
312
+ RETURNS TEXT AS $$
313
+ DECLARE
314
+ owner NAME := _get_rel_owner('S'::char, $1);
315
+ BEGIN
316
+ -- Make sure the sequence exists.
317
+ IF owner IS NULL THEN
318
+ RETURN ok(FALSE, $3) || E'\n' || diag(
319
+ E' Sequence ' || quote_ident($1) || ' does not exist'
320
+ );
321
+ END IF;
322
+
323
+ RETURN is(owner, $2, $3);
324
+ END;
325
+ $$ LANGUAGE plpgsql;
326
+
327
+ -- sequence_owner_is ( sequence, user )
328
+ CREATE OR REPLACE FUNCTION sequence_owner_is ( NAME, NAME )
329
+ RETURNS TEXT AS $$
330
+ SELECT sequence_owner_is(
331
+ $1, $2,
332
+ 'Sequence ' || quote_ident($1) || ' should be owned by ' || quote_ident($2)
333
+ );
334
+ $$ LANGUAGE sql;
335
+
336
+ -- composite_owner_is ( schema, composite, user, description )
337
+ CREATE OR REPLACE FUNCTION composite_owner_is ( NAME, NAME, NAME, TEXT )
338
+ RETURNS TEXT AS $$
339
+ DECLARE
340
+ owner NAME := _get_rel_owner('c'::char, $1, $2);
341
+ BEGIN
342
+ -- Make sure the composite exists.
343
+ IF owner IS NULL THEN
344
+ RETURN ok(FALSE, $4) || E'\n' || diag(
345
+ E' Composite type ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
346
+ );
347
+ END IF;
348
+
349
+ RETURN is(owner, $3, $4);
350
+ END;
351
+ $$ LANGUAGE plpgsql;
352
+
353
+ -- composite_owner_is ( schema, composite, user )
354
+ CREATE OR REPLACE FUNCTION composite_owner_is ( NAME, NAME, NAME )
355
+ RETURNS TEXT AS $$
356
+ SELECT composite_owner_is(
357
+ $1, $2, $3,
358
+ 'Composite type ' || quote_ident($1) || '.' || quote_ident($2) || ' should be owned by ' || quote_ident($3)
359
+ );
360
+ $$ LANGUAGE sql;
361
+
362
+ -- composite_owner_is ( composite, user, description )
363
+ CREATE OR REPLACE FUNCTION composite_owner_is ( NAME, NAME, TEXT )
364
+ RETURNS TEXT AS $$
365
+ DECLARE
366
+ owner NAME := _get_rel_owner('c'::char, $1);
367
+ BEGIN
368
+ -- Make sure the composite exists.
369
+ IF owner IS NULL THEN
370
+ RETURN ok(FALSE, $3) || E'\n' || diag(
371
+ E' Composite type ' || quote_ident($1) || ' does not exist'
372
+ );
373
+ END IF;
374
+
375
+ RETURN is(owner, $2, $3);
376
+ END;
377
+ $$ LANGUAGE plpgsql;
378
+
379
+ -- composite_owner_is ( composite, user )
380
+ CREATE OR REPLACE FUNCTION composite_owner_is ( NAME, NAME )
381
+ RETURNS TEXT AS $$
382
+ SELECT composite_owner_is(
383
+ $1, $2,
384
+ 'Composite type ' || quote_ident($1) || ' should be owned by ' || quote_ident($2)
385
+ );
386
+ $$ LANGUAGE sql;
387
+
388
+ -- foreign_table_owner_is ( schema, table, user, description )
389
+ CREATE OR REPLACE FUNCTION foreign_table_owner_is ( NAME, NAME, NAME, TEXT )
390
+ RETURNS TEXT AS $$
391
+ DECLARE
392
+ owner NAME := _get_rel_owner('f'::char, $1, $2);
393
+ BEGIN
394
+ -- Make sure the table exists.
395
+ IF owner IS NULL THEN
396
+ RETURN ok(FALSE, $4) || E'\n' || diag(
397
+ E' Foreign table ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
398
+ );
399
+ END IF;
400
+
401
+ RETURN is(owner, $3, $4);
402
+ END;
403
+ $$ LANGUAGE plpgsql;
404
+
405
+ -- foreign_table_owner_is ( schema, table, user )
406
+ CREATE OR REPLACE FUNCTION foreign_table_owner_is ( NAME, NAME, NAME )
407
+ RETURNS TEXT AS $$
408
+ SELECT foreign_table_owner_is(
409
+ $1, $2, $3,
410
+ 'Foreign table ' || quote_ident($1) || '.' || quote_ident($2) || ' should be owned by ' || quote_ident($3)
411
+ );
412
+ $$ LANGUAGE sql;
413
+
414
+ -- foreign_table_owner_is ( table, user, description )
415
+ CREATE OR REPLACE FUNCTION foreign_table_owner_is ( NAME, NAME, TEXT )
416
+ RETURNS TEXT AS $$
417
+ DECLARE
418
+ owner NAME := _get_rel_owner('f'::char, $1);
419
+ BEGIN
420
+ -- Make sure the table exists.
421
+ IF owner IS NULL THEN
422
+ RETURN ok(FALSE, $3) || E'\n' || diag(
423
+ E' Foreign table ' || quote_ident($1) || ' does not exist'
424
+ );
425
+ END IF;
426
+
427
+ RETURN is(owner, $2, $3);
428
+ END;
429
+ $$ LANGUAGE plpgsql;
430
+
431
+ -- foreign_table_owner_is ( table, user )
432
+ CREATE OR REPLACE FUNCTION foreign_table_owner_is ( NAME, NAME )
433
+ RETURNS TEXT AS $$
434
+ SELECT foreign_table_owner_is(
435
+ $1, $2,
436
+ 'Foreign table ' || quote_ident($1) || ' should be owned by ' || quote_ident($2)
437
+ );
438
+ $$ LANGUAGE sql;
439
+
440
+ CREATE OR REPLACE FUNCTION _relexists ( NAME, NAME )
441
+ RETURNS BOOLEAN AS $$
442
+ SELECT EXISTS(
443
+ SELECT true
444
+ FROM pg_catalog.pg_namespace n
445
+ JOIN pg_catalog.pg_class c ON n.oid = c.relnamespace
446
+ WHERE n.nspname = $1
447
+ AND c.relname = $2
448
+ );
449
+ $$ LANGUAGE SQL;
450
+
451
+ CREATE OR REPLACE FUNCTION _relexists ( NAME )
452
+ RETURNS BOOLEAN AS $$
453
+ SELECT EXISTS(
454
+ SELECT true
455
+ FROM pg_catalog.pg_class c
456
+ WHERE pg_catalog.pg_table_is_visible(c.oid)
457
+ AND c.relname = $1
458
+ );
459
+ $$ LANGUAGE SQL;
460
+
461
+ -- has_relation( schema, relation, description )
462
+ CREATE OR REPLACE FUNCTION has_relation ( NAME, NAME, TEXT )
463
+ RETURNS TEXT AS $$
464
+ SELECT ok( _relexists( $1, $2 ), $3 );
465
+ $$ LANGUAGE SQL;
466
+
467
+ -- has_relation( relation, description )
468
+ CREATE OR REPLACE FUNCTION has_relation ( NAME, TEXT )
469
+ RETURNS TEXT AS $$
470
+ SELECT ok( _relexists( $1 ), $2 );
471
+ $$ LANGUAGE SQL;
472
+
473
+ -- has_relation( relation )
474
+ CREATE OR REPLACE FUNCTION has_relation ( NAME )
475
+ RETURNS TEXT AS $$
476
+ SELECT has_relation( $1, 'Relation ' || quote_ident($1) || ' should exist' );
477
+ $$ LANGUAGE SQL;
478
+
479
+ -- hasnt_relation( schema, relation, description )
480
+ CREATE OR REPLACE FUNCTION hasnt_relation ( NAME, NAME, TEXT )
481
+ RETURNS TEXT AS $$
482
+ SELECT ok( NOT _relexists( $1, $2 ), $3 );
483
+ $$ LANGUAGE SQL;
484
+
485
+ -- hasnt_relation( relation, description )
486
+ CREATE OR REPLACE FUNCTION hasnt_relation ( NAME, TEXT )
487
+ RETURNS TEXT AS $$
488
+ SELECT ok( NOT _relexists( $1 ), $2 );
489
+ $$ LANGUAGE SQL;
490
+
491
+ -- hasnt_relation( relation )
492
+ CREATE OR REPLACE FUNCTION hasnt_relation ( NAME )
493
+ RETURNS TEXT AS $$
494
+ SELECT hasnt_relation( $1, 'Relation ' || quote_ident($1) || ' should not exist' );
495
+ $$ LANGUAGE SQL;
496
+
497
+ DROP VIEW tap_funky;
498
+ CREATE VIEW tap_funky
499
+ AS SELECT p.oid AS oid,
500
+ n.nspname AS schema,
501
+ p.proname AS name,
502
+ pg_catalog.pg_get_userbyid(p.proowner) AS owner,
503
+ array_to_string(p.proargtypes::regtype[], ',') AS args,
504
+ CASE p.proretset WHEN TRUE THEN 'setof ' ELSE '' END
505
+ || p.prorettype::regtype AS returns,
506
+ p.prolang AS langoid,
507
+ p.proisstrict AS is_strict,
508
+ p.proisagg AS is_agg,
509
+ p.prosecdef AS is_definer,
510
+ p.proretset AS returns_set,
511
+ p.provolatile::char AS volatility,
512
+ pg_catalog.pg_function_is_visible(p.oid) AS is_visible
513
+ FROM pg_catalog.pg_proc p
514
+ JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
515
+ ;
516
+
517
+ CREATE OR REPLACE FUNCTION _get_func_owner ( NAME, NAME, NAME[] )
518
+ RETURNS NAME AS $$
519
+ SELECT owner
520
+ FROM tap_funky
521
+ WHERE schema = $1
522
+ AND name = $2
523
+ AND args = array_to_string($3, ',')
524
+ $$ LANGUAGE SQL;
525
+
526
+ CREATE OR REPLACE FUNCTION _get_func_owner ( NAME, NAME[] )
527
+ RETURNS NAME AS $$
528
+ SELECT owner
529
+ FROM tap_funky
530
+ WHERE name = $1
531
+ AND args = array_to_string($2, ',')
532
+ AND is_visible
533
+ $$ LANGUAGE SQL;
534
+
535
+ -- function_owner_is( schema, function, args[], user, description )
536
+ CREATE OR REPLACE FUNCTION function_owner_is ( NAME, NAME, NAME[], NAME, TEXT )
537
+ RETURNS TEXT AS $$
538
+ DECLARE
539
+ owner NAME := _get_func_owner($1, $2, $3);
540
+ BEGIN
541
+ -- Make sure the function exists.
542
+ IF owner IS NULL THEN
543
+ RETURN ok(FALSE, $5) || E'\n' || diag(
544
+ E' Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
545
+ array_to_string($3, ', ') || ') does not exist'
546
+ );
547
+ END IF;
548
+
549
+ RETURN is(owner, $4, $5);
550
+ END;
551
+ $$ LANGUAGE plpgsql;
552
+
553
+ -- function_owner_is( schema, function, args[], user )
554
+ CREATE OR REPLACE FUNCTION function_owner_is( NAME, NAME, NAME[], NAME )
555
+ RETURNS TEXT AS $$
556
+ SELECT function_owner_is(
557
+ $1, $2, $3, $4,
558
+ 'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
559
+ array_to_string($3, ', ') || ') should be owned by ' || quote_ident($4)
560
+ );
561
+ $$ LANGUAGE sql;
562
+
563
+ -- function_owner_is( function, args[], user, description )
564
+ CREATE OR REPLACE FUNCTION function_owner_is ( NAME, NAME[], NAME, TEXT )
565
+ RETURNS TEXT AS $$
566
+ DECLARE
567
+ owner NAME := _get_func_owner($1, $2);
568
+ BEGIN
569
+ -- Make sure the function exists.
570
+ IF owner IS NULL THEN
571
+ RETURN ok(FALSE, $4) || E'\n' || diag(
572
+ E' Function ' || quote_ident($1) || '(' ||
573
+ array_to_string($2, ', ') || ') does not exist'
574
+ );
575
+ END IF;
576
+
577
+ RETURN is(owner, $3, $4);
578
+ END;
579
+ $$ LANGUAGE plpgsql;
580
+
581
+ -- function_owner_is( function, args[], user )
582
+ CREATE OR REPLACE FUNCTION function_owner_is( NAME, NAME[], NAME )
583
+ RETURNS TEXT AS $$
584
+ SELECT function_owner_is(
585
+ $1, $2, $3,
586
+ 'Function ' || quote_ident($1) || '(' ||
587
+ array_to_string($2, ', ') || ') should be owned by ' || quote_ident($3)
588
+ );
589
+ $$ LANGUAGE sql;
590
+
591
+ CREATE OR REPLACE FUNCTION _get_latest ( text )
592
+ RETURNS integer[] AS $$
593
+ DECLARE
594
+ ret integer[];
595
+ BEGIN
596
+ EXECUTE 'SELECT ARRAY[ id, value] FROM __tcache__ WHERE label = ' ||
597
+ quote_literal($1) || ' AND id = (SELECT MAX(id) FROM __tcache__ WHERE label = ' ||
598
+ quote_literal($1) || ') LIMIT 1' INTO ret;
599
+ RETURN ret;
600
+ EXCEPTION WHEN undefined_table THEN
601
+ RAISE EXCEPTION 'You tried to run a test without a plan! Gotta have a plan';
602
+ END;
603
+ $$ LANGUAGE plpgsql strict;
604
+
605
+ CREATE OR REPLACE FUNCTION _trig ( NAME, NAME, NAME )
606
+ RETURNS BOOLEAN AS $$
607
+ SELECT EXISTS(
608
+ SELECT true
609
+ FROM pg_catalog.pg_trigger t
610
+ JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
611
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
612
+ WHERE n.nspname = $1
613
+ AND c.relname = $2
614
+ AND t.tgname = $3
615
+ AND NOT t.tgisinternal
616
+ );
617
+ $$ LANGUAGE SQL;
618
+
619
+ CREATE OR REPLACE FUNCTION _trig ( NAME, NAME )
620
+ RETURNS BOOLEAN AS $$
621
+ SELECT EXISTS(
622
+ SELECT true
623
+ FROM pg_catalog.pg_trigger t
624
+ JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
625
+ WHERE c.relname = $1
626
+ AND t.tgname = $2
627
+ AND NOT t.tgisinternal
628
+ );
629
+ $$ LANGUAGE SQL;
630
+
631
+ -- triggers_are( schema, table, triggers[], description )
632
+ CREATE OR REPLACE FUNCTION triggers_are( NAME, NAME, NAME[], TEXT )
633
+ RETURNS TEXT AS $$
634
+ SELECT _are(
635
+ 'triggers',
636
+ ARRAY(
637
+ SELECT t.tgname
638
+ FROM pg_catalog.pg_trigger t
639
+ JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
640
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
641
+ WHERE n.nspname = $1
642
+ AND c.relname = $2
643
+ AND NOT t.tgisinternal
644
+ EXCEPT
645
+ SELECT $3[i]
646
+ FROM generate_series(1, array_upper($3, 1)) s(i)
647
+ ),
648
+ ARRAY(
649
+ SELECT $3[i]
650
+ FROM generate_series(1, array_upper($3, 1)) s(i)
651
+ EXCEPT
652
+ SELECT t.tgname
653
+ FROM pg_catalog.pg_trigger t
654
+ JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
655
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
656
+ WHERE n.nspname = $1
657
+ AND c.relname = $2
658
+ AND NOT t.tgisinternal
659
+ ),
660
+ $4
661
+ );
662
+ $$ LANGUAGE SQL;
663
+
664
+ -- triggers_are( table, triggers[], description )
665
+ CREATE OR REPLACE FUNCTION triggers_are( NAME, NAME[], TEXT )
666
+ RETURNS TEXT AS $$
667
+ SELECT _are(
668
+ 'triggers',
669
+ ARRAY(
670
+ SELECT t.tgname
671
+ FROM pg_catalog.pg_trigger t
672
+ JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
673
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
674
+ WHERE c.relname = $1
675
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema')
676
+ AND NOT t.tgisinternal
677
+ EXCEPT
678
+ SELECT $2[i]
679
+ FROM generate_series(1, array_upper($2, 1)) s(i)
680
+ ),
681
+ ARRAY(
682
+ SELECT $2[i]
683
+ FROM generate_series(1, array_upper($2, 1)) s(i)
684
+ EXCEPT
685
+ SELECT t.tgname
686
+ FROM pg_catalog.pg_trigger t
687
+ JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
688
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
689
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema')
690
+ AND NOT t.tgisinternal
691
+ ),
692
+ $3
693
+ );
694
+ $$ LANGUAGE SQL;
695
+
696
+ -- results_eq( cursor, cursor, description )
697
+ CREATE OR REPLACE FUNCTION results_eq( refcursor, refcursor, text )
698
+ RETURNS TEXT AS $$
699
+ DECLARE
700
+ have ALIAS FOR $1;
701
+ want ALIAS FOR $2;
702
+ have_rec RECORD;
703
+ want_rec RECORD;
704
+ have_found BOOLEAN;
705
+ want_found BOOLEAN;
706
+ rownum INTEGER := 1;
707
+ BEGIN
708
+ FETCH have INTO have_rec;
709
+ have_found := FOUND;
710
+ FETCH want INTO want_rec;
711
+ want_found := FOUND;
712
+ WHILE have_found OR want_found LOOP
713
+ IF have_rec IS DISTINCT FROM want_rec OR have_found <> want_found THEN
714
+ RETURN ok( false, $3 ) || E'\n' || diag(
715
+ ' Results differ beginning at row ' || rownum || E':\n' ||
716
+ ' have: ' || CASE WHEN have_found THEN have_rec::text ELSE 'NULL' END || E'\n' ||
717
+ ' want: ' || CASE WHEN want_found THEN want_rec::text ELSE 'NULL' END
718
+ );
719
+ END IF;
720
+ rownum = rownum + 1;
721
+ FETCH have INTO have_rec;
722
+ have_found := FOUND;
723
+ FETCH want INTO want_rec;
724
+ want_found := FOUND;
725
+ END LOOP;
726
+
727
+ RETURN ok( true, $3 );
728
+ EXCEPTION
729
+ WHEN datatype_mismatch THEN
730
+ RETURN ok( false, $3 ) || E'\n' || diag(
731
+ E' Number of columns or their types differ between the queries' ||
732
+ CASE WHEN have_rec::TEXT = want_rec::text THEN '' ELSE E':\n' ||
733
+ ' have: ' || CASE WHEN have_found THEN have_rec::text ELSE 'NULL' END || E'\n' ||
734
+ ' want: ' || CASE WHEN want_found THEN want_rec::text ELSE 'NULL' END
735
+ END
736
+ );
737
+ END;
738
+ $$ LANGUAGE plpgsql;
739
+
740
+ -- isnt_empty( sql, description )
741
+ CREATE OR REPLACE FUNCTION isnt_empty( TEXT, TEXT )
742
+ RETURNS TEXT AS $$
743
+ DECLARE
744
+ res BOOLEAN := FALSE;
745
+ rec RECORD;
746
+ BEGIN
747
+ -- Find extra records.
748
+ FOR rec in EXECUTE _query($1) LOOP
749
+ res := TRUE;
750
+ EXIT;
751
+ END LOOP;
752
+
753
+ RETURN ok(res, $2);
754
+ END;
755
+ $$ LANGUAGE plpgsql;
756
+
757
+ -- isnt_empty( sql )
758
+ CREATE OR REPLACE FUNCTION isnt_empty( TEXT )
759
+ RETURNS TEXT AS $$
760
+ SELECT isnt_empty( $1, NULL );
761
+ $$ LANGUAGE sql;
762
+
763
+ DROP FUNCTION _ikeys( NAME, NAME, NAME );
764
+ DROP FUNCTION _ikeys( NAME, NAME );
765
+ DROP FUNCTION _iexpr( NAME, NAME, NAME );
766
+ DROP FUNCTION _iexpr( NAME, NAME );
767
+
768
+ CREATE OR REPLACE FUNCTION _ikeys( NAME, NAME, NAME)
769
+ RETURNS TEXT[] AS $$
770
+ SELECT ARRAY(
771
+ SELECT pg_catalog.pg_get_indexdef( ci.oid, s.i + 1, false)
772
+ FROM pg_catalog.pg_index x
773
+ JOIN pg_catalog.pg_class ct ON ct.oid = x.indrelid
774
+ JOIN pg_catalog.pg_class ci ON ci.oid = x.indexrelid
775
+ JOIN pg_catalog.pg_namespace n ON n.oid = ct.relnamespace
776
+ JOIN generate_series(0, current_setting('max_index_keys')::int - 1) s(i)
777
+ ON x.indkey[s.i] IS NOT NULL
778
+ WHERE ct.relname = $2
779
+ AND ci.relname = $3
780
+ AND n.nspname = $1
781
+ ORDER BY s.i
782
+ );
783
+ $$ LANGUAGE sql;
784
+
785
+ CREATE OR REPLACE FUNCTION _ikeys( NAME, NAME)
786
+ RETURNS TEXT[] AS $$
787
+ SELECT ARRAY(
788
+ SELECT pg_catalog.pg_get_indexdef( ci.oid, s.i + 1, false)
789
+ FROM pg_catalog.pg_index x
790
+ JOIN pg_catalog.pg_class ct ON ct.oid = x.indrelid
791
+ JOIN pg_catalog.pg_class ci ON ci.oid = x.indexrelid
792
+ JOIN generate_series(0, current_setting('max_index_keys')::int - 1) s(i)
793
+ ON x.indkey[s.i] IS NOT NULL
794
+ WHERE ct.relname = $1
795
+ AND ci.relname = $2
796
+ AND pg_catalog.pg_table_is_visible(ct.oid)
797
+ ORDER BY s.i
798
+ );
799
+ $$ LANGUAGE sql;
800
+
801
+ -- has_index( schema, table, index, columns[], description )
802
+ CREATE OR REPLACE FUNCTION has_index ( NAME, NAME, NAME, NAME[], text )
803
+ RETURNS TEXT AS $$
804
+ DECLARE
805
+ index_cols name[];
806
+ BEGIN
807
+ index_cols := _ikeys($1, $2, $3 );
808
+
809
+ IF index_cols IS NULL OR index_cols = '{}'::name[] THEN
810
+ RETURN ok( false, $5 ) || E'\n'
811
+ || diag( 'Index ' || quote_ident($3) || ' ON ' || quote_ident($1) || '.' || quote_ident($2) || ' not found');
812
+ END IF;
813
+
814
+ RETURN is(
815
+ quote_ident($3) || ' ON ' || quote_ident($1) || '.' || quote_ident($2) || '(' || array_to_string( index_cols, ', ' ) || ')',
816
+ quote_ident($3) || ' ON ' || quote_ident($1) || '.' || quote_ident($2) || '(' || array_to_string( $4, ', ' ) || ')',
817
+ $5
818
+ );
819
+ END;
820
+ $$ LANGUAGE plpgsql;
821
+
822
+ CREATE OR REPLACE FUNCTION has_index ( NAME, NAME, NAME, NAME, text )
823
+ RETURNS TEXT AS $$
824
+ SELECT has_index( $1, $2, $3, ARRAY[$4], $5 );
825
+ $$ LANGUAGE sql;
826
+
827
+ -- has_index( table, index, columns[], description )
828
+ CREATE OR REPLACE FUNCTION has_index ( NAME, NAME, NAME[], text )
829
+ RETURNS TEXT AS $$
830
+ DECLARE
831
+ index_cols name[];
832
+ BEGIN
833
+ index_cols := _ikeys($1, $2 );
834
+
835
+ IF index_cols IS NULL OR index_cols = '{}'::name[] THEN
836
+ RETURN ok( false, $4 ) || E'\n'
837
+ || diag( 'Index ' || quote_ident($2) || ' ON ' || quote_ident($1) || ' not found');
838
+ END IF;
839
+
840
+ RETURN is(
841
+ quote_ident($2) || ' ON ' || quote_ident($1) || '(' || array_to_string( index_cols, ', ' ) || ')',
842
+ quote_ident($2) || ' ON ' || quote_ident($1) || '(' || array_to_string( $3, ', ' ) || ')',
843
+ $4
844
+ );
845
+ END;
846
+ $$ LANGUAGE plpgsql;
847
+
848
+ CREATE OR REPLACE FUNCTION has_index ( NAME, NAME, NAME, text )
849
+ RETURNS TEXT AS $$
850
+ SELECT CASE WHEN _is_schema( $1 ) THEN
851
+ -- Looking for schema.table index.
852
+ ok ( _have_index( $1, $2, $3 ), $4)
853
+ ELSE
854
+ -- Looking for particular columns.
855
+ has_index( $1, $2, ARRAY[$3], $4 )
856
+ END;
857
+ $$ LANGUAGE sql;
858
+
859
+ CREATE OR REPLACE FUNCTION _assets_are ( text, text[], text[], TEXT )
860
+ RETURNS TEXT AS $$
861
+ SELECT _areni(
862
+ $1,
863
+ ARRAY(
864
+ SELECT UPPER($2[i]) AS thing
865
+ FROM generate_series(1, array_upper($2, 1)) s(i)
866
+ EXCEPT
867
+ SELECT $3[i]
868
+ FROM generate_series(1, array_upper($3, 1)) s(i)
869
+ ORDER BY thing
870
+ ),
871
+ ARRAY(
872
+ SELECT $3[i] AS thing
873
+ FROM generate_series(1, array_upper($3, 1)) s(i)
874
+ EXCEPT
875
+ SELECT UPPER($2[i])
876
+ FROM generate_series(1, array_upper($2, 1)) s(i)
877
+ ORDER BY thing
878
+ ),
879
+ $4
880
+ );
881
+ $$ LANGUAGE SQL;
882
+
883
+ CREATE OR REPLACE FUNCTION _get_table_privs(NAME, TEXT)
884
+ RETURNS TEXT[] AS $$
885
+ DECLARE
886
+ privs TEXT[] := _table_privs();
887
+ grants TEXT[] := '{}';
888
+ BEGIN
889
+ FOR i IN 1..array_upper(privs, 1) LOOP
890
+ BEGIN
891
+ IF pg_catalog.has_table_privilege($1, $2, privs[i]) THEN
892
+ grants := grants || privs[i];
893
+ END IF;
894
+ EXCEPTION WHEN undefined_table THEN
895
+ -- Not a valid table name.
896
+ RETURN '{undefined_table}';
897
+ WHEN undefined_object THEN
898
+ -- Not a valid role.
899
+ RETURN '{undefined_role}';
900
+ WHEN invalid_parameter_value THEN
901
+ -- Not a valid permission on this version of PostgreSQL; ignore;
902
+ END;
903
+ END LOOP;
904
+ RETURN grants;
905
+ END;
906
+ $$ LANGUAGE plpgsql;
907
+
908
+ CREATE OR REPLACE FUNCTION _table_privs()
909
+ RETURNS NAME[] AS $$
910
+ DECLARE
911
+ pgversion INTEGER := pg_version_num();
912
+ BEGIN
913
+ IF pgversion < 80200 THEN RETURN ARRAY[
914
+ 'DELETE', 'INSERT', 'REFERENCES', 'RULE', 'SELECT', 'TRIGGER', 'UPDATE'
915
+ ];
916
+ ELSIF pgversion < 80400 THEN RETURN ARRAY[
917
+ 'DELETE', 'INSERT', 'REFERENCES', 'SELECT', 'TRIGGER', 'UPDATE'
918
+ ];
919
+ ELSE RETURN ARRAY[
920
+ 'DELETE', 'INSERT', 'REFERENCES', 'SELECT', 'TRIGGER', 'TRUNCATE', 'UPDATE'
921
+ ];
922
+ END IF;
923
+ END;
924
+ $$ language plpgsql;
925
+
926
+ -- table_privs_are ( schema, table, user, privileges[], description )
927
+ CREATE OR REPLACE FUNCTION table_privs_are ( NAME, NAME, NAME, NAME[], TEXT )
928
+ RETURNS TEXT AS $$
929
+ DECLARE
930
+ grants TEXT[] := _get_table_privs( $3, quote_ident($1) || '.' || quote_ident($2) );
931
+ BEGIN
932
+ IF grants[1] = 'undefined_table' THEN
933
+ RETURN ok(FALSE, $5) || E'\n' || diag(
934
+ ' Table ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
935
+ );
936
+ ELSIF grants[1] = 'undefined_role' THEN
937
+ RETURN ok(FALSE, $5) || E'\n' || diag(
938
+ ' Role ' || quote_ident($3) || ' does not exist'
939
+ );
940
+ END IF;
941
+ RETURN _assets_are('privileges', grants, $4, $5);
942
+ END;
943
+ $$ LANGUAGE plpgsql;
944
+
945
+ -- table_privs_are ( schema, table, user, privileges[] )
946
+ CREATE OR REPLACE FUNCTION table_privs_are ( NAME, NAME, NAME, NAME[] )
947
+ RETURNS TEXT AS $$
948
+ SELECT table_privs_are(
949
+ $1, $2, $3, $4,
950
+ 'Role ' || quote_ident($3) || ' should be granted '
951
+ || CASE WHEN $4[1] IS NULL THEN 'no privileges' ELSE array_to_string($4, ', ') END
952
+ || ' on table ' || quote_ident($1) || '.' || quote_ident($2)
953
+ );
954
+ $$ LANGUAGE SQL;
955
+
956
+ -- table_privs_are ( table, user, privileges[], description )
957
+ CREATE OR REPLACE FUNCTION table_privs_are ( NAME, NAME, NAME[], TEXT )
958
+ RETURNS TEXT AS $$
959
+ DECLARE
960
+ grants TEXT[] := _get_table_privs( $2, quote_ident($1) );
961
+ BEGIN
962
+ IF grants[1] = 'undefined_table' THEN
963
+ RETURN ok(FALSE, $4) || E'\n' || diag(
964
+ ' Table ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
965
+ );
966
+ ELSIF grants[1] = 'undefined_role' THEN
967
+ RETURN ok(FALSE, $4) || E'\n' || diag(
968
+ ' Role ' || quote_ident($2) || ' does not exist'
969
+ );
970
+ END IF;
971
+ RETURN _assets_are('privileges', grants, $3, $4);
972
+ END;
973
+ $$ LANGUAGE plpgsql;
974
+
975
+ -- table_privs_are ( table, user, privileges[] )
976
+ CREATE OR REPLACE FUNCTION table_privs_are ( NAME, NAME, NAME[] )
977
+ RETURNS TEXT AS $$
978
+ SELECT table_privs_are(
979
+ $1, $2, $3,
980
+ 'Role ' || quote_ident($2) || ' should be granted '
981
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
982
+ || ' on table ' || quote_ident($1)
983
+ );
984
+ $$ LANGUAGE SQL;
985
+
986
+ CREATE OR REPLACE FUNCTION _db_privs()
987
+ RETURNS NAME[] AS $$
988
+ DECLARE
989
+ pgversion INTEGER := pg_version_num();
990
+ BEGIN
991
+ IF pgversion < 80200 THEN
992
+ RETURN ARRAY['CREATE', 'TEMPORARY'];
993
+ ELSE
994
+ RETURN ARRAY['CREATE', 'CONNECT', 'TEMPORARY'];
995
+ END IF;
996
+ END;
997
+ $$ language plpgsql;
998
+
999
+ CREATE OR REPLACE FUNCTION _get_db_privs(NAME, TEXT)
1000
+ RETURNS TEXT[] AS $$
1001
+ DECLARE
1002
+ privs TEXT[] := _db_privs();
1003
+ grants TEXT[] := '{}';
1004
+ BEGIN
1005
+ FOR i IN 1..array_upper(privs, 1) LOOP
1006
+ BEGIN
1007
+ IF pg_catalog.has_database_privilege($1, $2, privs[i]) THEN
1008
+ grants := grants || privs[i];
1009
+ END IF;
1010
+ EXCEPTION WHEN invalid_catalog_name THEN
1011
+ -- Not a valid db name.
1012
+ RETURN '{invalid_catalog_name}';
1013
+ WHEN undefined_object THEN
1014
+ -- Not a valid role.
1015
+ RETURN '{undefined_role}';
1016
+ WHEN invalid_parameter_value THEN
1017
+ -- Not a valid permission on this version of PostgreSQL; ignore;
1018
+ END;
1019
+ END LOOP;
1020
+ RETURN grants;
1021
+ END;
1022
+ $$ LANGUAGE plpgsql;
1023
+
1024
+ -- database_privs_are ( db, user, privileges[], description )
1025
+ CREATE OR REPLACE FUNCTION database_privs_are ( NAME, NAME, NAME[], TEXT )
1026
+ RETURNS TEXT AS $$
1027
+ DECLARE
1028
+ grants TEXT[] := _get_db_privs( $2, quote_ident($1) );
1029
+ BEGIN
1030
+ IF grants[1] = 'invalid_catalog_name' THEN
1031
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1032
+ ' Database ' || quote_ident($1) || ' does not exist'
1033
+ );
1034
+ ELSIF grants[1] = 'undefined_role' THEN
1035
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1036
+ ' Role ' || quote_ident($2) || ' does not exist'
1037
+ );
1038
+ END IF;
1039
+ RETURN _assets_are('privileges', grants, $3, $4);
1040
+ END;
1041
+ $$ LANGUAGE plpgsql;
1042
+
1043
+ -- database_privs_are ( db, user, privileges[] )
1044
+ CREATE OR REPLACE FUNCTION database_privs_are ( NAME, NAME, NAME[] )
1045
+ RETURNS TEXT AS $$
1046
+ SELECT database_privs_are(
1047
+ $1, $2, $3,
1048
+ 'Role ' || quote_ident($2) || ' should be granted '
1049
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1050
+ || ' on database ' || quote_ident($1)
1051
+ );
1052
+ $$ LANGUAGE SQL;
1053
+
1054
+ CREATE OR REPLACE FUNCTION _get_func_privs(NAME, TEXT)
1055
+ RETURNS TEXT[] AS $$
1056
+ BEGIN
1057
+ IF pg_catalog.has_function_privilege($1, $2, 'EXECUTE') THEN
1058
+ RETURN '{EXECUTE}';
1059
+ ELSE
1060
+ RETURN '{}';
1061
+ END IF;
1062
+ EXCEPTION
1063
+ -- Not a valid func name.
1064
+ WHEN undefined_function THEN RETURN '{undefined_function}';
1065
+ -- Not a valid role.
1066
+ WHEN undefined_object THEN RETURN '{undefined_role}';
1067
+ END;
1068
+ $$ LANGUAGE plpgsql;
1069
+
1070
+ CREATE OR REPLACE FUNCTION _fprivs_are ( NAME, NAME, NAME[], TEXT )
1071
+ RETURNS TEXT AS $$
1072
+ DECLARE
1073
+ grants TEXT[] := _get_func_privs($2, $1);
1074
+ BEGIN
1075
+ IF grants[1] = 'undefined_function' THEN
1076
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1077
+ ' Function ' || $1 || ' does not exist'
1078
+ );
1079
+ ELSIF grants[1] = 'undefined_role' THEN
1080
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1081
+ ' Role ' || quote_ident($2) || ' does not exist'
1082
+ );
1083
+ END IF;
1084
+ RETURN _assets_are('privileges', grants, $3, $4);
1085
+ END;
1086
+ $$ LANGUAGE plpgsql;
1087
+
1088
+ -- function_privs_are ( schema, function, args[], user, privileges[], description )
1089
+ CREATE OR REPLACE FUNCTION function_privs_are ( NAME, NAME, NAME[], NAME, NAME[], TEXT )
1090
+ RETURNS TEXT AS $$
1091
+ SELECT _fprivs_are(
1092
+ quote_ident($1) || '.' || quote_ident($2) || '(' || array_to_string($3, ', ') || ')',
1093
+ $4, $5, $6
1094
+ );
1095
+ $$ LANGUAGE SQL;
1096
+
1097
+ -- function_privs_are ( schema, function, args[], user, privileges[] )
1098
+ CREATE OR REPLACE FUNCTION function_privs_are ( NAME, NAME, NAME[], NAME, NAME[] )
1099
+ RETURNS TEXT AS $$
1100
+ SELECT function_privs_are(
1101
+ $1, $2, $3, $4, $5,
1102
+ 'Role ' || quote_ident($4) || ' should be granted '
1103
+ || CASE WHEN $5[1] IS NULL THEN 'no privileges' ELSE array_to_string($5, ', ') END
1104
+ || ' on function ' || quote_ident($1) || '.' || quote_ident($2)
1105
+ || '(' || array_to_string($3, ', ') || ')'
1106
+ );
1107
+ $$ LANGUAGE SQL;
1108
+
1109
+ -- function_privs_are ( function, args[], user, privileges[], description )
1110
+ CREATE OR REPLACE FUNCTION function_privs_are ( NAME, NAME[], NAME, NAME[], TEXT )
1111
+ RETURNS TEXT AS $$
1112
+ SELECT _fprivs_are(
1113
+ quote_ident($1) || '(' || array_to_string($2, ', ') || ')',
1114
+ $3, $4, $5
1115
+ );
1116
+ $$ LANGUAGE SQL;
1117
+
1118
+ -- function_privs_are ( function, args[], user, privileges[] )
1119
+ CREATE OR REPLACE FUNCTION function_privs_are ( NAME, NAME[], NAME, NAME[] )
1120
+ RETURNS TEXT AS $$
1121
+ SELECT function_privs_are(
1122
+ $1, $2, $3, $4,
1123
+ 'Role ' || quote_ident($3) || ' should be granted '
1124
+ || CASE WHEN $4[1] IS NULL THEN 'no privileges' ELSE array_to_string($4, ', ') END
1125
+ || ' on function ' || quote_ident($1) || '(' || array_to_string($2, ', ') || ')'
1126
+ );
1127
+ $$ LANGUAGE SQL;
1128
+
1129
+ CREATE OR REPLACE FUNCTION _get_lang_privs (NAME, TEXT)
1130
+ RETURNS TEXT[] AS $$
1131
+ BEGIN
1132
+ IF pg_catalog.has_language_privilege($1, $2, 'USAGE') THEN
1133
+ RETURN '{USAGE}';
1134
+ ELSE
1135
+ RETURN '{}';
1136
+ END IF;
1137
+ EXCEPTION WHEN undefined_object THEN
1138
+ -- Same error code for unknown user or language. So figure out which.
1139
+ RETURN CASE WHEN SQLERRM LIKE '%' || $1 || '%' THEN
1140
+ '{undefined_role}'
1141
+ ELSE
1142
+ '{undefined_language}'
1143
+ END;
1144
+ END;
1145
+ $$ LANGUAGE plpgsql;
1146
+
1147
+ -- language_privs_are ( lang, user, privileges[], description )
1148
+ CREATE OR REPLACE FUNCTION language_privs_are ( NAME, NAME, NAME[], TEXT )
1149
+ RETURNS TEXT AS $$
1150
+ DECLARE
1151
+ grants TEXT[] := _get_lang_privs( $2, quote_ident($1) );
1152
+ BEGIN
1153
+ IF grants[1] = 'undefined_language' THEN
1154
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1155
+ ' Language ' || quote_ident($1) || ' does not exist'
1156
+ );
1157
+ ELSIF grants[1] = 'undefined_role' THEN
1158
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1159
+ ' Role ' || quote_ident($2) || ' does not exist'
1160
+ );
1161
+ END IF;
1162
+ RETURN _assets_are('privileges', grants, $3, $4);
1163
+ END;
1164
+ $$ LANGUAGE plpgsql;
1165
+
1166
+ -- language_privs_are ( lang, user, privileges[] )
1167
+ CREATE OR REPLACE FUNCTION language_privs_are ( NAME, NAME, NAME[] )
1168
+ RETURNS TEXT AS $$
1169
+ SELECT language_privs_are(
1170
+ $1, $2, $3,
1171
+ 'Role ' || quote_ident($2) || ' should be granted '
1172
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1173
+ || ' on language ' || quote_ident($1)
1174
+ );
1175
+ $$ LANGUAGE SQL;
1176
+
1177
+ CREATE OR REPLACE FUNCTION _get_schema_privs(NAME, TEXT)
1178
+ RETURNS TEXT[] AS $$
1179
+ DECLARE
1180
+ privs TEXT[] := ARRAY['CREATE', 'USAGE'];
1181
+ grants TEXT[] := '{}';
1182
+ BEGIN
1183
+ FOR i IN 1..array_upper(privs, 1) LOOP
1184
+ IF pg_catalog.has_schema_privilege($1, $2, privs[i]) THEN
1185
+ grants := grants || privs[i];
1186
+ END IF;
1187
+ END LOOP;
1188
+ RETURN grants;
1189
+ EXCEPTION
1190
+ -- Not a valid schema name.
1191
+ WHEN invalid_schema_name THEN RETURN '{invalid_schema_name}';
1192
+ -- Not a valid role.
1193
+ WHEN undefined_object THEN RETURN '{undefined_role}';
1194
+ END;
1195
+ $$ LANGUAGE plpgsql;
1196
+
1197
+ -- schema_privs_are ( schema, user, privileges[], description )
1198
+ CREATE OR REPLACE FUNCTION schema_privs_are ( NAME, NAME, NAME[], TEXT )
1199
+ RETURNS TEXT AS $$
1200
+ DECLARE
1201
+ grants TEXT[] := _get_schema_privs( $2, quote_ident($1) );
1202
+ BEGIN
1203
+ IF grants[1] = 'invalid_schema_name' THEN
1204
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1205
+ ' Schema ' || quote_ident($1) || ' does not exist'
1206
+ );
1207
+ ELSIF grants[1] = 'undefined_role' THEN
1208
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1209
+ ' Role ' || quote_ident($2) || ' does not exist'
1210
+ );
1211
+ END IF;
1212
+ RETURN _assets_are('privileges', grants, $3, $4);
1213
+ END;
1214
+ $$ LANGUAGE plpgsql;
1215
+
1216
+ -- schema_privs_are ( schema, user, privileges[] )
1217
+ CREATE OR REPLACE FUNCTION schema_privs_are ( NAME, NAME, NAME[] )
1218
+ RETURNS TEXT AS $$
1219
+ SELECT schema_privs_are(
1220
+ $1, $2, $3,
1221
+ 'Role ' || quote_ident($2) || ' should be granted '
1222
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1223
+ || ' on schema ' || quote_ident($1)
1224
+ );
1225
+ $$ LANGUAGE SQL;
1226
+
1227
+ CREATE OR REPLACE FUNCTION _get_tablespaceprivs (NAME, TEXT)
1228
+ RETURNS TEXT[] AS $$
1229
+ BEGIN
1230
+ IF pg_catalog.has_tablespace_privilege($1, $2, 'CREATE') THEN
1231
+ RETURN '{CREATE}';
1232
+ ELSE
1233
+ RETURN '{}';
1234
+ END IF;
1235
+ EXCEPTION WHEN undefined_object THEN
1236
+ -- Same error code for unknown user or tablespace. So figure out which.
1237
+ RETURN CASE WHEN SQLERRM LIKE '%' || $1 || '%' THEN
1238
+ '{undefined_role}'
1239
+ ELSE
1240
+ '{undefined_tablespace}'
1241
+ END;
1242
+ END;
1243
+ $$ LANGUAGE plpgsql;
1244
+
1245
+ -- tablespace_privs_are ( tablespace, user, privileges[], description )
1246
+ CREATE OR REPLACE FUNCTION tablespace_privs_are ( NAME, NAME, NAME[], TEXT )
1247
+ RETURNS TEXT AS $$
1248
+ DECLARE
1249
+ grants TEXT[] := _get_tablespaceprivs( $2, quote_ident($1) );
1250
+ BEGIN
1251
+ IF grants[1] = 'undefined_tablespace' THEN
1252
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1253
+ ' Tablespace ' || quote_ident($1) || ' does not exist'
1254
+ );
1255
+ ELSIF grants[1] = 'undefined_role' THEN
1256
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1257
+ ' Role ' || quote_ident($2) || ' does not exist'
1258
+ );
1259
+ END IF;
1260
+ RETURN _assets_are('privileges', grants, $3, $4);
1261
+ END;
1262
+ $$ LANGUAGE plpgsql;
1263
+
1264
+ -- tablespace_privs_are ( tablespace, user, privileges[] )
1265
+ CREATE OR REPLACE FUNCTION tablespace_privs_are ( NAME, NAME, NAME[] )
1266
+ RETURNS TEXT AS $$
1267
+ SELECT tablespace_privs_are(
1268
+ $1, $2, $3,
1269
+ 'Role ' || quote_ident($2) || ' should be granted '
1270
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1271
+ || ' on tablespace ' || quote_ident($1)
1272
+ );
1273
+ $$ LANGUAGE SQL;
1274
+
1275
+ CREATE OR REPLACE FUNCTION _get_sequence_privs(NAME, TEXT)
1276
+ RETURNS TEXT[] AS $$
1277
+ DECLARE
1278
+ privs TEXT[] := ARRAY['SELECT', 'UPDATE', 'USAGE'];
1279
+ grants TEXT[] := '{}';
1280
+ BEGIN
1281
+ FOR i IN 1..array_upper(privs, 1) LOOP
1282
+ BEGIN
1283
+ IF pg_catalog.has_sequence_privilege($1, $2, privs[i]) THEN
1284
+ grants := grants || privs[i];
1285
+ END IF;
1286
+ EXCEPTION WHEN undefined_table THEN
1287
+ -- Not a valid sequence name.
1288
+ RETURN '{undefined_table}';
1289
+ WHEN undefined_object THEN
1290
+ -- Not a valid role.
1291
+ RETURN '{undefined_role}';
1292
+ WHEN invalid_parameter_value THEN
1293
+ -- Not a valid permission on this version of PostgreSQL; ignore;
1294
+ END;
1295
+ END LOOP;
1296
+ RETURN grants;
1297
+ END;
1298
+ $$ LANGUAGE plpgsql;
1299
+
1300
+ -- sequence_privs_are ( schema, sequence, user, privileges[], description )
1301
+ CREATE OR REPLACE FUNCTION sequence_privs_are ( NAME, NAME, NAME, NAME[], TEXT )
1302
+ RETURNS TEXT AS $$
1303
+ DECLARE
1304
+ grants TEXT[] := _get_sequence_privs( $3, quote_ident($1) || '.' || quote_ident($2) );
1305
+ BEGIN
1306
+ IF grants[1] = 'undefined_table' THEN
1307
+ RETURN ok(FALSE, $5) || E'\n' || diag(
1308
+ ' Sequence ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
1309
+ );
1310
+ ELSIF grants[1] = 'undefined_role' THEN
1311
+ RETURN ok(FALSE, $5) || E'\n' || diag(
1312
+ ' Role ' || quote_ident($3) || ' does not exist'
1313
+ );
1314
+ END IF;
1315
+ RETURN _assets_are('privileges', grants, $4, $5);
1316
+ END;
1317
+ $$ LANGUAGE plpgsql;
1318
+
1319
+ -- sequence_privs_are ( schema, sequence, user, privileges[] )
1320
+ CREATE OR REPLACE FUNCTION sequence_privs_are ( NAME, NAME, NAME, NAME[] )
1321
+ RETURNS TEXT AS $$
1322
+ SELECT sequence_privs_are(
1323
+ $1, $2, $3, $4,
1324
+ 'Role ' || quote_ident($3) || ' should be granted '
1325
+ || CASE WHEN $4[1] IS NULL THEN 'no privileges' ELSE array_to_string($4, ', ') END
1326
+ || ' on sequence '|| quote_ident($1) || '.' || quote_ident($2)
1327
+ );
1328
+ $$ LANGUAGE SQL;
1329
+
1330
+ -- sequence_privs_are ( sequence, user, privileges[], description )
1331
+ CREATE OR REPLACE FUNCTION sequence_privs_are ( NAME, NAME, NAME[], TEXT )
1332
+ RETURNS TEXT AS $$
1333
+ DECLARE
1334
+ grants TEXT[] := _get_sequence_privs( $2, quote_ident($1) );
1335
+ BEGIN
1336
+ IF grants[1] = 'undefined_table' THEN
1337
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1338
+ ' Sequence ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
1339
+ );
1340
+ ELSIF grants[1] = 'undefined_role' THEN
1341
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1342
+ ' Role ' || quote_ident($2) || ' does not exist'
1343
+ );
1344
+ END IF;
1345
+ RETURN _assets_are('privileges', grants, $3, $4);
1346
+ END;
1347
+ $$ LANGUAGE plpgsql;
1348
+
1349
+ -- sequence_privs_are ( sequence, user, privileges[] )
1350
+ CREATE OR REPLACE FUNCTION sequence_privs_are ( NAME, NAME, NAME[] )
1351
+ RETURNS TEXT AS $$
1352
+ SELECT sequence_privs_are(
1353
+ $1, $2, $3,
1354
+ 'Role ' || quote_ident($2) || ' should be granted '
1355
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1356
+ || ' on sequence ' || quote_ident($1)
1357
+ );
1358
+ $$ LANGUAGE SQL;
1359
+
1360
+ CREATE OR REPLACE FUNCTION _get_ac_privs(NAME, TEXT)
1361
+ RETURNS TEXT[] AS $$
1362
+ DECLARE
1363
+ privs TEXT[] := ARRAY['INSERT', 'REFERENCES', 'SELECT', 'UPDATE'];
1364
+ grants TEXT[] := '{}';
1365
+ BEGIN
1366
+ FOR i IN 1..array_upper(privs, 1) LOOP
1367
+ BEGIN
1368
+ IF pg_catalog.has_any_column_privilege($1, $2, privs[i]) THEN
1369
+ grants := grants || privs[i];
1370
+ END IF;
1371
+ EXCEPTION WHEN undefined_table THEN
1372
+ -- Not a valid table name.
1373
+ RETURN '{undefined_table}';
1374
+ WHEN undefined_object THEN
1375
+ -- Not a valid role.
1376
+ RETURN '{undefined_role}';
1377
+ WHEN invalid_parameter_value THEN
1378
+ -- Not a valid permission on this version of PostgreSQL; ignore;
1379
+ END;
1380
+ END LOOP;
1381
+ RETURN grants;
1382
+ END;
1383
+ $$ LANGUAGE plpgsql;
1384
+
1385
+ -- any_column_privs_are ( schema, table, user, privileges[], description )
1386
+ CREATE OR REPLACE FUNCTION any_column_privs_are ( NAME, NAME, NAME, NAME[], TEXT )
1387
+ RETURNS TEXT AS $$
1388
+ DECLARE
1389
+ grants TEXT[] := _get_ac_privs( $3, quote_ident($1) || '.' || quote_ident($2) );
1390
+ BEGIN
1391
+ IF grants[1] = 'undefined_table' THEN
1392
+ RETURN ok(FALSE, $5) || E'\n' || diag(
1393
+ ' Table ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
1394
+ );
1395
+ ELSIF grants[1] = 'undefined_role' THEN
1396
+ RETURN ok(FALSE, $5) || E'\n' || diag(
1397
+ ' Role ' || quote_ident($3) || ' does not exist'
1398
+ );
1399
+ END IF;
1400
+ RETURN _assets_are('privileges', grants, $4, $5);
1401
+ END;
1402
+ $$ LANGUAGE plpgsql;
1403
+
1404
+ -- any_column_privs_are ( schema, table, user, privileges[] )
1405
+ CREATE OR REPLACE FUNCTION any_column_privs_are ( NAME, NAME, NAME, NAME[] )
1406
+ RETURNS TEXT AS $$
1407
+ SELECT any_column_privs_are(
1408
+ $1, $2, $3, $4,
1409
+ 'Role ' || quote_ident($3) || ' should be granted '
1410
+ || CASE WHEN $4[1] IS NULL THEN 'no privileges' ELSE array_to_string($4, ', ') END
1411
+ || ' on any column in '|| quote_ident($1) || '.' || quote_ident($2)
1412
+ );
1413
+ $$ LANGUAGE SQL;
1414
+
1415
+ -- any_column_privs_are ( table, user, privileges[], description )
1416
+ CREATE OR REPLACE FUNCTION any_column_privs_are ( NAME, NAME, NAME[], TEXT )
1417
+ RETURNS TEXT AS $$
1418
+ DECLARE
1419
+ grants TEXT[] := _get_ac_privs( $2, quote_ident($1) );
1420
+ BEGIN
1421
+ IF grants[1] = 'undefined_table' THEN
1422
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1423
+ ' Table ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
1424
+ );
1425
+ ELSIF grants[1] = 'undefined_role' THEN
1426
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1427
+ ' Role ' || quote_ident($2) || ' does not exist'
1428
+ );
1429
+ END IF;
1430
+ RETURN _assets_are('privileges', grants, $3, $4);
1431
+ END;
1432
+ $$ LANGUAGE plpgsql;
1433
+
1434
+ -- any_column_privs_are ( table, user, privileges[] )
1435
+ CREATE OR REPLACE FUNCTION any_column_privs_are ( NAME, NAME, NAME[] )
1436
+ RETURNS TEXT AS $$
1437
+ SELECT any_column_privs_are(
1438
+ $1, $2, $3,
1439
+ 'Role ' || quote_ident($2) || ' should be granted '
1440
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1441
+ || ' on any column in ' || quote_ident($1)
1442
+ );
1443
+ $$ LANGUAGE SQL;
1444
+
1445
+ CREATE OR REPLACE FUNCTION _get_col_privs(NAME, TEXT, NAME)
1446
+ RETURNS TEXT[] AS $$
1447
+ DECLARE
1448
+ privs TEXT[] := ARRAY['INSERT', 'REFERENCES', 'SELECT', 'UPDATE'];
1449
+ grants TEXT[] := '{}';
1450
+ BEGIN
1451
+ FOR i IN 1..array_upper(privs, 1) LOOP
1452
+ IF pg_catalog.has_column_privilege($1, $2, $3, privs[i]) THEN
1453
+ grants := grants || privs[i];
1454
+ END IF;
1455
+ END LOOP;
1456
+ RETURN grants;
1457
+ EXCEPTION
1458
+ -- Not a valid column name.
1459
+ WHEN undefined_column THEN RETURN '{undefined_column}';
1460
+ -- Not a valid table name.
1461
+ WHEN undefined_table THEN RETURN '{undefined_table}';
1462
+ -- Not a valid role.
1463
+ WHEN undefined_object THEN RETURN '{undefined_role}';
1464
+ END;
1465
+ $$ LANGUAGE plpgsql;
1466
+
1467
+ -- column_privs_are ( schema, table, column, user, privileges[], description )
1468
+ CREATE OR REPLACE FUNCTION column_privs_are ( NAME, NAME, NAME, NAME, NAME[], TEXT )
1469
+ RETURNS TEXT AS $$
1470
+ DECLARE
1471
+ grants TEXT[] := _get_col_privs( $4, quote_ident($1) || '.' || quote_ident($2), $3 );
1472
+ BEGIN
1473
+ IF grants[1] = 'undefined_column' THEN
1474
+ RETURN ok(FALSE, $6) || E'\n' || diag(
1475
+ ' Column ' || quote_ident($1) || '.' || quote_ident($2) || '.' || quote_ident($3)
1476
+ || ' does not exist'
1477
+ );
1478
+ ELSIF grants[1] = 'undefined_table' THEN
1479
+ RETURN ok(FALSE, $6) || E'\n' || diag(
1480
+ ' Table ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
1481
+ );
1482
+ ELSIF grants[1] = 'undefined_role' THEN
1483
+ RETURN ok(FALSE, $6) || E'\n' || diag(
1484
+ ' Role ' || quote_ident($4) || ' does not exist'
1485
+ );
1486
+ END IF;
1487
+ RETURN _assets_are('privileges', grants, $5, $6);
1488
+ END;
1489
+ $$ LANGUAGE plpgsql;
1490
+
1491
+ -- column_privs_are ( schema, table, column, user, privileges[] )
1492
+ CREATE OR REPLACE FUNCTION column_privs_are ( NAME, NAME, NAME, NAME, NAME[] )
1493
+ RETURNS TEXT AS $$
1494
+ SELECT column_privs_are(
1495
+ $1, $2, $3, $4, $5,
1496
+ 'Role ' || quote_ident($4) || ' should be granted '
1497
+ || CASE WHEN $5[1] IS NULL THEN 'no privileges' ELSE array_to_string($5, ', ') END
1498
+ || ' on column ' || quote_ident($1) || '.' || quote_ident($2) || '.' || quote_ident($3)
1499
+ );
1500
+ $$ LANGUAGE SQL;
1501
+
1502
+ -- column_privs_are ( table, column, user, privileges[], description )
1503
+ CREATE OR REPLACE FUNCTION column_privs_are ( NAME, NAME, NAME, NAME[], TEXT )
1504
+ RETURNS TEXT AS $$
1505
+ DECLARE
1506
+ grants TEXT[] := _get_col_privs( $3, quote_ident($1), $2 );
1507
+ BEGIN
1508
+ IF grants[1] = 'undefined_column' THEN
1509
+ RETURN ok(FALSE, $5) || E'\n' || diag(
1510
+ ' Column ' || quote_ident($1) || '.' || quote_ident($2) || ' does not exist'
1511
+ );
1512
+ ELSIF grants[1] = 'undefined_table' THEN
1513
+ RETURN ok(FALSE, $5) || E'\n' || diag(
1514
+ ' Table ' || quote_ident($1) || ' does not exist'
1515
+ );
1516
+ ELSIF grants[1] = 'undefined_role' THEN
1517
+ RETURN ok(FALSE, $5) || E'\n' || diag(
1518
+ ' Role ' || quote_ident($3) || ' does not exist'
1519
+ );
1520
+ END IF;
1521
+ RETURN _assets_are('privileges', grants, $4, $5);
1522
+ END;
1523
+ $$ LANGUAGE plpgsql;
1524
+
1525
+ -- column_privs_are ( table, column, user, privileges[] )
1526
+ CREATE OR REPLACE FUNCTION column_privs_are ( NAME, NAME, NAME, NAME[] )
1527
+ RETURNS TEXT AS $$
1528
+ SELECT column_privs_are(
1529
+ $1, $2, $3, $4,
1530
+ 'Role ' || quote_ident($3) || ' should be granted '
1531
+ || CASE WHEN $4[1] IS NULL THEN 'no privileges' ELSE array_to_string($4, ', ') END
1532
+ || ' on column ' || quote_ident($1) || '.' || quote_ident($2)
1533
+ );
1534
+ $$ LANGUAGE SQL;
1535
+
1536
+ CREATE OR REPLACE FUNCTION _get_fdw_privs (NAME, TEXT)
1537
+ RETURNS TEXT[] AS $$
1538
+ BEGIN
1539
+ IF pg_catalog.has_foreign_data_wrapper_privilege($1, $2, 'USAGE') THEN
1540
+ RETURN '{USAGE}';
1541
+ ELSE
1542
+ RETURN '{}';
1543
+ END IF;
1544
+ EXCEPTION WHEN undefined_object THEN
1545
+ -- Same error code for unknown user or fdw. So figure out which.
1546
+ RETURN CASE WHEN SQLERRM LIKE '%' || $1 || '%' THEN
1547
+ '{undefined_role}'
1548
+ ELSE
1549
+ '{undefined_fdw}'
1550
+ END;
1551
+ END;
1552
+ $$ LANGUAGE plpgsql;
1553
+
1554
+ -- fdw_privs_are ( fdw, user, privileges[], description )
1555
+ CREATE OR REPLACE FUNCTION fdw_privs_are ( NAME, NAME, NAME[], TEXT )
1556
+ RETURNS TEXT AS $$
1557
+ DECLARE
1558
+ grants TEXT[] := _get_fdw_privs( $2, quote_ident($1) );
1559
+ BEGIN
1560
+ IF grants[1] = 'undefined_fdw' THEN
1561
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1562
+ ' FDW ' || quote_ident($1) || ' does not exist'
1563
+ );
1564
+ ELSIF grants[1] = 'undefined_role' THEN
1565
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1566
+ ' Role ' || quote_ident($2) || ' does not exist'
1567
+ );
1568
+ END IF;
1569
+ RETURN _assets_are('privileges', grants, $3, $4);
1570
+ END;
1571
+ $$ LANGUAGE plpgsql;
1572
+
1573
+ -- fdw_privs_are ( fdw, user, privileges[] )
1574
+ CREATE OR REPLACE FUNCTION fdw_privs_are ( NAME, NAME, NAME[] )
1575
+ RETURNS TEXT AS $$
1576
+ SELECT fdw_privs_are(
1577
+ $1, $2, $3,
1578
+ 'Role ' || quote_ident($2) || ' should be granted '
1579
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1580
+ || ' on FDW ' || quote_ident($1)
1581
+ );
1582
+ $$ LANGUAGE SQL;
1583
+
1584
+ CREATE OR REPLACE FUNCTION _get_schema_privs(NAME, TEXT)
1585
+ RETURNS TEXT[] AS $$
1586
+ DECLARE
1587
+ privs TEXT[] := ARRAY['CREATE', 'USAGE'];
1588
+ grants TEXT[] := '{}';
1589
+ BEGIN
1590
+ FOR i IN 1..array_upper(privs, 1) LOOP
1591
+ IF pg_catalog.has_schema_privilege($1, $2, privs[i]) THEN
1592
+ grants := grants || privs[i];
1593
+ END IF;
1594
+ END LOOP;
1595
+ RETURN grants;
1596
+ EXCEPTION
1597
+ -- Not a valid schema name.
1598
+ WHEN invalid_schema_name THEN RETURN '{invalid_schema_name}';
1599
+ -- Not a valid role.
1600
+ WHEN undefined_object THEN RETURN '{undefined_role}';
1601
+ END;
1602
+ $$ LANGUAGE plpgsql;
1603
+
1604
+ CREATE OR REPLACE FUNCTION _get_server_privs (NAME, TEXT)
1605
+ RETURNS TEXT[] AS $$
1606
+ BEGIN
1607
+ IF pg_catalog.has_server_privilege($1, $2, 'USAGE') THEN
1608
+ RETURN '{USAGE}';
1609
+ ELSE
1610
+ RETURN '{}';
1611
+ END IF;
1612
+ EXCEPTION WHEN undefined_object THEN
1613
+ -- Same error code for unknown user or server. So figure out which.
1614
+ RETURN CASE WHEN SQLERRM LIKE '%' || $1 || '%' THEN
1615
+ '{undefined_role}'
1616
+ ELSE
1617
+ '{undefined_server}'
1618
+ END;
1619
+ END;
1620
+ $$ LANGUAGE plpgsql;
1621
+
1622
+ -- server_privs_are ( server, user, privileges[], description )
1623
+ CREATE OR REPLACE FUNCTION server_privs_are ( NAME, NAME, NAME[], TEXT )
1624
+ RETURNS TEXT AS $$
1625
+ DECLARE
1626
+ grants TEXT[] := _get_server_privs( $2, quote_ident($1) );
1627
+ BEGIN
1628
+ IF grants[1] = 'undefined_server' THEN
1629
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1630
+ ' Server ' || quote_ident($1) || ' does not exist'
1631
+ );
1632
+ ELSIF grants[1] = 'undefined_role' THEN
1633
+ RETURN ok(FALSE, $4) || E'\n' || diag(
1634
+ ' Role ' || quote_ident($2) || ' does not exist'
1635
+ );
1636
+ END IF;
1637
+ RETURN _assets_are('privileges', grants, $3, $4);
1638
+ END;
1639
+ $$ LANGUAGE plpgsql;
1640
+
1641
+ -- server_privs_are ( server, user, privileges[] )
1642
+ CREATE OR REPLACE FUNCTION server_privs_are ( NAME, NAME, NAME[] )
1643
+ RETURNS TEXT AS $$
1644
+ SELECT server_privs_are(
1645
+ $1, $2, $3,
1646
+ 'Role ' || quote_ident($2) || ' should be granted '
1647
+ || CASE WHEN $3[1] IS NULL THEN 'no privileges' ELSE array_to_string($3, ', ') END
1648
+ || ' on server ' || quote_ident($1)
1649
+ );
1650
+ $$ LANGUAGE SQL;