OSDN Git Service

CREATE DOMAIN ... DEFAULT NULL failed because gram.y special-cases DEFAULT
[pg-rex/syncrep.git] / src / backend / commands / typecmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * typecmds.c
4  *        Routines for SQL commands that manipulate types (and domains).
5  *
6  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.106 2007/06/20 18:15:49 tgl Exp $
12  *
13  * DESCRIPTION
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.
19  *
20  * NOTES
21  *        These things must be defined and committed in the following order:
22  *              "create function":
23  *                              input/output, recv/send functions
24  *              "create type":
25  *                              type
26  *              "create operator":
27  *                              operators
28  *
29  *
30  *-------------------------------------------------------------------------
31  */
32 #include "postgres.h"
33
34 #include "access/genam.h"
35 #include "access/heapam.h"
36 #include "access/xact.h"
37 #include "catalog/catalog.h"
38 #include "catalog/dependency.h"
39 #include "catalog/heap.h"
40 #include "catalog/indexing.h"
41 #include "catalog/pg_constraint.h"
42 #include "catalog/pg_depend.h"
43 #include "catalog/pg_enum.h"
44 #include "catalog/pg_namespace.h"
45 #include "catalog/pg_type.h"
46 #include "commands/defrem.h"
47 #include "commands/tablecmds.h"
48 #include "commands/typecmds.h"
49 #include "executor/executor.h"
50 #include "miscadmin.h"
51 #include "nodes/makefuncs.h"
52 #include "optimizer/planmain.h"
53 #include "optimizer/var.h"
54 #include "parser/parse_coerce.h"
55 #include "parser/parse_expr.h"
56 #include "parser/parse_func.h"
57 #include "parser/parse_type.h"
58 #include "utils/acl.h"
59 #include "utils/builtins.h"
60 #include "utils/fmgroids.h"
61 #include "utils/lsyscache.h"
62 #include "utils/memutils.h"
63 #include "utils/syscache.h"
64
65
66 /* result structure for get_rels_with_domain() */
67 typedef struct
68 {
69         Relation        rel;                    /* opened and locked relation */
70         int                     natts;                  /* number of attributes of interest */
71         int                *atts;                       /* attribute numbers */
72         /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
73 } RelToCheck;
74
75
76 static Oid      findTypeInputFunction(List *procname, Oid typeOid);
77 static Oid      findTypeOutputFunction(List *procname, Oid typeOid);
78 static Oid      findTypeReceiveFunction(List *procname, Oid typeOid);
79 static Oid      findTypeSendFunction(List *procname, Oid typeOid);
80 static Oid      findTypeTypmodinFunction(List *procname);
81 static Oid      findTypeTypmodoutFunction(List *procname);
82 static Oid      findTypeAnalyzeFunction(List *procname, Oid typeOid);
83 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
84 static void checkDomainOwner(HeapTuple tup, TypeName *typename);
85 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
86                                         Oid baseTypeOid,
87                                         int typMod, Constraint *constr,
88                                         char *domainName);
89
90
91 /*
92  * DefineType
93  *              Registers a new type.
94  */
95 void
96 DefineType(List *names, List *parameters)
97 {
98         char       *typeName;
99         Oid                     typeNamespace;
100         AclResult       aclresult;
101         int16           internalLength = -1;    /* default: variable-length */
102         Oid                     elemType = InvalidOid;
103         List       *inputName = NIL;
104         List       *outputName = NIL;
105         List       *receiveName = NIL;
106         List       *sendName = NIL;
107         List       *typmodinName = NIL;
108         List       *typmodoutName = NIL;
109         List       *analyzeName = NIL;
110         char       *defaultValue = NULL;
111         bool            byValue = false;
112         char            delimiter = DEFAULT_TYPDELIM;
113         char            alignment = 'i';        /* default alignment */
114         char            storage = 'p';  /* default TOAST storage method */
115         Oid                     inputOid;
116         Oid                     outputOid;
117         Oid                     receiveOid = InvalidOid;
118         Oid                     sendOid = InvalidOid;
119         Oid                     typmodinOid = InvalidOid;
120         Oid                     typmodoutOid = InvalidOid;
121         Oid                     analyzeOid = InvalidOid;
122         char       *array_type;
123         Oid         array_oid;
124         ListCell   *pl;
125         Oid                     typoid;
126         Oid                     resulttype;
127         Relation    pg_type;
128
129         /* Convert list of names to a name and namespace */
130         typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
131
132         /* Check we have creation rights in target namespace */
133         aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
134         if (aclresult != ACLCHECK_OK)
135                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
136                                            get_namespace_name(typeNamespace));
137
138         /*
139          * Look to see if type already exists (presumably as a shell; if not,
140          * TypeCreate will complain).
141          */
142         typoid = GetSysCacheOid(TYPENAMENSP,
143                                                         CStringGetDatum(typeName),
144                                                         ObjectIdGetDatum(typeNamespace),
145                                                         0, 0);
146
147         /*
148          * If it's not a shell, see if it's an autogenerated array type,
149          * and if so rename it out of the way.
150          */
151         if (OidIsValid(typoid) && get_typisdefined(typoid))
152         {
153                 if (moveArrayTypeName(typoid, typeName, typeNamespace))
154                         typoid = InvalidOid;
155         }
156
157         /*
158          * If it doesn't exist, create it as a shell, so that the OID is known
159          * for use in the I/O function definitions.
160          */
161         if (!OidIsValid(typoid))
162         {
163                 typoid = TypeShellMake(typeName, typeNamespace);
164                 /* Make new shell type visible for modification below */
165                 CommandCounterIncrement();
166
167                 /*
168                  * If the command was a parameterless CREATE TYPE, we're done ---
169                  * creating the shell type was all we're supposed to do.
170                  */
171                 if (parameters == NIL)
172                         return;
173         }
174         else
175         {
176                 /* Complain if dummy CREATE TYPE and entry already exists */
177                 if (parameters == NIL)
178                         ereport(ERROR,
179                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
180                                          errmsg("type \"%s\" already exists", typeName)));
181         }
182
183         foreach(pl, parameters)
184         {
185                 DefElem    *defel = (DefElem *) lfirst(pl);
186
187                 if (pg_strcasecmp(defel->defname, "internallength") == 0)
188                         internalLength = defGetTypeLength(defel);
189                 else if (pg_strcasecmp(defel->defname, "externallength") == 0)
190                         ;                                       /* ignored -- remove after 7.3 */
191                 else if (pg_strcasecmp(defel->defname, "input") == 0)
192                         inputName = defGetQualifiedName(defel);
193                 else if (pg_strcasecmp(defel->defname, "output") == 0)
194                         outputName = defGetQualifiedName(defel);
195                 else if (pg_strcasecmp(defel->defname, "receive") == 0)
196                         receiveName = defGetQualifiedName(defel);
197                 else if (pg_strcasecmp(defel->defname, "send") == 0)
198                         sendName = defGetQualifiedName(defel);
199                 else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
200                         typmodinName = defGetQualifiedName(defel);
201                 else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
202                         typmodoutName = defGetQualifiedName(defel);
203                 else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
204                                  pg_strcasecmp(defel->defname, "analyse") == 0)
205                         analyzeName = defGetQualifiedName(defel);
206                 else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
207                 {
208                         char       *p = defGetString(defel);
209
210                         delimiter = p[0];
211                 }
212                 else if (pg_strcasecmp(defel->defname, "element") == 0)
213                 {
214                         elemType = typenameTypeId(NULL, defGetTypeName(defel));
215                         /* disallow arrays of pseudotypes */
216                         if (get_typtype(elemType) == TYPTYPE_PSEUDO)
217                                 ereport(ERROR,
218                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
219                                                  errmsg("array element type cannot be %s",
220                                                                 format_type_be(elemType))));
221                 }
222                 else if (pg_strcasecmp(defel->defname, "default") == 0)
223                         defaultValue = defGetString(defel);
224                 else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
225                         byValue = defGetBoolean(defel);
226                 else if (pg_strcasecmp(defel->defname, "alignment") == 0)
227                 {
228                         char       *a = defGetString(defel);
229
230                         /*
231                          * Note: if argument was an unquoted identifier, parser will have
232                          * applied translations to it, so be prepared to recognize
233                          * translated type names as well as the nominal form.
234                          */
235                         if (pg_strcasecmp(a, "double") == 0 ||
236                                 pg_strcasecmp(a, "float8") == 0 ||
237                                 pg_strcasecmp(a, "pg_catalog.float8") == 0)
238                                 alignment = 'd';
239                         else if (pg_strcasecmp(a, "int4") == 0 ||
240                                          pg_strcasecmp(a, "pg_catalog.int4") == 0)
241                                 alignment = 'i';
242                         else if (pg_strcasecmp(a, "int2") == 0 ||
243                                          pg_strcasecmp(a, "pg_catalog.int2") == 0)
244                                 alignment = 's';
245                         else if (pg_strcasecmp(a, "char") == 0 ||
246                                          pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
247                                 alignment = 'c';
248                         else
249                                 ereport(ERROR,
250                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
251                                                  errmsg("alignment \"%s\" not recognized", a)));
252                 }
253                 else if (pg_strcasecmp(defel->defname, "storage") == 0)
254                 {
255                         char       *a = defGetString(defel);
256
257                         if (pg_strcasecmp(a, "plain") == 0)
258                                 storage = 'p';
259                         else if (pg_strcasecmp(a, "external") == 0)
260                                 storage = 'e';
261                         else if (pg_strcasecmp(a, "extended") == 0)
262                                 storage = 'x';
263                         else if (pg_strcasecmp(a, "main") == 0)
264                                 storage = 'm';
265                         else
266                                 ereport(ERROR,
267                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
268                                                  errmsg("storage \"%s\" not recognized", a)));
269                 }
270                 else
271                         ereport(WARNING,
272                                         (errcode(ERRCODE_SYNTAX_ERROR),
273                                          errmsg("type attribute \"%s\" not recognized",
274                                                         defel->defname)));
275         }
276
277         /*
278          * make sure we have our required definitions
279          */
280         if (inputName == NIL)
281                 ereport(ERROR,
282                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
283                                  errmsg("type input function must be specified")));
284         if (outputName == NIL)
285                 ereport(ERROR,
286                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
287                                  errmsg("type output function must be specified")));
288
289         if (typmodinName == NIL && typmodoutName != NIL)
290                 ereport(ERROR,
291                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
292                                  errmsg("type modifier output function is useless without a type modifier input function")));
293
294         /*
295          * Convert I/O proc names to OIDs
296          */
297         inputOid = findTypeInputFunction(inputName, typoid);
298         outputOid = findTypeOutputFunction(outputName, typoid);
299         if (receiveName)
300                 receiveOid = findTypeReceiveFunction(receiveName, typoid);
301         if (sendName)
302                 sendOid = findTypeSendFunction(sendName, typoid);
303
304         /*
305          * Verify that I/O procs return the expected thing.  If we see OPAQUE,
306          * complain and change it to the correct type-safe choice.
307          */
308         resulttype = get_func_rettype(inputOid);
309         if (resulttype != typoid)
310         {
311                 if (resulttype == OPAQUEOID)
312                 {
313                         /* backwards-compatibility hack */
314                         ereport(WARNING,
315                                         (errmsg("changing return type of function %s from \"opaque\" to %s",
316                                                         NameListToString(inputName), typeName)));
317                         SetFunctionReturnType(inputOid, typoid);
318                 }
319                 else
320                         ereport(ERROR,
321                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
322                                          errmsg("type input function %s must return type %s",
323                                                         NameListToString(inputName), typeName)));
324         }
325         resulttype = get_func_rettype(outputOid);
326         if (resulttype != CSTRINGOID)
327         {
328                 if (resulttype == OPAQUEOID)
329                 {
330                         /* backwards-compatibility hack */
331                         ereport(WARNING,
332                                         (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
333                                                         NameListToString(outputName))));
334                         SetFunctionReturnType(outputOid, CSTRINGOID);
335                 }
336                 else
337                         ereport(ERROR,
338                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
339                            errmsg("type output function %s must return type \"cstring\"",
340                                           NameListToString(outputName))));
341         }
342         if (receiveOid)
343         {
344                 resulttype = get_func_rettype(receiveOid);
345                 if (resulttype != typoid)
346                         ereport(ERROR,
347                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
348                                          errmsg("type receive function %s must return type %s",
349                                                         NameListToString(receiveName), typeName)));
350         }
351         if (sendOid)
352         {
353                 resulttype = get_func_rettype(sendOid);
354                 if (resulttype != BYTEAOID)
355                         ereport(ERROR,
356                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
357                                    errmsg("type send function %s must return type \"bytea\"",
358                                                   NameListToString(sendName))));
359         }
360
361         /*
362          * Convert typmodin/out function proc names to OIDs.
363          */
364         if (typmodinName)
365                 typmodinOid = findTypeTypmodinFunction(typmodinName);
366         if (typmodoutName)
367                 typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
368
369         /*
370          * Convert analysis function proc name to an OID. If no analysis function
371          * is specified, we'll use zero to select the built-in default algorithm.
372          */
373         if (analyzeName)
374                 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
375
376         /*
377          * Check permissions on functions.      We choose to require the creator/owner
378          * of a type to also own the underlying functions.      Since creating a type
379          * is tantamount to granting public execute access on the functions, the
380          * minimum sane check would be for execute-with-grant-option.  But we
381          * don't have a way to make the type go away if the grant option is
382          * revoked, so ownership seems better.
383          */
384         if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
385                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
386                                            NameListToString(inputName));
387         if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
388                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
389                                            NameListToString(outputName));
390         if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
391                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
392                                            NameListToString(receiveName));
393         if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
394                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
395                                            NameListToString(sendName));
396         if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
397                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
398                                            NameListToString(typmodinName));
399         if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
400                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
401                                            NameListToString(typmodoutName));
402         if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
403                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
404                                            NameListToString(analyzeName));
405
406         /* Preassign array type OID so we can insert it in pg_type.typarray */
407         pg_type = heap_open(TypeRelationId, AccessShareLock);   
408         array_oid = GetNewOid(pg_type);
409         heap_close(pg_type, AccessShareLock);
410
411         /*
412          * now have TypeCreate do all the real work.
413          */
414         typoid =
415                 TypeCreate(InvalidOid,  /* no predetermined type OID */
416                                    typeName,    /* type name */
417                                    typeNamespace,               /* namespace */
418                                    InvalidOid,  /* relation oid (n/a here) */
419                                    0,                   /* relation kind (ditto) */
420                                    internalLength,              /* internal size */
421                                    TYPTYPE_BASE,                /* type-type (base type) */
422                                    delimiter,   /* array element delimiter */
423                                    inputOid,    /* input procedure */
424                                    outputOid,   /* output procedure */
425                                    receiveOid,  /* receive procedure */
426                                    sendOid,             /* send procedure */
427                                    typmodinOid, /* typmodin procedure */
428                                    typmodoutOid,/* typmodout procedure */
429                                    analyzeOid,  /* analyze procedure */
430                                    elemType,    /* element type ID */
431                                    false,               /* this is not an array type */
432                                    array_oid,   /* array type we are about to create */
433                                    InvalidOid,  /* base type ID (only for domains) */
434                                    defaultValue,        /* default type value */
435                                    NULL,                /* no binary form available */
436                                    byValue,             /* passed by value */
437                                    alignment,   /* required alignment */
438                                    storage,             /* TOAST strategy */
439                                    -1,                  /* typMod (Domains only) */
440                                    0,                   /* Array Dimensions of typbasetype */
441                                    false);              /* Type NOT NULL */
442
443         /*
444          * Create the array type that goes with it.
445          */
446         array_type = makeArrayTypeName(typeName, typeNamespace);
447
448         /* alignment must be 'i' or 'd' for arrays */
449         alignment = (alignment == 'd') ? 'd' : 'i';
450
451         TypeCreate(array_oid,           /* force assignment of this type OID */
452                            array_type,          /* type name */
453                            typeNamespace,       /* namespace */
454                            InvalidOid,          /* relation oid (n/a here) */
455                            0,                           /* relation kind (ditto) */
456                            -1,                          /* internal size (always varlena) */
457                            TYPTYPE_BASE,        /* type-type (base type) */
458                            DEFAULT_TYPDELIM,    /* array element delimiter */
459                            F_ARRAY_IN,          /* input procedure */
460                            F_ARRAY_OUT,         /* output procedure */
461                            F_ARRAY_RECV,        /* receive procedure */
462                            F_ARRAY_SEND,        /* send procedure */
463                            typmodinOid,         /* typmodin procedure */
464                            typmodoutOid,        /* typmodout procedure */
465                            InvalidOid,          /* analyze procedure - default */
466                            typoid,                      /* element type ID */
467                            true,                        /* yes this is an array type */
468                            InvalidOid,          /* no further array type */
469                            InvalidOid,          /* base type ID */
470                            NULL,                        /* never a default type value */
471                            NULL,                        /* binary default isn't sent either */
472                            false,                       /* never passed by value */
473                            alignment,           /* see above */
474                            'x',                         /* ARRAY is always toastable */
475                            -1,                          /* typMod (Domains only) */
476                            0,                           /* Array dimensions of typbasetype */
477                            false);                      /* Type NOT NULL */
478
479         pfree(array_type);
480 }
481
482
483 /*
484  *      RemoveType
485  *              Removes a datatype.
486  */
487 void
488 RemoveType(List *names, DropBehavior behavior, bool missing_ok)
489 {
490         TypeName   *typename;
491         Oid                     typeoid;
492         HeapTuple       tup;
493         ObjectAddress object;
494         Form_pg_type typ;
495
496         /* Make a TypeName so we can use standard type lookup machinery */
497         typename = makeTypeNameFromNameList(names);
498
499         /* Use LookupTypeName here so that shell types can be removed. */
500         typeoid = LookupTypeName(NULL, typename);
501         if (!OidIsValid(typeoid))
502         {
503                 if (!missing_ok)
504                 {
505                         ereport(ERROR,
506                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
507                                          errmsg("type \"%s\" does not exist",
508                                                         TypeNameToString(typename))));
509                 }
510                 else
511                 {
512                         ereport(NOTICE,
513                                         (errmsg("type \"%s\" does not exist, skipping",
514                                                         TypeNameToString(typename))));
515                 }
516
517                 return;
518         }
519
520         tup = SearchSysCache(TYPEOID,
521                                                  ObjectIdGetDatum(typeoid),
522                                                  0, 0, 0);
523         if (!HeapTupleIsValid(tup))
524                 elog(ERROR, "cache lookup failed for type %u", typeoid);
525         typ = (Form_pg_type) GETSTRUCT(tup);
526
527         /* Permission check: must own type or its namespace */
528         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
529                 !pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
530                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
531                                            TypeNameToString(typename));
532
533         /*
534          * Note: we need no special check for array types here, as the normal
535          * treatment of internal dependencies handles it just fine
536          */
537
538         ReleaseSysCache(tup);
539
540         /*
541          * Do the deletion
542          */
543         object.classId = TypeRelationId;
544         object.objectId = typeoid;
545         object.objectSubId = 0;
546
547         performDeletion(&object, behavior);
548 }
549
550
551 /*
552  * Guts of type deletion.
553  */
554 void
555 RemoveTypeById(Oid typeOid)
556 {
557         Relation        relation;
558         HeapTuple       tup;
559
560         relation = heap_open(TypeRelationId, RowExclusiveLock);
561
562         tup = SearchSysCache(TYPEOID,
563                                                  ObjectIdGetDatum(typeOid),
564                                                  0, 0, 0);
565         if (!HeapTupleIsValid(tup))
566                 elog(ERROR, "cache lookup failed for type %u", typeOid);
567
568         simple_heap_delete(relation, &tup->t_self);
569
570         /*
571          * If it is an enum, delete the pg_enum entries too; we don't bother
572          * with making dependency entries for those, so it has to be done
573          * "by hand" here.
574          */
575         if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
576                 EnumValuesDelete(typeOid);
577
578         ReleaseSysCache(tup);
579
580         heap_close(relation, RowExclusiveLock);
581 }
582
583
584 /*
585  * DefineDomain
586  *              Registers a new domain.
587  */
588 void
589 DefineDomain(CreateDomainStmt *stmt)
590 {
591         char       *domainName;
592         Oid                     domainNamespace;
593         AclResult       aclresult;
594         int16           internalLength;
595         Oid                     inputProcedure;
596         Oid                     outputProcedure;
597         Oid                     receiveProcedure;
598         Oid                     sendProcedure;
599         Oid                     analyzeProcedure;
600         bool            byValue;
601         Oid                     typelem;
602         char            delimiter;
603         char            alignment;
604         char            storage;
605         char            typtype;
606         Datum           datum;
607         bool            isnull;
608         char       *defaultValue = NULL;
609         char       *defaultValueBin = NULL;
610         bool            saw_default = false;
611         bool            typNotNull = false;
612         bool            nullDefined = false;
613         int32           typNDims = list_length(stmt->typename->arrayBounds);
614         HeapTuple       typeTup;
615         List       *schema = stmt->constraints;
616         ListCell   *listptr;
617         Oid                     basetypeoid;
618         Oid                     domainoid;
619         Oid                     old_type_oid;
620         Form_pg_type baseType;
621         int32           basetypeMod;
622
623         /* Convert list of names to a name and namespace */
624         domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
625                                                                                                                 &domainName);
626
627         /* Check we have creation rights in target namespace */
628         aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
629                                                                           ACL_CREATE);
630         if (aclresult != ACLCHECK_OK)
631                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
632                                            get_namespace_name(domainNamespace));
633
634         /*
635          * Check for collision with an existing type name.  If there is one and
636          * it's an autogenerated array, we can rename it out of the way.
637          */
638         old_type_oid = GetSysCacheOid(TYPENAMENSP,
639                                                                   CStringGetDatum(domainName),
640                                                                   ObjectIdGetDatum(domainNamespace),
641                                                                   0, 0);
642         if (OidIsValid(old_type_oid))
643         {
644                 if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
645                         ereport(ERROR,
646                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
647                                          errmsg("type \"%s\" already exists", domainName)));
648         }
649
650         /*
651          * Look up the base type.
652          */
653         typeTup = typenameType(NULL, stmt->typename);
654         baseType = (Form_pg_type) GETSTRUCT(typeTup);
655         basetypeoid = HeapTupleGetOid(typeTup);
656         basetypeMod = typenameTypeMod(NULL, stmt->typename, basetypeoid);
657
658         /*
659          * Base type must be a plain base type, another domain or an enum.
660          * Domains over pseudotypes would create a security hole.  Domains
661          * over composite types might be made to work in the future, but not
662          * today.
663          */
664         typtype = baseType->typtype;
665         if (typtype != TYPTYPE_BASE &&
666                 typtype != TYPTYPE_DOMAIN &&
667                 typtype != TYPTYPE_ENUM)
668                 ereport(ERROR,
669                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
670                                  errmsg("\"%s\" is not a valid base type for a domain",
671                                                 TypeNameToString(stmt->typename))));
672
673         /* passed by value */
674         byValue = baseType->typbyval;
675
676         /* Required Alignment */
677         alignment = baseType->typalign;
678
679         /* TOAST Strategy */
680         storage = baseType->typstorage;
681
682         /* Storage Length */
683         internalLength = baseType->typlen;
684
685         /* Array element type (in case base type is an array) */
686         typelem = baseType->typelem;
687
688         /* Array element Delimiter */
689         delimiter = baseType->typdelim;
690
691         /* I/O Functions */
692         inputProcedure = F_DOMAIN_IN;
693         outputProcedure = baseType->typoutput;
694         receiveProcedure = F_DOMAIN_RECV;
695         sendProcedure = baseType->typsend;
696
697         /* Domains never accept typmods, so no typmodin/typmodout needed */
698
699         /* Analysis function */
700         analyzeProcedure = baseType->typanalyze;
701
702         /* Inherited default value */
703         datum = SysCacheGetAttr(TYPEOID, typeTup,
704                                                         Anum_pg_type_typdefault, &isnull);
705         if (!isnull)
706                 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
707
708         /* Inherited default binary value */
709         datum = SysCacheGetAttr(TYPEOID, typeTup,
710                                                         Anum_pg_type_typdefaultbin, &isnull);
711         if (!isnull)
712                 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
713
714         /*
715          * Run through constraints manually to avoid the additional processing
716          * conducted by DefineRelation() and friends.
717          */
718         foreach(listptr, schema)
719         {
720                 Node       *newConstraint = lfirst(listptr);
721                 Constraint *constr;
722
723                 /* Check for unsupported constraint types */
724                 if (IsA(newConstraint, FkConstraint))
725                         ereport(ERROR,
726                                         (errcode(ERRCODE_SYNTAX_ERROR),
727                                 errmsg("foreign key constraints not possible for domains")));
728
729                 /* otherwise it should be a plain Constraint */
730                 if (!IsA(newConstraint, Constraint))
731                         elog(ERROR, "unrecognized node type: %d",
732                                  (int) nodeTag(newConstraint));
733
734                 constr = (Constraint *) newConstraint;
735
736                 switch (constr->contype)
737                 {
738                         case CONSTR_DEFAULT:
739
740                                 /*
741                                  * The inherited default value may be overridden by the user
742                                  * with the DEFAULT <expr> clause ... but only once.
743                                  */
744                                 if (saw_default)
745                                         ereport(ERROR,
746                                                         (errcode(ERRCODE_SYNTAX_ERROR),
747                                                          errmsg("multiple default expressions")));
748                                 saw_default = true;
749
750                                 if (constr->raw_expr)
751                                 {
752                                         ParseState *pstate;
753                                         Node       *defaultExpr;
754
755                                         /* Create a dummy ParseState for transformExpr */
756                                         pstate = make_parsestate(NULL);
757
758                                         /*
759                                          * Cook the constr->raw_expr into an expression.
760                                          * Note: name is strictly for error message
761                                          */
762                                         defaultExpr = cookDefault(pstate, constr->raw_expr,
763                                                                                           basetypeoid,
764                                                                                           basetypeMod,
765                                                                                           domainName);
766
767                                         /*
768                                          * Expression must be stored as a nodeToString result, but
769                                          * we also require a valid textual representation (mainly
770                                          * to make life easier for pg_dump).
771                                          */
772                                         defaultValue =
773                                                 deparse_expression(defaultExpr,
774                                                                                    deparse_context_for(domainName,
775                                                                                                                            InvalidOid),
776                                                                                    false, false);
777                                         defaultValueBin = nodeToString(defaultExpr);
778                                 }
779                                 else
780                                 {
781                                         /* DEFAULT NULL is same as not having a default */
782                                         defaultValue = NULL;
783                                         defaultValueBin = NULL;
784                                 }
785                                 break;
786
787                         case CONSTR_NOTNULL:
788                                 if (nullDefined && !typNotNull)
789                                         ereport(ERROR,
790                                                         (errcode(ERRCODE_SYNTAX_ERROR),
791                                                    errmsg("conflicting NULL/NOT NULL constraints")));
792                                 typNotNull = true;
793                                 nullDefined = true;
794                                 break;
795
796                         case CONSTR_NULL:
797                                 if (nullDefined && typNotNull)
798                                         ereport(ERROR,
799                                                         (errcode(ERRCODE_SYNTAX_ERROR),
800                                                    errmsg("conflicting NULL/NOT NULL constraints")));
801                                 typNotNull = false;
802                                 nullDefined = true;
803                                 break;
804
805                         case CONSTR_CHECK:
806
807                                 /*
808                                  * Check constraints are handled after domain creation, as
809                                  * they require the Oid of the domain
810                                  */
811                                 break;
812
813                                 /*
814                                  * All else are error cases
815                                  */
816                         case CONSTR_UNIQUE:
817                                 ereport(ERROR,
818                                                 (errcode(ERRCODE_SYNTAX_ERROR),
819                                          errmsg("unique constraints not possible for domains")));
820                                 break;
821
822                         case CONSTR_PRIMARY:
823                                 ereport(ERROR,
824                                                 (errcode(ERRCODE_SYNTAX_ERROR),
825                                 errmsg("primary key constraints not possible for domains")));
826                                 break;
827
828                         case CONSTR_ATTR_DEFERRABLE:
829                         case CONSTR_ATTR_NOT_DEFERRABLE:
830                         case CONSTR_ATTR_DEFERRED:
831                         case CONSTR_ATTR_IMMEDIATE:
832                                 ereport(ERROR,
833                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
834                                                  errmsg("specifying constraint deferrability not supported for domains")));
835                                 break;
836
837                         default:
838                                 elog(ERROR, "unrecognized constraint subtype: %d",
839                                          (int) constr->contype);
840                                 break;
841                 }
842         }
843
844         /*
845          * Have TypeCreate do all the real work.
846          */
847         domainoid =
848                 TypeCreate(InvalidOid,  /* no predetermined type OID */
849                                    domainName,  /* type name */
850                                    domainNamespace,             /* namespace */
851                                    InvalidOid,  /* relation oid (n/a here) */
852                                    0,                   /* relation kind (ditto) */
853                                    internalLength,              /* internal size */
854                                    TYPTYPE_DOMAIN,              /* type-type (domain type) */
855                                    delimiter,   /* array element delimiter */
856                                    inputProcedure,              /* input procedure */
857                                    outputProcedure,             /* output procedure */
858                                    receiveProcedure,    /* receive procedure */
859                                    sendProcedure,               /* send procedure */
860                                    InvalidOid,                  /* typmodin procedure - none */
861                                    InvalidOid,                  /* typmodout procedure - none */
862                                    analyzeProcedure,    /* analyze procedure */
863                                    typelem,             /* element type ID */
864                                    false,               /* this isn't an array */
865                                    InvalidOid,  /* no arrays for domains (yet) */
866                                    basetypeoid, /* base type ID */
867                                    defaultValue,        /* default type value (text) */
868                                    defaultValueBin,             /* default type value (binary) */
869                                    byValue,             /* passed by value */
870                                    alignment,   /* required alignment */
871                                    storage,             /* TOAST strategy */
872                                    basetypeMod, /* typeMod value */
873                                    typNDims,    /* Array dimensions for base type */
874                                    typNotNull); /* Type NOT NULL */
875
876         /*
877          * Process constraints which refer to the domain ID returned by TypeCreate
878          */
879         foreach(listptr, schema)
880         {
881                 Constraint *constr = lfirst(listptr);
882
883                 /* it must be a Constraint, per check above */
884
885                 switch (constr->contype)
886                 {
887                         case CONSTR_CHECK:
888                                 domainAddConstraint(domainoid, domainNamespace,
889                                                                         basetypeoid, basetypeMod,
890                                                                         constr, domainName);
891                                 break;
892
893                                 /* Other constraint types were fully processed above */
894
895                         default:
896                                 break;
897                 }
898
899                 /* CCI so we can detect duplicate constraint names */
900                 CommandCounterIncrement();
901         }
902
903         /*
904          * Now we can clean up.
905          */
906         ReleaseSysCache(typeTup);
907 }
908
909
910 /*
911  *      RemoveDomain
912  *              Removes a domain.
913  *
914  * This is identical to RemoveType except we insist it be a domain.
915  */
916 void
917 RemoveDomain(List *names, DropBehavior behavior, bool missing_ok)
918 {
919         TypeName   *typename;
920         Oid                     typeoid;
921         HeapTuple       tup;
922         char            typtype;
923         ObjectAddress object;
924
925         /* Make a TypeName so we can use standard type lookup machinery */
926         typename = makeTypeNameFromNameList(names);
927
928         /* Use LookupTypeName here so that shell types can be removed. */
929         typeoid = LookupTypeName(NULL, typename);
930         if (!OidIsValid(typeoid))
931         {
932                 if (!missing_ok)
933                 {
934                         ereport(ERROR,
935                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
936                                          errmsg("type \"%s\" does not exist",
937                                                         TypeNameToString(typename))));
938                 }
939                 else
940                 {
941                         ereport(NOTICE,
942                                         (errmsg("type \"%s\" does not exist, skipping",
943                                                         TypeNameToString(typename))));
944                 }
945
946                 return;
947         }
948
949         tup = SearchSysCache(TYPEOID,
950                                                  ObjectIdGetDatum(typeoid),
951                                                  0, 0, 0);
952         if (!HeapTupleIsValid(tup))
953                 elog(ERROR, "cache lookup failed for type %u", typeoid);
954
955         /* Permission check: must own type or its namespace */
956         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
957           !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
958                                                            GetUserId()))
959                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
960                                            TypeNameToString(typename));
961
962         /* Check that this is actually a domain */
963         typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
964
965         if (typtype != TYPTYPE_DOMAIN)
966                 ereport(ERROR,
967                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
968                                  errmsg("\"%s\" is not a domain",
969                                                 TypeNameToString(typename))));
970
971         ReleaseSysCache(tup);
972
973         /*
974          * Do the deletion
975          */
976         object.classId = TypeRelationId;
977         object.objectId = typeoid;
978         object.objectSubId = 0;
979
980         performDeletion(&object, behavior);
981 }
982
983 /*
984  * DefineEnum
985  *              Registers a new enum.
986  */
987 void
988 DefineEnum(CreateEnumStmt *stmt)
989 {
990         char   *enumName;
991         char   *enumArrayName;
992         Oid             enumNamespace;
993         Oid             enumTypeOid;
994         AclResult       aclresult;
995         Oid             old_type_oid;
996         Oid     enumArrayOid;
997         Relation pg_type;
998
999         /* Convert list of names to a name and namespace */
1000         enumNamespace = QualifiedNameGetCreationNamespace(stmt->typename,
1001                                                                                                           &enumName);
1002
1003         /* Check we have creation rights in target namespace */
1004         aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
1005         if (aclresult != ACLCHECK_OK)
1006                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1007                                            get_namespace_name(enumNamespace));
1008
1009         /*
1010          * Check for collision with an existing type name.  If there is one and
1011          * it's an autogenerated array, we can rename it out of the way.
1012          */
1013         old_type_oid = GetSysCacheOid(TYPENAMENSP,
1014                                                                   CStringGetDatum(enumName),
1015                                                                   ObjectIdGetDatum(enumNamespace),
1016                                                                   0, 0);
1017         if (OidIsValid(old_type_oid))
1018         {
1019                 if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
1020                         ereport(ERROR,
1021                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
1022                                          errmsg("type \"%s\" already exists", enumName)));
1023         }
1024
1025         /* Preassign array type OID so we can insert it in pg_type.typarray */
1026         pg_type = heap_open(TypeRelationId, AccessShareLock);   
1027         enumArrayOid = GetNewOid(pg_type);
1028         heap_close(pg_type, AccessShareLock);
1029
1030         /* Create the pg_type entry */
1031         enumTypeOid = 
1032                 TypeCreate(InvalidOid,          /* no predetermined type OID */
1033                                    enumName,            /* type name */
1034                                    enumNamespace,       /* namespace */
1035                                    InvalidOid,          /* relation oid (n/a here) */
1036                                    0,                           /* relation kind (ditto) */
1037                                    sizeof(Oid),         /* internal size */
1038                                    TYPTYPE_ENUM,        /* type-type (enum type) */
1039                                    DEFAULT_TYPDELIM,    /* array element delimiter */
1040                                    F_ENUM_IN,           /* input procedure */
1041                                    F_ENUM_OUT,          /* output procedure */
1042                                    InvalidOid,          /* receive procedure - none */
1043                                    InvalidOid,          /* send procedure - none */
1044                                    InvalidOid,          /* typmodin procedure - none */
1045                                    InvalidOid,          /* typmodout procedure - none */
1046                                    InvalidOid,          /* analyze procedure - default */
1047                                    InvalidOid,          /* element type ID */
1048                                    false,                       /* this is not an array type */
1049                                    enumArrayOid,        /* array type we are about to create */
1050                                    InvalidOid,          /* base type ID (only for domains) */
1051                                    NULL,                        /* never a default type value */
1052                                    NULL,                        /* binary default isn't sent either */
1053                                    true,                        /* always passed by value */
1054                                    'i',                         /* int alignment */
1055                                    'p',                         /* TOAST strategy always plain */
1056                                    -1,                          /* typMod (Domains only) */
1057                                    0,                           /* Array dimensions of typbasetype */
1058                                    false);                      /* Type NOT NULL */
1059
1060         /* Enter the enum's values into pg_enum */
1061         EnumValuesCreate(enumTypeOid, stmt->vals);
1062
1063         /*
1064          * Create the array type that goes with it.
1065          */
1066         enumArrayName = makeArrayTypeName(enumName, enumNamespace);
1067
1068         TypeCreate(enumArrayOid,        /* force assignment of this type OID */
1069                            enumArrayName,   /* type name */
1070                            enumNamespace,   /* namespace */
1071                            InvalidOid,      /* relation oid (n/a here) */
1072                            0,               /* relation kind (ditto) */
1073                            -1,              /* internal size (always varlena) */
1074                            TYPTYPE_BASE,        /* type-type (base type) */
1075                            DEFAULT_TYPDELIM,    /* array element delimiter */
1076                            F_ARRAY_IN,      /* input procedure */
1077                            F_ARRAY_OUT,     /* output procedure */
1078                            F_ARRAY_RECV,    /* receive procedure */
1079                            F_ARRAY_SEND,    /* send procedure */
1080                            InvalidOid,          /* typmodin procedure - none */
1081                            InvalidOid,          /* typmodout procedure - none */
1082                            InvalidOid,      /* analyze procedure - default */
1083                            enumTypeOid,     /* element type ID */
1084                            true,                        /* yes this is an array type */
1085                            InvalidOid,          /* no further array type */
1086                            InvalidOid,      /* base type ID */
1087                            NULL,            /* never a default type value */
1088                            NULL,            /* binary default isn't sent either */
1089                            false,           /* never passed by value */
1090                            'i',             /* enums have align i, so do their arrays */
1091                            'x',             /* ARRAY is always toastable */
1092                            -1,              /* typMod (Domains only) */
1093                0,               /* Array dimensions of typbasetype */
1094                            false);                      /* Type NOT NULL */
1095
1096         pfree(enumArrayName);
1097 }
1098
1099
1100 /*
1101  * Find suitable I/O functions for a type.
1102  *
1103  * typeOid is the type's OID (which will already exist, if only as a shell
1104  * type).
1105  */
1106
1107 static Oid
1108 findTypeInputFunction(List *procname, Oid typeOid)
1109 {
1110         Oid                     argList[3];
1111         Oid                     procOid;
1112
1113         /*
1114          * Input functions can take a single argument of type CSTRING, or three
1115          * arguments (string, typioparam OID, typmod).
1116          *
1117          * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
1118          * see this, we issue a warning and fix up the pg_proc entry.
1119          */
1120         argList[0] = CSTRINGOID;
1121
1122         procOid = LookupFuncName(procname, 1, argList, true);
1123         if (OidIsValid(procOid))
1124                 return procOid;
1125
1126         argList[1] = OIDOID;
1127         argList[2] = INT4OID;
1128
1129         procOid = LookupFuncName(procname, 3, argList, true);
1130         if (OidIsValid(procOid))
1131                 return procOid;
1132
1133         /* No luck, try it with OPAQUE */
1134         argList[0] = OPAQUEOID;
1135
1136         procOid = LookupFuncName(procname, 1, argList, true);
1137
1138         if (!OidIsValid(procOid))
1139         {
1140                 argList[1] = OIDOID;
1141                 argList[2] = INT4OID;
1142
1143                 procOid = LookupFuncName(procname, 3, argList, true);
1144         }
1145
1146         if (OidIsValid(procOid))
1147         {
1148                 /* Found, but must complain and fix the pg_proc entry */
1149                 ereport(WARNING,
1150                                 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
1151                                                 NameListToString(procname))));
1152                 SetFunctionArgType(procOid, 0, CSTRINGOID);
1153
1154                 /*
1155                  * Need CommandCounterIncrement since DefineType will likely try to
1156                  * alter the pg_proc tuple again.
1157                  */
1158                 CommandCounterIncrement();
1159
1160                 return procOid;
1161         }
1162
1163         /* Use CSTRING (preferred) in the error message */
1164         argList[0] = CSTRINGOID;
1165
1166         ereport(ERROR,
1167                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1168                          errmsg("function %s does not exist",
1169                                         func_signature_string(procname, 1, argList))));
1170
1171         return InvalidOid;                      /* keep compiler quiet */
1172 }
1173
1174 static Oid
1175 findTypeOutputFunction(List *procname, Oid typeOid)
1176 {
1177         Oid                     argList[1];
1178         Oid                     procOid;
1179
1180         /*
1181          * Output functions can take a single argument of the type.
1182          *
1183          * For backwards compatibility we allow OPAQUE in place of the actual type
1184          * name; if we see this, we issue a warning and fix up the pg_proc entry.
1185          */
1186         argList[0] = typeOid;
1187
1188         procOid = LookupFuncName(procname, 1, argList, true);
1189         if (OidIsValid(procOid))
1190                 return procOid;
1191
1192         /* No luck, try it with OPAQUE */
1193         argList[0] = OPAQUEOID;
1194
1195         procOid = LookupFuncName(procname, 1, argList, true);
1196
1197         if (OidIsValid(procOid))
1198         {
1199                 /* Found, but must complain and fix the pg_proc entry */
1200                 ereport(WARNING,
1201                 (errmsg("changing argument type of function %s from \"opaque\" to %s",
1202                                 NameListToString(procname), format_type_be(typeOid))));
1203                 SetFunctionArgType(procOid, 0, typeOid);
1204
1205                 /*
1206                  * Need CommandCounterIncrement since DefineType will likely try to
1207                  * alter the pg_proc tuple again.
1208                  */
1209                 CommandCounterIncrement();
1210
1211                 return procOid;
1212         }
1213
1214         /* Use type name, not OPAQUE, in the failure message. */
1215         argList[0] = typeOid;
1216
1217         ereport(ERROR,
1218                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1219                          errmsg("function %s does not exist",
1220                                         func_signature_string(procname, 1, argList))));
1221
1222         return InvalidOid;                      /* keep compiler quiet */
1223 }
1224
1225 static Oid
1226 findTypeReceiveFunction(List *procname, Oid typeOid)
1227 {
1228         Oid                     argList[3];
1229         Oid                     procOid;
1230
1231         /*
1232          * Receive functions can take a single argument of type INTERNAL, or three
1233          * arguments (internal, typioparam OID, typmod).
1234          */
1235         argList[0] = INTERNALOID;
1236
1237         procOid = LookupFuncName(procname, 1, argList, true);
1238         if (OidIsValid(procOid))
1239                 return procOid;
1240
1241         argList[1] = OIDOID;
1242         argList[2] = INT4OID;
1243
1244         procOid = LookupFuncName(procname, 3, argList, true);
1245         if (OidIsValid(procOid))
1246                 return procOid;
1247
1248         ereport(ERROR,
1249                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1250                          errmsg("function %s does not exist",
1251                                         func_signature_string(procname, 1, argList))));
1252
1253         return InvalidOid;                      /* keep compiler quiet */
1254 }
1255
1256 static Oid
1257 findTypeSendFunction(List *procname, Oid typeOid)
1258 {
1259         Oid                     argList[1];
1260         Oid                     procOid;
1261
1262         /*
1263          * Send functions can take a single argument of the type.
1264          */
1265         argList[0] = typeOid;
1266
1267         procOid = LookupFuncName(procname, 1, argList, true);
1268         if (OidIsValid(procOid))
1269                 return procOid;
1270
1271         ereport(ERROR,
1272                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1273                          errmsg("function %s does not exist",
1274                                         func_signature_string(procname, 1, argList))));
1275
1276         return InvalidOid;                      /* keep compiler quiet */
1277 }
1278
1279 static Oid
1280 findTypeTypmodinFunction(List *procname)
1281 {
1282         Oid                     argList[1];
1283         Oid                     procOid;
1284
1285         /*
1286          * typmodin functions always take one cstring[] argument and return int4.
1287          */
1288         argList[0] = CSTRINGARRAYOID;
1289
1290         procOid = LookupFuncName(procname, 1, argList, true);
1291         if (!OidIsValid(procOid))
1292                 ereport(ERROR,
1293                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1294                                  errmsg("function %s does not exist",
1295                                                 func_signature_string(procname, 1, argList))));
1296
1297         if (get_func_rettype(procOid) != INT4OID)
1298                 ereport(ERROR,
1299                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1300                                  errmsg("typmod_in function %s must return type \"integer\"",
1301                                                 NameListToString(procname))));
1302
1303         return procOid;
1304 }
1305
1306 static Oid
1307 findTypeTypmodoutFunction(List *procname)
1308 {
1309         Oid                     argList[1];
1310         Oid                     procOid;
1311
1312         /*
1313          * typmodout functions always take one int4 argument and return cstring.
1314          */
1315         argList[0] = INT4OID;
1316
1317         procOid = LookupFuncName(procname, 1, argList, true);
1318         if (!OidIsValid(procOid))
1319                 ereport(ERROR,
1320                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1321                                  errmsg("function %s does not exist",
1322                                                 func_signature_string(procname, 1, argList))));
1323
1324         if (get_func_rettype(procOid) != CSTRINGOID)
1325                 ereport(ERROR,
1326                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1327                                  errmsg("typmod_out function %s must return type \"cstring\"",
1328                                                 NameListToString(procname))));
1329
1330         return procOid;
1331 }
1332
1333 static Oid
1334 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1335 {
1336         Oid                     argList[1];
1337         Oid                     procOid;
1338
1339         /*
1340          * Analyze functions always take one INTERNAL argument and return bool.
1341          */
1342         argList[0] = INTERNALOID;
1343
1344         procOid = LookupFuncName(procname, 1, argList, true);
1345         if (!OidIsValid(procOid))
1346                 ereport(ERROR,
1347                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1348                                  errmsg("function %s does not exist",
1349                                                 func_signature_string(procname, 1, argList))));
1350
1351         if (get_func_rettype(procOid) != BOOLOID)
1352                 ereport(ERROR,
1353                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1354                           errmsg("type analyze function %s must return type \"boolean\"",
1355                                          NameListToString(procname))));
1356
1357         return procOid;
1358 }
1359
1360
1361 /*-------------------------------------------------------------------
1362  * DefineCompositeType
1363  *
1364  * Create a Composite Type relation.
1365  * `DefineRelation' does all the work, we just provide the correct
1366  * arguments!
1367  *
1368  * If the relation already exists, then 'DefineRelation' will abort
1369  * the xact...
1370  *
1371  * DefineCompositeType returns relid for use when creating
1372  * an implicit composite type during function creation
1373  *-------------------------------------------------------------------
1374  */
1375 Oid
1376 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1377 {
1378         CreateStmt *createStmt = makeNode(CreateStmt);
1379
1380         if (coldeflist == NIL)
1381                 ereport(ERROR,
1382                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1383                                  errmsg("composite type must have at least one attribute")));
1384
1385         /*
1386          * now set the parameters for keys/inheritance etc. All of these are
1387          * uninteresting for composite types...
1388          */
1389         createStmt->relation = (RangeVar *) typevar;
1390         createStmt->tableElts = coldeflist;
1391         createStmt->inhRelations = NIL;
1392         createStmt->constraints = NIL;
1393         createStmt->options = list_make1(defWithOids(false));
1394         createStmt->oncommit = ONCOMMIT_NOOP;
1395         createStmt->tablespacename = NULL;
1396
1397         /*
1398          * finally create the relation...
1399          */
1400         return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1401 }
1402
1403 /*
1404  * AlterDomainDefault
1405  *
1406  * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1407  */
1408 void
1409 AlterDomainDefault(List *names, Node *defaultRaw)
1410 {
1411         TypeName   *typename;
1412         Oid                     domainoid;
1413         HeapTuple       tup;
1414         ParseState *pstate;
1415         Relation        rel;
1416         char       *defaultValue;
1417         Node       *defaultExpr = NULL;         /* NULL if no default specified */
1418         Datum           new_record[Natts_pg_type];
1419         char            new_record_nulls[Natts_pg_type];
1420         char            new_record_repl[Natts_pg_type];
1421         HeapTuple       newtuple;
1422         Form_pg_type typTup;
1423
1424         /* Make a TypeName so we can use standard type lookup machinery */
1425         typename = makeTypeNameFromNameList(names);
1426         domainoid = typenameTypeId(NULL, typename);
1427
1428         /* Look up the domain in the type table */
1429         rel = heap_open(TypeRelationId, RowExclusiveLock);
1430
1431         tup = SearchSysCacheCopy(TYPEOID,
1432                                                          ObjectIdGetDatum(domainoid),
1433                                                          0, 0, 0);
1434         if (!HeapTupleIsValid(tup))
1435                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1436         typTup = (Form_pg_type) GETSTRUCT(tup);
1437
1438         /* Check it's a domain and check user has permission for ALTER DOMAIN */
1439         checkDomainOwner(tup, typename);
1440
1441         /* Setup new tuple */
1442         MemSet(new_record, (Datum) 0, sizeof(new_record));
1443         MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1444         MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1445
1446         /* Store the new default, if null then skip this step */
1447         if (defaultRaw)
1448         {
1449                 /* Create a dummy ParseState for transformExpr */
1450                 pstate = make_parsestate(NULL);
1451
1452                 /*
1453                  * Cook the colDef->raw_expr into an expression. Note: Name is
1454                  * strictly for error message
1455                  */
1456                 defaultExpr = cookDefault(pstate, defaultRaw,
1457                                                                   typTup->typbasetype,
1458                                                                   typTup->typtypmod,
1459                                                                   NameStr(typTup->typname));
1460
1461                 /*
1462                  * Expression must be stored as a nodeToString result, but we also
1463                  * require a valid textual representation (mainly to make life easier
1464                  * for pg_dump).
1465                  */
1466                 defaultValue = deparse_expression(defaultExpr,
1467                                                                 deparse_context_for(NameStr(typTup->typname),
1468                                                                                                         InvalidOid),
1469                                                                                   false, false);
1470
1471                 /*
1472                  * Form an updated tuple with the new default and write it back.
1473                  */
1474                 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
1475                                                                                                                          CStringGetDatum(
1476                                                                                                  nodeToString(defaultExpr)));
1477
1478                 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1479                 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
1480                                                                                           CStringGetDatum(defaultValue));
1481                 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1482         }
1483         else
1484                 /* Default is NULL, drop it */
1485         {
1486                 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1487                 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1488                 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1489                 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1490         }
1491
1492         newtuple = heap_modifytuple(tup, RelationGetDescr(rel),
1493                                                                 new_record, new_record_nulls,
1494                                                                 new_record_repl);
1495
1496         simple_heap_update(rel, &tup->t_self, newtuple);
1497
1498         CatalogUpdateIndexes(rel, newtuple);
1499
1500         /* Rebuild dependencies */
1501         GenerateTypeDependencies(typTup->typnamespace,
1502                                                          domainoid,
1503                                                          InvalidOid, /* typrelid is n/a */
1504                                                          0, /* relation kind is n/a */
1505                                                          typTup->typowner,
1506                                                          typTup->typinput,
1507                                                          typTup->typoutput,
1508                                                          typTup->typreceive,
1509                                                          typTup->typsend,
1510                                                          typTup->typmodin,
1511                                                          typTup->typmodout,
1512                                                          typTup->typanalyze,
1513                                                          typTup->typelem,
1514                                                          false,         /* a domain isn't an implicit array */
1515                                                          typTup->typbasetype,
1516                                                          defaultExpr,
1517                                                          true);         /* Rebuild is true */
1518
1519         /* Clean up */
1520         heap_close(rel, NoLock);
1521         heap_freetuple(newtuple);
1522 }
1523
1524 /*
1525  * AlterDomainNotNull
1526  *
1527  * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1528  */
1529 void
1530 AlterDomainNotNull(List *names, bool notNull)
1531 {
1532         TypeName   *typename;
1533         Oid                     domainoid;
1534         Relation        typrel;
1535         HeapTuple       tup;
1536         Form_pg_type typTup;
1537
1538         /* Make a TypeName so we can use standard type lookup machinery */
1539         typename = makeTypeNameFromNameList(names);
1540         domainoid = typenameTypeId(NULL, typename);
1541
1542         /* Look up the domain in the type table */
1543         typrel = heap_open(TypeRelationId, RowExclusiveLock);
1544
1545         tup = SearchSysCacheCopy(TYPEOID,
1546                                                          ObjectIdGetDatum(domainoid),
1547                                                          0, 0, 0);
1548         if (!HeapTupleIsValid(tup))
1549                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1550         typTup = (Form_pg_type) GETSTRUCT(tup);
1551
1552         /* Check it's a domain and check user has permission for ALTER DOMAIN */
1553         checkDomainOwner(tup, typename);
1554
1555         /* Is the domain already set to the desired constraint? */
1556         if (typTup->typnotnull == notNull)
1557         {
1558                 heap_close(typrel, RowExclusiveLock);
1559                 return;
1560         }
1561
1562         /* Adding a NOT NULL constraint requires checking existing columns */
1563         if (notNull)
1564         {
1565                 List       *rels;
1566                 ListCell   *rt;
1567
1568                 /* Fetch relation list with attributes based on this domain */
1569                 /* ShareLock is sufficient to prevent concurrent data changes */
1570
1571                 rels = get_rels_with_domain(domainoid, ShareLock);
1572
1573                 foreach(rt, rels)
1574                 {
1575                         RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1576                         Relation        testrel = rtc->rel;
1577                         TupleDesc       tupdesc = RelationGetDescr(testrel);
1578                         HeapScanDesc scan;
1579                         HeapTuple       tuple;
1580
1581                         /* Scan all tuples in this relation */
1582                         scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1583                         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1584                         {
1585                                 int                     i;
1586
1587                                 /* Test attributes that are of the domain */
1588                                 for (i = 0; i < rtc->natts; i++)
1589                                 {
1590                                         int                     attnum = rtc->atts[i];
1591
1592                                         if (heap_attisnull(tuple, attnum))
1593                                                 ereport(ERROR,
1594                                                                 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1595                                                                  errmsg("column \"%s\" of table \"%s\" contains null values",
1596                                                                 NameStr(tupdesc->attrs[attnum - 1]->attname),
1597                                                                                 RelationGetRelationName(testrel))));
1598                                 }
1599                         }
1600                         heap_endscan(scan);
1601
1602                         /* Close each rel after processing, but keep lock */
1603                         heap_close(testrel, NoLock);
1604                 }
1605         }
1606
1607         /*
1608          * Okay to update pg_type row.  We can scribble on typTup because it's a
1609          * copy.
1610          */
1611         typTup->typnotnull = notNull;
1612
1613         simple_heap_update(typrel, &tup->t_self, tup);
1614
1615         CatalogUpdateIndexes(typrel, tup);
1616
1617         /* Clean up */
1618         heap_freetuple(tup);
1619         heap_close(typrel, RowExclusiveLock);
1620 }
1621
1622 /*
1623  * AlterDomainDropConstraint
1624  *
1625  * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1626  */
1627 void
1628 AlterDomainDropConstraint(List *names, const char *constrName,
1629                                                   DropBehavior behavior)
1630 {
1631         TypeName   *typename;
1632         Oid                     domainoid;
1633         HeapTuple       tup;
1634         Relation        rel;
1635         Relation        conrel;
1636         SysScanDesc conscan;
1637         ScanKeyData key[1];
1638         HeapTuple       contup;
1639
1640         /* Make a TypeName so we can use standard type lookup machinery */
1641         typename = makeTypeNameFromNameList(names);
1642         domainoid = typenameTypeId(NULL, typename);
1643
1644         /* Look up the domain in the type table */
1645         rel = heap_open(TypeRelationId, RowExclusiveLock);
1646
1647         tup = SearchSysCacheCopy(TYPEOID,
1648                                                          ObjectIdGetDatum(domainoid),
1649                                                          0, 0, 0);
1650         if (!HeapTupleIsValid(tup))
1651                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1652
1653         /* Check it's a domain and check user has permission for ALTER DOMAIN */
1654         checkDomainOwner(tup, typename);
1655
1656         /* Grab an appropriate lock on the pg_constraint relation */
1657         conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
1658
1659         /* Use the index to scan only constraints of the target relation */
1660         ScanKeyInit(&key[0],
1661                                 Anum_pg_constraint_contypid,
1662                                 BTEqualStrategyNumber, F_OIDEQ,
1663                                 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1664
1665         conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
1666                                                                  SnapshotNow, 1, key);
1667
1668         /*
1669          * Scan over the result set, removing any matching entries.
1670          */
1671         while ((contup = systable_getnext(conscan)) != NULL)
1672         {
1673                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1674
1675                 if (strcmp(NameStr(con->conname), constrName) == 0)
1676                 {
1677                         ObjectAddress conobj;
1678
1679                         conobj.classId = ConstraintRelationId;
1680                         conobj.objectId = HeapTupleGetOid(contup);
1681                         conobj.objectSubId = 0;
1682
1683                         performDeletion(&conobj, behavior);
1684                 }
1685         }
1686         /* Clean up after the scan */
1687         systable_endscan(conscan);
1688         heap_close(conrel, RowExclusiveLock);
1689
1690         heap_close(rel, NoLock);
1691 }
1692
1693 /*
1694  * AlterDomainAddConstraint
1695  *
1696  * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1697  */
1698 void
1699 AlterDomainAddConstraint(List *names, Node *newConstraint)
1700 {
1701         TypeName   *typename;
1702         Oid                     domainoid;
1703         Relation        typrel;
1704         HeapTuple       tup;
1705         Form_pg_type typTup;
1706         List       *rels;
1707         ListCell   *rt;
1708         EState     *estate;
1709         ExprContext *econtext;
1710         char       *ccbin;
1711         Expr       *expr;
1712         ExprState  *exprstate;
1713         Constraint *constr;
1714
1715         /* Make a TypeName so we can use standard type lookup machinery */
1716         typename = makeTypeNameFromNameList(names);
1717         domainoid = typenameTypeId(NULL, typename);
1718
1719         /* Look up the domain in the type table */
1720         typrel = heap_open(TypeRelationId, RowExclusiveLock);
1721
1722         tup = SearchSysCacheCopy(TYPEOID,
1723                                                          ObjectIdGetDatum(domainoid),
1724                                                          0, 0, 0);
1725         if (!HeapTupleIsValid(tup))
1726                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1727         typTup = (Form_pg_type) GETSTRUCT(tup);
1728
1729         /* Check it's a domain and check user has permission for ALTER DOMAIN */
1730         checkDomainOwner(tup, typename);
1731
1732         /* Check for unsupported constraint types */
1733         if (IsA(newConstraint, FkConstraint))
1734                 ereport(ERROR,
1735                                 (errcode(ERRCODE_SYNTAX_ERROR),
1736                                  errmsg("foreign key constraints not possible for domains")));
1737
1738         /* otherwise it should be a plain Constraint */
1739         if (!IsA(newConstraint, Constraint))
1740                 elog(ERROR, "unrecognized node type: %d",
1741                          (int) nodeTag(newConstraint));
1742
1743         constr = (Constraint *) newConstraint;
1744
1745         switch (constr->contype)
1746         {
1747                 case CONSTR_CHECK:
1748                         /* processed below */
1749                         break;
1750
1751                 case CONSTR_UNIQUE:
1752                         ereport(ERROR,
1753                                         (errcode(ERRCODE_SYNTAX_ERROR),
1754                                          errmsg("unique constraints not possible for domains")));
1755                         break;
1756
1757                 case CONSTR_PRIMARY:
1758                         ereport(ERROR,
1759                                         (errcode(ERRCODE_SYNTAX_ERROR),
1760                                 errmsg("primary key constraints not possible for domains")));
1761                         break;
1762
1763                 case CONSTR_ATTR_DEFERRABLE:
1764                 case CONSTR_ATTR_NOT_DEFERRABLE:
1765                 case CONSTR_ATTR_DEFERRED:
1766                 case CONSTR_ATTR_IMMEDIATE:
1767                         ereport(ERROR,
1768                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1769                                          errmsg("specifying constraint deferrability not supported for domains")));
1770                         break;
1771
1772                 default:
1773                         elog(ERROR, "unrecognized constraint subtype: %d",
1774                                  (int) constr->contype);
1775                         break;
1776         }
1777
1778         /*
1779          * Since all other constraint types throw errors, this must be a check
1780          * constraint.  First, process the constraint expression and add an entry
1781          * to pg_constraint.
1782          */
1783
1784         ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1785                                                                 typTup->typbasetype, typTup->typtypmod,
1786                                                                 constr, NameStr(typTup->typname));
1787
1788         /*
1789          * Test all values stored in the attributes based on the domain the
1790          * constraint is being added to.
1791          */
1792         expr = (Expr *) stringToNode(ccbin);
1793
1794         /* Need an EState to run ExecEvalExpr */
1795         estate = CreateExecutorState();
1796         econtext = GetPerTupleExprContext(estate);
1797
1798         /* build execution state for expr */
1799         exprstate = ExecPrepareExpr(expr, estate);
1800
1801         /* Fetch relation list with attributes based on this domain */
1802         /* ShareLock is sufficient to prevent concurrent data changes */
1803
1804         rels = get_rels_with_domain(domainoid, ShareLock);
1805
1806         foreach(rt, rels)
1807         {
1808                 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1809                 Relation        testrel = rtc->rel;
1810                 TupleDesc       tupdesc = RelationGetDescr(testrel);
1811                 HeapScanDesc scan;
1812                 HeapTuple       tuple;
1813
1814                 /* Scan all tuples in this relation */
1815                 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1816                 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1817                 {
1818                         int                     i;
1819
1820                         /* Test attributes that are of the domain */
1821                         for (i = 0; i < rtc->natts; i++)
1822                         {
1823                                 int                     attnum = rtc->atts[i];
1824                                 Datum           d;
1825                                 bool            isNull;
1826                                 Datum           conResult;
1827
1828                                 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1829
1830                                 econtext->domainValue_datum = d;
1831                                 econtext->domainValue_isNull = isNull;
1832
1833                                 conResult = ExecEvalExprSwitchContext(exprstate,
1834                                                                                                           econtext,
1835                                                                                                           &isNull, NULL);
1836
1837                                 if (!isNull && !DatumGetBool(conResult))
1838                                         ereport(ERROR,
1839                                                         (errcode(ERRCODE_CHECK_VIOLATION),
1840                                                          errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
1841                                                                 NameStr(tupdesc->attrs[attnum - 1]->attname),
1842                                                                         RelationGetRelationName(testrel))));
1843                         }
1844
1845                         ResetExprContext(econtext);
1846                 }
1847                 heap_endscan(scan);
1848
1849                 /* Hold relation lock till commit (XXX bad for concurrency) */
1850                 heap_close(testrel, NoLock);
1851         }
1852
1853         FreeExecutorState(estate);
1854
1855         /* Clean up */
1856         heap_close(typrel, RowExclusiveLock);
1857 }
1858
1859 /*
1860  * get_rels_with_domain
1861  *
1862  * Fetch all relations / attributes which are using the domain
1863  *
1864  * The result is a list of RelToCheck structs, one for each distinct
1865  * relation, each containing one or more attribute numbers that are of
1866  * the domain type.  We have opened each rel and acquired the specified lock
1867  * type on it.
1868  *
1869  * We support nested domains by including attributes that are of derived
1870  * domain types.  Current callers do not need to distinguish between attributes
1871  * that are of exactly the given domain and those that are of derived domains.
1872  *
1873  * XXX this is completely broken because there is no way to lock the domain
1874  * to prevent columns from being added or dropped while our command runs.
1875  * We can partially protect against column drops by locking relations as we
1876  * come across them, but there is still a race condition (the window between
1877  * seeing a pg_depend entry and acquiring lock on the relation it references).
1878  * Also, holding locks on all these relations simultaneously creates a non-
1879  * trivial risk of deadlock.  We can minimize but not eliminate the deadlock
1880  * risk by using the weakest suitable lock (ShareLock for most callers).
1881  *
1882  * XXX the API for this is not sufficient to support checking domain values
1883  * that are inside composite types or arrays.  Currently we just error out
1884  * if a composite type containing the target domain is stored anywhere.
1885  * There are not currently arrays of domains; if there were, we could take
1886  * the same approach, but it'd be nicer to fix it properly.
1887  *
1888  * Generally used for retrieving a list of tests when adding
1889  * new constraints to a domain.
1890  */
1891 static List *
1892 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1893 {
1894         List       *result = NIL;
1895         Relation        depRel;
1896         ScanKeyData key[2];
1897         SysScanDesc depScan;
1898         HeapTuple       depTup;
1899
1900         Assert(lockmode != NoLock);
1901
1902         /*
1903          * We scan pg_depend to find those things that depend on the domain. (We
1904          * assume we can ignore refobjsubid for a domain.)
1905          */
1906         depRel = heap_open(DependRelationId, AccessShareLock);
1907
1908         ScanKeyInit(&key[0],
1909                                 Anum_pg_depend_refclassid,
1910                                 BTEqualStrategyNumber, F_OIDEQ,
1911                                 ObjectIdGetDatum(TypeRelationId));
1912         ScanKeyInit(&key[1],
1913                                 Anum_pg_depend_refobjid,
1914                                 BTEqualStrategyNumber, F_OIDEQ,
1915                                 ObjectIdGetDatum(domainOid));
1916
1917         depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
1918                                                                  SnapshotNow, 2, key);
1919
1920         while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1921         {
1922                 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1923                 RelToCheck *rtc = NULL;
1924                 ListCell   *rellist;
1925                 Form_pg_attribute pg_att;
1926                 int                     ptr;
1927
1928                 /* Check for directly dependent types --- must be domains */
1929                 if (pg_depend->classid == TypeRelationId)
1930                 {
1931                         Assert(get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN);
1932                         /*
1933                          * Recursively add dependent columns to the output list.  This
1934                          * is a bit inefficient since we may fail to combine RelToCheck
1935                          * entries when attributes of the same rel have different derived
1936                          * domain types, but it's probably not worth improving.
1937                          */
1938                         result = list_concat(result,
1939                                                                  get_rels_with_domain(pg_depend->objid,
1940                                                                                                           lockmode));
1941                         continue;
1942                 }
1943
1944                 /* Else, ignore dependees that aren't user columns of relations */
1945                 /* (we assume system columns are never of domain types) */
1946                 if (pg_depend->classid != RelationRelationId ||
1947                         pg_depend->objsubid <= 0)
1948                         continue;
1949
1950                 /* See if we already have an entry for this relation */
1951                 foreach(rellist, result)
1952                 {
1953                         RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1954
1955                         if (RelationGetRelid(rt->rel) == pg_depend->objid)
1956                         {
1957                                 rtc = rt;
1958                                 break;
1959                         }
1960                 }
1961
1962                 if (rtc == NULL)
1963                 {
1964                         /* First attribute found for this relation */
1965                         Relation        rel;
1966
1967                         /* Acquire requested lock on relation */
1968                         rel = relation_open(pg_depend->objid, lockmode);
1969
1970                         /*
1971                          * Check to see if rowtype is stored anyplace as a composite-type
1972                          * column; if so we have to fail, for now anyway.
1973                          */
1974                         if (OidIsValid(rel->rd_rel->reltype))
1975                                 find_composite_type_dependencies(rel->rd_rel->reltype,
1976                                                                                                  NULL,
1977                                                                                                  format_type_be(domainOid));
1978
1979                         /* Otherwise we can ignore views, composite types, etc */
1980                         if (rel->rd_rel->relkind != RELKIND_RELATION)
1981                         {
1982                                 relation_close(rel, lockmode);
1983                                 continue;
1984                         }
1985
1986                         /* Build the RelToCheck entry with enough space for all atts */
1987                         rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1988                         rtc->rel = rel;
1989                         rtc->natts = 0;
1990                         rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1991                         result = lcons(rtc, result);
1992                 }
1993
1994                 /*
1995                  * Confirm column has not been dropped, and is of the expected type.
1996                  * This defends against an ALTER DROP COLUMN occuring just before we
1997                  * acquired lock ... but if the whole table were dropped, we'd still
1998                  * have a problem.
1999                  */
2000                 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
2001                         continue;
2002                 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
2003                 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
2004                         continue;
2005
2006                 /*
2007                  * Okay, add column to result.  We store the columns in column-number
2008                  * order; this is just a hack to improve predictability of regression
2009                  * test output ...
2010                  */
2011                 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
2012
2013                 ptr = rtc->natts++;
2014                 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
2015                 {
2016                         rtc->atts[ptr] = rtc->atts[ptr - 1];
2017                         ptr--;
2018                 }
2019                 rtc->atts[ptr] = pg_depend->objsubid;
2020         }
2021
2022         systable_endscan(depScan);
2023
2024         relation_close(depRel, AccessShareLock);
2025
2026         return result;
2027 }
2028
2029 /*
2030  * checkDomainOwner
2031  *
2032  * Check that the type is actually a domain and that the current user
2033  * has permission to do ALTER DOMAIN on it.  Throw an error if not.
2034  */
2035 static void
2036 checkDomainOwner(HeapTuple tup, TypeName *typename)
2037 {
2038         Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
2039
2040         /* Check that this is actually a domain */
2041         if (typTup->typtype != TYPTYPE_DOMAIN)
2042                 ereport(ERROR,
2043                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2044                                  errmsg("\"%s\" is not a domain",
2045                                                 TypeNameToString(typename))));
2046
2047         /* Permission check: must own type */
2048         if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2049                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2050                                            TypeNameToString(typename));
2051 }
2052
2053 /*
2054  * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
2055  */
2056 static char *
2057 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
2058                                         int typMod, Constraint *constr,
2059                                         char *domainName)
2060 {
2061         Node       *expr;
2062         char       *ccsrc;
2063         char       *ccbin;
2064         ParseState *pstate;
2065         CoerceToDomainValue *domVal;
2066
2067         /*
2068          * Assign or validate constraint name
2069          */
2070         if (constr->name)
2071         {
2072                 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
2073                                                                  domainOid,
2074                                                                  domainNamespace,
2075                                                                  constr->name))
2076                         ereport(ERROR,
2077                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
2078                                  errmsg("constraint \"%s\" for domain \"%s\" already exists",
2079                                                 constr->name, domainName)));
2080         }
2081         else
2082                 constr->name = ChooseConstraintName(domainName,
2083                                                                                         NULL,
2084                                                                                         "check",
2085                                                                                         domainNamespace,
2086                                                                                         NIL);
2087
2088         /*
2089          * Convert the A_EXPR in raw_expr into an EXPR
2090          */
2091         pstate = make_parsestate(NULL);
2092
2093         /*
2094          * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
2095          * the expression.      Note that it will appear to have the type of the base
2096          * type, not the domain.  This seems correct since within the check
2097          * expression, we should not assume the input value can be considered a
2098          * member of the domain.
2099          */
2100         domVal = makeNode(CoerceToDomainValue);
2101         domVal->typeId = baseTypeOid;
2102         domVal->typeMod = typMod;
2103
2104         pstate->p_value_substitute = (Node *) domVal;
2105
2106         expr = transformExpr(pstate, constr->raw_expr);
2107
2108         /*
2109          * Make sure it yields a boolean result.
2110          */
2111         expr = coerce_to_boolean(pstate, expr, "CHECK");
2112
2113         /*
2114          * Make sure no outside relations are referred to.
2115          */
2116         if (list_length(pstate->p_rtable) != 0)
2117                 ereport(ERROR,
2118                                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2119                   errmsg("cannot use table references in domain check constraint")));
2120
2121         /*
2122          * Domains don't allow var clauses (this should be redundant with the
2123          * above check, but make it anyway)
2124          */
2125         if (contain_var_clause(expr))
2126                 ereport(ERROR,
2127                                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2128                   errmsg("cannot use table references in domain check constraint")));
2129
2130         /*
2131          * No subplans or aggregates, either...
2132          */
2133         if (pstate->p_hasSubLinks)
2134                 ereport(ERROR,
2135                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2136                                  errmsg("cannot use subquery in check constraint")));
2137         if (pstate->p_hasAggs)
2138                 ereport(ERROR,
2139                                 (errcode(ERRCODE_GROUPING_ERROR),
2140                            errmsg("cannot use aggregate function in check constraint")));
2141
2142         /*
2143          * Convert to string form for storage.
2144          */
2145         ccbin = nodeToString(expr);
2146
2147         /*
2148          * Deparse it to produce text for consrc.
2149          *
2150          * Since VARNOs aren't allowed in domain constraints, relation context
2151          * isn't required as anything other than a shell.
2152          */
2153         ccsrc = deparse_expression(expr,
2154                                                            deparse_context_for(domainName,
2155                                                                                                    InvalidOid),
2156                                                            false, false);
2157
2158         /*
2159          * Store the constraint in pg_constraint
2160          */
2161         CreateConstraintEntry(constr->name, /* Constraint Name */
2162                                                   domainNamespace,              /* namespace */
2163                                                   CONSTRAINT_CHECK,             /* Constraint Type */
2164                                                   false,        /* Is Deferrable */
2165                                                   false,        /* Is Deferred */
2166                                                   InvalidOid,   /* not a relation constraint */
2167                                                   NULL,
2168                                                   0,
2169                                                   domainOid,    /* domain constraint */
2170                                                   InvalidOid,   /* Foreign key fields */
2171                                                   NULL,
2172                                                   NULL,
2173                                                   NULL,
2174                                                   NULL,
2175                                                   0,
2176                                                   ' ',
2177                                                   ' ',
2178                                                   ' ',
2179                                                   InvalidOid,
2180                                                   expr, /* Tree form check constraint */
2181                                                   ccbin,        /* Binary form check constraint */
2182                                                   ccsrc);               /* Source form check constraint */
2183
2184         /*
2185          * Return the compiled constraint expression so the calling routine can
2186          * perform any additional required tests.
2187          */
2188         return ccbin;
2189 }
2190
2191 /*
2192  * GetDomainConstraints - get a list of the current constraints of domain
2193  *
2194  * Returns a possibly-empty list of DomainConstraintState nodes.
2195  *
2196  * This is called by the executor during plan startup for a CoerceToDomain
2197  * expression node.  The given constraints will be checked for each value
2198  * passed through the node.
2199  *
2200  * We allow this to be called for non-domain types, in which case the result
2201  * is always NIL.
2202  */
2203 List *
2204 GetDomainConstraints(Oid typeOid)
2205 {
2206         List       *result = NIL;
2207         bool            notNull = false;
2208         Relation        conRel;
2209
2210         conRel = heap_open(ConstraintRelationId, AccessShareLock);
2211
2212         for (;;)
2213         {
2214                 HeapTuple       tup;
2215                 HeapTuple       conTup;
2216                 Form_pg_type typTup;
2217                 ScanKeyData key[1];
2218                 SysScanDesc scan;
2219
2220                 tup = SearchSysCache(TYPEOID,
2221                                                          ObjectIdGetDatum(typeOid),
2222                                                          0, 0, 0);
2223                 if (!HeapTupleIsValid(tup))
2224                         elog(ERROR, "cache lookup failed for type %u", typeOid);
2225                 typTup = (Form_pg_type) GETSTRUCT(tup);
2226
2227                 if (typTup->typtype != TYPTYPE_DOMAIN)
2228                 {
2229                         /* Not a domain, so done */
2230                         ReleaseSysCache(tup);
2231                         break;
2232                 }
2233
2234                 /* Test for NOT NULL Constraint */
2235                 if (typTup->typnotnull)
2236                         notNull = true;
2237
2238                 /* Look for CHECK Constraints on this domain */
2239                 ScanKeyInit(&key[0],
2240                                         Anum_pg_constraint_contypid,
2241                                         BTEqualStrategyNumber, F_OIDEQ,
2242                                         ObjectIdGetDatum(typeOid));
2243
2244                 scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
2245                                                                   SnapshotNow, 1, key);
2246
2247                 while (HeapTupleIsValid(conTup = systable_getnext(scan)))
2248                 {
2249                         Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
2250                         Datum           val;
2251                         bool            isNull;
2252                         Expr       *check_expr;
2253                         DomainConstraintState *r;
2254
2255                         /* Ignore non-CHECK constraints (presently, shouldn't be any) */
2256                         if (c->contype != CONSTRAINT_CHECK)
2257                                 continue;
2258
2259                         /*
2260                          * Not expecting conbin to be NULL, but we'll test for it anyway
2261                          */
2262                         val = fastgetattr(conTup, Anum_pg_constraint_conbin,
2263                                                           conRel->rd_att, &isNull);
2264                         if (isNull)
2265                                 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
2266                                          NameStr(typTup->typname), NameStr(c->conname));
2267
2268                         check_expr = (Expr *)
2269                                 stringToNode(DatumGetCString(DirectFunctionCall1(textout,
2270                                                                                                                                  val)));
2271
2272                         /* ExecInitExpr assumes we already fixed opfuncids */
2273                         fix_opfuncids((Node *) check_expr);
2274
2275                         r = makeNode(DomainConstraintState);
2276                         r->constrainttype = DOM_CONSTRAINT_CHECK;
2277                         r->name = pstrdup(NameStr(c->conname));
2278                         r->check_expr = ExecInitExpr(check_expr, NULL);
2279
2280                         /*
2281                          * use lcons() here because constraints of lower domains should be
2282                          * applied earlier.
2283                          */
2284                         result = lcons(r, result);
2285                 }
2286
2287                 systable_endscan(scan);
2288
2289                 /* loop to next domain in stack */
2290                 typeOid = typTup->typbasetype;
2291                 ReleaseSysCache(tup);
2292         }
2293
2294         heap_close(conRel, AccessShareLock);
2295
2296         /*
2297          * Only need to add one NOT NULL check regardless of how many domains in
2298          * the stack request it.
2299          */
2300         if (notNull)
2301         {
2302                 DomainConstraintState *r = makeNode(DomainConstraintState);
2303
2304                 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
2305                 r->name = pstrdup("NOT NULL");
2306                 r->check_expr = NULL;
2307
2308                 /* lcons to apply the nullness check FIRST */
2309                 result = lcons(r, result);
2310         }
2311
2312         return result;
2313 }
2314
2315 /*
2316  * Change the owner of a type.
2317  */
2318 void
2319 AlterTypeOwner(List *names, Oid newOwnerId)
2320 {
2321         TypeName   *typename;
2322         Oid                     typeOid;
2323         Relation        rel;
2324         HeapTuple       tup;
2325         Form_pg_type typTup;
2326         AclResult       aclresult;
2327
2328         /* Make a TypeName so we can use standard type lookup machinery */
2329         typename = makeTypeNameFromNameList(names);
2330
2331         /* Use LookupTypeName here so that shell types can be processed */
2332         typeOid = LookupTypeName(NULL, typename);
2333         if (!OidIsValid(typeOid))
2334                 ereport(ERROR,
2335                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
2336                                  errmsg("type \"%s\" does not exist",
2337                                                 TypeNameToString(typename))));
2338
2339         /* Look up the type in the type table */
2340         rel = heap_open(TypeRelationId, RowExclusiveLock);
2341
2342         tup = SearchSysCacheCopy(TYPEOID,
2343                                                          ObjectIdGetDatum(typeOid),
2344                                                          0, 0, 0);
2345         if (!HeapTupleIsValid(tup))
2346                 elog(ERROR, "cache lookup failed for type %u", typeOid);
2347         typTup = (Form_pg_type) GETSTRUCT(tup);
2348
2349         /*
2350          * If it's a composite type, we need to check that it really is a
2351          * free-standing composite type, and not a table's rowtype. We
2352          * want people to use ALTER TABLE not ALTER TYPE for that case.
2353          */
2354         if (typTup->typtype == TYPTYPE_COMPOSITE &&
2355                 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
2356                 ereport(ERROR,
2357                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2358                                  errmsg("\"%s\" is a table's row type",
2359                                                 TypeNameToString(typename))));
2360
2361         /* don't allow direct alteration of array types, either */
2362         if (OidIsValid(typTup->typelem) &&
2363                 get_array_type(typTup->typelem) == typeOid)
2364                 ereport(ERROR,
2365                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2366                                  errmsg("cannot alter array type %s",
2367                                                 format_type_be(typeOid)),
2368                                  errhint("You can alter type %s, which will alter the array type as well.",
2369                                                  format_type_be(typTup->typelem))));
2370
2371         /*
2372          * If the new owner is the same as the existing owner, consider the
2373          * command to have succeeded.  This is for dump restoration purposes.
2374          */
2375         if (typTup->typowner != newOwnerId)
2376         {
2377                 /* Superusers can always do it */
2378                 if (!superuser())
2379                 {
2380                         /* Otherwise, must be owner of the existing object */
2381                         if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2382                                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2383                                                            TypeNameToString(typename));
2384
2385                         /* Must be able to become new owner */
2386                         check_is_member_of_role(GetUserId(), newOwnerId);
2387
2388                         /* New owner must have CREATE privilege on namespace */
2389                         aclresult = pg_namespace_aclcheck(typTup->typnamespace,
2390                                                                                           newOwnerId,
2391                                                                                           ACL_CREATE);
2392                         if (aclresult != ACLCHECK_OK)
2393                                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
2394                                                            get_namespace_name(typTup->typnamespace));
2395                 }
2396
2397                 /*
2398                  * If it's a composite type, invoke ATExecChangeOwner so that we
2399                  * fix up the pg_class entry properly.  That will call back to
2400                  * AlterTypeOwnerInternal to take care of the pg_type entry(s).
2401                  */
2402                 if (typTup->typtype == TYPTYPE_COMPOSITE)
2403                         ATExecChangeOwner(typTup->typrelid, newOwnerId, true);
2404                 else
2405                 {
2406                         /*
2407                          * We can just apply the modification directly.
2408                          *
2409                          * okay to scribble on typTup because it's a copy
2410                          */
2411                         typTup->typowner = newOwnerId;
2412
2413                         simple_heap_update(rel, &tup->t_self, tup);
2414
2415                         CatalogUpdateIndexes(rel, tup);
2416
2417                         /* Update owner dependency reference */
2418                         changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2419
2420                         /* If it has an array type, update that too */
2421                         if (OidIsValid(typTup->typarray))
2422                                 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2423                 }
2424         }
2425
2426         /* Clean up */
2427         heap_close(rel, RowExclusiveLock);
2428 }
2429
2430 /*
2431  * AlterTypeOwnerInternal - change type owner unconditionally
2432  *
2433  * This is currently only used to propagate ALTER TABLE/TYPE OWNER to a 
2434  * table's rowtype or an array type, and to implement REASSIGN OWNED BY.
2435  * It assumes the caller has done all needed checks.  The function will
2436  * automatically recurse to an array type if the type has one.
2437  *
2438  * hasDependEntry should be TRUE if type is expected to have a pg_shdepend
2439  * entry (ie, it's not a table rowtype nor an array type).
2440  */
2441 void
2442 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
2443                                            bool hasDependEntry)
2444 {
2445         Relation        rel;
2446         HeapTuple       tup;
2447         Form_pg_type typTup;
2448
2449         rel = heap_open(TypeRelationId, RowExclusiveLock);
2450
2451         tup = SearchSysCacheCopy(TYPEOID,
2452                                                          ObjectIdGetDatum(typeOid),
2453                                                          0, 0, 0);
2454         if (!HeapTupleIsValid(tup))
2455                 elog(ERROR, "cache lookup failed for type %u", typeOid);
2456         typTup = (Form_pg_type) GETSTRUCT(tup);
2457
2458         /*
2459          * Modify the owner --- okay to scribble on typTup because it's a copy
2460          */
2461         typTup->typowner = newOwnerId;
2462
2463         simple_heap_update(rel, &tup->t_self, tup);
2464
2465         CatalogUpdateIndexes(rel, tup);
2466
2467         /* Update owner dependency reference, if it has one */
2468         if (hasDependEntry)
2469                 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2470
2471         /* If it has an array type, update that too */
2472         if (OidIsValid(typTup->typarray))
2473                 AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
2474
2475         /* Clean up */
2476         heap_close(rel, RowExclusiveLock);
2477 }
2478
2479 /*
2480  * Execute ALTER TYPE SET SCHEMA
2481  */
2482 void
2483 AlterTypeNamespace(List *names, const char *newschema)
2484 {
2485         TypeName   *typename;
2486         Oid                     typeOid;
2487         Oid                     nspOid;
2488         Oid                     elemOid;
2489
2490         /* Make a TypeName so we can use standard type lookup machinery */
2491         typename = makeTypeNameFromNameList(names);
2492         typeOid = typenameTypeId(NULL, typename);
2493
2494         /* check permissions on type */
2495         if (!pg_type_ownercheck(typeOid, GetUserId()))
2496                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
2497                                            format_type_be(typeOid));
2498
2499         /* get schema OID and check its permissions */
2500         nspOid = LookupCreationNamespace(newschema);
2501
2502         /* don't allow direct alteration of array types */
2503         elemOid = get_element_type(typeOid);
2504         if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
2505                 ereport(ERROR,
2506                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2507                                  errmsg("cannot alter array type %s",
2508                                                 format_type_be(typeOid)),
2509                                  errhint("You can alter type %s, which will alter the array type as well.",
2510                                                  format_type_be(elemOid))));
2511
2512         /* and do the work */
2513         AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
2514 }
2515
2516 /*
2517  * Move specified type to new namespace.
2518  *
2519  * Caller must have already checked privileges.
2520  *
2521  * The function automatically recurses to process the type's array type,
2522  * if any.  isImplicitArray should be TRUE only when doing this internal
2523  * recursion (outside callers must never try to move an array type directly).
2524  *
2525  * If errorOnTableType is TRUE, the function errors out if the type is
2526  * a table type.  ALTER TABLE has to be used to move a table to a new
2527  * namespace.
2528  */
2529 void
2530 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
2531                                                    bool isImplicitArray,
2532                                                    bool errorOnTableType)
2533 {
2534         Relation        rel;
2535         HeapTuple       tup;
2536         Form_pg_type typform;
2537         Oid                     oldNspOid;
2538         Oid                     arrayOid;
2539         bool            isCompositeType;
2540
2541         rel = heap_open(TypeRelationId, RowExclusiveLock);
2542
2543         tup = SearchSysCacheCopy(TYPEOID,
2544                                                          ObjectIdGetDatum(typeOid),
2545                                                          0, 0, 0);
2546         if (!HeapTupleIsValid(tup))
2547                 elog(ERROR, "cache lookup failed for type %u", typeOid);
2548         typform = (Form_pg_type) GETSTRUCT(tup);
2549
2550         oldNspOid = typform->typnamespace;
2551         arrayOid = typform->typarray;
2552
2553         if (oldNspOid == nspOid)
2554                 ereport(ERROR,
2555                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
2556                                  errmsg("type %s is already in schema \"%s\"",
2557                                                 format_type_be(typeOid),
2558                                                 get_namespace_name(nspOid))));
2559
2560         /* disallow renaming into or out of temp schemas */
2561         if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
2562                 ereport(ERROR,
2563                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2564                         errmsg("cannot move objects into or out of temporary schemas")));
2565
2566         /* same for TOAST schema */
2567         if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
2568                 ereport(ERROR,
2569                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2570                                  errmsg("cannot move objects into or out of TOAST schema")));
2571
2572         /* check for duplicate name (more friendly than unique-index failure) */
2573         if (SearchSysCacheExists(TYPENAMENSP,
2574                                                          CStringGetDatum(NameStr(typform->typname)),
2575                                                          ObjectIdGetDatum(nspOid),
2576                                                          0, 0))
2577                 ereport(ERROR,
2578                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
2579                                  errmsg("type \"%s\" already exists in schema \"%s\"",
2580                                                 NameStr(typform->typname),
2581                                                 get_namespace_name(nspOid))));
2582
2583         /* Detect whether type is a composite type (but not a table rowtype) */
2584         isCompositeType =
2585                 (typform->typtype == TYPTYPE_COMPOSITE &&
2586                  get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
2587
2588         /* Enforce not-table-type if requested */
2589         if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
2590                 errorOnTableType)
2591                 ereport(ERROR,
2592                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2593                                  errmsg("%s is a table's row type",
2594                                                 format_type_be(typeOid)),
2595                                  errhint("Use ALTER TABLE SET SCHEMA instead.")));
2596
2597         /* OK, modify the pg_type row */
2598
2599         /* tup is a copy, so we can scribble directly on it */
2600         typform->typnamespace = nspOid;
2601
2602         simple_heap_update(rel, &tup->t_self, tup);
2603         CatalogUpdateIndexes(rel, tup);
2604
2605         /*
2606          * Composite types have pg_class entries.
2607          *
2608          * We need to modify the pg_class tuple as well to reflect the change of
2609          * schema.
2610          */
2611         if (isCompositeType)
2612         {
2613                 Relation        classRel;
2614
2615                 classRel = heap_open(RelationRelationId, RowExclusiveLock);
2616
2617                 AlterRelationNamespaceInternal(classRel, typform->typrelid,
2618                                                                            oldNspOid, nspOid,
2619                                                                            false);
2620
2621                 heap_close(classRel, RowExclusiveLock);
2622
2623                 /*
2624                  * Check for constraints associated with the composite type (we don't
2625                  * currently support this, but probably will someday).
2626                  */
2627                 AlterConstraintNamespaces(typform->typrelid, oldNspOid,
2628                                                                   nspOid, false);
2629         }
2630         else
2631         {
2632                 /* If it's a domain, it might have constraints */
2633                 if (typform->typtype == TYPTYPE_DOMAIN)
2634                         AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
2635         }
2636
2637         /*
2638          * Update dependency on schema, if any --- a table rowtype has not got
2639          * one, and neither does an implicit array.
2640          */
2641         if ((isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
2642                 !isImplicitArray)
2643                 if (changeDependencyFor(TypeRelationId, typeOid,
2644                                                                 NamespaceRelationId, oldNspOid, nspOid) != 1)
2645                         elog(ERROR, "failed to change schema dependency for type %s",
2646                                  format_type_be(typeOid));
2647
2648         heap_freetuple(tup);
2649
2650         heap_close(rel, RowExclusiveLock);
2651
2652         /* Recursively alter the associated array type, if any */
2653         if (OidIsValid(arrayOid))
2654                 AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
2655 }