@@ -145,8 +145,8 @@ static void ExecInitInterpreter(void);
145
145
static void CheckVarSlotCompatibility (TupleTableSlot * slot , int attnum , Oid vartype );
146
146
static void CheckOpSlotCompatibility (ExprEvalStep * op , TupleTableSlot * slot );
147
147
static TupleDesc get_cached_rowtype (Oid type_id , int32 typmod ,
148
- TupleDesc * cache_field , ExprContext * econtext );
149
- static void ShutdownTupleDescRef ( Datum arg );
148
+ ExprEvalRowtypeCache * rowcache ,
149
+ bool * changed );
150
150
static void ExecEvalRowNullInt (ExprState * state , ExprEvalStep * op ,
151
151
ExprContext * econtext , bool checkisnull );
152
152
@@ -1942,56 +1942,78 @@ CheckOpSlotCompatibility(ExprEvalStep *op, TupleTableSlot *slot)
1942
1942
* get_cached_rowtype: utility function to lookup a rowtype tupdesc
1943
1943
*
1944
1944
* type_id, typmod: identity of the rowtype
1945
- * cache_field: where to cache the TupleDesc pointer in expression state node
1946
- * (field must be initialized to NULL)
1947
- * econtext: expression context we are executing in
1945
+ * rowcache: space for caching identity info
1946
+ * (rowcache->cacheptr must be initialized to NULL)
1947
+ * changed: if not NULL, *changed is set to true on any update
1948
1948
*
1949
- * NOTE: because the shutdown callback will be called during plan rescan,
1950
- * must be prepared to re-do this during any node execution; cannot call
1951
- * just once during expression initialization.
1949
+ * The returned TupleDesc is not guaranteed pinned; caller must pin it
1950
+ * to use it across any operation that might incur cache invalidation.
1951
+ * (The TupleDesc is always refcounted, so just use IncrTupleDescRefCount.)
1952
+ *
1953
+ * NOTE: because composite types can change contents, we must be prepared
1954
+ * to re-do this during any node execution; cannot call just once during
1955
+ * expression initialization.
1952
1956
*/
1953
1957
static TupleDesc
1954
1958
get_cached_rowtype (Oid type_id , int32 typmod ,
1955
- TupleDesc * cache_field , ExprContext * econtext )
1959
+ ExprEvalRowtypeCache * rowcache ,
1960
+ bool * changed )
1956
1961
{
1957
- TupleDesc tupDesc = * cache_field ;
1958
-
1959
- /* Do lookup if no cached value or if requested type changed */
1960
- if (tupDesc == NULL ||
1961
- type_id != tupDesc -> tdtypeid ||
1962
- typmod != tupDesc -> tdtypmod )
1962
+ if (type_id != RECORDOID )
1963
1963
{
1964
- tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
1964
+ /*
1965
+ * It's a named composite type, so use the regular typcache. Do a
1966
+ * lookup first time through, or if the composite type changed. Note:
1967
+ * "tupdesc_id == 0" may look redundant, but it protects against the
1968
+ * admittedly-theoretical possibility that type_id was RECORDOID the
1969
+ * last time through, so that the cacheptr isn't TypeCacheEntry *.
1970
+ */
1971
+ TypeCacheEntry * typentry = (TypeCacheEntry * ) rowcache -> cacheptr ;
1965
1972
1966
- if (* cache_field )
1973
+ if (unlikely (typentry == NULL ||
1974
+ rowcache -> tupdesc_id == 0 ||
1975
+ typentry -> tupDesc_identifier != rowcache -> tupdesc_id ))
1967
1976
{
1968
- /* Release old tupdesc; but callback is already registered */
1969
- ReleaseTupleDesc (* cache_field );
1970
- }
1971
- else
1972
- {
1973
- /* Need to register shutdown callback to release tupdesc */
1974
- RegisterExprContextCallback (econtext ,
1975
- ShutdownTupleDescRef ,
1976
- PointerGetDatum (cache_field ));
1977
- }
1978
- * cache_field = tupDesc ;
1977
+ typentry = lookup_type_cache (type_id , TYPECACHE_TUPDESC );
1978
+ if (typentry -> tupDesc == NULL )
1979
+ ereport (ERROR ,
1980
+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
1981
+ errmsg ("type %s is not composite" ,
1982
+ format_type_be (type_id ))));
1983
+ rowcache -> cacheptr = (void * ) typentry ;
1984
+ rowcache -> tupdesc_id = typentry -> tupDesc_identifier ;
1985
+ if (changed )
1986
+ * changed = true;
1987
+ }
1988
+ return typentry -> tupDesc ;
1989
+ }
1990
+ else
1991
+ {
1992
+ /*
1993
+ * A RECORD type, once registered, doesn't change for the life of the
1994
+ * backend. So we don't need a typcache entry as such, which is good
1995
+ * because there isn't one. It's possible that the caller is asking
1996
+ * about a different type than before, though.
1997
+ */
1998
+ TupleDesc tupDesc = (TupleDesc ) rowcache -> cacheptr ;
1999
+
2000
+ if (unlikely (tupDesc == NULL ||
2001
+ rowcache -> tupdesc_id != 0 ||
2002
+ type_id != tupDesc -> tdtypeid ||
2003
+ typmod != tupDesc -> tdtypmod ))
2004
+ {
2005
+ tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
2006
+ /* Drop pin acquired by lookup_rowtype_tupdesc */
2007
+ ReleaseTupleDesc (tupDesc );
2008
+ rowcache -> cacheptr = (void * ) tupDesc ;
2009
+ rowcache -> tupdesc_id = 0 ; /* not a valid value for non-RECORD */
2010
+ if (changed )
2011
+ * changed = true;
2012
+ }
2013
+ return tupDesc ;
1979
2014
}
1980
- return tupDesc ;
1981
2015
}
1982
2016
1983
- /*
1984
- * Callback function to release a tupdesc refcount at econtext shutdown
1985
- */
1986
- static void
1987
- ShutdownTupleDescRef (Datum arg )
1988
- {
1989
- TupleDesc * cache_field = (TupleDesc * ) DatumGetPointer (arg );
1990
-
1991
- if (* cache_field )
1992
- ReleaseTupleDesc (* cache_field );
1993
- * cache_field = NULL ;
1994
- }
1995
2017
1996
2018
/*
1997
2019
* Fast-path functions, for very simple expressions
@@ -2583,8 +2605,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
2583
2605
2584
2606
/* Lookup tupdesc if first time through or if type changes */
2585
2607
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
2586
- & op -> d .nulltest_row .argdesc ,
2587
- econtext );
2608
+ & op -> d .nulltest_row .rowcache , NULL );
2588
2609
2589
2610
/*
2590
2611
* heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
@@ -3017,8 +3038,7 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3017
3038
3018
3039
/* Lookup tupdesc if first time through or if type changes */
3019
3040
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
3020
- & op -> d .fieldselect .argdesc ,
3021
- econtext );
3041
+ & op -> d .fieldselect .rowcache , NULL );
3022
3042
3023
3043
/*
3024
3044
* Find field's attr record. Note we don't support system columns
@@ -3076,9 +3096,9 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
3076
3096
{
3077
3097
TupleDesc tupDesc ;
3078
3098
3079
- /* Lookup tupdesc if first time through or after rescan */
3099
+ /* Lookup tupdesc if first time through or if type changes */
3080
3100
tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
3081
- op -> d .fieldstore .argdesc , econtext );
3101
+ op -> d .fieldstore .rowcache , NULL );
3082
3102
3083
3103
/* Check that current tupdesc doesn't have more fields than we allocated */
3084
3104
if (unlikely (tupDesc -> natts > op -> d .fieldstore .ncolumns ))
@@ -3120,10 +3140,14 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
3120
3140
void
3121
3141
ExecEvalFieldStoreForm (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3122
3142
{
3143
+ TupleDesc tupDesc ;
3123
3144
HeapTuple tuple ;
3124
3145
3125
- /* argdesc should already be valid from the DeForm step */
3126
- tuple = heap_form_tuple (* op -> d .fieldstore .argdesc ,
3146
+ /* Lookup tupdesc (should be valid already) */
3147
+ tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
3148
+ op -> d .fieldstore .rowcache , NULL );
3149
+
3150
+ tuple = heap_form_tuple (tupDesc ,
3127
3151
op -> d .fieldstore .values ,
3128
3152
op -> d .fieldstore .nulls );
3129
3153
@@ -3334,13 +3358,13 @@ ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op)
3334
3358
void
3335
3359
ExecEvalConvertRowtype (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3336
3360
{
3337
- ConvertRowtypeExpr * convert = op -> d .convert_rowtype .convert ;
3338
3361
HeapTuple result ;
3339
3362
Datum tupDatum ;
3340
3363
HeapTupleHeader tuple ;
3341
3364
HeapTupleData tmptup ;
3342
3365
TupleDesc indesc ,
3343
3366
outdesc ;
3367
+ bool changed = false;
3344
3368
3345
3369
/* NULL in -> NULL out */
3346
3370
if (* op -> resnull )
@@ -3349,24 +3373,19 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3349
3373
tupDatum = * op -> resvalue ;
3350
3374
tuple = DatumGetHeapTupleHeader (tupDatum );
3351
3375
3352
- /* Lookup tupdescs if first time through or after rescan */
3353
- if (op -> d .convert_rowtype .indesc == NULL )
3354
- {
3355
- get_cached_rowtype (exprType ((Node * ) convert -> arg ), -1 ,
3356
- & op -> d .convert_rowtype .indesc ,
3357
- econtext );
3358
- op -> d .convert_rowtype .initialized = false;
3359
- }
3360
- if (op -> d .convert_rowtype .outdesc == NULL )
3361
- {
3362
- get_cached_rowtype (convert -> resulttype , -1 ,
3363
- & op -> d .convert_rowtype .outdesc ,
3364
- econtext );
3365
- op -> d .convert_rowtype .initialized = false;
3366
- }
3367
-
3368
- indesc = op -> d .convert_rowtype .indesc ;
3369
- outdesc = op -> d .convert_rowtype .outdesc ;
3376
+ /*
3377
+ * Lookup tupdescs if first time through or if type changes. We'd better
3378
+ * pin them since type conversion functions could do catalog lookups and
3379
+ * hence cause cache invalidation.
3380
+ */
3381
+ indesc = get_cached_rowtype (op -> d .convert_rowtype .inputtype , -1 ,
3382
+ op -> d .convert_rowtype .incache ,
3383
+ & changed );
3384
+ IncrTupleDescRefCount (indesc );
3385
+ outdesc = get_cached_rowtype (op -> d .convert_rowtype .outputtype , -1 ,
3386
+ op -> d .convert_rowtype .outcache ,
3387
+ & changed );
3388
+ IncrTupleDescRefCount (outdesc );
3370
3389
3371
3390
/*
3372
3391
* We used to be able to assert that incoming tuples are marked with
@@ -3377,8 +3396,8 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3377
3396
Assert (HeapTupleHeaderGetTypeId (tuple ) == indesc -> tdtypeid ||
3378
3397
HeapTupleHeaderGetTypeId (tuple ) == RECORDOID );
3379
3398
3380
- /* if first time through, initialize conversion map */
3381
- if (! op -> d . convert_rowtype . initialized )
3399
+ /* if first time through, or after change, initialize conversion map */
3400
+ if (changed )
3382
3401
{
3383
3402
MemoryContext old_cxt ;
3384
3403
@@ -3387,7 +3406,6 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3387
3406
3388
3407
/* prepare map from old to new attribute numbers */
3389
3408
op -> d .convert_rowtype .map = convert_tuples_by_name (indesc , outdesc );
3390
- op -> d .convert_rowtype .initialized = true;
3391
3409
3392
3410
MemoryContextSwitchTo (old_cxt );
3393
3411
}
@@ -3417,6 +3435,9 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3417
3435
*/
3418
3436
* op -> resvalue = heap_copy_tuple_as_datum (& tmptup , outdesc );
3419
3437
}
3438
+
3439
+ DecrTupleDescRefCount (indesc );
3440
+ DecrTupleDescRefCount (outdesc );
3420
3441
}
3421
3442
3422
3443
/*
0 commit comments