@@ -144,18 +144,22 @@ static ResourceOwner shared_simple_eval_resowner = NULL;
144
144
MemoryContextAllocZero(get_eval_mcontext(estate), sz)
145
145
146
146
/*
147
- * We usea session-wide hashtable for caching cast information.
147
+ * We usetwo session-wide hashtables for caching cast information.
148
148
*
149
- * Once built, the compiled expression trees (cast_expr fields) survive for
150
- * the life of the session. At some point it might be worth invalidating
151
- * those after pg_cast changes, but for the moment we don't bother.
149
+ * cast_expr_hash entries (of type plpgsql_CastExprHashEntry) hold compiled
150
+ * expression trees for casts. These survive for the life of the session and
151
+ * are shared across all PL/pgSQL functions and DO blocks. At some point it
152
+ * might be worth invalidating them after pg_cast changes, but for the moment
153
+ * we don't bother.
152
154
*
153
- * The evaluation state trees (cast_exprstate) are managed in the same way as
154
- * simple expressions (i.e., we assume cast expressions are always simple).
155
+ * There is a separate hash table shared_cast_hash (with entries of type
156
+ * plpgsql_CastHashEntry) containing evaluation state trees for these
157
+ * expressions, which are managed in the same way as simple expressions
158
+ * (i.e., we assume cast expressions are always simple).
155
159
*
156
- * As with simple expressions, DO blocks don't use theshared hash table but
157
- * must have their own. This isn't ideal, but we don't want to deal with
158
- * multiple simple_eval_estates within a DO block.
160
+ * As with simple expressions, DO blocks don't use theshared_cast_hash table
161
+ *but must have their own evaluation state trees . This isn't ideal, but we
162
+ *don't want to deal with multiple simple_eval_estates within a DO block.
159
163
*/
160
164
typedef struct /* lookup key for cast info */
161
165
{
@@ -166,18 +170,24 @@ typedef struct/* lookup key for cast info */
166
170
int32 dsttypmod ;/* destination typmod for cast */
167
171
}plpgsql_CastHashKey ;
168
172
169
- typedef struct /*cast_hash table entry */
173
+ typedef struct /*cast_expr_hash table entry */
170
174
{
171
175
plpgsql_CastHashKey key ;/* hash key --- MUST BE FIRST */
172
176
Expr * cast_expr ;/* cast expression, or NULL if no-op cast */
173
177
CachedExpression * cast_cexpr ;/* cached expression backing the above */
178
+ }plpgsql_CastExprHashEntry ;
179
+
180
+ typedef struct /* cast_hash table entry */
181
+ {
182
+ plpgsql_CastHashKey key ;/* hash key --- MUST BE FIRST */
183
+ plpgsql_CastExprHashEntry * cast_centry ;/* link to matching expr entry */
174
184
/* ExprState is valid only when cast_lxid matches current LXID */
175
185
ExprState * cast_exprstate ;/* expression's eval tree */
176
186
bool cast_in_use ;/* true while we're executing eval tree */
177
187
LocalTransactionId cast_lxid ;
178
188
}plpgsql_CastHashEntry ;
179
189
180
- static MemoryContext shared_cast_context = NULL ;
190
+ static HTAB * cast_expr_hash = NULL ;
181
191
static HTAB * shared_cast_hash = NULL ;
182
192
183
193
/*
@@ -3980,6 +3990,18 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
3980
3990
estate -> paramLI -> parserSetupArg = NULL ;/* filled during use */
3981
3991
estate -> paramLI -> numParams = estate -> ndatums ;
3982
3992
3993
+ /* Create the session-wide cast-expression hash if we didn't already */
3994
+ if (cast_expr_hash == NULL )
3995
+ {
3996
+ memset (& ctl ,0 ,sizeof (ctl ));
3997
+ ctl .keysize = sizeof (plpgsql_CastHashKey );
3998
+ ctl .entrysize = sizeof (plpgsql_CastExprHashEntry );
3999
+ cast_expr_hash = hash_create ("PLpgSQL cast expressions" ,
4000
+ 16 ,/* start small and extend */
4001
+ & ctl ,
4002
+ HASH_ELEM |HASH_BLOBS );
4003
+ }
4004
+
3983
4005
/* set up for use of appropriate simple-expression EState and cast hash */
3984
4006
if (simple_eval_estate )
3985
4007
{
@@ -3993,28 +4015,22 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
3993
4015
16 ,/* start small and extend */
3994
4016
& ctl ,
3995
4017
HASH_ELEM |HASH_BLOBS |HASH_CONTEXT );
3996
- estate -> cast_hash_context = CurrentMemoryContext ;
3997
4018
}
3998
4019
else
3999
4020
{
4000
4021
estate -> simple_eval_estate = shared_simple_eval_estate ;
4001
4022
/* Create the session-wide cast-info hash table if we didn't already */
4002
4023
if (shared_cast_hash == NULL )
4003
4024
{
4004
- shared_cast_context = AllocSetContextCreate (TopMemoryContext ,
4005
- "PLpgSQL cast info" ,
4006
- ALLOCSET_DEFAULT_SIZES );
4007
4025
memset (& ctl ,0 ,sizeof (ctl ));
4008
4026
ctl .keysize = sizeof (plpgsql_CastHashKey );
4009
4027
ctl .entrysize = sizeof (plpgsql_CastHashEntry );
4010
- ctl .hcxt = shared_cast_context ;
4011
4028
shared_cast_hash = hash_create ("PLpgSQL cast cache" ,
4012
4029
16 ,/* start small and extend */
4013
4030
& ctl ,
4014
- HASH_ELEM |HASH_BLOBS | HASH_CONTEXT );
4031
+ HASH_ELEM |HASH_BLOBS );
4015
4032
}
4016
4033
estate -> cast_hash = shared_cast_hash ;
4017
- estate -> cast_hash_context = shared_cast_context ;
4018
4034
}
4019
4035
/* likewise for the simple-expression resource owner */
4020
4036
if (simple_eval_resowner )
@@ -7916,6 +7932,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7916
7932
{
7917
7933
plpgsql_CastHashKey cast_key ;
7918
7934
plpgsql_CastHashEntry * cast_entry ;
7935
+ plpgsql_CastExprHashEntry * expr_entry ;
7919
7936
bool found ;
7920
7937
LocalTransactionId curlxid ;
7921
7938
MemoryContext oldcontext ;
@@ -7929,10 +7946,28 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7929
7946
(void * )& cast_key ,
7930
7947
HASH_ENTER ,& found );
7931
7948
if (!found )/* initialize if new entry */
7932
- cast_entry -> cast_cexpr = NULL ;
7949
+ {
7950
+ /* We need a second lookup to see if a cast_expr_hash entry exists */
7951
+ expr_entry = (plpgsql_CastExprHashEntry * )hash_search (cast_expr_hash ,
7952
+ & cast_key ,
7953
+ HASH_ENTER ,
7954
+ & found );
7955
+ if (!found )/* initialize if new expr entry */
7956
+ expr_entry -> cast_cexpr = NULL ;
7957
+
7958
+ cast_entry -> cast_centry = expr_entry ;
7959
+ cast_entry -> cast_exprstate = NULL ;
7960
+ cast_entry -> cast_in_use = false;
7961
+ cast_entry -> cast_lxid = InvalidLocalTransactionId ;
7962
+ }
7963
+ else
7964
+ {
7965
+ /* Use always-valid link to avoid a second hash lookup */
7966
+ expr_entry = cast_entry -> cast_centry ;
7967
+ }
7933
7968
7934
- if (cast_entry -> cast_cexpr == NULL ||
7935
- !cast_entry -> cast_cexpr -> is_valid )
7969
+ if (expr_entry -> cast_cexpr == NULL ||
7970
+ !expr_entry -> cast_cexpr -> is_valid )
7936
7971
{
7937
7972
/*
7938
7973
* We've not looked up this coercion before, or we have but the cached
@@ -7945,10 +7980,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7945
7980
/*
7946
7981
* Drop old cached expression if there is one.
7947
7982
*/
7948
- if (cast_entry -> cast_cexpr )
7983
+ if (expr_entry -> cast_cexpr )
7949
7984
{
7950
- FreeCachedExpression (cast_entry -> cast_cexpr );
7951
- cast_entry -> cast_cexpr = NULL ;
7985
+ FreeCachedExpression (expr_entry -> cast_cexpr );
7986
+ expr_entry -> cast_cexpr = NULL ;
7952
7987
}
7953
7988
7954
7989
/*
@@ -8024,9 +8059,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
8024
8059
((RelabelType * )cast_expr )-> arg == (Expr * )placeholder )
8025
8060
cast_expr = NULL ;
8026
8061
8027
- /* Now we can fill in the hashtable entry. */
8028
- cast_entry -> cast_cexpr = cast_cexpr ;
8029
- cast_entry -> cast_expr = (Expr * )cast_expr ;
8062
+ /* Now we can fill in the expression hashtable entry. */
8063
+ expr_entry -> cast_cexpr = cast_cexpr ;
8064
+ expr_entry -> cast_expr = (Expr * )cast_expr ;
8065
+
8066
+ /* Be sure to reset the exprstate hashtable entry, too. */
8030
8067
cast_entry -> cast_exprstate = NULL ;
8031
8068
cast_entry -> cast_in_use = false;
8032
8069
cast_entry -> cast_lxid = InvalidLocalTransactionId ;
@@ -8035,7 +8072,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
8035
8072
}
8036
8073
8037
8074
/* Done if we have determined that this is a no-op cast. */
8038
- if (cast_entry -> cast_expr == NULL )
8075
+ if (expr_entry -> cast_expr == NULL )
8039
8076
return NULL ;
8040
8077
8041
8078
/*
@@ -8054,7 +8091,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
8054
8091
if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
8055
8092
{
8056
8093
oldcontext = MemoryContextSwitchTo (estate -> simple_eval_estate -> es_query_cxt );
8057
- cast_entry -> cast_exprstate = ExecInitExpr (cast_entry -> cast_expr ,NULL );
8094
+ cast_entry -> cast_exprstate = ExecInitExpr (expr_entry -> cast_expr ,NULL );
8058
8095
cast_entry -> cast_in_use = false;
8059
8096
cast_entry -> cast_lxid = curlxid ;
8060
8097
MemoryContextSwitchTo (oldcontext );