1 /*-------------------------------------------------------------------------
4 * Routines for SQL commands that manipulate types (and domains).
6 * Portions Copyright (c) 1996-2005, 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.76 2005/07/14 21:46:29 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/heapam.h"
35 #include "access/genam.h"
36 #include "catalog/dependency.h"
37 #include "catalog/heap.h"
38 #include "catalog/indexing.h"
39 #include "catalog/namespace.h"
40 #include "catalog/pg_constraint.h"
41 #include "catalog/pg_depend.h"
42 #include "catalog/pg_type.h"
43 #include "commands/defrem.h"
44 #include "commands/tablecmds.h"
45 #include "commands/typecmds.h"
46 #include "executor/executor.h"
47 #include "miscadmin.h"
48 #include "nodes/execnodes.h"
49 #include "nodes/nodes.h"
50 #include "optimizer/clauses.h"
51 #include "optimizer/planmain.h"
52 #include "optimizer/var.h"
53 #include "parser/parse_coerce.h"
54 #include "parser/parse_expr.h"
55 #include "parser/parse_func.h"
56 #include "parser/parse_relation.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 findTypeAnalyzeFunction(List *procname, Oid typeOid);
81 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
82 static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
83 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
85 int typMod, Constraint *constr,
91 * Registers a new type.
94 DefineType(List *names, List *parameters)
99 int16 internalLength = -1; /* default: variable-length */
100 Oid elemType = InvalidOid;
101 List *inputName = NIL;
102 List *outputName = NIL;
103 List *receiveName = NIL;
104 List *sendName = NIL;
105 List *analyzeName = NIL;
106 char *defaultValue = NULL;
107 bool byValue = false;
108 char delimiter = DEFAULT_TYPDELIM;
109 char alignment = 'i'; /* default alignment */
110 char storage = 'p'; /* default TOAST storage method */
113 Oid receiveOid = InvalidOid;
114 Oid sendOid = InvalidOid;
115 Oid analyzeOid = InvalidOid;
121 /* Convert list of names to a name and namespace */
122 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
124 /* Check we have creation rights in target namespace */
125 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
126 if (aclresult != ACLCHECK_OK)
127 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
128 get_namespace_name(typeNamespace));
131 * Type names must be one character shorter than other names, allowing
132 * room to create the corresponding array type name with prepended
135 if (strlen(typeName) > (NAMEDATALEN - 2))
137 (errcode(ERRCODE_INVALID_NAME),
138 errmsg("type names must be %d characters or less",
141 foreach(pl, parameters)
143 DefElem *defel = (DefElem *) lfirst(pl);
145 if (pg_strcasecmp(defel->defname, "internallength") == 0)
146 internalLength = defGetTypeLength(defel);
147 else if (pg_strcasecmp(defel->defname, "externallength") == 0)
148 ; /* ignored -- remove after 7.3 */
149 else if (pg_strcasecmp(defel->defname, "input") == 0)
150 inputName = defGetQualifiedName(defel);
151 else if (pg_strcasecmp(defel->defname, "output") == 0)
152 outputName = defGetQualifiedName(defel);
153 else if (pg_strcasecmp(defel->defname, "receive") == 0)
154 receiveName = defGetQualifiedName(defel);
155 else if (pg_strcasecmp(defel->defname, "send") == 0)
156 sendName = defGetQualifiedName(defel);
157 else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
158 pg_strcasecmp(defel->defname, "analyse") == 0)
159 analyzeName = defGetQualifiedName(defel);
160 else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
162 char *p = defGetString(defel);
166 else if (pg_strcasecmp(defel->defname, "element") == 0)
168 elemType = typenameTypeId(defGetTypeName(defel));
169 /* disallow arrays of pseudotypes */
170 if (get_typtype(elemType) == 'p')
172 (errcode(ERRCODE_DATATYPE_MISMATCH),
173 errmsg("array element type cannot be %s",
174 format_type_be(elemType))));
176 else if (pg_strcasecmp(defel->defname, "default") == 0)
177 defaultValue = defGetString(defel);
178 else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
179 byValue = defGetBoolean(defel);
180 else if (pg_strcasecmp(defel->defname, "alignment") == 0)
182 char *a = defGetString(defel);
185 * Note: if argument was an unquoted identifier, parser will
186 * have applied translations to it, so be prepared to
187 * recognize translated type names as well as the nominal
190 if (pg_strcasecmp(a, "double") == 0 ||
191 pg_strcasecmp(a, "float8") == 0 ||
192 pg_strcasecmp(a, "pg_catalog.float8") == 0)
194 else if (pg_strcasecmp(a, "int4") == 0 ||
195 pg_strcasecmp(a, "pg_catalog.int4") == 0)
197 else if (pg_strcasecmp(a, "int2") == 0 ||
198 pg_strcasecmp(a, "pg_catalog.int2") == 0)
200 else if (pg_strcasecmp(a, "char") == 0 ||
201 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
205 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
206 errmsg("alignment \"%s\" not recognized", a)));
208 else if (pg_strcasecmp(defel->defname, "storage") == 0)
210 char *a = defGetString(defel);
212 if (pg_strcasecmp(a, "plain") == 0)
214 else if (pg_strcasecmp(a, "external") == 0)
216 else if (pg_strcasecmp(a, "extended") == 0)
218 else if (pg_strcasecmp(a, "main") == 0)
222 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
223 errmsg("storage \"%s\" not recognized", a)));
227 (errcode(ERRCODE_SYNTAX_ERROR),
228 errmsg("type attribute \"%s\" not recognized",
233 * make sure we have our required definitions
235 if (inputName == NIL)
237 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
238 errmsg("type input function must be specified")));
239 if (outputName == NIL)
241 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
242 errmsg("type output function must be specified")));
245 * Look to see if type already exists (presumably as a shell; if not,
246 * TypeCreate will complain). If it doesn't, create it as a shell, so
247 * that the OID is known for use in the I/O function definitions.
249 typoid = GetSysCacheOid(TYPENAMENSP,
250 CStringGetDatum(typeName),
251 ObjectIdGetDatum(typeNamespace),
253 if (!OidIsValid(typoid))
255 typoid = TypeShellMake(typeName, typeNamespace);
256 /* Make new shell type visible for modification below */
257 CommandCounterIncrement();
261 * Convert I/O proc names to OIDs
263 inputOid = findTypeInputFunction(inputName, typoid);
264 outputOid = findTypeOutputFunction(outputName, typoid);
266 receiveOid = findTypeReceiveFunction(receiveName, typoid);
268 sendOid = findTypeSendFunction(sendName, typoid);
271 * Verify that I/O procs return the expected thing. If we see OPAQUE,
272 * complain and change it to the correct type-safe choice.
274 resulttype = get_func_rettype(inputOid);
275 if (resulttype != typoid)
277 if (resulttype == OPAQUEOID)
279 /* backwards-compatibility hack */
281 (errmsg("changing return type of function %s from \"opaque\" to %s",
282 NameListToString(inputName), typeName)));
283 SetFunctionReturnType(inputOid, typoid);
287 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
288 errmsg("type input function %s must return type %s",
289 NameListToString(inputName), typeName)));
291 resulttype = get_func_rettype(outputOid);
292 if (resulttype != CSTRINGOID)
294 if (resulttype == OPAQUEOID)
296 /* backwards-compatibility hack */
298 (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
299 NameListToString(outputName))));
300 SetFunctionReturnType(outputOid, CSTRINGOID);
304 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
305 errmsg("type output function %s must return type \"cstring\"",
306 NameListToString(outputName))));
310 resulttype = get_func_rettype(receiveOid);
311 if (resulttype != typoid)
313 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
314 errmsg("type receive function %s must return type %s",
315 NameListToString(receiveName), typeName)));
319 resulttype = get_func_rettype(sendOid);
320 if (resulttype != BYTEAOID)
322 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
323 errmsg("type send function %s must return type \"bytea\"",
324 NameListToString(sendName))));
328 * Convert analysis function proc name to an OID. If no analysis
329 * function is specified, we'll use zero to select the built-in
333 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
336 * now have TypeCreate do all the real work.
339 TypeCreate(typeName, /* type name */
340 typeNamespace, /* namespace */
341 InvalidOid, /* preassigned type oid (not done here) */
342 InvalidOid, /* relation oid (n/a here) */
343 0, /* relation kind (ditto) */
344 internalLength, /* internal size */
345 'b', /* type-type (base type) */
346 delimiter, /* array element delimiter */
347 inputOid, /* input procedure */
348 outputOid, /* output procedure */
349 receiveOid, /* receive procedure */
350 sendOid, /* send procedure */
351 analyzeOid, /* analyze procedure */
352 elemType, /* element type ID */
353 InvalidOid, /* base type ID (only for domains) */
354 defaultValue, /* default type value */
355 NULL, /* no binary form available */
356 byValue, /* passed by value */
357 alignment, /* required alignment */
358 storage, /* TOAST strategy */
359 -1, /* typMod (Domains only) */
360 0, /* Array Dimensions of typbasetype */
361 false); /* Type NOT NULL */
364 * When we create a base type (as opposed to a complex type) we need
365 * to have an array entry for it in pg_type as well.
367 shadow_type = makeArrayTypeName(typeName);
369 /* alignment must be 'i' or 'd' for arrays */
370 alignment = (alignment == 'd') ? 'd' : 'i';
372 TypeCreate(shadow_type, /* type name */
373 typeNamespace, /* namespace */
374 InvalidOid, /* preassigned type oid (not done here) */
375 InvalidOid, /* relation oid (n/a here) */
376 0, /* relation kind (ditto) */
377 -1, /* internal size */
378 'b', /* type-type (base type) */
379 DEFAULT_TYPDELIM, /* array element delimiter */
380 F_ARRAY_IN, /* input procedure */
381 F_ARRAY_OUT, /* output procedure */
382 F_ARRAY_RECV, /* receive procedure */
383 F_ARRAY_SEND, /* send procedure */
384 InvalidOid, /* analyze procedure - default */
385 typoid, /* element type ID */
386 InvalidOid, /* base type ID */
387 NULL, /* never a default type value */
388 NULL, /* binary default isn't sent either */
389 false, /* never passed by value */
390 alignment, /* see above */
391 'x', /* ARRAY is always toastable */
392 -1, /* typMod (Domains only) */
393 0, /* Array dimensions of typbasetype */
394 false); /* Type NOT NULL */
402 * Removes a datatype.
405 RemoveType(List *names, DropBehavior behavior)
410 ObjectAddress object;
412 /* Make a TypeName so we can use standard type lookup machinery */
413 typename = makeNode(TypeName);
414 typename->names = names;
415 typename->typmod = -1;
416 typename->arrayBounds = NIL;
418 /* Use LookupTypeName here so that shell types can be removed. */
419 typeoid = LookupTypeName(typename);
420 if (!OidIsValid(typeoid))
422 (errcode(ERRCODE_UNDEFINED_OBJECT),
423 errmsg("type \"%s\" does not exist",
424 TypeNameToString(typename))));
426 tup = SearchSysCache(TYPEOID,
427 ObjectIdGetDatum(typeoid),
429 if (!HeapTupleIsValid(tup))
430 elog(ERROR, "cache lookup failed for type %u", typeoid);
432 /* Permission check: must own type or its namespace */
433 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
434 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
436 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
437 TypeNameToString(typename));
439 ReleaseSysCache(tup);
444 object.classId = TypeRelationId;
445 object.objectId = typeoid;
446 object.objectSubId = 0;
448 performDeletion(&object, behavior);
453 * Guts of type deletion.
456 RemoveTypeById(Oid typeOid)
461 relation = heap_open(TypeRelationId, RowExclusiveLock);
463 tup = SearchSysCache(TYPEOID,
464 ObjectIdGetDatum(typeOid),
466 if (!HeapTupleIsValid(tup))
467 elog(ERROR, "cache lookup failed for type %u", typeOid);
469 simple_heap_delete(relation, &tup->t_self);
471 ReleaseSysCache(tup);
473 heap_close(relation, RowExclusiveLock);
479 * Registers a new domain.
482 DefineDomain(CreateDomainStmt *stmt)
487 int16 internalLength;
490 Oid receiveProcedure;
492 Oid analyzeProcedure;
500 Node *defaultExpr = NULL;
501 char *defaultValue = NULL;
502 char *defaultValueBin = NULL;
503 bool typNotNull = false;
504 bool nullDefined = false;
506 int32 typNDims = list_length(stmt->typename->arrayBounds);
508 List *schema = stmt->constraints;
512 Form_pg_type baseType;
514 /* Convert list of names to a name and namespace */
515 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
518 /* Check we have creation rights in target namespace */
519 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
521 if (aclresult != ACLCHECK_OK)
522 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
523 get_namespace_name(domainNamespace));
526 * Domainnames, unlike typenames don't need to account for the '_'
527 * prefix. So they can be one character longer. (This test is
528 * presently useless since the parser will have truncated the name to
529 * fit. But leave it here since we may someday support arrays of
530 * domains, in which case we'll be back to needing to enforce
533 if (strlen(domainName) > (NAMEDATALEN - 1))
535 (errcode(ERRCODE_INVALID_NAME),
536 errmsg("domain names must be %d characters or less",
540 * Look up the base type.
542 typeTup = typenameType(stmt->typename);
544 baseType = (Form_pg_type) GETSTRUCT(typeTup);
545 basetypeoid = HeapTupleGetOid(typeTup);
548 * Base type must be a plain base type. Domains over pseudo types
549 * would create a security hole. Domains of domains might be made to
550 * work in the future, but not today. Ditto for domains over complex
553 typtype = baseType->typtype;
556 (errcode(ERRCODE_DATATYPE_MISMATCH),
557 errmsg("\"%s\" is not a valid base type for a domain",
558 TypeNameToString(stmt->typename))));
560 /* passed by value */
561 byValue = baseType->typbyval;
563 /* Required Alignment */
564 alignment = baseType->typalign;
567 storage = baseType->typstorage;
570 internalLength = baseType->typlen;
572 /* Array element Delimiter */
573 delimiter = baseType->typdelim;
576 inputProcedure = baseType->typinput;
577 outputProcedure = baseType->typoutput;
578 receiveProcedure = baseType->typreceive;
579 sendProcedure = baseType->typsend;
581 /* Analysis function */
582 analyzeProcedure = baseType->typanalyze;
584 /* Inherited default value */
585 datum = SysCacheGetAttr(TYPEOID, typeTup,
586 Anum_pg_type_typdefault, &isnull);
588 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
590 /* Inherited default binary value */
591 datum = SysCacheGetAttr(TYPEOID, typeTup,
592 Anum_pg_type_typdefaultbin, &isnull);
594 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
597 * Pull out the typelem name of the parent OID.
599 * This is what enables us to make a domain of an array
601 basetypelem = baseType->typelem;
604 * Run through constraints manually to avoid the additional processing
605 * conducted by DefineRelation() and friends.
607 foreach(listptr, schema)
609 Node *newConstraint = lfirst(listptr);
613 /* Check for unsupported constraint types */
614 if (IsA(newConstraint, FkConstraint))
616 (errcode(ERRCODE_SYNTAX_ERROR),
617 errmsg("foreign key constraints not possible for domains")));
619 /* otherwise it should be a plain Constraint */
620 if (!IsA(newConstraint, Constraint))
621 elog(ERROR, "unrecognized node type: %d",
622 (int) nodeTag(newConstraint));
624 constr = (Constraint *) newConstraint;
626 switch (constr->contype)
631 * The inherited default value may be overridden by the
632 * user with the DEFAULT <expr> statement.
636 (errcode(ERRCODE_SYNTAX_ERROR),
637 errmsg("multiple default expressions")));
639 /* Create a dummy ParseState for transformExpr */
640 pstate = make_parsestate(NULL);
643 * Cook the constr->raw_expr into an expression. Note:
644 * Name is strictly for error message
646 defaultExpr = cookDefault(pstate, constr->raw_expr,
648 stmt->typename->typmod,
652 * Expression must be stored as a nodeToString result, but
653 * we also require a valid textual representation (mainly
654 * to make life easier for pg_dump).
656 defaultValue = deparse_expression(defaultExpr,
657 deparse_context_for(domainName,
660 defaultValueBin = nodeToString(defaultExpr);
664 if (nullDefined && !typNotNull)
666 (errcode(ERRCODE_SYNTAX_ERROR),
667 errmsg("conflicting NULL/NOT NULL constraints")));
673 if (nullDefined && typNotNull)
675 (errcode(ERRCODE_SYNTAX_ERROR),
676 errmsg("conflicting NULL/NOT NULL constraints")));
684 * Check constraints are handled after domain creation, as
685 * they require the Oid of the domain
690 * All else are error cases
694 (errcode(ERRCODE_SYNTAX_ERROR),
695 errmsg("unique constraints not possible for domains")));
700 (errcode(ERRCODE_SYNTAX_ERROR),
701 errmsg("primary key constraints not possible for domains")));
704 case CONSTR_ATTR_DEFERRABLE:
705 case CONSTR_ATTR_NOT_DEFERRABLE:
706 case CONSTR_ATTR_DEFERRED:
707 case CONSTR_ATTR_IMMEDIATE:
709 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
710 errmsg("specifying constraint deferrability not supported for domains")));
714 elog(ERROR, "unrecognized constraint subtype: %d",
715 (int) constr->contype);
721 * Have TypeCreate do all the real work.
724 TypeCreate(domainName, /* type name */
725 domainNamespace, /* namespace */
726 InvalidOid, /* preassigned type oid (none here) */
727 InvalidOid, /* relation oid (n/a here) */
728 0, /* relation kind (ditto) */
729 internalLength, /* internal size */
730 'd', /* type-type (domain type) */
731 delimiter, /* array element delimiter */
732 inputProcedure, /* input procedure */
733 outputProcedure, /* output procedure */
734 receiveProcedure, /* receive procedure */
735 sendProcedure, /* send procedure */
736 analyzeProcedure, /* analyze procedure */
737 basetypelem, /* element type ID */
738 basetypeoid, /* base type ID */
739 defaultValue, /* default type value (text) */
740 defaultValueBin, /* default type value (binary) */
741 byValue, /* passed by value */
742 alignment, /* required alignment */
743 storage, /* TOAST strategy */
744 stmt->typename->typmod, /* typeMod value */
745 typNDims, /* Array dimensions for base type */
746 typNotNull); /* Type NOT NULL */
749 * Process constraints which refer to the domain ID returned by
752 foreach(listptr, schema)
754 Constraint *constr = lfirst(listptr);
756 /* it must be a Constraint, per check above */
758 switch (constr->contype)
761 domainAddConstraint(domainoid, domainNamespace,
762 basetypeoid, stmt->typename->typmod,
766 /* Other constraint types were fully processed above */
772 /* CCI so we can detect duplicate constraint names */
773 CommandCounterIncrement();
777 * Now we can clean up.
779 ReleaseSysCache(typeTup);
787 * This is identical to RemoveType except we insist it be a domain.
790 RemoveDomain(List *names, DropBehavior behavior)
796 ObjectAddress object;
798 /* Make a TypeName so we can use standard type lookup machinery */
799 typename = makeNode(TypeName);
800 typename->names = names;
801 typename->typmod = -1;
802 typename->arrayBounds = NIL;
804 /* Use LookupTypeName here so that shell types can be removed. */
805 typeoid = LookupTypeName(typename);
806 if (!OidIsValid(typeoid))
808 (errcode(ERRCODE_UNDEFINED_OBJECT),
809 errmsg("type \"%s\" does not exist",
810 TypeNameToString(typename))));
812 tup = SearchSysCache(TYPEOID,
813 ObjectIdGetDatum(typeoid),
815 if (!HeapTupleIsValid(tup))
816 elog(ERROR, "cache lookup failed for type %u", typeoid);
818 /* Permission check: must own type or its namespace */
819 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
820 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
822 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
823 TypeNameToString(typename));
825 /* Check that this is actually a domain */
826 typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
830 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
831 errmsg("\"%s\" is not a domain",
832 TypeNameToString(typename))));
834 ReleaseSysCache(tup);
839 object.classId = TypeRelationId;
840 object.objectId = typeoid;
841 object.objectSubId = 0;
843 performDeletion(&object, behavior);
848 * Find suitable I/O functions for a type.
850 * typeOid is the type's OID (which will already exist, if only as a shell
855 findTypeInputFunction(List *procname, Oid typeOid)
861 * Input functions can take a single argument of type CSTRING, or
862 * three arguments (string, typioparam OID, typmod).
864 * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
865 * see this, we issue a warning and fix up the pg_proc entry.
867 argList[0] = CSTRINGOID;
869 procOid = LookupFuncName(procname, 1, argList, true);
870 if (OidIsValid(procOid))
874 argList[2] = INT4OID;
876 procOid = LookupFuncName(procname, 3, argList, true);
877 if (OidIsValid(procOid))
880 /* No luck, try it with OPAQUE */
881 argList[0] = OPAQUEOID;
883 procOid = LookupFuncName(procname, 1, argList, true);
885 if (!OidIsValid(procOid))
888 argList[2] = INT4OID;
890 procOid = LookupFuncName(procname, 3, argList, true);
893 if (OidIsValid(procOid))
895 /* Found, but must complain and fix the pg_proc entry */
897 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
898 NameListToString(procname))));
899 SetFunctionArgType(procOid, 0, CSTRINGOID);
902 * Need CommandCounterIncrement since DefineType will likely try
903 * to alter the pg_proc tuple again.
905 CommandCounterIncrement();
910 /* Use CSTRING (preferred) in the error message */
911 argList[0] = CSTRINGOID;
914 (errcode(ERRCODE_UNDEFINED_FUNCTION),
915 errmsg("function %s does not exist",
916 func_signature_string(procname, 1, argList))));
918 return InvalidOid; /* keep compiler quiet */
922 findTypeOutputFunction(List *procname, Oid typeOid)
928 * Output functions can take a single argument of the type.
930 * For backwards compatibility we allow OPAQUE in place of the actual
931 * type name; if we see this, we issue a warning and fix up the
934 argList[0] = typeOid;
936 procOid = LookupFuncName(procname, 1, argList, true);
937 if (OidIsValid(procOid))
940 /* No luck, try it with OPAQUE */
941 argList[0] = OPAQUEOID;
943 procOid = LookupFuncName(procname, 1, argList, true);
945 if (OidIsValid(procOid))
947 /* Found, but must complain and fix the pg_proc entry */
949 (errmsg("changing argument type of function %s from \"opaque\" to %s",
950 NameListToString(procname), format_type_be(typeOid))));
951 SetFunctionArgType(procOid, 0, typeOid);
954 * Need CommandCounterIncrement since DefineType will likely try
955 * to alter the pg_proc tuple again.
957 CommandCounterIncrement();
962 /* Use type name, not OPAQUE, in the failure message. */
963 argList[0] = typeOid;
966 (errcode(ERRCODE_UNDEFINED_FUNCTION),
967 errmsg("function %s does not exist",
968 func_signature_string(procname, 1, argList))));
970 return InvalidOid; /* keep compiler quiet */
974 findTypeReceiveFunction(List *procname, Oid typeOid)
980 * Receive functions can take a single argument of type INTERNAL, or
981 * three arguments (internal, typioparam OID, typmod).
983 argList[0] = INTERNALOID;
985 procOid = LookupFuncName(procname, 1, argList, true);
986 if (OidIsValid(procOid))
990 argList[2] = INT4OID;
992 procOid = LookupFuncName(procname, 3, argList, true);
993 if (OidIsValid(procOid))
997 (errcode(ERRCODE_UNDEFINED_FUNCTION),
998 errmsg("function %s does not exist",
999 func_signature_string(procname, 1, argList))));
1001 return InvalidOid; /* keep compiler quiet */
1005 findTypeSendFunction(List *procname, Oid typeOid)
1011 * Send functions can take a single argument of the type.
1013 argList[0] = typeOid;
1015 procOid = LookupFuncName(procname, 1, argList, true);
1016 if (OidIsValid(procOid))
1020 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1021 errmsg("function %s does not exist",
1022 func_signature_string(procname, 1, argList))));
1024 return InvalidOid; /* keep compiler quiet */
1028 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1034 * Analyze functions always take one INTERNAL argument and return
1037 argList[0] = INTERNALOID;
1039 procOid = LookupFuncName(procname, 1, argList, true);
1040 if (!OidIsValid(procOid))
1042 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1043 errmsg("function %s does not exist",
1044 func_signature_string(procname, 1, argList))));
1046 if (get_func_rettype(procOid) != BOOLOID)
1048 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1049 errmsg("type analyze function %s must return type \"boolean\"",
1050 NameListToString(procname))));
1056 /*-------------------------------------------------------------------
1057 * DefineCompositeType
1059 * Create a Composite Type relation.
1060 * `DefineRelation' does all the work, we just provide the correct
1063 * If the relation already exists, then 'DefineRelation' will abort
1066 * DefineCompositeType returns relid for use when creating
1067 * an implicit composite type during function creation
1068 *-------------------------------------------------------------------
1071 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1073 CreateStmt *createStmt = makeNode(CreateStmt);
1075 if (coldeflist == NIL)
1077 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1078 errmsg("composite type must have at least one attribute")));
1081 * now set the parameters for keys/inheritance etc. All of these are
1082 * uninteresting for composite types...
1084 createStmt->relation = (RangeVar *) typevar;
1085 createStmt->tableElts = coldeflist;
1086 createStmt->inhRelations = NIL;
1087 createStmt->constraints = NIL;
1088 createStmt->hasoids = MUST_NOT_HAVE_OIDS;
1089 createStmt->oncommit = ONCOMMIT_NOOP;
1090 createStmt->tablespacename = NULL;
1093 * finally create the relation...
1095 return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1099 * AlterDomainDefault
1101 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1104 AlterDomainDefault(List *names, Node *defaultRaw)
1112 Node *defaultExpr = NULL; /* NULL if no default specified */
1113 Datum new_record[Natts_pg_type];
1114 char new_record_nulls[Natts_pg_type];
1115 char new_record_repl[Natts_pg_type];
1117 Form_pg_type typTup;
1119 /* Make a TypeName so we can use standard type lookup machinery */
1120 typename = makeNode(TypeName);
1121 typename->names = names;
1122 typename->typmod = -1;
1123 typename->arrayBounds = NIL;
1125 /* Lock the domain in the type table */
1126 rel = heap_open(TypeRelationId, RowExclusiveLock);
1128 /* Use LookupTypeName here so that shell types can be removed. */
1129 domainoid = LookupTypeName(typename);
1130 if (!OidIsValid(domainoid))
1132 (errcode(ERRCODE_UNDEFINED_OBJECT),
1133 errmsg("type \"%s\" does not exist",
1134 TypeNameToString(typename))));
1136 tup = SearchSysCacheCopy(TYPEOID,
1137 ObjectIdGetDatum(domainoid),
1139 if (!HeapTupleIsValid(tup))
1140 elog(ERROR, "cache lookup failed for type %u", domainoid);
1142 /* Doesn't return if user isn't allowed to alter the domain */
1143 domainOwnerCheck(tup, typename);
1145 /* Setup new tuple */
1146 MemSet(new_record, (Datum) 0, sizeof(new_record));
1147 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1148 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1151 typTup = (Form_pg_type) GETSTRUCT(tup);
1153 /* Store the new default, if null then skip this step */
1156 /* Create a dummy ParseState for transformExpr */
1157 pstate = make_parsestate(NULL);
1160 * Cook the colDef->raw_expr into an expression. Note: Name is
1161 * strictly for error message
1163 defaultExpr = cookDefault(pstate, defaultRaw,
1164 typTup->typbasetype,
1166 NameStr(typTup->typname));
1169 * Expression must be stored as a nodeToString result, but we also
1170 * require a valid textual representation (mainly to make life
1171 * easier for pg_dump).
1173 defaultValue = deparse_expression(defaultExpr,
1174 deparse_context_for(NameStr(typTup->typname),
1179 * Form an updated tuple with the new default and write it back.
1181 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
1183 nodeToString(defaultExpr)));
1185 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1186 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
1187 CStringGetDatum(defaultValue));
1188 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1191 /* Default is NULL, drop it */
1193 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1194 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1195 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1196 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1199 newtuple = heap_modifytuple(tup, RelationGetDescr(rel),
1200 new_record, new_record_nulls,
1203 simple_heap_update(rel, &tup->t_self, newtuple);
1205 CatalogUpdateIndexes(rel, newtuple);
1207 /* Rebuild dependencies */
1208 GenerateTypeDependencies(typTup->typnamespace,
1211 0, /* relation kind is n/a */
1219 typTup->typbasetype,
1221 true); /* Rebuild is true */
1224 heap_close(rel, NoLock);
1225 heap_freetuple(newtuple);
1229 * AlterDomainNotNull
1231 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1234 AlterDomainNotNull(List *names, bool notNull)
1240 Form_pg_type typTup;
1242 /* Make a TypeName so we can use standard type lookup machinery */
1243 typename = makeNode(TypeName);
1244 typename->names = names;
1245 typename->typmod = -1;
1246 typename->arrayBounds = NIL;
1248 /* Lock the type table */
1249 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1251 /* Use LookupTypeName here so that shell types can be found (why?). */
1252 domainoid = LookupTypeName(typename);
1253 if (!OidIsValid(domainoid))
1255 (errcode(ERRCODE_UNDEFINED_OBJECT),
1256 errmsg("type \"%s\" does not exist",
1257 TypeNameToString(typename))));
1259 tup = SearchSysCacheCopy(TYPEOID,
1260 ObjectIdGetDatum(domainoid),
1262 if (!HeapTupleIsValid(tup))
1263 elog(ERROR, "cache lookup failed for type %u", domainoid);
1264 typTup = (Form_pg_type) GETSTRUCT(tup);
1266 /* Doesn't return if user isn't allowed to alter the domain */
1267 domainOwnerCheck(tup, typename);
1269 /* Is the domain already set to the desired constraint? */
1270 if (typTup->typnotnull == notNull)
1272 heap_close(typrel, RowExclusiveLock);
1276 /* Adding a NOT NULL constraint requires checking existing columns */
1282 /* Fetch relation list with attributes based on this domain */
1283 /* ShareLock is sufficient to prevent concurrent data changes */
1285 rels = get_rels_with_domain(domainoid, ShareLock);
1289 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1290 Relation testrel = rtc->rel;
1291 TupleDesc tupdesc = RelationGetDescr(testrel);
1295 /* Scan all tuples in this relation */
1296 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1297 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1301 /* Test attributes that are of the domain */
1302 for (i = 0; i < rtc->natts; i++)
1304 int attnum = rtc->atts[i];
1306 if (heap_attisnull(tuple, attnum))
1308 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1309 errmsg("column \"%s\" of table \"%s\" contains null values",
1310 NameStr(tupdesc->attrs[attnum - 1]->attname),
1311 RelationGetRelationName(testrel))));
1316 /* Close each rel after processing, but keep lock */
1317 heap_close(testrel, NoLock);
1322 * Okay to update pg_type row. We can scribble on typTup because it's
1325 typTup->typnotnull = notNull;
1327 simple_heap_update(typrel, &tup->t_self, tup);
1329 CatalogUpdateIndexes(typrel, tup);
1332 heap_freetuple(tup);
1333 heap_close(typrel, RowExclusiveLock);
1337 * AlterDomainDropConstraint
1339 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1342 AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior)
1348 Form_pg_type typTup;
1350 SysScanDesc conscan;
1354 /* Make a TypeName so we can use standard type lookup machinery */
1355 typename = makeNode(TypeName);
1356 typename->names = names;
1357 typename->typmod = -1;
1358 typename->arrayBounds = NIL;
1360 /* Lock the type table */
1361 rel = heap_open(TypeRelationId, RowExclusiveLock);
1363 /* Use LookupTypeName here so that shell types can be removed. */
1364 domainoid = LookupTypeName(typename);
1365 if (!OidIsValid(domainoid))
1367 (errcode(ERRCODE_UNDEFINED_OBJECT),
1368 errmsg("type \"%s\" does not exist",
1369 TypeNameToString(typename))));
1371 tup = SearchSysCacheCopy(TYPEOID,
1372 ObjectIdGetDatum(domainoid),
1374 if (!HeapTupleIsValid(tup))
1375 elog(ERROR, "cache lookup failed for type %u", domainoid);
1377 /* Doesn't return if user isn't allowed to alter the domain */
1378 domainOwnerCheck(tup, typename);
1380 /* Grab an appropriate lock on the pg_constraint relation */
1381 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
1383 /* Use the index to scan only constraints of the target relation */
1384 ScanKeyInit(&key[0],
1385 Anum_pg_constraint_contypid,
1386 BTEqualStrategyNumber, F_OIDEQ,
1387 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1389 conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
1390 SnapshotNow, 1, key);
1392 typTup = (Form_pg_type) GETSTRUCT(tup);
1395 * Scan over the result set, removing any matching entries.
1397 while ((contup = systable_getnext(conscan)) != NULL)
1399 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1401 if (strcmp(NameStr(con->conname), constrName) == 0)
1403 ObjectAddress conobj;
1405 conobj.classId = ConstraintRelationId;
1406 conobj.objectId = HeapTupleGetOid(contup);
1407 conobj.objectSubId = 0;
1409 performDeletion(&conobj, behavior);
1412 /* Clean up after the scan */
1413 systable_endscan(conscan);
1414 heap_close(conrel, RowExclusiveLock);
1416 heap_close(rel, NoLock);
1420 * AlterDomainAddConstraint
1422 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1425 AlterDomainAddConstraint(List *names, Node *newConstraint)
1431 Form_pg_type typTup;
1435 ExprContext *econtext;
1438 ExprState *exprstate;
1441 /* Make a TypeName so we can use standard type lookup machinery */
1442 typename = makeNode(TypeName);
1443 typename->names = names;
1444 typename->typmod = -1;
1445 typename->arrayBounds = NIL;
1447 /* Lock the type table */
1448 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1450 /* Use LookupTypeName here so that shell types can be found (why?). */
1451 domainoid = LookupTypeName(typename);
1452 if (!OidIsValid(domainoid))
1454 (errcode(ERRCODE_UNDEFINED_OBJECT),
1455 errmsg("type \"%s\" does not exist",
1456 TypeNameToString(typename))));
1458 tup = SearchSysCacheCopy(TYPEOID,
1459 ObjectIdGetDatum(domainoid),
1461 if (!HeapTupleIsValid(tup))
1462 elog(ERROR, "cache lookup failed for type %u", domainoid);
1463 typTup = (Form_pg_type) GETSTRUCT(tup);
1465 /* Doesn't return if user isn't allowed to alter the domain */
1466 domainOwnerCheck(tup, typename);
1468 /* Check for unsupported constraint types */
1469 if (IsA(newConstraint, FkConstraint))
1471 (errcode(ERRCODE_SYNTAX_ERROR),
1472 errmsg("foreign key constraints not possible for domains")));
1474 /* otherwise it should be a plain Constraint */
1475 if (!IsA(newConstraint, Constraint))
1476 elog(ERROR, "unrecognized node type: %d",
1477 (int) nodeTag(newConstraint));
1479 constr = (Constraint *) newConstraint;
1481 switch (constr->contype)
1484 /* processed below */
1489 (errcode(ERRCODE_SYNTAX_ERROR),
1490 errmsg("unique constraints not possible for domains")));
1493 case CONSTR_PRIMARY:
1495 (errcode(ERRCODE_SYNTAX_ERROR),
1496 errmsg("primary key constraints not possible for domains")));
1499 case CONSTR_ATTR_DEFERRABLE:
1500 case CONSTR_ATTR_NOT_DEFERRABLE:
1501 case CONSTR_ATTR_DEFERRED:
1502 case CONSTR_ATTR_IMMEDIATE:
1504 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1505 errmsg("specifying constraint deferrability not supported for domains")));
1509 elog(ERROR, "unrecognized constraint subtype: %d",
1510 (int) constr->contype);
1515 * Since all other constraint types throw errors, this must be a check
1516 * constraint. First, process the constraint expression and add an
1517 * entry to pg_constraint.
1520 ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1521 typTup->typbasetype, typTup->typtypmod,
1522 constr, NameStr(typTup->typname));
1525 * Test all values stored in the attributes based on the domain the
1526 * constraint is being added to.
1528 expr = (Expr *) stringToNode(ccbin);
1530 /* Need an EState to run ExecEvalExpr */
1531 estate = CreateExecutorState();
1532 econtext = GetPerTupleExprContext(estate);
1534 /* build execution state for expr */
1535 exprstate = ExecPrepareExpr(expr, estate);
1537 /* Fetch relation list with attributes based on this domain */
1538 /* ShareLock is sufficient to prevent concurrent data changes */
1540 rels = get_rels_with_domain(domainoid, ShareLock);
1544 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1545 Relation testrel = rtc->rel;
1546 TupleDesc tupdesc = RelationGetDescr(testrel);
1550 /* Scan all tuples in this relation */
1551 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1552 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1556 /* Test attributes that are of the domain */
1557 for (i = 0; i < rtc->natts; i++)
1559 int attnum = rtc->atts[i];
1564 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1566 econtext->domainValue_datum = d;
1567 econtext->domainValue_isNull = isNull;
1569 conResult = ExecEvalExprSwitchContext(exprstate,
1573 if (!isNull && !DatumGetBool(conResult))
1575 (errcode(ERRCODE_CHECK_VIOLATION),
1576 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
1577 NameStr(tupdesc->attrs[attnum - 1]->attname),
1578 RelationGetRelationName(testrel))));
1581 ResetExprContext(econtext);
1585 /* Hold relation lock till commit (XXX bad for concurrency) */
1586 heap_close(testrel, NoLock);
1589 FreeExecutorState(estate);
1592 heap_close(typrel, RowExclusiveLock);
1596 * get_rels_with_domain
1598 * Fetch all relations / attributes which are using the domain
1600 * The result is a list of RelToCheck structs, one for each distinct
1601 * relation, each containing one or more attribute numbers that are of
1602 * the domain type. We have opened each rel and acquired the specified lock
1605 * XXX this is completely broken because there is no way to lock the domain
1606 * to prevent columns from being added or dropped while our command runs.
1607 * We can partially protect against column drops by locking relations as we
1608 * come across them, but there is still a race condition (the window between
1609 * seeing a pg_depend entry and acquiring lock on the relation it references).
1610 * Also, holding locks on all these relations simultaneously creates a non-
1611 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
1612 * risk by using the weakest suitable lock (ShareLock for most callers).
1614 * XXX to support domains over domains, we'd need to make this smarter,
1615 * or make its callers smarter, so that we could find columns of derived
1616 * domains. Arrays of domains would be a problem too.
1618 * Generally used for retrieving a list of tests when adding
1619 * new constraints to a domain.
1622 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1627 SysScanDesc depScan;
1631 * We scan pg_depend to find those things that depend on the domain.
1632 * (We assume we can ignore refobjsubid for a domain.)
1634 depRel = heap_open(DependRelationId, AccessShareLock);
1636 ScanKeyInit(&key[0],
1637 Anum_pg_depend_refclassid,
1638 BTEqualStrategyNumber, F_OIDEQ,
1639 ObjectIdGetDatum(TypeRelationId));
1640 ScanKeyInit(&key[1],
1641 Anum_pg_depend_refobjid,
1642 BTEqualStrategyNumber, F_OIDEQ,
1643 ObjectIdGetDatum(domainOid));
1645 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
1646 SnapshotNow, 2, key);
1648 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1650 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1651 RelToCheck *rtc = NULL;
1653 Form_pg_attribute pg_att;
1656 /* Ignore dependees that aren't user columns of relations */
1657 /* (we assume system columns are never of domain types) */
1658 if (pg_depend->classid != RelationRelationId ||
1659 pg_depend->objsubid <= 0)
1662 /* See if we already have an entry for this relation */
1663 foreach(rellist, result)
1665 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1667 if (RelationGetRelid(rt->rel) == pg_depend->objid)
1676 /* First attribute found for this relation */
1679 /* Acquire requested lock on relation */
1680 rel = relation_open(pg_depend->objid, lockmode);
1682 /* It could be a view or composite type; if so ignore it */
1683 if (rel->rd_rel->relkind != RELKIND_RELATION)
1685 relation_close(rel, lockmode);
1689 /* Build the RelToCheck entry with enough space for all atts */
1690 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1693 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1694 result = lcons(rtc, result);
1698 * Confirm column has not been dropped, and is of the expected
1699 * type. This defends against an ALTER DROP COLUMN occuring just
1700 * before we acquired lock ... but if the whole table were
1701 * dropped, we'd still have a problem.
1703 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
1705 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
1706 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
1710 * Okay, add column to result. We store the columns in
1711 * column-number order; this is just a hack to improve
1712 * predictability of regression test output ...
1714 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
1717 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
1719 rtc->atts[ptr] = rtc->atts[ptr - 1];
1722 rtc->atts[ptr] = pg_depend->objsubid;
1725 systable_endscan(depScan);
1727 relation_close(depRel, AccessShareLock);
1735 * Throw an error if the current user doesn't have permission to modify
1736 * the domain in an ALTER DOMAIN statement, or if the type isn't actually
1740 domainOwnerCheck(HeapTuple tup, TypeName *typename)
1742 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
1744 /* Check that this is actually a domain */
1745 if (typTup->typtype != 'd')
1747 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1748 errmsg("\"%s\" is not a domain",
1749 TypeNameToString(typename))));
1751 /* Permission check: must own type */
1752 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1753 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
1754 TypeNameToString(typename));
1758 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
1761 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
1762 int typMod, Constraint *constr,
1769 CoerceToDomainValue *domVal;
1772 * Assign or validate constraint name
1776 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
1781 (errcode(ERRCODE_DUPLICATE_OBJECT),
1782 errmsg("constraint \"%s\" for domain \"%s\" already exists",
1783 constr->name, domainName)));
1786 constr->name = ChooseConstraintName(domainName,
1793 * Convert the A_EXPR in raw_expr into an EXPR
1795 pstate = make_parsestate(NULL);
1798 * Set up a CoerceToDomainValue to represent the occurrence of VALUE
1799 * in the expression. Note that it will appear to have the type of
1800 * the base type, not the domain. This seems correct since within the
1801 * check expression, we should not assume the input value can be
1802 * considered a member of the domain.
1804 domVal = makeNode(CoerceToDomainValue);
1805 domVal->typeId = baseTypeOid;
1806 domVal->typeMod = typMod;
1808 pstate->p_value_substitute = (Node *) domVal;
1810 expr = transformExpr(pstate, constr->raw_expr);
1813 * Make sure it yields a boolean result.
1815 expr = coerce_to_boolean(pstate, expr, "CHECK");
1818 * Make sure no outside relations are referred to.
1820 if (list_length(pstate->p_rtable) != 0)
1822 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1823 errmsg("cannot use table references in domain check constraint")));
1826 * Domains don't allow var clauses (this should be redundant with the
1827 * above check, but make it anyway)
1829 if (contain_var_clause(expr))
1831 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1832 errmsg("cannot use table references in domain check constraint")));
1835 * No subplans or aggregates, either...
1837 if (pstate->p_hasSubLinks)
1839 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1840 errmsg("cannot use subquery in check constraint")));
1841 if (pstate->p_hasAggs)
1843 (errcode(ERRCODE_GROUPING_ERROR),
1844 errmsg("cannot use aggregate in check constraint")));
1847 * Convert to string form for storage.
1849 ccbin = nodeToString(expr);
1852 * Deparse it to produce text for consrc.
1854 * Since VARNOs aren't allowed in domain constraints, relation context
1855 * isn't required as anything other than a shell.
1857 ccsrc = deparse_expression(expr,
1858 deparse_context_for(domainName,
1863 * Store the constraint in pg_constraint
1865 CreateConstraintEntry(constr->name, /* Constraint Name */
1866 domainNamespace, /* namespace */
1867 CONSTRAINT_CHECK, /* Constraint Type */
1868 false, /* Is Deferrable */
1869 false, /* Is Deferred */
1870 InvalidOid, /* not a relation constraint */
1873 domainOid, /* domain constraint */
1874 InvalidOid, /* Foreign key fields */
1881 expr, /* Tree form check constraint */
1882 ccbin, /* Binary form check constraint */
1883 ccsrc); /* Source form check constraint */
1886 * Return the compiled constraint expression so the calling routine
1887 * can perform any additional required tests.
1893 * GetDomainConstraints - get a list of the current constraints of domain
1895 * Returns a possibly-empty list of DomainConstraintState nodes.
1897 * This is called by the executor during plan startup for a CoerceToDomain
1898 * expression node. The given constraints will be checked for each value
1899 * passed through the node.
1901 * We allow this to be called for non-domain types, in which case the result
1905 GetDomainConstraints(Oid typeOid)
1908 bool notNull = false;
1911 conRel = heap_open(ConstraintRelationId, AccessShareLock);
1917 Form_pg_type typTup;
1921 tup = SearchSysCache(TYPEOID,
1922 ObjectIdGetDatum(typeOid),
1924 if (!HeapTupleIsValid(tup))
1925 elog(ERROR, "cache lookup failed for type %u", typeOid);
1926 typTup = (Form_pg_type) GETSTRUCT(tup);
1928 if (typTup->typtype != 'd')
1930 /* Not a domain, so done */
1931 ReleaseSysCache(tup);
1935 /* Test for NOT NULL Constraint */
1936 if (typTup->typnotnull)
1939 /* Look for CHECK Constraints on this domain */
1940 ScanKeyInit(&key[0],
1941 Anum_pg_constraint_contypid,
1942 BTEqualStrategyNumber, F_OIDEQ,
1943 ObjectIdGetDatum(typeOid));
1945 scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
1946 SnapshotNow, 1, key);
1948 while (HeapTupleIsValid(conTup = systable_getnext(scan)))
1950 Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
1954 DomainConstraintState *r;
1956 /* Ignore non-CHECK constraints (presently, shouldn't be any) */
1957 if (c->contype != CONSTRAINT_CHECK)
1961 * Not expecting conbin to be NULL, but we'll test for it
1964 val = fastgetattr(conTup, Anum_pg_constraint_conbin,
1965 conRel->rd_att, &isNull);
1967 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1968 NameStr(typTup->typname), NameStr(c->conname));
1970 check_expr = (Expr *)
1971 stringToNode(DatumGetCString(DirectFunctionCall1(textout,
1974 /* ExecInitExpr assumes we already fixed opfuncids */
1975 fix_opfuncids((Node *) check_expr);
1977 r = makeNode(DomainConstraintState);
1978 r->constrainttype = DOM_CONSTRAINT_CHECK;
1979 r->name = pstrdup(NameStr(c->conname));
1980 r->check_expr = ExecInitExpr(check_expr, NULL);
1983 * use lcons() here because constraints of lower domains
1984 * should be applied earlier.
1986 result = lcons(r, result);
1989 systable_endscan(scan);
1991 /* loop to next domain in stack */
1992 typeOid = typTup->typbasetype;
1993 ReleaseSysCache(tup);
1996 heap_close(conRel, AccessShareLock);
1999 * Only need to add one NOT NULL check regardless of how many domains
2000 * in the stack request it.
2004 DomainConstraintState *r = makeNode(DomainConstraintState);
2006 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
2007 r->name = pstrdup("NOT NULL");
2008 r->check_expr = NULL;
2010 /* lcons to apply the nullness check FIRST */
2011 result = lcons(r, result);
2018 * Change the owner of a type.
2021 AlterTypeOwner(List *names, Oid newOwnerId)
2027 Form_pg_type typTup;
2028 AclResult aclresult;
2030 /* Make a TypeName so we can use standard type lookup machinery */
2031 typename = makeNode(TypeName);
2032 typename->names = names;
2033 typename->typmod = -1;
2034 typename->arrayBounds = NIL;
2036 /* Lock the type table */
2037 rel = heap_open(TypeRelationId, RowExclusiveLock);
2039 /* Use LookupTypeName here so that shell types can be processed (why?) */
2040 typeOid = LookupTypeName(typename);
2041 if (!OidIsValid(typeOid))
2043 (errcode(ERRCODE_UNDEFINED_OBJECT),
2044 errmsg("type \"%s\" does not exist",
2045 TypeNameToString(typename))));
2047 tup = SearchSysCacheCopy(TYPEOID,
2048 ObjectIdGetDatum(typeOid),
2050 if (!HeapTupleIsValid(tup))
2051 elog(ERROR, "cache lookup failed for type %u", typeOid);
2052 typTup = (Form_pg_type) GETSTRUCT(tup);
2055 * If it's a composite type, we need to check that it really is a
2056 * free-standing composite type, and not a table's underlying type. We
2057 * want people to use ALTER TABLE not ALTER TYPE for that case.
2059 if (typTup->typtype == 'c' && get_rel_relkind(typTup->typrelid) != 'c')
2061 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2062 errmsg("\"%s\" is a table's row type",
2063 TypeNameToString(typename))));
2066 * If the new owner is the same as the existing owner, consider the
2067 * command to have succeeded. This is for dump restoration purposes.
2069 if (typTup->typowner != newOwnerId)
2071 /* Otherwise, must be owner of the existing object */
2072 if (!pg_type_ownercheck(HeapTupleGetOid(tup),GetUserId()))
2073 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2074 TypeNameToString(typename));
2076 /* Must be able to become new owner */
2077 check_is_member_of_role(GetUserId(), newOwnerId);
2079 /* New owner must have CREATE privilege on namespace */
2080 aclresult = pg_namespace_aclcheck(typTup->typnamespace, newOwnerId,
2082 if (aclresult != ACLCHECK_OK)
2083 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
2084 get_namespace_name(typTup->typnamespace));
2087 * Modify the owner --- okay to scribble on typTup because it's a
2090 typTup->typowner = newOwnerId;
2092 simple_heap_update(rel, &tup->t_self, tup);
2094 CatalogUpdateIndexes(rel, tup);
2096 /* Update owner dependency reference */
2097 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2101 heap_close(rel, RowExclusiveLock);