1 /*-------------------------------------------------------------------------
4 * Routines for SQL commands that manipulate types (and domains).
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.40 2003/08/01 00:15:19 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/catname.h"
37 #include "catalog/dependency.h"
38 #include "catalog/heap.h"
39 #include "catalog/indexing.h"
40 #include "catalog/namespace.h"
41 #include "catalog/pg_constraint.h"
42 #include "catalog/pg_depend.h"
43 #include "catalog/pg_type.h"
44 #include "commands/defrem.h"
45 #include "commands/tablecmds.h"
46 #include "commands/typecmds.h"
47 #include "executor/executor.h"
48 #include "miscadmin.h"
49 #include "nodes/execnodes.h"
50 #include "nodes/nodes.h"
51 #include "optimizer/clauses.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_relation.h"
58 #include "parser/parse_type.h"
59 #include "utils/acl.h"
60 #include "utils/builtins.h"
61 #include "utils/fmgroids.h"
62 #include "utils/lsyscache.h"
63 #include "utils/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 List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
81 static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
82 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
84 int typMod, Constraint *constr,
85 int *counter, char *domainName);
90 * Registers a new type.
93 DefineType(List *names, List *parameters)
98 int16 internalLength = -1; /* default: variable-length */
99 Oid elemType = InvalidOid;
100 List *inputName = NIL;
101 List *outputName = NIL;
102 List *receiveName = NIL;
103 List *sendName = NIL;
104 char *defaultValue = NULL;
105 bool byValue = false;
106 char delimiter = DEFAULT_TYPDELIM;
107 char alignment = 'i'; /* default alignment */
108 char storage = 'p'; /* default TOAST storage method */
111 Oid receiveOid = InvalidOid;
112 Oid sendOid = InvalidOid;
118 /* Convert list of names to a name and namespace */
119 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
121 /* Check we have creation rights in target namespace */
122 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
123 if (aclresult != ACLCHECK_OK)
124 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
125 get_namespace_name(typeNamespace));
128 * Type names must be one character shorter than other names, allowing
129 * room to create the corresponding array type name with prepended
132 if (strlen(typeName) > (NAMEDATALEN - 2))
134 (errcode(ERRCODE_INVALID_NAME),
135 errmsg("type names must be %d characters or less",
138 foreach(pl, parameters)
140 DefElem *defel = (DefElem *) lfirst(pl);
142 if (strcasecmp(defel->defname, "internallength") == 0)
143 internalLength = defGetTypeLength(defel);
144 else if (strcasecmp(defel->defname, "externallength") == 0)
145 ; /* ignored -- remove after 7.3 */
146 else if (strcasecmp(defel->defname, "input") == 0)
147 inputName = defGetQualifiedName(defel);
148 else if (strcasecmp(defel->defname, "output") == 0)
149 outputName = defGetQualifiedName(defel);
150 else if (strcasecmp(defel->defname, "receive") == 0)
151 receiveName = defGetQualifiedName(defel);
152 else if (strcasecmp(defel->defname, "send") == 0)
153 sendName = defGetQualifiedName(defel);
154 else if (strcasecmp(defel->defname, "delimiter") == 0)
156 char *p = defGetString(defel);
160 else if (strcasecmp(defel->defname, "element") == 0)
162 elemType = typenameTypeId(defGetTypeName(defel));
163 /* disallow arrays of pseudotypes */
164 if (get_typtype(elemType) == 'p')
166 (errcode(ERRCODE_DATATYPE_MISMATCH),
167 errmsg("array element type cannot be %s",
168 format_type_be(elemType))));
170 else if (strcasecmp(defel->defname, "default") == 0)
171 defaultValue = defGetString(defel);
172 else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
174 else if (strcasecmp(defel->defname, "alignment") == 0)
176 char *a = defGetString(defel);
179 * Note: if argument was an unquoted identifier, parser will
180 * have applied translations to it, so be prepared to
181 * recognize translated type names as well as the nominal
184 if (strcasecmp(a, "double") == 0 ||
185 strcasecmp(a, "float8") == 0 ||
186 strcasecmp(a, "pg_catalog.float8") == 0)
188 else if (strcasecmp(a, "int4") == 0 ||
189 strcasecmp(a, "pg_catalog.int4") == 0)
191 else if (strcasecmp(a, "int2") == 0 ||
192 strcasecmp(a, "pg_catalog.int2") == 0)
194 else if (strcasecmp(a, "char") == 0 ||
195 strcasecmp(a, "pg_catalog.bpchar") == 0)
199 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
200 errmsg("alignment \"%s\" not recognized", a)));
202 else if (strcasecmp(defel->defname, "storage") == 0)
204 char *a = defGetString(defel);
206 if (strcasecmp(a, "plain") == 0)
208 else if (strcasecmp(a, "external") == 0)
210 else if (strcasecmp(a, "extended") == 0)
212 else if (strcasecmp(a, "main") == 0)
216 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
217 errmsg("storage \"%s\" not recognized", a)));
221 (errcode(ERRCODE_SYNTAX_ERROR),
222 errmsg("type attribute \"%s\" not recognized",
227 * make sure we have our required definitions
229 if (inputName == NIL)
231 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
232 errmsg("type input function must be specified")));
233 if (outputName == NIL)
235 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
236 errmsg("type output function must be specified")));
239 * Look to see if type already exists (presumably as a shell; if not,
240 * TypeCreate will complain). If it doesn't, create it as a shell,
241 * so that the OID is known for use in the I/O function definitions.
243 typoid = GetSysCacheOid(TYPENAMENSP,
244 CStringGetDatum(typeName),
245 ObjectIdGetDatum(typeNamespace),
247 if (!OidIsValid(typoid))
249 typoid = TypeShellMake(typeName, typeNamespace);
250 /* Make new shell type visible for modification below */
251 CommandCounterIncrement();
255 * Convert I/O proc names to OIDs
257 inputOid = findTypeInputFunction(inputName, typoid);
258 outputOid = findTypeOutputFunction(outputName, typoid);
260 receiveOid = findTypeReceiveFunction(receiveName, typoid);
262 sendOid = findTypeSendFunction(sendName, typoid);
265 * Verify that I/O procs return the expected thing. If we see OPAQUE,
266 * complain and change it to the correct type-safe choice.
268 resulttype = get_func_rettype(inputOid);
269 if (resulttype != typoid)
271 if (resulttype == OPAQUEOID)
273 /* backwards-compatibility hack */
275 (errmsg("changing return type of function %s from OPAQUE to %s",
276 NameListToString(inputName), typeName)));
277 SetFunctionReturnType(inputOid, typoid);
281 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
282 errmsg("type input function %s must return %s",
283 NameListToString(inputName), typeName)));
285 resulttype = get_func_rettype(outputOid);
286 if (resulttype != CSTRINGOID)
288 if (resulttype == OPAQUEOID)
290 /* backwards-compatibility hack */
292 (errmsg("changing return type of function %s from OPAQUE to CSTRING",
293 NameListToString(outputName))));
294 SetFunctionReturnType(outputOid, CSTRINGOID);
298 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
299 errmsg("type output function %s must return cstring",
300 NameListToString(outputName))));
304 resulttype = get_func_rettype(receiveOid);
305 if (resulttype != typoid)
307 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
308 errmsg("type receive function %s must return %s",
309 NameListToString(receiveName), typeName)));
313 resulttype = get_func_rettype(sendOid);
314 if (resulttype != BYTEAOID)
316 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
317 errmsg("type send function %s must return bytea",
318 NameListToString(sendName))));
322 * now have TypeCreate do all the real work.
325 TypeCreate(typeName, /* type name */
326 typeNamespace, /* namespace */
327 InvalidOid, /* preassigned type oid (not done here) */
328 InvalidOid, /* relation oid (n/a here) */
329 0, /* relation kind (ditto) */
330 internalLength, /* internal size */
331 'b', /* type-type (base type) */
332 delimiter, /* array element delimiter */
333 inputOid, /* input procedure */
334 outputOid, /* output procedure */
335 receiveOid, /* receive procedure */
336 sendOid, /* send procedure */
337 elemType, /* element type ID */
338 InvalidOid, /* base type ID (only for domains) */
339 defaultValue, /* default type value */
340 NULL, /* no binary form available */
341 byValue, /* passed by value */
342 alignment, /* required alignment */
343 storage, /* TOAST strategy */
344 -1, /* typMod (Domains only) */
345 0, /* Array Dimensions of typbasetype */
346 false); /* Type NOT NULL */
349 * When we create a base type (as opposed to a complex type) we need
350 * to have an array entry for it in pg_type as well.
352 shadow_type = makeArrayTypeName(typeName);
354 /* alignment must be 'i' or 'd' for arrays */
355 alignment = (alignment == 'd') ? 'd' : 'i';
357 TypeCreate(shadow_type, /* type name */
358 typeNamespace, /* namespace */
359 InvalidOid, /* preassigned type oid (not done here) */
360 InvalidOid, /* relation oid (n/a here) */
361 0, /* relation kind (ditto) */
362 -1, /* internal size */
363 'b', /* type-type (base type) */
364 DEFAULT_TYPDELIM, /* array element delimiter */
365 F_ARRAY_IN, /* input procedure */
366 F_ARRAY_OUT, /* output procedure */
367 F_ARRAY_RECV, /* receive procedure */
368 F_ARRAY_SEND, /* send procedure */
369 typoid, /* element type ID */
370 InvalidOid, /* base type ID */
371 NULL, /* never a default type value */
372 NULL, /* binary default isn't sent either */
373 false, /* never passed by value */
374 alignment, /* see above */
375 'x', /* ARRAY is always toastable */
376 -1, /* typMod (Domains only) */
377 0, /* Array dimensions of typbasetype */
378 false); /* Type NOT NULL */
386 * Removes a datatype.
389 RemoveType(List *names, DropBehavior behavior)
394 ObjectAddress object;
396 /* Make a TypeName so we can use standard type lookup machinery */
397 typename = makeNode(TypeName);
398 typename->names = names;
399 typename->typmod = -1;
400 typename->arrayBounds = NIL;
402 /* Use LookupTypeName here so that shell types can be removed. */
403 typeoid = LookupTypeName(typename);
404 if (!OidIsValid(typeoid))
406 (errcode(ERRCODE_UNDEFINED_OBJECT),
407 errmsg("type \"%s\" does not exist",
408 TypeNameToString(typename))));
410 tup = SearchSysCache(TYPEOID,
411 ObjectIdGetDatum(typeoid),
413 if (!HeapTupleIsValid(tup))
414 elog(ERROR, "cache lookup failed for type %u", typeoid);
416 /* Permission check: must own type or its namespace */
417 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
418 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
420 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
421 TypeNameToString(typename));
423 ReleaseSysCache(tup);
428 object.classId = RelOid_pg_type;
429 object.objectId = typeoid;
430 object.objectSubId = 0;
432 performDeletion(&object, behavior);
437 * Guts of type deletion.
440 RemoveTypeById(Oid typeOid)
445 relation = heap_openr(TypeRelationName, RowExclusiveLock);
447 tup = SearchSysCache(TYPEOID,
448 ObjectIdGetDatum(typeOid),
450 if (!HeapTupleIsValid(tup))
451 elog(ERROR, "cache lookup failed for type %u", typeOid);
453 simple_heap_delete(relation, &tup->t_self);
455 ReleaseSysCache(tup);
457 heap_close(relation, RowExclusiveLock);
463 * Registers a new domain.
466 DefineDomain(CreateDomainStmt *stmt)
471 int16 internalLength;
474 Oid receiveProcedure;
483 Node *defaultExpr = NULL;
484 char *defaultValue = NULL;
485 char *defaultValueBin = NULL;
486 bool typNotNull = false;
487 bool nullDefined = false;
489 int32 typNDims = length(stmt->typename->arrayBounds);
491 List *schema = stmt->constraints;
495 Form_pg_type baseType;
498 /* Convert list of names to a name and namespace */
499 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
502 /* Check we have creation rights in target namespace */
503 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
505 if (aclresult != ACLCHECK_OK)
506 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
507 get_namespace_name(domainNamespace));
510 * Domainnames, unlike typenames don't need to account for the '_'
511 * prefix. So they can be one character longer. (This test is presently
512 * useless since the parser will have truncated the name to fit. But
513 * leave it here since we may someday support arrays of domains, in
514 * which case we'll be back to needing to enforce NAMEDATALEN-2.)
516 if (strlen(domainName) > (NAMEDATALEN - 1))
518 (errcode(ERRCODE_INVALID_NAME),
519 errmsg("domain names must be %d characters or less",
523 * Look up the base type.
525 typeTup = typenameType(stmt->typename);
527 baseType = (Form_pg_type) GETSTRUCT(typeTup);
528 basetypeoid = HeapTupleGetOid(typeTup);
531 * Base type must be a plain base type. Domains over pseudo types
532 * would create a security hole. Domains of domains might be made to
533 * work in the future, but not today. Ditto for domains over complex
536 typtype = baseType->typtype;
539 (errcode(ERRCODE_DATATYPE_MISMATCH),
540 errmsg("\"%s\" is not a valid base type for a domain",
541 TypeNameToString(stmt->typename))));
543 /* passed by value */
544 byValue = baseType->typbyval;
546 /* Required Alignment */
547 alignment = baseType->typalign;
550 storage = baseType->typstorage;
553 internalLength = baseType->typlen;
555 /* Array element Delimiter */
556 delimiter = baseType->typdelim;
559 inputProcedure = baseType->typinput;
560 outputProcedure = baseType->typoutput;
561 receiveProcedure = baseType->typreceive;
562 sendProcedure = baseType->typsend;
564 /* Inherited default value */
565 datum = SysCacheGetAttr(TYPEOID, typeTup,
566 Anum_pg_type_typdefault, &isnull);
568 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
570 /* Inherited default binary value */
571 datum = SysCacheGetAttr(TYPEOID, typeTup,
572 Anum_pg_type_typdefaultbin, &isnull);
574 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
577 * Pull out the typelem name of the parent OID.
579 * This is what enables us to make a domain of an array
581 basetypelem = baseType->typelem;
584 * Run through constraints manually to avoid the additional
585 * processing conducted by DefineRelation() and friends.
587 foreach(listptr, schema)
589 Node *newConstraint = lfirst(listptr);
593 /* Check for unsupported constraint types */
594 if (IsA(newConstraint, FkConstraint))
596 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
597 errmsg("FOREIGN KEY constraints not supported for domains")));
599 /* otherwise it should be a plain Constraint */
600 if (!IsA(newConstraint, Constraint))
601 elog(ERROR, "unrecognized node type: %d",
602 (int) nodeTag(newConstraint));
604 constr = (Constraint *) newConstraint;
606 switch (constr->contype)
610 * The inherited default value may be overridden by the
611 * user with the DEFAULT <expr> statement.
615 (errcode(ERRCODE_SYNTAX_ERROR),
616 errmsg("multiple DEFAULT expressions")));
618 /* Create a dummy ParseState for transformExpr */
619 pstate = make_parsestate(NULL);
622 * Cook the constr->raw_expr into an expression. Note:
623 * Name is strictly for error message
625 defaultExpr = cookDefault(pstate, constr->raw_expr,
627 stmt->typename->typmod,
631 * Expression must be stored as a nodeToString result, but
632 * we also require a valid textual representation (mainly
633 * to make life easier for pg_dump).
635 defaultValue = deparse_expression(defaultExpr,
636 deparse_context_for(domainName,
639 defaultValueBin = nodeToString(defaultExpr);
643 if (nullDefined && !typNotNull)
645 (errcode(ERRCODE_SYNTAX_ERROR),
646 errmsg("conflicting NULL/NOT NULL constraints")));
652 if (nullDefined && typNotNull)
654 (errcode(ERRCODE_SYNTAX_ERROR),
655 errmsg("conflicting NULL/NOT NULL constraints")));
662 * Check constraints are handled after domain creation, as they
663 * require the Oid of the domain
668 * All else are error cases
672 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
673 errmsg("UNIQUE constraints not supported for domains")));
678 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
679 errmsg("PRIMARY KEY constraints not supported for domains")));
682 case CONSTR_ATTR_DEFERRABLE:
683 case CONSTR_ATTR_NOT_DEFERRABLE:
684 case CONSTR_ATTR_DEFERRED:
685 case CONSTR_ATTR_IMMEDIATE:
687 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
688 errmsg("deferrability constraints not supported for domains")));
692 elog(ERROR, "unrecognized constraint subtype: %d",
693 (int) constr->contype);
699 * Have TypeCreate do all the real work.
702 TypeCreate(domainName, /* type name */
703 domainNamespace, /* namespace */
704 InvalidOid, /* preassigned type oid (none here) */
705 InvalidOid, /* relation oid (n/a here) */
706 0, /* relation kind (ditto) */
707 internalLength, /* internal size */
708 'd', /* type-type (domain type) */
709 delimiter, /* array element delimiter */
710 inputProcedure, /* input procedure */
711 outputProcedure, /* output procedure */
712 receiveProcedure, /* receive procedure */
713 sendProcedure, /* send procedure */
714 basetypelem, /* element type ID */
715 basetypeoid, /* base type ID */
716 defaultValue, /* default type value (text) */
717 defaultValueBin, /* default type value (binary) */
718 byValue, /* passed by value */
719 alignment, /* required alignment */
720 storage, /* TOAST strategy */
721 stmt->typename->typmod, /* typeMod value */
722 typNDims, /* Array dimensions for base type */
723 typNotNull); /* Type NOT NULL */
726 * Process constraints which refer to the domain ID returned by TypeCreate
728 foreach(listptr, schema)
730 Constraint *constr = lfirst(listptr);
732 /* it must be a Constraint, per check above */
734 switch (constr->contype)
737 domainAddConstraint(domainoid, domainNamespace,
738 basetypeoid, stmt->typename->typmod,
739 constr, &counter, domainName);
742 /* Other constraint types were fully processed above */
750 * Now we can clean up.
752 ReleaseSysCache(typeTup);
760 * This is identical to RemoveType except we insist it be a domain.
763 RemoveDomain(List *names, DropBehavior behavior)
769 ObjectAddress object;
771 /* Make a TypeName so we can use standard type lookup machinery */
772 typename = makeNode(TypeName);
773 typename->names = names;
774 typename->typmod = -1;
775 typename->arrayBounds = NIL;
777 /* Use LookupTypeName here so that shell types can be removed. */
778 typeoid = LookupTypeName(typename);
779 if (!OidIsValid(typeoid))
781 (errcode(ERRCODE_UNDEFINED_OBJECT),
782 errmsg("type \"%s\" does not exist",
783 TypeNameToString(typename))));
785 tup = SearchSysCache(TYPEOID,
786 ObjectIdGetDatum(typeoid),
788 if (!HeapTupleIsValid(tup))
789 elog(ERROR, "cache lookup failed for type %u", typeoid);
791 /* Permission check: must own type or its namespace */
792 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
793 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
795 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
796 TypeNameToString(typename));
798 /* Check that this is actually a domain */
799 typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
803 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
804 errmsg("\"%s\" is not a domain",
805 TypeNameToString(typename))));
807 ReleaseSysCache(tup);
812 object.classId = RelOid_pg_type;
813 object.objectId = typeoid;
814 object.objectSubId = 0;
816 performDeletion(&object, behavior);
821 * Find suitable I/O functions for a type.
823 * typeOid is the type's OID (which will already exist, if only as a shell
828 findTypeInputFunction(List *procname, Oid typeOid)
830 Oid argList[FUNC_MAX_ARGS];
834 * Input functions can take a single argument of type CSTRING, or
835 * three arguments (string, element OID, typmod).
837 * For backwards compatibility we allow OPAQUE in place of CSTRING;
838 * if we see this, we issue a NOTICE and fix up the pg_proc entry.
840 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
842 argList[0] = CSTRINGOID;
844 procOid = LookupFuncName(procname, 1, argList, true);
845 if (OidIsValid(procOid))
849 argList[2] = INT4OID;
851 procOid = LookupFuncName(procname, 3, argList, true);
852 if (OidIsValid(procOid))
855 /* No luck, try it with OPAQUE */
856 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
858 argList[0] = OPAQUEOID;
860 procOid = LookupFuncName(procname, 1, argList, true);
862 if (!OidIsValid(procOid))
865 argList[2] = INT4OID;
867 procOid = LookupFuncName(procname, 3, argList, true);
870 if (OidIsValid(procOid))
872 /* Found, but must complain and fix the pg_proc entry */
874 (errmsg("changing argument type of function %s from OPAQUE to CSTRING",
875 NameListToString(procname))));
876 SetFunctionArgType(procOid, 0, CSTRINGOID);
878 * Need CommandCounterIncrement since DefineType will likely
879 * try to alter the pg_proc tuple again.
881 CommandCounterIncrement();
886 /* Use CSTRING (preferred) in the error message */
887 argList[0] = CSTRINGOID;
890 (errcode(ERRCODE_UNDEFINED_FUNCTION),
891 errmsg("function %s does not exist",
892 func_signature_string(procname, 1, argList))));
894 return InvalidOid; /* keep compiler quiet */
898 findTypeOutputFunction(List *procname, Oid typeOid)
900 Oid argList[FUNC_MAX_ARGS];
904 * Output functions can take a single argument of the type, or two
905 * arguments (data value, element OID).
907 * For backwards compatibility we allow OPAQUE in place of the actual
908 * type name; if we see this, we issue a NOTICE and fix up the
911 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
913 argList[0] = typeOid;
915 procOid = LookupFuncName(procname, 1, argList, true);
916 if (OidIsValid(procOid))
921 procOid = LookupFuncName(procname, 2, argList, true);
922 if (OidIsValid(procOid))
925 /* No luck, try it with OPAQUE */
926 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
928 argList[0] = OPAQUEOID;
930 procOid = LookupFuncName(procname, 1, argList, true);
932 if (!OidIsValid(procOid))
936 procOid = LookupFuncName(procname, 2, argList, true);
939 if (OidIsValid(procOid))
941 /* Found, but must complain and fix the pg_proc entry */
943 (errmsg("changing argument type of function %s from OPAQUE to %s",
944 NameListToString(procname), format_type_be(typeOid))));
945 SetFunctionArgType(procOid, 0, typeOid);
947 * Need CommandCounterIncrement since DefineType will likely
948 * try to alter the pg_proc tuple again.
950 CommandCounterIncrement();
955 /* Use type name, not OPAQUE, in the failure message. */
956 argList[0] = typeOid;
959 (errcode(ERRCODE_UNDEFINED_FUNCTION),
960 errmsg("function %s does not exist",
961 func_signature_string(procname, 1, argList))));
963 return InvalidOid; /* keep compiler quiet */
967 findTypeReceiveFunction(List *procname, Oid typeOid)
969 Oid argList[FUNC_MAX_ARGS];
973 * Receive functions can take a single argument of type INTERNAL, or
974 * two arguments (internal, oid).
976 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
978 argList[0] = INTERNALOID;
980 procOid = LookupFuncName(procname, 1, argList, true);
981 if (OidIsValid(procOid))
986 procOid = LookupFuncName(procname, 2, argList, true);
987 if (OidIsValid(procOid))
991 (errcode(ERRCODE_UNDEFINED_FUNCTION),
992 errmsg("function %s does not exist",
993 func_signature_string(procname, 1, argList))));
995 return InvalidOid; /* keep compiler quiet */
999 findTypeSendFunction(List *procname, Oid typeOid)
1001 Oid argList[FUNC_MAX_ARGS];
1005 * Send functions can take a single argument of the type, or two
1006 * arguments (data value, element OID).
1008 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
1010 argList[0] = typeOid;
1012 procOid = LookupFuncName(procname, 1, argList, true);
1013 if (OidIsValid(procOid))
1016 argList[1] = OIDOID;
1018 procOid = LookupFuncName(procname, 2, argList, true);
1019 if (OidIsValid(procOid))
1023 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1024 errmsg("function %s does not exist",
1025 func_signature_string(procname, 1, argList))));
1027 return InvalidOid; /* keep compiler quiet */
1031 /*-------------------------------------------------------------------
1032 * DefineCompositeType
1034 * Create a Composite Type relation.
1035 * `DefineRelation' does all the work, we just provide the correct
1038 * If the relation already exists, then 'DefineRelation' will abort
1041 * DefineCompositeType returns relid for use when creating
1042 * an implicit composite type during function creation
1043 *-------------------------------------------------------------------
1046 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1048 CreateStmt *createStmt = makeNode(CreateStmt);
1050 if (coldeflist == NIL)
1052 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1053 errmsg("composite type must have at least one attribute")));
1056 * now create the parameters for keys/inheritance etc. All of them are
1059 createStmt->relation = (RangeVar *) typevar;
1060 createStmt->tableElts = coldeflist;
1061 createStmt->inhRelations = NIL;
1062 createStmt->constraints = NIL;
1063 createStmt->hasoids = false;
1064 createStmt->oncommit = ONCOMMIT_NOOP;
1067 * finally create the relation...
1069 return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1073 * AlterDomainDefault
1075 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1078 AlterDomainDefault(List *names, Node *defaultRaw)
1086 Node *defaultExpr = NULL; /* NULL if no default specified */
1087 Datum new_record[Natts_pg_type];
1088 char new_record_nulls[Natts_pg_type];
1089 char new_record_repl[Natts_pg_type];
1091 Form_pg_type typTup;
1093 /* Make a TypeName so we can use standard type lookup machinery */
1094 typename = makeNode(TypeName);
1095 typename->names = names;
1096 typename->typmod = -1;
1097 typename->arrayBounds = NIL;
1099 /* Lock the domain in the type table */
1100 rel = heap_openr(TypeRelationName, RowExclusiveLock);
1102 /* Use LookupTypeName here so that shell types can be removed. */
1103 domainoid = LookupTypeName(typename);
1104 if (!OidIsValid(domainoid))
1106 (errcode(ERRCODE_UNDEFINED_OBJECT),
1107 errmsg("type \"%s\" does not exist",
1108 TypeNameToString(typename))));
1110 tup = SearchSysCacheCopy(TYPEOID,
1111 ObjectIdGetDatum(domainoid),
1113 if (!HeapTupleIsValid(tup))
1114 elog(ERROR, "cache lookup failed for type %u", domainoid);
1116 /* Doesn't return if user isn't allowed to alter the domain */
1117 domainOwnerCheck(tup, typename);
1119 /* Setup new tuple */
1120 MemSet(new_record, (Datum) 0, sizeof(new_record));
1121 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1122 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1125 typTup = (Form_pg_type) GETSTRUCT(tup);
1127 /* Store the new default, if null then skip this step */
1130 /* Create a dummy ParseState for transformExpr */
1131 pstate = make_parsestate(NULL);
1133 * Cook the colDef->raw_expr into an expression. Note:
1134 * Name is strictly for error message
1136 defaultExpr = cookDefault(pstate, defaultRaw,
1137 typTup->typbasetype,
1139 NameStr(typTup->typname));
1142 * Expression must be stored as a nodeToString result, but
1143 * we also require a valid textual representation (mainly
1144 * to make life easier for pg_dump).
1146 defaultValue = deparse_expression(defaultExpr,
1147 deparse_context_for(NameStr(typTup->typname),
1151 * Form an updated tuple with the new default and write it back.
1153 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
1155 nodeToString(defaultExpr)));
1157 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1158 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
1159 CStringGetDatum(defaultValue));
1160 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1162 else /* Default is NULL, drop it */
1164 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1165 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1166 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1167 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1170 newtuple = heap_modifytuple(tup, rel,
1171 new_record, new_record_nulls, new_record_repl);
1173 simple_heap_update(rel, &tup->t_self, newtuple);
1175 CatalogUpdateIndexes(rel, newtuple);
1177 /* Rebuild dependencies */
1178 GenerateTypeDependencies(typTup->typnamespace,
1181 0, /* relation kind is n/a */
1187 typTup->typbasetype,
1189 true); /* Rebuild is true */
1192 heap_close(rel, NoLock);
1193 heap_freetuple(newtuple);
1197 * AlterDomainNotNull
1199 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1202 AlterDomainNotNull(List *names, bool notNull)
1208 Form_pg_type typTup;
1210 /* Make a TypeName so we can use standard type lookup machinery */
1211 typename = makeNode(TypeName);
1212 typename->names = names;
1213 typename->typmod = -1;
1214 typename->arrayBounds = NIL;
1216 /* Lock the type table */
1217 typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1219 /* Use LookupTypeName here so that shell types can be found (why?). */
1220 domainoid = LookupTypeName(typename);
1221 if (!OidIsValid(domainoid))
1223 (errcode(ERRCODE_UNDEFINED_OBJECT),
1224 errmsg("type \"%s\" does not exist",
1225 TypeNameToString(typename))));
1227 tup = SearchSysCacheCopy(TYPEOID,
1228 ObjectIdGetDatum(domainoid),
1230 if (!HeapTupleIsValid(tup))
1231 elog(ERROR, "cache lookup failed for type %u", domainoid);
1232 typTup = (Form_pg_type) GETSTRUCT(tup);
1234 /* Doesn't return if user isn't allowed to alter the domain */
1235 domainOwnerCheck(tup, typename);
1237 /* Is the domain already set to the desired constraint? */
1238 if (typTup->typnotnull == notNull)
1241 (errmsg("\"%s\" is already set to %s",
1242 TypeNameToString(typename),
1243 notNull ? "NOT NULL" : "NULL")));
1244 heap_close(typrel, RowExclusiveLock);
1248 /* Adding a NOT NULL constraint requires checking existing columns */
1254 /* Fetch relation list with attributes based on this domain */
1255 /* ShareLock is sufficient to prevent concurrent data changes */
1257 rels = get_rels_with_domain(domainoid, ShareLock);
1261 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1262 Relation testrel = rtc->rel;
1263 TupleDesc tupdesc = RelationGetDescr(testrel);
1267 /* Scan all tuples in this relation */
1268 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1269 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1273 /* Test attributes that are of the domain */
1274 for (i = 0; i < rtc->natts; i++)
1276 int attnum = rtc->atts[i];
1280 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1284 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1285 errmsg("relation \"%s\" attribute \"%s\" contains NULL values",
1286 RelationGetRelationName(testrel),
1287 NameStr(tupdesc->attrs[attnum - 1]->attname))));
1292 /* Close each rel after processing, but keep lock */
1293 heap_close(testrel, NoLock);
1298 * Okay to update pg_type row. We can scribble on typTup because it's
1301 typTup->typnotnull = notNull;
1303 simple_heap_update(typrel, &tup->t_self, tup);
1305 CatalogUpdateIndexes(typrel, tup);
1308 heap_freetuple(tup);
1309 heap_close(typrel, RowExclusiveLock);
1313 * AlterDomainDropConstraint
1315 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1318 AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior)
1324 Form_pg_type typTup;
1326 SysScanDesc conscan;
1330 /* Make a TypeName so we can use standard type lookup machinery */
1331 typename = makeNode(TypeName);
1332 typename->names = names;
1333 typename->typmod = -1;
1334 typename->arrayBounds = NIL;
1336 /* Lock the type table */
1337 rel = heap_openr(TypeRelationName, RowExclusiveLock);
1339 /* Use LookupTypeName here so that shell types can be removed. */
1340 domainoid = LookupTypeName(typename);
1341 if (!OidIsValid(domainoid))
1343 (errcode(ERRCODE_UNDEFINED_OBJECT),
1344 errmsg("type \"%s\" does not exist",
1345 TypeNameToString(typename))));
1347 tup = SearchSysCacheCopy(TYPEOID,
1348 ObjectIdGetDatum(domainoid),
1350 if (!HeapTupleIsValid(tup))
1351 elog(ERROR, "cache lookup failed for type %u", domainoid);
1353 /* Doesn't return if user isn't allowed to alter the domain */
1354 domainOwnerCheck(tup, typename);
1356 /* Grab an appropriate lock on the pg_constraint relation */
1357 conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
1359 /* Use the index to scan only constraints of the target relation */
1360 ScanKeyEntryInitialize(&key[0], 0x0,
1361 Anum_pg_constraint_contypid, F_OIDEQ,
1362 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1364 conscan = systable_beginscan(conrel, ConstraintTypidIndex, true,
1365 SnapshotNow, 1, key);
1367 typTup = (Form_pg_type) GETSTRUCT(tup);
1370 * Scan over the result set, removing any matching entries.
1372 while ((contup = systable_getnext(conscan)) != NULL)
1374 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1376 if (strcmp(NameStr(con->conname), constrName) == 0)
1378 ObjectAddress conobj;
1380 conobj.classId = RelationGetRelid(conrel);
1381 conobj.objectId = HeapTupleGetOid(contup);
1382 conobj.objectSubId = 0;
1384 performDeletion(&conobj, behavior);
1387 /* Clean up after the scan */
1388 systable_endscan(conscan);
1389 heap_close(conrel, RowExclusiveLock);
1391 heap_close(rel, NoLock);
1395 * AlterDomainAddConstraint
1397 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1400 AlterDomainAddConstraint(List *names, Node *newConstraint)
1406 Form_pg_type typTup;
1410 ExprContext *econtext;
1413 ExprState *exprstate;
1417 /* Make a TypeName so we can use standard type lookup machinery */
1418 typename = makeNode(TypeName);
1419 typename->names = names;
1420 typename->typmod = -1;
1421 typename->arrayBounds = NIL;
1423 /* Lock the type table */
1424 typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1426 /* Use LookupTypeName here so that shell types can be found (why?). */
1427 domainoid = LookupTypeName(typename);
1428 if (!OidIsValid(domainoid))
1430 (errcode(ERRCODE_UNDEFINED_OBJECT),
1431 errmsg("type \"%s\" does not exist",
1432 TypeNameToString(typename))));
1434 tup = SearchSysCacheCopy(TYPEOID,
1435 ObjectIdGetDatum(domainoid),
1437 if (!HeapTupleIsValid(tup))
1438 elog(ERROR, "cache lookup failed for type %u", domainoid);
1439 typTup = (Form_pg_type) GETSTRUCT(tup);
1441 /* Doesn't return if user isn't allowed to alter the domain */
1442 domainOwnerCheck(tup, typename);
1444 /* Check for unsupported constraint types */
1445 if (IsA(newConstraint, FkConstraint))
1447 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1448 errmsg("FOREIGN KEY constraints not supported for domains")));
1450 /* otherwise it should be a plain Constraint */
1451 if (!IsA(newConstraint, Constraint))
1452 elog(ERROR, "unrecognized node type: %d",
1453 (int) nodeTag(newConstraint));
1455 constr = (Constraint *) newConstraint;
1457 switch (constr->contype)
1459 case CONSTR_DEFAULT:
1461 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1462 errmsg("use ALTER DOMAIN .. SET DEFAULT instead")));
1465 case CONSTR_NOTNULL:
1468 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1469 errmsg("use ALTER DOMAIN .. [ SET | DROP ] NOT NULL instead")));
1473 /* processed below */
1478 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1479 errmsg("UNIQUE constraints not supported for domains")));
1482 case CONSTR_PRIMARY:
1484 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1485 errmsg("PRIMARY KEY constraints not supported for domains")));
1488 case CONSTR_ATTR_DEFERRABLE:
1489 case CONSTR_ATTR_NOT_DEFERRABLE:
1490 case CONSTR_ATTR_DEFERRED:
1491 case CONSTR_ATTR_IMMEDIATE:
1493 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1494 errmsg("deferrability constraints not supported for domains")));
1498 elog(ERROR, "unrecognized constraint subtype: %d",
1499 (int) constr->contype);
1504 * Since all other constraint types throw errors, this must be
1505 * a check constraint. First, process the constraint expression
1506 * and add an entry to pg_constraint.
1509 ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1510 typTup->typbasetype, typTup->typtypmod,
1511 constr, &counter, NameStr(typTup->typname));
1514 * Test all values stored in the attributes based on the domain
1515 * the constraint is being added to.
1517 expr = (Expr *) stringToNode(ccbin);
1519 /* Need an EState to run ExecEvalExpr */
1520 estate = CreateExecutorState();
1521 econtext = GetPerTupleExprContext(estate);
1523 /* build execution state for expr */
1524 exprstate = ExecPrepareExpr(expr, estate);
1526 /* Fetch relation list with attributes based on this domain */
1527 /* ShareLock is sufficient to prevent concurrent data changes */
1529 rels = get_rels_with_domain(domainoid, ShareLock);
1533 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1534 Relation testrel = rtc->rel;
1535 TupleDesc tupdesc = RelationGetDescr(testrel);
1539 /* Scan all tuples in this relation */
1540 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1541 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1545 /* Test attributes that are of the domain */
1546 for (i = 0; i < rtc->natts; i++)
1548 int attnum = rtc->atts[i];
1553 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1555 econtext->domainValue_datum = d;
1556 econtext->domainValue_isNull = isNull;
1558 conResult = ExecEvalExprSwitchContext(exprstate,
1562 if (!isNull && !DatumGetBool(conResult))
1564 (errcode(ERRCODE_CHECK_VIOLATION),
1565 errmsg("relation \"%s\" attribute \"%s\" contains values that violate the new constraint",
1566 RelationGetRelationName(testrel),
1567 NameStr(tupdesc->attrs[attnum - 1]->attname))));
1570 ResetExprContext(econtext);
1574 /* Hold relation lock till commit (XXX bad for concurrency) */
1575 heap_close(testrel, NoLock);
1578 FreeExecutorState(estate);
1581 heap_close(typrel, RowExclusiveLock);
1585 * get_rels_with_domain
1587 * Fetch all relations / attributes which are using the domain
1589 * The result is a list of RelToCheck structs, one for each distinct
1590 * relation, each containing one or more attribute numbers that are of
1591 * the domain type. We have opened each rel and acquired the specified lock
1594 * XXX this is completely broken because there is no way to lock the domain
1595 * to prevent columns from being added or dropped while our command runs.
1596 * We can partially protect against column drops by locking relations as we
1597 * come across them, but there is still a race condition (the window between
1598 * seeing a pg_depend entry and acquiring lock on the relation it references).
1599 * Also, holding locks on all these relations simultaneously creates a non-
1600 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
1601 * risk by using the weakest suitable lock (ShareLock for most callers).
1603 * XXX to support domains over domains, we'd need to make this smarter,
1604 * or make its callers smarter, so that we could find columns of derived
1605 * domains. Arrays of domains would be a problem too.
1607 * Generally used for retrieving a list of tests when adding
1608 * new constraints to a domain.
1611 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1616 SysScanDesc depScan;
1620 * We scan pg_depend to find those things that depend on the domain.
1621 * (We assume we can ignore refobjsubid for a domain.)
1623 depRel = relation_openr(DependRelationName, AccessShareLock);
1625 ScanKeyEntryInitialize(&key[0], 0x0,
1626 Anum_pg_depend_refclassid, F_OIDEQ,
1627 ObjectIdGetDatum(RelOid_pg_type));
1628 ScanKeyEntryInitialize(&key[1], 0x0,
1629 Anum_pg_depend_refobjid, F_OIDEQ,
1630 ObjectIdGetDatum(domainOid));
1632 depScan = systable_beginscan(depRel, DependReferenceIndex, true,
1633 SnapshotNow, 2, key);
1635 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1637 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1638 RelToCheck *rtc = NULL;
1640 Form_pg_attribute pg_att;
1643 /* Ignore dependees that aren't user columns of tables */
1644 /* (we assume system columns are never of domain types) */
1645 if (pg_depend->classid != RelOid_pg_class ||
1646 pg_depend->objsubid <= 0)
1649 /* See if we already have an entry for this relation */
1650 foreach(rellist, result)
1652 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1654 if (RelationGetRelid(rt->rel) == pg_depend->objid)
1663 /* First attribute found for this relation */
1666 /* Acquire requested lock on relation */
1667 rel = heap_open(pg_depend->objid, lockmode);
1669 /* Build the RelToCheck entry with enough space for all atts */
1670 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1673 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1674 result = lcons(rtc, result);
1678 * Confirm column has not been dropped, and is of the expected type.
1679 * This defends against an ALTER DROP COLUMN occuring just before
1680 * we acquired lock ... but if the whole table were dropped, we'd
1681 * still have a problem.
1683 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
1685 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
1686 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
1690 * Okay, add column to result. We store the columns in column-number
1691 * order; this is just a hack to improve predictability of regression
1694 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
1697 while (ptr > 0 && rtc->atts[ptr-1] > pg_depend->objsubid)
1699 rtc->atts[ptr] = rtc->atts[ptr-1];
1702 rtc->atts[ptr] = pg_depend->objsubid;
1705 systable_endscan(depScan);
1707 relation_close(depRel, AccessShareLock);
1715 * Throw an error if the current user doesn't have permission to modify
1716 * the domain in an ALTER DOMAIN statement, or if the type isn't actually
1720 domainOwnerCheck(HeapTuple tup, TypeName *typename)
1722 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
1724 /* Check that this is actually a domain */
1725 if (typTup->typtype != 'd')
1727 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1728 errmsg("\"%s\" is not a domain",
1729 TypeNameToString(typename))));
1731 /* Permission check: must own type */
1732 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1733 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
1734 TypeNameToString(typename));
1738 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
1741 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
1742 int typMod, Constraint *constr,
1743 int *counter, char *domainName)
1749 CoerceToDomainValue *domVal;
1752 * Assign or validate constraint name
1756 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
1761 (errcode(ERRCODE_DUPLICATE_OBJECT),
1762 errmsg("constraint \"%s\" for domain \"%s\" already exists",
1763 constr->name, domainName)));
1766 constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN,
1772 * Convert the A_EXPR in raw_expr into an EXPR
1774 pstate = make_parsestate(NULL);
1777 * Set up a CoerceToDomainValue to represent the occurrence of VALUE
1778 * in the expression. Note that it will appear to have the type of the
1779 * base type, not the domain. This seems correct since within the
1780 * check expression, we should not assume the input value can be considered
1781 * a member of the domain.
1783 domVal = makeNode(CoerceToDomainValue);
1784 domVal->typeId = baseTypeOid;
1785 domVal->typeMod = typMod;
1787 pstate->p_value_substitute = (Node *) domVal;
1789 expr = transformExpr(pstate, constr->raw_expr);
1792 * Make sure it yields a boolean result.
1794 expr = coerce_to_boolean(pstate, expr, "CHECK");
1797 * Make sure no outside relations are referred to.
1799 if (length(pstate->p_rtable) != 0)
1801 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1802 errmsg("cannot use table references in domain CHECK constraint")));
1805 * Domains don't allow var clauses (this should be redundant with the
1806 * above check, but make it anyway)
1808 if (contain_var_clause(expr))
1810 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1811 errmsg("cannot use table references in domain CHECK constraint")));
1814 * No subplans or aggregates, either...
1816 if (pstate->p_hasSubLinks)
1818 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1819 errmsg("cannot use sub-select in CHECK constraint")));
1820 if (pstate->p_hasAggs)
1822 (errcode(ERRCODE_GROUPING_ERROR),
1823 errmsg("cannot use aggregate in CHECK constraint")));
1826 * Convert to string form for storage.
1828 ccbin = nodeToString(expr);
1831 * Deparse it to produce text for consrc.
1833 * Since VARNOs aren't allowed in domain constraints, relation context
1834 * isn't required as anything other than a shell.
1836 ccsrc = deparse_expression(expr,
1837 deparse_context_for(domainName,
1842 * Store the constraint in pg_constraint
1844 CreateConstraintEntry(constr->name, /* Constraint Name */
1845 domainNamespace, /* namespace */
1846 CONSTRAINT_CHECK, /* Constraint Type */
1847 false, /* Is Deferrable */
1848 false, /* Is Deferred */
1849 InvalidOid, /* not a relation constraint */
1852 domainOid, /* domain constraint */
1853 InvalidOid, /* Foreign key fields */
1860 expr, /* Tree form check constraint */
1861 ccbin, /* Binary form check constraint */
1862 ccsrc); /* Source form check constraint */
1865 * Return the compiled constraint expression so the calling routine can
1866 * perform any additional required tests.
1872 * GetDomainConstraints - get a list of the current constraints of domain
1874 * Returns a possibly-empty list of DomainConstraintState nodes.
1876 * This is called by the executor during plan startup for a CoerceToDomain
1877 * expression node. The given constraints will be checked for each value
1878 * passed through the node.
1881 GetDomainConstraints(Oid typeOid)
1884 bool notNull = false;
1887 conRel = heap_openr(ConstraintRelationName, AccessShareLock);
1893 Form_pg_type typTup;
1897 tup = SearchSysCache(TYPEOID,
1898 ObjectIdGetDatum(typeOid),
1900 if (!HeapTupleIsValid(tup))
1901 elog(ERROR, "cache lookup failed for type %u", typeOid);
1902 typTup = (Form_pg_type) GETSTRUCT(tup);
1904 /* Test for NOT NULL Constraint */
1905 if (typTup->typnotnull)
1908 /* Look for CHECK Constraints on this domain */
1909 ScanKeyEntryInitialize(&key[0], 0x0,
1910 Anum_pg_constraint_contypid, F_OIDEQ,
1911 ObjectIdGetDatum(typeOid));
1913 scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
1914 SnapshotNow, 1, key);
1916 while (HeapTupleIsValid(conTup = systable_getnext(scan)))
1918 Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
1922 DomainConstraintState *r;
1924 /* Ignore non-CHECK constraints (presently, shouldn't be any) */
1925 if (c->contype != CONSTRAINT_CHECK)
1928 /* Not expecting conbin to be NULL, but we'll test for it anyway */
1929 val = fastgetattr(conTup, Anum_pg_constraint_conbin,
1930 conRel->rd_att, &isNull);
1932 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1933 NameStr(typTup->typname), NameStr(c->conname));
1935 check_expr = (Expr *)
1936 stringToNode(DatumGetCString(DirectFunctionCall1(textout,
1939 /* ExecInitExpr assumes we already fixed opfuncids */
1940 fix_opfuncids((Node *) check_expr);
1942 r = makeNode(DomainConstraintState);
1943 r->constrainttype = DOM_CONSTRAINT_CHECK;
1944 r->name = pstrdup(NameStr(c->conname));
1945 r->check_expr = ExecInitExpr(check_expr, NULL);
1948 * use lcons() here because constraints of lower domains should
1949 * be applied earlier.
1951 result = lcons(r, result);
1954 systable_endscan(scan);
1956 if (typTup->typtype != 'd')
1958 /* Not a domain, so done */
1959 ReleaseSysCache(tup);
1963 /* else loop to next domain in stack */
1964 typeOid = typTup->typbasetype;
1965 ReleaseSysCache(tup);
1968 heap_close(conRel, AccessShareLock);
1971 * Only need to add one NOT NULL check regardless of how many domains
1972 * in the stack request it.
1976 DomainConstraintState *r = makeNode(DomainConstraintState);
1978 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
1979 r->name = pstrdup("NOT NULL");
1980 r->check_expr = NULL;
1982 /* lcons to apply the nullness check FIRST */
1983 result = lcons(r, result);
1990 * ALTER DOMAIN .. OWNER TO
1992 * Eventually this should allow changing ownership of other kinds of types,
1993 * but some thought must be given to handling complex types. (A table's
1994 * rowtype probably shouldn't be allowed as target, but what of a standalone
1997 * Assumes that permission checks have been completed earlier.
2000 AlterTypeOwner(List *names, AclId newOwnerSysId)
2006 Form_pg_type typTup;
2008 /* Make a TypeName so we can use standard type lookup machinery */
2009 typename = makeNode(TypeName);
2010 typename->names = names;
2011 typename->typmod = -1;
2012 typename->arrayBounds = NIL;
2014 /* Lock the type table */
2015 rel = heap_openr(TypeRelationName, RowExclusiveLock);
2017 /* Use LookupTypeName here so that shell types can be processed (why?) */
2018 typeOid = LookupTypeName(typename);
2019 if (!OidIsValid(typeOid))
2021 (errcode(ERRCODE_UNDEFINED_OBJECT),
2022 errmsg("type \"%s\" does not exist",
2023 TypeNameToString(typename))));
2025 tup = SearchSysCacheCopy(TYPEOID,
2026 ObjectIdGetDatum(typeOid),
2028 if (!HeapTupleIsValid(tup))
2029 elog(ERROR, "cache lookup failed for type %u", typeOid);
2030 typTup = (Form_pg_type) GETSTRUCT(tup);
2032 /* Check that this is actually a domain */
2033 if (typTup->typtype != 'd')
2035 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2036 errmsg("\"%s\" is not a domain",
2037 TypeNameToString(typename))));
2039 /* Modify the owner --- okay to scribble on typTup because it's a copy */
2040 typTup->typowner = newOwnerSysId;
2042 simple_heap_update(rel, &tup->t_self, tup);
2044 CatalogUpdateIndexes(rel, tup);
2047 heap_close(rel, RowExclusiveLock);