]> granicus.if.org Git - postgresql/blob - src/backend/commands/typecmds.c
First phase of project to use fixed OIDs for all system catalogs and
[postgresql] / 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-2005, 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.69 2005/04/14 01:38:17 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/heapam.h"
35 #include "access/genam.h"
36 #include "catalog/catname.h"
37 #include "catalog/dependency.h"
38 #include "catalog/heap.h"
39 #include "catalog/indexing.h"
40 #include "catalog/namespace.h"
41 #include "catalog/pg_constraint.h"
42 #include "catalog/pg_depend.h"
43 #include "catalog/pg_type.h"
44 #include "commands/defrem.h"
45 #include "commands/tablecmds.h"
46 #include "commands/typecmds.h"
47 #include "executor/executor.h"
48 #include "miscadmin.h"
49 #include "nodes/execnodes.h"
50 #include "nodes/nodes.h"
51 #include "optimizer/clauses.h"
52 #include "optimizer/planmain.h"
53 #include "optimizer/var.h"
54 #include "parser/parse_coerce.h"
55 #include "parser/parse_expr.h"
56 #include "parser/parse_func.h"
57 #include "parser/parse_relation.h"
58 #include "parser/parse_type.h"
59 #include "utils/acl.h"
60 #include "utils/builtins.h"
61 #include "utils/fmgroids.h"
62 #include "utils/lsyscache.h"
63 #include "utils/syscache.h"
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      findTypeAnalyzeFunction(List *procname, Oid typeOid);
81 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
82 static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
83 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
84                                         Oid baseTypeOid,
85                                         int typMod, Constraint *constr,
86                                         char *domainName);
87
88
89 /*
90  * DefineType
91  *              Registers a new type.
92  */
93 void
94 DefineType(List *names, List *parameters)
95 {
96         char       *typeName;
97         Oid                     typeNamespace;
98         AclResult       aclresult;
99         int16           internalLength = -1;    /* default: variable-length */
100         Oid                     elemType = InvalidOid;
101         List       *inputName = NIL;
102         List       *outputName = NIL;
103         List       *receiveName = NIL;
104         List       *sendName = NIL;
105         List       *analyzeName = NIL;
106         char       *defaultValue = NULL;
107         bool            byValue = false;
108         char            delimiter = DEFAULT_TYPDELIM;
109         char            alignment = 'i';        /* default alignment */
110         char            storage = 'p';  /* default TOAST storage method */
111         Oid                     inputOid;
112         Oid                     outputOid;
113         Oid                     receiveOid = InvalidOid;
114         Oid                     sendOid = InvalidOid;
115         Oid                     analyzeOid = InvalidOid;
116         char       *shadow_type;
117         ListCell   *pl;
118         Oid                     typoid;
119         Oid                     resulttype;
120
121         /* Convert list of names to a name and namespace */
122         typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
123
124         /* Check we have creation rights in target namespace */
125         aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
126         if (aclresult != ACLCHECK_OK)
127                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
128                                            get_namespace_name(typeNamespace));
129
130         /*
131          * Type names must be one character shorter than other names, allowing
132          * room to create the corresponding array type name with prepended
133          * "_".
134          */
135         if (strlen(typeName) > (NAMEDATALEN - 2))
136                 ereport(ERROR,
137                                 (errcode(ERRCODE_INVALID_NAME),
138                                  errmsg("type names must be %d characters or less",
139                                                 NAMEDATALEN - 2)));
140
141         foreach(pl, parameters)
142         {
143                 DefElem    *defel = (DefElem *) lfirst(pl);
144
145                 if (pg_strcasecmp(defel->defname, "internallength") == 0)
146                         internalLength = defGetTypeLength(defel);
147                 else if (pg_strcasecmp(defel->defname, "externallength") == 0)
148                         ;                                       /* ignored -- remove after 7.3 */
149                 else if (pg_strcasecmp(defel->defname, "input") == 0)
150                         inputName = defGetQualifiedName(defel);
151                 else if (pg_strcasecmp(defel->defname, "output") == 0)
152                         outputName = defGetQualifiedName(defel);
153                 else if (pg_strcasecmp(defel->defname, "receive") == 0)
154                         receiveName = defGetQualifiedName(defel);
155                 else if (pg_strcasecmp(defel->defname, "send") == 0)
156                         sendName = defGetQualifiedName(defel);
157                 else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
158                                  pg_strcasecmp(defel->defname, "analyse") == 0)
159                         analyzeName = defGetQualifiedName(defel);
160                 else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
161                 {
162                         char       *p = defGetString(defel);
163
164                         delimiter = p[0];
165                 }
166                 else if (pg_strcasecmp(defel->defname, "element") == 0)
167                 {
168                         elemType = typenameTypeId(defGetTypeName(defel));
169                         /* disallow arrays of pseudotypes */
170                         if (get_typtype(elemType) == 'p')
171                                 ereport(ERROR,
172                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
173                                                  errmsg("array element type cannot be %s",
174                                                                 format_type_be(elemType))));
175                 }
176                 else if (pg_strcasecmp(defel->defname, "default") == 0)
177                         defaultValue = defGetString(defel);
178                 else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
179                         byValue = defGetBoolean(defel);
180                 else if (pg_strcasecmp(defel->defname, "alignment") == 0)
181                 {
182                         char       *a = defGetString(defel);
183
184                         /*
185                          * Note: if argument was an unquoted identifier, parser will
186                          * have applied translations to it, so be prepared to
187                          * recognize translated type names as well as the nominal
188                          * form.
189                          */
190                         if (pg_strcasecmp(a, "double") == 0 ||
191                                 pg_strcasecmp(a, "float8") == 0 ||
192                                 pg_strcasecmp(a, "pg_catalog.float8") == 0)
193                                 alignment = 'd';
194                         else if (pg_strcasecmp(a, "int4") == 0 ||
195                                          pg_strcasecmp(a, "pg_catalog.int4") == 0)
196                                 alignment = 'i';
197                         else if (pg_strcasecmp(a, "int2") == 0 ||
198                                          pg_strcasecmp(a, "pg_catalog.int2") == 0)
199                                 alignment = 's';
200                         else if (pg_strcasecmp(a, "char") == 0 ||
201                                          pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
202                                 alignment = 'c';
203                         else
204                                 ereport(ERROR,
205                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
206                                                  errmsg("alignment \"%s\" not recognized", a)));
207                 }
208                 else if (pg_strcasecmp(defel->defname, "storage") == 0)
209                 {
210                         char       *a = defGetString(defel);
211
212                         if (pg_strcasecmp(a, "plain") == 0)
213                                 storage = 'p';
214                         else if (pg_strcasecmp(a, "external") == 0)
215                                 storage = 'e';
216                         else if (pg_strcasecmp(a, "extended") == 0)
217                                 storage = 'x';
218                         else if (pg_strcasecmp(a, "main") == 0)
219                                 storage = 'm';
220                         else
221                                 ereport(ERROR,
222                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
223                                                  errmsg("storage \"%s\" not recognized", a)));
224                 }
225                 else
226                         ereport(WARNING,
227                                         (errcode(ERRCODE_SYNTAX_ERROR),
228                                          errmsg("type attribute \"%s\" not recognized",
229                                                         defel->defname)));
230         }
231
232         /*
233          * make sure we have our required definitions
234          */
235         if (inputName == NIL)
236                 ereport(ERROR,
237                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
238                                  errmsg("type input function must be specified")));
239         if (outputName == NIL)
240                 ereport(ERROR,
241                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
242                                  errmsg("type output function must be specified")));
243
244         /*
245          * Look to see if type already exists (presumably as a shell; if not,
246          * TypeCreate will complain).  If it doesn't, create it as a shell, so
247          * that the OID is known for use in the I/O function definitions.
248          */
249         typoid = GetSysCacheOid(TYPENAMENSP,
250                                                         CStringGetDatum(typeName),
251                                                         ObjectIdGetDatum(typeNamespace),
252                                                         0, 0);
253         if (!OidIsValid(typoid))
254         {
255                 typoid = TypeShellMake(typeName, typeNamespace);
256                 /* Make new shell type visible for modification below */
257                 CommandCounterIncrement();
258         }
259
260         /*
261          * Convert I/O proc names to OIDs
262          */
263         inputOid = findTypeInputFunction(inputName, typoid);
264         outputOid = findTypeOutputFunction(outputName, typoid);
265         if (receiveName)
266                 receiveOid = findTypeReceiveFunction(receiveName, typoid);
267         if (sendName)
268                 sendOid = findTypeSendFunction(sendName, typoid);
269
270         /*
271          * Verify that I/O procs return the expected thing.  If we see OPAQUE,
272          * complain and change it to the correct type-safe choice.
273          */
274         resulttype = get_func_rettype(inputOid);
275         if (resulttype != typoid)
276         {
277                 if (resulttype == OPAQUEOID)
278                 {
279                         /* backwards-compatibility hack */
280                         ereport(WARNING,
281                                         (errmsg("changing return type of function %s from \"opaque\" to %s",
282                                                         NameListToString(inputName), typeName)));
283                         SetFunctionReturnType(inputOid, typoid);
284                 }
285                 else
286                         ereport(ERROR,
287                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
288                                          errmsg("type input function %s must return type %s",
289                                                         NameListToString(inputName), typeName)));
290         }
291         resulttype = get_func_rettype(outputOid);
292         if (resulttype != CSTRINGOID)
293         {
294                 if (resulttype == OPAQUEOID)
295                 {
296                         /* backwards-compatibility hack */
297                         ereport(WARNING,
298                                         (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
299                                                         NameListToString(outputName))));
300                         SetFunctionReturnType(outputOid, CSTRINGOID);
301                 }
302                 else
303                         ereport(ERROR,
304                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
305                         errmsg("type output function %s must return type \"cstring\"",
306                                    NameListToString(outputName))));
307         }
308         if (receiveOid)
309         {
310                 resulttype = get_func_rettype(receiveOid);
311                 if (resulttype != typoid)
312                         ereport(ERROR,
313                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
314                                    errmsg("type receive function %s must return type %s",
315                                                   NameListToString(receiveName), typeName)));
316         }
317         if (sendOid)
318         {
319                 resulttype = get_func_rettype(sendOid);
320                 if (resulttype != BYTEAOID)
321                         ereport(ERROR,
322                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
323                            errmsg("type send function %s must return type \"bytea\"",
324                                           NameListToString(sendName))));
325         }
326
327         /*
328          * Convert analysis function proc name to an OID. If no analysis
329          * function is specified, we'll use zero to select the built-in
330          * default algorithm.
331          */
332         if (analyzeName)
333                 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
334
335         /*
336          * now have TypeCreate do all the real work.
337          */
338         typoid =
339                 TypeCreate(typeName,    /* type name */
340                                    typeNamespace,               /* namespace */
341                                    InvalidOid,  /* preassigned type oid (not done here) */
342                                    InvalidOid,  /* relation oid (n/a here) */
343                                    0,                   /* relation kind (ditto) */
344                                    internalLength,              /* internal size */
345                                    'b',                 /* type-type (base type) */
346                                    delimiter,   /* array element delimiter */
347                                    inputOid,    /* input procedure */
348                                    outputOid,   /* output procedure */
349                                    receiveOid,  /* receive procedure */
350                                    sendOid,             /* send procedure */
351                                    analyzeOid,  /* analyze procedure */
352                                    elemType,    /* element type ID */
353                                    InvalidOid,  /* base type ID (only for domains) */
354                                    defaultValue,        /* default type value */
355                                    NULL,                /* no binary form available */
356                                    byValue,             /* passed by value */
357                                    alignment,   /* required alignment */
358                                    storage,             /* TOAST strategy */
359                                    -1,                  /* typMod (Domains only) */
360                                    0,                   /* Array Dimensions of typbasetype */
361                                    false);              /* Type NOT NULL */
362
363         /*
364          * When we create a base type (as opposed to a complex type) we need
365          * to have an array entry for it in pg_type as well.
366          */
367         shadow_type = makeArrayTypeName(typeName);
368
369         /* alignment must be 'i' or 'd' for arrays */
370         alignment = (alignment == 'd') ? 'd' : 'i';
371
372         TypeCreate(shadow_type,         /* type name */
373                            typeNamespace,       /* namespace */
374                            InvalidOid,          /* preassigned type oid (not done here) */
375                            InvalidOid,          /* relation oid (n/a here) */
376                            0,                           /* relation kind (ditto) */
377                            -1,                          /* internal size */
378                            'b',                         /* type-type (base type) */
379                            DEFAULT_TYPDELIM,    /* array element delimiter */
380                            F_ARRAY_IN,          /* input procedure */
381                            F_ARRAY_OUT,         /* output procedure */
382                            F_ARRAY_RECV,        /* receive procedure */
383                            F_ARRAY_SEND,        /* send procedure */
384                            InvalidOid,          /* analyze procedure - default */
385                            typoid,                      /* element type ID */
386                            InvalidOid,          /* base type ID */
387                            NULL,                        /* never a default type value */
388                            NULL,                        /* binary default isn't sent either */
389                            false,                       /* never passed by value */
390                            alignment,           /* see above */
391                            'x',                         /* ARRAY is always toastable */
392                            -1,                          /* typMod (Domains only) */
393                            0,                           /* Array dimensions of typbasetype */
394                            false);                      /* Type NOT NULL */
395
396         pfree(shadow_type);
397 }
398
399
400 /*
401  *      RemoveType
402  *              Removes a datatype.
403  */
404 void
405 RemoveType(List *names, DropBehavior behavior)
406 {
407         TypeName   *typename;
408         Oid                     typeoid;
409         HeapTuple       tup;
410         ObjectAddress object;
411
412         /* Make a TypeName so we can use standard type lookup machinery */
413         typename = makeNode(TypeName);
414         typename->names = names;
415         typename->typmod = -1;
416         typename->arrayBounds = NIL;
417
418         /* Use LookupTypeName here so that shell types can be removed. */
419         typeoid = LookupTypeName(typename);
420         if (!OidIsValid(typeoid))
421                 ereport(ERROR,
422                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
423                                  errmsg("type \"%s\" does not exist",
424                                                 TypeNameToString(typename))));
425
426         tup = SearchSysCache(TYPEOID,
427                                                  ObjectIdGetDatum(typeoid),
428                                                  0, 0, 0);
429         if (!HeapTupleIsValid(tup))
430                 elog(ERROR, "cache lookup failed for type %u", typeoid);
431
432         /* Permission check: must own type or its namespace */
433         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
434                 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
435                                                                  GetUserId()))
436                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
437                                            TypeNameToString(typename));
438
439         ReleaseSysCache(tup);
440
441         /*
442          * Do the deletion
443          */
444         object.classId = TypeRelationId;
445         object.objectId = typeoid;
446         object.objectSubId = 0;
447
448         performDeletion(&object, behavior);
449 }
450
451
452 /*
453  * Guts of type deletion.
454  */
455 void
456 RemoveTypeById(Oid typeOid)
457 {
458         Relation        relation;
459         HeapTuple       tup;
460
461         relation = heap_openr(TypeRelationName, RowExclusiveLock);
462
463         tup = SearchSysCache(TYPEOID,
464                                                  ObjectIdGetDatum(typeOid),
465                                                  0, 0, 0);
466         if (!HeapTupleIsValid(tup))
467                 elog(ERROR, "cache lookup failed for type %u", typeOid);
468
469         simple_heap_delete(relation, &tup->t_self);
470
471         ReleaseSysCache(tup);
472
473         heap_close(relation, RowExclusiveLock);
474 }
475
476
477 /*
478  * DefineDomain
479  *              Registers a new domain.
480  */
481 void
482 DefineDomain(CreateDomainStmt *stmt)
483 {
484         char       *domainName;
485         Oid                     domainNamespace;
486         AclResult       aclresult;
487         int16           internalLength;
488         Oid                     inputProcedure;
489         Oid                     outputProcedure;
490         Oid                     receiveProcedure;
491         Oid                     sendProcedure;
492         Oid                     analyzeProcedure;
493         bool            byValue;
494         char            delimiter;
495         char            alignment;
496         char            storage;
497         char            typtype;
498         Datum           datum;
499         bool            isnull;
500         Node       *defaultExpr = NULL;
501         char       *defaultValue = NULL;
502         char       *defaultValueBin = NULL;
503         bool            typNotNull = false;
504         bool            nullDefined = false;
505         Oid                     basetypelem;
506         int32           typNDims = list_length(stmt->typename->arrayBounds);
507         HeapTuple       typeTup;
508         List       *schema = stmt->constraints;
509         ListCell   *listptr;
510         Oid                     basetypeoid;
511         Oid                     domainoid;
512         Form_pg_type baseType;
513
514         /* Convert list of names to a name and namespace */
515         domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
516                                                                                                                 &domainName);
517
518         /* Check we have creation rights in target namespace */
519         aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
520                                                                           ACL_CREATE);
521         if (aclresult != ACLCHECK_OK)
522                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
523                                            get_namespace_name(domainNamespace));
524
525         /*
526          * Domainnames, unlike typenames don't need to account for the '_'
527          * prefix.      So they can be one character longer.  (This test is
528          * presently useless since the parser will have truncated the name to
529          * fit.  But leave it here since we may someday support arrays of
530          * domains, in which case we'll be back to needing to enforce
531          * NAMEDATALEN-2.)
532          */
533         if (strlen(domainName) > (NAMEDATALEN - 1))
534                 ereport(ERROR,
535                                 (errcode(ERRCODE_INVALID_NAME),
536                                  errmsg("domain names must be %d characters or less",
537                                                 NAMEDATALEN - 1)));
538
539         /*
540          * Look up the base type.
541          */
542         typeTup = typenameType(stmt->typename);
543
544         baseType = (Form_pg_type) GETSTRUCT(typeTup);
545         basetypeoid = HeapTupleGetOid(typeTup);
546
547         /*
548          * Base type must be a plain base type.  Domains over pseudo types
549          * would create a security hole.  Domains of domains might be made to
550          * work in the future, but not today.  Ditto for domains over complex
551          * types.
552          */
553         typtype = baseType->typtype;
554         if (typtype != 'b')
555                 ereport(ERROR,
556                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
557                                  errmsg("\"%s\" is not a valid base type for a domain",
558                                                 TypeNameToString(stmt->typename))));
559
560         /* passed by value */
561         byValue = baseType->typbyval;
562
563         /* Required Alignment */
564         alignment = baseType->typalign;
565
566         /* TOAST Strategy */
567         storage = baseType->typstorage;
568
569         /* Storage Length */
570         internalLength = baseType->typlen;
571
572         /* Array element Delimiter */
573         delimiter = baseType->typdelim;
574
575         /* I/O Functions */
576         inputProcedure = baseType->typinput;
577         outputProcedure = baseType->typoutput;
578         receiveProcedure = baseType->typreceive;
579         sendProcedure = baseType->typsend;
580
581         /* Analysis function */
582         analyzeProcedure = baseType->typanalyze;
583
584         /* Inherited default value */
585         datum = SysCacheGetAttr(TYPEOID, typeTup,
586                                                         Anum_pg_type_typdefault, &isnull);
587         if (!isnull)
588                 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
589
590         /* Inherited default binary value */
591         datum = SysCacheGetAttr(TYPEOID, typeTup,
592                                                         Anum_pg_type_typdefaultbin, &isnull);
593         if (!isnull)
594                 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
595
596         /*
597          * Pull out the typelem name of the parent OID.
598          *
599          * This is what enables us to make a domain of an array
600          */
601         basetypelem = baseType->typelem;
602
603         /*
604          * Run through constraints manually to avoid the additional processing
605          * conducted by DefineRelation() and friends.
606          */
607         foreach(listptr, schema)
608         {
609                 Node       *newConstraint = lfirst(listptr);
610                 Constraint *constr;
611                 ParseState *pstate;
612
613                 /* Check for unsupported constraint types */
614                 if (IsA(newConstraint, FkConstraint))
615                         ereport(ERROR,
616                                         (errcode(ERRCODE_SYNTAX_ERROR),
617                         errmsg("foreign key constraints not possible for domains")));
618
619                 /* otherwise it should be a plain Constraint */
620                 if (!IsA(newConstraint, Constraint))
621                         elog(ERROR, "unrecognized node type: %d",
622                                  (int) nodeTag(newConstraint));
623
624                 constr = (Constraint *) newConstraint;
625
626                 switch (constr->contype)
627                 {
628                         case CONSTR_DEFAULT:
629
630                                 /*
631                                  * The inherited default value may be overridden by the
632                                  * user with the DEFAULT <expr> statement.
633                                  */
634                                 if (defaultExpr)
635                                         ereport(ERROR,
636                                                         (errcode(ERRCODE_SYNTAX_ERROR),
637                                                          errmsg("multiple default expressions")));
638
639                                 /* Create a dummy ParseState for transformExpr */
640                                 pstate = make_parsestate(NULL);
641
642                                 /*
643                                  * Cook the constr->raw_expr into an expression. Note:
644                                  * Name is strictly for error message
645                                  */
646                                 defaultExpr = cookDefault(pstate, constr->raw_expr,
647                                                                                   basetypeoid,
648                                                                                   stmt->typename->typmod,
649                                                                                   domainName);
650
651                                 /*
652                                  * Expression must be stored as a nodeToString result, but
653                                  * we also require a valid textual representation (mainly
654                                  * to make life easier for pg_dump).
655                                  */
656                                 defaultValue = deparse_expression(defaultExpr,
657                                                                                   deparse_context_for(domainName,
658                                                                                                                           InvalidOid),
659                                                                                                   false, false);
660                                 defaultValueBin = nodeToString(defaultExpr);
661                                 break;
662
663                         case CONSTR_NOTNULL:
664                                 if (nullDefined && !typNotNull)
665                                         ereport(ERROR,
666                                                         (errcode(ERRCODE_SYNTAX_ERROR),
667                                            errmsg("conflicting NULL/NOT NULL constraints")));
668                                 typNotNull = true;
669                                 nullDefined = true;
670                                 break;
671
672                         case CONSTR_NULL:
673                                 if (nullDefined && typNotNull)
674                                         ereport(ERROR,
675                                                         (errcode(ERRCODE_SYNTAX_ERROR),
676                                            errmsg("conflicting NULL/NOT NULL constraints")));
677                                 typNotNull = false;
678                                 nullDefined = true;
679                                 break;
680
681                         case CONSTR_CHECK:
682
683                                 /*
684                                  * Check constraints are handled after domain creation, as
685                                  * they require the Oid of the domain
686                                  */
687                                 break;
688
689                                 /*
690                                  * All else are error cases
691                                  */
692                         case CONSTR_UNIQUE:
693                                 ereport(ERROR,
694                                                 (errcode(ERRCODE_SYNTAX_ERROR),
695                                  errmsg("unique constraints not possible for domains")));
696                                 break;
697
698                         case CONSTR_PRIMARY:
699                                 ereport(ERROR,
700                                                 (errcode(ERRCODE_SYNTAX_ERROR),
701                                                  errmsg("primary key constraints not possible for domains")));
702                                 break;
703
704                         case CONSTR_ATTR_DEFERRABLE:
705                         case CONSTR_ATTR_NOT_DEFERRABLE:
706                         case CONSTR_ATTR_DEFERRED:
707                         case CONSTR_ATTR_IMMEDIATE:
708                                 ereport(ERROR,
709                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
710                                                  errmsg("specifying constraint deferrability not supported for domains")));
711                                 break;
712
713                         default:
714                                 elog(ERROR, "unrecognized constraint subtype: %d",
715                                          (int) constr->contype);
716                                 break;
717                 }
718         }
719
720         /*
721          * Have TypeCreate do all the real work.
722          */
723         domainoid =
724                 TypeCreate(domainName,  /* type name */
725                                    domainNamespace,             /* namespace */
726                                    InvalidOid,  /* preassigned type oid (none here) */
727                                    InvalidOid,  /* relation oid (n/a here) */
728                                    0,                   /* relation kind (ditto) */
729                                    internalLength,              /* internal size */
730                                    'd',                 /* type-type (domain type) */
731                                    delimiter,   /* array element delimiter */
732                                    inputProcedure,              /* input procedure */
733                                    outputProcedure,             /* output procedure */
734                                    receiveProcedure,    /* receive procedure */
735                                    sendProcedure,               /* send procedure */
736                                    analyzeProcedure,    /* analyze procedure */
737                                    basetypelem, /* element type ID */
738                                    basetypeoid, /* base type ID */
739                                    defaultValue,        /* default type value (text) */
740                                    defaultValueBin,             /* default type value (binary) */
741                                    byValue,             /* passed by value */
742                                    alignment,   /* required alignment */
743                                    storage,             /* TOAST strategy */
744                                    stmt->typename->typmod,              /* typeMod value */
745                                    typNDims,    /* Array dimensions for base type */
746                                    typNotNull); /* Type NOT NULL */
747
748         /*
749          * Process constraints which refer to the domain ID returned by
750          * TypeCreate
751          */
752         foreach(listptr, schema)
753         {
754                 Constraint *constr = lfirst(listptr);
755
756                 /* it must be a Constraint, per check above */
757
758                 switch (constr->contype)
759                 {
760                         case CONSTR_CHECK:
761                                 domainAddConstraint(domainoid, domainNamespace,
762                                                                         basetypeoid, stmt->typename->typmod,
763                                                                         constr, domainName);
764                                 break;
765
766                                 /* Other constraint types were fully processed above */
767
768                         default:
769                                 break;
770                 }
771
772                 /* CCI so we can detect duplicate constraint names */
773                 CommandCounterIncrement();
774         }
775
776         /*
777          * Now we can clean up.
778          */
779         ReleaseSysCache(typeTup);
780 }
781
782
783 /*
784  *      RemoveDomain
785  *              Removes a domain.
786  *
787  * This is identical to RemoveType except we insist it be a domain.
788  */
789 void
790 RemoveDomain(List *names, DropBehavior behavior)
791 {
792         TypeName   *typename;
793         Oid                     typeoid;
794         HeapTuple       tup;
795         char            typtype;
796         ObjectAddress object;
797
798         /* Make a TypeName so we can use standard type lookup machinery */
799         typename = makeNode(TypeName);
800         typename->names = names;
801         typename->typmod = -1;
802         typename->arrayBounds = NIL;
803
804         /* Use LookupTypeName here so that shell types can be removed. */
805         typeoid = LookupTypeName(typename);
806         if (!OidIsValid(typeoid))
807                 ereport(ERROR,
808                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
809                                  errmsg("type \"%s\" does not exist",
810                                                 TypeNameToString(typename))));
811
812         tup = SearchSysCache(TYPEOID,
813                                                  ObjectIdGetDatum(typeoid),
814                                                  0, 0, 0);
815         if (!HeapTupleIsValid(tup))
816                 elog(ERROR, "cache lookup failed for type %u", typeoid);
817
818         /* Permission check: must own type or its namespace */
819         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
820                 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
821                                                                  GetUserId()))
822                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
823                                            TypeNameToString(typename));
824
825         /* Check that this is actually a domain */
826         typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
827
828         if (typtype != 'd')
829                 ereport(ERROR,
830                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
831                                  errmsg("\"%s\" is not a domain",
832                                                 TypeNameToString(typename))));
833
834         ReleaseSysCache(tup);
835
836         /*
837          * Do the deletion
838          */
839         object.classId = TypeRelationId;
840         object.objectId = typeoid;
841         object.objectSubId = 0;
842
843         performDeletion(&object, behavior);
844 }
845
846
847 /*
848  * Find suitable I/O functions for a type.
849  *
850  * typeOid is the type's OID (which will already exist, if only as a shell
851  * type).
852  */
853
854 static Oid
855 findTypeInputFunction(List *procname, Oid typeOid)
856 {
857         Oid                     argList[3];
858         Oid                     procOid;
859
860         /*
861          * Input functions can take a single argument of type CSTRING, or
862          * three arguments (string, element OID, typmod).
863          *
864          * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
865          * see this, we issue a warning and fix up the pg_proc entry.
866          */
867         argList[0] = CSTRINGOID;
868
869         procOid = LookupFuncName(procname, 1, argList, true);
870         if (OidIsValid(procOid))
871                 return procOid;
872
873         argList[1] = OIDOID;
874         argList[2] = INT4OID;
875
876         procOid = LookupFuncName(procname, 3, argList, true);
877         if (OidIsValid(procOid))
878                 return procOid;
879
880         /* No luck, try it with OPAQUE */
881         argList[0] = OPAQUEOID;
882
883         procOid = LookupFuncName(procname, 1, argList, true);
884
885         if (!OidIsValid(procOid))
886         {
887                 argList[1] = OIDOID;
888                 argList[2] = INT4OID;
889
890                 procOid = LookupFuncName(procname, 3, argList, true);
891         }
892
893         if (OidIsValid(procOid))
894         {
895                 /* Found, but must complain and fix the pg_proc entry */
896                 ereport(WARNING,
897                                 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
898                                                 NameListToString(procname))));
899                 SetFunctionArgType(procOid, 0, CSTRINGOID);
900
901                 /*
902                  * Need CommandCounterIncrement since DefineType will likely try
903                  * to alter the pg_proc tuple again.
904                  */
905                 CommandCounterIncrement();
906
907                 return procOid;
908         }
909
910         /* Use CSTRING (preferred) in the error message */
911         argList[0] = CSTRINGOID;
912
913         ereport(ERROR,
914                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
915                          errmsg("function %s does not exist",
916                                         func_signature_string(procname, 1, argList))));
917
918         return InvalidOid;                      /* keep compiler quiet */
919 }
920
921 static Oid
922 findTypeOutputFunction(List *procname, Oid typeOid)
923 {
924         Oid                     argList[2];
925         Oid                     procOid;
926
927         /*
928          * Output functions can take a single argument of the type, or two
929          * arguments (data value, element OID).
930          *
931          * For backwards compatibility we allow OPAQUE in place of the actual
932          * type name; if we see this, we issue a warning and fix up the
933          * pg_proc entry.
934          */
935         argList[0] = typeOid;
936
937         procOid = LookupFuncName(procname, 1, argList, true);
938         if (OidIsValid(procOid))
939                 return procOid;
940
941         argList[1] = OIDOID;
942
943         procOid = LookupFuncName(procname, 2, argList, true);
944         if (OidIsValid(procOid))
945                 return procOid;
946
947         /* No luck, try it with OPAQUE */
948         argList[0] = OPAQUEOID;
949
950         procOid = LookupFuncName(procname, 1, argList, true);
951
952         if (!OidIsValid(procOid))
953         {
954                 argList[1] = OIDOID;
955
956                 procOid = LookupFuncName(procname, 2, argList, true);
957         }
958
959         if (OidIsValid(procOid))
960         {
961                 /* Found, but must complain and fix the pg_proc entry */
962                 ereport(WARNING,
963                                 (errmsg("changing argument type of function %s from \"opaque\" to %s",
964                                   NameListToString(procname), format_type_be(typeOid))));
965                 SetFunctionArgType(procOid, 0, typeOid);
966
967                 /*
968                  * Need CommandCounterIncrement since DefineType will likely try
969                  * to alter the pg_proc tuple again.
970                  */
971                 CommandCounterIncrement();
972
973                 return procOid;
974         }
975
976         /* Use type name, not OPAQUE, in the failure message. */
977         argList[0] = typeOid;
978
979         ereport(ERROR,
980                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
981                          errmsg("function %s does not exist",
982                                         func_signature_string(procname, 1, argList))));
983
984         return InvalidOid;                      /* keep compiler quiet */
985 }
986
987 static Oid
988 findTypeReceiveFunction(List *procname, Oid typeOid)
989 {
990         Oid                     argList[2];
991         Oid                     procOid;
992
993         /*
994          * Receive functions can take a single argument of type INTERNAL, or
995          * two arguments (internal, oid).
996          */
997         argList[0] = INTERNALOID;
998
999         procOid = LookupFuncName(procname, 1, argList, true);
1000         if (OidIsValid(procOid))
1001                 return procOid;
1002
1003         argList[1] = OIDOID;
1004
1005         procOid = LookupFuncName(procname, 2, argList, true);
1006         if (OidIsValid(procOid))
1007                 return procOid;
1008
1009         ereport(ERROR,
1010                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1011                          errmsg("function %s does not exist",
1012                                         func_signature_string(procname, 1, argList))));
1013
1014         return InvalidOid;                      /* keep compiler quiet */
1015 }
1016
1017 static Oid
1018 findTypeSendFunction(List *procname, Oid typeOid)
1019 {
1020         Oid                     argList[2];
1021         Oid                     procOid;
1022
1023         /*
1024          * Send functions can take a single argument of the type, or two
1025          * arguments (data value, element OID).
1026          */
1027         argList[0] = typeOid;
1028
1029         procOid = LookupFuncName(procname, 1, argList, true);
1030         if (OidIsValid(procOid))
1031                 return procOid;
1032
1033         argList[1] = OIDOID;
1034
1035         procOid = LookupFuncName(procname, 2, argList, true);
1036         if (OidIsValid(procOid))
1037                 return procOid;
1038
1039         ereport(ERROR,
1040                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1041                          errmsg("function %s does not exist",
1042                                         func_signature_string(procname, 1, argList))));
1043
1044         return InvalidOid;                      /* keep compiler quiet */
1045 }
1046
1047 static Oid
1048 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1049 {
1050         Oid                     argList[1];
1051         Oid                     procOid;
1052
1053         /*
1054          * Analyze functions always take one INTERNAL argument and return
1055          * bool.
1056          */
1057         argList[0] = INTERNALOID;
1058
1059         procOid = LookupFuncName(procname, 1, argList, true);
1060         if (!OidIsValid(procOid))
1061                 ereport(ERROR,
1062                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1063                                  errmsg("function %s does not exist",
1064                                                 func_signature_string(procname, 1, argList))));
1065
1066         if (get_func_rettype(procOid) != BOOLOID)
1067                 ereport(ERROR,
1068                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1069                   errmsg("type analyze function %s must return type \"boolean\"",
1070                                  NameListToString(procname))));
1071
1072         return procOid;
1073 }
1074
1075
1076 /*-------------------------------------------------------------------
1077  * DefineCompositeType
1078  *
1079  * Create a Composite Type relation.
1080  * `DefineRelation' does all the work, we just provide the correct
1081  * arguments!
1082  *
1083  * If the relation already exists, then 'DefineRelation' will abort
1084  * the xact...
1085  *
1086  * DefineCompositeType returns relid for use when creating
1087  * an implicit composite type during function creation
1088  *-------------------------------------------------------------------
1089  */
1090 Oid
1091 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
1092 {
1093         CreateStmt *createStmt = makeNode(CreateStmt);
1094
1095         if (coldeflist == NIL)
1096                 ereport(ERROR,
1097                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1098                          errmsg("composite type must have at least one attribute")));
1099
1100         /*
1101          * now set the parameters for keys/inheritance etc. All of these are
1102          * uninteresting for composite types...
1103          */
1104         createStmt->relation = (RangeVar *) typevar;
1105         createStmt->tableElts = coldeflist;
1106         createStmt->inhRelations = NIL;
1107         createStmt->constraints = NIL;
1108         createStmt->hasoids = MUST_NOT_HAVE_OIDS;
1109         createStmt->oncommit = ONCOMMIT_NOOP;
1110         createStmt->tablespacename = NULL;
1111
1112         /*
1113          * finally create the relation...
1114          */
1115         return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
1116 }
1117
1118 /*
1119  * AlterDomainDefault
1120  *
1121  * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
1122  */
1123 void
1124 AlterDomainDefault(List *names, Node *defaultRaw)
1125 {
1126         TypeName   *typename;
1127         Oid                     domainoid;
1128         HeapTuple       tup;
1129         ParseState *pstate;
1130         Relation        rel;
1131         char       *defaultValue;
1132         Node       *defaultExpr = NULL;         /* NULL if no default specified */
1133         Datum           new_record[Natts_pg_type];
1134         char            new_record_nulls[Natts_pg_type];
1135         char            new_record_repl[Natts_pg_type];
1136         HeapTuple       newtuple;
1137         Form_pg_type typTup;
1138
1139         /* Make a TypeName so we can use standard type lookup machinery */
1140         typename = makeNode(TypeName);
1141         typename->names = names;
1142         typename->typmod = -1;
1143         typename->arrayBounds = NIL;
1144
1145         /* Lock the domain in the type table */
1146         rel = heap_openr(TypeRelationName, RowExclusiveLock);
1147
1148         /* Use LookupTypeName here so that shell types can be removed. */
1149         domainoid = LookupTypeName(typename);
1150         if (!OidIsValid(domainoid))
1151                 ereport(ERROR,
1152                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1153                                  errmsg("type \"%s\" does not exist",
1154                                                 TypeNameToString(typename))));
1155
1156         tup = SearchSysCacheCopy(TYPEOID,
1157                                                          ObjectIdGetDatum(domainoid),
1158                                                          0, 0, 0);
1159         if (!HeapTupleIsValid(tup))
1160                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1161
1162         /* Doesn't return if user isn't allowed to alter the domain */
1163         domainOwnerCheck(tup, typename);
1164
1165         /* Setup new tuple */
1166         MemSet(new_record, (Datum) 0, sizeof(new_record));
1167         MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1168         MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1169
1170         /* Useful later */
1171         typTup = (Form_pg_type) GETSTRUCT(tup);
1172
1173         /* Store the new default, if null then skip this step */
1174         if (defaultRaw)
1175         {
1176                 /* Create a dummy ParseState for transformExpr */
1177                 pstate = make_parsestate(NULL);
1178
1179                 /*
1180                  * Cook the colDef->raw_expr into an expression. Note: Name is
1181                  * strictly for error message
1182                  */
1183                 defaultExpr = cookDefault(pstate, defaultRaw,
1184                                                                   typTup->typbasetype,
1185                                                                   typTup->typtypmod,
1186                                                                   NameStr(typTup->typname));
1187
1188                 /*
1189                  * Expression must be stored as a nodeToString result, but we also
1190                  * require a valid textual representation (mainly to make life
1191                  * easier for pg_dump).
1192                  */
1193                 defaultValue = deparse_expression(defaultExpr,
1194                                                         deparse_context_for(NameStr(typTup->typname),
1195                                                                                                 InvalidOid),
1196                                                                                   false, false);
1197
1198                 /*
1199                  * Form an updated tuple with the new default and write it back.
1200                  */
1201                 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
1202                                                                                                                  CStringGetDatum(
1203                                                                                          nodeToString(defaultExpr)));
1204
1205                 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1206                 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
1207                                                                                   CStringGetDatum(defaultValue));
1208                 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1209         }
1210         else
1211         /* Default is NULL, drop it */
1212         {
1213                 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
1214                 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1215                 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1216                 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1217         }
1218
1219         newtuple = heap_modifytuple(tup, RelationGetDescr(rel),
1220                                                                 new_record, new_record_nulls,
1221                                                                 new_record_repl);
1222
1223         simple_heap_update(rel, &tup->t_self, newtuple);
1224
1225         CatalogUpdateIndexes(rel, newtuple);
1226
1227         /* Rebuild dependencies */
1228         GenerateTypeDependencies(typTup->typnamespace,
1229                                                          domainoid,
1230                                                          typTup->typrelid,
1231                                                          0, /* relation kind is n/a */
1232                                                          typTup->typinput,
1233                                                          typTup->typoutput,
1234                                                          typTup->typreceive,
1235                                                          typTup->typsend,
1236                                                          typTup->typanalyze,
1237                                                          typTup->typelem,
1238                                                          typTup->typbasetype,
1239                                                          defaultExpr,
1240                                                          true);         /* Rebuild is true */
1241
1242         /* Clean up */
1243         heap_close(rel, NoLock);
1244         heap_freetuple(newtuple);
1245 }
1246
1247 /*
1248  * AlterDomainNotNull
1249  *
1250  * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
1251  */
1252 void
1253 AlterDomainNotNull(List *names, bool notNull)
1254 {
1255         TypeName   *typename;
1256         Oid                     domainoid;
1257         Relation        typrel;
1258         HeapTuple       tup;
1259         Form_pg_type typTup;
1260
1261         /* Make a TypeName so we can use standard type lookup machinery */
1262         typename = makeNode(TypeName);
1263         typename->names = names;
1264         typename->typmod = -1;
1265         typename->arrayBounds = NIL;
1266
1267         /* Lock the type table */
1268         typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1269
1270         /* Use LookupTypeName here so that shell types can be found (why?). */
1271         domainoid = LookupTypeName(typename);
1272         if (!OidIsValid(domainoid))
1273                 ereport(ERROR,
1274                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1275                                  errmsg("type \"%s\" does not exist",
1276                                                 TypeNameToString(typename))));
1277
1278         tup = SearchSysCacheCopy(TYPEOID,
1279                                                          ObjectIdGetDatum(domainoid),
1280                                                          0, 0, 0);
1281         if (!HeapTupleIsValid(tup))
1282                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1283         typTup = (Form_pg_type) GETSTRUCT(tup);
1284
1285         /* Doesn't return if user isn't allowed to alter the domain */
1286         domainOwnerCheck(tup, typename);
1287
1288         /* Is the domain already set to the desired constraint? */
1289         if (typTup->typnotnull == notNull)
1290         {
1291                 heap_close(typrel, RowExclusiveLock);
1292                 return;
1293         }
1294
1295         /* Adding a NOT NULL constraint requires checking existing columns */
1296         if (notNull)
1297         {
1298                 List       *rels;
1299                 ListCell   *rt;
1300
1301                 /* Fetch relation list with attributes based on this domain */
1302                 /* ShareLock is sufficient to prevent concurrent data changes */
1303
1304                 rels = get_rels_with_domain(domainoid, ShareLock);
1305
1306                 foreach(rt, rels)
1307                 {
1308                         RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1309                         Relation        testrel = rtc->rel;
1310                         TupleDesc       tupdesc = RelationGetDescr(testrel);
1311                         HeapScanDesc scan;
1312                         HeapTuple       tuple;
1313
1314                         /* Scan all tuples in this relation */
1315                         scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1316                         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1317                         {
1318                                 int                     i;
1319
1320                                 /* Test attributes that are of the domain */
1321                                 for (i = 0; i < rtc->natts; i++)
1322                                 {
1323                                         int                     attnum = rtc->atts[i];
1324
1325                                         if (heap_attisnull(tuple, attnum))
1326                                                 ereport(ERROR,
1327                                                                 (errcode(ERRCODE_NOT_NULL_VIOLATION),
1328                                                                  errmsg("column \"%s\" of table \"%s\" contains null values",
1329                                                         NameStr(tupdesc->attrs[attnum - 1]->attname),
1330                                                                          RelationGetRelationName(testrel))));
1331                                 }
1332                         }
1333                         heap_endscan(scan);
1334
1335                         /* Close each rel after processing, but keep lock */
1336                         heap_close(testrel, NoLock);
1337                 }
1338         }
1339
1340         /*
1341          * Okay to update pg_type row.  We can scribble on typTup because it's
1342          * a copy.
1343          */
1344         typTup->typnotnull = notNull;
1345
1346         simple_heap_update(typrel, &tup->t_self, tup);
1347
1348         CatalogUpdateIndexes(typrel, tup);
1349
1350         /* Clean up */
1351         heap_freetuple(tup);
1352         heap_close(typrel, RowExclusiveLock);
1353 }
1354
1355 /*
1356  * AlterDomainDropConstraint
1357  *
1358  * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1359  */
1360 void
1361 AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior)
1362 {
1363         TypeName   *typename;
1364         Oid                     domainoid;
1365         HeapTuple       tup;
1366         Relation        rel;
1367         Form_pg_type typTup;
1368         Relation        conrel;
1369         SysScanDesc conscan;
1370         ScanKeyData key[1];
1371         HeapTuple       contup;
1372
1373         /* Make a TypeName so we can use standard type lookup machinery */
1374         typename = makeNode(TypeName);
1375         typename->names = names;
1376         typename->typmod = -1;
1377         typename->arrayBounds = NIL;
1378
1379         /* Lock the type table */
1380         rel = heap_openr(TypeRelationName, RowExclusiveLock);
1381
1382         /* Use LookupTypeName here so that shell types can be removed. */
1383         domainoid = LookupTypeName(typename);
1384         if (!OidIsValid(domainoid))
1385                 ereport(ERROR,
1386                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1387                                  errmsg("type \"%s\" does not exist",
1388                                                 TypeNameToString(typename))));
1389
1390         tup = SearchSysCacheCopy(TYPEOID,
1391                                                          ObjectIdGetDatum(domainoid),
1392                                                          0, 0, 0);
1393         if (!HeapTupleIsValid(tup))
1394                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1395
1396         /* Doesn't return if user isn't allowed to alter the domain */
1397         domainOwnerCheck(tup, typename);
1398
1399         /* Grab an appropriate lock on the pg_constraint relation */
1400         conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
1401
1402         /* Use the index to scan only constraints of the target relation */
1403         ScanKeyInit(&key[0],
1404                                 Anum_pg_constraint_contypid,
1405                                 BTEqualStrategyNumber, F_OIDEQ,
1406                                 ObjectIdGetDatum(HeapTupleGetOid(tup)));
1407
1408         conscan = systable_beginscan(conrel, ConstraintTypidIndex, true,
1409                                                                  SnapshotNow, 1, key);
1410
1411         typTup = (Form_pg_type) GETSTRUCT(tup);
1412
1413         /*
1414          * Scan over the result set, removing any matching entries.
1415          */
1416         while ((contup = systable_getnext(conscan)) != NULL)
1417         {
1418                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1419
1420                 if (strcmp(NameStr(con->conname), constrName) == 0)
1421                 {
1422                         ObjectAddress conobj;
1423
1424                         conobj.classId = RelationGetRelid(conrel);
1425                         conobj.objectId = HeapTupleGetOid(contup);
1426                         conobj.objectSubId = 0;
1427
1428                         performDeletion(&conobj, behavior);
1429                 }
1430         }
1431         /* Clean up after the scan */
1432         systable_endscan(conscan);
1433         heap_close(conrel, RowExclusiveLock);
1434
1435         heap_close(rel, NoLock);
1436 }
1437
1438 /*
1439  * AlterDomainAddConstraint
1440  *
1441  * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1442  */
1443 void
1444 AlterDomainAddConstraint(List *names, Node *newConstraint)
1445 {
1446         TypeName   *typename;
1447         Oid                     domainoid;
1448         Relation        typrel;
1449         HeapTuple       tup;
1450         Form_pg_type typTup;
1451         List       *rels;
1452         ListCell   *rt;
1453         EState     *estate;
1454         ExprContext *econtext;
1455         char       *ccbin;
1456         Expr       *expr;
1457         ExprState  *exprstate;
1458         Constraint *constr;
1459
1460         /* Make a TypeName so we can use standard type lookup machinery */
1461         typename = makeNode(TypeName);
1462         typename->names = names;
1463         typename->typmod = -1;
1464         typename->arrayBounds = NIL;
1465
1466         /* Lock the type table */
1467         typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1468
1469         /* Use LookupTypeName here so that shell types can be found (why?). */
1470         domainoid = LookupTypeName(typename);
1471         if (!OidIsValid(domainoid))
1472                 ereport(ERROR,
1473                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1474                                  errmsg("type \"%s\" does not exist",
1475                                                 TypeNameToString(typename))));
1476
1477         tup = SearchSysCacheCopy(TYPEOID,
1478                                                          ObjectIdGetDatum(domainoid),
1479                                                          0, 0, 0);
1480         if (!HeapTupleIsValid(tup))
1481                 elog(ERROR, "cache lookup failed for type %u", domainoid);
1482         typTup = (Form_pg_type) GETSTRUCT(tup);
1483
1484         /* Doesn't return if user isn't allowed to alter the domain */
1485         domainOwnerCheck(tup, typename);
1486
1487         /* Check for unsupported constraint types */
1488         if (IsA(newConstraint, FkConstraint))
1489                 ereport(ERROR,
1490                                 (errcode(ERRCODE_SYNTAX_ERROR),
1491                         errmsg("foreign key constraints not possible for domains")));
1492
1493         /* otherwise it should be a plain Constraint */
1494         if (!IsA(newConstraint, Constraint))
1495                 elog(ERROR, "unrecognized node type: %d",
1496                          (int) nodeTag(newConstraint));
1497
1498         constr = (Constraint *) newConstraint;
1499
1500         switch (constr->contype)
1501         {
1502                 case CONSTR_CHECK:
1503                         /* processed below */
1504                         break;
1505
1506                 case CONSTR_UNIQUE:
1507                         ereport(ERROR,
1508                                         (errcode(ERRCODE_SYNTAX_ERROR),
1509                                  errmsg("unique constraints not possible for domains")));
1510                         break;
1511
1512                 case CONSTR_PRIMARY:
1513                         ereport(ERROR,
1514                                         (errcode(ERRCODE_SYNTAX_ERROR),
1515                         errmsg("primary key constraints not possible for domains")));
1516                         break;
1517
1518                 case CONSTR_ATTR_DEFERRABLE:
1519                 case CONSTR_ATTR_NOT_DEFERRABLE:
1520                 case CONSTR_ATTR_DEFERRED:
1521                 case CONSTR_ATTR_IMMEDIATE:
1522                         ereport(ERROR,
1523                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1524                                          errmsg("specifying constraint deferrability not supported for domains")));
1525                         break;
1526
1527                 default:
1528                         elog(ERROR, "unrecognized constraint subtype: %d",
1529                                  (int) constr->contype);
1530                         break;
1531         }
1532
1533         /*
1534          * Since all other constraint types throw errors, this must be a check
1535          * constraint.  First, process the constraint expression and add an
1536          * entry to pg_constraint.
1537          */
1538
1539         ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1540                                                                 typTup->typbasetype, typTup->typtypmod,
1541                                                                 constr, NameStr(typTup->typname));
1542
1543         /*
1544          * Test all values stored in the attributes based on the domain the
1545          * constraint is being added to.
1546          */
1547         expr = (Expr *) stringToNode(ccbin);
1548
1549         /* Need an EState to run ExecEvalExpr */
1550         estate = CreateExecutorState();
1551         econtext = GetPerTupleExprContext(estate);
1552
1553         /* build execution state for expr */
1554         exprstate = ExecPrepareExpr(expr, estate);
1555
1556         /* Fetch relation list with attributes based on this domain */
1557         /* ShareLock is sufficient to prevent concurrent data changes */
1558
1559         rels = get_rels_with_domain(domainoid, ShareLock);
1560
1561         foreach(rt, rels)
1562         {
1563                 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1564                 Relation        testrel = rtc->rel;
1565                 TupleDesc       tupdesc = RelationGetDescr(testrel);
1566                 HeapScanDesc scan;
1567                 HeapTuple       tuple;
1568
1569                 /* Scan all tuples in this relation */
1570                 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1571                 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1572                 {
1573                         int                     i;
1574
1575                         /* Test attributes that are of the domain */
1576                         for (i = 0; i < rtc->natts; i++)
1577                         {
1578                                 int                     attnum = rtc->atts[i];
1579                                 Datum           d;
1580                                 bool            isNull;
1581                                 Datum           conResult;
1582
1583                                 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1584
1585                                 econtext->domainValue_datum = d;
1586                                 econtext->domainValue_isNull = isNull;
1587
1588                                 conResult = ExecEvalExprSwitchContext(exprstate,
1589                                                                                                           econtext,
1590                                                                                                           &isNull, NULL);
1591
1592                                 if (!isNull && !DatumGetBool(conResult))
1593                                         ereport(ERROR,
1594                                                         (errcode(ERRCODE_CHECK_VIOLATION),
1595                                                          errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
1596                                                         NameStr(tupdesc->attrs[attnum - 1]->attname),
1597                                                                         RelationGetRelationName(testrel))));
1598                         }
1599
1600                         ResetExprContext(econtext);
1601                 }
1602                 heap_endscan(scan);
1603
1604                 /* Hold relation lock till commit (XXX bad for concurrency) */
1605                 heap_close(testrel, NoLock);
1606         }
1607
1608         FreeExecutorState(estate);
1609
1610         /* Clean up */
1611         heap_close(typrel, RowExclusiveLock);
1612 }
1613
1614 /*
1615  * get_rels_with_domain
1616  *
1617  * Fetch all relations / attributes which are using the domain
1618  *
1619  * The result is a list of RelToCheck structs, one for each distinct
1620  * relation, each containing one or more attribute numbers that are of
1621  * the domain type.  We have opened each rel and acquired the specified lock
1622  * type on it.
1623  *
1624  * XXX this is completely broken because there is no way to lock the domain
1625  * to prevent columns from being added or dropped while our command runs.
1626  * We can partially protect against column drops by locking relations as we
1627  * come across them, but there is still a race condition (the window between
1628  * seeing a pg_depend entry and acquiring lock on the relation it references).
1629  * Also, holding locks on all these relations simultaneously creates a non-
1630  * trivial risk of deadlock.  We can minimize but not eliminate the deadlock
1631  * risk by using the weakest suitable lock (ShareLock for most callers).
1632  *
1633  * XXX to support domains over domains, we'd need to make this smarter,
1634  * or make its callers smarter, so that we could find columns of derived
1635  * domains.  Arrays of domains would be a problem too.
1636  *
1637  * Generally used for retrieving a list of tests when adding
1638  * new constraints to a domain.
1639  */
1640 static List *
1641 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1642 {
1643         List       *result = NIL;
1644         Relation        depRel;
1645         ScanKeyData key[2];
1646         SysScanDesc depScan;
1647         HeapTuple       depTup;
1648
1649         /*
1650          * We scan pg_depend to find those things that depend on the domain.
1651          * (We assume we can ignore refobjsubid for a domain.)
1652          */
1653         depRel = relation_openr(DependRelationName, AccessShareLock);
1654
1655         ScanKeyInit(&key[0],
1656                                 Anum_pg_depend_refclassid,
1657                                 BTEqualStrategyNumber, F_OIDEQ,
1658                                 ObjectIdGetDatum(TypeRelationId));
1659         ScanKeyInit(&key[1],
1660                                 Anum_pg_depend_refobjid,
1661                                 BTEqualStrategyNumber, F_OIDEQ,
1662                                 ObjectIdGetDatum(domainOid));
1663
1664         depScan = systable_beginscan(depRel, DependReferenceIndex, true,
1665                                                                  SnapshotNow, 2, key);
1666
1667         while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1668         {
1669                 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1670                 RelToCheck *rtc = NULL;
1671                 ListCell   *rellist;
1672                 Form_pg_attribute pg_att;
1673                 int                     ptr;
1674
1675                 /* Ignore dependees that aren't user columns of relations */
1676                 /* (we assume system columns are never of domain types) */
1677                 if (pg_depend->classid != RelationRelationId ||
1678                         pg_depend->objsubid <= 0)
1679                         continue;
1680
1681                 /* See if we already have an entry for this relation */
1682                 foreach(rellist, result)
1683                 {
1684                         RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1685
1686                         if (RelationGetRelid(rt->rel) == pg_depend->objid)
1687                         {
1688                                 rtc = rt;
1689                                 break;
1690                         }
1691                 }
1692
1693                 if (rtc == NULL)
1694                 {
1695                         /* First attribute found for this relation */
1696                         Relation        rel;
1697
1698                         /* Acquire requested lock on relation */
1699                         rel = relation_open(pg_depend->objid, lockmode);
1700
1701                         /* It could be a view or composite type; if so ignore it */
1702                         if (rel->rd_rel->relkind != RELKIND_RELATION)
1703                         {
1704                                 relation_close(rel, lockmode);
1705                                 continue;
1706                         }
1707
1708                         /* Build the RelToCheck entry with enough space for all atts */
1709                         rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1710                         rtc->rel = rel;
1711                         rtc->natts = 0;
1712                         rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1713                         result = lcons(rtc, result);
1714                 }
1715
1716                 /*
1717                  * Confirm column has not been dropped, and is of the expected
1718                  * type. This defends against an ALTER DROP COLUMN occuring just
1719                  * before we acquired lock ... but if the whole table were
1720                  * dropped, we'd still have a problem.
1721                  */
1722                 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
1723                         continue;
1724                 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
1725                 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
1726                         continue;
1727
1728                 /*
1729                  * Okay, add column to result.  We store the columns in
1730                  * column-number order; this is just a hack to improve
1731                  * predictability of regression test output ...
1732                  */
1733                 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
1734
1735                 ptr = rtc->natts++;
1736                 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
1737                 {
1738                         rtc->atts[ptr] = rtc->atts[ptr - 1];
1739                         ptr--;
1740                 }
1741                 rtc->atts[ptr] = pg_depend->objsubid;
1742         }
1743
1744         systable_endscan(depScan);
1745
1746         relation_close(depRel, AccessShareLock);
1747
1748         return result;
1749 }
1750
1751 /*
1752  * domainOwnerCheck
1753  *
1754  * Throw an error if the current user doesn't have permission to modify
1755  * the domain in an ALTER DOMAIN statement, or if the type isn't actually
1756  * a domain.
1757  */
1758 static void
1759 domainOwnerCheck(HeapTuple tup, TypeName *typename)
1760 {
1761         Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
1762
1763         /* Check that this is actually a domain */
1764         if (typTup->typtype != 'd')
1765                 ereport(ERROR,
1766                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1767                                  errmsg("\"%s\" is not a domain",
1768                                                 TypeNameToString(typename))));
1769
1770         /* Permission check: must own type */
1771         if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1772                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
1773                                            TypeNameToString(typename));
1774 }
1775
1776 /*
1777  * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
1778  */
1779 static char *
1780 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
1781                                         int typMod, Constraint *constr,
1782                                         char *domainName)
1783 {
1784         Node       *expr;
1785         char       *ccsrc;
1786         char       *ccbin;
1787         ParseState *pstate;
1788         CoerceToDomainValue *domVal;
1789
1790         /*
1791          * Assign or validate constraint name
1792          */
1793         if (constr->name)
1794         {
1795                 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
1796                                                                  domainOid,
1797                                                                  domainNamespace,
1798                                                                  constr->name))
1799                         ereport(ERROR,
1800                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
1801                          errmsg("constraint \"%s\" for domain \"%s\" already exists",
1802                                         constr->name, domainName)));
1803         }
1804         else
1805                 constr->name = ChooseConstraintName(domainName,
1806                                                                                         NULL,
1807                                                                                         "check",
1808                                                                                         domainNamespace,
1809                                                                                         NIL);
1810
1811         /*
1812          * Convert the A_EXPR in raw_expr into an EXPR
1813          */
1814         pstate = make_parsestate(NULL);
1815
1816         /*
1817          * Set up a CoerceToDomainValue to represent the occurrence of VALUE
1818          * in the expression.  Note that it will appear to have the type of
1819          * the base type, not the domain.  This seems correct since within the
1820          * check expression, we should not assume the input value can be
1821          * considered a member of the domain.
1822          */
1823         domVal = makeNode(CoerceToDomainValue);
1824         domVal->typeId = baseTypeOid;
1825         domVal->typeMod = typMod;
1826
1827         pstate->p_value_substitute = (Node *) domVal;
1828
1829         expr = transformExpr(pstate, constr->raw_expr);
1830
1831         /*
1832          * Make sure it yields a boolean result.
1833          */
1834         expr = coerce_to_boolean(pstate, expr, "CHECK");
1835
1836         /*
1837          * Make sure no outside relations are referred to.
1838          */
1839         if (list_length(pstate->p_rtable) != 0)
1840                 ereport(ERROR,
1841                                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1842                                  errmsg("cannot use table references in domain check constraint")));
1843
1844         /*
1845          * Domains don't allow var clauses (this should be redundant with the
1846          * above check, but make it anyway)
1847          */
1848         if (contain_var_clause(expr))
1849                 ereport(ERROR,
1850                                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1851                                  errmsg("cannot use table references in domain check constraint")));
1852
1853         /*
1854          * No subplans or aggregates, either...
1855          */
1856         if (pstate->p_hasSubLinks)
1857                 ereport(ERROR,
1858                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1859                                  errmsg("cannot use subquery in check constraint")));
1860         if (pstate->p_hasAggs)
1861                 ereport(ERROR,
1862                                 (errcode(ERRCODE_GROUPING_ERROR),
1863                                  errmsg("cannot use aggregate in check constraint")));
1864
1865         /*
1866          * Convert to string form for storage.
1867          */
1868         ccbin = nodeToString(expr);
1869
1870         /*
1871          * Deparse it to produce text for consrc.
1872          *
1873          * Since VARNOs aren't allowed in domain constraints, relation context
1874          * isn't required as anything other than a shell.
1875          */
1876         ccsrc = deparse_expression(expr,
1877                                                            deparse_context_for(domainName,
1878                                                                                                    InvalidOid),
1879                                                            false, false);
1880
1881         /*
1882          * Store the constraint in pg_constraint
1883          */
1884         CreateConstraintEntry(constr->name, /* Constraint Name */
1885                                                   domainNamespace,              /* namespace */
1886                                                   CONSTRAINT_CHECK,             /* Constraint Type */
1887                                                   false,        /* Is Deferrable */
1888                                                   false,        /* Is Deferred */
1889                                                   InvalidOid,   /* not a relation constraint */
1890                                                   NULL,
1891                                                   0,
1892                                                   domainOid,    /* domain constraint */
1893                                                   InvalidOid,   /* Foreign key fields */
1894                                                   NULL,
1895                                                   0,
1896                                                   ' ',
1897                                                   ' ',
1898                                                   ' ',
1899                                                   InvalidOid,
1900                                                   expr, /* Tree form check constraint */
1901                                                   ccbin,        /* Binary form check constraint */
1902                                                   ccsrc);               /* Source form check constraint */
1903
1904         /*
1905          * Return the compiled constraint expression so the calling routine
1906          * can perform any additional required tests.
1907          */
1908         return ccbin;
1909 }
1910
1911 /*
1912  * GetDomainConstraints - get a list of the current constraints of domain
1913  *
1914  * Returns a possibly-empty list of DomainConstraintState nodes.
1915  *
1916  * This is called by the executor during plan startup for a CoerceToDomain
1917  * expression node.  The given constraints will be checked for each value
1918  * passed through the node.
1919  *
1920  * We allow this to be called for non-domain types, in which case the result
1921  * is always NIL.
1922  */
1923 List *
1924 GetDomainConstraints(Oid typeOid)
1925 {
1926         List       *result = NIL;
1927         bool            notNull = false;
1928         Relation        conRel;
1929
1930         conRel = heap_openr(ConstraintRelationName, AccessShareLock);
1931
1932         for (;;)
1933         {
1934                 HeapTuple       tup;
1935                 HeapTuple       conTup;
1936                 Form_pg_type typTup;
1937                 ScanKeyData key[1];
1938                 SysScanDesc scan;
1939
1940                 tup = SearchSysCache(TYPEOID,
1941                                                          ObjectIdGetDatum(typeOid),
1942                                                          0, 0, 0);
1943                 if (!HeapTupleIsValid(tup))
1944                         elog(ERROR, "cache lookup failed for type %u", typeOid);
1945                 typTup = (Form_pg_type) GETSTRUCT(tup);
1946
1947                 if (typTup->typtype != 'd')
1948                 {
1949                         /* Not a domain, so done */
1950                         ReleaseSysCache(tup);
1951                         break;
1952                 }
1953
1954                 /* Test for NOT NULL Constraint */
1955                 if (typTup->typnotnull)
1956                         notNull = true;
1957
1958                 /* Look for CHECK Constraints on this domain */
1959                 ScanKeyInit(&key[0],
1960                                         Anum_pg_constraint_contypid,
1961                                         BTEqualStrategyNumber, F_OIDEQ,
1962                                         ObjectIdGetDatum(typeOid));
1963
1964                 scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
1965                                                                   SnapshotNow, 1, key);
1966
1967                 while (HeapTupleIsValid(conTup = systable_getnext(scan)))
1968                 {
1969                         Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
1970                         Datum           val;
1971                         bool            isNull;
1972                         Expr       *check_expr;
1973                         DomainConstraintState *r;
1974
1975                         /* Ignore non-CHECK constraints (presently, shouldn't be any) */
1976                         if (c->contype != CONSTRAINT_CHECK)
1977                                 continue;
1978
1979                         /*
1980                          * Not expecting conbin to be NULL, but we'll test for it
1981                          * anyway
1982                          */
1983                         val = fastgetattr(conTup, Anum_pg_constraint_conbin,
1984                                                           conRel->rd_att, &isNull);
1985                         if (isNull)
1986                                 elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
1987                                          NameStr(typTup->typname), NameStr(c->conname));
1988
1989                         check_expr = (Expr *)
1990                                 stringToNode(DatumGetCString(DirectFunctionCall1(textout,
1991                                                                                                                                  val)));
1992
1993                         /* ExecInitExpr assumes we already fixed opfuncids */
1994                         fix_opfuncids((Node *) check_expr);
1995
1996                         r = makeNode(DomainConstraintState);
1997                         r->constrainttype = DOM_CONSTRAINT_CHECK;
1998                         r->name = pstrdup(NameStr(c->conname));
1999                         r->check_expr = ExecInitExpr(check_expr, NULL);
2000
2001                         /*
2002                          * use lcons() here because constraints of lower domains
2003                          * should be applied earlier.
2004                          */
2005                         result = lcons(r, result);
2006                 }
2007
2008                 systable_endscan(scan);
2009
2010                 /* loop to next domain in stack */
2011                 typeOid = typTup->typbasetype;
2012                 ReleaseSysCache(tup);
2013         }
2014
2015         heap_close(conRel, AccessShareLock);
2016
2017         /*
2018          * Only need to add one NOT NULL check regardless of how many domains
2019          * in the stack request it.
2020          */
2021         if (notNull)
2022         {
2023                 DomainConstraintState *r = makeNode(DomainConstraintState);
2024
2025                 r->constrainttype = DOM_CONSTRAINT_NOTNULL;
2026                 r->name = pstrdup("NOT NULL");
2027                 r->check_expr = NULL;
2028
2029                 /* lcons to apply the nullness check FIRST */
2030                 result = lcons(r, result);
2031         }
2032
2033         return result;
2034 }
2035
2036 /*
2037  * Change the owner of a type.
2038  */
2039 void
2040 AlterTypeOwner(List *names, AclId newOwnerSysId)
2041 {
2042         TypeName   *typename;
2043         Oid                     typeOid;
2044         Relation        rel;
2045         HeapTuple       tup;
2046         Form_pg_type typTup;
2047
2048         /* Make a TypeName so we can use standard type lookup machinery */
2049         typename = makeNode(TypeName);
2050         typename->names = names;
2051         typename->typmod = -1;
2052         typename->arrayBounds = NIL;
2053
2054         /* Lock the type table */
2055         rel = heap_openr(TypeRelationName, RowExclusiveLock);
2056
2057         /* Use LookupTypeName here so that shell types can be processed (why?) */
2058         typeOid = LookupTypeName(typename);
2059         if (!OidIsValid(typeOid))
2060                 ereport(ERROR,
2061                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
2062                                  errmsg("type \"%s\" does not exist",
2063                                                 TypeNameToString(typename))));
2064
2065         tup = SearchSysCacheCopy(TYPEOID,
2066                                                          ObjectIdGetDatum(typeOid),
2067                                                          0, 0, 0);
2068         if (!HeapTupleIsValid(tup))
2069                 elog(ERROR, "cache lookup failed for type %u", typeOid);
2070         typTup = (Form_pg_type) GETSTRUCT(tup);
2071
2072         /*
2073          * If it's a composite type, we need to check that it really is a
2074          * free-standing composite type, and not a table's underlying type. We
2075          * want people to use ALTER TABLE not ALTER TYPE for that case.
2076          */
2077         if (typTup->typtype == 'c' && get_rel_relkind(typTup->typrelid) != 'c')
2078                 ereport(ERROR,
2079                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2080                                  errmsg("\"%s\" is a table's row type",
2081                                                 TypeNameToString(typename))));
2082
2083         /*
2084          * If the new owner is the same as the existing owner, consider the
2085          * command to have succeeded.  This is for dump restoration purposes.
2086          */
2087         if (typTup->typowner != newOwnerSysId)
2088         {
2089                 /* Otherwise, must be superuser to change object ownership */
2090                 if (!superuser())
2091                         ereport(ERROR,
2092                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2093                                          errmsg("must be superuser to change owner")));
2094
2095                 /*
2096                  * Modify the owner --- okay to scribble on typTup because it's a
2097                  * copy
2098                  */
2099                 typTup->typowner = newOwnerSysId;
2100
2101                 simple_heap_update(rel, &tup->t_self, tup);
2102
2103                 CatalogUpdateIndexes(rel, tup);
2104         }
2105
2106         /* Clean up */
2107         heap_close(rel, RowExclusiveLock);
2108 }