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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/THIRD_PARTY_NOTICES.md +20 -0
  4. package/extension-contract.json +31 -0
  5. package/package.json +49 -0
  6. package/runtime/share/licenses/pgtap/README.md +153 -0
  7. package/runtime/share/postgresql/extension/pgtap--0.90.0--0.91.0.sql +29 -0
  8. package/runtime/share/postgresql/extension/pgtap--0.91.0--0.92.0.sql +1650 -0
  9. package/runtime/share/postgresql/extension/pgtap--0.92.0--0.93.0.sql +679 -0
  10. package/runtime/share/postgresql/extension/pgtap--0.93.0--0.94.0.sql +193 -0
  11. package/runtime/share/postgresql/extension/pgtap--0.94.0--0.95.0.sql +639 -0
  12. package/runtime/share/postgresql/extension/pgtap--0.95.0--0.96.0.sql +415 -0
  13. package/runtime/share/postgresql/extension/pgtap--0.96.0--0.97.0.sql +375 -0
  14. package/runtime/share/postgresql/extension/pgtap--0.97.0--0.98.0.sql +441 -0
  15. package/runtime/share/postgresql/extension/pgtap--0.98.0--0.99.0.sql +209 -0
  16. package/runtime/share/postgresql/extension/pgtap--0.99.0--1.0.0.sql +737 -0
  17. package/runtime/share/postgresql/extension/pgtap--1.0.0--1.1.0.sql +186 -0
  18. package/runtime/share/postgresql/extension/pgtap--1.1.0--1.2.0.sql +889 -0
  19. package/runtime/share/postgresql/extension/pgtap--1.2.0--1.3.0.sql +495 -0
  20. package/runtime/share/postgresql/extension/pgtap--1.3.0--1.3.1.sql +229 -0
  21. package/runtime/share/postgresql/extension/pgtap--1.3.1--1.3.2.sql +34 -0
  22. package/runtime/share/postgresql/extension/pgtap--1.3.2--1.3.3.sql +42 -0
  23. package/runtime/share/postgresql/extension/pgtap--1.3.3--1.3.4.sql +89 -0
  24. package/runtime/share/postgresql/extension/pgtap--1.3.4--1.3.5.sql +1 -0
  25. package/runtime/share/postgresql/extension/pgtap--1.3.5.sql +11520 -0
  26. package/runtime/share/postgresql/extension/pgtap--unpackaged--0.91.0.sql +714 -0
  27. package/runtime/share/postgresql/extension/pgtap-core--1.3.5.sql +4390 -0
  28. package/runtime/share/postgresql/extension/pgtap-core.sql +4390 -0
  29. package/runtime/share/postgresql/extension/pgtap-schema--1.3.5.sql +6764 -0
  30. package/runtime/share/postgresql/extension/pgtap-schema.sql +6764 -0
  31. package/runtime/share/postgresql/extension/pgtap.control +6 -0
  32. package/runtime/share/postgresql/extension/pgtap.sql +11520 -0
  33. package/runtime/share/postgresql/extension/uninstall_pgtap.sql +1088 -0
  34. package/share/licenses/pgtap/README.md +153 -0
@@ -0,0 +1,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;