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