1 /*-------------------------------------------------------------------------
4 * Routines for SQL commands that manipulate types (and domains).
6 * Portions Copyright (c) 1996-2006, 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.91 2006/06/21 18:09:53 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_namespace.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/makefuncs.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/memutils.h"
64 #include "utils/syscache.h"
67 /* result structure for get_rels_with_domain() */
70 Relation rel; /* opened and locked relation */
71 int natts; /* number of attributes of interest */
72 int *atts; /* attribute numbers */
73 /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
77 static Oid findTypeInputFunction(List *procname, Oid typeOid);
78 static Oid findTypeOutputFunction(List *procname, Oid typeOid);
79 static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
80 static Oid findTypeSendFunction(List *procname, Oid typeOid);
81 static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
82 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
83 static void checkDomainOwner(HeapTuple tup, TypeName *typename);
84 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
86 int typMod, Constraint *constr,
92 * Registers a new type.
95 DefineType(List *names, List *parameters)
100 int16 internalLength = -1; /* default: variable-length */
101 Oid elemType = InvalidOid;
102 List *inputName = NIL;
103 List *outputName = NIL;
104 List *receiveName = NIL;
105 List *sendName = NIL;
106 List *analyzeName = NIL;
107 char *defaultValue = NULL;
108 bool byValue = false;
109 char delimiter = DEFAULT_TYPDELIM;
110 char alignment = 'i'; /* default alignment */
111 char storage = 'p'; /* default TOAST storage method */
114 Oid receiveOid = InvalidOid;
115 Oid sendOid = InvalidOid;
116 Oid analyzeOid = InvalidOid;
122 /* Convert list of names to a name and namespace */
123 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
125 /* Check we have creation rights in target namespace */
126 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
127 if (aclresult != ACLCHECK_OK)
128 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
129 get_namespace_name(typeNamespace));
132 * Type names must be one character shorter than other names, allowing
133 * 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",
142 * Look to see if type already exists (presumably as a shell; if not,
143 * TypeCreate will complain). If it doesn't, create it as a shell, so
144 * that the OID is known for use in the I/O function definitions.
146 typoid = GetSysCacheOid(TYPENAMENSP,
147 CStringGetDatum(typeName),
148 ObjectIdGetDatum(typeNamespace),
150 if (!OidIsValid(typoid))
152 typoid = TypeShellMake(typeName, typeNamespace);
153 /* Make new shell type visible for modification below */
154 CommandCounterIncrement();
157 * If the command was a parameterless CREATE TYPE, we're done ---
158 * creating the shell type was all we're supposed to do.
160 if (parameters == NIL)
165 /* Complain if dummy CREATE TYPE and entry already exists */
166 if (parameters == NIL)
168 (errcode(ERRCODE_DUPLICATE_OBJECT),
169 errmsg("type \"%s\" already exists", typeName)));
172 foreach(pl, parameters)
174 DefElem *defel = (DefElem *) lfirst(pl);
176 if (pg_strcasecmp(defel->defname, "internallength") == 0)
177 internalLength = defGetTypeLength(defel);
178 else if (pg_strcasecmp(defel->defname, "externallength") == 0)
179 ; /* ignored -- remove after 7.3 */
180 else if (pg_strcasecmp(defel->defname, "input") == 0)
181 inputName = defGetQualifiedName(defel);
182 else if (pg_strcasecmp(defel->defname, "output") == 0)
183 outputName = defGetQualifiedName(defel);
184 else if (pg_strcasecmp(defel->defname, "receive") == 0)
185 receiveName = defGetQualifiedName(defel);
186 else if (pg_strcasecmp(defel->defname, "send") == 0)
187 sendName = defGetQualifiedName(defel);
188 else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
189 pg_strcasecmp(defel->defname, "analyse") == 0)
190 analyzeName = defGetQualifiedName(defel);
191 else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
193 char *p = defGetString(defel);
197 else if (pg_strcasecmp(defel->defname, "element") == 0)
199 elemType = typenameTypeId(NULL, defGetTypeName(defel));
200 /* disallow arrays of pseudotypes */
201 if (get_typtype(elemType) == 'p')
203 (errcode(ERRCODE_DATATYPE_MISMATCH),
204 errmsg("array element type cannot be %s",
205 format_type_be(elemType))));
207 else if (pg_strcasecmp(defel->defname, "default") == 0)
208 defaultValue = defGetString(defel);
209 else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
210 byValue = defGetBoolean(defel);
211 else if (pg_strcasecmp(defel->defname, "alignment") == 0)
213 char *a = defGetString(defel);
216 * Note: if argument was an unquoted identifier, parser will have
217 * applied translations to it, so be prepared to recognize
218 * translated type names as well as the nominal form.
220 if (pg_strcasecmp(a, "double") == 0 ||
221 pg_strcasecmp(a, "float8") == 0 ||
222 pg_strcasecmp(a, "pg_catalog.float8") == 0)
224 else if (pg_strcasecmp(a, "int4") == 0 ||
225 pg_strcasecmp(a, "pg_catalog.int4") == 0)
227 else if (pg_strcasecmp(a, "int2") == 0 ||
228 pg_strcasecmp(a, "pg_catalog.int2") == 0)
230 else if (pg_strcasecmp(a, "char") == 0 ||
231 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
235 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
236 errmsg("alignment \"%s\" not recognized", a)));
238 else if (pg_strcasecmp(defel->defname, "storage") == 0)
240 char *a = defGetString(defel);
242 if (pg_strcasecmp(a, "plain") == 0)
244 else if (pg_strcasecmp(a, "external") == 0)
246 else if (pg_strcasecmp(a, "extended") == 0)
248 else if (pg_strcasecmp(a, "main") == 0)
252 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
253 errmsg("storage \"%s\" not recognized", a)));
257 (errcode(ERRCODE_SYNTAX_ERROR),
258 errmsg("type attribute \"%s\" not recognized",
263 * make sure we have our required definitions
265 if (inputName == NIL)
267 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
268 errmsg("type input function must be specified")));
269 if (outputName == NIL)
271 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
272 errmsg("type output function must be specified")));
275 * Convert I/O proc names to OIDs
277 inputOid = findTypeInputFunction(inputName, typoid);
278 outputOid = findTypeOutputFunction(outputName, typoid);
280 receiveOid = findTypeReceiveFunction(receiveName, typoid);
282 sendOid = findTypeSendFunction(sendName, typoid);
285 * Verify that I/O procs return the expected thing. If we see OPAQUE,
286 * complain and change it to the correct type-safe choice.
288 resulttype = get_func_rettype(inputOid);
289 if (resulttype != typoid)
291 if (resulttype == OPAQUEOID)
293 /* backwards-compatibility hack */
295 (errmsg("changing return type of function %s from \"opaque\" to %s",
296 NameListToString(inputName), typeName)));
297 SetFunctionReturnType(inputOid, typoid);
301 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
302 errmsg("type input function %s must return type %s",
303 NameListToString(inputName), typeName)));
305 resulttype = get_func_rettype(outputOid);
306 if (resulttype != CSTRINGOID)
308 if (resulttype == OPAQUEOID)
310 /* backwards-compatibility hack */
312 (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
313 NameListToString(outputName))));
314 SetFunctionReturnType(outputOid, CSTRINGOID);
318 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
319 errmsg("type output function %s must return type \"cstring\"",
320 NameListToString(outputName))));
324 resulttype = get_func_rettype(receiveOid);
325 if (resulttype != typoid)
327 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
328 errmsg("type receive function %s must return type %s",
329 NameListToString(receiveName), typeName)));
333 resulttype = get_func_rettype(sendOid);
334 if (resulttype != BYTEAOID)
336 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
337 errmsg("type send function %s must return type \"bytea\"",
338 NameListToString(sendName))));
342 * Convert analysis function proc name to an OID. If no analysis function
343 * is specified, we'll use zero to select the built-in default algorithm.
346 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
349 * Check permissions on functions. We choose to require the creator/owner
350 * of a type to also own the underlying functions. Since creating a type
351 * is tantamount to granting public execute access on the functions, the
352 * minimum sane check would be for execute-with-grant-option. But we don't
353 * have a way to make the type go away if the grant option is revoked, so
354 * ownership seems better.
356 if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
357 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
358 NameListToString(inputName));
359 if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
360 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
361 NameListToString(outputName));
362 if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
363 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
364 NameListToString(receiveName));
365 if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
366 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
367 NameListToString(sendName));
368 if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
369 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
370 NameListToString(analyzeName));
373 * now have TypeCreate do all the real work.
376 TypeCreate(typeName, /* type name */
377 typeNamespace, /* namespace */
378 InvalidOid, /* relation oid (n/a here) */
379 0, /* relation kind (ditto) */
380 internalLength, /* internal size */
381 'b', /* type-type (base type) */
382 delimiter, /* array element delimiter */
383 inputOid, /* input procedure */
384 outputOid, /* output procedure */
385 receiveOid, /* receive procedure */
386 sendOid, /* send procedure */
387 analyzeOid, /* analyze procedure */
388 elemType, /* element type ID */
389 InvalidOid, /* base type ID (only for domains) */
390 defaultValue, /* default type value */
391 NULL, /* no binary form available */
392 byValue, /* passed by value */
393 alignment, /* required alignment */
394 storage, /* TOAST strategy */
395 -1, /* typMod (Domains only) */
396 0, /* Array Dimensions of typbasetype */
397 false); /* Type NOT NULL */
400 * When we create a base type (as opposed to a complex type) we need to
401 * have an array entry for it in pg_type as well.
403 shadow_type = makeArrayTypeName(typeName);
405 /* alignment must be 'i' or 'd' for arrays */
406 alignment = (alignment == 'd') ? 'd' : 'i';
408 TypeCreate(shadow_type, /* type name */
409 typeNamespace, /* namespace */
410 InvalidOid, /* relation oid (n/a here) */
411 0, /* relation kind (ditto) */
412 -1, /* internal size */
413 'b', /* type-type (base type) */
414 DEFAULT_TYPDELIM, /* array element delimiter */
415 F_ARRAY_IN, /* input procedure */
416 F_ARRAY_OUT, /* output procedure */
417 F_ARRAY_RECV, /* receive procedure */
418 F_ARRAY_SEND, /* send procedure */
419 InvalidOid, /* analyze procedure - default */
420 typoid, /* element type ID */
421 InvalidOid, /* base type ID */
422 NULL, /* never a default type value */
423 NULL, /* binary default isn't sent either */
424 false, /* never passed by value */
425 alignment, /* see above */
426 'x', /* ARRAY is always toastable */
427 -1, /* typMod (Domains only) */
428 0, /* Array dimensions of typbasetype */
429 false); /* Type NOT NULL */
437 * Removes a datatype.
440 RemoveType(List *names, DropBehavior behavior, bool missing_ok)
445 ObjectAddress object;
447 /* Make a TypeName so we can use standard type lookup machinery */
448 typename = makeTypeNameFromNameList(names);
450 /* Use LookupTypeName here so that shell types can be removed. */
451 typeoid = LookupTypeName(NULL, typename);
452 if (!OidIsValid(typeoid))
457 (errcode(ERRCODE_UNDEFINED_OBJECT),
458 errmsg("type \"%s\" does not exist",
459 TypeNameToString(typename))));
464 (errmsg("type \"%s\" does not exist, skipping",
465 TypeNameToString(typename))));
471 tup = SearchSysCache(TYPEOID,
472 ObjectIdGetDatum(typeoid),
474 if (!HeapTupleIsValid(tup))
475 elog(ERROR, "cache lookup failed for type %u", typeoid);
477 /* Permission check: must own type or its namespace */
478 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
479 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
481 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
482 TypeNameToString(typename));
484 ReleaseSysCache(tup);
489 object.classId = TypeRelationId;
490 object.objectId = typeoid;
491 object.objectSubId = 0;
493 performDeletion(&object, behavior);
498 * Guts of type deletion.
501 RemoveTypeById(Oid typeOid)
506 relation = heap_open(TypeRelationId, RowExclusiveLock);
508 tup = SearchSysCache(TYPEOID,
509 ObjectIdGetDatum(typeOid),
511 if (!HeapTupleIsValid(tup))
512 elog(ERROR, "cache lookup failed for type %u", typeOid);
514 simple_heap_delete(relation, &tup->t_self);
516 ReleaseSysCache(tup);
518 heap_close(relation, RowExclusiveLock);
524 * Registers a new domain.
527 DefineDomain(CreateDomainStmt *stmt)
532 int16 internalLength;
535 Oid receiveProcedure;
537 Oid analyzeProcedure;
546 Node *defaultExpr = NULL;
547 char *defaultValue = NULL;
548 char *defaultValueBin = NULL;
549 bool typNotNull = false;
550 bool nullDefined = false;
551 int32 typNDims = list_length(stmt->typename->arrayBounds);
553 List *schema = stmt->constraints;
557 Form_pg_type baseType;
559 /* Convert list of names to a name and namespace */
560 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
563 /* Check we have creation rights in target namespace */
564 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
566 if (aclresult != ACLCHECK_OK)
567 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
568 get_namespace_name(domainNamespace));
571 * Domainnames, unlike typenames don't need to account for the '_' prefix.
572 * So they can be one character longer. (This test is presently useless
573 * since the parser will have truncated the name to fit. But leave it
574 * here since we may someday support arrays of domains, in which case
575 * we'll be back to needing to enforce NAMEDATALEN-2.)
577 if (strlen(domainName) > (NAMEDATALEN - 1))
579 (errcode(ERRCODE_INVALID_NAME),
580 errmsg("domain names must be %d characters or less",
584 * Look up the base type.
586 typeTup = typenameType(NULL, stmt->typename);
588 baseType = (Form_pg_type) GETSTRUCT(typeTup);
589 basetypeoid = HeapTupleGetOid(typeTup);
592 * Base type must be a plain base type or another domain. Domains over
593 * pseudotypes would create a security hole. Domains over composite
594 * types might be made to work in the future, but not today.
596 typtype = baseType->typtype;
597 if (typtype != 'b' && typtype != 'd')
599 (errcode(ERRCODE_DATATYPE_MISMATCH),
600 errmsg("\"%s\" is not a valid base type for a domain",
601 TypeNameToString(stmt->typename))));
603 /* passed by value */
604 byValue = baseType->typbyval;
606 /* Required Alignment */
607 alignment = baseType->typalign;
610 storage = baseType->typstorage;
613 internalLength = baseType->typlen;
615 /* Array element type (in case base type is an array) */
616 typelem = baseType->typelem;
618 /* Array element Delimiter */
619 delimiter = baseType->typdelim;
622 inputProcedure = F_DOMAIN_IN;
623 outputProcedure = baseType->typoutput;
624 receiveProcedure = F_DOMAIN_RECV;
625 sendProcedure = baseType->typsend;
627 /* Analysis function */
628 analyzeProcedure = baseType->typanalyze;
630 /* Inherited default value */
631 datum = SysCacheGetAttr(TYPEOID, typeTup,
632 Anum_pg_type_typdefault, &isnull);
634 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
636 /* Inherited default binary value */
637 datum = SysCacheGetAttr(TYPEOID, typeTup,
638 Anum_pg_type_typdefaultbin, &isnull);
640 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
643 * Run through constraints manually to avoid the additional processing
644 * conducted by DefineRelation() and friends.
646 foreach(listptr, schema)
648 Node *newConstraint = lfirst(listptr);
652 /* Check for unsupported constraint types */
653 if (IsA(newConstraint, FkConstraint))
655 (errcode(ERRCODE_SYNTAX_ERROR),
656 errmsg("foreign key constraints not possible for domains")));
658 /* otherwise it should be a plain Constraint */
659 if (!IsA(newConstraint, Constraint))
660 elog(ERROR, "unrecognized node type: %d",
661 (int) nodeTag(newConstraint));
663 constr = (Constraint *) newConstraint;
665 switch (constr->contype)
670 * The inherited default value may be overridden by the user
671 * with the DEFAULT <expr> statement.
675 (errcode(ERRCODE_SYNTAX_ERROR),
676 errmsg("multiple default expressions")));
678 /* Create a dummy ParseState for transformExpr */
679 pstate = make_parsestate(NULL);
682 * Cook the constr->raw_expr into an expression. Note: Name is
683 * strictly for error message
685 defaultExpr = cookDefault(pstate, constr->raw_expr,
687 stmt->typename->typmod,
691 * Expression must be stored as a nodeToString result, but we
692 * also require a valid textual representation (mainly to make
693 * life easier for pg_dump).
695 defaultValue = deparse_expression(defaultExpr,
696 deparse_context_for(domainName,
699 defaultValueBin = nodeToString(defaultExpr);
703 if (nullDefined && !typNotNull)
705 (errcode(ERRCODE_SYNTAX_ERROR),
706 errmsg("conflicting NULL/NOT NULL constraints")));
712 if (nullDefined && typNotNull)
714 (errcode(ERRCODE_SYNTAX_ERROR),
715 errmsg("conflicting NULL/NOT NULL constraints")));
723 * Check constraints are handled after domain creation, as
724 * they require the Oid of the domain
729 * All else are error cases
733 (errcode(ERRCODE_SYNTAX_ERROR),
734 errmsg("unique constraints not possible for domains")));
739 (errcode(ERRCODE_SYNTAX_ERROR),
740 errmsg("primary key constraints not possible for domains")));
743 case CONSTR_ATTR_DEFERRABLE:
744 case CONSTR_ATTR_NOT_DEFERRABLE:
745 case CONSTR_ATTR_DEFERRED:
746 case CONSTR_ATTR_IMMEDIATE:
748 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
749 errmsg("specifying constraint deferrability not supported for domains")));
753 elog(ERROR, "unrecognized constraint subtype: %d",
754 (int) constr->contype);
760 * Have TypeCreate do all the real work.
763 TypeCreate(domainName, /* type name */
764 domainNamespace, /* namespace */
765 InvalidOid, /* relation oid (n/a here) */
766 0, /* relation kind (ditto) */
767 internalLength, /* internal size */
768 'd', /* type-type (domain type) */
769 delimiter, /* array element delimiter */
770 inputProcedure, /* input procedure */
771 outputProcedure, /* output procedure */
772 receiveProcedure, /* receive procedure */
773 sendProcedure, /* send procedure */
774 analyzeProcedure, /* analyze procedure */
775 typelem, /* element type ID */
776 basetypeoid, /* base type ID */
777 defaultValue, /* default type value (text) */
778 defaultValueBin, /* default type value (binary) */
779 byValue, /* passed by value */
780 alignment, /* required alignment */
781 storage, /* TOAST strategy */
782 stmt->typename->typmod, /* typeMod value */
783 typNDims, /* Array dimensions for base type */
784 typNotNull); /* Type NOT NULL */
787 * Process constraints which refer to the domain ID returned by TypeCreate
789 foreach(listptr, schema)
791 Constraint *constr = lfirst(listptr);
793 /* it must be a Constraint, per check above */
795 switch (constr->contype)
798 domainAddConstraint(domainoid, domainNamespace,
799 basetypeoid, stmt->typename->typmod,
803 /* Other constraint types were fully processed above */
809 /* CCI so we can detect duplicate constraint names */
810 CommandCounterIncrement();
814 * Now we can clean up.
816 ReleaseSysCache(typeTup);
824 * This is identical to RemoveType except we insist it be a domain.
827 RemoveDomain(List *names, DropBehavior behavior, bool missing_ok)
833 ObjectAddress object;
835 /* Make a TypeName so we can use standard type lookup machinery */
836 typename = makeTypeNameFromNameList(names);
838 /* Use LookupTypeName here so that shell types can be removed. */
839 typeoid = LookupTypeName(NULL, typename);
840 if (!OidIsValid(typeoid))
845 (errcode(ERRCODE_UNDEFINED_OBJECT),
846 errmsg("type \"%s\" does not exist",
847 TypeNameToString(typename))));
852 (errmsg("type \"%s\" does not exist, skipping",
853 TypeNameToString(typename))));
859 tup = SearchSysCache(TYPEOID,
860 ObjectIdGetDatum(typeoid),
862 if (!HeapTupleIsValid(tup))
863 elog(ERROR, "cache lookup failed for type %u", typeoid);
865 /* Permission check: must own type or its namespace */
866 if (!pg_type_ownercheck(typeoid, GetUserId()) &&
867 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
869 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
870 TypeNameToString(typename));
872 /* Check that this is actually a domain */
873 typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
877 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
878 errmsg("\"%s\" is not a domain",
879 TypeNameToString(typename))));
881 ReleaseSysCache(tup);
886 object.classId = TypeRelationId;
887 object.objectId = typeoid;
888 object.objectSubId = 0;
890 performDeletion(&object, behavior);
895 * Find suitable I/O functions for a type.
897 * typeOid is the type's OID (which will already exist, if only as a shell
902 findTypeInputFunction(List *procname, Oid typeOid)
908 * Input functions can take a single argument of type CSTRING, or three
909 * arguments (string, typioparam OID, typmod).
911 * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
912 * see this, we issue a warning and fix up the pg_proc entry.
914 argList[0] = CSTRINGOID;
916 procOid = LookupFuncName(procname, 1, argList, true);
917 if (OidIsValid(procOid))
921 argList[2] = INT4OID;
923 procOid = LookupFuncName(procname, 3, argList, true);
924 if (OidIsValid(procOid))
927 /* No luck, try it with OPAQUE */
928 argList[0] = OPAQUEOID;
930 procOid = LookupFuncName(procname, 1, argList, true);
932 if (!OidIsValid(procOid))
935 argList[2] = INT4OID;
937 procOid = LookupFuncName(procname, 3, argList, true);
940 if (OidIsValid(procOid))
942 /* Found, but must complain and fix the pg_proc entry */
944 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
945 NameListToString(procname))));
946 SetFunctionArgType(procOid, 0, CSTRINGOID);
949 * Need CommandCounterIncrement since DefineType will likely try to
950 * alter the pg_proc tuple again.
952 CommandCounterIncrement();
957 /* Use CSTRING (preferred) in the error message */
958 argList[0] = CSTRINGOID;
961 (errcode(ERRCODE_UNDEFINED_FUNCTION),
962 errmsg("function %s does not exist",
963 func_signature_string(procname, 1, argList))));
965 return InvalidOid; /* keep compiler quiet */
969 findTypeOutputFunction(List *procname, Oid typeOid)
975 * Output functions can take a single argument of the type.
977 * For backwards compatibility we allow OPAQUE in place of the actual type
978 * name; if we see this, we issue a warning and fix up the pg_proc entry.
980 argList[0] = typeOid;
982 procOid = LookupFuncName(procname, 1, argList, true);
983 if (OidIsValid(procOid))
986 /* No luck, try it with OPAQUE */
987 argList[0] = OPAQUEOID;
989 procOid = LookupFuncName(procname, 1, argList, true);
991 if (OidIsValid(procOid))
993 /* Found, but must complain and fix the pg_proc entry */
995 (errmsg("changing argument type of function %s from \"opaque\" to %s",
996 NameListToString(procname), format_type_be(typeOid))));
997 SetFunctionArgType(procOid, 0, typeOid);
1000 * Need CommandCounterIncrement since DefineType will likely try to
1001 * alter the pg_proc tuple again.
1003 CommandCounterIncrement();
1008 /* Use type name, not OPAQUE, in the failure message. */
1009 argList[0] = typeOid;
1012 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1013 errmsg("function %s does not exist",
1014 func_signature_string(procname, 1, argList))));
1016 return InvalidOid; /* keep compiler quiet */
1020 findTypeReceiveFunction(List *procname, Oid typeOid)
1026 * Receive functions can take a single argument of type INTERNAL, or three
1027 * arguments (internal, typioparam OID, typmod).
1029 argList[0] = INTERNALOID;
1031 procOid = LookupFuncName(procname, 1, argList, true);
1032 if (OidIsValid(procOid))
1035 argList[1] = OIDOID;
1036 argList[2] = INT4OID;
1038 procOid = LookupFuncName(procname, 3, argList, true);
1039 if (OidIsValid(procOid))
1043 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1044 errmsg("function %s does not exist",
1045 func_signature_string(procname, 1, argList))));
1047 return InvalidOid; /* keep compiler quiet */
1051 findTypeSendFunction(List *procname, Oid typeOid)
1057 * Send functions can take a single argument of the type.
1059 argList[0] = typeOid;
1061 procOid = LookupFuncName(procname, 1, argList, true);
1062 if (OidIsValid(procOid))
1066 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1067 errmsg("function %s does not exist",
1068 func_signature_string(procname, 1, argList))));
1070 return InvalidOid; /* keep compiler quiet */
1074 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1080 * Analyze functions always take one INTERNAL argument and return bool.
1082 argList[0] = INTERNALOID;
1084 procOid = LookupFuncName(procname, 1, argList, true);
1085 if (!OidIsValid(procOid))
1087 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1088 errmsg("function %s does not exist",
1089 func_signature_string(procname, 1, argList))));
1091 if (get_func_rettype(procOid) != BOOLOID)
1093 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1094 errmsg("type analyze function %s must return type \"boolean\"",
1095 NameListToString(procname))));
1101 /*-------------------------------------------------------------------
1102 * DefineCompositeType
1104 * Create a Composite Type relation.
1105 * `DefineRelation' does all the work, we just provide the correct
1108 * If the relation already exists, then 'DefineRelation' will abort
1111 * DefineCompositeType returns relid for use when creating
1112 * an implicit composite type during function creation
1113 *-------------------------------------------------------------------
1116 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1118 CreateStmt *createStmt = makeNode(CreateStmt);
1120 if (coldeflist == NIL)
1122 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1123 errmsg("composite type must have at least one attribute")));
1126 * now set the parameters for keys/inheritance etc. All of these are
1127 * uninteresting for composite types...
1129 createStmt->relation = (RangeVar *) typevar;
1130 createStmt->tableElts = coldeflist;
1131 createStmt->inhRelations = NIL;
1132 createStmt->constraints = NIL;
1133 createStmt->hasoids = MUST_NOT_HAVE_OIDS;
1134 createStmt->oncommit = ONCOMMIT_NOOP;
1135 createStmt->tablespacename = NULL;
1138 * finally create the relation...
1140 return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1144 * AlterDomainDefault
1146 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1149 AlterDomainDefault(List *names, Node *defaultRaw)
1157 Node *defaultExpr = NULL; /* NULL if no default specified */
1158 Datum new_record[Natts_pg_type];
1159 char new_record_nulls[Natts_pg_type];
1160 char new_record_repl[Natts_pg_type];
1162 Form_pg_type typTup;
1164 /* Make a TypeName so we can use standard type lookup machinery */
1165 typename = makeTypeNameFromNameList(names);
1166 domainoid = typenameTypeId(NULL, typename);
1168 /* Look up the domain in the type table */
1169 rel = heap_open(TypeRelationId, RowExclusiveLock);
1171 tup = SearchSysCacheCopy(TYPEOID,
1172 ObjectIdGetDatum(domainoid),
1174 if (!HeapTupleIsValid(tup))
1175 elog(ERROR, "cache lookup failed for type %u", domainoid);
1176 typTup = (Form_pg_type) GETSTRUCT(tup);
1178 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1179 checkDomainOwner(tup, typename);
1181 /* Setup new tuple */
1182 MemSet(new_record, (Datum) 0, sizeof(new_record));
1183 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1184 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1186 /* Store the new default, if null then skip this step */
1189 /* Create a dummy ParseState for transformExpr */
1190 pstate = make_parsestate(NULL);
1193 * Cook the colDef->raw_expr into an expression. Note: Name is
1194 * strictly for error message
1196 defaultExpr = cookDefault(pstate, defaultRaw,
1197 typTup->typbasetype,
1199 NameStr(typTup->typname));
1202 * Expression must be stored as a nodeToString result, but we also
1203 * require a valid textual representation (mainly to make life easier
1206 defaultValue = deparse_expression(defaultExpr,
1207 deparse_context_for(NameStr(typTup->typname),
1212 * Form an updated tuple with the new default and write it back.
1214 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
1216 nodeToString(defaultExpr)));
1218 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1219 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
1220 CStringGetDatum(defaultValue));
1221 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1224 /* Default is NULL, drop it */
1226 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1227 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1228 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1229 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1232 newtuple = heap_modifytuple(tup, RelationGetDescr(rel),
1233 new_record, new_record_nulls,
1236 simple_heap_update(rel, &tup->t_self, newtuple);
1238 CatalogUpdateIndexes(rel, newtuple);
1240 /* Rebuild dependencies */
1241 GenerateTypeDependencies(typTup->typnamespace,
1244 0, /* relation kind is n/a */
1252 typTup->typbasetype,
1254 true); /* Rebuild is true */
1257 heap_close(rel, NoLock);
1258 heap_freetuple(newtuple);
1262 * AlterDomainNotNull
1264 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1267 AlterDomainNotNull(List *names, bool notNull)
1273 Form_pg_type typTup;
1275 /* Make a TypeName so we can use standard type lookup machinery */
1276 typename = makeTypeNameFromNameList(names);
1277 domainoid = typenameTypeId(NULL, typename);
1279 /* Look up the domain in the type table */
1280 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1282 tup = SearchSysCacheCopy(TYPEOID,
1283 ObjectIdGetDatum(domainoid),
1285 if (!HeapTupleIsValid(tup))
1286 elog(ERROR, "cache lookup failed for type %u", domainoid);
1287 typTup = (Form_pg_type) GETSTRUCT(tup);
1289 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1290 checkDomainOwner(tup, typename);
1292 /* Is the domain already set to the desired constraint? */
1293 if (typTup->typnotnull == notNull)
1295 heap_close(typrel, RowExclusiveLock);
1299 /* Adding a NOT NULL constraint requires checking existing columns */
1305 /* Fetch relation list with attributes based on this domain */
1306 /* ShareLock is sufficient to prevent concurrent data changes */
1308 rels = get_rels_with_domain(domainoid, ShareLock);
1312 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1313 Relation testrel = rtc->rel;
1314 TupleDesc tupdesc = RelationGetDescr(testrel);
1318 /* Scan all tuples in this relation */
1319 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1320 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1324 /* Test attributes that are of the domain */
1325 for (i = 0; i < rtc->natts; i++)
1327 int attnum = rtc->atts[i];
1329 if (heap_attisnull(tuple, attnum))
1331 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1332 errmsg("column \"%s\" of table \"%s\" contains null values",
1333 NameStr(tupdesc->attrs[attnum - 1]->attname),
1334 RelationGetRelationName(testrel))));
1339 /* Close each rel after processing, but keep lock */
1340 heap_close(testrel, NoLock);
1345 * Okay to update pg_type row. We can scribble on typTup because it's a
1348 typTup->typnotnull = notNull;
1350 simple_heap_update(typrel, &tup->t_self, tup);
1352 CatalogUpdateIndexes(typrel, tup);
1355 heap_freetuple(tup);
1356 heap_close(typrel, RowExclusiveLock);
1360 * AlterDomainDropConstraint
1362 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1365 AlterDomainDropConstraint(List *names, const char *constrName,
1366 DropBehavior behavior)
1373 SysScanDesc conscan;
1377 /* Make a TypeName so we can use standard type lookup machinery */
1378 typename = makeTypeNameFromNameList(names);
1379 domainoid = typenameTypeId(NULL, typename);
1381 /* Look up the domain in the type table */
1382 rel = heap_open(TypeRelationId, RowExclusiveLock);
1384 tup = SearchSysCacheCopy(TYPEOID,
1385 ObjectIdGetDatum(domainoid),
1387 if (!HeapTupleIsValid(tup))
1388 elog(ERROR, "cache lookup failed for type %u", domainoid);
1390 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1391 checkDomainOwner(tup, typename);
1393 /* Grab an appropriate lock on the pg_constraint relation */
1394 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
1396 /* Use the index to scan only constraints of the target relation */
1397 ScanKeyInit(&key[0],
1398 Anum_pg_constraint_contypid,
1399 BTEqualStrategyNumber, F_OIDEQ,
1400 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1402 conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
1403 SnapshotNow, 1, key);
1406 * Scan over the result set, removing any matching entries.
1408 while ((contup = systable_getnext(conscan)) != NULL)
1410 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1412 if (strcmp(NameStr(con->conname), constrName) == 0)
1414 ObjectAddress conobj;
1416 conobj.classId = ConstraintRelationId;
1417 conobj.objectId = HeapTupleGetOid(contup);
1418 conobj.objectSubId = 0;
1420 performDeletion(&conobj, behavior);
1423 /* Clean up after the scan */
1424 systable_endscan(conscan);
1425 heap_close(conrel, RowExclusiveLock);
1427 heap_close(rel, NoLock);
1431 * AlterDomainAddConstraint
1433 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1436 AlterDomainAddConstraint(List *names, Node *newConstraint)
1442 Form_pg_type typTup;
1446 ExprContext *econtext;
1449 ExprState *exprstate;
1452 /* Make a TypeName so we can use standard type lookup machinery */
1453 typename = makeTypeNameFromNameList(names);
1454 domainoid = typenameTypeId(NULL, typename);
1456 /* Look up the domain in the type table */
1457 typrel = heap_open(TypeRelationId, RowExclusiveLock);
1459 tup = SearchSysCacheCopy(TYPEOID,
1460 ObjectIdGetDatum(domainoid),
1462 if (!HeapTupleIsValid(tup))
1463 elog(ERROR, "cache lookup failed for type %u", domainoid);
1464 typTup = (Form_pg_type) GETSTRUCT(tup);
1466 /* Check it's a domain and check user has permission for ALTER DOMAIN */
1467 checkDomainOwner(tup, typename);
1469 /* Check for unsupported constraint types */
1470 if (IsA(newConstraint, FkConstraint))
1472 (errcode(ERRCODE_SYNTAX_ERROR),
1473 errmsg("foreign key constraints not possible for domains")));
1475 /* otherwise it should be a plain Constraint */
1476 if (!IsA(newConstraint, Constraint))
1477 elog(ERROR, "unrecognized node type: %d",
1478 (int) nodeTag(newConstraint));
1480 constr = (Constraint *) newConstraint;
1482 switch (constr->contype)
1485 /* processed below */
1490 (errcode(ERRCODE_SYNTAX_ERROR),
1491 errmsg("unique constraints not possible for domains")));
1494 case CONSTR_PRIMARY:
1496 (errcode(ERRCODE_SYNTAX_ERROR),
1497 errmsg("primary key constraints not possible for domains")));
1500 case CONSTR_ATTR_DEFERRABLE:
1501 case CONSTR_ATTR_NOT_DEFERRABLE:
1502 case CONSTR_ATTR_DEFERRED:
1503 case CONSTR_ATTR_IMMEDIATE:
1505 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1506 errmsg("specifying constraint deferrability not supported for domains")));
1510 elog(ERROR, "unrecognized constraint subtype: %d",
1511 (int) constr->contype);
1516 * Since all other constraint types throw errors, this must be a check
1517 * constraint. First, process the constraint expression and add an entry
1521 ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1522 typTup->typbasetype, typTup->typtypmod,
1523 constr, NameStr(typTup->typname));
1526 * Test all values stored in the attributes based on the domain the
1527 * constraint is being added to.
1529 expr = (Expr *) stringToNode(ccbin);
1531 /* Need an EState to run ExecEvalExpr */
1532 estate = CreateExecutorState();
1533 econtext = GetPerTupleExprContext(estate);
1535 /* build execution state for expr */
1536 exprstate = ExecPrepareExpr(expr, estate);
1538 /* Fetch relation list with attributes based on this domain */
1539 /* ShareLock is sufficient to prevent concurrent data changes */
1541 rels = get_rels_with_domain(domainoid, ShareLock);
1545 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1546 Relation testrel = rtc->rel;
1547 TupleDesc tupdesc = RelationGetDescr(testrel);
1551 /* Scan all tuples in this relation */
1552 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1553 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1557 /* Test attributes that are of the domain */
1558 for (i = 0; i < rtc->natts; i++)
1560 int attnum = rtc->atts[i];
1565 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1567 econtext->domainValue_datum = d;
1568 econtext->domainValue_isNull = isNull;
1570 conResult = ExecEvalExprSwitchContext(exprstate,
1574 if (!isNull && !DatumGetBool(conResult))
1576 (errcode(ERRCODE_CHECK_VIOLATION),
1577 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
1578 NameStr(tupdesc->attrs[attnum - 1]->attname),
1579 RelationGetRelationName(testrel))));
1582 ResetExprContext(econtext);
1586 /* Hold relation lock till commit (XXX bad for concurrency) */
1587 heap_close(testrel, NoLock);
1590 FreeExecutorState(estate);
1593 heap_close(typrel, RowExclusiveLock);
1597 * get_rels_with_domain
1599 * Fetch all relations / attributes which are using the domain
1601 * The result is a list of RelToCheck structs, one for each distinct
1602 * relation, each containing one or more attribute numbers that are of
1603 * the domain type. We have opened each rel and acquired the specified lock
1606 * XXX this is completely broken because there is no way to lock the domain
1607 * to prevent columns from being added or dropped while our command runs.
1608 * We can partially protect against column drops by locking relations as we
1609 * come across them, but there is still a race condition (the window between
1610 * seeing a pg_depend entry and acquiring lock on the relation it references).
1611 * Also, holding locks on all these relations simultaneously creates a non-
1612 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
1613 * risk by using the weakest suitable lock (ShareLock for most callers).
1615 * XXX to support domains over domains, we'd need to make this smarter,
1616 * or make its callers smarter, so that we could find columns of derived
1617 * domains. Arrays of domains would be a problem too.
1619 * Generally used for retrieving a list of tests when adding
1620 * new constraints to a domain.
1623 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1628 SysScanDesc depScan;
1632 * We scan pg_depend to find those things that depend on the domain. (We
1633 * assume we can ignore refobjsubid for a domain.)
1635 depRel = heap_open(DependRelationId, AccessShareLock);
1637 ScanKeyInit(&key[0],
1638 Anum_pg_depend_refclassid,
1639 BTEqualStrategyNumber, F_OIDEQ,
1640 ObjectIdGetDatum(TypeRelationId));
1641 ScanKeyInit(&key[1],
1642 Anum_pg_depend_refobjid,
1643 BTEqualStrategyNumber, F_OIDEQ,
1644 ObjectIdGetDatum(domainOid));
1646 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
1647 SnapshotNow, 2, key);
1649 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1651 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1652 RelToCheck *rtc = NULL;
1654 Form_pg_attribute pg_att;
1657 /* Ignore dependees that aren't user columns of relations */
1658 /* (we assume system columns are never of domain types) */
1659 if (pg_depend->classid != RelationRelationId ||
1660 pg_depend->objsubid <= 0)
1663 /* See if we already have an entry for this relation */
1664 foreach(rellist, result)
1666 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1668 if (RelationGetRelid(rt->rel) == pg_depend->objid)
1677 /* First attribute found for this relation */
1680 /* Acquire requested lock on relation */
1681 rel = relation_open(pg_depend->objid, lockmode);
1683 /* It could be a view or composite type; if so ignore it */
1684 if (rel->rd_rel->relkind != RELKIND_RELATION)
1686 relation_close(rel, lockmode);
1690 /* Build the RelToCheck entry with enough space for all atts */
1691 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1694 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1695 result = lcons(rtc, result);
1699 * Confirm column has not been dropped, and is of the expected type.
1700 * This defends against an ALTER DROP COLUMN occuring just before we
1701 * acquired lock ... but if the whole table were dropped, we'd still
1704 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
1706 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
1707 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
1711 * Okay, add column to result. We store the columns in column-number
1712 * order; this is just a hack to improve predictability of regression
1715 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
1718 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
1720 rtc->atts[ptr] = rtc->atts[ptr - 1];
1723 rtc->atts[ptr] = pg_depend->objsubid;
1726 systable_endscan(depScan);
1728 relation_close(depRel, AccessShareLock);
1736 * Check that the type is actually a domain and that the current user
1737 * has permission to do ALTER DOMAIN on it. Throw an error if not.
1740 checkDomainOwner(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 in
1799 * the expression. Note that it will appear to have the type of the base
1800 * type, not the domain. This seems correct since within the check
1801 * expression, we should not assume the input value can be considered a
1802 * 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 function 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 can
1887 * 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 anyway
1963 val = fastgetattr(conTup, Anum_pg_constraint_conbin,
1964 conRel->rd_att, &isNull);
1966 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1967 NameStr(typTup->typname), NameStr(c->conname));
1969 check_expr = (Expr *)
1970 stringToNode(DatumGetCString(DirectFunctionCall1(textout,
1973 /* ExecInitExpr assumes we already fixed opfuncids */
1974 fix_opfuncids((Node *) check_expr);
1976 r = makeNode(DomainConstraintState);
1977 r->constrainttype = DOM_CONSTRAINT_CHECK;
1978 r->name = pstrdup(NameStr(c->conname));
1979 r->check_expr = ExecInitExpr(check_expr, NULL);
1982 * use lcons() here because constraints of lower domains should be
1985 result = lcons(r, result);
1988 systable_endscan(scan);
1990 /* loop to next domain in stack */
1991 typeOid = typTup->typbasetype;
1992 ReleaseSysCache(tup);
1995 heap_close(conRel, AccessShareLock);
1998 * Only need to add one NOT NULL check regardless of how many domains in
1999 * the stack request it.
2003 DomainConstraintState *r = makeNode(DomainConstraintState);
2005 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
2006 r->name = pstrdup("NOT NULL");
2007 r->check_expr = NULL;
2009 /* lcons to apply the nullness check FIRST */
2010 result = lcons(r, result);
2017 * Change the owner of a type.
2020 AlterTypeOwner(List *names, Oid newOwnerId)
2026 Form_pg_type typTup;
2027 AclResult aclresult;
2029 /* Make a TypeName so we can use standard type lookup machinery */
2030 typename = makeTypeNameFromNameList(names);
2032 /* Use LookupTypeName here so that shell types can be processed */
2033 typeOid = LookupTypeName(NULL, typename);
2034 if (!OidIsValid(typeOid))
2036 (errcode(ERRCODE_UNDEFINED_OBJECT),
2037 errmsg("type \"%s\" does not exist",
2038 TypeNameToString(typename))));
2040 /* Look up the type in the type table */
2041 rel = heap_open(TypeRelationId, RowExclusiveLock);
2043 tup = SearchSysCacheCopy(TYPEOID,
2044 ObjectIdGetDatum(typeOid),
2046 if (!HeapTupleIsValid(tup))
2047 elog(ERROR, "cache lookup failed for type %u", typeOid);
2048 typTup = (Form_pg_type) GETSTRUCT(tup);
2051 * If it's a composite type, we need to check that it really is a
2052 * free-standing composite type, and not a table's underlying type. We
2053 * want people to use ALTER TABLE not ALTER TYPE for that case.
2055 if (typTup->typtype == 'c' &&
2056 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
2058 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2059 errmsg("\"%s\" is a table's row type",
2060 TypeNameToString(typename))));
2063 * If the new owner is the same as the existing owner, consider the
2064 * command to have succeeded. This is for dump restoration purposes.
2066 if (typTup->typowner != newOwnerId)
2068 /* Superusers can always do it */
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,
2083 if (aclresult != ACLCHECK_OK)
2084 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
2085 get_namespace_name(typTup->typnamespace));
2089 * Modify the owner --- okay to scribble on typTup because it's a copy
2091 typTup->typowner = newOwnerId;
2093 simple_heap_update(rel, &tup->t_self, tup);
2095 CatalogUpdateIndexes(rel, tup);
2097 /* Update owner dependency reference */
2098 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2102 heap_close(rel, RowExclusiveLock);
2106 * AlterTypeOwnerInternal - change type owner unconditionally
2108 * This is currently only used to propagate ALTER TABLE OWNER to the
2109 * table's rowtype, and to implement REASSIGN OWNED BY. It assumes the
2110 * caller has done all needed checks.
2113 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
2117 Form_pg_type typTup;
2119 rel = heap_open(TypeRelationId, RowExclusiveLock);
2121 tup = SearchSysCacheCopy(TYPEOID,
2122 ObjectIdGetDatum(typeOid),
2124 if (!HeapTupleIsValid(tup))
2125 elog(ERROR, "cache lookup failed for type %u", typeOid);
2126 typTup = (Form_pg_type) GETSTRUCT(tup);
2129 * Modify the owner --- okay to scribble on typTup because it's a copy
2131 typTup->typowner = newOwnerId;
2133 simple_heap_update(rel, &tup->t_self, tup);
2135 CatalogUpdateIndexes(rel, tup);
2137 /* Update owner dependency reference */
2138 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2141 heap_close(rel, RowExclusiveLock);
2145 * Execute ALTER TYPE SET SCHEMA
2148 AlterTypeNamespace(List *names, const char *newschema)
2154 /* Make a TypeName so we can use standard type lookup machinery */
2155 typename = makeTypeNameFromNameList(names);
2156 typeOid = typenameTypeId(NULL, typename);
2158 /* check permissions on type */
2159 if (!pg_type_ownercheck(typeOid, GetUserId()))
2160 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2161 format_type_be(typeOid));
2163 /* get schema OID and check its permissions */
2164 nspOid = LookupCreationNamespace(newschema);
2166 /* and do the work */
2167 AlterTypeNamespaceInternal(typeOid, nspOid, true);
2171 * Move specified type to new namespace.
2173 * Caller must have already checked privileges.
2175 * If errorOnTableType is TRUE, the function errors out if the type is
2176 * a table type. ALTER TABLE has to be used to move a table to a new
2180 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
2181 bool errorOnTableType)
2185 Form_pg_type typform;
2187 bool isCompositeType;
2189 rel = heap_open(TypeRelationId, RowExclusiveLock);
2191 tup = SearchSysCacheCopy(TYPEOID,
2192 ObjectIdGetDatum(typeOid),
2194 if (!HeapTupleIsValid(tup))
2195 elog(ERROR, "cache lookup failed for type %u", typeOid);
2196 typform = (Form_pg_type) GETSTRUCT(tup);
2198 oldNspOid = typform->typnamespace;
2200 if (oldNspOid == nspOid)
2202 (errcode(ERRCODE_DUPLICATE_OBJECT),
2203 errmsg("type %s is already in schema \"%s\"",
2204 format_type_be(typeOid),
2205 get_namespace_name(nspOid))));
2207 /* disallow renaming into or out of temp schemas */
2208 if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
2210 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2211 errmsg("cannot move objects into or out of temporary schemas")));
2213 /* same for TOAST schema */
2214 if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
2216 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2217 errmsg("cannot move objects into or out of TOAST schema")));
2219 /* check for duplicate name (more friendly than unique-index failure) */
2220 if (SearchSysCacheExists(TYPENAMENSP,
2221 CStringGetDatum(NameStr(typform->typname)),
2222 ObjectIdGetDatum(nspOid),
2225 (errcode(ERRCODE_DUPLICATE_OBJECT),
2226 errmsg("type \"%s\" already exists in schema \"%s\"",
2227 NameStr(typform->typname),
2228 get_namespace_name(nspOid))));
2230 /* Detect whether type is a composite type (but not a table rowtype) */
2232 (typform->typtype == 'c' &&
2233 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
2235 /* Enforce not-table-type if requested */
2236 if (typform->typtype == 'c' && !isCompositeType && errorOnTableType)
2238 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2239 errmsg("%s is a table's row type",
2240 format_type_be(typeOid)),
2241 errhint("Use ALTER TABLE SET SCHEMA instead.")));
2243 /* OK, modify the pg_type row */
2245 /* tup is a copy, so we can scribble directly on it */
2246 typform->typnamespace = nspOid;
2248 simple_heap_update(rel, &tup->t_self, tup);
2249 CatalogUpdateIndexes(rel, tup);
2252 * Composite types have pg_class entries.
2254 * We need to modify the pg_class tuple as well to reflect the change of
2257 if (isCompositeType)
2261 classRel = heap_open(RelationRelationId, RowExclusiveLock);
2264 * The dependency on the schema is listed under the pg_class entry, so
2265 * tell AlterRelationNamespaceInternal to fix it.
2267 AlterRelationNamespaceInternal(classRel, typform->typrelid,
2271 heap_close(classRel, RowExclusiveLock);
2274 * Check for constraints associated with the composite type (we don't
2275 * currently support this, but probably will someday).
2277 AlterConstraintNamespaces(typform->typrelid, oldNspOid,
2282 /* If it's a domain, it might have constraints */
2283 if (typform->typtype == 'd')
2284 AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
2287 * Update dependency on schema, if any --- a table rowtype has not got
2290 if (typform->typtype != 'c')
2291 if (changeDependencyFor(TypeRelationId, typeOid,
2292 NamespaceRelationId, oldNspOid, nspOid) != 1)
2293 elog(ERROR, "failed to change schema dependency for type %s",
2294 format_type_be(typeOid));
2297 heap_freetuple(tup);
2299 heap_close(rel, RowExclusiveLock);