@@ -53,21 +53,6 @@ typedef struct
53
53
bool * freevals ; /* which arguments are pfree-able */
54
54
} PreparedParamsData ;
55
55
56
- typedef struct
57
- {
58
- /* NB: we assume this struct contains no padding bytes */
59
- Oid srctype ; /* source type for cast */
60
- Oid dsttype ; /* destination type for cast */
61
- int32 srctypmod ; /* source typmod for cast */
62
- int32 dsttypmod ; /* destination typmod for cast */
63
- } plpgsql_CastHashKey ;
64
-
65
- typedef struct
66
- {
67
- plpgsql_CastHashKey key ; /* hash key --- MUST BE FIRST */
68
- ExprState * cast_exprstate ; /* cast expression, or NULL if no-op cast */
69
- } plpgsql_CastHashEntry ;
70
-
71
56
/*
72
57
* All plpgsql function executions within a single transaction share the same
73
58
* executor EState for evaluating "simple" expressions. Each function call
@@ -104,6 +89,38 @@ typedef struct SimpleEcontextStackEntry
104
89
static EState * shared_simple_eval_estate = NULL ;
105
90
static SimpleEcontextStackEntry * simple_econtext_stack = NULL ;
106
91
92
+ /*
93
+ * We use a session-wide hash table for caching cast information.
94
+ *
95
+ * Once built, the compiled expression trees (cast_expr fields) survive for
96
+ * the life of the session. At some point it might be worth invalidating
97
+ * those after pg_cast changes, but for the moment we don't bother.
98
+ *
99
+ * The evaluation state trees (cast_exprstate) are managed in the same way as
100
+ * simple expressions (i.e., we assume cast expressions are always simple).
101
+ */
102
+ typedef struct /* lookup key for cast info */
103
+ {
104
+ /* NB: we assume this struct contains no padding bytes */
105
+ Oid srctype ; /* source type for cast */
106
+ Oid dsttype ; /* destination type for cast */
107
+ int32 srctypmod ; /* source typmod for cast */
108
+ int32 dsttypmod ; /* destination typmod for cast */
109
+ } plpgsql_CastHashKey ;
110
+
111
+ typedef struct /* cast_hash table entry */
112
+ {
113
+ plpgsql_CastHashKey key ; /* hash key --- MUST BE FIRST */
114
+ Expr * cast_expr ; /* cast expression, or NULL if no-op cast */
115
+ /* The ExprState tree is valid only when cast_lxid matches current LXID */
116
+ ExprState * cast_exprstate ; /* expression's eval tree */
117
+ bool cast_in_use ; /* true while we're executing eval tree */
118
+ LocalTransactionId cast_lxid ;
119
+ } plpgsql_CastHashEntry ;
120
+
121
+ static MemoryContext cast_hash_context = NULL ;
122
+ static HTAB * cast_hash = NULL ;
123
+
107
124
/************************************************************
108
125
* Local function forward declarations
109
126
************************************************************/
@@ -238,8 +255,9 @@ static Datum exec_cast_value(PLpgSQL_execstate *estate,
238
255
Datum value , bool * isnull ,
239
256
Oid valtype , int32 valtypmod ,
240
257
Oid reqtype , int32 reqtypmod );
241
- static ExprState * get_cast_expression (PLpgSQL_execstate * estate ,
242
- Oid srctype , int32 srctypmod , Oid dsttype , int32 dsttypmod );
258
+ static plpgsql_CastHashEntry * get_cast_hashentry (PLpgSQL_execstate * estate ,
259
+ Oid srctype , int32 srctypmod ,
260
+ Oid dsttype , int32 dsttypmod );
243
261
static void exec_init_tuple_store (PLpgSQL_execstate * estate );
244
262
static void exec_set_found (PLpgSQL_execstate * estate , bool state );
245
263
static void plpgsql_create_econtext (PLpgSQL_execstate * estate );
@@ -6043,12 +6061,12 @@ exec_cast_value(PLpgSQL_execstate *estate,
6043
6061
if (valtype != reqtype ||
6044
6062
(valtypmod != reqtypmod && reqtypmod != -1 ))
6045
6063
{
6046
- ExprState * cast_expr ;
6064
+ plpgsql_CastHashEntry * cast_entry ;
6047
6065
6048
- cast_expr = get_cast_expression (estate ,
6066
+ cast_entry = get_cast_hashentry (estate ,
6049
6067
valtype , valtypmod ,
6050
6068
reqtype , reqtypmod );
6051
- if (cast_expr )
6069
+ if (cast_entry )
6052
6070
{
6053
6071
ExprContext * econtext = estate -> eval_econtext ;
6054
6072
MemoryContext oldcontext ;
@@ -6058,7 +6076,12 @@ exec_cast_value(PLpgSQL_execstate *estate,
6058
6076
econtext -> caseValue_datum = value ;
6059
6077
econtext -> caseValue_isNull = * isnull ;
6060
6078
6061
- value = ExecEvalExpr (cast_expr , econtext , isnull , NULL );
6079
+ cast_entry -> cast_in_use = true;
6080
+
6081
+ value = ExecEvalExpr (cast_entry -> cast_exprstate , econtext ,
6082
+ isnull , NULL );
6083
+
6084
+ cast_entry -> cast_in_use = false;
6062
6085
6063
6086
MemoryContextSwitchTo (oldcontext );
6064
6087
}
@@ -6068,46 +6091,44 @@ exec_cast_value(PLpgSQL_execstate *estate,
6068
6091
}
6069
6092
6070
6093
/* ----------
6071
- * get_cast_expression Look up how to perform a type cast
6072
- *
6073
- * Returns an expression evaluation tree based on a CaseTestExpr input,
6074
- * or NULL if the cast is a mere no-op relabeling.
6094
+ * get_cast_hashentry Look up how to perform a type cast
6075
6095
*
6076
- * We cache the results of the lookup in a per-function hash table.
6077
- * It's tempting to consider using a session-wide hash table instead,
6078
- * but that introduces some corner-case questions that probably aren't
6079
- * worth dealing with; in particular that re-entrant use of an evaluation
6080
- * tree might occur. That would also set in stone the assumption that
6081
- * collation isn't important to a cast function.
6096
+ * Returns a plpgsql_CastHashEntry if an expression has to be evaluated,
6097
+ * or NULL if the cast is a mere no-op relabeling. If there's work to be
6098
+ * done, the cast_exprstate field contains an expression evaluation tree
6099
+ * based on a CaseTestExpr input, and the cast_in_use field should be set
6100
+ * TRUE while executing it.
6082
6101
* ----------
6083
6102
*/
6084
- static ExprState *
6085
- get_cast_expression (PLpgSQL_execstate * estate ,
6086
- Oid srctype , int32 srctypmod , Oid dsttype , int32 dsttypmod )
6103
+ static plpgsql_CastHashEntry *
6104
+ get_cast_hashentry (PLpgSQL_execstate * estate ,
6105
+ Oid srctype , int32 srctypmod ,
6106
+ Oid dsttype , int32 dsttypmod )
6087
6107
{
6088
- HTAB * cast_hash = estate -> func -> cast_hash ;
6089
6108
plpgsql_CastHashKey cast_key ;
6090
6109
plpgsql_CastHashEntry * cast_entry ;
6091
6110
bool found ;
6092
- CaseTestExpr * placeholder ;
6093
- Node * cast_expr ;
6094
- ExprState * cast_exprstate ;
6111
+ LocalTransactionId curlxid ;
6095
6112
MemoryContext oldcontext ;
6096
6113
6097
- /* Create the cast-info hash table if we didn't already */
6114
+ /* Create the session-wide cast-info hash table if we didn't already */
6098
6115
if (cast_hash == NULL )
6099
6116
{
6100
6117
HASHCTL ctl ;
6101
6118
6119
+ cast_hash_context = AllocSetContextCreate (TopMemoryContext ,
6120
+ "PLpgSQL cast info" ,
6121
+ ALLOCSET_DEFAULT_MINSIZE ,
6122
+ ALLOCSET_DEFAULT_INITSIZE ,
6123
+ ALLOCSET_DEFAULT_MAXSIZE );
6102
6124
memset (& ctl , 0 , sizeof (ctl ));
6103
6125
ctl .keysize = sizeof (plpgsql_CastHashKey );
6104
6126
ctl .entrysize = sizeof (plpgsql_CastHashEntry );
6105
- ctl .hcxt = estate -> func -> fn_cxt ;
6127
+ ctl .hcxt = cast_hash_context ;
6106
6128
cast_hash = hash_create ("PLpgSQL cast cache" ,
6107
6129
16 , /* start small and extend */
6108
6130
& ctl ,
6109
6131
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
6110
- estate -> func -> cast_hash = cast_hash ;
6111
6132
}
6112
6133
6113
6134
/* Look for existing entry */
@@ -6118,102 +6139,131 @@ get_cast_expression(PLpgSQL_execstate *estate,
6118
6139
cast_entry = (plpgsql_CastHashEntry * ) hash_search (cast_hash ,
6119
6140
(void * ) & cast_key ,
6120
6141
HASH_FIND , NULL );
6121
- if (cast_entry )
6122
- return cast_entry -> cast_exprstate ;
6123
6142
6124
- /* Construct expression tree for coercion in function's context */
6125
- oldcontext = MemoryContextSwitchTo (estate -> func -> fn_cxt );
6143
+ if (cast_entry == NULL )
6144
+ {
6145
+ /* We've not looked up this coercion before */
6146
+ Node * cast_expr ;
6147
+ CaseTestExpr * placeholder ;
6126
6148
6127
- /*
6128
- * We use a CaseTestExpr as the base of the coercion tree, since it's very
6129
- * cheap to insert the source value for that.
6130
- */
6131
- placeholder = makeNode (CaseTestExpr );
6132
- placeholder -> typeId = srctype ;
6133
- placeholder -> typeMod = srctypmod ;
6134
- placeholder -> collation = get_typcollation (srctype );
6135
- if (OidIsValid (estate -> func -> fn_input_collation ) &&
6136
- OidIsValid (placeholder -> collation ))
6137
- placeholder -> collation = estate -> func -> fn_input_collation ;
6149
+ /*
6150
+ * Since we could easily fail (no such coercion), construct a
6151
+ * temporary coercion expression tree in a short-lived context, then
6152
+ * if successful copy it to cast_hash_context.
6153
+ */
6154
+ oldcontext = MemoryContextSwitchTo (estate -> eval_econtext -> ecxt_per_tuple_memory );
6138
6155
6139
- /*
6140
- * Apply coercion. We use ASSIGNMENT coercion because that's the closest
6141
- * match to plpgsql's historical behavior; in particular, EXPLICIT
6142
- * coercion would allow silent truncation to a destination
6143
- * varchar/bpchar's length, which we do not want.
6144
- *
6145
- * If source type is UNKNOWN, coerce_to_target_type will fail (it only
6146
- * expects to see that for Const input nodes), so don't call it; we'll
6147
- * apply CoerceViaIO instead. Likewise, it doesn't currently work for
6148
- * coercing RECORD to some other type, so skip for that too.
6149
- */
6150
- if (srctype == UNKNOWNOID || srctype == RECORDOID )
6151
- cast_expr = NULL ;
6152
- else
6153
- cast_expr = coerce_to_target_type (NULL ,
6154
- (Node * ) placeholder , srctype ,
6155
- dsttype , dsttypmod ,
6156
- COERCION_ASSIGNMENT ,
6157
- COERCE_IMPLICIT_CAST ,
6158
- -1 );
6156
+ /*
6157
+ * We use a CaseTestExpr as the base of the coercion tree, since it's
6158
+ * very cheap to insert the source value for that.
6159
+ */
6160
+ placeholder = makeNode (CaseTestExpr );
6161
+ placeholder -> typeId = srctype ;
6162
+ placeholder -> typeMod = srctypmod ;
6163
+ placeholder -> collation = get_typcollation (srctype );
6159
6164
6160
- /*
6161
- * If there's no cast path according to the parser, fall back to using an
6162
- * I/O coercion; this is semantically dubious but matches plpgsql's
6163
- * historical behavior. We would need something of the sort for UNKNOWN
6164
- * literals in any case.
6165
- */
6166
- if (cast_expr == NULL )
6167
- {
6168
- CoerceViaIO * iocoerce = makeNode (CoerceViaIO );
6169
-
6170
- iocoerce -> arg = (Expr * ) placeholder ;
6171
- iocoerce -> resulttype = dsttype ;
6172
- iocoerce -> resultcollid = InvalidOid ;
6173
- iocoerce -> coerceformat = COERCE_IMPLICIT_CAST ;
6174
- iocoerce -> location = -1 ;
6175
- cast_expr = (Node * ) iocoerce ;
6176
- if (dsttypmod != -1 )
6165
+ /*
6166
+ * Apply coercion. We use ASSIGNMENT coercion because that's the
6167
+ * closest match to plpgsql's historical behavior; in particular,
6168
+ * EXPLICIT coercion would allow silent truncation to a destination
6169
+ * varchar/bpchar's length, which we do not want.
6170
+ *
6171
+ * If source type is UNKNOWN, coerce_to_target_type will fail (it only
6172
+ * expects to see that for Const input nodes), so don't call it; we'll
6173
+ * apply CoerceViaIO instead. Likewise, it doesn't currently work for
6174
+ * coercing RECORD to some other type, so skip for that too.
6175
+ */
6176
+ if (srctype == UNKNOWNOID || srctype == RECORDOID )
6177
+ cast_expr = NULL ;
6178
+ else
6177
6179
cast_expr = coerce_to_target_type (NULL ,
6178
- cast_expr , dsttype ,
6180
+ ( Node * ) placeholder , srctype ,
6179
6181
dsttype , dsttypmod ,
6180
6182
COERCION_ASSIGNMENT ,
6181
6183
COERCE_IMPLICIT_CAST ,
6182
6184
-1 );
6183
- }
6184
6185
6185
- /* Note: we don't bother labeling the expression tree with collation */
6186
+ /*
6187
+ * If there's no cast path according to the parser, fall back to using
6188
+ * an I/O coercion; this is semantically dubious but matches plpgsql's
6189
+ * historical behavior. We would need something of the sort for
6190
+ * UNKNOWN literals in any case.
6191
+ */
6192
+ if (cast_expr == NULL )
6193
+ {
6194
+ CoerceViaIO * iocoerce = makeNode (CoerceViaIO );
6195
+
6196
+ iocoerce -> arg = (Expr * ) placeholder ;
6197
+ iocoerce -> resulttype = dsttype ;
6198
+ iocoerce -> resultcollid = InvalidOid ;
6199
+ iocoerce -> coerceformat = COERCE_IMPLICIT_CAST ;
6200
+ iocoerce -> location = -1 ;
6201
+ cast_expr = (Node * ) iocoerce ;
6202
+ if (dsttypmod != -1 )
6203
+ cast_expr = coerce_to_target_type (NULL ,
6204
+ cast_expr , dsttype ,
6205
+ dsttype , dsttypmod ,
6206
+ COERCION_ASSIGNMENT ,
6207
+ COERCE_IMPLICIT_CAST ,
6208
+ -1 );
6209
+ }
6210
+
6211
+ /* Note: we don't bother labeling the expression tree with collation */
6186
6212
6187
- /* Detect whether we have a no-op (RelabelType) coercion */
6188
- if (IsA (cast_expr , RelabelType ) &&
6189
- ((RelabelType * ) cast_expr )-> arg == (Expr * ) placeholder )
6190
- cast_expr = NULL ;
6213
+ /* Detect whether we have a no-op (RelabelType) coercion */
6214
+ if (IsA (cast_expr , RelabelType ) &&
6215
+ ((RelabelType * ) cast_expr )-> arg == (Expr * ) placeholder )
6216
+ cast_expr = NULL ;
6191
6217
6192
- if (cast_expr )
6193
- {
6194
- /* ExecInitExpr assumes we've planned the expression */
6195
- cast_expr = (Node * ) expression_planner ((Expr * ) cast_expr );
6196
- /* Create an expression eval state tree for it */
6197
- cast_exprstate = ExecInitExpr ((Expr * ) cast_expr , NULL );
6218
+ if (cast_expr )
6219
+ {
6220
+ /* ExecInitExpr assumes we've planned the expression */
6221
+ cast_expr = (Node * ) expression_planner ((Expr * ) cast_expr );
6222
+
6223
+ /* Now copy the tree into cast_hash_context */
6224
+ MemoryContextSwitchTo (cast_hash_context );
6225
+
6226
+ cast_expr = copyObject (cast_expr );
6227
+ }
6228
+
6229
+ MemoryContextSwitchTo (oldcontext );
6230
+
6231
+ /* Now we can fill in a hashtable entry. */
6232
+ cast_entry = (plpgsql_CastHashEntry * ) hash_search (cast_hash ,
6233
+ (void * ) & cast_key ,
6234
+ HASH_ENTER , & found );
6235
+ Assert (!found ); /* wasn't there a moment ago */
6236
+ cast_entry -> cast_expr = (Expr * ) cast_expr ;
6237
+ cast_entry -> cast_exprstate = NULL ;
6238
+ cast_entry -> cast_in_use = false;
6239
+ cast_entry -> cast_lxid = InvalidLocalTransactionId ;
6198
6240
}
6199
- else
6200
- cast_exprstate = NULL ;
6201
6241
6202
- MemoryContextSwitchTo (oldcontext );
6242
+ /* Done if we have determined that this is a no-op cast. */
6243
+ if (cast_entry -> cast_expr == NULL )
6244
+ return NULL ;
6203
6245
6204
6246
/*
6205
- * Now fill in a hashtable entry. If we fail anywhere up to/including
6206
- * this step, we've only leaked some memory in the function context, which
6207
- * isn't great but isn't disastrous either.
6208
- */
6209
- cast_entry = (plpgsql_CastHashEntry * ) hash_search (cast_hash ,
6210
- (void * ) & cast_key ,
6211
- HASH_ENTER , & found );
6212
- Assert (!found ); /* wasn't there a moment ago */
6213
-
6214
- cast_entry -> cast_exprstate = cast_exprstate ;
6247
+ * Prepare the expression for execution, if it's not been done already in
6248
+ * the current transaction; also, if it's marked busy in the current
6249
+ * transaction, abandon that expression tree and build a new one, so as to
6250
+ * avoid potential problems with recursive cast expressions and failed
6251
+ * executions. (We will leak some memory intra-transaction if that
6252
+ * happens a lot, but we don't expect it to.) It's okay to update the
6253
+ * hash table with the new tree because all plpgsql functions within a
6254
+ * given transaction share the same simple_eval_estate.
6255
+ */
6256
+ curlxid = MyProc -> lxid ;
6257
+ if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
6258
+ {
6259
+ oldcontext = MemoryContextSwitchTo (estate -> simple_eval_estate -> es_query_cxt );
6260
+ cast_entry -> cast_exprstate = ExecInitExpr (cast_entry -> cast_expr , NULL );
6261
+ cast_entry -> cast_in_use = false;
6262
+ cast_entry -> cast_lxid = curlxid ;
6263
+ MemoryContextSwitchTo (oldcontext );
6264
+ }
6215
6265
6216
- return cast_exprstate ;
6266
+ return cast_entry ;
6217
6267
}
6218
6268
6219
6269
/* ----------
0 commit comments