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