OSDN Git Service

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