PostgreSQL中ExecProcNode和ExecProcNodeFirst函数的实现逻辑是什么
这篇文章主要讲解了“PostgreSQL中ExecProcNode和ExecProcNodeFirst函数的实现逻辑是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL中ExecProcNode和ExecProcNodeFirst函数的实现逻辑是什么”吧!
创新互联网站建设提供从项目策划、软件开发,软件安全维护、网站优化(SEO)、网站分析、效果评估等整套的建站服务,主营业务为成都网站设计、网站建设,成都app软件开发以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。创新互联深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
一、基础信息
ExecProcNode/ExecProcNodeFirst函数使用的数据结构、宏定义以及依赖的函数等。
数据结构/宏定义
1、ExecProcNodeMtd
ExecProcNodeMtd是一个函数指针类型,指向的函数输入参数是PlanState结构体指针,输出参数是TupleTableSlot 结构体指针
/* ---------------- * ExecProcNodeMtd * * This is the method called by ExecProcNode to return the next tuple * from an executor node. It returns NULL, or an empty TupleTableSlot, * if no more tuples are available. * ---------------- */ typedef TupleTableSlot *(*ExecProcNodeMtd) (struct PlanState *pstate);
依赖的函数
1、check_stack_depth
//检查stack的深度,如超出系统限制,则主动报错 /* * check_stack_depth/stack_is_too_deep: check for excessively deep recursion * * This should be called someplace in any recursive routine that might possibly * recurse deep enough to overflow the stack. Most Unixen treat stack * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves * before hitting the hardware limit. * * check_stack_depth() just throws an error summarily. stack_is_too_deep() * can be used by code that wants to handle the error condition itself. */ void check_stack_depth(void) { if (stack_is_too_deep()) { ereport(ERROR, (errcode(ERRCODE_STATEMENT_TOO_COMPLEX), errmsg("stack depth limit exceeded"), errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), " "after ensuring the platform's stack depth limit is adequate.", max_stack_depth))); } } bool stack_is_too_deep(void) { char stack_top_loc; long stack_depth; /* * Compute distance from reference point to my local variables */ stack_depth = (long) (stack_base_ptr - &stack_top_loc); /* * Take abs value, since stacks grow up on some machines, down on others */ if (stack_depth < 0) stack_depth = -stack_depth; /* * Trouble? * * The test on stack_base_ptr prevents us from erroring out if called * during process setup or in a non-backend process. Logically it should * be done first, but putting it here avoids wasting cycles during normal * cases. */ if (stack_depth > max_stack_depth_bytes && stack_base_ptr != NULL) return true; /* * On IA64 there is a separate "register" stack that requires its own * independent check. For this, we have to measure the change in the * "BSP" pointer from PostgresMain to here. Logic is just as above, * except that we know IA64's register stack grows up. * * Note we assume that the same max_stack_depth applies to both stacks. */ #if defined(__ia64__) || defined(__ia64) stack_depth = (long) (ia64_get_bsp() - register_stack_base_ptr); if (stack_depth > max_stack_depth_bytes && register_stack_base_ptr != NULL) return true; #endif /* IA64 */ return false; }
2、ExecProcNodeInstr
/* * ExecProcNode wrapper that performs instrumentation calls. By keeping * this a separate function, we avoid overhead in the normal case where * no instrumentation is wanted. */ static TupleTableSlot * ExecProcNodeInstr(PlanState *node) { TupleTableSlot *result; InstrStartNode(node->instrument); result = node->ExecProcNodeReal(node); InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0); return result; }
二、源码解读
1、ExecProcNode
//外部调用者可通过改变node实现遍历 /* ---------------------------------------------------------------- * ExecProcNode * * Execute the given node to return a(nother) tuple. * ---------------------------------------------------------------- */ #ifndef FRONTEND static inline TupleTableSlot * ExecProcNode(PlanState *node) { if (node->chgParam != NULL) /* something changed? */ ExecReScan(node); /* let ReScan handle this */ return node->ExecProcNode(node); } #endif
2、ExecProcNodeFirst
/* * ExecProcNode wrapper that performs some one-time checks, before calling * the relevant node method (possibly via an instrumentation wrapper). */ /* 输入: node-PlanState指针 输出: 存储Tuple的Slot */ static TupleTableSlot * ExecProcNodeFirst(PlanState *node) { /* * Perform stack depth check during the first execution of the node. We * only do so the first time round because it turns out to not be cheap on * some common architectures (eg. x86). This relies on the assumption * that ExecProcNode calls for a given plan node will always be made at * roughly the same stack depth. */ //检查Stack是否超深 check_stack_depth(); /* * If instrumentation is required, change the wrapper to one that just * does instrumentation. Otherwise we can dispense with all wrappers and * have ExecProcNode() directly call the relevant function from now on. */ //如果instrument(TODO) if (node->instrument) node->ExecProcNode = ExecProcNodeInstr; else node->ExecProcNode = node->ExecProcNodeReal; //执行该Node的处理过程 return node->ExecProcNode(node); }
三、跟踪分析
插入测试数据:
testdb=# -- 获取pid testdb=# select pg_backend_pid(); pg_backend_pid ---------------- 2835 (1 row) testdb=# -- 插入1行 testdb=# insert into t_insert values(14,'ExecProcNodeFirst','ExecProcNodeFirst','ExecProcNodeFirst'); (挂起)
启动gdb分析:
[root@localhost ~]# gdb -p 2835 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7 Copyright (C) 2013 Free Software Foundation, Inc. ... (gdb) b ExecProcNodeFirst Breakpoint 1 at 0x69a797: file execProcnode.c, line 433. (gdb) c Continuing. Breakpoint 1, ExecProcNodeFirst (node=0x2cca790) at execProcnode.c:433 433 check_stack_depth(); #查看输入参数 (gdb) p *node $1 = {type = T_ModifyTableState, plan = 0x2c1d028, state = 0x2cca440, ExecProcNode = 0x69a78b, ExecProcNodeReal = 0x6c2485 , instrument = 0x0, worker_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x2ccb6a0, ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x0} #ExecProcNode 实际对应的函数是ExecProcNodeFirst #ExecProcNodeReal 实际对应的函数是ExecModifyTable(上一章节已粗略解析) (gdb) next 440 if (node->instrument) (gdb) #实际调用ExecModifyTable函数(这个函数由更高层的调用函数植入) 443 node->ExecProcNode = node->ExecProcNodeReal; (gdb) 445 return node->ExecProcNode(node); (gdb) next #第二次调用(TODO) Breakpoint 1, ExecProcNodeFirst (node=0x2ccac80) at execProcnode.c:433 433 check_stack_depth(); (gdb) next 440 if (node->instrument) (gdb) next 443 node->ExecProcNode = node->ExecProcNodeReal; (gdb) next 445 return node->ExecProcNode(node); (gdb) next 446 } (gdb) next ExecProcNode (node=0x2ccac80) at ../../../src/include/executor/executor.h:238 238 } #第二次调用的参数 (gdb) p *node $2 = {type = T_ResultState, plan = 0x2cd0488, state = 0x2cca440, ExecProcNode = 0x6c5094 , ExecProcNodeReal = 0x6c5094 , instrument = 0x0, worker_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x2ccad90, ps_ExprContext = 0x2ccab30, ps_ProjInfo = 0x2ccabc0, scandesc = 0x0} #ExecProcNode对应的实际函数是ExecResult (gdb)
感谢各位的阅读,以上就是“PostgreSQL中ExecProcNode和ExecProcNodeFirst函数的实现逻辑是什么”的内容了,经过本文的学习后,相信大家对PostgreSQL中ExecProcNode和ExecProcNodeFirst函数的实现逻辑是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!
文章题目:PostgreSQL中ExecProcNode和ExecProcNodeFirst函数的实现逻辑是什么
文章网址:http://myzitong.com/article/iijsso.html