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