1 /*-------------------------------------------------------------------------
4 * Routines for SQL commands that manipulate types (and domains).
6 * Portions Copyright (c) 1996-2007, 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.106 2007/06/20 18:15:49 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 "commands/defrem.h"
47 #include "commands/tablecmds.h"
48 #include "commands/typecmds.h"
49 #include "executor/executor.h"
50 #include "miscadmin.h"
51 #include "nodes/makefuncs.h"
52 #include "optimizer/planmain.h"
53 #include "optimizer/var.h"
54 #include "parser/parse_coerce.h"
55 #include "parser/parse_expr.h"
56 #include "parser/parse_func.h"
57 #include "parser/parse_type.h"
58 #include "utils/acl.h"
59 #include "utils/builtins.h"
60 #include "utils/fmgroids.h"
61 #include "utils/lsyscache.h"
62 #include "utils/memutils.h"
63 #include "utils/syscache.h"
66 /* result structure for get_rels_with_domain() */
69 Relation rel; /* opened and locked relation */
70 int natts; /* number of attributes of interest */
71 int *atts; /* attribute numbers */
72 /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
76 static Oid findTypeInputFunction(List *procname, Oid typeOid);
77 static Oid findTypeOutputFunction(List *procname, Oid typeOid);
78 static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
79 static Oid findTypeSendFunction(List *procname, Oid typeOid);
80 static Oid findTypeTypmodinFunction(List *procname);
81 static Oid findTypeTypmodoutFunction(List *procname);
82 static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
83 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
84 static void checkDomainOwner(HeapTuple tup, TypeName *typename);
85 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
87 int typMod, Constraint *constr,
93 * Registers a new type.
96 DefineType(List *names, List *parameters)
101 int16 internalLength = -1; /* default: variable-length */
102 Oid elemType = InvalidOid;
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 *defaultValue = NULL;
111 bool byValue = false;
112 char delimiter = DEFAULT_TYPDELIM;
113 char alignment = 'i'; /* default alignment */
114 char storage = 'p'; /* default TOAST storage method */
117 Oid receiveOid = InvalidOid;
118 Oid sendOid = InvalidOid;
119 Oid typmodinOid = InvalidOid;
120 Oid typmodoutOid = InvalidOid;
121 Oid analyzeOid = InvalidOid;
129 /* Convert list of names to a name and namespace */
130 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
132 /* Check we have creation rights in target namespace */
133 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
134 if (aclresult != ACLCHECK_OK)
135 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
136 get_namespace_name(typeNamespace));
139 * Look to see if type already exists (presumably as a shell; if not,
140 * TypeCreate will complain).
142 typoid = GetSysCacheOid(TYPENAMENSP,
143 CStringGetDatum(typeName),
144 ObjectIdGetDatum(typeNamespace),
148 * If it's not a shell, see if it's an autogenerated array type,
149 * and if so rename it out of the way.
151 if (OidIsValid(typoid) && get_typisdefined(typoid))
153 if (moveArrayTypeName(typoid, typeName, typeNamespace))
158 * If it doesn't exist, create it as a shell, so that the OID is known
159 * for use in the I/O function definitions.
161 if (!OidIsValid(typoid))
163 typoid = TypeShellMake(typeName, typeNamespace);
164 /* Make new shell type visible for modification below */
165 CommandCounterIncrement();
168 * If the command was a parameterless CREATE TYPE, we're done ---
169 * creating the shell type was all we're supposed to do.
171 if (parameters == NIL)
176 /* Complain if dummy CREATE TYPE and entry already exists */
177 if (parameters == NIL)
179 (errcode(ERRCODE_DUPLICATE_OBJECT),
180 errmsg("type \"%s\" already exists", typeName)));
183 foreach(pl, parameters)
185 DefElem *defel = (DefElem *) lfirst(pl);
187 if (pg_strcasecmp(defel->defname, "internallength") == 0)
188 internalLength = defGetTypeLength(defel);
189 else if (pg_strcasecmp(defel->defname, "externallength") == 0)
190 ; /* ignored -- remove after 7.3 */
191 else if (pg_strcasecmp(defel->defname, "input") == 0)
192 inputName = defGetQualifiedName(defel);
193 else if (pg_strcasecmp(defel->defname, "output") == 0)
194 outputName = defGetQualifiedName(defel);
195 else if (pg_strcasecmp(defel->defname, "receive") == 0)
196 receiveName = defGetQualifiedName(defel);
197 else if (pg_strcasecmp(defel->defname, "send") == 0)
198 sendName = defGetQualifiedName(defel);
199 else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
200 typmodinName = defGetQualifiedName(defel);
201 else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
202 typmodoutName = defGetQualifiedName(defel);
203 else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
204 pg_strcasecmp(defel->defname, "analyse") == 0)
205 analyzeName = defGetQualifiedName(defel);
206 else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
208 char *p = defGetString(defel);
212 else if (pg_strcasecmp(defel->defname, "element") == 0)
214 elemType = typenameTypeId(NULL, defGetTypeName(defel));
215 /* disallow arrays of pseudotypes */
216 if (get_typtype(elemType) == TYPTYPE_PSEUDO)
218 (errcode(ERRCODE_DATATYPE_MISMATCH),
219 errmsg("array element type cannot be %s",
220 format_type_be(elemType))));
222 else if (pg_strcasecmp(defel->defname, "default") == 0)
223 defaultValue = defGetString(defel);
224 else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
225 byValue = defGetBoolean(defel);
226 else if (pg_strcasecmp(defel->defname, "alignment") == 0)
228 char *a = defGetString(defel);
231 * Note: if argument was an unquoted identifier, parser will have
232 * applied translations to it, so be prepared to recognize
233 * translated type names as well as the nominal form.
235 if (pg_strcasecmp(a, "double") == 0 ||
236 pg_strcasecmp(a, "float8") == 0 ||
237 pg_strcasecmp(a, "pg_catalog.float8") == 0)
239 else if (pg_strcasecmp(a, "int4") == 0 ||
240 pg_strcasecmp(a, "pg_catalog.int4") == 0)
242 else if (pg_strcasecmp(a, "int2") == 0 ||
243 pg_strcasecmp(a, "pg_catalog.int2") == 0)
245 else if (pg_strcasecmp(a, "char") == 0 ||
246 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
250 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
251 errmsg("alignment \"%s\" not recognized", a)));
253 else if (pg_strcasecmp(defel->defname, "storage") == 0)
255 char *a = defGetString(defel);
257 if (pg_strcasecmp(a, "plain") == 0)
259 else if (pg_strcasecmp(a, "external") == 0)
261 else if (pg_strcasecmp(a, "extended") == 0)
263 else if (pg_strcasecmp(a, "main") == 0)
267 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
268 errmsg("storage \"%s\" not recognized", a)));
272 (errcode(ERRCODE_SYNTAX_ERROR),
273 errmsg("type attribute \"%s\" not recognized",
278 * make sure we have our required definitions
280 if (inputName == NIL)
282 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
283 errmsg("type input function must be specified")));
284 if (outputName == NIL)
286 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
287 errmsg("type output function must be specified")));
289 if (typmodinName == NIL && typmodoutName != NIL)
291 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
292 errmsg("type modifier output function is useless without a type modifier input function")));
295 * Convert I/O proc names to OIDs
297 inputOid = findTypeInputFunction(inputName, typoid);
298 outputOid = findTypeOutputFunction(outputName, typoid);
300 receiveOid = findTypeReceiveFunction(receiveName, typoid);
302 sendOid = findTypeSendFunction(sendName, typoid);
305 * Verify that I/O procs return the expected thing. If we see OPAQUE,
306 * complain and change it to the correct type-safe choice.
308 resulttype = get_func_rettype(inputOid);
309 if (resulttype != typoid)
311 if (resulttype == OPAQUEOID)
313 /* backwards-compatibility hack */
315 (errmsg("changing return type of function %s from \"opaque\" to %s",
316 NameListToString(inputName), typeName)));
317 SetFunctionReturnType(inputOid, typoid);
321 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
322 errmsg("type input function %s must return type %s",
323 NameListToString(inputName), typeName)));
325 resulttype = get_func_rettype(outputOid);
326 if (resulttype != CSTRINGOID)
328 if (resulttype == OPAQUEOID)
330 /* backwards-compatibility hack */
332 (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
333 NameListToString(outputName))));
334 SetFunctionReturnType(outputOid, CSTRINGOID);
338 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
339 errmsg("type output function %s must return type \"cstring\"",
340 NameListToString(outputName))));
344 resulttype = get_func_rettype(receiveOid);
345 if (resulttype != typoid)
347 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
348 errmsg("type receive function %s must return type %s",
349 NameListToString(receiveName), typeName)));
353 resulttype = get_func_rettype(sendOid);
354 if (resulttype != BYTEAOID)
356 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
357 errmsg("type send function %s must return type \"bytea\"",
358 NameListToString(sendName))));
362 * Convert typmodin/out function proc names to OIDs.
365 typmodinOid = findTypeTypmodinFunction(typmodinName);
367 typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
370 * Convert analysis function proc name to an OID. If no analysis function
371 * is specified, we'll use zero to select the built-in default algorithm.
374 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
377 * Check permissions on functions. We choose to require the creator/owner
378 * of a type to also own the underlying functions. Since creating a type
379 * is tantamount to granting public execute access on the functions, the
380 * minimum sane check would be for execute-with-grant-option. But we
381 * don't have a way to make the type go away if the grant option is
382 * revoked, so ownership seems better.
384 if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
385 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
386 NameListToString(inputName));
387 if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
388 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
389 NameListToString(outputName));
390 if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
391 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
392 NameListToString(receiveName));
393 if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
394 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
395 NameListToString(sendName));
396 if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
397 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
398 NameListToString(typmodinName));
399 if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
400 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
401 NameListToString(typmodoutName));
402 if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
403 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
404 NameListToString(analyzeName));
406 /* Preassign array type OID so we can insert it in pg_type.typarray */
407 pg_type = heap_open(TypeRelationId, AccessShareLock);
408 array_oid = GetNewOid(pg_type);
409 heap_close(pg_type, AccessShareLock);
412 * now have TypeCreate do all the real work.
415 TypeCreate(InvalidOid, /* no predetermined type OID */
416 typeName, /* type name */
417 typeNamespace, /* namespace */
418 InvalidOid, /* relation oid (n/a here) */
419 0, /* relation kind (ditto) */
420 internalLength, /* internal size */
421 TYPTYPE_BASE, /* type-type (base type) */
422 delimiter, /* array element delimiter */
423 inputOid, /* input procedure */
424 outputOid, /* output procedure */
425 receiveOid, /* receive procedure */
426 sendOid, /* send procedure */
427 typmodinOid, /* typmodin procedure */
428 typmodoutOid,/* typmodout procedure */
429 analyzeOid, /* analyze procedure */
430 elemType, /* element type ID */
431 false, /* this is not an array type */
432 array_oid, /* array type we are about to create */
433 InvalidOid, /* base type ID (only for domains) */
434 defaultValue, /* default type value */
435 NULL, /* no binary form available */
436 byValue, /* passed by value */
437 alignment, /* required alignment */
438 storage, /* TOAST strategy */
439 -1, /* typMod (Domains only) */
440 0, /* Array Dimensions of typbasetype */
441 false); /* Type NOT NULL */
444 * Create the array type that goes with it.
446 array_type = makeArrayTypeName(typeName, typeNamespace);
448 /* alignment must be 'i' or 'd' for arrays */
449 alignment = (alignment == 'd') ? 'd' : 'i';
451 TypeCreate(array_oid, /* force assignment of this type OID */
452 array_type, /* type name */
453 typeNamespace, /* namespace */
454 InvalidOid, /* relation oid (n/a here) */
455 0, /* relation kind (ditto) */
456 -1, /* internal size (always varlena) */
457 TYPTYPE_BASE, /* type-type (base type) */
458 DEFAULT_TYPDELIM, /* array element delimiter */
459 F_ARRAY_IN, /* input procedure */
460 F_ARRAY_OUT, /* output procedure */
461 F_ARRAY_RECV, /* receive procedure */
462 F_ARRAY_SEND, /* send procedure */
463 typmodinOid, /* typmodin procedure */
464 typmodoutOid, /* typmodout procedure */
465 InvalidOid, /* analyze procedure - default */
466 typoid, /* element type ID */
467 true, /* yes this is an array type */
468 InvalidOid, /* no further array type */
469 InvalidOid, /* base type ID */
470 NULL, /* never a default type value */
471 NULL, /* binary default isn't sent either */
472 false, /* never passed by value */
473 alignment, /* see above */
474 'x', /* ARRAY is always toastable */
475 -1, /* typMod (Domains only) */
476 0, /* Array dimensions of typbasetype */
477 false); /* Type NOT NULL */
485 * Removes a datatype.
488 RemoveType(List *names, DropBehavior behavior, bool missing_ok)
493 ObjectAddress object;
496 /* Make a TypeName so we can use standard type lookup machinery */
497 typename = makeTypeNameFromNameList(names);
499 /* Use LookupTypeName here so that shell types can be removed. */
500 typeoid = LookupTypeName(NULL, typename);
501 if (!OidIsValid(typeoid))
506 (errcode(ERRCODE_UNDEFINED_OBJECT),
507 errmsg("type \"%s\" does not exist",
508 TypeNameToString(typename))));
513 (errmsg("type \"%s\" does not exist, skipping",
514 TypeNameToString(typename))));
520 tup = SearchSysCache(TYPEOID,
521 ObjectIdGetDatum(typeoid),
523 if (!HeapTupleIsValid(tup))
524 elog(ERROR, "cache lookup failed for type %u", typeoid);
525 typ = (Form_pg_type) GETSTRUCT(tup);
527 /* Permission check: must own type or its namespace */
528 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
529 !pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
530 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
531 TypeNameToString(typename));
534 * Note: we need no special check for array types here, as the normal
535 * treatment of internal dependencies handles it just fine
538 ReleaseSysCache(tup);
543 object.classId = TypeRelationId;
544 object.objectId = typeoid;
545 object.objectSubId = 0;
547 performDeletion(&object, behavior);
552 * Guts of type deletion.
555 RemoveTypeById(Oid typeOid)
560 relation = heap_open(TypeRelationId, RowExclusiveLock);
562 tup = SearchSysCache(TYPEOID,
563 ObjectIdGetDatum(typeOid),
565 if (!HeapTupleIsValid(tup))
566 elog(ERROR, "cache lookup failed for type %u", typeOid);
568 simple_heap_delete(relation, &tup->t_self);
571 * If it is an enum, delete the pg_enum entries too; we don't bother
572 * with making dependency entries for those, so it has to be done
575 if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
576 EnumValuesDelete(typeOid);
578 ReleaseSysCache(tup);
580 heap_close(relation, RowExclusiveLock);
586 * Registers a new domain.
589 DefineDomain(CreateDomainStmt *stmt)
594 int16 internalLength;
597 Oid receiveProcedure;
599 Oid analyzeProcedure;
608 char *defaultValue = NULL;
609 char *defaultValueBin = NULL;
610 bool saw_default = false;
611 bool typNotNull = false;
612 bool nullDefined = false;
613 int32 typNDims = list_length(stmt->typename->arrayBounds);
615 List *schema = stmt->constraints;
620 Form_pg_type baseType;
623 /* Convert list of names to a name and namespace */
624 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
627 /* Check we have creation rights in target namespace */
628 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
630 if (aclresult != ACLCHECK_OK)
631 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
632 get_namespace_name(domainNamespace));
635 * Check for collision with an existing type name. If there is one and
636 * it's an autogenerated array, we can rename it out of the way.
638 old_type_oid = GetSysCacheOid(TYPENAMENSP,
639 CStringGetDatum(domainName),
640 ObjectIdGetDatum(domainNamespace),
642 if (OidIsValid(old_type_oid))
644 if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
646 (errcode(ERRCODE_DUPLICATE_OBJECT),
647 errmsg("type \"%s\" already exists", domainName)));
651 * Look up the base type.
653 typeTup = typenameType(NULL, stmt->typename);
654 baseType = (Form_pg_type) GETSTRUCT(typeTup);
655 basetypeoid = HeapTupleGetOid(typeTup);
656 basetypeMod = typenameTypeMod(NULL, stmt->typename, basetypeoid);
659 * Base type must be a plain base type, another domain or an enum.
660 * Domains over pseudotypes would create a security hole. Domains
661 * over composite types might be made to work in the future, but not
664 typtype = baseType->typtype;
665 if (typtype != TYPTYPE_BASE &&
666 typtype != TYPTYPE_DOMAIN &&
667 typtype != TYPTYPE_ENUM)
669 (errcode(ERRCODE_DATATYPE_MISMATCH),
670 errmsg("\"%s\" is not a valid base type for a domain",
671 TypeNameToString(stmt->typename))));
673 /* passed by value */
674 byValue = baseType->typbyval;
676 /* Required Alignment */
677 alignment = baseType->typalign;
680 storage = baseType->typstorage;
683 internalLength = baseType->typlen;
685 /* Array element type (in case base type is an array) */
686 typelem = baseType->typelem;
688 /* Array element Delimiter */
689 delimiter = baseType->typdelim;
692 inputProcedure = F_DOMAIN_IN;
693 outputProcedure = baseType->typoutput;
694 receiveProcedure = F_DOMAIN_RECV;
695 sendProcedure = baseType->typsend;
697 /* Domains never accept typmods, so no typmodin/typmodout needed */
699 /* Analysis function */
700 analyzeProcedure = baseType->typanalyze;
702 /* Inherited default value */
703 datum = SysCacheGetAttr(TYPEOID, typeTup,
704 Anum_pg_type_typdefault, &isnull);
706 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
708 /* Inherited default binary value */
709 datum = SysCacheGetAttr(TYPEOID, typeTup,
710 Anum_pg_type_typdefaultbin, &isnull);
712 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
715 * Run through constraints manually to avoid the additional processing
716 * conducted by DefineRelation() and friends.
718 foreach(listptr, schema)
720 Node *newConstraint = lfirst(listptr);
723 /* Check for unsupported constraint types */
724 if (IsA(newConstraint, FkConstraint))
726 (errcode(ERRCODE_SYNTAX_ERROR),
727 errmsg("foreign key constraints not possible for domains")));
729 /* otherwise it should be a plain Constraint */
730 if (!IsA(newConstraint, Constraint))
731 elog(ERROR, "unrecognized node type: %d",
732 (int) nodeTag(newConstraint));
734 constr = (Constraint *) newConstraint;
736 switch (constr->contype)
741 * The inherited default value may be overridden by the user
742 * with the DEFAULT <expr> clause ... but only once.
746 (errcode(ERRCODE_SYNTAX_ERROR),
747 errmsg("multiple default expressions")));
750 if (constr->raw_expr)
755 /* Create a dummy ParseState for transformExpr */
756 pstate = make_parsestate(NULL);
759 * Cook the constr->raw_expr into an expression.
760 * Note: name is strictly for error message
762 defaultExpr = cookDefault(pstate, constr->raw_expr,
768 * Expression must be stored as a nodeToString result, but
769 * we also require a valid textual representation (mainly
770 * to make life easier for pg_dump).
773 deparse_expression(defaultExpr,
774 deparse_context_for(domainName,
777 defaultValueBin = nodeToString(defaultExpr);
781 /* DEFAULT NULL is same as not having a default */
783 defaultValueBin = NULL;
788 if (nullDefined && !typNotNull)
790 (errcode(ERRCODE_SYNTAX_ERROR),
791 errmsg("conflicting NULL/NOT NULL constraints")));
797 if (nullDefined && typNotNull)
799 (errcode(ERRCODE_SYNTAX_ERROR),
800 errmsg("conflicting NULL/NOT NULL constraints")));
808 * Check constraints are handled after domain creation, as
809 * they require the Oid of the domain
814 * All else are error cases
818 (errcode(ERRCODE_SYNTAX_ERROR),
819 errmsg("unique constraints not possible for domains")));
824 (errcode(ERRCODE_SYNTAX_ERROR),
825 errmsg("primary key constraints not possible for domains")));
828 case CONSTR_ATTR_DEFERRABLE:
829 case CONSTR_ATTR_NOT_DEFERRABLE:
830 case CONSTR_ATTR_DEFERRED:
831 case CONSTR_ATTR_IMMEDIATE:
833 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
834 errmsg("specifying constraint deferrability not supported for domains")));
838 elog(ERROR, "unrecognized constraint subtype: %d",
839 (int) constr->contype);
845 * Have TypeCreate do all the real work.
848 TypeCreate(InvalidOid, /* no predetermined type OID */
849 domainName, /* type name */
850 domainNamespace, /* namespace */
851 InvalidOid, /* relation oid (n/a here) */
852 0, /* relation kind (ditto) */
853 internalLength, /* internal size */
854 TYPTYPE_DOMAIN, /* type-type (domain type) */
855 delimiter, /* array element delimiter */
856 inputProcedure, /* input procedure */
857 outputProcedure, /* output procedure */
858 receiveProcedure, /* receive procedure */
859 sendProcedure, /* send procedure */
860 InvalidOid, /* typmodin procedure - none */
861 InvalidOid, /* typmodout procedure - none */
862 analyzeProcedure, /* analyze procedure */
863 typelem, /* element type ID */
864 false, /* this isn't an array */
865 InvalidOid, /* no arrays for domains (yet) */
866 basetypeoid, /* base type ID */
867 defaultValue, /* default type value (text) */
868 defaultValueBin, /* default type value (binary) */
869 byValue, /* passed by value */
870 alignment, /* required alignment */
871 storage, /* TOAST strategy */
872 basetypeMod, /* typeMod value */
873 typNDims, /* Array dimensions for base type */
874 typNotNull); /* Type NOT NULL */
877 * Process constraints which refer to the domain ID returned by TypeCreate
879 foreach(listptr, schema)
881 Constraint *constr = lfirst(listptr);
883 /* it must be a Constraint, per check above */
885 switch (constr->contype)
888 domainAddConstraint(domainoid, domainNamespace,
889 basetypeoid, basetypeMod,
893 /* Other constraint types were fully processed above */
899 /* CCI so we can detect duplicate constraint names */
900 CommandCounterIncrement();
904 * Now we can clean up.
906 ReleaseSysCache(typeTup);
914 * This is identical to RemoveType except we insist it be a domain.
917 RemoveDomain(List *names, DropBehavior behavior, bool missing_ok)
923 ObjectAddress object;
925 /* Make a TypeName so we can use standard type lookup machinery */
926 typename = makeTypeNameFromNameList(names);
928 /* Use LookupTypeName here so that shell types can be removed. */
929 typeoid = LookupTypeName(NULL, typename);
930 if (!OidIsValid(typeoid))
935 (errcode(ERRCODE_UNDEFINED_OBJECT),
936 errmsg("type \"%s\" does not exist",
937 TypeNameToString(typename))));
942 (errmsg("type \"%s\" does not exist, skipping",
943 TypeNameToString(typename))));
949 tup = SearchSysCache(TYPEOID,
950 ObjectIdGetDatum(typeoid),
952 if (!HeapTupleIsValid(tup))
953 elog(ERROR, "cache lookup failed for type %u", typeoid);
955 /* Permission check: must own type or its namespace */
956 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
957 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
959 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
960 TypeNameToString(typename));
962 /* Check that this is actually a domain */
963 typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
965 if (typtype != TYPTYPE_DOMAIN)
967 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
968 errmsg("\"%s\" is not a domain",
969 TypeNameToString(typename))));
971 ReleaseSysCache(tup);
976 object.classId = TypeRelationId;
977 object.objectId = typeoid;
978 object.objectSubId = 0;
980 performDeletion(&object, behavior);
985 * Registers a new enum.
988 DefineEnum(CreateEnumStmt *stmt)
999 /* Convert list of names to a name and namespace */
1000 enumNamespace = QualifiedNameGetCreationNamespace(stmt->typename,
1003 /* Check we have creation rights in target namespace */
1004 aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
1005 if (aclresult != ACLCHECK_OK)
1006 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1007 get_namespace_name(enumNamespace));
1010 * Check for collision with an existing type name. If there is one and
1011 * it's an autogenerated array, we can rename it out of the way.
1013 old_type_oid = GetSysCacheOid(TYPENAMENSP,
1014 CStringGetDatum(enumName),
1015 ObjectIdGetDatum(enumNamespace),
1017 if (OidIsValid(old_type_oid))
1019 if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
1021 (errcode(ERRCODE_DUPLICATE_OBJECT),
1022 errmsg("type \"%s\" already exists", enumName)));
1025 /* Preassign array type OID so we can insert it in pg_type.typarray */
1026 pg_type = heap_open(TypeRelationId, AccessShareLock);
1027 enumArrayOid = GetNewOid(pg_type);
1028 heap_close(pg_type, AccessShareLock);
1030 /* Create the pg_type entry */
1032 TypeCreate(InvalidOid, /* no predetermined type OID */
1033 enumName, /* type name */
1034 enumNamespace, /* namespace */
1035 InvalidOid, /* relation oid (n/a here) */
1036 0, /* relation kind (ditto) */
1037 sizeof(Oid), /* internal size */
1038 TYPTYPE_ENUM, /* type-type (enum type) */
1039 DEFAULT_TYPDELIM, /* array element delimiter */
1040 F_ENUM_IN, /* input procedure */
1041 F_ENUM_OUT, /* output procedure */
1042 InvalidOid, /* receive procedure - none */
1043 InvalidOid, /* send procedure - none */
1044 InvalidOid, /* typmodin procedure - none */
1045 InvalidOid, /* typmodout procedure - none */
1046 InvalidOid, /* analyze procedure - default */
1047 InvalidOid, /* element type ID */
1048 false, /* this is not an array type */
1049 enumArrayOid, /* array type we are about to create */
1050 InvalidOid, /* base type ID (only for domains) */
1051 NULL, /* never a default type value */
1052 NULL, /* binary default isn't sent either */
1053 true, /* always passed by value */
1054 'i', /* int alignment */
1055 'p', /* TOAST strategy always plain */
1056 -1, /* typMod (Domains only) */
1057 0, /* Array dimensions of typbasetype */
1058 false); /* Type NOT NULL */
1060 /* Enter the enum's values into pg_enum */
1061 EnumValuesCreate(enumTypeOid, stmt->vals);
1064 * Create the array type that goes with it.
1066 enumArrayName = makeArrayTypeName(enumName, enumNamespace);
1068 TypeCreate(enumArrayOid, /* force assignment of this type OID */
1069 enumArrayName, /* type name */
1070 enumNamespace, /* namespace */
1071 InvalidOid, /* relation oid (n/a here) */
1072 0, /* relation kind (ditto) */
1073 -1, /* internal size (always varlena) */
1074 TYPTYPE_BASE, /* type-type (base type) */
1075 DEFAULT_TYPDELIM, /* array element delimiter */
1076 F_ARRAY_IN, /* input procedure */
1077 F_ARRAY_OUT, /* output procedure */
1078 F_ARRAY_RECV, /* receive procedure */
1079 F_ARRAY_SEND, /* send procedure */
1080 InvalidOid, /* typmodin procedure - none */
1081 InvalidOid, /* typmodout procedure - none */
1082 InvalidOid, /* analyze procedure - default */
1083 enumTypeOid, /* element type ID */
1084 true, /* yes this is an array type */
1085 InvalidOid, /* no further array type */
1086 InvalidOid, /* base type ID */
1087 NULL, /* never a default type value */
1088 NULL, /* binary default isn't sent either */
1089 false, /* never passed by value */
1090 'i', /* enums have align i, so do their arrays */
1091 'x', /* ARRAY is always toastable */
1092 -1, /* typMod (Domains only) */
1093 0, /* Array dimensions of typbasetype */
1094 false); /* Type NOT NULL */
1096 pfree(enumArrayName);
1101 * Find suitable I/O functions for a type.
1103 * typeOid is the type's OID (which will already exist, if only as a shell
1108 findTypeInputFunction(List *procname, Oid typeOid)
1114 * Input functions can take a single argument of type CSTRING, or three
1115 * arguments (string, typioparam OID, typmod).
1117 * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
1118 * see this, we issue a warning and fix up the pg_proc entry.
1120 argList[0] = CSTRINGOID;
1122 procOid = LookupFuncName(procname, 1, argList, true);
1123 if (OidIsValid(procOid))
1126 argList[1] = OIDOID;
1127 argList[2] = INT4OID;
1129 procOid = LookupFuncName(procname, 3, argList, true);
1130 if (OidIsValid(procOid))
1133 /* No luck, try it with OPAQUE */
1134 argList[0] = OPAQUEOID;
1136 procOid = LookupFuncName(procname, 1, argList, true);
1138 if (!OidIsValid(procOid))
1140 argList[1] = OIDOID;
1141 argList[2] = INT4OID;
1143 procOid = LookupFuncName(procname, 3, argList, true);
1146 if (OidIsValid(procOid))
1148 /* Found, but must complain and fix the pg_proc entry */
1150 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
1151 NameListToString(procname))));
1152 SetFunctionArgType(procOid, 0, CSTRINGOID);
1155 * Need CommandCounterIncrement since DefineType will likely try to
1156 * alter the pg_proc tuple again.
1158 CommandCounterIncrement();
1163 /* Use CSTRING (preferred) in the error message */
1164 argList[0] = CSTRINGOID;
1167 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1168 errmsg("function %s does not exist",
1169 func_signature_string(procname, 1, argList))));
1171 return InvalidOid; /* keep compiler quiet */
1175 findTypeOutputFunction(List *procname, Oid typeOid)
1181 * Output functions can take a single argument of the type.
1183 * For backwards compatibility we allow OPAQUE in place of the actual type
1184 * name; if we see this, we issue a warning and fix up the pg_proc entry.
1186 argList[0] = typeOid;
1188 procOid = LookupFuncName(procname, 1, argList, true);
1189 if (OidIsValid(procOid))
1192 /* No luck, try it with OPAQUE */
1193 argList[0] = OPAQUEOID;
1195 procOid = LookupFuncName(procname, 1, argList, true);
1197 if (OidIsValid(procOid))
1199 /* Found, but must complain and fix the pg_proc entry */
1201 (errmsg("changing argument type of function %s from \"opaque\" to %s",
1202 NameListToString(procname), format_type_be(typeOid))));
1203 SetFunctionArgType(procOid, 0, typeOid);
1206 * Need CommandCounterIncrement since DefineType will likely try to
1207 * alter the pg_proc tuple again.
1209 CommandCounterIncrement();
1214 /* Use type name, not OPAQUE, in the failure message. */
1215 argList[0] = typeOid;
1218 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1219 errmsg("function %s does not exist",
1220 func_signature_string(procname, 1, argList))));
1222 return InvalidOid; /* keep compiler quiet */
1226 findTypeReceiveFunction(List *procname, Oid typeOid)
1232 * Receive functions can take a single argument of type INTERNAL, or three
1233 * arguments (internal, typioparam OID, typmod).
1235 argList[0] = INTERNALOID;
1237 procOid = LookupFuncName(procname, 1, argList, true);
1238 if (OidIsValid(procOid))
1241 argList[1] = OIDOID;
1242 argList[2] = INT4OID;
1244 procOid = LookupFuncName(procname, 3, argList, true);
1245 if (OidIsValid(procOid))
1249 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1250 errmsg("function %s does not exist",
1251 func_signature_string(procname, 1, argList))));
1253 return InvalidOid; /* keep compiler quiet */
1257 findTypeSendFunction(List *procname, Oid typeOid)
1263 * Send functions can take a single argument of the type.
1265 argList[0] = typeOid;
1267 procOid = LookupFuncName(procname, 1, argList, true);
1268 if (OidIsValid(procOid))
1272 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1273 errmsg("function %s does not exist",
1274 func_signature_string(procname, 1, argList))));
1276 return InvalidOid; /* keep compiler quiet */
1280 findTypeTypmodinFunction(List *procname)
1286 * typmodin functions always take one cstring[] argument and return int4.
1288 argList[0] = CSTRINGARRAYOID;
1290 procOid = LookupFuncName(procname, 1, argList, true);
1291 if (!OidIsValid(procOid))
1293 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1294 errmsg("function %s does not exist",
1295 func_signature_string(procname, 1, argList))));
1297 if (get_func_rettype(procOid) != INT4OID)
1299 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1300 errmsg("typmod_in function %s must return type \"integer\"",
1301 NameListToString(procname))));
1307 findTypeTypmodoutFunction(List *procname)
1313 * typmodout functions always take one int4 argument and return cstring.
1315 argList[0] = INT4OID;
1317 procOid = LookupFuncName(procname, 1, argList, true);
1318 if (!OidIsValid(procOid))
1320 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1321 errmsg("function %s does not exist",
1322 func_signature_string(procname, 1, argList))));
1324 if (get_func_rettype(procOid) != CSTRINGOID)
1326 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1327 errmsg("typmod_out function %s must return type \"cstring\"",
1328 NameListToString(procname))));
1334 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1340 * Analyze functions always take one INTERNAL argument and return bool.
1342 argList[0] = INTERNALOID;
1344 procOid = LookupFuncName(procname, 1, argList, true);
1345 if (!OidIsValid(procOid))
1347 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1348 errmsg("function %s does not exist",
1349 func_signature_string(procname, 1, argList))));
1351 if (get_func_rettype(procOid) != BOOLOID)
1353 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1354 errmsg("type analyze function %s must return type \"boolean\"",
1355 NameListToString(procname))));
1361 /*-------------------------------------------------------------------
1362 * DefineCompositeType
1364 * Create a Composite Type relation.
1365 * `DefineRelation' does all the work, we just provide the correct
1368 * If the relation already exists, then 'DefineRelation' will abort
1371 * DefineCompositeType returns relid for use when creating
1372 * an implicit composite type during function creation
1373 *-------------------------------------------------------------------
1376 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1378 CreateStmt *createStmt = makeNode(CreateStmt);
1380 if (coldeflist == NIL)
1382 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1383 errmsg("composite type must have at least one attribute")));
1386 * now set the parameters for keys/inheritance etc. All of these are
1387 * uninteresting for composite types...
1389 createStmt->relation = (RangeVar *) typevar;
1390 createStmt->tableElts = coldeflist;
1391 createStmt->inhRelations = NIL;
1392 createStmt->constraints = NIL;
1393 createStmt->options = list_make1(defWithOids(false));
1394 createStmt->oncommit = ONCOMMIT_NOOP;
1395 createStmt->tablespacename = NULL;
1398 * finally create the relation...
1400 return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1404 * AlterDomainDefault
1406 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1409 AlterDomainDefault(List *names, Node *defaultRaw)
1417 Node *defaultExpr = NULL; /* NULL if no default specified */
1418 Datum new_record[Natts_pg_type];
1419 char new_record_nulls[Natts_pg_type];
1420 char new_record_repl[Natts_pg_type];
1422 Form_pg_type typTup;
1424 /* Make a TypeName so we can use standard type lookup machinery */
1425 typename = makeTypeNameFromNameList(names);
1426 domainoid = typenameTypeId(NULL, typename);
1428 /* Look up the domain in the type table */
1429 rel = heap_open(TypeRelationId, RowExclusiveLock);
1431 tup = SearchSysCacheCopy(TYPEOID,
1432 ObjectIdGetDatum(domainoid),
1434 if (!HeapTupleIsValid(tup))
1435 elog(ERROR, "cache lookup failed for type %u", domainoid);
1436 typTup = (Form_pg_type) GETSTRUCT(tup);
1438 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1439 checkDomainOwner(tup, typename);
1441 /* Setup new tuple */
1442 MemSet(new_record, (Datum) 0, sizeof(new_record));
1443 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1444 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1446 /* Store the new default, if null then skip this step */
1449 /* Create a dummy ParseState for transformExpr */
1450 pstate = make_parsestate(NULL);
1453 * Cook the colDef->raw_expr into an expression. Note: Name is
1454 * strictly for error message
1456 defaultExpr = cookDefault(pstate, defaultRaw,
1457 typTup->typbasetype,
1459 NameStr(typTup->typname));
1462 * Expression must be stored as a nodeToString result, but we also
1463 * require a valid textual representation (mainly to make life easier
1466 defaultValue = deparse_expression(defaultExpr,
1467 deparse_context_for(NameStr(typTup->typname),
1472 * Form an updated tuple with the new default and write it back.
1474 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
1476 nodeToString(defaultExpr)));
1478 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1479 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
1480 CStringGetDatum(defaultValue));
1481 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1484 /* Default is NULL, drop it */
1486 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1487 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1488 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1489 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1492 newtuple = heap_modifytuple(tup, RelationGetDescr(rel),
1493 new_record, new_record_nulls,
1496 simple_heap_update(rel, &tup->t_self, newtuple);
1498 CatalogUpdateIndexes(rel, newtuple);
1500 /* Rebuild dependencies */
1501 GenerateTypeDependencies(typTup->typnamespace,
1503 InvalidOid, /* typrelid is n/a */
1504 0, /* relation kind is n/a */
1514 false, /* a domain isn't an implicit array */
1515 typTup->typbasetype,
1517 true); /* Rebuild is true */
1520 heap_close(rel, NoLock);
1521 heap_freetuple(newtuple);
1525 * AlterDomainNotNull
1527 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1530 AlterDomainNotNull(List *names, bool notNull)
1536 Form_pg_type typTup;
1538 /* Make a TypeName so we can use standard type lookup machinery */
1539 typename = makeTypeNameFromNameList(names);
1540 domainoid = typenameTypeId(NULL, typename);
1542 /* Look up the domain in the type table */
1543 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1545 tup = SearchSysCacheCopy(TYPEOID,
1546 ObjectIdGetDatum(domainoid),
1548 if (!HeapTupleIsValid(tup))
1549 elog(ERROR, "cache lookup failed for type %u", domainoid);
1550 typTup = (Form_pg_type) GETSTRUCT(tup);
1552 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1553 checkDomainOwner(tup, typename);
1555 /* Is the domain already set to the desired constraint? */
1556 if (typTup->typnotnull == notNull)
1558 heap_close(typrel, RowExclusiveLock);
1562 /* Adding a NOT NULL constraint requires checking existing columns */
1568 /* Fetch relation list with attributes based on this domain */
1569 /* ShareLock is sufficient to prevent concurrent data changes */
1571 rels = get_rels_with_domain(domainoid, ShareLock);
1575 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1576 Relation testrel = rtc->rel;
1577 TupleDesc tupdesc = RelationGetDescr(testrel);
1581 /* Scan all tuples in this relation */
1582 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1583 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1587 /* Test attributes that are of the domain */
1588 for (i = 0; i < rtc->natts; i++)
1590 int attnum = rtc->atts[i];
1592 if (heap_attisnull(tuple, attnum))
1594 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1595 errmsg("column \"%s\" of table \"%s\" contains null values",
1596 NameStr(tupdesc->attrs[attnum - 1]->attname),
1597 RelationGetRelationName(testrel))));
1602 /* Close each rel after processing, but keep lock */
1603 heap_close(testrel, NoLock);
1608 * Okay to update pg_type row. We can scribble on typTup because it's a
1611 typTup->typnotnull = notNull;
1613 simple_heap_update(typrel, &tup->t_self, tup);
1615 CatalogUpdateIndexes(typrel, tup);
1618 heap_freetuple(tup);
1619 heap_close(typrel, RowExclusiveLock);
1623 * AlterDomainDropConstraint
1625 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1628 AlterDomainDropConstraint(List *names, const char *constrName,
1629 DropBehavior behavior)
1636 SysScanDesc conscan;
1640 /* Make a TypeName so we can use standard type lookup machinery */
1641 typename = makeTypeNameFromNameList(names);
1642 domainoid = typenameTypeId(NULL, typename);
1644 /* Look up the domain in the type table */
1645 rel = heap_open(TypeRelationId, RowExclusiveLock);
1647 tup = SearchSysCacheCopy(TYPEOID,
1648 ObjectIdGetDatum(domainoid),
1650 if (!HeapTupleIsValid(tup))
1651 elog(ERROR, "cache lookup failed for type %u", domainoid);
1653 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1654 checkDomainOwner(tup, typename);
1656 /* Grab an appropriate lock on the pg_constraint relation */
1657 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
1659 /* Use the index to scan only constraints of the target relation */
1660 ScanKeyInit(&key[0],
1661 Anum_pg_constraint_contypid,
1662 BTEqualStrategyNumber, F_OIDEQ,
1663 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1665 conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
1666 SnapshotNow, 1, key);
1669 * Scan over the result set, removing any matching entries.
1671 while ((contup = systable_getnext(conscan)) != NULL)
1673 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1675 if (strcmp(NameStr(con->conname), constrName) == 0)
1677 ObjectAddress conobj;
1679 conobj.classId = ConstraintRelationId;
1680 conobj.objectId = HeapTupleGetOid(contup);
1681 conobj.objectSubId = 0;
1683 performDeletion(&conobj, behavior);
1686 /* Clean up after the scan */
1687 systable_endscan(conscan);
1688 heap_close(conrel, RowExclusiveLock);
1690 heap_close(rel, NoLock);
1694 * AlterDomainAddConstraint
1696 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1699 AlterDomainAddConstraint(List *names, Node *newConstraint)
1705 Form_pg_type typTup;
1709 ExprContext *econtext;
1712 ExprState *exprstate;
1715 /* Make a TypeName so we can use standard type lookup machinery */
1716 typename = makeTypeNameFromNameList(names);
1717 domainoid = typenameTypeId(NULL, typename);
1719 /* Look up the domain in the type table */
1720 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1722 tup = SearchSysCacheCopy(TYPEOID,
1723 ObjectIdGetDatum(domainoid),
1725 if (!HeapTupleIsValid(tup))
1726 elog(ERROR, "cache lookup failed for type %u", domainoid);
1727 typTup = (Form_pg_type) GETSTRUCT(tup);
1729 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1730 checkDomainOwner(tup, typename);
1732 /* Check for unsupported constraint types */
1733 if (IsA(newConstraint, FkConstraint))
1735 (errcode(ERRCODE_SYNTAX_ERROR),
1736 errmsg("foreign key constraints not possible for domains")));
1738 /* otherwise it should be a plain Constraint */
1739 if (!IsA(newConstraint, Constraint))
1740 elog(ERROR, "unrecognized node type: %d",
1741 (int) nodeTag(newConstraint));
1743 constr = (Constraint *) newConstraint;
1745 switch (constr->contype)
1748 /* processed below */
1753 (errcode(ERRCODE_SYNTAX_ERROR),
1754 errmsg("unique constraints not possible for domains")));
1757 case CONSTR_PRIMARY:
1759 (errcode(ERRCODE_SYNTAX_ERROR),
1760 errmsg("primary key constraints not possible for domains")));
1763 case CONSTR_ATTR_DEFERRABLE:
1764 case CONSTR_ATTR_NOT_DEFERRABLE:
1765 case CONSTR_ATTR_DEFERRED:
1766 case CONSTR_ATTR_IMMEDIATE:
1768 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1769 errmsg("specifying constraint deferrability not supported for domains")));
1773 elog(ERROR, "unrecognized constraint subtype: %d",
1774 (int) constr->contype);
1779 * Since all other constraint types throw errors, this must be a check
1780 * constraint. First, process the constraint expression and add an entry
1784 ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1785 typTup->typbasetype, typTup->typtypmod,
1786 constr, NameStr(typTup->typname));
1789 * Test all values stored in the attributes based on the domain the
1790 * constraint is being added to.
1792 expr = (Expr *) stringToNode(ccbin);
1794 /* Need an EState to run ExecEvalExpr */
1795 estate = CreateExecutorState();
1796 econtext = GetPerTupleExprContext(estate);
1798 /* build execution state for expr */
1799 exprstate = ExecPrepareExpr(expr, estate);
1801 /* Fetch relation list with attributes based on this domain */
1802 /* ShareLock is sufficient to prevent concurrent data changes */
1804 rels = get_rels_with_domain(domainoid, ShareLock);
1808 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1809 Relation testrel = rtc->rel;
1810 TupleDesc tupdesc = RelationGetDescr(testrel);
1814 /* Scan all tuples in this relation */
1815 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1816 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1820 /* Test attributes that are of the domain */
1821 for (i = 0; i < rtc->natts; i++)
1823 int attnum = rtc->atts[i];
1828 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1830 econtext->domainValue_datum = d;
1831 econtext->domainValue_isNull = isNull;
1833 conResult = ExecEvalExprSwitchContext(exprstate,
1837 if (!isNull && !DatumGetBool(conResult))
1839 (errcode(ERRCODE_CHECK_VIOLATION),
1840 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
1841 NameStr(tupdesc->attrs[attnum - 1]->attname),
1842 RelationGetRelationName(testrel))));
1845 ResetExprContext(econtext);
1849 /* Hold relation lock till commit (XXX bad for concurrency) */
1850 heap_close(testrel, NoLock);
1853 FreeExecutorState(estate);
1856 heap_close(typrel, RowExclusiveLock);
1860 * get_rels_with_domain
1862 * Fetch all relations / attributes which are using the domain
1864 * The result is a list of RelToCheck structs, one for each distinct
1865 * relation, each containing one or more attribute numbers that are of
1866 * the domain type. We have opened each rel and acquired the specified lock
1869 * We support nested domains by including attributes that are of derived
1870 * domain types. Current callers do not need to distinguish between attributes
1871 * that are of exactly the given domain and those that are of derived domains.
1873 * XXX this is completely broken because there is no way to lock the domain
1874 * to prevent columns from being added or dropped while our command runs.
1875 * We can partially protect against column drops by locking relations as we
1876 * come across them, but there is still a race condition (the window between
1877 * seeing a pg_depend entry and acquiring lock on the relation it references).
1878 * Also, holding locks on all these relations simultaneously creates a non-
1879 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
1880 * risk by using the weakest suitable lock (ShareLock for most callers).
1882 * XXX the API for this is not sufficient to support checking domain values
1883 * that are inside composite types or arrays. Currently we just error out
1884 * if a composite type containing the target domain is stored anywhere.
1885 * There are not currently arrays of domains; if there were, we could take
1886 * the same approach, but it'd be nicer to fix it properly.
1888 * Generally used for retrieving a list of tests when adding
1889 * new constraints to a domain.
1892 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1897 SysScanDesc depScan;
1900 Assert(lockmode != NoLock);
1903 * We scan pg_depend to find those things that depend on the domain. (We
1904 * assume we can ignore refobjsubid for a domain.)
1906 depRel = heap_open(DependRelationId, AccessShareLock);
1908 ScanKeyInit(&key[0],
1909 Anum_pg_depend_refclassid,
1910 BTEqualStrategyNumber, F_OIDEQ,
1911 ObjectIdGetDatum(TypeRelationId));
1912 ScanKeyInit(&key[1],
1913 Anum_pg_depend_refobjid,
1914 BTEqualStrategyNumber, F_OIDEQ,
1915 ObjectIdGetDatum(domainOid));
1917 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
1918 SnapshotNow, 2, key);
1920 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1922 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1923 RelToCheck *rtc = NULL;
1925 Form_pg_attribute pg_att;
1928 /* Check for directly dependent types --- must be domains */
1929 if (pg_depend->classid == TypeRelationId)
1931 Assert(get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN);
1933 * Recursively add dependent columns to the output list. This
1934 * is a bit inefficient since we may fail to combine RelToCheck
1935 * entries when attributes of the same rel have different derived
1936 * domain types, but it's probably not worth improving.
1938 result = list_concat(result,
1939 get_rels_with_domain(pg_depend->objid,
1944 /* Else, ignore dependees that aren't user columns of relations */
1945 /* (we assume system columns are never of domain types) */
1946 if (pg_depend->classid != RelationRelationId ||
1947 pg_depend->objsubid <= 0)
1950 /* See if we already have an entry for this relation */
1951 foreach(rellist, result)
1953 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1955 if (RelationGetRelid(rt->rel) == pg_depend->objid)
1964 /* First attribute found for this relation */
1967 /* Acquire requested lock on relation */
1968 rel = relation_open(pg_depend->objid, lockmode);
1971 * Check to see if rowtype is stored anyplace as a composite-type
1972 * column; if so we have to fail, for now anyway.
1974 if (OidIsValid(rel->rd_rel->reltype))
1975 find_composite_type_dependencies(rel->rd_rel->reltype,
1977 format_type_be(domainOid));
1979 /* Otherwise we can ignore views, composite types, etc */
1980 if (rel->rd_rel->relkind != RELKIND_RELATION)
1982 relation_close(rel, lockmode);
1986 /* Build the RelToCheck entry with enough space for all atts */
1987 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1990 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1991 result = lcons(rtc, result);
1995 * Confirm column has not been dropped, and is of the expected type.
1996 * This defends against an ALTER DROP COLUMN occuring just before we
1997 * acquired lock ... but if the whole table were dropped, we'd still
2000 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
2002 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
2003 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
2007 * Okay, add column to result. We store the columns in column-number
2008 * order; this is just a hack to improve predictability of regression
2011 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
2014 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
2016 rtc->atts[ptr] = rtc->atts[ptr - 1];
2019 rtc->atts[ptr] = pg_depend->objsubid;
2022 systable_endscan(depScan);
2024 relation_close(depRel, AccessShareLock);
2032 * Check that the type is actually a domain and that the current user
2033 * has permission to do ALTER DOMAIN on it. Throw an error if not.
2036 checkDomainOwner(HeapTuple tup, TypeName *typename)
2038 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
2040 /* Check that this is actually a domain */
2041 if (typTup->typtype != TYPTYPE_DOMAIN)
2043 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2044 errmsg("\"%s\" is not a domain",
2045 TypeNameToString(typename))));
2047 /* Permission check: must own type */
2048 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2049 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2050 TypeNameToString(typename));
2054 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
2057 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
2058 int typMod, Constraint *constr,
2065 CoerceToDomainValue *domVal;
2068 * Assign or validate constraint name
2072 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
2077 (errcode(ERRCODE_DUPLICATE_OBJECT),
2078 errmsg("constraint \"%s\" for domain \"%s\" already exists",
2079 constr->name, domainName)));
2082 constr->name = ChooseConstraintName(domainName,
2089 * Convert the A_EXPR in raw_expr into an EXPR
2091 pstate = make_parsestate(NULL);
2094 * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
2095 * the expression. Note that it will appear to have the type of the base
2096 * type, not the domain. This seems correct since within the check
2097 * expression, we should not assume the input value can be considered a
2098 * member of the domain.
2100 domVal = makeNode(CoerceToDomainValue);
2101 domVal->typeId = baseTypeOid;
2102 domVal->typeMod = typMod;
2104 pstate->p_value_substitute = (Node *) domVal;
2106 expr = transformExpr(pstate, constr->raw_expr);
2109 * Make sure it yields a boolean result.
2111 expr = coerce_to_boolean(pstate, expr, "CHECK");
2114 * Make sure no outside relations are referred to.
2116 if (list_length(pstate->p_rtable) != 0)
2118 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2119 errmsg("cannot use table references in domain check constraint")));
2122 * Domains don't allow var clauses (this should be redundant with the
2123 * above check, but make it anyway)
2125 if (contain_var_clause(expr))
2127 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2128 errmsg("cannot use table references in domain check constraint")));
2131 * No subplans or aggregates, either...
2133 if (pstate->p_hasSubLinks)
2135 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2136 errmsg("cannot use subquery in check constraint")));
2137 if (pstate->p_hasAggs)
2139 (errcode(ERRCODE_GROUPING_ERROR),
2140 errmsg("cannot use aggregate function in check constraint")));
2143 * Convert to string form for storage.
2145 ccbin = nodeToString(expr);
2148 * Deparse it to produce text for consrc.
2150 * Since VARNOs aren't allowed in domain constraints, relation context
2151 * isn't required as anything other than a shell.
2153 ccsrc = deparse_expression(expr,
2154 deparse_context_for(domainName,
2159 * Store the constraint in pg_constraint
2161 CreateConstraintEntry(constr->name, /* Constraint Name */
2162 domainNamespace, /* namespace */
2163 CONSTRAINT_CHECK, /* Constraint Type */
2164 false, /* Is Deferrable */
2165 false, /* Is Deferred */
2166 InvalidOid, /* not a relation constraint */
2169 domainOid, /* domain constraint */
2170 InvalidOid, /* Foreign key fields */
2180 expr, /* Tree form check constraint */
2181 ccbin, /* Binary form check constraint */
2182 ccsrc); /* Source form check constraint */
2185 * Return the compiled constraint expression so the calling routine can
2186 * perform any additional required tests.
2192 * GetDomainConstraints - get a list of the current constraints of domain
2194 * Returns a possibly-empty list of DomainConstraintState nodes.
2196 * This is called by the executor during plan startup for a CoerceToDomain
2197 * expression node. The given constraints will be checked for each value
2198 * passed through the node.
2200 * We allow this to be called for non-domain types, in which case the result
2204 GetDomainConstraints(Oid typeOid)
2207 bool notNull = false;
2210 conRel = heap_open(ConstraintRelationId, AccessShareLock);
2216 Form_pg_type typTup;
2220 tup = SearchSysCache(TYPEOID,
2221 ObjectIdGetDatum(typeOid),
2223 if (!HeapTupleIsValid(tup))
2224 elog(ERROR, "cache lookup failed for type %u", typeOid);
2225 typTup = (Form_pg_type) GETSTRUCT(tup);
2227 if (typTup->typtype != TYPTYPE_DOMAIN)
2229 /* Not a domain, so done */
2230 ReleaseSysCache(tup);
2234 /* Test for NOT NULL Constraint */
2235 if (typTup->typnotnull)
2238 /* Look for CHECK Constraints on this domain */
2239 ScanKeyInit(&key[0],
2240 Anum_pg_constraint_contypid,
2241 BTEqualStrategyNumber, F_OIDEQ,
2242 ObjectIdGetDatum(typeOid));
2244 scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
2245 SnapshotNow, 1, key);
2247 while (HeapTupleIsValid(conTup = systable_getnext(scan)))
2249 Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
2253 DomainConstraintState *r;
2255 /* Ignore non-CHECK constraints (presently, shouldn't be any) */
2256 if (c->contype != CONSTRAINT_CHECK)
2260 * Not expecting conbin to be NULL, but we'll test for it anyway
2262 val = fastgetattr(conTup, Anum_pg_constraint_conbin,
2263 conRel->rd_att, &isNull);
2265 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
2266 NameStr(typTup->typname), NameStr(c->conname));
2268 check_expr = (Expr *)
2269 stringToNode(DatumGetCString(DirectFunctionCall1(textout,
2272 /* ExecInitExpr assumes we already fixed opfuncids */
2273 fix_opfuncids((Node *) check_expr);
2275 r = makeNode(DomainConstraintState);
2276 r->constrainttype = DOM_CONSTRAINT_CHECK;
2277 r->name = pstrdup(NameStr(c->conname));
2278 r->check_expr = ExecInitExpr(check_expr, NULL);
2281 * use lcons() here because constraints of lower domains should be
2284 result = lcons(r, result);
2287 systable_endscan(scan);
2289 /* loop to next domain in stack */
2290 typeOid = typTup->typbasetype;
2291 ReleaseSysCache(tup);
2294 heap_close(conRel, AccessShareLock);
2297 * Only need to add one NOT NULL check regardless of how many domains in
2298 * the stack request it.
2302 DomainConstraintState *r = makeNode(DomainConstraintState);
2304 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
2305 r->name = pstrdup("NOT NULL");
2306 r->check_expr = NULL;
2308 /* lcons to apply the nullness check FIRST */
2309 result = lcons(r, result);
2316 * Change the owner of a type.
2319 AlterTypeOwner(List *names, Oid newOwnerId)
2325 Form_pg_type typTup;
2326 AclResult aclresult;
2328 /* Make a TypeName so we can use standard type lookup machinery */
2329 typename = makeTypeNameFromNameList(names);
2331 /* Use LookupTypeName here so that shell types can be processed */
2332 typeOid = LookupTypeName(NULL, typename);
2333 if (!OidIsValid(typeOid))
2335 (errcode(ERRCODE_UNDEFINED_OBJECT),
2336 errmsg("type \"%s\" does not exist",
2337 TypeNameToString(typename))));
2339 /* Look up the type in the type table */
2340 rel = heap_open(TypeRelationId, RowExclusiveLock);
2342 tup = SearchSysCacheCopy(TYPEOID,
2343 ObjectIdGetDatum(typeOid),
2345 if (!HeapTupleIsValid(tup))
2346 elog(ERROR, "cache lookup failed for type %u", typeOid);
2347 typTup = (Form_pg_type) GETSTRUCT(tup);
2350 * If it's a composite type, we need to check that it really is a
2351 * free-standing composite type, and not a table's rowtype. We
2352 * want people to use ALTER TABLE not ALTER TYPE for that case.
2354 if (typTup->typtype == TYPTYPE_COMPOSITE &&
2355 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
2357 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2358 errmsg("\"%s\" is a table's row type",
2359 TypeNameToString(typename))));
2361 /* don't allow direct alteration of array types, either */
2362 if (OidIsValid(typTup->typelem) &&
2363 get_array_type(typTup->typelem) == typeOid)
2365 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2366 errmsg("cannot alter array type %s",
2367 format_type_be(typeOid)),
2368 errhint("You can alter type %s, which will alter the array type as well.",
2369 format_type_be(typTup->typelem))));
2372 * If the new owner is the same as the existing owner, consider the
2373 * command to have succeeded. This is for dump restoration purposes.
2375 if (typTup->typowner != newOwnerId)
2377 /* Superusers can always do it */
2380 /* Otherwise, must be owner of the existing object */
2381 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2382 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2383 TypeNameToString(typename));
2385 /* Must be able to become new owner */
2386 check_is_member_of_role(GetUserId(), newOwnerId);
2388 /* New owner must have CREATE privilege on namespace */
2389 aclresult = pg_namespace_aclcheck(typTup->typnamespace,
2392 if (aclresult != ACLCHECK_OK)
2393 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
2394 get_namespace_name(typTup->typnamespace));
2398 * If it's a composite type, invoke ATExecChangeOwner so that we
2399 * fix up the pg_class entry properly. That will call back to
2400 * AlterTypeOwnerInternal to take care of the pg_type entry(s).
2402 if (typTup->typtype == TYPTYPE_COMPOSITE)
2403 ATExecChangeOwner(typTup->typrelid, newOwnerId, true);
2407 * We can just apply the modification directly.
2409 * okay to scribble on typTup because it's a copy
2411 typTup->typowner = newOwnerId;
2413 simple_heap_update(rel, &tup->t_self, tup);
2415 CatalogUpdateIndexes(rel, tup);
2417 /* Update owner dependency reference */
2418 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2420 /* If it has an array type, update that too */
2421 if (OidIsValid(typTup->typarray))
2422 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2427 heap_close(rel, RowExclusiveLock);
2431 * AlterTypeOwnerInternal - change type owner unconditionally
2433 * This is currently only used to propagate ALTER TABLE/TYPE OWNER to a
2434 * table's rowtype or an array type, and to implement REASSIGN OWNED BY.
2435 * It assumes the caller has done all needed checks. The function will
2436 * automatically recurse to an array type if the type has one.
2438 * hasDependEntry should be TRUE if type is expected to have a pg_shdepend
2439 * entry (ie, it's not a table rowtype nor an array type).
2442 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
2443 bool hasDependEntry)
2447 Form_pg_type typTup;
2449 rel = heap_open(TypeRelationId, RowExclusiveLock);
2451 tup = SearchSysCacheCopy(TYPEOID,
2452 ObjectIdGetDatum(typeOid),
2454 if (!HeapTupleIsValid(tup))
2455 elog(ERROR, "cache lookup failed for type %u", typeOid);
2456 typTup = (Form_pg_type) GETSTRUCT(tup);
2459 * Modify the owner --- okay to scribble on typTup because it's a copy
2461 typTup->typowner = newOwnerId;
2463 simple_heap_update(rel, &tup->t_self, tup);
2465 CatalogUpdateIndexes(rel, tup);
2467 /* Update owner dependency reference, if it has one */
2469 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2471 /* If it has an array type, update that too */
2472 if (OidIsValid(typTup->typarray))
2473 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2476 heap_close(rel, RowExclusiveLock);
2480 * Execute ALTER TYPE SET SCHEMA
2483 AlterTypeNamespace(List *names, const char *newschema)
2490 /* Make a TypeName so we can use standard type lookup machinery */
2491 typename = makeTypeNameFromNameList(names);
2492 typeOid = typenameTypeId(NULL, typename);
2494 /* check permissions on type */
2495 if (!pg_type_ownercheck(typeOid, GetUserId()))
2496 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2497 format_type_be(typeOid));
2499 /* get schema OID and check its permissions */
2500 nspOid = LookupCreationNamespace(newschema);
2502 /* don't allow direct alteration of array types */
2503 elemOid = get_element_type(typeOid);
2504 if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
2506 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2507 errmsg("cannot alter array type %s",
2508 format_type_be(typeOid)),
2509 errhint("You can alter type %s, which will alter the array type as well.",
2510 format_type_be(elemOid))));
2512 /* and do the work */
2513 AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
2517 * Move specified type to new namespace.
2519 * Caller must have already checked privileges.
2521 * The function automatically recurses to process the type's array type,
2522 * if any. isImplicitArray should be TRUE only when doing this internal
2523 * recursion (outside callers must never try to move an array type directly).
2525 * If errorOnTableType is TRUE, the function errors out if the type is
2526 * a table type. ALTER TABLE has to be used to move a table to a new
2530 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
2531 bool isImplicitArray,
2532 bool errorOnTableType)
2536 Form_pg_type typform;
2539 bool isCompositeType;
2541 rel = heap_open(TypeRelationId, RowExclusiveLock);
2543 tup = SearchSysCacheCopy(TYPEOID,
2544 ObjectIdGetDatum(typeOid),
2546 if (!HeapTupleIsValid(tup))
2547 elog(ERROR, "cache lookup failed for type %u", typeOid);
2548 typform = (Form_pg_type) GETSTRUCT(tup);
2550 oldNspOid = typform->typnamespace;
2551 arrayOid = typform->typarray;
2553 if (oldNspOid == nspOid)
2555 (errcode(ERRCODE_DUPLICATE_OBJECT),
2556 errmsg("type %s is already in schema \"%s\"",
2557 format_type_be(typeOid),
2558 get_namespace_name(nspOid))));
2560 /* disallow renaming into or out of temp schemas */
2561 if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
2563 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2564 errmsg("cannot move objects into or out of temporary schemas")));
2566 /* same for TOAST schema */
2567 if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
2569 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2570 errmsg("cannot move objects into or out of TOAST schema")));
2572 /* check for duplicate name (more friendly than unique-index failure) */
2573 if (SearchSysCacheExists(TYPENAMENSP,
2574 CStringGetDatum(NameStr(typform->typname)),
2575 ObjectIdGetDatum(nspOid),
2578 (errcode(ERRCODE_DUPLICATE_OBJECT),
2579 errmsg("type \"%s\" already exists in schema \"%s\"",
2580 NameStr(typform->typname),
2581 get_namespace_name(nspOid))));
2583 /* Detect whether type is a composite type (but not a table rowtype) */
2585 (typform->typtype == TYPTYPE_COMPOSITE &&
2586 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
2588 /* Enforce not-table-type if requested */
2589 if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
2592 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2593 errmsg("%s is a table's row type",
2594 format_type_be(typeOid)),
2595 errhint("Use ALTER TABLE SET SCHEMA instead.")));
2597 /* OK, modify the pg_type row */
2599 /* tup is a copy, so we can scribble directly on it */
2600 typform->typnamespace = nspOid;
2602 simple_heap_update(rel, &tup->t_self, tup);
2603 CatalogUpdateIndexes(rel, tup);
2606 * Composite types have pg_class entries.
2608 * We need to modify the pg_class tuple as well to reflect the change of
2611 if (isCompositeType)
2615 classRel = heap_open(RelationRelationId, RowExclusiveLock);
2617 AlterRelationNamespaceInternal(classRel, typform->typrelid,
2621 heap_close(classRel, RowExclusiveLock);
2624 * Check for constraints associated with the composite type (we don't
2625 * currently support this, but probably will someday).
2627 AlterConstraintNamespaces(typform->typrelid, oldNspOid,
2632 /* If it's a domain, it might have constraints */
2633 if (typform->typtype == TYPTYPE_DOMAIN)
2634 AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
2638 * Update dependency on schema, if any --- a table rowtype has not got
2639 * one, and neither does an implicit array.
2641 if ((isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
2643 if (changeDependencyFor(TypeRelationId, typeOid,
2644 NamespaceRelationId, oldNspOid, nspOid) != 1)
2645 elog(ERROR, "failed to change schema dependency for type %s",
2646 format_type_be(typeOid));
2648 heap_freetuple(tup);
2650 heap_close(rel, RowExclusiveLock);
2652 /* Recursively alter the associated array type, if any */
2653 if (OidIsValid(arrayOid))
2654 AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);