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