Age Owner Branch data TLA 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-2025, 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/htup_details.h"
18 : : #include "access/table.h"
19 : : #include "access/xact.h"
20 : : #include "catalog/binary_upgrade.h"
21 : : #include "catalog/catalog.h"
22 : : #include "catalog/dependency.h"
23 : : #include "catalog/indexing.h"
24 : : #include "catalog/objectaccess.h"
25 : : #include "catalog/pg_collation.h"
26 : : #include "catalog/pg_namespace.h"
27 : : #include "catalog/pg_proc.h"
28 : : #include "catalog/pg_type.h"
29 : : #include "commands/defrem.h"
30 : : #include "commands/typecmds.h"
31 : : #include "mb/pg_wchar.h"
32 : : #include "miscadmin.h"
33 : : #include "utils/acl.h"
34 : : #include "utils/builtins.h"
35 : : #include "utils/fmgroids.h"
36 : : #include "utils/lsyscache.h"
37 : : #include "utils/rel.h"
38 : : #include "utils/syscache.h"
39 : :
40 : : /* Potentially set by pg_upgrade_support functions */
41 : : Oid binary_upgrade_next_pg_type_oid = InvalidOid;
42 : :
43 : : /* ----------------------------------------------------------------
44 : : * TypeShellMake
45 : : *
46 : : * This procedure inserts a "shell" tuple into the pg_type relation.
47 : : * The type tuple inserted has valid but dummy values, and its
48 : : * "typisdefined" field is false indicating it's not really defined.
49 : : *
50 : : * This is used so that a tuple exists in the catalogs. The I/O
51 : : * functions for the type will link to this tuple. When the full
52 : : * CREATE TYPE command is issued, the bogus values will be replaced
53 : : * with correct ones, and "typisdefined" will be set to true.
54 : : * ----------------------------------------------------------------
55 : : */
56 : : ObjectAddress
6038 tgl@sss.pgh.pa.us 57 :CBC 121 : TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
58 : : {
59 : : Relation pg_type_desc;
60 : : TupleDesc tupDesc;
61 : : int i;
62 : : HeapTuple tup;
63 : : Datum values[Natts_pg_type];
64 : : bool nulls[Natts_pg_type];
65 : : Oid typoid;
66 : : NameData name;
67 : : ObjectAddress address;
68 : :
8562 69 [ - + ]: 121 : Assert(PointerIsValid(typeName));
70 : :
71 : : /*
72 : : * open pg_type
73 : : */
2420 andres@anarazel.de 74 : 121 : pg_type_desc = table_open(TypeRelationId, RowExclusiveLock);
8562 tgl@sss.pgh.pa.us 75 : 121 : tupDesc = pg_type_desc->rd_att;
76 : :
77 : : /*
78 : : * initialize our *nulls and *values arrays
79 : : */
10226 bruce@momjian.us 80 [ + + ]: 3993 : for (i = 0; i < Natts_pg_type; ++i)
81 : : {
6152 tgl@sss.pgh.pa.us 82 : 3872 : nulls[i] = false;
29 tgl@sss.pgh.pa.us 83 :GNC 3872 : values[i] = (Datum) 0; /* redundant, but safe */
84 : : }
85 : :
86 : : /*
87 : : * initialize *values with the type name and dummy values
88 : : *
89 : : * The representational details are the same as int4 ... it doesn't really
90 : : * matter what they are so long as they are consistent. Also note that we
91 : : * give it typtype = TYPTYPE_PSEUDO as extra insurance that it won't be
92 : : * mistaken for a usable type.
93 : : */
10020 scrappy@hub.org 94 :CBC 121 : namestrcpy(&name, typeName);
5196 tgl@sss.pgh.pa.us 95 : 121 : values[Anum_pg_type_typname - 1] = NameGetDatum(&name);
96 : 121 : values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace);
97 : 121 : values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId);
4821 peter_e@gmx.net 98 : 121 : values[Anum_pg_type_typlen - 1] = Int16GetDatum(sizeof(int32));
5196 tgl@sss.pgh.pa.us 99 : 121 : values[Anum_pg_type_typbyval - 1] = BoolGetDatum(true);
100 : 121 : values[Anum_pg_type_typtype - 1] = CharGetDatum(TYPTYPE_PSEUDO);
101 : 121 : values[Anum_pg_type_typcategory - 1] = CharGetDatum(TYPCATEGORY_PSEUDOTYPE);
102 : 121 : values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(false);
103 : 121 : values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(false);
104 : 121 : values[Anum_pg_type_typdelim - 1] = CharGetDatum(DEFAULT_TYPDELIM);
105 : 121 : values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(InvalidOid);
1732 106 : 121 : values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(InvalidOid);
5196 107 : 121 : values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(InvalidOid);
108 : 121 : values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(InvalidOid);
109 : 121 : values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(F_SHELL_IN);
110 : 121 : values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(F_SHELL_OUT);
111 : 121 : values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(InvalidOid);
112 : 121 : values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(InvalidOid);
113 : 121 : values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(InvalidOid);
114 : 121 : values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(InvalidOid);
115 : 121 : values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(InvalidOid);
2012 116 : 121 : values[Anum_pg_type_typalign - 1] = CharGetDatum(TYPALIGN_INT);
117 : 121 : values[Anum_pg_type_typstorage - 1] = CharGetDatum(TYPSTORAGE_PLAIN);
5196 118 : 121 : values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(false);
119 : 121 : values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(InvalidOid);
120 : 121 : values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
121 : 121 : values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
122 : 121 : values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
123 : 121 : nulls[Anum_pg_type_typdefaultbin - 1] = true;
124 : 121 : nulls[Anum_pg_type_typdefault - 1] = true;
5009 peter_e@gmx.net 125 : 121 : nulls[Anum_pg_type_typacl - 1] = true;
126 : :
127 : : /* Use binary-upgrade override for pg_type.oid? */
4030 bruce@momjian.us 128 [ + + ]: 121 : if (IsBinaryUpgrade)
129 : : {
130 [ - + ]: 8 : if (!OidIsValid(binary_upgrade_next_pg_type_oid))
4030 bruce@momjian.us 131 [ # # ]:UBC 0 : ereport(ERROR,
132 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
133 : : errmsg("pg_type OID value not set when in binary upgrade mode")));
134 : :
2482 andres@anarazel.de 135 :CBC 8 : typoid = binary_upgrade_next_pg_type_oid;
5735 bruce@momjian.us 136 : 8 : binary_upgrade_next_pg_type_oid = InvalidOid;
137 : : }
138 : : else
139 : : {
2482 andres@anarazel.de 140 : 113 : typoid = GetNewOidWithIndex(pg_type_desc, TypeOidIndexId,
141 : : Anum_pg_type_oid);
142 : : }
143 : :
144 : 121 : values[Anum_pg_type_oid - 1] = ObjectIdGetDatum(typoid);
145 : :
146 : : /*
147 : : * create a new type tuple
148 : : */
149 : 121 : tup = heap_form_tuple(tupDesc, values, nulls);
150 : :
151 : : /*
152 : : * insert the tuple in the relation and get the tuple's oid.
153 : : */
154 : 121 : CatalogTupleInsert(pg_type_desc, tup);
155 : :
156 : : /*
157 : : * Create dependencies. We can/must skip this in bootstrap mode.
158 : : */
8277 tgl@sss.pgh.pa.us 159 [ + - ]: 121 : if (!IsBootstrapProcessingMode())
2010 160 : 121 : GenerateTypeDependencies(tup,
161 : : pg_type_desc,
162 : : NULL,
163 : : NULL,
164 : : 0,
165 : : false,
166 : : false,
167 : : true, /* make extension dependency */
168 : : false);
169 : :
170 : : /* Post creation hook for new shell type */
4567 rhaas@postgresql.org 171 [ - + ]: 121 : InvokeObjectPostCreateHook(TypeRelationId, typoid, 0);
172 : :
3840 alvherre@alvh.no-ip. 173 : 121 : ObjectAddressSet(address, TypeRelationId, typoid);
174 : :
175 : : /*
176 : : * clean up and return the type-oid
177 : : */
9396 JanWieck@Yahoo.com 178 : 121 : heap_freetuple(tup);
2420 andres@anarazel.de 179 : 121 : table_close(pg_type_desc, RowExclusiveLock);
180 : :
3840 alvherre@alvh.no-ip. 181 : 121 : 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
6693 tgl@sss.pgh.pa.us 195 : 66723 : 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 subscriptProcedure,
214 : : Oid elementType,
215 : : bool isImplicitArray,
216 : : Oid arrayType,
217 : : Oid baseType,
218 : : const char *defaultTypeValue, /* human-readable rep */
219 : : char *defaultTypeBin, /* cooked rep */
220 : : bool passedByValue,
221 : : char alignment,
222 : : char storage,
223 : : int32 typeMod,
224 : : int32 typNDims, /* Array dimensions for baseType */
225 : : bool typeNotNull,
226 : : Oid typeCollation)
227 : : {
228 : : Relation pg_type_desc;
229 : : Oid typeObjectId;
230 : : bool isDependentType;
8277 231 : 66723 : bool rebuildDeps = false;
232 : : Acl *typacl;
233 : : HeapTuple tup;
234 : : bool nulls[Natts_pg_type];
235 : : bool replaces[Natts_pg_type];
236 : : Datum values[Natts_pg_type];
237 : : NameData name;
238 : : int i;
239 : : ObjectAddress address;
240 : :
241 : : /*
242 : : * We assume that the caller validated the arguments individually, but did
243 : : * not check for bad combinations.
244 : : *
245 : : * Validate size specifications: either positive (fixed-length) or -1
246 : : * (varlena) or -2 (cstring).
247 : : */
8414 248 [ + + - + : 66723 : if (!(internalSize > 0 ||
- - ]
249 : : internalSize == -1 ||
250 : : internalSize == -2))
8083 tgl@sss.pgh.pa.us 251 [ # # ]:UBC 0 : ereport(ERROR,
252 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
253 : : errmsg("invalid type internal size %d",
254 : : internalSize)));
255 : :
6243 tgl@sss.pgh.pa.us 256 [ + + ]:CBC 66723 : if (passedByValue)
257 : : {
258 : : /*
259 : : * Pass-by-value types must have a fixed length that is one of the
260 : : * values supported by fetch_att() and store_att_byval(); and the
261 : : * alignment had better agree, too. All this code must match
262 : : * access/tupmacs.h!
263 : : */
264 [ + + ]: 546 : if (internalSize == (int16) sizeof(char))
265 : : {
2012 266 [ - + ]: 6 : if (alignment != TYPALIGN_CHAR)
6243 tgl@sss.pgh.pa.us 267 [ # # ]:UBC 0 : ereport(ERROR,
268 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
269 : : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
270 : : alignment, internalSize)));
271 : : }
6243 tgl@sss.pgh.pa.us 272 [ + + ]:CBC 540 : else if (internalSize == (int16) sizeof(int16))
273 : : {
2012 274 [ - + ]: 2 : if (alignment != TYPALIGN_SHORT)
6243 tgl@sss.pgh.pa.us 275 [ # # ]:UBC 0 : ereport(ERROR,
276 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
277 : : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
278 : : alignment, internalSize)));
279 : : }
6243 tgl@sss.pgh.pa.us 280 [ + + ]:CBC 538 : else if (internalSize == (int16) sizeof(int32))
281 : : {
2012 282 [ - + ]: 466 : if (alignment != TYPALIGN_INT)
6243 tgl@sss.pgh.pa.us 283 [ # # ]:UBC 0 : ereport(ERROR,
284 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
285 : : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
286 : : alignment, internalSize)));
287 : : }
24 tgl@sss.pgh.pa.us 288 [ + - ]:GNC 72 : else if (internalSize == (int16) sizeof(int64))
289 : : {
2012 tgl@sss.pgh.pa.us 290 [ - + ]:CBC 72 : if (alignment != TYPALIGN_DOUBLE)
6243 tgl@sss.pgh.pa.us 291 [ # # ]:UBC 0 : ereport(ERROR,
292 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
293 : : errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
294 : : alignment, internalSize)));
295 : : }
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 */
2012 tgl@sss.pgh.pa.us 305 [ + + + + ]:CBC 66177 : if (internalSize == -1 &&
306 [ - + ]: 64610 : !(alignment == TYPALIGN_INT || alignment == TYPALIGN_DOUBLE))
6243 tgl@sss.pgh.pa.us 307 [ # # ]:UBC 0 : ereport(ERROR,
308 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
309 : : errmsg("alignment \"%c\" is invalid for variable-length type",
310 : : alignment)));
311 : : /* cstring must have char alignment */
2012 tgl@sss.pgh.pa.us 312 [ - + - - ]:CBC 66177 : if (internalSize == -2 && !(alignment == TYPALIGN_CHAR))
6243 tgl@sss.pgh.pa.us 313 [ # # ]:UBC 0 : ereport(ERROR,
314 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
315 : : errmsg("alignment \"%c\" is invalid for variable-length type",
316 : : alignment)));
317 : : }
318 : :
319 : : /* Only varlena types can be toasted */
2012 tgl@sss.pgh.pa.us 320 [ + + - + ]:CBC 66723 : if (storage != TYPSTORAGE_PLAIN && internalSize != -1)
8083 tgl@sss.pgh.pa.us 321 [ # # ]:UBC 0 : ereport(ERROR,
322 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
323 : : errmsg("fixed-size types must have storage PLAIN")));
324 : :
325 : : /*
326 : : * This is a dependent type if it's an implicitly-created array type or
327 : : * multirange type, or if it's a relation rowtype that's not a composite
328 : : * type. For such types we'll leave the ACL empty, and we'll skip
329 : : * creating some dependency records because there will be a dependency
330 : : * already through the depended-on type or relation. (Caution: this is
331 : : * closely intertwined with some behavior in GenerateTypeDependencies.)
332 : : */
2493 tgl@sss.pgh.pa.us 333 [ + + ]:CBC 33368 : isDependentType = isImplicitArray ||
570 334 [ + + + + ]: 132291 : typeType == TYPTYPE_MULTIRANGE ||
2493 335 [ + + ]: 32200 : (OidIsValid(relationOid) && relationKind != RELKIND_COMPOSITE_TYPE);
336 : :
337 : : /*
338 : : * initialize arrays needed for heap_form_tuple or heap_modify_tuple
339 : : */
10226 bruce@momjian.us 340 [ + + ]: 2201859 : for (i = 0; i < Natts_pg_type; ++i)
341 : : {
6152 tgl@sss.pgh.pa.us 342 : 2135136 : nulls[i] = false;
343 : 2135136 : replaces[i] = true;
8766 344 : 2135136 : values[i] = (Datum) 0;
345 : : }
346 : :
347 : : /*
348 : : * insert data values
349 : : */
9867 bruce@momjian.us 350 : 66723 : namestrcpy(&name, typeName);
5196 tgl@sss.pgh.pa.us 351 : 66723 : values[Anum_pg_type_typname - 1] = NameGetDatum(&name);
352 : 66723 : values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace);
353 : 66723 : values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId);
354 : 66723 : values[Anum_pg_type_typlen - 1] = Int16GetDatum(internalSize);
355 : 66723 : values[Anum_pg_type_typbyval - 1] = BoolGetDatum(passedByValue);
356 : 66723 : values[Anum_pg_type_typtype - 1] = CharGetDatum(typeType);
357 : 66723 : values[Anum_pg_type_typcategory - 1] = CharGetDatum(typeCategory);
358 : 66723 : values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(typePreferred);
359 : 66723 : values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(true);
360 : 66723 : values[Anum_pg_type_typdelim - 1] = CharGetDatum(typDelim);
361 : 66723 : values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(relationOid);
1732 362 : 66723 : values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(subscriptProcedure);
5196 363 : 66723 : values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(elementType);
364 : 66723 : values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(arrayType);
365 : 66723 : values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(inputProcedure);
366 : 66723 : values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(outputProcedure);
367 : 66723 : values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(receiveProcedure);
368 : 66723 : values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(sendProcedure);
369 : 66723 : values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(typmodinProcedure);
370 : 66723 : values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(typmodoutProcedure);
371 : 66723 : values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(analyzeProcedure);
372 : 66723 : values[Anum_pg_type_typalign - 1] = CharGetDatum(alignment);
373 : 66723 : values[Anum_pg_type_typstorage - 1] = CharGetDatum(storage);
374 : 66723 : values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(typeNotNull);
375 : 66723 : values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(baseType);
376 : 66723 : values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
377 : 66723 : values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
378 : 66723 : values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
379 : :
380 : : /*
381 : : * initialize the default binary value for this type. Check for nulls of
382 : : * course.
383 : : */
8572 bruce@momjian.us 384 [ + + ]: 66723 : if (defaultTypeBin)
5196 tgl@sss.pgh.pa.us 385 : 81 : values[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(defaultTypeBin);
386 : : else
387 : 66642 : nulls[Anum_pg_type_typdefaultbin - 1] = true;
388 : :
389 : : /*
390 : : * initialize the default value for this type.
391 : : */
8766 392 [ + + ]: 66723 : if (defaultTypeValue)
5196 393 : 90 : values[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultTypeValue);
394 : : else
395 : 66633 : nulls[Anum_pg_type_typdefault - 1] = true;
396 : :
397 : : /*
398 : : * Initialize the type's ACL, too. But dependent types don't get one.
399 : : */
2493 400 [ + + ]: 66723 : if (isDependentType)
401 : 65279 : typacl = NULL;
402 : : else
403 : 1444 : typacl = get_user_default_acl(OBJECT_TYPE, ownerId,
404 : : typeNamespace);
5009 peter_e@gmx.net 405 [ + + ]: 66723 : if (typacl != NULL)
406 : 3 : values[Anum_pg_type_typacl - 1] = PointerGetDatum(typacl);
407 : : else
408 : 66720 : nulls[Anum_pg_type_typacl - 1] = true;
409 : :
410 : : /*
411 : : * open pg_type and prepare to insert or update a row.
412 : : *
413 : : * NOTE: updating will not work correctly in bootstrap mode; but we don't
414 : : * expect to be overwriting any shell types in bootstrap mode.
415 : : */
2420 andres@anarazel.de 416 : 66723 : pg_type_desc = table_open(TypeRelationId, RowExclusiveLock);
417 : :
5683 rhaas@postgresql.org 418 : 66723 : tup = SearchSysCacheCopy2(TYPENAMENSP,
419 : : CStringGetDatum(typeName),
420 : : ObjectIdGetDatum(typeNamespace));
10226 bruce@momjian.us 421 [ + + ]: 66723 : if (HeapTupleIsValid(tup))
422 : : {
2482 andres@anarazel.de 423 : 109 : Form_pg_type typform = (Form_pg_type) GETSTRUCT(tup);
424 : :
425 : : /*
426 : : * check that the type is not already defined. It may exist as a
427 : : * shell type, however.
428 : : */
429 [ - + ]: 109 : if (typform->typisdefined)
8083 tgl@sss.pgh.pa.us 430 [ # # ]:UBC 0 : ereport(ERROR,
431 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
432 : : errmsg("type \"%s\" already exists", typeName)));
433 : :
434 : : /*
435 : : * shell type must have been created by same owner
436 : : */
2482 andres@anarazel.de 437 [ - + ]:CBC 109 : if (typform->typowner != ownerId)
2835 peter_e@gmx.net 438 :UBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TYPE, typeName);
439 : :
440 : : /* trouble if caller wanted to force the OID */
6693 tgl@sss.pgh.pa.us 441 [ - + ]:CBC 109 : if (OidIsValid(newTypeOid))
6693 tgl@sss.pgh.pa.us 442 [ # # ]:UBC 0 : elog(ERROR, "cannot assign new OID to existing shell type");
443 : :
2482 andres@anarazel.de 444 :CBC 109 : replaces[Anum_pg_type_oid - 1] = false;
445 : :
446 : : /*
447 : : * Okay to update existing shell type tuple
448 : : */
6152 tgl@sss.pgh.pa.us 449 : 109 : tup = heap_modify_tuple(tup,
450 : : RelationGetDescr(pg_type_desc),
451 : : values,
452 : : nulls,
453 : : replaces);
454 : :
3140 alvherre@alvh.no-ip. 455 : 109 : CatalogTupleUpdate(pg_type_desc, &tup->t_self, tup);
456 : :
2482 andres@anarazel.de 457 : 109 : typeObjectId = typform->oid;
458 : :
8277 tgl@sss.pgh.pa.us 459 : 109 : rebuildDeps = true; /* get rid of shell type's dependencies */
460 : : }
461 : : else
462 : : {
463 : : /* Force the OID if requested by caller */
6693 464 [ + + ]: 66614 : if (OidIsValid(newTypeOid))
2482 andres@anarazel.de 465 : 33685 : typeObjectId = newTypeOid;
466 : : /* Use binary-upgrade override for pg_type.oid, if supplied. */
4030 bruce@momjian.us 467 [ + + ]: 32929 : else if (IsBinaryUpgrade)
468 : : {
469 [ - + ]: 854 : if (!OidIsValid(binary_upgrade_next_pg_type_oid))
4030 bruce@momjian.us 470 [ # # ]:UBC 0 : ereport(ERROR,
471 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
472 : : errmsg("pg_type OID value not set when in binary upgrade mode")));
473 : :
2482 andres@anarazel.de 474 :CBC 854 : typeObjectId = binary_upgrade_next_pg_type_oid;
5735 bruce@momjian.us 475 : 854 : binary_upgrade_next_pg_type_oid = InvalidOid;
476 : : }
477 : : else
478 : : {
2482 andres@anarazel.de 479 : 32075 : typeObjectId = GetNewOidWithIndex(pg_type_desc, TypeOidIndexId,
480 : : Anum_pg_type_oid);
481 : : }
482 : :
483 : 66614 : values[Anum_pg_type_oid - 1] = ObjectIdGetDatum(typeObjectId);
484 : :
485 : 66614 : tup = heap_form_tuple(RelationGetDescr(pg_type_desc),
486 : : values, nulls);
487 : :
488 : 66614 : CatalogTupleInsert(pg_type_desc, tup);
489 : : }
490 : :
491 : : /*
492 : : * Create dependencies. We can/must skip this in bootstrap mode.
493 : : */
8451 tgl@sss.pgh.pa.us 494 [ + + ]: 66723 : if (!IsBootstrapProcessingMode())
2010 495 [ + + ]: 60804 : GenerateTypeDependencies(tup,
496 : : pg_type_desc,
497 : : (defaultTypeBin ?
8277 498 : 81 : stringToNode(defaultTypeBin) :
499 : : NULL),
500 : : typacl,
501 : : relationKind,
502 : : isImplicitArray,
503 : : isDependentType,
504 : : true, /* make extension dependency */
505 : : rebuildDeps);
506 : :
507 : : /* Post creation hook for new type */
4567 rhaas@postgresql.org 508 [ + + ]: 66722 : InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0);
509 : :
3840 alvherre@alvh.no-ip. 510 : 66722 : ObjectAddressSet(address, TypeRelationId, typeObjectId);
511 : :
512 : : /*
513 : : * finish up
514 : : */
2420 andres@anarazel.de 515 : 66722 : table_close(pg_type_desc, RowExclusiveLock);
516 : :
3840 alvherre@alvh.no-ip. 517 : 66722 : return address;
518 : : }
519 : :
520 : : /*
521 : : * GenerateTypeDependencies: build the dependencies needed for a type
522 : : *
523 : : * Most of what this function needs to know about the type is passed as the
524 : : * new pg_type row, typeTuple. We make callers pass the pg_type Relation
525 : : * as well, so that we have easy access to a tuple descriptor for the row.
526 : : *
527 : : * While this is able to extract the defaultExpr and typacl from the tuple,
528 : : * doing so is relatively expensive, and callers may have those values at
529 : : * hand already. Pass those if handy, otherwise pass NULL. (typacl is really
530 : : * "Acl *", but we declare it "void *" to avoid including acl.h in pg_type.h.)
531 : : *
532 : : * relationKind and isImplicitArray are likewise somewhat expensive to deduce
533 : : * from the tuple, so we make callers pass those (they're not optional).
534 : : *
535 : : * isDependentType is true if this is an implicit array, multirange, or
536 : : * relation rowtype; that means it doesn't need its own dependencies on owner
537 : : * etc.
538 : : *
539 : : * We make an extension-membership dependency if we're in an extension
540 : : * script and makeExtensionDep is true.
541 : : * makeExtensionDep should be true when creating a new type or replacing a
542 : : * shell type, but not for ALTER TYPE on an existing type. Passing false
543 : : * causes the type's extension membership to be left alone.
544 : : *
545 : : * rebuild should be true if this is a pre-existing type. We will remove
546 : : * existing dependencies and rebuild them from scratch. This is needed for
547 : : * ALTER TYPE, and also when replacing a shell type. We don't remove any
548 : : * existing extension dependency, though; hence, if makeExtensionDep is also
549 : : * true and we're in an extension script, an error will occur unless the
550 : : * type already belongs to the current extension. That's the behavior we
551 : : * want when replacing a shell type, which is the only case where both flags
552 : : * are true.
553 : : */
554 : : void
2010 tgl@sss.pgh.pa.us 555 : 60884 : GenerateTypeDependencies(HeapTuple typeTuple,
556 : : Relation typeCatalog,
557 : : Node *defaultExpr,
558 : : void *typacl,
559 : : char relationKind, /* only for relation rowtypes */
560 : : bool isImplicitArray,
561 : : bool isDependentType,
562 : : bool makeExtensionDep,
563 : : bool rebuild)
564 : : {
565 : 60884 : Form_pg_type typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
566 : 60884 : Oid typeObjectId = typeForm->oid;
567 : : Datum datum;
568 : : bool isNull;
569 : : ObjectAddress myself,
570 : : referenced;
571 : : ObjectAddresses *addrs_normal;
572 : :
573 : : /* Extract defaultExpr if caller didn't pass it */
574 [ + + ]: 60884 : if (defaultExpr == NULL)
575 : : {
576 : 60799 : datum = heap_getattr(typeTuple, Anum_pg_type_typdefaultbin,
577 : : RelationGetDescr(typeCatalog), &isNull);
578 [ - + ]: 60799 : if (!isNull)
2010 tgl@sss.pgh.pa.us 579 :UBC 0 : defaultExpr = stringToNode(TextDatumGetCString(datum));
580 : : }
581 : : /* Extract typacl if caller didn't pass it */
2010 tgl@sss.pgh.pa.us 582 [ + + ]:CBC 60884 : if (typacl == NULL)
583 : : {
584 : 60881 : datum = heap_getattr(typeTuple, Anum_pg_type_typacl,
585 : : RelationGetDescr(typeCatalog), &isNull);
586 [ - + ]: 60881 : if (!isNull)
2010 tgl@sss.pgh.pa.us 587 :UBC 0 : typacl = DatumGetAclPCopy(datum);
588 : : }
589 : :
590 : : /* If rebuild, first flush old dependencies, except extension deps */
8310 bruce@momjian.us 591 [ + + ]:CBC 60884 : if (rebuild)
592 : : {
5324 tgl@sss.pgh.pa.us 593 : 149 : deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true);
6071 594 : 149 : deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0);
595 : : }
596 : :
1895 michael@paquier.xyz 597 : 60884 : ObjectAddressSet(myself, TypeRelationId, typeObjectId);
598 : :
599 : : /*
600 : : * Make dependencies on namespace, owner, ACL.
601 : : *
602 : : * Skip these for a dependent type, since it will have such dependencies
603 : : * indirectly through its depended-on type or relation. An exception is
604 : : * that multiranges need their own namespace dependency, since we don't
605 : : * force them to be in the same schema as their range type.
606 : : */
607 : :
608 : : /* collects normal dependencies for bulk recording */
1827 609 : 60884 : addrs_normal = new_object_addresses();
610 : :
570 tgl@sss.pgh.pa.us 611 [ + + + + ]: 60884 : if (!isDependentType || typeForm->typtype == TYPTYPE_MULTIRANGE)
612 : : {
1895 michael@paquier.xyz 613 : 1682 : ObjectAddressSet(referenced, NamespaceRelationId,
614 : : typeForm->typnamespace);
570 tgl@sss.pgh.pa.us 615 : 1682 : add_exact_object_address(&referenced, addrs_normal);
616 : : }
617 : :
618 [ + + ]: 60884 : if (!isDependentType)
619 : : {
2493 620 : 1602 : recordDependencyOnOwner(TypeRelationId, typeObjectId,
621 : : typeForm->typowner);
622 : :
623 : 1602 : recordDependencyOnNewAcl(TypeRelationId, typeObjectId, 0,
624 : : typeForm->typowner, typacl);
625 : : }
626 : :
627 : : /*
628 : : * Make extension dependency if requested.
629 : : *
630 : : * We used to skip this for dependent types, but it seems better to record
631 : : * their extension membership explicitly; otherwise code such as
632 : : * postgres_fdw's shippability test will be fooled.
633 : : */
551 634 [ + + ]: 60884 : if (makeExtensionDep)
635 : 60844 : recordDependencyOnCurrentExtension(&myself, rebuild);
636 : :
637 : : /* Normal dependencies on the I/O and support functions */
2493 638 [ + - ]: 60883 : if (OidIsValid(typeForm->typinput))
639 : : {
1895 michael@paquier.xyz 640 : 60883 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typinput);
1827 641 : 60883 : add_exact_object_address(&referenced, addrs_normal);
642 : : }
643 : :
2493 tgl@sss.pgh.pa.us 644 [ + - ]: 60883 : if (OidIsValid(typeForm->typoutput))
645 : : {
1895 michael@paquier.xyz 646 : 60883 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typoutput);
1827 647 : 60883 : add_exact_object_address(&referenced, addrs_normal);
648 : : }
649 : :
2493 tgl@sss.pgh.pa.us 650 [ + + ]: 60883 : if (OidIsValid(typeForm->typreceive))
651 : : {
1895 michael@paquier.xyz 652 : 60660 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typreceive);
1827 653 : 60660 : add_exact_object_address(&referenced, addrs_normal);
654 : : }
655 : :
2493 tgl@sss.pgh.pa.us 656 [ + + ]: 60883 : if (OidIsValid(typeForm->typsend))
657 : : {
1895 michael@paquier.xyz 658 : 60654 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typsend);
1827 659 : 60654 : add_exact_object_address(&referenced, addrs_normal);
660 : : }
661 : :
2493 tgl@sss.pgh.pa.us 662 [ + + ]: 60883 : if (OidIsValid(typeForm->typmodin))
663 : : {
1895 michael@paquier.xyz 664 : 14 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typmodin);
1827 665 : 14 : add_exact_object_address(&referenced, addrs_normal);
666 : : }
667 : :
2493 tgl@sss.pgh.pa.us 668 [ + + ]: 60883 : if (OidIsValid(typeForm->typmodout))
669 : : {
1895 michael@paquier.xyz 670 : 14 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typmodout);
1827 671 : 14 : add_exact_object_address(&referenced, addrs_normal);
672 : : }
673 : :
2493 tgl@sss.pgh.pa.us 674 [ + + ]: 60883 : if (OidIsValid(typeForm->typanalyze))
675 : : {
1895 michael@paquier.xyz 676 : 30606 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typanalyze);
1827 677 : 30606 : add_exact_object_address(&referenced, addrs_normal);
678 : : }
679 : :
1732 tgl@sss.pgh.pa.us 680 [ + + ]: 60883 : if (OidIsValid(typeForm->typsubscript))
681 : : {
682 : 30372 : ObjectAddressSet(referenced, ProcedureRelationId, typeForm->typsubscript);
683 : 30372 : add_exact_object_address(&referenced, addrs_normal);
684 : : }
685 : :
686 : : /* Normal dependency from a domain to its base type. */
1827 michael@paquier.xyz 687 [ + + ]: 60883 : if (OidIsValid(typeForm->typbasetype))
688 : : {
689 : 682 : ObjectAddressSet(referenced, TypeRelationId, typeForm->typbasetype);
690 : 682 : add_exact_object_address(&referenced, addrs_normal);
691 : : }
692 : :
693 : : /*
694 : : * Normal dependency from a domain to its collation. We know the default
695 : : * collation is pinned, so don't bother recording it.
696 : : */
697 [ + + ]: 60883 : if (OidIsValid(typeForm->typcollation) &&
698 [ + + ]: 520 : typeForm->typcollation != DEFAULT_COLLATION_OID)
699 : : {
700 : 308 : ObjectAddressSet(referenced, CollationRelationId, typeForm->typcollation);
701 : 308 : add_exact_object_address(&referenced, addrs_normal);
702 : : }
703 : :
704 : 60883 : record_object_address_dependencies(&myself, addrs_normal, DEPENDENCY_NORMAL);
705 : 60883 : free_object_addresses(addrs_normal);
706 : :
707 : : /* Normal dependency on the default expression. */
708 [ + + ]: 60883 : if (defaultExpr)
709 : 85 : recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
710 : :
711 : : /*
712 : : * If the type is a rowtype for a relation, mark it as internally
713 : : * dependent on the relation, *unless* it is a stand-alone composite type
714 : : * relation. For the latter case, we have to reverse the dependency.
715 : : *
716 : : * In the former case, this allows the type to be auto-dropped when the
717 : : * relation is, and not otherwise. And in the latter, of course we get the
718 : : * opposite effect.
719 : : */
2493 tgl@sss.pgh.pa.us 720 [ + + ]: 60883 : if (OidIsValid(typeForm->typrelid))
721 : : {
1895 michael@paquier.xyz 722 : 29200 : ObjectAddressSet(referenced, RelationRelationId, typeForm->typrelid);
723 : :
8310 bruce@momjian.us 724 [ + + ]: 29200 : if (relationKind != RELKIND_COMPOSITE_TYPE)
725 : 28844 : recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
726 : : else
727 : 356 : recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
728 : : }
729 : :
730 : : /*
731 : : * If the type is an implicitly-created array type, mark it as internally
732 : : * dependent on the element type. Otherwise, if it has an element type,
733 : : * the dependency is a normal one.
734 : : */
2493 tgl@sss.pgh.pa.us 735 [ + + ]: 60883 : if (OidIsValid(typeForm->typelem))
736 : : {
1895 michael@paquier.xyz 737 : 30362 : ObjectAddressSet(referenced, TypeRelationId, typeForm->typelem);
6693 tgl@sss.pgh.pa.us 738 [ + + ]: 30362 : recordDependencyOn(&myself, &referenced,
739 : : isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL);
740 : : }
741 : :
742 : : /*
743 : : * Note: you might expect that we should record an internal dependency of
744 : : * a multirange on its range type here, by analogy with the cases above.
745 : : * But instead, that is done by RangeCreate(), which also handles
746 : : * recording of other range-type-specific dependencies. That's pretty
747 : : * bogus. It's okay for now, because there are no cases where we need to
748 : : * regenerate the dependencies of a range or multirange type. But someday
749 : : * we might need to move that logic here to allow such regeneration.
750 : : */
10651 scrappy@hub.org 751 : 60883 : }
752 : :
753 : : /*
754 : : * RenameTypeInternal
755 : : * This renames a type, as well as any associated array type.
756 : : *
757 : : * Caller must have already checked privileges.
758 : : *
759 : : * Currently this is used for renaming table rowtypes and for
760 : : * ALTER TYPE RENAME TO command.
761 : : */
762 : : void
6380 tgl@sss.pgh.pa.us 763 : 166 : RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
764 : : {
765 : : Relation pg_type_desc;
766 : : HeapTuple tuple;
767 : : Form_pg_type typ;
768 : : Oid arrayOid;
769 : : Oid oldTypeOid;
770 : :
2420 andres@anarazel.de 771 : 166 : pg_type_desc = table_open(TypeRelationId, RowExclusiveLock);
772 : :
5683 rhaas@postgresql.org 773 : 166 : tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
9060 tgl@sss.pgh.pa.us 774 [ - + ]: 166 : if (!HeapTupleIsValid(tuple))
6692 tgl@sss.pgh.pa.us 775 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", typeOid);
6692 tgl@sss.pgh.pa.us 776 :CBC 166 : typ = (Form_pg_type) GETSTRUCT(tuple);
777 : :
778 : : /* We are not supposed to be changing schemas here */
779 [ - + ]: 166 : Assert(typeNamespace == typ->typnamespace);
780 : :
781 : 166 : arrayOid = typ->typarray;
782 : :
783 : : /* Check for a conflicting type name. */
2482 andres@anarazel.de 784 : 166 : oldTypeOid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
785 : : CStringGetDatum(newTypeName),
786 : : ObjectIdGetDatum(typeNamespace));
787 : :
788 : : /*
789 : : * If there is one, see if it's an autogenerated array type, and if so
790 : : * rename it out of the way. (But we must skip that for a shell type
791 : : * because moveArrayTypeName will do the wrong thing in that case.)
792 : : * Otherwise, we can at least give a more friendly error than unique-index
793 : : * violation.
794 : : */
3025 tgl@sss.pgh.pa.us 795 [ + + ]: 166 : if (OidIsValid(oldTypeOid))
796 : : {
797 [ + - + - ]: 12 : if (get_typisdefined(oldTypeOid) &&
798 : 6 : moveArrayTypeName(oldTypeOid, newTypeName, typeNamespace))
799 : : /* successfully dodged the problem */ ;
800 : : else
3025 tgl@sss.pgh.pa.us 801 [ # # ]:UBC 0 : ereport(ERROR,
802 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
803 : : errmsg("type \"%s\" already exists", newTypeName)));
804 : : }
805 : :
806 : : /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
6692 tgl@sss.pgh.pa.us 807 :CBC 166 : namestrcpy(&(typ->typname), newTypeName);
808 : :
3140 alvherre@alvh.no-ip. 809 : 166 : CatalogTupleUpdate(pg_type_desc, &tuple->t_self, tuple);
810 : :
4556 rhaas@postgresql.org 811 [ - + ]: 166 : InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
812 : :
9060 tgl@sss.pgh.pa.us 813 : 166 : heap_freetuple(tuple);
2420 andres@anarazel.de 814 : 166 : table_close(pg_type_desc, RowExclusiveLock);
815 : :
816 : : /*
817 : : * If the type has an array type, recurse to handle that. But we don't
818 : : * need to do anything more if we already renamed that array type above
819 : : * (which would happen when, eg, renaming "foo" to "_foo").
820 : : */
3025 tgl@sss.pgh.pa.us 821 [ + + + + ]: 166 : if (OidIsValid(arrayOid) && arrayOid != oldTypeOid)
822 : : {
6505 bruce@momjian.us 823 : 75 : char *arrname = makeArrayTypeName(newTypeName, typeNamespace);
824 : :
6380 tgl@sss.pgh.pa.us 825 : 75 : RenameTypeInternal(arrayOid, arrname, typeNamespace);
6692 826 : 75 : pfree(arrname);
827 : : }
10651 scrappy@hub.org 828 : 166 : }
829 : :
830 : :
831 : : /*
832 : : * makeArrayTypeName
833 : : * - given a base type name, make an array type name for it
834 : : *
835 : : * the caller is responsible for pfreeing the result
836 : : */
837 : : char *
6693 tgl@sss.pgh.pa.us 838 : 33443 : makeArrayTypeName(const char *typeName, Oid typeNamespace)
839 : : {
840 : : char *arr_name;
1138 841 : 33443 : int pass = 0;
842 : : char suffix[NAMEDATALEN];
843 : :
844 : : /*
845 : : * Per ancient Postgres tradition, array type names are made by prepending
846 : : * an underscore to the base type name. Much client code knows that
847 : : * convention, so don't muck with it. However, the tradition is less
848 : : * clear about what to do in the corner cases where the resulting name is
849 : : * too long or conflicts with an existing name. Our current rules are (1)
850 : : * truncate the base name on the right as needed, and (2) if there is a
851 : : * conflict, append another underscore and some digits chosen to make it
852 : : * unique. This is similar to what ChooseRelationName() does.
853 : : *
854 : : * The actual name generation can be farmed out to makeObjectName() by
855 : : * giving it an empty first name component.
856 : : */
857 : :
858 : : /* First, try with no numeric suffix */
859 : 33443 : arr_name = makeObjectName("", typeName, NULL);
860 : :
861 : : for (;;)
862 : : {
863 [ + + ]: 33453 : if (!SearchSysCacheExists2(TYPENAMENSP,
864 : : CStringGetDatum(arr_name),
865 : : ObjectIdGetDatum(typeNamespace)))
866 : 33443 : break;
867 : :
868 : : /* That attempt conflicted. Prepare a new name with some digits. */
869 : 10 : pfree(arr_name);
870 : 10 : snprintf(suffix, sizeof(suffix), "%d", ++pass);
871 : 10 : arr_name = makeObjectName("", typeName, suffix);
872 : : }
873 : :
874 : 33443 : return arr_name;
875 : : }
876 : :
877 : :
878 : : /*
879 : : * moveArrayTypeName
880 : : * - try to reassign an array type name that the user wants to use.
881 : : *
882 : : * The given type name has been discovered to already exist (with the given
883 : : * OID). If it is an autogenerated array type, change the array type's name
884 : : * to not conflict. This allows the user to create type "foo" followed by
885 : : * type "_foo" without problems. (Of course, there are race conditions if
886 : : * two backends try to create similarly-named types concurrently, but the
887 : : * worst that can happen is an unnecessary failure --- anything we do here
888 : : * will be rolled back if the type creation fails due to conflicting names.)
889 : : *
890 : : * Note that this must be called *before* calling makeArrayTypeName to
891 : : * determine the new type's own array type name; else the latter will
892 : : * certainly pick the same name.
893 : : *
894 : : * Returns true if successfully moved the type, false if not.
895 : : *
896 : : * We also return true if the given type is a shell type. In this case
897 : : * the type has not been renamed out of the way, but nonetheless it can
898 : : * be expected that TypeCreate will succeed. This behavior is convenient
899 : : * for most callers --- those that need to distinguish the shell-type case
900 : : * must do their own typisdefined test.
901 : : */
902 : : bool
6692 903 : 20 : moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
904 : : {
905 : : Oid elemOid;
906 : : char *newname;
907 : :
908 : : /* We need do nothing if it's a shell type. */
909 [ + + ]: 20 : if (!get_typisdefined(typeOid))
910 : 1 : return true;
911 : :
912 : : /* Can't change it if it's not an autogenerated array type. */
913 : 19 : elemOid = get_element_type(typeOid);
914 [ + + - + ]: 32 : if (!OidIsValid(elemOid) ||
915 : 13 : get_array_type(elemOid) != typeOid)
916 : 6 : return false;
917 : :
918 : : /*
919 : : * OK, use makeArrayTypeName to pick an unused modification of the name.
920 : : * Note that since makeArrayTypeName is an iterative process, this will
921 : : * produce a name that it might have produced the first time, had the
922 : : * conflicting type we are about to create already existed.
923 : : */
924 : 13 : newname = makeArrayTypeName(typeName, typeNamespace);
925 : :
926 : : /* Apply the rename */
6380 927 : 13 : RenameTypeInternal(typeOid, newname, typeNamespace);
928 : :
929 : : /*
930 : : * We must bump the command counter so that any subsequent use of
931 : : * makeArrayTypeName sees what we just did and doesn't pick the same name.
932 : : */
6692 933 : 13 : CommandCounterIncrement();
934 : :
935 : 13 : pfree(newname);
936 : :
937 : 13 : return true;
938 : : }
939 : :
940 : :
941 : : /*
942 : : * makeMultirangeTypeName
943 : : * - given a range type name, make a multirange type name for it
944 : : *
945 : : * caller is responsible for pfreeing the result
946 : : */
947 : : char *
1721 akorotkov@postgresql 948 : 71 : makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
949 : : {
950 : : char *buf;
951 : : char *rangestr;
952 : :
953 : : /*
954 : : * If the range type name contains "range" then change that to
955 : : * "multirange". Otherwise add "_multirange" to the end.
956 : : */
957 : 71 : rangestr = strstr(rangeTypeName, "range");
958 [ + + ]: 71 : if (rangestr)
959 : : {
960 : 62 : char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
961 : :
962 : 62 : buf = psprintf("%s%s%s", prefix, "multi", rangestr);
963 : : }
964 : : else
965 : 9 : buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
966 : :
967 : : /* clip it at NAMEDATALEN-1 bytes */
968 : 71 : buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
969 : :
970 [ + + ]: 71 : if (SearchSysCacheExists2(TYPENAMENSP,
971 : : CStringGetDatum(buf),
972 : : ObjectIdGetDatum(typeNamespace)))
973 [ + - ]: 6 : ereport(ERROR,
974 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
975 : : errmsg("type \"%s\" already exists", buf),
976 : : errdetail("Failed while creating a multirange type for type \"%s\".", rangeTypeName),
977 : : errhint("You can manually specify a multirange type name using the \"multirange_type_name\" attribute.")));
978 : :
979 : 65 : return pstrdup(buf);
980 : : }
|