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.26 2003/01/04 00:46:08 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 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/nodes.h"
50 #include "optimizer/clauses.h"
51 #include "optimizer/var.h"
52 #include "parser/parse_coerce.h"
53 #include "parser/parse_expr.h"
54 #include "parser/parse_func.h"
55 #include "parser/parse_relation.h"
56 #include "parser/parse_type.h"
57 #include "utils/acl.h"
58 #include "utils/builtins.h"
59 #include "utils/fmgroids.h"
60 #include "utils/lsyscache.h"
61 #include "utils/syscache.h"
64 /* result structure for get_rels_with_domain() */
67 Relation rel; /* opened and locked relation */
68 int natts; /* number of attributes of interest */
69 int *atts; /* attribute numbers */
70 /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
74 static Oid findTypeIOFunction(List *procname, Oid typeOid, bool isOutput);
75 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
76 static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
77 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
79 int typMod, Constraint *constr,
80 int *counter, char *domainName);
85 * Registers a new type.
88 DefineType(List *names, List *parameters)
93 int16 internalLength = -1; /* int2 */
94 Oid elemType = InvalidOid;
95 List *inputName = NIL;
96 List *outputName = NIL;
97 char *defaultValue = NULL;
99 char delimiter = DEFAULT_TYPDELIM;
100 char alignment = 'i'; /* default alignment */
101 char storage = 'p'; /* default TOAST storage method */
109 /* Convert list of names to a name and namespace */
110 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
112 /* Check we have creation rights in target namespace */
113 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
114 if (aclresult != ACLCHECK_OK)
115 aclcheck_error(aclresult, get_namespace_name(typeNamespace));
118 * Type names must be one character shorter than other names, allowing
119 * room to create the corresponding array type name with prepended
122 if (strlen(typeName) > (NAMEDATALEN - 2))
123 elog(ERROR, "DefineType: type names must be %d characters or less",
126 foreach(pl, parameters)
128 DefElem *defel = (DefElem *) lfirst(pl);
130 if (strcasecmp(defel->defname, "internallength") == 0)
131 internalLength = defGetTypeLength(defel);
132 else if (strcasecmp(defel->defname, "externallength") == 0)
133 ; /* ignored -- remove after 7.3 */
134 else if (strcasecmp(defel->defname, "input") == 0)
135 inputName = defGetQualifiedName(defel);
136 else if (strcasecmp(defel->defname, "output") == 0)
137 outputName = defGetQualifiedName(defel);
138 else if (strcasecmp(defel->defname, "send") == 0)
139 ; /* ignored -- remove after 7.3 */
140 else if (strcasecmp(defel->defname, "receive") == 0)
141 ; /* ignored -- remove after 7.3 */
142 else if (strcasecmp(defel->defname, "delimiter") == 0)
144 char *p = defGetString(defel);
148 else if (strcasecmp(defel->defname, "element") == 0)
150 elemType = typenameTypeId(defGetTypeName(defel));
151 /* disallow arrays of pseudotypes */
152 if (get_typtype(elemType) == 'p')
153 elog(ERROR, "Array element type cannot be %s",
154 format_type_be(elemType));
156 else if (strcasecmp(defel->defname, "default") == 0)
157 defaultValue = defGetString(defel);
158 else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
160 else if (strcasecmp(defel->defname, "alignment") == 0)
162 char *a = defGetString(defel);
165 * Note: if argument was an unquoted identifier, parser will
166 * have applied translations to it, so be prepared to
167 * recognize translated type names as well as the nominal
170 if (strcasecmp(a, "double") == 0 ||
171 strcasecmp(a, "float8") == 0 ||
172 strcasecmp(a, "pg_catalog.float8") == 0)
174 else if (strcasecmp(a, "int4") == 0 ||
175 strcasecmp(a, "pg_catalog.int4") == 0)
177 else if (strcasecmp(a, "int2") == 0 ||
178 strcasecmp(a, "pg_catalog.int2") == 0)
180 else if (strcasecmp(a, "char") == 0 ||
181 strcasecmp(a, "pg_catalog.bpchar") == 0)
184 elog(ERROR, "DefineType: \"%s\" alignment not recognized",
187 else if (strcasecmp(defel->defname, "storage") == 0)
189 char *a = defGetString(defel);
191 if (strcasecmp(a, "plain") == 0)
193 else if (strcasecmp(a, "external") == 0)
195 else if (strcasecmp(a, "extended") == 0)
197 else if (strcasecmp(a, "main") == 0)
200 elog(ERROR, "DefineType: \"%s\" storage not recognized",
205 elog(WARNING, "DefineType: attribute \"%s\" not recognized",
211 * make sure we have our required definitions
213 if (inputName == NIL)
214 elog(ERROR, "Define: \"input\" unspecified");
215 if (outputName == NIL)
216 elog(ERROR, "Define: \"output\" unspecified");
219 * Look to see if type already exists (presumably as a shell; if not,
220 * TypeCreate will complain). If it doesn't, create it as a shell,
221 * so that the OID is known for use in the I/O function definitions.
223 typoid = GetSysCacheOid(TYPENAMENSP,
224 CStringGetDatum(typeName),
225 ObjectIdGetDatum(typeNamespace),
227 if (!OidIsValid(typoid))
229 typoid = TypeShellMake(typeName, typeNamespace);
230 /* Make new shell type visible for modification below */
231 CommandCounterIncrement();
235 * Convert I/O proc names to OIDs
237 inputOid = findTypeIOFunction(inputName, typoid, false);
238 outputOid = findTypeIOFunction(outputName, typoid, true);
241 * Verify that I/O procs return the expected thing. If we see OPAQUE,
242 * complain and change it to the correct type-safe choice.
244 resulttype = get_func_rettype(inputOid);
245 if (resulttype != typoid)
247 if (resulttype == OPAQUEOID)
249 elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to %s",
250 NameListToString(inputName), typeName);
251 SetFunctionReturnType(inputOid, typoid);
254 elog(ERROR, "Type input function %s must return %s",
255 NameListToString(inputName), typeName);
257 resulttype = get_func_rettype(outputOid);
258 if (resulttype != CSTRINGOID)
260 if (resulttype == OPAQUEOID)
262 elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to CSTRING",
263 NameListToString(outputName));
264 SetFunctionReturnType(outputOid, CSTRINGOID);
267 elog(ERROR, "Type output function %s must return cstring",
268 NameListToString(outputName));
272 * now have TypeCreate do all the real work.
275 TypeCreate(typeName, /* type name */
276 typeNamespace, /* namespace */
277 InvalidOid, /* preassigned type oid (not done here) */
278 InvalidOid, /* relation oid (n/a here) */
279 0, /* relation kind (ditto) */
280 internalLength, /* internal size */
281 'b', /* type-type (base type) */
282 delimiter, /* array element delimiter */
283 inputOid, /* input procedure */
284 outputOid, /* output procedure */
285 elemType, /* element type ID */
286 InvalidOid, /* base type ID (only for domains) */
287 defaultValue, /* default type value */
288 NULL, /* no binary form available */
289 byValue, /* passed by value */
290 alignment, /* required alignment */
291 storage, /* TOAST strategy */
292 -1, /* typMod (Domains only) */
293 0, /* Array Dimensions of typbasetype */
294 false); /* Type NOT NULL */
297 * When we create a base type (as opposed to a complex type) we need
298 * to have an array entry for it in pg_type as well.
300 shadow_type = makeArrayTypeName(typeName);
302 /* alignment must be 'i' or 'd' for arrays */
303 alignment = (alignment == 'd') ? 'd' : 'i';
305 TypeCreate(shadow_type, /* type name */
306 typeNamespace, /* namespace */
307 InvalidOid, /* preassigned type oid (not done here) */
308 InvalidOid, /* relation oid (n/a here) */
309 0, /* relation kind (ditto) */
310 -1, /* internal size */
311 'b', /* type-type (base type) */
312 DEFAULT_TYPDELIM, /* array element delimiter */
313 F_ARRAY_IN, /* input procedure */
314 F_ARRAY_OUT, /* output procedure */
315 typoid, /* element type ID */
316 InvalidOid, /* base type ID */
317 NULL, /* never a default type value */
318 NULL, /* binary default isn't sent either */
319 false, /* never passed by value */
320 alignment, /* see above */
321 'x', /* ARRAY is always toastable */
322 -1, /* typMod (Domains only) */
323 0, /* Array dimensions of typbasetype */
324 false); /* Type NOT NULL */
332 * Removes a datatype.
335 RemoveType(List *names, DropBehavior behavior)
340 ObjectAddress object;
342 /* Make a TypeName so we can use standard type lookup machinery */
343 typename = makeNode(TypeName);
344 typename->names = names;
345 typename->typmod = -1;
346 typename->arrayBounds = NIL;
348 /* Use LookupTypeName here so that shell types can be removed. */
349 typeoid = LookupTypeName(typename);
350 if (!OidIsValid(typeoid))
351 elog(ERROR, "Type \"%s\" does not exist",
352 TypeNameToString(typename));
354 tup = SearchSysCache(TYPEOID,
355 ObjectIdGetDatum(typeoid),
357 if (!HeapTupleIsValid(tup))
358 elog(ERROR, "Type \"%s\" does not exist",
359 TypeNameToString(typename));
361 /* Permission check: must own type or its namespace */
362 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
363 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
365 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
367 ReleaseSysCache(tup);
372 object.classId = RelOid_pg_type;
373 object.objectId = typeoid;
374 object.objectSubId = 0;
376 performDeletion(&object, behavior);
381 * Guts of type deletion.
384 RemoveTypeById(Oid typeOid)
389 relation = heap_openr(TypeRelationName, RowExclusiveLock);
391 tup = SearchSysCache(TYPEOID,
392 ObjectIdGetDatum(typeOid),
394 if (!HeapTupleIsValid(tup))
395 elog(ERROR, "RemoveTypeById: type %u not found",
398 simple_heap_delete(relation, &tup->t_self);
400 ReleaseSysCache(tup);
402 heap_close(relation, RowExclusiveLock);
408 * Registers a new domain.
411 DefineDomain(CreateDomainStmt *stmt)
416 int16 internalLength;
426 Node *defaultExpr = NULL;
427 char *defaultValue = NULL;
428 char *defaultValueBin = NULL;
429 bool typNotNull = false;
430 bool nullDefined = false;
432 int32 typNDims = length(stmt->typename->arrayBounds);
434 List *schema = stmt->constraints;
438 Form_pg_type baseType;
441 /* Convert list of names to a name and namespace */
442 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
445 /* Check we have creation rights in target namespace */
446 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
448 if (aclresult != ACLCHECK_OK)
449 aclcheck_error(aclresult, get_namespace_name(domainNamespace));
452 * Domainnames, unlike typenames don't need to account for the '_'
453 * prefix. So they can be one character longer.
455 if (strlen(domainName) > (NAMEDATALEN - 1))
456 elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
460 * Look up the base type.
462 typeTup = typenameType(stmt->typename);
464 baseType = (Form_pg_type) GETSTRUCT(typeTup);
465 basetypeoid = HeapTupleGetOid(typeTup);
468 * Base type must be a plain base type. Domains over pseudo types
469 * would create a security hole. Domains of domains might be made to
470 * work in the future, but not today. Ditto for domains over complex
473 typtype = baseType->typtype;
475 elog(ERROR, "DefineDomain: %s is not a basetype",
476 TypeNameToString(stmt->typename));
478 /* passed by value */
479 byValue = baseType->typbyval;
481 /* Required Alignment */
482 alignment = baseType->typalign;
485 storage = baseType->typstorage;
488 internalLength = baseType->typlen;
490 /* Array element Delimiter */
491 delimiter = baseType->typdelim;
494 inputProcedure = baseType->typinput;
495 outputProcedure = baseType->typoutput;
497 /* Inherited default value */
498 datum = SysCacheGetAttr(TYPEOID, typeTup,
499 Anum_pg_type_typdefault, &isnull);
501 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
503 /* Inherited default binary value */
504 datum = SysCacheGetAttr(TYPEOID, typeTup,
505 Anum_pg_type_typdefaultbin, &isnull);
507 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
510 * Pull out the typelem name of the parent OID.
512 * This is what enables us to make a domain of an array
514 basetypelem = baseType->typelem;
517 * Run through constraints manually to avoid the additional
518 * processing conducted by DefineRelation() and friends.
520 foreach(listptr, schema)
522 Node *newConstraint = lfirst(listptr);
526 /* Check for unsupported constraint types */
527 if (IsA(newConstraint, FkConstraint))
528 elog(ERROR, "CREATE DOMAIN / FOREIGN KEY constraints not supported");
530 /* this case should not happen */
531 if (!IsA(newConstraint, Constraint))
532 elog(ERROR, "DefineDomain: unexpected constraint node type");
534 colDef = (Constraint *) newConstraint;
536 switch (colDef->contype)
540 * The inherited default value may be overridden by the
541 * user with the DEFAULT <expr> statement.
544 elog(ERROR, "CREATE DOMAIN has multiple DEFAULT expressions");
545 /* Create a dummy ParseState for transformExpr */
546 pstate = make_parsestate(NULL);
549 * Cook the colDef->raw_expr into an expression. Note:
550 * Name is strictly for error message
552 defaultExpr = cookDefault(pstate, colDef->raw_expr,
554 stmt->typename->typmod,
558 * Expression must be stored as a nodeToString result, but
559 * we also require a valid textual representation (mainly
560 * to make life easier for pg_dump).
562 defaultValue = deparse_expression(defaultExpr,
563 deparse_context_for(domainName,
566 defaultValueBin = nodeToString(defaultExpr);
570 if (nullDefined && !typNotNull)
571 elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
577 if (nullDefined && typNotNull)
578 elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
585 * Check constraints are handled after domain creation, as they
586 * require the Oid of the domain
591 * All else are error cases
594 elog(ERROR, "CREATE DOMAIN / UNIQUE not supported");
598 elog(ERROR, "CREATE DOMAIN / PRIMARY KEY not supported");
601 case CONSTR_ATTR_DEFERRABLE:
602 case CONSTR_ATTR_NOT_DEFERRABLE:
603 case CONSTR_ATTR_DEFERRED:
604 case CONSTR_ATTR_IMMEDIATE:
605 elog(ERROR, "CREATE DOMAIN: DEFERRABLE, NON DEFERRABLE, DEFERRED"
606 " and IMMEDIATE not supported");
610 elog(ERROR, "DefineDomain: unrecognized constraint subtype");
616 * Have TypeCreate do all the real work.
619 TypeCreate(domainName, /* type name */
620 domainNamespace, /* namespace */
621 InvalidOid, /* preassigned type oid (none here) */
622 InvalidOid, /* relation oid (n/a here) */
623 0, /* relation kind (ditto) */
624 internalLength, /* internal size */
625 'd', /* type-type (domain type) */
626 delimiter, /* array element delimiter */
627 inputProcedure, /* input procedure */
628 outputProcedure, /* output procedure */
629 basetypelem, /* element type ID */
630 basetypeoid, /* base type ID */
631 defaultValue, /* default type value (text) */
632 defaultValueBin, /* default type value (binary) */
633 byValue, /* passed by value */
634 alignment, /* required alignment */
635 storage, /* TOAST strategy */
636 stmt->typename->typmod, /* typeMod value */
637 typNDims, /* Array dimensions for base type */
638 typNotNull); /* Type NOT NULL */
641 * Process constraints which refer to the domain ID returned by TypeCreate
643 foreach(listptr, schema)
645 Constraint *constr = lfirst(listptr);
647 /* it must be a Constraint, per check above */
649 switch (constr->contype)
652 domainAddConstraint(domainoid, domainNamespace,
653 basetypeoid, stmt->typename->typmod,
654 constr, &counter, domainName);
657 /* Other constraint types were fully processed above */
665 * Now we can clean up.
667 ReleaseSysCache(typeTup);
675 * This is identical to RemoveType except we insist it be a domain.
678 RemoveDomain(List *names, DropBehavior behavior)
684 ObjectAddress object;
686 /* Make a TypeName so we can use standard type lookup machinery */
687 typename = makeNode(TypeName);
688 typename->names = names;
689 typename->typmod = -1;
690 typename->arrayBounds = NIL;
692 /* Use LookupTypeName here so that shell types can be removed. */
693 typeoid = LookupTypeName(typename);
694 if (!OidIsValid(typeoid))
695 elog(ERROR, "Type \"%s\" does not exist",
696 TypeNameToString(typename));
698 tup = SearchSysCache(TYPEOID,
699 ObjectIdGetDatum(typeoid),
701 if (!HeapTupleIsValid(tup))
702 elog(ERROR, "RemoveDomain: type \"%s\" does not exist",
703 TypeNameToString(typename));
705 /* Permission check: must own type or its namespace */
706 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
707 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
709 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
711 /* Check that this is actually a domain */
712 typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
715 elog(ERROR, "%s is not a domain",
716 TypeNameToString(typename));
718 ReleaseSysCache(tup);
723 object.classId = RelOid_pg_type;
724 object.objectId = typeoid;
725 object.objectSubId = 0;
727 performDeletion(&object, behavior);
732 * Find a suitable I/O function for a type.
734 * typeOid is the type's OID (which will already exist, if only as a shell
738 findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
740 Oid argList[FUNC_MAX_ARGS];
746 * Output functions can take a single argument of the type, or two
747 * arguments (data value, element OID).
749 * For backwards compatibility we allow OPAQUE in place of the actual
750 * type name; if we see this, we issue a NOTICE and fix up the
753 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
755 argList[0] = typeOid;
757 procOid = LookupFuncName(procname, 1, argList);
758 if (OidIsValid(procOid))
763 procOid = LookupFuncName(procname, 2, argList);
764 if (OidIsValid(procOid))
767 /* No luck, try it with OPAQUE */
768 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
770 argList[0] = OPAQUEOID;
772 procOid = LookupFuncName(procname, 1, argList);
774 if (!OidIsValid(procOid))
778 procOid = LookupFuncName(procname, 2, argList);
781 if (OidIsValid(procOid))
783 /* Found, but must complain and fix the pg_proc entry */
784 elog(NOTICE, "TypeCreate: changing argument type of function %s from OPAQUE to %s",
785 NameListToString(procname), format_type_be(typeOid));
786 SetFunctionArgType(procOid, 0, typeOid);
788 * Need CommandCounterIncrement since DefineType will likely
789 * try to alter the pg_proc tuple again.
791 CommandCounterIncrement();
796 /* Use type name, not OPAQUE, in the failure message. */
797 argList[0] = typeOid;
799 func_error("TypeCreate", procname, 1, argList, NULL);
804 * Input functions can take a single argument of type CSTRING, or
805 * three arguments (string, element OID, typmod).
807 * For backwards compatibility we allow OPAQUE in place of CSTRING;
808 * if we see this, we issue a NOTICE and fix up the pg_proc entry.
810 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
812 argList[0] = CSTRINGOID;
814 procOid = LookupFuncName(procname, 1, argList);
815 if (OidIsValid(procOid))
819 argList[2] = INT4OID;
821 procOid = LookupFuncName(procname, 3, argList);
822 if (OidIsValid(procOid))
825 /* No luck, try it with OPAQUE */
826 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
828 argList[0] = OPAQUEOID;
830 procOid = LookupFuncName(procname, 1, argList);
832 if (!OidIsValid(procOid))
835 argList[2] = INT4OID;
837 procOid = LookupFuncName(procname, 3, argList);
840 if (OidIsValid(procOid))
842 /* Found, but must complain and fix the pg_proc entry */
843 elog(NOTICE, "TypeCreate: changing argument type of function %s "
844 "from OPAQUE to CSTRING",
845 NameListToString(procname));
846 SetFunctionArgType(procOid, 0, CSTRINGOID);
848 * Need CommandCounterIncrement since DefineType will likely
849 * try to alter the pg_proc tuple again.
851 CommandCounterIncrement();
856 /* Use CSTRING (preferred) in the error message */
857 argList[0] = CSTRINGOID;
859 func_error("TypeCreate", procname, 1, argList, NULL);
862 return InvalidOid; /* keep compiler quiet */
866 /*-------------------------------------------------------------------
867 * DefineCompositeType
869 * Create a Composite Type relation.
870 * `DefineRelation' does all the work, we just provide the correct
873 * If the relation already exists, then 'DefineRelation' will abort
876 * DefineCompositeType returns relid for use when creating
877 * an implicit composite type during function creation
878 *-------------------------------------------------------------------
881 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
883 CreateStmt *createStmt = makeNode(CreateStmt);
885 if (coldeflist == NIL)
886 elog(ERROR, "attempted to define composite type relation with"
890 * now create the parameters for keys/inheritance etc. All of them are
893 createStmt->relation = (RangeVar *) typevar;
894 createStmt->tableElts = coldeflist;
895 createStmt->inhRelations = NIL;
896 createStmt->constraints = NIL;
897 createStmt->hasoids = false;
898 createStmt->oncommit = ONCOMMIT_NOOP;
901 * finally create the relation...
903 return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
909 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
912 AlterDomainDefault(List *names, Node *defaultRaw)
920 Node *defaultExpr = NULL; /* NULL if no default specified */
921 Datum new_record[Natts_pg_type];
922 char new_record_nulls[Natts_pg_type];
923 char new_record_repl[Natts_pg_type];
927 /* Make a TypeName so we can use standard type lookup machinery */
928 typename = makeNode(TypeName);
929 typename->names = names;
930 typename->typmod = -1;
931 typename->arrayBounds = NIL;
933 /* Lock the domain in the type table */
934 rel = heap_openr(TypeRelationName, RowExclusiveLock);
936 /* Use LookupTypeName here so that shell types can be removed. */
937 domainoid = LookupTypeName(typename);
938 if (!OidIsValid(domainoid))
939 elog(ERROR, "Type \"%s\" does not exist",
940 TypeNameToString(typename));
942 tup = SearchSysCacheCopy(TYPEOID,
943 ObjectIdGetDatum(domainoid),
946 if (!HeapTupleIsValid(tup))
947 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
948 TypeNameToString(typename));
950 /* Doesn't return if user isn't allowed to alter the domain */
951 domainOwnerCheck(tup, typename);
953 /* Setup new tuple */
954 MemSet(new_record, (Datum) 0, sizeof(new_record));
955 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
956 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
959 typTup = (Form_pg_type) GETSTRUCT(tup);
961 /* Store the new default, if null then skip this step */
964 /* Create a dummy ParseState for transformExpr */
965 pstate = make_parsestate(NULL);
967 * Cook the colDef->raw_expr into an expression. Note:
968 * Name is strictly for error message
970 defaultExpr = cookDefault(pstate, defaultRaw,
973 NameStr(typTup->typname));
976 * Expression must be stored as a nodeToString result, but
977 * we also require a valid textual representation (mainly
978 * to make life easier for pg_dump).
980 defaultValue = deparse_expression(defaultExpr,
981 deparse_context_for(NameStr(typTup->typname),
985 * Form an updated tuple with the new default and write it back.
987 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
989 nodeToString(defaultExpr)));
991 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
992 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
993 CStringGetDatum(defaultValue));
994 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
996 else /* Default is NULL, drop it */
998 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
999 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1000 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1001 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1004 newtuple = heap_modifytuple(tup, rel,
1005 new_record, new_record_nulls, new_record_repl);
1007 simple_heap_update(rel, &tup->t_self, newtuple);
1009 CatalogUpdateIndexes(rel, newtuple);
1011 /* Rebuild dependencies */
1012 GenerateTypeDependencies(typTup->typnamespace,
1019 typTup->typbasetype,
1020 nodeToString(defaultExpr),
1021 true); /* Rebuild is true */
1024 heap_close(rel, NoLock);
1025 heap_freetuple(newtuple);
1029 * AlterDomainNotNull
1031 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1034 AlterDomainNotNull(List *names, bool notNull)
1040 Form_pg_type typTup;
1042 /* Make a TypeName so we can use standard type lookup machinery */
1043 typename = makeNode(TypeName);
1044 typename->names = names;
1045 typename->typmod = -1;
1046 typename->arrayBounds = NIL;
1048 /* Lock the type table */
1049 typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1051 /* Use LookupTypeName here so that shell types can be found (why?). */
1052 domainoid = LookupTypeName(typename);
1053 if (!OidIsValid(domainoid))
1054 elog(ERROR, "Type \"%s\" does not exist",
1055 TypeNameToString(typename));
1057 tup = SearchSysCacheCopy(TYPEOID,
1058 ObjectIdGetDatum(domainoid),
1060 if (!HeapTupleIsValid(tup))
1061 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
1062 TypeNameToString(typename));
1063 typTup = (Form_pg_type) GETSTRUCT(tup);
1065 /* Doesn't return if user isn't allowed to alter the domain */
1066 domainOwnerCheck(tup, typename);
1068 /* Is the domain already set to the desired constraint? */
1069 if (typTup->typnotnull == notNull)
1071 elog(NOTICE, "AlterDomain: %s is already set to %s",
1072 TypeNameToString(typename),
1073 notNull ? "NOT NULL" : "NULL");
1074 heap_close(typrel, RowExclusiveLock);
1078 /* Adding a NOT NULL constraint requires checking existing columns */
1084 /* Fetch relation list with attributes based on this domain */
1085 /* ShareLock is sufficient to prevent concurrent data changes */
1087 rels = get_rels_with_domain(domainoid, ShareLock);
1091 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1092 Relation testrel = rtc->rel;
1093 TupleDesc tupdesc = RelationGetDescr(testrel);
1097 /* Scan all tuples in this relation */
1098 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1099 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1103 /* Test attributes that are of the domain */
1104 for (i = 0; i < rtc->natts; i++)
1106 int attnum = rtc->atts[i];
1110 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1113 elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains NULL values",
1114 RelationGetRelationName(testrel),
1115 NameStr(tupdesc->attrs[attnum - 1]->attname));
1120 /* Close each rel after processing, but keep lock */
1121 heap_close(testrel, NoLock);
1126 * Okay to update pg_type row. We can scribble on typTup because it's
1129 typTup->typnotnull = notNull;
1131 simple_heap_update(typrel, &tup->t_self, tup);
1133 CatalogUpdateIndexes(typrel, tup);
1136 heap_freetuple(tup);
1137 heap_close(typrel, RowExclusiveLock);
1141 * AlterDomainDropConstraint
1143 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1146 AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior)
1152 Form_pg_type typTup;
1154 SysScanDesc conscan;
1158 /* Make a TypeName so we can use standard type lookup machinery */
1159 typename = makeNode(TypeName);
1160 typename->names = names;
1161 typename->typmod = -1;
1162 typename->arrayBounds = NIL;
1164 /* Lock the type table */
1165 rel = heap_openr(TypeRelationName, RowExclusiveLock);
1167 /* Use LookupTypeName here so that shell types can be removed. */
1168 domainoid = LookupTypeName(typename);
1169 if (!OidIsValid(domainoid))
1170 elog(ERROR, "Type \"%s\" does not exist",
1171 TypeNameToString(typename));
1173 tup = SearchSysCacheCopy(TYPEOID,
1174 ObjectIdGetDatum(domainoid),
1177 if (!HeapTupleIsValid(tup))
1178 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
1179 TypeNameToString(typename));
1181 /* Doesn't return if user isn't allowed to alter the domain */
1182 domainOwnerCheck(tup, typename);
1184 /* Grab an appropriate lock on the pg_constraint relation */
1185 conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
1187 /* Use the index to scan only constraints of the target relation */
1188 ScanKeyEntryInitialize(&key[0], 0x0,
1189 Anum_pg_constraint_contypid, F_OIDEQ,
1190 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1192 conscan = systable_beginscan(conrel, ConstraintTypidIndex, true,
1193 SnapshotNow, 1, key);
1195 typTup = (Form_pg_type) GETSTRUCT(tup);
1198 * Scan over the result set, removing any matching entries.
1200 while ((contup = systable_getnext(conscan)) != NULL)
1202 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1204 if (strcmp(NameStr(con->conname), constrName) == 0)
1206 ObjectAddress conobj;
1208 conobj.classId = RelationGetRelid(conrel);
1209 conobj.objectId = HeapTupleGetOid(contup);
1210 conobj.objectSubId = 0;
1212 performDeletion(&conobj, behavior);
1215 /* Clean up after the scan */
1216 systable_endscan(conscan);
1217 heap_close(conrel, RowExclusiveLock);
1219 heap_close(rel, NoLock);
1223 * AlterDomainAddConstraint
1225 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1228 AlterDomainAddConstraint(List *names, Node *newConstraint)
1234 Form_pg_type typTup;
1238 ExprContext *econtext;
1241 ExprState *exprstate;
1245 /* Make a TypeName so we can use standard type lookup machinery */
1246 typename = makeNode(TypeName);
1247 typename->names = names;
1248 typename->typmod = -1;
1249 typename->arrayBounds = NIL;
1251 /* Lock the type table */
1252 typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1254 /* Use LookupTypeName here so that shell types can be found (why?). */
1255 domainoid = LookupTypeName(typename);
1256 if (!OidIsValid(domainoid))
1257 elog(ERROR, "Type \"%s\" does not exist",
1258 TypeNameToString(typename));
1260 tup = SearchSysCacheCopy(TYPEOID,
1261 ObjectIdGetDatum(domainoid),
1263 if (!HeapTupleIsValid(tup))
1264 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
1265 TypeNameToString(typename));
1266 typTup = (Form_pg_type) GETSTRUCT(tup);
1268 /* Doesn't return if user isn't allowed to alter the domain */
1269 domainOwnerCheck(tup, typename);
1271 /* Check for unsupported constraint types */
1272 if (IsA(newConstraint, FkConstraint))
1273 elog(ERROR, "ALTER DOMAIN / FOREIGN KEY constraints not supported");
1275 /* this case should not happen */
1276 if (!IsA(newConstraint, Constraint))
1277 elog(ERROR, "AlterDomainAddConstraint: unexpected constraint node type");
1279 constr = (Constraint *) newConstraint;
1281 switch (constr->contype)
1283 case CONSTR_DEFAULT:
1284 elog(ERROR, "Use ALTER DOMAIN .. SET DEFAULT instead");
1287 case CONSTR_NOTNULL:
1289 elog(ERROR, "Use ALTER DOMAIN .. [ SET | DROP ] NOT NULL instead");
1293 /* processed below */
1297 elog(ERROR, "ALTER DOMAIN / UNIQUE indexes not supported");
1300 case CONSTR_PRIMARY:
1301 elog(ERROR, "ALTER DOMAIN / PRIMARY KEY indexes not supported");
1304 case CONSTR_ATTR_DEFERRABLE:
1305 case CONSTR_ATTR_NOT_DEFERRABLE:
1306 case CONSTR_ATTR_DEFERRED:
1307 case CONSTR_ATTR_IMMEDIATE:
1308 elog(ERROR, "ALTER DOMAIN: DEFERRABLE, NON DEFERRABLE, DEFERRED"
1309 " and IMMEDIATE not supported");
1313 elog(ERROR, "AlterDomainAddConstraint: unrecognized constraint node type");
1318 * Since all other constraint types throw errors, this must be
1319 * a check constraint. First, process the constraint expression
1320 * and add an entry to pg_constraint.
1323 ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1324 typTup->typbasetype, typTup->typtypmod,
1325 constr, &counter, NameStr(typTup->typname));
1328 * Test all values stored in the attributes based on the domain
1329 * the constraint is being added to.
1331 expr = (Expr *) stringToNode(ccbin);
1333 /* Need an EState to run ExecEvalExpr */
1334 estate = CreateExecutorState();
1335 econtext = GetPerTupleExprContext(estate);
1337 /* build execution state for expr */
1338 exprstate = ExecPrepareExpr(expr, estate);
1340 /* Fetch relation list with attributes based on this domain */
1341 /* ShareLock is sufficient to prevent concurrent data changes */
1343 rels = get_rels_with_domain(domainoid, ShareLock);
1347 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1348 Relation testrel = rtc->rel;
1349 TupleDesc tupdesc = RelationGetDescr(testrel);
1353 /* Scan all tuples in this relation */
1354 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1355 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1359 /* Test attributes that are of the domain */
1360 for (i = 0; i < rtc->natts; i++)
1362 int attnum = rtc->atts[i];
1367 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1369 econtext->domainValue_datum = d;
1370 econtext->domainValue_isNull = isNull;
1372 conResult = ExecEvalExprSwitchContext(exprstate,
1376 if (!isNull && !DatumGetBool(conResult))
1377 elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains values that fail the new constraint",
1378 RelationGetRelationName(testrel),
1379 NameStr(tupdesc->attrs[attnum - 1]->attname));
1382 ResetExprContext(econtext);
1386 /* Hold relation lock till commit (XXX bad for concurrency) */
1387 heap_close(testrel, NoLock);
1390 FreeExecutorState(estate);
1393 heap_close(typrel, RowExclusiveLock);
1397 * get_rels_with_domain
1399 * Fetch all relations / attributes which are using the domain
1401 * The result is a list of RelToCheck structs, one for each distinct
1402 * relation, each containing one or more attribute numbers that are of
1403 * the domain type. We have opened each rel and acquired the specified lock
1406 * XXX this is completely broken because there is no way to lock the domain
1407 * to prevent columns from being added or dropped while our command runs.
1408 * We can partially protect against column drops by locking relations as we
1409 * come across them, but there is still a race condition (the window between
1410 * seeing a pg_depend entry and acquiring lock on the relation it references).
1411 * Also, holding locks on all these relations simultaneously creates a non-
1412 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
1413 * risk by using the weakest suitable lock (ShareLock for most callers).
1415 * XXX to support domains over domains, we'd need to make this smarter,
1416 * or make its callers smarter, so that we could find columns of derived
1417 * domains. Arrays of domains would be a problem too.
1419 * Generally used for retrieving a list of tests when adding
1420 * new constraints to a domain.
1423 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1428 SysScanDesc depScan;
1432 * We scan pg_depend to find those things that depend on the domain.
1433 * (We assume we can ignore refobjsubid for a domain.)
1435 depRel = relation_openr(DependRelationName, AccessShareLock);
1437 ScanKeyEntryInitialize(&key[0], 0x0,
1438 Anum_pg_depend_refclassid, F_OIDEQ,
1439 ObjectIdGetDatum(RelOid_pg_type));
1440 ScanKeyEntryInitialize(&key[1], 0x0,
1441 Anum_pg_depend_refobjid, F_OIDEQ,
1442 ObjectIdGetDatum(domainOid));
1444 depScan = systable_beginscan(depRel, DependReferenceIndex, true,
1445 SnapshotNow, 2, key);
1447 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1449 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1450 RelToCheck *rtc = NULL;
1452 Form_pg_attribute pg_att;
1455 /* Ignore dependees that aren't user columns of tables */
1456 /* (we assume system columns are never of domain types) */
1457 if (pg_depend->classid != RelOid_pg_class ||
1458 pg_depend->objsubid <= 0)
1461 /* See if we already have an entry for this relation */
1462 foreach(rellist, result)
1464 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1466 if (RelationGetRelid(rt->rel) == pg_depend->objid)
1475 /* First attribute found for this relation */
1478 /* Acquire requested lock on relation */
1479 rel = heap_open(pg_depend->objid, lockmode);
1481 /* Build the RelToCheck entry with enough space for all atts */
1482 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1485 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1486 result = lcons(rtc, result);
1490 * Confirm column has not been dropped, and is of the expected type.
1491 * This defends against an ALTER DROP COLUMN occuring just before
1492 * we acquired lock ... but if the whole table were dropped, we'd
1493 * still have a problem.
1495 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
1497 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
1498 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
1502 * Okay, add column to result. We store the columns in column-number
1503 * order; this is just a hack to improve predictability of regression
1506 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
1509 while (ptr > 0 && rtc->atts[ptr-1] > pg_depend->objsubid)
1511 rtc->atts[ptr] = rtc->atts[ptr-1];
1514 rtc->atts[ptr] = pg_depend->objsubid;
1517 systable_endscan(depScan);
1519 relation_close(depRel, AccessShareLock);
1527 * Throw an error if the current user doesn't have permission to modify
1528 * the domain in an ALTER DOMAIN statement, or if the type isn't actually
1532 domainOwnerCheck(HeapTuple tup, TypeName *typename)
1534 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
1536 /* Check that this is actually a domain */
1537 if (typTup->typtype != 'd')
1538 elog(ERROR, "%s is not a domain",
1539 TypeNameToString(typename));
1541 /* Permission check: must own type */
1542 if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1543 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
1547 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
1550 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
1551 int typMod, Constraint *constr,
1552 int *counter, char *domainName)
1558 ConstraintTestValue *domVal;
1561 * Assign or validate constraint name
1565 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
1569 elog(ERROR, "constraint \"%s\" already exists for domain \"%s\"",
1574 constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN,
1580 * Convert the A_EXPR in raw_expr into an EXPR
1582 pstate = make_parsestate(NULL);
1585 * Set up a ConstraintTestValue to represent the occurrence of VALUE
1586 * in the expression. Note that it will appear to have the type of the
1587 * base type, not the domain. This seems correct since within the
1588 * check expression, we should not assume the input value can be considered
1589 * a member of the domain.
1591 domVal = makeNode(ConstraintTestValue);
1592 domVal->typeId = baseTypeOid;
1593 domVal->typeMod = typMod;
1595 pstate->p_value_substitute = (Node *) domVal;
1597 expr = transformExpr(pstate, constr->raw_expr);
1600 * Make sure it yields a boolean result.
1602 expr = coerce_to_boolean(expr, "CHECK");
1605 * Make sure no outside relations are
1608 if (length(pstate->p_rtable) != 0)
1609 elog(ERROR, "Relations cannot be referenced in domain CHECK constraint");
1612 * Domains don't allow var clauses (this should be redundant with the
1613 * above check, but make it anyway)
1615 if (contain_var_clause(expr))
1616 elog(ERROR, "cannot use column references in domain CHECK clause");
1619 * No subplans or aggregates, either...
1621 if (contain_subplans(expr))
1622 elog(ERROR, "cannot use subselect in CHECK constraint expression");
1623 if (contain_agg_clause(expr))
1624 elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
1627 * Might as well try to reduce any constant expressions.
1629 expr = eval_const_expressions(expr);
1632 * Convert to string form for storage.
1634 ccbin = nodeToString(expr);
1637 * Deparse it to produce text for consrc.
1639 * Since VARNOs aren't allowed in domain constraints, relation context
1640 * isn't required as anything other than a shell.
1642 ccsrc = deparse_expression(expr,
1643 deparse_context_for(domainName,
1648 * Store the constraint in pg_constraint
1650 CreateConstraintEntry(constr->name, /* Constraint Name */
1651 domainNamespace, /* namespace */
1652 CONSTRAINT_CHECK, /* Constraint Type */
1653 false, /* Is Deferrable */
1654 false, /* Is Deferred */
1655 InvalidOid, /* not a relation constraint */
1658 domainOid, /* domain constraint */
1659 InvalidOid, /* Foreign key fields */
1666 expr, /* Tree form check constraint */
1667 ccbin, /* Binary form check constraint */
1668 ccsrc); /* Source form check constraint */
1671 * Return the compiled constraint expression so the calling routine can
1672 * perform any additional required tests.