1 /*-------------------------------------------------------------------------
4 * Routines for SQL commands that manipulate types (and domains).
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.139 2009/12/07 05:22:21 tgl Exp $
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.
21 * These things must be defined and committed in the following order:
23 * input/output, recv/send functions
30 *-------------------------------------------------------------------------
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/planner.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() */
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) */
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,
89 int typMod, Constraint *constr,
95 * Registers a new base type.
98 DefineType(List *names, List *parameters)
102 int16 internalLength = -1; /* default: variable-length */
103 List *inputName = NIL;
104 List *outputName = NIL;
105 List *receiveName = NIL;
106 List *sendName = NIL;
107 List *typmodinName = NIL;
108 List *typmodoutName = NIL;
109 List *analyzeName = NIL;
110 char category = TYPCATEGORY_USER;
111 bool preferred = false;
112 char delimiter = DEFAULT_TYPDELIM;
113 Oid elemType = InvalidOid;
114 char *defaultValue = NULL;
115 bool byValue = false;
116 char alignment = 'i'; /* default alignment */
117 char storage = 'p'; /* default TOAST storage method */
118 DefElem *likeTypeEl = NULL;
119 DefElem *internalLengthEl = NULL;
120 DefElem *inputNameEl = NULL;
121 DefElem *outputNameEl = NULL;
122 DefElem *receiveNameEl = NULL;
123 DefElem *sendNameEl = NULL;
124 DefElem *typmodinNameEl = NULL;
125 DefElem *typmodoutNameEl = NULL;
126 DefElem *analyzeNameEl = NULL;
127 DefElem *categoryEl = NULL;
128 DefElem *preferredEl = NULL;
129 DefElem *delimiterEl = NULL;
130 DefElem *elemTypeEl = NULL;
131 DefElem *defaultValueEl = NULL;
132 DefElem *byValueEl = NULL;
133 DefElem *alignmentEl = NULL;
134 DefElem *storageEl = NULL;
137 Oid receiveOid = InvalidOid;
138 Oid sendOid = InvalidOid;
139 Oid typmodinOid = InvalidOid;
140 Oid typmodoutOid = InvalidOid;
141 Oid analyzeOid = InvalidOid;
150 * As of Postgres 8.4, we require superuser privilege to create a base
151 * type. This is simple paranoia: there are too many ways to mess up the
152 * system with an incorrect type definition (for instance, representation
153 * parameters that don't match what the C code expects). In practice it
154 * takes superuser privilege to create the I/O functions, and so the
155 * former requirement that you own the I/O functions pretty much forced
156 * superuserness anyway. We're just making doubly sure here.
158 * XXX re-enable NOT_USED code sections below if you remove this test.
162 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
163 errmsg("must be superuser to create a base type")));
165 /* Convert list of names to a name and namespace */
166 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
169 /* XXX this is unnecessary given the superuser check above */
170 /* Check we have creation rights in target namespace */
171 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
172 if (aclresult != ACLCHECK_OK)
173 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
174 get_namespace_name(typeNamespace));
178 * Look to see if type already exists (presumably as a shell; if not,
179 * TypeCreate will complain).
181 typoid = GetSysCacheOid(TYPENAMENSP,
182 CStringGetDatum(typeName),
183 ObjectIdGetDatum(typeNamespace),
187 * If it's not a shell, see if it's an autogenerated array type, and if so
188 * rename it out of the way.
190 if (OidIsValid(typoid) && get_typisdefined(typoid))
192 if (moveArrayTypeName(typoid, typeName, typeNamespace))
197 * If it doesn't exist, create it as a shell, so that the OID is known for
198 * use in the I/O function definitions.
200 if (!OidIsValid(typoid))
202 typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
203 /* Make new shell type visible for modification below */
204 CommandCounterIncrement();
207 * If the command was a parameterless CREATE TYPE, we're done ---
208 * creating the shell type was all we're supposed to do.
210 if (parameters == NIL)
215 /* Complain if dummy CREATE TYPE and entry already exists */
216 if (parameters == NIL)
218 (errcode(ERRCODE_DUPLICATE_OBJECT),
219 errmsg("type \"%s\" already exists", typeName)));
222 /* Extract the parameters from the parameter list */
223 foreach(pl, parameters)
225 DefElem *defel = (DefElem *) lfirst(pl);
228 if (pg_strcasecmp(defel->defname, "like") == 0)
229 defelp = &likeTypeEl;
230 else if (pg_strcasecmp(defel->defname, "internallength") == 0)
231 defelp = &internalLengthEl;
232 else if (pg_strcasecmp(defel->defname, "input") == 0)
233 defelp = &inputNameEl;
234 else if (pg_strcasecmp(defel->defname, "output") == 0)
235 defelp = &outputNameEl;
236 else if (pg_strcasecmp(defel->defname, "receive") == 0)
237 defelp = &receiveNameEl;
238 else if (pg_strcasecmp(defel->defname, "send") == 0)
239 defelp = &sendNameEl;
240 else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
241 defelp = &typmodinNameEl;
242 else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
243 defelp = &typmodoutNameEl;
244 else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
245 pg_strcasecmp(defel->defname, "analyse") == 0)
246 defelp = &analyzeNameEl;
247 else if (pg_strcasecmp(defel->defname, "category") == 0)
248 defelp = &categoryEl;
249 else if (pg_strcasecmp(defel->defname, "preferred") == 0)
250 defelp = &preferredEl;
251 else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
252 defelp = &delimiterEl;
253 else if (pg_strcasecmp(defel->defname, "element") == 0)
254 defelp = &elemTypeEl;
255 else if (pg_strcasecmp(defel->defname, "default") == 0)
256 defelp = &defaultValueEl;
257 else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
259 else if (pg_strcasecmp(defel->defname, "alignment") == 0)
260 defelp = &alignmentEl;
261 else if (pg_strcasecmp(defel->defname, "storage") == 0)
265 /* WARNING, not ERROR, for historical backwards-compatibility */
267 (errcode(ERRCODE_SYNTAX_ERROR),
268 errmsg("type attribute \"%s\" not recognized",
274 (errcode(ERRCODE_SYNTAX_ERROR),
275 errmsg("conflicting or redundant options")));
280 * Now interpret the options; we do this separately so that LIKE can be
281 * overridden by other options regardless of the ordering in the parameter
287 Form_pg_type likeForm;
289 likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
290 likeForm = (Form_pg_type) GETSTRUCT(likeType);
291 internalLength = likeForm->typlen;
292 byValue = likeForm->typbyval;
293 alignment = likeForm->typalign;
294 storage = likeForm->typstorage;
295 ReleaseSysCache(likeType);
297 if (internalLengthEl)
298 internalLength = defGetTypeLength(internalLengthEl);
300 inputName = defGetQualifiedName(inputNameEl);
302 outputName = defGetQualifiedName(outputNameEl);
304 receiveName = defGetQualifiedName(receiveNameEl);
306 sendName = defGetQualifiedName(sendNameEl);
308 typmodinName = defGetQualifiedName(typmodinNameEl);
310 typmodoutName = defGetQualifiedName(typmodoutNameEl);
312 analyzeName = defGetQualifiedName(analyzeNameEl);
315 char *p = defGetString(categoryEl);
318 /* restrict to non-control ASCII */
319 if (category < 32 || category > 126)
321 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
322 errmsg("invalid type category \"%s\": must be simple ASCII",
326 preferred = defGetBoolean(preferredEl);
329 char *p = defGetString(delimiterEl);
332 /* XXX shouldn't we restrict the delimiter? */
336 elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl), NULL);
337 /* disallow arrays of pseudotypes */
338 if (get_typtype(elemType) == TYPTYPE_PSEUDO)
340 (errcode(ERRCODE_DATATYPE_MISMATCH),
341 errmsg("array element type cannot be %s",
342 format_type_be(elemType))));
345 defaultValue = defGetString(defaultValueEl);
347 byValue = defGetBoolean(byValueEl);
350 char *a = defGetString(alignmentEl);
353 * Note: if argument was an unquoted identifier, parser will have
354 * applied translations to it, so be prepared to recognize translated
355 * type names as well as the nominal form.
357 if (pg_strcasecmp(a, "double") == 0 ||
358 pg_strcasecmp(a, "float8") == 0 ||
359 pg_strcasecmp(a, "pg_catalog.float8") == 0)
361 else if (pg_strcasecmp(a, "int4") == 0 ||
362 pg_strcasecmp(a, "pg_catalog.int4") == 0)
364 else if (pg_strcasecmp(a, "int2") == 0 ||
365 pg_strcasecmp(a, "pg_catalog.int2") == 0)
367 else if (pg_strcasecmp(a, "char") == 0 ||
368 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
372 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
373 errmsg("alignment \"%s\" not recognized", a)));
377 char *a = defGetString(storageEl);
379 if (pg_strcasecmp(a, "plain") == 0)
381 else if (pg_strcasecmp(a, "external") == 0)
383 else if (pg_strcasecmp(a, "extended") == 0)
385 else if (pg_strcasecmp(a, "main") == 0)
389 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
390 errmsg("storage \"%s\" not recognized", a)));
394 * make sure we have our required definitions
396 if (inputName == NIL)
398 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
399 errmsg("type input function must be specified")));
400 if (outputName == NIL)
402 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
403 errmsg("type output function must be specified")));
405 if (typmodinName == NIL && typmodoutName != NIL)
407 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
408 errmsg("type modifier output function is useless without a type modifier input function")));
411 * Convert I/O proc names to OIDs
413 inputOid = findTypeInputFunction(inputName, typoid);
414 outputOid = findTypeOutputFunction(outputName, typoid);
416 receiveOid = findTypeReceiveFunction(receiveName, typoid);
418 sendOid = findTypeSendFunction(sendName, typoid);
421 * Verify that I/O procs return the expected thing. If we see OPAQUE,
422 * complain and change it to the correct type-safe choice.
424 resulttype = get_func_rettype(inputOid);
425 if (resulttype != typoid)
427 if (resulttype == OPAQUEOID)
429 /* backwards-compatibility hack */
431 (errmsg("changing return type of function %s from \"opaque\" to %s",
432 NameListToString(inputName), typeName)));
433 SetFunctionReturnType(inputOid, typoid);
437 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
438 errmsg("type input function %s must return type %s",
439 NameListToString(inputName), typeName)));
441 resulttype = get_func_rettype(outputOid);
442 if (resulttype != CSTRINGOID)
444 if (resulttype == OPAQUEOID)
446 /* backwards-compatibility hack */
448 (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
449 NameListToString(outputName))));
450 SetFunctionReturnType(outputOid, CSTRINGOID);
454 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
455 errmsg("type output function %s must return type \"cstring\"",
456 NameListToString(outputName))));
460 resulttype = get_func_rettype(receiveOid);
461 if (resulttype != typoid)
463 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
464 errmsg("type receive function %s must return type %s",
465 NameListToString(receiveName), typeName)));
469 resulttype = get_func_rettype(sendOid);
470 if (resulttype != BYTEAOID)
472 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
473 errmsg("type send function %s must return type \"bytea\"",
474 NameListToString(sendName))));
478 * Convert typmodin/out function proc names to OIDs.
481 typmodinOid = findTypeTypmodinFunction(typmodinName);
483 typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
486 * Convert analysis function proc name to an OID. If no analysis function
487 * is specified, we'll use zero to select the built-in default algorithm.
490 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
493 * Check permissions on functions. We choose to require the creator/owner
494 * of a type to also own the underlying functions. Since creating a type
495 * is tantamount to granting public execute access on the functions, the
496 * minimum sane check would be for execute-with-grant-option. But we
497 * don't have a way to make the type go away if the grant option is
498 * revoked, so ownership seems better.
501 /* XXX this is unnecessary given the superuser check above */
502 if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
503 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
504 NameListToString(inputName));
505 if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
506 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
507 NameListToString(outputName));
508 if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
509 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
510 NameListToString(receiveName));
511 if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
512 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
513 NameListToString(sendName));
514 if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
515 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
516 NameListToString(typmodinName));
517 if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
518 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
519 NameListToString(typmodoutName));
520 if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
521 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
522 NameListToString(analyzeName));
525 /* Preassign array type OID so we can insert it in pg_type.typarray */
526 pg_type = heap_open(TypeRelationId, AccessShareLock);
527 array_oid = GetNewOid(pg_type);
528 heap_close(pg_type, AccessShareLock);
531 * now have TypeCreate do all the real work.
534 TypeCreate(InvalidOid, /* no predetermined type OID */
535 typeName, /* type name */
536 typeNamespace, /* namespace */
537 InvalidOid, /* relation oid (n/a here) */
538 0, /* relation kind (ditto) */
539 GetUserId(), /* owner's ID */
540 internalLength, /* internal size */
541 TYPTYPE_BASE, /* type-type (base type) */
542 category, /* type-category */
543 preferred, /* is it a preferred type? */
544 delimiter, /* array element delimiter */
545 inputOid, /* input procedure */
546 outputOid, /* output procedure */
547 receiveOid, /* receive procedure */
548 sendOid, /* send procedure */
549 typmodinOid, /* typmodin procedure */
550 typmodoutOid, /* typmodout procedure */
551 analyzeOid, /* analyze procedure */
552 elemType, /* element type ID */
553 false, /* this is not an array type */
554 array_oid, /* array type we are about to create */
555 InvalidOid, /* base type ID (only for domains) */
556 defaultValue, /* default type value */
557 NULL, /* no binary form available */
558 byValue, /* passed by value */
559 alignment, /* required alignment */
560 storage, /* TOAST strategy */
561 -1, /* typMod (Domains only) */
562 0, /* Array Dimensions of typbasetype */
563 false); /* Type NOT NULL */
566 * Create the array type that goes with it.
568 array_type = makeArrayTypeName(typeName, typeNamespace);
570 /* alignment must be 'i' or 'd' for arrays */
571 alignment = (alignment == 'd') ? 'd' : 'i';
573 TypeCreate(array_oid, /* force assignment of this type OID */
574 array_type, /* type name */
575 typeNamespace, /* namespace */
576 InvalidOid, /* relation oid (n/a here) */
577 0, /* relation kind (ditto) */
578 GetUserId(), /* owner's ID */
579 -1, /* internal size (always varlena) */
580 TYPTYPE_BASE, /* type-type (base type) */
581 TYPCATEGORY_ARRAY, /* type-category (array) */
582 false, /* array types are never preferred */
583 delimiter, /* array element delimiter */
584 F_ARRAY_IN, /* input procedure */
585 F_ARRAY_OUT, /* output procedure */
586 F_ARRAY_RECV, /* receive procedure */
587 F_ARRAY_SEND, /* send procedure */
588 typmodinOid, /* typmodin procedure */
589 typmodoutOid, /* typmodout procedure */
590 InvalidOid, /* analyze procedure - default */
591 typoid, /* element type ID */
592 true, /* yes this is an array type */
593 InvalidOid, /* no further array type */
594 InvalidOid, /* base type ID */
595 NULL, /* never a default type value */
596 NULL, /* binary default isn't sent either */
597 false, /* never passed by value */
598 alignment, /* see above */
599 'x', /* ARRAY is always toastable */
600 -1, /* typMod (Domains only) */
601 0, /* Array dimensions of typbasetype */
602 false); /* Type NOT NULL */
610 * Implements DROP TYPE and DROP DOMAIN
612 * Note: if DOMAIN is specified, we enforce that each type is a domain, but
613 * we don't enforce the converse for DROP TYPE
616 RemoveTypes(DropStmt *drop)
618 ObjectAddresses *objects;
622 * First we identify all the types, then we delete them in a single
623 * performMultipleDeletions() call. This is to avoid unwanted DROP
624 * RESTRICT errors if one of the types depends on another.
626 objects = new_object_addresses();
628 foreach(cell, drop->objects)
630 List *names = (List *) lfirst(cell);
634 ObjectAddress object;
637 /* Make a TypeName so we can use standard type lookup machinery */
638 typename = makeTypeNameFromNameList(names);
640 /* Use LookupTypeName here so that shell types can be removed. */
641 tup = LookupTypeName(NULL, typename, NULL);
644 if (!drop->missing_ok)
647 (errcode(ERRCODE_UNDEFINED_OBJECT),
648 errmsg("type \"%s\" does not exist",
649 TypeNameToString(typename))));
654 (errmsg("type \"%s\" does not exist, skipping",
655 TypeNameToString(typename))));
660 typeoid = typeTypeId(tup);
661 typ = (Form_pg_type) GETSTRUCT(tup);
663 /* Permission check: must own type or its namespace */
664 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
665 !pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
666 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
667 format_type_be(typeoid));
669 if (drop->removeType == OBJECT_DOMAIN)
671 /* Check that this is actually a domain */
672 if (typ->typtype != TYPTYPE_DOMAIN)
674 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
675 errmsg("\"%s\" is not a domain",
676 TypeNameToString(typename))));
680 * Note: we need no special check for array types here, as the normal
681 * treatment of internal dependencies handles it just fine
684 object.classId = TypeRelationId;
685 object.objectId = typeoid;
686 object.objectSubId = 0;
688 add_exact_object_address(&object, objects);
690 ReleaseSysCache(tup);
693 performMultipleDeletions(objects, drop->behavior);
695 free_object_addresses(objects);
700 * Guts of type deletion.
703 RemoveTypeById(Oid typeOid)
708 relation = heap_open(TypeRelationId, RowExclusiveLock);
710 tup = SearchSysCache(TYPEOID,
711 ObjectIdGetDatum(typeOid),
713 if (!HeapTupleIsValid(tup))
714 elog(ERROR, "cache lookup failed for type %u", typeOid);
716 simple_heap_delete(relation, &tup->t_self);
719 * If it is an enum, delete the pg_enum entries too; we don't bother with
720 * making dependency entries for those, so it has to be done "by hand"
723 if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
724 EnumValuesDelete(typeOid);
726 ReleaseSysCache(tup);
728 heap_close(relation, RowExclusiveLock);
734 * Registers a new domain.
737 DefineDomain(CreateDomainStmt *stmt)
742 int16 internalLength;
745 Oid receiveProcedure;
747 Oid analyzeProcedure;
757 char *defaultValue = NULL;
758 char *defaultValueBin = NULL;
759 bool saw_default = false;
760 bool typNotNull = false;
761 bool nullDefined = false;
762 int32 typNDims = list_length(stmt->typeName->arrayBounds);
764 List *schema = stmt->constraints;
769 Form_pg_type baseType;
772 /* Convert list of names to a name and namespace */
773 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
776 /* Check we have creation rights in target namespace */
777 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
779 if (aclresult != ACLCHECK_OK)
780 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
781 get_namespace_name(domainNamespace));
784 * Check for collision with an existing type name. If there is one and
785 * it's an autogenerated array, we can rename it out of the way.
787 old_type_oid = GetSysCacheOid(TYPENAMENSP,
788 CStringGetDatum(domainName),
789 ObjectIdGetDatum(domainNamespace),
791 if (OidIsValid(old_type_oid))
793 if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
795 (errcode(ERRCODE_DUPLICATE_OBJECT),
796 errmsg("type \"%s\" already exists", domainName)));
800 * Look up the base type.
802 typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
803 baseType = (Form_pg_type) GETSTRUCT(typeTup);
804 basetypeoid = HeapTupleGetOid(typeTup);
807 * Base type must be a plain base type, another domain or an enum. Domains
808 * over pseudotypes would create a security hole. Domains over composite
809 * types might be made to work in the future, but not today.
811 typtype = baseType->typtype;
812 if (typtype != TYPTYPE_BASE &&
813 typtype != TYPTYPE_DOMAIN &&
814 typtype != TYPTYPE_ENUM)
816 (errcode(ERRCODE_DATATYPE_MISMATCH),
817 errmsg("\"%s\" is not a valid base type for a domain",
818 TypeNameToString(stmt->typeName))));
820 /* passed by value */
821 byValue = baseType->typbyval;
823 /* Required Alignment */
824 alignment = baseType->typalign;
827 storage = baseType->typstorage;
830 internalLength = baseType->typlen;
833 category = baseType->typcategory;
835 /* Array element type (in case base type is an array) */
836 typelem = baseType->typelem;
838 /* Array element Delimiter */
839 delimiter = baseType->typdelim;
842 inputProcedure = F_DOMAIN_IN;
843 outputProcedure = baseType->typoutput;
844 receiveProcedure = F_DOMAIN_RECV;
845 sendProcedure = baseType->typsend;
847 /* Domains never accept typmods, so no typmodin/typmodout needed */
849 /* Analysis function */
850 analyzeProcedure = baseType->typanalyze;
852 /* Inherited default value */
853 datum = SysCacheGetAttr(TYPEOID, typeTup,
854 Anum_pg_type_typdefault, &isnull);
856 defaultValue = TextDatumGetCString(datum);
858 /* Inherited default binary value */
859 datum = SysCacheGetAttr(TYPEOID, typeTup,
860 Anum_pg_type_typdefaultbin, &isnull);
862 defaultValueBin = TextDatumGetCString(datum);
865 * Run through constraints manually to avoid the additional processing
866 * conducted by DefineRelation() and friends.
868 foreach(listptr, schema)
870 Constraint *constr = lfirst(listptr);
872 if (!IsA(constr, Constraint))
873 elog(ERROR, "unrecognized node type: %d",
874 (int) nodeTag(constr));
875 switch (constr->contype)
880 * The inherited default value may be overridden by the user
881 * with the DEFAULT <expr> clause ... but only once.
885 (errcode(ERRCODE_SYNTAX_ERROR),
886 errmsg("multiple default expressions")));
889 if (constr->raw_expr)
894 /* Create a dummy ParseState for transformExpr */
895 pstate = make_parsestate(NULL);
898 * Cook the constr->raw_expr into an expression. Note:
899 * name is strictly for error message
901 defaultExpr = cookDefault(pstate, constr->raw_expr,
907 * If the expression is just a NULL constant, we treat it
908 * like not having a default.
910 * Note that if the basetype is another domain, we'll see
911 * a CoerceToDomain expr here and not discard the default.
912 * This is critical because the domain default needs to be
913 * retained to override any default that the base domain
916 if (defaultExpr == NULL ||
917 (IsA(defaultExpr, Const) &&
918 ((Const *) defaultExpr)->constisnull))
921 defaultValueBin = NULL;
926 * Expression must be stored as a nodeToString result,
927 * but we also require a valid textual representation
928 * (mainly to make life easier for pg_dump).
931 deparse_expression(defaultExpr,
932 deparse_context_for(domainName,
935 defaultValueBin = nodeToString(defaultExpr);
940 /* No default (can this still happen?) */
942 defaultValueBin = NULL;
947 if (nullDefined && !typNotNull)
949 (errcode(ERRCODE_SYNTAX_ERROR),
950 errmsg("conflicting NULL/NOT NULL constraints")));
956 if (nullDefined && typNotNull)
958 (errcode(ERRCODE_SYNTAX_ERROR),
959 errmsg("conflicting NULL/NOT NULL constraints")));
967 * Check constraints are handled after domain creation, as
968 * they require the Oid of the domain
973 * All else are error cases
977 (errcode(ERRCODE_SYNTAX_ERROR),
978 errmsg("unique constraints not possible for domains")));
983 (errcode(ERRCODE_SYNTAX_ERROR),
984 errmsg("primary key constraints not possible for domains")));
987 case CONSTR_EXCLUSION:
989 (errcode(ERRCODE_SYNTAX_ERROR),
990 errmsg("exclusion constraints not possible for domains")));
995 (errcode(ERRCODE_SYNTAX_ERROR),
996 errmsg("foreign key constraints not possible for domains")));
999 case CONSTR_ATTR_DEFERRABLE:
1000 case CONSTR_ATTR_NOT_DEFERRABLE:
1001 case CONSTR_ATTR_DEFERRED:
1002 case CONSTR_ATTR_IMMEDIATE:
1004 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1005 errmsg("specifying constraint deferrability not supported for domains")));
1009 elog(ERROR, "unrecognized constraint subtype: %d",
1010 (int) constr->contype);
1016 * Have TypeCreate do all the real work.
1019 TypeCreate(InvalidOid, /* no predetermined type OID */
1020 domainName, /* type name */
1021 domainNamespace, /* namespace */
1022 InvalidOid, /* relation oid (n/a here) */
1023 0, /* relation kind (ditto) */
1024 GetUserId(), /* owner's ID */
1025 internalLength, /* internal size */
1026 TYPTYPE_DOMAIN, /* type-type (domain type) */
1027 category, /* type-category */
1028 false, /* domain types are never preferred */
1029 delimiter, /* array element delimiter */
1030 inputProcedure, /* input procedure */
1031 outputProcedure, /* output procedure */
1032 receiveProcedure, /* receive procedure */
1033 sendProcedure, /* send procedure */
1034 InvalidOid, /* typmodin procedure - none */
1035 InvalidOid, /* typmodout procedure - none */
1036 analyzeProcedure, /* analyze procedure */
1037 typelem, /* element type ID */
1038 false, /* this isn't an array */
1039 InvalidOid, /* no arrays for domains (yet) */
1040 basetypeoid, /* base type ID */
1041 defaultValue, /* default type value (text) */
1042 defaultValueBin, /* default type value (binary) */
1043 byValue, /* passed by value */
1044 alignment, /* required alignment */
1045 storage, /* TOAST strategy */
1046 basetypeMod, /* typeMod value */
1047 typNDims, /* Array dimensions for base type */
1048 typNotNull); /* Type NOT NULL */
1051 * Process constraints which refer to the domain ID returned by TypeCreate
1053 foreach(listptr, schema)
1055 Constraint *constr = lfirst(listptr);
1057 /* it must be a Constraint, per check above */
1059 switch (constr->contype)
1062 domainAddConstraint(domainoid, domainNamespace,
1063 basetypeoid, basetypeMod,
1064 constr, domainName);
1067 /* Other constraint types were fully processed above */
1073 /* CCI so we can detect duplicate constraint names */
1074 CommandCounterIncrement();
1078 * Now we can clean up.
1080 ReleaseSysCache(typeTup);
1086 * Registers a new enum.
1089 DefineEnum(CreateEnumStmt *stmt)
1092 char *enumArrayName;
1095 AclResult aclresult;
1100 /* Convert list of names to a name and namespace */
1101 enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
1104 /* Check we have creation rights in target namespace */
1105 aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
1106 if (aclresult != ACLCHECK_OK)
1107 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1108 get_namespace_name(enumNamespace));
1111 * Check for collision with an existing type name. If there is one and
1112 * it's an autogenerated array, we can rename it out of the way.
1114 old_type_oid = GetSysCacheOid(TYPENAMENSP,
1115 CStringGetDatum(enumName),
1116 ObjectIdGetDatum(enumNamespace),
1118 if (OidIsValid(old_type_oid))
1120 if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
1122 (errcode(ERRCODE_DUPLICATE_OBJECT),
1123 errmsg("type \"%s\" already exists", enumName)));
1126 /* Preassign array type OID so we can insert it in pg_type.typarray */
1127 pg_type = heap_open(TypeRelationId, AccessShareLock);
1128 enumArrayOid = GetNewOid(pg_type);
1129 heap_close(pg_type, AccessShareLock);
1131 /* Create the pg_type entry */
1133 TypeCreate(InvalidOid, /* no predetermined type OID */
1134 enumName, /* type name */
1135 enumNamespace, /* namespace */
1136 InvalidOid, /* relation oid (n/a here) */
1137 0, /* relation kind (ditto) */
1138 GetUserId(), /* owner's ID */
1139 sizeof(Oid), /* internal size */
1140 TYPTYPE_ENUM, /* type-type (enum type) */
1141 TYPCATEGORY_ENUM, /* type-category (enum type) */
1142 false, /* enum types are never preferred */
1143 DEFAULT_TYPDELIM, /* array element delimiter */
1144 F_ENUM_IN, /* input procedure */
1145 F_ENUM_OUT, /* output procedure */
1146 F_ENUM_RECV, /* receive procedure */
1147 F_ENUM_SEND, /* send procedure */
1148 InvalidOid, /* typmodin procedure - none */
1149 InvalidOid, /* typmodout procedure - none */
1150 InvalidOid, /* analyze procedure - default */
1151 InvalidOid, /* element type ID */
1152 false, /* this is not an array type */
1153 enumArrayOid, /* array type we are about to create */
1154 InvalidOid, /* base type ID (only for domains) */
1155 NULL, /* never a default type value */
1156 NULL, /* binary default isn't sent either */
1157 true, /* always passed by value */
1158 'i', /* int alignment */
1159 'p', /* TOAST strategy always plain */
1160 -1, /* typMod (Domains only) */
1161 0, /* Array dimensions of typbasetype */
1162 false); /* Type NOT NULL */
1164 /* Enter the enum's values into pg_enum */
1165 EnumValuesCreate(enumTypeOid, stmt->vals);
1168 * Create the array type that goes with it.
1170 enumArrayName = makeArrayTypeName(enumName, enumNamespace);
1172 TypeCreate(enumArrayOid, /* force assignment of this type OID */
1173 enumArrayName, /* type name */
1174 enumNamespace, /* namespace */
1175 InvalidOid, /* relation oid (n/a here) */
1176 0, /* relation kind (ditto) */
1177 GetUserId(), /* owner's ID */
1178 -1, /* internal size (always varlena) */
1179 TYPTYPE_BASE, /* type-type (base type) */
1180 TYPCATEGORY_ARRAY, /* type-category (array) */
1181 false, /* array types are never preferred */
1182 DEFAULT_TYPDELIM, /* array element delimiter */
1183 F_ARRAY_IN, /* input procedure */
1184 F_ARRAY_OUT, /* output procedure */
1185 F_ARRAY_RECV, /* receive procedure */
1186 F_ARRAY_SEND, /* send procedure */
1187 InvalidOid, /* typmodin procedure - none */
1188 InvalidOid, /* typmodout procedure - none */
1189 InvalidOid, /* analyze procedure - default */
1190 enumTypeOid, /* element type ID */
1191 true, /* yes this is an array type */
1192 InvalidOid, /* no further array type */
1193 InvalidOid, /* base type ID */
1194 NULL, /* never a default type value */
1195 NULL, /* binary default isn't sent either */
1196 false, /* never passed by value */
1197 'i', /* enums have align i, so do their arrays */
1198 'x', /* ARRAY is always toastable */
1199 -1, /* typMod (Domains only) */
1200 0, /* Array dimensions of typbasetype */
1201 false); /* Type NOT NULL */
1203 pfree(enumArrayName);
1208 * Find suitable I/O functions for a type.
1210 * typeOid is the type's OID (which will already exist, if only as a shell
1215 findTypeInputFunction(List *procname, Oid typeOid)
1221 * Input functions can take a single argument of type CSTRING, or three
1222 * arguments (string, typioparam OID, typmod).
1224 * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
1225 * see this, we issue a warning and fix up the pg_proc entry.
1227 argList[0] = CSTRINGOID;
1229 procOid = LookupFuncName(procname, 1, argList, true);
1230 if (OidIsValid(procOid))
1233 argList[1] = OIDOID;
1234 argList[2] = INT4OID;
1236 procOid = LookupFuncName(procname, 3, argList, true);
1237 if (OidIsValid(procOid))
1240 /* No luck, try it with OPAQUE */
1241 argList[0] = OPAQUEOID;
1243 procOid = LookupFuncName(procname, 1, argList, true);
1245 if (!OidIsValid(procOid))
1247 argList[1] = OIDOID;
1248 argList[2] = INT4OID;
1250 procOid = LookupFuncName(procname, 3, argList, true);
1253 if (OidIsValid(procOid))
1255 /* Found, but must complain and fix the pg_proc entry */
1257 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
1258 NameListToString(procname))));
1259 SetFunctionArgType(procOid, 0, CSTRINGOID);
1262 * Need CommandCounterIncrement since DefineType will likely try to
1263 * alter the pg_proc tuple again.
1265 CommandCounterIncrement();
1270 /* Use CSTRING (preferred) in the error message */
1271 argList[0] = CSTRINGOID;
1274 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1275 errmsg("function %s does not exist",
1276 func_signature_string(procname, 1, NIL, argList))));
1278 return InvalidOid; /* keep compiler quiet */
1282 findTypeOutputFunction(List *procname, Oid typeOid)
1288 * Output functions can take a single argument of the type.
1290 * For backwards compatibility we allow OPAQUE in place of the actual type
1291 * name; if we see this, we issue a warning and fix up the pg_proc entry.
1293 argList[0] = typeOid;
1295 procOid = LookupFuncName(procname, 1, argList, true);
1296 if (OidIsValid(procOid))
1299 /* No luck, try it with OPAQUE */
1300 argList[0] = OPAQUEOID;
1302 procOid = LookupFuncName(procname, 1, argList, true);
1304 if (OidIsValid(procOid))
1306 /* Found, but must complain and fix the pg_proc entry */
1308 (errmsg("changing argument type of function %s from \"opaque\" to %s",
1309 NameListToString(procname), format_type_be(typeOid))));
1310 SetFunctionArgType(procOid, 0, typeOid);
1313 * Need CommandCounterIncrement since DefineType will likely try to
1314 * alter the pg_proc tuple again.
1316 CommandCounterIncrement();
1321 /* Use type name, not OPAQUE, in the failure message. */
1322 argList[0] = typeOid;
1325 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1326 errmsg("function %s does not exist",
1327 func_signature_string(procname, 1, NIL, argList))));
1329 return InvalidOid; /* keep compiler quiet */
1333 findTypeReceiveFunction(List *procname, Oid typeOid)
1339 * Receive functions can take a single argument of type INTERNAL, or three
1340 * arguments (internal, typioparam OID, typmod).
1342 argList[0] = INTERNALOID;
1344 procOid = LookupFuncName(procname, 1, argList, true);
1345 if (OidIsValid(procOid))
1348 argList[1] = OIDOID;
1349 argList[2] = INT4OID;
1351 procOid = LookupFuncName(procname, 3, argList, true);
1352 if (OidIsValid(procOid))
1356 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1357 errmsg("function %s does not exist",
1358 func_signature_string(procname, 1, NIL, argList))));
1360 return InvalidOid; /* keep compiler quiet */
1364 findTypeSendFunction(List *procname, Oid typeOid)
1370 * Send functions can take a single argument of the type.
1372 argList[0] = typeOid;
1374 procOid = LookupFuncName(procname, 1, argList, true);
1375 if (OidIsValid(procOid))
1379 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1380 errmsg("function %s does not exist",
1381 func_signature_string(procname, 1, NIL, argList))));
1383 return InvalidOid; /* keep compiler quiet */
1387 findTypeTypmodinFunction(List *procname)
1393 * typmodin functions always take one cstring[] argument and return int4.
1395 argList[0] = CSTRINGARRAYOID;
1397 procOid = LookupFuncName(procname, 1, argList, true);
1398 if (!OidIsValid(procOid))
1400 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1401 errmsg("function %s does not exist",
1402 func_signature_string(procname, 1, NIL, argList))));
1404 if (get_func_rettype(procOid) != INT4OID)
1406 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1407 errmsg("typmod_in function %s must return type \"integer\"",
1408 NameListToString(procname))));
1414 findTypeTypmodoutFunction(List *procname)
1420 * typmodout functions always take one int4 argument and return cstring.
1422 argList[0] = INT4OID;
1424 procOid = LookupFuncName(procname, 1, argList, true);
1425 if (!OidIsValid(procOid))
1427 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1428 errmsg("function %s does not exist",
1429 func_signature_string(procname, 1, NIL, argList))));
1431 if (get_func_rettype(procOid) != CSTRINGOID)
1433 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1434 errmsg("typmod_out function %s must return type \"cstring\"",
1435 NameListToString(procname))));
1441 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1447 * Analyze functions always take one INTERNAL argument and return bool.
1449 argList[0] = INTERNALOID;
1451 procOid = LookupFuncName(procname, 1, argList, true);
1452 if (!OidIsValid(procOid))
1454 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1455 errmsg("function %s does not exist",
1456 func_signature_string(procname, 1, NIL, argList))));
1458 if (get_func_rettype(procOid) != BOOLOID)
1460 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1461 errmsg("type analyze function %s must return type \"boolean\"",
1462 NameListToString(procname))));
1468 /*-------------------------------------------------------------------
1469 * DefineCompositeType
1471 * Create a Composite Type relation.
1472 * `DefineRelation' does all the work, we just provide the correct
1475 * If the relation already exists, then 'DefineRelation' will abort
1478 * DefineCompositeType returns relid for use when creating
1479 * an implicit composite type during function creation
1480 *-------------------------------------------------------------------
1483 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1485 CreateStmt *createStmt = makeNode(CreateStmt);
1487 if (coldeflist == NIL)
1489 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1490 errmsg("composite type must have at least one attribute")));
1493 * now set the parameters for keys/inheritance etc. All of these are
1494 * uninteresting for composite types...
1496 createStmt->relation = (RangeVar *) typevar;
1497 createStmt->tableElts = coldeflist;
1498 createStmt->inhRelations = NIL;
1499 createStmt->constraints = NIL;
1500 createStmt->options = list_make1(defWithOids(false));
1501 createStmt->oncommit = ONCOMMIT_NOOP;
1502 createStmt->tablespacename = NULL;
1505 * finally create the relation...
1507 return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1511 * AlterDomainDefault
1513 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1516 AlterDomainDefault(List *names, Node *defaultRaw)
1524 Node *defaultExpr = NULL; /* NULL if no default specified */
1525 Datum new_record[Natts_pg_type];
1526 bool new_record_nulls[Natts_pg_type];
1527 bool new_record_repl[Natts_pg_type];
1529 Form_pg_type typTup;
1531 /* Make a TypeName so we can use standard type lookup machinery */
1532 typename = makeTypeNameFromNameList(names);
1533 domainoid = typenameTypeId(NULL, typename, NULL);
1535 /* Look up the domain in the type table */
1536 rel = heap_open(TypeRelationId, RowExclusiveLock);
1538 tup = SearchSysCacheCopy(TYPEOID,
1539 ObjectIdGetDatum(domainoid),
1541 if (!HeapTupleIsValid(tup))
1542 elog(ERROR, "cache lookup failed for type %u", domainoid);
1543 typTup = (Form_pg_type) GETSTRUCT(tup);
1545 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1546 checkDomainOwner(tup, typename);
1548 /* Setup new tuple */
1549 MemSet(new_record, (Datum) 0, sizeof(new_record));
1550 MemSet(new_record_nulls, false, sizeof(new_record_nulls));
1551 MemSet(new_record_repl, false, sizeof(new_record_repl));
1553 /* Store the new default into the tuple */
1556 /* Create a dummy ParseState for transformExpr */
1557 pstate = make_parsestate(NULL);
1560 * Cook the colDef->raw_expr into an expression. Note: Name is
1561 * strictly for error message
1563 defaultExpr = cookDefault(pstate, defaultRaw,
1564 typTup->typbasetype,
1566 NameStr(typTup->typname));
1569 * If the expression is just a NULL constant, we treat the command
1570 * like ALTER ... DROP DEFAULT. (But see note for same test in
1573 if (defaultExpr == NULL ||
1574 (IsA(defaultExpr, Const) &&((Const *) defaultExpr)->constisnull))
1576 /* Default is NULL, drop it */
1577 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
1578 new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
1579 new_record_nulls[Anum_pg_type_typdefault - 1] = true;
1580 new_record_repl[Anum_pg_type_typdefault - 1] = true;
1585 * Expression must be stored as a nodeToString result, but we also
1586 * require a valid textual representation (mainly to make life
1587 * easier for pg_dump).
1589 defaultValue = deparse_expression(defaultExpr,
1590 deparse_context_for(NameStr(typTup->typname),
1595 * Form an updated tuple with the new default and write it back.
1597 new_record[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(nodeToString(defaultExpr));
1599 new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
1600 new_record[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultValue);
1601 new_record_repl[Anum_pg_type_typdefault - 1] = true;
1606 /* ALTER ... DROP DEFAULT */
1607 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
1608 new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
1609 new_record_nulls[Anum_pg_type_typdefault - 1] = true;
1610 new_record_repl[Anum_pg_type_typdefault - 1] = true;
1613 newtuple = heap_modify_tuple(tup, RelationGetDescr(rel),
1614 new_record, new_record_nulls,
1617 simple_heap_update(rel, &tup->t_self, newtuple);
1619 CatalogUpdateIndexes(rel, newtuple);
1621 /* Rebuild dependencies */
1622 GenerateTypeDependencies(typTup->typnamespace,
1624 InvalidOid, /* typrelid is n/a */
1625 0, /* relation kind is n/a */
1635 false, /* a domain isn't an implicit array */
1636 typTup->typbasetype,
1638 true); /* Rebuild is true */
1641 heap_close(rel, NoLock);
1642 heap_freetuple(newtuple);
1646 * AlterDomainNotNull
1648 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1651 AlterDomainNotNull(List *names, bool notNull)
1657 Form_pg_type typTup;
1659 /* Make a TypeName so we can use standard type lookup machinery */
1660 typename = makeTypeNameFromNameList(names);
1661 domainoid = typenameTypeId(NULL, typename, NULL);
1663 /* Look up the domain in the type table */
1664 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1666 tup = SearchSysCacheCopy(TYPEOID,
1667 ObjectIdGetDatum(domainoid),
1669 if (!HeapTupleIsValid(tup))
1670 elog(ERROR, "cache lookup failed for type %u", domainoid);
1671 typTup = (Form_pg_type) GETSTRUCT(tup);
1673 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1674 checkDomainOwner(tup, typename);
1676 /* Is the domain already set to the desired constraint? */
1677 if (typTup->typnotnull == notNull)
1679 heap_close(typrel, RowExclusiveLock);
1683 /* Adding a NOT NULL constraint requires checking existing columns */
1689 /* Fetch relation list with attributes based on this domain */
1690 /* ShareLock is sufficient to prevent concurrent data changes */
1692 rels = get_rels_with_domain(domainoid, ShareLock);
1696 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1697 Relation testrel = rtc->rel;
1698 TupleDesc tupdesc = RelationGetDescr(testrel);
1702 /* Scan all tuples in this relation */
1703 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1704 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1708 /* Test attributes that are of the domain */
1709 for (i = 0; i < rtc->natts; i++)
1711 int attnum = rtc->atts[i];
1713 if (heap_attisnull(tuple, attnum))
1715 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1716 errmsg("column \"%s\" of table \"%s\" contains null values",
1717 NameStr(tupdesc->attrs[attnum - 1]->attname),
1718 RelationGetRelationName(testrel))));
1723 /* Close each rel after processing, but keep lock */
1724 heap_close(testrel, NoLock);
1729 * Okay to update pg_type row. We can scribble on typTup because it's a
1732 typTup->typnotnull = notNull;
1734 simple_heap_update(typrel, &tup->t_self, tup);
1736 CatalogUpdateIndexes(typrel, tup);
1739 heap_freetuple(tup);
1740 heap_close(typrel, RowExclusiveLock);
1744 * AlterDomainDropConstraint
1746 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1749 AlterDomainDropConstraint(List *names, const char *constrName,
1750 DropBehavior behavior)
1757 SysScanDesc conscan;
1761 /* Make a TypeName so we can use standard type lookup machinery */
1762 typename = makeTypeNameFromNameList(names);
1763 domainoid = typenameTypeId(NULL, typename, NULL);
1765 /* Look up the domain in the type table */
1766 rel = heap_open(TypeRelationId, RowExclusiveLock);
1768 tup = SearchSysCacheCopy(TYPEOID,
1769 ObjectIdGetDatum(domainoid),
1771 if (!HeapTupleIsValid(tup))
1772 elog(ERROR, "cache lookup failed for type %u", domainoid);
1774 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1775 checkDomainOwner(tup, typename);
1777 /* Grab an appropriate lock on the pg_constraint relation */
1778 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
1780 /* Use the index to scan only constraints of the target relation */
1781 ScanKeyInit(&key[0],
1782 Anum_pg_constraint_contypid,
1783 BTEqualStrategyNumber, F_OIDEQ,
1784 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1786 conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
1787 SnapshotNow, 1, key);
1790 * Scan over the result set, removing any matching entries.
1792 while ((contup = systable_getnext(conscan)) != NULL)
1794 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1796 if (strcmp(NameStr(con->conname), constrName) == 0)
1798 ObjectAddress conobj;
1800 conobj.classId = ConstraintRelationId;
1801 conobj.objectId = HeapTupleGetOid(contup);
1802 conobj.objectSubId = 0;
1804 performDeletion(&conobj, behavior);
1807 /* Clean up after the scan */
1808 systable_endscan(conscan);
1809 heap_close(conrel, RowExclusiveLock);
1811 heap_close(rel, NoLock);
1815 * AlterDomainAddConstraint
1817 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1820 AlterDomainAddConstraint(List *names, Node *newConstraint)
1826 Form_pg_type typTup;
1830 ExprContext *econtext;
1833 ExprState *exprstate;
1836 /* Make a TypeName so we can use standard type lookup machinery */
1837 typename = makeTypeNameFromNameList(names);
1838 domainoid = typenameTypeId(NULL, typename, NULL);
1840 /* Look up the domain in the type table */
1841 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1843 tup = SearchSysCacheCopy(TYPEOID,
1844 ObjectIdGetDatum(domainoid),
1846 if (!HeapTupleIsValid(tup))
1847 elog(ERROR, "cache lookup failed for type %u", domainoid);
1848 typTup = (Form_pg_type) GETSTRUCT(tup);
1850 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1851 checkDomainOwner(tup, typename);
1853 if (!IsA(newConstraint, Constraint))
1854 elog(ERROR, "unrecognized node type: %d",
1855 (int) nodeTag(newConstraint));
1857 constr = (Constraint *) newConstraint;
1859 switch (constr->contype)
1862 /* processed below */
1867 (errcode(ERRCODE_SYNTAX_ERROR),
1868 errmsg("unique constraints not possible for domains")));
1871 case CONSTR_PRIMARY:
1873 (errcode(ERRCODE_SYNTAX_ERROR),
1874 errmsg("primary key constraints not possible for domains")));
1877 case CONSTR_EXCLUSION:
1879 (errcode(ERRCODE_SYNTAX_ERROR),
1880 errmsg("exclusion constraints not possible for domains")));
1883 case CONSTR_FOREIGN:
1885 (errcode(ERRCODE_SYNTAX_ERROR),
1886 errmsg("foreign key constraints not possible for domains")));
1889 case CONSTR_ATTR_DEFERRABLE:
1890 case CONSTR_ATTR_NOT_DEFERRABLE:
1891 case CONSTR_ATTR_DEFERRED:
1892 case CONSTR_ATTR_IMMEDIATE:
1894 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1895 errmsg("specifying constraint deferrability not supported for domains")));
1899 elog(ERROR, "unrecognized constraint subtype: %d",
1900 (int) constr->contype);
1905 * Since all other constraint types throw errors, this must be a check
1906 * constraint. First, process the constraint expression and add an entry
1910 ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1911 typTup->typbasetype, typTup->typtypmod,
1912 constr, NameStr(typTup->typname));
1915 * Test all values stored in the attributes based on the domain the
1916 * constraint is being added to.
1918 expr = (Expr *) stringToNode(ccbin);
1920 /* Need an EState to run ExecEvalExpr */
1921 estate = CreateExecutorState();
1922 econtext = GetPerTupleExprContext(estate);
1924 /* build execution state for expr */
1925 exprstate = ExecPrepareExpr(expr, estate);
1927 /* Fetch relation list with attributes based on this domain */
1928 /* ShareLock is sufficient to prevent concurrent data changes */
1930 rels = get_rels_with_domain(domainoid, ShareLock);
1934 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1935 Relation testrel = rtc->rel;
1936 TupleDesc tupdesc = RelationGetDescr(testrel);
1940 /* Scan all tuples in this relation */
1941 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1942 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1946 /* Test attributes that are of the domain */
1947 for (i = 0; i < rtc->natts; i++)
1949 int attnum = rtc->atts[i];
1954 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1956 econtext->domainValue_datum = d;
1957 econtext->domainValue_isNull = isNull;
1959 conResult = ExecEvalExprSwitchContext(exprstate,
1963 if (!isNull && !DatumGetBool(conResult))
1965 (errcode(ERRCODE_CHECK_VIOLATION),
1966 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
1967 NameStr(tupdesc->attrs[attnum - 1]->attname),
1968 RelationGetRelationName(testrel))));
1971 ResetExprContext(econtext);
1975 /* Hold relation lock till commit (XXX bad for concurrency) */
1976 heap_close(testrel, NoLock);
1979 FreeExecutorState(estate);
1982 heap_close(typrel, RowExclusiveLock);
1986 * get_rels_with_domain
1988 * Fetch all relations / attributes which are using the domain
1990 * The result is a list of RelToCheck structs, one for each distinct
1991 * relation, each containing one or more attribute numbers that are of
1992 * the domain type. We have opened each rel and acquired the specified lock
1995 * We support nested domains by including attributes that are of derived
1996 * domain types. Current callers do not need to distinguish between attributes
1997 * that are of exactly the given domain and those that are of derived domains.
1999 * XXX this is completely broken because there is no way to lock the domain
2000 * to prevent columns from being added or dropped while our command runs.
2001 * We can partially protect against column drops by locking relations as we
2002 * come across them, but there is still a race condition (the window between
2003 * seeing a pg_depend entry and acquiring lock on the relation it references).
2004 * Also, holding locks on all these relations simultaneously creates a non-
2005 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
2006 * risk by using the weakest suitable lock (ShareLock for most callers).
2008 * XXX the API for this is not sufficient to support checking domain values
2009 * that are inside composite types or arrays. Currently we just error out
2010 * if a composite type containing the target domain is stored anywhere.
2011 * There are not currently arrays of domains; if there were, we could take
2012 * the same approach, but it'd be nicer to fix it properly.
2014 * Generally used for retrieving a list of tests when adding
2015 * new constraints to a domain.
2018 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
2023 SysScanDesc depScan;
2026 Assert(lockmode != NoLock);
2029 * We scan pg_depend to find those things that depend on the domain. (We
2030 * assume we can ignore refobjsubid for a domain.)
2032 depRel = heap_open(DependRelationId, AccessShareLock);
2034 ScanKeyInit(&key[0],
2035 Anum_pg_depend_refclassid,
2036 BTEqualStrategyNumber, F_OIDEQ,
2037 ObjectIdGetDatum(TypeRelationId));
2038 ScanKeyInit(&key[1],
2039 Anum_pg_depend_refobjid,
2040 BTEqualStrategyNumber, F_OIDEQ,
2041 ObjectIdGetDatum(domainOid));
2043 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
2044 SnapshotNow, 2, key);
2046 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2048 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2049 RelToCheck *rtc = NULL;
2051 Form_pg_attribute pg_att;
2054 /* Check for directly dependent types --- must be domains */
2055 if (pg_depend->classid == TypeRelationId)
2057 Assert(get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN);
2060 * Recursively add dependent columns to the output list. This is
2061 * a bit inefficient since we may fail to combine RelToCheck
2062 * entries when attributes of the same rel have different derived
2063 * domain types, but it's probably not worth improving.
2065 result = list_concat(result,
2066 get_rels_with_domain(pg_depend->objid,
2071 /* Else, ignore dependees that aren't user columns of relations */
2072 /* (we assume system columns are never of domain types) */
2073 if (pg_depend->classid != RelationRelationId ||
2074 pg_depend->objsubid <= 0)
2077 /* See if we already have an entry for this relation */
2078 foreach(rellist, result)
2080 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
2082 if (RelationGetRelid(rt->rel) == pg_depend->objid)
2091 /* First attribute found for this relation */
2094 /* Acquire requested lock on relation */
2095 rel = relation_open(pg_depend->objid, lockmode);
2098 * Check to see if rowtype is stored anyplace as a composite-type
2099 * column; if so we have to fail, for now anyway.
2101 if (OidIsValid(rel->rd_rel->reltype))
2102 find_composite_type_dependencies(rel->rd_rel->reltype,
2104 format_type_be(domainOid));
2106 /* Otherwise we can ignore views, composite types, etc */
2107 if (rel->rd_rel->relkind != RELKIND_RELATION)
2109 relation_close(rel, lockmode);
2113 /* Build the RelToCheck entry with enough space for all atts */
2114 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
2117 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
2118 result = lcons(rtc, result);
2122 * Confirm column has not been dropped, and is of the expected type.
2123 * This defends against an ALTER DROP COLUMN occuring just before we
2124 * acquired lock ... but if the whole table were dropped, we'd still
2127 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
2129 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
2130 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
2134 * Okay, add column to result. We store the columns in column-number
2135 * order; this is just a hack to improve predictability of regression
2138 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
2141 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
2143 rtc->atts[ptr] = rtc->atts[ptr - 1];
2146 rtc->atts[ptr] = pg_depend->objsubid;
2149 systable_endscan(depScan);
2151 relation_close(depRel, AccessShareLock);
2159 * Check that the type is actually a domain and that the current user
2160 * has permission to do ALTER DOMAIN on it. Throw an error if not.
2163 checkDomainOwner(HeapTuple tup, TypeName *typename)
2165 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
2167 /* Check that this is actually a domain */
2168 if (typTup->typtype != TYPTYPE_DOMAIN)
2170 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2171 errmsg("\"%s\" is not a domain",
2172 TypeNameToString(typename))));
2174 /* Permission check: must own type */
2175 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2176 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2177 format_type_be(HeapTupleGetOid(tup)));
2181 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
2184 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
2185 int typMod, Constraint *constr,
2192 CoerceToDomainValue *domVal;
2195 * Assign or validate constraint name
2197 if (constr->conname)
2199 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
2204 (errcode(ERRCODE_DUPLICATE_OBJECT),
2205 errmsg("constraint \"%s\" for domain \"%s\" already exists",
2206 constr->conname, domainName)));
2209 constr->conname = ChooseConstraintName(domainName,
2216 * Convert the A_EXPR in raw_expr into an EXPR
2218 pstate = make_parsestate(NULL);
2221 * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
2222 * the expression. Note that it will appear to have the type of the base
2223 * type, not the domain. This seems correct since within the check
2224 * expression, we should not assume the input value can be considered a
2225 * member of the domain.
2227 domVal = makeNode(CoerceToDomainValue);
2228 domVal->typeId = baseTypeOid;
2229 domVal->typeMod = typMod;
2230 domVal->location = -1; /* will be set when/if used */
2232 pstate->p_value_substitute = (Node *) domVal;
2234 expr = transformExpr(pstate, constr->raw_expr);
2237 * Make sure it yields a boolean result.
2239 expr = coerce_to_boolean(pstate, expr, "CHECK");
2242 * Make sure no outside relations are referred to.
2244 if (list_length(pstate->p_rtable) != 0)
2246 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2247 errmsg("cannot use table references in domain check constraint")));
2250 * Domains don't allow var clauses (this should be redundant with the
2251 * above check, but make it anyway)
2253 if (contain_var_clause(expr))
2255 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2256 errmsg("cannot use table references in domain check constraint")));
2259 * No subplans or aggregates, either...
2261 if (pstate->p_hasSubLinks)
2263 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2264 errmsg("cannot use subquery in check constraint")));
2265 if (pstate->p_hasAggs)
2267 (errcode(ERRCODE_GROUPING_ERROR),
2268 errmsg("cannot use aggregate function in check constraint")));
2269 if (pstate->p_hasWindowFuncs)
2271 (errcode(ERRCODE_WINDOWING_ERROR),
2272 errmsg("cannot use window function in check constraint")));
2275 * Convert to string form for storage.
2277 ccbin = nodeToString(expr);
2280 * Deparse it to produce text for consrc.
2282 * Since VARNOs aren't allowed in domain constraints, relation context
2283 * isn't required as anything other than a shell.
2285 ccsrc = deparse_expression(expr,
2286 deparse_context_for(domainName,
2291 * Store the constraint in pg_constraint
2293 CreateConstraintEntry(constr->conname, /* Constraint Name */
2294 domainNamespace, /* namespace */
2295 CONSTRAINT_CHECK, /* Constraint Type */
2296 false, /* Is Deferrable */
2297 false, /* Is Deferred */
2298 InvalidOid, /* not a relation constraint */
2301 domainOid, /* domain constraint */
2302 InvalidOid, /* no associated index */
2303 InvalidOid, /* Foreign key fields */
2312 NULL, /* not an exclusion constraint */
2313 expr, /* Tree form of check constraint */
2314 ccbin, /* Binary form of check constraint */
2315 ccsrc, /* Source form of check constraint */
2316 true, /* is local */
2320 * Return the compiled constraint expression so the calling routine can
2321 * perform any additional required tests.
2327 * GetDomainConstraints - get a list of the current constraints of domain
2329 * Returns a possibly-empty list of DomainConstraintState nodes.
2331 * This is called by the executor during plan startup for a CoerceToDomain
2332 * expression node. The given constraints will be checked for each value
2333 * passed through the node.
2335 * We allow this to be called for non-domain types, in which case the result
2339 GetDomainConstraints(Oid typeOid)
2342 bool notNull = false;
2345 conRel = heap_open(ConstraintRelationId, AccessShareLock);
2351 Form_pg_type typTup;
2355 tup = SearchSysCache(TYPEOID,
2356 ObjectIdGetDatum(typeOid),
2358 if (!HeapTupleIsValid(tup))
2359 elog(ERROR, "cache lookup failed for type %u", typeOid);
2360 typTup = (Form_pg_type) GETSTRUCT(tup);
2362 if (typTup->typtype != TYPTYPE_DOMAIN)
2364 /* Not a domain, so done */
2365 ReleaseSysCache(tup);
2369 /* Test for NOT NULL Constraint */
2370 if (typTup->typnotnull)
2373 /* Look for CHECK Constraints on this domain */
2374 ScanKeyInit(&key[0],
2375 Anum_pg_constraint_contypid,
2376 BTEqualStrategyNumber, F_OIDEQ,
2377 ObjectIdGetDatum(typeOid));
2379 scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
2380 SnapshotNow, 1, key);
2382 while (HeapTupleIsValid(conTup = systable_getnext(scan)))
2384 Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
2388 DomainConstraintState *r;
2390 /* Ignore non-CHECK constraints (presently, shouldn't be any) */
2391 if (c->contype != CONSTRAINT_CHECK)
2395 * Not expecting conbin to be NULL, but we'll test for it anyway
2397 val = fastgetattr(conTup, Anum_pg_constraint_conbin,
2398 conRel->rd_att, &isNull);
2400 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
2401 NameStr(typTup->typname), NameStr(c->conname));
2403 check_expr = (Expr *) stringToNode(TextDatumGetCString(val));
2405 /* ExecInitExpr assumes we've planned the expression */
2406 check_expr = expression_planner(check_expr);
2408 r = makeNode(DomainConstraintState);
2409 r->constrainttype = DOM_CONSTRAINT_CHECK;
2410 r->name = pstrdup(NameStr(c->conname));
2411 r->check_expr = ExecInitExpr(check_expr, NULL);
2414 * use lcons() here because constraints of lower domains should be
2417 result = lcons(r, result);
2420 systable_endscan(scan);
2422 /* loop to next domain in stack */
2423 typeOid = typTup->typbasetype;
2424 ReleaseSysCache(tup);
2427 heap_close(conRel, AccessShareLock);
2430 * Only need to add one NOT NULL check regardless of how many domains in
2431 * the stack request it.
2435 DomainConstraintState *r = makeNode(DomainConstraintState);
2437 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
2438 r->name = pstrdup("NOT NULL");
2439 r->check_expr = NULL;
2441 /* lcons to apply the nullness check FIRST */
2442 result = lcons(r, result);
2450 * Execute ALTER TYPE RENAME
2453 RenameType(List *names, const char *newTypeName)
2459 Form_pg_type typTup;
2461 /* Make a TypeName so we can use standard type lookup machinery */
2462 typename = makeTypeNameFromNameList(names);
2463 typeOid = typenameTypeId(NULL, typename, NULL);
2465 /* Look up the type in the type table */
2466 rel = heap_open(TypeRelationId, RowExclusiveLock);
2468 tup = SearchSysCacheCopy(TYPEOID,
2469 ObjectIdGetDatum(typeOid),
2471 if (!HeapTupleIsValid(tup))
2472 elog(ERROR, "cache lookup failed for type %u", typeOid);
2473 typTup = (Form_pg_type) GETSTRUCT(tup);
2475 /* check permissions on type */
2476 if (!pg_type_ownercheck(typeOid, GetUserId()))
2477 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2478 format_type_be(typeOid));
2481 * If it's a composite type, we need to check that it really is a
2482 * free-standing composite type, and not a table's rowtype. We want people
2483 * to use ALTER TABLE not ALTER TYPE for that case.
2485 if (typTup->typtype == TYPTYPE_COMPOSITE &&
2486 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
2488 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2489 errmsg("%s is a table's row type",
2490 format_type_be(typeOid)),
2491 errhint("Use ALTER TABLE instead.")));
2493 /* don't allow direct alteration of array types, either */
2494 if (OidIsValid(typTup->typelem) &&
2495 get_array_type(typTup->typelem) == typeOid)
2497 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2498 errmsg("cannot alter array type %s",
2499 format_type_be(typeOid)),
2500 errhint("You can alter type %s, which will alter the array type as well.",
2501 format_type_be(typTup->typelem))));
2504 * If type is composite we need to rename associated pg_class entry too.
2505 * RenameRelationInternal will call RenameTypeInternal automatically.
2507 if (typTup->typtype == TYPTYPE_COMPOSITE)
2508 RenameRelationInternal(typTup->typrelid, newTypeName,
2509 typTup->typnamespace);
2511 RenameTypeInternal(typeOid, newTypeName,
2512 typTup->typnamespace);
2515 heap_close(rel, RowExclusiveLock);
2519 * Change the owner of a type.
2522 AlterTypeOwner(List *names, Oid newOwnerId)
2529 Form_pg_type typTup;
2530 AclResult aclresult;
2532 rel = heap_open(TypeRelationId, RowExclusiveLock);
2534 /* Make a TypeName so we can use standard type lookup machinery */
2535 typename = makeTypeNameFromNameList(names);
2537 /* Use LookupTypeName here so that shell types can be processed */
2538 tup = LookupTypeName(NULL, typename, NULL);
2541 (errcode(ERRCODE_UNDEFINED_OBJECT),
2542 errmsg("type \"%s\" does not exist",
2543 TypeNameToString(typename))));
2544 typeOid = typeTypeId(tup);
2546 /* Copy the syscache entry so we can scribble on it below */
2547 newtup = heap_copytuple(tup);
2548 ReleaseSysCache(tup);
2550 typTup = (Form_pg_type) GETSTRUCT(tup);
2553 * If it's a composite type, we need to check that it really is a
2554 * free-standing composite type, and not a table's rowtype. We want people
2555 * to use ALTER TABLE not ALTER TYPE for that case.
2557 if (typTup->typtype == TYPTYPE_COMPOSITE &&
2558 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
2560 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2561 errmsg("%s is a table's row type",
2562 format_type_be(typeOid)),
2563 errhint("Use ALTER TABLE instead.")));
2565 /* don't allow direct alteration of array types, either */
2566 if (OidIsValid(typTup->typelem) &&
2567 get_array_type(typTup->typelem) == typeOid)
2569 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2570 errmsg("cannot alter array type %s",
2571 format_type_be(typeOid)),
2572 errhint("You can alter type %s, which will alter the array type as well.",
2573 format_type_be(typTup->typelem))));
2576 * If the new owner is the same as the existing owner, consider the
2577 * command to have succeeded. This is for dump restoration purposes.
2579 if (typTup->typowner != newOwnerId)
2581 /* Superusers can always do it */
2584 /* Otherwise, must be owner of the existing object */
2585 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2586 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2587 format_type_be(HeapTupleGetOid(tup)));
2589 /* Must be able to become new owner */
2590 check_is_member_of_role(GetUserId(), newOwnerId);
2592 /* New owner must have CREATE privilege on namespace */
2593 aclresult = pg_namespace_aclcheck(typTup->typnamespace,
2596 if (aclresult != ACLCHECK_OK)
2597 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
2598 get_namespace_name(typTup->typnamespace));
2602 * If it's a composite type, invoke ATExecChangeOwner so that we fix
2603 * up the pg_class entry properly. That will call back to
2604 * AlterTypeOwnerInternal to take care of the pg_type entry(s).
2606 if (typTup->typtype == TYPTYPE_COMPOSITE)
2607 ATExecChangeOwner(typTup->typrelid, newOwnerId, true);
2611 * We can just apply the modification directly.
2613 * okay to scribble on typTup because it's a copy
2615 typTup->typowner = newOwnerId;
2617 simple_heap_update(rel, &tup->t_self, tup);
2619 CatalogUpdateIndexes(rel, tup);
2621 /* Update owner dependency reference */
2622 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2624 /* If it has an array type, update that too */
2625 if (OidIsValid(typTup->typarray))
2626 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2631 heap_close(rel, RowExclusiveLock);
2635 * AlterTypeOwnerInternal - change type owner unconditionally
2637 * This is currently only used to propagate ALTER TABLE/TYPE OWNER to a
2638 * table's rowtype or an array type, and to implement REASSIGN OWNED BY.
2639 * It assumes the caller has done all needed checks. The function will
2640 * automatically recurse to an array type if the type has one.
2642 * hasDependEntry should be TRUE if type is expected to have a pg_shdepend
2643 * entry (ie, it's not a table rowtype nor an array type).
2646 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
2647 bool hasDependEntry)
2651 Form_pg_type typTup;
2653 rel = heap_open(TypeRelationId, RowExclusiveLock);
2655 tup = SearchSysCacheCopy(TYPEOID,
2656 ObjectIdGetDatum(typeOid),
2658 if (!HeapTupleIsValid(tup))
2659 elog(ERROR, "cache lookup failed for type %u", typeOid);
2660 typTup = (Form_pg_type) GETSTRUCT(tup);
2663 * Modify the owner --- okay to scribble on typTup because it's a copy
2665 typTup->typowner = newOwnerId;
2667 simple_heap_update(rel, &tup->t_self, tup);
2669 CatalogUpdateIndexes(rel, tup);
2671 /* Update owner dependency reference, if it has one */
2673 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2675 /* If it has an array type, update that too */
2676 if (OidIsValid(typTup->typarray))
2677 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2680 heap_close(rel, RowExclusiveLock);
2684 * Execute ALTER TYPE SET SCHEMA
2687 AlterTypeNamespace(List *names, const char *newschema)
2694 /* Make a TypeName so we can use standard type lookup machinery */
2695 typename = makeTypeNameFromNameList(names);
2696 typeOid = typenameTypeId(NULL, typename, NULL);
2698 /* check permissions on type */
2699 if (!pg_type_ownercheck(typeOid, GetUserId()))
2700 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2701 format_type_be(typeOid));
2703 /* get schema OID and check its permissions */
2704 nspOid = LookupCreationNamespace(newschema);
2706 /* don't allow direct alteration of array types */
2707 elemOid = get_element_type(typeOid);
2708 if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
2710 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2711 errmsg("cannot alter array type %s",
2712 format_type_be(typeOid)),
2713 errhint("You can alter type %s, which will alter the array type as well.",
2714 format_type_be(elemOid))));
2716 /* and do the work */
2717 AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
2721 * Move specified type to new namespace.
2723 * Caller must have already checked privileges.
2725 * The function automatically recurses to process the type's array type,
2726 * if any. isImplicitArray should be TRUE only when doing this internal
2727 * recursion (outside callers must never try to move an array type directly).
2729 * If errorOnTableType is TRUE, the function errors out if the type is
2730 * a table type. ALTER TABLE has to be used to move a table to a new
2734 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
2735 bool isImplicitArray,
2736 bool errorOnTableType)
2740 Form_pg_type typform;
2743 bool isCompositeType;
2745 rel = heap_open(TypeRelationId, RowExclusiveLock);
2747 tup = SearchSysCacheCopy(TYPEOID,
2748 ObjectIdGetDatum(typeOid),
2750 if (!HeapTupleIsValid(tup))
2751 elog(ERROR, "cache lookup failed for type %u", typeOid);
2752 typform = (Form_pg_type) GETSTRUCT(tup);
2754 oldNspOid = typform->typnamespace;
2755 arrayOid = typform->typarray;
2757 if (oldNspOid == nspOid)
2759 (errcode(ERRCODE_DUPLICATE_OBJECT),
2760 errmsg("type %s is already in schema \"%s\"",
2761 format_type_be(typeOid),
2762 get_namespace_name(nspOid))));
2764 /* disallow renaming into or out of temp schemas */
2765 if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
2767 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2768 errmsg("cannot move objects into or out of temporary schemas")));
2770 /* same for TOAST schema */
2771 if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
2773 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2774 errmsg("cannot move objects into or out of TOAST schema")));
2776 /* check for duplicate name (more friendly than unique-index failure) */
2777 if (SearchSysCacheExists(TYPENAMENSP,
2778 CStringGetDatum(NameStr(typform->typname)),
2779 ObjectIdGetDatum(nspOid),
2782 (errcode(ERRCODE_DUPLICATE_OBJECT),
2783 errmsg("type \"%s\" already exists in schema \"%s\"",
2784 NameStr(typform->typname),
2785 get_namespace_name(nspOid))));
2787 /* Detect whether type is a composite type (but not a table rowtype) */
2789 (typform->typtype == TYPTYPE_COMPOSITE &&
2790 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
2792 /* Enforce not-table-type if requested */
2793 if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
2796 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2797 errmsg("%s is a table's row type",
2798 format_type_be(typeOid)),
2799 errhint("Use ALTER TABLE instead.")));
2801 /* OK, modify the pg_type row */
2803 /* tup is a copy, so we can scribble directly on it */
2804 typform->typnamespace = nspOid;
2806 simple_heap_update(rel, &tup->t_self, tup);
2807 CatalogUpdateIndexes(rel, tup);
2810 * Composite types have pg_class entries.
2812 * We need to modify the pg_class tuple as well to reflect the change of
2815 if (isCompositeType)
2819 classRel = heap_open(RelationRelationId, RowExclusiveLock);
2821 AlterRelationNamespaceInternal(classRel, typform->typrelid,
2825 heap_close(classRel, RowExclusiveLock);
2828 * Check for constraints associated with the composite type (we don't
2829 * currently support this, but probably will someday).
2831 AlterConstraintNamespaces(typform->typrelid, oldNspOid,
2836 /* If it's a domain, it might have constraints */
2837 if (typform->typtype == TYPTYPE_DOMAIN)
2838 AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
2842 * Update dependency on schema, if any --- a table rowtype has not got
2843 * one, and neither does an implicit array.
2845 if ((isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
2847 if (changeDependencyFor(TypeRelationId, typeOid,
2848 NamespaceRelationId, oldNspOid, nspOid) != 1)
2849 elog(ERROR, "failed to change schema dependency for type %s",
2850 format_type_be(typeOid));
2852 heap_freetuple(tup);
2854 heap_close(rel, RowExclusiveLock);
2856 /* Recursively alter the associated array type, if any */
2857 if (OidIsValid(arrayOid))
2858 AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);