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