@@ -144,8 +144,8 @@ static void ExecInitInterpreter(void);
144
144
/* support functions */
145
145
static void CheckVarSlotCompatibility (TupleTableSlot * slot , int attnum , Oid vartype );
146
146
static TupleDesc get_cached_rowtype (Oid type_id , int32 typmod ,
147
- TupleDesc * cache_field , ExprContext * econtext );
148
- static void ShutdownTupleDescRef ( Datum arg );
147
+ ExprEvalRowtypeCache * rowcache ,
148
+ bool * changed );
149
149
static void ExecEvalRowNullInt (ExprState * state , ExprEvalStep * op ,
150
150
ExprContext * econtext , bool checkisnull );
151
151
@@ -1903,56 +1903,78 @@ CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype)
1903
1903
* get_cached_rowtype: utility function to lookup a rowtype tupdesc
1904
1904
*
1905
1905
* type_id, typmod: identity of the rowtype
1906
- * cache_field: where to cache the TupleDesc pointer in expression state node
1907
- * (field must be initialized to NULL)
1908
- * econtext: expression context we are executing in
1906
+ * rowcache: space for caching identity info
1907
+ * (rowcache->cacheptr must be initialized to NULL)
1908
+ * changed: if not NULL, *changed is set to true on any update
1909
1909
*
1910
- * NOTE: because the shutdown callback will be called during plan rescan,
1911
- * must be prepared to re-do this during any node execution; cannot call
1912
- * just once during expression initialization.
1910
+ * The returned TupleDesc is not guaranteed pinned; caller must pin it
1911
+ * to use it across any operation that might incur cache invalidation.
1912
+ * (The TupleDesc is always refcounted, so just use IncrTupleDescRefCount.)
1913
+ *
1914
+ * NOTE: because composite types can change contents, we must be prepared
1915
+ * to re-do this during any node execution; cannot call just once during
1916
+ * expression initialization.
1913
1917
*/
1914
1918
static TupleDesc
1915
1919
get_cached_rowtype (Oid type_id , int32 typmod ,
1916
- TupleDesc * cache_field , ExprContext * econtext )
1920
+ ExprEvalRowtypeCache * rowcache ,
1921
+ bool * changed )
1917
1922
{
1918
- TupleDesc tupDesc = * cache_field ;
1919
-
1920
- /* Do lookup if no cached value or if requested type changed */
1921
- if (tupDesc == NULL ||
1922
- type_id != tupDesc -> tdtypeid ||
1923
- typmod != tupDesc -> tdtypmod )
1923
+ if (type_id != RECORDOID )
1924
1924
{
1925
- tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
1925
+ /*
1926
+ * It's a named composite type, so use the regular typcache. Do a
1927
+ * lookup first time through, or if the composite type changed. Note:
1928
+ * "tupdesc_id == 0" may look redundant, but it protects against the
1929
+ * admittedly-theoretical possibility that type_id was RECORDOID the
1930
+ * last time through, so that the cacheptr isn't TypeCacheEntry *.
1931
+ */
1932
+ TypeCacheEntry * typentry = (TypeCacheEntry * ) rowcache -> cacheptr ;
1926
1933
1927
- if (* cache_field )
1934
+ if (unlikely (typentry == NULL ||
1935
+ rowcache -> tupdesc_id == 0 ||
1936
+ typentry -> tupDesc_identifier != rowcache -> tupdesc_id ))
1928
1937
{
1929
- /* Release old tupdesc; but callback is already registered */
1930
- ReleaseTupleDesc (* cache_field );
1938
+ typentry = lookup_type_cache (type_id , TYPECACHE_TUPDESC );
1939
+ if (typentry -> tupDesc == NULL )
1940
+ ereport (ERROR ,
1941
+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
1942
+ errmsg ("type %s is not composite" ,
1943
+ format_type_be (type_id ))));
1944
+ rowcache -> cacheptr = (void * ) typentry ;
1945
+ rowcache -> tupdesc_id = typentry -> tupDesc_identifier ;
1946
+ if (changed )
1947
+ * changed = true;
1931
1948
}
1932
- else
1949
+ return typentry -> tupDesc ;
1950
+ }
1951
+ else
1952
+ {
1953
+ /*
1954
+ * A RECORD type, once registered, doesn't change for the life of the
1955
+ * backend. So we don't need a typcache entry as such, which is good
1956
+ * because there isn't one. It's possible that the caller is asking
1957
+ * about a different type than before, though.
1958
+ */
1959
+ TupleDesc tupDesc = (TupleDesc ) rowcache -> cacheptr ;
1960
+
1961
+ if (unlikely (tupDesc == NULL ||
1962
+ rowcache -> tupdesc_id != 0 ||
1963
+ type_id != tupDesc -> tdtypeid ||
1964
+ typmod != tupDesc -> tdtypmod ))
1933
1965
{
1934
- /* Need to register shutdown callback to release tupdesc */
1935
- RegisterExprContextCallback (econtext ,
1936
- ShutdownTupleDescRef ,
1937
- PointerGetDatum (cache_field ));
1966
+ tupDesc = lookup_rowtype_tupdesc (type_id , typmod );
1967
+ /* Drop pin acquired by lookup_rowtype_tupdesc */
1968
+ ReleaseTupleDesc (tupDesc );
1969
+ rowcache -> cacheptr = (void * ) tupDesc ;
1970
+ rowcache -> tupdesc_id = 0 ; /* not a valid value for non-RECORD */
1971
+ if (changed )
1972
+ * changed = true;
1938
1973
}
1939
- * cache_field = tupDesc ;
1974
+ return tupDesc ;
1940
1975
}
1941
- return tupDesc ;
1942
1976
}
1943
1977
1944
- /*
1945
- * Callback function to release a tupdesc refcount at econtext shutdown
1946
- */
1947
- static void
1948
- ShutdownTupleDescRef (Datum arg )
1949
- {
1950
- TupleDesc * cache_field = (TupleDesc * ) DatumGetPointer (arg );
1951
-
1952
- if (* cache_field )
1953
- ReleaseTupleDesc (* cache_field );
1954
- * cache_field = NULL ;
1955
- }
1956
1978
1957
1979
/*
1958
1980
* Fast-path functions, for very simple expressions
@@ -2473,8 +2495,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
2473
2495
2474
2496
/* Lookup tupdesc if first time through or if type changes */
2475
2497
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
2476
- & op -> d .nulltest_row .argdesc ,
2477
- econtext );
2498
+ & op -> d .nulltest_row .rowcache , NULL );
2478
2499
2479
2500
/*
2480
2501
* heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
@@ -2910,8 +2931,7 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
2910
2931
2911
2932
/* Lookup tupdesc if first time through or if type changes */
2912
2933
tupDesc = get_cached_rowtype (tupType , tupTypmod ,
2913
- & op -> d .fieldselect .argdesc ,
2914
- econtext );
2934
+ & op -> d .fieldselect .rowcache , NULL );
2915
2935
2916
2936
/*
2917
2937
* Find field's attr record. Note we don't support system columns
@@ -2969,9 +2989,9 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
2969
2989
{
2970
2990
TupleDesc tupDesc ;
2971
2991
2972
- /* Lookup tupdesc if first time through or after rescan */
2992
+ /* Lookup tupdesc if first time through or if type changes */
2973
2993
tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
2974
- op -> d .fieldstore .argdesc , econtext );
2994
+ op -> d .fieldstore .rowcache , NULL );
2975
2995
2976
2996
/* Check that current tupdesc doesn't have more fields than we allocated */
2977
2997
if (unlikely (tupDesc -> natts > op -> d .fieldstore .ncolumns ))
@@ -3013,10 +3033,14 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
3013
3033
void
3014
3034
ExecEvalFieldStoreForm (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3015
3035
{
3036
+ TupleDesc tupDesc ;
3016
3037
HeapTuple tuple ;
3017
3038
3018
- /* argdesc should already be valid from the DeForm step */
3019
- tuple = heap_form_tuple (* op -> d .fieldstore .argdesc ,
3039
+ /* Lookup tupdesc (should be valid already) */
3040
+ tupDesc = get_cached_rowtype (op -> d .fieldstore .fstore -> resulttype , -1 ,
3041
+ op -> d .fieldstore .rowcache , NULL );
3042
+
3043
+ tuple = heap_form_tuple (tupDesc ,
3020
3044
op -> d .fieldstore .values ,
3021
3045
op -> d .fieldstore .nulls );
3022
3046
@@ -3227,13 +3251,13 @@ ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
3227
3251
void
3228
3252
ExecEvalConvertRowtype (ExprState * state , ExprEvalStep * op , ExprContext * econtext )
3229
3253
{
3230
- ConvertRowtypeExpr * convert = op -> d .convert_rowtype .convert ;
3231
3254
HeapTuple result ;
3232
3255
Datum tupDatum ;
3233
3256
HeapTupleHeader tuple ;
3234
3257
HeapTupleData tmptup ;
3235
3258
TupleDesc indesc ,
3236
3259
outdesc ;
3260
+ bool changed = false;
3237
3261
3238
3262
/* NULL in -> NULL out */
3239
3263
if (* op -> resnull )
@@ -3242,24 +3266,19 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3242
3266
tupDatum = * op -> resvalue ;
3243
3267
tuple = DatumGetHeapTupleHeader (tupDatum );
3244
3268
3245
- /* Lookup tupdescs if first time through or after rescan */
3246
- if (op -> d .convert_rowtype .indesc == NULL )
3247
- {
3248
- get_cached_rowtype (exprType ((Node * ) convert -> arg ), -1 ,
3249
- & op -> d .convert_rowtype .indesc ,
3250
- econtext );
3251
- op -> d .convert_rowtype .initialized = false;
3252
- }
3253
- if (op -> d .convert_rowtype .outdesc == NULL )
3254
- {
3255
- get_cached_rowtype (convert -> resulttype , -1 ,
3256
- & op -> d .convert_rowtype .outdesc ,
3257
- econtext );
3258
- op -> d .convert_rowtype .initialized = false;
3259
- }
3260
-
3261
- indesc = op -> d .convert_rowtype .indesc ;
3262
- outdesc = op -> d .convert_rowtype .outdesc ;
3269
+ /*
3270
+ * Lookup tupdescs if first time through or if type changes. We'd better
3271
+ * pin them since type conversion functions could do catalog lookups and
3272
+ * hence cause cache invalidation.
3273
+ */
3274
+ indesc = get_cached_rowtype (op -> d .convert_rowtype .inputtype , -1 ,
3275
+ op -> d .convert_rowtype .incache ,
3276
+ & changed );
3277
+ IncrTupleDescRefCount (indesc );
3278
+ outdesc = get_cached_rowtype (op -> d .convert_rowtype .outputtype , -1 ,
3279
+ op -> d .convert_rowtype .outcache ,
3280
+ & changed );
3281
+ IncrTupleDescRefCount (outdesc );
3263
3282
3264
3283
/*
3265
3284
* We used to be able to assert that incoming tuples are marked with
@@ -3270,8 +3289,8 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3270
3289
Assert (HeapTupleHeaderGetTypeId (tuple ) == indesc -> tdtypeid ||
3271
3290
HeapTupleHeaderGetTypeId (tuple ) == RECORDOID );
3272
3291
3273
- /* if first time through, initialize conversion map */
3274
- if (! op -> d . convert_rowtype . initialized )
3292
+ /* if first time through, or after change, initialize conversion map */
3293
+ if (changed )
3275
3294
{
3276
3295
MemoryContext old_cxt ;
3277
3296
@@ -3282,7 +3301,6 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3282
3301
op -> d .convert_rowtype .map =
3283
3302
convert_tuples_by_name (indesc , outdesc ,
3284
3303
gettext_noop ("could not convert row type" ));
3285
- op -> d .convert_rowtype .initialized = true;
3286
3304
3287
3305
MemoryContextSwitchTo (old_cxt );
3288
3306
}
@@ -3312,6 +3330,9 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
3312
3330
*/
3313
3331
* op -> resvalue = heap_copy_tuple_as_datum (& tmptup , outdesc );
3314
3332
}
3333
+
3334
+ DecrTupleDescRefCount (indesc );
3335
+ DecrTupleDescRefCount (outdesc );
3315
3336
}
3316
3337
3317
3338
/*
0 commit comments