当前位置: 首页 > news >正文

小额贷网站建设织梦网站安装教程视频

小额贷网站建设,织梦网站安装教程视频,网络设计过程,自己做一元夺宝网站为了能够对分区表有优异的处理能力#xff0c;对于查询优化系统来说一个最基本的能力就是做分区裁剪partition pruning#xff0c;将query中并不涉及的分区提前排除掉。如下执行计划所示#xff0c;由于单表谓词在parititon key上#xff0c;在优化期间即可确定哪些可以分区…为了能够对分区表有优异的处理能力对于查询优化系统来说一个最基本的能力就是做分区裁剪partition pruning将query中并不涉及的分区提前排除掉。如下执行计划所示由于单表谓词在parititon key上在优化期间即可确定哪些可以分区可以避免访问即静态pruning。 explain select * from test where collecttime 2023-02-22 00:00:00 and collecttime 2023-02-28 00:00:00;QUERY PLAN ------------------------------------------------------------------------------------------------------------- Gather Motion 12:1 (slice1; segments: 12) (cost0.00..431.18 rows39 width1626)- Sequence (cost0.00..431.02 rows4 width1626)- Partition Selector for test (dynamic scan id: 1) (cost10.00...100.00 rows9 width4)Partition selected: 6 (out of 33)- Dynamic Seq Scan on test (dynamic scan id: 1) (cost0.00...431.02 rows4 width1626)Filter: ((collecttime 2023-02-22 00:00:00::timestamp without time zone) AND (collecttime 2023-02-28 00:00:00::timestamp without time zone)) Optimizer: Pivotal Optimizer (GPORCA) explain select * from test where collecttime 2023-02-22 00:00:00 and collecttime 2023-02-28 00:00:00; QUERY PLAN ------------------------------------------------------------------------------------------------------------- Gather Motion 12:1 (slice1; segments: 12) (cost0.00..431.18 rows6 width3748)- Append (cost0.00...0.00 rows1 width3748)- Seq Scan on test_1_prt_p20230222_1 (cost0.00..0.00 rows1 width3748)Filter: ((collecttime 2023-02-22 00:00:00::timestamp without time zone) AND (collecttime 2023-02-28 00:00:00::timestamp without time zone))...- Seq Scan on test_1_prt_p20230227_1 (cost0.00..0.00 rows1 width3748)Filter: ((collecttime 2023-02-22 00:00:00::timestamp without time zone) AND (collecttime 2023-02-28 00:00:00::timestamp without time zone)) Optimizer: Postgres query optimizer 如下语句date_id的值将需要根据date_dim表的动态输出决定因此对orders表的partition pruning只能在执行期完成类似的例子还有动态绑定变量称为动态pruning。 SELECT avg(amount) FROM ordersWHERE date_id IN (SELECT date_id FROM date_dim WHERE year 2013 AND month BETWEEN 10 AND 12);引入了3个新的算子来做pruningPartitionSelector、DynamicScan、SequenceSequence用于描述PartitionSelector-DynamicScan的生产消费关系确定谁先执行PartitionSelector根据谓词生成相关partition ids主要实现了做partition选择的功能并将筛选后的ids传递给DynamicScanDynamicSeqScan物理scan算子基于PartitionSelector传递的ids在做table scan时跳过不需要的partition。上一节已经描述过DynamicScan的执行流程了该节主要介绍Partition Selector。 Partition Selector Partition Selector用于计算和传播(Compute and propagate)使用Dynamic table scan的分区表oid。执行PartitionSelector有两种途径Constant partition elimination和Join partition elimination如下图 (a) 表示做full table scan这时是没有filter的PartitionSelector直接生成全量partition T1 - T100给DynamicScan。 (b/c) 表示在partition key(PK)上有单表条件(PK5/PK30)这时可以只选出目标分区给DynamicScan。 (d) 表示了两表 R join T on R.A T.PK由于T表是partition table其scan算子是DynamicScan。这里有所不同的是对T的过滤需要基于R.A列的值因此PartitionSelector要加在R的scan上方用来获取R.A列的value用于过滤分区并传递给右侧T的DynamicScan。另外可以看到这里不再有Sequence算子了由于这里Join算子已经保障了R和T执行的先后顺序先R后T也就保证了PartitionSelector-DynamicScan的顺序Sequence不再必要。 Constant partition elimination PartitionSelector评估常量分区约束(constant partition constraints)来计算和传播(Compute and propagate)分区表oid。It only need to be called once. 1. Constant partition eliminationPlan structure:Sequence # 是一个同步的概念用于描述PartitionSelector-DynamicScan的生产消费关系确定谁先执行|--PartitionSelector # PartitionSelector根据谓词生成相关partition ids主要实现了做partition选择的功能并将筛选后的ids传递给DynamicScan|--DynamicSeqScan # 物理scan算子基于PartitionSelector传递的ids在做table scan时跳过不需要的partitionJoin partition elimination PartitionSelector与DynamicSeqScan、DynamicIndexScan或DynamicBitmapHeapScan位于同一切片slice中。它是为来自其子节点的每个元组执行的。它使用输入元组评估分区约束并传播匹配的分区表Oids。 2. Join partition eliminationPlan structure:...:|--DynamicSeqScan|--...|--PartitionSelector|--...可以有其他节点使用PartSelected qual来过滤行而不是动态表扫描根据选择的分区来过滤行。目前ORCA使用动态表扫描而非ORCA计划器生成的计划可以使用具有PartSelected quals的门控Result节点来排除不需要的分区。Instead of a Dynamic Table Scan, there can be other nodes that use a PartSelected qual to filter rows, based on which partitions are selected. Currently, ORCA uses Dynamic Table Scans, while plans produced by the non-ORCA planner use gating Result nodes with PartSelected quals, to exclude unwanted partitions. src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp文件中的Plan *CTranslatorDXLToPlStmt::TranslateDXLPartSelector(const CDXLNode *partition_selector_dxlnode, CDXLTranslateContext *output_context, CDXLTranslationContextArray *ctxt_translation_prev_siblings)函数将DXL PartitionSelector转化为GPDB PartitionSelector节点。 src/backend/optimizer/plan/planpartition.c文件中的Plan *create_partition_selector_plan(PlannerInfo *root, PartitionSelectorPath *best_path)函数从PartitionSelectorPath结构体创建一个PartitionSelector plan。 ExecInitPartitionSelector 首先介绍PartitionSelector node其通过给定的root表OID找到叶子子分区表的集合和可选选择谓词。它隐藏了分区选择和传播的逻辑而不是用它来污染计划使计划看起来一致且易于理解。很容易找到分区选择在计划中发生的位置。(It hides the logic of partition selection and propagation instead of polluting the plan with it to make a plan look consistent and easy to understand. It will be easy to locate where partition selection happens in a plan.) PartitionSelection可以以如下三种方式运行A PartitionSelection can work in three different ways: Dynamic selection, based on tuples that pass through it.Dynamic selection, with a projected tuple.Static selection, performed at beginning of execution. PartitionSelector结构体除了包含DynamicScan算子唯一编号和PartitionSelector算子唯一编号、root表oid和该分区表的层级外。还包含了如上三种运行方式的变量。比如对于Dynamic selection, based on tuples that pass through it来说levelExpressions就是保存了每个分区层级使用的谓词表达式、levelEqExpressions就是保存了每个分区层级使用的等值表达式、最后应用的剩余谓词residualPredicate、传播表达式propagationExpression。对于Dynamic selection, with a projected tuplepartTabTargetlist是用于动态选择的字段通过将输入元组投影到具有与分区表中相同位置的分区键值的元组。对于Static selection, performed at beginning of executionstaticPartOids是静态选择的分区子表oid和staticScanIds用于传播静态选择的分区子表oid的扫描id。 typedef struct PartitionSelector{Plan plan;Oid relid; /* OID of target relation */int nLevels; /* number of partition levels */int32 scanId; /* id of the corresponding dynamic scan */ // 每个DynamicScan算子有其唯一编号int32 selectorId; /* id of this partition selector */ // 每个PartitionSelector算子有其唯一编号/* Fields for dynamic selection */List *levelEqExpressions; /* list of list of equality expressions used for individual levels */List *levelExpressions; /* predicates used for individual levels */Node *residualPredicate; /* residual predicate (to be applied at the end) */Node *propagationExpression; /* propagation expression */Node *printablePredicate; /* printable predicate (for explain purposes) *//* Fields for dynamic selection, by projecting input tuples to a tuple that has the partitioning key values in the same positions as in the partitioned table. */List *partTabTargetlist;/* Fields for static selection */bool staticSelection; /* static selection performed? */List *staticPartOids; /* list of statically selected parts */List *staticScanIds; /* scan ids used to propagate statically selected part oids */ } PartitionSelector;ExecInitPartitionSelector函数为Orca生成的PartitionSelector节点创建运行时状态信息并初始化外部子节点如果存在。Create the run-time state information for PartitionSelector node produced by Orca and initializes outer child if exists. 其主要执行的步骤如下 调用initPartitonSelection创建PartitionSelectorState其levelPartRules中的每个元素都是每层子分区的PartitionRule信息我们知道分区表除了root表之外的inheritor和leaf分区在pg_partition_rule中都有一个条目其信息就是PartitionRule为levelEqExpressions、levelExpressions、residualPredicate、propagationExpression初始化表达式执行ExprState列表。对于ParitionSelector来说其没有Inner plan也就是没有右子树这里仅仅需要调用ExecInitNode初始化psstate-lefttree即可也就是Join partition elimination这种情况。如果PartitionSeletor.partTabTargetlist不为NILpartTabTargetlist是用于动态选择的字段通过将输入元组投影到具有与分区表中相同位置的分区键值的元组。初始化投影以生成一个元组该元组的分区键列位于与分区表中相同的位置。 PartitionSelectorState *ExecInitPartitionSelector(PartitionSelector *node, EState *estate, int eflags){PartitionSelectorState *psstate initPartitionSelection(node, estate); ExecInitResultTupleSlot(estate, psstate-ps); /* tuple table initialization */ExecAssignResultTypeFromTL(psstate-ps);ExecAssignProjectionInfo(psstate-ps, NULL);/* initialize child nodes */if (NULL ! outerPlan(node)){ outerPlanState(psstate) ExecInitNode(outerPlan(node), estate, eflags); }/* Initialize projection, to produce a tuple that has the partitioning key columns at the same positions as in the partitioned table. */if (node-partTabTargetlist){ // partition key target listList *exprStates (List *) ExecInitExpr((Expr *) node-partTabTargetlist, (PlanState *) psstate);psstate-partTabDesc ExecTypeFromTL(node-partTabTargetlist, false);psstate-partTabSlot MakeSingleTupleTableSlot(psstate-partTabDesc);psstate-partTabProj ExecBuildProjectionInfo(exprStates, psstate-ps.ps_ExprContext, psstate-partTabSlot, ExecGetResultType(psstate-ps));}return psstate; }ExecPartitionSelector 从root表获取PartitionNode和access method。首先看一下DynamicTableScanInfo的List *partsMetadata其存放了所有相关分区表的分区元数据。getPartitionNodeAndAccessMethod从root表获取PartitionNode和accessMethod其实就是调用findPartitionMetadataEntry函数该函数从partsMetadata列表中查找到相应partition oid的PartitionMetadata对象输入参数为partsMetadata: list of PartitionMetadata、partOid: Part Oid输出参数为partsAndRules: output parameter for matched PartitionNodeaccessMethods: output parameter for PartitionAccessMethods。PartitionMetadata结构体其实就包含了PartitionNode *partsAndRules和PartitionAccessMethods *accessMethods两个成员 if (NULL node-rootPartitionNode){getPartitionNodeAndAccessMethod(ps-relid,estate-dynamicTableScanInfo-partsMetadata,estate-es_query_cxt,node-rootPartitionNode,node-accessMethods); } /* ----------------------------------------------------------------* getPartitionNodeAndAccessMethod Retrieve PartitionNode and access method from root table* ---------------------------------------------------------------- */ void getPartitionNodeAndAccessMethod(Oid rootOid, List *partsMetadata, MemoryContext memoryContext, PartitionNode **partsAndRules, PartitionAccessMethods **accessMethods){findPartitionMetadataEntry(partsMetadata, rootOid, partsAndRules, accessMethods);(*accessMethods)-part_cxt memoryContext; } void findPartitionMetadataEntry(List *partsMetadata, Oid partOid, PartitionNode **partsAndRules, PartitionAccessMethods **accessMethods){ListCell *lc NULL;foreach(lc, partsMetadata){PartitionMetadata *metadata (PartitionMetadata *) lfirst(lc);*partsAndRules findPartitionNodeEntry(metadata-partsAndRules, partOid);if (NULL ! *partsAndRules){/* accessMethods define the lookup access methods for partitions, one for each level */*accessMethods metadata-accessMethods;return;}} }描述findPartitionNodeEntry函数之前需要了解一下pg_partition和pg_partition_rule系统表每个分区父表(root table和intermediate table)在其中都有一个条目且该条目有标识自己的oid也就是Partition结构体中的partid而parrelid字段就是该表的oidparkind指明分区类型list或rangelevel是分区树的层级。PartitionRule结构体则代表pg_partition_rule系统表中的条目除了root tableintermediate table和leaf table都应该有对应的条目同样每个条目都有标识自己的oid(parruleid)paroid则代表该分区表的oid通过parparentrule可以关联父表的rule条目从而组成自下而上的父子链。 struct PartitionNode{NodeTag type;Partition *part; List *rules; /* rules for this level */ struct PartitionRule *default_part; }; typedef struct Partition{NodeTag type;Oid partid; /* OID of row in pg_partition. */Oid parrelid; /* OID in pg_class of top-level partitioned relation */char parkind; /* r, l, or (unsupported) h */int16 parlevel; /* depth below parent partitioned table */bool paristemplate; /* just a template, or really a part? */int16 parnatts; /* number of partitioning attributes */AttrNumber *paratts;/* attribute number vector */ Oid *parclass; /* operator class vector */ } Partition; typedef struct PartitionRule{NodeTag type;Oid parruleid; Oid paroid; Oid parchildrelid; Oid parparentrule;bool parisdefault; char *parname; Node *parrangestart; bool parrangestartincl;Node *parrangeend; bool parrangeendincl; Node *parrangeevery; List *parlistvalues;int16 parruleord; List *parreloptions; Oid partemplatespaceId; /* the tablespace id for the template (or InvalidOid for non-template rules */struct PartitionNode *children; /* sub partition */ } PartitionRule;findPartitionNodeEntry函数用于通过给定分区表oid查找到PartitonNode如果partitionNode代表的是给定partOid则返回该partitonNode如果不是有可能partOid是PartitionNode的子分区通过递归子分区获取到以partOid对应的PartitionNode并返回。 static PartitionNode *findPartitionNodeEntry(PartitionNode *partitionNode, Oid partOid){if (NULL partitionNode){ return NULL; }if (partitionNode-part-parrelid partOid){ return partitionNode; } ListCell *lcChild NULL; /* check recursively in child parts in case we have the oid of an intermediate node */foreach(lcChild, partitionNode-rules){PartitionRule *childRule (PartitionRule *) lfirst(lcChild);PartitionNode *childNode findPartitionNodeEntry(childRule-children, partOid);if (NULL ! childNode){ return childNode; }} if (NULL ! partitionNode-default_part){ /* check recursively in the default part, if any */childNode findPartitionNodeEntry(partitionNode-default_part-children, partOid);}return childNode; }Static selection 如果PartitionSelector.staticSelection为true需要执行Static selection, performed at beginning of execution。staticPartOids是静态选择的分区子表oid和staticScanIds用于传播静态选择的分区子表oid的扫描id。 TupleTableSlot *ExecPartitionSelector(PartitionSelectorState *node){PartitionSelector *ps (PartitionSelector *) node-ps.plan; EState *estate node-ps.state;if (ps-staticSelection){/* propagate the part oids obtained via static partition selection */partition_propagation(estate, ps-staticPartOids, ps-staticScanIds, ps-selectorId);return NULL;}首先学习一下InsertPidIntoDynamicTableScanInfo函数用于将分区oid插入dynamicTableScanInfo的pidIndexes[scanId]如果partOid分区oid为非法oid该函数将不会插入该oid但是会确保dynamicTableScanInfo的pidIndexes[scanId]的HTAB该HTAB的key为partition table oidentry为partition table oid和selectorId列表的组合存在。从DynamicSeqScan的逻辑可知需要扫描的分区oid就存在于dynamicTableScanInfo的pidIndexes[scanId]中。partition_propagation函数就是将该selector为不同DynamicScan裁剪的分区oid放入dynamicTableScanInfo-pidIndexes缓冲中供DynamicScan扫描。 static void partition_propagation(EState *estate, List *partOids, List *scanIds, int32 selectorId){ListCell *lcOid NULL; ListCell *lcScanId NULL;forboth (lcOid, partOids, lcScanId, scanIds) {Oid partOid lfirst_oid(lcOid); int scanId lfirst_int(lcScanId);InsertPidIntoDynamicTableScanInfo(estate, scanId, partOid, selectorId);} } void InsertPidIntoDynamicTableScanInfo(EState *estate, int32 index, Oid partOid, int32 selectorId){DynamicTableScanInfo *dynamicTableScanInfo estate-dynamicTableScanInfo;MemoryContext oldCxt MemoryContextSwitchTo(estate-es_query_cxt);if (index dynamicTableScanInfo-numScans){ increaseScanArraySize(estate, index); } // 对array扩容if ((dynamicTableScanInfo-pidIndexes)[index - 1] NULL){ dynamicTableScanInfo-pidIndexes[index - 1] createPidIndex(estate, index); } // 为该index创建HTABif (partOid ! InvalidOid){bool found false;PartOidEntry *hashEntry hash_search(dynamicTableScanInfo-pidIndexes[index - 1], partOid, HASH_ENTER, found);if (found){hashEntry-selectorList list_append_unique_int(hashEntry-selectorList, selectorId);}else{hashEntry-partOid partOid; hashEntry-selectorList list_make1_int(selectorId);}}MemoryContextSwitchTo(oldCxt); }Dynamic selection 如果NULL ! outerPlanState(node)则需要处理Join partition elimination。调用ExecProcNode(outerPlan)执行partitionselector的左子树的执行计划。如果inputSlot为null其实就是说明outerPlan没有匹配的tuple了也就是说明不需要继续判定哪些分区需要扫描了。 PlanState *outerPlan outerPlanState(node);TupleTableSlot *inputSlot ExecProcNode(outerPlan);if (TupIsNull(inputSlot)){ /* no more tuples from outerPlan *//* Make sure we have an entry for this scan id in dynamicTableScanInfo. Normally, this wouldve been done the first time a partition is selected, but we must ensure that there is an entry even if no partitions were selected. (The traditional Postgres planner uses this method.) */if (ps-partTabTargetlist) InsertPidIntoDynamicTableScanInfo(estate, ps-scanId, InvalidOid, ps-selectorId);else LogPartitionSelection(estate, ps-selectorId);return NULL;}如果有partitioning投影Dynamic selection, with a projected tuple将输入tuple投影成看上去来自partitioned table的tuple然后使用selectPartitionMulti函数选择分区。Postgres planner使用这种方式 ListCell *lc;TupleTableSlot *slot ExecProject(node-partTabProj, NULL); slot_getallattrs(slot);List *oids selectPartitionMulti(node-rootPartitionNode,slot_get_values(slot),slot_get_isnull(slot),slot-tts_tupleDescriptor,node-accessMethods);foreach (lc, oids){InsertPidIntoDynamicTableScanInfo(estate, ps-scanId, lfirst_oid(lc), ps-selectorId);}list_free(oids);selectPartitionMulti函数通过提供的partition node、tuple values nullness和partition state找到匹配的叶子分区。该函数和selectPartition相似但是对于nulls的处理不同。该函数的处理方式是如果有partitioning key的值为null则所有子分区都被认为是匹配的如果没有partitioning key的值为null调用selectPartition1获取对应partitioning key值下的子分区。The input values/isnull should match the layout of tuples in the partitioned table. List *selectPartitionMulti(PartitionNode *partnode, Datum *values, bool *isnull, TupleDesc tupdesc, PartitionAccessMethods *accessMethods){List *leafPartitionOids NIL;List *inputList list_make1(partnode);while (list_length(inputList) 0){ // 按层处理这里的InputList是每层分区表的PartitionNode列表List *levelOutput NIL; ListCell *lc NULL; foreach(lc, inputList){ // 遍历所有PartitionNodePartitionNode *candidatePartNode (PartitionNode *) lfirst(lc);bool foundNull false;for (int i 0; i candidatePartNode-part-parnatts; i){ // 遍历该partition node的所有分区键AttrNumber attno candidatePartNode-part-paratts[i]; if (isnull[attno - 1]){ /* If corresponding value is null, then we should pick all of its children (or itself if it is a leaf partition) */foundNull true;if (IsLeafPartitionNode(candidatePartNode)){ /* Extract out Oids of all children */leafPartitionOids list_concat(leafPartitionOids, all_partition_relids(candidatePartNode));}else {levelOutput list_concat(levelOutput, PartitionChildren(candidatePartNode));}}}/* If null was not found on the attribute, and if this is a leaf partition, then there will be an exact match. If it is not a leaf partition, then we have to find the right child to investigate. */if (!foundNull) {if (IsLeafPartitionNode(candidatePartNode)){Oid matchOid selectPartition1(candidatePartNode, values, isnull, tupdesc, accessMethods, NULL, NULL);if (matchOid ! InvalidOid){ leafPartitionOids lappend_oid(leafPartitionOids, matchOid); }}else{PartitionNode *childPartitionNode NULL;selectPartition1(candidatePartNode, values, isnull, tupdesc, accessMethods, NULL, childPartitionNode);if (childPartitionNode){ levelOutput lappend(levelOutput, childPartitionNode); }}}}list_free(inputList); inputList levelOutput; /* Start new level */}return leafPartitionOids; }如果没有partitioning投影则利用levelEqExpressions和levelExpressions来选择分区。ORCA使用这种方式 typedef struct SelectedParts{ /* SelectedParts node This is the result of partition selection. It has a list of leaf part oids and the corresponding ScanIds to which they should be propagated */List *partOids;List *scanIds; } SelectedParts;SelectedParts *selparts processLevel(node, 0 /* level */, inputSlot); if (NULL ! ps-propagationExpression){ /* partition propagation */partition_propagation(estate, selparts-partOids, selparts-scanIds, ps-selectorId);}list_free(selparts-partOids); list_free(selparts-scanIds); pfree(selparts);processLevel函数返回符合predicates处于指定分区层级的分区表oid。如果处理intermediate层级存储符合要求的分区oid继续下一个层级分区如果处于叶子层级获取满足要求的分区oid。其执行流程如下所示 首先获取指定层级的谓词表达式equalityPredicate和generalPredicate定位到root PartitionNode如果level为0则parentNode为node-rootPartitionNode否则为node-levelPartRules[level - 1]-children。调用partition_rules_for_equality_predicate和partition_rules_for_general_predicate获取匹配equalityPredicate和generalPredicate谓词的PartitionRule列表。如果没有上述谓词则需要把下一个层级的PartitionRule全部返回。遍历satisfied PartitionRules如果当前层级为叶子层级调用eval_part_qual函数处理residual predicate调用eval_propagation_expression函数propagationExpression获取scanId将partoid和scanId设置到selparts否则需要递归调用processLevel(node, level 1, inputTuple)到next level’s partition elimination SelectedParts *processLevel(PartitionSelectorState *node, int level, TupleTableSlot *inputTuple){SelectedParts *selparts makeNode(SelectedParts); selparts-partOids NIL; selparts-scanIds NIL;PartitionSelector *ps (PartitionSelector *) node-ps.plan;/* get equality and general predicate for the current level */ // 获取指定层级的谓词表达式List *equalityPredicate (List *) lfirst(list_nth_cell(ps-levelEqExpressions, level));Expr *generalPredicate (Expr *) lfirst(list_nth_cell(ps-levelExpressions, level));/* get parent PartitionNode if in level 0, its the root PartitionNode */PartitionNode *parentNode node-rootPartitionNode;if (0 ! level){parentNode node-levelPartRules[level - 1]-children;}/* list of PartitionRule that satisfied the predicates */List *satisfiedRules NIL; if (NULL ! equalityPredicate){ /* If equalityPredicate exists */List *chosenRules partition_rules_for_equality_predicate(node, level, inputTuple, parentNode);satisfiedRules list_concat(satisfiedRules, chosenRules);}else if (NULL ! generalPredicate) /* If generalPredicate exists */{List *chosenRules partition_rules_for_general_predicate(node, level, inputTuple, parentNode);satisfiedRules list_concat(satisfiedRules, chosenRules);}else { /* None of the predicate exists *//** Neither equality predicate nor general predicate exists. Return all the next level PartitionRule.* WARNING: Do NOT use list_concat with satisfiedRules and parentNode-rules. list_concat will destructively modify satisfiedRules to point to parentNode-rules, which will then be freed when we free satisfiedRules. This does not apply when we execute partition_rules_for_general_predicate as it creates its own list. */ListCell *lc NULL; foreach(lc, parentNode-rules){PartitionRule *rule (PartitionRule *) lfirst(lc); satisfiedRules lappend(satisfiedRules, rule);}if (NULL ! parentNode-default_part){satisfiedRules lappend(satisfiedRules, parentNode-default_part);}}/* Based on the satisfied PartitionRules, go to next level or propagate PartOids if we are in the leaf level */ListCell *lc NULL;foreach(lc, satisfiedRules){PartitionRule *rule (PartitionRule *) lfirst(lc);node-levelPartRules[level] rule; if (level ps-nLevels - 1){ /* If we already in the leaf level */bool shouldPropagate true; if (NULL ! ps-residualPredicate){ /* if residual predicate exists */ shouldPropagate eval_part_qual(node-ps.ps_ExprContext, inputTuple, node-residualPredicateExprStateList); /* evaluate residualPredicate */}if (shouldPropagate){if (NULL ! ps-propagationExpression){if (!list_member_oid(selparts-partOids, rule-parchildrelid)){selparts-partOids lappend_oid(selparts-partOids, rule-parchildrelid);int scanId eval_propagation_expression(node, rule-parchildrelid);selparts-scanIds lappend_int(selparts-scanIds, scanId);}}}}else{ /* Recursively call this function for next levels partition elimination */SelectedParts *selpartsChild processLevel(node, level 1, inputTuple);selparts-partOids list_concat(selparts-partOids, selpartsChild-partOids);selparts-scanIds list_concat(selparts-scanIds, selpartsChild-scanIds);pfree(selpartsChild);}}list_free(satisfiedRules); node-levelPartRules[level] NULL; /* After finish iteration, reset this levels PartitionRule */return selparts; }https://zhuanlan.zhihu.com/p/367068030
http://www.dnsts.com.cn/news/23110.html

相关文章:

  • 网站建设上传宝贝xamp便利的广州微网站建设
  • 高端电子网站建设国内空间没备案可以打开网站吗
  • 有哪些网站是拐角型wordpress 编辑页脚
  • 山东专业网站建设哪家便宜建设一个视频网站的成本
  • 企业网站开发使用方法网页设计概览图
  • 小说网站开发猪八戒wordpress开发网站模板
  • 芜湖网站建设芜湖海南微信网站制作平台
  • 三大电商平台是哪三个东莞seo推广运营服务
  • 福建网站建设模板wordpress登录不
  • 迷失传奇网站naocq找装修公司的网站
  • 建设局招标网站915x1830建筑模板价格
  • 网站建设对企业的作用购物网站建设行业现状
  • 凡客另一购物网站网站改版要重新备案吗
  • 河南大学学科建设处网站中国有什么网站做跨境零售
  • 简单的手机网站模板下载安装应用商店下载软件
  • 纯静态网站挂马义乌网站制作多少钱
  • 响应式网站高度如何计算做网站首选科远网络
  • 江苏聚峰建设集团网站邻水县规划和建设局 网站
  • 网站根目录表示做效果图的方便的网站
  • 水墨背景风格企业网站模板适合学生做的网站类型
  • 个人网站备案可以放什么内容网站怎么提升关键词排名
  • 南京做微网站做兼职上什么网站找
  • 企业门户网站的建设与实现论文怎么用html做百度首页网站
  • 做啥网站赚钱网站的优化通过什么做上去
  • 杭州富阳建设局网站一个网站源码值多少钱
  • discuz可以做门户网站么宜昌做网站哪家最便宜
  • 浦东做网站的公司怎么把网站的标题做的炫酷
  • 同一个域名可以做几个网站吗最近一个月的热点事件
  • 单页网站制作 在线 支付企业网站关键词优化排名应该怎么做
  • 平邑建设银行网站公司的网站建设价格