]> granicus.if.org Git - postgresql/blob - src/backend/commands/typecmds.c
Partial code review for ALTER DOMAIN patch. Incorporates Rod Taylor's
[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-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.26 2003/01/04 00:46:08 tgl Exp $
12  *
13  * DESCRIPTION
14  *        The "DefineFoo" routines take the parse tree and pick out the
15  *        appropriate arguments/flags, passing the results to the
16  *        corresponding "FooDefine" routines (in src/catalog) that do
17  *        the actual catalog-munging.  These routines also verify permission
18  *        of the user to execute the command.
19  *
20  * NOTES
21  *        These things must be defined and committed in the following order:
22  *              "create function":
23  *                              input/output functions
24  *              "create type":
25  *                              type
26  *              "create operator":
27  *                              operators
28  *
29  *
30  *-------------------------------------------------------------------------
31  */
32 #include "postgres.h"
33
34 #include "access/heapam.h"
35 #include "access/genam.h"
36 #include "catalog/catname.h"
37 #include "catalog/dependency.h"
38 #include "catalog/heap.h"
39 #include "catalog/indexing.h"
40 #include "catalog/namespace.h"
41 #include "catalog/pg_constraint.h"
42 #include "catalog/pg_depend.h"
43 #include "catalog/pg_type.h"
44 #include "commands/defrem.h"
45 #include "commands/tablecmds.h"
46 #include "commands/typecmds.h"
47 #include "executor/executor.h"
48 #include "miscadmin.h"
49 #include "nodes/nodes.h"
50 #include "optimizer/clauses.h"
51 #include "optimizer/var.h"
52 #include "parser/parse_coerce.h"
53 #include "parser/parse_expr.h"
54 #include "parser/parse_func.h"
55 #include "parser/parse_relation.h"
56 #include "parser/parse_type.h"
57 #include "utils/acl.h"
58 #include "utils/builtins.h"
59 #include "utils/fmgroids.h"
60 #include "utils/lsyscache.h"
61 #include "utils/syscache.h"
62
63
64 /* result structure for get_rels_with_domain() */
65 typedef struct
66 {
67         Relation rel;                           /* opened and locked relation */
68         int             natts;                          /* number of attributes of interest */
69         int             *atts;                          /* attribute numbers */
70         /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
71 } RelToCheck;
72
73
74 static Oid      findTypeIOFunction(List *procname, Oid typeOid, bool isOutput);
75 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
76 static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
77 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
78                                                                  Oid baseTypeOid,
79                                                                  int typMod, Constraint *constr,
80                                                                  int *counter, char *domainName);
81
82
83 /*
84  * DefineType
85  *              Registers a new type.
86  */
87 void
88 DefineType(List *names, List *parameters)
89 {
90         char       *typeName;
91         Oid                     typeNamespace;
92         AclResult       aclresult;
93         int16           internalLength = -1;    /* int2 */
94         Oid                     elemType = InvalidOid;
95         List       *inputName = NIL;
96         List       *outputName = NIL;
97         char       *defaultValue = NULL;
98         bool            byValue = false;
99         char            delimiter = DEFAULT_TYPDELIM;
100         char            alignment = 'i';        /* default alignment */
101         char            storage = 'p';  /* default TOAST storage method */
102         Oid                     inputOid;
103         Oid                     outputOid;
104         char       *shadow_type;
105         List       *pl;
106         Oid                     typoid;
107         Oid                     resulttype;
108
109         /* Convert list of names to a name and namespace */
110         typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
111
112         /* Check we have creation rights in target namespace */
113         aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
114         if (aclresult != ACLCHECK_OK)
115                 aclcheck_error(aclresult, get_namespace_name(typeNamespace));
116
117         /*
118          * Type names must be one character shorter than other names, allowing
119          * room to create the corresponding array type name with prepended
120          * "_".
121          */
122         if (strlen(typeName) > (NAMEDATALEN - 2))
123                 elog(ERROR, "DefineType: type names must be %d characters or less",
124                          NAMEDATALEN - 2);
125
126         foreach(pl, parameters)
127         {
128                 DefElem    *defel = (DefElem *) lfirst(pl);
129
130                 if (strcasecmp(defel->defname, "internallength") == 0)
131                         internalLength = defGetTypeLength(defel);
132                 else if (strcasecmp(defel->defname, "externallength") == 0)
133                         ;                                       /* ignored -- remove after 7.3 */
134                 else if (strcasecmp(defel->defname, "input") == 0)
135                         inputName = defGetQualifiedName(defel);
136                 else if (strcasecmp(defel->defname, "output") == 0)
137                         outputName = defGetQualifiedName(defel);
138                 else if (strcasecmp(defel->defname, "send") == 0)
139                         ;                                       /* ignored -- remove after 7.3 */
140                 else if (strcasecmp(defel->defname, "receive") == 0)
141                         ;                                       /* ignored -- remove after 7.3 */
142                 else if (strcasecmp(defel->defname, "delimiter") == 0)
143                 {
144                         char       *p = defGetString(defel);
145
146                         delimiter = p[0];
147                 }
148                 else if (strcasecmp(defel->defname, "element") == 0)
149                 {
150                         elemType = typenameTypeId(defGetTypeName(defel));
151                         /* disallow arrays of pseudotypes */
152                         if (get_typtype(elemType) == 'p')
153                                 elog(ERROR, "Array element type cannot be %s",
154                                          format_type_be(elemType));
155                 }
156                 else if (strcasecmp(defel->defname, "default") == 0)
157                         defaultValue = defGetString(defel);
158                 else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
159                         byValue = true;
160                 else if (strcasecmp(defel->defname, "alignment") == 0)
161                 {
162                         char       *a = defGetString(defel);
163
164                         /*
165                          * Note: if argument was an unquoted identifier, parser will
166                          * have applied translations to it, so be prepared to
167                          * recognize translated type names as well as the nominal
168                          * form.
169                          */
170                         if (strcasecmp(a, "double") == 0 ||
171                                 strcasecmp(a, "float8") == 0 ||
172                                 strcasecmp(a, "pg_catalog.float8") == 0)
173                                 alignment = 'd';
174                         else if (strcasecmp(a, "int4") == 0 ||
175                                          strcasecmp(a, "pg_catalog.int4") == 0)
176                                 alignment = 'i';
177                         else if (strcasecmp(a, "int2") == 0 ||
178                                          strcasecmp(a, "pg_catalog.int2") == 0)
179                                 alignment = 's';
180                         else if (strcasecmp(a, "char") == 0 ||
181                                          strcasecmp(a, "pg_catalog.bpchar") == 0)
182                                 alignment = 'c';
183                         else
184                                 elog(ERROR, "DefineType: \"%s\" alignment not recognized",
185                                          a);
186                 }
187                 else if (strcasecmp(defel->defname, "storage") == 0)
188                 {
189                         char       *a = defGetString(defel);
190
191                         if (strcasecmp(a, "plain") == 0)
192                                 storage = 'p';
193                         else if (strcasecmp(a, "external") == 0)
194                                 storage = 'e';
195                         else if (strcasecmp(a, "extended") == 0)
196                                 storage = 'x';
197                         else if (strcasecmp(a, "main") == 0)
198                                 storage = 'm';
199                         else
200                                 elog(ERROR, "DefineType: \"%s\" storage not recognized",
201                                          a);
202                 }
203                 else
204                 {
205                         elog(WARNING, "DefineType: attribute \"%s\" not recognized",
206                                  defel->defname);
207                 }
208         }
209
210         /*
211          * make sure we have our required definitions
212          */
213         if (inputName == NIL)
214                 elog(ERROR, "Define: \"input\" unspecified");
215         if (outputName == NIL)
216                 elog(ERROR, "Define: \"output\" unspecified");
217
218         /*
219          * Look to see if type already exists (presumably as a shell; if not,
220          * TypeCreate will complain).  If it doesn't, create it as a shell,
221          * so that the OID is known for use in the I/O function definitions.
222          */
223         typoid = GetSysCacheOid(TYPENAMENSP,
224                                                         CStringGetDatum(typeName),
225                                                         ObjectIdGetDatum(typeNamespace),
226                                                         0, 0);
227         if (!OidIsValid(typoid))
228         {
229                 typoid = TypeShellMake(typeName, typeNamespace);
230                 /* Make new shell type visible for modification below */
231                 CommandCounterIncrement();
232         }
233
234         /*
235          * Convert I/O proc names to OIDs
236          */
237         inputOid = findTypeIOFunction(inputName, typoid, false);
238         outputOid = findTypeIOFunction(outputName, typoid, true);
239
240         /*
241          * Verify that I/O procs return the expected thing.  If we see OPAQUE,
242          * complain and change it to the correct type-safe choice.
243          */
244         resulttype = get_func_rettype(inputOid);
245         if (resulttype != typoid)
246         {
247                 if (resulttype == OPAQUEOID)
248                 {
249                         elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to %s",
250                                  NameListToString(inputName), typeName);
251                         SetFunctionReturnType(inputOid, typoid);
252                 }
253                 else
254                         elog(ERROR, "Type input function %s must return %s",
255                                  NameListToString(inputName), typeName);
256         }
257         resulttype = get_func_rettype(outputOid);
258         if (resulttype != CSTRINGOID)
259         {
260                 if (resulttype == OPAQUEOID)
261                 {
262                         elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to CSTRING",
263                                  NameListToString(outputName));
264                         SetFunctionReturnType(outputOid, CSTRINGOID);
265                 }
266                 else
267                         elog(ERROR, "Type output function %s must return cstring",
268                                  NameListToString(outputName));
269         }
270
271         /*
272          * now have TypeCreate do all the real work.
273          */
274         typoid =
275                 TypeCreate(typeName,    /* type name */
276                                    typeNamespace,               /* namespace */
277                                    InvalidOid,  /* preassigned type oid (not done here) */
278                                    InvalidOid,  /* relation oid (n/a here) */
279                                    0,                   /* relation kind (ditto) */
280                                    internalLength,              /* internal size */
281                                    'b',                 /* type-type (base type) */
282                                    delimiter,   /* array element delimiter */
283                                    inputOid,    /* input procedure */
284                                    outputOid,   /* output procedure */
285                                    elemType,    /* element type ID */
286                                    InvalidOid,  /* base type ID (only for domains) */
287                                    defaultValue,        /* default type value */
288                                    NULL,                /* no binary form available */
289                                    byValue,             /* passed by value */
290                                    alignment,   /* required alignment */
291                                    storage,             /* TOAST strategy */
292                                    -1,                  /* typMod (Domains only) */
293                                    0,                   /* Array Dimensions of typbasetype */
294                                    false);              /* Type NOT NULL */
295
296         /*
297          * When we create a base type (as opposed to a complex type) we need
298          * to have an array entry for it in pg_type as well.
299          */
300         shadow_type = makeArrayTypeName(typeName);
301
302         /* alignment must be 'i' or 'd' for arrays */
303         alignment = (alignment == 'd') ? 'd' : 'i';
304
305         TypeCreate(shadow_type,         /* type name */
306                            typeNamespace,       /* namespace */
307                            InvalidOid,          /* preassigned type oid (not done here) */
308                            InvalidOid,          /* relation oid (n/a here) */
309                            0,                           /* relation kind (ditto) */
310                            -1,                          /* internal size */
311                            'b',                         /* type-type (base type) */
312                            DEFAULT_TYPDELIM,    /* array element delimiter */
313                            F_ARRAY_IN,          /* input procedure */
314                            F_ARRAY_OUT,         /* output procedure */
315                            typoid,                      /* element type ID */
316                            InvalidOid,          /* base type ID */
317                            NULL,                        /* never a default type value */
318                            NULL,                        /* binary default isn't sent either */
319                            false,                       /* never passed by value */
320                            alignment,           /* see above */
321                            'x',                         /* ARRAY is always toastable */
322                            -1,                          /* typMod (Domains only) */
323                            0,                           /* Array dimensions of typbasetype */
324                            false);                      /* Type NOT NULL */
325
326         pfree(shadow_type);
327 }
328
329
330 /*
331  *      RemoveType
332  *              Removes a datatype.
333  */
334 void
335 RemoveType(List *names, DropBehavior behavior)
336 {
337         TypeName   *typename;
338         Oid                     typeoid;
339         HeapTuple       tup;
340         ObjectAddress object;
341
342         /* Make a TypeName so we can use standard type lookup machinery */
343         typename = makeNode(TypeName);
344         typename->names = names;
345         typename->typmod = -1;
346         typename->arrayBounds = NIL;
347
348         /* Use LookupTypeName here so that shell types can be removed. */
349         typeoid = LookupTypeName(typename);
350         if (!OidIsValid(typeoid))
351                 elog(ERROR, "Type \"%s\" does not exist",
352                          TypeNameToString(typename));
353
354         tup = SearchSysCache(TYPEOID,
355                                                  ObjectIdGetDatum(typeoid),
356                                                  0, 0, 0);
357         if (!HeapTupleIsValid(tup))
358                 elog(ERROR, "Type \"%s\" does not exist",
359                          TypeNameToString(typename));
360
361         /* Permission check: must own type or its namespace */
362         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
363                 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
364                                                                  GetUserId()))
365                 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
366
367         ReleaseSysCache(tup);
368
369         /*
370          * Do the deletion
371          */
372         object.classId = RelOid_pg_type;
373         object.objectId = typeoid;
374         object.objectSubId = 0;
375
376         performDeletion(&object, behavior);
377 }
378
379
380 /*
381  * Guts of type deletion.
382  */
383 void
384 RemoveTypeById(Oid typeOid)
385 {
386         Relation        relation;
387         HeapTuple       tup;
388
389         relation = heap_openr(TypeRelationName, RowExclusiveLock);
390
391         tup = SearchSysCache(TYPEOID,
392                                                  ObjectIdGetDatum(typeOid),
393                                                  0, 0, 0);
394         if (!HeapTupleIsValid(tup))
395                 elog(ERROR, "RemoveTypeById: type %u not found",
396                          typeOid);
397
398         simple_heap_delete(relation, &tup->t_self);
399
400         ReleaseSysCache(tup);
401
402         heap_close(relation, RowExclusiveLock);
403 }
404
405
406 /*
407  * DefineDomain
408  *              Registers a new domain.
409  */
410 void
411 DefineDomain(CreateDomainStmt *stmt)
412 {
413         char       *domainName;
414         Oid                     domainNamespace;
415         AclResult       aclresult;
416         int16           internalLength;
417         Oid                     inputProcedure;
418         Oid                     outputProcedure;
419         bool            byValue;
420         char            delimiter;
421         char            alignment;
422         char            storage;
423         char            typtype;
424         Datum           datum;
425         bool            isnull;
426         Node       *defaultExpr = NULL;
427         char       *defaultValue = NULL;
428         char       *defaultValueBin = NULL;
429         bool            typNotNull = false;
430         bool            nullDefined = false;
431         Oid                     basetypelem;
432         int32           typNDims = length(stmt->typename->arrayBounds);
433         HeapTuple       typeTup;
434         List       *schema = stmt->constraints;
435         List       *listptr;
436         Oid                     basetypeoid;
437         Oid                     domainoid;
438         Form_pg_type    baseType;
439         int                     counter = 0;
440
441         /* Convert list of names to a name and namespace */
442         domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
443                                                                                                                 &domainName);
444
445         /* Check we have creation rights in target namespace */
446         aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
447                                                                           ACL_CREATE);
448         if (aclresult != ACLCHECK_OK)
449                 aclcheck_error(aclresult, get_namespace_name(domainNamespace));
450
451         /*
452          * Domainnames, unlike typenames don't need to account for the '_'
453          * prefix.      So they can be one character longer.
454          */
455         if (strlen(domainName) > (NAMEDATALEN - 1))
456                 elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
457                          NAMEDATALEN - 1);
458
459         /*
460          * Look up the base type.
461          */
462         typeTup = typenameType(stmt->typename);
463
464         baseType = (Form_pg_type) GETSTRUCT(typeTup);
465         basetypeoid = HeapTupleGetOid(typeTup);
466
467         /*
468          * Base type must be a plain base type.  Domains over pseudo types
469          * would create a security hole.  Domains of domains might be made to
470          * work in the future, but not today.  Ditto for domains over complex
471          * types.
472          */
473         typtype = baseType->typtype;
474         if (typtype != 'b')
475                 elog(ERROR, "DefineDomain: %s is not a basetype",
476                          TypeNameToString(stmt->typename));
477
478         /* passed by value */
479         byValue = baseType->typbyval;
480
481         /* Required Alignment */
482         alignment = baseType->typalign;
483
484         /* TOAST Strategy */
485         storage = baseType->typstorage;
486
487         /* Storage Length */
488         internalLength = baseType->typlen;
489
490         /* Array element Delimiter */
491         delimiter = baseType->typdelim;
492
493         /* I/O Functions */
494         inputProcedure = baseType->typinput;
495         outputProcedure = baseType->typoutput;
496
497         /* Inherited default value */
498         datum = SysCacheGetAttr(TYPEOID, typeTup,
499                                                         Anum_pg_type_typdefault, &isnull);
500         if (!isnull)
501                 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
502
503         /* Inherited default binary value */
504         datum = SysCacheGetAttr(TYPEOID, typeTup,
505                                                         Anum_pg_type_typdefaultbin, &isnull);
506         if (!isnull)
507                 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
508
509         /*
510          * Pull out the typelem name of the parent OID.
511          *
512          * This is what enables us to make a domain of an array
513          */
514         basetypelem = baseType->typelem;
515
516         /*
517          * Run through constraints manually to avoid the additional
518          * processing conducted by DefineRelation() and friends.
519          */
520         foreach(listptr, schema)
521         {
522                 Node       *newConstraint = lfirst(listptr);
523                 Constraint *colDef;
524                 ParseState *pstate;
525
526                 /* Check for unsupported constraint types */
527                 if (IsA(newConstraint, FkConstraint))
528                         elog(ERROR, "CREATE DOMAIN / FOREIGN KEY constraints not supported");
529
530                 /* this case should not happen */
531                 if (!IsA(newConstraint, Constraint))
532                         elog(ERROR, "DefineDomain: unexpected constraint node type");
533
534                 colDef = (Constraint *) newConstraint;
535
536                 switch (colDef->contype)
537                 {
538                         case CONSTR_DEFAULT:
539                                 /*
540                                  * The inherited default value may be overridden by the
541                                  * user with the DEFAULT <expr> statement.
542                                  */
543                                 if (defaultExpr)
544                                         elog(ERROR, "CREATE DOMAIN has multiple DEFAULT expressions");
545                                 /* Create a dummy ParseState for transformExpr */
546                                 pstate = make_parsestate(NULL);
547
548                                 /*
549                                  * Cook the colDef->raw_expr into an expression. Note:
550                                  * Name is strictly for error message
551                                  */
552                                 defaultExpr = cookDefault(pstate, colDef->raw_expr,
553                                                                                   basetypeoid,
554                                                                                   stmt->typename->typmod,
555                                                                                   domainName);
556
557                                 /*
558                                  * Expression must be stored as a nodeToString result, but
559                                  * we also require a valid textual representation (mainly
560                                  * to make life easier for pg_dump).
561                                  */
562                                 defaultValue = deparse_expression(defaultExpr,
563                                                                                   deparse_context_for(domainName,
564                                                                                                                           InvalidOid),
565                                                                                                   false, false);
566                                 defaultValueBin = nodeToString(defaultExpr);
567                                 break;
568
569                         case CONSTR_NOTNULL:
570                                 if (nullDefined && !typNotNull)
571                                         elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
572                                 typNotNull = true;
573                                 nullDefined = true;
574                                 break;
575
576                         case CONSTR_NULL:
577                                 if (nullDefined && typNotNull)
578                                         elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
579                                 typNotNull = false;
580                                 nullDefined = true;
581                                 break;
582
583                         case CONSTR_CHECK:
584                                 /*
585                                  * Check constraints are handled after domain creation, as they
586                                  * require the Oid of the domain
587                                  */
588                                 break;
589
590                                 /*
591                                  * All else are error cases
592                                  */
593                         case CONSTR_UNIQUE:
594                                 elog(ERROR, "CREATE DOMAIN / UNIQUE not supported");
595                                 break;
596
597                         case CONSTR_PRIMARY:
598                                 elog(ERROR, "CREATE DOMAIN / PRIMARY KEY not supported");
599                                 break;
600
601                         case CONSTR_ATTR_DEFERRABLE:
602                         case CONSTR_ATTR_NOT_DEFERRABLE:
603                         case CONSTR_ATTR_DEFERRED:
604                         case CONSTR_ATTR_IMMEDIATE:
605                                 elog(ERROR, "CREATE DOMAIN: DEFERRABLE, NON DEFERRABLE, DEFERRED"
606                                                         " and IMMEDIATE not supported");
607                                 break;
608
609                         default:
610                                 elog(ERROR, "DefineDomain: unrecognized constraint subtype");
611                                 break;
612                 }
613         }
614
615         /*
616          * Have TypeCreate do all the real work.
617          */
618         domainoid =
619                 TypeCreate(domainName,  /* type name */
620                                    domainNamespace,             /* namespace */
621                                    InvalidOid,  /* preassigned type oid (none here) */
622                                    InvalidOid,  /* relation oid (n/a here) */
623                                    0,                   /* relation kind (ditto) */
624                                    internalLength,              /* internal size */
625                                    'd',                 /* type-type (domain type) */
626                                    delimiter,   /* array element delimiter */
627                                    inputProcedure,              /* input procedure */
628                                    outputProcedure,             /* output procedure */
629                                    basetypelem, /* element type ID */
630                                    basetypeoid, /* base type ID */
631                                    defaultValue,        /* default type value (text) */
632                                    defaultValueBin,             /* default type value (binary) */
633                                    byValue,                             /* passed by value */
634                                    alignment,                   /* required alignment */
635                                    storage,                             /* TOAST strategy */
636                                    stmt->typename->typmod, /* typeMod value */
637                                    typNDims,                    /* Array dimensions for base type */
638                                    typNotNull);                 /* Type NOT NULL */
639
640         /*
641          * Process constraints which refer to the domain ID returned by TypeCreate
642          */
643         foreach(listptr, schema)
644         {
645                 Constraint *constr = lfirst(listptr);
646
647                 /* it must be a Constraint, per check above */
648
649                 switch (constr->contype)
650                 {
651                         case CONSTR_CHECK:
652                                 domainAddConstraint(domainoid, domainNamespace,
653                                                                         basetypeoid, stmt->typename->typmod,
654                                                                         constr, &counter, domainName);
655                                 break;
656
657                         /* Other constraint types were fully processed above */
658
659                         default:
660                                 break;
661                 }
662         }
663
664         /*
665          * Now we can clean up.
666          */
667         ReleaseSysCache(typeTup);
668 }
669
670
671 /*
672  *      RemoveDomain
673  *              Removes a domain.
674  *
675  * This is identical to RemoveType except we insist it be a domain.
676  */
677 void
678 RemoveDomain(List *names, DropBehavior behavior)
679 {
680         TypeName   *typename;
681         Oid                     typeoid;
682         HeapTuple       tup;
683         char            typtype;
684         ObjectAddress object;
685
686         /* Make a TypeName so we can use standard type lookup machinery */
687         typename = makeNode(TypeName);
688         typename->names = names;
689         typename->typmod = -1;
690         typename->arrayBounds = NIL;
691
692         /* Use LookupTypeName here so that shell types can be removed. */
693         typeoid = LookupTypeName(typename);
694         if (!OidIsValid(typeoid))
695                 elog(ERROR, "Type \"%s\" does not exist",
696                          TypeNameToString(typename));
697
698         tup = SearchSysCache(TYPEOID,
699                                                  ObjectIdGetDatum(typeoid),
700                                                  0, 0, 0);
701         if (!HeapTupleIsValid(tup))
702                 elog(ERROR, "RemoveDomain: type \"%s\" does not exist",
703                          TypeNameToString(typename));
704
705         /* Permission check: must own type or its namespace */
706         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
707                 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
708                                                                  GetUserId()))
709                 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
710
711         /* Check that this is actually a domain */
712         typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
713
714         if (typtype != 'd')
715                 elog(ERROR, "%s is not a domain",
716                          TypeNameToString(typename));
717
718         ReleaseSysCache(tup);
719
720         /*
721          * Do the deletion
722          */
723         object.classId = RelOid_pg_type;
724         object.objectId = typeoid;
725         object.objectSubId = 0;
726
727         performDeletion(&object, behavior);
728 }
729
730
731 /*
732  * Find a suitable I/O function for a type.
733  *
734  * typeOid is the type's OID (which will already exist, if only as a shell
735  * type).
736  */
737 static Oid
738 findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
739 {
740         Oid                     argList[FUNC_MAX_ARGS];
741         Oid                     procOid;
742
743         if (isOutput)
744         {
745                 /*
746                  * Output functions can take a single argument of the type, or two
747                  * arguments (data value, element OID).
748                  *
749                  * For backwards compatibility we allow OPAQUE in place of the actual
750                  * type name; if we see this, we issue a NOTICE and fix up the
751                  * pg_proc entry.
752                  */
753                 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
754
755                 argList[0] = typeOid;
756
757                 procOid = LookupFuncName(procname, 1, argList);
758                 if (OidIsValid(procOid))
759                         return procOid;
760
761                 argList[1] = OIDOID;
762
763                 procOid = LookupFuncName(procname, 2, argList);
764                 if (OidIsValid(procOid))
765                         return procOid;
766
767                 /* No luck, try it with OPAQUE */
768                 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
769
770                 argList[0] = OPAQUEOID;
771
772                 procOid = LookupFuncName(procname, 1, argList);
773
774                 if (!OidIsValid(procOid))
775                 {
776                         argList[1] = OIDOID;
777
778                         procOid = LookupFuncName(procname, 2, argList);
779                 }
780
781                 if (OidIsValid(procOid))
782                 {
783                         /* Found, but must complain and fix the pg_proc entry */
784                         elog(NOTICE, "TypeCreate: changing argument type of function %s from OPAQUE to %s",
785                                  NameListToString(procname), format_type_be(typeOid));
786                         SetFunctionArgType(procOid, 0, typeOid);
787                         /*
788                          * Need CommandCounterIncrement since DefineType will likely
789                          * try to alter the pg_proc tuple again.
790                          */
791                         CommandCounterIncrement();
792
793                         return procOid;
794                 }
795
796                 /* Use type name, not OPAQUE, in the failure message. */
797                 argList[0] = typeOid;
798
799                 func_error("TypeCreate", procname, 1, argList, NULL);
800         }
801         else
802         {
803                 /*
804                  * Input functions can take a single argument of type CSTRING, or
805                  * three arguments (string, element OID, typmod).
806                  *
807                  * For backwards compatibility we allow OPAQUE in place of CSTRING;
808                  * if we see this, we issue a NOTICE and fix up the pg_proc entry.
809                  */
810                 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
811
812                 argList[0] = CSTRINGOID;
813
814                 procOid = LookupFuncName(procname, 1, argList);
815                 if (OidIsValid(procOid))
816                         return procOid;
817
818                 argList[1] = OIDOID;
819                 argList[2] = INT4OID;
820
821                 procOid = LookupFuncName(procname, 3, argList);
822                 if (OidIsValid(procOid))
823                         return procOid;
824
825                 /* No luck, try it with OPAQUE */
826                 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
827
828                 argList[0] = OPAQUEOID;
829
830                 procOid = LookupFuncName(procname, 1, argList);
831
832                 if (!OidIsValid(procOid))
833                 {
834                         argList[1] = OIDOID;
835                         argList[2] = INT4OID;
836
837                         procOid = LookupFuncName(procname, 3, argList);
838                 }
839
840                 if (OidIsValid(procOid))
841                 {
842                         /* Found, but must complain and fix the pg_proc entry */
843                         elog(NOTICE, "TypeCreate: changing argument type of function %s "
844                                  "from OPAQUE to CSTRING",
845                                  NameListToString(procname));
846                         SetFunctionArgType(procOid, 0, CSTRINGOID);
847                         /*
848                          * Need CommandCounterIncrement since DefineType will likely
849                          * try to alter the pg_proc tuple again.
850                          */
851                         CommandCounterIncrement();
852
853                         return procOid;
854                 }
855
856                 /* Use CSTRING (preferred) in the error message */
857                 argList[0] = CSTRINGOID;
858
859                 func_error("TypeCreate", procname, 1, argList, NULL);
860         }
861
862         return InvalidOid;                      /* keep compiler quiet */
863 }
864
865
866 /*-------------------------------------------------------------------
867  * DefineCompositeType
868  *
869  * Create a Composite Type relation.
870  * `DefineRelation' does all the work, we just provide the correct
871  * arguments!
872  *
873  * If the relation already exists, then 'DefineRelation' will abort
874  * the xact...
875  *
876  * DefineCompositeType returns relid for use when creating
877  * an implicit composite type during function creation
878  *-------------------------------------------------------------------
879  */
880 Oid
881 DefineCompositeType(const RangeVar *typevar, List *coldeflist)
882 {
883         CreateStmt *createStmt = makeNode(CreateStmt);
884
885         if (coldeflist == NIL)
886                 elog(ERROR, "attempted to define composite type relation with"
887                          " no attrs");
888
889         /*
890          * now create the parameters for keys/inheritance etc. All of them are
891          * nil...
892          */
893         createStmt->relation = (RangeVar *) typevar;
894         createStmt->tableElts = coldeflist;
895         createStmt->inhRelations = NIL;
896         createStmt->constraints = NIL;
897         createStmt->hasoids = false;
898         createStmt->oncommit = ONCOMMIT_NOOP;
899
900         /*
901          * finally create the relation...
902          */
903         return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
904 }
905
906 /*
907  * AlterDomainDefault
908  *
909  * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements. 
910  */
911 void
912 AlterDomainDefault(List *names, Node *defaultRaw)
913 {
914         TypeName   *typename;
915         Oid                     domainoid;
916         HeapTuple       tup;
917         ParseState *pstate;
918         Relation        rel;
919         char       *defaultValue;
920         Node       *defaultExpr = NULL; /* NULL if no default specified */
921         Datum           new_record[Natts_pg_type];
922         char            new_record_nulls[Natts_pg_type];
923         char            new_record_repl[Natts_pg_type];
924         HeapTuple       newtuple;
925         Form_pg_type    typTup;
926
927         /* Make a TypeName so we can use standard type lookup machinery */
928         typename = makeNode(TypeName);
929         typename->names = names;
930         typename->typmod = -1;
931         typename->arrayBounds = NIL;
932
933         /* Lock the domain in the type table */
934         rel = heap_openr(TypeRelationName, RowExclusiveLock);
935
936         /* Use LookupTypeName here so that shell types can be removed. */
937         domainoid = LookupTypeName(typename);
938         if (!OidIsValid(domainoid))
939                 elog(ERROR, "Type \"%s\" does not exist",
940                          TypeNameToString(typename));
941
942         tup = SearchSysCacheCopy(TYPEOID,
943                                                          ObjectIdGetDatum(domainoid),
944                                                          0, 0, 0);
945
946         if (!HeapTupleIsValid(tup))
947                 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
948                          TypeNameToString(typename));
949
950         /* Doesn't return if user isn't allowed to alter the domain */ 
951         domainOwnerCheck(tup, typename);
952
953         /* Setup new tuple */
954         MemSet(new_record, (Datum) 0, sizeof(new_record));
955         MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
956         MemSet(new_record_repl, ' ', sizeof(new_record_repl));
957
958         /* Useful later */
959         typTup = (Form_pg_type) GETSTRUCT(tup);
960
961         /* Store the new default, if null then skip this step */
962         if (defaultRaw)
963         {
964                 /* Create a dummy ParseState for transformExpr */
965                 pstate = make_parsestate(NULL);
966                 /*
967                  * Cook the colDef->raw_expr into an expression. Note:
968                  * Name is strictly for error message
969                  */
970                 defaultExpr = cookDefault(pstate, defaultRaw,
971                                                                   typTup->typbasetype,
972                                                                   typTup->typtypmod,
973                                                                   NameStr(typTup->typname));
974
975                 /*
976                  * Expression must be stored as a nodeToString result, but
977                  * we also require a valid textual representation (mainly
978                  * to make life easier for pg_dump).
979                  */
980                 defaultValue = deparse_expression(defaultExpr,
981                                                                   deparse_context_for(NameStr(typTup->typname),
982                                                                                                           InvalidOid),
983                                                                                   false, false);
984                 /*
985                  * Form an updated tuple with the new default and write it back.
986                  */
987                 new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
988                                                                                                                 CStringGetDatum(
989                                                                                                                         nodeToString(defaultExpr)));
990
991                 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
992                 new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
993                                                                                                         CStringGetDatum(defaultValue));
994                 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
995         }
996         else /* Default is NULL, drop it */
997         {
998                 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
999                 new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
1000                 new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
1001                 new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
1002         }
1003
1004         newtuple = heap_modifytuple(tup, rel,
1005                                                                 new_record, new_record_nulls, new_record_repl);
1006
1007         simple_heap_update(rel, &tup->t_self, newtuple);
1008
1009         CatalogUpdateIndexes(rel, newtuple);
1010
1011         /* Rebuild dependencies */
1012         GenerateTypeDependencies(typTup->typnamespace,
1013                                                          domainoid,
1014                                                          typTup->typrelid,
1015                                                          InvalidOid,
1016                                                          typTup->typinput,
1017                                                          typTup->typoutput,
1018                                                          typTup->typelem,
1019                                                          typTup->typbasetype,
1020                                                          nodeToString(defaultExpr),
1021                                                          true); /* Rebuild is true */
1022
1023         /* Clean up */
1024         heap_close(rel, NoLock);
1025         heap_freetuple(newtuple);
1026 };
1027
1028 /*
1029  * AlterDomainNotNull
1030  *
1031  * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements. 
1032  */
1033 void
1034 AlterDomainNotNull(List *names, bool notNull)
1035 {
1036         TypeName   *typename;
1037         Oid                     domainoid;
1038         Relation        typrel;
1039         HeapTuple       tup;
1040         Form_pg_type    typTup;
1041
1042         /* Make a TypeName so we can use standard type lookup machinery */
1043         typename = makeNode(TypeName);
1044         typename->names = names;
1045         typename->typmod = -1;
1046         typename->arrayBounds = NIL;
1047
1048         /* Lock the type table */
1049         typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1050
1051         /* Use LookupTypeName here so that shell types can be found (why?). */
1052         domainoid = LookupTypeName(typename);
1053         if (!OidIsValid(domainoid))
1054                 elog(ERROR, "Type \"%s\" does not exist",
1055                          TypeNameToString(typename));
1056
1057         tup = SearchSysCacheCopy(TYPEOID,
1058                                                          ObjectIdGetDatum(domainoid),
1059                                                          0, 0, 0);
1060         if (!HeapTupleIsValid(tup))
1061                 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
1062                          TypeNameToString(typename));
1063         typTup = (Form_pg_type) GETSTRUCT(tup);
1064
1065         /* Doesn't return if user isn't allowed to alter the domain */ 
1066         domainOwnerCheck(tup, typename);
1067
1068         /* Is the domain already set to the desired constraint? */
1069         if (typTup->typnotnull == notNull)
1070         {
1071                 elog(NOTICE, "AlterDomain: %s is already set to %s",
1072                          TypeNameToString(typename),
1073                          notNull ? "NOT NULL" : "NULL");
1074                 heap_close(typrel, RowExclusiveLock);
1075                 return;
1076         }
1077
1078         /* Adding a NOT NULL constraint requires checking existing columns */
1079         if (notNull)
1080         {
1081                 List   *rels;
1082                 List   *rt;
1083
1084                 /* Fetch relation list with attributes based on this domain */
1085                 /* ShareLock is sufficient to prevent concurrent data changes */
1086
1087                 rels = get_rels_with_domain(domainoid, ShareLock);
1088
1089                 foreach (rt, rels)
1090                 {
1091                         RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1092                         Relation        testrel = rtc->rel;
1093                         TupleDesc       tupdesc = RelationGetDescr(testrel);
1094                         HeapScanDesc scan;
1095                         HeapTuple       tuple;
1096
1097                         /* Scan all tuples in this relation */
1098                         scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1099                         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1100                         {
1101                                 int             i;
1102
1103                                 /* Test attributes that are of the domain */
1104                                 for (i = 0; i < rtc->natts; i++)
1105                                 {
1106                                         int             attnum = rtc->atts[i];
1107                                         Datum   d;
1108                                         bool    isNull;
1109
1110                                         d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1111
1112                                         if (isNull)
1113                                                 elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains NULL values",
1114                                                          RelationGetRelationName(testrel),
1115                                                          NameStr(tupdesc->attrs[attnum - 1]->attname));
1116                                 }
1117                         }
1118                         heap_endscan(scan);
1119
1120                         /* Close each rel after processing, but keep lock */
1121                         heap_close(testrel, NoLock);
1122                 }
1123         }
1124
1125         /*
1126          * Okay to update pg_type row.  We can scribble on typTup because it's
1127          * a copy.
1128          */
1129         typTup->typnotnull = notNull;
1130
1131         simple_heap_update(typrel, &tup->t_self, tup);
1132
1133         CatalogUpdateIndexes(typrel, tup);
1134
1135         /* Clean up */
1136         heap_freetuple(tup);
1137         heap_close(typrel, RowExclusiveLock);
1138 }
1139
1140 /*
1141  * AlterDomainDropConstraint
1142  *
1143  * Implements the ALTER DOMAIN DROP CONSTRAINT statement
1144  */
1145 void
1146 AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior)
1147 {
1148         TypeName   *typename;
1149         Oid                     domainoid;
1150         HeapTuple       tup;
1151         Relation        rel;
1152         Form_pg_type    typTup;
1153         Relation        conrel;
1154         SysScanDesc conscan;
1155         ScanKeyData key[1];
1156         HeapTuple       contup;
1157
1158         /* Make a TypeName so we can use standard type lookup machinery */
1159         typename = makeNode(TypeName);
1160         typename->names = names;
1161         typename->typmod = -1;
1162         typename->arrayBounds = NIL;
1163
1164         /* Lock the type table */
1165         rel = heap_openr(TypeRelationName, RowExclusiveLock);
1166
1167         /* Use LookupTypeName here so that shell types can be removed. */
1168         domainoid = LookupTypeName(typename);
1169         if (!OidIsValid(domainoid))
1170                 elog(ERROR, "Type \"%s\" does not exist",
1171                          TypeNameToString(typename));
1172
1173         tup = SearchSysCacheCopy(TYPEOID,
1174                                                          ObjectIdGetDatum(domainoid),
1175                                                          0, 0, 0);
1176
1177         if (!HeapTupleIsValid(tup))
1178                 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
1179                          TypeNameToString(typename));
1180
1181         /* Doesn't return if user isn't allowed to alter the domain */ 
1182         domainOwnerCheck(tup, typename);
1183
1184         /* Grab an appropriate lock on the pg_constraint relation */
1185         conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
1186
1187         /* Use the index to scan only constraints of the target relation */
1188         ScanKeyEntryInitialize(&key[0], 0x0,
1189                                                    Anum_pg_constraint_contypid, F_OIDEQ,
1190                                                    ObjectIdGetDatum(HeapTupleGetOid(tup)));
1191
1192         conscan = systable_beginscan(conrel, ConstraintTypidIndex, true,
1193                                                                  SnapshotNow, 1, key);
1194
1195         typTup = (Form_pg_type) GETSTRUCT(tup);
1196
1197         /*
1198          * Scan over the result set, removing any matching entries.
1199          */
1200         while ((contup = systable_getnext(conscan)) != NULL)
1201         {
1202                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
1203
1204                 if (strcmp(NameStr(con->conname), constrName) == 0)
1205                 {
1206                         ObjectAddress conobj;
1207
1208                         conobj.classId = RelationGetRelid(conrel);
1209                         conobj.objectId = HeapTupleGetOid(contup);
1210                         conobj.objectSubId = 0;
1211
1212                         performDeletion(&conobj, behavior);
1213                 }
1214         }
1215         /* Clean up after the scan */
1216         systable_endscan(conscan);
1217         heap_close(conrel, RowExclusiveLock);
1218
1219         heap_close(rel, NoLock);
1220 };
1221
1222 /*
1223  * AlterDomainAddConstraint
1224  *
1225  * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
1226  */
1227 void
1228 AlterDomainAddConstraint(List *names, Node *newConstraint)
1229 {
1230         TypeName   *typename;
1231         Oid                     domainoid;
1232         Relation        typrel;
1233         HeapTuple       tup;
1234         Form_pg_type    typTup;
1235         List   *rels;
1236         List   *rt;
1237         EState *estate;
1238         ExprContext *econtext;
1239         char   *ccbin;
1240         Expr   *expr;
1241         ExprState *exprstate;
1242         int             counter = 0;
1243         Constraint *constr;
1244
1245         /* Make a TypeName so we can use standard type lookup machinery */
1246         typename = makeNode(TypeName);
1247         typename->names = names;
1248         typename->typmod = -1;
1249         typename->arrayBounds = NIL;
1250
1251         /* Lock the type table */
1252         typrel = heap_openr(TypeRelationName, RowExclusiveLock);
1253
1254         /* Use LookupTypeName here so that shell types can be found (why?). */
1255         domainoid = LookupTypeName(typename);
1256         if (!OidIsValid(domainoid))
1257                 elog(ERROR, "Type \"%s\" does not exist",
1258                          TypeNameToString(typename));
1259
1260         tup = SearchSysCacheCopy(TYPEOID,
1261                                                          ObjectIdGetDatum(domainoid),
1262                                                          0, 0, 0);
1263         if (!HeapTupleIsValid(tup))
1264                 elog(ERROR, "AlterDomain: type \"%s\" does not exist",
1265                          TypeNameToString(typename));
1266         typTup = (Form_pg_type) GETSTRUCT(tup);
1267
1268         /* Doesn't return if user isn't allowed to alter the domain */ 
1269         domainOwnerCheck(tup, typename);
1270
1271         /* Check for unsupported constraint types */
1272         if (IsA(newConstraint, FkConstraint))
1273                 elog(ERROR, "ALTER DOMAIN / FOREIGN KEY constraints not supported");
1274
1275         /* this case should not happen */
1276         if (!IsA(newConstraint, Constraint))
1277                 elog(ERROR, "AlterDomainAddConstraint: unexpected constraint node type");
1278
1279         constr = (Constraint *) newConstraint;
1280
1281         switch (constr->contype)
1282         {
1283                 case CONSTR_DEFAULT:
1284                         elog(ERROR, "Use ALTER DOMAIN .. SET DEFAULT instead");
1285                         break;
1286
1287                 case CONSTR_NOTNULL:
1288                 case CONSTR_NULL:
1289                         elog(ERROR, "Use ALTER DOMAIN .. [ SET | DROP ] NOT NULL instead");
1290                         break;
1291
1292                 case CONSTR_CHECK:
1293                         /* processed below */
1294                         break;
1295
1296                 case CONSTR_UNIQUE:
1297                         elog(ERROR, "ALTER DOMAIN / UNIQUE indexes not supported");
1298                         break;
1299
1300                 case CONSTR_PRIMARY:
1301                         elog(ERROR, "ALTER DOMAIN / PRIMARY KEY indexes not supported");
1302                         break;
1303
1304                 case CONSTR_ATTR_DEFERRABLE:
1305                 case CONSTR_ATTR_NOT_DEFERRABLE:
1306                 case CONSTR_ATTR_DEFERRED:
1307                 case CONSTR_ATTR_IMMEDIATE:
1308                         elog(ERROR, "ALTER DOMAIN: DEFERRABLE, NON DEFERRABLE, DEFERRED"
1309                                  " and IMMEDIATE not supported");
1310                         break;
1311
1312                 default:
1313                         elog(ERROR, "AlterDomainAddConstraint: unrecognized constraint node type");
1314                         break;
1315         }
1316
1317         /*
1318          * Since all other constraint types throw errors, this must be
1319          * a check constraint.  First, process the constraint expression
1320          * and add an entry to pg_constraint.
1321          */
1322
1323         ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
1324                                                                 typTup->typbasetype, typTup->typtypmod,
1325                                                                 constr, &counter, NameStr(typTup->typname));
1326
1327         /*
1328          * Test all values stored in the attributes based on the domain
1329          * the constraint is being added to.
1330          */
1331         expr = (Expr *) stringToNode(ccbin);
1332
1333         /* Need an EState to run ExecEvalExpr */
1334         estate = CreateExecutorState();
1335         econtext = GetPerTupleExprContext(estate);
1336
1337         /* build execution state for expr */
1338         exprstate = ExecPrepareExpr(expr, estate);
1339
1340         /* Fetch relation list with attributes based on this domain */
1341         /* ShareLock is sufficient to prevent concurrent data changes */
1342
1343         rels = get_rels_with_domain(domainoid, ShareLock);
1344
1345         foreach (rt, rels)
1346         {
1347                 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
1348                 Relation        testrel = rtc->rel;
1349                 TupleDesc       tupdesc = RelationGetDescr(testrel);
1350                 HeapScanDesc scan;
1351                 HeapTuple       tuple;
1352
1353                 /* Scan all tuples in this relation */
1354                 scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
1355                 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1356                 {
1357                         int             i;
1358
1359                         /* Test attributes that are of the domain */
1360                         for (i = 0; i < rtc->natts; i++)
1361                         {
1362                                 int             attnum = rtc->atts[i];
1363                                 Datum   d;
1364                                 bool    isNull;
1365                                 Datum   conResult;
1366
1367                                 d = heap_getattr(tuple, attnum, tupdesc, &isNull);
1368
1369                                 econtext->domainValue_datum = d;
1370                                 econtext->domainValue_isNull = isNull;
1371
1372                                 conResult = ExecEvalExprSwitchContext(exprstate,
1373                                                                                                           econtext,
1374                                                                                                           &isNull, NULL);
1375
1376                                 if (!isNull && !DatumGetBool(conResult))
1377                                         elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains values that fail the new constraint",
1378                                                  RelationGetRelationName(testrel),
1379                                                  NameStr(tupdesc->attrs[attnum - 1]->attname));
1380                         }
1381
1382                         ResetExprContext(econtext);
1383                 }
1384                 heap_endscan(scan);
1385
1386                 /* Hold relation lock till commit (XXX bad for concurrency) */
1387                 heap_close(testrel, NoLock);
1388         }
1389
1390         FreeExecutorState(estate);
1391
1392         /* Clean up */
1393         heap_close(typrel, RowExclusiveLock);
1394 }
1395
1396 /*
1397  * get_rels_with_domain
1398  *
1399  * Fetch all relations / attributes which are using the domain
1400  *
1401  * The result is a list of RelToCheck structs, one for each distinct
1402  * relation, each containing one or more attribute numbers that are of
1403  * the domain type.  We have opened each rel and acquired the specified lock
1404  * type on it.
1405  *
1406  * XXX this is completely broken because there is no way to lock the domain
1407  * to prevent columns from being added or dropped while our command runs.
1408  * We can partially protect against column drops by locking relations as we
1409  * come across them, but there is still a race condition (the window between
1410  * seeing a pg_depend entry and acquiring lock on the relation it references).
1411  * Also, holding locks on all these relations simultaneously creates a non-
1412  * trivial risk of deadlock.  We can minimize but not eliminate the deadlock
1413  * risk by using the weakest suitable lock (ShareLock for most callers).
1414  *
1415  * XXX to support domains over domains, we'd need to make this smarter,
1416  * or make its callers smarter, so that we could find columns of derived
1417  * domains.  Arrays of domains would be a problem too.
1418  *
1419  * Generally used for retrieving a list of tests when adding
1420  * new constraints to a domain.
1421  */
1422 static List *
1423 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
1424 {
1425         List *result = NIL;
1426         Relation        depRel;
1427         ScanKeyData key[2];
1428         SysScanDesc depScan;
1429         HeapTuple       depTup;
1430
1431         /*
1432          * We scan pg_depend to find those things that depend on the domain.
1433          * (We assume we can ignore refobjsubid for a domain.)
1434          */
1435         depRel = relation_openr(DependRelationName, AccessShareLock);
1436
1437         ScanKeyEntryInitialize(&key[0], 0x0,
1438                                                    Anum_pg_depend_refclassid, F_OIDEQ,
1439                                                    ObjectIdGetDatum(RelOid_pg_type));
1440         ScanKeyEntryInitialize(&key[1], 0x0,
1441                                                    Anum_pg_depend_refobjid, F_OIDEQ,
1442                                                    ObjectIdGetDatum(domainOid));
1443
1444         depScan = systable_beginscan(depRel, DependReferenceIndex, true,
1445                                                                  SnapshotNow, 2, key);
1446
1447         while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
1448         {
1449                 Form_pg_depend          pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
1450                 RelToCheck *rtc = NULL;
1451                 List       *rellist;
1452                 Form_pg_attribute       pg_att;
1453                 int                     ptr;
1454
1455                 /* Ignore dependees that aren't user columns of tables */
1456                 /* (we assume system columns are never of domain types) */
1457                 if (pg_depend->classid != RelOid_pg_class ||
1458                         pg_depend->objsubid <= 0)
1459                         continue;
1460
1461                 /* See if we already have an entry for this relation */
1462                 foreach(rellist, result)
1463                 {
1464                         RelToCheck *rt = (RelToCheck *) lfirst(rellist);
1465
1466                         if (RelationGetRelid(rt->rel) == pg_depend->objid)
1467                         {
1468                                 rtc = rt;
1469                                 break;
1470                         }
1471                 }
1472
1473                 if (rtc == NULL)
1474                 {
1475                         /* First attribute found for this relation */
1476                         Relation        rel;
1477
1478                         /* Acquire requested lock on relation */
1479                         rel = heap_open(pg_depend->objid, lockmode);
1480
1481                         /* Build the RelToCheck entry with enough space for all atts */
1482                         rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
1483                         rtc->rel = rel;
1484                         rtc->natts = 0;
1485                         rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
1486                         result = lcons(rtc, result);
1487                 }
1488
1489                 /*
1490                  * Confirm column has not been dropped, and is of the expected type.
1491                  * This defends against an ALTER DROP COLUMN occuring just before
1492                  * we acquired lock ... but if the whole table were dropped, we'd
1493                  * still have a problem.
1494                  */
1495                 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
1496                         continue;
1497                 pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
1498                 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
1499                         continue;
1500
1501                 /*
1502                  * Okay, add column to result.  We store the columns in column-number
1503                  * order; this is just a hack to improve predictability of regression
1504                  * test output ...
1505                  */
1506                 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
1507
1508                 ptr = rtc->natts++;
1509                 while (ptr > 0 && rtc->atts[ptr-1] > pg_depend->objsubid)
1510                 {
1511                         rtc->atts[ptr] = rtc->atts[ptr-1];
1512                         ptr--;
1513                 }
1514                 rtc->atts[ptr] = pg_depend->objsubid;
1515         }
1516
1517         systable_endscan(depScan);
1518
1519         relation_close(depRel, AccessShareLock);
1520
1521         return result;
1522 }
1523
1524 /*
1525  * domainOwnerCheck
1526  *
1527  * Throw an error if the current user doesn't have permission to modify
1528  * the domain in an ALTER DOMAIN statement, or if the type isn't actually
1529  * a domain.
1530  */
1531 static void
1532 domainOwnerCheck(HeapTuple tup, TypeName *typename)
1533 {
1534         Form_pg_type    typTup = (Form_pg_type) GETSTRUCT(tup);
1535
1536         /* Check that this is actually a domain */
1537         if (typTup->typtype != 'd')
1538                 elog(ERROR, "%s is not a domain",
1539                          TypeNameToString(typename));
1540
1541         /* Permission check: must own type */
1542         if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1543                 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
1544 }
1545
1546 /*
1547  * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
1548  */
1549 static char *
1550 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
1551                                         int typMod, Constraint *constr,
1552                                         int *counter, char *domainName)
1553 {
1554         Node       *expr;
1555         char       *ccsrc;
1556         char       *ccbin;
1557         ParseState *pstate;
1558         ConstraintTestValue  *domVal;
1559
1560         /*
1561          * Assign or validate constraint name
1562          */
1563         if (constr->name)
1564         {
1565                 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
1566                                                                  domainOid,
1567                                                                  domainNamespace,
1568                                                                  constr->name))
1569                         elog(ERROR, "constraint \"%s\" already exists for domain \"%s\"",
1570                                  constr->name,
1571                                  domainName);
1572         }
1573         else
1574                 constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN,
1575                                                                                           domainOid,
1576                                                                                           domainNamespace,
1577                                                                                           counter);
1578
1579         /*
1580          * Convert the A_EXPR in raw_expr into an EXPR
1581          */
1582         pstate = make_parsestate(NULL);
1583
1584         /*
1585          * Set up a ConstraintTestValue to represent the occurrence of VALUE
1586          * in the expression.  Note that it will appear to have the type of the
1587          * base type, not the domain.  This seems correct since within the
1588          * check expression, we should not assume the input value can be considered
1589          * a member of the domain.
1590          */
1591         domVal = makeNode(ConstraintTestValue);
1592         domVal->typeId = baseTypeOid;
1593         domVal->typeMod = typMod;
1594
1595         pstate->p_value_substitute = (Node *) domVal;
1596
1597         expr = transformExpr(pstate, constr->raw_expr);
1598
1599         /*
1600          * Make sure it yields a boolean result.
1601          */
1602         expr = coerce_to_boolean(expr, "CHECK");
1603
1604         /*
1605          * Make sure no outside relations are
1606          * referred to.
1607          */
1608         if (length(pstate->p_rtable) != 0)
1609                 elog(ERROR, "Relations cannot be referenced in domain CHECK constraint");
1610
1611         /*
1612          * Domains don't allow var clauses (this should be redundant with the
1613          * above check, but make it anyway)
1614          */
1615         if (contain_var_clause(expr))
1616                 elog(ERROR, "cannot use column references in domain CHECK clause");
1617
1618         /*
1619          * No subplans or aggregates, either...
1620          */
1621         if (contain_subplans(expr))
1622                 elog(ERROR, "cannot use subselect in CHECK constraint expression");
1623         if (contain_agg_clause(expr))
1624                 elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
1625
1626         /*
1627          * Might as well try to reduce any constant expressions.
1628          */
1629         expr = eval_const_expressions(expr);
1630
1631         /*
1632          * Convert to string form for storage.
1633          */
1634         ccbin = nodeToString(expr);
1635
1636         /*
1637          * Deparse it to produce text for consrc.
1638          *
1639          * Since VARNOs aren't allowed in domain constraints, relation context
1640          * isn't required as anything other than a shell.
1641          */
1642         ccsrc = deparse_expression(expr,
1643                                                            deparse_context_for(domainName,
1644                                                                                                    InvalidOid),
1645                                                            false, false);
1646
1647         /*
1648          * Store the constraint in pg_constraint
1649          */
1650         CreateConstraintEntry(constr->name,             /* Constraint Name */
1651                                                   domainNamespace,      /* namespace */
1652                                                   CONSTRAINT_CHECK,             /* Constraint Type */
1653                                                   false,        /* Is Deferrable */
1654                                                   false,        /* Is Deferred */
1655                                                   InvalidOid,           /* not a relation constraint */
1656                                                   NULL, 
1657                                                   0,
1658                                                   domainOid,    /* domain constraint */
1659                                                   InvalidOid,   /* Foreign key fields */
1660                                                   NULL,
1661                                                   0,
1662                                                   ' ',
1663                                                   ' ',
1664                                                   ' ',
1665                                                   InvalidOid,
1666                                                   expr,         /* Tree form check constraint */
1667                                                   ccbin,        /* Binary form check constraint */
1668                                                   ccsrc);       /* Source form check constraint */
1669
1670         /*
1671          * Return the compiled constraint expression so the calling routine can
1672          * perform any additional required tests.
1673          */
1674         return ccbin;
1675 }