@@ -136,18 +136,22 @@ static ResourceOwner shared_simple_eval_resowner = NULL;
136136MemoryContextAllocZero(get_eval_mcontext(estate), sz)
137137
138138/*
139- * We usea session-wide hashtable for caching cast information.
139+ * We usetwo session-wide hashtables for caching cast information.
140140 *
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.
144146 *
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).
147151 *
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.
151155 */
152156typedef struct /* lookup key for cast info */
153157{
@@ -158,18 +162,24 @@ typedef struct/* lookup key for cast info */
158162int32 dsttypmod ;/* destination typmod for cast */
159163}plpgsql_CastHashKey ;
160164
161- typedef struct /*cast_hash table entry */
165+ typedef struct /*cast_expr_hash table entry */
162166{
163167plpgsql_CastHashKey key ;/* hash key --- MUST BE FIRST */
164168Expr * cast_expr ;/* cast expression, or NULL if no-op cast */
165169CachedExpression * 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 */
166176/* ExprState is valid only when cast_lxid matches current LXID */
167177ExprState * cast_exprstate ;/* expression's eval tree */
168178bool cast_in_use ;/* true while we're executing eval tree */
169179LocalTransactionId cast_lxid ;
170180}plpgsql_CastHashEntry ;
171181
172- static MemoryContext shared_cast_context = NULL ;
182+ static HTAB * cast_expr_hash = NULL ;
173183static HTAB * shared_cast_hash = NULL ;
174184
175185/*
@@ -4019,6 +4029,17 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
40194029estate -> paramLI -> parserSetupArg = NULL ;/* filled during use */
40204030estate -> paramLI -> numParams = estate -> ndatums ;
40214031
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+
40224043/* set up for use of appropriate simple-expression EState and cast hash */
40234044if (simple_eval_estate )
40244045{
@@ -4031,27 +4052,21 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
4031405216 ,/* start small and extend */
40324053& ctl ,
40334054HASH_ELEM |HASH_BLOBS |HASH_CONTEXT );
4034- estate -> cast_hash_context = CurrentMemoryContext ;
40354055}
40364056else
40374057{
40384058estate -> simple_eval_estate = shared_simple_eval_estate ;
40394059/* Create the session-wide cast-info hash table if we didn't already */
40404060if (shared_cast_hash == NULL )
40414061{
4042- shared_cast_context = AllocSetContextCreate (TopMemoryContext ,
4043- "PLpgSQL cast info" ,
4044- ALLOCSET_DEFAULT_SIZES );
40454062ctl .keysize = sizeof (plpgsql_CastHashKey );
40464063ctl .entrysize = sizeof (plpgsql_CastHashEntry );
4047- ctl .hcxt = shared_cast_context ;
40484064shared_cast_hash = hash_create ("PLpgSQL cast cache" ,
4049406516 ,/* start small and extend */
40504066& ctl ,
4051- HASH_ELEM |HASH_BLOBS | HASH_CONTEXT );
4067+ HASH_ELEM |HASH_BLOBS );
40524068}
40534069estate -> cast_hash = shared_cast_hash ;
4054- estate -> cast_hash_context = shared_cast_context ;
40554070}
40564071/* likewise for the simple-expression resource owner */
40574072if (simple_eval_resowner )
@@ -7765,6 +7780,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
77657780{
77667781plpgsql_CastHashKey cast_key ;
77677782plpgsql_CastHashEntry * cast_entry ;
7783+ plpgsql_CastExprHashEntry * expr_entry ;
77687784bool found ;
77697785LocalTransactionId curlxid ;
77707786MemoryContext oldcontext ;
@@ -7778,10 +7794,28 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
77787794 (void * )& cast_key ,
77797795HASH_ENTER ,& found );
77807796if (!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 ;
77827805
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 )
77857819{
77867820/*
77877821 * We've not looked up this coercion before, or we have but the cached
@@ -7794,10 +7828,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
77947828/*
77957829 * Drop old cached expression if there is one.
77967830 */
7797- if (cast_entry -> cast_cexpr )
7831+ if (expr_entry -> cast_cexpr )
77987832{
7799- FreeCachedExpression (cast_entry -> cast_cexpr );
7800- cast_entry -> cast_cexpr = NULL ;
7833+ FreeCachedExpression (expr_entry -> cast_cexpr );
7834+ expr_entry -> cast_cexpr = NULL ;
78017835}
78027836
78037837/*
@@ -7878,9 +7912,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
78787912((RelabelType * )cast_expr )-> arg == (Expr * )placeholder )
78797913cast_expr = NULL ;
78807914
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. */
78847920cast_entry -> cast_exprstate = NULL ;
78857921cast_entry -> cast_in_use = false;
78867922cast_entry -> cast_lxid = InvalidLocalTransactionId ;
@@ -7889,7 +7925,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
78897925}
78907926
78917927/* 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 )
78937929return NULL ;
78947930
78957931/*
@@ -7908,7 +7944,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
79087944if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
79097945{
79107946oldcontext = 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 );
79127948cast_entry -> cast_in_use = false;
79137949cast_entry -> cast_lxid = curlxid ;
79147950MemoryContextSwitchTo (oldcontext );