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