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