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