@@ -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
@@ -1959,56 +1959,78 @@ CheckOpSlotCompatibility(ExprEvalStep *op, TupleTableSlot *slot)
1959
1959
* get_cached_rowtype: utility function to lookup a rowtype tupdesc
1960
1960
*
1961
1961
* type_id, typmod: identity of the rowtype
1962
- * cache_field: where to cache the TupleDesc pointer in expression state node
1963
- * (field must be initialized to NULL)
1964
- * econtext: expression context we are executing in
1962
+ * rowcache: space for caching identity info
1963
+ * (rowcache->cacheptr must be initialized to NULL)
1964
+ * changed: if not NULL, *changed is set to true on any update
1965
1965
*
1966
- * NOTE: because the shutdown callback will be called during plan rescan,
1967
- * must be prepared to re-do this during any node execution; cannot call
1968
- * just once during expression initialization.
1966
+ * The returned TupleDesc is not guaranteed pinned; caller must pin it
1967
+ * to use it across any operation that might incur cache invalidation.
1968
+ * (The TupleDesc is always refcounted, so just use IncrTupleDescRefCount.)
1969
+ *
1970
+ * NOTE: because composite types can change contents, we must be prepared
1971
+ * to re-do this during any node execution; cannot call just once during
1972
+ * expression initialization.
1969
1973
*/
1970
1974
static TupleDesc
1971
1975
get_cached_rowtype (Oid type_id , int32 typmod ,
1972
- TupleDesc * cache_field , ExprContext * econtext )
1976
+ ExprEvalRowtypeCache * rowcache ,
1977
+ bool * changed )
1973
1978
{
1974
- TupleDesc tupDesc = * cache_field ;
1975
-
1976
- /* Do lookup if no cached value or if requested type changed */
1977
- if (tupDesc == NULL ||
1978
- type_id != tupDesc -> tdtypeid ||
1979
- typmod != tupDesc -> tdtypmod )
1979
+ if (type_id != RECORDOID )
1980
1980
{
1981
- tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
1981
+ /*
1982
+ * It's a named composite type, so use the regular typcache. Do a
1983
+ * lookup first time through, or if the composite type changed. Note:
1984
+ * "tupdesc_id == 0" may look redundant, but it protects against the
1985
+ * admittedly-theoretical possibility that type_id was RECORDOID the
1986
+ * last time through, so that the cacheptr isn't TypeCacheEntry *.
1987
+ */
1988
+ TypeCacheEntry * typentry = (TypeCacheEntry * ) rowcache -> cacheptr ;
1982
1989
1983
- if (* cache_field )
1990
+ if (unlikely (typentry == NULL ||
1991
+ rowcache -> tupdesc_id == 0 ||
1992
+ typentry -> tupDesc_identifier != rowcache -> tupdesc_id ))
1984
1993
{
1985
- /* Release old tupdesc; but callback is already registered */
1986
- ReleaseTupleDesc (* cache_field );
1987
- }
1988
- else
1989
- {
1990
- /* Need to register shutdown callback to release tupdesc */
1991
- RegisterExprContextCallback (econtext ,
1992
- ShutdownTupleDescRef ,
1993
- PointerGetDatum (cache_field ));
1994
- }
1995
- * cache_field = tupDesc ;
1994
+ typentry = lookup_type_cache (type_id , TYPECACHE_TUPDESC );
1995
+ if (typentry -> tupDesc == NULL )
1996
+ ereport (ERROR ,
1997
+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
1998
+ errmsg ("type %s is not composite" ,
1999
+ format_type_be (type_id ))));
2000
+ rowcache -> cacheptr = (void * ) typentry ;
2001
+ rowcache -> tupdesc_id = typentry -> tupDesc_identifier ;
2002
+ if (changed )
2003
+ * changed = true;
2004
+ }
2005
+ return typentry -> tupDesc ;
2006
+ }
2007
+ else
2008
+ {
2009
+ /*
2010
+ * A RECORD type, once registered, doesn't change for the life of the
2011
+ * backend. So we don't need a typcache entry as such, which is good
2012
+ * because there isn't one. It's possible that the caller is asking
2013
+ * about a different type than before, though.
2014
+ */
2015
+ TupleDesc tupDesc = (TupleDesc ) rowcache -> cacheptr ;
2016
+
2017
+ if (unlikely (tupDesc == NULL ||
2018
+ rowcache -> tupdesc_id != 0 ||
2019
+ type_id != tupDesc -> tdtypeid ||
2020
+ typmod != tupDesc -> tdtypmod ))
2021
+ {
2022
+ tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
2023
+ /* Drop pin acquired by lookup_rowtype_tupdesc */
2024
+ ReleaseTupleDesc (tupDesc );
2025
+ rowcache -> cacheptr = (void * ) tupDesc ;
2026
+ rowcache -> tupdesc_id = 0 ; /* not a valid value for non-RECORD */
2027
+ if (changed )
2028
+ * changed = true;
2029
+ }
2030
+ return tupDesc ;
1996
2031
}
1997
- return tupDesc ;
1998
2032
}
1999
2033
2000
- /*
2001
- * Callback function to release a tupdesc refcount at econtext shutdown
2002
- */
2003
- static void
2004
- ShutdownTupleDescRef (Datum arg )
2005
- {
2006
- TupleDesc * cache_field = (TupleDesc * ) DatumGetPointer (arg );
2007
-
2008
- if (* cache_field )
2009
- ReleaseTupleDesc (* cache_field );
2010
- * cache_field = NULL ;
2011
- }
2012
2034
2013
2035
/*
2014
2036
* Fast-path functions, for very simple expressions
@@ -2600,8 +2622,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
2600
2622
2601
2623
/* Lookup tupdesc if first time through or if type changes */
2602
2624
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
2603
- & op -> d .nulltest_row .argdesc ,
2604
- econtext );
2625
+ & op -> d .nulltest_row .rowcache , NULL );
2605
2626
2606
2627
/*
2607
2628
* heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
@@ -3034,8 +3055,7 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3034
3055
3035
3056
/* Lookup tupdesc if first time through or if type changes */
3036
3057
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
3037
- & op -> d .fieldselect .argdesc ,
3038
- econtext );
3058
+ & op -> d .fieldselect .rowcache , NULL );
3039
3059
3040
3060
/*
3041
3061
* Find field's attr record. Note we don't support system columns
@@ -3093,9 +3113,9 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
3093
3113
{
3094
3114
TupleDesc tupDesc ;
3095
3115
3096
- /* Lookup tupdesc if first time through or after rescan */
3116
+ /* Lookup tupdesc if first time through or if type changes */
3097
3117
tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
3098
- op -> d .fieldstore .argdesc , econtext );
3118
+ op -> d .fieldstore .rowcache , NULL );
3099
3119
3100
3120
/* Check that current tupdesc doesn't have more fields than we allocated */
3101
3121
if (unlikely (tupDesc -> natts > op -> d .fieldstore .ncolumns ))
@@ -3137,10 +3157,14 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
3137
3157
void
3138
3158
ExecEvalFieldStoreForm (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3139
3159
{
3160
+ TupleDesc tupDesc ;
3140
3161
HeapTuple tuple ;
3141
3162
3142
- /* argdesc should already be valid from the DeForm step */
3143
- tuple = heap_form_tuple (* op -> d .fieldstore .argdesc ,
3163
+ /* Lookup tupdesc (should be valid already) */
3164
+ tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
3165
+ op -> d .fieldstore .rowcache , NULL );
3166
+
3167
+ tuple = heap_form_tuple (tupDesc ,
3144
3168
op -> d .fieldstore .values ,
3145
3169
op -> d .fieldstore .nulls );
3146
3170
@@ -3157,13 +3181,13 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3157
3181
void
3158
3182
ExecEvalConvertRowtype (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3159
3183
{
3160
- ConvertRowtypeExpr * convert = op -> d .convert_rowtype .convert ;
3161
3184
HeapTuple result ;
3162
3185
Datum tupDatum ;
3163
3186
HeapTupleHeader tuple ;
3164
3187
HeapTupleData tmptup ;
3165
3188
TupleDesc indesc ,
3166
3189
outdesc ;
3190
+ bool changed = false;
3167
3191
3168
3192
/* NULL in -> NULL out */
3169
3193
if (* op -> resnull )
@@ -3172,24 +3196,19 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3172
3196
tupDatum = * op -> resvalue ;
3173
3197
tuple = DatumGetHeapTupleHeader (tupDatum );
3174
3198
3175
- /* Lookup tupdescs if first time through or after rescan */
3176
- if (op -> d .convert_rowtype .indesc == NULL )
3177
- {
3178
- get_cached_rowtype (exprType ((Node * ) convert -> arg ), -1 ,
3179
- & op -> d .convert_rowtype .indesc ,
3180
- econtext );
3181
- op -> d .convert_rowtype .initialized = false;
3182
- }
3183
- if (op -> d .convert_rowtype .outdesc == NULL )
3184
- {
3185
- get_cached_rowtype (convert -> resulttype , -1 ,
3186
- & op -> d .convert_rowtype .outdesc ,
3187
- econtext );
3188
- op -> d .convert_rowtype .initialized = false;
3189
- }
3190
-
3191
- indesc = op -> d .convert_rowtype .indesc ;
3192
- outdesc = op -> d .convert_rowtype .outdesc ;
3199
+ /*
3200
+ * Lookup tupdescs if first time through or if type changes. We'd better
3201
+ * pin them since type conversion functions could do catalog lookups and
3202
+ * hence cause cache invalidation.
3203
+ */
3204
+ indesc = get_cached_rowtype (op -> d .convert_rowtype .inputtype , -1 ,
3205
+ op -> d .convert_rowtype .incache ,
3206
+ & changed );
3207
+ IncrTupleDescRefCount (indesc );
3208
+ outdesc = get_cached_rowtype (op -> d .convert_rowtype .outputtype , -1 ,
3209
+ op -> d .convert_rowtype .outcache ,
3210
+ & changed );
3211
+ IncrTupleDescRefCount (outdesc );
3193
3212
3194
3213
/*
3195
3214
* We used to be able to assert that incoming tuples are marked with
@@ -3200,8 +3219,8 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3200
3219
Assert (HeapTupleHeaderGetTypeId (tuple ) == indesc -> tdtypeid ||
3201
3220
HeapTupleHeaderGetTypeId (tuple ) == RECORDOID );
3202
3221
3203
- /* if first time through, initialize conversion map */
3204
- if (! op -> d . convert_rowtype . initialized )
3222
+ /* if first time through, or after change, initialize conversion map */
3223
+ if (changed )
3205
3224
{
3206
3225
MemoryContext old_cxt ;
3207
3226
@@ -3210,7 +3229,6 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3210
3229
3211
3230
/* prepare map from old to new attribute numbers */
3212
3231
op -> d .convert_rowtype .map = convert_tuples_by_name (indesc , outdesc );
3213
- op -> d .convert_rowtype .initialized = true;
3214
3232
3215
3233
MemoryContextSwitchTo (old_cxt );
3216
3234
}
@@ -3240,6 +3258,9 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3240
3258
*/
3241
3259
* op -> resvalue = heap_copy_tuple_as_datum (& tmptup , outdesc );
3242
3260
}
3261
+
3262
+ DecrTupleDescRefCount (indesc );
3263
+ DecrTupleDescRefCount (outdesc );
3243
3264
}
3244
3265
3245
3266
/*
0 commit comments