@@ -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
@@ -1919,56 +1919,78 @@ CheckOpSlotCompatibility(ExprEvalStep *op, TupleTableSlot *slot)
1919
1919
* get_cached_rowtype: utility function to lookup a rowtype tupdesc
1920
1920
*
1921
1921
* type_id, typmod: identity of the rowtype
1922
- * cache_field: where to cache the TupleDesc pointer in expression state node
1923
- * (field must be initialized to NULL)
1924
- * econtext: expression context we are executing in
1922
+ * rowcache: space for caching identity info
1923
+ * (rowcache->cacheptr must be initialized to NULL)
1924
+ * changed: if not NULL, *changed is set to true on any update
1925
1925
*
1926
- * NOTE: because the shutdown callback will be called during plan rescan,
1927
- * must be prepared to re-do this during any node execution; cannot call
1928
- * just once during expression initialization.
1926
+ * The returned TupleDesc is not guaranteed pinned; caller must pin it
1927
+ * to use it across any operation that might incur cache invalidation.
1928
+ * (The TupleDesc is always refcounted, so just use IncrTupleDescRefCount.)
1929
+ *
1930
+ * NOTE: because composite types can change contents, we must be prepared
1931
+ * to re-do this during any node execution; cannot call just once during
1932
+ * expression initialization.
1929
1933
*/
1930
1934
static TupleDesc
1931
1935
get_cached_rowtype (Oid type_id , int32 typmod ,
1932
- TupleDesc * cache_field , ExprContext * econtext )
1936
+ ExprEvalRowtypeCache * rowcache ,
1937
+ bool * changed )
1933
1938
{
1934
- TupleDesc tupDesc = * cache_field ;
1935
-
1936
- /* Do lookup if no cached value or if requested type changed */
1937
- if (tupDesc == NULL ||
1938
- type_id != tupDesc -> tdtypeid ||
1939
- typmod != tupDesc -> tdtypmod )
1939
+ if (type_id != RECORDOID )
1940
1940
{
1941
- tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
1941
+ /*
1942
+ * It's a named composite type, so use the regular typcache. Do a
1943
+ * lookup first time through, or if the composite type changed. Note:
1944
+ * "tupdesc_id == 0" may look redundant, but it protects against the
1945
+ * admittedly-theoretical possibility that type_id was RECORDOID the
1946
+ * last time through, so that the cacheptr isn't TypeCacheEntry *.
1947
+ */
1948
+ TypeCacheEntry * typentry = (TypeCacheEntry * ) rowcache -> cacheptr ;
1942
1949
1943
- if (* cache_field )
1950
+ if (unlikely (typentry == NULL ||
1951
+ rowcache -> tupdesc_id == 0 ||
1952
+ typentry -> tupDesc_identifier != rowcache -> tupdesc_id ))
1944
1953
{
1945
- /* Release old tupdesc; but callback is already registered */
1946
- ReleaseTupleDesc (* cache_field );
1954
+ typentry = lookup_type_cache (type_id , TYPECACHE_TUPDESC );
1955
+ if (typentry -> tupDesc == NULL )
1956
+ ereport (ERROR ,
1957
+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
1958
+ errmsg ("type %s is not composite" ,
1959
+ format_type_be (type_id ))));
1960
+ rowcache -> cacheptr = (void * ) typentry ;
1961
+ rowcache -> tupdesc_id = typentry -> tupDesc_identifier ;
1962
+ if (changed )
1963
+ * changed = true;
1947
1964
}
1948
- else
1965
+ return typentry -> tupDesc ;
1966
+ }
1967
+ else
1968
+ {
1969
+ /*
1970
+ * A RECORD type, once registered, doesn't change for the life of the
1971
+ * backend. So we don't need a typcache entry as such, which is good
1972
+ * because there isn't one. It's possible that the caller is asking
1973
+ * about a different type than before, though.
1974
+ */
1975
+ TupleDesc tupDesc = (TupleDesc ) rowcache -> cacheptr ;
1976
+
1977
+ if (unlikely (tupDesc == NULL ||
1978
+ rowcache -> tupdesc_id != 0 ||
1979
+ type_id != tupDesc -> tdtypeid ||
1980
+ typmod != tupDesc -> tdtypmod ))
1949
1981
{
1950
- /* Need to register shutdown callback to release tupdesc */
1951
- RegisterExprContextCallback (econtext ,
1952
- ShutdownTupleDescRef ,
1953
- PointerGetDatum (cache_field ));
1982
+ tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
1983
+ /* Drop pin acquired by lookup_rowtype_tupdesc */
1984
+ ReleaseTupleDesc (tupDesc );
1985
+ rowcache -> cacheptr = (void * ) tupDesc ;
1986
+ rowcache -> tupdesc_id = 0 ; /* not a valid value for non-RECORD */
1987
+ if (changed )
1988
+ * changed = true;
1954
1989
}
1955
- * cache_field = tupDesc ;
1990
+ return tupDesc ;
1956
1991
}
1957
- return tupDesc ;
1958
1992
}
1959
1993
1960
- /*
1961
- * Callback function to release a tupdesc refcount at econtext shutdown
1962
- */
1963
- static void
1964
- ShutdownTupleDescRef (Datum arg )
1965
- {
1966
- TupleDesc * cache_field = (TupleDesc * ) DatumGetPointer (arg );
1967
-
1968
- if (* cache_field )
1969
- ReleaseTupleDesc (* cache_field );
1970
- * cache_field = NULL ;
1971
- }
1972
1994
1973
1995
/*
1974
1996
* Fast-path functions, for very simple expressions
@@ -2501,8 +2523,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
2501
2523
2502
2524
/* Lookup tupdesc if first time through or if type changes */
2503
2525
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
2504
- & op -> d .nulltest_row .argdesc ,
2505
- econtext );
2526
+ & op -> d .nulltest_row .rowcache , NULL );
2506
2527
2507
2528
/*
2508
2529
* heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
@@ -2938,8 +2959,7 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
2938
2959
2939
2960
/* Lookup tupdesc if first time through or if type changes */
2940
2961
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
2941
- & op -> d .fieldselect .argdesc ,
2942
- econtext );
2962
+ & op -> d .fieldselect .rowcache , NULL );
2943
2963
2944
2964
/*
2945
2965
* Find field's attr record. Note we don't support system columns
@@ -2997,9 +3017,9 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
2997
3017
{
2998
3018
TupleDesc tupDesc ;
2999
3019
3000
- /* Lookup tupdesc if first time through or after rescan */
3020
+ /* Lookup tupdesc if first time through or if type changes */
3001
3021
tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
3002
- op -> d .fieldstore .argdesc , econtext );
3022
+ op -> d .fieldstore .rowcache , NULL );
3003
3023
3004
3024
/* Check that current tupdesc doesn't have more fields than we allocated */
3005
3025
if (unlikely (tupDesc -> natts > op -> d .fieldstore .ncolumns ))
@@ -3041,10 +3061,14 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
3041
3061
void
3042
3062
ExecEvalFieldStoreForm (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3043
3063
{
3064
+ TupleDesc tupDesc ;
3044
3065
HeapTuple tuple ;
3045
3066
3046
- /* argdesc should already be valid from the DeForm step */
3047
- tuple = heap_form_tuple (* op -> d .fieldstore .argdesc ,
3067
+ /* Lookup tupdesc (should be valid already) */
3068
+ tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
3069
+ op -> d .fieldstore .rowcache , NULL );
3070
+
3071
+ tuple = heap_form_tuple (tupDesc ,
3048
3072
op -> d .fieldstore .values ,
3049
3073
op -> d .fieldstore .nulls );
3050
3074
@@ -3255,13 +3279,13 @@ ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op)
3255
3279
void
3256
3280
ExecEvalConvertRowtype (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3257
3281
{
3258
- ConvertRowtypeExpr * convert = op -> d .convert_rowtype .convert ;
3259
3282
HeapTuple result ;
3260
3283
Datum tupDatum ;
3261
3284
HeapTupleHeader tuple ;
3262
3285
HeapTupleData tmptup ;
3263
3286
TupleDesc indesc ,
3264
3287
outdesc ;
3288
+ bool changed = false;
3265
3289
3266
3290
/* NULL in -> NULL out */
3267
3291
if (* op -> resnull )
@@ -3270,24 +3294,19 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3270
3294
tupDatum = * op -> resvalue ;
3271
3295
tuple = DatumGetHeapTupleHeader (tupDatum );
3272
3296
3273
- /* Lookup tupdescs if first time through or after rescan */
3274
- if (op -> d .convert_rowtype .indesc == NULL )
3275
- {
3276
- get_cached_rowtype (exprType ((Node * ) convert -> arg ), -1 ,
3277
- & op -> d .convert_rowtype .indesc ,
3278
- econtext );
3279
- op -> d .convert_rowtype .initialized = false;
3280
- }
3281
- if (op -> d .convert_rowtype .outdesc == NULL )
3282
- {
3283
- get_cached_rowtype (convert -> resulttype , -1 ,
3284
- & op -> d .convert_rowtype .outdesc ,
3285
- econtext );
3286
- op -> d .convert_rowtype .initialized = false;
3287
- }
3288
-
3289
- indesc = op -> d .convert_rowtype .indesc ;
3290
- outdesc = op -> d .convert_rowtype .outdesc ;
3297
+ /*
3298
+ * Lookup tupdescs if first time through or if type changes. We'd better
3299
+ * pin them since type conversion functions could do catalog lookups and
3300
+ * hence cause cache invalidation.
3301
+ */
3302
+ indesc = get_cached_rowtype (op -> d .convert_rowtype .inputtype , -1 ,
3303
+ op -> d .convert_rowtype .incache ,
3304
+ & changed );
3305
+ IncrTupleDescRefCount (indesc );
3306
+ outdesc = get_cached_rowtype (op -> d .convert_rowtype .outputtype , -1 ,
3307
+ op -> d .convert_rowtype .outcache ,
3308
+ & changed );
3309
+ IncrTupleDescRefCount (outdesc );
3291
3310
3292
3311
/*
3293
3312
* We used to be able to assert that incoming tuples are marked with
@@ -3298,8 +3317,8 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3298
3317
Assert (HeapTupleHeaderGetTypeId (tuple ) == indesc -> tdtypeid ||
3299
3318
HeapTupleHeaderGetTypeId (tuple ) == RECORDOID );
3300
3319
3301
- /* if first time through, initialize conversion map */
3302
- if (! op -> d . convert_rowtype . initialized )
3320
+ /* if first time through, or after change, initialize conversion map */
3321
+ if (changed )
3303
3322
{
3304
3323
MemoryContext old_cxt ;
3305
3324
@@ -3310,7 +3329,6 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3310
3329
op -> d .convert_rowtype .map =
3311
3330
convert_tuples_by_name (indesc , outdesc ,
3312
3331
gettext_noop ("could not convert row type" ));
3313
- op -> d .convert_rowtype .initialized = true;
3314
3332
3315
3333
MemoryContextSwitchTo (old_cxt );
3316
3334
}
@@ -3340,6 +3358,9 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3340
3358
*/
3341
3359
* op -> resvalue = heap_copy_tuple_as_datum (& tmptup , outdesc );
3342
3360
}
3361
+
3362
+ DecrTupleDescRefCount (indesc );
3363
+ DecrTupleDescRefCount (outdesc );
3343
3364
}
3344
3365
3345
3366
/*
0 commit comments