Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_type.c
4 : * routines to support manipulation of the pg_type relation
5 : *
6 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/catalog/pg_type.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/heapam.h"
18 : #include "access/htup_details.h"
19 : #include "access/xact.h"
20 : #include "catalog/binary_upgrade.h"
21 : #include "catalog/dependency.h"
22 : #include "catalog/indexing.h"
23 : #include "catalog/objectaccess.h"
24 : #include "catalog/pg_collation.h"
25 : #include "catalog/pg_namespace.h"
26 : #include "catalog/pg_proc.h"
27 : #include "catalog/pg_type.h"
28 : #include "catalog/pg_type_fn.h"
29 : #include "commands/typecmds.h"
30 : #include "miscadmin.h"
31 : #include "parser/scansup.h"
32 : #include "utils/acl.h"
33 : #include "utils/builtins.h"
34 : #include "utils/fmgroids.h"
35 : #include "utils/lsyscache.h"
36 : #include "utils/rel.h"
37 : #include "utils/syscache.h"
38 :
39 : /* Potentially set by pg_upgrade_support functions */
40 : Oid binary_upgrade_next_pg_type_oid = InvalidOid;
41 :
42 : /* ----------------------------------------------------------------
43 : * TypeShellMake
44 : *
45 : * This procedure inserts a "shell" tuple into the pg_type relation.
46 : * The type tuple inserted has valid but dummy values, and its
47 : * "typisdefined" field is false indicating it's not really defined.
48 : *
49 : * This is used so that a tuple exists in the catalogs. The I/O
50 : * functions for the type will link to this tuple. When the full
51 : * CREATE TYPE command is issued, the bogus values will be replaced
52 : * with correct ones, and "typisdefined" will be set to true.
53 : * ----------------------------------------------------------------
54 : */
55 : ObjectAddress
56 21 : TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
57 : {
58 : Relation pg_type_desc;
59 : TupleDesc tupDesc;
60 : int i;
61 : HeapTuple tup;
62 : Datum values[Natts_pg_type];
63 : bool nulls[Natts_pg_type];
64 : Oid typoid;
65 : NameData name;
66 : ObjectAddress address;
67 :
68 21 : Assert(PointerIsValid(typeName));
69 :
70 : /*
71 : * open pg_type
72 : */
73 21 : pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
74 21 : tupDesc = pg_type_desc->rd_att;
75 :
76 : /*
77 : * initialize our *nulls and *values arrays
78 : */
79 651 : for (i = 0; i < Natts_pg_type; ++i)
80 : {
81 630 : nulls[i] = false;
82 630 : values[i] = (Datum) NULL; /* redundant, but safe */
83 : }
84 :
85 : /*
86 : * initialize *values with the type name and dummy values
87 : *
88 : * The representational details are the same as int4 ... it doesn't really
89 : * matter what they are so long as they are consistent. Also note that we
90 : * give it typtype = TYPTYPE_PSEUDO as extra insurance that it won't be
91 : * mistaken for a usable type.
92 : */
93 21 : namestrcpy(&name, typeName);
94 21 : values[Anum_pg_type_typname - 1] = NameGetDatum(&name);
95 21 : values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace);
96 21 : values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId);
97 21 : values[Anum_pg_type_typlen - 1] = Int16GetDatum(sizeof(int32));
98 21 : values[Anum_pg_type_typbyval - 1] = BoolGetDatum(true);
99 21 : values[Anum_pg_type_typtype - 1] = CharGetDatum(TYPTYPE_PSEUDO);
100 21 : values[Anum_pg_type_typcategory - 1] = CharGetDatum(TYPCATEGORY_PSEUDOTYPE);
101 21 : values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(false);
102 21 : values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(false);
103 21 : values[Anum_pg_type_typdelim - 1] = CharGetDatum(DEFAULT_TYPDELIM);
104 21 : values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(InvalidOid);
105 21 : values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(InvalidOid);
106 21 : values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(InvalidOid);
107 21 : values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(F_SHELL_IN);
108 21 : values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(F_SHELL_OUT);
109 21 : values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(InvalidOid);
110 21 : values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(InvalidOid);
111 21 : values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(InvalidOid);
112 21 : values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(InvalidOid);
113 21 : values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(InvalidOid);
114 21 : values[Anum_pg_type_typalign - 1] = CharGetDatum('i');
115 21 : values[Anum_pg_type_typstorage - 1] = CharGetDatum('p');
116 21 : values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(false);
117 21 : values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(InvalidOid);
118 21 : values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
119 21 : values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
120 21 : values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
121 21 : nulls[Anum_pg_type_typdefaultbin - 1] = true;
122 21 : nulls[Anum_pg_type_typdefault - 1] = true;
123 21 : nulls[Anum_pg_type_typacl - 1] = true;
124 :
125 : /*
126 : * create a new type tuple
127 : */
128 21 : tup = heap_form_tuple(tupDesc, values, nulls);
129 :
130 : /* Use binary-upgrade override for pg_type.oid? */
131 21 : if (IsBinaryUpgrade)
132 : {
133 0 : if (!OidIsValid(binary_upgrade_next_pg_type_oid))
134 0 : ereport(ERROR,
135 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
136 : errmsg("pg_type OID value not set when in binary upgrade mode")));
137 :
138 0 : HeapTupleSetOid(tup, binary_upgrade_next_pg_type_oid);
139 0 : binary_upgrade_next_pg_type_oid = InvalidOid;
140 : }
141 :
142 : /*
143 : * insert the tuple in the relation and get the tuple's oid.
144 : */
145 21 : typoid = CatalogTupleInsert(pg_type_desc, tup);
146 :
147 : /*
148 : * Create dependencies. We can/must skip this in bootstrap mode.
149 : */
150 21 : if (!IsBootstrapProcessingMode())
151 21 : GenerateTypeDependencies(typeNamespace,
152 : typoid,
153 : InvalidOid,
154 : 0,
155 : ownerId,
156 : F_SHELL_IN,
157 : F_SHELL_OUT,
158 : InvalidOid,
159 : InvalidOid,
160 : InvalidOid,
161 : InvalidOid,
162 : InvalidOid,
163 : InvalidOid,
164 : false,
165 : InvalidOid,
166 : InvalidOid,
167 : NULL,
168 : false);
169 :
170 : /* Post creation hook for new shell type */
171 21 : InvokeObjectPostCreateHook(TypeRelationId, typoid, 0);
172 :
173 21 : ObjectAddressSet(address, TypeRelationId, typoid);
174 :
175 : /*
176 : * clean up and return the type-oid
177 : */
178 21 : heap_freetuple(tup);
179 21 : heap_close(pg_type_desc, RowExclusiveLock);
180 :
181 21 : return address;
182 : }
183 :
184 : /* ----------------------------------------------------------------
185 : * TypeCreate
186 : *
187 : * This does all the necessary work needed to define a new type.
188 : *
189 : * Returns the ObjectAddress assigned to the new type.
190 : * If newTypeOid is zero (the normal case), a new OID is created;
191 : * otherwise we use exactly that OID.
192 : * ----------------------------------------------------------------
193 : */
194 : ObjectAddress
195 5104 : TypeCreate(Oid newTypeOid,
196 : const char *typeName,
197 : Oid typeNamespace,
198 : Oid relationOid, /* only for relation rowtypes */
199 : char relationKind, /* ditto */
200 : Oid ownerId,
201 : int16 internalSize,
202 : char typeType,
203 : char typeCategory,
204 : bool typePreferred,
205 : char typDelim,
206 : Oid inputProcedure,
207 : Oid outputProcedure,
208 : Oid receiveProcedure,
209 : Oid sendProcedure,
210 : Oid typmodinProcedure,
211 : Oid typmodoutProcedure,
212 : Oid analyzeProcedure,
213 : Oid elementType,
214 : bool isImplicitArray,
215 : Oid arrayType,
216 : Oid baseType,
217 : const char *defaultTypeValue, /* human readable rep */
218 : char *defaultTypeBin, /* cooked rep */
219 : bool passedByValue,
220 : char alignment,
221 : char storage,
222 : int32 typeMod,
223 : int32 typNDims, /* Array dimensions for baseType */
224 : bool typeNotNull,
225 : Oid typeCollation)
226 : {
227 : Relation pg_type_desc;
228 : Oid typeObjectId;
229 5104 : bool rebuildDeps = false;
230 : HeapTuple tup;
231 : bool nulls[Natts_pg_type];
232 : bool replaces[Natts_pg_type];
233 : Datum values[Natts_pg_type];
234 : NameData name;
235 : int i;
236 5104 : Acl *typacl = NULL;
237 : ObjectAddress address;
238 :
239 : /*
240 : * We assume that the caller validated the arguments individually, but did
241 : * not check for bad combinations.
242 : *
243 : * Validate size specifications: either positive (fixed-length) or -1
244 : * (varlena) or -2 (cstring).
245 : */
246 5104 : if (!(internalSize > 0 ||
247 : internalSize == -1 ||
248 : internalSize == -2))
249 0 : ereport(ERROR,
250 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
251 : errmsg("invalid type internal size %d",
252 : internalSize)));
253 :
254 5104 : if (passedByValue)
255 : {
256 : /*
257 : * Pass-by-value types must have a fixed length that is one of the
258 : * values supported by fetch_att() and store_att_byval(); and the
259 : * alignment had better agree, too. All this code must match
260 : * access/tupmacs.h!
261 : */
262 51 : if (internalSize == (int16) sizeof(char))
263 : {
264 1 : if (alignment != 'c')
265 0 : ereport(ERROR,
266 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
267 : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
268 : alignment, internalSize)));
269 : }
270 50 : else if (internalSize == (int16) sizeof(int16))
271 : {
272 0 : if (alignment != 's')
273 0 : ereport(ERROR,
274 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
275 : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
276 : alignment, internalSize)));
277 : }
278 50 : else if (internalSize == (int16) sizeof(int32))
279 : {
280 50 : if (alignment != 'i')
281 0 : ereport(ERROR,
282 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
283 : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
284 : alignment, internalSize)));
285 : }
286 : #if SIZEOF_DATUM == 8
287 : else if (internalSize == (int16) sizeof(Datum))
288 : {
289 : if (alignment != 'd')
290 : ereport(ERROR,
291 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
292 : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
293 : alignment, internalSize)));
294 : }
295 : #endif
296 : else
297 0 : ereport(ERROR,
298 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
299 : errmsg("internal size %d is invalid for passed-by-value type",
300 : internalSize)));
301 : }
302 : else
303 : {
304 : /* varlena types must have int align or better */
305 5053 : if (internalSize == -1 && !(alignment == 'i' || alignment == 'd'))
306 0 : ereport(ERROR,
307 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
308 : errmsg("alignment \"%c\" is invalid for variable-length type",
309 : alignment)));
310 : /* cstring must have char alignment */
311 5053 : if (internalSize == -2 && !(alignment == 'c'))
312 0 : ereport(ERROR,
313 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
314 : errmsg("alignment \"%c\" is invalid for variable-length type",
315 : alignment)));
316 : }
317 :
318 : /* Only varlena types can be toasted */
319 5104 : if (storage != 'p' && internalSize != -1)
320 0 : ereport(ERROR,
321 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
322 : errmsg("fixed-size types must have storage PLAIN")));
323 :
324 : /*
325 : * initialize arrays needed for heap_form_tuple or heap_modify_tuple
326 : */
327 158224 : for (i = 0; i < Natts_pg_type; ++i)
328 : {
329 153120 : nulls[i] = false;
330 153120 : replaces[i] = true;
331 153120 : values[i] = (Datum) 0;
332 : }
333 :
334 : /*
335 : * insert data values
336 : */
337 5104 : namestrcpy(&name, typeName);
338 5104 : values[Anum_pg_type_typname - 1] = NameGetDatum(&name);
339 5104 : values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace);
340 5104 : values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId);
341 5104 : values[Anum_pg_type_typlen - 1] = Int16GetDatum(internalSize);
342 5104 : values[Anum_pg_type_typbyval - 1] = BoolGetDatum(passedByValue);
343 5104 : values[Anum_pg_type_typtype - 1] = CharGetDatum(typeType);
344 5104 : values[Anum_pg_type_typcategory - 1] = CharGetDatum(typeCategory);
345 5104 : values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(typePreferred);
346 5104 : values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(true);
347 5104 : values[Anum_pg_type_typdelim - 1] = CharGetDatum(typDelim);
348 5104 : values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(relationOid);
349 5104 : values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(elementType);
350 5104 : values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(arrayType);
351 5104 : values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(inputProcedure);
352 5104 : values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(outputProcedure);
353 5104 : values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(receiveProcedure);
354 5104 : values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(sendProcedure);
355 5104 : values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(typmodinProcedure);
356 5104 : values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(typmodoutProcedure);
357 5104 : values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(analyzeProcedure);
358 5104 : values[Anum_pg_type_typalign - 1] = CharGetDatum(alignment);
359 5104 : values[Anum_pg_type_typstorage - 1] = CharGetDatum(storage);
360 5104 : values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(typeNotNull);
361 5104 : values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(baseType);
362 5104 : values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
363 5104 : values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
364 5104 : values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
365 :
366 : /*
367 : * initialize the default binary value for this type. Check for nulls of
368 : * course.
369 : */
370 5104 : if (defaultTypeBin)
371 7 : values[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(defaultTypeBin);
372 : else
373 5097 : nulls[Anum_pg_type_typdefaultbin - 1] = true;
374 :
375 : /*
376 : * initialize the default value for this type.
377 : */
378 5104 : if (defaultTypeValue)
379 9 : values[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultTypeValue);
380 : else
381 5095 : nulls[Anum_pg_type_typdefault - 1] = true;
382 :
383 5104 : typacl = get_user_default_acl(ACL_OBJECT_TYPE, ownerId,
384 : typeNamespace);
385 5104 : if (typacl != NULL)
386 3 : values[Anum_pg_type_typacl - 1] = PointerGetDatum(typacl);
387 : else
388 5101 : nulls[Anum_pg_type_typacl - 1] = true;
389 :
390 : /*
391 : * open pg_type and prepare to insert or update a row.
392 : *
393 : * NOTE: updating will not work correctly in bootstrap mode; but we don't
394 : * expect to be overwriting any shell types in bootstrap mode.
395 : */
396 5104 : pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
397 :
398 5104 : tup = SearchSysCacheCopy2(TYPENAMENSP,
399 : CStringGetDatum(typeName),
400 : ObjectIdGetDatum(typeNamespace));
401 5104 : if (HeapTupleIsValid(tup))
402 : {
403 : /*
404 : * check that the type is not already defined. It may exist as a
405 : * shell type, however.
406 : */
407 17 : if (((Form_pg_type) GETSTRUCT(tup))->typisdefined)
408 0 : ereport(ERROR,
409 : (errcode(ERRCODE_DUPLICATE_OBJECT),
410 : errmsg("type \"%s\" already exists", typeName)));
411 :
412 : /*
413 : * shell type must have been created by same owner
414 : */
415 17 : if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId)
416 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName);
417 :
418 : /* trouble if caller wanted to force the OID */
419 17 : if (OidIsValid(newTypeOid))
420 0 : elog(ERROR, "cannot assign new OID to existing shell type");
421 :
422 : /*
423 : * Okay to update existing shell type tuple
424 : */
425 17 : tup = heap_modify_tuple(tup,
426 : RelationGetDescr(pg_type_desc),
427 : values,
428 : nulls,
429 : replaces);
430 :
431 17 : CatalogTupleUpdate(pg_type_desc, &tup->t_self, tup);
432 :
433 17 : typeObjectId = HeapTupleGetOid(tup);
434 :
435 17 : rebuildDeps = true; /* get rid of shell type's dependencies */
436 : }
437 : else
438 : {
439 5087 : tup = heap_form_tuple(RelationGetDescr(pg_type_desc),
440 : values,
441 : nulls);
442 :
443 : /* Force the OID if requested by caller */
444 5087 : if (OidIsValid(newTypeOid))
445 2012 : HeapTupleSetOid(tup, newTypeOid);
446 : /* Use binary-upgrade override for pg_type.oid, if supplied. */
447 3075 : else if (IsBinaryUpgrade)
448 : {
449 0 : if (!OidIsValid(binary_upgrade_next_pg_type_oid))
450 0 : ereport(ERROR,
451 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
452 : errmsg("pg_type OID value not set when in binary upgrade mode")));
453 :
454 0 : HeapTupleSetOid(tup, binary_upgrade_next_pg_type_oid);
455 0 : binary_upgrade_next_pg_type_oid = InvalidOid;
456 : }
457 : /* else allow system to assign oid */
458 :
459 5087 : typeObjectId = CatalogTupleInsert(pg_type_desc, tup);
460 : }
461 :
462 : /*
463 : * Create dependencies. We can/must skip this in bootstrap mode.
464 : */
465 5104 : if (!IsBootstrapProcessingMode())
466 5034 : GenerateTypeDependencies(typeNamespace,
467 : typeObjectId,
468 : relationOid,
469 : relationKind,
470 : ownerId,
471 : inputProcedure,
472 : outputProcedure,
473 : receiveProcedure,
474 : sendProcedure,
475 : typmodinProcedure,
476 : typmodoutProcedure,
477 : analyzeProcedure,
478 : elementType,
479 : isImplicitArray,
480 : baseType,
481 : typeCollation,
482 : (defaultTypeBin ?
483 : stringToNode(defaultTypeBin) :
484 : NULL),
485 : rebuildDeps);
486 :
487 : /* Post creation hook for new type */
488 5104 : InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0);
489 :
490 5104 : ObjectAddressSet(address, TypeRelationId, typeObjectId);
491 :
492 : /*
493 : * finish up
494 : */
495 5104 : heap_close(pg_type_desc, RowExclusiveLock);
496 :
497 5104 : return address;
498 : }
499 :
500 : /*
501 : * GenerateTypeDependencies: build the dependencies needed for a type
502 : *
503 : * If rebuild is true, we remove existing dependencies and rebuild them
504 : * from scratch. This is needed for ALTER TYPE, and also when replacing
505 : * a shell type. We don't remove an existing extension dependency, though.
506 : * (That means an extension can't absorb a shell type created in another
507 : * extension, nor ALTER a type created by another extension. Also, if it
508 : * replaces a free-standing shell type or ALTERs a free-standing type,
509 : * that type will become a member of the extension.)
510 : */
511 : void
512 5057 : GenerateTypeDependencies(Oid typeNamespace,
513 : Oid typeObjectId,
514 : Oid relationOid, /* only for relation rowtypes */
515 : char relationKind, /* ditto */
516 : Oid owner,
517 : Oid inputProcedure,
518 : Oid outputProcedure,
519 : Oid receiveProcedure,
520 : Oid sendProcedure,
521 : Oid typmodinProcedure,
522 : Oid typmodoutProcedure,
523 : Oid analyzeProcedure,
524 : Oid elementType,
525 : bool isImplicitArray,
526 : Oid baseType,
527 : Oid typeCollation,
528 : Node *defaultExpr,
529 : bool rebuild)
530 : {
531 : ObjectAddress myself,
532 : referenced;
533 :
534 : /* If rebuild, first flush old dependencies, except extension deps */
535 5057 : if (rebuild)
536 : {
537 19 : deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true);
538 19 : deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0);
539 : }
540 :
541 5057 : myself.classId = TypeRelationId;
542 5057 : myself.objectId = typeObjectId;
543 5057 : myself.objectSubId = 0;
544 :
545 : /*
546 : * Make dependencies on namespace, owner, extension.
547 : *
548 : * For a relation rowtype (that's not a composite type), we should skip
549 : * these because we'll depend on them indirectly through the pg_class
550 : * entry. Likewise, skip for implicit arrays since we'll depend on them
551 : * through the element type.
552 : */
553 5057 : if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) &&
554 : !isImplicitArray)
555 : {
556 179 : referenced.classId = NamespaceRelationId;
557 179 : referenced.objectId = typeNamespace;
558 179 : referenced.objectSubId = 0;
559 179 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
560 :
561 179 : recordDependencyOnOwner(TypeRelationId, typeObjectId, owner);
562 :
563 179 : recordDependencyOnCurrentExtension(&myself, rebuild);
564 : }
565 :
566 : /* Normal dependencies on the I/O functions */
567 5057 : if (OidIsValid(inputProcedure))
568 : {
569 5057 : referenced.classId = ProcedureRelationId;
570 5057 : referenced.objectId = inputProcedure;
571 5057 : referenced.objectSubId = 0;
572 5057 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
573 : }
574 :
575 5057 : if (OidIsValid(outputProcedure))
576 : {
577 5057 : referenced.classId = ProcedureRelationId;
578 5057 : referenced.objectId = outputProcedure;
579 5057 : referenced.objectSubId = 0;
580 5057 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
581 : }
582 :
583 5057 : if (OidIsValid(receiveProcedure))
584 : {
585 5028 : referenced.classId = ProcedureRelationId;
586 5028 : referenced.objectId = receiveProcedure;
587 5028 : referenced.objectSubId = 0;
588 5028 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
589 : }
590 :
591 5057 : if (OidIsValid(sendProcedure))
592 : {
593 5028 : referenced.classId = ProcedureRelationId;
594 5028 : referenced.objectId = sendProcedure;
595 5028 : referenced.objectSubId = 0;
596 5028 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
597 : }
598 :
599 5057 : if (OidIsValid(typmodinProcedure))
600 : {
601 2 : referenced.classId = ProcedureRelationId;
602 2 : referenced.objectId = typmodinProcedure;
603 2 : referenced.objectSubId = 0;
604 2 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
605 : }
606 :
607 5057 : if (OidIsValid(typmodoutProcedure))
608 : {
609 2 : referenced.classId = ProcedureRelationId;
610 2 : referenced.objectId = typmodoutProcedure;
611 2 : referenced.objectSubId = 0;
612 2 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
613 : }
614 :
615 5057 : if (OidIsValid(analyzeProcedure))
616 : {
617 2033 : referenced.classId = ProcedureRelationId;
618 2033 : referenced.objectId = analyzeProcedure;
619 2033 : referenced.objectSubId = 0;
620 2033 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
621 : }
622 :
623 : /*
624 : * If the type is a rowtype for a relation, mark it as internally
625 : * dependent on the relation, *unless* it is a stand-alone composite type
626 : * relation. For the latter case, we have to reverse the dependency.
627 : *
628 : * In the former case, this allows the type to be auto-dropped when the
629 : * relation is, and not otherwise. And in the latter, of course we get the
630 : * opposite effect.
631 : */
632 5057 : if (OidIsValid(relationOid))
633 : {
634 2920 : referenced.classId = RelationRelationId;
635 2920 : referenced.objectId = relationOid;
636 2920 : referenced.objectSubId = 0;
637 :
638 2920 : if (relationKind != RELKIND_COMPOSITE_TYPE)
639 2871 : recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
640 : else
641 49 : recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
642 : }
643 :
644 : /*
645 : * If the type is an implicitly-created array type, mark it as internally
646 : * dependent on the element type. Otherwise, if it has an element type,
647 : * the dependency is a normal one.
648 : */
649 5057 : if (OidIsValid(elementType))
650 : {
651 2008 : referenced.classId = TypeRelationId;
652 2008 : referenced.objectId = elementType;
653 2008 : referenced.objectSubId = 0;
654 2008 : recordDependencyOn(&myself, &referenced,
655 : isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL);
656 : }
657 :
658 : /* Normal dependency from a domain to its base type. */
659 5057 : if (OidIsValid(baseType))
660 : {
661 77 : referenced.classId = TypeRelationId;
662 77 : referenced.objectId = baseType;
663 77 : referenced.objectSubId = 0;
664 77 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
665 : }
666 :
667 : /* Normal dependency from a domain to its collation. */
668 : /* We know the default collation is pinned, so don't bother recording it */
669 5057 : if (OidIsValid(typeCollation) && typeCollation != DEFAULT_COLLATION_OID)
670 : {
671 1 : referenced.classId = CollationRelationId;
672 1 : referenced.objectId = typeCollation;
673 1 : referenced.objectSubId = 0;
674 1 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
675 : }
676 :
677 : /* Normal dependency on the default expression. */
678 5057 : if (defaultExpr)
679 8 : recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
680 5057 : }
681 :
682 : /*
683 : * RenameTypeInternal
684 : * This renames a type, as well as any associated array type.
685 : *
686 : * Caller must have already checked privileges.
687 : *
688 : * Currently this is used for renaming table rowtypes and for
689 : * ALTER TYPE RENAME TO command.
690 : */
691 : void
692 80 : RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
693 : {
694 : Relation pg_type_desc;
695 : HeapTuple tuple;
696 : Form_pg_type typ;
697 : Oid arrayOid;
698 : Oid oldTypeOid;
699 :
700 80 : pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
701 :
702 80 : tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
703 80 : if (!HeapTupleIsValid(tuple))
704 0 : elog(ERROR, "cache lookup failed for type %u", typeOid);
705 80 : typ = (Form_pg_type) GETSTRUCT(tuple);
706 :
707 : /* We are not supposed to be changing schemas here */
708 80 : Assert(typeNamespace == typ->typnamespace);
709 :
710 80 : arrayOid = typ->typarray;
711 :
712 : /* Check for a conflicting type name. */
713 80 : oldTypeOid = GetSysCacheOid2(TYPENAMENSP,
714 : CStringGetDatum(newTypeName),
715 : ObjectIdGetDatum(typeNamespace));
716 :
717 : /*
718 : * If there is one, see if it's an autogenerated array type, and if so
719 : * rename it out of the way. (But we must skip that for a shell type
720 : * because moveArrayTypeName will do the wrong thing in that case.)
721 : * Otherwise, we can at least give a more friendly error than unique-index
722 : * violation.
723 : */
724 80 : if (OidIsValid(oldTypeOid))
725 : {
726 4 : if (get_typisdefined(oldTypeOid) &&
727 2 : moveArrayTypeName(oldTypeOid, newTypeName, typeNamespace))
728 : /* successfully dodged the problem */ ;
729 : else
730 0 : ereport(ERROR,
731 : (errcode(ERRCODE_DUPLICATE_OBJECT),
732 : errmsg("type \"%s\" already exists", newTypeName)));
733 : }
734 :
735 : /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
736 80 : namestrcpy(&(typ->typname), newTypeName);
737 :
738 80 : CatalogTupleUpdate(pg_type_desc, &tuple->t_self, tuple);
739 :
740 80 : InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
741 :
742 80 : heap_freetuple(tuple);
743 80 : heap_close(pg_type_desc, RowExclusiveLock);
744 :
745 : /*
746 : * If the type has an array type, recurse to handle that. But we don't
747 : * need to do anything more if we already renamed that array type above
748 : * (which would happen when, eg, renaming "foo" to "_foo").
749 : */
750 80 : if (OidIsValid(arrayOid) && arrayOid != oldTypeOid)
751 : {
752 15 : char *arrname = makeArrayTypeName(newTypeName, typeNamespace);
753 :
754 15 : RenameTypeInternal(arrayOid, arrname, typeNamespace);
755 15 : pfree(arrname);
756 : }
757 80 : }
758 :
759 :
760 : /*
761 : * makeArrayTypeName
762 : * - given a base type name, make an array type name for it
763 : *
764 : * the caller is responsible for pfreeing the result
765 : */
766 : char *
767 2025 : makeArrayTypeName(const char *typeName, Oid typeNamespace)
768 : {
769 2025 : char *arr = (char *) palloc(NAMEDATALEN);
770 2025 : int namelen = strlen(typeName);
771 : Relation pg_type_desc;
772 : int i;
773 :
774 : /*
775 : * The idea is to prepend underscores as needed until we make a name that
776 : * doesn't collide with anything...
777 : */
778 2025 : pg_type_desc = heap_open(TypeRelationId, AccessShareLock);
779 :
780 2027 : for (i = 1; i < NAMEDATALEN - 1; i++)
781 : {
782 2027 : arr[i - 1] = '_';
783 2027 : if (i + namelen < NAMEDATALEN)
784 2027 : strcpy(arr + i, typeName);
785 : else
786 : {
787 0 : memcpy(arr + i, typeName, NAMEDATALEN - i);
788 0 : truncate_identifier(arr, NAMEDATALEN, false);
789 : }
790 2027 : if (!SearchSysCacheExists2(TYPENAMENSP,
791 : CStringGetDatum(arr),
792 : ObjectIdGetDatum(typeNamespace)))
793 2025 : break;
794 : }
795 :
796 2025 : heap_close(pg_type_desc, AccessShareLock);
797 :
798 2025 : if (i >= NAMEDATALEN - 1)
799 0 : ereport(ERROR,
800 : (errcode(ERRCODE_DUPLICATE_OBJECT),
801 : errmsg("could not form array type name for type \"%s\"",
802 : typeName)));
803 :
804 2025 : return arr;
805 : }
806 :
807 :
808 : /*
809 : * moveArrayTypeName
810 : * - try to reassign an array type name that the user wants to use.
811 : *
812 : * The given type name has been discovered to already exist (with the given
813 : * OID). If it is an autogenerated array type, change the array type's name
814 : * to not conflict. This allows the user to create type "foo" followed by
815 : * type "_foo" without problems. (Of course, there are race conditions if
816 : * two backends try to create similarly-named types concurrently, but the
817 : * worst that can happen is an unnecessary failure --- anything we do here
818 : * will be rolled back if the type creation fails due to conflicting names.)
819 : *
820 : * Note that this must be called *before* calling makeArrayTypeName to
821 : * determine the new type's own array type name; else the latter will
822 : * certainly pick the same name.
823 : *
824 : * Returns TRUE if successfully moved the type, FALSE if not.
825 : *
826 : * We also return TRUE if the given type is a shell type. In this case
827 : * the type has not been renamed out of the way, but nonetheless it can
828 : * be expected that TypeCreate will succeed. This behavior is convenient
829 : * for most callers --- those that need to distinguish the shell-type case
830 : * must do their own typisdefined test.
831 : */
832 : bool
833 4 : moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
834 : {
835 : Oid elemOid;
836 : char *newname;
837 :
838 : /* We need do nothing if it's a shell type. */
839 4 : if (!get_typisdefined(typeOid))
840 0 : return true;
841 :
842 : /* Can't change it if it's not an autogenerated array type. */
843 4 : elemOid = get_element_type(typeOid);
844 7 : if (!OidIsValid(elemOid) ||
845 3 : get_array_type(elemOid) != typeOid)
846 1 : return false;
847 :
848 : /*
849 : * OK, use makeArrayTypeName to pick an unused modification of the name.
850 : * Note that since makeArrayTypeName is an iterative process, this will
851 : * produce a name that it might have produced the first time, had the
852 : * conflicting type we are about to create already existed.
853 : */
854 3 : newname = makeArrayTypeName(typeName, typeNamespace);
855 :
856 : /* Apply the rename */
857 3 : RenameTypeInternal(typeOid, newname, typeNamespace);
858 :
859 : /*
860 : * We must bump the command counter so that any subsequent use of
861 : * makeArrayTypeName sees what we just did and doesn't pick the same name.
862 : */
863 3 : CommandCounterIncrement();
864 :
865 3 : pfree(newname);
866 :
867 3 : return true;
868 : }
|