diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index e271ae5..0a4ffa8 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -163,15 +163,22 @@ typedef struct /* cast_hash table entry */ plpgsql_CastHashKey key; /* hash key --- MUST BE FIRST */ Expr *cast_expr; /* cast expression, or NULL if no-op cast */ CachedExpression *cast_cexpr; /* cached expression backing the above */ +} plpgsql_SessionCastHashEntry; + +typedef struct +{ + plpgsql_CastHashKey key; /* hash key --- MUST BE FIRST */ /* ExprState is valid only when cast_lxid matches current LXID */ ExprState *cast_exprstate; /* expression's eval tree */ bool cast_in_use; /* true while we're executing eval tree */ LocalTransactionId cast_lxid; -} plpgsql_CastHashEntry; +} plpgsql_PrivateCastHashEntry; -static MemoryContext shared_cast_context = NULL; -static HTAB *shared_cast_hash = NULL; +static MemoryContext shared_cast_upper_context = NULL; +static HTAB *shared_cast_upper_hash = NULL; +static MemoryContext shared_cast_lower_context = NULL; +static HTAB *shared_cast_lower_hash = NULL; /* * LOOP_RC_PROCESSING encapsulates common logic for looping statements to * handle return/exit/continue result codes from the loop body statement(s). @@ -422,9 +429,9 @@ static Datum do_cast_value(PLpgSQL_execstate *estate, Datum value, bool *isnull, Oid valtype, int32 valtypmod, Oid reqtype, int32 reqtypmod); -static plpgsql_CastHashEntry *get_cast_hashentry(PLpgSQL_execstate *estate, - Oid srctype, int32 srctypmod, - Oid dsttype, int32 dsttypmod); +static plpgsql_PrivateCastHashEntry * get_cast_hashentry(PLpgSQL_execstate *estate, + Oid srctype, int32 srctypmod, + Oid dsttype, int32 dsttypmod); static void exec_init_tuple_store(PLpgSQL_execstate *estate); static void exec_set_found(PLpgSQL_execstate *estate, bool state); static void plpgsql_create_econtext(PLpgSQL_execstate *estate); @@ -4031,7 +4038,7 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate, estate->simple_eval_estate = simple_eval_estate; /* Private cast hash just lives in function's main context */ ctl.keysize = sizeof(plpgsql_CastHashKey); - ctl.entrysize = sizeof(plpgsql_CastHashEntry); + ctl.entrysize = sizeof(plpgsql_PrivateCastHashEntry); ctl.hcxt = CurrentMemoryContext; estate->cast_hash = hash_create("PLpgSQL private cast cache", 16, /* start small and extend */ @@ -4042,23 +4049,42 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate, else { estate->simple_eval_estate = shared_simple_eval_estate; - /* Create the session-wide cast-info hash table if we didn't already */ - if (shared_cast_hash == NULL) + if (shared_cast_upper_hash == NULL) { - shared_cast_context = AllocSetContextCreate(TopMemoryContext, - "PLpgSQL cast info", - ALLOCSET_DEFAULT_SIZES); + shared_cast_upper_context = AllocSetContextCreate(TopMemoryContext, + "SPL cast upper info", + ALLOCSET_DEFAULT_SIZES); ctl.keysize = sizeof(plpgsql_CastHashKey); - ctl.entrysize = sizeof(plpgsql_CastHashEntry); - ctl.hcxt = shared_cast_context; - shared_cast_hash = hash_create("PLpgSQL cast cache", - 16, /* start small and extend */ - &ctl, - HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); + ctl.entrysize = sizeof(plpgsql_PrivateCastHashEntry); + ctl.hcxt = shared_cast_upper_context; + shared_cast_upper_hash = hash_create("PLpgSQL private cast cache", + 16, /* start small and extend */ + &ctl, + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); + } - estate->cast_hash = shared_cast_hash; - estate->cast_hash_context = shared_cast_context; + estate->cast_hash_context = shared_cast_upper_context; + estate->cast_hash = shared_cast_upper_hash; + } + + + /* Create the session-wide cast-info hash table if we didn't already */ + if (shared_cast_lower_hash == NULL) + { + shared_cast_lower_context = AllocSetContextCreate(TopMemoryContext, + "PLpgSQL cast info", + ALLOCSET_DEFAULT_SIZES); + ctl.keysize = sizeof(plpgsql_CastHashKey); + ctl.entrysize = sizeof(plpgsql_SessionCastHashEntry); + ctl.hcxt = shared_cast_lower_context; + shared_cast_lower_hash = hash_create("PLpgSQL cast cache", + 16, /* start small and extend */ + &ctl, + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); } + estate->shared_cast_hash = shared_cast_lower_hash; + estate->shared_cast_hash_context = shared_cast_lower_context; + /* likewise for the simple-expression resource owner */ if (simple_eval_resowner) estate->simple_eval_resowner = simple_eval_resowner; @@ -7723,7 +7749,7 @@ do_cast_value(PLpgSQL_execstate *estate, Oid valtype, int32 valtypmod, Oid reqtype, int32 reqtypmod) { - plpgsql_CastHashEntry *cast_entry; + plpgsql_PrivateCastHashEntry *cast_entry; cast_entry = get_cast_hashentry(estate, valtype, valtypmod, @@ -7761,13 +7787,15 @@ do_cast_value(PLpgSQL_execstate *estate, * true while executing it. * ---------- */ -static plpgsql_CastHashEntry * +static plpgsql_PrivateCastHashEntry * get_cast_hashentry(PLpgSQL_execstate *estate, Oid srctype, int32 srctypmod, Oid dsttype, int32 dsttypmod) { plpgsql_CastHashKey cast_key; - plpgsql_CastHashEntry *cast_entry; + plpgsql_PrivateCastHashEntry *private_cast_entry; + plpgsql_SessionCastHashEntry *cast_entry; + bool found; LocalTransactionId curlxid; MemoryContext oldcontext; @@ -7777,9 +7805,9 @@ get_cast_hashentry(PLpgSQL_execstate *estate, cast_key.dsttype = dsttype; cast_key.srctypmod = srctypmod; cast_key.dsttypmod = dsttypmod; - cast_entry = (plpgsql_CastHashEntry *) hash_search(estate->cast_hash, - &cast_key, - HASH_ENTER, &found); + cast_entry = (plpgsql_SessionCastHashEntry *) hash_search(estate->shared_cast_hash, + &cast_key, + HASH_ENTER, &found); if (!found) /* initialize if new entry */ cast_entry->cast_cexpr = NULL; @@ -7884,10 +7912,6 @@ get_cast_hashentry(PLpgSQL_execstate *estate, /* Now we can fill in the hashtable entry. */ cast_entry->cast_cexpr = cast_cexpr; cast_entry->cast_expr = (Expr *) cast_expr; - cast_entry->cast_exprstate = NULL; - cast_entry->cast_in_use = false; - cast_entry->cast_lxid = InvalidLocalTransactionId; - MemoryContextSwitchTo(oldcontext); } @@ -7895,6 +7919,18 @@ get_cast_hashentry(PLpgSQL_execstate *estate, if (cast_entry->cast_expr == NULL) return NULL; + + private_cast_entry = (plpgsql_PrivateCastHashEntry *) hash_search(estate->cast_hash, + &cast_key, + HASH_ENTER, &found); + + if (!found) + { + private_cast_entry->cast_exprstate = NULL; + private_cast_entry->cast_in_use = false; + private_cast_entry->cast_lxid = InvalidLocalTransactionId; + } + /* * Prepare the expression for execution, if it's not been done already in * the current transaction; also, if it's marked busy in the current @@ -7908,16 +7944,16 @@ get_cast_hashentry(PLpgSQL_execstate *estate, * cast hash tables to go with them.) */ curlxid = MyProc->lxid; - if (cast_entry->cast_lxid != curlxid || cast_entry->cast_in_use) + if (private_cast_entry->cast_lxid != curlxid || private_cast_entry->cast_in_use) { oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt); - cast_entry->cast_exprstate = ExecInitExpr(cast_entry->cast_expr, NULL); - cast_entry->cast_in_use = false; - cast_entry->cast_lxid = curlxid; + private_cast_entry->cast_exprstate = ExecInitExpr(cast_entry->cast_expr, NULL); + private_cast_entry->cast_in_use = false; + private_cast_entry->cast_lxid = curlxid; MemoryContextSwitchTo(oldcontext); } - return cast_entry; + return private_cast_entry; } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index c40471b..765bb9e 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -1074,10 +1074,14 @@ typedef struct PLpgSQL_execstate /* if running nonatomic procedure or DO block, resowner to use for CALL */ ResourceOwner procedure_resowner; - /* lookup table to use for executing type casts */ + /* lookup local table to use for executing type casts */ HTAB *cast_hash; MemoryContext cast_hash_context; + /* look Session table to use for executing type casts */ + HTAB *shared_cast_hash; + MemoryContext shared_cast_hash_context; + /* memory context for statement-lifespan temporary values */ MemoryContext stmt_mcontext; /* current stmt context, or NULL if none */ MemoryContext stmt_mcontext_parent; /* parent of current context */