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