]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_type.c
Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[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-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.58 2001/01/24 19:42:52 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/catname.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_type.h"
21 #include "miscadmin.h"
22 #include "parser/parse_func.h"
23 #include "utils/builtins.h"
24 #include "utils/fmgroids.h"
25 #include "utils/syscache.h"
26
27
28 static Oid TypeShellMakeWithOpenRelation(Relation pg_type_desc,
29                                                           char *typeName);
30
31 /* ----------------------------------------------------------------
32  *              TypeGetWithOpenRelation
33  *
34  *              preforms a scan on pg_type for a type tuple with the
35  *              given type name.
36  * ----------------------------------------------------------------
37  *              pg_type_desc                     -- reldesc for pg_type
38  *              typeName                                 -- name of type to be fetched
39  *              defined                                  -- has the type been defined?
40  */
41 static Oid
42 TypeGetWithOpenRelation(Relation pg_type_desc,
43                                                 char *typeName,
44                                                 bool *defined)
45 {
46         HeapScanDesc scan;
47         HeapTuple       tup;
48         Oid                     typoid;
49
50         static ScanKeyData typeKey[1] = {
51                 {0, Anum_pg_type_typname, F_NAMEEQ}
52         };
53
54         /* ----------------
55          *      initialize the scan key and begin a scan of pg_type
56          * ----------------
57          */
58         fmgr_info(F_NAMEEQ, &typeKey[0].sk_func);
59         typeKey[0].sk_nargs = typeKey[0].sk_func.fn_nargs;
60         typeKey[0].sk_argument = PointerGetDatum(typeName);
61
62         scan = heap_beginscan(pg_type_desc,
63                                                   0,
64                                                   SnapshotSelf, /* cache? */
65                                                   1,
66                                                   typeKey);
67
68         /* ----------------
69          *      get the type tuple, if it exists.
70          * ----------------
71          */
72         tup = heap_getnext(scan, 0);
73
74         /* ----------------
75          *      if no type tuple exists for the given type name, then
76          *      end the scan and return appropriate information.
77          * ----------------
78          */
79         if (!HeapTupleIsValid(tup))
80         {
81                 heap_endscan(scan);
82                 *defined = false;
83                 return InvalidOid;
84         }
85
86         /* ----------------
87          *      here, the type tuple does exist so we pull information from
88          *      the typisdefined field of the tuple and return the tuple's
89          *      oid, which is the oid of the type.
90          * ----------------
91          */
92         *defined = (bool) ((Form_pg_type) GETSTRUCT(tup))->typisdefined;
93         typoid = tup->t_data->t_oid;
94
95         heap_endscan(scan);
96
97         return typoid;
98 }
99
100 /* ----------------------------------------------------------------
101  *              TypeGet
102  *
103  *              Finds the ObjectId of a type, even if uncommitted; "defined"
104  *              is only set if the type has actually been defined, i.e., if
105  *              the type tuple is not a shell.
106  *
107  *              Note: the meat of this function is now in the function
108  *                        TypeGetWithOpenRelation().  -cim 6/15/90
109  *
110  *              Also called from util/remove.c
111  * ----------------------------------------------------------------
112  */
113 Oid
114 TypeGet(char *typeName,                 /* name of type to be fetched */
115                 bool *defined)                  /* has the type been defined? */
116 {
117         Relation        pg_type_desc;
118         Oid                     typeoid;
119
120         /* ----------------
121          *      open the pg_type relation
122          * ----------------
123          */
124         pg_type_desc = heap_openr(TypeRelationName, AccessShareLock);
125
126         /* ----------------
127          *      scan the type relation for the information we want
128          * ----------------
129          */
130         typeoid = TypeGetWithOpenRelation(pg_type_desc,
131                                                                           typeName,
132                                                                           defined);
133
134         /* ----------------
135          *      close the type relation and return the type oid.
136          * ----------------
137          */
138         heap_close(pg_type_desc, AccessShareLock);
139
140         return typeoid;
141 }
142
143 /* ----------------------------------------------------------------
144  *              TypeShellMakeWithOpenRelation
145  *
146  * ----------------------------------------------------------------
147  */
148 static Oid
149 TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName)
150 {
151         int                     i;
152         HeapTuple       tup;
153         Datum           values[Natts_pg_type];
154         char            nulls[Natts_pg_type];
155         Oid                     typoid;
156         NameData        name;
157         TupleDesc       tupDesc;
158
159         /* ----------------
160          *      initialize our *nulls and *values arrays
161          * ----------------
162          */
163         for (i = 0; i < Natts_pg_type; ++i)
164         {
165                 nulls[i] = ' ';
166                 values[i] = (Datum) NULL;               /* redundant, but safe */
167         }
168
169         /* ----------------
170          *      initialize *values with the type name and dummy values
171          * ----------------
172          */
173         i = 0;
174         namestrcpy(&name, typeName);
175         values[i++] = NameGetDatum(&name);                      /* 1 */
176         values[i++] = ObjectIdGetDatum(InvalidOid);     /* 2 */
177         values[i++] = Int16GetDatum(0);                         /* 3 */
178         values[i++] = Int16GetDatum(0);                         /* 4 */
179         values[i++] = BoolGetDatum(false);                      /* 5 */
180         values[i++] = CharGetDatum(0);                          /* 6 */
181         values[i++] = BoolGetDatum(false);                      /* 7 */
182         values[i++] = CharGetDatum(0);                          /* 8 */
183         values[i++] = ObjectIdGetDatum(InvalidOid);     /* 9 */
184         values[i++] = ObjectIdGetDatum(InvalidOid);     /* 10 */
185         values[i++] = ObjectIdGetDatum(InvalidOid);     /* 11 */
186         values[i++] = ObjectIdGetDatum(InvalidOid);     /* 12 */
187         values[i++] = ObjectIdGetDatum(InvalidOid);     /* 13 */
188         values[i++] = ObjectIdGetDatum(InvalidOid);     /* 14 */
189         values[i++] = CharGetDatum('i');                        /* 15 */
190         values[i++] = CharGetDatum('p');                        /* 16 */
191         values[i++] = DirectFunctionCall1(textin,
192                                                                           CStringGetDatum(typeName));   /* 17 */
193
194         /* ----------------
195          *      create a new type tuple with FormHeapTuple
196          * ----------------
197          */
198         tupDesc = pg_type_desc->rd_att;
199
200         tup = heap_formtuple(tupDesc, values, nulls);
201
202         /* ----------------
203          *      insert the tuple in the relation and get the tuple's oid.
204          * ----------------
205          */
206         heap_insert(pg_type_desc, tup);
207         typoid = tup->t_data->t_oid;
208
209         if (RelationGetForm(pg_type_desc)->relhasindex)
210         {
211                 Relation        idescs[Num_pg_type_indices];
212
213                 CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
214                 CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
215                 CatalogCloseIndices(Num_pg_type_indices, idescs);
216         }
217         /* ----------------
218          *      free the tuple and return the type-oid
219          * ----------------
220          */
221         heap_freetuple(tup);
222
223         return typoid;
224 }
225
226 /* ----------------------------------------------------------------
227  *              TypeShellMake
228  *
229  *              This procedure inserts a "shell" tuple into the type
230  *              relation.  The type tuple inserted has invalid values
231  *              and in particular, the "typisdefined" field is false.
232  *
233  *              This is used so that a tuple exists in the catalogs.
234  *              The invalid fields should be fixed up sometime after
235  *              this routine is called, and then the "typeisdefined"
236  *              field is set to true. -cim 6/15/90
237  * ----------------------------------------------------------------
238  */
239 Oid
240 TypeShellMake(char *typeName)
241 {
242         Relation        pg_type_desc;
243         Oid                     typoid;
244
245         Assert(PointerIsValid(typeName));
246
247         /* ----------------
248          *      open pg_type
249          * ----------------
250          */
251         pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
252
253         /* ----------------
254          *      insert the shell tuple
255          * ----------------
256          */
257         typoid = TypeShellMakeWithOpenRelation(pg_type_desc, typeName);
258
259         /* ----------------
260          *      close pg_type and return the tuple's oid.
261          * ----------------
262          */
263         heap_close(pg_type_desc, RowExclusiveLock);
264
265         return typoid;
266 }
267
268 /* ----------------------------------------------------------------
269  *              TypeCreate
270  *
271  *              This does all the necessary work needed to define a new type.
272  * ----------------------------------------------------------------
273  */
274 Oid
275 TypeCreate(char *typeName,
276                    Oid relationOid,             /* only for 'c'atalog typeTypes */
277                    int16 internalSize,
278                    int16 externalSize,
279                    char typeType,
280                    char typDelim,
281                    char *inputProcedure,
282                    char *outputProcedure,
283                    char *receiveProcedure,
284                    char *sendProcedure,
285                    char *elementTypeName,
286                    char *defaultTypeValue,              /* internal rep */
287                    bool passedByValue,
288                    char alignment,
289                    char storage)
290 {
291         int                     i,
292                                 j;
293         Relation        pg_type_desc;
294         HeapScanDesc pg_type_scan;
295
296         Oid                     typeObjectId;
297         Oid                     elementObjectId = InvalidOid;
298
299         HeapTuple       tup;
300         char            nulls[Natts_pg_type];
301         char            replaces[Natts_pg_type];
302         Datum           values[Natts_pg_type];
303
304         char       *procname;
305         char       *procs[4];
306         bool            defined;
307         NameData        name;
308         TupleDesc       tupDesc;
309         Oid                     argList[FUNC_MAX_ARGS];
310
311         static ScanKeyData typeKey[1] = {
312                 {0, Anum_pg_type_typname, F_NAMEEQ}
313         };
314
315         fmgr_info(F_NAMEEQ, &typeKey[0].sk_func);
316         typeKey[0].sk_nargs = typeKey[0].sk_func.fn_nargs;
317
318         /* ----------------
319          *      check that the type is not already defined.
320          * ----------------
321          */
322         typeObjectId = TypeGet(typeName, &defined);
323         if (OidIsValid(typeObjectId) && defined)
324                 elog(ERROR, "TypeCreate: type %s already defined", typeName);
325
326         /* ----------------
327          *      if this type has an associated elementType, then we check that
328          *      it is defined.
329          * ----------------
330          */
331         if (elementTypeName)
332         {
333                 elementObjectId = TypeGet(elementTypeName, &defined);
334                 if (!defined)
335                         elog(ERROR, "TypeCreate: type %s is not defined", elementTypeName);
336         }
337
338         /* ----------------
339          *      XXX comment me
340          * ----------------
341          */
342         if (externalSize == 0)
343                 externalSize = -1;              /* variable length */
344
345         /* ----------------
346          *      initialize arrays needed by FormHeapTuple
347          * ----------------
348          */
349         for (i = 0; i < Natts_pg_type; ++i)
350         {
351                 nulls[i] = ' ';
352                 replaces[i] = 'r';
353                 values[i] = (Datum) NULL;               /* redundant, but nice */
354         }
355
356         /*
357          * XXX
358          *
359          * Do this so that user-defined types have size -1 instead of zero if
360          * they are variable-length - this is so that everything else in the
361          * backend works.
362          */
363
364         if (internalSize == 0)
365                 internalSize = -1;
366
367         /* ----------------
368          *      initialize the *values information
369          * ----------------
370          */
371         i = 0;
372         namestrcpy(&name, typeName);
373         values[i++] = NameGetDatum(&name);                      /* 1 */
374         values[i++] = Int32GetDatum(GetUserId());       /* 2 */
375         values[i++] = Int16GetDatum(internalSize);      /* 3 */
376         values[i++] = Int16GetDatum(externalSize);      /* 4 */
377         values[i++] = BoolGetDatum(passedByValue);      /* 5 */
378         values[i++] = CharGetDatum(typeType);           /* 6 */
379         values[i++] = BoolGetDatum(true);                       /* 7 */
380         values[i++] = CharGetDatum(typDelim);           /* 8 */
381         values[i++] = ObjectIdGetDatum(typeType == 'c' ? relationOid : InvalidOid); /* 9 */
382         values[i++] = ObjectIdGetDatum(elementObjectId); /* 10 */
383
384         procs[0] = inputProcedure;
385         procs[1] = outputProcedure;
386         procs[2] = (receiveProcedure) ? receiveProcedure : inputProcedure;
387         procs[3] = (sendProcedure) ? sendProcedure : outputProcedure;
388
389         for (j = 0; j < 4; ++j)
390         {
391                 Oid             procOid;
392
393                 procname = procs[j];
394
395                 /*
396                  * First look for a 1-argument func with all argtypes 0. This is
397                  * valid for all four kinds of procedure.
398                  */
399                 MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
400
401                 procOid = GetSysCacheOid(PROCNAME,
402                                                                  PointerGetDatum(procname),
403                                                                  Int32GetDatum(1),
404                                                                  PointerGetDatum(argList),
405                                                                  0);
406
407                 if (!OidIsValid(procOid))
408                 {
409
410                         /*
411                          * For array types, the input procedures may take 3 args (data
412                          * value, element OID, atttypmod); the pg_proc argtype
413                          * signature is 0,OIDOID,INT4OID.  The output procedures may
414                          * take 2 args (data value, element OID).
415                          */
416                         if (OidIsValid(elementObjectId))
417                         {
418                                 int                     nargs;
419
420                                 if (j % 2)
421                                 {
422                                         /* output proc */
423                                         nargs = 2;
424                                         argList[1] = OIDOID;
425                                 }
426                                 else
427                                 {
428                                         /* input proc */
429                                         nargs = 3;
430                                         argList[1] = OIDOID;
431                                         argList[2] = INT4OID;
432                                 }
433                                 procOid = GetSysCacheOid(PROCNAME,
434                                                                                  PointerGetDatum(procname),
435                                                                                  Int32GetDatum(nargs),
436                                                                                  PointerGetDatum(argList),
437                                                                                  0);
438                         }
439                         if (!OidIsValid(procOid))
440                                 func_error("TypeCreate", procname, 1, argList, NULL);
441                 }
442
443                 values[i++] = ObjectIdGetDatum(procOid);        /* 11 - 14 */
444         }
445
446         /* ----------------
447          * set default alignment
448          * ----------------
449          */
450         values[i++] = CharGetDatum(alignment);  /* 15 */
451
452         /* ----------------
453          *      set default storage for TOAST
454          * ----------------
455          */
456         values[i++] = CharGetDatum(storage);    /* 16 */
457
458         /* ----------------
459          *      initialize the default value for this type.
460          * ----------------
461          */
462         values[i] = DirectFunctionCall1(textin, /* 17 */
463                                 CStringGetDatum(defaultTypeValue ? defaultTypeValue : "-"));
464
465         /* ----------------
466          *      open pg_type and begin a scan for the type name.
467          * ----------------
468          */
469         pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
470
471         typeKey[0].sk_argument = PointerGetDatum(typeName);
472         pg_type_scan = heap_beginscan(pg_type_desc,
473                                                                   0,
474                                                                   SnapshotSelf, /* cache? */
475                                                                   1,
476                                                                   typeKey);
477
478         /* ----------------
479          *      define the type either by adding a tuple to the type
480          *      relation, or by updating the fields of the "shell" tuple
481          *      already there.
482          * ----------------
483          */
484         tup = heap_getnext(pg_type_scan, 0);
485         if (HeapTupleIsValid(tup))
486         {
487                 tup = heap_modifytuple(tup,
488                                                            pg_type_desc,
489                                                            values,
490                                                            nulls,
491                                                            replaces);
492
493                 simple_heap_update(pg_type_desc, &tup->t_self, tup);
494
495                 typeObjectId = tup->t_data->t_oid;
496         }
497         else
498         {
499                 tupDesc = pg_type_desc->rd_att;
500
501                 tup = heap_formtuple(tupDesc,
502                                                          values,
503                                                          nulls);
504
505                 heap_insert(pg_type_desc, tup);
506
507                 typeObjectId = tup->t_data->t_oid;
508         }
509
510         /* ----------------
511          *      finish up
512          * ----------------
513          */
514         heap_endscan(pg_type_scan);
515
516         if (RelationGetForm(pg_type_desc)->relhasindex)
517         {
518                 Relation        idescs[Num_pg_type_indices];
519
520                 CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
521                 CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
522                 CatalogCloseIndices(Num_pg_type_indices, idescs);
523         }
524
525         heap_close(pg_type_desc, RowExclusiveLock);
526
527         return typeObjectId;
528 }
529
530 /* ----------------------------------------------------------------
531  *              TypeRename
532  *
533  *              This renames a type
534  * ----------------------------------------------------------------
535  */
536 void
537 TypeRename(const char *oldTypeName, const char *newTypeName)
538 {
539         Relation        pg_type_desc;
540         Relation        idescs[Num_pg_type_indices];
541         HeapTuple       tuple;
542
543         pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
544
545         tuple = SearchSysCacheCopy(TYPENAME,
546                                                            PointerGetDatum(oldTypeName),
547                                                            0, 0, 0);
548         if (!HeapTupleIsValid(tuple))
549                 elog(ERROR, "TypeRename: type \"%s\" not defined", oldTypeName);
550
551         if (SearchSysCacheExists(TYPENAME,
552                                                          PointerGetDatum(newTypeName),
553                                                          0, 0, 0))
554                 elog(ERROR, "TypeRename: type \"%s\" already defined", newTypeName);
555
556         namestrcpy(&(((Form_pg_type) GETSTRUCT(tuple))->typname), newTypeName);
557
558         simple_heap_update(pg_type_desc, &tuple->t_self, tuple);
559
560         /* update the system catalog indices */
561         CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
562         CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tuple);
563         CatalogCloseIndices(Num_pg_type_indices, idescs);
564
565         heap_freetuple(tuple);
566         heap_close(pg_type_desc, RowExclusiveLock);
567 }
568
569 /*
570  * makeArrayTypeName(typeName);
571  *        - given a base type name, make an array of type name out of it
572  *
573  * the CALLER is responsible for pfreeing the
574  */
575
576 char *
577 makeArrayTypeName(char *typeName)
578 {
579         char       *arr;
580
581         if (!typeName)
582                 return NULL;
583         arr = palloc(strlen(typeName) + 2);
584         arr[0] = '_';
585         strcpy(arr + 1, typeName);
586
587         return arr;
588
589 }