1--
2-- TYPE_SANITY
3-- Sanity checks for common errors in making type-related system tables:
4-- pg_type, pg_class, pg_attribute, pg_range.
5--
6-- None of the SELECTs here should ever find any matching entries,
7-- so the expected output is easy to maintain ;-).
8-- A test failure indicates someone messed up an entry in the system tables.
9--
10-- NB: we assume the oidjoins test will have caught any dangling links,
11-- that is OID or REGPROC fields that are not zero and do not match some
12-- row in the linked-to table.  However, if we want to enforce that a link
13-- field can't be 0, we have to check it here.
14
15-- **************** pg_type ****************
16
17-- Look for illegal values in pg_type fields.
18
19SELECT p1.oid, p1.typname
20FROM pg_type as p1
21WHERE p1.typnamespace = 0 OR
22    (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
23    (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
24    NOT p1.typisdefined OR
25    (p1.typalign not in ('c', 's', 'i', 'd')) OR
26    (p1.typstorage not in ('p', 'x', 'e', 'm'));
27
28-- Look for "pass by value" types that can't be passed by value.
29
30SELECT p1.oid, p1.typname
31FROM pg_type as p1
32WHERE p1.typbyval AND
33    (p1.typlen != 1 OR p1.typalign != 'c') AND
34    (p1.typlen != 2 OR p1.typalign != 's') AND
35    (p1.typlen != 4 OR p1.typalign != 'i') AND
36    (p1.typlen != 8 OR p1.typalign != 'd');
37
38-- Look for "toastable" types that aren't varlena.
39
40SELECT p1.oid, p1.typname
41FROM pg_type as p1
42WHERE p1.typstorage != 'p' AND
43    (p1.typbyval OR p1.typlen != -1);
44
45-- Look for complex types that do not have a typrelid entry,
46-- or basic types that do.
47
48SELECT p1.oid, p1.typname
49FROM pg_type as p1
50WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
51    (p1.typtype != 'c' AND p1.typrelid != 0);
52
53-- Look for types that should have an array type according to their typtype,
54-- but don't.  We exclude composites here because we have not bothered to
55-- make array types corresponding to the system catalogs' rowtypes.
56-- NOTE: as of v10, this check finds pg_node_tree, pg_ndistinct, smgr.
57
58SELECT p1.oid, p1.typname
59FROM pg_type as p1
60WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%'
61    AND NOT EXISTS
62    (SELECT 1 FROM pg_type as p2
63     WHERE p2.typname = ('_' || p1.typname)::name AND
64           p2.typelem = p1.oid and p1.typarray = p2.oid);
65
66-- Make sure typarray points to a varlena array type of our own base
67SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
68       p2.typelem, p2.typlen
69FROM   pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid)
70WHERE  p1.typarray <> 0 AND
71       (p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1);
72
73-- Look for range types that do not have a pg_range entry
74SELECT p1.oid, p1.typname
75FROM pg_type as p1
76WHERE p1.typtype = 'r' AND
77   NOT EXISTS(SELECT 1 FROM pg_range r WHERE rngtypid = p1.oid);
78
79-- Look for range types whose typalign isn't sufficient
80SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign
81FROM pg_type as p1
82     LEFT JOIN pg_range as r ON rngtypid = p1.oid
83     LEFT JOIN pg_type as p2 ON rngsubtype = p2.oid
84WHERE p1.typtype = 'r' AND
85    (p1.typalign != (CASE WHEN p2.typalign = 'd' THEN 'd'::"char"
86                          ELSE 'i'::"char" END)
87     OR p2.oid IS NULL);
88
89-- Text conversion routines must be provided.
90
91SELECT p1.oid, p1.typname
92FROM pg_type as p1
93WHERE (p1.typinput = 0 OR p1.typoutput = 0);
94
95-- Check for bogus typinput routines
96
97SELECT p1.oid, p1.typname, p2.oid, p2.proname
98FROM pg_type AS p1, pg_proc AS p2
99WHERE p1.typinput = p2.oid AND NOT
100    ((p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring'::regtype) OR
101     (p2.pronargs = 2 AND p2.proargtypes[0] = 'cstring'::regtype AND
102      p2.proargtypes[1] = 'oid'::regtype) OR
103     (p2.pronargs = 3 AND p2.proargtypes[0] = 'cstring'::regtype AND
104      p2.proargtypes[1] = 'oid'::regtype AND
105      p2.proargtypes[2] = 'int4'::regtype));
106
107-- Check for type of the variadic array parameter's elements.
108-- provariadic should be ANYOID if the type of the last element is ANYOID,
109-- ANYELEMENTOID if the type of the last element is ANYARRAYOID,
110-- ANYCOMPATIBLEOID if the type of the last element is ANYCOMPATIBLEARRAYOID,
111-- and otherwise the element type corresponding to the array type.
112
113SELECT oid::regprocedure, provariadic::regtype, proargtypes::regtype[]
114FROM pg_proc
115WHERE provariadic != 0
116AND case proargtypes[array_length(proargtypes, 1)-1]
117	WHEN '"any"'::regtype THEN '"any"'::regtype
118	WHEN 'anyarray'::regtype THEN 'anyelement'::regtype
119	WHEN 'anycompatiblearray'::regtype THEN 'anycompatible'::regtype
120	ELSE (SELECT t.oid
121		  FROM pg_type t
122		  WHERE t.typarray = proargtypes[array_length(proargtypes, 1)-1])
123	END  != provariadic;
124
125-- Check that all and only those functions with a variadic type have
126-- a variadic argument.
127SELECT oid::regprocedure, proargmodes, provariadic
128FROM pg_proc
129WHERE (proargmodes IS NOT NULL AND 'v' = any(proargmodes))
130    IS DISTINCT FROM
131    (provariadic != 0);
132
133-- As of 8.0, this check finds refcursor, which is borrowing
134-- other types' I/O routines
135SELECT p1.oid, p1.typname, p2.oid, p2.proname
136FROM pg_type AS p1, pg_proc AS p2
137WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
138    (p1.typelem != 0 AND p1.typlen < 0) AND NOT
139    (p2.prorettype = p1.oid AND NOT p2.proretset)
140ORDER BY 1;
141
142-- Varlena array types will point to array_in
143-- Exception as of 8.1: int2vector and oidvector have their own I/O routines
144SELECT p1.oid, p1.typname, p2.oid, p2.proname
145FROM pg_type AS p1, pg_proc AS p2
146WHERE p1.typinput = p2.oid AND
147    (p1.typelem != 0 AND p1.typlen < 0) AND NOT
148    (p2.oid = 'array_in'::regproc)
149ORDER BY 1;
150
151-- typinput routines should not be volatile
152SELECT p1.oid, p1.typname, p2.oid, p2.proname
153FROM pg_type AS p1, pg_proc AS p2
154WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
155
156-- Composites, domains, enums, ranges should all use the same input routines
157SELECT DISTINCT typtype, typinput
158FROM pg_type AS p1
159WHERE p1.typtype not in ('b', 'p')
160ORDER BY 1;
161
162-- Check for bogus typoutput routines
163
164-- As of 8.0, this check finds refcursor, which is borrowing
165-- other types' I/O routines
166SELECT p1.oid, p1.typname, p2.oid, p2.proname
167FROM pg_type AS p1, pg_proc AS p2
168WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
169    (p2.pronargs = 1 AND
170     (p2.proargtypes[0] = p1.oid OR
171      (p2.oid = 'array_out'::regproc AND
172       p1.typelem != 0 AND p1.typlen = -1)))
173ORDER BY 1;
174
175SELECT p1.oid, p1.typname, p2.oid, p2.proname
176FROM pg_type AS p1, pg_proc AS p2
177WHERE p1.typoutput = p2.oid AND NOT
178    (p2.prorettype = 'cstring'::regtype AND NOT p2.proretset);
179
180-- typoutput routines should not be volatile
181SELECT p1.oid, p1.typname, p2.oid, p2.proname
182FROM pg_type AS p1, pg_proc AS p2
183WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
184
185-- Composites, enums, ranges should all use the same output routines
186SELECT DISTINCT typtype, typoutput
187FROM pg_type AS p1
188WHERE p1.typtype not in ('b', 'd', 'p')
189ORDER BY 1;
190
191-- Domains should have same typoutput as their base types
192SELECT p1.oid, p1.typname, p2.oid, p2.typname
193FROM pg_type AS p1 LEFT JOIN pg_type AS p2 ON p1.typbasetype = p2.oid
194WHERE p1.typtype = 'd' AND p1.typoutput IS DISTINCT FROM p2.typoutput;
195
196-- Check for bogus typreceive routines
197
198SELECT p1.oid, p1.typname, p2.oid, p2.proname
199FROM pg_type AS p1, pg_proc AS p2
200WHERE p1.typreceive = p2.oid AND NOT
201    ((p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype) OR
202     (p2.pronargs = 2 AND p2.proargtypes[0] = 'internal'::regtype AND
203      p2.proargtypes[1] = 'oid'::regtype) OR
204     (p2.pronargs = 3 AND p2.proargtypes[0] = 'internal'::regtype AND
205      p2.proargtypes[1] = 'oid'::regtype AND
206      p2.proargtypes[2] = 'int4'::regtype));
207
208-- As of 7.4, this check finds refcursor, which is borrowing
209-- other types' I/O routines
210SELECT p1.oid, p1.typname, p2.oid, p2.proname
211FROM pg_type AS p1, pg_proc AS p2
212WHERE p1.typreceive = p2.oid AND p1.typtype in ('b', 'p') AND NOT
213    (p1.typelem != 0 AND p1.typlen < 0) AND NOT
214    (p2.prorettype = p1.oid AND NOT p2.proretset)
215ORDER BY 1;
216
217-- Varlena array types will point to array_recv
218-- Exception as of 8.1: int2vector and oidvector have their own I/O routines
219SELECT p1.oid, p1.typname, p2.oid, p2.proname
220FROM pg_type AS p1, pg_proc AS p2
221WHERE p1.typreceive = p2.oid AND
222    (p1.typelem != 0 AND p1.typlen < 0) AND NOT
223    (p2.oid = 'array_recv'::regproc)
224ORDER BY 1;
225
226-- Suspicious if typreceive doesn't take same number of args as typinput
227SELECT p1.oid, p1.typname, p2.oid, p2.proname, p3.oid, p3.proname
228FROM pg_type AS p1, pg_proc AS p2, pg_proc AS p3
229WHERE p1.typinput = p2.oid AND p1.typreceive = p3.oid AND
230    p2.pronargs != p3.pronargs;
231
232-- typreceive routines should not be volatile
233SELECT p1.oid, p1.typname, p2.oid, p2.proname
234FROM pg_type AS p1, pg_proc AS p2
235WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
236
237-- Composites, domains, enums, ranges should all use the same receive routines
238SELECT DISTINCT typtype, typreceive
239FROM pg_type AS p1
240WHERE p1.typtype not in ('b', 'p')
241ORDER BY 1;
242
243-- Check for bogus typsend routines
244
245-- As of 7.4, this check finds refcursor, which is borrowing
246-- other types' I/O routines
247SELECT p1.oid, p1.typname, p2.oid, p2.proname
248FROM pg_type AS p1, pg_proc AS p2
249WHERE p1.typsend = p2.oid AND p1.typtype in ('b', 'p') AND NOT
250    (p2.pronargs = 1 AND
251     (p2.proargtypes[0] = p1.oid OR
252      (p2.oid = 'array_send'::regproc AND
253       p1.typelem != 0 AND p1.typlen = -1)))
254ORDER BY 1;
255
256SELECT p1.oid, p1.typname, p2.oid, p2.proname
257FROM pg_type AS p1, pg_proc AS p2
258WHERE p1.typsend = p2.oid AND NOT
259    (p2.prorettype = 'bytea'::regtype AND NOT p2.proretset);
260
261-- typsend routines should not be volatile
262SELECT p1.oid, p1.typname, p2.oid, p2.proname
263FROM pg_type AS p1, pg_proc AS p2
264WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
265
266-- Composites, enums, ranges should all use the same send routines
267SELECT DISTINCT typtype, typsend
268FROM pg_type AS p1
269WHERE p1.typtype not in ('b', 'd', 'p')
270ORDER BY 1;
271
272-- Domains should have same typsend as their base types
273SELECT p1.oid, p1.typname, p2.oid, p2.typname
274FROM pg_type AS p1 LEFT JOIN pg_type AS p2 ON p1.typbasetype = p2.oid
275WHERE p1.typtype = 'd' AND p1.typsend IS DISTINCT FROM p2.typsend;
276
277-- Check for bogus typmodin routines
278
279SELECT p1.oid, p1.typname, p2.oid, p2.proname
280FROM pg_type AS p1, pg_proc AS p2
281WHERE p1.typmodin = p2.oid AND NOT
282    (p2.pronargs = 1 AND
283     p2.proargtypes[0] = 'cstring[]'::regtype AND
284     p2.prorettype = 'int4'::regtype AND NOT p2.proretset);
285
286-- typmodin routines should not be volatile
287SELECT p1.oid, p1.typname, p2.oid, p2.proname
288FROM pg_type AS p1, pg_proc AS p2
289WHERE p1.typmodin = p2.oid AND p2.provolatile NOT IN ('i', 's');
290
291-- Check for bogus typmodout routines
292
293SELECT p1.oid, p1.typname, p2.oid, p2.proname
294FROM pg_type AS p1, pg_proc AS p2
295WHERE p1.typmodout = p2.oid AND NOT
296    (p2.pronargs = 1 AND
297     p2.proargtypes[0] = 'int4'::regtype AND
298     p2.prorettype = 'cstring'::regtype AND NOT p2.proretset);
299
300-- typmodout routines should not be volatile
301SELECT p1.oid, p1.typname, p2.oid, p2.proname
302FROM pg_type AS p1, pg_proc AS p2
303WHERE p1.typmodout = p2.oid AND p2.provolatile NOT IN ('i', 's');
304
305-- Array types should have same typmodin/out as their element types
306
307SELECT p1.oid, p1.typname, p2.oid, p2.typname
308FROM pg_type AS p1, pg_type AS p2
309WHERE p1.typelem = p2.oid AND NOT
310    (p1.typmodin = p2.typmodin AND p1.typmodout = p2.typmodout);
311
312-- Array types should have same typdelim as their element types
313
314SELECT p1.oid, p1.typname, p2.oid, p2.typname
315FROM pg_type AS p1, pg_type AS p2
316WHERE p1.typarray = p2.oid AND NOT (p1.typdelim = p2.typdelim);
317
318-- Look for array types whose typalign isn't sufficient
319
320SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign
321FROM pg_type AS p1, pg_type AS p2
322WHERE p1.typarray = p2.oid AND
323    p2.typalign != (CASE WHEN p1.typalign = 'd' THEN 'd'::"char"
324                         ELSE 'i'::"char" END);
325
326-- Check for bogus typanalyze routines
327
328SELECT p1.oid, p1.typname, p2.oid, p2.proname
329FROM pg_type AS p1, pg_proc AS p2
330WHERE p1.typanalyze = p2.oid AND NOT
331    (p2.pronargs = 1 AND
332     p2.proargtypes[0] = 'internal'::regtype AND
333     p2.prorettype = 'bool'::regtype AND NOT p2.proretset);
334
335-- there does not seem to be a reason to care about volatility of typanalyze
336
337-- domains inherit their base type's typanalyze
338
339SELECT d.oid, d.typname, d.typanalyze, t.oid, t.typname, t.typanalyze
340FROM pg_type d JOIN pg_type t ON d.typbasetype = t.oid
341WHERE d.typanalyze != t.typanalyze;
342
343-- range_typanalyze should be used for all and only range types
344-- (but exclude domains, which we checked above)
345
346SELECT t.oid, t.typname, t.typanalyze
347FROM pg_type t LEFT JOIN pg_range r on t.oid = r.rngtypid
348WHERE t.typbasetype = 0 AND
349    (t.typanalyze = 'range_typanalyze'::regproc) != (r.rngtypid IS NOT NULL);
350
351-- array_typanalyze should be used for all and only array types
352-- (but exclude domains, which we checked above)
353-- As of 9.2 this finds int2vector and oidvector, which are weird anyway
354
355SELECT t.oid, t.typname, t.typanalyze
356FROM pg_type t
357WHERE t.typbasetype = 0 AND
358    (t.typanalyze = 'array_typanalyze'::regproc) !=
359    (typelem != 0 AND typlen < 0)
360ORDER BY 1;
361
362-- **************** pg_class ****************
363
364-- Look for illegal values in pg_class fields
365
366SELECT p1.oid, p1.relname
367FROM pg_class as p1
368WHERE relkind NOT IN ('r', 'i', 'S', 't', 'v', 'm', 'c', 'f', 'p') OR
369    relpersistence NOT IN ('p', 'u', 't') OR
370    relreplident NOT IN ('d', 'n', 'f', 'i');
371
372-- All tables and indexes should have an access method.
373SELECT p1.oid, p1.relname
374FROM pg_class as p1
375WHERE p1.relkind NOT IN ('S', 'v', 'f', 'c') and
376    p1.relam = 0;
377
378-- Conversely, sequences, views, types shouldn't have them
379SELECT p1.oid, p1.relname
380FROM pg_class as p1
381WHERE p1.relkind IN ('S', 'v', 'f', 'c') and
382    p1.relam != 0;
383
384-- Indexes should have AMs of type 'i'
385SELECT pc.oid, pc.relname, pa.amname, pa.amtype
386FROM pg_class as pc JOIN pg_am AS pa ON (pc.relam = pa.oid)
387WHERE pc.relkind IN ('i') and
388    pa.amtype != 'i';
389
390-- Tables, matviews etc should have AMs of type 't'
391SELECT pc.oid, pc.relname, pa.amname, pa.amtype
392FROM pg_class as pc JOIN pg_am AS pa ON (pc.relam = pa.oid)
393WHERE pc.relkind IN ('r', 't', 'm') and
394    pa.amtype != 't';
395
396-- **************** pg_attribute ****************
397
398-- Look for illegal values in pg_attribute fields
399
400SELECT p1.attrelid, p1.attname
401FROM pg_attribute as p1
402WHERE p1.attrelid = 0 OR p1.atttypid = 0 OR p1.attnum = 0 OR
403    p1.attcacheoff != -1 OR p1.attinhcount < 0 OR
404    (p1.attinhcount = 0 AND NOT p1.attislocal);
405
406-- Cross-check attnum against parent relation
407
408SELECT p1.attrelid, p1.attname, p2.oid, p2.relname
409FROM pg_attribute AS p1, pg_class AS p2
410WHERE p1.attrelid = p2.oid AND p1.attnum > p2.relnatts;
411
412-- Detect missing pg_attribute entries: should have as many non-system
413-- attributes as parent relation expects
414
415SELECT p1.oid, p1.relname
416FROM pg_class AS p1
417WHERE p1.relnatts != (SELECT count(*) FROM pg_attribute AS p2
418                      WHERE p2.attrelid = p1.oid AND p2.attnum > 0);
419
420-- Cross-check against pg_type entry
421-- NOTE: we allow attstorage to be 'plain' even when typstorage is not;
422-- this is mainly for toast tables.
423
424SELECT p1.attrelid, p1.attname, p2.oid, p2.typname
425FROM pg_attribute AS p1, pg_type AS p2
426WHERE p1.atttypid = p2.oid AND
427    (p1.attlen != p2.typlen OR
428     p1.attalign != p2.typalign OR
429     p1.attbyval != p2.typbyval OR
430     (p1.attstorage != p2.typstorage AND p1.attstorage != 'p'));
431
432-- **************** pg_range ****************
433
434-- Look for illegal values in pg_range fields.
435
436SELECT p1.rngtypid, p1.rngsubtype
437FROM pg_range as p1
438WHERE p1.rngtypid = 0 OR p1.rngsubtype = 0 OR p1.rngsubopc = 0;
439
440-- rngcollation should be specified iff subtype is collatable
441
442SELECT p1.rngtypid, p1.rngsubtype, p1.rngcollation, t.typcollation
443FROM pg_range p1 JOIN pg_type t ON t.oid = p1.rngsubtype
444WHERE (rngcollation = 0) != (typcollation = 0);
445
446-- opclass had better be a btree opclass accepting the subtype.
447-- We must allow anyarray matches, cf opr_sanity's binary_coercible()
448
449SELECT p1.rngtypid, p1.rngsubtype, o.opcmethod, o.opcname
450FROM pg_range p1 JOIN pg_opclass o ON o.oid = p1.rngsubopc
451WHERE o.opcmethod != 403 OR
452    ((o.opcintype != p1.rngsubtype) AND NOT
453     (o.opcintype = 'pg_catalog.anyarray'::regtype AND
454      EXISTS(select 1 from pg_catalog.pg_type where
455             oid = p1.rngsubtype and typelem != 0 and typlen = -1)));
456
457-- canonical function, if any, had better match the range type
458
459SELECT p1.rngtypid, p1.rngsubtype, p.proname
460FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngcanonical
461WHERE pronargs != 1 OR proargtypes[0] != rngtypid OR prorettype != rngtypid;
462
463-- subdiff function, if any, had better match the subtype
464
465SELECT p1.rngtypid, p1.rngsubtype, p.proname
466FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
467WHERE pronargs != 2
468    OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
469    OR prorettype != 'pg_catalog.float8'::regtype;
470