@@ -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
/*
@@ -3985,6 +3995,17 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
3985
3995
estate -> paramLI -> parserSetupArg = NULL ;/* filled during use */
3986
3996
estate -> paramLI -> numParams = estate -> ndatums ;
3987
3997
3998
+ /* Create the session-wide cast-expression hash if we didn't already */
3999
+ if (cast_expr_hash == NULL )
4000
+ {
4001
+ ctl .keysize = sizeof (plpgsql_CastHashKey );
4002
+ ctl .entrysize = sizeof (plpgsql_CastExprHashEntry );
4003
+ cast_expr_hash = hash_create ("PLpgSQL cast expressions" ,
4004
+ 16 ,/* start small and extend */
4005
+ & ctl ,
4006
+ HASH_ELEM |HASH_BLOBS );
4007
+ }
4008
+
3988
4009
/* set up for use of appropriate simple-expression EState and cast hash */
3989
4010
if (simple_eval_estate )
3990
4011
{
@@ -3997,27 +4018,21 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
3997
4018
16 ,/* start small and extend */
3998
4019
& ctl ,
3999
4020
HASH_ELEM |HASH_BLOBS |HASH_CONTEXT );
4000
- estate -> cast_hash_context = CurrentMemoryContext ;
4001
4021
}
4002
4022
else
4003
4023
{
4004
4024
estate -> simple_eval_estate = shared_simple_eval_estate ;
4005
4025
/* Create the session-wide cast-info hash table if we didn't already */
4006
4026
if (shared_cast_hash == NULL )
4007
4027
{
4008
- shared_cast_context = AllocSetContextCreate (TopMemoryContext ,
4009
- "PLpgSQL cast info" ,
4010
- ALLOCSET_DEFAULT_SIZES );
4011
4028
ctl .keysize = sizeof (plpgsql_CastHashKey );
4012
4029
ctl .entrysize = sizeof (plpgsql_CastHashEntry );
4013
- ctl .hcxt = shared_cast_context ;
4014
4030
shared_cast_hash = hash_create ("PLpgSQL cast cache" ,
4015
4031
16 ,/* start small and extend */
4016
4032
& ctl ,
4017
- HASH_ELEM |HASH_BLOBS | HASH_CONTEXT );
4033
+ HASH_ELEM |HASH_BLOBS );
4018
4034
}
4019
4035
estate -> cast_hash = shared_cast_hash ;
4020
- estate -> cast_hash_context = shared_cast_context ;
4021
4036
}
4022
4037
/* likewise for the simple-expression resource owner */
4023
4038
if (simple_eval_resowner )
@@ -7715,6 +7730,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7715
7730
{
7716
7731
plpgsql_CastHashKey cast_key ;
7717
7732
plpgsql_CastHashEntry * cast_entry ;
7733
+ plpgsql_CastExprHashEntry * expr_entry ;
7718
7734
bool found ;
7719
7735
LocalTransactionId curlxid ;
7720
7736
MemoryContext oldcontext ;
@@ -7728,10 +7744,28 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7728
7744
(void * )& cast_key ,
7729
7745
HASH_ENTER ,& found );
7730
7746
if (!found )/* initialize if new entry */
7731
- cast_entry -> cast_cexpr = NULL ;
7747
+ {
7748
+ /* We need a second lookup to see if a cast_expr_hash entry exists */
7749
+ expr_entry = (plpgsql_CastExprHashEntry * )hash_search (cast_expr_hash ,
7750
+ & cast_key ,
7751
+ HASH_ENTER ,
7752
+ & found );
7753
+ if (!found )/* initialize if new expr entry */
7754
+ expr_entry -> cast_cexpr = NULL ;
7732
7755
7733
- if (cast_entry -> cast_cexpr == NULL ||
7734
- !cast_entry -> cast_cexpr -> is_valid )
7756
+ cast_entry -> cast_centry = expr_entry ;
7757
+ cast_entry -> cast_exprstate = NULL ;
7758
+ cast_entry -> cast_in_use = false;
7759
+ cast_entry -> cast_lxid = InvalidLocalTransactionId ;
7760
+ }
7761
+ else
7762
+ {
7763
+ /* Use always-valid link to avoid a second hash lookup */
7764
+ expr_entry = cast_entry -> cast_centry ;
7765
+ }
7766
+
7767
+ if (expr_entry -> cast_cexpr == NULL ||
7768
+ !expr_entry -> cast_cexpr -> is_valid )
7735
7769
{
7736
7770
/*
7737
7771
* We've not looked up this coercion before, or we have but the cached
@@ -7744,10 +7778,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7744
7778
/*
7745
7779
* Drop old cached expression if there is one.
7746
7780
*/
7747
- if (cast_entry -> cast_cexpr )
7781
+ if (expr_entry -> cast_cexpr )
7748
7782
{
7749
- FreeCachedExpression (cast_entry -> cast_cexpr );
7750
- cast_entry -> cast_cexpr = NULL ;
7783
+ FreeCachedExpression (expr_entry -> cast_cexpr );
7784
+ expr_entry -> cast_cexpr = NULL ;
7751
7785
}
7752
7786
7753
7787
/*
@@ -7828,9 +7862,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7828
7862
((RelabelType * )cast_expr )-> arg == (Expr * )placeholder )
7829
7863
cast_expr = NULL ;
7830
7864
7831
- /* Now we can fill in the hashtable entry. */
7832
- cast_entry -> cast_cexpr = cast_cexpr ;
7833
- cast_entry -> cast_expr = (Expr * )cast_expr ;
7865
+ /* Now we can fill in the expression hashtable entry. */
7866
+ expr_entry -> cast_cexpr = cast_cexpr ;
7867
+ expr_entry -> cast_expr = (Expr * )cast_expr ;
7868
+
7869
+ /* Be sure to reset the exprstate hashtable entry, too. */
7834
7870
cast_entry -> cast_exprstate = NULL ;
7835
7871
cast_entry -> cast_in_use = false;
7836
7872
cast_entry -> cast_lxid = InvalidLocalTransactionId ;
@@ -7839,7 +7875,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7839
7875
}
7840
7876
7841
7877
/* Done if we have determined that this is a no-op cast. */
7842
- if (cast_entry -> cast_expr == NULL )
7878
+ if (expr_entry -> cast_expr == NULL )
7843
7879
return NULL ;
7844
7880
7845
7881
/*
@@ -7858,7 +7894,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7858
7894
if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
7859
7895
{
7860
7896
oldcontext = MemoryContextSwitchTo (estate -> simple_eval_estate -> es_query_cxt );
7861
- cast_entry -> cast_exprstate = ExecInitExpr (cast_entry -> cast_expr ,NULL );
7897
+ cast_entry -> cast_exprstate = ExecInitExpr (expr_entry -> cast_expr ,NULL );
7862
7898
cast_entry -> cast_in_use = false;
7863
7899
cast_entry -> cast_lxid = curlxid ;
7864
7900
MemoryContextSwitchTo (oldcontext );