@@ -136,18 +136,22 @@ static ResourceOwner shared_simple_eval_resowner = NULL;
136
136
MemoryContextAllocZero(get_eval_mcontext(estate), sz)
137
137
138
138
/*
139
- * We usea session-wide hashtable for caching cast information.
139
+ * We usetwo session-wide hashtables for caching cast information.
140
140
*
141
- * Once built, the compiled expression trees (cast_expr fields) survive for
142
- * the life of the session. At some point it might be worth invalidating
143
- * those after pg_cast changes, but for the moment we don't bother.
141
+ * cast_expr_hash entries (of type plpgsql_CastExprHashEntry) hold compiled
142
+ * expression trees for casts. These survive for the life of the session and
143
+ * are shared across all PL/pgSQL functions and DO blocks. At some point it
144
+ * might be worth invalidating them after pg_cast changes, but for the moment
145
+ * we don't bother.
144
146
*
145
- * The evaluation state trees (cast_exprstate) are managed in the same way as
146
- * simple expressions (i.e., we assume cast expressions are always simple).
147
+ * There is a separate hash table shared_cast_hash (with entries of type
148
+ * plpgsql_CastHashEntry) containing evaluation state trees for these
149
+ * expressions, which are managed in the same way as simple expressions
150
+ * (i.e., we assume cast expressions are always simple).
147
151
*
148
- * As with simple expressions, DO blocks don't use theshared hash table but
149
- * must have their own. This isn't ideal, but we don't want to deal with
150
- * multiple simple_eval_estates within a DO block.
152
+ * As with simple expressions, DO blocks don't use theshared_cast_hash table
153
+ *but must have their own evaluation state trees . This isn't ideal, but we
154
+ *don't want to deal with multiple simple_eval_estates within a DO block.
151
155
*/
152
156
typedef struct /* lookup key for cast info */
153
157
{
@@ -158,18 +162,24 @@ typedef struct/* lookup key for cast info */
158
162
int32 dsttypmod ;/* destination typmod for cast */
159
163
}plpgsql_CastHashKey ;
160
164
161
- typedef struct /*cast_hash table entry */
165
+ typedef struct /*cast_expr_hash table entry */
162
166
{
163
167
plpgsql_CastHashKey key ;/* hash key --- MUST BE FIRST */
164
168
Expr * cast_expr ;/* cast expression, or NULL if no-op cast */
165
169
CachedExpression * cast_cexpr ;/* cached expression backing the above */
170
+ }plpgsql_CastExprHashEntry ;
171
+
172
+ typedef struct /* cast_hash table entry */
173
+ {
174
+ plpgsql_CastHashKey key ;/* hash key --- MUST BE FIRST */
175
+ plpgsql_CastExprHashEntry * cast_centry ;/* link to matching expr entry */
166
176
/* ExprState is valid only when cast_lxid matches current LXID */
167
177
ExprState * cast_exprstate ;/* expression's eval tree */
168
178
bool cast_in_use ;/* true while we're executing eval tree */
169
179
LocalTransactionId cast_lxid ;
170
180
}plpgsql_CastHashEntry ;
171
181
172
- static MemoryContext shared_cast_context = NULL ;
182
+ static HTAB * cast_expr_hash = NULL ;
173
183
static HTAB * shared_cast_hash = NULL ;
174
184
175
185
/*
@@ -4019,6 +4029,17 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
4019
4029
estate -> paramLI -> parserSetupArg = NULL ;/* filled during use */
4020
4030
estate -> paramLI -> numParams = estate -> ndatums ;
4021
4031
4032
+ /* Create the session-wide cast-expression hash if we didn't already */
4033
+ if (cast_expr_hash == NULL )
4034
+ {
4035
+ ctl .keysize = sizeof (plpgsql_CastHashKey );
4036
+ ctl .entrysize = sizeof (plpgsql_CastExprHashEntry );
4037
+ cast_expr_hash = hash_create ("PLpgSQL cast expressions" ,
4038
+ 16 ,/* start small and extend */
4039
+ & ctl ,
4040
+ HASH_ELEM |HASH_BLOBS );
4041
+ }
4042
+
4022
4043
/* set up for use of appropriate simple-expression EState and cast hash */
4023
4044
if (simple_eval_estate )
4024
4045
{
@@ -4031,27 +4052,21 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
4031
4052
16 ,/* start small and extend */
4032
4053
& ctl ,
4033
4054
HASH_ELEM |HASH_BLOBS |HASH_CONTEXT );
4034
- estate -> cast_hash_context = CurrentMemoryContext ;
4035
4055
}
4036
4056
else
4037
4057
{
4038
4058
estate -> simple_eval_estate = shared_simple_eval_estate ;
4039
4059
/* Create the session-wide cast-info hash table if we didn't already */
4040
4060
if (shared_cast_hash == NULL )
4041
4061
{
4042
- shared_cast_context = AllocSetContextCreate (TopMemoryContext ,
4043
- "PLpgSQL cast info" ,
4044
- ALLOCSET_DEFAULT_SIZES );
4045
4062
ctl .keysize = sizeof (plpgsql_CastHashKey );
4046
4063
ctl .entrysize = sizeof (plpgsql_CastHashEntry );
4047
- ctl .hcxt = shared_cast_context ;
4048
4064
shared_cast_hash = hash_create ("PLpgSQL cast cache" ,
4049
4065
16 ,/* start small and extend */
4050
4066
& ctl ,
4051
- HASH_ELEM |HASH_BLOBS | HASH_CONTEXT );
4067
+ HASH_ELEM |HASH_BLOBS );
4052
4068
}
4053
4069
estate -> cast_hash = shared_cast_hash ;
4054
- estate -> cast_hash_context = shared_cast_context ;
4055
4070
}
4056
4071
/* likewise for the simple-expression resource owner */
4057
4072
if (simple_eval_resowner )
@@ -7765,6 +7780,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7765
7780
{
7766
7781
plpgsql_CastHashKey cast_key ;
7767
7782
plpgsql_CastHashEntry * cast_entry ;
7783
+ plpgsql_CastExprHashEntry * expr_entry ;
7768
7784
bool found ;
7769
7785
LocalTransactionId curlxid ;
7770
7786
MemoryContext oldcontext ;
@@ -7778,10 +7794,28 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7778
7794
(void * )& cast_key ,
7779
7795
HASH_ENTER ,& found );
7780
7796
if (!found )/* initialize if new entry */
7781
- cast_entry -> cast_cexpr = NULL ;
7797
+ {
7798
+ /* We need a second lookup to see if a cast_expr_hash entry exists */
7799
+ expr_entry = (plpgsql_CastExprHashEntry * )hash_search (cast_expr_hash ,
7800
+ & cast_key ,
7801
+ HASH_ENTER ,
7802
+ & found );
7803
+ if (!found )/* initialize if new expr entry */
7804
+ expr_entry -> cast_cexpr = NULL ;
7782
7805
7783
- if (cast_entry -> cast_cexpr == NULL ||
7784
- !cast_entry -> cast_cexpr -> is_valid )
7806
+ cast_entry -> cast_centry = expr_entry ;
7807
+ cast_entry -> cast_exprstate = NULL ;
7808
+ cast_entry -> cast_in_use = false;
7809
+ cast_entry -> cast_lxid = InvalidLocalTransactionId ;
7810
+ }
7811
+ else
7812
+ {
7813
+ /* Use always-valid link to avoid a second hash lookup */
7814
+ expr_entry = cast_entry -> cast_centry ;
7815
+ }
7816
+
7817
+ if (expr_entry -> cast_cexpr == NULL ||
7818
+ !expr_entry -> cast_cexpr -> is_valid )
7785
7819
{
7786
7820
/*
7787
7821
* We've not looked up this coercion before, or we have but the cached
@@ -7794,10 +7828,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7794
7828
/*
7795
7829
* Drop old cached expression if there is one.
7796
7830
*/
7797
- if (cast_entry -> cast_cexpr )
7831
+ if (expr_entry -> cast_cexpr )
7798
7832
{
7799
- FreeCachedExpression (cast_entry -> cast_cexpr );
7800
- cast_entry -> cast_cexpr = NULL ;
7833
+ FreeCachedExpression (expr_entry -> cast_cexpr );
7834
+ expr_entry -> cast_cexpr = NULL ;
7801
7835
}
7802
7836
7803
7837
/*
@@ -7878,9 +7912,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7878
7912
((RelabelType * )cast_expr )-> arg == (Expr * )placeholder )
7879
7913
cast_expr = NULL ;
7880
7914
7881
- /* Now we can fill in the hashtable entry. */
7882
- cast_entry -> cast_cexpr = cast_cexpr ;
7883
- cast_entry -> cast_expr = (Expr * )cast_expr ;
7915
+ /* Now we can fill in the expression hashtable entry. */
7916
+ expr_entry -> cast_cexpr = cast_cexpr ;
7917
+ expr_entry -> cast_expr = (Expr * )cast_expr ;
7918
+
7919
+ /* Be sure to reset the exprstate hashtable entry, too. */
7884
7920
cast_entry -> cast_exprstate = NULL ;
7885
7921
cast_entry -> cast_in_use = false;
7886
7922
cast_entry -> cast_lxid = InvalidLocalTransactionId ;
@@ -7889,7 +7925,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7889
7925
}
7890
7926
7891
7927
/* Done if we have determined that this is a no-op cast. */
7892
- if (cast_entry -> cast_expr == NULL )
7928
+ if (expr_entry -> cast_expr == NULL )
7893
7929
return NULL ;
7894
7930
7895
7931
/*
@@ -7908,7 +7944,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7908
7944
if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
7909
7945
{
7910
7946
oldcontext = MemoryContextSwitchTo (estate -> simple_eval_estate -> es_query_cxt );
7911
- cast_entry -> cast_exprstate = ExecInitExpr (cast_entry -> cast_expr ,NULL );
7947
+ cast_entry -> cast_exprstate = ExecInitExpr (expr_entry -> cast_expr ,NULL );
7912
7948
cast_entry -> cast_in_use = false;
7913
7949
cast_entry -> cast_lxid = curlxid ;
7914
7950
MemoryContextSwitchTo (oldcontext );