Refactor the handling of the various DropStmt variants so that when multiple
[PostgreSQL.git] / src / backend / commands / typecmds.c
blobecd54be078e2e09d18410e41ed91562dc4005bf1
1 /*-------------------------------------------------------------------------
3 * typecmds.c
4 * Routines for SQL commands that manipulate types (and domains).
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 * DESCRIPTION
14 * The "DefineFoo" routines take the parse tree and pick out the
15 * appropriate arguments/flags, passing the results to the
16 * corresponding "FooDefine" routines (in src/catalog) that do
17 * the actual catalog-munging. These routines also verify permission
18 * of the user to execute the command.
20 * NOTES
21 * These things must be defined and committed in the following order:
22 * "create function":
23 * input/output, recv/send functions
24 * "create type":
25 * type
26 * "create operator":
27 * operators
30 *-------------------------------------------------------------------------
32 #include "postgres.h"
34 #include "access/genam.h"
35 #include "access/heapam.h"
36 #include "access/xact.h"
37 #include "catalog/catalog.h"
38 #include "catalog/dependency.h"
39 #include "catalog/heap.h"
40 #include "catalog/indexing.h"
41 #include "catalog/pg_constraint.h"
42 #include "catalog/pg_depend.h"
43 #include "catalog/pg_enum.h"
44 #include "catalog/pg_namespace.h"
45 #include "catalog/pg_type.h"
46 #include "catalog/pg_type_fn.h"
47 #include "commands/defrem.h"
48 #include "commands/tablecmds.h"
49 #include "commands/typecmds.h"
50 #include "executor/executor.h"
51 #include "miscadmin.h"
52 #include "nodes/makefuncs.h"
53 #include "optimizer/planmain.h"
54 #include "optimizer/var.h"
55 #include "parser/parse_coerce.h"
56 #include "parser/parse_expr.h"
57 #include "parser/parse_func.h"
58 #include "parser/parse_type.h"
59 #include "utils/acl.h"
60 #include "utils/builtins.h"
61 #include "utils/fmgroids.h"
62 #include "utils/lsyscache.h"
63 #include "utils/memutils.h"
64 #include "utils/syscache.h"
65 #include "utils/tqual.h"
68 /* result structure for get_rels_with_domain() */
69 typedef struct
71 Relation rel; /* opened and locked relation */
72 int natts; /* number of attributes of interest */
73 int *atts; /* attribute numbers */
74 /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
75 } RelToCheck;
78 static Oid findTypeInputFunction(List *procname, Oid typeOid);
79 static Oid findTypeOutputFunction(List *procname, Oid typeOid);
80 static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
81 static Oid findTypeSendFunction(List *procname, Oid typeOid);
82 static Oid findTypeTypmodinFunction(List *procname);
83 static Oid findTypeTypmodoutFunction(List *procname);
84 static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
85 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
86 static void checkDomainOwner(HeapTuple tup, TypeName *typename);
87 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
88 Oid baseTypeOid,
89 int typMod, Constraint *constr,
90 char *domainName);
94 * DefineType
95 * Registers a new type.
97 void
98 DefineType(List *names, List *parameters)
100 char *typeName;
101 Oid typeNamespace;
102 AclResult aclresult;
103 int16 internalLength = -1; /* default: variable-length */
104 Oid elemType = InvalidOid;
105 List *inputName = NIL;
106 List *outputName = NIL;
107 List *receiveName = NIL;
108 List *sendName = NIL;
109 List *typmodinName = NIL;
110 List *typmodoutName = NIL;
111 List *analyzeName = NIL;
112 char *defaultValue = NULL;
113 bool byValue = false;
114 char delimiter = DEFAULT_TYPDELIM;
115 char alignment = 'i'; /* default alignment */
116 char storage = 'p'; /* default TOAST storage method */
117 Oid inputOid;
118 Oid outputOid;
119 Oid receiveOid = InvalidOid;
120 Oid sendOid = InvalidOid;
121 Oid typmodinOid = InvalidOid;
122 Oid typmodoutOid = InvalidOid;
123 Oid analyzeOid = InvalidOid;
124 char *array_type;
125 Oid array_oid;
126 ListCell *pl;
127 Oid typoid;
128 Oid resulttype;
129 Relation pg_type;
131 /* Convert list of names to a name and namespace */
132 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
134 /* Check we have creation rights in target namespace */
135 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
136 if (aclresult != ACLCHECK_OK)
137 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
138 get_namespace_name(typeNamespace));
141 * Look to see if type already exists (presumably as a shell; if not,
142 * TypeCreate will complain).
144 typoid = GetSysCacheOid(TYPENAMENSP,
145 CStringGetDatum(typeName),
146 ObjectIdGetDatum(typeNamespace),
147 0, 0);
150 * If it's not a shell, see if it's an autogenerated array type, and if so
151 * rename it out of the way.
153 if (OidIsValid(typoid) && get_typisdefined(typoid))
155 if (moveArrayTypeName(typoid, typeName, typeNamespace))
156 typoid = InvalidOid;
160 * If it doesn't exist, create it as a shell, so that the OID is known for
161 * use in the I/O function definitions.
163 if (!OidIsValid(typoid))
165 typoid = TypeShellMake(typeName, typeNamespace);
166 /* Make new shell type visible for modification below */
167 CommandCounterIncrement();
170 * If the command was a parameterless CREATE TYPE, we're done ---
171 * creating the shell type was all we're supposed to do.
173 if (parameters == NIL)
174 return;
176 else
178 /* Complain if dummy CREATE TYPE and entry already exists */
179 if (parameters == NIL)
180 ereport(ERROR,
181 (errcode(ERRCODE_DUPLICATE_OBJECT),
182 errmsg("type \"%s\" already exists", typeName)));
185 foreach(pl, parameters)
187 DefElem *defel = (DefElem *) lfirst(pl);
189 if (pg_strcasecmp(defel->defname, "internallength") == 0)
190 internalLength = defGetTypeLength(defel);
191 else if (pg_strcasecmp(defel->defname, "externallength") == 0)
192 ; /* ignored -- remove after 7.3 */
193 else if (pg_strcasecmp(defel->defname, "input") == 0)
194 inputName = defGetQualifiedName(defel);
195 else if (pg_strcasecmp(defel->defname, "output") == 0)
196 outputName = defGetQualifiedName(defel);
197 else if (pg_strcasecmp(defel->defname, "receive") == 0)
198 receiveName = defGetQualifiedName(defel);
199 else if (pg_strcasecmp(defel->defname, "send") == 0)
200 sendName = defGetQualifiedName(defel);
201 else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
202 typmodinName = defGetQualifiedName(defel);
203 else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
204 typmodoutName = defGetQualifiedName(defel);
205 else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
206 pg_strcasecmp(defel->defname, "analyse") == 0)
207 analyzeName = defGetQualifiedName(defel);
208 else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
210 char *p = defGetString(defel);
212 delimiter = p[0];
214 else if (pg_strcasecmp(defel->defname, "element") == 0)
216 elemType = typenameTypeId(NULL, defGetTypeName(defel), NULL);
217 /* disallow arrays of pseudotypes */
218 if (get_typtype(elemType) == TYPTYPE_PSEUDO)
219 ereport(ERROR,
220 (errcode(ERRCODE_DATATYPE_MISMATCH),
221 errmsg("array element type cannot be %s",
222 format_type_be(elemType))));
224 else if (pg_strcasecmp(defel->defname, "default") == 0)
225 defaultValue = defGetString(defel);
226 else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
227 byValue = defGetBoolean(defel);
228 else if (pg_strcasecmp(defel->defname, "alignment") == 0)
230 char *a = defGetString(defel);
233 * Note: if argument was an unquoted identifier, parser will have
234 * applied translations to it, so be prepared to recognize
235 * translated type names as well as the nominal form.
237 if (pg_strcasecmp(a, "double") == 0 ||
238 pg_strcasecmp(a, "float8") == 0 ||
239 pg_strcasecmp(a, "pg_catalog.float8") == 0)
240 alignment = 'd';
241 else if (pg_strcasecmp(a, "int4") == 0 ||
242 pg_strcasecmp(a, "pg_catalog.int4") == 0)
243 alignment = 'i';
244 else if (pg_strcasecmp(a, "int2") == 0 ||
245 pg_strcasecmp(a, "pg_catalog.int2") == 0)
246 alignment = 's';
247 else if (pg_strcasecmp(a, "char") == 0 ||
248 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
249 alignment = 'c';
250 else
251 ereport(ERROR,
252 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
253 errmsg("alignment \"%s\" not recognized", a)));
255 else if (pg_strcasecmp(defel->defname, "storage") == 0)
257 char *a = defGetString(defel);
259 if (pg_strcasecmp(a, "plain") == 0)
260 storage = 'p';
261 else if (pg_strcasecmp(a, "external") == 0)
262 storage = 'e';
263 else if (pg_strcasecmp(a, "extended") == 0)
264 storage = 'x';
265 else if (pg_strcasecmp(a, "main") == 0)
266 storage = 'm';
267 else
268 ereport(ERROR,
269 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
270 errmsg("storage \"%s\" not recognized", a)));
272 else
273 ereport(WARNING,
274 (errcode(ERRCODE_SYNTAX_ERROR),
275 errmsg("type attribute \"%s\" not recognized",
276 defel->defname)));
280 * make sure we have our required definitions
282 if (inputName == NIL)
283 ereport(ERROR,
284 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
285 errmsg("type input function must be specified")));
286 if (outputName == NIL)
287 ereport(ERROR,
288 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
289 errmsg("type output function must be specified")));
291 if (typmodinName == NIL && typmodoutName != NIL)
292 ereport(ERROR,
293 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
294 errmsg("type modifier output function is useless without a type modifier input function")));
297 * Convert I/O proc names to OIDs
299 inputOid = findTypeInputFunction(inputName, typoid);
300 outputOid = findTypeOutputFunction(outputName, typoid);
301 if (receiveName)
302 receiveOid = findTypeReceiveFunction(receiveName, typoid);
303 if (sendName)
304 sendOid = findTypeSendFunction(sendName, typoid);
307 * Verify that I/O procs return the expected thing. If we see OPAQUE,
308 * complain and change it to the correct type-safe choice.
310 resulttype = get_func_rettype(inputOid);
311 if (resulttype != typoid)
313 if (resulttype == OPAQUEOID)
315 /* backwards-compatibility hack */
316 ereport(WARNING,
317 (errmsg("changing return type of function %s from \"opaque\" to %s",
318 NameListToString(inputName), typeName)));
319 SetFunctionReturnType(inputOid, typoid);
321 else
322 ereport(ERROR,
323 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
324 errmsg("type input function %s must return type %s",
325 NameListToString(inputName), typeName)));
327 resulttype = get_func_rettype(outputOid);
328 if (resulttype != CSTRINGOID)
330 if (resulttype == OPAQUEOID)
332 /* backwards-compatibility hack */
333 ereport(WARNING,
334 (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
335 NameListToString(outputName))));
336 SetFunctionReturnType(outputOid, CSTRINGOID);
338 else
339 ereport(ERROR,
340 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
341 errmsg("type output function %s must return type \"cstring\"",
342 NameListToString(outputName))));
344 if (receiveOid)
346 resulttype = get_func_rettype(receiveOid);
347 if (resulttype != typoid)
348 ereport(ERROR,
349 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
350 errmsg("type receive function %s must return type %s",
351 NameListToString(receiveName), typeName)));
353 if (sendOid)
355 resulttype = get_func_rettype(sendOid);
356 if (resulttype != BYTEAOID)
357 ereport(ERROR,
358 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
359 errmsg("type send function %s must return type \"bytea\"",
360 NameListToString(sendName))));
364 * Convert typmodin/out function proc names to OIDs.
366 if (typmodinName)
367 typmodinOid = findTypeTypmodinFunction(typmodinName);
368 if (typmodoutName)
369 typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
372 * Convert analysis function proc name to an OID. If no analysis function
373 * is specified, we'll use zero to select the built-in default algorithm.
375 if (analyzeName)
376 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
379 * Check permissions on functions. We choose to require the creator/owner
380 * of a type to also own the underlying functions. Since creating a type
381 * is tantamount to granting public execute access on the functions, the
382 * minimum sane check would be for execute-with-grant-option. But we
383 * don't have a way to make the type go away if the grant option is
384 * revoked, so ownership seems better.
386 if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
387 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
388 NameListToString(inputName));
389 if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
390 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
391 NameListToString(outputName));
392 if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
393 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
394 NameListToString(receiveName));
395 if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
396 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
397 NameListToString(sendName));
398 if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
399 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
400 NameListToString(typmodinName));
401 if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
402 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
403 NameListToString(typmodoutName));
404 if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
405 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
406 NameListToString(analyzeName));
408 /* Preassign array type OID so we can insert it in pg_type.typarray */
409 pg_type = heap_open(TypeRelationId, AccessShareLock);
410 array_oid = GetNewOid(pg_type);
411 heap_close(pg_type, AccessShareLock);
414 * now have TypeCreate do all the real work.
416 typoid =
417 TypeCreate(InvalidOid, /* no predetermined type OID */
418 typeName, /* type name */
419 typeNamespace, /* namespace */
420 InvalidOid, /* relation oid (n/a here) */
421 0, /* relation kind (ditto) */
422 internalLength, /* internal size */
423 TYPTYPE_BASE, /* type-type (base type) */
424 delimiter, /* array element delimiter */
425 inputOid, /* input procedure */
426 outputOid, /* output procedure */
427 receiveOid, /* receive procedure */
428 sendOid, /* send procedure */
429 typmodinOid, /* typmodin procedure */
430 typmodoutOid, /* typmodout procedure */
431 analyzeOid, /* analyze procedure */
432 elemType, /* element type ID */
433 false, /* this is not an array type */
434 array_oid, /* array type we are about to create */
435 InvalidOid, /* base type ID (only for domains) */
436 defaultValue, /* default type value */
437 NULL, /* no binary form available */
438 byValue, /* passed by value */
439 alignment, /* required alignment */
440 storage, /* TOAST strategy */
441 -1, /* typMod (Domains only) */
442 0, /* Array Dimensions of typbasetype */
443 false); /* Type NOT NULL */
446 * Create the array type that goes with it.
448 array_type = makeArrayTypeName(typeName, typeNamespace);
450 /* alignment must be 'i' or 'd' for arrays */
451 alignment = (alignment == 'd') ? 'd' : 'i';
453 TypeCreate(array_oid, /* force assignment of this type OID */
454 array_type, /* type name */
455 typeNamespace, /* namespace */
456 InvalidOid, /* relation oid (n/a here) */
457 0, /* relation kind (ditto) */
458 -1, /* internal size (always varlena) */
459 TYPTYPE_BASE, /* type-type (base type) */
460 DEFAULT_TYPDELIM, /* array element delimiter */
461 F_ARRAY_IN, /* input procedure */
462 F_ARRAY_OUT, /* output procedure */
463 F_ARRAY_RECV, /* receive procedure */
464 F_ARRAY_SEND, /* send procedure */
465 typmodinOid, /* typmodin procedure */
466 typmodoutOid, /* typmodout procedure */
467 InvalidOid, /* analyze procedure - default */
468 typoid, /* element type ID */
469 true, /* yes this is an array type */
470 InvalidOid, /* no further array type */
471 InvalidOid, /* base type ID */
472 NULL, /* never a default type value */
473 NULL, /* binary default isn't sent either */
474 false, /* never passed by value */
475 alignment, /* see above */
476 'x', /* ARRAY is always toastable */
477 -1, /* typMod (Domains only) */
478 0, /* Array dimensions of typbasetype */
479 false); /* Type NOT NULL */
481 pfree(array_type);
486 * RemoveTypes
487 * Implements DROP TYPE and DROP DOMAIN
489 * Note: if DOMAIN is specified, we enforce that each type is a domain, but
490 * we don't enforce the converse for DROP TYPE
492 void
493 RemoveTypes(DropStmt *drop)
495 ObjectAddresses *objects;
496 ListCell *cell;
499 * First we identify all the types, then we delete them in a single
500 * performMultipleDeletions() call. This is to avoid unwanted
501 * DROP RESTRICT errors if one of the types depends on another.
503 objects = new_object_addresses();
505 foreach(cell, drop->objects)
507 List *names = (List *) lfirst(cell);
508 TypeName *typename;
509 Oid typeoid;
510 HeapTuple tup;
511 ObjectAddress object;
512 Form_pg_type typ;
514 /* Make a TypeName so we can use standard type lookup machinery */
515 typename = makeTypeNameFromNameList(names);
517 /* Use LookupTypeName here so that shell types can be removed. */
518 tup = LookupTypeName(NULL, typename, NULL);
519 if (tup == NULL)
521 if (!drop->missing_ok)
523 ereport(ERROR,
524 (errcode(ERRCODE_UNDEFINED_OBJECT),
525 errmsg("type \"%s\" does not exist",
526 TypeNameToString(typename))));
528 else
530 ereport(NOTICE,
531 (errmsg("type \"%s\" does not exist, skipping",
532 TypeNameToString(typename))));
534 continue;
537 typeoid = typeTypeId(tup);
538 typ = (Form_pg_type) GETSTRUCT(tup);
540 /* Permission check: must own type or its namespace */
541 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
542 !pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
543 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
544 TypeNameToString(typename));
546 if (drop->removeType == OBJECT_DOMAIN)
548 /* Check that this is actually a domain */
549 if (typ->typtype != TYPTYPE_DOMAIN)
550 ereport(ERROR,
551 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
552 errmsg("\"%s\" is not a domain",
553 TypeNameToString(typename))));
557 * Note: we need no special check for array types here, as the normal
558 * treatment of internal dependencies handles it just fine
561 object.classId = TypeRelationId;
562 object.objectId = typeoid;
563 object.objectSubId = 0;
565 add_exact_object_address(&object, objects);
567 ReleaseSysCache(tup);
570 performMultipleDeletions(objects, drop->behavior);
572 free_object_addresses(objects);
577 * Guts of type deletion.
579 void
580 RemoveTypeById(Oid typeOid)
582 Relation relation;
583 HeapTuple tup;
585 relation = heap_open(TypeRelationId, RowExclusiveLock);
587 tup = SearchSysCache(TYPEOID,
588 ObjectIdGetDatum(typeOid),
589 0, 0, 0);
590 if (!HeapTupleIsValid(tup))
591 elog(ERROR, "cache lookup failed for type %u", typeOid);
593 simple_heap_delete(relation, &tup->t_self);
596 * If it is an enum, delete the pg_enum entries too; we don't bother with
597 * making dependency entries for those, so it has to be done "by hand"
598 * here.
600 if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
601 EnumValuesDelete(typeOid);
603 ReleaseSysCache(tup);
605 heap_close(relation, RowExclusiveLock);
610 * DefineDomain
611 * Registers a new domain.
613 void
614 DefineDomain(CreateDomainStmt *stmt)
616 char *domainName;
617 Oid domainNamespace;
618 AclResult aclresult;
619 int16 internalLength;
620 Oid inputProcedure;
621 Oid outputProcedure;
622 Oid receiveProcedure;
623 Oid sendProcedure;
624 Oid analyzeProcedure;
625 bool byValue;
626 Oid typelem;
627 char delimiter;
628 char alignment;
629 char storage;
630 char typtype;
631 Datum datum;
632 bool isnull;
633 char *defaultValue = NULL;
634 char *defaultValueBin = NULL;
635 bool saw_default = false;
636 bool typNotNull = false;
637 bool nullDefined = false;
638 int32 typNDims = list_length(stmt->typename->arrayBounds);
639 HeapTuple typeTup;
640 List *schema = stmt->constraints;
641 ListCell *listptr;
642 Oid basetypeoid;
643 Oid domainoid;
644 Oid old_type_oid;
645 Form_pg_type baseType;
646 int32 basetypeMod;
648 /* Convert list of names to a name and namespace */
649 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
650 &domainName);
652 /* Check we have creation rights in target namespace */
653 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
654 ACL_CREATE);
655 if (aclresult != ACLCHECK_OK)
656 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
657 get_namespace_name(domainNamespace));
660 * Check for collision with an existing type name. If there is one and
661 * it's an autogenerated array, we can rename it out of the way.
663 old_type_oid = GetSysCacheOid(TYPENAMENSP,
664 CStringGetDatum(domainName),
665 ObjectIdGetDatum(domainNamespace),
666 0, 0);
667 if (OidIsValid(old_type_oid))
669 if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
670 ereport(ERROR,
671 (errcode(ERRCODE_DUPLICATE_OBJECT),
672 errmsg("type \"%s\" already exists", domainName)));
676 * Look up the base type.
678 typeTup = typenameType(NULL, stmt->typename, &basetypeMod);
679 baseType = (Form_pg_type) GETSTRUCT(typeTup);
680 basetypeoid = HeapTupleGetOid(typeTup);
683 * Base type must be a plain base type, another domain or an enum. Domains
684 * over pseudotypes would create a security hole. Domains over composite
685 * types might be made to work in the future, but not today.
687 typtype = baseType->typtype;
688 if (typtype != TYPTYPE_BASE &&
689 typtype != TYPTYPE_DOMAIN &&
690 typtype != TYPTYPE_ENUM)
691 ereport(ERROR,
692 (errcode(ERRCODE_DATATYPE_MISMATCH),
693 errmsg("\"%s\" is not a valid base type for a domain",
694 TypeNameToString(stmt->typename))));
696 /* passed by value */
697 byValue = baseType->typbyval;
699 /* Required Alignment */
700 alignment = baseType->typalign;
702 /* TOAST Strategy */
703 storage = baseType->typstorage;
705 /* Storage Length */
706 internalLength = baseType->typlen;
708 /* Array element type (in case base type is an array) */
709 typelem = baseType->typelem;
711 /* Array element Delimiter */
712 delimiter = baseType->typdelim;
714 /* I/O Functions */
715 inputProcedure = F_DOMAIN_IN;
716 outputProcedure = baseType->typoutput;
717 receiveProcedure = F_DOMAIN_RECV;
718 sendProcedure = baseType->typsend;
720 /* Domains never accept typmods, so no typmodin/typmodout needed */
722 /* Analysis function */
723 analyzeProcedure = baseType->typanalyze;
725 /* Inherited default value */
726 datum = SysCacheGetAttr(TYPEOID, typeTup,
727 Anum_pg_type_typdefault, &isnull);
728 if (!isnull)
729 defaultValue = TextDatumGetCString(datum);
731 /* Inherited default binary value */
732 datum = SysCacheGetAttr(TYPEOID, typeTup,
733 Anum_pg_type_typdefaultbin, &isnull);
734 if (!isnull)
735 defaultValueBin = TextDatumGetCString(datum);
738 * Run through constraints manually to avoid the additional processing
739 * conducted by DefineRelation() and friends.
741 foreach(listptr, schema)
743 Node *newConstraint = lfirst(listptr);
744 Constraint *constr;
746 /* Check for unsupported constraint types */
747 if (IsA(newConstraint, FkConstraint))
748 ereport(ERROR,
749 (errcode(ERRCODE_SYNTAX_ERROR),
750 errmsg("foreign key constraints not possible for domains")));
752 /* otherwise it should be a plain Constraint */
753 if (!IsA(newConstraint, Constraint))
754 elog(ERROR, "unrecognized node type: %d",
755 (int) nodeTag(newConstraint));
757 constr = (Constraint *) newConstraint;
759 switch (constr->contype)
761 case CONSTR_DEFAULT:
764 * The inherited default value may be overridden by the user
765 * with the DEFAULT <expr> clause ... but only once.
767 if (saw_default)
768 ereport(ERROR,
769 (errcode(ERRCODE_SYNTAX_ERROR),
770 errmsg("multiple default expressions")));
771 saw_default = true;
773 if (constr->raw_expr)
775 ParseState *pstate;
776 Node *defaultExpr;
778 /* Create a dummy ParseState for transformExpr */
779 pstate = make_parsestate(NULL);
782 * Cook the constr->raw_expr into an expression. Note:
783 * name is strictly for error message
785 defaultExpr = cookDefault(pstate, constr->raw_expr,
786 basetypeoid,
787 basetypeMod,
788 domainName);
791 * If the expression is just a NULL constant, we treat it
792 * like not having a default.
794 * Note that if the basetype is another domain, we'll see
795 * a CoerceToDomain expr here and not discard the default.
796 * This is critical because the domain default needs to be
797 * retained to override any default that the base domain
798 * might have.
800 if (defaultExpr == NULL ||
801 (IsA(defaultExpr, Const) &&
802 ((Const *) defaultExpr)->constisnull))
804 defaultValue = NULL;
805 defaultValueBin = NULL;
807 else
810 * Expression must be stored as a nodeToString result,
811 * but we also require a valid textual representation
812 * (mainly to make life easier for pg_dump).
814 defaultValue =
815 deparse_expression(defaultExpr,
816 deparse_context_for(domainName,
817 InvalidOid),
818 false, false);
819 defaultValueBin = nodeToString(defaultExpr);
822 else
824 /* No default (can this still happen?) */
825 defaultValue = NULL;
826 defaultValueBin = NULL;
828 break;
830 case CONSTR_NOTNULL:
831 if (nullDefined && !typNotNull)
832 ereport(ERROR,
833 (errcode(ERRCODE_SYNTAX_ERROR),
834 errmsg("conflicting NULL/NOT NULL constraints")));
835 typNotNull = true;
836 nullDefined = true;
837 break;
839 case CONSTR_NULL:
840 if (nullDefined && typNotNull)
841 ereport(ERROR,
842 (errcode(ERRCODE_SYNTAX_ERROR),
843 errmsg("conflicting NULL/NOT NULL constraints")));
844 typNotNull = false;
845 nullDefined = true;
846 break;
848 case CONSTR_CHECK:
851 * Check constraints are handled after domain creation, as
852 * they require the Oid of the domain
854 break;
857 * All else are error cases
859 case CONSTR_UNIQUE:
860 ereport(ERROR,
861 (errcode(ERRCODE_SYNTAX_ERROR),
862 errmsg("unique constraints not possible for domains")));
863 break;
865 case CONSTR_PRIMARY:
866 ereport(ERROR,
867 (errcode(ERRCODE_SYNTAX_ERROR),
868 errmsg("primary key constraints not possible for domains")));
869 break;
871 case CONSTR_ATTR_DEFERRABLE:
872 case CONSTR_ATTR_NOT_DEFERRABLE:
873 case CONSTR_ATTR_DEFERRED:
874 case CONSTR_ATTR_IMMEDIATE:
875 ereport(ERROR,
876 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
877 errmsg("specifying constraint deferrability not supported for domains")));
878 break;
880 default:
881 elog(ERROR, "unrecognized constraint subtype: %d",
882 (int) constr->contype);
883 break;
888 * Have TypeCreate do all the real work.
890 domainoid =
891 TypeCreate(InvalidOid, /* no predetermined type OID */
892 domainName, /* type name */
893 domainNamespace, /* namespace */
894 InvalidOid, /* relation oid (n/a here) */
895 0, /* relation kind (ditto) */
896 internalLength, /* internal size */
897 TYPTYPE_DOMAIN, /* type-type (domain type) */
898 delimiter, /* array element delimiter */
899 inputProcedure, /* input procedure */
900 outputProcedure, /* output procedure */
901 receiveProcedure, /* receive procedure */
902 sendProcedure, /* send procedure */
903 InvalidOid, /* typmodin procedure - none */
904 InvalidOid, /* typmodout procedure - none */
905 analyzeProcedure, /* analyze procedure */
906 typelem, /* element type ID */
907 false, /* this isn't an array */
908 InvalidOid, /* no arrays for domains (yet) */
909 basetypeoid, /* base type ID */
910 defaultValue, /* default type value (text) */
911 defaultValueBin, /* default type value (binary) */
912 byValue, /* passed by value */
913 alignment, /* required alignment */
914 storage, /* TOAST strategy */
915 basetypeMod, /* typeMod value */
916 typNDims, /* Array dimensions for base type */
917 typNotNull); /* Type NOT NULL */
920 * Process constraints which refer to the domain ID returned by TypeCreate
922 foreach(listptr, schema)
924 Constraint *constr = lfirst(listptr);
926 /* it must be a Constraint, per check above */
928 switch (constr->contype)
930 case CONSTR_CHECK:
931 domainAddConstraint(domainoid, domainNamespace,
932 basetypeoid, basetypeMod,
933 constr, domainName);
934 break;
936 /* Other constraint types were fully processed above */
938 default:
939 break;
942 /* CCI so we can detect duplicate constraint names */
943 CommandCounterIncrement();
947 * Now we can clean up.
949 ReleaseSysCache(typeTup);
954 * DefineEnum
955 * Registers a new enum.
957 void
958 DefineEnum(CreateEnumStmt *stmt)
960 char *enumName;
961 char *enumArrayName;
962 Oid enumNamespace;
963 Oid enumTypeOid;
964 AclResult aclresult;
965 Oid old_type_oid;
966 Oid enumArrayOid;
967 Relation pg_type;
969 /* Convert list of names to a name and namespace */
970 enumNamespace = QualifiedNameGetCreationNamespace(stmt->typename,
971 &enumName);
973 /* Check we have creation rights in target namespace */
974 aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
975 if (aclresult != ACLCHECK_OK)
976 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
977 get_namespace_name(enumNamespace));
980 * Check for collision with an existing type name. If there is one and
981 * it's an autogenerated array, we can rename it out of the way.
983 old_type_oid = GetSysCacheOid(TYPENAMENSP,
984 CStringGetDatum(enumName),
985 ObjectIdGetDatum(enumNamespace),
986 0, 0);
987 if (OidIsValid(old_type_oid))
989 if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
990 ereport(ERROR,
991 (errcode(ERRCODE_DUPLICATE_OBJECT),
992 errmsg("type \"%s\" already exists", enumName)));
995 /* Preassign array type OID so we can insert it in pg_type.typarray */
996 pg_type = heap_open(TypeRelationId, AccessShareLock);
997 enumArrayOid = GetNewOid(pg_type);
998 heap_close(pg_type, AccessShareLock);
1000 /* Create the pg_type entry */
1001 enumTypeOid =
1002 TypeCreate(InvalidOid, /* no predetermined type OID */
1003 enumName, /* type name */
1004 enumNamespace, /* namespace */
1005 InvalidOid, /* relation oid (n/a here) */
1006 0, /* relation kind (ditto) */
1007 sizeof(Oid), /* internal size */
1008 TYPTYPE_ENUM, /* type-type (enum type) */
1009 DEFAULT_TYPDELIM, /* array element delimiter */
1010 F_ENUM_IN, /* input procedure */
1011 F_ENUM_OUT, /* output procedure */
1012 F_ENUM_RECV, /* receive procedure */
1013 F_ENUM_SEND, /* send procedure */
1014 InvalidOid, /* typmodin procedure - none */
1015 InvalidOid, /* typmodout procedure - none */
1016 InvalidOid, /* analyze procedure - default */
1017 InvalidOid, /* element type ID */
1018 false, /* this is not an array type */
1019 enumArrayOid, /* array type we are about to create */
1020 InvalidOid, /* base type ID (only for domains) */
1021 NULL, /* never a default type value */
1022 NULL, /* binary default isn't sent either */
1023 true, /* always passed by value */
1024 'i', /* int alignment */
1025 'p', /* TOAST strategy always plain */
1026 -1, /* typMod (Domains only) */
1027 0, /* Array dimensions of typbasetype */
1028 false); /* Type NOT NULL */
1030 /* Enter the enum's values into pg_enum */
1031 EnumValuesCreate(enumTypeOid, stmt->vals);
1034 * Create the array type that goes with it.
1036 enumArrayName = makeArrayTypeName(enumName, enumNamespace);
1038 TypeCreate(enumArrayOid, /* force assignment of this type OID */
1039 enumArrayName, /* type name */
1040 enumNamespace, /* namespace */
1041 InvalidOid, /* relation oid (n/a here) */
1042 0, /* relation kind (ditto) */
1043 -1, /* internal size (always varlena) */
1044 TYPTYPE_BASE, /* type-type (base type) */
1045 DEFAULT_TYPDELIM, /* array element delimiter */
1046 F_ARRAY_IN, /* input procedure */
1047 F_ARRAY_OUT, /* output procedure */
1048 F_ARRAY_RECV, /* receive procedure */
1049 F_ARRAY_SEND, /* send procedure */
1050 InvalidOid, /* typmodin procedure - none */
1051 InvalidOid, /* typmodout procedure - none */
1052 InvalidOid, /* analyze procedure - default */
1053 enumTypeOid, /* element type ID */
1054 true, /* yes this is an array type */
1055 InvalidOid, /* no further array type */
1056 InvalidOid, /* base type ID */
1057 NULL, /* never a default type value */
1058 NULL, /* binary default isn't sent either */
1059 false, /* never passed by value */
1060 'i', /* enums have align i, so do their arrays */
1061 'x', /* ARRAY is always toastable */
1062 -1, /* typMod (Domains only) */
1063 0, /* Array dimensions of typbasetype */
1064 false); /* Type NOT NULL */
1066 pfree(enumArrayName);
1071 * Find suitable I/O functions for a type.
1073 * typeOid is the type's OID (which will already exist, if only as a shell
1074 * type).
1077 static Oid
1078 findTypeInputFunction(List *procname, Oid typeOid)
1080 Oid argList[3];
1081 Oid procOid;
1084 * Input functions can take a single argument of type CSTRING, or three
1085 * arguments (string, typioparam OID, typmod).
1087 * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
1088 * see this, we issue a warning and fix up the pg_proc entry.
1090 argList[0] = CSTRINGOID;
1092 procOid = LookupFuncName(procname, 1, argList, true);
1093 if (OidIsValid(procOid))
1094 return procOid;
1096 argList[1] = OIDOID;
1097 argList[2] = INT4OID;
1099 procOid = LookupFuncName(procname, 3, argList, true);
1100 if (OidIsValid(procOid))
1101 return procOid;
1103 /* No luck, try it with OPAQUE */
1104 argList[0] = OPAQUEOID;
1106 procOid = LookupFuncName(procname, 1, argList, true);
1108 if (!OidIsValid(procOid))
1110 argList[1] = OIDOID;
1111 argList[2] = INT4OID;
1113 procOid = LookupFuncName(procname, 3, argList, true);
1116 if (OidIsValid(procOid))
1118 /* Found, but must complain and fix the pg_proc entry */
1119 ereport(WARNING,
1120 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
1121 NameListToString(procname))));
1122 SetFunctionArgType(procOid, 0, CSTRINGOID);
1125 * Need CommandCounterIncrement since DefineType will likely try to
1126 * alter the pg_proc tuple again.
1128 CommandCounterIncrement();
1130 return procOid;
1133 /* Use CSTRING (preferred) in the error message */
1134 argList[0] = CSTRINGOID;
1136 ereport(ERROR,
1137 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1138 errmsg("function %s does not exist",
1139 func_signature_string(procname, 1, argList))));
1141 return InvalidOid; /* keep compiler quiet */
1144 static Oid
1145 findTypeOutputFunction(List *procname, Oid typeOid)
1147 Oid argList[1];
1148 Oid procOid;
1151 * Output functions can take a single argument of the type.
1153 * For backwards compatibility we allow OPAQUE in place of the actual type
1154 * name; if we see this, we issue a warning and fix up the pg_proc entry.
1156 argList[0] = typeOid;
1158 procOid = LookupFuncName(procname, 1, argList, true);
1159 if (OidIsValid(procOid))
1160 return procOid;
1162 /* No luck, try it with OPAQUE */
1163 argList[0] = OPAQUEOID;
1165 procOid = LookupFuncName(procname, 1, argList, true);
1167 if (OidIsValid(procOid))
1169 /* Found, but must complain and fix the pg_proc entry */
1170 ereport(WARNING,
1171 (errmsg("changing argument type of function %s from \"opaque\" to %s",
1172 NameListToString(procname), format_type_be(typeOid))));
1173 SetFunctionArgType(procOid, 0, typeOid);
1176 * Need CommandCounterIncrement since DefineType will likely try to
1177 * alter the pg_proc tuple again.
1179 CommandCounterIncrement();
1181 return procOid;
1184 /* Use type name, not OPAQUE, in the failure message. */
1185 argList[0] = typeOid;
1187 ereport(ERROR,
1188 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1189 errmsg("function %s does not exist",
1190 func_signature_string(procname, 1, argList))));
1192 return InvalidOid; /* keep compiler quiet */
1195 static Oid
1196 findTypeReceiveFunction(List *procname, Oid typeOid)
1198 Oid argList[3];
1199 Oid procOid;
1202 * Receive functions can take a single argument of type INTERNAL, or three
1203 * arguments (internal, typioparam OID, typmod).
1205 argList[0] = INTERNALOID;
1207 procOid = LookupFuncName(procname, 1, argList, true);
1208 if (OidIsValid(procOid))
1209 return procOid;
1211 argList[1] = OIDOID;
1212 argList[2] = INT4OID;
1214 procOid = LookupFuncName(procname, 3, argList, true);
1215 if (OidIsValid(procOid))
1216 return procOid;
1218 ereport(ERROR,
1219 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1220 errmsg("function %s does not exist",
1221 func_signature_string(procname, 1, argList))));
1223 return InvalidOid; /* keep compiler quiet */
1226 static Oid
1227 findTypeSendFunction(List *procname, Oid typeOid)
1229 Oid argList[1];
1230 Oid procOid;
1233 * Send functions can take a single argument of the type.
1235 argList[0] = typeOid;
1237 procOid = LookupFuncName(procname, 1, argList, true);
1238 if (OidIsValid(procOid))
1239 return procOid;
1241 ereport(ERROR,
1242 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1243 errmsg("function %s does not exist",
1244 func_signature_string(procname, 1, argList))));
1246 return InvalidOid; /* keep compiler quiet */
1249 static Oid
1250 findTypeTypmodinFunction(List *procname)
1252 Oid argList[1];
1253 Oid procOid;
1256 * typmodin functions always take one cstring[] argument and return int4.
1258 argList[0] = CSTRINGARRAYOID;
1260 procOid = LookupFuncName(procname, 1, argList, true);
1261 if (!OidIsValid(procOid))
1262 ereport(ERROR,
1263 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1264 errmsg("function %s does not exist",
1265 func_signature_string(procname, 1, argList))));
1267 if (get_func_rettype(procOid) != INT4OID)
1268 ereport(ERROR,
1269 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1270 errmsg("typmod_in function %s must return type \"integer\"",
1271 NameListToString(procname))));
1273 return procOid;
1276 static Oid
1277 findTypeTypmodoutFunction(List *procname)
1279 Oid argList[1];
1280 Oid procOid;
1283 * typmodout functions always take one int4 argument and return cstring.
1285 argList[0] = INT4OID;
1287 procOid = LookupFuncName(procname, 1, argList, true);
1288 if (!OidIsValid(procOid))
1289 ereport(ERROR,
1290 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1291 errmsg("function %s does not exist",
1292 func_signature_string(procname, 1, argList))));
1294 if (get_func_rettype(procOid) != CSTRINGOID)
1295 ereport(ERROR,
1296 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1297 errmsg("typmod_out function %s must return type \"cstring\"",
1298 NameListToString(procname))));
1300 return procOid;
1303 static Oid
1304 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1306 Oid argList[1];
1307 Oid procOid;
1310 * Analyze functions always take one INTERNAL argument and return bool.
1312 argList[0] = INTERNALOID;
1314 procOid = LookupFuncName(procname, 1, argList, true);
1315 if (!OidIsValid(procOid))
1316 ereport(ERROR,
1317 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1318 errmsg("function %s does not exist",
1319 func_signature_string(procname, 1, argList))));
1321 if (get_func_rettype(procOid) != BOOLOID)
1322 ereport(ERROR,
1323 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1324 errmsg("type analyze function %s must return type \"boolean\"",
1325 NameListToString(procname))));
1327 return procOid;
1331 /*-------------------------------------------------------------------
1332 * DefineCompositeType
1334 * Create a Composite Type relation.
1335 * `DefineRelation' does all the work, we just provide the correct
1336 * arguments!
1338 * If the relation already exists, then 'DefineRelation' will abort
1339 * the xact...
1341 * DefineCompositeType returns relid for use when creating
1342 * an implicit composite type during function creation
1343 *-------------------------------------------------------------------
1346 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1348 CreateStmt *createStmt = makeNode(CreateStmt);
1350 if (coldeflist == NIL)
1351 ereport(ERROR,
1352 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1353 errmsg("composite type must have at least one attribute")));
1356 * now set the parameters for keys/inheritance etc. All of these are
1357 * uninteresting for composite types...
1359 createStmt->relation = (RangeVar *) typevar;
1360 createStmt->tableElts = coldeflist;
1361 createStmt->inhRelations = NIL;
1362 createStmt->constraints = NIL;
1363 createStmt->options = list_make1(defWithOids(false));
1364 createStmt->oncommit = ONCOMMIT_NOOP;
1365 createStmt->tablespacename = NULL;
1368 * finally create the relation...
1370 return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1374 * AlterDomainDefault
1376 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1378 void
1379 AlterDomainDefault(List *names, Node *defaultRaw)
1381 TypeName *typename;
1382 Oid domainoid;
1383 HeapTuple tup;
1384 ParseState *pstate;
1385 Relation rel;
1386 char *defaultValue;
1387 Node *defaultExpr = NULL; /* NULL if no default specified */
1388 Datum new_record[Natts_pg_type];
1389 char new_record_nulls[Natts_pg_type];
1390 char new_record_repl[Natts_pg_type];
1391 HeapTuple newtuple;
1392 Form_pg_type typTup;
1394 /* Make a TypeName so we can use standard type lookup machinery */
1395 typename = makeTypeNameFromNameList(names);
1396 domainoid = typenameTypeId(NULL, typename, NULL);
1398 /* Look up the domain in the type table */
1399 rel = heap_open(TypeRelationId, RowExclusiveLock);
1401 tup = SearchSysCacheCopy(TYPEOID,
1402 ObjectIdGetDatum(domainoid),
1403 0, 0, 0);
1404 if (!HeapTupleIsValid(tup))
1405 elog(ERROR, "cache lookup failed for type %u", domainoid);
1406 typTup = (Form_pg_type) GETSTRUCT(tup);
1408 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1409 checkDomainOwner(tup, typename);
1411 /* Setup new tuple */
1412 MemSet(new_record, (Datum) 0, sizeof(new_record));
1413 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1414 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1416 /* Store the new default into the tuple */
1417 if (defaultRaw)
1419 /* Create a dummy ParseState for transformExpr */
1420 pstate = make_parsestate(NULL);
1423 * Cook the colDef->raw_expr into an expression. Note: Name is
1424 * strictly for error message
1426 defaultExpr = cookDefault(pstate, defaultRaw,
1427 typTup->typbasetype,
1428 typTup->typtypmod,
1429 NameStr(typTup->typname));
1432 * If the expression is just a NULL constant, we treat the command
1433 * like ALTER ... DROP DEFAULT. (But see note for same test in
1434 * DefineDomain.)
1436 if (defaultExpr == NULL ||
1437 (IsA(defaultExpr, Const) &&((Const *) defaultExpr)->constisnull))
1439 /* Default is NULL, drop it */
1440 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1441 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1442 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1443 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1445 else
1448 * Expression must be stored as a nodeToString result, but we also
1449 * require a valid textual representation (mainly to make life
1450 * easier for pg_dump).
1452 defaultValue = deparse_expression(defaultExpr,
1453 deparse_context_for(NameStr(typTup->typname),
1454 InvalidOid),
1455 false, false);
1458 * Form an updated tuple with the new default and write it back.
1460 new_record[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(nodeToString(defaultExpr));
1462 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1463 new_record[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultValue);
1464 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1467 else
1469 /* ALTER ... DROP DEFAULT */
1470 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1471 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1472 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1473 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1476 newtuple = heap_modifytuple(tup, RelationGetDescr(rel),
1477 new_record, new_record_nulls,
1478 new_record_repl);
1480 simple_heap_update(rel, &tup->t_self, newtuple);
1482 CatalogUpdateIndexes(rel, newtuple);
1484 /* Rebuild dependencies */
1485 GenerateTypeDependencies(typTup->typnamespace,
1486 domainoid,
1487 InvalidOid, /* typrelid is n/a */
1488 0, /* relation kind is n/a */
1489 typTup->typowner,
1490 typTup->typinput,
1491 typTup->typoutput,
1492 typTup->typreceive,
1493 typTup->typsend,
1494 typTup->typmodin,
1495 typTup->typmodout,
1496 typTup->typanalyze,
1497 typTup->typelem,
1498 false, /* a domain isn't an implicit array */
1499 typTup->typbasetype,
1500 defaultExpr,
1501 true); /* Rebuild is true */
1503 /* Clean up */
1504 heap_close(rel, NoLock);
1505 heap_freetuple(newtuple);
1509 * AlterDomainNotNull
1511 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1513 void
1514 AlterDomainNotNull(List *names, bool notNull)
1516 TypeName *typename;
1517 Oid domainoid;
1518 Relation typrel;
1519 HeapTuple tup;
1520 Form_pg_type typTup;
1522 /* Make a TypeName so we can use standard type lookup machinery */
1523 typename = makeTypeNameFromNameList(names);
1524 domainoid = typenameTypeId(NULL, typename, NULL);
1526 /* Look up the domain in the type table */
1527 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1529 tup = SearchSysCacheCopy(TYPEOID,
1530 ObjectIdGetDatum(domainoid),
1531 0, 0, 0);
1532 if (!HeapTupleIsValid(tup))
1533 elog(ERROR, "cache lookup failed for type %u", domainoid);
1534 typTup = (Form_pg_type) GETSTRUCT(tup);
1536 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1537 checkDomainOwner(tup, typename);
1539 /* Is the domain already set to the desired constraint? */
1540 if (typTup->typnotnull == notNull)
1542 heap_close(typrel, RowExclusiveLock);
1543 return;
1546 /* Adding a NOT NULL constraint requires checking existing columns */
1547 if (notNull)
1549 List *rels;
1550 ListCell *rt;
1552 /* Fetch relation list with attributes based on this domain */
1553 /* ShareLock is sufficient to prevent concurrent data changes */
1555 rels = get_rels_with_domain(domainoid, ShareLock);
1557 foreach(rt, rels)
1559 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1560 Relation testrel = rtc->rel;
1561 TupleDesc tupdesc = RelationGetDescr(testrel);
1562 HeapScanDesc scan;
1563 HeapTuple tuple;
1565 /* Scan all tuples in this relation */
1566 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1567 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1569 int i;
1571 /* Test attributes that are of the domain */
1572 for (i = 0; i < rtc->natts; i++)
1574 int attnum = rtc->atts[i];
1576 if (heap_attisnull(tuple, attnum))
1577 ereport(ERROR,
1578 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1579 errmsg("column \"%s\" of table \"%s\" contains null values",
1580 NameStr(tupdesc->attrs[attnum - 1]->attname),
1581 RelationGetRelationName(testrel))));
1584 heap_endscan(scan);
1586 /* Close each rel after processing, but keep lock */
1587 heap_close(testrel, NoLock);
1592 * Okay to update pg_type row. We can scribble on typTup because it's a
1593 * copy.
1595 typTup->typnotnull = notNull;
1597 simple_heap_update(typrel, &tup->t_self, tup);
1599 CatalogUpdateIndexes(typrel, tup);
1601 /* Clean up */
1602 heap_freetuple(tup);
1603 heap_close(typrel, RowExclusiveLock);
1607 * AlterDomainDropConstraint
1609 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1611 void
1612 AlterDomainDropConstraint(List *names, const char *constrName,
1613 DropBehavior behavior)
1615 TypeName *typename;
1616 Oid domainoid;
1617 HeapTuple tup;
1618 Relation rel;
1619 Relation conrel;
1620 SysScanDesc conscan;
1621 ScanKeyData key[1];
1622 HeapTuple contup;
1624 /* Make a TypeName so we can use standard type lookup machinery */
1625 typename = makeTypeNameFromNameList(names);
1626 domainoid = typenameTypeId(NULL, typename, NULL);
1628 /* Look up the domain in the type table */
1629 rel = heap_open(TypeRelationId, RowExclusiveLock);
1631 tup = SearchSysCacheCopy(TYPEOID,
1632 ObjectIdGetDatum(domainoid),
1633 0, 0, 0);
1634 if (!HeapTupleIsValid(tup))
1635 elog(ERROR, "cache lookup failed for type %u", domainoid);
1637 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1638 checkDomainOwner(tup, typename);
1640 /* Grab an appropriate lock on the pg_constraint relation */
1641 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
1643 /* Use the index to scan only constraints of the target relation */
1644 ScanKeyInit(&key[0],
1645 Anum_pg_constraint_contypid,
1646 BTEqualStrategyNumber, F_OIDEQ,
1647 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1649 conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
1650 SnapshotNow, 1, key);
1653 * Scan over the result set, removing any matching entries.
1655 while ((contup = systable_getnext(conscan)) != NULL)
1657 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1659 if (strcmp(NameStr(con->conname), constrName) == 0)
1661 ObjectAddress conobj;
1663 conobj.classId = ConstraintRelationId;
1664 conobj.objectId = HeapTupleGetOid(contup);
1665 conobj.objectSubId = 0;
1667 performDeletion(&conobj, behavior);
1670 /* Clean up after the scan */
1671 systable_endscan(conscan);
1672 heap_close(conrel, RowExclusiveLock);
1674 heap_close(rel, NoLock);
1678 * AlterDomainAddConstraint
1680 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1682 void
1683 AlterDomainAddConstraint(List *names, Node *newConstraint)
1685 TypeName *typename;
1686 Oid domainoid;
1687 Relation typrel;
1688 HeapTuple tup;
1689 Form_pg_type typTup;
1690 List *rels;
1691 ListCell *rt;
1692 EState *estate;
1693 ExprContext *econtext;
1694 char *ccbin;
1695 Expr *expr;
1696 ExprState *exprstate;
1697 Constraint *constr;
1699 /* Make a TypeName so we can use standard type lookup machinery */
1700 typename = makeTypeNameFromNameList(names);
1701 domainoid = typenameTypeId(NULL, typename, NULL);
1703 /* Look up the domain in the type table */
1704 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1706 tup = SearchSysCacheCopy(TYPEOID,
1707 ObjectIdGetDatum(domainoid),
1708 0, 0, 0);
1709 if (!HeapTupleIsValid(tup))
1710 elog(ERROR, "cache lookup failed for type %u", domainoid);
1711 typTup = (Form_pg_type) GETSTRUCT(tup);
1713 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1714 checkDomainOwner(tup, typename);
1716 /* Check for unsupported constraint types */
1717 if (IsA(newConstraint, FkConstraint))
1718 ereport(ERROR,
1719 (errcode(ERRCODE_SYNTAX_ERROR),
1720 errmsg("foreign key constraints not possible for domains")));
1722 /* otherwise it should be a plain Constraint */
1723 if (!IsA(newConstraint, Constraint))
1724 elog(ERROR, "unrecognized node type: %d",
1725 (int) nodeTag(newConstraint));
1727 constr = (Constraint *) newConstraint;
1729 switch (constr->contype)
1731 case CONSTR_CHECK:
1732 /* processed below */
1733 break;
1735 case CONSTR_UNIQUE:
1736 ereport(ERROR,
1737 (errcode(ERRCODE_SYNTAX_ERROR),
1738 errmsg("unique constraints not possible for domains")));
1739 break;
1741 case CONSTR_PRIMARY:
1742 ereport(ERROR,
1743 (errcode(ERRCODE_SYNTAX_ERROR),
1744 errmsg("primary key constraints not possible for domains")));
1745 break;
1747 case CONSTR_ATTR_DEFERRABLE:
1748 case CONSTR_ATTR_NOT_DEFERRABLE:
1749 case CONSTR_ATTR_DEFERRED:
1750 case CONSTR_ATTR_IMMEDIATE:
1751 ereport(ERROR,
1752 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1753 errmsg("specifying constraint deferrability not supported for domains")));
1754 break;
1756 default:
1757 elog(ERROR, "unrecognized constraint subtype: %d",
1758 (int) constr->contype);
1759 break;
1763 * Since all other constraint types throw errors, this must be a check
1764 * constraint. First, process the constraint expression and add an entry
1765 * to pg_constraint.
1768 ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1769 typTup->typbasetype, typTup->typtypmod,
1770 constr, NameStr(typTup->typname));
1773 * Test all values stored in the attributes based on the domain the
1774 * constraint is being added to.
1776 expr = (Expr *) stringToNode(ccbin);
1778 /* Need an EState to run ExecEvalExpr */
1779 estate = CreateExecutorState();
1780 econtext = GetPerTupleExprContext(estate);
1782 /* build execution state for expr */
1783 exprstate = ExecPrepareExpr(expr, estate);
1785 /* Fetch relation list with attributes based on this domain */
1786 /* ShareLock is sufficient to prevent concurrent data changes */
1788 rels = get_rels_with_domain(domainoid, ShareLock);
1790 foreach(rt, rels)
1792 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1793 Relation testrel = rtc->rel;
1794 TupleDesc tupdesc = RelationGetDescr(testrel);
1795 HeapScanDesc scan;
1796 HeapTuple tuple;
1798 /* Scan all tuples in this relation */
1799 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1800 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1802 int i;
1804 /* Test attributes that are of the domain */
1805 for (i = 0; i < rtc->natts; i++)
1807 int attnum = rtc->atts[i];
1808 Datum d;
1809 bool isNull;
1810 Datum conResult;
1812 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1814 econtext->domainValue_datum = d;
1815 econtext->domainValue_isNull = isNull;
1817 conResult = ExecEvalExprSwitchContext(exprstate,
1818 econtext,
1819 &isNull, NULL);
1821 if (!isNull && !DatumGetBool(conResult))
1822 ereport(ERROR,
1823 (errcode(ERRCODE_CHECK_VIOLATION),
1824 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
1825 NameStr(tupdesc->attrs[attnum - 1]->attname),
1826 RelationGetRelationName(testrel))));
1829 ResetExprContext(econtext);
1831 heap_endscan(scan);
1833 /* Hold relation lock till commit (XXX bad for concurrency) */
1834 heap_close(testrel, NoLock);
1837 FreeExecutorState(estate);
1839 /* Clean up */
1840 heap_close(typrel, RowExclusiveLock);
1844 * get_rels_with_domain
1846 * Fetch all relations / attributes which are using the domain
1848 * The result is a list of RelToCheck structs, one for each distinct
1849 * relation, each containing one or more attribute numbers that are of
1850 * the domain type. We have opened each rel and acquired the specified lock
1851 * type on it.
1853 * We support nested domains by including attributes that are of derived
1854 * domain types. Current callers do not need to distinguish between attributes
1855 * that are of exactly the given domain and those that are of derived domains.
1857 * XXX this is completely broken because there is no way to lock the domain
1858 * to prevent columns from being added or dropped while our command runs.
1859 * We can partially protect against column drops by locking relations as we
1860 * come across them, but there is still a race condition (the window between
1861 * seeing a pg_depend entry and acquiring lock on the relation it references).
1862 * Also, holding locks on all these relations simultaneously creates a non-
1863 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
1864 * risk by using the weakest suitable lock (ShareLock for most callers).
1866 * XXX the API for this is not sufficient to support checking domain values
1867 * that are inside composite types or arrays. Currently we just error out
1868 * if a composite type containing the target domain is stored anywhere.
1869 * There are not currently arrays of domains; if there were, we could take
1870 * the same approach, but it'd be nicer to fix it properly.
1872 * Generally used for retrieving a list of tests when adding
1873 * new constraints to a domain.
1875 static List *
1876 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1878 List *result = NIL;
1879 Relation depRel;
1880 ScanKeyData key[2];
1881 SysScanDesc depScan;
1882 HeapTuple depTup;
1884 Assert(lockmode != NoLock);
1887 * We scan pg_depend to find those things that depend on the domain. (We
1888 * assume we can ignore refobjsubid for a domain.)
1890 depRel = heap_open(DependRelationId, AccessShareLock);
1892 ScanKeyInit(&key[0],
1893 Anum_pg_depend_refclassid,
1894 BTEqualStrategyNumber, F_OIDEQ,
1895 ObjectIdGetDatum(TypeRelationId));
1896 ScanKeyInit(&key[1],
1897 Anum_pg_depend_refobjid,
1898 BTEqualStrategyNumber, F_OIDEQ,
1899 ObjectIdGetDatum(domainOid));
1901 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
1902 SnapshotNow, 2, key);
1904 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1906 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1907 RelToCheck *rtc = NULL;
1908 ListCell *rellist;
1909 Form_pg_attribute pg_att;
1910 int ptr;
1912 /* Check for directly dependent types --- must be domains */
1913 if (pg_depend->classid == TypeRelationId)
1915 Assert(get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN);
1918 * Recursively add dependent columns to the output list. This is
1919 * a bit inefficient since we may fail to combine RelToCheck
1920 * entries when attributes of the same rel have different derived
1921 * domain types, but it's probably not worth improving.
1923 result = list_concat(result,
1924 get_rels_with_domain(pg_depend->objid,
1925 lockmode));
1926 continue;
1929 /* Else, ignore dependees that aren't user columns of relations */
1930 /* (we assume system columns are never of domain types) */
1931 if (pg_depend->classid != RelationRelationId ||
1932 pg_depend->objsubid <= 0)
1933 continue;
1935 /* See if we already have an entry for this relation */
1936 foreach(rellist, result)
1938 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1940 if (RelationGetRelid(rt->rel) == pg_depend->objid)
1942 rtc = rt;
1943 break;
1947 if (rtc == NULL)
1949 /* First attribute found for this relation */
1950 Relation rel;
1952 /* Acquire requested lock on relation */
1953 rel = relation_open(pg_depend->objid, lockmode);
1956 * Check to see if rowtype is stored anyplace as a composite-type
1957 * column; if so we have to fail, for now anyway.
1959 if (OidIsValid(rel->rd_rel->reltype))
1960 find_composite_type_dependencies(rel->rd_rel->reltype,
1961 NULL,
1962 format_type_be(domainOid));
1964 /* Otherwise we can ignore views, composite types, etc */
1965 if (rel->rd_rel->relkind != RELKIND_RELATION)
1967 relation_close(rel, lockmode);
1968 continue;
1971 /* Build the RelToCheck entry with enough space for all atts */
1972 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1973 rtc->rel = rel;
1974 rtc->natts = 0;
1975 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1976 result = lcons(rtc, result);
1980 * Confirm column has not been dropped, and is of the expected type.
1981 * This defends against an ALTER DROP COLUMN occuring just before we
1982 * acquired lock ... but if the whole table were dropped, we'd still
1983 * have a problem.
1985 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
1986 continue;
1987 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
1988 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
1989 continue;
1992 * Okay, add column to result. We store the columns in column-number
1993 * order; this is just a hack to improve predictability of regression
1994 * test output ...
1996 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
1998 ptr = rtc->natts++;
1999 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
2001 rtc->atts[ptr] = rtc->atts[ptr - 1];
2002 ptr--;
2004 rtc->atts[ptr] = pg_depend->objsubid;
2007 systable_endscan(depScan);
2009 relation_close(depRel, AccessShareLock);
2011 return result;
2015 * checkDomainOwner
2017 * Check that the type is actually a domain and that the current user
2018 * has permission to do ALTER DOMAIN on it. Throw an error if not.
2020 static void
2021 checkDomainOwner(HeapTuple tup, TypeName *typename)
2023 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
2025 /* Check that this is actually a domain */
2026 if (typTup->typtype != TYPTYPE_DOMAIN)
2027 ereport(ERROR,
2028 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2029 errmsg("\"%s\" is not a domain",
2030 TypeNameToString(typename))));
2032 /* Permission check: must own type */
2033 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2034 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2035 TypeNameToString(typename));
2039 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
2041 static char *
2042 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
2043 int typMod, Constraint *constr,
2044 char *domainName)
2046 Node *expr;
2047 char *ccsrc;
2048 char *ccbin;
2049 ParseState *pstate;
2050 CoerceToDomainValue *domVal;
2053 * Assign or validate constraint name
2055 if (constr->name)
2057 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
2058 domainOid,
2059 domainNamespace,
2060 constr->name))
2061 ereport(ERROR,
2062 (errcode(ERRCODE_DUPLICATE_OBJECT),
2063 errmsg("constraint \"%s\" for domain \"%s\" already exists",
2064 constr->name, domainName)));
2066 else
2067 constr->name = ChooseConstraintName(domainName,
2068 NULL,
2069 "check",
2070 domainNamespace,
2071 NIL);
2074 * Convert the A_EXPR in raw_expr into an EXPR
2076 pstate = make_parsestate(NULL);
2079 * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
2080 * the expression. Note that it will appear to have the type of the base
2081 * type, not the domain. This seems correct since within the check
2082 * expression, we should not assume the input value can be considered a
2083 * member of the domain.
2085 domVal = makeNode(CoerceToDomainValue);
2086 domVal->typeId = baseTypeOid;
2087 domVal->typeMod = typMod;
2089 pstate->p_value_substitute = (Node *) domVal;
2091 expr = transformExpr(pstate, constr->raw_expr);
2094 * Make sure it yields a boolean result.
2096 expr = coerce_to_boolean(pstate, expr, "CHECK");
2099 * Make sure no outside relations are referred to.
2101 if (list_length(pstate->p_rtable) != 0)
2102 ereport(ERROR,
2103 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2104 errmsg("cannot use table references in domain check constraint")));
2107 * Domains don't allow var clauses (this should be redundant with the
2108 * above check, but make it anyway)
2110 if (contain_var_clause(expr))
2111 ereport(ERROR,
2112 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2113 errmsg("cannot use table references in domain check constraint")));
2116 * No subplans or aggregates, either...
2118 if (pstate->p_hasSubLinks)
2119 ereport(ERROR,
2120 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2121 errmsg("cannot use subquery in check constraint")));
2122 if (pstate->p_hasAggs)
2123 ereport(ERROR,
2124 (errcode(ERRCODE_GROUPING_ERROR),
2125 errmsg("cannot use aggregate function in check constraint")));
2128 * Convert to string form for storage.
2130 ccbin = nodeToString(expr);
2133 * Deparse it to produce text for consrc.
2135 * Since VARNOs aren't allowed in domain constraints, relation context
2136 * isn't required as anything other than a shell.
2138 ccsrc = deparse_expression(expr,
2139 deparse_context_for(domainName,
2140 InvalidOid),
2141 false, false);
2144 * Store the constraint in pg_constraint
2146 CreateConstraintEntry(constr->name, /* Constraint Name */
2147 domainNamespace, /* namespace */
2148 CONSTRAINT_CHECK, /* Constraint Type */
2149 false, /* Is Deferrable */
2150 false, /* Is Deferred */
2151 InvalidOid, /* not a relation constraint */
2152 NULL,
2154 domainOid, /* domain constraint */
2155 InvalidOid, /* Foreign key fields */
2156 NULL,
2157 NULL,
2158 NULL,
2159 NULL,
2161 ' ',
2162 ' ',
2163 ' ',
2164 InvalidOid,
2165 expr, /* Tree form check constraint */
2166 ccbin, /* Binary form check constraint */
2167 ccsrc, /* Source form check constraint */
2168 true, /* is local */
2169 0); /* inhcount */
2172 * Return the compiled constraint expression so the calling routine can
2173 * perform any additional required tests.
2175 return ccbin;
2179 * GetDomainConstraints - get a list of the current constraints of domain
2181 * Returns a possibly-empty list of DomainConstraintState nodes.
2183 * This is called by the executor during plan startup for a CoerceToDomain
2184 * expression node. The given constraints will be checked for each value
2185 * passed through the node.
2187 * We allow this to be called for non-domain types, in which case the result
2188 * is always NIL.
2190 List *
2191 GetDomainConstraints(Oid typeOid)
2193 List *result = NIL;
2194 bool notNull = false;
2195 Relation conRel;
2197 conRel = heap_open(ConstraintRelationId, AccessShareLock);
2199 for (;;)
2201 HeapTuple tup;
2202 HeapTuple conTup;
2203 Form_pg_type typTup;
2204 ScanKeyData key[1];
2205 SysScanDesc scan;
2207 tup = SearchSysCache(TYPEOID,
2208 ObjectIdGetDatum(typeOid),
2209 0, 0, 0);
2210 if (!HeapTupleIsValid(tup))
2211 elog(ERROR, "cache lookup failed for type %u", typeOid);
2212 typTup = (Form_pg_type) GETSTRUCT(tup);
2214 if (typTup->typtype != TYPTYPE_DOMAIN)
2216 /* Not a domain, so done */
2217 ReleaseSysCache(tup);
2218 break;
2221 /* Test for NOT NULL Constraint */
2222 if (typTup->typnotnull)
2223 notNull = true;
2225 /* Look for CHECK Constraints on this domain */
2226 ScanKeyInit(&key[0],
2227 Anum_pg_constraint_contypid,
2228 BTEqualStrategyNumber, F_OIDEQ,
2229 ObjectIdGetDatum(typeOid));
2231 scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
2232 SnapshotNow, 1, key);
2234 while (HeapTupleIsValid(conTup = systable_getnext(scan)))
2236 Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
2237 Datum val;
2238 bool isNull;
2239 Expr *check_expr;
2240 DomainConstraintState *r;
2242 /* Ignore non-CHECK constraints (presently, shouldn't be any) */
2243 if (c->contype != CONSTRAINT_CHECK)
2244 continue;
2247 * Not expecting conbin to be NULL, but we'll test for it anyway
2249 val = fastgetattr(conTup, Anum_pg_constraint_conbin,
2250 conRel->rd_att, &isNull);
2251 if (isNull)
2252 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
2253 NameStr(typTup->typname), NameStr(c->conname));
2255 check_expr = (Expr *) stringToNode(TextDatumGetCString(val));
2257 /* ExecInitExpr assumes we already fixed opfuncids */
2258 fix_opfuncids((Node *) check_expr);
2260 r = makeNode(DomainConstraintState);
2261 r->constrainttype = DOM_CONSTRAINT_CHECK;
2262 r->name = pstrdup(NameStr(c->conname));
2263 r->check_expr = ExecInitExpr(check_expr, NULL);
2266 * use lcons() here because constraints of lower domains should be
2267 * applied earlier.
2269 result = lcons(r, result);
2272 systable_endscan(scan);
2274 /* loop to next domain in stack */
2275 typeOid = typTup->typbasetype;
2276 ReleaseSysCache(tup);
2279 heap_close(conRel, AccessShareLock);
2282 * Only need to add one NOT NULL check regardless of how many domains in
2283 * the stack request it.
2285 if (notNull)
2287 DomainConstraintState *r = makeNode(DomainConstraintState);
2289 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
2290 r->name = pstrdup("NOT NULL");
2291 r->check_expr = NULL;
2293 /* lcons to apply the nullness check FIRST */
2294 result = lcons(r, result);
2297 return result;
2302 * Execute ALTER TYPE RENAME
2304 void
2305 RenameType(List *names, const char *newTypeName)
2307 TypeName *typename;
2308 Oid typeOid;
2309 Relation rel;
2310 HeapTuple tup;
2311 Form_pg_type typTup;
2313 /* Make a TypeName so we can use standard type lookup machinery */
2314 typename = makeTypeNameFromNameList(names);
2315 typeOid = typenameTypeId(NULL, typename, NULL);
2317 /* Look up the type in the type table */
2318 rel = heap_open(TypeRelationId, RowExclusiveLock);
2320 tup = SearchSysCacheCopy(TYPEOID,
2321 ObjectIdGetDatum(typeOid),
2322 0, 0, 0);
2323 if (!HeapTupleIsValid(tup))
2324 elog(ERROR, "cache lookup failed for type %u", typeOid);
2325 typTup = (Form_pg_type) GETSTRUCT(tup);
2327 /* check permissions on type */
2328 if (!pg_type_ownercheck(typeOid, GetUserId()))
2329 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2330 format_type_be(typeOid));
2333 * If it's a composite type, we need to check that it really is a
2334 * free-standing composite type, and not a table's rowtype. We
2335 * want people to use ALTER TABLE not ALTER TYPE for that case.
2337 if (typTup->typtype == TYPTYPE_COMPOSITE &&
2338 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
2339 ereport(ERROR,
2340 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2341 errmsg("%s is a table's row type",
2342 format_type_be(typeOid)),
2343 errhint("Use ALTER TABLE instead.")));
2345 /* don't allow direct alteration of array types, either */
2346 if (OidIsValid(typTup->typelem) &&
2347 get_array_type(typTup->typelem) == typeOid)
2348 ereport(ERROR,
2349 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2350 errmsg("cannot alter array type %s",
2351 format_type_be(typeOid)),
2352 errhint("You can alter type %s, which will alter the array type as well.",
2353 format_type_be(typTup->typelem))));
2356 * If type is composite we need to rename associated pg_class entry too.
2357 * RenameRelationInternal will call RenameTypeInternal automatically.
2359 if (typTup->typtype == TYPTYPE_COMPOSITE)
2360 RenameRelationInternal(typTup->typrelid, newTypeName,
2361 typTup->typnamespace);
2362 else
2363 RenameTypeInternal(typeOid, newTypeName,
2364 typTup->typnamespace);
2366 /* Clean up */
2367 heap_close(rel, RowExclusiveLock);
2371 * Change the owner of a type.
2373 void
2374 AlterTypeOwner(List *names, Oid newOwnerId)
2376 TypeName *typename;
2377 Oid typeOid;
2378 Relation rel;
2379 HeapTuple tup;
2380 HeapTuple newtup;
2381 Form_pg_type typTup;
2382 AclResult aclresult;
2384 rel = heap_open(TypeRelationId, RowExclusiveLock);
2386 /* Make a TypeName so we can use standard type lookup machinery */
2387 typename = makeTypeNameFromNameList(names);
2389 /* Use LookupTypeName here so that shell types can be processed */
2390 tup = LookupTypeName(NULL, typename, NULL);
2391 if (tup == NULL)
2392 ereport(ERROR,
2393 (errcode(ERRCODE_UNDEFINED_OBJECT),
2394 errmsg("type \"%s\" does not exist",
2395 TypeNameToString(typename))));
2396 typeOid = typeTypeId(tup);
2398 /* Copy the syscache entry so we can scribble on it below */
2399 newtup = heap_copytuple(tup);
2400 ReleaseSysCache(tup);
2401 tup = newtup;
2402 typTup = (Form_pg_type) GETSTRUCT(tup);
2405 * If it's a composite type, we need to check that it really is a
2406 * free-standing composite type, and not a table's rowtype. We want people
2407 * to use ALTER TABLE not ALTER TYPE for that case.
2409 if (typTup->typtype == TYPTYPE_COMPOSITE &&
2410 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
2411 ereport(ERROR,
2412 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2413 errmsg("%s is a table's row type",
2414 format_type_be(typeOid)),
2415 errhint("Use ALTER TABLE instead.")));
2417 /* don't allow direct alteration of array types, either */
2418 if (OidIsValid(typTup->typelem) &&
2419 get_array_type(typTup->typelem) == typeOid)
2420 ereport(ERROR,
2421 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2422 errmsg("cannot alter array type %s",
2423 format_type_be(typeOid)),
2424 errhint("You can alter type %s, which will alter the array type as well.",
2425 format_type_be(typTup->typelem))));
2428 * If the new owner is the same as the existing owner, consider the
2429 * command to have succeeded. This is for dump restoration purposes.
2431 if (typTup->typowner != newOwnerId)
2433 /* Superusers can always do it */
2434 if (!superuser())
2436 /* Otherwise, must be owner of the existing object */
2437 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2438 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2439 TypeNameToString(typename));
2441 /* Must be able to become new owner */
2442 check_is_member_of_role(GetUserId(), newOwnerId);
2444 /* New owner must have CREATE privilege on namespace */
2445 aclresult = pg_namespace_aclcheck(typTup->typnamespace,
2446 newOwnerId,
2447 ACL_CREATE);
2448 if (aclresult != ACLCHECK_OK)
2449 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
2450 get_namespace_name(typTup->typnamespace));
2454 * If it's a composite type, invoke ATExecChangeOwner so that we fix
2455 * up the pg_class entry properly. That will call back to
2456 * AlterTypeOwnerInternal to take care of the pg_type entry(s).
2458 if (typTup->typtype == TYPTYPE_COMPOSITE)
2459 ATExecChangeOwner(typTup->typrelid, newOwnerId, true);
2460 else
2463 * We can just apply the modification directly.
2465 * okay to scribble on typTup because it's a copy
2467 typTup->typowner = newOwnerId;
2469 simple_heap_update(rel, &tup->t_self, tup);
2471 CatalogUpdateIndexes(rel, tup);
2473 /* Update owner dependency reference */
2474 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2476 /* If it has an array type, update that too */
2477 if (OidIsValid(typTup->typarray))
2478 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2482 /* Clean up */
2483 heap_close(rel, RowExclusiveLock);
2487 * AlterTypeOwnerInternal - change type owner unconditionally
2489 * This is currently only used to propagate ALTER TABLE/TYPE OWNER to a
2490 * table's rowtype or an array type, and to implement REASSIGN OWNED BY.
2491 * It assumes the caller has done all needed checks. The function will
2492 * automatically recurse to an array type if the type has one.
2494 * hasDependEntry should be TRUE if type is expected to have a pg_shdepend
2495 * entry (ie, it's not a table rowtype nor an array type).
2497 void
2498 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
2499 bool hasDependEntry)
2501 Relation rel;
2502 HeapTuple tup;
2503 Form_pg_type typTup;
2505 rel = heap_open(TypeRelationId, RowExclusiveLock);
2507 tup = SearchSysCacheCopy(TYPEOID,
2508 ObjectIdGetDatum(typeOid),
2509 0, 0, 0);
2510 if (!HeapTupleIsValid(tup))
2511 elog(ERROR, "cache lookup failed for type %u", typeOid);
2512 typTup = (Form_pg_type) GETSTRUCT(tup);
2515 * Modify the owner --- okay to scribble on typTup because it's a copy
2517 typTup->typowner = newOwnerId;
2519 simple_heap_update(rel, &tup->t_self, tup);
2521 CatalogUpdateIndexes(rel, tup);
2523 /* Update owner dependency reference, if it has one */
2524 if (hasDependEntry)
2525 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2527 /* If it has an array type, update that too */
2528 if (OidIsValid(typTup->typarray))
2529 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2531 /* Clean up */
2532 heap_close(rel, RowExclusiveLock);
2536 * Execute ALTER TYPE SET SCHEMA
2538 void
2539 AlterTypeNamespace(List *names, const char *newschema)
2541 TypeName *typename;
2542 Oid typeOid;
2543 Oid nspOid;
2544 Oid elemOid;
2546 /* Make a TypeName so we can use standard type lookup machinery */
2547 typename = makeTypeNameFromNameList(names);
2548 typeOid = typenameTypeId(NULL, typename, NULL);
2550 /* check permissions on type */
2551 if (!pg_type_ownercheck(typeOid, GetUserId()))
2552 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2553 format_type_be(typeOid));
2555 /* get schema OID and check its permissions */
2556 nspOid = LookupCreationNamespace(newschema);
2558 /* don't allow direct alteration of array types */
2559 elemOid = get_element_type(typeOid);
2560 if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
2561 ereport(ERROR,
2562 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2563 errmsg("cannot alter array type %s",
2564 format_type_be(typeOid)),
2565 errhint("You can alter type %s, which will alter the array type as well.",
2566 format_type_be(elemOid))));
2568 /* and do the work */
2569 AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
2573 * Move specified type to new namespace.
2575 * Caller must have already checked privileges.
2577 * The function automatically recurses to process the type's array type,
2578 * if any. isImplicitArray should be TRUE only when doing this internal
2579 * recursion (outside callers must never try to move an array type directly).
2581 * If errorOnTableType is TRUE, the function errors out if the type is
2582 * a table type. ALTER TABLE has to be used to move a table to a new
2583 * namespace.
2585 void
2586 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
2587 bool isImplicitArray,
2588 bool errorOnTableType)
2590 Relation rel;
2591 HeapTuple tup;
2592 Form_pg_type typform;
2593 Oid oldNspOid;
2594 Oid arrayOid;
2595 bool isCompositeType;
2597 rel = heap_open(TypeRelationId, RowExclusiveLock);
2599 tup = SearchSysCacheCopy(TYPEOID,
2600 ObjectIdGetDatum(typeOid),
2601 0, 0, 0);
2602 if (!HeapTupleIsValid(tup))
2603 elog(ERROR, "cache lookup failed for type %u", typeOid);
2604 typform = (Form_pg_type) GETSTRUCT(tup);
2606 oldNspOid = typform->typnamespace;
2607 arrayOid = typform->typarray;
2609 if (oldNspOid == nspOid)
2610 ereport(ERROR,
2611 (errcode(ERRCODE_DUPLICATE_OBJECT),
2612 errmsg("type %s is already in schema \"%s\"",
2613 format_type_be(typeOid),
2614 get_namespace_name(nspOid))));
2616 /* disallow renaming into or out of temp schemas */
2617 if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
2618 ereport(ERROR,
2619 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2620 errmsg("cannot move objects into or out of temporary schemas")));
2622 /* same for TOAST schema */
2623 if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
2624 ereport(ERROR,
2625 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2626 errmsg("cannot move objects into or out of TOAST schema")));
2628 /* check for duplicate name (more friendly than unique-index failure) */
2629 if (SearchSysCacheExists(TYPENAMENSP,
2630 CStringGetDatum(NameStr(typform->typname)),
2631 ObjectIdGetDatum(nspOid),
2632 0, 0))
2633 ereport(ERROR,
2634 (errcode(ERRCODE_DUPLICATE_OBJECT),
2635 errmsg("type \"%s\" already exists in schema \"%s\"",
2636 NameStr(typform->typname),
2637 get_namespace_name(nspOid))));
2639 /* Detect whether type is a composite type (but not a table rowtype) */
2640 isCompositeType =
2641 (typform->typtype == TYPTYPE_COMPOSITE &&
2642 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
2644 /* Enforce not-table-type if requested */
2645 if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
2646 errorOnTableType)
2647 ereport(ERROR,
2648 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2649 errmsg("%s is a table's row type",
2650 format_type_be(typeOid)),
2651 errhint("Use ALTER TABLE instead.")));
2653 /* OK, modify the pg_type row */
2655 /* tup is a copy, so we can scribble directly on it */
2656 typform->typnamespace = nspOid;
2658 simple_heap_update(rel, &tup->t_self, tup);
2659 CatalogUpdateIndexes(rel, tup);
2662 * Composite types have pg_class entries.
2664 * We need to modify the pg_class tuple as well to reflect the change of
2665 * schema.
2667 if (isCompositeType)
2669 Relation classRel;
2671 classRel = heap_open(RelationRelationId, RowExclusiveLock);
2673 AlterRelationNamespaceInternal(classRel, typform->typrelid,
2674 oldNspOid, nspOid,
2675 false);
2677 heap_close(classRel, RowExclusiveLock);
2680 * Check for constraints associated with the composite type (we don't
2681 * currently support this, but probably will someday).
2683 AlterConstraintNamespaces(typform->typrelid, oldNspOid,
2684 nspOid, false);
2686 else
2688 /* If it's a domain, it might have constraints */
2689 if (typform->typtype == TYPTYPE_DOMAIN)
2690 AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
2694 * Update dependency on schema, if any --- a table rowtype has not got
2695 * one, and neither does an implicit array.
2697 if ((isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
2698 !isImplicitArray)
2699 if (changeDependencyFor(TypeRelationId, typeOid,
2700 NamespaceRelationId, oldNspOid, nspOid) != 1)
2701 elog(ERROR, "failed to change schema dependency for type %s",
2702 format_type_be(typeOid));
2704 heap_freetuple(tup);
2706 heap_close(rel, RowExclusiveLock);
2708 /* Recursively alter the associated array type, if any */
2709 if (OidIsValid(arrayOid))
2710 AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);