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