@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.
- package/LICENSE +21 -0
- package/README.md +6 -0
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/extension-contract.json +31 -0
- package/package.json +49 -0
- package/runtime/share/licenses/pgtap/README.md +153 -0
- package/runtime/share/postgresql/extension/pgtap--0.90.0--0.91.0.sql +29 -0
- package/runtime/share/postgresql/extension/pgtap--0.91.0--0.92.0.sql +1650 -0
- package/runtime/share/postgresql/extension/pgtap--0.92.0--0.93.0.sql +679 -0
- package/runtime/share/postgresql/extension/pgtap--0.93.0--0.94.0.sql +193 -0
- package/runtime/share/postgresql/extension/pgtap--0.94.0--0.95.0.sql +639 -0
- package/runtime/share/postgresql/extension/pgtap--0.95.0--0.96.0.sql +415 -0
- package/runtime/share/postgresql/extension/pgtap--0.96.0--0.97.0.sql +375 -0
- package/runtime/share/postgresql/extension/pgtap--0.97.0--0.98.0.sql +441 -0
- package/runtime/share/postgresql/extension/pgtap--0.98.0--0.99.0.sql +209 -0
- package/runtime/share/postgresql/extension/pgtap--0.99.0--1.0.0.sql +737 -0
- package/runtime/share/postgresql/extension/pgtap--1.0.0--1.1.0.sql +186 -0
- package/runtime/share/postgresql/extension/pgtap--1.1.0--1.2.0.sql +889 -0
- package/runtime/share/postgresql/extension/pgtap--1.2.0--1.3.0.sql +495 -0
- package/runtime/share/postgresql/extension/pgtap--1.3.0--1.3.1.sql +229 -0
- package/runtime/share/postgresql/extension/pgtap--1.3.1--1.3.2.sql +34 -0
- package/runtime/share/postgresql/extension/pgtap--1.3.2--1.3.3.sql +42 -0
- package/runtime/share/postgresql/extension/pgtap--1.3.3--1.3.4.sql +89 -0
- package/runtime/share/postgresql/extension/pgtap--1.3.4--1.3.5.sql +1 -0
- package/runtime/share/postgresql/extension/pgtap--1.3.5.sql +11520 -0
- package/runtime/share/postgresql/extension/pgtap--unpackaged--0.91.0.sql +714 -0
- package/runtime/share/postgresql/extension/pgtap-core--1.3.5.sql +4390 -0
- package/runtime/share/postgresql/extension/pgtap-core.sql +4390 -0
- package/runtime/share/postgresql/extension/pgtap-schema--1.3.5.sql +6764 -0
- package/runtime/share/postgresql/extension/pgtap-schema.sql +6764 -0
- package/runtime/share/postgresql/extension/pgtap.control +6 -0
- package/runtime/share/postgresql/extension/pgtap.sql +11520 -0
- package/runtime/share/postgresql/extension/uninstall_pgtap.sql +1088 -0
- 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;
|