@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,4390 @@
|
|
|
1
|
+
|
|
2
|
+
-- This file defines pgTAP Core, a portable collection of assertion
|
|
3
|
+
-- functions for TAP-based unit testing on PostgreSQL 9.1 or higher. It is
|
|
4
|
+
-- distributed under the revised FreeBSD license. The home page for the pgTAP
|
|
5
|
+
-- project is:
|
|
6
|
+
|
|
7
|
+
--
|
|
8
|
+
-- https://pgtap.org/
|
|
9
|
+
--
|
|
10
|
+
|
|
11
|
+
CREATE OR REPLACE FUNCTION pg_version()
|
|
12
|
+
RETURNS text AS 'SELECT current_setting(''server_version'')'
|
|
13
|
+
LANGUAGE SQL IMMUTABLE;
|
|
14
|
+
|
|
15
|
+
CREATE OR REPLACE FUNCTION pg_version_num()
|
|
16
|
+
RETURNS integer AS $$
|
|
17
|
+
SELECT current_setting('server_version_num')::integer;
|
|
18
|
+
$$ LANGUAGE SQL IMMUTABLE;
|
|
19
|
+
|
|
20
|
+
CREATE OR REPLACE FUNCTION pgtap_version()
|
|
21
|
+
RETURNS NUMERIC AS 'SELECT 1.3;'
|
|
22
|
+
LANGUAGE SQL IMMUTABLE;
|
|
23
|
+
|
|
24
|
+
CREATE OR REPLACE FUNCTION plan( integer )
|
|
25
|
+
RETURNS TEXT AS $$
|
|
26
|
+
DECLARE
|
|
27
|
+
rcount INTEGER;
|
|
28
|
+
BEGIN
|
|
29
|
+
BEGIN
|
|
30
|
+
EXECUTE '
|
|
31
|
+
CREATE TEMP SEQUENCE __tcache___id_seq;
|
|
32
|
+
CREATE TEMP TABLE __tcache__ (
|
|
33
|
+
id INTEGER NOT NULL DEFAULT nextval(''__tcache___id_seq''),
|
|
34
|
+
label TEXT NOT NULL,
|
|
35
|
+
value INTEGER NOT NULL,
|
|
36
|
+
note TEXT NOT NULL DEFAULT ''''
|
|
37
|
+
);
|
|
38
|
+
CREATE UNIQUE INDEX __tcache___key ON __tcache__(id);
|
|
39
|
+
GRANT ALL ON TABLE __tcache__ TO PUBLIC;
|
|
40
|
+
GRANT ALL ON TABLE __tcache___id_seq TO PUBLIC;
|
|
41
|
+
|
|
42
|
+
CREATE TEMP SEQUENCE __tresults___numb_seq;
|
|
43
|
+
GRANT ALL ON TABLE __tresults___numb_seq TO PUBLIC;
|
|
44
|
+
';
|
|
45
|
+
|
|
46
|
+
EXCEPTION WHEN duplicate_table THEN
|
|
47
|
+
-- Raise an exception if there's already a plan.
|
|
48
|
+
EXECUTE 'SELECT TRUE FROM __tcache__ WHERE label = ''plan''';
|
|
49
|
+
GET DIAGNOSTICS rcount = ROW_COUNT;
|
|
50
|
+
IF rcount > 0 THEN
|
|
51
|
+
RAISE EXCEPTION 'You tried to plan twice!';
|
|
52
|
+
END IF;
|
|
53
|
+
END;
|
|
54
|
+
|
|
55
|
+
-- Save the plan and return.
|
|
56
|
+
PERFORM _set('plan', $1 );
|
|
57
|
+
PERFORM _set('failed', 0 );
|
|
58
|
+
RETURN '1..' || $1;
|
|
59
|
+
END;
|
|
60
|
+
$$ LANGUAGE plpgsql strict;
|
|
61
|
+
|
|
62
|
+
CREATE OR REPLACE FUNCTION no_plan()
|
|
63
|
+
RETURNS SETOF boolean AS $$
|
|
64
|
+
BEGIN
|
|
65
|
+
PERFORM plan(0);
|
|
66
|
+
RETURN;
|
|
67
|
+
END;
|
|
68
|
+
$$ LANGUAGE plpgsql strict;
|
|
69
|
+
|
|
70
|
+
CREATE OR REPLACE FUNCTION _get ( text )
|
|
71
|
+
RETURNS integer AS $$
|
|
72
|
+
DECLARE
|
|
73
|
+
ret integer;
|
|
74
|
+
BEGIN
|
|
75
|
+
EXECUTE 'SELECT value FROM __tcache__ WHERE label = ' || quote_literal($1) || ' LIMIT 1' INTO ret;
|
|
76
|
+
RETURN ret;
|
|
77
|
+
END;
|
|
78
|
+
$$ LANGUAGE plpgsql strict;
|
|
79
|
+
|
|
80
|
+
CREATE OR REPLACE FUNCTION _get_latest ( text )
|
|
81
|
+
RETURNS integer[] AS $$
|
|
82
|
+
DECLARE
|
|
83
|
+
ret integer[];
|
|
84
|
+
BEGIN
|
|
85
|
+
EXECUTE 'SELECT ARRAY[id, value] FROM __tcache__ WHERE label = ' ||
|
|
86
|
+
quote_literal($1) || ' AND id = (SELECT MAX(id) FROM __tcache__ WHERE label = ' ||
|
|
87
|
+
quote_literal($1) || ') LIMIT 1' INTO ret;
|
|
88
|
+
RETURN ret;
|
|
89
|
+
EXCEPTION WHEN undefined_table THEN
|
|
90
|
+
RAISE EXCEPTION 'You tried to run a test without a plan! Gotta have a plan';
|
|
91
|
+
END;
|
|
92
|
+
$$ LANGUAGE plpgsql strict;
|
|
93
|
+
|
|
94
|
+
CREATE OR REPLACE FUNCTION _get_latest ( text, integer )
|
|
95
|
+
RETURNS integer AS $$
|
|
96
|
+
DECLARE
|
|
97
|
+
ret integer;
|
|
98
|
+
BEGIN
|
|
99
|
+
EXECUTE 'SELECT MAX(id) FROM __tcache__ WHERE label = ' ||
|
|
100
|
+
quote_literal($1) || ' AND value = ' || $2 INTO ret;
|
|
101
|
+
RETURN ret;
|
|
102
|
+
END;
|
|
103
|
+
$$ LANGUAGE plpgsql strict;
|
|
104
|
+
|
|
105
|
+
CREATE OR REPLACE FUNCTION _get_note ( text )
|
|
106
|
+
RETURNS text AS $$
|
|
107
|
+
DECLARE
|
|
108
|
+
ret text;
|
|
109
|
+
BEGIN
|
|
110
|
+
EXECUTE 'SELECT note FROM __tcache__ WHERE label = ' || quote_literal($1) || ' LIMIT 1' INTO ret;
|
|
111
|
+
RETURN ret;
|
|
112
|
+
END;
|
|
113
|
+
$$ LANGUAGE plpgsql strict;
|
|
114
|
+
|
|
115
|
+
CREATE OR REPLACE FUNCTION _get_note ( integer )
|
|
116
|
+
RETURNS text AS $$
|
|
117
|
+
DECLARE
|
|
118
|
+
ret text;
|
|
119
|
+
BEGIN
|
|
120
|
+
EXECUTE 'SELECT note FROM __tcache__ WHERE id = ' || $1 || ' LIMIT 1' INTO ret;
|
|
121
|
+
RETURN ret;
|
|
122
|
+
END;
|
|
123
|
+
$$ LANGUAGE plpgsql strict;
|
|
124
|
+
|
|
125
|
+
CREATE OR REPLACE FUNCTION _set ( text, integer, text )
|
|
126
|
+
RETURNS integer AS $$
|
|
127
|
+
DECLARE
|
|
128
|
+
rcount integer;
|
|
129
|
+
BEGIN
|
|
130
|
+
EXECUTE 'UPDATE __tcache__ SET value = ' || $2
|
|
131
|
+
|| CASE WHEN $3 IS NULL THEN '' ELSE ', note = ' || quote_literal($3) END
|
|
132
|
+
|| ' WHERE label = ' || quote_literal($1);
|
|
133
|
+
GET DIAGNOSTICS rcount = ROW_COUNT;
|
|
134
|
+
IF rcount = 0 THEN
|
|
135
|
+
RETURN _add( $1, $2, $3 );
|
|
136
|
+
END IF;
|
|
137
|
+
RETURN $2;
|
|
138
|
+
END;
|
|
139
|
+
$$ LANGUAGE plpgsql;
|
|
140
|
+
|
|
141
|
+
CREATE OR REPLACE FUNCTION _set ( text, integer )
|
|
142
|
+
RETURNS integer AS $$
|
|
143
|
+
SELECT _set($1, $2, '')
|
|
144
|
+
$$ LANGUAGE SQL;
|
|
145
|
+
|
|
146
|
+
CREATE OR REPLACE FUNCTION _set ( integer, integer )
|
|
147
|
+
RETURNS integer AS $$
|
|
148
|
+
BEGIN
|
|
149
|
+
EXECUTE 'UPDATE __tcache__ SET value = ' || $2
|
|
150
|
+
|| ' WHERE id = ' || $1;
|
|
151
|
+
RETURN $2;
|
|
152
|
+
END;
|
|
153
|
+
$$ LANGUAGE plpgsql;
|
|
154
|
+
|
|
155
|
+
CREATE OR REPLACE FUNCTION _add ( text, integer, text )
|
|
156
|
+
RETURNS integer AS $$
|
|
157
|
+
BEGIN
|
|
158
|
+
EXECUTE 'INSERT INTO __tcache__ (label, value, note) values (' ||
|
|
159
|
+
quote_literal($1) || ', ' || $2 || ', ' || quote_literal(COALESCE($3, '')) || ')';
|
|
160
|
+
RETURN $2;
|
|
161
|
+
END;
|
|
162
|
+
$$ LANGUAGE plpgsql;
|
|
163
|
+
|
|
164
|
+
CREATE OR REPLACE FUNCTION _add ( text, integer )
|
|
165
|
+
RETURNS integer AS $$
|
|
166
|
+
SELECT _add($1, $2, '')
|
|
167
|
+
$$ LANGUAGE SQL;
|
|
168
|
+
|
|
169
|
+
CREATE OR REPLACE FUNCTION add_result ( bool, bool, text, text, text )
|
|
170
|
+
RETURNS integer AS $$
|
|
171
|
+
BEGIN
|
|
172
|
+
IF NOT $1 THEN PERFORM _set('failed', _get('failed') + 1); END IF;
|
|
173
|
+
RETURN nextval('__tresults___numb_seq');
|
|
174
|
+
END;
|
|
175
|
+
$$ LANGUAGE plpgsql;
|
|
176
|
+
|
|
177
|
+
CREATE OR REPLACE FUNCTION num_failed ()
|
|
178
|
+
RETURNS INTEGER AS $$
|
|
179
|
+
SELECT _get('failed');
|
|
180
|
+
$$ LANGUAGE SQL strict;
|
|
181
|
+
|
|
182
|
+
CREATE OR REPLACE FUNCTION _finish (INTEGER, INTEGER, INTEGER, BOOLEAN DEFAULT NULL)
|
|
183
|
+
RETURNS SETOF TEXT AS $$
|
|
184
|
+
DECLARE
|
|
185
|
+
curr_test ALIAS FOR $1;
|
|
186
|
+
exp_tests INTEGER := $2;
|
|
187
|
+
num_faild ALIAS FOR $3;
|
|
188
|
+
plural CHAR;
|
|
189
|
+
raise_ex ALIAS FOR $4;
|
|
190
|
+
BEGIN
|
|
191
|
+
plural := CASE exp_tests WHEN 1 THEN '' ELSE 's' END;
|
|
192
|
+
|
|
193
|
+
IF curr_test IS NULL THEN
|
|
194
|
+
RAISE EXCEPTION '# No tests run!';
|
|
195
|
+
END IF;
|
|
196
|
+
|
|
197
|
+
IF exp_tests = 0 OR exp_tests IS NULL THEN
|
|
198
|
+
-- No plan. Output one now.
|
|
199
|
+
exp_tests = curr_test;
|
|
200
|
+
RETURN NEXT '1..' || exp_tests;
|
|
201
|
+
END IF;
|
|
202
|
+
|
|
203
|
+
IF curr_test <> exp_tests THEN
|
|
204
|
+
RETURN NEXT diag(
|
|
205
|
+
'Looks like you planned ' || exp_tests || ' test' ||
|
|
206
|
+
plural || ' but ran ' || curr_test
|
|
207
|
+
);
|
|
208
|
+
ELSIF num_faild > 0 THEN
|
|
209
|
+
IF raise_ex THEN
|
|
210
|
+
RAISE EXCEPTION '% test% failed of %', num_faild, CASE num_faild WHEN 1 THEN '' ELSE 's' END, exp_tests;
|
|
211
|
+
END IF;
|
|
212
|
+
RETURN NEXT diag(
|
|
213
|
+
'Looks like you failed ' || num_faild || ' test' ||
|
|
214
|
+
CASE num_faild WHEN 1 THEN '' ELSE 's' END
|
|
215
|
+
|| ' of ' || exp_tests
|
|
216
|
+
);
|
|
217
|
+
ELSE
|
|
218
|
+
|
|
219
|
+
END IF;
|
|
220
|
+
RETURN;
|
|
221
|
+
END;
|
|
222
|
+
$$ LANGUAGE plpgsql;
|
|
223
|
+
|
|
224
|
+
CREATE OR REPLACE FUNCTION finish (exception_on_failure BOOLEAN DEFAULT NULL)
|
|
225
|
+
RETURNS SETOF TEXT AS $$
|
|
226
|
+
SELECT * FROM _finish(
|
|
227
|
+
_get('curr_test'),
|
|
228
|
+
_get('plan'),
|
|
229
|
+
num_failed(),
|
|
230
|
+
$1
|
|
231
|
+
);
|
|
232
|
+
$$ LANGUAGE sql;
|
|
233
|
+
|
|
234
|
+
CREATE OR REPLACE FUNCTION diag ( msg text )
|
|
235
|
+
RETURNS TEXT AS $$
|
|
236
|
+
SELECT '# ' || replace(
|
|
237
|
+
replace(
|
|
238
|
+
replace( $1, E'\r\n', E'\n# ' ),
|
|
239
|
+
E'\n',
|
|
240
|
+
E'\n# '
|
|
241
|
+
),
|
|
242
|
+
E'\r',
|
|
243
|
+
E'\n# '
|
|
244
|
+
);
|
|
245
|
+
$$ LANGUAGE sql strict;
|
|
246
|
+
|
|
247
|
+
CREATE OR REPLACE FUNCTION diag ( msg anyelement )
|
|
248
|
+
RETURNS TEXT AS $$
|
|
249
|
+
SELECT diag($1::text);
|
|
250
|
+
$$ LANGUAGE sql;
|
|
251
|
+
|
|
252
|
+
CREATE OR REPLACE FUNCTION diag( VARIADIC text[] )
|
|
253
|
+
RETURNS TEXT AS $$
|
|
254
|
+
SELECT diag(array_to_string($1, ''));
|
|
255
|
+
$$ LANGUAGE sql;
|
|
256
|
+
|
|
257
|
+
CREATE OR REPLACE FUNCTION diag( VARIADIC anyarray )
|
|
258
|
+
RETURNS TEXT AS $$
|
|
259
|
+
SELECT diag(array_to_string($1, ''));
|
|
260
|
+
$$ LANGUAGE sql;
|
|
261
|
+
|
|
262
|
+
CREATE OR REPLACE FUNCTION ok ( boolean, text )
|
|
263
|
+
RETURNS TEXT AS $$
|
|
264
|
+
DECLARE
|
|
265
|
+
aok ALIAS FOR $1;
|
|
266
|
+
descr text := $2;
|
|
267
|
+
test_num INTEGER;
|
|
268
|
+
todo_why TEXT;
|
|
269
|
+
ok BOOL;
|
|
270
|
+
BEGIN
|
|
271
|
+
todo_why := _todo();
|
|
272
|
+
ok := CASE
|
|
273
|
+
WHEN aok = TRUE THEN aok
|
|
274
|
+
WHEN todo_why IS NULL THEN COALESCE(aok, false)
|
|
275
|
+
ELSE TRUE
|
|
276
|
+
END;
|
|
277
|
+
IF _get('plan') IS NULL THEN
|
|
278
|
+
RAISE EXCEPTION 'You tried to run a test without a plan! Gotta have a plan';
|
|
279
|
+
END IF;
|
|
280
|
+
|
|
281
|
+
test_num := add_result(
|
|
282
|
+
ok,
|
|
283
|
+
COALESCE(aok, false),
|
|
284
|
+
descr,
|
|
285
|
+
CASE WHEN todo_why IS NULL THEN '' ELSE 'todo' END,
|
|
286
|
+
COALESCE(todo_why, '')
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
RETURN (CASE aok WHEN TRUE THEN '' ELSE 'not ' END)
|
|
290
|
+
|| 'ok ' || _set( 'curr_test', test_num )
|
|
291
|
+
|| CASE descr WHEN '' THEN '' ELSE COALESCE( ' - ' || substr(diag( descr ), 3), '' ) END
|
|
292
|
+
|| COALESCE( ' ' || diag( 'TODO ' || todo_why ), '')
|
|
293
|
+
|| CASE aok WHEN TRUE THEN '' ELSE E'\n' ||
|
|
294
|
+
diag('Failed ' ||
|
|
295
|
+
CASE WHEN todo_why IS NULL THEN '' ELSE '(TODO) ' END ||
|
|
296
|
+
'test ' || test_num ||
|
|
297
|
+
CASE descr WHEN '' THEN '' ELSE COALESCE(': "' || descr || '"', '') END ) ||
|
|
298
|
+
CASE WHEN aok IS NULL THEN E'\n' || diag(' (test result was NULL)') ELSE '' END
|
|
299
|
+
END;
|
|
300
|
+
END;
|
|
301
|
+
$$ LANGUAGE plpgsql;
|
|
302
|
+
|
|
303
|
+
CREATE OR REPLACE FUNCTION ok ( boolean )
|
|
304
|
+
RETURNS TEXT AS $$
|
|
305
|
+
SELECT ok( $1, NULL );
|
|
306
|
+
$$ LANGUAGE SQL;
|
|
307
|
+
|
|
308
|
+
CREATE OR REPLACE FUNCTION is (anyelement, anyelement, text)
|
|
309
|
+
RETURNS TEXT AS $$
|
|
310
|
+
DECLARE
|
|
311
|
+
result BOOLEAN;
|
|
312
|
+
output TEXT;
|
|
313
|
+
BEGIN
|
|
314
|
+
-- Would prefer $1 IS NOT DISTINCT FROM, but that's not supported by 8.1.
|
|
315
|
+
result := NOT $1 IS DISTINCT FROM $2;
|
|
316
|
+
output := ok( result, $3 );
|
|
317
|
+
RETURN output || CASE result WHEN TRUE THEN '' ELSE E'\n' || diag(
|
|
318
|
+
' have: ' || CASE WHEN $1 IS NULL THEN 'NULL' ELSE $1::text END ||
|
|
319
|
+
E'\n want: ' || CASE WHEN $2 IS NULL THEN 'NULL' ELSE $2::text END
|
|
320
|
+
) END;
|
|
321
|
+
END;
|
|
322
|
+
$$ LANGUAGE plpgsql;
|
|
323
|
+
|
|
324
|
+
CREATE OR REPLACE FUNCTION is (anyelement, anyelement)
|
|
325
|
+
RETURNS TEXT AS $$
|
|
326
|
+
SELECT is( $1, $2, NULL);
|
|
327
|
+
$$ LANGUAGE SQL;
|
|
328
|
+
|
|
329
|
+
CREATE OR REPLACE FUNCTION isnt (anyelement, anyelement, text)
|
|
330
|
+
RETURNS TEXT AS $$
|
|
331
|
+
DECLARE
|
|
332
|
+
result BOOLEAN;
|
|
333
|
+
output TEXT;
|
|
334
|
+
BEGIN
|
|
335
|
+
result := $1 IS DISTINCT FROM $2;
|
|
336
|
+
output := ok( result, $3 );
|
|
337
|
+
RETURN output || CASE result WHEN TRUE THEN '' ELSE E'\n' || diag(
|
|
338
|
+
' have: ' || COALESCE( $1::text, 'NULL' ) ||
|
|
339
|
+
E'\n want: anything else'
|
|
340
|
+
) END;
|
|
341
|
+
END;
|
|
342
|
+
$$ LANGUAGE plpgsql;
|
|
343
|
+
|
|
344
|
+
CREATE OR REPLACE FUNCTION isnt (anyelement, anyelement)
|
|
345
|
+
RETURNS TEXT AS $$
|
|
346
|
+
SELECT isnt( $1, $2, NULL);
|
|
347
|
+
$$ LANGUAGE SQL;
|
|
348
|
+
|
|
349
|
+
CREATE OR REPLACE FUNCTION _alike ( BOOLEAN, ANYELEMENT, TEXT, TEXT )
|
|
350
|
+
RETURNS TEXT AS $$
|
|
351
|
+
DECLARE
|
|
352
|
+
result ALIAS FOR $1;
|
|
353
|
+
got ALIAS FOR $2;
|
|
354
|
+
rx ALIAS FOR $3;
|
|
355
|
+
descr ALIAS FOR $4;
|
|
356
|
+
output TEXT;
|
|
357
|
+
BEGIN
|
|
358
|
+
output := ok( result, descr );
|
|
359
|
+
RETURN output || CASE result WHEN TRUE THEN '' ELSE E'\n' || diag(
|
|
360
|
+
' ' || COALESCE( quote_literal(got), 'NULL' ) ||
|
|
361
|
+
E'\n doesn''t match: ' || COALESCE( quote_literal(rx), 'NULL' )
|
|
362
|
+
) END;
|
|
363
|
+
END;
|
|
364
|
+
$$ LANGUAGE plpgsql;
|
|
365
|
+
|
|
366
|
+
CREATE OR REPLACE FUNCTION matches ( anyelement, text, text )
|
|
367
|
+
RETURNS TEXT AS $$
|
|
368
|
+
SELECT _alike( $1 ~ $2, $1, $2, $3 );
|
|
369
|
+
$$ LANGUAGE SQL;
|
|
370
|
+
|
|
371
|
+
CREATE OR REPLACE FUNCTION matches ( anyelement, text )
|
|
372
|
+
RETURNS TEXT AS $$
|
|
373
|
+
SELECT _alike( $1 ~ $2, $1, $2, NULL );
|
|
374
|
+
$$ LANGUAGE SQL;
|
|
375
|
+
|
|
376
|
+
CREATE OR REPLACE FUNCTION imatches ( anyelement, text, text )
|
|
377
|
+
RETURNS TEXT AS $$
|
|
378
|
+
SELECT _alike( $1 ~* $2, $1, $2, $3 );
|
|
379
|
+
$$ LANGUAGE SQL;
|
|
380
|
+
|
|
381
|
+
CREATE OR REPLACE FUNCTION imatches ( anyelement, text )
|
|
382
|
+
RETURNS TEXT AS $$
|
|
383
|
+
SELECT _alike( $1 ~* $2, $1, $2, NULL );
|
|
384
|
+
$$ LANGUAGE SQL;
|
|
385
|
+
|
|
386
|
+
CREATE OR REPLACE FUNCTION alike ( anyelement, text, text )
|
|
387
|
+
RETURNS TEXT AS $$
|
|
388
|
+
SELECT _alike( $1 ~~ $2, $1, $2, $3 );
|
|
389
|
+
$$ LANGUAGE SQL;
|
|
390
|
+
|
|
391
|
+
CREATE OR REPLACE FUNCTION alike ( anyelement, text )
|
|
392
|
+
RETURNS TEXT AS $$
|
|
393
|
+
SELECT _alike( $1 ~~ $2, $1, $2, NULL );
|
|
394
|
+
$$ LANGUAGE SQL;
|
|
395
|
+
|
|
396
|
+
CREATE OR REPLACE FUNCTION ialike ( anyelement, text, text )
|
|
397
|
+
RETURNS TEXT AS $$
|
|
398
|
+
SELECT _alike( $1 ~~* $2, $1, $2, $3 );
|
|
399
|
+
$$ LANGUAGE SQL;
|
|
400
|
+
|
|
401
|
+
CREATE OR REPLACE FUNCTION ialike ( anyelement, text )
|
|
402
|
+
RETURNS TEXT AS $$
|
|
403
|
+
SELECT _alike( $1 ~~* $2, $1, $2, NULL );
|
|
404
|
+
$$ LANGUAGE SQL;
|
|
405
|
+
|
|
406
|
+
CREATE OR REPLACE FUNCTION _unalike ( BOOLEAN, ANYELEMENT, TEXT, TEXT )
|
|
407
|
+
RETURNS TEXT AS $$
|
|
408
|
+
DECLARE
|
|
409
|
+
result ALIAS FOR $1;
|
|
410
|
+
got ALIAS FOR $2;
|
|
411
|
+
rx ALIAS FOR $3;
|
|
412
|
+
descr ALIAS FOR $4;
|
|
413
|
+
output TEXT;
|
|
414
|
+
BEGIN
|
|
415
|
+
output := ok( result, descr );
|
|
416
|
+
RETURN output || CASE result WHEN TRUE THEN '' ELSE E'\n' || diag(
|
|
417
|
+
' ' || COALESCE( quote_literal(got), 'NULL' ) ||
|
|
418
|
+
E'\n matches: ' || COALESCE( quote_literal(rx), 'NULL' )
|
|
419
|
+
) END;
|
|
420
|
+
END;
|
|
421
|
+
$$ LANGUAGE plpgsql;
|
|
422
|
+
|
|
423
|
+
CREATE OR REPLACE FUNCTION doesnt_match ( anyelement, text, text )
|
|
424
|
+
RETURNS TEXT AS $$
|
|
425
|
+
SELECT _unalike( $1 !~ $2, $1, $2, $3 );
|
|
426
|
+
$$ LANGUAGE SQL;
|
|
427
|
+
|
|
428
|
+
CREATE OR REPLACE FUNCTION doesnt_match ( anyelement, text )
|
|
429
|
+
RETURNS TEXT AS $$
|
|
430
|
+
SELECT _unalike( $1 !~ $2, $1, $2, NULL );
|
|
431
|
+
$$ LANGUAGE SQL;
|
|
432
|
+
|
|
433
|
+
CREATE OR REPLACE FUNCTION doesnt_imatch ( anyelement, text, text )
|
|
434
|
+
RETURNS TEXT AS $$
|
|
435
|
+
SELECT _unalike( $1 !~* $2, $1, $2, $3 );
|
|
436
|
+
$$ LANGUAGE SQL;
|
|
437
|
+
|
|
438
|
+
CREATE OR REPLACE FUNCTION doesnt_imatch ( anyelement, text )
|
|
439
|
+
RETURNS TEXT AS $$
|
|
440
|
+
SELECT _unalike( $1 !~* $2, $1, $2, NULL );
|
|
441
|
+
$$ LANGUAGE SQL;
|
|
442
|
+
|
|
443
|
+
CREATE OR REPLACE FUNCTION unalike ( anyelement, text, text )
|
|
444
|
+
RETURNS TEXT AS $$
|
|
445
|
+
SELECT _unalike( $1 !~~ $2, $1, $2, $3 );
|
|
446
|
+
$$ LANGUAGE SQL;
|
|
447
|
+
|
|
448
|
+
CREATE OR REPLACE FUNCTION unalike ( anyelement, text )
|
|
449
|
+
RETURNS TEXT AS $$
|
|
450
|
+
SELECT _unalike( $1 !~~ $2, $1, $2, NULL );
|
|
451
|
+
$$ LANGUAGE SQL;
|
|
452
|
+
|
|
453
|
+
CREATE OR REPLACE FUNCTION unialike ( anyelement, text, text )
|
|
454
|
+
RETURNS TEXT AS $$
|
|
455
|
+
SELECT _unalike( $1 !~~* $2, $1, $2, $3 );
|
|
456
|
+
$$ LANGUAGE SQL;
|
|
457
|
+
|
|
458
|
+
CREATE OR REPLACE FUNCTION unialike ( anyelement, text )
|
|
459
|
+
RETURNS TEXT AS $$
|
|
460
|
+
SELECT _unalike( $1 !~~* $2, $1, $2, NULL );
|
|
461
|
+
$$ LANGUAGE SQL;
|
|
462
|
+
|
|
463
|
+
CREATE OR REPLACE FUNCTION cmp_ok (anyelement, text, anyelement, text)
|
|
464
|
+
RETURNS TEXT AS $$
|
|
465
|
+
DECLARE
|
|
466
|
+
have ALIAS FOR $1;
|
|
467
|
+
op ALIAS FOR $2;
|
|
468
|
+
want ALIAS FOR $3;
|
|
469
|
+
descr ALIAS FOR $4;
|
|
470
|
+
result BOOLEAN;
|
|
471
|
+
output TEXT;
|
|
472
|
+
BEGIN
|
|
473
|
+
EXECUTE 'SELECT ' ||
|
|
474
|
+
COALESCE(quote_literal( have ), 'NULL') || '::' || pg_typeof(have) || ' '
|
|
475
|
+
|| op || ' ' ||
|
|
476
|
+
COALESCE(quote_literal( want ), 'NULL') || '::' || pg_typeof(want)
|
|
477
|
+
INTO result;
|
|
478
|
+
output := ok( COALESCE(result, FALSE), descr );
|
|
479
|
+
RETURN output || CASE result WHEN TRUE THEN '' ELSE E'\n' || diag(
|
|
480
|
+
' ' || COALESCE( quote_literal(have), 'NULL' ) ||
|
|
481
|
+
E'\n ' || op ||
|
|
482
|
+
E'\n ' || COALESCE( quote_literal(want), 'NULL' )
|
|
483
|
+
) END;
|
|
484
|
+
END;
|
|
485
|
+
$$ LANGUAGE plpgsql;
|
|
486
|
+
|
|
487
|
+
CREATE OR REPLACE FUNCTION cmp_ok (anyelement, text, anyelement)
|
|
488
|
+
RETURNS TEXT AS $$
|
|
489
|
+
SELECT cmp_ok( $1, $2, $3, NULL );
|
|
490
|
+
$$ LANGUAGE sql;
|
|
491
|
+
|
|
492
|
+
CREATE OR REPLACE FUNCTION pass ( text )
|
|
493
|
+
RETURNS TEXT AS $$
|
|
494
|
+
SELECT ok( TRUE, $1 );
|
|
495
|
+
$$ LANGUAGE SQL;
|
|
496
|
+
|
|
497
|
+
CREATE OR REPLACE FUNCTION pass ()
|
|
498
|
+
RETURNS TEXT AS $$
|
|
499
|
+
SELECT ok( TRUE, NULL );
|
|
500
|
+
$$ LANGUAGE SQL;
|
|
501
|
+
|
|
502
|
+
CREATE OR REPLACE FUNCTION fail ( text )
|
|
503
|
+
RETURNS TEXT AS $$
|
|
504
|
+
SELECT ok( FALSE, $1 );
|
|
505
|
+
$$ LANGUAGE SQL;
|
|
506
|
+
|
|
507
|
+
CREATE OR REPLACE FUNCTION fail ()
|
|
508
|
+
RETURNS TEXT AS $$
|
|
509
|
+
SELECT ok( FALSE, NULL );
|
|
510
|
+
$$ LANGUAGE SQL;
|
|
511
|
+
|
|
512
|
+
CREATE OR REPLACE FUNCTION todo ( why text, how_many int )
|
|
513
|
+
RETURNS SETOF BOOLEAN AS $$
|
|
514
|
+
BEGIN
|
|
515
|
+
PERFORM _add('todo', COALESCE(how_many, 1), COALESCE(why, ''));
|
|
516
|
+
RETURN;
|
|
517
|
+
END;
|
|
518
|
+
$$ LANGUAGE plpgsql;
|
|
519
|
+
|
|
520
|
+
CREATE OR REPLACE FUNCTION todo ( how_many int, why text )
|
|
521
|
+
RETURNS SETOF BOOLEAN AS $$
|
|
522
|
+
BEGIN
|
|
523
|
+
PERFORM _add('todo', COALESCE(how_many, 1), COALESCE(why, ''));
|
|
524
|
+
RETURN;
|
|
525
|
+
END;
|
|
526
|
+
$$ LANGUAGE plpgsql;
|
|
527
|
+
|
|
528
|
+
CREATE OR REPLACE FUNCTION todo ( why text )
|
|
529
|
+
RETURNS SETOF BOOLEAN AS $$
|
|
530
|
+
BEGIN
|
|
531
|
+
PERFORM _add('todo', 1, COALESCE(why, ''));
|
|
532
|
+
RETURN;
|
|
533
|
+
END;
|
|
534
|
+
$$ LANGUAGE plpgsql;
|
|
535
|
+
|
|
536
|
+
CREATE OR REPLACE FUNCTION todo ( how_many int )
|
|
537
|
+
RETURNS SETOF BOOLEAN AS $$
|
|
538
|
+
BEGIN
|
|
539
|
+
PERFORM _add('todo', COALESCE(how_many, 1), '');
|
|
540
|
+
RETURN;
|
|
541
|
+
END;
|
|
542
|
+
$$ LANGUAGE plpgsql;
|
|
543
|
+
|
|
544
|
+
CREATE OR REPLACE FUNCTION todo_start (text)
|
|
545
|
+
RETURNS SETOF BOOLEAN AS $$
|
|
546
|
+
BEGIN
|
|
547
|
+
PERFORM _add('todo', -1, COALESCE($1, ''));
|
|
548
|
+
RETURN;
|
|
549
|
+
END;
|
|
550
|
+
$$ LANGUAGE plpgsql;
|
|
551
|
+
|
|
552
|
+
CREATE OR REPLACE FUNCTION todo_start ()
|
|
553
|
+
RETURNS SETOF BOOLEAN AS $$
|
|
554
|
+
BEGIN
|
|
555
|
+
PERFORM _add('todo', -1, '');
|
|
556
|
+
RETURN;
|
|
557
|
+
END;
|
|
558
|
+
$$ LANGUAGE plpgsql;
|
|
559
|
+
|
|
560
|
+
CREATE OR REPLACE FUNCTION in_todo ()
|
|
561
|
+
RETURNS BOOLEAN AS $$
|
|
562
|
+
DECLARE
|
|
563
|
+
todos integer;
|
|
564
|
+
BEGIN
|
|
565
|
+
todos := _get('todo');
|
|
566
|
+
RETURN CASE WHEN todos IS NULL THEN FALSE ELSE TRUE END;
|
|
567
|
+
END;
|
|
568
|
+
$$ LANGUAGE plpgsql;
|
|
569
|
+
|
|
570
|
+
CREATE OR REPLACE FUNCTION todo_end ()
|
|
571
|
+
RETURNS SETOF BOOLEAN AS $$
|
|
572
|
+
DECLARE
|
|
573
|
+
id integer;
|
|
574
|
+
BEGIN
|
|
575
|
+
id := _get_latest( 'todo', -1 );
|
|
576
|
+
IF id IS NULL THEN
|
|
577
|
+
RAISE EXCEPTION 'todo_end() called without todo_start()';
|
|
578
|
+
END IF;
|
|
579
|
+
EXECUTE 'DELETE FROM __tcache__ WHERE id = ' || id;
|
|
580
|
+
RETURN;
|
|
581
|
+
END;
|
|
582
|
+
$$ LANGUAGE plpgsql;
|
|
583
|
+
|
|
584
|
+
CREATE OR REPLACE FUNCTION _todo()
|
|
585
|
+
RETURNS TEXT AS $$
|
|
586
|
+
DECLARE
|
|
587
|
+
todos INT[];
|
|
588
|
+
note text;
|
|
589
|
+
BEGIN
|
|
590
|
+
-- Get the latest id and value, because todo() might have been called
|
|
591
|
+
-- again before the todos ran out for the first call to todo(). This
|
|
592
|
+
-- allows them to nest.
|
|
593
|
+
todos := _get_latest('todo');
|
|
594
|
+
IF todos IS NULL THEN
|
|
595
|
+
-- No todos.
|
|
596
|
+
RETURN NULL;
|
|
597
|
+
END IF;
|
|
598
|
+
IF todos[2] = 0 THEN
|
|
599
|
+
-- Todos depleted. Clean up.
|
|
600
|
+
EXECUTE 'DELETE FROM __tcache__ WHERE id = ' || todos[1];
|
|
601
|
+
RETURN NULL;
|
|
602
|
+
END IF;
|
|
603
|
+
-- Decrement the count of counted todos and return the reason.
|
|
604
|
+
IF todos[2] <> -1 THEN
|
|
605
|
+
PERFORM _set(todos[1], todos[2] - 1);
|
|
606
|
+
END IF;
|
|
607
|
+
note := _get_note(todos[1]);
|
|
608
|
+
|
|
609
|
+
IF todos[2] = 1 THEN
|
|
610
|
+
-- This was the last todo, so delete the record.
|
|
611
|
+
EXECUTE 'DELETE FROM __tcache__ WHERE id = ' || todos[1];
|
|
612
|
+
END IF;
|
|
613
|
+
|
|
614
|
+
RETURN note;
|
|
615
|
+
END;
|
|
616
|
+
$$ LANGUAGE plpgsql;
|
|
617
|
+
|
|
618
|
+
CREATE OR REPLACE FUNCTION skip ( why text, how_many int )
|
|
619
|
+
RETURNS TEXT AS $$
|
|
620
|
+
DECLARE
|
|
621
|
+
output TEXT[];
|
|
622
|
+
BEGIN
|
|
623
|
+
output := '{}';
|
|
624
|
+
FOR i IN 1..how_many LOOP
|
|
625
|
+
output = array_append(
|
|
626
|
+
output,
|
|
627
|
+
ok( TRUE ) || ' ' || diag( 'SKIP' || COALESCE( ' ' || why, '') )
|
|
628
|
+
);
|
|
629
|
+
END LOOP;
|
|
630
|
+
RETURN array_to_string(output, E'\n');
|
|
631
|
+
END;
|
|
632
|
+
$$ LANGUAGE plpgsql;
|
|
633
|
+
|
|
634
|
+
CREATE OR REPLACE FUNCTION skip ( text )
|
|
635
|
+
RETURNS TEXT AS $$
|
|
636
|
+
SELECT ok( TRUE ) || ' ' || diag( 'SKIP' || COALESCE(' ' || $1, '') );
|
|
637
|
+
$$ LANGUAGE sql;
|
|
638
|
+
|
|
639
|
+
CREATE OR REPLACE FUNCTION skip( int, text )
|
|
640
|
+
RETURNS TEXT AS 'SELECT skip($2, $1)'
|
|
641
|
+
LANGUAGE sql;
|
|
642
|
+
|
|
643
|
+
CREATE OR REPLACE FUNCTION skip( int )
|
|
644
|
+
RETURNS TEXT AS 'SELECT skip(NULL, $1)'
|
|
645
|
+
LANGUAGE sql;
|
|
646
|
+
|
|
647
|
+
CREATE OR REPLACE FUNCTION _query( TEXT )
|
|
648
|
+
RETURNS TEXT AS $$
|
|
649
|
+
SELECT CASE
|
|
650
|
+
WHEN $1 LIKE '"%' OR $1 !~ '[[:space:]]' THEN 'EXECUTE ' || $1
|
|
651
|
+
ELSE $1
|
|
652
|
+
END;
|
|
653
|
+
$$ LANGUAGE SQL;
|
|
654
|
+
|
|
655
|
+
-- throws_ok ( sql, errcode, errmsg, description )
|
|
656
|
+
CREATE OR REPLACE FUNCTION throws_ok ( TEXT, CHAR(5), TEXT, TEXT )
|
|
657
|
+
RETURNS TEXT AS $$
|
|
658
|
+
DECLARE
|
|
659
|
+
query TEXT := _query($1);
|
|
660
|
+
errcode ALIAS FOR $2;
|
|
661
|
+
errmsg ALIAS FOR $3;
|
|
662
|
+
desctext ALIAS FOR $4;
|
|
663
|
+
descr TEXT;
|
|
664
|
+
BEGIN
|
|
665
|
+
descr := COALESCE(
|
|
666
|
+
desctext,
|
|
667
|
+
'threw ' || errcode || ': ' || errmsg,
|
|
668
|
+
'threw ' || errcode,
|
|
669
|
+
'threw ' || errmsg,
|
|
670
|
+
'threw an exception'
|
|
671
|
+
);
|
|
672
|
+
EXECUTE query;
|
|
673
|
+
RETURN ok( FALSE, descr ) || E'\n' || diag(
|
|
674
|
+
' caught: no exception' ||
|
|
675
|
+
E'\n wanted: ' || COALESCE( errcode, 'an exception' )
|
|
676
|
+
);
|
|
677
|
+
EXCEPTION WHEN OTHERS THEN
|
|
678
|
+
IF (errcode IS NULL OR SQLSTATE = errcode)
|
|
679
|
+
AND ( errmsg IS NULL OR SQLERRM = errmsg)
|
|
680
|
+
THEN
|
|
681
|
+
-- The expected errcode and/or message was thrown.
|
|
682
|
+
RETURN ok( TRUE, descr );
|
|
683
|
+
ELSE
|
|
684
|
+
-- This was not the expected errcode or errmsg.
|
|
685
|
+
RETURN ok( FALSE, descr ) || E'\n' || diag(
|
|
686
|
+
' caught: ' || SQLSTATE || ': ' || SQLERRM ||
|
|
687
|
+
E'\n wanted: ' || COALESCE( errcode, 'an exception' ) ||
|
|
688
|
+
COALESCE( ': ' || errmsg, '')
|
|
689
|
+
);
|
|
690
|
+
END IF;
|
|
691
|
+
END;
|
|
692
|
+
$$ LANGUAGE plpgsql;
|
|
693
|
+
|
|
694
|
+
-- throws_ok ( sql, errcode, errmsg )
|
|
695
|
+
-- throws_ok ( sql, errmsg, description )
|
|
696
|
+
CREATE OR REPLACE FUNCTION throws_ok ( TEXT, TEXT, TEXT )
|
|
697
|
+
RETURNS TEXT AS $$
|
|
698
|
+
BEGIN
|
|
699
|
+
IF octet_length($2) = 5 THEN
|
|
700
|
+
RETURN throws_ok( $1, $2::char(5), $3, NULL );
|
|
701
|
+
ELSE
|
|
702
|
+
RETURN throws_ok( $1, NULL, $2, $3 );
|
|
703
|
+
END IF;
|
|
704
|
+
END;
|
|
705
|
+
$$ LANGUAGE plpgsql;
|
|
706
|
+
|
|
707
|
+
-- throws_ok ( query, errcode )
|
|
708
|
+
-- throws_ok ( query, errmsg )
|
|
709
|
+
CREATE OR REPLACE FUNCTION throws_ok ( TEXT, TEXT )
|
|
710
|
+
RETURNS TEXT AS $$
|
|
711
|
+
BEGIN
|
|
712
|
+
IF octet_length($2) = 5 THEN
|
|
713
|
+
RETURN throws_ok( $1, $2::char(5), NULL, NULL );
|
|
714
|
+
ELSE
|
|
715
|
+
RETURN throws_ok( $1, NULL, $2, NULL );
|
|
716
|
+
END IF;
|
|
717
|
+
END;
|
|
718
|
+
$$ LANGUAGE plpgsql;
|
|
719
|
+
|
|
720
|
+
-- throws_ok ( sql )
|
|
721
|
+
CREATE OR REPLACE FUNCTION throws_ok ( TEXT )
|
|
722
|
+
RETURNS TEXT AS $$
|
|
723
|
+
SELECT throws_ok( $1, NULL, NULL, NULL );
|
|
724
|
+
$$ LANGUAGE SQL;
|
|
725
|
+
|
|
726
|
+
-- Magically cast integer error codes.
|
|
727
|
+
-- throws_ok ( sql, errcode, errmsg, description )
|
|
728
|
+
CREATE OR REPLACE FUNCTION throws_ok ( TEXT, int4, TEXT, TEXT )
|
|
729
|
+
RETURNS TEXT AS $$
|
|
730
|
+
SELECT throws_ok( $1, $2::char(5), $3, $4 );
|
|
731
|
+
$$ LANGUAGE SQL;
|
|
732
|
+
|
|
733
|
+
-- throws_ok ( sql, errcode, errmsg )
|
|
734
|
+
CREATE OR REPLACE FUNCTION throws_ok ( TEXT, int4, TEXT )
|
|
735
|
+
RETURNS TEXT AS $$
|
|
736
|
+
SELECT throws_ok( $1, $2::char(5), $3, NULL );
|
|
737
|
+
$$ LANGUAGE SQL;
|
|
738
|
+
|
|
739
|
+
-- throws_ok ( sql, errcode )
|
|
740
|
+
CREATE OR REPLACE FUNCTION throws_ok ( TEXT, int4 )
|
|
741
|
+
RETURNS TEXT AS $$
|
|
742
|
+
SELECT throws_ok( $1, $2::char(5), NULL, NULL );
|
|
743
|
+
$$ LANGUAGE SQL;
|
|
744
|
+
|
|
745
|
+
CREATE OR REPLACE FUNCTION _error_diag( TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT )
|
|
746
|
+
RETURNS TEXT AS $$
|
|
747
|
+
SELECT COALESCE(
|
|
748
|
+
COALESCE( NULLIF($1, '') || ': ', '' ) || COALESCE( NULLIF($2, ''), '' ),
|
|
749
|
+
'NO ERROR FOUND'
|
|
750
|
+
)
|
|
751
|
+
|| COALESCE(E'\n DETAIL: ' || nullif($3, ''), '')
|
|
752
|
+
|| COALESCE(E'\n HINT: ' || nullif($4, ''), '')
|
|
753
|
+
|| COALESCE(E'\n SCHEMA: ' || nullif($6, ''), '')
|
|
754
|
+
|| COALESCE(E'\n TABLE: ' || nullif($7, ''), '')
|
|
755
|
+
|| COALESCE(E'\n COLUMN: ' || nullif($8, ''), '')
|
|
756
|
+
|| COALESCE(E'\n CONSTRAINT: ' || nullif($9, ''), '')
|
|
757
|
+
|| COALESCE(E'\n TYPE: ' || nullif($10, ''), '')
|
|
758
|
+
-- We need to manually indent all the context lines
|
|
759
|
+
|| COALESCE(E'\n CONTEXT:\n'
|
|
760
|
+
|| regexp_replace(NULLIF( $5, ''), '^', ' ', 'gn'
|
|
761
|
+
), '');
|
|
762
|
+
$$ LANGUAGE sql IMMUTABLE;
|
|
763
|
+
|
|
764
|
+
-- lives_ok( sql, description )
|
|
765
|
+
CREATE OR REPLACE FUNCTION lives_ok ( TEXT, TEXT )
|
|
766
|
+
RETURNS TEXT AS $$
|
|
767
|
+
DECLARE
|
|
768
|
+
code TEXT := _query($1);
|
|
769
|
+
descr ALIAS FOR $2;
|
|
770
|
+
detail text;
|
|
771
|
+
hint text;
|
|
772
|
+
context text;
|
|
773
|
+
schname text;
|
|
774
|
+
tabname text;
|
|
775
|
+
colname text;
|
|
776
|
+
chkname text;
|
|
777
|
+
typname text;
|
|
778
|
+
BEGIN
|
|
779
|
+
EXECUTE code;
|
|
780
|
+
RETURN ok( TRUE, descr );
|
|
781
|
+
EXCEPTION WHEN OTHERS THEN
|
|
782
|
+
-- There should have been no exception.
|
|
783
|
+
RETURN ok( FALSE, descr ) || E'\n' || diag(
|
|
784
|
+
' died: ' || _error_diag(SQLSTATE, SQLERRM, detail, hint, context, schname, tabname, colname, chkname, typname)
|
|
785
|
+
);
|
|
786
|
+
END;
|
|
787
|
+
$$ LANGUAGE plpgsql;
|
|
788
|
+
|
|
789
|
+
-- lives_ok( sql )
|
|
790
|
+
CREATE OR REPLACE FUNCTION lives_ok ( TEXT )
|
|
791
|
+
RETURNS TEXT AS $$
|
|
792
|
+
SELECT lives_ok( $1, NULL );
|
|
793
|
+
$$ LANGUAGE SQL;
|
|
794
|
+
|
|
795
|
+
-- performs_ok ( sql, milliseconds, description )
|
|
796
|
+
CREATE OR REPLACE FUNCTION performs_ok ( TEXT, NUMERIC, TEXT )
|
|
797
|
+
RETURNS TEXT AS $$
|
|
798
|
+
DECLARE
|
|
799
|
+
query TEXT := _query($1);
|
|
800
|
+
max_time ALIAS FOR $2;
|
|
801
|
+
descr ALIAS FOR $3;
|
|
802
|
+
starts_at TEXT;
|
|
803
|
+
act_time NUMERIC;
|
|
804
|
+
BEGIN
|
|
805
|
+
starts_at := timeofday();
|
|
806
|
+
EXECUTE query;
|
|
807
|
+
act_time := extract( millisecond from timeofday()::timestamptz - starts_at::timestamptz);
|
|
808
|
+
IF act_time < max_time THEN RETURN ok(TRUE, descr); END IF;
|
|
809
|
+
RETURN ok( FALSE, descr ) || E'\n' || diag(
|
|
810
|
+
' runtime: ' || act_time || ' ms' ||
|
|
811
|
+
E'\n exceeds: ' || max_time || ' ms'
|
|
812
|
+
);
|
|
813
|
+
END;
|
|
814
|
+
$$ LANGUAGE plpgsql;
|
|
815
|
+
|
|
816
|
+
-- performs_ok ( sql, milliseconds )
|
|
817
|
+
CREATE OR REPLACE FUNCTION performs_ok ( TEXT, NUMERIC )
|
|
818
|
+
RETURNS TEXT AS $$
|
|
819
|
+
SELECT performs_ok(
|
|
820
|
+
$1, $2, 'Should run in less than ' || $2 || ' ms'
|
|
821
|
+
);
|
|
822
|
+
$$ LANGUAGE sql;
|
|
823
|
+
|
|
824
|
+
-- Convenience function to run a query many times and returns
|
|
825
|
+
-- the middle set of those times as defined by the last argument
|
|
826
|
+
-- e.g. _time_trials('SELECT 1', 100, 0.8) will execute 'SELECT 1'
|
|
827
|
+
-- 100 times, and return the execution times for the middle 80 runs
|
|
828
|
+
--
|
|
829
|
+
-- I could have left this logic in performs_within, but I have
|
|
830
|
+
-- plans to hook into this function for other purposes outside
|
|
831
|
+
-- of pgTAP
|
|
832
|
+
CREATE TYPE _time_trial_type
|
|
833
|
+
AS (a_time NUMERIC);
|
|
834
|
+
CREATE OR REPLACE FUNCTION _time_trials(TEXT, INT, NUMERIC)
|
|
835
|
+
RETURNS SETOF _time_trial_type AS $$
|
|
836
|
+
DECLARE
|
|
837
|
+
query TEXT := _query($1);
|
|
838
|
+
iterations ALIAS FOR $2;
|
|
839
|
+
return_percent ALIAS FOR $3;
|
|
840
|
+
start_time TEXT;
|
|
841
|
+
act_time NUMERIC;
|
|
842
|
+
times NUMERIC[];
|
|
843
|
+
offset_it INT;
|
|
844
|
+
limit_it INT;
|
|
845
|
+
offset_percent NUMERIC;
|
|
846
|
+
a_time _time_trial_type;
|
|
847
|
+
BEGIN
|
|
848
|
+
-- Execute the query over and over
|
|
849
|
+
FOR i IN 1..iterations LOOP
|
|
850
|
+
start_time := timeofday();
|
|
851
|
+
EXECUTE query;
|
|
852
|
+
-- Store the execution time for the run in an array of times
|
|
853
|
+
times[i] := extract(millisecond from timeofday()::timestamptz - start_time::timestamptz);
|
|
854
|
+
END LOOP;
|
|
855
|
+
offset_percent := (1.0 - return_percent) / 2.0;
|
|
856
|
+
-- Ensure that offset skips the bottom X% of runs, or set it to 0
|
|
857
|
+
SELECT GREATEST((offset_percent * iterations)::int, 0) INTO offset_it;
|
|
858
|
+
-- Ensure that with limit the query to returning only the middle X% of runs
|
|
859
|
+
SELECT GREATEST((return_percent * iterations)::int, 1) INTO limit_it;
|
|
860
|
+
|
|
861
|
+
FOR a_time IN SELECT times[i]
|
|
862
|
+
FROM generate_series(array_lower(times, 1), array_upper(times, 1)) i
|
|
863
|
+
ORDER BY 1
|
|
864
|
+
OFFSET offset_it
|
|
865
|
+
LIMIT limit_it LOOP
|
|
866
|
+
RETURN NEXT a_time;
|
|
867
|
+
END LOOP;
|
|
868
|
+
END;
|
|
869
|
+
$$ LANGUAGE plpgsql;
|
|
870
|
+
|
|
871
|
+
-- performs_within( sql, average_milliseconds, within, iterations, description )
|
|
872
|
+
CREATE OR REPLACE FUNCTION performs_within(TEXT, NUMERIC, NUMERIC, INT, TEXT)
|
|
873
|
+
RETURNS TEXT AS $$
|
|
874
|
+
DECLARE
|
|
875
|
+
query TEXT := _query($1);
|
|
876
|
+
expected_avg ALIAS FOR $2;
|
|
877
|
+
within ALIAS FOR $3;
|
|
878
|
+
iterations ALIAS FOR $4;
|
|
879
|
+
descr ALIAS FOR $5;
|
|
880
|
+
avg_time NUMERIC;
|
|
881
|
+
BEGIN
|
|
882
|
+
SELECT avg(a_time) FROM _time_trials(query, iterations, 0.8) t1 INTO avg_time;
|
|
883
|
+
IF abs(avg_time - expected_avg) < within THEN RETURN ok(TRUE, descr); END IF;
|
|
884
|
+
RETURN ok(FALSE, descr) || E'\n' || diag(' average runtime: ' || avg_time || ' ms'
|
|
885
|
+
|| E'\n desired average: ' || expected_avg || ' +/- ' || within || ' ms'
|
|
886
|
+
);
|
|
887
|
+
END;
|
|
888
|
+
$$ LANGUAGE plpgsql;
|
|
889
|
+
|
|
890
|
+
-- performs_within( sql, average_milliseconds, within, iterations )
|
|
891
|
+
CREATE OR REPLACE FUNCTION performs_within(TEXT, NUMERIC, NUMERIC, INT)
|
|
892
|
+
RETURNS TEXT AS $$
|
|
893
|
+
SELECT performs_within(
|
|
894
|
+
$1, $2, $3, $4,
|
|
895
|
+
'Should run within ' || $2 || ' +/- ' || $3 || ' ms');
|
|
896
|
+
$$ LANGUAGE sql;
|
|
897
|
+
-- performs_within( sql, average_milliseconds, within, description )
|
|
898
|
+
CREATE OR REPLACE FUNCTION performs_within(TEXT, NUMERIC, NUMERIC, TEXT)
|
|
899
|
+
RETURNS TEXT AS $$
|
|
900
|
+
SELECT performs_within(
|
|
901
|
+
$1, $2, $3, 10, $4
|
|
902
|
+
);
|
|
903
|
+
$$ LANGUAGE sql;
|
|
904
|
+
|
|
905
|
+
-- performs_within( sql, average_milliseconds, within )
|
|
906
|
+
CREATE OR REPLACE FUNCTION performs_within(TEXT, NUMERIC, NUMERIC)
|
|
907
|
+
RETURNS TEXT AS $$
|
|
908
|
+
SELECT performs_within(
|
|
909
|
+
$1, $2, $3, 10,
|
|
910
|
+
'Should run within ' || $2 || ' +/- ' || $3 || ' ms');
|
|
911
|
+
$$ LANGUAGE sql;
|
|
912
|
+
|
|
913
|
+
CREATE OR REPLACE FUNCTION _ident_array_to_string( name[], text )
|
|
914
|
+
RETURNS text AS $$
|
|
915
|
+
SELECT array_to_string(ARRAY(
|
|
916
|
+
SELECT quote_ident($1[i])
|
|
917
|
+
FROM generate_series(1, array_upper($1, 1)) s(i)
|
|
918
|
+
ORDER BY i
|
|
919
|
+
), $2);
|
|
920
|
+
$$ LANGUAGE SQL immutable;
|
|
921
|
+
|
|
922
|
+
-- Borrowed from newsysviews: https://www.postgresql.org/ftp/projects/pgFoundry/newsysviews/
|
|
923
|
+
CREATE OR REPLACE FUNCTION _prokind( p_oid oid )
|
|
924
|
+
RETURNS "char" AS $$
|
|
925
|
+
BEGIN
|
|
926
|
+
IF pg_version_num() >= 110000 THEN
|
|
927
|
+
RETURN prokind FROM pg_catalog.pg_proc WHERE oid = p_oid;
|
|
928
|
+
ELSE
|
|
929
|
+
RETURN CASE WHEN proisagg THEN 'a' WHEN proiswindow THEN 'w' ELSE 'f' END
|
|
930
|
+
FROM pg_catalog.pg_proc WHERE oid = p_oid;
|
|
931
|
+
END IF;
|
|
932
|
+
END;
|
|
933
|
+
$$ LANGUAGE plpgsql STABLE;
|
|
934
|
+
|
|
935
|
+
CREATE OR REPLACE VIEW tap_funky
|
|
936
|
+
AS SELECT p.oid AS oid,
|
|
937
|
+
n.nspname AS schema,
|
|
938
|
+
p.proname AS name,
|
|
939
|
+
pg_catalog.pg_get_userbyid(p.proowner) AS owner,
|
|
940
|
+
array_to_string(p.proargtypes::regtype[], ',') AS args,
|
|
941
|
+
CASE p.proretset WHEN TRUE THEN 'setof ' ELSE '' END
|
|
942
|
+
|| p.prorettype::regtype AS returns,
|
|
943
|
+
p.prolang AS langoid,
|
|
944
|
+
p.proisstrict AS is_strict,
|
|
945
|
+
_prokind(p.oid) AS kind,
|
|
946
|
+
p.prosecdef AS is_definer,
|
|
947
|
+
p.proretset AS returns_set,
|
|
948
|
+
p.provolatile::char AS volatility,
|
|
949
|
+
pg_catalog.pg_function_is_visible(p.oid) AS is_visible
|
|
950
|
+
FROM pg_catalog.pg_proc p
|
|
951
|
+
JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
|
|
952
|
+
;
|
|
953
|
+
|
|
954
|
+
CREATE OR REPLACE FUNCTION _funkargs ( NAME[] )
|
|
955
|
+
RETURNS TEXT AS $$
|
|
956
|
+
BEGIN
|
|
957
|
+
RETURN array_to_string($1::regtype[], ',');
|
|
958
|
+
EXCEPTION WHEN undefined_object THEN
|
|
959
|
+
RETURN array_to_string($1, ',');
|
|
960
|
+
END;
|
|
961
|
+
$$ LANGUAGE PLPGSQL STABLE;
|
|
962
|
+
|
|
963
|
+
CREATE OR REPLACE FUNCTION _got_func ( NAME, NAME, NAME[] )
|
|
964
|
+
RETURNS BOOLEAN AS $$
|
|
965
|
+
SELECT EXISTS(
|
|
966
|
+
SELECT TRUE
|
|
967
|
+
FROM tap_funky
|
|
968
|
+
WHERE schema = $1
|
|
969
|
+
AND name = $2
|
|
970
|
+
AND args = _funkargs($3)
|
|
971
|
+
);
|
|
972
|
+
$$ LANGUAGE SQL;
|
|
973
|
+
|
|
974
|
+
CREATE OR REPLACE FUNCTION _got_func ( NAME, NAME )
|
|
975
|
+
RETURNS BOOLEAN AS $$
|
|
976
|
+
SELECT EXISTS( SELECT TRUE FROM tap_funky WHERE schema = $1 AND name = $2 );
|
|
977
|
+
$$ LANGUAGE SQL;
|
|
978
|
+
|
|
979
|
+
CREATE OR REPLACE FUNCTION _got_func ( NAME, NAME[] )
|
|
980
|
+
RETURNS BOOLEAN AS $$
|
|
981
|
+
SELECT EXISTS(
|
|
982
|
+
SELECT TRUE
|
|
983
|
+
FROM tap_funky
|
|
984
|
+
WHERE name = $1
|
|
985
|
+
AND args = _funkargs($2)
|
|
986
|
+
AND is_visible
|
|
987
|
+
);
|
|
988
|
+
$$ LANGUAGE SQL;
|
|
989
|
+
|
|
990
|
+
CREATE OR REPLACE FUNCTION _got_func ( NAME )
|
|
991
|
+
RETURNS BOOLEAN AS $$
|
|
992
|
+
SELECT EXISTS( SELECT TRUE FROM tap_funky WHERE name = $1 AND is_visible);
|
|
993
|
+
$$ LANGUAGE SQL;
|
|
994
|
+
|
|
995
|
+
-- has_function( schema, function, args[], description )
|
|
996
|
+
CREATE OR REPLACE FUNCTION has_function ( NAME, NAME, NAME[], TEXT )
|
|
997
|
+
RETURNS TEXT AS $$
|
|
998
|
+
SELECT ok( _got_func($1, $2, $3), $4 );
|
|
999
|
+
$$ LANGUAGE SQL;
|
|
1000
|
+
|
|
1001
|
+
-- has_function( schema, function, args[] )
|
|
1002
|
+
CREATE OR REPLACE FUNCTION has_function( NAME, NAME, NAME[] )
|
|
1003
|
+
RETURNS TEXT AS $$
|
|
1004
|
+
SELECT ok(
|
|
1005
|
+
_got_func($1, $2, $3),
|
|
1006
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
1007
|
+
array_to_string($3, ', ') || ') should exist'
|
|
1008
|
+
);
|
|
1009
|
+
$$ LANGUAGE sql;
|
|
1010
|
+
|
|
1011
|
+
-- has_function( schema, function, description )
|
|
1012
|
+
CREATE OR REPLACE FUNCTION has_function ( NAME, NAME, TEXT )
|
|
1013
|
+
RETURNS TEXT AS $$
|
|
1014
|
+
SELECT ok( _got_func($1, $2), $3 );
|
|
1015
|
+
$$ LANGUAGE SQL;
|
|
1016
|
+
|
|
1017
|
+
-- has_function( schema, function )
|
|
1018
|
+
CREATE OR REPLACE FUNCTION has_function( NAME, NAME )
|
|
1019
|
+
RETURNS TEXT AS $$
|
|
1020
|
+
SELECT ok(
|
|
1021
|
+
_got_func($1, $2),
|
|
1022
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should exist'
|
|
1023
|
+
);
|
|
1024
|
+
$$ LANGUAGE sql;
|
|
1025
|
+
|
|
1026
|
+
-- has_function( function, args[], description )
|
|
1027
|
+
CREATE OR REPLACE FUNCTION has_function ( NAME, NAME[], TEXT )
|
|
1028
|
+
RETURNS TEXT AS $$
|
|
1029
|
+
SELECT ok( _got_func($1, $2), $3 );
|
|
1030
|
+
$$ LANGUAGE SQL;
|
|
1031
|
+
|
|
1032
|
+
-- has_function( function, args[] )
|
|
1033
|
+
CREATE OR REPLACE FUNCTION has_function( NAME, NAME[] )
|
|
1034
|
+
RETURNS TEXT AS $$
|
|
1035
|
+
SELECT ok(
|
|
1036
|
+
_got_func($1, $2),
|
|
1037
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
1038
|
+
array_to_string($2, ', ') || ') should exist'
|
|
1039
|
+
);
|
|
1040
|
+
$$ LANGUAGE sql;
|
|
1041
|
+
|
|
1042
|
+
-- has_function( function, description )
|
|
1043
|
+
CREATE OR REPLACE FUNCTION has_function( NAME, TEXT )
|
|
1044
|
+
RETURNS TEXT AS $$
|
|
1045
|
+
SELECT ok( _got_func($1), $2 );
|
|
1046
|
+
$$ LANGUAGE sql;
|
|
1047
|
+
|
|
1048
|
+
-- has_function( function )
|
|
1049
|
+
CREATE OR REPLACE FUNCTION has_function( NAME )
|
|
1050
|
+
RETURNS TEXT AS $$
|
|
1051
|
+
SELECT ok( _got_func($1), 'Function ' || quote_ident($1) || '() should exist' );
|
|
1052
|
+
$$ LANGUAGE sql;
|
|
1053
|
+
|
|
1054
|
+
-- hasnt_function( schema, function, args[], description )
|
|
1055
|
+
CREATE OR REPLACE FUNCTION hasnt_function ( NAME, NAME, NAME[], TEXT )
|
|
1056
|
+
RETURNS TEXT AS $$
|
|
1057
|
+
SELECT ok( NOT _got_func($1, $2, $3), $4 );
|
|
1058
|
+
$$ LANGUAGE SQL;
|
|
1059
|
+
|
|
1060
|
+
-- hasnt_function( schema, function, args[] )
|
|
1061
|
+
CREATE OR REPLACE FUNCTION hasnt_function( NAME, NAME, NAME[] )
|
|
1062
|
+
RETURNS TEXT AS $$
|
|
1063
|
+
SELECT ok(
|
|
1064
|
+
NOT _got_func($1, $2, $3),
|
|
1065
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
1066
|
+
array_to_string($3, ', ') || ') should not exist'
|
|
1067
|
+
);
|
|
1068
|
+
$$ LANGUAGE sql;
|
|
1069
|
+
|
|
1070
|
+
-- hasnt_function( schema, function, description )
|
|
1071
|
+
CREATE OR REPLACE FUNCTION hasnt_function ( NAME, NAME, TEXT )
|
|
1072
|
+
RETURNS TEXT AS $$
|
|
1073
|
+
SELECT ok( NOT _got_func($1, $2), $3 );
|
|
1074
|
+
$$ LANGUAGE SQL;
|
|
1075
|
+
|
|
1076
|
+
-- hasnt_function( schema, function )
|
|
1077
|
+
CREATE OR REPLACE FUNCTION hasnt_function( NAME, NAME )
|
|
1078
|
+
RETURNS TEXT AS $$
|
|
1079
|
+
SELECT ok(
|
|
1080
|
+
NOT _got_func($1, $2),
|
|
1081
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should not exist'
|
|
1082
|
+
);
|
|
1083
|
+
$$ LANGUAGE sql;
|
|
1084
|
+
|
|
1085
|
+
-- hasnt_function( function, args[], description )
|
|
1086
|
+
CREATE OR REPLACE FUNCTION hasnt_function ( NAME, NAME[], TEXT )
|
|
1087
|
+
RETURNS TEXT AS $$
|
|
1088
|
+
SELECT ok( NOT _got_func($1, $2), $3 );
|
|
1089
|
+
$$ LANGUAGE SQL;
|
|
1090
|
+
|
|
1091
|
+
-- hasnt_function( function, args[] )
|
|
1092
|
+
CREATE OR REPLACE FUNCTION hasnt_function( NAME, NAME[] )
|
|
1093
|
+
RETURNS TEXT AS $$
|
|
1094
|
+
SELECT ok(
|
|
1095
|
+
NOT _got_func($1, $2),
|
|
1096
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
1097
|
+
array_to_string($2, ', ') || ') should not exist'
|
|
1098
|
+
);
|
|
1099
|
+
$$ LANGUAGE sql;
|
|
1100
|
+
|
|
1101
|
+
-- hasnt_function( function, description )
|
|
1102
|
+
CREATE OR REPLACE FUNCTION hasnt_function( NAME, TEXT )
|
|
1103
|
+
RETURNS TEXT AS $$
|
|
1104
|
+
SELECT ok( NOT _got_func($1), $2 );
|
|
1105
|
+
$$ LANGUAGE sql;
|
|
1106
|
+
|
|
1107
|
+
-- hasnt_function( function )
|
|
1108
|
+
CREATE OR REPLACE FUNCTION hasnt_function( NAME )
|
|
1109
|
+
RETURNS TEXT AS $$
|
|
1110
|
+
SELECT ok( NOT _got_func($1), 'Function ' || quote_ident($1) || '() should not exist' );
|
|
1111
|
+
$$ LANGUAGE sql;
|
|
1112
|
+
|
|
1113
|
+
CREATE OR REPLACE FUNCTION _pg_sv_type_array( OID[] )
|
|
1114
|
+
RETURNS NAME[] AS $$
|
|
1115
|
+
SELECT ARRAY(
|
|
1116
|
+
SELECT t.typname
|
|
1117
|
+
FROM pg_catalog.pg_type t
|
|
1118
|
+
JOIN generate_series(1, array_upper($1, 1)) s(i) ON t.oid = $1[i]
|
|
1119
|
+
ORDER BY i
|
|
1120
|
+
)
|
|
1121
|
+
$$ LANGUAGE SQL stable;
|
|
1122
|
+
|
|
1123
|
+
-- can( schema, functions[], description )
|
|
1124
|
+
CREATE OR REPLACE FUNCTION can ( NAME, NAME[], TEXT )
|
|
1125
|
+
RETURNS TEXT AS $$
|
|
1126
|
+
DECLARE
|
|
1127
|
+
missing text[];
|
|
1128
|
+
BEGIN
|
|
1129
|
+
SELECT ARRAY(
|
|
1130
|
+
SELECT quote_ident($2[i])
|
|
1131
|
+
FROM generate_series(1, array_upper($2, 1)) s(i)
|
|
1132
|
+
LEFT JOIN tap_funky ON name = $2[i] AND schema = $1
|
|
1133
|
+
WHERE oid IS NULL
|
|
1134
|
+
GROUP BY $2[i], s.i
|
|
1135
|
+
ORDER BY MIN(s.i)
|
|
1136
|
+
) INTO missing;
|
|
1137
|
+
IF missing[1] IS NULL THEN
|
|
1138
|
+
RETURN ok( true, $3 );
|
|
1139
|
+
END IF;
|
|
1140
|
+
RETURN ok( false, $3 ) || E'\n' || diag(
|
|
1141
|
+
' ' || quote_ident($1) || '.' ||
|
|
1142
|
+
array_to_string( missing, E'() missing\n ' || quote_ident($1) || '.') ||
|
|
1143
|
+
'() missing'
|
|
1144
|
+
);
|
|
1145
|
+
END;
|
|
1146
|
+
$$ LANGUAGE plpgsql;
|
|
1147
|
+
|
|
1148
|
+
-- can( schema, functions[] )
|
|
1149
|
+
CREATE OR REPLACE FUNCTION can ( NAME, NAME[] )
|
|
1150
|
+
RETURNS TEXT AS $$
|
|
1151
|
+
SELECT can( $1, $2, 'Schema ' || quote_ident($1) || ' can' );
|
|
1152
|
+
$$ LANGUAGE sql;
|
|
1153
|
+
|
|
1154
|
+
-- can( functions[], description )
|
|
1155
|
+
CREATE OR REPLACE FUNCTION can ( NAME[], TEXT )
|
|
1156
|
+
RETURNS TEXT AS $$
|
|
1157
|
+
DECLARE
|
|
1158
|
+
missing text[];
|
|
1159
|
+
BEGIN
|
|
1160
|
+
SELECT ARRAY(
|
|
1161
|
+
SELECT quote_ident($1[i])
|
|
1162
|
+
FROM generate_series(1, array_upper($1, 1)) s(i)
|
|
1163
|
+
LEFT JOIN pg_catalog.pg_proc p
|
|
1164
|
+
ON $1[i] = p.proname
|
|
1165
|
+
AND pg_catalog.pg_function_is_visible(p.oid)
|
|
1166
|
+
WHERE p.oid IS NULL
|
|
1167
|
+
ORDER BY s.i
|
|
1168
|
+
) INTO missing;
|
|
1169
|
+
IF missing[1] IS NULL THEN
|
|
1170
|
+
RETURN ok( true, $2 );
|
|
1171
|
+
END IF;
|
|
1172
|
+
RETURN ok( false, $2 ) || E'\n' || diag(
|
|
1173
|
+
' ' ||
|
|
1174
|
+
array_to_string( missing, E'() missing\n ') ||
|
|
1175
|
+
'() missing'
|
|
1176
|
+
);
|
|
1177
|
+
END;
|
|
1178
|
+
$$ LANGUAGE plpgsql;
|
|
1179
|
+
|
|
1180
|
+
-- can( functions[] )
|
|
1181
|
+
CREATE OR REPLACE FUNCTION can ( NAME[] )
|
|
1182
|
+
RETURNS TEXT AS $$
|
|
1183
|
+
SELECT can( $1, 'Schema ' || _ident_array_to_string(current_schemas(true), ' or ') || ' can' );
|
|
1184
|
+
$$ LANGUAGE sql;
|
|
1185
|
+
|
|
1186
|
+
CREATE OR REPLACE FUNCTION _has_type( NAME, NAME, CHAR[] )
|
|
1187
|
+
RETURNS BOOLEAN AS $$
|
|
1188
|
+
SELECT EXISTS(
|
|
1189
|
+
SELECT true
|
|
1190
|
+
FROM pg_catalog.pg_type t
|
|
1191
|
+
JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
|
|
1192
|
+
WHERE t.typisdefined
|
|
1193
|
+
AND n.nspname = $1
|
|
1194
|
+
AND t.typname = $2
|
|
1195
|
+
AND t.typtype = ANY( COALESCE($3, ARRAY['b', 'c', 'd', 'p', 'e']) )
|
|
1196
|
+
);
|
|
1197
|
+
$$ LANGUAGE sql;
|
|
1198
|
+
|
|
1199
|
+
CREATE OR REPLACE FUNCTION _has_type( NAME, CHAR[] )
|
|
1200
|
+
RETURNS BOOLEAN AS $$
|
|
1201
|
+
SELECT EXISTS(
|
|
1202
|
+
SELECT true
|
|
1203
|
+
FROM pg_catalog.pg_type t
|
|
1204
|
+
WHERE t.typisdefined
|
|
1205
|
+
AND pg_catalog.pg_type_is_visible(t.oid)
|
|
1206
|
+
AND t.typname = $1
|
|
1207
|
+
AND t.typtype = ANY( COALESCE($2, ARRAY['b', 'c', 'd', 'p', 'e']) )
|
|
1208
|
+
);
|
|
1209
|
+
$$ LANGUAGE sql;
|
|
1210
|
+
|
|
1211
|
+
-- has_type( schema, type, description )
|
|
1212
|
+
CREATE OR REPLACE FUNCTION has_type( NAME, NAME, TEXT )
|
|
1213
|
+
RETURNS TEXT AS $$
|
|
1214
|
+
SELECT ok( _has_type( $1, $2, NULL ), $3 );
|
|
1215
|
+
$$ LANGUAGE sql;
|
|
1216
|
+
|
|
1217
|
+
-- has_type( schema, type )
|
|
1218
|
+
CREATE OR REPLACE FUNCTION has_type( NAME, NAME )
|
|
1219
|
+
RETURNS TEXT AS $$
|
|
1220
|
+
SELECT has_type( $1, $2, 'Type ' || quote_ident($1) || '.' || quote_ident($2) || ' should exist' );
|
|
1221
|
+
$$ LANGUAGE sql;
|
|
1222
|
+
|
|
1223
|
+
-- has_type( type, description )
|
|
1224
|
+
CREATE OR REPLACE FUNCTION has_type( NAME, TEXT )
|
|
1225
|
+
RETURNS TEXT AS $$
|
|
1226
|
+
SELECT ok( _has_type( $1, NULL ), $2 );
|
|
1227
|
+
$$ LANGUAGE sql;
|
|
1228
|
+
|
|
1229
|
+
-- has_type( type )
|
|
1230
|
+
CREATE OR REPLACE FUNCTION has_type( NAME )
|
|
1231
|
+
RETURNS TEXT AS $$
|
|
1232
|
+
SELECT ok( _has_type( $1, NULL ), ('Type ' || quote_ident($1) || ' should exist')::text );
|
|
1233
|
+
$$ LANGUAGE sql;
|
|
1234
|
+
|
|
1235
|
+
-- hasnt_type( schema, type, description )
|
|
1236
|
+
CREATE OR REPLACE FUNCTION hasnt_type( NAME, NAME, TEXT )
|
|
1237
|
+
RETURNS TEXT AS $$
|
|
1238
|
+
SELECT ok( NOT _has_type( $1, $2, NULL ), $3 );
|
|
1239
|
+
$$ LANGUAGE sql;
|
|
1240
|
+
|
|
1241
|
+
-- hasnt_type( schema, type )
|
|
1242
|
+
CREATE OR REPLACE FUNCTION hasnt_type( NAME, NAME )
|
|
1243
|
+
RETURNS TEXT AS $$
|
|
1244
|
+
SELECT hasnt_type( $1, $2, 'Type ' || quote_ident($1) || '.' || quote_ident($2) || ' should not exist' );
|
|
1245
|
+
$$ LANGUAGE sql;
|
|
1246
|
+
|
|
1247
|
+
-- hasnt_type( type, description )
|
|
1248
|
+
CREATE OR REPLACE FUNCTION hasnt_type( NAME, TEXT )
|
|
1249
|
+
RETURNS TEXT AS $$
|
|
1250
|
+
SELECT ok( NOT _has_type( $1, NULL ), $2 );
|
|
1251
|
+
$$ LANGUAGE sql;
|
|
1252
|
+
|
|
1253
|
+
-- hasnt_type( type )
|
|
1254
|
+
CREATE OR REPLACE FUNCTION hasnt_type( NAME )
|
|
1255
|
+
RETURNS TEXT AS $$
|
|
1256
|
+
SELECT ok( NOT _has_type( $1, NULL ), ('Type ' || quote_ident($1) || ' should not exist')::text );
|
|
1257
|
+
$$ LANGUAGE sql;
|
|
1258
|
+
|
|
1259
|
+
-- has_domain( schema, domain, description )
|
|
1260
|
+
CREATE OR REPLACE FUNCTION has_domain( NAME, NAME, TEXT )
|
|
1261
|
+
RETURNS TEXT AS $$
|
|
1262
|
+
SELECT ok( _has_type( $1, $2, ARRAY['d'] ), $3 );
|
|
1263
|
+
$$ LANGUAGE sql;
|
|
1264
|
+
|
|
1265
|
+
-- has_domain( schema, domain )
|
|
1266
|
+
CREATE OR REPLACE FUNCTION has_domain( NAME, NAME )
|
|
1267
|
+
RETURNS TEXT AS $$
|
|
1268
|
+
SELECT has_domain( $1, $2, 'Domain ' || quote_ident($1) || '.' || quote_ident($2) || ' should exist' );
|
|
1269
|
+
$$ LANGUAGE sql;
|
|
1270
|
+
|
|
1271
|
+
-- has_domain( domain, description )
|
|
1272
|
+
CREATE OR REPLACE FUNCTION has_domain( NAME, TEXT )
|
|
1273
|
+
RETURNS TEXT AS $$
|
|
1274
|
+
SELECT ok( _has_type( $1, ARRAY['d'] ), $2 );
|
|
1275
|
+
$$ LANGUAGE sql;
|
|
1276
|
+
|
|
1277
|
+
-- has_domain( domain )
|
|
1278
|
+
CREATE OR REPLACE FUNCTION has_domain( NAME )
|
|
1279
|
+
RETURNS TEXT AS $$
|
|
1280
|
+
SELECT ok( _has_type( $1, ARRAY['d'] ), ('Domain ' || quote_ident($1) || ' should exist')::text );
|
|
1281
|
+
$$ LANGUAGE sql;
|
|
1282
|
+
|
|
1283
|
+
-- hasnt_domain( schema, domain, description )
|
|
1284
|
+
CREATE OR REPLACE FUNCTION hasnt_domain( NAME, NAME, TEXT )
|
|
1285
|
+
RETURNS TEXT AS $$
|
|
1286
|
+
SELECT ok( NOT _has_type( $1, $2, ARRAY['d'] ), $3 );
|
|
1287
|
+
$$ LANGUAGE sql;
|
|
1288
|
+
|
|
1289
|
+
-- hasnt_domain( schema, domain )
|
|
1290
|
+
CREATE OR REPLACE FUNCTION hasnt_domain( NAME, NAME )
|
|
1291
|
+
RETURNS TEXT AS $$
|
|
1292
|
+
SELECT hasnt_domain( $1, $2, 'Domain ' || quote_ident($1) || '.' || quote_ident($2) || ' should not exist' );
|
|
1293
|
+
$$ LANGUAGE sql;
|
|
1294
|
+
|
|
1295
|
+
-- hasnt_domain( domain, description )
|
|
1296
|
+
CREATE OR REPLACE FUNCTION hasnt_domain( NAME, TEXT )
|
|
1297
|
+
RETURNS TEXT AS $$
|
|
1298
|
+
SELECT ok( NOT _has_type( $1, ARRAY['d'] ), $2 );
|
|
1299
|
+
$$ LANGUAGE sql;
|
|
1300
|
+
|
|
1301
|
+
-- hasnt_domain( domain )
|
|
1302
|
+
CREATE OR REPLACE FUNCTION hasnt_domain( NAME )
|
|
1303
|
+
RETURNS TEXT AS $$
|
|
1304
|
+
SELECT ok( NOT _has_type( $1, ARRAY['d'] ), ('Domain ' || quote_ident($1) || ' should not exist')::text );
|
|
1305
|
+
$$ LANGUAGE sql;
|
|
1306
|
+
|
|
1307
|
+
-- has_enum( schema, enum, description )
|
|
1308
|
+
CREATE OR REPLACE FUNCTION has_enum( NAME, NAME, TEXT )
|
|
1309
|
+
RETURNS TEXT AS $$
|
|
1310
|
+
SELECT ok( _has_type( $1, $2, ARRAY['e'] ), $3 );
|
|
1311
|
+
$$ LANGUAGE sql;
|
|
1312
|
+
|
|
1313
|
+
-- has_enum( schema, enum )
|
|
1314
|
+
CREATE OR REPLACE FUNCTION has_enum( NAME, NAME )
|
|
1315
|
+
RETURNS TEXT AS $$
|
|
1316
|
+
SELECT has_enum( $1, $2, 'Enum ' || quote_ident($1) || '.' || quote_ident($2) || ' should exist' );
|
|
1317
|
+
$$ LANGUAGE sql;
|
|
1318
|
+
|
|
1319
|
+
-- has_enum( enum, description )
|
|
1320
|
+
CREATE OR REPLACE FUNCTION has_enum( NAME, TEXT )
|
|
1321
|
+
RETURNS TEXT AS $$
|
|
1322
|
+
SELECT ok( _has_type( $1, ARRAY['e'] ), $2 );
|
|
1323
|
+
$$ LANGUAGE sql;
|
|
1324
|
+
|
|
1325
|
+
-- has_enum( enum )
|
|
1326
|
+
CREATE OR REPLACE FUNCTION has_enum( NAME )
|
|
1327
|
+
RETURNS TEXT AS $$
|
|
1328
|
+
SELECT ok( _has_type( $1, ARRAY['e'] ), ('Enum ' || quote_ident($1) || ' should exist')::text );
|
|
1329
|
+
$$ LANGUAGE sql;
|
|
1330
|
+
|
|
1331
|
+
-- hasnt_enum( schema, enum, description )
|
|
1332
|
+
CREATE OR REPLACE FUNCTION hasnt_enum( NAME, NAME, TEXT )
|
|
1333
|
+
RETURNS TEXT AS $$
|
|
1334
|
+
SELECT ok( NOT _has_type( $1, $2, ARRAY['e'] ), $3 );
|
|
1335
|
+
$$ LANGUAGE sql;
|
|
1336
|
+
|
|
1337
|
+
-- hasnt_enum( schema, enum )
|
|
1338
|
+
CREATE OR REPLACE FUNCTION hasnt_enum( NAME, NAME )
|
|
1339
|
+
RETURNS TEXT AS $$
|
|
1340
|
+
SELECT hasnt_enum( $1, $2, 'Enum ' || quote_ident($1) || '.' || quote_ident($2) || ' should not exist' );
|
|
1341
|
+
$$ LANGUAGE sql;
|
|
1342
|
+
|
|
1343
|
+
-- hasnt_enum( enum, description )
|
|
1344
|
+
CREATE OR REPLACE FUNCTION hasnt_enum( NAME, TEXT )
|
|
1345
|
+
RETURNS TEXT AS $$
|
|
1346
|
+
SELECT ok( NOT _has_type( $1, ARRAY['e'] ), $2 );
|
|
1347
|
+
$$ LANGUAGE sql;
|
|
1348
|
+
|
|
1349
|
+
-- hasnt_enum( enum )
|
|
1350
|
+
CREATE OR REPLACE FUNCTION hasnt_enum( NAME )
|
|
1351
|
+
RETURNS TEXT AS $$
|
|
1352
|
+
SELECT ok( NOT _has_type( $1, ARRAY['e'] ), ('Enum ' || quote_ident($1) || ' should not exist')::text );
|
|
1353
|
+
$$ LANGUAGE sql;
|
|
1354
|
+
|
|
1355
|
+
-- enum_has_labels( schema, enum, labels, description )
|
|
1356
|
+
CREATE OR REPLACE FUNCTION enum_has_labels( NAME, NAME, NAME[], TEXT )
|
|
1357
|
+
RETURNS TEXT AS $$
|
|
1358
|
+
SELECT is(
|
|
1359
|
+
ARRAY(
|
|
1360
|
+
SELECT e.enumlabel
|
|
1361
|
+
FROM pg_catalog.pg_type t
|
|
1362
|
+
JOIN pg_catalog.pg_enum e ON t.oid = e.enumtypid
|
|
1363
|
+
JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
|
|
1364
|
+
WHERE t.typisdefined
|
|
1365
|
+
AND n.nspname = $1
|
|
1366
|
+
AND t.typname = $2
|
|
1367
|
+
AND t.typtype = 'e'
|
|
1368
|
+
ORDER BY e.enumsortorder
|
|
1369
|
+
),
|
|
1370
|
+
$3,
|
|
1371
|
+
$4
|
|
1372
|
+
);
|
|
1373
|
+
$$ LANGUAGE sql;
|
|
1374
|
+
|
|
1375
|
+
-- enum_has_labels( schema, enum, labels )
|
|
1376
|
+
CREATE OR REPLACE FUNCTION enum_has_labels( NAME, NAME, NAME[] )
|
|
1377
|
+
RETURNS TEXT AS $$
|
|
1378
|
+
SELECT enum_has_labels(
|
|
1379
|
+
$1, $2, $3,
|
|
1380
|
+
'Enum ' || quote_ident($1) || '.' || quote_ident($2) || ' should have labels (' || array_to_string( $3, ', ' ) || ')'
|
|
1381
|
+
);
|
|
1382
|
+
$$ LANGUAGE sql;
|
|
1383
|
+
|
|
1384
|
+
-- enum_has_labels( enum, labels, description )
|
|
1385
|
+
CREATE OR REPLACE FUNCTION enum_has_labels( NAME, NAME[], TEXT )
|
|
1386
|
+
RETURNS TEXT AS $$
|
|
1387
|
+
SELECT is(
|
|
1388
|
+
ARRAY(
|
|
1389
|
+
SELECT e.enumlabel
|
|
1390
|
+
FROM pg_catalog.pg_type t
|
|
1391
|
+
JOIN pg_catalog.pg_enum e ON t.oid = e.enumtypid
|
|
1392
|
+
WHERE t.typisdefined
|
|
1393
|
+
AND pg_catalog.pg_type_is_visible(t.oid)
|
|
1394
|
+
AND t.typname = $1
|
|
1395
|
+
AND t.typtype = 'e'
|
|
1396
|
+
ORDER BY e.enumsortorder
|
|
1397
|
+
),
|
|
1398
|
+
$2,
|
|
1399
|
+
$3
|
|
1400
|
+
);
|
|
1401
|
+
$$ LANGUAGE sql;
|
|
1402
|
+
|
|
1403
|
+
-- enum_has_labels( enum, labels )
|
|
1404
|
+
CREATE OR REPLACE FUNCTION enum_has_labels( NAME, NAME[] )
|
|
1405
|
+
RETURNS TEXT AS $$
|
|
1406
|
+
SELECT enum_has_labels(
|
|
1407
|
+
$1, $2,
|
|
1408
|
+
'Enum ' || quote_ident($1) || ' should have labels (' || array_to_string( $2, ', ' ) || ')'
|
|
1409
|
+
);
|
|
1410
|
+
$$ LANGUAGE sql;
|
|
1411
|
+
|
|
1412
|
+
CREATE OR REPLACE FUNCTION _cmp_types(oid, name)
|
|
1413
|
+
RETURNS BOOLEAN AS $$
|
|
1414
|
+
SELECT pg_catalog.format_type($1, NULL) = _typename($2);
|
|
1415
|
+
$$ LANGUAGE sql;
|
|
1416
|
+
|
|
1417
|
+
CREATE OR REPLACE FUNCTION _cast_exists ( NAME, NAME, NAME, NAME )
|
|
1418
|
+
RETURNS BOOLEAN AS $$
|
|
1419
|
+
SELECT EXISTS (
|
|
1420
|
+
SELECT TRUE
|
|
1421
|
+
FROM pg_catalog.pg_cast c
|
|
1422
|
+
JOIN pg_catalog.pg_proc p ON c.castfunc = p.oid
|
|
1423
|
+
JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
|
|
1424
|
+
WHERE _cmp_types(castsource, $1)
|
|
1425
|
+
AND _cmp_types(casttarget, $2)
|
|
1426
|
+
AND n.nspname = $3
|
|
1427
|
+
AND p.proname = $4
|
|
1428
|
+
);
|
|
1429
|
+
$$ LANGUAGE SQL;
|
|
1430
|
+
|
|
1431
|
+
CREATE OR REPLACE FUNCTION _cast_exists ( NAME, NAME, NAME )
|
|
1432
|
+
RETURNS BOOLEAN AS $$
|
|
1433
|
+
SELECT EXISTS (
|
|
1434
|
+
SELECT TRUE
|
|
1435
|
+
FROM pg_catalog.pg_cast c
|
|
1436
|
+
JOIN pg_catalog.pg_proc p ON c.castfunc = p.oid
|
|
1437
|
+
WHERE _cmp_types(castsource, $1)
|
|
1438
|
+
AND _cmp_types(casttarget, $2)
|
|
1439
|
+
AND p.proname = $3
|
|
1440
|
+
);
|
|
1441
|
+
$$ LANGUAGE SQL;
|
|
1442
|
+
|
|
1443
|
+
CREATE OR REPLACE FUNCTION _cast_exists ( NAME, NAME )
|
|
1444
|
+
RETURNS BOOLEAN AS $$
|
|
1445
|
+
SELECT EXISTS (
|
|
1446
|
+
SELECT TRUE
|
|
1447
|
+
FROM pg_catalog.pg_cast c
|
|
1448
|
+
WHERE _cmp_types(castsource, $1)
|
|
1449
|
+
AND _cmp_types(casttarget, $2)
|
|
1450
|
+
);
|
|
1451
|
+
$$ LANGUAGE SQL;
|
|
1452
|
+
|
|
1453
|
+
-- has_cast( source_type, target_type, schema, function, description )
|
|
1454
|
+
CREATE OR REPLACE FUNCTION has_cast ( NAME, NAME, NAME, NAME, TEXT )
|
|
1455
|
+
RETURNS TEXT AS $$
|
|
1456
|
+
SELECT ok( _cast_exists( $1, $2, $3, $4 ), $5 );
|
|
1457
|
+
$$ LANGUAGE SQL;
|
|
1458
|
+
|
|
1459
|
+
-- has_cast( source_type, target_type, schema, function )
|
|
1460
|
+
CREATE OR REPLACE FUNCTION has_cast ( NAME, NAME, NAME, NAME )
|
|
1461
|
+
RETURNS TEXT AS $$
|
|
1462
|
+
SELECT ok(
|
|
1463
|
+
_cast_exists( $1, $2, $3, $4 ),
|
|
1464
|
+
'Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1465
|
+
|| ') WITH FUNCTION ' || quote_ident($3)
|
|
1466
|
+
|| '.' || quote_ident($4) || '() should exist'
|
|
1467
|
+
);
|
|
1468
|
+
$$ LANGUAGE SQL;
|
|
1469
|
+
|
|
1470
|
+
-- has_cast( source_type, target_type, function, description )
|
|
1471
|
+
CREATE OR REPLACE FUNCTION has_cast ( NAME, NAME, NAME, TEXT )
|
|
1472
|
+
RETURNS TEXT AS $$
|
|
1473
|
+
SELECT ok( _cast_exists( $1, $2, $3 ), $4 );
|
|
1474
|
+
$$ LANGUAGE SQL;
|
|
1475
|
+
|
|
1476
|
+
-- has_cast( source_type, target_type, function )
|
|
1477
|
+
CREATE OR REPLACE FUNCTION has_cast ( NAME, NAME, NAME )
|
|
1478
|
+
RETURNS TEXT AS $$
|
|
1479
|
+
SELECT ok(
|
|
1480
|
+
_cast_exists( $1, $2, $3 ),
|
|
1481
|
+
'Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1482
|
+
|| ') WITH FUNCTION ' || quote_ident($3) || '() should exist'
|
|
1483
|
+
);
|
|
1484
|
+
$$ LANGUAGE SQL;
|
|
1485
|
+
|
|
1486
|
+
-- has_cast( source_type, target_type, description )
|
|
1487
|
+
CREATE OR REPLACE FUNCTION has_cast ( NAME, NAME, TEXT )
|
|
1488
|
+
RETURNS TEXT AS $$
|
|
1489
|
+
SELECT ok( _cast_exists( $1, $2 ), $3 );
|
|
1490
|
+
$$ LANGUAGE SQL;
|
|
1491
|
+
|
|
1492
|
+
-- has_cast( source_type, target_type )
|
|
1493
|
+
CREATE OR REPLACE FUNCTION has_cast ( NAME, NAME )
|
|
1494
|
+
RETURNS TEXT AS $$
|
|
1495
|
+
SELECT ok(
|
|
1496
|
+
_cast_exists( $1, $2 ),
|
|
1497
|
+
'Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1498
|
+
|| ') should exist'
|
|
1499
|
+
);
|
|
1500
|
+
$$ LANGUAGE SQL;
|
|
1501
|
+
|
|
1502
|
+
-- hasnt_cast( source_type, target_type, schema, function, description )
|
|
1503
|
+
CREATE OR REPLACE FUNCTION hasnt_cast ( NAME, NAME, NAME, NAME, TEXT )
|
|
1504
|
+
RETURNS TEXT AS $$
|
|
1505
|
+
SELECT ok( NOT _cast_exists( $1, $2, $3, $4 ), $5 );
|
|
1506
|
+
$$ LANGUAGE SQL;
|
|
1507
|
+
|
|
1508
|
+
-- hasnt_cast( source_type, target_type, schema, function )
|
|
1509
|
+
CREATE OR REPLACE FUNCTION hasnt_cast ( NAME, NAME, NAME, NAME )
|
|
1510
|
+
RETURNS TEXT AS $$
|
|
1511
|
+
SELECT ok(
|
|
1512
|
+
NOT _cast_exists( $1, $2, $3, $4 ),
|
|
1513
|
+
'Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1514
|
+
|| ') WITH FUNCTION ' || quote_ident($3)
|
|
1515
|
+
|| '.' || quote_ident($4) || '() should not exist'
|
|
1516
|
+
);
|
|
1517
|
+
$$ LANGUAGE SQL;
|
|
1518
|
+
|
|
1519
|
+
-- hasnt_cast( source_type, target_type, function, description )
|
|
1520
|
+
CREATE OR REPLACE FUNCTION hasnt_cast ( NAME, NAME, NAME, TEXT )
|
|
1521
|
+
RETURNS TEXT AS $$
|
|
1522
|
+
SELECT ok( NOT _cast_exists( $1, $2, $3 ), $4 );
|
|
1523
|
+
$$ LANGUAGE SQL;
|
|
1524
|
+
|
|
1525
|
+
-- hasnt_cast( source_type, target_type, function )
|
|
1526
|
+
CREATE OR REPLACE FUNCTION hasnt_cast ( NAME, NAME, NAME )
|
|
1527
|
+
RETURNS TEXT AS $$
|
|
1528
|
+
SELECT ok(
|
|
1529
|
+
NOT _cast_exists( $1, $2, $3 ),
|
|
1530
|
+
'Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1531
|
+
|| ') WITH FUNCTION ' || quote_ident($3) || '() should not exist'
|
|
1532
|
+
);
|
|
1533
|
+
$$ LANGUAGE SQL;
|
|
1534
|
+
|
|
1535
|
+
-- hasnt_cast( source_type, target_type, description )
|
|
1536
|
+
CREATE OR REPLACE FUNCTION hasnt_cast ( NAME, NAME, TEXT )
|
|
1537
|
+
RETURNS TEXT AS $$
|
|
1538
|
+
SELECT ok( NOT _cast_exists( $1, $2 ), $3 );
|
|
1539
|
+
$$ LANGUAGE SQL;
|
|
1540
|
+
|
|
1541
|
+
-- hasnt_cast( source_type, target_type )
|
|
1542
|
+
CREATE OR REPLACE FUNCTION hasnt_cast ( NAME, NAME )
|
|
1543
|
+
RETURNS TEXT AS $$
|
|
1544
|
+
SELECT ok(
|
|
1545
|
+
NOT _cast_exists( $1, $2 ),
|
|
1546
|
+
'Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1547
|
+
|| ') should not exist'
|
|
1548
|
+
);
|
|
1549
|
+
$$ LANGUAGE SQL;
|
|
1550
|
+
|
|
1551
|
+
CREATE OR REPLACE FUNCTION _expand_context( char )
|
|
1552
|
+
RETURNS text AS $$
|
|
1553
|
+
SELECT CASE $1
|
|
1554
|
+
WHEN 'i' THEN 'implicit'
|
|
1555
|
+
WHEN 'a' THEN 'assignment'
|
|
1556
|
+
WHEN 'e' THEN 'explicit'
|
|
1557
|
+
ELSE 'unknown' END
|
|
1558
|
+
$$ LANGUAGE SQL IMMUTABLE;
|
|
1559
|
+
|
|
1560
|
+
CREATE OR REPLACE FUNCTION _get_context( NAME, NAME )
|
|
1561
|
+
RETURNS "char" AS $$
|
|
1562
|
+
SELECT c.castcontext
|
|
1563
|
+
FROM pg_catalog.pg_cast c
|
|
1564
|
+
WHERE _cmp_types(castsource, $1)
|
|
1565
|
+
AND _cmp_types(casttarget, $2)
|
|
1566
|
+
$$ LANGUAGE SQL;
|
|
1567
|
+
|
|
1568
|
+
-- cast_context_is( source_type, target_type, context, description )
|
|
1569
|
+
CREATE OR REPLACE FUNCTION cast_context_is( NAME, NAME, TEXT, TEXT )
|
|
1570
|
+
RETURNS TEXT AS $$
|
|
1571
|
+
DECLARE
|
|
1572
|
+
want char = substring(LOWER($3) FROM 1 FOR 1);
|
|
1573
|
+
have char := _get_context($1, $2);
|
|
1574
|
+
BEGIN
|
|
1575
|
+
IF have IS NOT NULL THEN
|
|
1576
|
+
RETURN is( _expand_context(have), _expand_context(want), $4 );
|
|
1577
|
+
END IF;
|
|
1578
|
+
|
|
1579
|
+
RETURN ok( false, $4 ) || E'\n' || diag(
|
|
1580
|
+
' Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1581
|
+
|| ') does not exist'
|
|
1582
|
+
);
|
|
1583
|
+
END;
|
|
1584
|
+
$$ LANGUAGE plpgsql;
|
|
1585
|
+
|
|
1586
|
+
-- cast_context_is( source_type, target_type, context )
|
|
1587
|
+
CREATE OR REPLACE FUNCTION cast_context_is( NAME, NAME, TEXT )
|
|
1588
|
+
RETURNS TEXT AS $$
|
|
1589
|
+
SELECT cast_context_is(
|
|
1590
|
+
$1, $2, $3,
|
|
1591
|
+
'Cast (' || quote_ident($1) || ' AS ' || quote_ident($2)
|
|
1592
|
+
|| ') context should be ' || _expand_context(substring(LOWER($3) FROM 1 FOR 1))
|
|
1593
|
+
);
|
|
1594
|
+
$$ LANGUAGE SQL;
|
|
1595
|
+
|
|
1596
|
+
-- _op_exists( left_type, schema, name, right_type, return_type )
|
|
1597
|
+
CREATE OR REPLACE FUNCTION _op_exists ( NAME, NAME, NAME, NAME, NAME )
|
|
1598
|
+
RETURNS BOOLEAN AS $$
|
|
1599
|
+
SELECT EXISTS (
|
|
1600
|
+
SELECT TRUE
|
|
1601
|
+
FROM pg_catalog.pg_operator o
|
|
1602
|
+
JOIN pg_catalog.pg_namespace n ON o.oprnamespace = n.oid
|
|
1603
|
+
WHERE n.nspname = $2
|
|
1604
|
+
AND o.oprname = $3
|
|
1605
|
+
AND CASE o.oprkind WHEN 'l' THEN $1 IS NULL
|
|
1606
|
+
ELSE _cmp_types(o.oprleft, _typename($1)) END
|
|
1607
|
+
AND CASE o.oprkind WHEN 'r' THEN $4 IS NULL
|
|
1608
|
+
ELSE _cmp_types(o.oprright, _typename($4)) END
|
|
1609
|
+
AND _cmp_types(o.oprresult, $5)
|
|
1610
|
+
);
|
|
1611
|
+
$$ LANGUAGE SQL;
|
|
1612
|
+
|
|
1613
|
+
-- _op_exists( left_type, name, right_type, return_type )
|
|
1614
|
+
CREATE OR REPLACE FUNCTION _op_exists ( NAME, NAME, NAME, NAME )
|
|
1615
|
+
RETURNS BOOLEAN AS $$
|
|
1616
|
+
SELECT EXISTS (
|
|
1617
|
+
SELECT TRUE
|
|
1618
|
+
FROM pg_catalog.pg_operator o
|
|
1619
|
+
WHERE pg_catalog.pg_operator_is_visible(o.oid)
|
|
1620
|
+
AND o.oprname = $2
|
|
1621
|
+
AND CASE o.oprkind WHEN 'l' THEN $1 IS NULL
|
|
1622
|
+
ELSE _cmp_types(o.oprleft, _typename($1)) END
|
|
1623
|
+
AND CASE o.oprkind WHEN 'r' THEN $3 IS NULL
|
|
1624
|
+
ELSE _cmp_types(o.oprright, _typename($3)) END
|
|
1625
|
+
AND _cmp_types(o.oprresult, $4)
|
|
1626
|
+
);
|
|
1627
|
+
$$ LANGUAGE SQL;
|
|
1628
|
+
|
|
1629
|
+
-- _op_exists( left_type, name, right_type )
|
|
1630
|
+
CREATE OR REPLACE FUNCTION _op_exists ( NAME, NAME, NAME )
|
|
1631
|
+
RETURNS BOOLEAN AS $$
|
|
1632
|
+
SELECT EXISTS (
|
|
1633
|
+
SELECT TRUE
|
|
1634
|
+
FROM pg_catalog.pg_operator o
|
|
1635
|
+
WHERE pg_catalog.pg_operator_is_visible(o.oid)
|
|
1636
|
+
AND o.oprname = $2
|
|
1637
|
+
AND CASE o.oprkind WHEN 'l' THEN $1 IS NULL
|
|
1638
|
+
ELSE _cmp_types(o.oprleft, _typename($1)) END
|
|
1639
|
+
AND CASE o.oprkind WHEN 'r' THEN $3 IS NULL
|
|
1640
|
+
ELSE _cmp_types(o.oprright, _typename($3)) END
|
|
1641
|
+
);
|
|
1642
|
+
$$ LANGUAGE SQL;
|
|
1643
|
+
|
|
1644
|
+
-- has_operator( left_type, schema, name, right_type, return_type, description )
|
|
1645
|
+
CREATE OR REPLACE FUNCTION has_operator ( NAME, NAME, NAME, NAME, NAME, TEXT )
|
|
1646
|
+
RETURNS TEXT AS $$
|
|
1647
|
+
SELECT ok( _op_exists($1, $2, $3, $4, $5 ), $6 );
|
|
1648
|
+
$$ LANGUAGE SQL;
|
|
1649
|
+
|
|
1650
|
+
-- has_operator( left_type, schema, name, right_type, return_type )
|
|
1651
|
+
CREATE OR REPLACE FUNCTION has_operator ( NAME, NAME, NAME, NAME, NAME )
|
|
1652
|
+
RETURNS TEXT AS $$
|
|
1653
|
+
SELECT ok(
|
|
1654
|
+
_op_exists($1, $2, $3, $4, $5 ),
|
|
1655
|
+
'Operator ' || quote_ident($2) || '.' || $3 || '(' || $1 || ',' || $4
|
|
1656
|
+
|| ') RETURNS ' || $5 || ' should exist'
|
|
1657
|
+
);
|
|
1658
|
+
$$ LANGUAGE SQL;
|
|
1659
|
+
|
|
1660
|
+
-- has_operator( left_type, name, right_type, return_type, description )
|
|
1661
|
+
CREATE OR REPLACE FUNCTION has_operator ( NAME, NAME, NAME, NAME, TEXT )
|
|
1662
|
+
RETURNS TEXT AS $$
|
|
1663
|
+
SELECT ok( _op_exists($1, $2, $3, $4 ), $5 );
|
|
1664
|
+
$$ LANGUAGE SQL;
|
|
1665
|
+
|
|
1666
|
+
-- has_operator( left_type, name, right_type, return_type )
|
|
1667
|
+
CREATE OR REPLACE FUNCTION has_operator ( NAME, NAME, NAME, NAME )
|
|
1668
|
+
RETURNS TEXT AS $$
|
|
1669
|
+
SELECT ok(
|
|
1670
|
+
_op_exists($1, $2, $3, $4 ),
|
|
1671
|
+
'Operator ' || $2 || '(' || $1 || ',' || $3
|
|
1672
|
+
|| ') RETURNS ' || $4 || ' should exist'
|
|
1673
|
+
);
|
|
1674
|
+
$$ LANGUAGE SQL;
|
|
1675
|
+
|
|
1676
|
+
-- has_operator( left_type, name, right_type, description )
|
|
1677
|
+
CREATE OR REPLACE FUNCTION has_operator ( NAME, NAME, NAME, TEXT )
|
|
1678
|
+
RETURNS TEXT AS $$
|
|
1679
|
+
SELECT ok( _op_exists($1, $2, $3 ), $4 );
|
|
1680
|
+
$$ LANGUAGE SQL;
|
|
1681
|
+
|
|
1682
|
+
-- has_operator( left_type, name, right_type )
|
|
1683
|
+
CREATE OR REPLACE FUNCTION has_operator ( NAME, NAME, NAME )
|
|
1684
|
+
RETURNS TEXT AS $$
|
|
1685
|
+
SELECT ok(
|
|
1686
|
+
_op_exists($1, $2, $3 ),
|
|
1687
|
+
'Operator ' || $2 || '(' || $1 || ',' || $3
|
|
1688
|
+
|| ') should exist'
|
|
1689
|
+
);
|
|
1690
|
+
$$ LANGUAGE SQL;
|
|
1691
|
+
|
|
1692
|
+
-- hasnt_operator( left_type, schema, name, right_type, return_type, description )
|
|
1693
|
+
CREATE OR REPLACE FUNCTION has_leftop ( NAME, NAME, NAME, NAME, TEXT )
|
|
1694
|
+
RETURNS TEXT AS $$
|
|
1695
|
+
SELECT ok( _op_exists(NULL, $1, $2, $3, $4), $5 );
|
|
1696
|
+
$$ LANGUAGE SQL;
|
|
1697
|
+
|
|
1698
|
+
-- has_leftop( schema, name, right_type, return_type )
|
|
1699
|
+
CREATE OR REPLACE FUNCTION has_leftop ( NAME, NAME, NAME, NAME )
|
|
1700
|
+
RETURNS TEXT AS $$
|
|
1701
|
+
SELECT ok(
|
|
1702
|
+
_op_exists(NULL, $1, $2, $3, $4 ),
|
|
1703
|
+
'Left operator ' || quote_ident($1) || '.' || $2 || '(NONE,'
|
|
1704
|
+
|| $3 || ') RETURNS ' || $4 || ' should exist'
|
|
1705
|
+
);
|
|
1706
|
+
$$ LANGUAGE SQL;
|
|
1707
|
+
|
|
1708
|
+
-- has_leftop( name, right_type, return_type, description )
|
|
1709
|
+
CREATE OR REPLACE FUNCTION has_leftop ( NAME, NAME, NAME, TEXT )
|
|
1710
|
+
RETURNS TEXT AS $$
|
|
1711
|
+
SELECT ok( _op_exists(NULL, $1, $2, $3), $4 );
|
|
1712
|
+
$$ LANGUAGE SQL;
|
|
1713
|
+
|
|
1714
|
+
-- has_leftop( name, right_type, return_type )
|
|
1715
|
+
CREATE OR REPLACE FUNCTION has_leftop ( NAME, NAME, NAME )
|
|
1716
|
+
RETURNS TEXT AS $$
|
|
1717
|
+
SELECT ok(
|
|
1718
|
+
_op_exists(NULL, $1, $2, $3 ),
|
|
1719
|
+
'Left operator ' || $1 || '(NONE,' || $2 || ') RETURNS ' || $3 || ' should exist'
|
|
1720
|
+
);
|
|
1721
|
+
$$ LANGUAGE SQL;
|
|
1722
|
+
|
|
1723
|
+
-- has_leftop( name, right_type, description )
|
|
1724
|
+
CREATE OR REPLACE FUNCTION has_leftop ( NAME, NAME, TEXT )
|
|
1725
|
+
RETURNS TEXT AS $$
|
|
1726
|
+
SELECT ok( _op_exists(NULL, $1, $2), $3 );
|
|
1727
|
+
$$ LANGUAGE SQL;
|
|
1728
|
+
|
|
1729
|
+
-- has_leftop( name, right_type )
|
|
1730
|
+
CREATE OR REPLACE FUNCTION has_leftop ( NAME, NAME )
|
|
1731
|
+
RETURNS TEXT AS $$
|
|
1732
|
+
SELECT ok(
|
|
1733
|
+
_op_exists(NULL, $1, $2 ),
|
|
1734
|
+
'Left operator ' || $1 || '(NONE,' || $2 || ') should exist'
|
|
1735
|
+
);
|
|
1736
|
+
$$ LANGUAGE SQL;
|
|
1737
|
+
|
|
1738
|
+
-- hasnt_leftop( schema, name, right_type, return_type, description )
|
|
1739
|
+
CREATE OR REPLACE FUNCTION has_rightop ( NAME, NAME, NAME, NAME, TEXT )
|
|
1740
|
+
RETURNS TEXT AS $$
|
|
1741
|
+
SELECT ok( _op_exists( $1, $2, $3, NULL, $4), $5 );
|
|
1742
|
+
$$ LANGUAGE SQL;
|
|
1743
|
+
|
|
1744
|
+
-- has_rightop( left_type, schema, name, return_type )
|
|
1745
|
+
CREATE OR REPLACE FUNCTION has_rightop ( NAME, NAME, NAME, NAME )
|
|
1746
|
+
RETURNS TEXT AS $$
|
|
1747
|
+
SELECT ok(
|
|
1748
|
+
_op_exists($1, $2, $3, NULL, $4 ),
|
|
1749
|
+
'Right operator ' || quote_ident($2) || '.' || $3 || '('
|
|
1750
|
+
|| $1 || ',NONE) RETURNS ' || $4 || ' should exist'
|
|
1751
|
+
);
|
|
1752
|
+
$$ LANGUAGE SQL;
|
|
1753
|
+
|
|
1754
|
+
-- has_rightop( left_type, name, return_type, description )
|
|
1755
|
+
CREATE OR REPLACE FUNCTION has_rightop ( NAME, NAME, NAME, TEXT )
|
|
1756
|
+
RETURNS TEXT AS $$
|
|
1757
|
+
SELECT ok( _op_exists( $1, $2, NULL, $3), $4 );
|
|
1758
|
+
$$ LANGUAGE SQL;
|
|
1759
|
+
|
|
1760
|
+
-- has_rightop( left_type, name, return_type )
|
|
1761
|
+
CREATE OR REPLACE FUNCTION has_rightop ( NAME, NAME, NAME )
|
|
1762
|
+
RETURNS TEXT AS $$
|
|
1763
|
+
SELECT ok(
|
|
1764
|
+
_op_exists($1, $2, NULL, $3 ),
|
|
1765
|
+
'Right operator ' || $2 || '('
|
|
1766
|
+
|| $1 || ',NONE) RETURNS ' || $3 || ' should exist'
|
|
1767
|
+
);
|
|
1768
|
+
$$ LANGUAGE SQL;
|
|
1769
|
+
|
|
1770
|
+
-- has_rightop( left_type, name, description )
|
|
1771
|
+
CREATE OR REPLACE FUNCTION has_rightop ( NAME, NAME, TEXT )
|
|
1772
|
+
RETURNS TEXT AS $$
|
|
1773
|
+
SELECT ok( _op_exists( $1, $2, NULL), $3 );
|
|
1774
|
+
$$ LANGUAGE SQL;
|
|
1775
|
+
|
|
1776
|
+
-- has_rightop( left_type, name )
|
|
1777
|
+
CREATE OR REPLACE FUNCTION has_rightop ( NAME, NAME )
|
|
1778
|
+
RETURNS TEXT AS $$
|
|
1779
|
+
SELECT ok(
|
|
1780
|
+
_op_exists($1, $2, NULL ),
|
|
1781
|
+
'Right operator ' || $2 || '(' || $1 || ',NONE) should exist'
|
|
1782
|
+
);
|
|
1783
|
+
$$ LANGUAGE SQL;
|
|
1784
|
+
|
|
1785
|
+
-- hasnt_rightop( left_type, schema, name, return_type, description )
|
|
1786
|
+
CREATE OR REPLACE FUNCTION _is_trusted( NAME )
|
|
1787
|
+
RETURNS BOOLEAN AS $$
|
|
1788
|
+
SELECT lanpltrusted FROM pg_catalog.pg_language WHERE lanname = $1;
|
|
1789
|
+
$$ LANGUAGE SQL;
|
|
1790
|
+
|
|
1791
|
+
-- has_language( language, description)
|
|
1792
|
+
CREATE OR REPLACE FUNCTION has_language( NAME, TEXT )
|
|
1793
|
+
RETURNS TEXT AS $$
|
|
1794
|
+
SELECT ok( _is_trusted($1) IS NOT NULL, $2 );
|
|
1795
|
+
$$ LANGUAGE SQL;
|
|
1796
|
+
|
|
1797
|
+
-- has_language( language )
|
|
1798
|
+
CREATE OR REPLACE FUNCTION has_language( NAME )
|
|
1799
|
+
RETURNS TEXT AS $$
|
|
1800
|
+
SELECT ok( _is_trusted($1) IS NOT NULL, 'Procedural language ' || quote_ident($1) || ' should exist' );
|
|
1801
|
+
$$ LANGUAGE SQL;
|
|
1802
|
+
|
|
1803
|
+
-- hasnt_language( language, description)
|
|
1804
|
+
CREATE OR REPLACE FUNCTION hasnt_language( NAME, TEXT )
|
|
1805
|
+
RETURNS TEXT AS $$
|
|
1806
|
+
SELECT ok( _is_trusted($1) IS NULL, $2 );
|
|
1807
|
+
$$ LANGUAGE SQL;
|
|
1808
|
+
|
|
1809
|
+
-- hasnt_language( language )
|
|
1810
|
+
CREATE OR REPLACE FUNCTION hasnt_language( NAME )
|
|
1811
|
+
RETURNS TEXT AS $$
|
|
1812
|
+
SELECT ok( _is_trusted($1) IS NULL, 'Procedural language ' || quote_ident($1) || ' should not exist' );
|
|
1813
|
+
$$ LANGUAGE SQL;
|
|
1814
|
+
|
|
1815
|
+
-- language_is_trusted( language, description )
|
|
1816
|
+
CREATE OR REPLACE FUNCTION language_is_trusted( NAME, TEXT )
|
|
1817
|
+
RETURNS TEXT AS $$
|
|
1818
|
+
DECLARE
|
|
1819
|
+
is_trusted boolean := _is_trusted($1);
|
|
1820
|
+
BEGIN
|
|
1821
|
+
IF is_trusted IS NULL THEN
|
|
1822
|
+
RETURN fail( $2 ) || E'\n' || diag( ' Procedural language ' || quote_ident($1) || ' does not exist') ;
|
|
1823
|
+
END IF;
|
|
1824
|
+
RETURN ok( is_trusted, $2 );
|
|
1825
|
+
END;
|
|
1826
|
+
$$ LANGUAGE plpgsql;
|
|
1827
|
+
|
|
1828
|
+
-- language_is_trusted( language )
|
|
1829
|
+
CREATE OR REPLACE FUNCTION language_is_trusted( NAME )
|
|
1830
|
+
RETURNS TEXT AS $$
|
|
1831
|
+
SELECT language_is_trusted($1, 'Procedural language ' || quote_ident($1) || ' should be trusted' );
|
|
1832
|
+
$$ LANGUAGE SQL;
|
|
1833
|
+
|
|
1834
|
+
CREATE OR REPLACE FUNCTION _opc_exists( NAME, NAME )
|
|
1835
|
+
RETURNS BOOLEAN AS $$
|
|
1836
|
+
SELECT EXISTS (
|
|
1837
|
+
SELECT TRUE
|
|
1838
|
+
FROM pg_catalog.pg_opclass oc
|
|
1839
|
+
JOIN pg_catalog.pg_namespace n ON oc.opcnamespace = n.oid
|
|
1840
|
+
WHERE n.nspname = $1
|
|
1841
|
+
AND oc.opcname = $2
|
|
1842
|
+
);
|
|
1843
|
+
$$ LANGUAGE SQL;
|
|
1844
|
+
|
|
1845
|
+
CREATE OR REPLACE FUNCTION _opc_exists( NAME )
|
|
1846
|
+
RETURNS BOOLEAN AS $$
|
|
1847
|
+
SELECT EXISTS (
|
|
1848
|
+
SELECT TRUE
|
|
1849
|
+
FROM pg_catalog.pg_opclass oc
|
|
1850
|
+
WHERE oc.opcname = $1
|
|
1851
|
+
AND pg_opclass_is_visible(oid)
|
|
1852
|
+
);
|
|
1853
|
+
$$ LANGUAGE SQL;
|
|
1854
|
+
|
|
1855
|
+
-- has_opclass( schema, name, description )
|
|
1856
|
+
CREATE OR REPLACE FUNCTION has_opclass( NAME, NAME, TEXT )
|
|
1857
|
+
RETURNS TEXT AS $$
|
|
1858
|
+
SELECT ok( _opc_exists( $1, $2 ), $3 );
|
|
1859
|
+
$$ LANGUAGE SQL;
|
|
1860
|
+
|
|
1861
|
+
-- has_opclass( schema, name )
|
|
1862
|
+
CREATE OR REPLACE FUNCTION has_opclass( NAME, NAME )
|
|
1863
|
+
RETURNS TEXT AS $$
|
|
1864
|
+
SELECT ok( _opc_exists( $1, $2 ), 'Operator class ' || quote_ident($1) || '.' || quote_ident($2) || ' should exist' );
|
|
1865
|
+
$$ LANGUAGE SQL;
|
|
1866
|
+
|
|
1867
|
+
-- has_opclass( name, description )
|
|
1868
|
+
CREATE OR REPLACE FUNCTION has_opclass( NAME, TEXT )
|
|
1869
|
+
RETURNS TEXT AS $$
|
|
1870
|
+
SELECT ok( _opc_exists( $1 ), $2)
|
|
1871
|
+
$$ LANGUAGE SQL;
|
|
1872
|
+
|
|
1873
|
+
-- has_opclass( name )
|
|
1874
|
+
CREATE OR REPLACE FUNCTION has_opclass( NAME )
|
|
1875
|
+
RETURNS TEXT AS $$
|
|
1876
|
+
SELECT ok( _opc_exists( $1 ), 'Operator class ' || quote_ident($1) || ' should exist' );
|
|
1877
|
+
$$ LANGUAGE SQL;
|
|
1878
|
+
|
|
1879
|
+
-- hasnt_opclass( schema, name, description )
|
|
1880
|
+
CREATE OR REPLACE FUNCTION hasnt_opclass( NAME, NAME, TEXT )
|
|
1881
|
+
RETURNS TEXT AS $$
|
|
1882
|
+
SELECT ok( NOT _opc_exists( $1, $2 ), $3 );
|
|
1883
|
+
$$ LANGUAGE SQL;
|
|
1884
|
+
|
|
1885
|
+
-- hasnt_opclass( schema, name )
|
|
1886
|
+
CREATE OR REPLACE FUNCTION hasnt_opclass( NAME, NAME )
|
|
1887
|
+
RETURNS TEXT AS $$
|
|
1888
|
+
SELECT ok( NOT _opc_exists( $1, $2 ), 'Operator class ' || quote_ident($1) || '.' || quote_ident($2) || ' should not exist' );
|
|
1889
|
+
$$ LANGUAGE SQL;
|
|
1890
|
+
|
|
1891
|
+
-- hasnt_opclass( name, description )
|
|
1892
|
+
CREATE OR REPLACE FUNCTION hasnt_opclass( NAME, TEXT )
|
|
1893
|
+
RETURNS TEXT AS $$
|
|
1894
|
+
SELECT ok( NOT _opc_exists( $1 ), $2)
|
|
1895
|
+
$$ LANGUAGE SQL;
|
|
1896
|
+
|
|
1897
|
+
-- hasnt_opclass( name )
|
|
1898
|
+
CREATE OR REPLACE FUNCTION hasnt_opclass( NAME )
|
|
1899
|
+
RETURNS TEXT AS $$
|
|
1900
|
+
SELECT ok( NOT _opc_exists( $1 ), 'Operator class ' || quote_ident($1) || ' should not exist' );
|
|
1901
|
+
$$ LANGUAGE SQL;
|
|
1902
|
+
|
|
1903
|
+
-- opclasses_are( schema, opclasses[], description )
|
|
1904
|
+
CREATE OR REPLACE FUNCTION _nosuch( NAME, NAME, NAME[])
|
|
1905
|
+
RETURNS TEXT AS $$
|
|
1906
|
+
SELECT E'\n' || diag(
|
|
1907
|
+
' Function '
|
|
1908
|
+
|| CASE WHEN $1 IS NOT NULL THEN quote_ident($1) || '.' ELSE '' END
|
|
1909
|
+
|| quote_ident($2) || '('
|
|
1910
|
+
|| array_to_string($3, ', ') || ') does not exist'
|
|
1911
|
+
);
|
|
1912
|
+
$$ LANGUAGE SQL IMMUTABLE;
|
|
1913
|
+
|
|
1914
|
+
CREATE OR REPLACE FUNCTION _func_compare( NAME, NAME, NAME[], anyelement, anyelement, TEXT)
|
|
1915
|
+
RETURNS TEXT AS $$
|
|
1916
|
+
SELECT CASE WHEN $4 IS NULL
|
|
1917
|
+
THEN ok( FALSE, $6 ) || _nosuch($1, $2, $3)
|
|
1918
|
+
ELSE is( $4, $5, $6 )
|
|
1919
|
+
END;
|
|
1920
|
+
$$ LANGUAGE SQL;
|
|
1921
|
+
|
|
1922
|
+
CREATE OR REPLACE FUNCTION _func_compare( NAME, NAME, NAME[], boolean, TEXT)
|
|
1923
|
+
RETURNS TEXT AS $$
|
|
1924
|
+
SELECT CASE WHEN $4 IS NULL
|
|
1925
|
+
THEN ok( FALSE, $5 ) || _nosuch($1, $2, $3)
|
|
1926
|
+
ELSE ok( $4, $5 )
|
|
1927
|
+
END;
|
|
1928
|
+
$$ LANGUAGE sql;
|
|
1929
|
+
|
|
1930
|
+
CREATE OR REPLACE FUNCTION _func_compare( NAME, NAME, anyelement, anyelement, TEXT)
|
|
1931
|
+
RETURNS TEXT AS $$
|
|
1932
|
+
SELECT CASE WHEN $3 IS NULL
|
|
1933
|
+
THEN ok( FALSE, $5 ) || _nosuch($1, $2, '{}')
|
|
1934
|
+
ELSE is( $3, $4, $5 )
|
|
1935
|
+
END;
|
|
1936
|
+
$$ LANGUAGE SQL;
|
|
1937
|
+
|
|
1938
|
+
CREATE OR REPLACE FUNCTION _func_compare( NAME, NAME, boolean, TEXT)
|
|
1939
|
+
RETURNS TEXT AS $$
|
|
1940
|
+
SELECT CASE WHEN $3 IS NULL
|
|
1941
|
+
THEN ok( FALSE, $4 ) || _nosuch($1, $2, '{}')
|
|
1942
|
+
ELSE ok( $3, $4 )
|
|
1943
|
+
END;
|
|
1944
|
+
$$ LANGUAGE SQL;
|
|
1945
|
+
|
|
1946
|
+
CREATE OR REPLACE FUNCTION _lang ( NAME, NAME, NAME[] )
|
|
1947
|
+
RETURNS NAME AS $$
|
|
1948
|
+
SELECT l.lanname
|
|
1949
|
+
FROM tap_funky f
|
|
1950
|
+
JOIN pg_catalog.pg_language l ON f.langoid = l.oid
|
|
1951
|
+
WHERE f.schema = $1
|
|
1952
|
+
and f.name = $2
|
|
1953
|
+
AND f.args = _funkargs($3)
|
|
1954
|
+
$$ LANGUAGE SQL;
|
|
1955
|
+
|
|
1956
|
+
CREATE OR REPLACE FUNCTION _lang ( NAME, NAME )
|
|
1957
|
+
RETURNS NAME AS $$
|
|
1958
|
+
SELECT l.lanname
|
|
1959
|
+
FROM tap_funky f
|
|
1960
|
+
JOIN pg_catalog.pg_language l ON f.langoid = l.oid
|
|
1961
|
+
WHERE f.schema = $1
|
|
1962
|
+
and f.name = $2
|
|
1963
|
+
$$ LANGUAGE SQL;
|
|
1964
|
+
|
|
1965
|
+
CREATE OR REPLACE FUNCTION _lang ( NAME, NAME[] )
|
|
1966
|
+
RETURNS NAME AS $$
|
|
1967
|
+
SELECT l.lanname
|
|
1968
|
+
FROM tap_funky f
|
|
1969
|
+
JOIN pg_catalog.pg_language l ON f.langoid = l.oid
|
|
1970
|
+
WHERE f.name = $1
|
|
1971
|
+
AND f.args = _funkargs($2)
|
|
1972
|
+
AND f.is_visible;
|
|
1973
|
+
$$ LANGUAGE SQL;
|
|
1974
|
+
|
|
1975
|
+
CREATE OR REPLACE FUNCTION _lang ( NAME )
|
|
1976
|
+
RETURNS NAME AS $$
|
|
1977
|
+
SELECT l.lanname
|
|
1978
|
+
FROM tap_funky f
|
|
1979
|
+
JOIN pg_catalog.pg_language l ON f.langoid = l.oid
|
|
1980
|
+
WHERE f.name = $1
|
|
1981
|
+
AND f.is_visible;
|
|
1982
|
+
$$ LANGUAGE SQL;
|
|
1983
|
+
|
|
1984
|
+
-- function_lang_is( schema, function, args[], language, description )
|
|
1985
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME, NAME[], NAME, TEXT )
|
|
1986
|
+
RETURNS TEXT AS $$
|
|
1987
|
+
SELECT _func_compare($1, $2, $3, _lang($1, $2, $3), $4, $5 );
|
|
1988
|
+
$$ LANGUAGE SQL;
|
|
1989
|
+
|
|
1990
|
+
-- function_lang_is( schema, function, args[], language )
|
|
1991
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME, NAME[], NAME )
|
|
1992
|
+
RETURNS TEXT AS $$
|
|
1993
|
+
SELECT function_lang_is(
|
|
1994
|
+
$1, $2, $3, $4,
|
|
1995
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
1996
|
+
array_to_string($3, ', ') || ') should be written in ' || quote_ident($4)
|
|
1997
|
+
);
|
|
1998
|
+
$$ LANGUAGE SQL;
|
|
1999
|
+
|
|
2000
|
+
-- function_lang_is( schema, function, language, description )
|
|
2001
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME, NAME, TEXT )
|
|
2002
|
+
RETURNS TEXT AS $$
|
|
2003
|
+
SELECT _func_compare($1, $2, _lang($1, $2), $3, $4 );
|
|
2004
|
+
$$ LANGUAGE SQL;
|
|
2005
|
+
|
|
2006
|
+
-- function_lang_is( schema, function, language )
|
|
2007
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME, NAME )
|
|
2008
|
+
RETURNS TEXT AS $$
|
|
2009
|
+
SELECT function_lang_is(
|
|
2010
|
+
$1, $2, $3,
|
|
2011
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2)
|
|
2012
|
+
|| '() should be written in ' || quote_ident($3)
|
|
2013
|
+
);
|
|
2014
|
+
$$ LANGUAGE SQL;
|
|
2015
|
+
|
|
2016
|
+
-- function_lang_is( function, args[], language, description )
|
|
2017
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME[], NAME, TEXT )
|
|
2018
|
+
RETURNS TEXT AS $$
|
|
2019
|
+
SELECT _func_compare(NULL, $1, $2, _lang($1, $2), $3, $4 );
|
|
2020
|
+
$$ LANGUAGE SQL;
|
|
2021
|
+
|
|
2022
|
+
-- function_lang_is( function, args[], language )
|
|
2023
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME[], NAME )
|
|
2024
|
+
RETURNS TEXT AS $$
|
|
2025
|
+
SELECT function_lang_is(
|
|
2026
|
+
$1, $2, $3,
|
|
2027
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2028
|
+
array_to_string($2, ', ') || ') should be written in ' || quote_ident($3)
|
|
2029
|
+
);
|
|
2030
|
+
$$ LANGUAGE SQL;
|
|
2031
|
+
|
|
2032
|
+
-- function_lang_is( function, language, description )
|
|
2033
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME, TEXT )
|
|
2034
|
+
RETURNS TEXT AS $$
|
|
2035
|
+
SELECT _func_compare(NULL, $1, _lang($1), $2, $3 );
|
|
2036
|
+
$$ LANGUAGE SQL;
|
|
2037
|
+
|
|
2038
|
+
-- function_lang_is( function, language )
|
|
2039
|
+
CREATE OR REPLACE FUNCTION function_lang_is( NAME, NAME )
|
|
2040
|
+
RETURNS TEXT AS $$
|
|
2041
|
+
SELECT function_lang_is(
|
|
2042
|
+
$1, $2,
|
|
2043
|
+
'Function ' || quote_ident($1)
|
|
2044
|
+
|| '() should be written in ' || quote_ident($2)
|
|
2045
|
+
);
|
|
2046
|
+
$$ LANGUAGE SQL;
|
|
2047
|
+
|
|
2048
|
+
CREATE OR REPLACE FUNCTION _returns ( NAME, NAME, NAME[] )
|
|
2049
|
+
RETURNS TEXT AS $$
|
|
2050
|
+
SELECT returns
|
|
2051
|
+
FROM tap_funky
|
|
2052
|
+
WHERE schema = $1
|
|
2053
|
+
AND name = $2
|
|
2054
|
+
AND args = _funkargs($3)
|
|
2055
|
+
$$ LANGUAGE SQL;
|
|
2056
|
+
|
|
2057
|
+
CREATE OR REPLACE FUNCTION _returns ( NAME, NAME )
|
|
2058
|
+
RETURNS TEXT AS $$
|
|
2059
|
+
SELECT returns FROM tap_funky WHERE schema = $1 AND name = $2
|
|
2060
|
+
$$ LANGUAGE SQL;
|
|
2061
|
+
|
|
2062
|
+
CREATE OR REPLACE FUNCTION _returns ( NAME, NAME[] )
|
|
2063
|
+
RETURNS TEXT AS $$
|
|
2064
|
+
SELECT returns
|
|
2065
|
+
FROM tap_funky
|
|
2066
|
+
WHERE name = $1
|
|
2067
|
+
AND args = _funkargs($2)
|
|
2068
|
+
AND is_visible;
|
|
2069
|
+
$$ LANGUAGE SQL;
|
|
2070
|
+
|
|
2071
|
+
CREATE OR REPLACE FUNCTION _returns ( NAME )
|
|
2072
|
+
RETURNS TEXT AS $$
|
|
2073
|
+
SELECT returns FROM tap_funky WHERE name = $1 AND is_visible;
|
|
2074
|
+
$$ LANGUAGE SQL;
|
|
2075
|
+
|
|
2076
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, NAME, NAME[], TEXT, TEXT )
|
|
2077
|
+
RETURNS TEXT AS $$
|
|
2078
|
+
SELECT _func_compare($1, $2, $3, _returns($1, $2, $3), _retval($4), $5 );
|
|
2079
|
+
$$ LANGUAGE SQL;
|
|
2080
|
+
|
|
2081
|
+
-- function_returns( schema, function, args[], type )
|
|
2082
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, NAME, NAME[], TEXT )
|
|
2083
|
+
RETURNS TEXT AS $$
|
|
2084
|
+
SELECT function_returns(
|
|
2085
|
+
$1, $2, $3, $4,
|
|
2086
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2087
|
+
array_to_string($3, ', ') || ') should return ' || $4
|
|
2088
|
+
);
|
|
2089
|
+
$$ LANGUAGE SQL;
|
|
2090
|
+
|
|
2091
|
+
-- function_returns( schema, function, type, description )
|
|
2092
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, NAME, TEXT, TEXT )
|
|
2093
|
+
RETURNS TEXT AS $$
|
|
2094
|
+
SELECT _func_compare($1, $2, _returns($1, $2), _retval($3), $4 );
|
|
2095
|
+
$$ LANGUAGE SQL;
|
|
2096
|
+
|
|
2097
|
+
-- function_returns( schema, function, type )
|
|
2098
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, NAME, TEXT )
|
|
2099
|
+
RETURNS TEXT AS $$
|
|
2100
|
+
SELECT function_returns(
|
|
2101
|
+
$1, $2, $3,
|
|
2102
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2)
|
|
2103
|
+
|| '() should return ' || $3
|
|
2104
|
+
);
|
|
2105
|
+
$$ LANGUAGE SQL;
|
|
2106
|
+
|
|
2107
|
+
-- function_returns( function, args[], type, description )
|
|
2108
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, NAME[], TEXT, TEXT )
|
|
2109
|
+
RETURNS TEXT AS $$
|
|
2110
|
+
SELECT _func_compare(NULL, $1, $2, _returns($1, $2), _retval($3), $4 );
|
|
2111
|
+
$$ LANGUAGE SQL;
|
|
2112
|
+
|
|
2113
|
+
-- function_returns( function, args[], type )
|
|
2114
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, NAME[], TEXT )
|
|
2115
|
+
RETURNS TEXT AS $$
|
|
2116
|
+
SELECT function_returns(
|
|
2117
|
+
$1, $2, $3,
|
|
2118
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2119
|
+
array_to_string($2, ', ') || ') should return ' || $3
|
|
2120
|
+
);
|
|
2121
|
+
$$ LANGUAGE SQL;
|
|
2122
|
+
|
|
2123
|
+
-- function_returns( function, type, description )
|
|
2124
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, TEXT, TEXT )
|
|
2125
|
+
RETURNS TEXT AS $$
|
|
2126
|
+
SELECT _func_compare(NULL, $1, _returns($1), _retval($2), $3 );
|
|
2127
|
+
$$ LANGUAGE SQL;
|
|
2128
|
+
|
|
2129
|
+
-- function_returns( function, type )
|
|
2130
|
+
CREATE OR REPLACE FUNCTION function_returns( NAME, TEXT )
|
|
2131
|
+
RETURNS TEXT AS $$
|
|
2132
|
+
SELECT function_returns(
|
|
2133
|
+
$1, $2,
|
|
2134
|
+
'Function ' || quote_ident($1) || '() should return ' || $2
|
|
2135
|
+
);
|
|
2136
|
+
$$ LANGUAGE SQL;
|
|
2137
|
+
|
|
2138
|
+
CREATE OR REPLACE FUNCTION _definer ( NAME, NAME, NAME[] )
|
|
2139
|
+
RETURNS BOOLEAN AS $$
|
|
2140
|
+
SELECT is_definer
|
|
2141
|
+
FROM tap_funky
|
|
2142
|
+
WHERE schema = $1
|
|
2143
|
+
AND name = $2
|
|
2144
|
+
AND args = _funkargs($3)
|
|
2145
|
+
$$ LANGUAGE SQL;
|
|
2146
|
+
|
|
2147
|
+
CREATE OR REPLACE FUNCTION _definer ( NAME, NAME )
|
|
2148
|
+
RETURNS BOOLEAN AS $$
|
|
2149
|
+
SELECT is_definer FROM tap_funky WHERE schema = $1 AND name = $2
|
|
2150
|
+
$$ LANGUAGE SQL;
|
|
2151
|
+
|
|
2152
|
+
CREATE OR REPLACE FUNCTION _definer ( NAME, NAME[] )
|
|
2153
|
+
RETURNS BOOLEAN AS $$
|
|
2154
|
+
SELECT is_definer
|
|
2155
|
+
FROM tap_funky
|
|
2156
|
+
WHERE name = $1
|
|
2157
|
+
AND args = _funkargs($2)
|
|
2158
|
+
AND is_visible;
|
|
2159
|
+
$$ LANGUAGE SQL;
|
|
2160
|
+
|
|
2161
|
+
CREATE OR REPLACE FUNCTION _definer ( NAME )
|
|
2162
|
+
RETURNS BOOLEAN AS $$
|
|
2163
|
+
SELECT is_definer FROM tap_funky WHERE name = $1 AND is_visible;
|
|
2164
|
+
$$ LANGUAGE SQL;
|
|
2165
|
+
|
|
2166
|
+
-- is_definer( schema, function, args[], description )
|
|
2167
|
+
CREATE OR REPLACE FUNCTION is_definer ( NAME, NAME, NAME[], TEXT )
|
|
2168
|
+
RETURNS TEXT AS $$
|
|
2169
|
+
SELECT _func_compare($1, $2, $3, _definer($1, $2, $3), $4 );
|
|
2170
|
+
$$ LANGUAGE SQL;
|
|
2171
|
+
|
|
2172
|
+
-- is_definer( schema, function, args[] )
|
|
2173
|
+
CREATE OR REPLACE FUNCTION is_definer( NAME, NAME, NAME[] )
|
|
2174
|
+
RETURNS TEXT AS $$
|
|
2175
|
+
SELECT ok(
|
|
2176
|
+
_definer($1, $2, $3),
|
|
2177
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2178
|
+
array_to_string($3, ', ') || ') should be security definer'
|
|
2179
|
+
);
|
|
2180
|
+
$$ LANGUAGE sql;
|
|
2181
|
+
|
|
2182
|
+
-- is_definer( schema, function, description )
|
|
2183
|
+
CREATE OR REPLACE FUNCTION is_definer ( NAME, NAME, TEXT )
|
|
2184
|
+
RETURNS TEXT AS $$
|
|
2185
|
+
SELECT _func_compare($1, $2, _definer($1, $2), $3 );
|
|
2186
|
+
$$ LANGUAGE SQL;
|
|
2187
|
+
|
|
2188
|
+
-- is_definer( schema, function )
|
|
2189
|
+
CREATE OR REPLACE FUNCTION is_definer( NAME, NAME )
|
|
2190
|
+
RETURNS TEXT AS $$
|
|
2191
|
+
SELECT ok(
|
|
2192
|
+
_definer($1, $2),
|
|
2193
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should be security definer'
|
|
2194
|
+
);
|
|
2195
|
+
$$ LANGUAGE sql;
|
|
2196
|
+
|
|
2197
|
+
-- is_definer( function, args[], description )
|
|
2198
|
+
CREATE OR REPLACE FUNCTION is_definer ( NAME, NAME[], TEXT )
|
|
2199
|
+
RETURNS TEXT AS $$
|
|
2200
|
+
SELECT _func_compare(NULL, $1, $2, _definer($1, $2), $3 );
|
|
2201
|
+
$$ LANGUAGE SQL;
|
|
2202
|
+
|
|
2203
|
+
-- is_definer( function, args[] )
|
|
2204
|
+
CREATE OR REPLACE FUNCTION is_definer( NAME, NAME[] )
|
|
2205
|
+
RETURNS TEXT AS $$
|
|
2206
|
+
SELECT ok(
|
|
2207
|
+
_definer($1, $2),
|
|
2208
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2209
|
+
array_to_string($2, ', ') || ') should be security definer'
|
|
2210
|
+
);
|
|
2211
|
+
$$ LANGUAGE sql;
|
|
2212
|
+
|
|
2213
|
+
-- is_definer( function, description )
|
|
2214
|
+
CREATE OR REPLACE FUNCTION is_definer( NAME, TEXT )
|
|
2215
|
+
RETURNS TEXT AS $$
|
|
2216
|
+
SELECT _func_compare(NULL, $1, _definer($1), $2 );
|
|
2217
|
+
$$ LANGUAGE sql;
|
|
2218
|
+
|
|
2219
|
+
-- is_definer( function )
|
|
2220
|
+
CREATE OR REPLACE FUNCTION is_definer( NAME )
|
|
2221
|
+
RETURNS TEXT AS $$
|
|
2222
|
+
SELECT ok( _definer($1), 'Function ' || quote_ident($1) || '() should be security definer' );
|
|
2223
|
+
$$ LANGUAGE sql;
|
|
2224
|
+
|
|
2225
|
+
-- isnt_definer( schema, function, args[], description )
|
|
2226
|
+
CREATE OR REPLACE FUNCTION isnt_definer ( NAME, NAME, NAME[], TEXT )
|
|
2227
|
+
RETURNS TEXT AS $$
|
|
2228
|
+
SELECT _func_compare($1, $2, $3, NOT _definer($1, $2, $3), $4 );
|
|
2229
|
+
$$ LANGUAGE SQL;
|
|
2230
|
+
|
|
2231
|
+
-- isnt_definer( schema, function, args[] )
|
|
2232
|
+
CREATE OR REPLACE FUNCTION isnt_definer( NAME, NAME, NAME[] )
|
|
2233
|
+
RETURNS TEXT AS $$
|
|
2234
|
+
SELECT ok(
|
|
2235
|
+
NOT _definer($1, $2, $3),
|
|
2236
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2237
|
+
array_to_string($3, ', ') || ') should not be security definer'
|
|
2238
|
+
);
|
|
2239
|
+
$$ LANGUAGE sql;
|
|
2240
|
+
|
|
2241
|
+
-- isnt_definer( schema, function, description )
|
|
2242
|
+
CREATE OR REPLACE FUNCTION isnt_definer ( NAME, NAME, TEXT )
|
|
2243
|
+
RETURNS TEXT AS $$
|
|
2244
|
+
SELECT _func_compare($1, $2, NOT _definer($1, $2), $3 );
|
|
2245
|
+
$$ LANGUAGE SQL;
|
|
2246
|
+
|
|
2247
|
+
-- isnt_definer( schema, function )
|
|
2248
|
+
CREATE OR REPLACE FUNCTION isnt_definer( NAME, NAME )
|
|
2249
|
+
RETURNS TEXT AS $$
|
|
2250
|
+
SELECT ok(
|
|
2251
|
+
NOT _definer($1, $2),
|
|
2252
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should not be security definer'
|
|
2253
|
+
);
|
|
2254
|
+
$$ LANGUAGE sql;
|
|
2255
|
+
|
|
2256
|
+
-- isnt_definer( function, args[], description )
|
|
2257
|
+
CREATE OR REPLACE FUNCTION isnt_definer ( NAME, NAME[], TEXT )
|
|
2258
|
+
RETURNS TEXT AS $$
|
|
2259
|
+
SELECT _func_compare(NULL, $1, $2, NOT _definer($1, $2), $3 );
|
|
2260
|
+
$$ LANGUAGE SQL;
|
|
2261
|
+
|
|
2262
|
+
-- isnt_definer( function, args[] )
|
|
2263
|
+
CREATE OR REPLACE FUNCTION isnt_definer( NAME, NAME[] )
|
|
2264
|
+
RETURNS TEXT AS $$
|
|
2265
|
+
SELECT ok(
|
|
2266
|
+
NOT _definer($1, $2),
|
|
2267
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2268
|
+
array_to_string($2, ', ') || ') should not be security definer'
|
|
2269
|
+
);
|
|
2270
|
+
$$ LANGUAGE sql;
|
|
2271
|
+
|
|
2272
|
+
-- isnt_definer( function, description )
|
|
2273
|
+
CREATE OR REPLACE FUNCTION isnt_definer( NAME, TEXT )
|
|
2274
|
+
RETURNS TEXT AS $$
|
|
2275
|
+
SELECT _func_compare(NULL, $1, NOT _definer($1), $2 );
|
|
2276
|
+
$$ LANGUAGE sql;
|
|
2277
|
+
|
|
2278
|
+
-- isnt_definer( function )
|
|
2279
|
+
CREATE OR REPLACE FUNCTION isnt_definer( NAME )
|
|
2280
|
+
RETURNS TEXT AS $$
|
|
2281
|
+
SELECT ok( NOT _definer($1), 'Function ' || quote_ident($1) || '() should not be security definer' );
|
|
2282
|
+
$$ LANGUAGE sql;
|
|
2283
|
+
|
|
2284
|
+
-- Returns true if the specified function exists and is the specified type,
|
|
2285
|
+
-- false if it exists and is not the specified type, and NULL if it does not
|
|
2286
|
+
-- exist. Types are f for a normal function, p for a procedure, a for an
|
|
2287
|
+
-- aggregate function, or w for a window function
|
|
2288
|
+
-- _type_func(type, schema, function, args[])
|
|
2289
|
+
CREATE OR REPLACE FUNCTION _type_func ( "char", NAME, NAME, NAME[] )
|
|
2290
|
+
RETURNS BOOLEAN AS $$
|
|
2291
|
+
SELECT kind = $1
|
|
2292
|
+
FROM tap_funky
|
|
2293
|
+
WHERE schema = $2
|
|
2294
|
+
AND name = $3
|
|
2295
|
+
AND args = _funkargs($4)
|
|
2296
|
+
$$ LANGUAGE SQL;
|
|
2297
|
+
|
|
2298
|
+
-- _type_func(type, schema, function)
|
|
2299
|
+
CREATE OR REPLACE FUNCTION _type_func ( "char", NAME, NAME )
|
|
2300
|
+
RETURNS BOOLEAN AS $$
|
|
2301
|
+
SELECT kind = $1 FROM tap_funky WHERE schema = $2 AND name = $3
|
|
2302
|
+
$$ LANGUAGE SQL;
|
|
2303
|
+
|
|
2304
|
+
-- _type_func(type, function, args[])
|
|
2305
|
+
CREATE OR REPLACE FUNCTION _type_func ( "char", NAME, NAME[] )
|
|
2306
|
+
RETURNS BOOLEAN AS $$
|
|
2307
|
+
SELECT kind = $1
|
|
2308
|
+
FROM tap_funky
|
|
2309
|
+
WHERE name = $2
|
|
2310
|
+
AND args = _funkargs($3)
|
|
2311
|
+
AND is_visible;
|
|
2312
|
+
$$ LANGUAGE SQL;
|
|
2313
|
+
|
|
2314
|
+
-- _type_func(type, function)
|
|
2315
|
+
CREATE OR REPLACE FUNCTION _type_func ( "char", NAME )
|
|
2316
|
+
RETURNS BOOLEAN AS $$
|
|
2317
|
+
SELECT kind = $1 FROM tap_funky WHERE name = $2 AND is_visible;
|
|
2318
|
+
$$ LANGUAGE SQL;
|
|
2319
|
+
|
|
2320
|
+
-- is_aggregate( schema, function, args[], description )
|
|
2321
|
+
CREATE OR REPLACE FUNCTION is_aggregate ( NAME, NAME, NAME[], TEXT )
|
|
2322
|
+
RETURNS TEXT AS $$
|
|
2323
|
+
SELECT _func_compare($1, $2, $3, _type_func( 'a', $1, $2, $3), $4 );
|
|
2324
|
+
$$ LANGUAGE SQL;
|
|
2325
|
+
|
|
2326
|
+
-- is_aggregate( schema, function, args[] )
|
|
2327
|
+
CREATE OR REPLACE FUNCTION is_aggregate( NAME, NAME, NAME[] )
|
|
2328
|
+
RETURNS TEXT AS $$
|
|
2329
|
+
SELECT _func_compare(
|
|
2330
|
+
$1, $2, $3, _type_func('a', $1, $2, $3),
|
|
2331
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2332
|
+
array_to_string($3, ', ') || ') should be an aggregate function'
|
|
2333
|
+
);
|
|
2334
|
+
$$ LANGUAGE sql;
|
|
2335
|
+
|
|
2336
|
+
-- is_aggregate( schema, function, description )
|
|
2337
|
+
CREATE OR REPLACE FUNCTION is_aggregate ( NAME, NAME, TEXT )
|
|
2338
|
+
RETURNS TEXT AS $$
|
|
2339
|
+
SELECT _func_compare($1, $2, _type_func('a', $1, $2), $3 );
|
|
2340
|
+
$$ LANGUAGE SQL;
|
|
2341
|
+
|
|
2342
|
+
-- is_aggregate( schema, function )
|
|
2343
|
+
CREATE OR REPLACE FUNCTION is_aggregate( NAME, NAME )
|
|
2344
|
+
RETURNS TEXT AS $$
|
|
2345
|
+
SELECT _func_compare(
|
|
2346
|
+
$1, $2, _type_func('a', $1, $2),
|
|
2347
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should be an aggregate function'
|
|
2348
|
+
);
|
|
2349
|
+
$$ LANGUAGE sql;
|
|
2350
|
+
|
|
2351
|
+
-- is_aggregate( function, args[], description )
|
|
2352
|
+
CREATE OR REPLACE FUNCTION is_aggregate ( NAME, NAME[], TEXT )
|
|
2353
|
+
RETURNS TEXT AS $$
|
|
2354
|
+
SELECT _func_compare( NULL, $1, $2, _type_func('a', $1, $2), $3 );
|
|
2355
|
+
$$ LANGUAGE SQL;
|
|
2356
|
+
|
|
2357
|
+
-- is_aggregate( function, args[] )
|
|
2358
|
+
CREATE OR REPLACE FUNCTION is_aggregate( NAME, NAME[] )
|
|
2359
|
+
RETURNS TEXT AS $$
|
|
2360
|
+
SELECT _func_compare(
|
|
2361
|
+
NULL, $1, $2, _type_func('a', $1, $2),
|
|
2362
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2363
|
+
array_to_string($2, ', ') || ') should be an aggregate function'
|
|
2364
|
+
);
|
|
2365
|
+
$$ LANGUAGE sql;
|
|
2366
|
+
|
|
2367
|
+
-- is_aggregate( function, description )
|
|
2368
|
+
CREATE OR REPLACE FUNCTION is_aggregate( NAME, TEXT )
|
|
2369
|
+
RETURNS TEXT AS $$
|
|
2370
|
+
SELECT _func_compare(NULL, $1, _type_func('a', $1), $2 );
|
|
2371
|
+
$$ LANGUAGE sql;
|
|
2372
|
+
|
|
2373
|
+
-- is_aggregate( function )
|
|
2374
|
+
CREATE OR REPLACE FUNCTION is_aggregate( NAME )
|
|
2375
|
+
RETURNS TEXT AS $$
|
|
2376
|
+
SELECT _func_compare(
|
|
2377
|
+
NULL, $1, _type_func('a', $1),
|
|
2378
|
+
'Function ' || quote_ident($1) || '() should be an aggregate function'
|
|
2379
|
+
);
|
|
2380
|
+
$$ LANGUAGE sql;
|
|
2381
|
+
|
|
2382
|
+
-- isnt_aggregate( schema, function, args[], description )
|
|
2383
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate ( NAME, NAME, NAME[], TEXT )
|
|
2384
|
+
RETURNS TEXT AS $$
|
|
2385
|
+
SELECT _func_compare($1, $2, $3, NOT _type_func('a', $1, $2, $3), $4 );
|
|
2386
|
+
$$ LANGUAGE SQL;
|
|
2387
|
+
|
|
2388
|
+
-- isnt_aggregate( schema, function, args[] )
|
|
2389
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate( NAME, NAME, NAME[] )
|
|
2390
|
+
RETURNS TEXT AS $$
|
|
2391
|
+
SELECT _func_compare(
|
|
2392
|
+
$1, $2, $3, NOT _type_func('a', $1, $2, $3),
|
|
2393
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2394
|
+
array_to_string($3, ', ') || ') should not be an aggregate function'
|
|
2395
|
+
);
|
|
2396
|
+
$$ LANGUAGE sql;
|
|
2397
|
+
|
|
2398
|
+
-- isnt_aggregate( schema, function, description )
|
|
2399
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate ( NAME, NAME, TEXT )
|
|
2400
|
+
RETURNS TEXT AS $$
|
|
2401
|
+
SELECT _func_compare($1, $2, NOT _type_func('a', $1, $2), $3 );
|
|
2402
|
+
$$ LANGUAGE SQL;
|
|
2403
|
+
|
|
2404
|
+
-- isnt_aggregate( schema, function )
|
|
2405
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate( NAME, NAME )
|
|
2406
|
+
RETURNS TEXT AS $$
|
|
2407
|
+
SELECT _func_compare(
|
|
2408
|
+
$1, $2, NOT _type_func('a', $1, $2),
|
|
2409
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should not be an aggregate function'
|
|
2410
|
+
);
|
|
2411
|
+
$$ LANGUAGE sql;
|
|
2412
|
+
|
|
2413
|
+
-- isnt_aggregate( function, args[], description )
|
|
2414
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate ( NAME, NAME[], TEXT )
|
|
2415
|
+
RETURNS TEXT AS $$
|
|
2416
|
+
SELECT _func_compare(NULL, $1, $2, NOT _type_func('a', $1, $2), $3 );
|
|
2417
|
+
$$ LANGUAGE SQL;
|
|
2418
|
+
|
|
2419
|
+
-- isnt_aggregate( function, args[] )
|
|
2420
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate( NAME, NAME[] )
|
|
2421
|
+
RETURNS TEXT AS $$
|
|
2422
|
+
SELECT _func_compare(
|
|
2423
|
+
NULL, $1, $2, NOT _type_func('a', $1, $2),
|
|
2424
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2425
|
+
array_to_string($2, ', ') || ') should not be an aggregate function'
|
|
2426
|
+
);
|
|
2427
|
+
$$ LANGUAGE sql;
|
|
2428
|
+
|
|
2429
|
+
-- isnt_aggregate( function, description )
|
|
2430
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate( NAME, TEXT )
|
|
2431
|
+
RETURNS TEXT AS $$
|
|
2432
|
+
SELECT _func_compare(NULL, $1, NOT _type_func('a', $1), $2 );
|
|
2433
|
+
$$ LANGUAGE sql;
|
|
2434
|
+
|
|
2435
|
+
-- isnt_aggregate( function )
|
|
2436
|
+
CREATE OR REPLACE FUNCTION isnt_aggregate( NAME )
|
|
2437
|
+
RETURNS TEXT AS $$
|
|
2438
|
+
SELECT _func_compare(
|
|
2439
|
+
NULL, $1, NOT _type_func('a', $1),
|
|
2440
|
+
'Function ' || quote_ident($1) || '() should not be an aggregate function'
|
|
2441
|
+
);
|
|
2442
|
+
$$ LANGUAGE sql;
|
|
2443
|
+
|
|
2444
|
+
CREATE OR REPLACE FUNCTION _strict ( NAME, NAME, NAME[] )
|
|
2445
|
+
RETURNS BOOLEAN AS $$
|
|
2446
|
+
SELECT is_strict
|
|
2447
|
+
FROM tap_funky
|
|
2448
|
+
WHERE schema = $1
|
|
2449
|
+
AND name = $2
|
|
2450
|
+
AND args = _funkargs($3)
|
|
2451
|
+
$$ LANGUAGE SQL;
|
|
2452
|
+
|
|
2453
|
+
CREATE OR REPLACE FUNCTION _strict ( NAME, NAME )
|
|
2454
|
+
RETURNS BOOLEAN AS $$
|
|
2455
|
+
SELECT is_strict FROM tap_funky WHERE schema = $1 AND name = $2
|
|
2456
|
+
$$ LANGUAGE SQL;
|
|
2457
|
+
|
|
2458
|
+
CREATE OR REPLACE FUNCTION _strict ( NAME, NAME[] )
|
|
2459
|
+
RETURNS BOOLEAN AS $$
|
|
2460
|
+
SELECT is_strict
|
|
2461
|
+
FROM tap_funky
|
|
2462
|
+
WHERE name = $1
|
|
2463
|
+
AND args = _funkargs($2)
|
|
2464
|
+
AND is_visible;
|
|
2465
|
+
$$ LANGUAGE SQL;
|
|
2466
|
+
|
|
2467
|
+
CREATE OR REPLACE FUNCTION _strict ( NAME )
|
|
2468
|
+
RETURNS BOOLEAN AS $$
|
|
2469
|
+
SELECT is_strict FROM tap_funky WHERE name = $1 AND is_visible;
|
|
2470
|
+
$$ LANGUAGE SQL;
|
|
2471
|
+
|
|
2472
|
+
-- is_strict( schema, function, args[], description )
|
|
2473
|
+
CREATE OR REPLACE FUNCTION is_strict ( NAME, NAME, NAME[], TEXT )
|
|
2474
|
+
RETURNS TEXT AS $$
|
|
2475
|
+
SELECT _func_compare($1, $2, $3, _strict($1, $2, $3), $4 );
|
|
2476
|
+
$$ LANGUAGE SQL;
|
|
2477
|
+
|
|
2478
|
+
-- is_strict( schema, function, args[] )
|
|
2479
|
+
CREATE OR REPLACE FUNCTION is_strict( NAME, NAME, NAME[] )
|
|
2480
|
+
RETURNS TEXT AS $$
|
|
2481
|
+
SELECT ok(
|
|
2482
|
+
_strict($1, $2, $3),
|
|
2483
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2484
|
+
array_to_string($3, ', ') || ') should be strict'
|
|
2485
|
+
);
|
|
2486
|
+
$$ LANGUAGE sql;
|
|
2487
|
+
|
|
2488
|
+
-- is_strict( schema, function, description )
|
|
2489
|
+
CREATE OR REPLACE FUNCTION is_strict ( NAME, NAME, TEXT )
|
|
2490
|
+
RETURNS TEXT AS $$
|
|
2491
|
+
SELECT _func_compare($1, $2, _strict($1, $2), $3 );
|
|
2492
|
+
$$ LANGUAGE SQL;
|
|
2493
|
+
|
|
2494
|
+
-- is_strict( schema, function )
|
|
2495
|
+
CREATE OR REPLACE FUNCTION is_strict( NAME, NAME )
|
|
2496
|
+
RETURNS TEXT AS $$
|
|
2497
|
+
SELECT ok(
|
|
2498
|
+
_strict($1, $2),
|
|
2499
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should be strict'
|
|
2500
|
+
);
|
|
2501
|
+
$$ LANGUAGE sql;
|
|
2502
|
+
|
|
2503
|
+
-- is_strict( function, args[], description )
|
|
2504
|
+
CREATE OR REPLACE FUNCTION is_strict ( NAME, NAME[], TEXT )
|
|
2505
|
+
RETURNS TEXT AS $$
|
|
2506
|
+
SELECT _func_compare(NULL, $1, $2, _strict($1, $2), $3 );
|
|
2507
|
+
$$ LANGUAGE SQL;
|
|
2508
|
+
|
|
2509
|
+
-- is_strict( function, args[] )
|
|
2510
|
+
CREATE OR REPLACE FUNCTION is_strict( NAME, NAME[] )
|
|
2511
|
+
RETURNS TEXT AS $$
|
|
2512
|
+
SELECT ok(
|
|
2513
|
+
_strict($1, $2),
|
|
2514
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2515
|
+
array_to_string($2, ', ') || ') should be strict'
|
|
2516
|
+
);
|
|
2517
|
+
$$ LANGUAGE sql;
|
|
2518
|
+
|
|
2519
|
+
-- is_strict( function, description )
|
|
2520
|
+
CREATE OR REPLACE FUNCTION is_strict( NAME, TEXT )
|
|
2521
|
+
RETURNS TEXT AS $$
|
|
2522
|
+
SELECT _func_compare(NULL, $1, _strict($1), $2 );
|
|
2523
|
+
$$ LANGUAGE sql;
|
|
2524
|
+
|
|
2525
|
+
-- is_strict( function )
|
|
2526
|
+
CREATE OR REPLACE FUNCTION is_strict( NAME )
|
|
2527
|
+
RETURNS TEXT AS $$
|
|
2528
|
+
SELECT ok( _strict($1), 'Function ' || quote_ident($1) || '() should be strict' );
|
|
2529
|
+
$$ LANGUAGE sql;
|
|
2530
|
+
|
|
2531
|
+
-- isnt_strict( schema, function, args[], description )
|
|
2532
|
+
CREATE OR REPLACE FUNCTION isnt_strict ( NAME, NAME, NAME[], TEXT )
|
|
2533
|
+
RETURNS TEXT AS $$
|
|
2534
|
+
SELECT _func_compare($1, $2, $3, NOT _strict($1, $2, $3), $4 );
|
|
2535
|
+
$$ LANGUAGE SQL;
|
|
2536
|
+
|
|
2537
|
+
-- isnt_strict( schema, function, args[] )
|
|
2538
|
+
CREATE OR REPLACE FUNCTION isnt_strict( NAME, NAME, NAME[] )
|
|
2539
|
+
RETURNS TEXT AS $$
|
|
2540
|
+
SELECT ok(
|
|
2541
|
+
NOT _strict($1, $2, $3),
|
|
2542
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2543
|
+
array_to_string($3, ', ') || ') should not be strict'
|
|
2544
|
+
);
|
|
2545
|
+
$$ LANGUAGE sql;
|
|
2546
|
+
|
|
2547
|
+
-- isnt_strict( schema, function, description )
|
|
2548
|
+
CREATE OR REPLACE FUNCTION isnt_strict ( NAME, NAME, TEXT )
|
|
2549
|
+
RETURNS TEXT AS $$
|
|
2550
|
+
SELECT _func_compare($1, $2, NOT _strict($1, $2), $3 );
|
|
2551
|
+
$$ LANGUAGE SQL;
|
|
2552
|
+
|
|
2553
|
+
-- isnt_strict( schema, function )
|
|
2554
|
+
CREATE OR REPLACE FUNCTION isnt_strict( NAME, NAME )
|
|
2555
|
+
RETURNS TEXT AS $$
|
|
2556
|
+
SELECT ok(
|
|
2557
|
+
NOT _strict($1, $2),
|
|
2558
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should not be strict'
|
|
2559
|
+
);
|
|
2560
|
+
$$ LANGUAGE sql;
|
|
2561
|
+
|
|
2562
|
+
-- isnt_strict( function, args[], description )
|
|
2563
|
+
CREATE OR REPLACE FUNCTION isnt_strict ( NAME, NAME[], TEXT )
|
|
2564
|
+
RETURNS TEXT AS $$
|
|
2565
|
+
SELECT _func_compare(NULL, $1, $2, NOT _strict($1, $2), $3 );
|
|
2566
|
+
$$ LANGUAGE SQL;
|
|
2567
|
+
|
|
2568
|
+
-- isnt_strict( function, args[] )
|
|
2569
|
+
CREATE OR REPLACE FUNCTION isnt_strict( NAME, NAME[] )
|
|
2570
|
+
RETURNS TEXT AS $$
|
|
2571
|
+
SELECT ok(
|
|
2572
|
+
NOT _strict($1, $2),
|
|
2573
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2574
|
+
array_to_string($2, ', ') || ') should not be strict'
|
|
2575
|
+
);
|
|
2576
|
+
$$ LANGUAGE sql;
|
|
2577
|
+
|
|
2578
|
+
-- isnt_strict( function, description )
|
|
2579
|
+
CREATE OR REPLACE FUNCTION isnt_strict( NAME, TEXT )
|
|
2580
|
+
RETURNS TEXT AS $$
|
|
2581
|
+
SELECT _func_compare(NULL, $1, NOT _strict($1), $2 );
|
|
2582
|
+
$$ LANGUAGE sql;
|
|
2583
|
+
|
|
2584
|
+
-- isnt_strict( function )
|
|
2585
|
+
CREATE OR REPLACE FUNCTION isnt_strict( NAME )
|
|
2586
|
+
RETURNS TEXT AS $$
|
|
2587
|
+
SELECT ok( NOT _strict($1), 'Function ' || quote_ident($1) || '() should not be strict' );
|
|
2588
|
+
$$ LANGUAGE sql;
|
|
2589
|
+
|
|
2590
|
+
CREATE OR REPLACE FUNCTION _expand_vol( char )
|
|
2591
|
+
RETURNS TEXT AS $$
|
|
2592
|
+
SELECT CASE $1
|
|
2593
|
+
WHEN 'i' THEN 'IMMUTABLE'
|
|
2594
|
+
WHEN 's' THEN 'STABLE'
|
|
2595
|
+
WHEN 'v' THEN 'VOLATILE'
|
|
2596
|
+
ELSE 'UNKNOWN' END
|
|
2597
|
+
$$ LANGUAGE SQL IMMUTABLE;
|
|
2598
|
+
|
|
2599
|
+
CREATE OR REPLACE FUNCTION _refine_vol( text )
|
|
2600
|
+
RETURNS text AS $$
|
|
2601
|
+
SELECT _expand_vol(substring(LOWER($1) FROM 1 FOR 1)::char);
|
|
2602
|
+
$$ LANGUAGE SQL IMMUTABLE;
|
|
2603
|
+
|
|
2604
|
+
CREATE OR REPLACE FUNCTION _vol ( NAME, NAME, NAME[] )
|
|
2605
|
+
RETURNS TEXT AS $$
|
|
2606
|
+
SELECT _expand_vol(volatility)
|
|
2607
|
+
FROM tap_funky f
|
|
2608
|
+
WHERE f.schema = $1
|
|
2609
|
+
and f.name = $2
|
|
2610
|
+
AND f.args = _funkargs($3)
|
|
2611
|
+
$$ LANGUAGE SQL;
|
|
2612
|
+
|
|
2613
|
+
CREATE OR REPLACE FUNCTION _vol ( NAME, NAME )
|
|
2614
|
+
RETURNS TEXT AS $$
|
|
2615
|
+
SELECT _expand_vol(volatility) FROM tap_funky f
|
|
2616
|
+
WHERE f.schema = $1 and f.name = $2
|
|
2617
|
+
$$ LANGUAGE SQL;
|
|
2618
|
+
|
|
2619
|
+
CREATE OR REPLACE FUNCTION _vol ( NAME, NAME[] )
|
|
2620
|
+
RETURNS TEXT AS $$
|
|
2621
|
+
SELECT _expand_vol(volatility)
|
|
2622
|
+
FROM tap_funky f
|
|
2623
|
+
WHERE f.name = $1
|
|
2624
|
+
AND f.args = _funkargs($2)
|
|
2625
|
+
AND f.is_visible;
|
|
2626
|
+
$$ LANGUAGE SQL;
|
|
2627
|
+
|
|
2628
|
+
CREATE OR REPLACE FUNCTION _vol ( NAME )
|
|
2629
|
+
RETURNS TEXT AS $$
|
|
2630
|
+
SELECT _expand_vol(volatility) FROM tap_funky f
|
|
2631
|
+
WHERE f.name = $1 AND f.is_visible;
|
|
2632
|
+
$$ LANGUAGE SQL;
|
|
2633
|
+
|
|
2634
|
+
-- volatility_is( schema, function, args[], volatility, description )
|
|
2635
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, NAME, NAME[], TEXT, TEXT )
|
|
2636
|
+
RETURNS TEXT AS $$
|
|
2637
|
+
SELECT _func_compare($1, $2, $3, _vol($1, $2, $3), _refine_vol($4), $5 );
|
|
2638
|
+
$$ LANGUAGE SQL;
|
|
2639
|
+
|
|
2640
|
+
-- volatility_is( schema, function, args[], volatility )
|
|
2641
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, NAME, NAME[], TEXT )
|
|
2642
|
+
RETURNS TEXT AS $$
|
|
2643
|
+
SELECT volatility_is(
|
|
2644
|
+
$1, $2, $3, $4,
|
|
2645
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
2646
|
+
array_to_string($3, ', ') || ') should be ' || _refine_vol($4)
|
|
2647
|
+
);
|
|
2648
|
+
$$ LANGUAGE SQL;
|
|
2649
|
+
|
|
2650
|
+
-- volatility_is( schema, function, volatility, description )
|
|
2651
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, NAME, TEXT, TEXT )
|
|
2652
|
+
RETURNS TEXT AS $$
|
|
2653
|
+
SELECT _func_compare($1, $2, _vol($1, $2), _refine_vol($3), $4 );
|
|
2654
|
+
$$ LANGUAGE SQL;
|
|
2655
|
+
|
|
2656
|
+
-- volatility_is( schema, function, volatility )
|
|
2657
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, NAME, TEXT )
|
|
2658
|
+
RETURNS TEXT AS $$
|
|
2659
|
+
SELECT volatility_is(
|
|
2660
|
+
$1, $2, $3,
|
|
2661
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2)
|
|
2662
|
+
|| '() should be ' || _refine_vol($3)
|
|
2663
|
+
);
|
|
2664
|
+
$$ LANGUAGE SQL;
|
|
2665
|
+
|
|
2666
|
+
-- volatility_is( function, args[], volatility, description )
|
|
2667
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, NAME[], TEXT, TEXT )
|
|
2668
|
+
RETURNS TEXT AS $$
|
|
2669
|
+
SELECT _func_compare(NULL, $1, $2, _vol($1, $2), _refine_vol($3), $4 );
|
|
2670
|
+
$$ LANGUAGE SQL;
|
|
2671
|
+
|
|
2672
|
+
-- volatility_is( function, args[], volatility )
|
|
2673
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, NAME[], TEXT )
|
|
2674
|
+
RETURNS TEXT AS $$
|
|
2675
|
+
SELECT volatility_is(
|
|
2676
|
+
$1, $2, $3,
|
|
2677
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
2678
|
+
array_to_string($2, ', ') || ') should be ' || _refine_vol($3)
|
|
2679
|
+
);
|
|
2680
|
+
$$ LANGUAGE SQL;
|
|
2681
|
+
|
|
2682
|
+
-- volatility_is( function, volatility, description )
|
|
2683
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, TEXT, TEXT )
|
|
2684
|
+
RETURNS TEXT AS $$
|
|
2685
|
+
SELECT _func_compare(NULL, $1, _vol($1), _refine_vol($2), $3 );
|
|
2686
|
+
$$ LANGUAGE SQL;
|
|
2687
|
+
|
|
2688
|
+
-- volatility_is( function, volatility )
|
|
2689
|
+
CREATE OR REPLACE FUNCTION volatility_is( NAME, TEXT )
|
|
2690
|
+
RETURNS TEXT AS $$
|
|
2691
|
+
SELECT volatility_is(
|
|
2692
|
+
$1, $2,
|
|
2693
|
+
'Function ' || quote_ident($1) || '() should be ' || _refine_vol($2)
|
|
2694
|
+
);
|
|
2695
|
+
$$ LANGUAGE SQL;
|
|
2696
|
+
|
|
2697
|
+
-- check_test( test_output, pass, name, description, diag, match_diag )
|
|
2698
|
+
CREATE OR REPLACE FUNCTION findfuncs( NAME, TEXT, TEXT )
|
|
2699
|
+
RETURNS TEXT[] AS $$
|
|
2700
|
+
SELECT ARRAY(
|
|
2701
|
+
SELECT DISTINCT (quote_ident(n.nspname) || '.' || quote_ident(p.proname)) COLLATE "C" AS pname
|
|
2702
|
+
FROM pg_catalog.pg_proc p
|
|
2703
|
+
JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
|
|
2704
|
+
WHERE n.nspname = $1
|
|
2705
|
+
AND p.proname ~ $2
|
|
2706
|
+
AND ($3 IS NULL OR p.proname !~ $3)
|
|
2707
|
+
ORDER BY pname
|
|
2708
|
+
);
|
|
2709
|
+
$$ LANGUAGE sql;
|
|
2710
|
+
CREATE OR REPLACE FUNCTION findfuncs( NAME, TEXT )
|
|
2711
|
+
RETURNS TEXT[] AS $$
|
|
2712
|
+
SELECT findfuncs( $1, $2, NULL )
|
|
2713
|
+
$$ LANGUAGE sql;
|
|
2714
|
+
|
|
2715
|
+
CREATE OR REPLACE FUNCTION findfuncs( TEXT, TEXT )
|
|
2716
|
+
RETURNS TEXT[] AS $$
|
|
2717
|
+
SELECT ARRAY(
|
|
2718
|
+
SELECT DISTINCT (quote_ident(n.nspname) || '.' || quote_ident(p.proname)) COLLATE "C" AS pname
|
|
2719
|
+
FROM pg_catalog.pg_proc p
|
|
2720
|
+
JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
|
|
2721
|
+
WHERE pg_catalog.pg_function_is_visible(p.oid)
|
|
2722
|
+
AND p.proname ~ $1
|
|
2723
|
+
AND ($2 IS NULL OR p.proname !~ $2)
|
|
2724
|
+
ORDER BY pname
|
|
2725
|
+
);
|
|
2726
|
+
$$ LANGUAGE sql;
|
|
2727
|
+
CREATE OR REPLACE FUNCTION findfuncs( TEXT )
|
|
2728
|
+
RETURNS TEXT[] AS $$
|
|
2729
|
+
SELECT findfuncs( $1, NULL )
|
|
2730
|
+
$$ LANGUAGE sql;
|
|
2731
|
+
|
|
2732
|
+
CREATE OR REPLACE FUNCTION _runem( text[], boolean )
|
|
2733
|
+
RETURNS SETOF TEXT AS $$
|
|
2734
|
+
DECLARE
|
|
2735
|
+
tap text;
|
|
2736
|
+
lbound int := array_lower($1, 1);
|
|
2737
|
+
BEGIN
|
|
2738
|
+
IF lbound IS NULL THEN RETURN; END IF;
|
|
2739
|
+
FOR i IN lbound..array_upper($1, 1) LOOP
|
|
2740
|
+
-- Send the name of the function to diag if warranted.
|
|
2741
|
+
IF $2 THEN RETURN NEXT diag( $1[i] || '()' ); END IF;
|
|
2742
|
+
-- Execute the tap function and return its results.
|
|
2743
|
+
FOR tap IN EXECUTE 'SELECT * FROM ' || $1[i] || '()' LOOP
|
|
2744
|
+
RETURN NEXT tap;
|
|
2745
|
+
END LOOP;
|
|
2746
|
+
END LOOP;
|
|
2747
|
+
RETURN;
|
|
2748
|
+
END;
|
|
2749
|
+
$$ LANGUAGE plpgsql;
|
|
2750
|
+
|
|
2751
|
+
CREATE OR REPLACE FUNCTION _is_verbose()
|
|
2752
|
+
RETURNS BOOLEAN AS $$
|
|
2753
|
+
SELECT current_setting('client_min_messages') NOT IN (
|
|
2754
|
+
'warning', 'error', 'fatal', 'panic'
|
|
2755
|
+
);
|
|
2756
|
+
$$ LANGUAGE sql STABLE;
|
|
2757
|
+
|
|
2758
|
+
-- do_tap( schema, pattern )
|
|
2759
|
+
CREATE OR REPLACE FUNCTION do_tap( name, text )
|
|
2760
|
+
RETURNS SETOF TEXT AS $$
|
|
2761
|
+
SELECT * FROM _runem( findfuncs($1, $2), _is_verbose() );
|
|
2762
|
+
$$ LANGUAGE sql;
|
|
2763
|
+
|
|
2764
|
+
-- do_tap( schema )
|
|
2765
|
+
CREATE OR REPLACE FUNCTION do_tap( name )
|
|
2766
|
+
RETURNS SETOF TEXT AS $$
|
|
2767
|
+
SELECT * FROM _runem( findfuncs($1, '^test'), _is_verbose() );
|
|
2768
|
+
$$ LANGUAGE sql;
|
|
2769
|
+
|
|
2770
|
+
-- do_tap( pattern )
|
|
2771
|
+
CREATE OR REPLACE FUNCTION do_tap( text )
|
|
2772
|
+
RETURNS SETOF TEXT AS $$
|
|
2773
|
+
SELECT * FROM _runem( findfuncs($1), _is_verbose() );
|
|
2774
|
+
$$ LANGUAGE sql;
|
|
2775
|
+
|
|
2776
|
+
-- do_tap()
|
|
2777
|
+
CREATE OR REPLACE FUNCTION do_tap( )
|
|
2778
|
+
RETURNS SETOF TEXT AS $$
|
|
2779
|
+
SELECT * FROM _runem( findfuncs('^test'), _is_verbose());
|
|
2780
|
+
$$ LANGUAGE sql;
|
|
2781
|
+
|
|
2782
|
+
CREATE OR REPLACE FUNCTION _currtest()
|
|
2783
|
+
RETURNS INTEGER AS $$
|
|
2784
|
+
BEGIN
|
|
2785
|
+
RETURN currval('__tresults___numb_seq');
|
|
2786
|
+
EXCEPTION
|
|
2787
|
+
WHEN object_not_in_prerequisite_state THEN RETURN 0;
|
|
2788
|
+
END;
|
|
2789
|
+
$$ LANGUAGE plpgsql;
|
|
2790
|
+
|
|
2791
|
+
CREATE OR REPLACE FUNCTION _cleanup()
|
|
2792
|
+
RETURNS boolean AS $$
|
|
2793
|
+
DROP SEQUENCE __tresults___numb_seq;
|
|
2794
|
+
DROP TABLE __tcache__;
|
|
2795
|
+
DROP SEQUENCE __tcache___id_seq;
|
|
2796
|
+
SELECT TRUE;
|
|
2797
|
+
$$ LANGUAGE sql;
|
|
2798
|
+
|
|
2799
|
+
-- diag_test_name ( test_name )
|
|
2800
|
+
CREATE OR REPLACE FUNCTION diag_test_name(TEXT)
|
|
2801
|
+
RETURNS TEXT AS $$
|
|
2802
|
+
SELECT diag($1 || '()');
|
|
2803
|
+
$$ LANGUAGE SQL;
|
|
2804
|
+
|
|
2805
|
+
CREATE OR REPLACE FUNCTION _runner( text[], text[], text[], text[], text[] )
|
|
2806
|
+
RETURNS SETOF TEXT AS $$
|
|
2807
|
+
DECLARE
|
|
2808
|
+
startup ALIAS FOR $1;
|
|
2809
|
+
shutdown ALIAS FOR $2;
|
|
2810
|
+
setup ALIAS FOR $3;
|
|
2811
|
+
teardown ALIAS FOR $4;
|
|
2812
|
+
tests ALIAS FOR $5;
|
|
2813
|
+
tap TEXT;
|
|
2814
|
+
tfaild INTEGER := 0;
|
|
2815
|
+
ffaild INTEGER := 0;
|
|
2816
|
+
tnumb INTEGER := 0;
|
|
2817
|
+
fnumb INTEGER := 0;
|
|
2818
|
+
tok BOOLEAN := TRUE;
|
|
2819
|
+
BEGIN
|
|
2820
|
+
BEGIN
|
|
2821
|
+
-- No plan support.
|
|
2822
|
+
PERFORM * FROM no_plan();
|
|
2823
|
+
FOR tap IN SELECT * FROM _runem(startup, false) LOOP RETURN NEXT tap; END LOOP;
|
|
2824
|
+
EXCEPTION
|
|
2825
|
+
-- Catch all exceptions and simply rethrow custom exceptions. This
|
|
2826
|
+
-- will roll back everything in the above block.
|
|
2827
|
+
WHEN raise_exception THEN RAISE EXCEPTION '%', SQLERRM;
|
|
2828
|
+
END;
|
|
2829
|
+
|
|
2830
|
+
-- Record how startup tests have failed.
|
|
2831
|
+
tfaild := num_failed();
|
|
2832
|
+
|
|
2833
|
+
FOR i IN 1..COALESCE(array_upper(tests, 1), 0) LOOP
|
|
2834
|
+
|
|
2835
|
+
-- What subtest are we running?
|
|
2836
|
+
RETURN NEXT diag_test_name('Subtest: ' || tests[i]);
|
|
2837
|
+
|
|
2838
|
+
-- Reset the results.
|
|
2839
|
+
tok := TRUE;
|
|
2840
|
+
tnumb := COALESCE(_get('curr_test'), 0);
|
|
2841
|
+
|
|
2842
|
+
IF tnumb > 0 THEN
|
|
2843
|
+
EXECUTE 'ALTER SEQUENCE __tresults___numb_seq RESTART WITH 1';
|
|
2844
|
+
PERFORM _set('curr_test', 0);
|
|
2845
|
+
PERFORM _set('failed', 0);
|
|
2846
|
+
END IF;
|
|
2847
|
+
|
|
2848
|
+
DECLARE
|
|
2849
|
+
errstate text;
|
|
2850
|
+
errmsg text;
|
|
2851
|
+
detail text;
|
|
2852
|
+
hint text;
|
|
2853
|
+
context text;
|
|
2854
|
+
schname text;
|
|
2855
|
+
tabname text;
|
|
2856
|
+
colname text;
|
|
2857
|
+
chkname text;
|
|
2858
|
+
typname text;
|
|
2859
|
+
BEGIN
|
|
2860
|
+
BEGIN
|
|
2861
|
+
-- Run the setup functions.
|
|
2862
|
+
FOR tap IN SELECT * FROM _runem(setup, false) LOOP
|
|
2863
|
+
RETURN NEXT regexp_replace(tap, '^', ' ', 'gn');
|
|
2864
|
+
END LOOP;
|
|
2865
|
+
|
|
2866
|
+
-- Run the actual test function.
|
|
2867
|
+
FOR tap IN EXECUTE 'SELECT * FROM ' || tests[i] || '()' LOOP
|
|
2868
|
+
RETURN NEXT regexp_replace(tap, '^', ' ', 'gn');
|
|
2869
|
+
END LOOP;
|
|
2870
|
+
|
|
2871
|
+
-- Run the teardown functions.
|
|
2872
|
+
FOR tap IN SELECT * FROM _runem(teardown, false) LOOP
|
|
2873
|
+
RETURN NEXT regexp_replace(tap, '^', ' ', 'gn');
|
|
2874
|
+
END LOOP;
|
|
2875
|
+
|
|
2876
|
+
-- Emit the plan.
|
|
2877
|
+
fnumb := COALESCE(_get('curr_test'), 0);
|
|
2878
|
+
RETURN NEXT ' 1..' || fnumb;
|
|
2879
|
+
|
|
2880
|
+
-- Emit any error messages.
|
|
2881
|
+
IF fnumb = 0 THEN
|
|
2882
|
+
RETURN NEXT ' # No tests run!';
|
|
2883
|
+
tok = false;
|
|
2884
|
+
ELSE
|
|
2885
|
+
-- Report failures.
|
|
2886
|
+
ffaild := num_failed();
|
|
2887
|
+
IF ffaild > 0 THEN
|
|
2888
|
+
tok := FALSE;
|
|
2889
|
+
RETURN NEXT ' ' || diag(
|
|
2890
|
+
'Looks like you failed ' || ffaild || ' test' ||
|
|
2891
|
+
CASE ffaild WHEN 1 THEN '' ELSE 's' END
|
|
2892
|
+
|| ' of ' || fnumb
|
|
2893
|
+
);
|
|
2894
|
+
END IF;
|
|
2895
|
+
END IF;
|
|
2896
|
+
|
|
2897
|
+
EXCEPTION WHEN OTHERS THEN
|
|
2898
|
+
-- Something went wrong. Record that fact.
|
|
2899
|
+
errstate := SQLSTATE;
|
|
2900
|
+
errmsg := SQLERRM;
|
|
2901
|
+
END;
|
|
2902
|
+
|
|
2903
|
+
-- Always raise an exception to rollback any changes.
|
|
2904
|
+
RAISE EXCEPTION '__TAP_ROLLBACK__';
|
|
2905
|
+
|
|
2906
|
+
EXCEPTION WHEN raise_exception THEN
|
|
2907
|
+
IF errmsg IS NOT NULL THEN
|
|
2908
|
+
-- Something went wrong. Emit the error message.
|
|
2909
|
+
tok := FALSE;
|
|
2910
|
+
RETURN NEXT regexp_replace( diag('Test died: ' || _error_diag(
|
|
2911
|
+
errstate, errmsg, detail, hint, context, schname, tabname, colname, chkname, typname
|
|
2912
|
+
)), '^', ' ', 'gn');
|
|
2913
|
+
errmsg := NULL;
|
|
2914
|
+
END IF;
|
|
2915
|
+
END;
|
|
2916
|
+
|
|
2917
|
+
-- Restore the sequence.
|
|
2918
|
+
EXECUTE 'ALTER SEQUENCE __tresults___numb_seq RESTART WITH ' || tnumb + 1;
|
|
2919
|
+
PERFORM _set('curr_test', tnumb);
|
|
2920
|
+
PERFORM _set('failed', tfaild);
|
|
2921
|
+
|
|
2922
|
+
-- Record this test.
|
|
2923
|
+
RETURN NEXT ok(tok, tests[i]);
|
|
2924
|
+
IF NOT tok THEN tfaild := tfaild + 1; END IF;
|
|
2925
|
+
|
|
2926
|
+
END LOOP;
|
|
2927
|
+
|
|
2928
|
+
-- Run the shutdown functions.
|
|
2929
|
+
FOR tap IN SELECT * FROM _runem(shutdown, false) LOOP RETURN NEXT tap; END LOOP;
|
|
2930
|
+
|
|
2931
|
+
-- Finish up.
|
|
2932
|
+
FOR tap IN SELECT * FROM _finish( COALESCE(_get('curr_test'), 0), 0, tfaild ) LOOP
|
|
2933
|
+
RETURN NEXT tap;
|
|
2934
|
+
END LOOP;
|
|
2935
|
+
|
|
2936
|
+
-- Clean up and return.
|
|
2937
|
+
PERFORM _cleanup();
|
|
2938
|
+
RETURN;
|
|
2939
|
+
END;
|
|
2940
|
+
$$ LANGUAGE plpgsql;
|
|
2941
|
+
|
|
2942
|
+
-- runtests( schema, match )
|
|
2943
|
+
CREATE OR REPLACE FUNCTION runtests( NAME, TEXT )
|
|
2944
|
+
RETURNS SETOF TEXT AS $$
|
|
2945
|
+
SELECT * FROM _runner(
|
|
2946
|
+
findfuncs( $1, '^startup' ),
|
|
2947
|
+
findfuncs( $1, '^shutdown' ),
|
|
2948
|
+
findfuncs( $1, '^setup' ),
|
|
2949
|
+
findfuncs( $1, '^teardown' ),
|
|
2950
|
+
findfuncs( $1, $2, '^(startup|shutdown|setup|teardown)' )
|
|
2951
|
+
);
|
|
2952
|
+
$$ LANGUAGE sql;
|
|
2953
|
+
|
|
2954
|
+
-- runtests( schema )
|
|
2955
|
+
CREATE OR REPLACE FUNCTION runtests( NAME )
|
|
2956
|
+
RETURNS SETOF TEXT AS $$
|
|
2957
|
+
SELECT * FROM runtests( $1, '^test' );
|
|
2958
|
+
$$ LANGUAGE sql;
|
|
2959
|
+
|
|
2960
|
+
-- runtests( match )
|
|
2961
|
+
CREATE OR REPLACE FUNCTION runtests( TEXT )
|
|
2962
|
+
RETURNS SETOF TEXT AS $$
|
|
2963
|
+
SELECT * FROM _runner(
|
|
2964
|
+
findfuncs( '^startup' ),
|
|
2965
|
+
findfuncs( '^shutdown' ),
|
|
2966
|
+
findfuncs( '^setup' ),
|
|
2967
|
+
findfuncs( '^teardown' ),
|
|
2968
|
+
findfuncs( $1, '^(startup|shutdown|setup|teardown)' )
|
|
2969
|
+
);
|
|
2970
|
+
$$ LANGUAGE sql;
|
|
2971
|
+
|
|
2972
|
+
-- runtests( )
|
|
2973
|
+
CREATE OR REPLACE FUNCTION runtests( )
|
|
2974
|
+
RETURNS SETOF TEXT AS $$
|
|
2975
|
+
SELECT * FROM runtests( '^test' );
|
|
2976
|
+
$$ LANGUAGE sql;
|
|
2977
|
+
|
|
2978
|
+
CREATE OR REPLACE FUNCTION _temptable ( TEXT, TEXT )
|
|
2979
|
+
RETURNS TEXT AS $$
|
|
2980
|
+
BEGIN
|
|
2981
|
+
EXECUTE 'CREATE TEMP TABLE ' || $2 || ' AS ' || _query($1);
|
|
2982
|
+
return $2;
|
|
2983
|
+
END;
|
|
2984
|
+
$$ LANGUAGE plpgsql;
|
|
2985
|
+
|
|
2986
|
+
CREATE OR REPLACE FUNCTION _temptable ( anyarray, TEXT )
|
|
2987
|
+
RETURNS TEXT AS $$
|
|
2988
|
+
BEGIN
|
|
2989
|
+
CREATE TEMP TABLE _____coltmp___ AS
|
|
2990
|
+
SELECT $1[i]
|
|
2991
|
+
FROM generate_series(array_lower($1, 1), array_upper($1, 1)) s(i);
|
|
2992
|
+
EXECUTE 'ALTER TABLE _____coltmp___ RENAME TO ' || $2;
|
|
2993
|
+
return $2;
|
|
2994
|
+
END;
|
|
2995
|
+
$$ LANGUAGE plpgsql;
|
|
2996
|
+
|
|
2997
|
+
CREATE OR REPLACE FUNCTION _temptypes( TEXT )
|
|
2998
|
+
RETURNS TEXT AS $$
|
|
2999
|
+
SELECT array_to_string(ARRAY(
|
|
3000
|
+
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
|
|
3001
|
+
FROM pg_catalog.pg_attribute a
|
|
3002
|
+
JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
|
|
3003
|
+
WHERE c.oid = ('pg_temp.' || $1)::pg_catalog.regclass
|
|
3004
|
+
AND attnum > 0
|
|
3005
|
+
AND NOT attisdropped
|
|
3006
|
+
ORDER BY attnum
|
|
3007
|
+
), ',');
|
|
3008
|
+
$$ LANGUAGE sql;
|
|
3009
|
+
|
|
3010
|
+
CREATE OR REPLACE FUNCTION _docomp( TEXT, TEXT, TEXT, TEXT )
|
|
3011
|
+
RETURNS TEXT AS $$
|
|
3012
|
+
DECLARE
|
|
3013
|
+
have ALIAS FOR $1;
|
|
3014
|
+
want ALIAS FOR $2;
|
|
3015
|
+
extras TEXT[] := '{}';
|
|
3016
|
+
missing TEXT[] := '{}';
|
|
3017
|
+
res BOOLEAN := TRUE;
|
|
3018
|
+
msg TEXT := '';
|
|
3019
|
+
rec RECORD;
|
|
3020
|
+
BEGIN
|
|
3021
|
+
BEGIN
|
|
3022
|
+
-- Find extra records.
|
|
3023
|
+
FOR rec in EXECUTE 'SELECT * FROM ' || have || ' EXCEPT ' || $4
|
|
3024
|
+
|| 'SELECT * FROM ' || want LOOP
|
|
3025
|
+
extras := extras || rec::text;
|
|
3026
|
+
END LOOP;
|
|
3027
|
+
|
|
3028
|
+
-- Find missing records.
|
|
3029
|
+
FOR rec in EXECUTE 'SELECT * FROM ' || want || ' EXCEPT ' || $4
|
|
3030
|
+
|| 'SELECT * FROM ' || have LOOP
|
|
3031
|
+
missing := missing || rec::text;
|
|
3032
|
+
END LOOP;
|
|
3033
|
+
|
|
3034
|
+
-- Drop the temporary tables.
|
|
3035
|
+
EXECUTE 'DROP TABLE ' || have;
|
|
3036
|
+
EXECUTE 'DROP TABLE ' || want;
|
|
3037
|
+
EXCEPTION WHEN syntax_error OR datatype_mismatch THEN
|
|
3038
|
+
msg := E'\n' || diag(
|
|
3039
|
+
E' Columns differ between queries:\n'
|
|
3040
|
+
|| ' have: (' || _temptypes(have) || E')\n'
|
|
3041
|
+
|| ' want: (' || _temptypes(want) || ')'
|
|
3042
|
+
);
|
|
3043
|
+
EXECUTE 'DROP TABLE ' || have;
|
|
3044
|
+
EXECUTE 'DROP TABLE ' || want;
|
|
3045
|
+
RETURN ok(FALSE, $3) || msg;
|
|
3046
|
+
END;
|
|
3047
|
+
|
|
3048
|
+
-- What extra records do we have?
|
|
3049
|
+
IF extras[1] IS NOT NULL THEN
|
|
3050
|
+
res := FALSE;
|
|
3051
|
+
msg := E'\n' || diag(
|
|
3052
|
+
E' Extra records:\n '
|
|
3053
|
+
|| array_to_string( extras, E'\n ' )
|
|
3054
|
+
);
|
|
3055
|
+
END IF;
|
|
3056
|
+
|
|
3057
|
+
-- What missing records do we have?
|
|
3058
|
+
IF missing[1] IS NOT NULL THEN
|
|
3059
|
+
res := FALSE;
|
|
3060
|
+
msg := msg || E'\n' || diag(
|
|
3061
|
+
E' Missing records:\n '
|
|
3062
|
+
|| array_to_string( missing, E'\n ' )
|
|
3063
|
+
);
|
|
3064
|
+
END IF;
|
|
3065
|
+
|
|
3066
|
+
RETURN ok(res, $3) || msg;
|
|
3067
|
+
END;
|
|
3068
|
+
$$ LANGUAGE plpgsql;
|
|
3069
|
+
|
|
3070
|
+
CREATE OR REPLACE FUNCTION _relcomp( TEXT, TEXT, TEXT, TEXT )
|
|
3071
|
+
RETURNS TEXT AS $$
|
|
3072
|
+
SELECT _docomp(
|
|
3073
|
+
_temptable( $1, '__taphave__' ),
|
|
3074
|
+
_temptable( $2, '__tapwant__' ),
|
|
3075
|
+
$3, $4
|
|
3076
|
+
);
|
|
3077
|
+
$$ LANGUAGE sql;
|
|
3078
|
+
|
|
3079
|
+
CREATE OR REPLACE FUNCTION _relcomp( TEXT, anyarray, TEXT, TEXT )
|
|
3080
|
+
RETURNS TEXT AS $$
|
|
3081
|
+
SELECT _docomp(
|
|
3082
|
+
_temptable( $1, '__taphave__' ),
|
|
3083
|
+
_temptable( $2, '__tapwant__' ),
|
|
3084
|
+
$3, $4
|
|
3085
|
+
);
|
|
3086
|
+
$$ LANGUAGE sql;
|
|
3087
|
+
|
|
3088
|
+
-- set_eq( sql, sql, description )
|
|
3089
|
+
CREATE OR REPLACE FUNCTION set_eq( TEXT, TEXT, TEXT )
|
|
3090
|
+
RETURNS TEXT AS $$
|
|
3091
|
+
SELECT _relcomp( $1, $2, $3, '' );
|
|
3092
|
+
$$ LANGUAGE sql;
|
|
3093
|
+
|
|
3094
|
+
-- set_eq( sql, sql )
|
|
3095
|
+
CREATE OR REPLACE FUNCTION set_eq( TEXT, TEXT )
|
|
3096
|
+
RETURNS TEXT AS $$
|
|
3097
|
+
SELECT _relcomp( $1, $2, NULL::text, '' );
|
|
3098
|
+
$$ LANGUAGE sql;
|
|
3099
|
+
|
|
3100
|
+
-- set_eq( sql, array, description )
|
|
3101
|
+
CREATE OR REPLACE FUNCTION set_eq( TEXT, anyarray, TEXT )
|
|
3102
|
+
RETURNS TEXT AS $$
|
|
3103
|
+
SELECT _relcomp( $1, $2, $3, '' );
|
|
3104
|
+
$$ LANGUAGE sql;
|
|
3105
|
+
|
|
3106
|
+
-- set_eq( sql, array )
|
|
3107
|
+
CREATE OR REPLACE FUNCTION set_eq( TEXT, anyarray )
|
|
3108
|
+
RETURNS TEXT AS $$
|
|
3109
|
+
SELECT _relcomp( $1, $2, NULL::text, '' );
|
|
3110
|
+
$$ LANGUAGE sql;
|
|
3111
|
+
|
|
3112
|
+
-- bag_eq( sql, sql, description )
|
|
3113
|
+
CREATE OR REPLACE FUNCTION bag_eq( TEXT, TEXT, TEXT )
|
|
3114
|
+
RETURNS TEXT AS $$
|
|
3115
|
+
SELECT _relcomp( $1, $2, $3, 'ALL ' );
|
|
3116
|
+
$$ LANGUAGE sql;
|
|
3117
|
+
|
|
3118
|
+
-- bag_eq( sql, sql )
|
|
3119
|
+
CREATE OR REPLACE FUNCTION bag_eq( TEXT, TEXT )
|
|
3120
|
+
RETURNS TEXT AS $$
|
|
3121
|
+
SELECT _relcomp( $1, $2, NULL::text, 'ALL ' );
|
|
3122
|
+
$$ LANGUAGE sql;
|
|
3123
|
+
|
|
3124
|
+
-- bag_eq( sql, array, description )
|
|
3125
|
+
CREATE OR REPLACE FUNCTION bag_eq( TEXT, anyarray, TEXT )
|
|
3126
|
+
RETURNS TEXT AS $$
|
|
3127
|
+
SELECT _relcomp( $1, $2, $3, 'ALL ' );
|
|
3128
|
+
$$ LANGUAGE sql;
|
|
3129
|
+
|
|
3130
|
+
-- bag_eq( sql, array )
|
|
3131
|
+
CREATE OR REPLACE FUNCTION bag_eq( TEXT, anyarray )
|
|
3132
|
+
RETURNS TEXT AS $$
|
|
3133
|
+
SELECT _relcomp( $1, $2, NULL::text, 'ALL ' );
|
|
3134
|
+
$$ LANGUAGE sql;
|
|
3135
|
+
|
|
3136
|
+
CREATE OR REPLACE FUNCTION _do_ne( TEXT, TEXT, TEXT, TEXT )
|
|
3137
|
+
RETURNS TEXT AS $$
|
|
3138
|
+
DECLARE
|
|
3139
|
+
have ALIAS FOR $1;
|
|
3140
|
+
want ALIAS FOR $2;
|
|
3141
|
+
extras TEXT[] := '{}';
|
|
3142
|
+
missing TEXT[] := '{}';
|
|
3143
|
+
res BOOLEAN := TRUE;
|
|
3144
|
+
msg TEXT := '';
|
|
3145
|
+
BEGIN
|
|
3146
|
+
BEGIN
|
|
3147
|
+
-- Find extra records.
|
|
3148
|
+
EXECUTE 'SELECT EXISTS ( '
|
|
3149
|
+
|| '( SELECT * FROM ' || have || ' EXCEPT ' || $4
|
|
3150
|
+
|| ' SELECT * FROM ' || want
|
|
3151
|
+
|| ' ) UNION ( '
|
|
3152
|
+
|| ' SELECT * FROM ' || want || ' EXCEPT ' || $4
|
|
3153
|
+
|| ' SELECT * FROM ' || have
|
|
3154
|
+
|| ' ) LIMIT 1 )' INTO res;
|
|
3155
|
+
|
|
3156
|
+
-- Drop the temporary tables.
|
|
3157
|
+
EXECUTE 'DROP TABLE ' || have;
|
|
3158
|
+
EXECUTE 'DROP TABLE ' || want;
|
|
3159
|
+
EXCEPTION WHEN syntax_error OR datatype_mismatch THEN
|
|
3160
|
+
msg := E'\n' || diag(
|
|
3161
|
+
E' Columns differ between queries:\n'
|
|
3162
|
+
|| ' have: (' || _temptypes(have) || E')\n'
|
|
3163
|
+
|| ' want: (' || _temptypes(want) || ')'
|
|
3164
|
+
);
|
|
3165
|
+
EXECUTE 'DROP TABLE ' || have;
|
|
3166
|
+
EXECUTE 'DROP TABLE ' || want;
|
|
3167
|
+
RETURN ok(FALSE, $3) || msg;
|
|
3168
|
+
END;
|
|
3169
|
+
|
|
3170
|
+
-- Return the value from the query.
|
|
3171
|
+
RETURN ok(res, $3);
|
|
3172
|
+
END;
|
|
3173
|
+
$$ LANGUAGE plpgsql;
|
|
3174
|
+
|
|
3175
|
+
CREATE OR REPLACE FUNCTION _relne( TEXT, TEXT, TEXT, TEXT )
|
|
3176
|
+
RETURNS TEXT AS $$
|
|
3177
|
+
SELECT _do_ne(
|
|
3178
|
+
_temptable( $1, '__taphave__' ),
|
|
3179
|
+
_temptable( $2, '__tapwant__' ),
|
|
3180
|
+
$3, $4
|
|
3181
|
+
);
|
|
3182
|
+
$$ LANGUAGE sql;
|
|
3183
|
+
|
|
3184
|
+
CREATE OR REPLACE FUNCTION _relne( TEXT, anyarray, TEXT, TEXT )
|
|
3185
|
+
RETURNS TEXT AS $$
|
|
3186
|
+
SELECT _do_ne(
|
|
3187
|
+
_temptable( $1, '__taphave__' ),
|
|
3188
|
+
_temptable( $2, '__tapwant__' ),
|
|
3189
|
+
$3, $4
|
|
3190
|
+
);
|
|
3191
|
+
$$ LANGUAGE sql;
|
|
3192
|
+
|
|
3193
|
+
-- set_ne( sql, sql, description )
|
|
3194
|
+
CREATE OR REPLACE FUNCTION set_ne( TEXT, TEXT, TEXT )
|
|
3195
|
+
RETURNS TEXT AS $$
|
|
3196
|
+
SELECT _relne( $1, $2, $3, '' );
|
|
3197
|
+
$$ LANGUAGE sql;
|
|
3198
|
+
|
|
3199
|
+
-- set_ne( sql, sql )
|
|
3200
|
+
CREATE OR REPLACE FUNCTION set_ne( TEXT, TEXT )
|
|
3201
|
+
RETURNS TEXT AS $$
|
|
3202
|
+
SELECT _relne( $1, $2, NULL::text, '' );
|
|
3203
|
+
$$ LANGUAGE sql;
|
|
3204
|
+
|
|
3205
|
+
-- set_ne( sql, array, description )
|
|
3206
|
+
CREATE OR REPLACE FUNCTION set_ne( TEXT, anyarray, TEXT )
|
|
3207
|
+
RETURNS TEXT AS $$
|
|
3208
|
+
SELECT _relne( $1, $2, $3, '' );
|
|
3209
|
+
$$ LANGUAGE sql;
|
|
3210
|
+
|
|
3211
|
+
-- set_ne( sql, array )
|
|
3212
|
+
CREATE OR REPLACE FUNCTION set_ne( TEXT, anyarray )
|
|
3213
|
+
RETURNS TEXT AS $$
|
|
3214
|
+
SELECT _relne( $1, $2, NULL::text, '' );
|
|
3215
|
+
$$ LANGUAGE sql;
|
|
3216
|
+
|
|
3217
|
+
-- bag_ne( sql, sql, description )
|
|
3218
|
+
CREATE OR REPLACE FUNCTION bag_ne( TEXT, TEXT, TEXT )
|
|
3219
|
+
RETURNS TEXT AS $$
|
|
3220
|
+
SELECT _relne( $1, $2, $3, 'ALL ' );
|
|
3221
|
+
$$ LANGUAGE sql;
|
|
3222
|
+
|
|
3223
|
+
-- bag_ne( sql, sql )
|
|
3224
|
+
CREATE OR REPLACE FUNCTION bag_ne( TEXT, TEXT )
|
|
3225
|
+
RETURNS TEXT AS $$
|
|
3226
|
+
SELECT _relne( $1, $2, NULL::text, 'ALL ' );
|
|
3227
|
+
$$ LANGUAGE sql;
|
|
3228
|
+
|
|
3229
|
+
-- bag_ne( sql, array, description )
|
|
3230
|
+
CREATE OR REPLACE FUNCTION bag_ne( TEXT, anyarray, TEXT )
|
|
3231
|
+
RETURNS TEXT AS $$
|
|
3232
|
+
SELECT _relne( $1, $2, $3, 'ALL ' );
|
|
3233
|
+
$$ LANGUAGE sql;
|
|
3234
|
+
|
|
3235
|
+
-- bag_ne( sql, array )
|
|
3236
|
+
CREATE OR REPLACE FUNCTION bag_ne( TEXT, anyarray )
|
|
3237
|
+
RETURNS TEXT AS $$
|
|
3238
|
+
SELECT _relne( $1, $2, NULL::text, 'ALL ' );
|
|
3239
|
+
$$ LANGUAGE sql;
|
|
3240
|
+
|
|
3241
|
+
CREATE OR REPLACE FUNCTION _relcomp( TEXT, TEXT, TEXT, TEXT, TEXT )
|
|
3242
|
+
RETURNS TEXT AS $$
|
|
3243
|
+
DECLARE
|
|
3244
|
+
have TEXT := _temptable( $1, '__taphave__' );
|
|
3245
|
+
want TEXT := _temptable( $2, '__tapwant__' );
|
|
3246
|
+
results TEXT[] := '{}';
|
|
3247
|
+
res BOOLEAN := TRUE;
|
|
3248
|
+
msg TEXT := '';
|
|
3249
|
+
rec RECORD;
|
|
3250
|
+
BEGIN
|
|
3251
|
+
BEGIN
|
|
3252
|
+
-- Find relevant records.
|
|
3253
|
+
FOR rec in EXECUTE 'SELECT * FROM ' || want || ' ' || $4
|
|
3254
|
+
|| ' SELECT * FROM ' || have LOOP
|
|
3255
|
+
results := results || rec::text;
|
|
3256
|
+
END LOOP;
|
|
3257
|
+
|
|
3258
|
+
-- Drop the temporary tables.
|
|
3259
|
+
EXECUTE 'DROP TABLE ' || have;
|
|
3260
|
+
EXECUTE 'DROP TABLE ' || want;
|
|
3261
|
+
EXCEPTION WHEN syntax_error OR datatype_mismatch THEN
|
|
3262
|
+
msg := E'\n' || diag(
|
|
3263
|
+
E' Columns differ between queries:\n'
|
|
3264
|
+
|| ' have: (' || _temptypes(have) || E')\n'
|
|
3265
|
+
|| ' want: (' || _temptypes(want) || ')'
|
|
3266
|
+
);
|
|
3267
|
+
EXECUTE 'DROP TABLE ' || have;
|
|
3268
|
+
EXECUTE 'DROP TABLE ' || want;
|
|
3269
|
+
RETURN ok(FALSE, $3) || msg;
|
|
3270
|
+
END;
|
|
3271
|
+
|
|
3272
|
+
-- What records do we have?
|
|
3273
|
+
IF results[1] IS NOT NULL THEN
|
|
3274
|
+
res := FALSE;
|
|
3275
|
+
msg := msg || E'\n' || diag(
|
|
3276
|
+
' ' || $5 || E' records:\n '
|
|
3277
|
+
|| array_to_string( results, E'\n ' )
|
|
3278
|
+
);
|
|
3279
|
+
END IF;
|
|
3280
|
+
|
|
3281
|
+
RETURN ok(res, $3) || msg;
|
|
3282
|
+
END;
|
|
3283
|
+
$$ LANGUAGE plpgsql;
|
|
3284
|
+
|
|
3285
|
+
-- set_has( sql, sql, description )
|
|
3286
|
+
CREATE OR REPLACE FUNCTION set_has( TEXT, TEXT, TEXT )
|
|
3287
|
+
RETURNS TEXT AS $$
|
|
3288
|
+
SELECT _relcomp( $1, $2, $3, 'EXCEPT', 'Missing' );
|
|
3289
|
+
$$ LANGUAGE sql;
|
|
3290
|
+
|
|
3291
|
+
-- set_has( sql, sql )
|
|
3292
|
+
CREATE OR REPLACE FUNCTION set_has( TEXT, TEXT )
|
|
3293
|
+
RETURNS TEXT AS $$
|
|
3294
|
+
SELECT _relcomp( $1, $2, NULL::TEXT, 'EXCEPT', 'Missing' );
|
|
3295
|
+
$$ LANGUAGE sql;
|
|
3296
|
+
|
|
3297
|
+
-- bag_has( sql, sql, description )
|
|
3298
|
+
CREATE OR REPLACE FUNCTION bag_has( TEXT, TEXT, TEXT )
|
|
3299
|
+
RETURNS TEXT AS $$
|
|
3300
|
+
SELECT _relcomp( $1, $2, $3, 'EXCEPT ALL', 'Missing' );
|
|
3301
|
+
$$ LANGUAGE sql;
|
|
3302
|
+
|
|
3303
|
+
-- bag_has( sql, sql )
|
|
3304
|
+
CREATE OR REPLACE FUNCTION bag_has( TEXT, TEXT )
|
|
3305
|
+
RETURNS TEXT AS $$
|
|
3306
|
+
SELECT _relcomp( $1, $2, NULL::TEXT, 'EXCEPT ALL', 'Missing' );
|
|
3307
|
+
$$ LANGUAGE sql;
|
|
3308
|
+
|
|
3309
|
+
-- set_hasnt( sql, sql, description )
|
|
3310
|
+
CREATE OR REPLACE FUNCTION set_hasnt( TEXT, TEXT, TEXT )
|
|
3311
|
+
RETURNS TEXT AS $$
|
|
3312
|
+
SELECT _relcomp( $1, $2, $3, 'INTERSECT', 'Extra' );
|
|
3313
|
+
$$ LANGUAGE sql;
|
|
3314
|
+
|
|
3315
|
+
-- set_hasnt( sql, sql )
|
|
3316
|
+
CREATE OR REPLACE FUNCTION set_hasnt( TEXT, TEXT )
|
|
3317
|
+
RETURNS TEXT AS $$
|
|
3318
|
+
SELECT _relcomp( $1, $2, NULL::TEXT, 'INTERSECT', 'Extra' );
|
|
3319
|
+
$$ LANGUAGE sql;
|
|
3320
|
+
|
|
3321
|
+
-- bag_hasnt( sql, sql, description )
|
|
3322
|
+
CREATE OR REPLACE FUNCTION bag_hasnt( TEXT, TEXT, TEXT )
|
|
3323
|
+
RETURNS TEXT AS $$
|
|
3324
|
+
SELECT _relcomp( $1, $2, $3, 'INTERSECT ALL', 'Extra' );
|
|
3325
|
+
$$ LANGUAGE sql;
|
|
3326
|
+
|
|
3327
|
+
-- bag_hasnt( sql, sql )
|
|
3328
|
+
CREATE OR REPLACE FUNCTION bag_hasnt( TEXT, TEXT )
|
|
3329
|
+
RETURNS TEXT AS $$
|
|
3330
|
+
SELECT _relcomp( $1, $2, NULL::TEXT, 'INTERSECT ALL', 'Extra' );
|
|
3331
|
+
$$ LANGUAGE sql;
|
|
3332
|
+
|
|
3333
|
+
-- results_eq( cursor, cursor, description )
|
|
3334
|
+
CREATE OR REPLACE FUNCTION results_eq( refcursor, refcursor, text )
|
|
3335
|
+
RETURNS TEXT AS $$
|
|
3336
|
+
DECLARE
|
|
3337
|
+
have ALIAS FOR $1;
|
|
3338
|
+
want ALIAS FOR $2;
|
|
3339
|
+
have_rec RECORD;
|
|
3340
|
+
want_rec RECORD;
|
|
3341
|
+
have_found BOOLEAN;
|
|
3342
|
+
want_found BOOLEAN;
|
|
3343
|
+
rownum INTEGER := 1;
|
|
3344
|
+
err_msg text := 'details not available in pg <= 9.1';
|
|
3345
|
+
BEGIN
|
|
3346
|
+
FETCH have INTO have_rec;
|
|
3347
|
+
have_found := FOUND;
|
|
3348
|
+
FETCH want INTO want_rec;
|
|
3349
|
+
want_found := FOUND;
|
|
3350
|
+
WHILE have_found OR want_found LOOP
|
|
3351
|
+
IF have_rec IS DISTINCT FROM want_rec OR have_found <> want_found THEN
|
|
3352
|
+
RETURN ok( false, $3 ) || E'\n' || diag(
|
|
3353
|
+
' Results differ beginning at row ' || rownum || E':\n' ||
|
|
3354
|
+
' have: ' || CASE WHEN have_found THEN have_rec::text ELSE 'NULL' END || E'\n' ||
|
|
3355
|
+
' want: ' || CASE WHEN want_found THEN want_rec::text ELSE 'NULL' END
|
|
3356
|
+
);
|
|
3357
|
+
END IF;
|
|
3358
|
+
rownum = rownum + 1;
|
|
3359
|
+
FETCH have INTO have_rec;
|
|
3360
|
+
have_found := FOUND;
|
|
3361
|
+
FETCH want INTO want_rec;
|
|
3362
|
+
want_found := FOUND;
|
|
3363
|
+
END LOOP;
|
|
3364
|
+
|
|
3365
|
+
RETURN ok( true, $3 );
|
|
3366
|
+
EXCEPTION
|
|
3367
|
+
WHEN datatype_mismatch THEN
|
|
3368
|
+
RETURN ok( false, $3 ) || E'\n' || diag(
|
|
3369
|
+
E' Number of columns or their types differ between the queries' ||
|
|
3370
|
+
CASE WHEN have_rec::TEXT = want_rec::text THEN '' ELSE E':\n' ||
|
|
3371
|
+
' have: ' || CASE WHEN have_found THEN have_rec::text ELSE 'NULL' END || E'\n' ||
|
|
3372
|
+
' want: ' || CASE WHEN want_found THEN want_rec::text ELSE 'NULL' END
|
|
3373
|
+
END || E'\n ERROR: ' || err_msg
|
|
3374
|
+
);
|
|
3375
|
+
END;
|
|
3376
|
+
$$ LANGUAGE plpgsql;
|
|
3377
|
+
|
|
3378
|
+
-- results_eq( cursor, cursor )
|
|
3379
|
+
CREATE OR REPLACE FUNCTION results_eq( refcursor, refcursor )
|
|
3380
|
+
RETURNS TEXT AS $$
|
|
3381
|
+
SELECT results_eq( $1, $2, NULL::text );
|
|
3382
|
+
$$ LANGUAGE sql;
|
|
3383
|
+
|
|
3384
|
+
-- results_eq( sql, sql, description )
|
|
3385
|
+
CREATE OR REPLACE FUNCTION results_eq( TEXT, TEXT, TEXT )
|
|
3386
|
+
RETURNS TEXT AS $$
|
|
3387
|
+
DECLARE
|
|
3388
|
+
have REFCURSOR;
|
|
3389
|
+
want REFCURSOR;
|
|
3390
|
+
res TEXT;
|
|
3391
|
+
BEGIN
|
|
3392
|
+
OPEN have FOR EXECUTE _query($1);
|
|
3393
|
+
OPEN want FOR EXECUTE _query($2);
|
|
3394
|
+
res := results_eq(have, want, $3);
|
|
3395
|
+
CLOSE have;
|
|
3396
|
+
CLOSE want;
|
|
3397
|
+
RETURN res;
|
|
3398
|
+
END;
|
|
3399
|
+
$$ LANGUAGE plpgsql;
|
|
3400
|
+
|
|
3401
|
+
-- results_eq( sql, sql )
|
|
3402
|
+
CREATE OR REPLACE FUNCTION results_eq( TEXT, TEXT )
|
|
3403
|
+
RETURNS TEXT AS $$
|
|
3404
|
+
SELECT results_eq( $1, $2, NULL::text );
|
|
3405
|
+
$$ LANGUAGE sql;
|
|
3406
|
+
|
|
3407
|
+
-- results_eq( sql, array, description )
|
|
3408
|
+
CREATE OR REPLACE FUNCTION results_eq( TEXT, anyarray, TEXT )
|
|
3409
|
+
RETURNS TEXT AS $$
|
|
3410
|
+
DECLARE
|
|
3411
|
+
have REFCURSOR;
|
|
3412
|
+
want REFCURSOR;
|
|
3413
|
+
res TEXT;
|
|
3414
|
+
BEGIN
|
|
3415
|
+
OPEN have FOR EXECUTE _query($1);
|
|
3416
|
+
OPEN want FOR SELECT $2[i]
|
|
3417
|
+
FROM generate_series(array_lower($2, 1), array_upper($2, 1)) s(i);
|
|
3418
|
+
res := results_eq(have, want, $3);
|
|
3419
|
+
CLOSE have;
|
|
3420
|
+
CLOSE want;
|
|
3421
|
+
RETURN res;
|
|
3422
|
+
END;
|
|
3423
|
+
$$ LANGUAGE plpgsql;
|
|
3424
|
+
|
|
3425
|
+
-- results_eq( sql, array )
|
|
3426
|
+
CREATE OR REPLACE FUNCTION results_eq( TEXT, anyarray )
|
|
3427
|
+
RETURNS TEXT AS $$
|
|
3428
|
+
SELECT results_eq( $1, $2, NULL::text );
|
|
3429
|
+
$$ LANGUAGE sql;
|
|
3430
|
+
|
|
3431
|
+
-- results_eq( sql, cursor, description )
|
|
3432
|
+
CREATE OR REPLACE FUNCTION results_eq( TEXT, refcursor, TEXT )
|
|
3433
|
+
RETURNS TEXT AS $$
|
|
3434
|
+
DECLARE
|
|
3435
|
+
have REFCURSOR;
|
|
3436
|
+
res TEXT;
|
|
3437
|
+
BEGIN
|
|
3438
|
+
OPEN have FOR EXECUTE _query($1);
|
|
3439
|
+
res := results_eq(have, $2, $3);
|
|
3440
|
+
CLOSE have;
|
|
3441
|
+
RETURN res;
|
|
3442
|
+
END;
|
|
3443
|
+
$$ LANGUAGE plpgsql;
|
|
3444
|
+
|
|
3445
|
+
-- results_eq( sql, cursor )
|
|
3446
|
+
CREATE OR REPLACE FUNCTION results_eq( TEXT, refcursor )
|
|
3447
|
+
RETURNS TEXT AS $$
|
|
3448
|
+
SELECT results_eq( $1, $2, NULL::text );
|
|
3449
|
+
$$ LANGUAGE sql;
|
|
3450
|
+
|
|
3451
|
+
-- results_eq( cursor, sql, description )
|
|
3452
|
+
CREATE OR REPLACE FUNCTION results_eq( refcursor, TEXT, TEXT )
|
|
3453
|
+
RETURNS TEXT AS $$
|
|
3454
|
+
DECLARE
|
|
3455
|
+
want REFCURSOR;
|
|
3456
|
+
res TEXT;
|
|
3457
|
+
BEGIN
|
|
3458
|
+
OPEN want FOR EXECUTE _query($2);
|
|
3459
|
+
res := results_eq($1, want, $3);
|
|
3460
|
+
CLOSE want;
|
|
3461
|
+
RETURN res;
|
|
3462
|
+
END;
|
|
3463
|
+
$$ LANGUAGE plpgsql;
|
|
3464
|
+
|
|
3465
|
+
-- results_eq( cursor, sql )
|
|
3466
|
+
CREATE OR REPLACE FUNCTION results_eq( refcursor, TEXT )
|
|
3467
|
+
RETURNS TEXT AS $$
|
|
3468
|
+
SELECT results_eq( $1, $2, NULL::text );
|
|
3469
|
+
$$ LANGUAGE sql;
|
|
3470
|
+
|
|
3471
|
+
-- results_eq( cursor, array, description )
|
|
3472
|
+
CREATE OR REPLACE FUNCTION results_eq( refcursor, anyarray, TEXT )
|
|
3473
|
+
RETURNS TEXT AS $$
|
|
3474
|
+
DECLARE
|
|
3475
|
+
want REFCURSOR;
|
|
3476
|
+
res TEXT;
|
|
3477
|
+
BEGIN
|
|
3478
|
+
OPEN want FOR SELECT $2[i]
|
|
3479
|
+
FROM generate_series(array_lower($2, 1), array_upper($2, 1)) s(i);
|
|
3480
|
+
res := results_eq($1, want, $3);
|
|
3481
|
+
CLOSE want;
|
|
3482
|
+
RETURN res;
|
|
3483
|
+
END;
|
|
3484
|
+
$$ LANGUAGE plpgsql;
|
|
3485
|
+
|
|
3486
|
+
-- results_eq( cursor, array )
|
|
3487
|
+
CREATE OR REPLACE FUNCTION results_eq( refcursor, anyarray )
|
|
3488
|
+
RETURNS TEXT AS $$
|
|
3489
|
+
SELECT results_eq( $1, $2, NULL::text );
|
|
3490
|
+
$$ LANGUAGE sql;
|
|
3491
|
+
|
|
3492
|
+
-- results_ne( cursor, cursor, description )
|
|
3493
|
+
CREATE OR REPLACE FUNCTION results_ne( refcursor, refcursor, text )
|
|
3494
|
+
RETURNS TEXT AS $$
|
|
3495
|
+
DECLARE
|
|
3496
|
+
have ALIAS FOR $1;
|
|
3497
|
+
want ALIAS FOR $2;
|
|
3498
|
+
have_rec RECORD;
|
|
3499
|
+
want_rec RECORD;
|
|
3500
|
+
have_found BOOLEAN;
|
|
3501
|
+
want_found BOOLEAN;
|
|
3502
|
+
err_msg text := 'details not available in pg <= 9.1';
|
|
3503
|
+
BEGIN
|
|
3504
|
+
FETCH have INTO have_rec;
|
|
3505
|
+
have_found := FOUND;
|
|
3506
|
+
FETCH want INTO want_rec;
|
|
3507
|
+
want_found := FOUND;
|
|
3508
|
+
WHILE have_found OR want_found LOOP
|
|
3509
|
+
IF have_rec IS DISTINCT FROM want_rec OR have_found <> want_found THEN
|
|
3510
|
+
RETURN ok( true, $3 );
|
|
3511
|
+
ELSE
|
|
3512
|
+
FETCH have INTO have_rec;
|
|
3513
|
+
have_found := FOUND;
|
|
3514
|
+
FETCH want INTO want_rec;
|
|
3515
|
+
want_found := FOUND;
|
|
3516
|
+
END IF;
|
|
3517
|
+
END LOOP;
|
|
3518
|
+
RETURN ok( false, $3 );
|
|
3519
|
+
EXCEPTION
|
|
3520
|
+
WHEN datatype_mismatch THEN
|
|
3521
|
+
RETURN ok( false, $3 ) || E'\n' || diag(
|
|
3522
|
+
E' Number of columns or their types differ between the queries' ||
|
|
3523
|
+
CASE WHEN have_rec::TEXT = want_rec::text THEN '' ELSE E':\n' ||
|
|
3524
|
+
' have: ' || CASE WHEN have_found THEN have_rec::text ELSE 'NULL' END || E'\n' ||
|
|
3525
|
+
' want: ' || CASE WHEN want_found THEN want_rec::text ELSE 'NULL' END
|
|
3526
|
+
END || E'\n ERROR: ' || err_msg
|
|
3527
|
+
);
|
|
3528
|
+
END;
|
|
3529
|
+
$$ LANGUAGE plpgsql;
|
|
3530
|
+
|
|
3531
|
+
-- results_ne( cursor, cursor )
|
|
3532
|
+
CREATE OR REPLACE FUNCTION results_ne( refcursor, refcursor )
|
|
3533
|
+
RETURNS TEXT AS $$
|
|
3534
|
+
SELECT results_ne( $1, $2, NULL::text );
|
|
3535
|
+
$$ LANGUAGE sql;
|
|
3536
|
+
|
|
3537
|
+
-- results_ne( sql, sql, description )
|
|
3538
|
+
CREATE OR REPLACE FUNCTION results_ne( TEXT, TEXT, TEXT )
|
|
3539
|
+
RETURNS TEXT AS $$
|
|
3540
|
+
DECLARE
|
|
3541
|
+
have REFCURSOR;
|
|
3542
|
+
want REFCURSOR;
|
|
3543
|
+
res TEXT;
|
|
3544
|
+
BEGIN
|
|
3545
|
+
OPEN have FOR EXECUTE _query($1);
|
|
3546
|
+
OPEN want FOR EXECUTE _query($2);
|
|
3547
|
+
res := results_ne(have, want, $3);
|
|
3548
|
+
CLOSE have;
|
|
3549
|
+
CLOSE want;
|
|
3550
|
+
RETURN res;
|
|
3551
|
+
END;
|
|
3552
|
+
$$ LANGUAGE plpgsql;
|
|
3553
|
+
|
|
3554
|
+
-- results_ne( sql, sql )
|
|
3555
|
+
CREATE OR REPLACE FUNCTION results_ne( TEXT, TEXT )
|
|
3556
|
+
RETURNS TEXT AS $$
|
|
3557
|
+
SELECT results_ne( $1, $2, NULL::text );
|
|
3558
|
+
$$ LANGUAGE sql;
|
|
3559
|
+
|
|
3560
|
+
-- results_ne( sql, array, description )
|
|
3561
|
+
CREATE OR REPLACE FUNCTION results_ne( TEXT, anyarray, TEXT )
|
|
3562
|
+
RETURNS TEXT AS $$
|
|
3563
|
+
DECLARE
|
|
3564
|
+
have REFCURSOR;
|
|
3565
|
+
want REFCURSOR;
|
|
3566
|
+
res TEXT;
|
|
3567
|
+
BEGIN
|
|
3568
|
+
OPEN have FOR EXECUTE _query($1);
|
|
3569
|
+
OPEN want FOR SELECT $2[i]
|
|
3570
|
+
FROM generate_series(array_lower($2, 1), array_upper($2, 1)) s(i);
|
|
3571
|
+
res := results_ne(have, want, $3);
|
|
3572
|
+
CLOSE have;
|
|
3573
|
+
CLOSE want;
|
|
3574
|
+
RETURN res;
|
|
3575
|
+
END;
|
|
3576
|
+
$$ LANGUAGE plpgsql;
|
|
3577
|
+
|
|
3578
|
+
-- results_ne( sql, array )
|
|
3579
|
+
CREATE OR REPLACE FUNCTION results_ne( TEXT, anyarray )
|
|
3580
|
+
RETURNS TEXT AS $$
|
|
3581
|
+
SELECT results_ne( $1, $2, NULL::text );
|
|
3582
|
+
$$ LANGUAGE sql;
|
|
3583
|
+
|
|
3584
|
+
-- results_ne( sql, cursor, description )
|
|
3585
|
+
CREATE OR REPLACE FUNCTION results_ne( TEXT, refcursor, TEXT )
|
|
3586
|
+
RETURNS TEXT AS $$
|
|
3587
|
+
DECLARE
|
|
3588
|
+
have REFCURSOR;
|
|
3589
|
+
res TEXT;
|
|
3590
|
+
BEGIN
|
|
3591
|
+
OPEN have FOR EXECUTE _query($1);
|
|
3592
|
+
res := results_ne(have, $2, $3);
|
|
3593
|
+
CLOSE have;
|
|
3594
|
+
RETURN res;
|
|
3595
|
+
END;
|
|
3596
|
+
$$ LANGUAGE plpgsql;
|
|
3597
|
+
|
|
3598
|
+
-- results_ne( sql, cursor )
|
|
3599
|
+
CREATE OR REPLACE FUNCTION results_ne( TEXT, refcursor )
|
|
3600
|
+
RETURNS TEXT AS $$
|
|
3601
|
+
SELECT results_ne( $1, $2, NULL::text );
|
|
3602
|
+
$$ LANGUAGE sql;
|
|
3603
|
+
|
|
3604
|
+
-- results_ne( cursor, sql, description )
|
|
3605
|
+
CREATE OR REPLACE FUNCTION results_ne( refcursor, TEXT, TEXT )
|
|
3606
|
+
RETURNS TEXT AS $$
|
|
3607
|
+
DECLARE
|
|
3608
|
+
want REFCURSOR;
|
|
3609
|
+
res TEXT;
|
|
3610
|
+
BEGIN
|
|
3611
|
+
OPEN want FOR EXECUTE _query($2);
|
|
3612
|
+
res := results_ne($1, want, $3);
|
|
3613
|
+
CLOSE want;
|
|
3614
|
+
RETURN res;
|
|
3615
|
+
END;
|
|
3616
|
+
$$ LANGUAGE plpgsql;
|
|
3617
|
+
|
|
3618
|
+
-- results_ne( cursor, sql )
|
|
3619
|
+
CREATE OR REPLACE FUNCTION results_ne( refcursor, TEXT )
|
|
3620
|
+
RETURNS TEXT AS $$
|
|
3621
|
+
SELECT results_ne( $1, $2, NULL::text );
|
|
3622
|
+
$$ LANGUAGE sql;
|
|
3623
|
+
|
|
3624
|
+
-- results_ne( cursor, array, description )
|
|
3625
|
+
CREATE OR REPLACE FUNCTION results_ne( refcursor, anyarray, TEXT )
|
|
3626
|
+
RETURNS TEXT AS $$
|
|
3627
|
+
DECLARE
|
|
3628
|
+
want REFCURSOR;
|
|
3629
|
+
res TEXT;
|
|
3630
|
+
BEGIN
|
|
3631
|
+
OPEN want FOR SELECT $2[i]
|
|
3632
|
+
FROM generate_series(array_lower($2, 1), array_upper($2, 1)) s(i);
|
|
3633
|
+
res := results_ne($1, want, $3);
|
|
3634
|
+
CLOSE want;
|
|
3635
|
+
RETURN res;
|
|
3636
|
+
END;
|
|
3637
|
+
$$ LANGUAGE plpgsql;
|
|
3638
|
+
|
|
3639
|
+
-- results_ne( cursor, array )
|
|
3640
|
+
CREATE OR REPLACE FUNCTION results_ne( refcursor, anyarray )
|
|
3641
|
+
RETURNS TEXT AS $$
|
|
3642
|
+
SELECT results_ne( $1, $2, NULL::text );
|
|
3643
|
+
$$ LANGUAGE sql;
|
|
3644
|
+
|
|
3645
|
+
-- isa_ok( value, regtype, description )
|
|
3646
|
+
CREATE OR REPLACE FUNCTION isa_ok( anyelement, regtype, TEXT )
|
|
3647
|
+
RETURNS TEXT AS $$
|
|
3648
|
+
DECLARE
|
|
3649
|
+
typeof regtype := pg_typeof($1);
|
|
3650
|
+
BEGIN
|
|
3651
|
+
IF typeof = $2 THEN RETURN ok(true, $3 || ' isa ' || $2 ); END IF;
|
|
3652
|
+
RETURN ok(false, $3 || ' isa ' || $2 ) || E'\n' ||
|
|
3653
|
+
diag(' ' || $3 || ' isn''t a "' || $2 || '" it''s a "' || typeof || '"');
|
|
3654
|
+
END;
|
|
3655
|
+
$$ LANGUAGE plpgsql;
|
|
3656
|
+
|
|
3657
|
+
-- isa_ok( value, regtype )
|
|
3658
|
+
CREATE OR REPLACE FUNCTION isa_ok( anyelement, regtype )
|
|
3659
|
+
RETURNS TEXT AS $$
|
|
3660
|
+
SELECT isa_ok($1, $2, 'the value');
|
|
3661
|
+
$$ LANGUAGE sql;
|
|
3662
|
+
|
|
3663
|
+
-- is_empty( sql, description )
|
|
3664
|
+
CREATE OR REPLACE FUNCTION is_empty( TEXT, TEXT )
|
|
3665
|
+
RETURNS TEXT AS $$
|
|
3666
|
+
DECLARE
|
|
3667
|
+
extras TEXT[] := '{}';
|
|
3668
|
+
res BOOLEAN := TRUE;
|
|
3669
|
+
msg TEXT := '';
|
|
3670
|
+
rec RECORD;
|
|
3671
|
+
BEGIN
|
|
3672
|
+
-- Find extra records.
|
|
3673
|
+
FOR rec in EXECUTE _query($1) LOOP
|
|
3674
|
+
extras := extras || rec::text;
|
|
3675
|
+
END LOOP;
|
|
3676
|
+
|
|
3677
|
+
-- What extra records do we have?
|
|
3678
|
+
IF extras[1] IS NOT NULL THEN
|
|
3679
|
+
res := FALSE;
|
|
3680
|
+
msg := E'\n' || diag(
|
|
3681
|
+
E' Unexpected records:\n '
|
|
3682
|
+
|| array_to_string( extras, E'\n ' )
|
|
3683
|
+
);
|
|
3684
|
+
END IF;
|
|
3685
|
+
|
|
3686
|
+
RETURN ok(res, $2) || msg;
|
|
3687
|
+
END;
|
|
3688
|
+
$$ LANGUAGE plpgsql;
|
|
3689
|
+
|
|
3690
|
+
-- is_empty( sql )
|
|
3691
|
+
CREATE OR REPLACE FUNCTION is_empty( TEXT )
|
|
3692
|
+
RETURNS TEXT AS $$
|
|
3693
|
+
SELECT is_empty( $1, NULL );
|
|
3694
|
+
$$ LANGUAGE sql;
|
|
3695
|
+
|
|
3696
|
+
-- isnt_empty( sql, description )
|
|
3697
|
+
CREATE OR REPLACE FUNCTION collect_tap( VARIADIC text[] )
|
|
3698
|
+
RETURNS TEXT AS $$
|
|
3699
|
+
SELECT array_to_string($1, E'\n');
|
|
3700
|
+
$$ LANGUAGE sql;
|
|
3701
|
+
|
|
3702
|
+
-- collect_tap( tap[] )
|
|
3703
|
+
CREATE OR REPLACE FUNCTION collect_tap( VARCHAR[] )
|
|
3704
|
+
RETURNS TEXT AS $$
|
|
3705
|
+
SELECT array_to_string($1, E'\n');
|
|
3706
|
+
$$ LANGUAGE sql;
|
|
3707
|
+
|
|
3708
|
+
CREATE OR REPLACE FUNCTION _tlike ( BOOLEAN, TEXT, TEXT, TEXT )
|
|
3709
|
+
RETURNS TEXT AS $$
|
|
3710
|
+
SELECT ok( $1, $4 ) || CASE WHEN $1 THEN '' ELSE E'\n' || diag(
|
|
3711
|
+
' error message: ' || COALESCE( quote_literal($2), 'NULL' ) ||
|
|
3712
|
+
E'\n doesn''t match: ' || COALESCE( quote_literal($3), 'NULL' )
|
|
3713
|
+
) END;
|
|
3714
|
+
$$ LANGUAGE sql;
|
|
3715
|
+
|
|
3716
|
+
-- throws_like ( sql, pattern, description )
|
|
3717
|
+
CREATE OR REPLACE FUNCTION throws_like ( TEXT, TEXT, TEXT )
|
|
3718
|
+
RETURNS TEXT AS $$
|
|
3719
|
+
BEGIN
|
|
3720
|
+
EXECUTE _query($1);
|
|
3721
|
+
RETURN ok( FALSE, $3 ) || E'\n' || diag( ' no exception thrown' );
|
|
3722
|
+
EXCEPTION WHEN OTHERS THEN
|
|
3723
|
+
return _tlike( SQLERRM ~~ $2, SQLERRM, $2, $3 );
|
|
3724
|
+
END;
|
|
3725
|
+
$$ LANGUAGE plpgsql;
|
|
3726
|
+
|
|
3727
|
+
-- throws_like ( sql, pattern )
|
|
3728
|
+
CREATE OR REPLACE FUNCTION throws_like ( TEXT, TEXT )
|
|
3729
|
+
RETURNS TEXT AS $$
|
|
3730
|
+
SELECT throws_like($1, $2, 'Should throw exception like ' || quote_literal($2) );
|
|
3731
|
+
$$ LANGUAGE sql;
|
|
3732
|
+
|
|
3733
|
+
-- throws_ilike ( sql, pattern, description )
|
|
3734
|
+
CREATE OR REPLACE FUNCTION throws_ilike ( TEXT, TEXT, TEXT )
|
|
3735
|
+
RETURNS TEXT AS $$
|
|
3736
|
+
BEGIN
|
|
3737
|
+
EXECUTE _query($1);
|
|
3738
|
+
RETURN ok( FALSE, $3 ) || E'\n' || diag( ' no exception thrown' );
|
|
3739
|
+
EXCEPTION WHEN OTHERS THEN
|
|
3740
|
+
return _tlike( SQLERRM ~~* $2, SQLERRM, $2, $3 );
|
|
3741
|
+
END;
|
|
3742
|
+
$$ LANGUAGE plpgsql;
|
|
3743
|
+
|
|
3744
|
+
-- throws_ilike ( sql, pattern )
|
|
3745
|
+
CREATE OR REPLACE FUNCTION throws_ilike ( TEXT, TEXT )
|
|
3746
|
+
RETURNS TEXT AS $$
|
|
3747
|
+
SELECT throws_ilike($1, $2, 'Should throw exception like ' || quote_literal($2) );
|
|
3748
|
+
$$ LANGUAGE sql;
|
|
3749
|
+
|
|
3750
|
+
-- throws_matching ( sql, pattern, description )
|
|
3751
|
+
CREATE OR REPLACE FUNCTION throws_matching ( TEXT, TEXT, TEXT )
|
|
3752
|
+
RETURNS TEXT AS $$
|
|
3753
|
+
BEGIN
|
|
3754
|
+
EXECUTE _query($1);
|
|
3755
|
+
RETURN ok( FALSE, $3 ) || E'\n' || diag( ' no exception thrown' );
|
|
3756
|
+
EXCEPTION WHEN OTHERS THEN
|
|
3757
|
+
return _tlike( SQLERRM ~ $2, SQLERRM, $2, $3 );
|
|
3758
|
+
END;
|
|
3759
|
+
$$ LANGUAGE plpgsql;
|
|
3760
|
+
|
|
3761
|
+
-- throws_matching ( sql, pattern )
|
|
3762
|
+
CREATE OR REPLACE FUNCTION throws_matching ( TEXT, TEXT )
|
|
3763
|
+
RETURNS TEXT AS $$
|
|
3764
|
+
SELECT throws_matching($1, $2, 'Should throw exception matching ' || quote_literal($2) );
|
|
3765
|
+
$$ LANGUAGE sql;
|
|
3766
|
+
|
|
3767
|
+
-- throws_imatching ( sql, pattern, description )
|
|
3768
|
+
CREATE OR REPLACE FUNCTION throws_imatching ( TEXT, TEXT, TEXT )
|
|
3769
|
+
RETURNS TEXT AS $$
|
|
3770
|
+
BEGIN
|
|
3771
|
+
EXECUTE _query($1);
|
|
3772
|
+
RETURN ok( FALSE, $3 ) || E'\n' || diag( ' no exception thrown' );
|
|
3773
|
+
EXCEPTION WHEN OTHERS THEN
|
|
3774
|
+
return _tlike( SQLERRM ~* $2, SQLERRM, $2, $3 );
|
|
3775
|
+
END;
|
|
3776
|
+
$$ LANGUAGE plpgsql;
|
|
3777
|
+
|
|
3778
|
+
-- throws_imatching ( sql, pattern )
|
|
3779
|
+
CREATE OR REPLACE FUNCTION throws_imatching ( TEXT, TEXT )
|
|
3780
|
+
RETURNS TEXT AS $$
|
|
3781
|
+
SELECT throws_imatching($1, $2, 'Should throw exception matching ' || quote_literal($2) );
|
|
3782
|
+
$$ LANGUAGE sql;
|
|
3783
|
+
|
|
3784
|
+
-- roles_are( roles[], description )
|
|
3785
|
+
CREATE OR REPLACE FUNCTION _dexists ( NAME, NAME )
|
|
3786
|
+
RETURNS BOOLEAN AS $$
|
|
3787
|
+
SELECT EXISTS(
|
|
3788
|
+
SELECT true
|
|
3789
|
+
FROM pg_catalog.pg_namespace n
|
|
3790
|
+
JOIN pg_catalog.pg_type t on n.oid = t.typnamespace
|
|
3791
|
+
WHERE n.nspname = $1
|
|
3792
|
+
AND t.typname = $2
|
|
3793
|
+
);
|
|
3794
|
+
$$ LANGUAGE SQL;
|
|
3795
|
+
|
|
3796
|
+
CREATE OR REPLACE FUNCTION _dexists ( NAME )
|
|
3797
|
+
RETURNS BOOLEAN AS $$
|
|
3798
|
+
SELECT EXISTS(
|
|
3799
|
+
SELECT true
|
|
3800
|
+
FROM pg_catalog.pg_type t
|
|
3801
|
+
WHERE t.typname = $1
|
|
3802
|
+
AND pg_catalog.pg_type_is_visible(t.oid)
|
|
3803
|
+
);
|
|
3804
|
+
$$ LANGUAGE SQL;
|
|
3805
|
+
|
|
3806
|
+
CREATE OR REPLACE FUNCTION _get_dtype( NAME, TEXT, BOOLEAN )
|
|
3807
|
+
RETURNS TEXT AS $$
|
|
3808
|
+
SELECT CASE WHEN $3 AND pg_catalog.pg_type_is_visible(t.oid)
|
|
3809
|
+
THEN quote_ident(tn.nspname) || '.'
|
|
3810
|
+
ELSE ''
|
|
3811
|
+
END || pg_catalog.format_type(t.oid, t.typtypmod)
|
|
3812
|
+
FROM pg_catalog.pg_type d
|
|
3813
|
+
JOIN pg_catalog.pg_namespace dn ON d.typnamespace = dn.oid
|
|
3814
|
+
JOIN pg_catalog.pg_type t ON d.typbasetype = t.oid
|
|
3815
|
+
JOIN pg_catalog.pg_namespace tn ON t.typnamespace = tn.oid
|
|
3816
|
+
WHERE d.typisdefined
|
|
3817
|
+
AND dn.nspname = $1
|
|
3818
|
+
AND d.typname = LOWER($2)
|
|
3819
|
+
AND d.typtype = 'd'
|
|
3820
|
+
$$ LANGUAGE sql;
|
|
3821
|
+
|
|
3822
|
+
CREATE OR REPLACE FUNCTION _get_dtype( NAME )
|
|
3823
|
+
RETURNS TEXT AS $$
|
|
3824
|
+
SELECT pg_catalog.format_type(t.oid, t.typtypmod)
|
|
3825
|
+
FROM pg_catalog.pg_type d
|
|
3826
|
+
JOIN pg_catalog.pg_type t ON d.typbasetype = t.oid
|
|
3827
|
+
WHERE d.typisdefined
|
|
3828
|
+
AND pg_catalog.pg_type_is_visible(d.oid)
|
|
3829
|
+
AND d.typname = LOWER($1)
|
|
3830
|
+
AND d.typtype = 'd'
|
|
3831
|
+
$$ LANGUAGE sql;
|
|
3832
|
+
|
|
3833
|
+
-- domain_type_is( schema, domain, schema, type, description )
|
|
3834
|
+
CREATE OR REPLACE FUNCTION domain_type_is( NAME, TEXT, NAME, TEXT, TEXT )
|
|
3835
|
+
RETURNS TEXT AS $$
|
|
3836
|
+
DECLARE
|
|
3837
|
+
actual_type TEXT := _get_dtype($1, $2, true);
|
|
3838
|
+
BEGIN
|
|
3839
|
+
IF actual_type IS NULL THEN
|
|
3840
|
+
RETURN fail( $5 ) || E'\n' || diag (
|
|
3841
|
+
' Domain ' || quote_ident($1) || '.' || $2
|
|
3842
|
+
|| ' does not exist'
|
|
3843
|
+
);
|
|
3844
|
+
END IF;
|
|
3845
|
+
|
|
3846
|
+
IF quote_ident($3) = ANY(current_schemas(true)) THEN
|
|
3847
|
+
RETURN is( actual_type, quote_ident($3) || '.' || _typename($4), $5);
|
|
3848
|
+
END IF;
|
|
3849
|
+
RETURN is( actual_type, _typename(quote_ident($3) || '.' || $4), $5);
|
|
3850
|
+
END;
|
|
3851
|
+
$$ LANGUAGE plpgsql;
|
|
3852
|
+
|
|
3853
|
+
-- domain_type_is( schema, domain, schema, type )
|
|
3854
|
+
CREATE OR REPLACE FUNCTION domain_type_is( NAME, TEXT, NAME, TEXT )
|
|
3855
|
+
RETURNS TEXT AS $$
|
|
3856
|
+
SELECT domain_type_is(
|
|
3857
|
+
$1, $2, $3, $4,
|
|
3858
|
+
'Domain ' || quote_ident($1) || '.' || $2
|
|
3859
|
+
|| ' should extend type ' || quote_ident($3) || '.' || $4
|
|
3860
|
+
);
|
|
3861
|
+
$$ LANGUAGE SQL;
|
|
3862
|
+
|
|
3863
|
+
-- domain_type_is( schema, domain, type, description )
|
|
3864
|
+
CREATE OR REPLACE FUNCTION domain_type_is( NAME, TEXT, TEXT, TEXT )
|
|
3865
|
+
RETURNS TEXT AS $$
|
|
3866
|
+
DECLARE
|
|
3867
|
+
actual_type TEXT := _get_dtype($1, $2, false);
|
|
3868
|
+
BEGIN
|
|
3869
|
+
IF actual_type IS NULL THEN
|
|
3870
|
+
RETURN fail( $4 ) || E'\n' || diag (
|
|
3871
|
+
' Domain ' || quote_ident($1) || '.' || $2
|
|
3872
|
+
|| ' does not exist'
|
|
3873
|
+
);
|
|
3874
|
+
END IF;
|
|
3875
|
+
|
|
3876
|
+
RETURN is( actual_type, _typename($3), $4 );
|
|
3877
|
+
END;
|
|
3878
|
+
$$ LANGUAGE plpgsql;
|
|
3879
|
+
|
|
3880
|
+
-- domain_type_is( schema, domain, type )
|
|
3881
|
+
CREATE OR REPLACE FUNCTION domain_type_is( NAME, TEXT, TEXT )
|
|
3882
|
+
RETURNS TEXT AS $$
|
|
3883
|
+
SELECT domain_type_is(
|
|
3884
|
+
$1, $2, $3,
|
|
3885
|
+
'Domain ' || quote_ident($1) || '.' || $2
|
|
3886
|
+
|| ' should extend type ' || $3
|
|
3887
|
+
);
|
|
3888
|
+
$$ LANGUAGE SQL;
|
|
3889
|
+
|
|
3890
|
+
-- domain_type_is( domain, type, description )
|
|
3891
|
+
CREATE OR REPLACE FUNCTION domain_type_is( TEXT, TEXT, TEXT )
|
|
3892
|
+
RETURNS TEXT AS $$
|
|
3893
|
+
DECLARE
|
|
3894
|
+
actual_type TEXT := _get_dtype($1);
|
|
3895
|
+
BEGIN
|
|
3896
|
+
IF actual_type IS NULL THEN
|
|
3897
|
+
RETURN fail( $3 ) || E'\n' || diag (
|
|
3898
|
+
' Domain ' || $1 || ' does not exist'
|
|
3899
|
+
);
|
|
3900
|
+
END IF;
|
|
3901
|
+
|
|
3902
|
+
RETURN is( actual_type, _typename($2), $3 );
|
|
3903
|
+
END;
|
|
3904
|
+
$$ LANGUAGE plpgsql;
|
|
3905
|
+
|
|
3906
|
+
-- domain_type_is( domain, type )
|
|
3907
|
+
CREATE OR REPLACE FUNCTION domain_type_is( TEXT, TEXT )
|
|
3908
|
+
RETURNS TEXT AS $$
|
|
3909
|
+
SELECT domain_type_is(
|
|
3910
|
+
$1, $2,
|
|
3911
|
+
'Domain ' || $1 || ' should extend type ' || $2
|
|
3912
|
+
);
|
|
3913
|
+
$$ LANGUAGE SQL;
|
|
3914
|
+
|
|
3915
|
+
-- domain_type_isnt( schema, domain, schema, type, description )
|
|
3916
|
+
CREATE OR REPLACE FUNCTION domain_type_isnt( NAME, TEXT, NAME, TEXT, TEXT )
|
|
3917
|
+
RETURNS TEXT AS $$
|
|
3918
|
+
DECLARE
|
|
3919
|
+
actual_type TEXT := _get_dtype($1, $2, true);
|
|
3920
|
+
BEGIN
|
|
3921
|
+
IF actual_type IS NULL THEN
|
|
3922
|
+
RETURN fail( $5 ) || E'\n' || diag (
|
|
3923
|
+
' Domain ' || quote_ident($1) || '.' || $2
|
|
3924
|
+
|| ' does not exist'
|
|
3925
|
+
);
|
|
3926
|
+
END IF;
|
|
3927
|
+
|
|
3928
|
+
IF quote_ident($3) = ANY(current_schemas(true)) THEN
|
|
3929
|
+
RETURN isnt( actual_type, quote_ident($3) || '.' || _typename($4), $5);
|
|
3930
|
+
END IF;
|
|
3931
|
+
RETURN isnt( actual_type, _typename(quote_ident($3) || '.' || $4), $5);
|
|
3932
|
+
END;
|
|
3933
|
+
$$ LANGUAGE plpgsql;
|
|
3934
|
+
|
|
3935
|
+
-- domain_type_isnt( schema, domain, schema, type )
|
|
3936
|
+
CREATE OR REPLACE FUNCTION domain_type_isnt( NAME, TEXT, NAME, TEXT )
|
|
3937
|
+
RETURNS TEXT AS $$
|
|
3938
|
+
SELECT domain_type_isnt(
|
|
3939
|
+
$1, $2, $3, $4,
|
|
3940
|
+
'Domain ' || quote_ident($1) || '.' || $2
|
|
3941
|
+
|| ' should not extend type ' || quote_ident($3) || '.' || $4
|
|
3942
|
+
);
|
|
3943
|
+
$$ LANGUAGE SQL;
|
|
3944
|
+
|
|
3945
|
+
-- domain_type_isnt( schema, domain, type, description )
|
|
3946
|
+
CREATE OR REPLACE FUNCTION domain_type_isnt( NAME, TEXT, TEXT, TEXT )
|
|
3947
|
+
RETURNS TEXT AS $$
|
|
3948
|
+
DECLARE
|
|
3949
|
+
actual_type TEXT := _get_dtype($1, $2, false);
|
|
3950
|
+
BEGIN
|
|
3951
|
+
IF actual_type IS NULL THEN
|
|
3952
|
+
RETURN fail( $4 ) || E'\n' || diag (
|
|
3953
|
+
' Domain ' || quote_ident($1) || '.' || $2
|
|
3954
|
+
|| ' does not exist'
|
|
3955
|
+
);
|
|
3956
|
+
END IF;
|
|
3957
|
+
|
|
3958
|
+
RETURN isnt( actual_type, _typename($3), $4 );
|
|
3959
|
+
END;
|
|
3960
|
+
$$ LANGUAGE plpgsql;
|
|
3961
|
+
|
|
3962
|
+
-- domain_type_isnt( schema, domain, type )
|
|
3963
|
+
CREATE OR REPLACE FUNCTION domain_type_isnt( NAME, TEXT, TEXT )
|
|
3964
|
+
RETURNS TEXT AS $$
|
|
3965
|
+
SELECT domain_type_isnt(
|
|
3966
|
+
$1, $2, $3,
|
|
3967
|
+
'Domain ' || quote_ident($1) || '.' || $2
|
|
3968
|
+
|| ' should not extend type ' || $3
|
|
3969
|
+
);
|
|
3970
|
+
$$ LANGUAGE SQL;
|
|
3971
|
+
|
|
3972
|
+
-- domain_type_isnt( domain, type, description )
|
|
3973
|
+
CREATE OR REPLACE FUNCTION domain_type_isnt( TEXT, TEXT, TEXT )
|
|
3974
|
+
RETURNS TEXT AS $$
|
|
3975
|
+
DECLARE
|
|
3976
|
+
actual_type TEXT := _get_dtype($1);
|
|
3977
|
+
BEGIN
|
|
3978
|
+
IF actual_type IS NULL THEN
|
|
3979
|
+
RETURN fail( $3 ) || E'\n' || diag (
|
|
3980
|
+
' Domain ' || $1 || ' does not exist'
|
|
3981
|
+
);
|
|
3982
|
+
END IF;
|
|
3983
|
+
|
|
3984
|
+
RETURN isnt( actual_type, _typename($2), $3 );
|
|
3985
|
+
END;
|
|
3986
|
+
$$ LANGUAGE plpgsql;
|
|
3987
|
+
|
|
3988
|
+
-- domain_type_isnt( domain, type )
|
|
3989
|
+
CREATE OR REPLACE FUNCTION domain_type_isnt( TEXT, TEXT )
|
|
3990
|
+
RETURNS TEXT AS $$
|
|
3991
|
+
SELECT domain_type_isnt(
|
|
3992
|
+
$1, $2,
|
|
3993
|
+
'Domain ' || $1 || ' should not extend type ' || $2
|
|
3994
|
+
);
|
|
3995
|
+
$$ LANGUAGE SQL;
|
|
3996
|
+
|
|
3997
|
+
-- row_eq( sql, record, description )
|
|
3998
|
+
CREATE OR REPLACE FUNCTION row_eq( TEXT, anyelement, TEXT )
|
|
3999
|
+
RETURNS TEXT AS $$
|
|
4000
|
+
DECLARE
|
|
4001
|
+
rec RECORD;
|
|
4002
|
+
BEGIN
|
|
4003
|
+
EXECUTE _query($1) INTO rec;
|
|
4004
|
+
IF NOT rec IS DISTINCT FROM $2 THEN RETURN ok(true, $3); END IF;
|
|
4005
|
+
RETURN ok(false, $3 ) || E'\n' || diag(
|
|
4006
|
+
' have: ' || CASE WHEN rec IS NULL THEN 'NULL' ELSE rec::text END ||
|
|
4007
|
+
E'\n want: ' || CASE WHEN $2 IS NULL THEN 'NULL' ELSE $2::text END
|
|
4008
|
+
);
|
|
4009
|
+
END;
|
|
4010
|
+
$$ LANGUAGE plpgsql;
|
|
4011
|
+
|
|
4012
|
+
-- row_eq( sql, record )
|
|
4013
|
+
CREATE OR REPLACE FUNCTION row_eq( TEXT, anyelement )
|
|
4014
|
+
RETURNS TEXT AS $$
|
|
4015
|
+
SELECT row_eq($1, $2, NULL );
|
|
4016
|
+
$$ LANGUAGE sql;
|
|
4017
|
+
|
|
4018
|
+
-- triggers_are( schema, table, triggers[], description )
|
|
4019
|
+
CREATE OR REPLACE FUNCTION is_normal_function ( NAME, NAME, NAME[], TEXT )
|
|
4020
|
+
RETURNS TEXT AS $$
|
|
4021
|
+
SELECT _func_compare($1, $2, $3, _type_func('f', $1, $2, $3), $4 );
|
|
4022
|
+
$$ LANGUAGE SQL;
|
|
4023
|
+
|
|
4024
|
+
-- is_normal_function( schema, function, args[] )
|
|
4025
|
+
CREATE OR REPLACE FUNCTION is_normal_function( NAME, NAME, NAME[] )
|
|
4026
|
+
RETURNS TEXT AS $$
|
|
4027
|
+
SELECT _func_compare(
|
|
4028
|
+
$1, $2, $3,
|
|
4029
|
+
_type_func('f', $1, $2, $3),
|
|
4030
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
4031
|
+
array_to_string($3, ', ') || ') should be a normal function'
|
|
4032
|
+
);
|
|
4033
|
+
$$ LANGUAGE sql;
|
|
4034
|
+
|
|
4035
|
+
-- is_normal_function( schema, function, description )
|
|
4036
|
+
CREATE OR REPLACE FUNCTION is_normal_function ( NAME, NAME, TEXT )
|
|
4037
|
+
RETURNS TEXT AS $$
|
|
4038
|
+
SELECT _func_compare($1, $2, _type_func('f', $1, $2), $3 );
|
|
4039
|
+
$$ LANGUAGE SQL;
|
|
4040
|
+
|
|
4041
|
+
-- is_normal_function( schema, function )
|
|
4042
|
+
CREATE OR REPLACE FUNCTION is_normal_function( NAME, NAME )
|
|
4043
|
+
RETURNS TEXT AS $$
|
|
4044
|
+
SELECT _func_compare(
|
|
4045
|
+
$1, $2, _type_func('f', $1, $2),
|
|
4046
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should be a normal function'
|
|
4047
|
+
);
|
|
4048
|
+
$$ LANGUAGE sql;
|
|
4049
|
+
|
|
4050
|
+
-- is_normal_function( function, args[], description )
|
|
4051
|
+
CREATE OR REPLACE FUNCTION is_normal_function ( NAME, NAME[], TEXT )
|
|
4052
|
+
RETURNS TEXT AS $$
|
|
4053
|
+
SELECT _func_compare(NULL, $1, $2, _type_func('f', $1, $2), $3 );
|
|
4054
|
+
$$ LANGUAGE SQL;
|
|
4055
|
+
|
|
4056
|
+
-- is_normal_function( function, args[] )
|
|
4057
|
+
CREATE OR REPLACE FUNCTION is_normal_function( NAME, NAME[] )
|
|
4058
|
+
RETURNS TEXT AS $$
|
|
4059
|
+
SELECT _func_compare(
|
|
4060
|
+
NULL, $1, $2, _type_func('f', $1, $2),
|
|
4061
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
4062
|
+
array_to_string($2, ', ') || ') should be a normal function'
|
|
4063
|
+
);
|
|
4064
|
+
$$ LANGUAGE sql;
|
|
4065
|
+
|
|
4066
|
+
-- is_normal_function( function, description )
|
|
4067
|
+
CREATE OR REPLACE FUNCTION is_normal_function( NAME, TEXT )
|
|
4068
|
+
RETURNS TEXT AS $$
|
|
4069
|
+
SELECT _func_compare(NULL, $1, _type_func('f', $1), $2 );
|
|
4070
|
+
$$ LANGUAGE sql;
|
|
4071
|
+
|
|
4072
|
+
-- is_normal_function( function )
|
|
4073
|
+
CREATE OR REPLACE FUNCTION is_normal_function( NAME )
|
|
4074
|
+
RETURNS TEXT AS $$
|
|
4075
|
+
SELECT _func_compare(
|
|
4076
|
+
NULL, $1, _type_func('f', $1),
|
|
4077
|
+
'Function ' || quote_ident($1) || '() should be a normal function'
|
|
4078
|
+
);
|
|
4079
|
+
$$ LANGUAGE sql;
|
|
4080
|
+
|
|
4081
|
+
-- isnt_normal_function( schema, function, args[], description )
|
|
4082
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function ( NAME, NAME, NAME[], TEXT )
|
|
4083
|
+
RETURNS TEXT AS $$
|
|
4084
|
+
SELECT _func_compare($1, $2, $3, NOT _type_func('f', $1, $2, $3), $4 );
|
|
4085
|
+
$$ LANGUAGE SQL;
|
|
4086
|
+
|
|
4087
|
+
-- isnt_normal_function( schema, function, args[] )
|
|
4088
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function( NAME, NAME, NAME[] )
|
|
4089
|
+
RETURNS TEXT AS $$
|
|
4090
|
+
SELECT _func_compare(
|
|
4091
|
+
$1, $2, $3, NOT _type_func('f', $1, $2, $3),
|
|
4092
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
4093
|
+
array_to_string($3, ', ') || ') should not be a normal function'
|
|
4094
|
+
);
|
|
4095
|
+
$$ LANGUAGE sql;
|
|
4096
|
+
|
|
4097
|
+
-- isnt_normal_function( schema, function, description )
|
|
4098
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function ( NAME, NAME, TEXT )
|
|
4099
|
+
RETURNS TEXT AS $$
|
|
4100
|
+
SELECT _func_compare($1, $2, NOT _type_func('f', $1, $2), $3 );
|
|
4101
|
+
$$ LANGUAGE SQL;
|
|
4102
|
+
|
|
4103
|
+
-- isnt_normal_function( schema, function )
|
|
4104
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function( NAME, NAME )
|
|
4105
|
+
RETURNS TEXT AS $$
|
|
4106
|
+
SELECT _func_compare(
|
|
4107
|
+
$1, $2, NOT _type_func('f', $1, $2),
|
|
4108
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should not be a normal function'
|
|
4109
|
+
);
|
|
4110
|
+
$$ LANGUAGE sql;
|
|
4111
|
+
|
|
4112
|
+
-- isnt_normal_function( function, args[], description )
|
|
4113
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function ( NAME, NAME[], TEXT )
|
|
4114
|
+
RETURNS TEXT AS $$
|
|
4115
|
+
SELECT _func_compare(NULL, $1, $2, NOT _type_func('f', $1, $2), $3 );
|
|
4116
|
+
$$ LANGUAGE SQL;
|
|
4117
|
+
|
|
4118
|
+
-- isnt_normal_function( function, args[] )
|
|
4119
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function( NAME, NAME[] )
|
|
4120
|
+
RETURNS TEXT AS $$
|
|
4121
|
+
SELECT _func_compare(
|
|
4122
|
+
NULL, $1, $2,
|
|
4123
|
+
NOT _type_func('f', $1, $2),
|
|
4124
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
4125
|
+
array_to_string($2, ', ') || ') should not be a normal function'
|
|
4126
|
+
);
|
|
4127
|
+
$$ LANGUAGE sql;
|
|
4128
|
+
|
|
4129
|
+
-- isnt_normal_function( function, description )
|
|
4130
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function( NAME, TEXT )
|
|
4131
|
+
RETURNS TEXT AS $$
|
|
4132
|
+
SELECT _func_compare(NULL, $1, NOT _type_func('f', $1), $2 );
|
|
4133
|
+
$$ LANGUAGE sql;
|
|
4134
|
+
|
|
4135
|
+
-- isnt_normal_function( function )
|
|
4136
|
+
CREATE OR REPLACE FUNCTION isnt_normal_function( NAME )
|
|
4137
|
+
RETURNS TEXT AS $$
|
|
4138
|
+
SELECT _func_compare(
|
|
4139
|
+
NULL, $1, NOT _type_func('f', $1),
|
|
4140
|
+
'Function ' || quote_ident($1) || '() should not be a normal function'
|
|
4141
|
+
);
|
|
4142
|
+
$$ LANGUAGE sql;
|
|
4143
|
+
|
|
4144
|
+
-- is_window( schema, function, args[], description )
|
|
4145
|
+
CREATE OR REPLACE FUNCTION is_window ( NAME, NAME, NAME[], TEXT )
|
|
4146
|
+
RETURNS TEXT AS $$
|
|
4147
|
+
SELECT _func_compare($1, $2, $3, _type_func( 'w', $1, $2, $3), $4 );
|
|
4148
|
+
$$ LANGUAGE SQL;
|
|
4149
|
+
|
|
4150
|
+
-- is_window( schema, function, args[] )
|
|
4151
|
+
CREATE OR REPLACE FUNCTION is_window( NAME, NAME, NAME[] )
|
|
4152
|
+
RETURNS TEXT AS $$
|
|
4153
|
+
SELECT _func_compare(
|
|
4154
|
+
$1, $2, $3, _type_func('w', $1, $2, $3),
|
|
4155
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
4156
|
+
array_to_string($3, ', ') || ') should be a window function'
|
|
4157
|
+
);
|
|
4158
|
+
$$ LANGUAGE sql;
|
|
4159
|
+
|
|
4160
|
+
-- is_window( schema, function, description )
|
|
4161
|
+
CREATE OR REPLACE FUNCTION is_window ( NAME, NAME, TEXT )
|
|
4162
|
+
RETURNS TEXT AS $$
|
|
4163
|
+
SELECT _func_compare($1, $2, _type_func('w', $1, $2), $3 );
|
|
4164
|
+
$$ LANGUAGE SQL;
|
|
4165
|
+
|
|
4166
|
+
-- is_window( schema, function )
|
|
4167
|
+
CREATE OR REPLACE FUNCTION is_window( NAME, NAME )
|
|
4168
|
+
RETURNS TEXT AS $$
|
|
4169
|
+
SELECT _func_compare(
|
|
4170
|
+
$1, $2, _type_func('w', $1, $2),
|
|
4171
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should be a window function'
|
|
4172
|
+
);
|
|
4173
|
+
$$ LANGUAGE sql;
|
|
4174
|
+
|
|
4175
|
+
-- is_window( function, args[], description )
|
|
4176
|
+
CREATE OR REPLACE FUNCTION is_window ( NAME, NAME[], TEXT )
|
|
4177
|
+
RETURNS TEXT AS $$
|
|
4178
|
+
SELECT _func_compare(NULL, $1, $2, _type_func('w', $1, $2), $3 );
|
|
4179
|
+
$$ LANGUAGE SQL;
|
|
4180
|
+
|
|
4181
|
+
-- is_window( function, args[] )
|
|
4182
|
+
CREATE OR REPLACE FUNCTION is_window( NAME, NAME[] )
|
|
4183
|
+
RETURNS TEXT AS $$
|
|
4184
|
+
SELECT _func_compare(
|
|
4185
|
+
NULL, $1, $2, _type_func('w', $1, $2),
|
|
4186
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
4187
|
+
array_to_string($2, ', ') || ') should be a window function'
|
|
4188
|
+
);
|
|
4189
|
+
$$ LANGUAGE sql;
|
|
4190
|
+
|
|
4191
|
+
-- is_window( function, description )
|
|
4192
|
+
CREATE OR REPLACE FUNCTION is_window( NAME, TEXT )
|
|
4193
|
+
RETURNS TEXT AS $$
|
|
4194
|
+
SELECT _func_compare(NULL, $1, _type_func('w', $1), $2 );
|
|
4195
|
+
$$ LANGUAGE sql;
|
|
4196
|
+
|
|
4197
|
+
-- is_window( function )
|
|
4198
|
+
CREATE OR REPLACE FUNCTION is_window( NAME )
|
|
4199
|
+
RETURNS TEXT AS $$
|
|
4200
|
+
SELECT _func_compare(
|
|
4201
|
+
NULL, $1, _type_func('w', $1),
|
|
4202
|
+
'Function ' || quote_ident($1) || '() should be a window function'
|
|
4203
|
+
);
|
|
4204
|
+
$$ LANGUAGE sql;
|
|
4205
|
+
|
|
4206
|
+
-- isnt_window( schema, function, args[], description )
|
|
4207
|
+
CREATE OR REPLACE FUNCTION isnt_window ( NAME, NAME, NAME[], TEXT )
|
|
4208
|
+
RETURNS TEXT AS $$
|
|
4209
|
+
SELECT _func_compare($1, $2, $3, NOT _type_func('w', $1, $2, $3), $4 );
|
|
4210
|
+
$$ LANGUAGE SQL;
|
|
4211
|
+
|
|
4212
|
+
-- isnt_window( schema, function, args[] )
|
|
4213
|
+
CREATE OR REPLACE FUNCTION isnt_window( NAME, NAME, NAME[] )
|
|
4214
|
+
RETURNS TEXT AS $$
|
|
4215
|
+
SELECT _func_compare(
|
|
4216
|
+
$1, $2, $3, NOT _type_func('w', $1, $2, $3),
|
|
4217
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
4218
|
+
array_to_string($3, ', ') || ') should not be a window function'
|
|
4219
|
+
);
|
|
4220
|
+
$$ LANGUAGE sql;
|
|
4221
|
+
|
|
4222
|
+
-- isnt_window( schema, function, description )
|
|
4223
|
+
CREATE OR REPLACE FUNCTION isnt_window ( NAME, NAME, TEXT )
|
|
4224
|
+
RETURNS TEXT AS $$
|
|
4225
|
+
SELECT _func_compare($1, $2, NOT _type_func('w', $1, $2), $3 );
|
|
4226
|
+
$$ LANGUAGE SQL;
|
|
4227
|
+
|
|
4228
|
+
-- isnt_window( schema, function )
|
|
4229
|
+
CREATE OR REPLACE FUNCTION isnt_window( NAME, NAME )
|
|
4230
|
+
RETURNS TEXT AS $$
|
|
4231
|
+
SELECT _func_compare(
|
|
4232
|
+
$1, $2, NOT _type_func('w', $1, $2),
|
|
4233
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should not be a window function'
|
|
4234
|
+
);
|
|
4235
|
+
$$ LANGUAGE sql;
|
|
4236
|
+
|
|
4237
|
+
-- isnt_window( function, args[], description )
|
|
4238
|
+
CREATE OR REPLACE FUNCTION isnt_window ( NAME, NAME[], TEXT )
|
|
4239
|
+
RETURNS TEXT AS $$
|
|
4240
|
+
SELECT _func_compare(NULL, $1, $2, NOT _type_func('w', $1, $2), $3 );
|
|
4241
|
+
$$ LANGUAGE SQL;
|
|
4242
|
+
|
|
4243
|
+
-- isnt_window( function, args[] )
|
|
4244
|
+
CREATE OR REPLACE FUNCTION isnt_window( NAME, NAME[] )
|
|
4245
|
+
RETURNS TEXT AS $$
|
|
4246
|
+
SELECT _func_compare(
|
|
4247
|
+
NULL, $1, $2, NOT _type_func('w', $1, $2),
|
|
4248
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
4249
|
+
array_to_string($2, ', ') || ') should not be a window function'
|
|
4250
|
+
);
|
|
4251
|
+
$$ LANGUAGE sql;
|
|
4252
|
+
|
|
4253
|
+
-- isnt_window( function, description )
|
|
4254
|
+
CREATE OR REPLACE FUNCTION isnt_window( NAME, TEXT )
|
|
4255
|
+
RETURNS TEXT AS $$
|
|
4256
|
+
SELECT _func_compare(NULL, $1, NOT _type_func('w', $1), $2 );
|
|
4257
|
+
$$ LANGUAGE sql;
|
|
4258
|
+
|
|
4259
|
+
-- isnt_window( function )
|
|
4260
|
+
CREATE OR REPLACE FUNCTION isnt_window( NAME )
|
|
4261
|
+
RETURNS TEXT AS $$
|
|
4262
|
+
SELECT _func_compare(
|
|
4263
|
+
NULL, $1, NOT _type_func('w', $1),
|
|
4264
|
+
'Function ' || quote_ident($1) || '() should not be a window function'
|
|
4265
|
+
);
|
|
4266
|
+
$$ LANGUAGE sql;
|
|
4267
|
+
|
|
4268
|
+
-- is_procedure( schema, function, args[], description )
|
|
4269
|
+
CREATE OR REPLACE FUNCTION is_procedure ( NAME, NAME, NAME[], TEXT )
|
|
4270
|
+
RETURNS TEXT AS $$
|
|
4271
|
+
SELECT _func_compare($1, $2, $3, _type_func( 'p', $1, $2, $3), $4 );
|
|
4272
|
+
$$ LANGUAGE SQL;
|
|
4273
|
+
|
|
4274
|
+
-- is_procedure( schema, function, args[] )
|
|
4275
|
+
CREATE OR REPLACE FUNCTION is_procedure( NAME, NAME, NAME[] )
|
|
4276
|
+
RETURNS TEXT AS $$
|
|
4277
|
+
SELECT _func_compare(
|
|
4278
|
+
$1, $2, $3, _type_func('p', $1, $2, $3),
|
|
4279
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
4280
|
+
array_to_string($3, ', ') || ') should be a procedure'
|
|
4281
|
+
);
|
|
4282
|
+
$$ LANGUAGE sql;
|
|
4283
|
+
|
|
4284
|
+
-- is_procedure( schema, function, description )
|
|
4285
|
+
CREATE OR REPLACE FUNCTION is_procedure ( NAME, NAME, TEXT )
|
|
4286
|
+
RETURNS TEXT AS $$
|
|
4287
|
+
SELECT _func_compare($1, $2, _type_func('p', $1, $2), $3 );
|
|
4288
|
+
$$ LANGUAGE SQL;
|
|
4289
|
+
|
|
4290
|
+
-- is_procedure( schema, function )
|
|
4291
|
+
CREATE OR REPLACE FUNCTION is_procedure( NAME, NAME )
|
|
4292
|
+
RETURNS TEXT AS $$
|
|
4293
|
+
SELECT _func_compare(
|
|
4294
|
+
$1, $2, _type_func('p', $1, $2),
|
|
4295
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should be a procedure'
|
|
4296
|
+
);
|
|
4297
|
+
$$ LANGUAGE sql;
|
|
4298
|
+
|
|
4299
|
+
-- is_procedure( function, args[], description )
|
|
4300
|
+
CREATE OR REPLACE FUNCTION is_procedure ( NAME, NAME[], TEXT )
|
|
4301
|
+
RETURNS TEXT AS $$
|
|
4302
|
+
SELECT _func_compare(NULL, $1, $2, _type_func('p', $1, $2), $3 );
|
|
4303
|
+
$$ LANGUAGE SQL;
|
|
4304
|
+
|
|
4305
|
+
-- is_procedure( function, args[] )
|
|
4306
|
+
CREATE OR REPLACE FUNCTION is_procedure( NAME, NAME[] )
|
|
4307
|
+
RETURNS TEXT AS $$
|
|
4308
|
+
SELECT _func_compare(
|
|
4309
|
+
NULL, $1, $2, _type_func('p', $1, $2),
|
|
4310
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
4311
|
+
array_to_string($2, ', ') || ') should be a procedure'
|
|
4312
|
+
);
|
|
4313
|
+
$$ LANGUAGE sql;
|
|
4314
|
+
|
|
4315
|
+
-- is_procedure( function, description )
|
|
4316
|
+
CREATE OR REPLACE FUNCTION is_procedure( NAME, TEXT )
|
|
4317
|
+
RETURNS TEXT AS $$
|
|
4318
|
+
SELECT _func_compare(NULL, $1, _type_func('p', $1), $2 );
|
|
4319
|
+
$$ LANGUAGE sql;
|
|
4320
|
+
|
|
4321
|
+
-- is_procedure( function )
|
|
4322
|
+
CREATE OR REPLACE FUNCTION is_procedure( NAME )
|
|
4323
|
+
RETURNS TEXT AS $$
|
|
4324
|
+
SELECT _func_compare(
|
|
4325
|
+
NULL, $1, _type_func('p', $1),
|
|
4326
|
+
'Function ' || quote_ident($1) || '() should be a procedure'
|
|
4327
|
+
);
|
|
4328
|
+
$$ LANGUAGE sql;
|
|
4329
|
+
|
|
4330
|
+
-- isnt_procedure( schema, function, args[], description )
|
|
4331
|
+
CREATE OR REPLACE FUNCTION isnt_procedure ( NAME, NAME, NAME[], TEXT )
|
|
4332
|
+
RETURNS TEXT AS $$
|
|
4333
|
+
SELECT _func_compare($1, $2, $3, NOT _type_func('p', $1, $2, $3), $4 );
|
|
4334
|
+
$$ LANGUAGE SQL;
|
|
4335
|
+
|
|
4336
|
+
-- isnt_procedure( schema, function, args[] )
|
|
4337
|
+
CREATE OR REPLACE FUNCTION isnt_procedure( NAME, NAME, NAME[] )
|
|
4338
|
+
RETURNS TEXT AS $$
|
|
4339
|
+
SELECT _func_compare(
|
|
4340
|
+
$1, $2, $3, NOT _type_func('p', $1, $2, $3),
|
|
4341
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '(' ||
|
|
4342
|
+
array_to_string($3, ', ') || ') should not be a procedure'
|
|
4343
|
+
);
|
|
4344
|
+
$$ LANGUAGE sql;
|
|
4345
|
+
|
|
4346
|
+
-- isnt_procedure( schema, function, description )
|
|
4347
|
+
CREATE OR REPLACE FUNCTION isnt_procedure ( NAME, NAME, TEXT )
|
|
4348
|
+
RETURNS TEXT AS $$
|
|
4349
|
+
SELECT _func_compare($1, $2, NOT _type_func('p', $1, $2), $3 );
|
|
4350
|
+
$$ LANGUAGE SQL;
|
|
4351
|
+
|
|
4352
|
+
-- isnt_procedure( schema, function )
|
|
4353
|
+
CREATE OR REPLACE FUNCTION isnt_procedure( NAME, NAME )
|
|
4354
|
+
RETURNS TEXT AS $$
|
|
4355
|
+
SELECT _func_compare(
|
|
4356
|
+
$1, $2, NOT _type_func('p', $1, $2),
|
|
4357
|
+
'Function ' || quote_ident($1) || '.' || quote_ident($2) || '() should not be a procedure'
|
|
4358
|
+
);
|
|
4359
|
+
$$ LANGUAGE sql;
|
|
4360
|
+
|
|
4361
|
+
-- isnt_procedure( function, args[], description )
|
|
4362
|
+
CREATE OR REPLACE FUNCTION isnt_procedure ( NAME, NAME[], TEXT )
|
|
4363
|
+
RETURNS TEXT AS $$
|
|
4364
|
+
SELECT _func_compare(NULL, $1, $2, NOT _type_func('p', $1, $2), $3 );
|
|
4365
|
+
$$ LANGUAGE SQL;
|
|
4366
|
+
|
|
4367
|
+
-- isnt_procedure( function, args[] )
|
|
4368
|
+
CREATE OR REPLACE FUNCTION isnt_procedure( NAME, NAME[] )
|
|
4369
|
+
RETURNS TEXT AS $$
|
|
4370
|
+
SELECT _func_compare(
|
|
4371
|
+
NULL, $1, $2, NOT _type_func('p', $1, $2),
|
|
4372
|
+
'Function ' || quote_ident($1) || '(' ||
|
|
4373
|
+
array_to_string($2, ', ') || ') should not be a procedure'
|
|
4374
|
+
);
|
|
4375
|
+
$$ LANGUAGE sql;
|
|
4376
|
+
|
|
4377
|
+
-- isnt_procedure( function, description )
|
|
4378
|
+
CREATE OR REPLACE FUNCTION isnt_procedure( NAME, TEXT )
|
|
4379
|
+
RETURNS TEXT AS $$
|
|
4380
|
+
SELECT _func_compare(NULL, $1, NOT _type_func('p', $1), $2 );
|
|
4381
|
+
$$ LANGUAGE sql;
|
|
4382
|
+
|
|
4383
|
+
-- isnt_procedure( function )
|
|
4384
|
+
CREATE OR REPLACE FUNCTION isnt_procedure( NAME )
|
|
4385
|
+
RETURNS TEXT AS $$
|
|
4386
|
+
SELECT _func_compare(
|
|
4387
|
+
NULL, $1, NOT _type_func('p', $1),
|
|
4388
|
+
'Function ' || quote_ident($1) || '() should not be a procedure'
|
|
4389
|
+
);
|
|
4390
|
+
$$ LANGUAGE sql;
|