OSDN Git Service

Remove 576 references of include files that were not needed.
[pg-rex/syncrep.git] / src / backend / utils / cache / typcache.c
1 /*-------------------------------------------------------------------------
2  *
3  * typcache.c
4  *        POSTGRES type cache code
5  *
6  * The type cache exists to speed lookup of certain information about data
7  * types that is not directly available from a type's pg_type row.  In
8  * particular, we use a type's default btree opclass, or the default hash
9  * opclass if no btree opclass exists, to determine which operators should
10  * be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC).
11  *
12  * Several seemingly-odd choices have been made to support use of the type
13  * cache by the generic array comparison routines array_eq() and array_cmp().
14  * Because these routines are used as index support operations, they cannot
15  * leak memory.  To allow them to execute efficiently, all information that
16  * either of them would like to re-use across calls is made available in the
17  * type cache.
18  *
19  * Once created, a type cache entry lives as long as the backend does, so
20  * there is no need for a call to release a cache entry.  (For present uses,
21  * it would be okay to flush type cache entries at the ends of transactions,
22  * if we needed to reclaim space.)
23  *
24  * There is presently no provision for clearing out a cache entry if the
25  * stored data becomes obsolete.  (The code will work if a type acquires
26  * opclasses it didn't have before while a backend runs --- but not if the
27  * definition of an existing opclass is altered.)  However, the relcache
28  * doesn't cope with opclasses changing under it, either, so this seems
29  * a low-priority problem.
30  *
31  * We do support clearing the tuple descriptor part of a rowtype's cache
32  * entry, since that may need to change as a consequence of ALTER TABLE.
33  *
34  *
35  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
36  * Portions Copyright (c) 1994, Regents of the University of California
37  *
38  * IDENTIFICATION
39  *        $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.21 2006/07/14 14:52:25 momjian Exp $
40  *
41  *-------------------------------------------------------------------------
42  */
43 #include "postgres.h"
44
45 #include "access/hash.h"
46 #include "access/heapam.h"
47 #include "access/nbtree.h"
48 #include "catalog/pg_type.h"
49 #include "commands/defrem.h"
50 #include "utils/builtins.h"
51 #include "utils/lsyscache.h"
52 #include "utils/syscache.h"
53 #include "utils/typcache.h"
54
55
56 /* The main type cache hashtable searched by lookup_type_cache */
57 static HTAB *TypeCacheHash = NULL;
58
59 /*
60  * We use a separate table for storing the definitions of non-anonymous
61  * record types.  Once defined, a record type will be remembered for the
62  * life of the backend.  Subsequent uses of the "same" record type (where
63  * sameness means equalTupleDescs) will refer to the existing table entry.
64  *
65  * Stored record types are remembered in a linear array of TupleDescs,
66  * which can be indexed quickly with the assigned typmod.  There is also
67  * a hash table to speed searches for matching TupleDescs.      The hash key
68  * uses just the first N columns' type OIDs, and so we may have multiple
69  * entries with the same hash key.
70  */
71 #define REC_HASH_KEYS   16              /* use this many columns in hash key */
72
73 typedef struct RecordCacheEntry
74 {
75         /* the hash lookup key MUST BE FIRST */
76         Oid                     hashkey[REC_HASH_KEYS]; /* column type IDs, zero-filled */
77
78         /* list of TupleDescs for record types with this hashkey */
79         List       *tupdescs;
80 } RecordCacheEntry;
81
82 static HTAB *RecordCacheHash = NULL;
83
84 static TupleDesc *RecordCacheArray = NULL;
85 static int32 RecordCacheArrayLen = 0;   /* allocated length of array */
86 static int32 NextRecordTypmod = 0;              /* number of entries used */
87
88
89 /*
90  * lookup_type_cache
91  *
92  * Fetch the type cache entry for the specified datatype, and make sure that
93  * all the fields requested by bits in 'flags' are valid.
94  *
95  * The result is never NULL --- we will elog() if the passed type OID is
96  * invalid.  Note however that we may fail to find one or more of the
97  * requested opclass-dependent fields; the caller needs to check whether
98  * the fields are InvalidOid or not.
99  */
100 TypeCacheEntry *
101 lookup_type_cache(Oid type_id, int flags)
102 {
103         TypeCacheEntry *typentry;
104         bool            found;
105
106         if (TypeCacheHash == NULL)
107         {
108                 /* First time through: initialize the hash table */
109                 HASHCTL         ctl;
110
111                 if (!CacheMemoryContext)
112                         CreateCacheMemoryContext();
113
114                 MemSet(&ctl, 0, sizeof(ctl));
115                 ctl.keysize = sizeof(Oid);
116                 ctl.entrysize = sizeof(TypeCacheEntry);
117                 ctl.hash = oid_hash;
118                 TypeCacheHash = hash_create("Type information cache", 64,
119                                                                         &ctl, HASH_ELEM | HASH_FUNCTION);
120         }
121
122         /* Try to look up an existing entry */
123         typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
124                                                                                           (void *) &type_id,
125                                                                                           HASH_FIND, NULL);
126         if (typentry == NULL)
127         {
128                 /*
129                  * If we didn't find one, we want to make one.  But first look up the
130                  * pg_type row, just to make sure we don't make a cache entry for an
131                  * invalid type OID.
132                  */
133                 HeapTuple       tp;
134                 Form_pg_type typtup;
135
136                 tp = SearchSysCache(TYPEOID,
137                                                         ObjectIdGetDatum(type_id),
138                                                         0, 0, 0);
139                 if (!HeapTupleIsValid(tp))
140                         elog(ERROR, "cache lookup failed for type %u", type_id);
141                 typtup = (Form_pg_type) GETSTRUCT(tp);
142                 if (!typtup->typisdefined)
143                         ereport(ERROR,
144                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
145                                          errmsg("type \"%s\" is only a shell",
146                                                         NameStr(typtup->typname))));
147
148                 /* Now make the typcache entry */
149                 typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
150                                                                                                   (void *) &type_id,
151                                                                                                   HASH_ENTER, &found);
152                 Assert(!found);                 /* it wasn't there a moment ago */
153
154                 MemSet(typentry, 0, sizeof(TypeCacheEntry));
155                 typentry->type_id = type_id;
156                 typentry->typlen = typtup->typlen;
157                 typentry->typbyval = typtup->typbyval;
158                 typentry->typalign = typtup->typalign;
159                 typentry->typtype = typtup->typtype;
160                 typentry->typrelid = typtup->typrelid;
161
162                 ReleaseSysCache(tp);
163         }
164
165         /* If we haven't already found the opclass, try to do so */
166         if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
167                                   TYPECACHE_CMP_PROC |
168                                   TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO)) &&
169                 typentry->btree_opc == InvalidOid)
170         {
171                 typentry->btree_opc = GetDefaultOpClass(type_id,
172                                                                                                 BTREE_AM_OID);
173                 /* Only care about hash opclass if no btree opclass... */
174                 if (typentry->btree_opc == InvalidOid)
175                 {
176                         if (typentry->hash_opc == InvalidOid)
177                                 typentry->hash_opc = GetDefaultOpClass(type_id,
178                                                                                                            HASH_AM_OID);
179                 }
180                 else
181                 {
182                         /*
183                          * If we find a btree opclass where previously we only found a
184                          * hash opclass, forget the hash equality operator so we can use
185                          * the btree operator instead.
186                          */
187                         typentry->eq_opr = InvalidOid;
188                         typentry->eq_opr_finfo.fn_oid = InvalidOid;
189                 }
190         }
191
192         /* Look for requested operators and functions */
193         if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
194                 typentry->eq_opr == InvalidOid)
195         {
196                 if (typentry->btree_opc != InvalidOid)
197                         typentry->eq_opr = get_opclass_member(typentry->btree_opc,
198                                                                                                   InvalidOid,
199                                                                                                   BTEqualStrategyNumber);
200                 if (typentry->eq_opr == InvalidOid &&
201                         typentry->hash_opc != InvalidOid)
202                         typentry->eq_opr = get_opclass_member(typentry->hash_opc,
203                                                                                                   InvalidOid,
204                                                                                                   HTEqualStrategyNumber);
205         }
206         if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
207         {
208                 if (typentry->btree_opc != InvalidOid)
209                         typentry->lt_opr = get_opclass_member(typentry->btree_opc,
210                                                                                                   InvalidOid,
211                                                                                                   BTLessStrategyNumber);
212         }
213         if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
214         {
215                 if (typentry->btree_opc != InvalidOid)
216                         typentry->gt_opr = get_opclass_member(typentry->btree_opc,
217                                                                                                   InvalidOid,
218                                                                                                   BTGreaterStrategyNumber);
219         }
220         if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
221                 typentry->cmp_proc == InvalidOid)
222         {
223                 if (typentry->btree_opc != InvalidOid)
224                         typentry->cmp_proc = get_opclass_proc(typentry->btree_opc,
225                                                                                                   InvalidOid,
226                                                                                                   BTORDER_PROC);
227         }
228
229         /*
230          * Set up fmgr lookup info as requested
231          *
232          * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
233          * which is not quite right (they're really in DynaHashContext) but this
234          * will do for our purposes.
235          */
236         if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
237                 typentry->eq_opr_finfo.fn_oid == InvalidOid &&
238                 typentry->eq_opr != InvalidOid)
239         {
240                 Oid                     eq_opr_func;
241
242                 eq_opr_func = get_opcode(typentry->eq_opr);
243                 if (eq_opr_func != InvalidOid)
244                         fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
245                                                   CacheMemoryContext);
246         }
247         if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
248                 typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
249                 typentry->cmp_proc != InvalidOid)
250         {
251                 fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
252                                           CacheMemoryContext);
253         }
254
255         /*
256          * If it's a composite type (row type), get tupdesc if requested
257          */
258         if ((flags & TYPECACHE_TUPDESC) &&
259                 typentry->tupDesc == NULL &&
260                 typentry->typtype == 'c')
261         {
262                 Relation        rel;
263
264                 if (!OidIsValid(typentry->typrelid))    /* should not happen */
265                         elog(ERROR, "invalid typrelid for composite type %u",
266                                  typentry->type_id);
267                 rel = relation_open(typentry->typrelid, AccessShareLock);
268                 Assert(rel->rd_rel->reltype == typentry->type_id);
269
270                 /*
271                  * Link to the tupdesc and increment its refcount (we assert it's
272                  * a refcounted descriptor).  We don't use IncrTupleDescRefCount()
273                  * for this, because the reference mustn't be entered in the current
274                  * resource owner; it can outlive the current query.
275                  */
276                 typentry->tupDesc = RelationGetDescr(rel);
277
278                 Assert(typentry->tupDesc->tdrefcount > 0);
279                 typentry->tupDesc->tdrefcount++;
280
281                 relation_close(rel, AccessShareLock);
282         }
283
284         return typentry;
285 }
286
287 /*
288  * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
289  *
290  * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
291  * hasn't had its refcount bumped.
292  */
293 static TupleDesc
294 lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
295 {
296         if (type_id != RECORDOID)
297         {
298                 /*
299                  * It's a named composite type, so use the regular typcache.
300                  */
301                 TypeCacheEntry *typentry;
302
303                 typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
304                 if (typentry->tupDesc == NULL && !noError)
305                         ereport(ERROR,
306                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
307                                          errmsg("type %s is not composite",
308                                                         format_type_be(type_id))));
309                 return typentry->tupDesc;
310         }
311         else
312         {
313                 /*
314                  * It's a transient record type, so look in our record-type table.
315                  */
316                 if (typmod < 0 || typmod >= NextRecordTypmod)
317                 {
318                         if (!noError)
319                                 ereport(ERROR,
320                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
321                                                  errmsg("record type has not been registered")));
322                         return NULL;
323                 }
324                 return RecordCacheArray[typmod];
325         }
326 }
327
328 /*
329  * lookup_rowtype_tupdesc
330  *
331  * Given a typeid/typmod that should describe a known composite type,
332  * return the tuple descriptor for the type.  Will ereport on failure.
333  *
334  * Note: on success, we increment the refcount of the returned TupleDesc,
335  * and log the reference in CurrentResourceOwner.  Caller should call
336  * ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc.
337  */
338 TupleDesc
339 lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
340 {
341         TupleDesc       tupDesc;
342
343         tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
344         IncrTupleDescRefCount(tupDesc);
345         return tupDesc;
346 }
347
348 /*
349  * lookup_rowtype_tupdesc_noerror
350  *
351  * As above, but if the type is not a known composite type and noError
352  * is true, returns NULL instead of ereport'ing.  (Note that if a bogus
353  * type_id is passed, you'll get an ereport anyway.)
354  */
355 TupleDesc
356 lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
357 {
358         TupleDesc       tupDesc;
359
360         tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
361         if (tupDesc != NULL)
362                 IncrTupleDescRefCount(tupDesc);
363         return tupDesc;
364 }
365
366 /*
367  * lookup_rowtype_tupdesc_copy
368  *
369  * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
370  * copied into the CurrentMemoryContext and is not reference-counted.
371  */
372 TupleDesc
373 lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
374 {
375         TupleDesc tmp;
376
377         tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
378         return CreateTupleDescCopyConstr(tmp);
379 }
380
381
382 /*
383  * assign_record_type_typmod
384  *
385  * Given a tuple descriptor for a RECORD type, find or create a cache entry
386  * for the type, and set the tupdesc's tdtypmod field to a value that will
387  * identify this cache entry to lookup_rowtype_tupdesc.
388  */
389 void
390 assign_record_type_typmod(TupleDesc tupDesc)
391 {
392         RecordCacheEntry *recentry;
393         TupleDesc       entDesc;
394         Oid                     hashkey[REC_HASH_KEYS];
395         bool            found;
396         int                     i;
397         ListCell   *l;
398         int32           newtypmod;
399         MemoryContext oldcxt;
400
401         Assert(tupDesc->tdtypeid == RECORDOID);
402
403         if (RecordCacheHash == NULL)
404         {
405                 /* First time through: initialize the hash table */
406                 HASHCTL         ctl;
407
408                 if (!CacheMemoryContext)
409                         CreateCacheMemoryContext();
410
411                 MemSet(&ctl, 0, sizeof(ctl));
412                 ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
413                 ctl.entrysize = sizeof(RecordCacheEntry);
414                 ctl.hash = tag_hash;
415                 RecordCacheHash = hash_create("Record information cache", 64,
416                                                                           &ctl, HASH_ELEM | HASH_FUNCTION);
417         }
418
419         /* Find or create a hashtable entry for this hash class */
420         MemSet(hashkey, 0, sizeof(hashkey));
421         for (i = 0; i < tupDesc->natts; i++)
422         {
423                 if (i >= REC_HASH_KEYS)
424                         break;
425                 hashkey[i] = tupDesc->attrs[i]->atttypid;
426         }
427         recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
428                                                                                                 (void *) hashkey,
429                                                                                                 HASH_ENTER, &found);
430         if (!found)
431         {
432                 /* New entry ... hash_search initialized only the hash key */
433                 recentry->tupdescs = NIL;
434         }
435
436         /* Look for existing record cache entry */
437         foreach(l, recentry->tupdescs)
438         {
439                 entDesc = (TupleDesc) lfirst(l);
440                 if (equalTupleDescs(tupDesc, entDesc))
441                 {
442                         tupDesc->tdtypmod = entDesc->tdtypmod;
443                         return;
444                 }
445         }
446
447         /* Not present, so need to manufacture an entry */
448         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
449
450         if (RecordCacheArray == NULL)
451         {
452                 RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
453                 RecordCacheArrayLen = 64;
454         }
455         else if (NextRecordTypmod >= RecordCacheArrayLen)
456         {
457                 int32           newlen = RecordCacheArrayLen * 2;
458
459                 RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
460                                                                                                   newlen * sizeof(TupleDesc));
461                 RecordCacheArrayLen = newlen;
462         }
463
464         /* if fail in subrs, no damage except possibly some wasted memory... */
465         entDesc = CreateTupleDescCopy(tupDesc);
466         recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
467         /* mark it as a reference-counted tupdesc */
468         entDesc->tdrefcount = 1;
469         /* now it's safe to advance NextRecordTypmod */
470         newtypmod = NextRecordTypmod++;
471         entDesc->tdtypmod = newtypmod;
472         RecordCacheArray[newtypmod] = entDesc;
473
474         /* report to caller as well */
475         tupDesc->tdtypmod = newtypmod;
476
477         MemoryContextSwitchTo(oldcxt);
478 }
479
480 /*
481  * flush_rowtype_cache
482  *
483  * If a typcache entry exists for a rowtype, delete the entry's cached
484  * tuple descriptor link.  This is called from relcache.c when a cached
485  * relation tupdesc is about to be dropped.
486  */
487 void
488 flush_rowtype_cache(Oid type_id)
489 {
490         TypeCacheEntry *typentry;
491
492         if (TypeCacheHash == NULL)
493                 return;                                 /* no table, so certainly no entry */
494
495         typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
496                                                                                           (void *) &type_id,
497                                                                                           HASH_FIND, NULL);
498         if (typentry == NULL)
499                 return;                                 /* no matching entry */
500         if (typentry->tupDesc == NULL)
501                 return;                                 /* tupdesc hasn't been requested */
502
503         /*
504          * Release our refcount and free the tupdesc if none remain.
505          * (Can't use DecrTupleDescRefCount because this reference is not
506          * logged in current resource owner.)
507          */
508         Assert(typentry->tupDesc->tdrefcount > 0);
509         if (--typentry->tupDesc->tdrefcount == 0)
510                 FreeTupleDesc(typentry->tupDesc);
511
512         typentry->tupDesc = NULL;
513 }