怎么使用PostgreSQ中的ExecMaterial函数
发表于:2025-01-22 作者:千家信息网编辑
千家信息网最后更新 2025年01月22日,本篇内容介绍了"怎么使用PostgreSQ中的ExecMaterial函数"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细
千家信息网最后更新 2025年01月22日怎么使用PostgreSQ中的ExecMaterial函数
本篇内容介绍了"怎么使用PostgreSQ中的ExecMaterial函数"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
一、数据结构
SubPlanState
子计划运行期状态
/* ---------------- * SubPlanState node * ---------------- */typedef struct SubPlanState{ NodeTag type; SubPlan *subplan; /* expression plan node */ struct PlanState *planstate; /* subselect plan's state tree */ struct PlanState *parent; /* parent plan node's state tree */ ExprState *testexpr; /* 组合表达式状态;state of combining expression */ List *args; /* 参数表达式状态;states of argument _expression(s) */ HeapTuple curTuple; /* subplan最近的元组;copy of most recent tuple from subplan */ Datum curArray; /* most recent array from ARRAY() subplan */ /* these are used when hashing the subselect's output: */ TupleDesc descRight; /* 投影后的子查询描述符;subselect desc after projection */ ProjectionInfo *projLeft; /* for projecting lefthand exprs */ ProjectionInfo *projRight; /* for projecting subselect output */ TupleHashTable hashtable; /* hash table for no-nulls subselect rows */ TupleHashTable hashnulls; /* hash table for rows with null(s) */ bool havehashrows; /* true if hashtable is not empty */ bool havenullrows; /* true if hashnulls is not empty */ MemoryContext hashtablecxt; /* memory context containing hash tables */ MemoryContext hashtempcxt; /* temp memory context for hash tables */ ExprContext *innerecontext; /* econtext for computing inner tuples */ AttrNumber *keyColIdx; /* control data for hash tables */ Oid *tab_eq_funcoids; /* equality func oids for table * datatype(s) */ Oid *tab_collations; /* collations for hash and comparison */ FmgrInfo *tab_hash_funcs; /* hash functions for table datatype(s) */ FmgrInfo *tab_eq_funcs; /* equality functions for table datatype(s) */ FmgrInfo *lhs_hash_funcs; /* hash functions for lefthand datatype(s) */ FmgrInfo *cur_eq_funcs; /* equality functions for LHS vs. table */ ExprState *cur_eq_comp; /* equality comparator for LHS vs. table */} SubPlanState;
SubPlan
子查询计划
/* * SubPlan - executable expression node for a subplan (sub-SELECT) * * The planner replaces SubLink nodes in expression trees with SubPlan * nodes after it has finished planning the subquery. SubPlan references * a sub-plantree stored in the subplans list of the toplevel PlannedStmt. * (We avoid a direct link to make it easier to copy expression trees * without causing multiple processing of the subplan.) * 查询规划器在完成子查询的规划后使用SubPlan节点替换表达式树中的SubLink节点。 * SubPlan引用了存储在高层PlannedStmt中的subplans链表中的sub-plantree。 * (避免使用直接链接,从而使得拷贝表达式树相对比较简单) * * In an ordinary subplan, testexpr points to an executable expression * (OpExpr, an AND/OR tree of OpExprs, or RowCompareExpr) for the combining * operator(s); the left-hand arguments are the original lefthand expressions, * and the right-hand arguments are PARAM_EXEC Param nodes representing the * outputs of the sub-select. (NOTE: runtime coercion functions may be * inserted as well.) This is just the same expression tree as testexpr in * the original SubLink node, but the PARAM_SUBLINK nodes are replaced by * suitably numbered PARAM_EXEC nodes. * 常规情况下,testexpr指向用于组合操作的可执行表达式(OpExpr、OpExprs的AND/OR树或者RowCompareExpr); * 左参数是原始的左表达式,右参数是PARAM_EXEC参数节点用以表示子查询的输出。 * 与原始SubLink节点的testexpr具有相同的表达式树,但PARAM_SUBLINK节点则使用合适的已编号PARAM_EXEC节点替代。 * * If the sub-select becomes an initplan rather than a subplan, the executable * expression is part of the outer plan's expression tree (and the SubPlan * node itself is not, but rather is found in the outer plan's initPlan * list). In this case testexpr is NULL to avoid duplication. * 如果子查询成了initplan而不是subplan,可执行的表达式是外层plan表达式树的一部分。 * 这种情况下,testexpr为NULL以避免重复。 * * The planner also derives lists of the values that need to be passed into * and out of the subplan. Input values are represented as a list "args" of * expressions to be evaluated in the outer-query context (currently these * args are always just Vars, but in principle they could be any expression). * The values are assigned to the global PARAM_EXEC params indexed by parParam * (the parParam and args lists must have the same ordering). setParam is a * list of the PARAM_EXEC params that are computed by the sub-select, if it * is an initplan; they are listed in order by sub-select output column * position. (parParam and setParam are integer Lists, not Bitmapsets, * because their ordering is significant.) * 规划器还派生了需要传入和传出子计划的值的链表。 * 输入值标识位表达式的"args"链表,在外层查询上下文中进行解析。 * (这些args通常是Vars,但原则上它们可以是任意表达式) * 这些值以parParam为索引给全局PARAM_EXEC参数赋值。 * setParam是PARAM_EXEC参数链表,通过子查询(如为initplan)计算所得。 * 它们按子查询输出列的位置进行排序组织为链表形式。 * (parParam和setParam是整型链表,而不是Bitmapsets链表) * * Also, the planner computes startup and per-call costs for use of the * SubPlan. Note that these include the cost of the subquery proper, * evaluation of the testexpr if any, and any hashtable management overhead. * 同时,规划器计算SubPlan启动和每次调用的成本。注意:包括子查询正常解析testexpr的成本以及哈希表管理成本。 */typedef struct SubPlan{ Expr xpr;//表达式 /* Fields copied from original SubLink: */ //从SubLink中拷贝而来 SubLinkType subLinkType; /* see above */ /* The combining operators, transformed to an executable expression: */ //组合操作符,转换为可执行的表达式 Node *testexpr; /* OpExpr or RowCompareExpr expression tree */ List *paramIds; /* 参数IDs;IDs of Params embedded in the above */ /* Identification of the Plan tree to use: */ //Plan tree标识 int plan_id; /* Index (from 1) in PlannedStmt.subplans */ /* Identification of the SubPlan for EXPLAIN and debugging purposes: */ //EXPLAIN和debug目的的SubPlan标识 char *plan_name; /* A name assigned during planning */ /* Extra data useful for determining subplan's output type: */ //用于确定subplan输出类型的额外信息 Oid firstColType; /* subplan结果的第一个列类型;Type of first column of subplan result */ int32 firstColTypmod; /* 第一列的Typmod;Typmod of first column of subplan result */ Oid firstColCollation; /* 第一列的Collation;Collation of first column of subplan * result */ /* Information about execution strategy: */ //执行阶段的相关信息 bool useHashTable; /* 是否使用哈希表存储子查询输出;true to store subselect output in a hash * table (implies we are doing "IN") */ bool unknownEqFalse; /* 如OK为T,如为未知则为F;快速处理null值;true if it's okay to return FALSE when the * spec result is UNKNOWN; this allows much * simpler handling of null values */ bool parallel_safe; /* 是否并行安全?is the subplan parallel-safe? */ /* Note: parallel_safe does not consider contents of testexpr or args */ /* Information for passing params into and out of the subselect: */ //用于给子查询传入和传出参数的信息 /* setParam and parParam are lists of integers (param IDs) */ //setParam和parParam是整型链表(param IDs) List *setParam; /* initplan subqueries have to set these * Params for parent plan */ List *parParam; /* indices of input Params from parent plan */ List *args; /* 以parParam值进行传递的表达式;exprs to pass as parParam values */ /* Estimated execution costs: */ //估算执行成本 Cost startup_cost; /* one-time setup cost */ Cost per_call_cost; /* cost for each subplan evaluation */} SubPlan;
SubLinkType
SubLink类型
/* * SubLink * * A SubLink represents a subselect appearing in an expression, and in some * cases also the combining operator(s) just above it. The subLinkType * indicates the form of the expression represented: * EXISTS_SUBLINK EXISTS(SELECT ...) * ALL_SUBLINK (lefthand) op ALL (SELECT ...) * ANY_SUBLINK (lefthand) op ANY (SELECT ...) * ROWCOMPARE_SUBLINK (lefthand) op (SELECT ...) * EXPR_SUBLINK (SELECT with single targetlist item ...) * MULTIEXPR_SUBLINK (SELECT with multiple targetlist items ...) * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...) * CTE_SUBLINK WITH query (never actually part of an expression) * For ALL, ANY, and ROWCOMPARE, the lefthand is a list of expressions of the * same length as the subselect's targetlist. ROWCOMPARE will *always* have * a list with more than one entry; if the subselect has just one target * then the parser will create an EXPR_SUBLINK instead (and any operator * above the subselect will be represented separately). * ROWCOMPARE, EXPR, and MULTIEXPR require the subselect to deliver at most * one row (if it returns no rows, the result is NULL). * ALL, ANY, and ROWCOMPARE require the combining operators to deliver boolean * results. ALL and ANY combine the per-row results using AND and OR * semantics respectively. * ARRAY requires just one target column, and creates an array of the target * column's type using any number of rows resulting from the subselect. * * SubLink is classed as an Expr node, but it is not actually executable; * it must be replaced in the expression tree by a SubPlan node during * planning. * * NOTE: in the raw output of gram.y, testexpr contains just the raw form * of the lefthand _expression (if any), and operName is the String name of * the combining operator. Also, subselect is a raw parsetree. During parse * analysis, the parser transforms testexpr into a complete boolean expression * that compares the lefthand value(s) to PARAM_SUBLINK nodes representing the * output columns of the subselect. And subselect is transformed to a Query. * This is the representation seen in saved rules and in the rewriter. * * In EXISTS, EXPR, MULTIEXPR, and ARRAY SubLinks, testexpr and operName * are unused and are always null. * * subLinkId is currently used only for MULTIEXPR SubLinks, and is zero in * other SubLinks. This number identifies different multiple-assignment * subqueries within an UPDATE statement's SET list. It is unique only * within a particular targetlist. The output column(s) of the MULTIEXPR * are referenced by PARAM_MULTIEXPR Params appearing elsewhere in the tlist. * * The CTE_SUBLINK case never occurs in actual SubLink nodes, but it is used * in SubPlans generated for WITH subqueries. */typedef enum SubLinkType{ EXISTS_SUBLINK, ALL_SUBLINK, ANY_SUBLINK, ROWCOMPARE_SUBLINK, EXPR_SUBLINK, MULTIEXPR_SUBLINK, ARRAY_SUBLINK, CTE_SUBLINK /* for SubPlans only */} SubLinkType;
SubLink
SubLink结构体
typedef struct SubLink{ Expr xpr; SubLinkType subLinkType; /* see above */ int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */ Node *testexpr; /* outer-query test for ALL/ANY/ROWCOMPARE */ List *operName; /* originally specified operator name */ Node *subselect; /* subselect as Query* or raw parsetree */ int location; /* token location, or -1 if unknown */} SubLink;
MaterialState
Material状态
/* ---------------- * MaterialState information * * materialize nodes are used to materialize the results * of a subplan into a temporary file. * materialize节点用于物化subplan的结果为临时文件。 * * ss.ss_ScanTupleSlot refers to output of underlying plan. * ss.ss_ScanTupleSlot指向underlyling plan的输出(subplan) * ---------------- */typedef struct MaterialState{ ScanState ss; /* its first field is NodeTag */ int eflags; /* 传递给tuplestore的capability标记;capability flags to pass to tuplestore */ bool eof_underlying; /* 已经到达underlying plan的末尾?reached end of underlying plan? */ Tuplestorestate *tuplestorestate;} MaterialState;
二、源码解读
/* ---------------------------------------------------------------- * ExecMaterial * * As long as we are at the end of the data collected in the tuplestore, * we collect one new row from the subplan on each call, and stash it * aside in the tuplestore before returning it. The tuplestore is * only read if we are asked to scan backwards, rescan, or mark/restore. * 只要在tuplestore中数据收集结束时,就会在每次调用时从subplan中收集一条新行, * 并在返回之前将其保存在tuplestore中。 * 只要在往后扫描、重新扫描或标记/恢复时tuplestore才会读取。 * * ---------------------------------------------------------------- */static TupleTableSlot * /* 从subplan中返回的结果;result tuple from subplan */ExecMaterial(PlanState *pstate){ MaterialState *node = castNode(MaterialState, pstate);//物化节点 EState *estate;//运行期状态 ScanDirection dir;//扫描方向 bool forward;//是否往前扫描 Tuplestorestate *tuplestorestate;//Tuplestorestate结构体指针 bool eof_tuplestore;//是否完成? TupleTableSlot *slot;//存储元组的slot CHECK_FOR_INTERRUPTS(); /* * get state info from node * 从物化节点中获取相关信息 */ estate = node->ss.ps.state; dir = estate->es_direction;//方向 forward = ScanDirectionIsForward(dir);//是否往前扫描 tuplestorestate = node->tuplestorestate; /* * If first time through, and we need a tuplestore, initialize it. * 第一次,需要tuplestore并初始化 */ if (tuplestorestate == NULL && node->eflags != 0) { tuplestorestate = tuplestore_begin_heap(true, false, work_mem); tuplestore_set_eflags(tuplestorestate, node->eflags); if (node->eflags & EXEC_FLAG_MARK) { /* * Allocate a second read pointer to serve as the mark. We know it * must have index 1, so needn't store that. * 分配用于mark的读指针 */ int ptrno PG_USED_FOR_ASSERTS_ONLY; ptrno = tuplestore_alloc_read_pointer(tuplestorestate, node->eflags); Assert(ptrno == 1); } node->tuplestorestate = tuplestorestate; } /* * If we are not at the end of the tuplestore, or are going backwards, try * to fetch a tuple from tuplestore. * 如果不在tuplestore的末尾或者正在往后扫描,尝试从tuplestore中提取一个元组 */ eof_tuplestore = (tuplestorestate == NULL) || tuplestore_ateof(tuplestorestate); if (!forward && eof_tuplestore) { if (!node->eof_underlying) { /* * When reversing direction at tuplestore EOF, the first * gettupleslot call will fetch the last-added tuple; but we want * to return the one before that, if possible. So do an extra * fetch. * 在EOF处反转方向,第一次的gettupleslot调用会提取最后添加的元组; * 但如可能,希望返回在此之前的元组,执行额外的提取操作。 */ if (!tuplestore_advance(tuplestorestate, forward)) return NULL; /* the tuplestore must be empty */ } eof_tuplestore = false; } /* * If we can fetch another tuple from the tuplestore, return it. * 如能从tuplestore中提取另外一个tuple,返回 */ slot = node->ss.ps.ps_ResultTupleSlot; if (!eof_tuplestore) { if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot)) return slot; if (forward) eof_tuplestore = true; } /* * If necessary, try to fetch another row from the subplan. * 如需要(tuplestore末尾),尝试从subplan中提取另外一行 * * Note: the eof_underlying state variable exists to short-circuit further * subplan calls. It's not optional, unfortunately, because some plan * node types are not robust about being called again when they've already * returned NULL. */ if (eof_tuplestore && !node->eof_underlying) { PlanState *outerNode; TupleTableSlot *outerslot; /* * We can only get here with forward==true, so no need to worry about * which direction the subplan will go. */ outerNode = outerPlanState(node); outerslot = ExecProcNode(outerNode); if (TupIsNull(outerslot)) { node->eof_underlying = true; return NULL; } /* * Append a copy of the returned tuple to tuplestore. NOTE: because * the tuplestore is certainly in EOF state, its read position will * move forward over the added tuple. This is what we want. * 追加返回的元组到tuplestore中。 * 注意:因为tuplestore当前处于EOF状态,读取的位置会前移至已添加的tuple前面,这是我们希望看到的。 */ if (tuplestorestate) tuplestore_puttupleslot(tuplestorestate, outerslot); ExecCopySlot(slot, outerslot); return slot; } /* * Nothing left ... */ return ExecClearTuple(slot);}
三、跟踪分析
执行SQL:
[pg12@localhost ~]$ psql -d testdbTiming is on.Expanded display is used automatically.psql (12.0)Type "help" for help.[local]:5432 pg12@testdb=# [local]:5432 pg12@testdb=# select * from tbl; id | value ----+------- 1 | 2(1 row)Time: 2.678 ms[local]:5432 pg12@testdb=# select count(*) from t_big_null; count ---------- 10000001(1 row)Time: 679.972 ms[local]:5432 pg12@testdb=# analyze tbl;ANALYZETime: 64.442 ms[local]:5432 pg12@testdb=# analyze t_big_null;ANALYZETime: 434.702 ms[local]:5432 pg12@testdb=# [local]:5432 pg12@testdb=# select pg_backend_pid(); pg_backend_pid ---------------- 18758(1 row)Time: 1.990 ms[local]:5432 pg12@testdb=# select * from tbl a where a.id not in (select b.id from t_big_null b);
启动gdb跟踪
(gdb) b ExecMaterialBreakpoint 1 at 0x720edb: file nodeMaterial.c, line 41.(gdb) cContinuing.Breakpoint 1, ExecMaterial (pstate=0x1230128) at nodeMaterial.c:4141 MaterialState *node = castNode(MaterialState, pstate);(gdb)
输入参数
(gdb) p *pstate$4 = {type = T_MaterialState, plan = 0x1211858, state = 0x122fe88, ExecProcNode = 0x720ecf, ExecProcNodeReal = 0x720ecf , instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0, lefttree = 0x1230240, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleDesc = 0x1230660, ps_ResultTupleSlot = 0x1230778, ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x1230548, scanops = 0xc3e720 , outerops = 0x0, innerops = 0x0, resultops = 0xc3e720 , scanopsfixed = true, outeropsfixed = false, inneropsfixed = false, resultopsfixed = true, scanopsset = true, outeropsset = false, inneropsset = false, resultopsset = true}(gdb)
MaterialState结构体指针数据
(gdb) p *node$1 = {ss = {ps = {type = T_MaterialState, plan = 0x1211858, state = 0x122fe88, ExecProcNode = 0x720ecf, ExecProcNodeReal = 0x720ecf , instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0, lefttree = 0x1230240, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleDesc = 0x1230660, ps_ResultTupleSlot = 0x1230778, ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x1230548, scanops = 0xc3e720 , outerops = 0x0, innerops = 0x0, resultops = 0xc3e720 , scanopsfixed = true, outeropsfixed = false, inneropsfixed = false, resultopsfixed = true, scanopsset = true, outeropsset = false, inneropsset = false, resultopsset = true}, ss_currentRelation = 0x0, ss_currentScanDesc = 0x0, ss_ScanTupleSlot = 0x1230838}, eflags = 2, eof_underlying = false, tuplestorestate = 0x0}(gdb) p *node->ss->ps->plan$2 = {type = T_Material, startup_cost = 0, total_cost = 233310.685, plan_rows = 9999979, plan_width = 4, parallel_aware = false, parallel_safe = true, plan_node_id = 1, targetlist = 0x1257600, qual = 0x0, lefttree = 0x1210f58, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}(gdb)
运行期信息和方向
(gdb) p *estate$5 = {type = T_EState, es_direction = ForwardScanDirection, es_snapshot = 0x1164d50, es_crosscheck_snapshot = 0x0, es_range_table = 0x1257328, es_range_table_array = 0x12300d8, es_range_table_size = 2, es_relations = 0x1230100, es_rowmarks = 0x0, es_plannedstmt = 0x1257748, es_sourceText = 0x113cd88 "select * from tbl a where a.id not in (select b.id from t_big_null b);", es_junkFilter = 0x0, es_output_cid = 0, es_result_relations = 0x0, es_num_result_relations = 0, es_result_relation_info = 0x0, es_root_result_relations = 0x0, es_num_root_result_relations = 0, es_partition_directory = 0x0, es_tuple_routing_result_relations = 0x0, es_trig_target_relations = 0x0, es_param_list_info = 0x0, es_param_exec_vals = 0x12300a0, es_queryEnv = 0x0, es_query_cxt = 0x122fd70, es_tupleTable = 0x1230510, es_processed = 0, es_top_eflags = 16, es_instrument = 0, es_finished = false, es_exprcontexts = 0x1230418, es_subplanstates = 0x1230920, es_auxmodifytables = 0x0, es_per_tuple_exprcontext = 0x0, es_epq_active = 0x0, es_use_parallel_mode = false, es_query_dsa = 0x0, es_jit_flags = 25, es_jit = 0x0, es_jit_worker_instr = 0x0}(gdb) p dir$6 = ForwardScanDirection(gdb)
初始化tuplestore
(gdb) n64 tuplestorestate = tuplestore_begin_heap(true, false, work_mem);(gdb) n65 tuplestore_set_eflags(tuplestorestate, node->eflags);(gdb) p *tuplestorestate$7 = {status = TSS_INMEM, eflags = 6, backward = false, interXact = false, truncated = false, availMem = 4177896, allowedMem = 4194304, tuples = 0, myfile = 0x0, context = 0x122fd70, resowner = 0x116e308, copytup = 0xaba7bd, writetup = 0xaba811 , readtup = 0xaba9d9 , memtuples = 0x1219e60, memtupdeleted = 0, memtupcount = 0, memtupsize = 2048, growmemtuples = true, readptrs = 0x123ca50, activeptr = 0, readptrcount = 1, readptrsize = 8, writepos_file = 0, writepos_offset = 0}(gdb) n66 if (node->eflags & EXEC_FLAG_MARK)(gdb) 78 node->tuplestorestate = tuplestorestate;(gdb) 85 eof_tuplestore = (tuplestorestate == NULL) ||(gdb) p *tuplestorestate$8 = {status = TSS_INMEM, eflags = 2, backward = false, interXact = false, truncated = false, availMem = 4177896, allowedMem = 4194304, tuples = 0, myfile = 0x0, context = 0x122fd70, resowner = 0x116e308, copytup = 0xaba7bd , writetup = 0xaba811 , readtup = 0xaba9d9 , memtuples = 0x1219e60, memtupdeleted = 0, memtupcount = 0, memtupsize = 2048, growmemtuples = true, readptrs = 0x123ca50, activeptr = 0, readptrcount = 1, readptrsize = 8, writepos_file = 0, writepos_offset = 0}(gdb)
既不是往后扫描也没有到达EOF
(gdb) n86 tuplestore_ateof(tuplestorestate);(gdb) 85 eof_tuplestore = (tuplestorestate == NULL) ||(gdb) 88 if (!forward && eof_tuplestore)(gdb) p eof_tuplestore$9 = false(gdb) p forward$10 = true(gdb)
如能从tuplestore中提取另外一个tuple,则返回此slot
(gdb) n107 slot = node->ss.ps.ps_ResultTupleSlot;(gdb) 108 if (!eof_tuplestore)(gdb) p *slot$11 = {type = T_TupleTableSlot, tts_flags = 18, tts_nvalid = 0, tts_ops = 0xc3e720, tts_tupleDescriptor = 0x1230660, tts_values = 0x12307e8, tts_isnull = 0x12307f0, tts_mcxt = 0x122fd70, tts_tid = { ip_blkid = {bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, tts_tableOid = 0}(gdb) p slot->tts_values[0]$12 = 0(gdb) n110 if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot))(gdb)
如扫描方向为往前,则设置eof_tuplestore为T,填充tuple到tuplestore中
112 if (forward)(gdb) 113 eof_tuplestore = true;(gdb)
如需要(处于tuplestore末尾),尝试从subplan(扫描t_big_null)中提取另外一行
124 if (eof_tuplestore && !node->eof_underlying)(gdb) p node->eof_underlying$13 = false(gdb) n133 outerNode = outerPlanState(node);(gdb) n134 outerslot = ExecProcNode(outerNode);(gdb) 135 if (TupIsNull(outerslot))(gdb) p *outerNode$14 = {type = T_SeqScanState, plan = 0x1210f58, state = 0x122fe88, ExecProcNode = 0x72b904, ExecProcNodeReal = 0x72b904 , instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleDesc = 0x1230548, ps_ResultTupleSlot = 0x0, ps_ExprContext = 0x1230358, ps_ProjInfo = 0x0, scandesc = 0x7f6ec53386a8, scanops = 0xc3e780 , outerops = 0x0, innerops = 0x0, resultops = 0xc3e780 , scanopsfixed = true, outeropsfixed = false, inneropsfixed = false, resultopsfixed = true, scanopsset = true, outeropsset = false, inneropsset = false, resultopsset = true}(gdb) p *outerslot$15 = {type = T_TupleTableSlot, tts_flags = 16, tts_nvalid = 0, tts_ops = 0xc3e780 , tts_tupleDescriptor = 0x7f6ec53386a8, tts_values = 0x12304c0, tts_isnull = 0x12304c8, tts_mcxt = 0x122fd70, tts_tid = { ip_blkid = {bi_hi = 0, bi_lo = 44240}, ip_posid = 1}, tts_tableOid = 49155}(gdb) p outerslot->tts_values[0]$16 = 0(gdb) p outerslot->tts_values[1]$17 = 0(gdb) ###[local]:5432 pg12@testdb=# select oid,relname from pg_class where oid = 49155; oid | relname -------+------------ 49155 | t_big_null(1 row)###
拷贝到tuplestore中,返回slot
(gdb) n146 if (tuplestorestate)(gdb) 147 tuplestore_puttupleslot(tuplestorestate, outerslot);(gdb) 149 ExecCopySlot(slot, outerslot);(gdb) 150 return slot;(gdb) p *slot$18 = {type = T_TupleTableSlot, tts_flags = 20, tts_nvalid = 0, tts_ops = 0xc3e720, tts_tupleDescriptor = 0x1230660, tts_values = 0x12307e8, tts_isnull = 0x12307f0, tts_mcxt = 0x122fd70, tts_tid = { ip_blkid = {bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, tts_tableOid = 0}(gdb) p *slot->tts_values$19 = 0(gdb)
继续执行
...(gdb) p slot->tts_values[0]$24 = 9998243(gdb) cContinuing.Breakpoint 1, ExecMaterial (pstate=0x1230128) at nodeMaterial.c:4141 MaterialState *node = castNode(MaterialState, pstate);(gdb) n49 CHECK_FOR_INTERRUPTS();(gdb) 54 estate = node->ss.ps.state;(gdb) 55 dir = estate->es_direction;(gdb) 56 forward = ScanDirectionIsForward(dir);(gdb) 57 tuplestorestate = node->tuplestorestate;(gdb) 62 if (tuplestorestate == NULL && node->eflags != 0)(gdb) 85 eof_tuplestore = (tuplestorestate == NULL) ||(gdb) 86 tuplestore_ateof(tuplestorestate);(gdb) 85 eof_tuplestore = (tuplestorestate == NULL) ||(gdb) 88 if (!forward && eof_tuplestore)(gdb) 107 slot = node->ss.ps.ps_ResultTupleSlot;(gdb) 108 if (!eof_tuplestore)(gdb) 124 if (eof_tuplestore && !node->eof_underlying)(gdb) 133 outerNode = outerPlanState(node);(gdb) 134 outerslot = ExecProcNode(outerNode);(gdb) 135 if (TupIsNull(outerslot))(gdb) 146 if (tuplestorestate)(gdb) 147 tuplestore_puttupleslot(tuplestorestate, outerslot);(gdb) 149 ExecCopySlot(slot, outerslot);(gdb) 150 return slot;(gdb) p slot->tts_values[0]$25 = 9998244(gdb) ...
"怎么使用PostgreSQ中的ExecMaterial函数"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
表达式
查询
参数
节点
状态
输出
信息
方向
成本
末尾
结构
规划
情况
拷贝
指针
数据
标识
类型
结果
行期
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
魔兽世界数据库7.2
2路服务器单价
服务器应用程序总是不定时关闭
服务器机箱怎么装硬盘
搞软件开发的自己开工作室
网站服务器硬件选择问题
将在履行网络安全保护
朝阳区品牌软件开发
甘肃日报林铎在网络安全
长宁区大型软件开发售后保障
高校教师网络安全意识
杭州服务器机房专用空调
空间数据库 特点
网络安全审核专员是什么
多线程数据库链接关闭
arm 处理器服务器
新浪云服务器平台
大型分布式数据库
web应用开发数据库表
金华抢单软件开发
网络安全答题有效时间
网络技术员应该做什么
仓储管理系统数据库课程设计过程
网信部网络安全
数据库is表示什么
大传奇服务器安全吗
软件开发与交流
安卓app软件开发平台
苏州软件开发培训学校
时空v7数据库查看工具