0 总结
(可以最后看)
- PLpgSQL_execstate中包含的两个结构:
EState *simple_eval_estate
、ExprContext *eval_econtext
- 丢给SQL引擎执行时一般需要ExprContext就够了,但是ExprContext会依赖EState结构才能创建出来,所以PL在执行时,plpgsql_exec_function函数需要传入EState结构,方便后面ExprContext的创建。
- PL中使用的ExprContext,创建后,会自动压入simple_econtext_stack堆栈。因为PL中的异常处理会自动启动子事务,为了让表达式计算申请的资源能和子事务一块释放(避免污染顶层事务的ExprContext),需要将ExprContext与子事务关联起来:
- 所以如果没有发生异常,那么eval_econtext会跟着ReleaseCurrentSubTransaction在子事务提交中释放。
- 如果发生异常了,那么eval_econtext会跟着RollbackAndReleaseCurrentSubTransaction在子事务回滚中释放。
1 PL运行时信息:PLpgSQL_execstate
PostgreSQL的PLpg/SQL中任何语句的运行,都需要记录运行时的状态信息。在SQL层的执行器中运行时状态使用EState
记录,在PL中状态信息使用PLpgSQL_execstate
结构记录。
/*
* Runtime execution data
*/
typedef struct PLpgSQL_execstate
{
PLpgSQL_function *func; /* function being executed */
...
Datum retval;
bool retisnull;
Oid rettype; // 返回值处理
...
Oid fn_rettype;
bool retistuple;
bool retisset;
bool readonly_func;
bool atomic; // 函数是原子的,过程是非原子的,非原子的能执行commit/rollback
// return next of 缓存
Tuplestorestate *tuple_store;
TupleDesc tuple_store_desc;
...
int ndatums; // 变量数组个数
PLpgSQL_datum **datums; // 变量数组
...
EState *simple_eval_estate; // 为什么这里需要有EState?
ResourceOwner simple_eval_resowner;
...
SPITupleTable *eval_tuptable;
uint64 eval_processed;
ExprContext *eval_econtext; // 为什么这里需要有ExprContext?
...
} PLpgSQL_execstate;
在上述PLpgSQL_execstate结构中,为什么会出现EState呢,simple_eval_estate的作用是什么?
答案:表达式计算。
2 PL表达式计算
在PL中,存在大量语法需要调用主解析器进行计算,例如:
sql">CREATE or replace function tp14_outter(
a in integer ,
b out integer,
c out integer)
RETURNS int
LANGUAGE plpgsql
AS $$
DECLARE
rr int;
b int;
c int;
BEGIN
b := 1 + 1;
c := b / 2;
rr := b + c + other_func(1,2,3);
return rr;
END;
$$;
目前PL引擎会把assign语句的右值封装成字符串的形式保存下来,等到运行时会发送给SQL引擎计算结果。
例如上面的c := b / 2
:
- 在PL编译后,会记录字符串
select b / 2
。 - 在PL运行时,会调用SQL引擎,将字符串
select b / 2
通过SPI发过去,走一遍完成的语法、语义分析,优化器,执行器(表达式计算模块),最终拿到结果。(主解析器应该不认识b
,怎么计算呢?答案:回调钩子函数拿值)。
那么调用SQL引擎的表达式计算模块,一定需要SQL引擎的运行时结构EState。
所以PLpgSQL_execstate中会包含EState *simple_eval_estate;
、ExprContext *eval_econtext;
结构。
3 PL表达式运行时内存结构ExprContext
PLpgSQL_execstate中包含的两个结构:
- EState *simple_eval_estate
- ExprContext *eval_econtext
实际上呢,丢给SQL引擎执行时一般需要ExprContext就够了,但是ExprContext会依赖EState结构才能创建出来,所以PL在执行时,plpgsql_exec_function函数需要传入EState结构,方便后面ExprContext的创建。
- PL中的函数会使用共享的EState结构用于创建ExprContext:shared_simple_eval_estate
- PL中的匿名块会使用私有的EState结构用于创建ExprContext
PL中使用的ExprContext,创建后,会自动压入simple_econtext_stack堆栈,为什么呢?
因为PL中的异常处理会自动启动子事务,为了让表达式计算申请的资源能和子事务一块释放,需要将ExprContext与子事务关联起来:
一旦子事务释放,在回调函数plpgsql_subxact_cb中,会释放simple_econtext_stack堆栈中所有和该子事务相关的ExprContext。
void
plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg)
{
if (event == SUBXACT_EVENT_COMMIT_SUB || event == SUBXACT_EVENT_ABORT_SUB)
{
while (simple_econtext_stack != NULL &&
simple_econtext_stack->xact_subxid == mySubid)
{
SimpleEcontextStackEntry *next;
FreeExprContext(simple_econtext_stack->stack_econtext,
(event == SUBXACT_EVENT_COMMIT_SUB));
next = simple_econtext_stack->next;
pfree(simple_econtext_stack);
simple_econtext_stack = next;
}
}
}
所以如果没有发生异常,那么eval_econtext会跟着ReleaseCurrentSubTransaction在子事务提交中释放。
if (block->exceptions)
ExprContext *old_eval_econtext = estate->eval_econtext;
BeginInternalSubTransaction(NULL);
PG_TRY();
{
plpgsql_create_econtext(estate);
rc = exec_stmts(estate, block->body);
ReleaseCurrentSubTransaction(); <<<<<<--------
estate->eval_econtext = old_eval_econtext;
}
如果发生异常了,那么eval_econtext会跟着RollbackAndReleaseCurrentSubTransaction在子事务回滚中释放。
if (block->exceptions)
ExprContext *old_eval_econtext = estate->eval_econtext;
BeginInternalSubTransaction(NULL);
PG_TRY();
{
plpgsql_create_econtext(estate);
rc = exec_stmts(estate, block->body);
ReleaseCurrentSubTransaction(); <<<<<<--------
estate->eval_econtext = old_eval_econtext;
}
PG_CATCH();
{
RollbackAndReleaseCurrentSubTransaction();
estate->eval_econtext = old_eval_econtext;
}
4 相关全局变量、函数
typedef struct SimpleEcontextStackEntry
{
ExprContext *stack_econtext; /* a stacked econtext */
SubTransactionId xact_subxid; /* ID for current subxact */
struct SimpleEcontextStackEntry *next; /* next stack entry up */
} SimpleEcontextStackEntry;
static EState *shared_simple_eval_estate = NULL;
static SimpleEcontextStackEntry *simple_econtext_stack = NULL;
全局变量:
- simple_econtext_stack:ExprContext堆栈,每个元素对应一个子事务,需要再子事务创建后主动申请出来,子事务释放后会跟随子事务清理被释放。
- shared_simple_eval_estate:ExprContext依赖的EState结构,SQL引擎的运行时结构非常重要,在PL中主要用于创建ExprContext。
函数: