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