1 /*-------------------------------------------------------------------------
2 *
3 * typecmds.c
4 * Routines for SQL commands that manipulate types (and domains).
5 *
6 * Portions Copyright (c) 1996-2019, 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/htup_details.h"
37 #include "access/tableam.h"
38 #include "access/xact.h"
39 #include "catalog/binary_upgrade.h"
40 #include "catalog/catalog.h"
41 #include "catalog/heap.h"
42 #include "catalog/objectaccess.h"
43 #include "catalog/pg_am.h"
44 #include "catalog/pg_authid.h"
45 #include "catalog/pg_collation.h"
46 #include "catalog/pg_constraint.h"
47 #include "catalog/pg_depend.h"
48 #include "catalog/pg_enum.h"
49 #include "catalog/pg_language.h"
50 #include "catalog/pg_namespace.h"
51 #include "catalog/pg_proc.h"
52 #include "catalog/pg_range.h"
53 #include "catalog/pg_type.h"
54 #include "commands/defrem.h"
55 #include "commands/tablecmds.h"
56 #include "commands/typecmds.h"
57 #include "executor/executor.h"
58 #include "miscadmin.h"
59 #include "nodes/makefuncs.h"
60 #include "optimizer/optimizer.h"
61 #include "parser/parse_coerce.h"
62 #include "parser/parse_collate.h"
63 #include "parser/parse_expr.h"
64 #include "parser/parse_func.h"
65 #include "parser/parse_type.h"
66 #include "utils/builtins.h"
67 #include "utils/fmgroids.h"
68 #include "utils/inval.h"
69 #include "utils/lsyscache.h"
70 #include "utils/memutils.h"
71 #include "utils/rel.h"
72 #include "utils/ruleutils.h"
73 #include "utils/snapmgr.h"
74 #include "utils/syscache.h"
75
76
77 /* result structure for get_rels_with_domain() */
78 typedef struct
79 {
80 Relation rel; /* opened and locked relation */
81 int natts; /* number of attributes of interest */
82 int *atts; /* attribute numbers */
83 /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
84 } RelToCheck;
85
86 /* Potentially set by pg_upgrade_support functions */
87 Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
88
89 static void makeRangeConstructors(const char *name, Oid namespace,
90 Oid rangeOid, Oid subtype);
91 static Oid findTypeInputFunction(List *procname, Oid typeOid);
92 static Oid findTypeOutputFunction(List *procname, Oid typeOid);
93 static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
94 static Oid findTypeSendFunction(List *procname, Oid typeOid);
95 static Oid findTypeTypmodinFunction(List *procname);
96 static Oid findTypeTypmodoutFunction(List *procname);
97 static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
98 static Oid findRangeSubOpclass(List *opcname, Oid subtype);
99 static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
100 static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
101 static void validateDomainConstraint(Oid domainoid, char *ccbin);
102 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
103 static void checkEnumOwner(HeapTuple tup);
104 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
105 Oid baseTypeOid,
106 int typMod, Constraint *constr,
107 const char *domainName, ObjectAddress *constrAddr);
108 static Node *replace_domain_constraint_value(ParseState *pstate,
109 ColumnRef *cref);
110
111
112 /*
113 * DefineType
114 * Registers a new base type.
115 */
116 ObjectAddress
DefineType(ParseState * pstate,List * names,List * parameters)117 DefineType(ParseState *pstate, List *names, List *parameters)
118 {
119 char *typeName;
120 Oid typeNamespace;
121 int16 internalLength = -1; /* default: variable-length */
122 List *inputName = NIL;
123 List *outputName = NIL;
124 List *receiveName = NIL;
125 List *sendName = NIL;
126 List *typmodinName = NIL;
127 List *typmodoutName = NIL;
128 List *analyzeName = NIL;
129 char category = TYPCATEGORY_USER;
130 bool preferred = false;
131 char delimiter = DEFAULT_TYPDELIM;
132 Oid elemType = InvalidOid;
133 char *defaultValue = NULL;
134 bool byValue = false;
135 char alignment = 'i'; /* default alignment */
136 char storage = 'p'; /* default TOAST storage method */
137 Oid collation = InvalidOid;
138 DefElem *likeTypeEl = NULL;
139 DefElem *internalLengthEl = NULL;
140 DefElem *inputNameEl = NULL;
141 DefElem *outputNameEl = NULL;
142 DefElem *receiveNameEl = NULL;
143 DefElem *sendNameEl = NULL;
144 DefElem *typmodinNameEl = NULL;
145 DefElem *typmodoutNameEl = NULL;
146 DefElem *analyzeNameEl = NULL;
147 DefElem *categoryEl = NULL;
148 DefElem *preferredEl = NULL;
149 DefElem *delimiterEl = NULL;
150 DefElem *elemTypeEl = NULL;
151 DefElem *defaultValueEl = NULL;
152 DefElem *byValueEl = NULL;
153 DefElem *alignmentEl = NULL;
154 DefElem *storageEl = NULL;
155 DefElem *collatableEl = NULL;
156 Oid inputOid;
157 Oid outputOid;
158 Oid receiveOid = InvalidOid;
159 Oid sendOid = InvalidOid;
160 Oid typmodinOid = InvalidOid;
161 Oid typmodoutOid = InvalidOid;
162 Oid analyzeOid = InvalidOid;
163 char *array_type;
164 Oid array_oid;
165 Oid typoid;
166 Oid resulttype;
167 ListCell *pl;
168 ObjectAddress address;
169
170 /*
171 * As of Postgres 8.4, we require superuser privilege to create a base
172 * type. This is simple paranoia: there are too many ways to mess up the
173 * system with an incorrect type definition (for instance, representation
174 * parameters that don't match what the C code expects). In practice it
175 * takes superuser privilege to create the I/O functions, and so the
176 * former requirement that you own the I/O functions pretty much forced
177 * superuserness anyway. We're just making doubly sure here.
178 *
179 * XXX re-enable NOT_USED code sections below if you remove this test.
180 */
181 if (!superuser())
182 ereport(ERROR,
183 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
184 errmsg("must be superuser to create a base type")));
185
186 /* Convert list of names to a name and namespace */
187 typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
188
189 #ifdef NOT_USED
190 /* XXX this is unnecessary given the superuser check above */
191 /* Check we have creation rights in target namespace */
192 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
193 if (aclresult != ACLCHECK_OK)
194 aclcheck_error(aclresult, OBJECT_SCHEMA,
195 get_namespace_name(typeNamespace));
196 #endif
197
198 /*
199 * Look to see if type already exists (presumably as a shell; if not,
200 * TypeCreate will complain).
201 */
202 typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
203 CStringGetDatum(typeName),
204 ObjectIdGetDatum(typeNamespace));
205
206 /*
207 * If it's not a shell, see if it's an autogenerated array type, and if so
208 * rename it out of the way.
209 */
210 if (OidIsValid(typoid) && get_typisdefined(typoid))
211 {
212 if (moveArrayTypeName(typoid, typeName, typeNamespace))
213 typoid = InvalidOid;
214 }
215
216 /*
217 * If it doesn't exist, create it as a shell, so that the OID is known for
218 * use in the I/O function definitions.
219 */
220 if (!OidIsValid(typoid))
221 {
222 address = TypeShellMake(typeName, typeNamespace, GetUserId());
223 typoid = address.objectId;
224 /* Make new shell type visible for modification below */
225 CommandCounterIncrement();
226
227 /*
228 * If the command was a parameterless CREATE TYPE, we're done ---
229 * creating the shell type was all we're supposed to do.
230 */
231 if (parameters == NIL)
232 return address;
233 }
234 else
235 {
236 /* Complain if dummy CREATE TYPE and entry already exists */
237 if (parameters == NIL)
238 ereport(ERROR,
239 (errcode(ERRCODE_DUPLICATE_OBJECT),
240 errmsg("type \"%s\" already exists", typeName)));
241 }
242
243 /* Extract the parameters from the parameter list */
244 foreach(pl, parameters)
245 {
246 DefElem *defel = (DefElem *) lfirst(pl);
247 DefElem **defelp;
248
249 if (strcmp(defel->defname, "like") == 0)
250 defelp = &likeTypeEl;
251 else if (strcmp(defel->defname, "internallength") == 0)
252 defelp = &internalLengthEl;
253 else if (strcmp(defel->defname, "input") == 0)
254 defelp = &inputNameEl;
255 else if (strcmp(defel->defname, "output") == 0)
256 defelp = &outputNameEl;
257 else if (strcmp(defel->defname, "receive") == 0)
258 defelp = &receiveNameEl;
259 else if (strcmp(defel->defname, "send") == 0)
260 defelp = &sendNameEl;
261 else if (strcmp(defel->defname, "typmod_in") == 0)
262 defelp = &typmodinNameEl;
263 else if (strcmp(defel->defname, "typmod_out") == 0)
264 defelp = &typmodoutNameEl;
265 else if (strcmp(defel->defname, "analyze") == 0 ||
266 strcmp(defel->defname, "analyse") == 0)
267 defelp = &analyzeNameEl;
268 else if (strcmp(defel->defname, "category") == 0)
269 defelp = &categoryEl;
270 else if (strcmp(defel->defname, "preferred") == 0)
271 defelp = &preferredEl;
272 else if (strcmp(defel->defname, "delimiter") == 0)
273 defelp = &delimiterEl;
274 else if (strcmp(defel->defname, "element") == 0)
275 defelp = &elemTypeEl;
276 else if (strcmp(defel->defname, "default") == 0)
277 defelp = &defaultValueEl;
278 else if (strcmp(defel->defname, "passedbyvalue") == 0)
279 defelp = &byValueEl;
280 else if (strcmp(defel->defname, "alignment") == 0)
281 defelp = &alignmentEl;
282 else if (strcmp(defel->defname, "storage") == 0)
283 defelp = &storageEl;
284 else if (strcmp(defel->defname, "collatable") == 0)
285 defelp = &collatableEl;
286 else
287 {
288 /* WARNING, not ERROR, for historical backwards-compatibility */
289 ereport(WARNING,
290 (errcode(ERRCODE_SYNTAX_ERROR),
291 errmsg("type attribute \"%s\" not recognized",
292 defel->defname),
293 parser_errposition(pstate, defel->location)));
294 continue;
295 }
296 if (*defelp != NULL)
297 ereport(ERROR,
298 (errcode(ERRCODE_SYNTAX_ERROR),
299 errmsg("conflicting or redundant options"),
300 parser_errposition(pstate, defel->location)));
301 *defelp = defel;
302 }
303
304 /*
305 * Now interpret the options; we do this separately so that LIKE can be
306 * overridden by other options regardless of the ordering in the parameter
307 * list.
308 */
309 if (likeTypeEl)
310 {
311 Type likeType;
312 Form_pg_type likeForm;
313
314 likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
315 likeForm = (Form_pg_type) GETSTRUCT(likeType);
316 internalLength = likeForm->typlen;
317 byValue = likeForm->typbyval;
318 alignment = likeForm->typalign;
319 storage = likeForm->typstorage;
320 ReleaseSysCache(likeType);
321 }
322 if (internalLengthEl)
323 internalLength = defGetTypeLength(internalLengthEl);
324 if (inputNameEl)
325 inputName = defGetQualifiedName(inputNameEl);
326 if (outputNameEl)
327 outputName = defGetQualifiedName(outputNameEl);
328 if (receiveNameEl)
329 receiveName = defGetQualifiedName(receiveNameEl);
330 if (sendNameEl)
331 sendName = defGetQualifiedName(sendNameEl);
332 if (typmodinNameEl)
333 typmodinName = defGetQualifiedName(typmodinNameEl);
334 if (typmodoutNameEl)
335 typmodoutName = defGetQualifiedName(typmodoutNameEl);
336 if (analyzeNameEl)
337 analyzeName = defGetQualifiedName(analyzeNameEl);
338 if (categoryEl)
339 {
340 char *p = defGetString(categoryEl);
341
342 category = p[0];
343 /* restrict to non-control ASCII */
344 if (category < 32 || category > 126)
345 ereport(ERROR,
346 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
347 errmsg("invalid type category \"%s\": must be simple ASCII",
348 p)));
349 }
350 if (preferredEl)
351 preferred = defGetBoolean(preferredEl);
352 if (delimiterEl)
353 {
354 char *p = defGetString(delimiterEl);
355
356 delimiter = p[0];
357 /* XXX shouldn't we restrict the delimiter? */
358 }
359 if (elemTypeEl)
360 {
361 elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl));
362 /* disallow arrays of pseudotypes */
363 if (get_typtype(elemType) == TYPTYPE_PSEUDO)
364 ereport(ERROR,
365 (errcode(ERRCODE_DATATYPE_MISMATCH),
366 errmsg("array element type cannot be %s",
367 format_type_be(elemType))));
368 }
369 if (defaultValueEl)
370 defaultValue = defGetString(defaultValueEl);
371 if (byValueEl)
372 byValue = defGetBoolean(byValueEl);
373 if (alignmentEl)
374 {
375 char *a = defGetString(alignmentEl);
376
377 /*
378 * Note: if argument was an unquoted identifier, parser will have
379 * applied translations to it, so be prepared to recognize translated
380 * type names as well as the nominal form.
381 */
382 if (pg_strcasecmp(a, "double") == 0 ||
383 pg_strcasecmp(a, "float8") == 0 ||
384 pg_strcasecmp(a, "pg_catalog.float8") == 0)
385 alignment = 'd';
386 else if (pg_strcasecmp(a, "int4") == 0 ||
387 pg_strcasecmp(a, "pg_catalog.int4") == 0)
388 alignment = 'i';
389 else if (pg_strcasecmp(a, "int2") == 0 ||
390 pg_strcasecmp(a, "pg_catalog.int2") == 0)
391 alignment = 's';
392 else if (pg_strcasecmp(a, "char") == 0 ||
393 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
394 alignment = 'c';
395 else
396 ereport(ERROR,
397 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
398 errmsg("alignment \"%s\" not recognized", a)));
399 }
400 if (storageEl)
401 {
402 char *a = defGetString(storageEl);
403
404 if (pg_strcasecmp(a, "plain") == 0)
405 storage = 'p';
406 else if (pg_strcasecmp(a, "external") == 0)
407 storage = 'e';
408 else if (pg_strcasecmp(a, "extended") == 0)
409 storage = 'x';
410 else if (pg_strcasecmp(a, "main") == 0)
411 storage = 'm';
412 else
413 ereport(ERROR,
414 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
415 errmsg("storage \"%s\" not recognized", a)));
416 }
417 if (collatableEl)
418 collation = defGetBoolean(collatableEl) ? DEFAULT_COLLATION_OID : InvalidOid;
419
420 /*
421 * make sure we have our required definitions
422 */
423 if (inputName == NIL)
424 ereport(ERROR,
425 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
426 errmsg("type input function must be specified")));
427 if (outputName == NIL)
428 ereport(ERROR,
429 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
430 errmsg("type output function must be specified")));
431
432 if (typmodinName == NIL && typmodoutName != NIL)
433 ereport(ERROR,
434 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
435 errmsg("type modifier output function is useless without a type modifier input function")));
436
437 /*
438 * Convert I/O proc names to OIDs
439 */
440 inputOid = findTypeInputFunction(inputName, typoid);
441 outputOid = findTypeOutputFunction(outputName, typoid);
442 if (receiveName)
443 receiveOid = findTypeReceiveFunction(receiveName, typoid);
444 if (sendName)
445 sendOid = findTypeSendFunction(sendName, typoid);
446
447 /*
448 * Verify that I/O procs return the expected thing. If we see OPAQUE,
449 * complain and change it to the correct type-safe choice.
450 */
451 resulttype = get_func_rettype(inputOid);
452 if (resulttype != typoid)
453 {
454 if (resulttype == OPAQUEOID)
455 {
456 /* backwards-compatibility hack */
457 ereport(WARNING,
458 (errmsg("changing return type of function %s from %s to %s",
459 NameListToString(inputName), "opaque", typeName)));
460 SetFunctionReturnType(inputOid, typoid);
461 }
462 else
463 ereport(ERROR,
464 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
465 errmsg("type input function %s must return type %s",
466 NameListToString(inputName), typeName)));
467 }
468 resulttype = get_func_rettype(outputOid);
469 if (resulttype != CSTRINGOID)
470 {
471 if (resulttype == OPAQUEOID)
472 {
473 /* backwards-compatibility hack */
474 ereport(WARNING,
475 (errmsg("changing return type of function %s from %s to %s",
476 NameListToString(outputName), "opaque", "cstring")));
477 SetFunctionReturnType(outputOid, CSTRINGOID);
478 }
479 else
480 ereport(ERROR,
481 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
482 errmsg("type output function %s must return type %s",
483 NameListToString(outputName), "cstring")));
484 }
485 if (receiveOid)
486 {
487 resulttype = get_func_rettype(receiveOid);
488 if (resulttype != typoid)
489 ereport(ERROR,
490 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
491 errmsg("type receive function %s must return type %s",
492 NameListToString(receiveName), typeName)));
493 }
494 if (sendOid)
495 {
496 resulttype = get_func_rettype(sendOid);
497 if (resulttype != BYTEAOID)
498 ereport(ERROR,
499 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
500 errmsg("type send function %s must return type %s",
501 NameListToString(sendName), "bytea")));
502 }
503
504 /*
505 * Convert typmodin/out function proc names to OIDs.
506 */
507 if (typmodinName)
508 typmodinOid = findTypeTypmodinFunction(typmodinName);
509 if (typmodoutName)
510 typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
511
512 /*
513 * Convert analysis function proc name to an OID. If no analysis function
514 * is specified, we'll use zero to select the built-in default algorithm.
515 */
516 if (analyzeName)
517 analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
518
519 /*
520 * Check permissions on functions. We choose to require the creator/owner
521 * of a type to also own the underlying functions. Since creating a type
522 * is tantamount to granting public execute access on the functions, the
523 * minimum sane check would be for execute-with-grant-option. But we
524 * don't have a way to make the type go away if the grant option is
525 * revoked, so ownership seems better.
526 */
527 #ifdef NOT_USED
528 /* XXX this is unnecessary given the superuser check above */
529 if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
530 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
531 NameListToString(inputName));
532 if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
533 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
534 NameListToString(outputName));
535 if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
536 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
537 NameListToString(receiveName));
538 if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
539 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
540 NameListToString(sendName));
541 if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
542 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
543 NameListToString(typmodinName));
544 if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
545 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
546 NameListToString(typmodoutName));
547 if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
548 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
549 NameListToString(analyzeName));
550 #endif
551
552 /*
553 * Print warnings if any of the type's I/O functions are marked volatile.
554 * There is a general assumption that I/O functions are stable or
555 * immutable; this allows us for example to mark record_in/record_out
556 * stable rather than volatile. Ideally we would throw errors not just
557 * warnings here; but since this check is new as of 9.5, and since the
558 * volatility marking might be just an error-of-omission and not a true
559 * indication of how the function behaves, we'll let it pass as a warning
560 * for now.
561 */
562 if (inputOid && func_volatile(inputOid) == PROVOLATILE_VOLATILE)
563 ereport(WARNING,
564 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
565 errmsg("type input function %s should not be volatile",
566 NameListToString(inputName))));
567 if (outputOid && func_volatile(outputOid) == PROVOLATILE_VOLATILE)
568 ereport(WARNING,
569 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
570 errmsg("type output function %s should not be volatile",
571 NameListToString(outputName))));
572 if (receiveOid && func_volatile(receiveOid) == PROVOLATILE_VOLATILE)
573 ereport(WARNING,
574 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
575 errmsg("type receive function %s should not be volatile",
576 NameListToString(receiveName))));
577 if (sendOid && func_volatile(sendOid) == PROVOLATILE_VOLATILE)
578 ereport(WARNING,
579 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
580 errmsg("type send function %s should not be volatile",
581 NameListToString(sendName))));
582 if (typmodinOid && func_volatile(typmodinOid) == PROVOLATILE_VOLATILE)
583 ereport(WARNING,
584 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
585 errmsg("type modifier input function %s should not be volatile",
586 NameListToString(typmodinName))));
587 if (typmodoutOid && func_volatile(typmodoutOid) == PROVOLATILE_VOLATILE)
588 ereport(WARNING,
589 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
590 errmsg("type modifier output function %s should not be volatile",
591 NameListToString(typmodoutName))));
592
593 /*
594 * OK, we're done checking, time to make the type. We must assign the
595 * array type OID ahead of calling TypeCreate, since the base type and
596 * array type each refer to the other.
597 */
598 array_oid = AssignTypeArrayOid();
599
600 /*
601 * now have TypeCreate do all the real work.
602 *
603 * Note: the pg_type.oid is stored in user tables as array elements (base
604 * types) in ArrayType and in composite types in DatumTupleFields. This
605 * oid must be preserved by binary upgrades.
606 */
607 address =
608 TypeCreate(InvalidOid, /* no predetermined type OID */
609 typeName, /* type name */
610 typeNamespace, /* namespace */
611 InvalidOid, /* relation oid (n/a here) */
612 0, /* relation kind (ditto) */
613 GetUserId(), /* owner's ID */
614 internalLength, /* internal size */
615 TYPTYPE_BASE, /* type-type (base type) */
616 category, /* type-category */
617 preferred, /* is it a preferred type? */
618 delimiter, /* array element delimiter */
619 inputOid, /* input procedure */
620 outputOid, /* output procedure */
621 receiveOid, /* receive procedure */
622 sendOid, /* send procedure */
623 typmodinOid, /* typmodin procedure */
624 typmodoutOid, /* typmodout procedure */
625 analyzeOid, /* analyze procedure */
626 elemType, /* element type ID */
627 false, /* this is not an array type */
628 array_oid, /* array type we are about to create */
629 InvalidOid, /* base type ID (only for domains) */
630 defaultValue, /* default type value */
631 NULL, /* no binary form available */
632 byValue, /* passed by value */
633 alignment, /* required alignment */
634 storage, /* TOAST strategy */
635 -1, /* typMod (Domains only) */
636 0, /* Array Dimensions of typbasetype */
637 false, /* Type NOT NULL */
638 collation); /* type's collation */
639 Assert(typoid == address.objectId);
640
641 /*
642 * Create the array type that goes with it.
643 */
644 array_type = makeArrayTypeName(typeName, typeNamespace);
645
646 /* alignment must be 'i' or 'd' for arrays */
647 alignment = (alignment == 'd') ? 'd' : 'i';
648
649 TypeCreate(array_oid, /* force assignment of this type OID */
650 array_type, /* type name */
651 typeNamespace, /* namespace */
652 InvalidOid, /* relation oid (n/a here) */
653 0, /* relation kind (ditto) */
654 GetUserId(), /* owner's ID */
655 -1, /* internal size (always varlena) */
656 TYPTYPE_BASE, /* type-type (base type) */
657 TYPCATEGORY_ARRAY, /* type-category (array) */
658 false, /* array types are never preferred */
659 delimiter, /* array element delimiter */
660 F_ARRAY_IN, /* input procedure */
661 F_ARRAY_OUT, /* output procedure */
662 F_ARRAY_RECV, /* receive procedure */
663 F_ARRAY_SEND, /* send procedure */
664 typmodinOid, /* typmodin procedure */
665 typmodoutOid, /* typmodout procedure */
666 F_ARRAY_TYPANALYZE, /* analyze procedure */
667 typoid, /* element type ID */
668 true, /* yes this is an array type */
669 InvalidOid, /* no further array type */
670 InvalidOid, /* base type ID */
671 NULL, /* never a default type value */
672 NULL, /* binary default isn't sent either */
673 false, /* never passed by value */
674 alignment, /* see above */
675 'x', /* ARRAY is always toastable */
676 -1, /* typMod (Domains only) */
677 0, /* Array dimensions of typbasetype */
678 false, /* Type NOT NULL */
679 collation); /* type's collation */
680
681 pfree(array_type);
682
683 return address;
684 }
685
686 /*
687 * Guts of type deletion.
688 */
689 void
RemoveTypeById(Oid typeOid)690 RemoveTypeById(Oid typeOid)
691 {
692 Relation relation;
693 HeapTuple tup;
694
695 relation = table_open(TypeRelationId, RowExclusiveLock);
696
697 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
698 if (!HeapTupleIsValid(tup))
699 elog(ERROR, "cache lookup failed for type %u", typeOid);
700
701 CatalogTupleDelete(relation, &tup->t_self);
702
703 /*
704 * If it is an enum, delete the pg_enum entries too; we don't bother with
705 * making dependency entries for those, so it has to be done "by hand"
706 * here.
707 */
708 if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
709 EnumValuesDelete(typeOid);
710
711 /*
712 * If it is a range type, delete the pg_range entry too; we don't bother
713 * with making a dependency entry for that, so it has to be done "by hand"
714 * here.
715 */
716 if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE)
717 RangeDelete(typeOid);
718
719 ReleaseSysCache(tup);
720
721 table_close(relation, RowExclusiveLock);
722 }
723
724
725 /*
726 * DefineDomain
727 * Registers a new domain.
728 */
729 ObjectAddress
DefineDomain(CreateDomainStmt * stmt)730 DefineDomain(CreateDomainStmt *stmt)
731 {
732 char *domainName;
733 char *domainArrayName;
734 Oid domainNamespace;
735 AclResult aclresult;
736 int16 internalLength;
737 Oid inputProcedure;
738 Oid outputProcedure;
739 Oid receiveProcedure;
740 Oid sendProcedure;
741 Oid analyzeProcedure;
742 bool byValue;
743 char category;
744 char delimiter;
745 char alignment;
746 char storage;
747 char typtype;
748 Datum datum;
749 bool isnull;
750 char *defaultValue = NULL;
751 char *defaultValueBin = NULL;
752 bool saw_default = false;
753 bool typNotNull = false;
754 bool nullDefined = false;
755 int32 typNDims = list_length(stmt->typeName->arrayBounds);
756 HeapTuple typeTup;
757 List *schema = stmt->constraints;
758 ListCell *listptr;
759 Oid basetypeoid;
760 Oid old_type_oid;
761 Oid domaincoll;
762 Oid domainArrayOid;
763 Form_pg_type baseType;
764 int32 basetypeMod;
765 Oid baseColl;
766 ObjectAddress address;
767
768 /* Convert list of names to a name and namespace */
769 domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
770 &domainName);
771
772 /* Check we have creation rights in target namespace */
773 aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
774 ACL_CREATE);
775 if (aclresult != ACLCHECK_OK)
776 aclcheck_error(aclresult, OBJECT_SCHEMA,
777 get_namespace_name(domainNamespace));
778
779 /*
780 * Check for collision with an existing type name. If there is one and
781 * it's an autogenerated array, we can rename it out of the way.
782 */
783 old_type_oid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
784 CStringGetDatum(domainName),
785 ObjectIdGetDatum(domainNamespace));
786 if (OidIsValid(old_type_oid))
787 {
788 if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
789 ereport(ERROR,
790 (errcode(ERRCODE_DUPLICATE_OBJECT),
791 errmsg("type \"%s\" already exists", domainName)));
792 }
793
794 /*
795 * Look up the base type.
796 */
797 typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
798 baseType = (Form_pg_type) GETSTRUCT(typeTup);
799 basetypeoid = baseType->oid;
800
801 /*
802 * Base type must be a plain base type, a composite type, another domain,
803 * an enum or a range type. Domains over pseudotypes would create a
804 * security hole. (It would be shorter to code this to just check for
805 * pseudotypes; but it seems safer to call out the specific typtypes that
806 * are supported, rather than assume that all future typtypes would be
807 * automatically supported.)
808 */
809 typtype = baseType->typtype;
810 if (typtype != TYPTYPE_BASE &&
811 typtype != TYPTYPE_COMPOSITE &&
812 typtype != TYPTYPE_DOMAIN &&
813 typtype != TYPTYPE_ENUM &&
814 typtype != TYPTYPE_RANGE)
815 ereport(ERROR,
816 (errcode(ERRCODE_DATATYPE_MISMATCH),
817 errmsg("\"%s\" is not a valid base type for a domain",
818 TypeNameToString(stmt->typeName))));
819
820 aclresult = pg_type_aclcheck(basetypeoid, GetUserId(), ACL_USAGE);
821 if (aclresult != ACLCHECK_OK)
822 aclcheck_error_type(aclresult, basetypeoid);
823
824 /*
825 * Identify the collation if any
826 */
827 baseColl = baseType->typcollation;
828 if (stmt->collClause)
829 domaincoll = get_collation_oid(stmt->collClause->collname, false);
830 else
831 domaincoll = baseColl;
832
833 /* Complain if COLLATE is applied to an uncollatable type */
834 if (OidIsValid(domaincoll) && !OidIsValid(baseColl))
835 ereport(ERROR,
836 (errcode(ERRCODE_DATATYPE_MISMATCH),
837 errmsg("collations are not supported by type %s",
838 format_type_be(basetypeoid))));
839
840 /* passed by value */
841 byValue = baseType->typbyval;
842
843 /* Required Alignment */
844 alignment = baseType->typalign;
845
846 /* TOAST Strategy */
847 storage = baseType->typstorage;
848
849 /* Storage Length */
850 internalLength = baseType->typlen;
851
852 /* Type Category */
853 category = baseType->typcategory;
854
855 /* Array element Delimiter */
856 delimiter = baseType->typdelim;
857
858 /* I/O Functions */
859 inputProcedure = F_DOMAIN_IN;
860 outputProcedure = baseType->typoutput;
861 receiveProcedure = F_DOMAIN_RECV;
862 sendProcedure = baseType->typsend;
863
864 /* Domains never accept typmods, so no typmodin/typmodout needed */
865
866 /* Analysis function */
867 analyzeProcedure = baseType->typanalyze;
868
869 /* Inherited default value */
870 datum = SysCacheGetAttr(TYPEOID, typeTup,
871 Anum_pg_type_typdefault, &isnull);
872 if (!isnull)
873 defaultValue = TextDatumGetCString(datum);
874
875 /* Inherited default binary value */
876 datum = SysCacheGetAttr(TYPEOID, typeTup,
877 Anum_pg_type_typdefaultbin, &isnull);
878 if (!isnull)
879 defaultValueBin = TextDatumGetCString(datum);
880
881 /*
882 * Run through constraints manually to avoid the additional processing
883 * conducted by DefineRelation() and friends.
884 */
885 foreach(listptr, schema)
886 {
887 Constraint *constr = lfirst(listptr);
888
889 if (!IsA(constr, Constraint))
890 elog(ERROR, "unrecognized node type: %d",
891 (int) nodeTag(constr));
892 switch (constr->contype)
893 {
894 case CONSTR_DEFAULT:
895
896 /*
897 * The inherited default value may be overridden by the user
898 * with the DEFAULT <expr> clause ... but only once.
899 */
900 if (saw_default)
901 ereport(ERROR,
902 (errcode(ERRCODE_SYNTAX_ERROR),
903 errmsg("multiple default expressions")));
904 saw_default = true;
905
906 if (constr->raw_expr)
907 {
908 ParseState *pstate;
909 Node *defaultExpr;
910
911 /* Create a dummy ParseState for transformExpr */
912 pstate = make_parsestate(NULL);
913
914 /*
915 * Cook the constr->raw_expr into an expression; copy it
916 * in case the input is in plan cache. Note: name is used
917 * only for error messages.
918 */
919 defaultExpr = cookDefault(pstate,
920 copyObject(constr->raw_expr),
921 basetypeoid,
922 basetypeMod,
923 domainName,
924 0);
925
926 /*
927 * If the expression is just a NULL constant, we treat it
928 * like not having a default.
929 *
930 * Note that if the basetype is another domain, we'll see
931 * a CoerceToDomain expr here and not discard the default.
932 * This is critical because the domain default needs to be
933 * retained to override any default that the base domain
934 * might have.
935 */
936 if (defaultExpr == NULL ||
937 (IsA(defaultExpr, Const) &&
938 ((Const *) defaultExpr)->constisnull))
939 {
940 defaultValue = NULL;
941 defaultValueBin = NULL;
942 }
943 else
944 {
945 /*
946 * Expression must be stored as a nodeToString result,
947 * but we also require a valid textual representation
948 * (mainly to make life easier for pg_dump).
949 */
950 defaultValue =
951 deparse_expression(defaultExpr,
952 NIL, false, false);
953 defaultValueBin = nodeToString(defaultExpr);
954 }
955 }
956 else
957 {
958 /* No default (can this still happen?) */
959 defaultValue = NULL;
960 defaultValueBin = NULL;
961 }
962 break;
963
964 case CONSTR_NOTNULL:
965 if (nullDefined && !typNotNull)
966 ereport(ERROR,
967 (errcode(ERRCODE_SYNTAX_ERROR),
968 errmsg("conflicting NULL/NOT NULL constraints")));
969 typNotNull = true;
970 nullDefined = true;
971 break;
972
973 case CONSTR_NULL:
974 if (nullDefined && typNotNull)
975 ereport(ERROR,
976 (errcode(ERRCODE_SYNTAX_ERROR),
977 errmsg("conflicting NULL/NOT NULL constraints")));
978 typNotNull = false;
979 nullDefined = true;
980 break;
981
982 case CONSTR_CHECK:
983
984 /*
985 * Check constraints are handled after domain creation, as
986 * they require the Oid of the domain; at this point we can
987 * only check that they're not marked NO INHERIT, because that
988 * would be bogus.
989 */
990 if (constr->is_no_inherit)
991 ereport(ERROR,
992 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
993 errmsg("check constraints for domains cannot be marked NO INHERIT")));
994 break;
995
996 /*
997 * All else are error cases
998 */
999 case CONSTR_UNIQUE:
1000 ereport(ERROR,
1001 (errcode(ERRCODE_SYNTAX_ERROR),
1002 errmsg("unique constraints not possible for domains")));
1003 break;
1004
1005 case CONSTR_PRIMARY:
1006 ereport(ERROR,
1007 (errcode(ERRCODE_SYNTAX_ERROR),
1008 errmsg("primary key constraints not possible for domains")));
1009 break;
1010
1011 case CONSTR_EXCLUSION:
1012 ereport(ERROR,
1013 (errcode(ERRCODE_SYNTAX_ERROR),
1014 errmsg("exclusion constraints not possible for domains")));
1015 break;
1016
1017 case CONSTR_FOREIGN:
1018 ereport(ERROR,
1019 (errcode(ERRCODE_SYNTAX_ERROR),
1020 errmsg("foreign key constraints not possible for domains")));
1021 break;
1022
1023 case CONSTR_ATTR_DEFERRABLE:
1024 case CONSTR_ATTR_NOT_DEFERRABLE:
1025 case CONSTR_ATTR_DEFERRED:
1026 case CONSTR_ATTR_IMMEDIATE:
1027 ereport(ERROR,
1028 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1029 errmsg("specifying constraint deferrability not supported for domains")));
1030 break;
1031
1032 default:
1033 elog(ERROR, "unrecognized constraint subtype: %d",
1034 (int) constr->contype);
1035 break;
1036 }
1037 }
1038
1039 /* Allocate OID for array type */
1040 domainArrayOid = AssignTypeArrayOid();
1041
1042 /*
1043 * Have TypeCreate do all the real work.
1044 */
1045 address =
1046 TypeCreate(InvalidOid, /* no predetermined type OID */
1047 domainName, /* type name */
1048 domainNamespace, /* namespace */
1049 InvalidOid, /* relation oid (n/a here) */
1050 0, /* relation kind (ditto) */
1051 GetUserId(), /* owner's ID */
1052 internalLength, /* internal size */
1053 TYPTYPE_DOMAIN, /* type-type (domain type) */
1054 category, /* type-category */
1055 false, /* domain types are never preferred */
1056 delimiter, /* array element delimiter */
1057 inputProcedure, /* input procedure */
1058 outputProcedure, /* output procedure */
1059 receiveProcedure, /* receive procedure */
1060 sendProcedure, /* send procedure */
1061 InvalidOid, /* typmodin procedure - none */
1062 InvalidOid, /* typmodout procedure - none */
1063 analyzeProcedure, /* analyze procedure */
1064 InvalidOid, /* no array element type */
1065 false, /* this isn't an array */
1066 domainArrayOid, /* array type we are about to create */
1067 basetypeoid, /* base type ID */
1068 defaultValue, /* default type value (text) */
1069 defaultValueBin, /* default type value (binary) */
1070 byValue, /* passed by value */
1071 alignment, /* required alignment */
1072 storage, /* TOAST strategy */
1073 basetypeMod, /* typeMod value */
1074 typNDims, /* Array dimensions for base type */
1075 typNotNull, /* Type NOT NULL */
1076 domaincoll); /* type's collation */
1077
1078 /*
1079 * Create the array type that goes with it.
1080 */
1081 domainArrayName = makeArrayTypeName(domainName, domainNamespace);
1082
1083 /* alignment must be 'i' or 'd' for arrays */
1084 alignment = (alignment == 'd') ? 'd' : 'i';
1085
1086 TypeCreate(domainArrayOid, /* force assignment of this type OID */
1087 domainArrayName, /* type name */
1088 domainNamespace, /* namespace */
1089 InvalidOid, /* relation oid (n/a here) */
1090 0, /* relation kind (ditto) */
1091 GetUserId(), /* owner's ID */
1092 -1, /* internal size (always varlena) */
1093 TYPTYPE_BASE, /* type-type (base type) */
1094 TYPCATEGORY_ARRAY, /* type-category (array) */
1095 false, /* array types are never preferred */
1096 delimiter, /* array element delimiter */
1097 F_ARRAY_IN, /* input procedure */
1098 F_ARRAY_OUT, /* output procedure */
1099 F_ARRAY_RECV, /* receive procedure */
1100 F_ARRAY_SEND, /* send procedure */
1101 InvalidOid, /* typmodin procedure - none */
1102 InvalidOid, /* typmodout procedure - none */
1103 F_ARRAY_TYPANALYZE, /* analyze procedure */
1104 address.objectId, /* element type ID */
1105 true, /* yes this is an array type */
1106 InvalidOid, /* no further array type */
1107 InvalidOid, /* base type ID */
1108 NULL, /* never a default type value */
1109 NULL, /* binary default isn't sent either */
1110 false, /* never passed by value */
1111 alignment, /* see above */
1112 'x', /* ARRAY is always toastable */
1113 -1, /* typMod (Domains only) */
1114 0, /* Array dimensions of typbasetype */
1115 false, /* Type NOT NULL */
1116 domaincoll); /* type's collation */
1117
1118 pfree(domainArrayName);
1119
1120 /*
1121 * Process constraints which refer to the domain ID returned by TypeCreate
1122 */
1123 foreach(listptr, schema)
1124 {
1125 Constraint *constr = lfirst(listptr);
1126
1127 /* it must be a Constraint, per check above */
1128
1129 switch (constr->contype)
1130 {
1131 case CONSTR_CHECK:
1132 domainAddConstraint(address.objectId, domainNamespace,
1133 basetypeoid, basetypeMod,
1134 constr, domainName, NULL);
1135 break;
1136
1137 /* Other constraint types were fully processed above */
1138
1139 default:
1140 break;
1141 }
1142
1143 /* CCI so we can detect duplicate constraint names */
1144 CommandCounterIncrement();
1145 }
1146
1147 /*
1148 * Now we can clean up.
1149 */
1150 ReleaseSysCache(typeTup);
1151
1152 return address;
1153 }
1154
1155
1156 /*
1157 * DefineEnum
1158 * Registers a new enum.
1159 */
1160 ObjectAddress
DefineEnum(CreateEnumStmt * stmt)1161 DefineEnum(CreateEnumStmt *stmt)
1162 {
1163 char *enumName;
1164 char *enumArrayName;
1165 Oid enumNamespace;
1166 AclResult aclresult;
1167 Oid old_type_oid;
1168 Oid enumArrayOid;
1169 ObjectAddress enumTypeAddr;
1170
1171 /* Convert list of names to a name and namespace */
1172 enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
1173 &enumName);
1174
1175 /* Check we have creation rights in target namespace */
1176 aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
1177 if (aclresult != ACLCHECK_OK)
1178 aclcheck_error(aclresult, OBJECT_SCHEMA,
1179 get_namespace_name(enumNamespace));
1180
1181 /*
1182 * Check for collision with an existing type name. If there is one and
1183 * it's an autogenerated array, we can rename it out of the way.
1184 */
1185 old_type_oid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
1186 CStringGetDatum(enumName),
1187 ObjectIdGetDatum(enumNamespace));
1188 if (OidIsValid(old_type_oid))
1189 {
1190 if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
1191 ereport(ERROR,
1192 (errcode(ERRCODE_DUPLICATE_OBJECT),
1193 errmsg("type \"%s\" already exists", enumName)));
1194 }
1195
1196 /* Allocate OID for array type */
1197 enumArrayOid = AssignTypeArrayOid();
1198
1199 /* Create the pg_type entry */
1200 enumTypeAddr =
1201 TypeCreate(InvalidOid, /* no predetermined type OID */
1202 enumName, /* type name */
1203 enumNamespace, /* namespace */
1204 InvalidOid, /* relation oid (n/a here) */
1205 0, /* relation kind (ditto) */
1206 GetUserId(), /* owner's ID */
1207 sizeof(Oid), /* internal size */
1208 TYPTYPE_ENUM, /* type-type (enum type) */
1209 TYPCATEGORY_ENUM, /* type-category (enum type) */
1210 false, /* enum types are never preferred */
1211 DEFAULT_TYPDELIM, /* array element delimiter */
1212 F_ENUM_IN, /* input procedure */
1213 F_ENUM_OUT, /* output procedure */
1214 F_ENUM_RECV, /* receive procedure */
1215 F_ENUM_SEND, /* send procedure */
1216 InvalidOid, /* typmodin procedure - none */
1217 InvalidOid, /* typmodout procedure - none */
1218 InvalidOid, /* analyze procedure - default */
1219 InvalidOid, /* element type ID */
1220 false, /* this is not an array type */
1221 enumArrayOid, /* array type we are about to create */
1222 InvalidOid, /* base type ID (only for domains) */
1223 NULL, /* never a default type value */
1224 NULL, /* binary default isn't sent either */
1225 true, /* always passed by value */
1226 'i', /* int alignment */
1227 'p', /* TOAST strategy always plain */
1228 -1, /* typMod (Domains only) */
1229 0, /* Array dimensions of typbasetype */
1230 false, /* Type NOT NULL */
1231 InvalidOid); /* type's collation */
1232
1233 /* Enter the enum's values into pg_enum */
1234 EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
1235
1236 /*
1237 * Create the array type that goes with it.
1238 */
1239 enumArrayName = makeArrayTypeName(enumName, enumNamespace);
1240
1241 TypeCreate(enumArrayOid, /* force assignment of this type OID */
1242 enumArrayName, /* type name */
1243 enumNamespace, /* namespace */
1244 InvalidOid, /* relation oid (n/a here) */
1245 0, /* relation kind (ditto) */
1246 GetUserId(), /* owner's ID */
1247 -1, /* internal size (always varlena) */
1248 TYPTYPE_BASE, /* type-type (base type) */
1249 TYPCATEGORY_ARRAY, /* type-category (array) */
1250 false, /* array types are never preferred */
1251 DEFAULT_TYPDELIM, /* array element delimiter */
1252 F_ARRAY_IN, /* input procedure */
1253 F_ARRAY_OUT, /* output procedure */
1254 F_ARRAY_RECV, /* receive procedure */
1255 F_ARRAY_SEND, /* send procedure */
1256 InvalidOid, /* typmodin procedure - none */
1257 InvalidOid, /* typmodout procedure - none */
1258 F_ARRAY_TYPANALYZE, /* analyze procedure */
1259 enumTypeAddr.objectId, /* element type ID */
1260 true, /* yes this is an array type */
1261 InvalidOid, /* no further array type */
1262 InvalidOid, /* base type ID */
1263 NULL, /* never a default type value */
1264 NULL, /* binary default isn't sent either */
1265 false, /* never passed by value */
1266 'i', /* enums have align i, so do their arrays */
1267 'x', /* ARRAY is always toastable */
1268 -1, /* typMod (Domains only) */
1269 0, /* Array dimensions of typbasetype */
1270 false, /* Type NOT NULL */
1271 InvalidOid); /* type's collation */
1272
1273 pfree(enumArrayName);
1274
1275 return enumTypeAddr;
1276 }
1277
1278 /*
1279 * AlterEnum
1280 * Adds a new label to an existing enum.
1281 */
1282 ObjectAddress
AlterEnum(AlterEnumStmt * stmt)1283 AlterEnum(AlterEnumStmt *stmt)
1284 {
1285 Oid enum_type_oid;
1286 TypeName *typename;
1287 HeapTuple tup;
1288 ObjectAddress address;
1289
1290 /* Make a TypeName so we can use standard type lookup machinery */
1291 typename = makeTypeNameFromNameList(stmt->typeName);
1292 enum_type_oid = typenameTypeId(NULL, typename);
1293
1294 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
1295 if (!HeapTupleIsValid(tup))
1296 elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
1297
1298 /* Check it's an enum and check user has permission to ALTER the enum */
1299 checkEnumOwner(tup);
1300
1301 ReleaseSysCache(tup);
1302
1303 if (stmt->oldVal)
1304 {
1305 /* Rename an existing label */
1306 RenameEnumLabel(enum_type_oid, stmt->oldVal, stmt->newVal);
1307 }
1308 else
1309 {
1310 /* Add a new label */
1311 AddEnumLabel(enum_type_oid, stmt->newVal,
1312 stmt->newValNeighbor, stmt->newValIsAfter,
1313 stmt->skipIfNewValExists);
1314 }
1315
1316 InvokeObjectPostAlterHook(TypeRelationId, enum_type_oid, 0);
1317
1318 ObjectAddressSet(address, TypeRelationId, enum_type_oid);
1319
1320 return address;
1321 }
1322
1323
1324 /*
1325 * checkEnumOwner
1326 *
1327 * Check that the type is actually an enum and that the current user
1328 * has permission to do ALTER TYPE on it. Throw an error if not.
1329 */
1330 static void
checkEnumOwner(HeapTuple tup)1331 checkEnumOwner(HeapTuple tup)
1332 {
1333 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
1334
1335 /* Check that this is actually an enum */
1336 if (typTup->typtype != TYPTYPE_ENUM)
1337 ereport(ERROR,
1338 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1339 errmsg("%s is not an enum",
1340 format_type_be(typTup->oid))));
1341
1342 /* Permission check: must own type */
1343 if (!pg_type_ownercheck(typTup->oid, GetUserId()))
1344 aclcheck_error_type(ACLCHECK_NOT_OWNER, typTup->oid);
1345 }
1346
1347
1348 /*
1349 * DefineRange
1350 * Registers a new range type.
1351 */
1352 ObjectAddress
DefineRange(CreateRangeStmt * stmt)1353 DefineRange(CreateRangeStmt *stmt)
1354 {
1355 char *typeName;
1356 Oid typeNamespace;
1357 Oid typoid;
1358 char *rangeArrayName;
1359 Oid rangeArrayOid;
1360 Oid rangeSubtype = InvalidOid;
1361 List *rangeSubOpclassName = NIL;
1362 List *rangeCollationName = NIL;
1363 List *rangeCanonicalName = NIL;
1364 List *rangeSubtypeDiffName = NIL;
1365 Oid rangeSubOpclass;
1366 Oid rangeCollation;
1367 regproc rangeCanonical;
1368 regproc rangeSubtypeDiff;
1369 int16 subtyplen;
1370 bool subtypbyval;
1371 char subtypalign;
1372 char alignment;
1373 AclResult aclresult;
1374 ListCell *lc;
1375 ObjectAddress address;
1376
1377 /* Convert list of names to a name and namespace */
1378 typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
1379 &typeName);
1380
1381 /* Check we have creation rights in target namespace */
1382 aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
1383 if (aclresult != ACLCHECK_OK)
1384 aclcheck_error(aclresult, OBJECT_SCHEMA,
1385 get_namespace_name(typeNamespace));
1386
1387 /*
1388 * Look to see if type already exists.
1389 */
1390 typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
1391 CStringGetDatum(typeName),
1392 ObjectIdGetDatum(typeNamespace));
1393
1394 /*
1395 * If it's not a shell, see if it's an autogenerated array type, and if so
1396 * rename it out of the way.
1397 */
1398 if (OidIsValid(typoid) && get_typisdefined(typoid))
1399 {
1400 if (moveArrayTypeName(typoid, typeName, typeNamespace))
1401 typoid = InvalidOid;
1402 else
1403 ereport(ERROR,
1404 (errcode(ERRCODE_DUPLICATE_OBJECT),
1405 errmsg("type \"%s\" already exists", typeName)));
1406 }
1407
1408 /*
1409 * If it doesn't exist, create it as a shell, so that the OID is known for
1410 * use in the range function definitions.
1411 */
1412 if (!OidIsValid(typoid))
1413 {
1414 address = TypeShellMake(typeName, typeNamespace, GetUserId());
1415 typoid = address.objectId;
1416 /* Make new shell type visible for modification below */
1417 CommandCounterIncrement();
1418 }
1419
1420 /* Extract the parameters from the parameter list */
1421 foreach(lc, stmt->params)
1422 {
1423 DefElem *defel = (DefElem *) lfirst(lc);
1424
1425 if (strcmp(defel->defname, "subtype") == 0)
1426 {
1427 if (OidIsValid(rangeSubtype))
1428 ereport(ERROR,
1429 (errcode(ERRCODE_SYNTAX_ERROR),
1430 errmsg("conflicting or redundant options")));
1431 /* we can look up the subtype name immediately */
1432 rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
1433 }
1434 else if (strcmp(defel->defname, "subtype_opclass") == 0)
1435 {
1436 if (rangeSubOpclassName != NIL)
1437 ereport(ERROR,
1438 (errcode(ERRCODE_SYNTAX_ERROR),
1439 errmsg("conflicting or redundant options")));
1440 rangeSubOpclassName = defGetQualifiedName(defel);
1441 }
1442 else if (strcmp(defel->defname, "collation") == 0)
1443 {
1444 if (rangeCollationName != NIL)
1445 ereport(ERROR,
1446 (errcode(ERRCODE_SYNTAX_ERROR),
1447 errmsg("conflicting or redundant options")));
1448 rangeCollationName = defGetQualifiedName(defel);
1449 }
1450 else if (strcmp(defel->defname, "canonical") == 0)
1451 {
1452 if (rangeCanonicalName != NIL)
1453 ereport(ERROR,
1454 (errcode(ERRCODE_SYNTAX_ERROR),
1455 errmsg("conflicting or redundant options")));
1456 rangeCanonicalName = defGetQualifiedName(defel);
1457 }
1458 else if (strcmp(defel->defname, "subtype_diff") == 0)
1459 {
1460 if (rangeSubtypeDiffName != NIL)
1461 ereport(ERROR,
1462 (errcode(ERRCODE_SYNTAX_ERROR),
1463 errmsg("conflicting or redundant options")));
1464 rangeSubtypeDiffName = defGetQualifiedName(defel);
1465 }
1466 else
1467 ereport(ERROR,
1468 (errcode(ERRCODE_SYNTAX_ERROR),
1469 errmsg("type attribute \"%s\" not recognized",
1470 defel->defname)));
1471 }
1472
1473 /* Must have a subtype */
1474 if (!OidIsValid(rangeSubtype))
1475 ereport(ERROR,
1476 (errcode(ERRCODE_SYNTAX_ERROR),
1477 errmsg("type attribute \"subtype\" is required")));
1478 /* disallow ranges of pseudotypes */
1479 if (get_typtype(rangeSubtype) == TYPTYPE_PSEUDO)
1480 ereport(ERROR,
1481 (errcode(ERRCODE_DATATYPE_MISMATCH),
1482 errmsg("range subtype cannot be %s",
1483 format_type_be(rangeSubtype))));
1484
1485 /* Identify subopclass */
1486 rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
1487
1488 /* Identify collation to use, if any */
1489 if (type_is_collatable(rangeSubtype))
1490 {
1491 if (rangeCollationName != NIL)
1492 rangeCollation = get_collation_oid(rangeCollationName, false);
1493 else
1494 rangeCollation = get_typcollation(rangeSubtype);
1495 }
1496 else
1497 {
1498 if (rangeCollationName != NIL)
1499 ereport(ERROR,
1500 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1501 errmsg("range collation specified but subtype does not support collation")));
1502 rangeCollation = InvalidOid;
1503 }
1504
1505 /* Identify support functions, if provided */
1506 if (rangeCanonicalName != NIL)
1507 rangeCanonical = findRangeCanonicalFunction(rangeCanonicalName,
1508 typoid);
1509 else
1510 rangeCanonical = InvalidOid;
1511
1512 if (rangeSubtypeDiffName != NIL)
1513 rangeSubtypeDiff = findRangeSubtypeDiffFunction(rangeSubtypeDiffName,
1514 rangeSubtype);
1515 else
1516 rangeSubtypeDiff = InvalidOid;
1517
1518 get_typlenbyvalalign(rangeSubtype,
1519 &subtyplen, &subtypbyval, &subtypalign);
1520
1521 /* alignment must be 'i' or 'd' for ranges */
1522 alignment = (subtypalign == 'd') ? 'd' : 'i';
1523
1524 /* Allocate OID for array type */
1525 rangeArrayOid = AssignTypeArrayOid();
1526
1527 /* Create the pg_type entry */
1528 address =
1529 TypeCreate(InvalidOid, /* no predetermined type OID */
1530 typeName, /* type name */
1531 typeNamespace, /* namespace */
1532 InvalidOid, /* relation oid (n/a here) */
1533 0, /* relation kind (ditto) */
1534 GetUserId(), /* owner's ID */
1535 -1, /* internal size (always varlena) */
1536 TYPTYPE_RANGE, /* type-type (range type) */
1537 TYPCATEGORY_RANGE, /* type-category (range type) */
1538 false, /* range types are never preferred */
1539 DEFAULT_TYPDELIM, /* array element delimiter */
1540 F_RANGE_IN, /* input procedure */
1541 F_RANGE_OUT, /* output procedure */
1542 F_RANGE_RECV, /* receive procedure */
1543 F_RANGE_SEND, /* send procedure */
1544 InvalidOid, /* typmodin procedure - none */
1545 InvalidOid, /* typmodout procedure - none */
1546 F_RANGE_TYPANALYZE, /* analyze procedure */
1547 InvalidOid, /* element type ID - none */
1548 false, /* this is not an array type */
1549 rangeArrayOid, /* array type we are about to create */
1550 InvalidOid, /* base type ID (only for domains) */
1551 NULL, /* never a default type value */
1552 NULL, /* no binary form available either */
1553 false, /* never passed by value */
1554 alignment, /* alignment */
1555 'x', /* TOAST strategy (always extended) */
1556 -1, /* typMod (Domains only) */
1557 0, /* Array dimensions of typbasetype */
1558 false, /* Type NOT NULL */
1559 InvalidOid); /* type's collation (ranges never have one) */
1560 Assert(typoid == address.objectId);
1561
1562 /* Create the entry in pg_range */
1563 RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
1564 rangeCanonical, rangeSubtypeDiff);
1565
1566 /*
1567 * Create the array type that goes with it.
1568 */
1569 rangeArrayName = makeArrayTypeName(typeName, typeNamespace);
1570
1571 TypeCreate(rangeArrayOid, /* force assignment of this type OID */
1572 rangeArrayName, /* type name */
1573 typeNamespace, /* namespace */
1574 InvalidOid, /* relation oid (n/a here) */
1575 0, /* relation kind (ditto) */
1576 GetUserId(), /* owner's ID */
1577 -1, /* internal size (always varlena) */
1578 TYPTYPE_BASE, /* type-type (base type) */
1579 TYPCATEGORY_ARRAY, /* type-category (array) */
1580 false, /* array types are never preferred */
1581 DEFAULT_TYPDELIM, /* array element delimiter */
1582 F_ARRAY_IN, /* input procedure */
1583 F_ARRAY_OUT, /* output procedure */
1584 F_ARRAY_RECV, /* receive procedure */
1585 F_ARRAY_SEND, /* send procedure */
1586 InvalidOid, /* typmodin procedure - none */
1587 InvalidOid, /* typmodout procedure - none */
1588 F_ARRAY_TYPANALYZE, /* analyze procedure */
1589 typoid, /* element type ID */
1590 true, /* yes this is an array type */
1591 InvalidOid, /* no further array type */
1592 InvalidOid, /* base type ID */
1593 NULL, /* never a default type value */
1594 NULL, /* binary default isn't sent either */
1595 false, /* never passed by value */
1596 alignment, /* alignment - same as range's */
1597 'x', /* ARRAY is always toastable */
1598 -1, /* typMod (Domains only) */
1599 0, /* Array dimensions of typbasetype */
1600 false, /* Type NOT NULL */
1601 InvalidOid); /* typcollation */
1602
1603 pfree(rangeArrayName);
1604
1605 /* And create the constructor functions for this range type */
1606 makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
1607
1608 return address;
1609 }
1610
1611 /*
1612 * Because there may exist several range types over the same subtype, the
1613 * range type can't be uniquely determined from the subtype. So it's
1614 * impossible to define a polymorphic constructor; we have to generate new
1615 * constructor functions explicitly for each range type.
1616 *
1617 * We actually define 4 functions, with 0 through 3 arguments. This is just
1618 * to offer more convenience for the user.
1619 */
1620 static void
makeRangeConstructors(const char * name,Oid namespace,Oid rangeOid,Oid subtype)1621 makeRangeConstructors(const char *name, Oid namespace,
1622 Oid rangeOid, Oid subtype)
1623 {
1624 static const char *const prosrc[2] = {"range_constructor2",
1625 "range_constructor3"};
1626 static const int pronargs[2] = {2, 3};
1627
1628 Oid constructorArgTypes[3];
1629 ObjectAddress myself,
1630 referenced;
1631 int i;
1632
1633 constructorArgTypes[0] = subtype;
1634 constructorArgTypes[1] = subtype;
1635 constructorArgTypes[2] = TEXTOID;
1636
1637 referenced.classId = TypeRelationId;
1638 referenced.objectId = rangeOid;
1639 referenced.objectSubId = 0;
1640
1641 for (i = 0; i < lengthof(prosrc); i++)
1642 {
1643 oidvector *constructorArgTypesVector;
1644
1645 constructorArgTypesVector = buildoidvector(constructorArgTypes,
1646 pronargs[i]);
1647
1648 myself = ProcedureCreate(name, /* name: same as range type */
1649 namespace, /* namespace */
1650 false, /* replace */
1651 false, /* returns set */
1652 rangeOid, /* return type */
1653 BOOTSTRAP_SUPERUSERID, /* proowner */
1654 INTERNALlanguageId, /* language */
1655 F_FMGR_INTERNAL_VALIDATOR, /* language validator */
1656 prosrc[i], /* prosrc */
1657 NULL, /* probin */
1658 PROKIND_FUNCTION,
1659 false, /* security_definer */
1660 false, /* leakproof */
1661 false, /* isStrict */
1662 PROVOLATILE_IMMUTABLE, /* volatility */
1663 PROPARALLEL_SAFE, /* parallel safety */
1664 constructorArgTypesVector, /* parameterTypes */
1665 PointerGetDatum(NULL), /* allParameterTypes */
1666 PointerGetDatum(NULL), /* parameterModes */
1667 PointerGetDatum(NULL), /* parameterNames */
1668 NIL, /* parameterDefaults */
1669 PointerGetDatum(NULL), /* trftypes */
1670 PointerGetDatum(NULL), /* proconfig */
1671 InvalidOid, /* prosupport */
1672 1.0, /* procost */
1673 0.0); /* prorows */
1674
1675 /*
1676 * Make the constructors internally-dependent on the range type so
1677 * that they go away silently when the type is dropped. Note that
1678 * pg_dump depends on this choice to avoid dumping the constructors.
1679 */
1680 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1681 }
1682 }
1683
1684
1685 /*
1686 * Find suitable I/O functions for a type.
1687 *
1688 * typeOid is the type's OID (which will already exist, if only as a shell
1689 * type).
1690 */
1691
1692 static Oid
findTypeInputFunction(List * procname,Oid typeOid)1693 findTypeInputFunction(List *procname, Oid typeOid)
1694 {
1695 Oid argList[3];
1696 int nmatches = 0;
1697 Oid procOid;
1698 Oid procOid2;
1699 Oid procOid3;
1700 Oid procOid4;
1701
1702 /*
1703 * Input functions can take a single argument of type CSTRING, or three
1704 * arguments (string, typioparam OID, typmod).
1705 *
1706 * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
1707 * see this, we issue a warning and fix up the pg_proc entry.
1708 *
1709 * Whine about ambiguity if multiple forms exist.
1710 */
1711 argList[0] = CSTRINGOID;
1712 argList[1] = OIDOID;
1713 argList[2] = INT4OID;
1714
1715 procOid = LookupFuncName(procname, 1, argList, true);
1716 if (OidIsValid(procOid))
1717 nmatches++;
1718 procOid2 = LookupFuncName(procname, 3, argList, true);
1719 if (OidIsValid(procOid2))
1720 nmatches++;
1721
1722 argList[0] = OPAQUEOID;
1723
1724 procOid3 = LookupFuncName(procname, 1, argList, true);
1725 if (OidIsValid(procOid3))
1726 nmatches++;
1727 procOid4 = LookupFuncName(procname, 3, argList, true);
1728 if (OidIsValid(procOid4))
1729 nmatches++;
1730
1731 if (nmatches > 1)
1732 ereport(ERROR,
1733 (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
1734 errmsg("type input function %s has multiple matches",
1735 NameListToString(procname))));
1736
1737 if (OidIsValid(procOid))
1738 return procOid;
1739 if (OidIsValid(procOid2))
1740 return procOid2;
1741
1742 /* Cases with OPAQUE need adjustment */
1743 if (OidIsValid(procOid3))
1744 procOid = procOid3;
1745 else
1746 procOid = procOid4;
1747
1748 if (OidIsValid(procOid))
1749 {
1750 /* Found, but must complain and fix the pg_proc entry */
1751 ereport(WARNING,
1752 (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
1753 NameListToString(procname))));
1754 SetFunctionArgType(procOid, 0, CSTRINGOID);
1755
1756 /*
1757 * Need CommandCounterIncrement since DefineType will likely try to
1758 * alter the pg_proc tuple again.
1759 */
1760 CommandCounterIncrement();
1761
1762 return procOid;
1763 }
1764
1765 /* Use CSTRING (preferred) in the error message */
1766 argList[0] = CSTRINGOID;
1767
1768 ereport(ERROR,
1769 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1770 errmsg("function %s does not exist",
1771 func_signature_string(procname, 1, NIL, argList))));
1772
1773 return InvalidOid; /* keep compiler quiet */
1774 }
1775
1776 static Oid
findTypeOutputFunction(List * procname,Oid typeOid)1777 findTypeOutputFunction(List *procname, Oid typeOid)
1778 {
1779 Oid argList[1];
1780 Oid procOid;
1781
1782 /*
1783 * Output functions can take a single argument of the type.
1784 *
1785 * For backwards compatibility we allow OPAQUE in place of the actual type
1786 * name; if we see this, we issue a warning and fix up the pg_proc entry.
1787 */
1788 argList[0] = typeOid;
1789
1790 procOid = LookupFuncName(procname, 1, argList, true);
1791 if (OidIsValid(procOid))
1792 return procOid;
1793
1794 /* No luck, try it with OPAQUE */
1795 argList[0] = OPAQUEOID;
1796
1797 procOid = LookupFuncName(procname, 1, argList, true);
1798
1799 if (OidIsValid(procOid))
1800 {
1801 /* Found, but must complain and fix the pg_proc entry */
1802 ereport(WARNING,
1803 (errmsg("changing argument type of function %s from \"opaque\" to %s",
1804 NameListToString(procname), format_type_be(typeOid))));
1805 SetFunctionArgType(procOid, 0, typeOid);
1806
1807 /*
1808 * Need CommandCounterIncrement since DefineType will likely try to
1809 * alter the pg_proc tuple again.
1810 */
1811 CommandCounterIncrement();
1812
1813 return procOid;
1814 }
1815
1816 /* Use type name, not OPAQUE, in the failure message. */
1817 argList[0] = typeOid;
1818
1819 ereport(ERROR,
1820 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1821 errmsg("function %s does not exist",
1822 func_signature_string(procname, 1, NIL, argList))));
1823
1824 return InvalidOid; /* keep compiler quiet */
1825 }
1826
1827 static Oid
findTypeReceiveFunction(List * procname,Oid typeOid)1828 findTypeReceiveFunction(List *procname, Oid typeOid)
1829 {
1830 Oid argList[3];
1831 Oid procOid;
1832 Oid procOid2;
1833
1834 /*
1835 * Receive functions can take a single argument of type INTERNAL, or three
1836 * arguments (internal, typioparam OID, typmod). Whine about ambiguity if
1837 * both forms exist.
1838 */
1839 argList[0] = INTERNALOID;
1840 argList[1] = OIDOID;
1841 argList[2] = INT4OID;
1842
1843 procOid = LookupFuncName(procname, 1, argList, true);
1844 procOid2 = LookupFuncName(procname, 3, argList, true);
1845 if (OidIsValid(procOid))
1846 {
1847 if (OidIsValid(procOid2))
1848 ereport(ERROR,
1849 (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
1850 errmsg("type receive function %s has multiple matches",
1851 NameListToString(procname))));
1852 return procOid;
1853 }
1854 else if (OidIsValid(procOid2))
1855 return procOid2;
1856
1857 /* If not found, reference the 1-argument signature in error msg */
1858 ereport(ERROR,
1859 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1860 errmsg("function %s does not exist",
1861 func_signature_string(procname, 1, NIL, argList))));
1862
1863 return InvalidOid; /* keep compiler quiet */
1864 }
1865
1866 static Oid
findTypeSendFunction(List * procname,Oid typeOid)1867 findTypeSendFunction(List *procname, Oid typeOid)
1868 {
1869 Oid argList[1];
1870 Oid procOid;
1871
1872 /*
1873 * Send functions can take a single argument of the type.
1874 */
1875 argList[0] = typeOid;
1876
1877 procOid = LookupFuncName(procname, 1, argList, true);
1878 if (OidIsValid(procOid))
1879 return procOid;
1880
1881 ereport(ERROR,
1882 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1883 errmsg("function %s does not exist",
1884 func_signature_string(procname, 1, NIL, argList))));
1885
1886 return InvalidOid; /* keep compiler quiet */
1887 }
1888
1889 static Oid
findTypeTypmodinFunction(List * procname)1890 findTypeTypmodinFunction(List *procname)
1891 {
1892 Oid argList[1];
1893 Oid procOid;
1894
1895 /*
1896 * typmodin functions always take one cstring[] argument and return int4.
1897 */
1898 argList[0] = CSTRINGARRAYOID;
1899
1900 procOid = LookupFuncName(procname, 1, argList, true);
1901 if (!OidIsValid(procOid))
1902 ereport(ERROR,
1903 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1904 errmsg("function %s does not exist",
1905 func_signature_string(procname, 1, NIL, argList))));
1906
1907 if (get_func_rettype(procOid) != INT4OID)
1908 ereport(ERROR,
1909 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1910 errmsg("typmod_in function %s must return type %s",
1911 NameListToString(procname), "integer")));
1912
1913 return procOid;
1914 }
1915
1916 static Oid
findTypeTypmodoutFunction(List * procname)1917 findTypeTypmodoutFunction(List *procname)
1918 {
1919 Oid argList[1];
1920 Oid procOid;
1921
1922 /*
1923 * typmodout functions always take one int4 argument and return cstring.
1924 */
1925 argList[0] = INT4OID;
1926
1927 procOid = LookupFuncName(procname, 1, argList, true);
1928 if (!OidIsValid(procOid))
1929 ereport(ERROR,
1930 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1931 errmsg("function %s does not exist",
1932 func_signature_string(procname, 1, NIL, argList))));
1933
1934 if (get_func_rettype(procOid) != CSTRINGOID)
1935 ereport(ERROR,
1936 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1937 errmsg("typmod_out function %s must return type %s",
1938 NameListToString(procname), "cstring")));
1939
1940 return procOid;
1941 }
1942
1943 static Oid
findTypeAnalyzeFunction(List * procname,Oid typeOid)1944 findTypeAnalyzeFunction(List *procname, Oid typeOid)
1945 {
1946 Oid argList[1];
1947 Oid procOid;
1948
1949 /*
1950 * Analyze functions always take one INTERNAL argument and return bool.
1951 */
1952 argList[0] = INTERNALOID;
1953
1954 procOid = LookupFuncName(procname, 1, argList, true);
1955 if (!OidIsValid(procOid))
1956 ereport(ERROR,
1957 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1958 errmsg("function %s does not exist",
1959 func_signature_string(procname, 1, NIL, argList))));
1960
1961 if (get_func_rettype(procOid) != BOOLOID)
1962 ereport(ERROR,
1963 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1964 errmsg("type analyze function %s must return type %s",
1965 NameListToString(procname), "boolean")));
1966
1967 return procOid;
1968 }
1969
1970 /*
1971 * Find suitable support functions and opclasses for a range type.
1972 */
1973
1974 /*
1975 * Find named btree opclass for subtype, or default btree opclass if
1976 * opcname is NIL.
1977 */
1978 static Oid
findRangeSubOpclass(List * opcname,Oid subtype)1979 findRangeSubOpclass(List *opcname, Oid subtype)
1980 {
1981 Oid opcid;
1982 Oid opInputType;
1983
1984 if (opcname != NIL)
1985 {
1986 opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
1987
1988 /*
1989 * Verify that the operator class accepts this datatype. Note we will
1990 * accept binary compatibility.
1991 */
1992 opInputType = get_opclass_input_type(opcid);
1993 if (!IsBinaryCoercible(subtype, opInputType))
1994 ereport(ERROR,
1995 (errcode(ERRCODE_DATATYPE_MISMATCH),
1996 errmsg("operator class \"%s\" does not accept data type %s",
1997 NameListToString(opcname),
1998 format_type_be(subtype))));
1999 }
2000 else
2001 {
2002 opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
2003 if (!OidIsValid(opcid))
2004 {
2005 /* We spell the error message identically to ResolveOpClass */
2006 ereport(ERROR,
2007 (errcode(ERRCODE_UNDEFINED_OBJECT),
2008 errmsg("data type %s has no default operator class for access method \"%s\"",
2009 format_type_be(subtype), "btree"),
2010 errhint("You must specify an operator class for the range type or define a default operator class for the subtype.")));
2011 }
2012 }
2013
2014 return opcid;
2015 }
2016
2017 static Oid
findRangeCanonicalFunction(List * procname,Oid typeOid)2018 findRangeCanonicalFunction(List *procname, Oid typeOid)
2019 {
2020 Oid argList[1];
2021 Oid procOid;
2022 AclResult aclresult;
2023
2024 /*
2025 * Range canonical functions must take and return the range type, and must
2026 * be immutable.
2027 */
2028 argList[0] = typeOid;
2029
2030 procOid = LookupFuncName(procname, 1, argList, true);
2031
2032 if (!OidIsValid(procOid))
2033 ereport(ERROR,
2034 (errcode(ERRCODE_UNDEFINED_FUNCTION),
2035 errmsg("function %s does not exist",
2036 func_signature_string(procname, 1, NIL, argList))));
2037
2038 if (get_func_rettype(procOid) != typeOid)
2039 ereport(ERROR,
2040 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2041 errmsg("range canonical function %s must return range type",
2042 func_signature_string(procname, 1, NIL, argList))));
2043
2044 if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
2045 ereport(ERROR,
2046 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2047 errmsg("range canonical function %s must be immutable",
2048 func_signature_string(procname, 1, NIL, argList))));
2049
2050 /* Also, range type's creator must have permission to call function */
2051 aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
2052 if (aclresult != ACLCHECK_OK)
2053 aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
2054
2055 return procOid;
2056 }
2057
2058 static Oid
findRangeSubtypeDiffFunction(List * procname,Oid subtype)2059 findRangeSubtypeDiffFunction(List *procname, Oid subtype)
2060 {
2061 Oid argList[2];
2062 Oid procOid;
2063 AclResult aclresult;
2064
2065 /*
2066 * Range subtype diff functions must take two arguments of the subtype,
2067 * must return float8, and must be immutable.
2068 */
2069 argList[0] = subtype;
2070 argList[1] = subtype;
2071
2072 procOid = LookupFuncName(procname, 2, argList, true);
2073
2074 if (!OidIsValid(procOid))
2075 ereport(ERROR,
2076 (errcode(ERRCODE_UNDEFINED_FUNCTION),
2077 errmsg("function %s does not exist",
2078 func_signature_string(procname, 2, NIL, argList))));
2079
2080 if (get_func_rettype(procOid) != FLOAT8OID)
2081 ereport(ERROR,
2082 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2083 errmsg("range subtype diff function %s must return type %s",
2084 func_signature_string(procname, 2, NIL, argList),
2085 "double precision")));
2086
2087 if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
2088 ereport(ERROR,
2089 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2090 errmsg("range subtype diff function %s must be immutable",
2091 func_signature_string(procname, 2, NIL, argList))));
2092
2093 /* Also, range type's creator must have permission to call function */
2094 aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
2095 if (aclresult != ACLCHECK_OK)
2096 aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
2097
2098 return procOid;
2099 }
2100
2101 /*
2102 * AssignTypeArrayOid
2103 *
2104 * Pre-assign the type's array OID for use in pg_type.typarray
2105 */
2106 Oid
AssignTypeArrayOid(void)2107 AssignTypeArrayOid(void)
2108 {
2109 Oid type_array_oid;
2110
2111 /* Use binary-upgrade override for pg_type.typarray? */
2112 if (IsBinaryUpgrade)
2113 {
2114 if (!OidIsValid(binary_upgrade_next_array_pg_type_oid))
2115 ereport(ERROR,
2116 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2117 errmsg("pg_type array OID value not set when in binary upgrade mode")));
2118
2119 type_array_oid = binary_upgrade_next_array_pg_type_oid;
2120 binary_upgrade_next_array_pg_type_oid = InvalidOid;
2121 }
2122 else
2123 {
2124 Relation pg_type = table_open(TypeRelationId, AccessShareLock);
2125
2126 type_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
2127 Anum_pg_type_oid);
2128 table_close(pg_type, AccessShareLock);
2129 }
2130
2131 return type_array_oid;
2132 }
2133
2134
2135 /*-------------------------------------------------------------------
2136 * DefineCompositeType
2137 *
2138 * Create a Composite Type relation.
2139 * `DefineRelation' does all the work, we just provide the correct
2140 * arguments!
2141 *
2142 * If the relation already exists, then 'DefineRelation' will abort
2143 * the xact...
2144 *
2145 * Return type is the new type's object address.
2146 *-------------------------------------------------------------------
2147 */
2148 ObjectAddress
DefineCompositeType(RangeVar * typevar,List * coldeflist)2149 DefineCompositeType(RangeVar *typevar, List *coldeflist)
2150 {
2151 CreateStmt *createStmt = makeNode(CreateStmt);
2152 Oid old_type_oid;
2153 Oid typeNamespace;
2154 ObjectAddress address;
2155
2156 /*
2157 * now set the parameters for keys/inheritance etc. All of these are
2158 * uninteresting for composite types...
2159 */
2160 createStmt->relation = typevar;
2161 createStmt->tableElts = coldeflist;
2162 createStmt->inhRelations = NIL;
2163 createStmt->constraints = NIL;
2164 createStmt->options = NIL;
2165 createStmt->oncommit = ONCOMMIT_NOOP;
2166 createStmt->tablespacename = NULL;
2167 createStmt->if_not_exists = false;
2168
2169 /*
2170 * Check for collision with an existing type name. If there is one and
2171 * it's an autogenerated array, we can rename it out of the way. This
2172 * check is here mainly to get a better error message about a "type"
2173 * instead of below about a "relation".
2174 */
2175 typeNamespace = RangeVarGetAndCheckCreationNamespace(createStmt->relation,
2176 NoLock, NULL);
2177 RangeVarAdjustRelationPersistence(createStmt->relation, typeNamespace);
2178 old_type_oid =
2179 GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
2180 CStringGetDatum(createStmt->relation->relname),
2181 ObjectIdGetDatum(typeNamespace));
2182 if (OidIsValid(old_type_oid))
2183 {
2184 if (!moveArrayTypeName(old_type_oid, createStmt->relation->relname, typeNamespace))
2185 ereport(ERROR,
2186 (errcode(ERRCODE_DUPLICATE_OBJECT),
2187 errmsg("type \"%s\" already exists", createStmt->relation->relname)));
2188 }
2189
2190 /*
2191 * Finally create the relation. This also creates the type.
2192 */
2193 DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
2194 NULL);
2195
2196 return address;
2197 }
2198
2199 /*
2200 * AlterDomainDefault
2201 *
2202 * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
2203 *
2204 * Returns ObjectAddress of the modified domain.
2205 */
2206 ObjectAddress
AlterDomainDefault(List * names,Node * defaultRaw)2207 AlterDomainDefault(List *names, Node *defaultRaw)
2208 {
2209 TypeName *typename;
2210 Oid domainoid;
2211 HeapTuple tup;
2212 ParseState *pstate;
2213 Relation rel;
2214 char *defaultValue;
2215 Node *defaultExpr = NULL; /* NULL if no default specified */
2216 Acl *typacl;
2217 Datum aclDatum;
2218 bool isNull;
2219 Datum new_record[Natts_pg_type];
2220 bool new_record_nulls[Natts_pg_type];
2221 bool new_record_repl[Natts_pg_type];
2222 HeapTuple newtuple;
2223 Form_pg_type typTup;
2224 ObjectAddress address;
2225
2226 /* Make a TypeName so we can use standard type lookup machinery */
2227 typename = makeTypeNameFromNameList(names);
2228 domainoid = typenameTypeId(NULL, typename);
2229
2230 /* Look up the domain in the type table */
2231 rel = table_open(TypeRelationId, RowExclusiveLock);
2232
2233 tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2234 if (!HeapTupleIsValid(tup))
2235 elog(ERROR, "cache lookup failed for type %u", domainoid);
2236 typTup = (Form_pg_type) GETSTRUCT(tup);
2237
2238 /* Check it's a domain and check user has permission for ALTER DOMAIN */
2239 checkDomainOwner(tup);
2240
2241 /* Setup new tuple */
2242 MemSet(new_record, (Datum) 0, sizeof(new_record));
2243 MemSet(new_record_nulls, false, sizeof(new_record_nulls));
2244 MemSet(new_record_repl, false, sizeof(new_record_repl));
2245
2246 /* Store the new default into the tuple */
2247 if (defaultRaw)
2248 {
2249 /* Create a dummy ParseState for transformExpr */
2250 pstate = make_parsestate(NULL);
2251
2252 /*
2253 * Cook the raw default into an expression; copy it in case the input
2254 * is in plan cache. Note: name is used only for error messages.
2255 */
2256 defaultExpr = cookDefault(pstate, copyObject(defaultRaw),
2257 typTup->typbasetype,
2258 typTup->typtypmod,
2259 NameStr(typTup->typname),
2260 0);
2261
2262 /*
2263 * If the expression is just a NULL constant, we treat the command
2264 * like ALTER ... DROP DEFAULT. (But see note for same test in
2265 * DefineDomain.)
2266 */
2267 if (defaultExpr == NULL ||
2268 (IsA(defaultExpr, Const) &&((Const *) defaultExpr)->constisnull))
2269 {
2270 /* Default is NULL, drop it */
2271 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
2272 new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2273 new_record_nulls[Anum_pg_type_typdefault - 1] = true;
2274 new_record_repl[Anum_pg_type_typdefault - 1] = true;
2275 }
2276 else
2277 {
2278 /*
2279 * Expression must be stored as a nodeToString result, but we also
2280 * require a valid textual representation (mainly to make life
2281 * easier for pg_dump).
2282 */
2283 defaultValue = deparse_expression(defaultExpr,
2284 NIL, false, false);
2285
2286 /*
2287 * Form an updated tuple with the new default and write it back.
2288 */
2289 new_record[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(nodeToString(defaultExpr));
2290
2291 new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2292 new_record[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultValue);
2293 new_record_repl[Anum_pg_type_typdefault - 1] = true;
2294 }
2295 }
2296 else
2297 {
2298 /* ALTER ... DROP DEFAULT */
2299 new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
2300 new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2301 new_record_nulls[Anum_pg_type_typdefault - 1] = true;
2302 new_record_repl[Anum_pg_type_typdefault - 1] = true;
2303 }
2304
2305 newtuple = heap_modify_tuple(tup, RelationGetDescr(rel),
2306 new_record, new_record_nulls,
2307 new_record_repl);
2308
2309 CatalogTupleUpdate(rel, &tup->t_self, newtuple);
2310
2311 /* Must extract ACL for use of GenerateTypeDependencies */
2312 aclDatum = heap_getattr(newtuple, Anum_pg_type_typacl,
2313 RelationGetDescr(rel), &isNull);
2314 if (isNull)
2315 typacl = NULL;
2316 else
2317 typacl = DatumGetAclPCopy(aclDatum);
2318
2319 /* Rebuild dependencies */
2320 GenerateTypeDependencies(domainoid,
2321 (Form_pg_type) GETSTRUCT(newtuple),
2322 defaultExpr,
2323 typacl,
2324 0, /* relation kind is n/a */
2325 false, /* a domain isn't an implicit array */
2326 false, /* nor is it any kind of dependent type */
2327 true); /* We do need to rebuild dependencies */
2328
2329 InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
2330
2331 ObjectAddressSet(address, TypeRelationId, domainoid);
2332
2333 /* Clean up */
2334 table_close(rel, RowExclusiveLock);
2335 heap_freetuple(newtuple);
2336
2337 return address;
2338 }
2339
2340 /*
2341 * AlterDomainNotNull
2342 *
2343 * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
2344 *
2345 * Returns ObjectAddress of the modified domain.
2346 */
2347 ObjectAddress
AlterDomainNotNull(List * names,bool notNull)2348 AlterDomainNotNull(List *names, bool notNull)
2349 {
2350 TypeName *typename;
2351 Oid domainoid;
2352 Relation typrel;
2353 HeapTuple tup;
2354 Form_pg_type typTup;
2355 ObjectAddress address = InvalidObjectAddress;
2356
2357 /* Make a TypeName so we can use standard type lookup machinery */
2358 typename = makeTypeNameFromNameList(names);
2359 domainoid = typenameTypeId(NULL, typename);
2360
2361 /* Look up the domain in the type table */
2362 typrel = table_open(TypeRelationId, RowExclusiveLock);
2363
2364 tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2365 if (!HeapTupleIsValid(tup))
2366 elog(ERROR, "cache lookup failed for type %u", domainoid);
2367 typTup = (Form_pg_type) GETSTRUCT(tup);
2368
2369 /* Check it's a domain and check user has permission for ALTER DOMAIN */
2370 checkDomainOwner(tup);
2371
2372 /* Is the domain already set to the desired constraint? */
2373 if (typTup->typnotnull == notNull)
2374 {
2375 table_close(typrel, RowExclusiveLock);
2376 return address;
2377 }
2378
2379 /* Adding a NOT NULL constraint requires checking existing columns */
2380 if (notNull)
2381 {
2382 List *rels;
2383 ListCell *rt;
2384
2385 /* Fetch relation list with attributes based on this domain */
2386 /* ShareLock is sufficient to prevent concurrent data changes */
2387
2388 rels = get_rels_with_domain(domainoid, ShareLock);
2389
2390 foreach(rt, rels)
2391 {
2392 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
2393 Relation testrel = rtc->rel;
2394 TupleDesc tupdesc = RelationGetDescr(testrel);
2395 TupleTableSlot *slot;
2396 TableScanDesc scan;
2397 Snapshot snapshot;
2398
2399 /* Scan all tuples in this relation */
2400 snapshot = RegisterSnapshot(GetLatestSnapshot());
2401 scan = table_beginscan(testrel, snapshot, 0, NULL);
2402 slot = table_slot_create(testrel, NULL);
2403 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
2404 {
2405 int i;
2406
2407 /* Test attributes that are of the domain */
2408 for (i = 0; i < rtc->natts; i++)
2409 {
2410 int attnum = rtc->atts[i];
2411 Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2412
2413 if (slot_attisnull(slot, attnum))
2414 {
2415 /*
2416 * In principle the auxiliary information for this
2417 * error should be errdatatype(), but errtablecol()
2418 * seems considerably more useful in practice. Since
2419 * this code only executes in an ALTER DOMAIN command,
2420 * the client should already know which domain is in
2421 * question.
2422 */
2423 ereport(ERROR,
2424 (errcode(ERRCODE_NOT_NULL_VIOLATION),
2425 errmsg("column \"%s\" of table \"%s\" contains null values",
2426 NameStr(attr->attname),
2427 RelationGetRelationName(testrel)),
2428 errtablecol(testrel, attnum)));
2429 }
2430 }
2431 }
2432 ExecDropSingleTupleTableSlot(slot);
2433 table_endscan(scan);
2434 UnregisterSnapshot(snapshot);
2435
2436 /* Close each rel after processing, but keep lock */
2437 table_close(testrel, NoLock);
2438 }
2439 }
2440
2441 /*
2442 * Okay to update pg_type row. We can scribble on typTup because it's a
2443 * copy.
2444 */
2445 typTup->typnotnull = notNull;
2446
2447 CatalogTupleUpdate(typrel, &tup->t_self, tup);
2448
2449 InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
2450
2451 ObjectAddressSet(address, TypeRelationId, domainoid);
2452
2453 /* Clean up */
2454 heap_freetuple(tup);
2455 table_close(typrel, RowExclusiveLock);
2456
2457 return address;
2458 }
2459
2460 /*
2461 * AlterDomainDropConstraint
2462 *
2463 * Implements the ALTER DOMAIN DROP CONSTRAINT statement
2464 *
2465 * Returns ObjectAddress of the modified domain.
2466 */
2467 ObjectAddress
AlterDomainDropConstraint(List * names,const char * constrName,DropBehavior behavior,bool missing_ok)2468 AlterDomainDropConstraint(List *names, const char *constrName,
2469 DropBehavior behavior, bool missing_ok)
2470 {
2471 TypeName *typename;
2472 Oid domainoid;
2473 HeapTuple tup;
2474 Relation rel;
2475 Relation conrel;
2476 SysScanDesc conscan;
2477 ScanKeyData skey[3];
2478 HeapTuple contup;
2479 bool found = false;
2480 ObjectAddress address;
2481
2482 /* Make a TypeName so we can use standard type lookup machinery */
2483 typename = makeTypeNameFromNameList(names);
2484 domainoid = typenameTypeId(NULL, typename);
2485
2486 /* Look up the domain in the type table */
2487 rel = table_open(TypeRelationId, RowExclusiveLock);
2488
2489 tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2490 if (!HeapTupleIsValid(tup))
2491 elog(ERROR, "cache lookup failed for type %u", domainoid);
2492
2493 /* Check it's a domain and check user has permission for ALTER DOMAIN */
2494 checkDomainOwner(tup);
2495
2496 /* Grab an appropriate lock on the pg_constraint relation */
2497 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
2498
2499 /* Find and remove the target constraint */
2500 ScanKeyInit(&skey[0],
2501 Anum_pg_constraint_conrelid,
2502 BTEqualStrategyNumber, F_OIDEQ,
2503 ObjectIdGetDatum(InvalidOid));
2504 ScanKeyInit(&skey[1],
2505 Anum_pg_constraint_contypid,
2506 BTEqualStrategyNumber, F_OIDEQ,
2507 ObjectIdGetDatum(domainoid));
2508 ScanKeyInit(&skey[2],
2509 Anum_pg_constraint_conname,
2510 BTEqualStrategyNumber, F_NAMEEQ,
2511 CStringGetDatum(constrName));
2512
2513 conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
2514 NULL, 3, skey);
2515
2516 /* There can be at most one matching row */
2517 if ((contup = systable_getnext(conscan)) != NULL)
2518 {
2519 ObjectAddress conobj;
2520
2521 conobj.classId = ConstraintRelationId;
2522 conobj.objectId = ((Form_pg_constraint) GETSTRUCT(contup))->oid;
2523 conobj.objectSubId = 0;
2524
2525 performDeletion(&conobj, behavior, 0);
2526 found = true;
2527 }
2528
2529 /* Clean up after the scan */
2530 systable_endscan(conscan);
2531 table_close(conrel, RowExclusiveLock);
2532
2533 if (!found)
2534 {
2535 if (!missing_ok)
2536 ereport(ERROR,
2537 (errcode(ERRCODE_UNDEFINED_OBJECT),
2538 errmsg("constraint \"%s\" of domain \"%s\" does not exist",
2539 constrName, TypeNameToString(typename))));
2540 else
2541 ereport(NOTICE,
2542 (errmsg("constraint \"%s\" of domain \"%s\" does not exist, skipping",
2543 constrName, TypeNameToString(typename))));
2544 }
2545
2546 /*
2547 * We must send out an sinval message for the domain, to ensure that any
2548 * dependent plans get rebuilt. Since this command doesn't change the
2549 * domain's pg_type row, that won't happen automatically; do it manually.
2550 */
2551 CacheInvalidateHeapTuple(rel, tup, NULL);
2552
2553 ObjectAddressSet(address, TypeRelationId, domainoid);
2554
2555 /* Clean up */
2556 table_close(rel, RowExclusiveLock);
2557
2558 return address;
2559 }
2560
2561 /*
2562 * AlterDomainAddConstraint
2563 *
2564 * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
2565 */
2566 ObjectAddress
AlterDomainAddConstraint(List * names,Node * newConstraint,ObjectAddress * constrAddr)2567 AlterDomainAddConstraint(List *names, Node *newConstraint,
2568 ObjectAddress *constrAddr)
2569 {
2570 TypeName *typename;
2571 Oid domainoid;
2572 Relation typrel;
2573 HeapTuple tup;
2574 Form_pg_type typTup;
2575 Constraint *constr;
2576 char *ccbin;
2577 ObjectAddress address;
2578
2579 /* Make a TypeName so we can use standard type lookup machinery */
2580 typename = makeTypeNameFromNameList(names);
2581 domainoid = typenameTypeId(NULL, typename);
2582
2583 /* Look up the domain in the type table */
2584 typrel = table_open(TypeRelationId, RowExclusiveLock);
2585
2586 tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2587 if (!HeapTupleIsValid(tup))
2588 elog(ERROR, "cache lookup failed for type %u", domainoid);
2589 typTup = (Form_pg_type) GETSTRUCT(tup);
2590
2591 /* Check it's a domain and check user has permission for ALTER DOMAIN */
2592 checkDomainOwner(tup);
2593
2594 if (!IsA(newConstraint, Constraint))
2595 elog(ERROR, "unrecognized node type: %d",
2596 (int) nodeTag(newConstraint));
2597
2598 constr = (Constraint *) newConstraint;
2599
2600 switch (constr->contype)
2601 {
2602 case CONSTR_CHECK:
2603 /* processed below */
2604 break;
2605
2606 case CONSTR_UNIQUE:
2607 ereport(ERROR,
2608 (errcode(ERRCODE_SYNTAX_ERROR),
2609 errmsg("unique constraints not possible for domains")));
2610 break;
2611
2612 case CONSTR_PRIMARY:
2613 ereport(ERROR,
2614 (errcode(ERRCODE_SYNTAX_ERROR),
2615 errmsg("primary key constraints not possible for domains")));
2616 break;
2617
2618 case CONSTR_EXCLUSION:
2619 ereport(ERROR,
2620 (errcode(ERRCODE_SYNTAX_ERROR),
2621 errmsg("exclusion constraints not possible for domains")));
2622 break;
2623
2624 case CONSTR_FOREIGN:
2625 ereport(ERROR,
2626 (errcode(ERRCODE_SYNTAX_ERROR),
2627 errmsg("foreign key constraints not possible for domains")));
2628 break;
2629
2630 case CONSTR_ATTR_DEFERRABLE:
2631 case CONSTR_ATTR_NOT_DEFERRABLE:
2632 case CONSTR_ATTR_DEFERRED:
2633 case CONSTR_ATTR_IMMEDIATE:
2634 ereport(ERROR,
2635 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2636 errmsg("specifying constraint deferrability not supported for domains")));
2637 break;
2638
2639 default:
2640 elog(ERROR, "unrecognized constraint subtype: %d",
2641 (int) constr->contype);
2642 break;
2643 }
2644
2645 /*
2646 * Since all other constraint types throw errors, this must be a check
2647 * constraint. First, process the constraint expression and add an entry
2648 * to pg_constraint.
2649 */
2650
2651 ccbin = domainAddConstraint(domainoid, typTup->typnamespace,
2652 typTup->typbasetype, typTup->typtypmod,
2653 constr, NameStr(typTup->typname), constrAddr);
2654
2655 /*
2656 * If requested to validate the constraint, test all values stored in the
2657 * attributes based on the domain the constraint is being added to.
2658 */
2659 if (!constr->skip_validation)
2660 validateDomainConstraint(domainoid, ccbin);
2661
2662 /*
2663 * We must send out an sinval message for the domain, to ensure that any
2664 * dependent plans get rebuilt. Since this command doesn't change the
2665 * domain's pg_type row, that won't happen automatically; do it manually.
2666 */
2667 CacheInvalidateHeapTuple(typrel, tup, NULL);
2668
2669 ObjectAddressSet(address, TypeRelationId, domainoid);
2670
2671 /* Clean up */
2672 table_close(typrel, RowExclusiveLock);
2673
2674 return address;
2675 }
2676
2677 /*
2678 * AlterDomainValidateConstraint
2679 *
2680 * Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement.
2681 */
2682 ObjectAddress
AlterDomainValidateConstraint(List * names,const char * constrName)2683 AlterDomainValidateConstraint(List *names, const char *constrName)
2684 {
2685 TypeName *typename;
2686 Oid domainoid;
2687 Relation typrel;
2688 Relation conrel;
2689 HeapTuple tup;
2690 Form_pg_constraint con;
2691 Form_pg_constraint copy_con;
2692 char *conbin;
2693 SysScanDesc scan;
2694 Datum val;
2695 bool isnull;
2696 HeapTuple tuple;
2697 HeapTuple copyTuple;
2698 ScanKeyData skey[3];
2699 ObjectAddress address;
2700
2701 /* Make a TypeName so we can use standard type lookup machinery */
2702 typename = makeTypeNameFromNameList(names);
2703 domainoid = typenameTypeId(NULL, typename);
2704
2705 /* Look up the domain in the type table */
2706 typrel = table_open(TypeRelationId, AccessShareLock);
2707
2708 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(domainoid));
2709 if (!HeapTupleIsValid(tup))
2710 elog(ERROR, "cache lookup failed for type %u", domainoid);
2711
2712 /* Check it's a domain and check user has permission for ALTER DOMAIN */
2713 checkDomainOwner(tup);
2714
2715 /*
2716 * Find and check the target constraint
2717 */
2718 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
2719
2720 ScanKeyInit(&skey[0],
2721 Anum_pg_constraint_conrelid,
2722 BTEqualStrategyNumber, F_OIDEQ,
2723 ObjectIdGetDatum(InvalidOid));
2724 ScanKeyInit(&skey[1],
2725 Anum_pg_constraint_contypid,
2726 BTEqualStrategyNumber, F_OIDEQ,
2727 ObjectIdGetDatum(domainoid));
2728 ScanKeyInit(&skey[2],
2729 Anum_pg_constraint_conname,
2730 BTEqualStrategyNumber, F_NAMEEQ,
2731 CStringGetDatum(constrName));
2732
2733 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
2734 NULL, 3, skey);
2735
2736 /* There can be at most one matching row */
2737 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
2738 ereport(ERROR,
2739 (errcode(ERRCODE_UNDEFINED_OBJECT),
2740 errmsg("constraint \"%s\" of domain \"%s\" does not exist",
2741 constrName, TypeNameToString(typename))));
2742
2743 con = (Form_pg_constraint) GETSTRUCT(tuple);
2744 if (con->contype != CONSTRAINT_CHECK)
2745 ereport(ERROR,
2746 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2747 errmsg("constraint \"%s\" of domain \"%s\" is not a check constraint",
2748 constrName, TypeNameToString(typename))));
2749
2750 val = SysCacheGetAttr(CONSTROID, tuple,
2751 Anum_pg_constraint_conbin,
2752 &isnull);
2753 if (isnull)
2754 elog(ERROR, "null conbin for constraint %u",
2755 con->oid);
2756 conbin = TextDatumGetCString(val);
2757
2758 validateDomainConstraint(domainoid, conbin);
2759
2760 /*
2761 * Now update the catalog, while we have the door open.
2762 */
2763 copyTuple = heap_copytuple(tuple);
2764 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
2765 copy_con->convalidated = true;
2766 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
2767
2768 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
2769
2770 ObjectAddressSet(address, TypeRelationId, domainoid);
2771
2772 heap_freetuple(copyTuple);
2773
2774 systable_endscan(scan);
2775
2776 table_close(typrel, AccessShareLock);
2777 table_close(conrel, RowExclusiveLock);
2778
2779 ReleaseSysCache(tup);
2780
2781 return address;
2782 }
2783
2784 static void
validateDomainConstraint(Oid domainoid,char * ccbin)2785 validateDomainConstraint(Oid domainoid, char *ccbin)
2786 {
2787 Expr *expr = (Expr *) stringToNode(ccbin);
2788 List *rels;
2789 ListCell *rt;
2790 EState *estate;
2791 ExprContext *econtext;
2792 ExprState *exprstate;
2793
2794 /* Need an EState to run ExecEvalExpr */
2795 estate = CreateExecutorState();
2796 econtext = GetPerTupleExprContext(estate);
2797
2798 /* build execution state for expr */
2799 exprstate = ExecPrepareExpr(expr, estate);
2800
2801 /* Fetch relation list with attributes based on this domain */
2802 /* ShareLock is sufficient to prevent concurrent data changes */
2803
2804 rels = get_rels_with_domain(domainoid, ShareLock);
2805
2806 foreach(rt, rels)
2807 {
2808 RelToCheck *rtc = (RelToCheck *) lfirst(rt);
2809 Relation testrel = rtc->rel;
2810 TupleDesc tupdesc = RelationGetDescr(testrel);
2811 TupleTableSlot *slot;
2812 TableScanDesc scan;
2813 Snapshot snapshot;
2814
2815 /* Scan all tuples in this relation */
2816 snapshot = RegisterSnapshot(GetLatestSnapshot());
2817 scan = table_beginscan(testrel, snapshot, 0, NULL);
2818 slot = table_slot_create(testrel, NULL);
2819 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
2820 {
2821 int i;
2822
2823 /* Test attributes that are of the domain */
2824 for (i = 0; i < rtc->natts; i++)
2825 {
2826 int attnum = rtc->atts[i];
2827 Datum d;
2828 bool isNull;
2829 Datum conResult;
2830 Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2831
2832 d = slot_getattr(slot, attnum, &isNull);
2833
2834 econtext->domainValue_datum = d;
2835 econtext->domainValue_isNull = isNull;
2836
2837 conResult = ExecEvalExprSwitchContext(exprstate,
2838 econtext,
2839 &isNull);
2840
2841 if (!isNull && !DatumGetBool(conResult))
2842 {
2843 /*
2844 * In principle the auxiliary information for this error
2845 * should be errdomainconstraint(), but errtablecol()
2846 * seems considerably more useful in practice. Since this
2847 * code only executes in an ALTER DOMAIN command, the
2848 * client should already know which domain is in question,
2849 * and which constraint too.
2850 */
2851 ereport(ERROR,
2852 (errcode(ERRCODE_CHECK_VIOLATION),
2853 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
2854 NameStr(attr->attname),
2855 RelationGetRelationName(testrel)),
2856 errtablecol(testrel, attnum)));
2857 }
2858 }
2859
2860 ResetExprContext(econtext);
2861 }
2862 ExecDropSingleTupleTableSlot(slot);
2863 table_endscan(scan);
2864 UnregisterSnapshot(snapshot);
2865
2866 /* Hold relation lock till commit (XXX bad for concurrency) */
2867 table_close(testrel, NoLock);
2868 }
2869
2870 FreeExecutorState(estate);
2871 }
2872
2873 /*
2874 * get_rels_with_domain
2875 *
2876 * Fetch all relations / attributes which are using the domain
2877 *
2878 * The result is a list of RelToCheck structs, one for each distinct
2879 * relation, each containing one or more attribute numbers that are of
2880 * the domain type. We have opened each rel and acquired the specified lock
2881 * type on it.
2882 *
2883 * We support nested domains by including attributes that are of derived
2884 * domain types. Current callers do not need to distinguish between attributes
2885 * that are of exactly the given domain and those that are of derived domains.
2886 *
2887 * XXX this is completely broken because there is no way to lock the domain
2888 * to prevent columns from being added or dropped while our command runs.
2889 * We can partially protect against column drops by locking relations as we
2890 * come across them, but there is still a race condition (the window between
2891 * seeing a pg_depend entry and acquiring lock on the relation it references).
2892 * Also, holding locks on all these relations simultaneously creates a non-
2893 * trivial risk of deadlock. We can minimize but not eliminate the deadlock
2894 * risk by using the weakest suitable lock (ShareLock for most callers).
2895 *
2896 * XXX the API for this is not sufficient to support checking domain values
2897 * that are inside container types, such as composite types, arrays, or
2898 * ranges. Currently we just error out if a container type containing the
2899 * target domain is stored anywhere.
2900 *
2901 * Generally used for retrieving a list of tests when adding
2902 * new constraints to a domain.
2903 */
2904 static List *
get_rels_with_domain(Oid domainOid,LOCKMODE lockmode)2905 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
2906 {
2907 List *result = NIL;
2908 char *domainTypeName = format_type_be(domainOid);
2909 Relation depRel;
2910 ScanKeyData key[2];
2911 SysScanDesc depScan;
2912 HeapTuple depTup;
2913
2914 Assert(lockmode != NoLock);
2915
2916 /* since this function recurses, it could be driven to stack overflow */
2917 check_stack_depth();
2918
2919 /*
2920 * We scan pg_depend to find those things that depend on the domain. (We
2921 * assume we can ignore refobjsubid for a domain.)
2922 */
2923 depRel = table_open(DependRelationId, AccessShareLock);
2924
2925 ScanKeyInit(&key[0],
2926 Anum_pg_depend_refclassid,
2927 BTEqualStrategyNumber, F_OIDEQ,
2928 ObjectIdGetDatum(TypeRelationId));
2929 ScanKeyInit(&key[1],
2930 Anum_pg_depend_refobjid,
2931 BTEqualStrategyNumber, F_OIDEQ,
2932 ObjectIdGetDatum(domainOid));
2933
2934 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
2935 NULL, 2, key);
2936
2937 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2938 {
2939 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2940 RelToCheck *rtc = NULL;
2941 ListCell *rellist;
2942 Form_pg_attribute pg_att;
2943 int ptr;
2944
2945 /* Check for directly dependent types */
2946 if (pg_depend->classid == TypeRelationId)
2947 {
2948 if (get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN)
2949 {
2950 /*
2951 * This is a sub-domain, so recursively add dependent columns
2952 * to the output list. This is a bit inefficient since we may
2953 * fail to combine RelToCheck entries when attributes of the
2954 * same rel have different derived domain types, but it's
2955 * probably not worth improving.
2956 */
2957 result = list_concat(result,
2958 get_rels_with_domain(pg_depend->objid,
2959 lockmode));
2960 }
2961 else
2962 {
2963 /*
2964 * Otherwise, it is some container type using the domain, so
2965 * fail if there are any columns of this type.
2966 */
2967 find_composite_type_dependencies(pg_depend->objid,
2968 NULL,
2969 domainTypeName);
2970 }
2971 continue;
2972 }
2973
2974 /* Else, ignore dependees that aren't user columns of relations */
2975 /* (we assume system columns are never of domain types) */
2976 if (pg_depend->classid != RelationRelationId ||
2977 pg_depend->objsubid <= 0)
2978 continue;
2979
2980 /* See if we already have an entry for this relation */
2981 foreach(rellist, result)
2982 {
2983 RelToCheck *rt = (RelToCheck *) lfirst(rellist);
2984
2985 if (RelationGetRelid(rt->rel) == pg_depend->objid)
2986 {
2987 rtc = rt;
2988 break;
2989 }
2990 }
2991
2992 if (rtc == NULL)
2993 {
2994 /* First attribute found for this relation */
2995 Relation rel;
2996
2997 /* Acquire requested lock on relation */
2998 rel = relation_open(pg_depend->objid, lockmode);
2999
3000 /*
3001 * Check to see if rowtype is stored anyplace as a composite-type
3002 * column; if so we have to fail, for now anyway.
3003 */
3004 if (OidIsValid(rel->rd_rel->reltype))
3005 find_composite_type_dependencies(rel->rd_rel->reltype,
3006 NULL,
3007 domainTypeName);
3008
3009 /*
3010 * Otherwise, we can ignore relations except those with both
3011 * storage and user-chosen column types.
3012 *
3013 * XXX If an index-only scan could satisfy "col::some_domain" from
3014 * a suitable expression index, this should also check expression
3015 * index columns.
3016 */
3017 if (rel->rd_rel->relkind != RELKIND_RELATION &&
3018 rel->rd_rel->relkind != RELKIND_MATVIEW)
3019 {
3020 relation_close(rel, lockmode);
3021 continue;
3022 }
3023
3024 /* Build the RelToCheck entry with enough space for all atts */
3025 rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
3026 rtc->rel = rel;
3027 rtc->natts = 0;
3028 rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
3029 result = lcons(rtc, result);
3030 }
3031
3032 /*
3033 * Confirm column has not been dropped, and is of the expected type.
3034 * This defends against an ALTER DROP COLUMN occurring just before we
3035 * acquired lock ... but if the whole table were dropped, we'd still
3036 * have a problem.
3037 */
3038 if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
3039 continue;
3040 pg_att = TupleDescAttr(rtc->rel->rd_att, pg_depend->objsubid - 1);
3041 if (pg_att->attisdropped || pg_att->atttypid != domainOid)
3042 continue;
3043
3044 /*
3045 * Okay, add column to result. We store the columns in column-number
3046 * order; this is just a hack to improve predictability of regression
3047 * test output ...
3048 */
3049 Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
3050
3051 ptr = rtc->natts++;
3052 while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
3053 {
3054 rtc->atts[ptr] = rtc->atts[ptr - 1];
3055 ptr--;
3056 }
3057 rtc->atts[ptr] = pg_depend->objsubid;
3058 }
3059
3060 systable_endscan(depScan);
3061
3062 relation_close(depRel, AccessShareLock);
3063
3064 return result;
3065 }
3066
3067 /*
3068 * checkDomainOwner
3069 *
3070 * Check that the type is actually a domain and that the current user
3071 * has permission to do ALTER DOMAIN on it. Throw an error if not.
3072 */
3073 void
checkDomainOwner(HeapTuple tup)3074 checkDomainOwner(HeapTuple tup)
3075 {
3076 Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
3077
3078 /* Check that this is actually a domain */
3079 if (typTup->typtype != TYPTYPE_DOMAIN)
3080 ereport(ERROR,
3081 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3082 errmsg("%s is not a domain",
3083 format_type_be(typTup->oid))));
3084
3085 /* Permission check: must own type */
3086 if (!pg_type_ownercheck(typTup->oid, GetUserId()))
3087 aclcheck_error_type(ACLCHECK_NOT_OWNER, typTup->oid);
3088 }
3089
3090 /*
3091 * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
3092 */
3093 static char *
domainAddConstraint(Oid domainOid,Oid domainNamespace,Oid baseTypeOid,int typMod,Constraint * constr,const char * domainName,ObjectAddress * constrAddr)3094 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
3095 int typMod, Constraint *constr,
3096 const char *domainName, ObjectAddress *constrAddr)
3097 {
3098 Node *expr;
3099 char *ccbin;
3100 ParseState *pstate;
3101 CoerceToDomainValue *domVal;
3102 Oid ccoid;
3103
3104 /*
3105 * Assign or validate constraint name
3106 */
3107 if (constr->conname)
3108 {
3109 if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
3110 domainOid,
3111 constr->conname))
3112 ereport(ERROR,
3113 (errcode(ERRCODE_DUPLICATE_OBJECT),
3114 errmsg("constraint \"%s\" for domain \"%s\" already exists",
3115 constr->conname, domainName)));
3116 }
3117 else
3118 constr->conname = ChooseConstraintName(domainName,
3119 NULL,
3120 "check",
3121 domainNamespace,
3122 NIL);
3123
3124 /*
3125 * Convert the A_EXPR in raw_expr into an EXPR
3126 */
3127 pstate = make_parsestate(NULL);
3128
3129 /*
3130 * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
3131 * the expression. Note that it will appear to have the type of the base
3132 * type, not the domain. This seems correct since within the check
3133 * expression, we should not assume the input value can be considered a
3134 * member of the domain.
3135 */
3136 domVal = makeNode(CoerceToDomainValue);
3137 domVal->typeId = baseTypeOid;
3138 domVal->typeMod = typMod;
3139 domVal->collation = get_typcollation(baseTypeOid);
3140 domVal->location = -1; /* will be set when/if used */
3141
3142 pstate->p_pre_columnref_hook = replace_domain_constraint_value;
3143 pstate->p_ref_hook_state = (void *) domVal;
3144
3145 /*
3146 * Transform the expression; first we must copy the input, in case it's in
3147 * plan cache.
3148 */
3149 expr = transformExpr(pstate, copyObject(constr->raw_expr),
3150 EXPR_KIND_DOMAIN_CHECK);
3151
3152 /*
3153 * Make sure it yields a boolean result.
3154 */
3155 expr = coerce_to_boolean(pstate, expr, "CHECK");
3156
3157 /*
3158 * Fix up collation information.
3159 */
3160 assign_expr_collations(pstate, expr);
3161
3162 /*
3163 * Domains don't allow variables (this is probably dead code now that
3164 * add_missing_from is history, but let's be sure).
3165 */
3166 if (list_length(pstate->p_rtable) != 0 ||
3167 contain_var_clause(expr))
3168 ereport(ERROR,
3169 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
3170 errmsg("cannot use table references in domain check constraint")));
3171
3172 /*
3173 * Convert to string form for storage.
3174 */
3175 ccbin = nodeToString(expr);
3176
3177 /*
3178 * Store the constraint in pg_constraint
3179 */
3180 ccoid =
3181 CreateConstraintEntry(constr->conname, /* Constraint Name */
3182 domainNamespace, /* namespace */
3183 CONSTRAINT_CHECK, /* Constraint Type */
3184 false, /* Is Deferrable */
3185 false, /* Is Deferred */
3186 !constr->skip_validation, /* Is Validated */
3187 InvalidOid, /* no parent constraint */
3188 InvalidOid, /* not a relation constraint */
3189 NULL,
3190 0,
3191 0,
3192 domainOid, /* domain constraint */
3193 InvalidOid, /* no associated index */
3194 InvalidOid, /* Foreign key fields */
3195 NULL,
3196 NULL,
3197 NULL,
3198 NULL,
3199 0,
3200 ' ',
3201 ' ',
3202 ' ',
3203 NULL, /* not an exclusion constraint */
3204 expr, /* Tree form of check constraint */
3205 ccbin, /* Binary form of check constraint */
3206 true, /* is local */
3207 0, /* inhcount */
3208 false, /* connoinherit */
3209 false); /* is_internal */
3210 if (constrAddr)
3211 ObjectAddressSet(*constrAddr, ConstraintRelationId, ccoid);
3212
3213 /*
3214 * Return the compiled constraint expression so the calling routine can
3215 * perform any additional required tests.
3216 */
3217 return ccbin;
3218 }
3219
3220 /* Parser pre_columnref_hook for domain CHECK constraint parsing */
3221 static Node *
replace_domain_constraint_value(ParseState * pstate,ColumnRef * cref)3222 replace_domain_constraint_value(ParseState *pstate, ColumnRef *cref)
3223 {
3224 /*
3225 * Check for a reference to "value", and if that's what it is, replace
3226 * with a CoerceToDomainValue as prepared for us by domainAddConstraint.
3227 * (We handle VALUE as a name, not a keyword, to avoid breaking a lot of
3228 * applications that have used VALUE as a column name in the past.)
3229 */
3230 if (list_length(cref->fields) == 1)
3231 {
3232 Node *field1 = (Node *) linitial(cref->fields);
3233 char *colname;
3234
3235 Assert(IsA(field1, String));
3236 colname = strVal(field1);
3237 if (strcmp(colname, "value") == 0)
3238 {
3239 CoerceToDomainValue *domVal = copyObject(pstate->p_ref_hook_state);
3240
3241 /* Propagate location knowledge, if any */
3242 domVal->location = cref->location;
3243 return (Node *) domVal;
3244 }
3245 }
3246 return NULL;
3247 }
3248
3249
3250 /*
3251 * Execute ALTER TYPE RENAME
3252 */
3253 ObjectAddress
RenameType(RenameStmt * stmt)3254 RenameType(RenameStmt *stmt)
3255 {
3256 List *names = castNode(List, stmt->object);
3257 const char *newTypeName = stmt->newname;
3258 TypeName *typename;
3259 Oid typeOid;
3260 Relation rel;
3261 HeapTuple tup;
3262 Form_pg_type typTup;
3263 ObjectAddress address;
3264
3265 /* Make a TypeName so we can use standard type lookup machinery */
3266 typename = makeTypeNameFromNameList(names);
3267 typeOid = typenameTypeId(NULL, typename);
3268
3269 /* Look up the type in the type table */
3270 rel = table_open(TypeRelationId, RowExclusiveLock);
3271
3272 tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3273 if (!HeapTupleIsValid(tup))
3274 elog(ERROR, "cache lookup failed for type %u", typeOid);
3275 typTup = (Form_pg_type) GETSTRUCT(tup);
3276
3277 /* check permissions on type */
3278 if (!pg_type_ownercheck(typeOid, GetUserId()))
3279 aclcheck_error_type(ACLCHECK_NOT_OWNER, typeOid);
3280
3281 /* ALTER DOMAIN used on a non-domain? */
3282 if (stmt->renameType == OBJECT_DOMAIN && typTup->typtype != TYPTYPE_DOMAIN)
3283 ereport(ERROR,
3284 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3285 errmsg("%s is not a domain",
3286 format_type_be(typeOid))));
3287
3288 /*
3289 * If it's a composite type, we need to check that it really is a
3290 * free-standing composite type, and not a table's rowtype. We want people
3291 * to use ALTER TABLE not ALTER TYPE for that case.
3292 */
3293 if (typTup->typtype == TYPTYPE_COMPOSITE &&
3294 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
3295 ereport(ERROR,
3296 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3297 errmsg("%s is a table's row type",
3298 format_type_be(typeOid)),
3299 errhint("Use ALTER TABLE instead.")));
3300
3301 /* don't allow direct alteration of array types, either */
3302 if (OidIsValid(typTup->typelem) &&
3303 get_array_type(typTup->typelem) == typeOid)
3304 ereport(ERROR,
3305 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3306 errmsg("cannot alter array type %s",
3307 format_type_be(typeOid)),
3308 errhint("You can alter type %s, which will alter the array type as well.",
3309 format_type_be(typTup->typelem))));
3310
3311 /*
3312 * If type is composite we need to rename associated pg_class entry too.
3313 * RenameRelationInternal will call RenameTypeInternal automatically.
3314 */
3315 if (typTup->typtype == TYPTYPE_COMPOSITE)
3316 RenameRelationInternal(typTup->typrelid, newTypeName, false, false);
3317 else
3318 RenameTypeInternal(typeOid, newTypeName,
3319 typTup->typnamespace);
3320
3321 ObjectAddressSet(address, TypeRelationId, typeOid);
3322 /* Clean up */
3323 table_close(rel, RowExclusiveLock);
3324
3325 return address;
3326 }
3327
3328 /*
3329 * Change the owner of a type.
3330 */
3331 ObjectAddress
AlterTypeOwner(List * names,Oid newOwnerId,ObjectType objecttype)3332 AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
3333 {
3334 TypeName *typename;
3335 Oid typeOid;
3336 Relation rel;
3337 HeapTuple tup;
3338 HeapTuple newtup;
3339 Form_pg_type typTup;
3340 AclResult aclresult;
3341 ObjectAddress address;
3342
3343 rel = table_open(TypeRelationId, RowExclusiveLock);
3344
3345 /* Make a TypeName so we can use standard type lookup machinery */
3346 typename = makeTypeNameFromNameList(names);
3347
3348 /* Use LookupTypeName here so that shell types can be processed */
3349 tup = LookupTypeName(NULL, typename, NULL, false);
3350 if (tup == NULL)
3351 ereport(ERROR,
3352 (errcode(ERRCODE_UNDEFINED_OBJECT),
3353 errmsg("type \"%s\" does not exist",
3354 TypeNameToString(typename))));
3355 typeOid = typeTypeId(tup);
3356
3357 /* Copy the syscache entry so we can scribble on it below */
3358 newtup = heap_copytuple(tup);
3359 ReleaseSysCache(tup);
3360 tup = newtup;
3361 typTup = (Form_pg_type) GETSTRUCT(tup);
3362
3363 /* Don't allow ALTER DOMAIN on a type */
3364 if (objecttype == OBJECT_DOMAIN && typTup->typtype != TYPTYPE_DOMAIN)
3365 ereport(ERROR,
3366 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3367 errmsg("%s is not a domain",
3368 format_type_be(typeOid))));
3369
3370 /*
3371 * If it's a composite type, we need to check that it really is a
3372 * free-standing composite type, and not a table's rowtype. We want people
3373 * to use ALTER TABLE not ALTER TYPE for that case.
3374 */
3375 if (typTup->typtype == TYPTYPE_COMPOSITE &&
3376 get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
3377 ereport(ERROR,
3378 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3379 errmsg("%s is a table's row type",
3380 format_type_be(typeOid)),
3381 errhint("Use ALTER TABLE instead.")));
3382
3383 /* don't allow direct alteration of array types, either */
3384 if (OidIsValid(typTup->typelem) &&
3385 get_array_type(typTup->typelem) == typeOid)
3386 ereport(ERROR,
3387 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3388 errmsg("cannot alter array type %s",
3389 format_type_be(typeOid)),
3390 errhint("You can alter type %s, which will alter the array type as well.",
3391 format_type_be(typTup->typelem))));
3392
3393 /*
3394 * If the new owner is the same as the existing owner, consider the
3395 * command to have succeeded. This is for dump restoration purposes.
3396 */
3397 if (typTup->typowner != newOwnerId)
3398 {
3399 /* Superusers can always do it */
3400 if (!superuser())
3401 {
3402 /* Otherwise, must be owner of the existing object */
3403 if (!pg_type_ownercheck(typTup->oid, GetUserId()))
3404 aclcheck_error_type(ACLCHECK_NOT_OWNER, typTup->oid);
3405
3406 /* Must be able to become new owner */
3407 check_is_member_of_role(GetUserId(), newOwnerId);
3408
3409 /* New owner must have CREATE privilege on namespace */
3410 aclresult = pg_namespace_aclcheck(typTup->typnamespace,
3411 newOwnerId,
3412 ACL_CREATE);
3413 if (aclresult != ACLCHECK_OK)
3414 aclcheck_error(aclresult, OBJECT_SCHEMA,
3415 get_namespace_name(typTup->typnamespace));
3416 }
3417
3418 AlterTypeOwner_oid(typeOid, newOwnerId, true);
3419 }
3420
3421 ObjectAddressSet(address, TypeRelationId, typeOid);
3422
3423 /* Clean up */
3424 table_close(rel, RowExclusiveLock);
3425
3426 return address;
3427 }
3428
3429 /*
3430 * AlterTypeOwner_oid - change type owner unconditionally
3431 *
3432 * This function recurses to handle a pg_class entry, if necessary. It
3433 * invokes any necessary access object hooks. If hasDependEntry is true, this
3434 * function modifies the pg_shdepend entry appropriately (this should be
3435 * passed as false only for table rowtypes and array types).
3436 *
3437 * This is used by ALTER TABLE/TYPE OWNER commands, as well as by REASSIGN
3438 * OWNED BY. It assumes the caller has done all needed check.
3439 */
3440 void
AlterTypeOwner_oid(Oid typeOid,Oid newOwnerId,bool hasDependEntry)3441 AlterTypeOwner_oid(Oid typeOid, Oid newOwnerId, bool hasDependEntry)
3442 {
3443 Relation rel;
3444 HeapTuple tup;
3445 Form_pg_type typTup;
3446
3447 rel = table_open(TypeRelationId, RowExclusiveLock);
3448
3449 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
3450 if (!HeapTupleIsValid(tup))
3451 elog(ERROR, "cache lookup failed for type %u", typeOid);
3452 typTup = (Form_pg_type) GETSTRUCT(tup);
3453
3454 /*
3455 * If it's a composite type, invoke ATExecChangeOwner so that we fix up
3456 * the pg_class entry properly. That will call back to
3457 * AlterTypeOwnerInternal to take care of the pg_type entry(s).
3458 */
3459 if (typTup->typtype == TYPTYPE_COMPOSITE)
3460 ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock);
3461 else
3462 AlterTypeOwnerInternal(typeOid, newOwnerId);
3463
3464 /* Update owner dependency reference */
3465 if (hasDependEntry)
3466 changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
3467
3468 InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
3469
3470 ReleaseSysCache(tup);
3471 table_close(rel, RowExclusiveLock);
3472 }
3473
3474 /*
3475 * AlterTypeOwnerInternal - bare-bones type owner change.
3476 *
3477 * This routine simply modifies the owner of a pg_type entry, and recurses
3478 * to handle a possible array type.
3479 */
3480 void
AlterTypeOwnerInternal(Oid typeOid,Oid newOwnerId)3481 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
3482 {
3483 Relation rel;
3484 HeapTuple tup;
3485 Form_pg_type typTup;
3486 Datum repl_val[Natts_pg_type];
3487 bool repl_null[Natts_pg_type];
3488 bool repl_repl[Natts_pg_type];
3489 Acl *newAcl;
3490 Datum aclDatum;
3491 bool isNull;
3492
3493 rel = table_open(TypeRelationId, RowExclusiveLock);
3494
3495 tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3496 if (!HeapTupleIsValid(tup))
3497 elog(ERROR, "cache lookup failed for type %u", typeOid);
3498 typTup = (Form_pg_type) GETSTRUCT(tup);
3499
3500 memset(repl_null, false, sizeof(repl_null));
3501 memset(repl_repl, false, sizeof(repl_repl));
3502
3503 repl_repl[Anum_pg_type_typowner - 1] = true;
3504 repl_val[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(newOwnerId);
3505
3506 aclDatum = heap_getattr(tup,
3507 Anum_pg_type_typacl,
3508 RelationGetDescr(rel),
3509 &isNull);
3510 /* Null ACLs do not require changes */
3511 if (!isNull)
3512 {
3513 newAcl = aclnewowner(DatumGetAclP(aclDatum),
3514 typTup->typowner, newOwnerId);
3515 repl_repl[Anum_pg_type_typacl - 1] = true;
3516 repl_val[Anum_pg_type_typacl - 1] = PointerGetDatum(newAcl);
3517 }
3518
3519 tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
3520 repl_repl);
3521
3522 CatalogTupleUpdate(rel, &tup->t_self, tup);
3523
3524 /* If it has an array type, update that too */
3525 if (OidIsValid(typTup->typarray))
3526 AlterTypeOwnerInternal(typTup->typarray, newOwnerId);
3527
3528 /* Clean up */
3529 table_close(rel, RowExclusiveLock);
3530 }
3531
3532 /*
3533 * Execute ALTER TYPE SET SCHEMA
3534 */
3535 ObjectAddress
AlterTypeNamespace(List * names,const char * newschema,ObjectType objecttype,Oid * oldschema)3536 AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype,
3537 Oid *oldschema)
3538 {
3539 TypeName *typename;
3540 Oid typeOid;
3541 Oid nspOid;
3542 Oid oldNspOid;
3543 ObjectAddresses *objsMoved;
3544 ObjectAddress myself;
3545
3546 /* Make a TypeName so we can use standard type lookup machinery */
3547 typename = makeTypeNameFromNameList(names);
3548 typeOid = typenameTypeId(NULL, typename);
3549
3550 /* Don't allow ALTER DOMAIN on a type */
3551 if (objecttype == OBJECT_DOMAIN && get_typtype(typeOid) != TYPTYPE_DOMAIN)
3552 ereport(ERROR,
3553 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3554 errmsg("%s is not a domain",
3555 format_type_be(typeOid))));
3556
3557 /* get schema OID and check its permissions */
3558 nspOid = LookupCreationNamespace(newschema);
3559
3560 objsMoved = new_object_addresses();
3561 oldNspOid = AlterTypeNamespace_oid(typeOid, nspOid, objsMoved);
3562 free_object_addresses(objsMoved);
3563
3564 if (oldschema)
3565 *oldschema = oldNspOid;
3566
3567 ObjectAddressSet(myself, TypeRelationId, typeOid);
3568
3569 return myself;
3570 }
3571
3572 Oid
AlterTypeNamespace_oid(Oid typeOid,Oid nspOid,ObjectAddresses * objsMoved)3573 AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, ObjectAddresses *objsMoved)
3574 {
3575 Oid elemOid;
3576
3577 /* check permissions on type */
3578 if (!pg_type_ownercheck(typeOid, GetUserId()))
3579 aclcheck_error_type(ACLCHECK_NOT_OWNER, typeOid);
3580
3581 /* don't allow direct alteration of array types */
3582 elemOid = get_element_type(typeOid);
3583 if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
3584 ereport(ERROR,
3585 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3586 errmsg("cannot alter array type %s",
3587 format_type_be(typeOid)),
3588 errhint("You can alter type %s, which will alter the array type as well.",
3589 format_type_be(elemOid))));
3590
3591 /* and do the work */
3592 return AlterTypeNamespaceInternal(typeOid, nspOid, false, true, objsMoved);
3593 }
3594
3595 /*
3596 * Move specified type to new namespace.
3597 *
3598 * Caller must have already checked privileges.
3599 *
3600 * The function automatically recurses to process the type's array type,
3601 * if any. isImplicitArray should be true only when doing this internal
3602 * recursion (outside callers must never try to move an array type directly).
3603 *
3604 * If errorOnTableType is true, the function errors out if the type is
3605 * a table type. ALTER TABLE has to be used to move a table to a new
3606 * namespace.
3607 *
3608 * Returns the type's old namespace OID.
3609 */
3610 Oid
AlterTypeNamespaceInternal(Oid typeOid,Oid nspOid,bool isImplicitArray,bool errorOnTableType,ObjectAddresses * objsMoved)3611 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
3612 bool isImplicitArray,
3613 bool errorOnTableType,
3614 ObjectAddresses *objsMoved)
3615 {
3616 Relation rel;
3617 HeapTuple tup;
3618 Form_pg_type typform;
3619 Oid oldNspOid;
3620 Oid arrayOid;
3621 bool isCompositeType;
3622 ObjectAddress thisobj;
3623
3624 /*
3625 * Make sure we haven't moved this object previously.
3626 */
3627 thisobj.classId = TypeRelationId;
3628 thisobj.objectId = typeOid;
3629 thisobj.objectSubId = 0;
3630
3631 if (object_address_present(&thisobj, objsMoved))
3632 return InvalidOid;
3633
3634 rel = table_open(TypeRelationId, RowExclusiveLock);
3635
3636 tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3637 if (!HeapTupleIsValid(tup))
3638 elog(ERROR, "cache lookup failed for type %u", typeOid);
3639 typform = (Form_pg_type) GETSTRUCT(tup);
3640
3641 oldNspOid = typform->typnamespace;
3642 arrayOid = typform->typarray;
3643
3644 /* If the type is already there, we scan skip these next few checks. */
3645 if (oldNspOid != nspOid)
3646 {
3647 /* common checks on switching namespaces */
3648 CheckSetNamespace(oldNspOid, nspOid);
3649
3650 /* check for duplicate name (more friendly than unique-index failure) */
3651 if (SearchSysCacheExists2(TYPENAMENSP,
3652 NameGetDatum(&typform->typname),
3653 ObjectIdGetDatum(nspOid)))
3654 ereport(ERROR,
3655 (errcode(ERRCODE_DUPLICATE_OBJECT),
3656 errmsg("type \"%s\" already exists in schema \"%s\"",
3657 NameStr(typform->typname),
3658 get_namespace_name(nspOid))));
3659 }
3660
3661 /* Detect whether type is a composite type (but not a table rowtype) */
3662 isCompositeType =
3663 (typform->typtype == TYPTYPE_COMPOSITE &&
3664 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
3665
3666 /* Enforce not-table-type if requested */
3667 if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
3668 errorOnTableType)
3669 ereport(ERROR,
3670 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3671 errmsg("%s is a table's row type",
3672 format_type_be(typeOid)),
3673 errhint("Use ALTER TABLE instead.")));
3674
3675 if (oldNspOid != nspOid)
3676 {
3677 /* OK, modify the pg_type row */
3678
3679 /* tup is a copy, so we can scribble directly on it */
3680 typform->typnamespace = nspOid;
3681
3682 CatalogTupleUpdate(rel, &tup->t_self, tup);
3683 }
3684
3685 /*
3686 * Composite types have pg_class entries.
3687 *
3688 * We need to modify the pg_class tuple as well to reflect the change of
3689 * schema.
3690 */
3691 if (isCompositeType)
3692 {
3693 Relation classRel;
3694
3695 classRel = table_open(RelationRelationId, RowExclusiveLock);
3696
3697 AlterRelationNamespaceInternal(classRel, typform->typrelid,
3698 oldNspOid, nspOid,
3699 false, objsMoved);
3700
3701 table_close(classRel, RowExclusiveLock);
3702
3703 /*
3704 * Check for constraints associated with the composite type (we don't
3705 * currently support this, but probably will someday).
3706 */
3707 AlterConstraintNamespaces(typform->typrelid, oldNspOid,
3708 nspOid, false, objsMoved);
3709 }
3710 else
3711 {
3712 /* If it's a domain, it might have constraints */
3713 if (typform->typtype == TYPTYPE_DOMAIN)
3714 AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true,
3715 objsMoved);
3716 }
3717
3718 /*
3719 * Update dependency on schema, if any --- a table rowtype has not got
3720 * one, and neither does an implicit array.
3721 */
3722 if (oldNspOid != nspOid &&
3723 (isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
3724 !isImplicitArray)
3725 if (changeDependencyFor(TypeRelationId, typeOid,
3726 NamespaceRelationId, oldNspOid, nspOid) != 1)
3727 elog(ERROR, "failed to change schema dependency for type %s",
3728 format_type_be(typeOid));
3729
3730 InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
3731
3732 heap_freetuple(tup);
3733
3734 table_close(rel, RowExclusiveLock);
3735
3736 add_exact_object_address(&thisobj, objsMoved);
3737
3738 /* Recursively alter the associated array type, if any */
3739 if (OidIsValid(arrayOid))
3740 AlterTypeNamespaceInternal(arrayOid, nspOid, true, true, objsMoved);
3741
3742 return oldNspOid;
3743 }
3744