@@ -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 use a session-wide hash table for caching cast information.
139
+ * We use two session-wide hash tables 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 the shared 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 the shared_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
/*
@@ -4025,6 +4035,17 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
4025
4035
estate -> paramLI -> parserSetupArg = NULL ; /* filled during use */
4026
4036
estate -> paramLI -> numParams = estate -> ndatums ;
4027
4037
4038
+ /* Create the session-wide cast-expression hash if we didn't already */
4039
+ if (cast_expr_hash == NULL )
4040
+ {
4041
+ ctl .keysize = sizeof (plpgsql_CastHashKey );
4042
+ ctl .entrysize = sizeof (plpgsql_CastExprHashEntry );
4043
+ cast_expr_hash = hash_create ("PLpgSQL cast expressions" ,
4044
+ 16 , /* start small and extend */
4045
+ & ctl ,
4046
+ HASH_ELEM | HASH_BLOBS );
4047
+ }
4048
+
4028
4049
/* set up for use of appropriate simple-expression EState and cast hash */
4029
4050
if (simple_eval_estate )
4030
4051
{
@@ -4037,27 +4058,21 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
4037
4058
16 , /* start small and extend */
4038
4059
& ctl ,
4039
4060
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
4040
- estate -> cast_hash_context = CurrentMemoryContext ;
4041
4061
}
4042
4062
else
4043
4063
{
4044
4064
estate -> simple_eval_estate = shared_simple_eval_estate ;
4045
4065
/* Create the session-wide cast-info hash table if we didn't already */
4046
4066
if (shared_cast_hash == NULL )
4047
4067
{
4048
- shared_cast_context = AllocSetContextCreate (TopMemoryContext ,
4049
- "PLpgSQL cast info" ,
4050
- ALLOCSET_DEFAULT_SIZES );
4051
4068
ctl .keysize = sizeof (plpgsql_CastHashKey );
4052
4069
ctl .entrysize = sizeof (plpgsql_CastHashEntry );
4053
- ctl .hcxt = shared_cast_context ;
4054
4070
shared_cast_hash = hash_create ("PLpgSQL cast cache" ,
4055
4071
16 , /* start small and extend */
4056
4072
& ctl ,
4057
- HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
4073
+ HASH_ELEM | HASH_BLOBS );
4058
4074
}
4059
4075
estate -> cast_hash = shared_cast_hash ;
4060
- estate -> cast_hash_context = shared_cast_context ;
4061
4076
}
4062
4077
/* likewise for the simple-expression resource owner */
4063
4078
if (simple_eval_resowner )
@@ -7768,6 +7783,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7768
7783
{
7769
7784
plpgsql_CastHashKey cast_key ;
7770
7785
plpgsql_CastHashEntry * cast_entry ;
7786
+ plpgsql_CastExprHashEntry * expr_entry ;
7771
7787
bool found ;
7772
7788
LocalTransactionId curlxid ;
7773
7789
MemoryContext oldcontext ;
@@ -7781,10 +7797,28 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7781
7797
& cast_key ,
7782
7798
HASH_ENTER , & found );
7783
7799
if (!found ) /* initialize if new entry */
7784
- cast_entry -> cast_cexpr = NULL ;
7800
+ {
7801
+ /* We need a second lookup to see if a cast_expr_hash entry exists */
7802
+ expr_entry = (plpgsql_CastExprHashEntry * ) hash_search (cast_expr_hash ,
7803
+ & cast_key ,
7804
+ HASH_ENTER ,
7805
+ & found );
7806
+ if (!found ) /* initialize if new expr entry */
7807
+ expr_entry -> cast_cexpr = NULL ;
7785
7808
7786
- if (cast_entry -> cast_cexpr == NULL ||
7787
- !cast_entry -> cast_cexpr -> is_valid )
7809
+ cast_entry -> cast_centry = expr_entry ;
7810
+ cast_entry -> cast_exprstate = NULL ;
7811
+ cast_entry -> cast_in_use = false;
7812
+ cast_entry -> cast_lxid = InvalidLocalTransactionId ;
7813
+ }
7814
+ else
7815
+ {
7816
+ /* Use always-valid link to avoid a second hash lookup */
7817
+ expr_entry = cast_entry -> cast_centry ;
7818
+ }
7819
+
7820
+ if (expr_entry -> cast_cexpr == NULL ||
7821
+ !expr_entry -> cast_cexpr -> is_valid )
7788
7822
{
7789
7823
/*
7790
7824
* We've not looked up this coercion before, or we have but the cached
@@ -7797,10 +7831,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7797
7831
/*
7798
7832
* Drop old cached expression if there is one.
7799
7833
*/
7800
- if (cast_entry -> cast_cexpr )
7834
+ if (expr_entry -> cast_cexpr )
7801
7835
{
7802
- FreeCachedExpression (cast_entry -> cast_cexpr );
7803
- cast_entry -> cast_cexpr = NULL ;
7836
+ FreeCachedExpression (expr_entry -> cast_cexpr );
7837
+ expr_entry -> cast_cexpr = NULL ;
7804
7838
}
7805
7839
7806
7840
/*
@@ -7881,9 +7915,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7881
7915
((RelabelType * ) cast_expr )-> arg == (Expr * ) placeholder )
7882
7916
cast_expr = NULL ;
7883
7917
7884
- /* Now we can fill in the hashtable entry. */
7885
- cast_entry -> cast_cexpr = cast_cexpr ;
7886
- cast_entry -> cast_expr = (Expr * ) cast_expr ;
7918
+ /* Now we can fill in the expression hashtable entry. */
7919
+ expr_entry -> cast_cexpr = cast_cexpr ;
7920
+ expr_entry -> cast_expr = (Expr * ) cast_expr ;
7921
+
7922
+ /* Be sure to reset the exprstate hashtable entry, too. */
7887
7923
cast_entry -> cast_exprstate = NULL ;
7888
7924
cast_entry -> cast_in_use = false;
7889
7925
cast_entry -> cast_lxid = InvalidLocalTransactionId ;
@@ -7892,7 +7928,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7892
7928
}
7893
7929
7894
7930
/* Done if we have determined that this is a no-op cast. */
7895
- if (cast_entry -> cast_expr == NULL )
7931
+ if (expr_entry -> cast_expr == NULL )
7896
7932
return NULL ;
7897
7933
7898
7934
/*
@@ -7911,7 +7947,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
7911
7947
if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
7912
7948
{
7913
7949
oldcontext = MemoryContextSwitchTo (estate -> simple_eval_estate -> es_query_cxt );
7914
- cast_entry -> cast_exprstate = ExecInitExpr (cast_entry -> cast_expr , NULL );
7950
+ cast_entry -> cast_exprstate = ExecInitExpr (expr_entry -> cast_expr , NULL );
7915
7951
cast_entry -> cast_in_use = false;
7916
7952
cast_entry -> cast_lxid = curlxid ;
7917
7953
MemoryContextSwitchTo (oldcontext );
0 commit comments