@@ -136,18 +136,22 @@ static ResourceOwner shared_simple_eval_resowner = NULL;
136136 MemoryContextAllocZero(get_eval_mcontext(estate), sz)
137137
138138/*
139- * We use a session-wide hash table for caching cast information.
139+ * We use two session-wide hash tables 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 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.
151155 */
152156typedef struct /* lookup key for cast info */
153157{
@@ -158,18 +162,24 @@ typedef struct /* lookup key for cast info */
158162 int32 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{
163167 plpgsql_CastHashKey key ; /* hash key --- MUST BE FIRST */
164168 Expr * cast_expr ; /* cast expression, or NULL if no-op cast */
165169 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 */
166176 /* ExprState is valid only when cast_lxid matches current LXID */
167177 ExprState * cast_exprstate ; /* expression's eval tree */
168178 bool cast_in_use ; /* true while we're executing eval tree */
169179 LocalTransactionId 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/*
@@ -4025,6 +4035,17 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
40254035 estate -> paramLI -> parserSetupArg = NULL ; /* filled during use */
40264036 estate -> paramLI -> numParams = estate -> ndatums ;
40274037
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+
40284049 /* set up for use of appropriate simple-expression EState and cast hash */
40294050 if (simple_eval_estate )
40304051 {
@@ -4037,27 +4058,21 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
40374058 16 , /* start small and extend */
40384059 & ctl ,
40394060 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
4040- estate -> cast_hash_context = CurrentMemoryContext ;
40414061 }
40424062 else
40434063 {
40444064 estate -> simple_eval_estate = shared_simple_eval_estate ;
40454065 /* Create the session-wide cast-info hash table if we didn't already */
40464066 if (shared_cast_hash == NULL )
40474067 {
4048- shared_cast_context = AllocSetContextCreate (TopMemoryContext ,
4049- "PLpgSQL cast info" ,
4050- ALLOCSET_DEFAULT_SIZES );
40514068 ctl .keysize = sizeof (plpgsql_CastHashKey );
40524069 ctl .entrysize = sizeof (plpgsql_CastHashEntry );
4053- ctl .hcxt = shared_cast_context ;
40544070 shared_cast_hash = hash_create ("PLpgSQL cast cache" ,
40554071 16 , /* start small and extend */
40564072 & ctl ,
4057- HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
4073+ HASH_ELEM | HASH_BLOBS );
40584074 }
40594075 estate -> cast_hash = shared_cast_hash ;
4060- estate -> cast_hash_context = shared_cast_context ;
40614076 }
40624077 /* likewise for the simple-expression resource owner */
40634078 if (simple_eval_resowner )
@@ -7768,6 +7783,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
77687783{
77697784 plpgsql_CastHashKey cast_key ;
77707785 plpgsql_CastHashEntry * cast_entry ;
7786+ plpgsql_CastExprHashEntry * expr_entry ;
77717787 bool found ;
77727788 LocalTransactionId curlxid ;
77737789 MemoryContext oldcontext ;
@@ -7781,10 +7797,28 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
77817797 & cast_key ,
77827798 HASH_ENTER , & found );
77837799 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 ;
77857808
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 )
77887822 {
77897823 /*
77907824 * We've not looked up this coercion before, or we have but the cached
@@ -7797,10 +7831,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
77977831 /*
77987832 * Drop old cached expression if there is one.
77997833 */
7800- if (cast_entry -> cast_cexpr )
7834+ if (expr_entry -> cast_cexpr )
78017835 {
7802- FreeCachedExpression (cast_entry -> cast_cexpr );
7803- cast_entry -> cast_cexpr = NULL ;
7836+ FreeCachedExpression (expr_entry -> cast_cexpr );
7837+ expr_entry -> cast_cexpr = NULL ;
78047838 }
78057839
78067840 /*
@@ -7881,9 +7915,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
78817915 ((RelabelType * ) cast_expr )-> arg == (Expr * ) placeholder )
78827916 cast_expr = NULL ;
78837917
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. */
78877923 cast_entry -> cast_exprstate = NULL ;
78887924 cast_entry -> cast_in_use = false;
78897925 cast_entry -> cast_lxid = InvalidLocalTransactionId ;
@@ -7892,7 +7928,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
78927928 }
78937929
78947930 /* 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 )
78967932 return NULL ;
78977933
78987934 /*
@@ -7911,7 +7947,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
79117947 if (cast_entry -> cast_lxid != curlxid || cast_entry -> cast_in_use )
79127948 {
79137949 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 );
79157951 cast_entry -> cast_in_use = false;
79167952 cast_entry -> cast_lxid = curlxid ;
79177953 MemoryContextSwitchTo (oldcontext );
0 commit comments