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