@@ -134,18 +134,22 @@ static SimpleEcontextStackEntry *simple_econtext_stack = NULL;
134
134
MemoryContextAllocZero(get_eval_mcontext(estate), sz)
135
135
136
136
/*
137
- * We usea session-wide hashtable for caching cast information.
137
+ * We usetwo session-wide hashtables for caching cast information.
138
138
*
139
- * Once built, the compiled expression trees (cast_expr fields) survive for
140
- * the life of the session. At some point it might be worth invalidating
141
- * those after pg_cast changes, but for the moment we don't bother.
139
+ * cast_expr_hash entries (of type plpgsql_CastExprHashEntry) hold compiled
140
+ * expression trees for casts. These survive for the life of the session and
141
+ * are shared across all PL/pgSQL functions and DO blocks. At some point it
142
+ * might be worth invalidating them after pg_cast changes, but for the moment
143
+ * we don't bother.
142
144
*
143
- * The evaluation state trees (cast_exprstate) are managed in the same way as
144
- * simple expressions (i.e., we assume cast expressions are always simple).
145
+ * There is a separate hash table shared_cast_hash (with entries of type
146
+ * plpgsql_CastHashEntry) containing evaluation state trees for these
147
+ * expressions, which are managed in the same way as simple expressions
148
+ * (i.e., we assume cast expressions are always simple).
145
149
*
146
- * As with simple expressions, DO blocks don't use theshared hash table but
147
- * must have their own. This isn't ideal, but we don't want to deal with
148
- * multiple simple_eval_estates within a DO block.
150
+ * As with simple expressions, DO blocks don't use theshared_cast_hash table
151
+ *but must have their own evaluation state trees . This isn't ideal, but we
152
+ *don't want to deal with multiple simple_eval_estates within a DO block.
149
153
*/
150
154
typedef struct /* lookup key for cast info */
151
155
{
@@ -156,18 +160,24 @@ typedef struct/* lookup key for cast info */
156
160
int32 dsttypmod ;/* destination typmod for cast */
157
161
}plpgsql_CastHashKey ;
158
162
159
- typedef struct /*cast_hash table entry */
163
+ typedef struct /*cast_expr_hash table entry */
160
164
{
161
165
plpgsql_CastHashKey key ;/* hash key --- MUST BE FIRST */
162
166
Expr * cast_expr ;/* cast expression, or NULL if no-op cast */
163
167
CachedExpression * cast_cexpr ;/* cached expression backing the above */
168
+ }plpgsql_CastExprHashEntry ;
169
+
170
+ typedef struct /* cast_hash table entry */
171
+ {
172
+ plpgsql_CastHashKey key ;/* hash key --- MUST BE FIRST */
173
+ plpgsql_CastExprHashEntry * cast_centry ;/* link to matching expr entry */
164
174
/* ExprState is valid only when cast_lxid matches current LXID */
165
175
ExprState * cast_exprstate ;/* expression's eval tree */
166
176
bool cast_in_use ;/* true while we're executing eval tree */
167
177
LocalTransactionId cast_lxid ;
168
178
}plpgsql_CastHashEntry ;
169
179
170
- static MemoryContext shared_cast_context = NULL ;
180
+ static HTAB * cast_expr_hash = NULL ;
171
181
static HTAB * shared_cast_hash = NULL ;
172
182
173
183
/*
@@ -3966,6 +3976,18 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
3966
3976
estate -> paramLI -> parserSetupArg = NULL ;/* filled during use */
3967
3977
estate -> paramLI -> numParams = estate -> ndatums ;
3968
3978
3979
+ /* Create the session-wide cast-expression hash if we didn't already */
3980
+ if (cast_expr_hash == NULL )
3981
+ {
3982
+ memset (& ctl ,0 ,sizeof (ctl ));
3983
+ ctl .keysize = sizeof (plpgsql_CastHashKey );
3984
+ ctl .entrysize = sizeof (plpgsql_CastExprHashEntry );
3985
+ cast_expr_hash = hash_create ("PLpgSQL cast expressions" ,
3986
+ 16 ,/* start small and extend */
3987
+ & ctl ,
3988
+ HASH_ELEM |HASH_BLOBS );
3989
+ }
3990
+
3969
3991
/* set up for use of appropriate simple-expression EState and cast hash */
3970
3992
if (simple_eval_estate )
3971
3993
{
@@ -3979,28 +4001,22 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
3979
4001
16 ,/* start small and extend */
3980
4002
& ctl ,
3981
4003
HASH_ELEM |HASH_BLOBS |HASH_CONTEXT );
3982
- estate -> cast_hash_context = CurrentMemoryContext ;
3983
4004
}
3984
4005
else
3985
4006
{
3986
4007
estate -> simple_eval_estate = shared_simple_eval_estate ;
3987
4008
/* Create the session-wide cast-info hash table if we didn't already */
3988
4009
if (shared_cast_hash == NULL )
3989
4010
{
3990
- shared_cast_context = AllocSetContextCreate (TopMemoryContext ,
3991
- "PLpgSQL cast info" ,
3992
- ALLOCSET_DEFAULT_SIZES );
3993
4011
memset (& ctl ,0 ,sizeof (ctl ));
3994
4012
ctl .keysize = sizeof (plpgsql_CastHashKey );
3995
4013
ctl .entrysize = sizeof (plpgsql_CastHashEntry );
3996
- ctl .hcxt = shared_cast_context ;
3997
4014
shared_cast_hash = hash_create ("PLpgSQL cast cache" ,
3998
4015
16 ,/* start small and extend */
3999
4016
& ctl ,
4000
- HASH_ELEM |HASH_BLOBS | HASH_CONTEXT );
4017
+ HASH_ELEM |HASH_BLOBS );
4001
4018
}
4002
4019
estate -> cast_hash = shared_cast_hash ;
4003
- estate -> cast_hash_context = shared_cast_context ;
4004
4020
}
4005
4021
4006
4022
/*
@@ -7825,6 +7841,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7825
7841
{
7826
7842
plpgsql_CastHashKey cast_key ;
7827
7843
plpgsql_CastHashEntry * cast_entry ;
7844
+ plpgsql_CastExprHashEntry * expr_entry ;
7828
7845
bool found ;
7829
7846
LocalTransactionId curlxid ;
7830
7847
MemoryContext oldcontext ;
@@ -7838,10 +7855,28 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7838
7855
(void * )& cast_key ,
7839
7856
HASH_ENTER ,& found );
7840
7857
if (!found )/* initialize if new entry */
7841
- cast_entry -> cast_cexpr = NULL ;
7858
+ {
7859
+ /* We need a second lookup to see if a cast_expr_hash entry exists */
7860
+ expr_entry = (plpgsql_CastExprHashEntry * )hash_search (cast_expr_hash ,
7861
+ & cast_key ,
7862
+ HASH_ENTER ,
7863
+ & found );
7864
+ if (!found )/* initialize if new expr entry */
7865
+ expr_entry -> cast_cexpr = NULL ;
7866
+
7867
+ cast_entry -> cast_centry = expr_entry ;
7868
+ cast_entry -> cast_exprstate = NULL ;
7869
+ cast_entry -> cast_in_use = false;
7870
+ cast_entry -> cast_lxid = InvalidLocalTransactionId ;
7871
+ }
7872
+ else
7873
+ {
7874
+ /* Use always-valid link to avoid a second hash lookup */
7875
+ expr_entry = cast_entry -> cast_centry ;
7876
+ }
7842
7877
7843
- if (cast_entry -> cast_cexpr == NULL ||
7844
- !cast_entry -> cast_cexpr -> is_valid )
7878
+ if (expr_entry -> cast_cexpr == NULL ||
7879
+ !expr_entry -> cast_cexpr -> is_valid )
7845
7880
{
7846
7881
/*
7847
7882
* We've not looked up this coercion before, or we have but the cached
@@ -7854,10 +7889,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7854
7889
/*
7855
7890
* Drop old cached expression if there is one.
7856
7891
*/
7857
- if (cast_entry -> cast_cexpr )
7892
+ if (expr_entry -> cast_cexpr )
7858
7893
{
7859
- FreeCachedExpression (cast_entry -> cast_cexpr );
7860
- cast_entry -> cast_cexpr = NULL ;
7894
+ FreeCachedExpression (expr_entry -> cast_cexpr );
7895
+ expr_entry -> cast_cexpr = NULL ;
7861
7896
}
7862
7897
7863
7898
/*
@@ -7933,9 +7968,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7933
7968
((RelabelType * )cast_expr )-> arg == (Expr * )placeholder )
7934
7969
cast_expr = NULL ;
7935
7970
7936
- /* Now we can fill in the hashtable entry. */
7937
- cast_entry -> cast_cexpr = cast_cexpr ;
7938
- cast_entry -> cast_expr = (Expr * )cast_expr ;
7971
+ /* Now we can fill in the expression hashtable entry. */
7972
+ expr_entry -> cast_cexpr = cast_cexpr ;
7973
+ expr_entry -> cast_expr = (Expr * )cast_expr ;
7974
+
7975
+ /* Be sure to reset the exprstate hashtable entry, too. */
7939
7976
cast_entry -> cast_exprstate = NULL ;
7940
7977
cast_entry -> cast_in_use = false;
7941
7978
cast_entry -> cast_lxid = InvalidLocalTransactionId ;
@@ -7944,7 +7981,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7944
7981
}
7945
7982
7946
7983
/* Done if we have determined that this is a no-op cast. */
7947
- if (cast_entry -> cast_expr == NULL )
7984
+ if (expr_entry -> cast_expr == NULL )
7948
7985
return NULL ;
7949
7986
7950
7987
/*
@@ -7963,7 +8000,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7963
8000
if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
7964
8001
{
7965
8002
oldcontext = MemoryContextSwitchTo (estate -> simple_eval_estate -> es_query_cxt );
7966
- cast_entry -> cast_exprstate = ExecInitExpr (cast_entry -> cast_expr ,NULL );
8003
+ cast_entry -> cast_exprstate = ExecInitExpr (expr_entry -> cast_expr ,NULL );
7967
8004
cast_entry -> cast_in_use = false;
7968
8005
cast_entry -> cast_lxid = curlxid ;
7969
8006
MemoryContextSwitchTo (oldcontext );