/* * Copyright (c) 2020 Huawei Technologies Co.,Ltd. * Portions Copyright (c) 2021, openGauss Contributors * * openGauss is licensed under Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * * http://license.coscl.org.cn/MulanPSL2 * * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. * ------------------------------------------------------------------------- * * pruning.cpp * data partition * * IDENTIFICATION * src/gausskernel/optimizer/util/pruning.cpp * * ------------------------------------------------------------------------- */ #include "postgres.h" #include "knl/knl_variable.h" #include "catalog/pg_type.h" #include "catalog/index.h" #include "commands/tablecmds.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "nodes/relation.h" #include "optimizer/clauses.h" #include "optimizer/pruning.h" #include "optimizer/pruningboundary.h" #include "optimizer/subpartitionpruning.h" #include "utils/array.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/rel_gs.h" #include "utils/syscache.h" #include "utils/relcache.h" #include "catalog/pg_partition_fn.h" static PruningResult* partitionPruningFromBoolExpr(const BoolExpr* expr, PruningContext* context); static PruningResult* intersectChildPruningResult(const List* resultList, PruningContext* context); static PruningResult* unionChildPruningResult(const List* resultList, PruningContext* context); static PruningResult* partitionPruningFromScalarArrayOpExpr (const ScalarArrayOpExpr* arrayExpr, PruningContext* pruningCtx); static PruningResult* recordBoundaryFromOpExpr(const OpExpr* expr, PruningContext* context); static PruningResult* partitionPruningFromNullTest(NullTest* expr, PruningContext* context); static void partitionFilter(PruningContext* context, PruningResult* pruningResult); static bool partitionShouldEliminated(PruningContext* context, int partSeq, PruningBoundary* boundary); static PartKeyRange* constructPartKeyRange(PruningContext* context, int partSeq); static PruningBoundary* mergeBoundary(PruningBoundary* leftBoundary, PruningBoundary* rightBoundary); static void cleanPruningBottom(PruningContext* context, PartitionIdentifier* bottomSeq, Const* value); static void cleanPruningTop(PruningContext* context, PartitionIdentifier* topSeq, Const* value); static void destroyPruningResultList(List* resultList); static Node* EvalExprValueWhenPruning(PruningContext* context, Node* node); static PruningResult* recordEqualFromOpExpr(PartitionType partType, const OpExpr* expr, PruningContext* context); static PruningResult* partitionPruningFromBoolExpr(PartitionType partType, const BoolExpr* expr, PruningContext* context); static PruningResult* partitionPruningFromNullTest(PartitionType partType, NullTest* expr, PruningContext* context); static PruningResult* partitionPruningFromScalarArrayOpExpr(PartitionType partType, const ScalarArrayOpExpr* arrayExpr, PruningContext* pruningCtx); static PruningResult* partitionEqualPruningWalker(PartitionType partType, Expr* expr, PruningContext* pruningCtx); #define NoNeedPruning(pruningResult) \ (PruningResultIsFull(pruningResult) || PruningResultIsEmpty(pruningResult) || \ !PointerIsValid((pruningResult)->boundary)) #define IsCleanPruningBottom(bottomSeqPtr, pruningResult, bottomValue) \ ((pruningResult)->boundary->partitionKeyNum > 1 && PointerIsValid((bottomValue)[0]) && \ !(pruningResult)->boundary->minClose[0]) #define IsCleanPruningTop(topSeqPtr, pruningResult, topValue) \ ((pruningResult)->boundary->partitionKeyNum > 1 && (topValue) && PointerIsValid((topValue)[0]) && \ !(pruningResult)->boundary->maxClose[0]) static PartitionMap* GetRelPartitionMap(Relation relation) { return relation->partMap; } static void CollectSubpartitionPruningResults(PruningResult* resPartition, Relation current_relation) { if (!RelationIsSubPartitioned(current_relation)) { return; } int partSeq; ListCell *cell = NULL; Oid partitionOid = InvalidOid; foreach (cell, resPartition->ls_rangeSelectedPartitions) { partSeq = lfirst_int(cell); partitionOid = getPartitionOidFromSequence(current_relation, partSeq, resPartition->partMap); SubPartitionPruningResult *subPartPruningRes = PreGetSubPartitionFullPruningResult(current_relation, partitionOid); if (subPartPruningRes == NULL) { continue; } subPartPruningRes->partSeq = partSeq; resPartition->ls_selectedSubPartitions = lappend(resPartition->ls_selectedSubPartitions, subPartPruningRes); } } /* * @@GaussDB@@ * Brief * Description :Get Partition Info from expr when Partition Info not in plan. * return value: */ PruningResult* GetPartitionInfo(PruningResult* result, EState* estate, Relation current_relation) { PruningResult* resPartition = NULL; PruningContext context; context.pruningType = PruningPartition; context.GetPartitionMap = GetRelPartitionMap; context.root = NULL; context.rte = NULL; context.estate = estate; context.relation = current_relation; /* if the partmap which copied before static pruning exists, it will replace the rel->partMap, seen in * GetPartitionMap */ context.partmap = result->partMap; if (current_relation->partMap->type == PART_TYPE_LIST || current_relation->partMap->type == PART_TYPE_HASH) { resPartition = partitionEqualPruningWalker(current_relation->partMap->type, result->expr, &context); } else { resPartition = partitionPruningWalker(result->expr, &context); } partitionPruningFromBoundary(&context, resPartition); if (PruningResultIsFull(resPartition) || (resPartition->bm_rangeSelectedPartitions == NULL && PruningResultIsSubset(resPartition))) { destroyPruningResult(resPartition); resPartition = getFullPruningResult(current_relation); CollectSubpartitionPruningResults(resPartition, current_relation); return resPartition; } if (PointerIsValid(resPartition) && !PruningResultIsFull(resPartition)) generateListFromPruningBM(resPartition); CollectSubpartitionPruningResults(resPartition, current_relation); return resPartition; } /* * @@GaussDB@@ * Brief * Description : * return value: */ PruningResult* copyPruningResult(PruningResult* srcPruningResult) { if (srcPruningResult != NULL) { PruningResult* newpruningInfo = makeNode(PruningResult); newpruningInfo->state = srcPruningResult->state; newpruningInfo->bm_rangeSelectedPartitions = bms_copy(srcPruningResult->bm_rangeSelectedPartitions); newpruningInfo->intervalOffset = srcPruningResult->intervalOffset; newpruningInfo->intervalSelectedPartitions = bms_copy(srcPruningResult->intervalSelectedPartitions); newpruningInfo->ls_rangeSelectedPartitions = (List*)copyObject(srcPruningResult->ls_rangeSelectedPartitions); newpruningInfo->ls_selectedSubPartitions = (List*)copyObject(srcPruningResult->ls_selectedSubPartitions); newpruningInfo->paramArg = (Param *)copyObject(srcPruningResult->paramArg); newpruningInfo->expr = (Expr *)copyObject(srcPruningResult->expr); newpruningInfo->exprPart = (OpExpr *)copyObject(srcPruningResult->exprPart); newpruningInfo->partMap = (PartitionMap *)CopyPartitionMap(srcPruningResult->partMap); return newpruningInfo; } else { return NULL; } } void generateListFromPruningBM(PruningResult* result) { int partitions = 0; int i = 0; int tmpcheck = 0; Bitmapset* tmpset = NULL; result->ls_rangeSelectedPartitions = NULL; tmpset = bms_copy(result->bm_rangeSelectedPartitions); partitions = bms_num_members(result->bm_rangeSelectedPartitions); for (; i < partitions; i++) { tmpcheck = bms_first_member(tmpset); AssertEreport(-1 != tmpcheck, MOD_OPT, ""); if (-1 != tmpcheck) { result->ls_rangeSelectedPartitions = lappend_int(result->ls_rangeSelectedPartitions, tmpcheck); } } bms_free_ext(tmpset); } List* restrictInfoListToExprList(List* restrictInfoList) { ListCell* cell = NULL; RestrictInfo* iteratorRestrict = NULL; List* exprList = NIL; Expr* expr = NULL; foreach (cell, restrictInfoList) { iteratorRestrict = (RestrictInfo*)lfirst(cell); if (PointerIsValid(iteratorRestrict->clause)) { expr = (Expr*)copyObject(iteratorRestrict->clause); exprList = lappend(exprList, expr); } } return exprList; } /* * @@GaussDB@@ * Brief * Description : eliminate partitions which don't contain those tuple satisfy expression. * return value: non-eliminated partitions. */ PruningResult* partitionPruningForRestrictInfo( PlannerInfo* root, RangeTblEntry* rte, Relation rel, List* restrictInfoList, PartitionMap *partmap) { PruningResult* result = NULL; Expr* expr = NULL; if (0 == list_length(restrictInfoList)) { result = getFullPruningResult(rel); } else { List* exprList = NULL; int length = 0; exprList = restrictInfoListToExprList(restrictInfoList); length = list_length(exprList); if (length == 0) { result = getFullPruningResult(rel); } else if (length == 1) { expr = (Expr*)list_nth(exprList, 0); result = partitionPruningForExpr(root, rte, rel, expr); } else { expr = makeBoolExpr(AND_EXPR, exprList, 0); result = partitionPruningForExpr(root, rte, rel, expr); } } if (PointerIsValid(result) && !PruningResultIsFull(result)) { generateListFromPruningBM(result); } if (RelationIsSubPartitioned(rel) && PointerIsValid(result)) { Bitmapset *partIdx = NULL; List* part_seqs = result->ls_rangeSelectedPartitions; ListCell *cell = NULL; RangeTblEntry *partRte = (RangeTblEntry *)copyObject(rte); foreach (cell, part_seqs) { int part_seq = lfirst_int(cell); Oid partOid = getPartitionOidFromSequence(rel, part_seq, partmap); Partition partTable = tryPartitionOpen(rel, partOid, AccessShareLock); if (!partTable) { PartStatus currStatus = PartitionGetMetadataStatus(partOid, false); if (currStatus != PART_METADATA_INVISIBLE) { ReportPartitionOpenError(rel, partOid); } continue; } Relation partRel = partitionGetRelation(rel, partTable); PruningResult *subResult = NULL; partRte->relid = partOid; if (list_length(restrictInfoList) == 0) { subResult = getFullPruningResult(partRel); } else { subResult = partitionPruningForExpr(root, partRte, partRel, expr); } if (PointerIsValid(subResult) && !PruningResultIsEmpty(subResult)) { generateListFromPruningBM(subResult); if (bms_num_members(subResult->bm_rangeSelectedPartitions) > 0) { SubPartitionPruningResult *subPruning = makeNode(SubPartitionPruningResult); subPruning->partSeq = part_seq; subPruning->bm_selectedSubPartitions = subResult->bm_rangeSelectedPartitions; subPruning->ls_selectedSubPartitions = subResult->ls_rangeSelectedPartitions; result->ls_selectedSubPartitions = lappend(result->ls_selectedSubPartitions, subPruning); partIdx = bms_add_member(partIdx, part_seq); } } releaseDummyRelation(&partRel); partitionClose(rel, partTable, AccessShareLock); } // adjust if (!bms_equal(result->bm_rangeSelectedPartitions, partIdx)) { result->bm_rangeSelectedPartitions = partIdx; generateListFromPruningBM(result); } } return result; } /* * @@GaussDB@@ * Target : data partition * Brief : select * from partition (partition_name) or select * from partition for (partition_values_list) * Description : eliminate partitions which don't contain those tuple satisfy expression. * return value : non-eliminated partitions. */ PruningResult* singlePartitionPruningForRestrictInfo(Oid partitionOid, Relation rel) { bool find = false; int partitionSeq = 0; PruningResult* pruningRes = NULL; int counter = 0; if (!PointerIsValid(rel)) { return NULL; } /* shouldn't happen */ if (!PointerIsValid(rel->partMap)) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("relation of oid=\"%u\" is not partitioned table", rel->rd_id))); } /* * only one possible partitionOid is InvalidOid: * select * from partitioned_table_name partition for (values); * and values pruning interval partition which does not physical exist. */ if (!OidIsValid(partitionOid) && rel->partMap->type != PART_TYPE_INTERVAL) { return NULL; } pruningRes = makeNode(PruningResult); pruningRes->state = PRUNING_RESULT_SUBSET; /* it's a pattitioned table without interval*/ if (rel->partMap->type == PART_TYPE_RANGE || rel->partMap->type == PART_TYPE_INTERVAL) { RangePartitionMap* rangePartMap = NULL; rangePartMap = (RangePartitionMap*)rel->partMap; for (counter = 0; counter < rangePartMap->rangeElementsNum; counter++) { /* the partition is a range partition */ if ((rangePartMap->rangeElements + counter)->partitionOid == partitionOid) { find = true; partitionSeq = counter; break; } } } else if (PART_TYPE_LIST == rel->partMap->type) { ListPartitionMap* listPartMap = NULL; listPartMap = (ListPartitionMap*)rel->partMap; for (counter = 0; counter < listPartMap->listElementsNum; counter++) { /* the partition is a range partition */ if ((listPartMap->listElements + counter)->partitionOid == partitionOid) { find = true; partitionSeq = counter; break; } } } else if (PART_TYPE_HASH == rel->partMap->type) { HashPartitionMap* hashPartMap = NULL; hashPartMap = (HashPartitionMap*)rel->partMap; for (counter = 0; counter < hashPartMap->hashElementsNum; counter++) { /* the partition is a range partition */ if ((hashPartMap->hashElements + counter)->partitionOid == partitionOid) { find = true; partitionSeq = counter; break; } } } else { /* shouldn't happen */ pfree_ext(pruningRes); ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupport partition type"))); } /* In normal condition, it should never happen. * But if the Query is from a view/rule contains a partition, this case may happen if the partition is dropped by * DDL operation, such as DROP/SPLIT/MERGE/TRUNCATE (UPDATE GLOBAL INDEX)/EXCHANGE (UPDATE GLOBAL INDEX) */ if (!find) { pfree_ext(pruningRes); ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_TABLE), errmsg("fail to find partition with oid %u for partitioned table %u", partitionOid, rel->rd_id), errdetail("this partition may have already been dropped by DDL operation"), errhint("Check if this query contains a view that refrences the target partition. " "If so, REBUILD this view."))); } pruningRes->bm_rangeSelectedPartitions = bms_make_singleton(partitionSeq); generateListFromPruningBM(pruningRes); if (RelationIsSubPartitioned(rel)) { SubPartitionPruningResult *subPartPruningRes = PreGetSubPartitionFullPruningResult(rel, partitionOid); if (subPartPruningRes == NULL) { return pruningRes; } subPartPruningRes->partSeq = partitionSeq; pruningRes->ls_selectedSubPartitions = lappend(pruningRes->ls_selectedSubPartitions, subPartPruningRes); } return pruningRes; } /* * @@GaussDB@@ * Target : data subpartition * Brief : select * from subpartition (subpartition_name) * Description : eliminate subpartitions which don't contain those tuple satisfy expression. * return value : non-eliminated subpartitions. */ PruningResult* SingleSubPartitionPruningForRestrictInfo(Oid subPartitionOid, Relation rel, Oid partOid) { int partitionSeq = 0; int subPartitionSeq = 0; PruningResult* pruningRes = NULL; if (!PointerIsValid(rel) || !OidIsValid(subPartitionOid) || !OidIsValid(partOid)) { return NULL; } /* shouldn't happen */ if (!PointerIsValid(rel->partMap)) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("relation of oid=\"%u\" is not partitioned table", rel->rd_id))); } pruningRes = makeNode(PruningResult); pruningRes->state = PRUNING_RESULT_SUBSET; partitionSeq = getPartitionElementsIndexByOid(rel, partOid); /* In normal condition, it should never happen. * But if the Query is from a view/rule contains a subpartition, this case may happen if the parent partition of * this subpartition is dropped by DDL operation, such as DROP/TRUNCATE (UPDATE GLOBAL INDEX) */ if (partitionSeq < 0) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_TABLE), errmsg("fail to find partition with oid %u for partitioned table %u", partOid, rel->rd_id), errdetail("this partition may have already been dropped by DDL operation"), errhint("Check if this query contains a view that refrences a subpartition owned by the target partition. " "If so, REBUILD this view."))); } Partition part = partitionOpen(rel, partOid, NoLock); Relation partRel = partitionGetRelation(rel, part); subPartitionSeq = getPartitionElementsIndexByOid(partRel, subPartitionOid); /* In normal condition, it should never happen. * But if the Query is from a view/rule contains a subpartition, this case may happen if the subpartition is dropped * by DDL operation, such as DROP/SPLIT/MERGE/TRUNCATE (UPDATE GLOBAL INDEX)/EXCHANGE (UPDATE GLOBAL INDEX) */ if (subPartitionSeq < 0) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_TABLE), errmsg("fail to find subpartition with oid %u for partitioned table %u", subPartitionOid, rel->rd_id), errdetail("this subpartition may have already been dropped by DDL operation"), errhint("Check if this query contains a view that refrences the target subpartition. " "If so, REBUILD this view."))); } releaseDummyRelation(&partRel); partitionClose(rel, part, NoLock); SubPartitionPruningResult *subPartPruningRes = makeNode(SubPartitionPruningResult); subPartPruningRes->bm_selectedSubPartitions = bms_add_member(subPartPruningRes->bm_selectedSubPartitions, subPartitionSeq); subPartPruningRes->ls_selectedSubPartitions = lappend_int(subPartPruningRes->ls_selectedSubPartitions, subPartitionSeq); subPartPruningRes->partSeq = partitionSeq; pruningRes->ls_selectedSubPartitions = lappend(pruningRes->ls_selectedSubPartitions, subPartPruningRes); pruningRes->ls_rangeSelectedPartitions = lappend_int(pruningRes->ls_rangeSelectedPartitions, partitionSeq); pruningRes->bm_rangeSelectedPartitions = bms_make_singleton(partitionSeq); return pruningRes; } /* * @@GaussDB@@ * Brief * Description : eliminate partitions which don't contain those tuple satisfy expression. * return value: non-eliminated partitions. */ PruningResult* partitionPruningForExpr(PlannerInfo* root, RangeTblEntry* rte, Relation rel, Expr* expr) { PruningContext* context = NULL; PruningResult* result = NULL; if (!PointerIsValid(rel) || !PointerIsValid(rte) || !PointerIsValid(expr)) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("partitionPruningForExpr: parameter can not be null"))); } context = (PruningContext*)palloc0(sizeof(PruningContext)); context->root = root; context->relation = rel; context->estate = NULL; context->rte = rte; context->GetPartitionMap = GetRelPartitionMap; context->pruningType = PruningPartition; bool isnull = PartExprKeyIsNull(rel, NULL); if (isnull) { if (rel->partMap != NULL && (rel->partMap->type == PART_TYPE_LIST || rel->partMap->type == PART_TYPE_HASH)) { // for List/Hash partitioned table result = partitionEqualPruningWalker(rel->partMap->type, expr, context); } else { // for Range/Interval partitioned table result = partitionPruningWalker(expr, context); } } else { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; } if (result->exprPart != NULL || result->paramArg != NULL) { Param* paramArg = (Param *)copyObject(result->paramArg); bool isPbeSinlePartition = result->isPbeSinlePartition; destroyPruningResult(result); result = getFullPruningResult(rel); result->expr = expr; result->paramArg = paramArg; result->isPbeSinlePartition = isPbeSinlePartition; return result; } /* Never happen, just to be self-contained */ if (!PointerIsValid(result)) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("get null for partition pruning"))); } partitionPruningFromBoundary(context, result); if (PruningResultIsFull(result)) { destroyPruningResult(result); result = getFullPruningResult(rel); } if (PointerIsValid(context)) { pfree_ext(context); } return result; } /* * @@GaussDB@@ * Brief * Description : the hook function for expression walker function. * If the expression node is AND,OR or OpExpr(>,>=,=,<=,<), this function would pruning, * else get all partitions of partitioned table. * return value: true if success, else false. * This function will print log, if failed, however fill the PruningReslut with full set of partitions. */ PruningResult* partitionPruningWalker(Expr* expr, PruningContext* pruningCtx) { PruningResult* result = NULL; switch (nodeTag(expr)) { case T_BoolExpr: { BoolExpr* boolExpr = NULL; boolExpr = (BoolExpr*)expr; result = partitionPruningFromBoolExpr(boolExpr, pruningCtx); } break; case T_OpExpr: { OpExpr* opExpr = NULL; opExpr = (OpExpr*)expr; result = recordBoundaryFromOpExpr(opExpr, pruningCtx); } break; case T_NullTest: { NullTest* ntExpr = NULL; ntExpr = (NullTest*)expr; result = partitionPruningFromNullTest(ntExpr, pruningCtx); } break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr* arrayExpr = NULL; arrayExpr = (ScalarArrayOpExpr*)expr; result = partitionPruningFromScalarArrayOpExpr(arrayExpr, pruningCtx); } break; case T_Const: { Const* cst = (Const*)expr; if (BOOLOID == cst->consttype && !cst->constisnull && 0 == cst->constvalue) { result = makeNode(PruningResult); result->state = PRUNING_RESULT_EMPTY; } else { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; } result->isPbeSinlePartition = false; } break; default: { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; } break; } return result; } static PruningResult* partitionEqualPruningWalker(PartitionType partType, Expr* expr, PruningContext* pruningCtx) { PruningResult* result = NULL; AssertEreport(PointerIsValid(pruningCtx), MOD_OPT, "pruningCtx pointer is NULL."); AssertEreport(PointerIsValid(expr), MOD_OPT, "expr pointer is NULL."); switch (nodeTag(expr)) { case T_BoolExpr: { BoolExpr* boolExpr = NULL; boolExpr = (BoolExpr*)expr; result = partitionPruningFromBoolExpr(partType, boolExpr, pruningCtx); } break; case T_OpExpr: { OpExpr* opExpr = NULL; opExpr = (OpExpr*)expr; result = recordEqualFromOpExpr(partType, opExpr, pruningCtx); } break; case T_NullTest: { NullTest* ntExpr = NULL; ntExpr = (NullTest*)expr; result = partitionPruningFromNullTest(partType, ntExpr, pruningCtx); } break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr* arrayExpr = NULL; arrayExpr = (ScalarArrayOpExpr*)expr; result = partitionPruningFromScalarArrayOpExpr(partType, arrayExpr, pruningCtx); } break; case T_Const: { Const* cst = (Const*)expr; if (BOOLOID == cst->consttype && !cst->constisnull && 0 == cst->constvalue) { result = makeNode(PruningResult); result->state = PRUNING_RESULT_EMPTY; } else { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; } result->isPbeSinlePartition = false; } break; default: { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; } break; } return result; } /* * @@GaussDB@@ * Brief * Description : If expression node is AND or OR, this function would be triggered. * It will process left child and right child by call expression_tree_walker(). * return value: true if success, else false. * This function will print log, if failed, however fill the PruningReslut with full set of partitions. */ static PruningResult* partitionPruningFromBoolExpr(const BoolExpr* expr, PruningContext* context) { Expr* arg = NULL; List* resultList = NULL; ListCell* cell = NULL; PruningResult* result = NULL; PruningResult* iterator = NULL; if (context != NULL) { AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL."); } if (expr->boolop == NOT_EXPR) { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } foreach (cell, expr->args) { arg = (Expr*)lfirst(cell); iterator = partitionPruningWalker(arg, context); if (iterator->paramArg != NULL || iterator->exprPart != NULL) { return iterator; } resultList = lappend(resultList, iterator); } switch (expr->boolop) { case AND_EXPR: result = intersectChildPruningResult(resultList, context); break; case OR_EXPR: result = unionChildPruningResult(resultList, context); result->isPbeSinlePartition = false; break; case NOT_EXPR: default: break; } destroyPruningResultList(resultList); return result; } static PruningResult* partitionPruningFromBoolExpr(PartitionType partType, const BoolExpr* expr, PruningContext* context) { Expr* arg = NULL; List* resultList = NULL; ListCell* cell = NULL; PruningResult* result = NULL; PruningResult* iterator = NULL; AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL."); AssertEreport(PointerIsValid(context), MOD_OPT, "Pointer context is NULL."); AssertEreport(PointerIsValid(context->relation), MOD_OPT, "Pointer context->relation is NULL."); if (expr->boolop == NOT_EXPR) { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } foreach (cell, expr->args) { arg = (Expr*)lfirst(cell); iterator = partitionEqualPruningWalker(partType, arg, context); if (iterator->paramArg != NULL || iterator->exprPart != NULL) { return iterator; } resultList = lappend(resultList, iterator); } switch (expr->boolop) { case AND_EXPR: result = intersectChildPruningResult(resultList, context); break; case OR_EXPR: result = unionChildPruningResult(resultList, context); result->isPbeSinlePartition = false; break; case NOT_EXPR: default: break; } destroyPruningResultList(resultList); return result; } static PruningResult* partitionPruningFromNullTest(NullTest* expr, PruningContext* context) { PruningResult* result = NULL; RangePartitionMap* partMap = NULL; int partKeyNum = 0; int attrOffset = 0; Expr* arg = NULL; Var* var = NULL; RangeElement range; AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL."); arg = expr->arg; result = makeNode(PruningResult); if (T_RelabelType == nodeTag(arg)) { arg = ((RelabelType*)arg)->arg; } if (T_Var != nodeTag(arg)) { result->state = PRUNING_RESULT_FULL; return result; } var = (Var*)arg; /* Var's column MUST belongs to parition key columns */ partMap = (RangePartitionMap*)(GetPartitionMap(context)); partKeyNum = partMap->partitionKey->dim1; attrOffset = varIsInPartitionKey(var->varattno, partMap->partitionKey, partKeyNum); if (attrOffset != 0) { result->state = PRUNING_RESULT_FULL; return result; } if (expr->nulltesttype != IS_NULL) { result->state = PRUNING_RESULT_FULL; return result; } if (PartitionMapIsInterval(partMap)) { result->state = PRUNING_RESULT_EMPTY; return result; } range = partMap->rangeElements[partMap->rangeElementsNum - 1]; if (!(range.boundary[0]->ismaxvalue)) { result->state = PRUNING_RESULT_EMPTY; return result; } result->state = PRUNING_RESULT_SUBSET; result->isPbeSinlePartition = true; result->bm_rangeSelectedPartitions = bms_make_singleton(partMap->rangeElementsNum - 1); return result; } static PruningResult* partitionPruningFromNullTest(PartitionType partType, NullTest* expr, PruningContext* context) { PruningResult* result = NULL; int partKeyNum = 0; int attrOffset = 0; Expr* arg = NULL; Var* var = NULL; AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL."); arg = expr->arg; result = makeNode(PruningResult); if (T_RelabelType == nodeTag(arg)) { arg = ((RelabelType*)arg)->arg; } if (T_Var != nodeTag(arg)) { result->state = PRUNING_RESULT_FULL; return result; } var = (Var*)arg; /* Var's column MUST belongs to parition key columns */ if (partType == PART_TYPE_LIST) { ListPartitionMap* listPartMap = (ListPartitionMap*)(context->relation->partMap); partKeyNum = listPartMap->partitionKey->dim1; attrOffset = varIsInPartitionKey(var->varattno, listPartMap->partitionKey, partKeyNum); } else { HashPartitionMap* hashPartMap = (HashPartitionMap*)(context->relation->partMap); partKeyNum = hashPartMap->partitionKey->dim1; attrOffset = varIsInPartitionKey(var->varattno, hashPartMap->partitionKey, partKeyNum); } if (attrOffset != 0) { result->state = PRUNING_RESULT_FULL; return result; } if (expr->nulltesttype == IS_NULL && partType == PART_TYPE_LIST) { ListPartitionMap* listPartMap = (ListPartitionMap*)(context->relation->partMap); bool hasDefault = false; int defaultPartitionIndex = -1; for (int i = 0; i < listPartMap->listElementsNum; i++) { ListPartElement list = listPartMap->listElements[i]; if (list.boundary[0]->ismaxvalue) { hasDefault = true; defaultPartitionIndex = i; break; } } if (hasDefault) { result->state = PRUNING_RESULT_SUBSET; result->isPbeSinlePartition = true; result->bm_rangeSelectedPartitions = bms_make_singleton(defaultPartitionIndex); return result; } } if (expr->nulltesttype != IS_NULL) { result->state = PRUNING_RESULT_FULL; } else { result->state = PRUNING_RESULT_EMPTY; } result->isPbeSinlePartition = false; return result; } static PruningResult* intersectChildPruningResult(const List* resultList, PruningContext* context) { PruningResult* iteratorResult = NULL; PruningBoundary* tempBoundary = NULL; PruningResult* result = NULL; Bitmapset* bmsRange = NULL; int intervalOffset = -1; ListCell* cell = NULL; result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; AssertEreport(resultList, MOD_OPT, ""); foreach (cell, resultList) { iteratorResult = (PruningResult*)lfirst(cell); AssertEreport(iteratorResult, MOD_OPT, "iteratorResult context is NNULL."); if (iteratorResult->state == PRUNING_RESULT_EMPTY) { result->state = PRUNING_RESULT_EMPTY; result->isPbeSinlePartition = false; return result; } else if (iteratorResult->state == PRUNING_RESULT_FULL) { continue; } if (PruningResultIsFull(result)) { result->boundary = copyBoundary(iteratorResult->boundary); result->intervalOffset = iteratorResult->intervalOffset; result->bm_rangeSelectedPartitions = bms_copy(iteratorResult->bm_rangeSelectedPartitions); result->intervalSelectedPartitions = bms_copy(iteratorResult->intervalSelectedPartitions); result->state = iteratorResult->state; result->paramArg = (Param *)copyObject(iteratorResult->paramArg); } else if (result != NULL) { if (intervalOffset == -1 && iteratorResult->intervalOffset >= 0) { intervalOffset = iteratorResult->intervalOffset; } if (intervalOffset >= 0 && iteratorResult->intervalOffset >= 0 && intervalOffset != iteratorResult->intervalOffset) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("For every node in same expression, pruning result's intervalOffset MUST be same"))); } if (result->bm_rangeSelectedPartitions == NULL) { result->bm_rangeSelectedPartitions = bms_copy(iteratorResult->bm_rangeSelectedPartitions); } else if (iteratorResult->bm_rangeSelectedPartitions != NULL) { bmsRange = bms_intersect(result->bm_rangeSelectedPartitions, iteratorResult->bm_rangeSelectedPartitions); bms_free_ext(result->bm_rangeSelectedPartitions); result->bm_rangeSelectedPartitions = bmsRange; } if (result->boundary == NULL) { result->boundary = copyBoundary(iteratorResult->boundary); } else { tempBoundary = mergeBoundary(result->boundary, iteratorResult->boundary); destroyPruningBoundary(result->boundary); result->boundary = tempBoundary; } if (BoundaryIsEmpty(result->boundary)) { result->state = PRUNING_RESULT_EMPTY; result->isPbeSinlePartition = false; break; } result->state = PRUNING_RESULT_SUBSET; } if (result->state != PRUNING_RESULT_EMPTY && iteratorResult->isPbeSinlePartition) { result->isPbeSinlePartition = true; } } if (PruningResultIsEmpty(result)) { destroyPruningResult(result); result = makeNode(PruningResult); result->state = PRUNING_RESULT_EMPTY; result->isPbeSinlePartition = false; result->intervalOffset = -1; } return result; } static PruningResult* unionChildPruningResult(const List* resultList, PruningContext* context) { PruningResult* iteratorResult = NULL; PruningResult* result = NULL; Bitmapset* bmsRange = NULL; Bitmapset* bmsInterval = NULL; int intervalOffset = -1; ListCell* cell = NULL; result = makeNode(PruningResult); if (list_length(resultList) == 0) { result->state = PRUNING_RESULT_FULL; return result; } foreach (cell, resultList) { iteratorResult = (PruningResult*)lfirst(cell); if (!PointerIsValid(iteratorResult)) { continue; } partitionPruningFromBoundary(context, iteratorResult); if (iteratorResult->state == PRUNING_RESULT_EMPTY) { continue; } else if (iteratorResult->state == PRUNING_RESULT_FULL || iteratorResult->paramArg != NULL) { result->state = PRUNING_RESULT_FULL; return result; } else { if (intervalOffset == -1 && iteratorResult->intervalOffset >= 0) { intervalOffset = iteratorResult->intervalOffset; } if (intervalOffset >= 0 && iteratorResult->intervalOffset >= 0 && intervalOffset != iteratorResult->intervalOffset) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("For every node in same expression, pruning result's intervalOffset MUST be same"))); } bmsRange = bms_union(result->bm_rangeSelectedPartitions, iteratorResult->bm_rangeSelectedPartitions); bms_free_ext(result->bm_rangeSelectedPartitions); result->bm_rangeSelectedPartitions = bmsRange; if (intervalOffset >= 0) { bmsInterval = bms_union(result->intervalSelectedPartitions, iteratorResult->intervalSelectedPartitions); bms_free_ext(result->intervalSelectedPartitions); result->intervalSelectedPartitions = bmsInterval; } } } if (bms_is_empty(result->intervalSelectedPartitions) && bms_is_empty(result->bm_rangeSelectedPartitions)) { destroyPruningResult(result); result = makeNode(PruningResult); result->state = PRUNING_RESULT_EMPTY; result->intervalOffset = -1; } else { result->intervalOffset = intervalOffset; result->state = PRUNING_RESULT_SUBSET; } return result; } static PruningResult* partitionPruningFromScalarArrayOpExpr (const ScalarArrayOpExpr* arrayExpr, PruningContext* pruningCtx) { OpExpr* expr = NULL; Expr* larg = NULL; Expr* rarg = NULL; List* exprList = NULL; PruningResult* result = NULL; bool success = false; AssertEreport(list_length(arrayExpr->args) == 2, MOD_OPT, "Expected two operands but get exception."); larg = (Expr*)list_nth(arrayExpr->args, 0); rarg = (Expr*)list_nth(arrayExpr->args, 1); if (T_RelabelType == nodeTag(larg)) { larg = ((RelabelType*)larg)->arg; } if (T_Var != nodeTag(larg) || (T_ArrayExpr != nodeTag(rarg) && T_Const != nodeTag(rarg))) { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } if (T_ArrayExpr == nodeTag(rarg)) { List* eleList = NULL; ListCell* element = NULL; eleList = ((ArrayExpr*)rarg)->elements; foreach (element, eleList) { Expr* eleExpr = (Expr*)lfirst(element); List* eleArgs = NULL; eleArgs = list_make2(copyObject(larg), copyObject(eleExpr)); expr = (OpExpr*)makeNode(OpExpr); expr->args = eleArgs; expr->inputcollid = arrayExpr->inputcollid; expr->location = 0; expr->opcollid = arrayExpr->opfuncid; expr->opfuncid = arrayExpr->opfuncid; expr->opno = arrayExpr->opno; expr->opresulttype = BOOLOID; expr->opretset = false; exprList = lappend(exprList, expr); } if (arrayExpr->useOr) { result = partitionPruningWalker( (Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx); } else { result = partitionPruningWalker( (Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx); } success = true; } else if (T_Const == nodeTag(rarg)) { Const* con = (Const*)rarg; Oid eleType = get_element_type(con->consttype); if (!OidIsValid(eleType)) ereport( ERROR, (errmodule(MOD_OPT), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type. ")))); int16 typlen = get_typlen(eleType); bool typbyval = get_typbyval(eleType); /* Cannot be zero for an existing type */ AssertEreport(typlen, MOD_OPT, "Unexpected 0 type length."); if (type_is_array(con->consttype) && PointerIsValid(con->constvalue) && ARR_NDIM((ArrayType*)con->constvalue) == 1) { Datum value; bool isnull = false; Const* eleConst = NULL; ArrayType* arrayValue = (ArrayType*)con->constvalue; ArrayIterator itr = array_create_iterator(arrayValue, 0); while (array_iterate(itr, &value, &isnull)) { List* eleArgs = NULL; eleConst = makeConst(eleType, con->consttypmod, con->constcollid, typlen, isnull ? PointerGetDatum(NULL) : value, isnull, typbyval); eleArgs = list_make2(copyObject(larg), eleConst); expr = (OpExpr*)makeNode(OpExpr); expr->args = eleArgs; expr->inputcollid = arrayExpr->inputcollid; expr->location = 0; expr->opcollid = arrayExpr->opfuncid; expr->opfuncid = arrayExpr->opfuncid; expr->opno = arrayExpr->opno; expr->opresulttype = BOOLOID; expr->opretset = false; exprList = lappend(exprList, expr); } if (arrayExpr->useOr) { result = partitionPruningWalker( (Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx); } else { result = partitionPruningWalker( (Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx); } success = true; } } if (success) { return result; } else { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } } static PruningResult* partitionPruningFromScalarArrayOpExpr(PartitionType partType, const ScalarArrayOpExpr* arrayExpr, PruningContext* pruningCtx) { OpExpr* expr = NULL; Expr* larg = NULL; Expr* rarg = NULL; List* exprList = NULL; PruningResult* result = NULL; bool success = false; AssertEreport(list_length(arrayExpr->args) == 2, MOD_OPT, "Expected two operands but get exception."); larg = (Expr*)list_nth(arrayExpr->args, 0); rarg = (Expr*)list_nth(arrayExpr->args, 1); if (T_RelabelType == nodeTag(larg)) { larg = ((RelabelType*)larg)->arg; } if (T_Var != nodeTag(larg) || (T_ArrayExpr != nodeTag(rarg) && T_Const != nodeTag(rarg))) { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } if (T_ArrayExpr == nodeTag(rarg)) { List* eleList = NULL; ListCell* element = NULL; eleList = ((ArrayExpr*)rarg)->elements; foreach (element, eleList) { Expr* eleExpr = (Expr*)lfirst(element); List* eleArgs = NULL; eleArgs = list_make2(copyObject(larg), copyObject(eleExpr)); expr = (OpExpr*)makeNode(OpExpr); expr->args = eleArgs; expr->inputcollid = arrayExpr->inputcollid; expr->location = 0; expr->opcollid = arrayExpr->opfuncid; expr->opfuncid = arrayExpr->opfuncid; expr->opno = arrayExpr->opno; expr->opresulttype = BOOLOID; expr->opretset = false; exprList = lappend(exprList, expr); } if (arrayExpr->useOr) { result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx); } else { result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx); } success = true; } else if (T_Const == nodeTag(rarg)) { Const* con = (Const*)rarg; Oid eleType = get_element_type(con->consttype); if (!OidIsValid(eleType)) ereport( ERROR, (errmodule(MOD_OPT), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type. ")))); int16 typlen = get_typlen(eleType); bool typbyval = get_typbyval(eleType); /* Cannot be zero for an existing type */ AssertEreport(typlen, MOD_OPT, "Unexpected 0 type length."); if (type_is_array(con->consttype) && PointerIsValid(con->constvalue) && ARR_NDIM((ArrayType*)con->constvalue) == 1) { Datum value; bool isnull = false; Const* eleConst = NULL; ArrayType* arrayValue = (ArrayType*)con->constvalue; ArrayIterator itr = array_create_iterator(arrayValue, 0); while (array_iterate(itr, &value, &isnull)) { List* eleArgs = NULL; eleConst = makeConst(eleType, con->consttypmod, con->constcollid, typlen, isnull ? PointerGetDatum(NULL) : value, isnull, typbyval); eleArgs = list_make2(copyObject(larg), eleConst); expr = (OpExpr*)makeNode(OpExpr); expr->args = eleArgs; expr->inputcollid = arrayExpr->inputcollid; expr->location = 0; expr->opcollid = arrayExpr->opfuncid; expr->opfuncid = arrayExpr->opfuncid; expr->opno = arrayExpr->opno; expr->opresulttype = BOOLOID; expr->opretset = false; exprList = lappend(exprList, expr); } if (arrayExpr->useOr) { result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx); } else { result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx); } success = true; } } if (success) { return result; } else { result = makeNode(PruningResult); result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } } static Node* EvalExprValueWhenPruning(PruningContext* context, Node* node) { Node* result = NULL; switch (context->pruningType) { case PruningPartition: result = estimate_expression_value(context->root, node, context->estate); break; case PruningSlice: result = eval_const_expressions_params(NULL, node, context->boundParams); break; default: Assert(false); } return result; } /* * @@GaussDB@@ * Brief * Description : If expression node is OpExpr(>,>=,=,<=,<), this function would be triggered. * This function fill PruningReslut's boundary with const from expression. * return value: true if success, else false. * This function will print log, if failed, however fill the PruningReslut with full set of partitions. */ static PruningResult* recordBoundaryFromOpExpr(const OpExpr* expr, PruningContext* context) { int partKeyNum = 0; int attrOffset = -1; char* opName = NULL; Expr* leftArg = NULL; Expr* rightArg = NULL; Const* constArg = NULL; Const* constMax = NULL; Var* varArg = NULL; RangePartitionMap* partMap = NULL; PruningBoundary* boundary = NULL; PruningResult* result = NULL; bool rightArgIsConst = true; Node* node = NULL; Param* paramArg = NULL; OpExpr* exprPart = NULL; if (context != NULL) { AssertEreport(PointerIsValid(context->relation), MOD_OPT, "Unexpected NULL pointer for context->relation."); AssertEreport(PointerIsValid(GetPartitionMap(context)), MOD_OPT, "Unexpected NULL pointer for context->relation->partMap."); } result = makeNode(PruningResult); /* length of args MUST be 2 */ if (!PointerIsValid(expr) || list_length(expr->args) != 2 || !PointerIsValid(opName = get_opname(expr->opno))) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } leftArg = (Expr*)list_nth(expr->args, 0); rightArg = (Expr*)list_nth(expr->args, 1); /* In some case, there are several levels of relabel type */ while (leftArg && IsA(leftArg, RelabelType)) leftArg = ((RelabelType*)leftArg)->arg; while (rightArg && IsA(rightArg, RelabelType)) rightArg = ((RelabelType*)rightArg)->arg; if (leftArg == NULL || rightArg == NULL) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("Could not find enough valid args for Boundary From OpExpr")))); if (IsA(leftArg, Var)) { node = EvalExprValueWhenPruning(context, (Node*)rightArg); if (node != NULL) { rightArg = (Expr*)node; } } else if (IsA(rightArg, Var)) { node = EvalExprValueWhenPruning(context, (Node*)leftArg); if (node != NULL) { leftArg = (Expr*)node; } } /* one of args MUST be Const, and another argument Must be Var */ /* Be const or param, for PBE */ if (!(((T_Const == nodeTag(leftArg) || T_Param == nodeTag(leftArg) || T_OpExpr == nodeTag(leftArg)) && T_Var == nodeTag(rightArg)) || ((T_Const == nodeTag(rightArg) || T_Param == nodeTag(rightArg) || T_OpExpr == nodeTag(rightArg)) && T_Var == nodeTag(leftArg)))) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } if (T_Var == nodeTag(rightArg)) { if (T_Const == nodeTag(leftArg)) { constArg = (Const*)leftArg; } else if (T_Param == nodeTag(leftArg)) { paramArg = (Param*)leftArg; } else { exprPart = (OpExpr*)leftArg; } varArg = (Var*)rightArg; rightArgIsConst = false; } else { if (T_Const == nodeTag(rightArg)) { constArg = (Const*)rightArg; } else if (T_Param == nodeTag(rightArg)) { paramArg = (Param*)rightArg; } else { exprPart = (OpExpr*)rightArg; } varArg = (Var*)leftArg; } /* Var MUST represents for current relation */ if (context->pruningType == PruningPartition) { if (context->rte != NULL && context->rte->relid != context->relation->rd_id) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } } else { /* list/range distributed table slice pruning */ if (varArg->varlevelsup != 0 || varArg->varno != context->varno || paramArg != NULL || exprPart != NULL) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } } /* Var's column MUST belongs to parition key columns */ partMap = (RangePartitionMap*)(GetPartitionMap(context)); partKeyNum = partMap->partitionKey->dim1; attrOffset = varIsInPartitionKey(varArg->varattno, partMap->partitionKey, partKeyNum); if (attrOffset < 0) { result->state = PRUNING_RESULT_FULL; return result; } if (exprPart != NULL) { if (!PartitionMapIsRange(partMap)) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } else { result->exprPart = exprPart; result->state = PRUNING_RESULT_SUBSET; result->isPbeSinlePartition = false; return result; } } else if (paramArg != NULL) { if (paramArg->paramkind != PARAM_EXTERN || !PartitionMapIsRange(partMap)) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } else { result->paramArg = paramArg; result->state = PRUNING_RESULT_SUBSET; if (0 == strcmp("=", opName)) { result->isPbeSinlePartition = true; } return result; } } if (constArg->constisnull) { result->state = PRUNING_RESULT_EMPTY; result->isPbeSinlePartition = false; return result; } /* initialize PruningBoundary */ result->boundary = makePruningBoundary(partKeyNum); boundary = result->boundary; result->isPbeSinlePartition = false; /* decide the const is the top or bottom of boundary */ if ((0 == strcmp(">", opName) && rightArgIsConst) || (0 == strcmp("<", opName) && !rightArgIsConst)) { if (constArg->constisnull) { boundary->minClose[attrOffset] = false; boundary->min[attrOffset] = PointerGetDatum(constArg); boundary->state = PRUNING_RESULT_EMPTY; result->state = PRUNING_RESULT_EMPTY; } else { boundary->minClose[attrOffset] = false; boundary->min[attrOffset] = PointerGetDatum(constArg); boundary->state = PRUNING_RESULT_SUBSET; result->state = PRUNING_RESULT_SUBSET; } } else if ((0 == strcmp(">=", opName) && rightArgIsConst) || (0 == strcmp("<=", opName) && !rightArgIsConst)) { boundary->minClose[attrOffset] = true; boundary->min[attrOffset] = PointerGetDatum(constArg); boundary->state = PRUNING_RESULT_SUBSET; result->state = PRUNING_RESULT_SUBSET; } else if (0 == strcmp("=", opName)) { boundary->minClose[attrOffset] = true; boundary->min[attrOffset] = PointerGetDatum(constArg); boundary->maxClose[attrOffset] = true; if (constArg->constisnull) { boundary->max[attrOffset] = PointerGetDatum(constMax); } else { boundary->max[attrOffset] = PointerGetDatum(copyObject((void*)constArg)); } boundary->state = PRUNING_RESULT_SUBSET; result->state = PRUNING_RESULT_SUBSET; result->isPbeSinlePartition = true; } else if ((0 == strcmp("<=", opName) && rightArgIsConst) || (0 == strcmp(">=", opName) && !rightArgIsConst)) { boundary->maxClose[attrOffset] = true; boundary->max[attrOffset] = PointerGetDatum(constArg); if (constArg->constisnull) { boundary->state = PRUNING_RESULT_FULL; result->state = PRUNING_RESULT_FULL; } else { boundary->state = PRUNING_RESULT_SUBSET; result->state = PRUNING_RESULT_SUBSET; } } else if ((0 == strcmp("<", opName) && rightArgIsConst) || (0 == strcmp(">", opName) && !rightArgIsConst)) { boundary->maxClose[attrOffset] = false; boundary->max[attrOffset] = PointerGetDatum(constArg); boundary->state = PRUNING_RESULT_SUBSET; result->state = PRUNING_RESULT_SUBSET; } else { boundary->state = PRUNING_RESULT_FULL; result->state = PRUNING_RESULT_FULL; } result->intervalOffset = -1; return result; } static PruningResult* RecordEqualFromOpExprPart(const PartitionType partType, const PruningContext* context, const char* opName, Const* constMax, const Var* varArg, int attrOffset, PruningResult* result, Param* paramArg, OpExpr* exprPart, const Const* constArg, PruningBoundary* boundary) { int partKeyNum = 0; /* Var's column MUST belongs to parition key columns */ if (partType == PART_TYPE_LIST) { ListPartitionMap *partMap = (ListPartitionMap*)(context->relation->partMap); partKeyNum = partMap->partitionKey->dim1; attrOffset = varIsInPartitionKey(varArg->varattno, partMap->partitionKey, partKeyNum); } else if (partType == PART_TYPE_HASH) { HashPartitionMap *partMap = (HashPartitionMap*)(context->relation->partMap); partKeyNum = partMap->partitionKey->dim1; attrOffset = varIsInPartitionKey(varArg->varattno, partMap->partitionKey, partKeyNum); } if (attrOffset < 0) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } if (exprPart != NULL) { result->exprPart = exprPart; result->state = PRUNING_RESULT_SUBSET; result->isPbeSinlePartition = false; return result; } else if (paramArg != NULL) { result->paramArg = paramArg; result->state = PRUNING_RESULT_SUBSET; if (0 == strcmp("=", opName)) { result->isPbeSinlePartition = true; } return result; } if (constArg->constisnull) { result->state = PRUNING_RESULT_EMPTY; result->isPbeSinlePartition = false; return result; } /* initialize PruningBoundary */ result->boundary = makePruningBoundary(partKeyNum); boundary = result->boundary; /* decide the const is the top or bottom of boundary */ if (strcmp("=", opName) == 0) { boundary->minClose[attrOffset] = true; boundary->min[attrOffset] = PointerGetDatum(constArg); boundary->maxClose[attrOffset] = true; if (constArg->constisnull) { boundary->max[attrOffset] = PointerGetDatum(constMax); } else { boundary->max[attrOffset] = PointerGetDatum(copyObject((void*)constArg)); } boundary->state = PRUNING_RESULT_SUBSET; result->state = PRUNING_RESULT_SUBSET; result->isPbeSinlePartition = true; } else { boundary->state = PRUNING_RESULT_FULL; result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; } result->intervalOffset = -1; return result; } static PruningResult* recordEqualFromOpExpr(PartitionType partType, const OpExpr* expr, PruningContext* context) { int attrOffset = -1; char* opName = NULL; Expr* leftArg = NULL; Expr* rightArg = NULL; Const* constArg = NULL; Const* constMax = NULL; Var* varArg = NULL; PruningBoundary* boundary = NULL; PruningResult* result = NULL; bool rightArgIsConst = true; Node* node = NULL; Param* paramArg = NULL; OpExpr* exprPart = NULL; AssertEreport(PointerIsValid(context), MOD_OPT, "Unexpected NULL pointer for context."); AssertEreport(PointerIsValid(context->relation), MOD_OPT, "Unexpected NULL pointer for context->relation."); AssertEreport( PointerIsValid(context->relation->partMap), MOD_OPT, "Unexpected NULL pointer for context->relation->partMap."); result = makeNode(PruningResult); /* length of args MUST be 2 */ if (!PointerIsValid(expr) || list_length(expr->args) != 2 || !PointerIsValid(opName = get_opname(expr->opno))) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } leftArg = (Expr*)list_nth(expr->args, 0); rightArg = (Expr*)list_nth(expr->args, 1); /* In some case, there are several levels of relabel type */ while (leftArg && IsA(leftArg, RelabelType)) leftArg = ((RelabelType*)leftArg)->arg; while (rightArg && IsA(rightArg, RelabelType)) rightArg = ((RelabelType*)rightArg)->arg; if (leftArg == NULL || rightArg == NULL) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("Could not find enough valid args for Boundary From OpExpr")))); if (IsA(leftArg, Var)) { node = EvalExprValueWhenPruning(context, (Node*)rightArg); if (node != NULL) { rightArg = (Expr*)node; } } else if (IsA(rightArg, Var)) { node = EvalExprValueWhenPruning(context, (Node*)leftArg); if (node != NULL) { leftArg = (Expr*)node; } } /* one of args MUST be Const, and another argument Must be Var */ if (!(((T_Const == nodeTag(leftArg) || T_Param == nodeTag(leftArg) || T_OpExpr == nodeTag(leftArg)) && T_Var == nodeTag(rightArg)) || ((T_Const == nodeTag(rightArg) || T_Param == nodeTag(rightArg) || T_OpExpr == nodeTag(rightArg)) && T_Var == nodeTag(leftArg)))) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } if (T_Var == nodeTag(rightArg)) { if (T_Const == nodeTag(leftArg)) { constArg = (Const*)leftArg; } else if (T_Param == nodeTag(leftArg)) { paramArg = (Param*)leftArg; } else { exprPart = (OpExpr*)leftArg; } varArg = (Var*)rightArg; rightArgIsConst = false; } else { if (T_Const == nodeTag(rightArg)) { constArg = (Const*)rightArg; } else if (T_Param == nodeTag(rightArg)) { paramArg = (Param*)rightArg; } else { exprPart = (OpExpr*)rightArg; } varArg = (Var*)leftArg; } /* Var MUST represents for current relation */ if (context->pruningType == PruningPartition) { if (context->rte != NULL && context->rte->relid != context->relation->rd_id) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } } else { /* list/range distributed table slice pruning */ if (varArg->varlevelsup != 0 || varArg->varno != context->varno) { result->state = PRUNING_RESULT_FULL; result->isPbeSinlePartition = false; return result; } } PruningResult* res = RecordEqualFromOpExprPart(partType, context, opName, constMax, varArg, attrOffset, result, paramArg, exprPart, constArg, boundary); return res; } int varIsInPartitionKey(int attrNo, int2vector* partKeyAttrs, int partKeyNum) { int i = 0; int nKeyColumn = 0; char* cAttNums = NULL; int16* attNums = NULL; if (!PointerIsValid(partKeyAttrs) || attrNo <= 0) { return -1; } nKeyColumn = ARR_DIMS(partKeyAttrs)[0]; AssertEreport(nKeyColumn == partKeyNum, MOD_OPT, "Partition key deminsion inconsistent."); cAttNums = (char*)ARR_DATA_PTR(partKeyAttrs); attNums = (int16*)cAttNums; for (i = 0; i < nKeyColumn; i++) { if (attNums[i] == attrNo) { return i; } } return -1; } /* * @@GaussDB@@ * Brief * Description : Get eliminated partitions according to boundary of PruningResult. * Fill PruningResult bitmap attribute with bitmap for eliminated partitions * return value: true if success, else false. * if failed, This function will print log. */ int getRangeEnd(PruningContext* context) { int rangeEnd = -1; PartitionMap* partMap = GetPartitionMap(context); if (partMap->type == PART_TYPE_LIST) { rangeEnd = ((ListPartitionMap*)partMap)->listElementsNum - 1; } else if (partMap->type == PART_TYPE_HASH) { rangeEnd = ((HashPartitionMap*)partMap)->hashElementsNum - 1; } else { rangeEnd = ((RangePartitionMap*)partMap)->rangeElementsNum - 1; } return rangeEnd; } void partitionPruningFromBoundary(PruningContext *context, PruningResult* pruningResult) { Const** bottomValue = NULL; Const** topValue = NULL; Bitmapset* rangeBms = NULL; int rangeStart = -1; int rangeEnd = -1; int i = 0; int compare = 0; bool isTopClosed = true; AssertEreport(PointerIsValid(context), MOD_OPT, "Unexpected NULL pointer for context."); AssertEreport(PointerIsValid(pruningResult), MOD_OPT, "Unexpected NULL pointer for pruningResult."); if (NoNeedPruning(pruningResult)) { return; } bottomValue = (Const**)pruningResult->boundary->min; topValue = (Const**)pruningResult->boundary->max; for (i = 0; i < pruningResult->boundary->partitionKeyNum; i++) { if (!PointerIsValid(topValue[i])) { topValue[i] = makeMaxConst(InvalidOid, -1, InvalidOid); } if (!(pruningResult->boundary->maxClose[i])) { isTopClosed = false; } } compare = partitonKeyCompare(bottomValue, topValue, pruningResult->boundary->partitionKeyNum); if (compare > 0) { pruningResult->state = PRUNING_RESULT_EMPTY; return; } // compare the bottom and the intervalMax, if the bottom is large than or equal than intervalMax, pruning result is // empty. if ((context->pruningType == PruningPartition) && ( (GetPartitionMap(context))->type == PART_TYPE_LIST || (GetPartitionMap(context))->type == PART_TYPE_HASH)) { partitionRoutingForValueEqual( context->relation, bottomValue, pruningResult->boundary->partitionKeyNum, true, u_sess->opt_cxt.bottom_seq); u_sess->opt_cxt.top_seq->partArea = u_sess->opt_cxt.bottom_seq->partArea; u_sess->opt_cxt.top_seq->partitionId = u_sess->opt_cxt.bottom_seq->partitionId; u_sess->opt_cxt.top_seq->fileExist = u_sess->opt_cxt.bottom_seq->fileExist; u_sess->opt_cxt.top_seq->partSeq = u_sess->opt_cxt.bottom_seq->partSeq; } else { partitionRoutingForValueRange( context, bottomValue, pruningResult->boundary->partitionKeyNum, true, true, u_sess->opt_cxt.bottom_seq); if (IsCleanPruningBottom(u_sess->opt_cxt.bottom_seq, pruningResult, bottomValue)) { cleanPruningBottom(context, u_sess->opt_cxt.bottom_seq, bottomValue[0]); } partitionRoutingForValueRange( context, topValue, pruningResult->boundary->partitionKeyNum, isTopClosed, false, u_sess->opt_cxt.top_seq); if (IsCleanPruningTop(u_sess->opt_cxt.top_seq, pruningResult, topValue)) { cleanPruningTop(context, u_sess->opt_cxt.top_seq, topValue[0]); } } rangeEnd = getRangeEnd(context); if (!PartitionLogicalExist(u_sess->opt_cxt.bottom_seq) && !PartitionLogicalExist(u_sess->opt_cxt.top_seq)) { /* pruning failed or result contains all partition */ pruningResult->state = PRUNING_RESULT_EMPTY; } else if (!PartitionLogicalExist(u_sess->opt_cxt.bottom_seq)) { rangeStart = 0; rangeEnd = u_sess->opt_cxt.top_seq->partSeq; } else if (!PartitionLogicalExist(u_sess->opt_cxt.top_seq)) { rangeStart = u_sess->opt_cxt.bottom_seq->partSeq; } else { rangeStart = u_sess->opt_cxt.bottom_seq->partSeq; rangeEnd = u_sess->opt_cxt.top_seq->partSeq; } if (0 <= rangeStart) { for (i = rangeStart; i <= rangeEnd; i++) { rangeBms = bms_add_member(rangeBms, i); } if (PointerIsValid(pruningResult->bm_rangeSelectedPartitions)) { Bitmapset* tempBms = NULL; tempBms = bms_intersect(pruningResult->bm_rangeSelectedPartitions, rangeBms); bms_free_ext(pruningResult->bm_rangeSelectedPartitions); bms_free_ext(rangeBms); pruningResult->bm_rangeSelectedPartitions = tempBms; } else { pruningResult->bm_rangeSelectedPartitions = rangeBms; } } partitionFilter(context, pruningResult); if (pruningResult->boundary) { destroyPruningBoundary(pruningResult->boundary); pruningResult->boundary = NULL; } } /************************************************************************************ * pruning for multi-column partition key ************************************************************************************/ static void partitionFilter(PruningContext* context, PruningResult* pruningResult) { Bitmapset* resourceBms = NULL; Bitmapset* result = NULL; if (context->pruningType == PruningPartition) { /* partition pruning */ if (!RELATION_IS_PARTITIONED(context->relation) || !PartitionMapIsRange(GetPartitionMap(context)) || pruningResult->boundary->partitionKeyNum == 1) { return; } } else { /* slice pruning */ if (!PartitionMapIsRange(GetPartitionMap(context)) || pruningResult->boundary->partitionKeyNum == 1) { return; } } if (bms_is_empty(pruningResult->bm_rangeSelectedPartitions)) { return; } resourceBms = bms_copy(pruningResult->bm_rangeSelectedPartitions); while (true) { int curPart; curPart = bms_first_member(resourceBms); if (curPart < 0) { break; } if (!partitionShouldEliminated(context, curPart, pruningResult->boundary)) { result = bms_add_member(result, curPart); } } bms_free_ext(pruningResult->bm_rangeSelectedPartitions); if (bms_is_empty(result)) { pruningResult->bm_rangeSelectedPartitions = NULL; } else { pruningResult->bm_rangeSelectedPartitions = result; } bms_free_ext(resourceBms); } #define CONSTISMIN(value) ((value) == NULL || (value)->constisnull) #define CONSTISMAX(value) ((value) != NULL && (value)->ismaxvalue) static bool partitionShouldEliminated(PruningContext* context, int partSeq, PruningBoundary* boundary) { int i = 0; PartKeyRange* partRange = NULL; bool isSelected = true; partRange = constructPartKeyRange(context, partSeq); if (!PointerIsValid(partRange)) { return false; } for (i = 0; i < partRange->num && isSelected; i++) { PartKeyColumnRange* columnRange = &(partRange->columnRanges[i]); int prevCompareMin = 0; int prevCompareMax = 0; int nextCompareMin = 0; int nextCompareMax = 0; bool frontSectionIntersected = false; bool behindSectionIntersected = false; switch (columnRange->mode) { case PARTKEY_RANGE_MODE_POINT: AssertEreport(0 == partitonKeyCompare(&(columnRange->prev), &(columnRange->next), 1) && !CONSTISMIN(columnRange->prev) && !CONSTISMAX(columnRange->prev), MOD_OPT, ""); prevCompareMin = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->min + i)), 1); prevCompareMax = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->max + i)), 1); if ((prevCompareMin > 0 && prevCompareMax < 0) || (prevCompareMin == 0 && boundary->minClose[i]) || (prevCompareMax == 0 && boundary->maxClose[i])) { isSelected = true; } else { isSelected = false; } break; case PARTKEY_RANGE_MODE_INCREASE: case PARTKEY_RANGE_MODE_RANGE: if ((CONSTISMIN(columnRange->prev) && CONSTISMIN((Const*)(boundary->min[i]))) || (CONSTISMAX(columnRange->next) && CONSTISMAX((Const*)(boundary->max[i])))) { isSelected = true; break; } prevCompareMin = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->min + i)), 1); prevCompareMax = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->max + i)), 1); nextCompareMin = partitonKeyCompare(&(columnRange->next), ((Const**)(boundary->min + i)), 1); nextCompareMax = partitonKeyCompare(&(columnRange->next), ((Const**)(boundary->max + i)), 1); if ((prevCompareMin > 0 && prevCompareMax < 0) || (prevCompareMin < 0 && nextCompareMin > 0) || (prevCompareMax == 0 && boundary->maxClose[i]) || (nextCompareMin == 0 && boundary->minClose[i]) || (prevCompareMin == 0) || (nextCompareMax == 0)) { isSelected = true; } else { isSelected = false; } break; case PARTKEY_RANGE_MODE_UNION: AssertEreport(!CONSTISMIN(columnRange->prev) && !CONSTISMAX(columnRange->prev) && !CONSTISMIN(columnRange->next) && !CONSTISMAX(columnRange->next), MOD_OPT, ""); if (CONSTISMIN((Const*)(boundary->min[i])) || CONSTISMAX((Const*)(boundary->max[i]))) { isSelected = true; break; } nextCompareMin = partitonKeyCompare(&(columnRange->next), ((Const**)(boundary->min + i)), 1); if (nextCompareMin > 0) { frontSectionIntersected = true; } if (!frontSectionIntersected) { prevCompareMax = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->max + i)), 1); if ((prevCompareMax < 0) || (prevCompareMax == 0 && boundary->maxClose[i])) { behindSectionIntersected = true; } } if (frontSectionIntersected || behindSectionIntersected) { isSelected = true; } else { isSelected = false; } break; default: ereport(ERROR, (errmodule(MOD_OPT), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("unsupported partition key column range mode")))); break; } } pfree_ext(partRange); return !isSelected; } static PartKeyRange* constructPartKeyRange(PruningContext* context, int partSeq) { RangeElement* prePartition = NULL; RangeElement* nextPartition = NULL; RangePartitionMap* partMap = NULL; PartKeyRange* result = NULL; Const* left = NULL; Const* right = NULL; int i = 0; int partKeyNum = 0; result = (PartKeyRange*)palloc0(sizeof(PartKeyRange)); partMap = (RangePartitionMap*)GetPartitionMap(context); partKeyNum = partMap->partitionKey->dim1; nextPartition = &(partMap->rangeElements[partSeq]); if (partSeq == 0) { result->num = 1; result->columnRanges[0].mode = PARTKEY_RANGE_MODE_RANGE; result->columnRanges[0].prev = NULL; result->columnRanges[0].next = nextPartition->boundary[0]; return result; } prePartition = &(partMap->rangeElements[partSeq - 1]); for (i = 0; i < partKeyNum; i++) { bool typeIsNumerable = false; PartKeyColumnRange* columnRange = NULL; int compare = 0; left = prePartition->boundary[i]; right = nextPartition->boundary[i]; if (left->consttype == INT2OID || left->consttype == INT4OID || left->consttype == INT8OID) { typeIsNumerable = true; } else { typeIsNumerable = false; } compare = partitonKeyCompare(&left, &right, 1); AssertEreport(compare <= 0, MOD_OPT, ""); columnRange = &(result->columnRanges[i]); if (compare == 0) { columnRange->mode = PARTKEY_RANGE_MODE_POINT; columnRange->next = right; columnRange->prev = left; continue; } else { if (typeIsNumerable && !CONSTISMIN(left) && !CONSTISMAX(right) && right != NULL) { int8 leftInt = (int8)left->constvalue; int8 rightInt = (int8)right->constvalue; if (leftInt + 1 == rightInt) { columnRange->mode = PARTKEY_RANGE_MODE_INCREASE; columnRange->prev = left; columnRange->next = right; result->num = i + 1; if (i + 1 >= partKeyNum) { break; } left = *(prePartition->boundary + i + 1); right = *(nextPartition->boundary + i + 1); columnRange = result->columnRanges + i + 1; if (left->ismaxvalue) { result->columnRanges[i].mode = PARTKEY_RANGE_MODE_POINT; result->columnRanges[i].prev = result->columnRanges[i].next; columnRange->mode = PARTKEY_RANGE_MODE_RANGE; columnRange->prev = NULL; columnRange->next = right; result->num++; } else if (partitonKeyCompare(&left, &right, 1) > 0) { columnRange->mode = PARTKEY_RANGE_MODE_UNION; columnRange->prev = left; columnRange->next = right; result->num++; } break; } else { columnRange->mode = PARTKEY_RANGE_MODE_RANGE; columnRange->prev = left; columnRange->next = right; result->num = i + 1; break; } } else { columnRange->mode = PARTKEY_RANGE_MODE_RANGE; columnRange->prev = left; columnRange->next = right; result->num = i + 1; break; } } } return result; } static PruningBoundary* mergeBoundary(PruningBoundary* leftBoundary, PruningBoundary* rightBoundary) { int i = 0; PruningBoundary* result = NULL; Const* leftValue = NULL; Const* rightValue = NULL; if (!PointerIsValid(leftBoundary) && !PointerIsValid(rightBoundary)) { return NULL; } if (!PointerIsValid(leftBoundary)) { return copyBoundary(leftBoundary); } if (!PointerIsValid(rightBoundary)) { return copyBoundary(rightBoundary); } AssertEreport(leftBoundary->partitionKeyNum == rightBoundary->partitionKeyNum, MOD_OPT, "Expected two boundaries to have same number of partition key, run into exception."); if (BoundaryIsFull(leftBoundary) || BoundaryIsEmpty(rightBoundary)) { return copyBoundary(rightBoundary); } if (BoundaryIsFull(rightBoundary) || BoundaryIsEmpty(leftBoundary)) { return copyBoundary(leftBoundary); } result = makePruningBoundary(leftBoundary->partitionKeyNum); if (BoundaryIsEmpty(leftBoundary) || BoundaryIsEmpty(rightBoundary)) { result->state = PRUNING_RESULT_EMPTY; return result; } result->state = PRUNING_RESULT_SUBSET; for (; i < result->partitionKeyNum; i++) { int compare = 0; /* merge bottom value */ leftValue = (Const*)DatumGetPointer(leftBoundary->min[i]); rightValue = (Const*)DatumGetPointer(rightBoundary->min[i]); if (!PointerIsValid(rightValue) && PointerIsValid(leftValue)) { result->min[i] = PointerGetDatum(copyObject((void*)leftValue)); result->minClose[i] = leftBoundary->minClose[i]; } else if (!PointerIsValid(leftValue) && PointerIsValid(rightValue)) { result->min[i] = PointerGetDatum(copyObject((void*)rightValue)); result->minClose[i] = rightBoundary->minClose[i]; } else if (PointerIsValid(leftValue) && PointerIsValid(rightValue)) { compare = partitonKeyCompare(&leftValue, &rightValue, 1); if (compare > 0) { result->min[i] = PointerGetDatum(copyObject((void*)leftValue)); result->minClose[i] = leftBoundary->minClose[i]; } else if (compare < 0) { result->min[i] = PointerGetDatum(copyObject((void*)rightValue)); result->minClose[i] = rightBoundary->minClose[i]; } else { result->min[i] = PointerGetDatum(copyObject((void*)leftValue)); if (!leftBoundary->minClose[i] || !rightBoundary->minClose[i]) { result->minClose[i] = false; } else { result->minClose[i] = true; } } } /* merge top value */ leftValue = (Const*)DatumGetPointer(leftBoundary->max[i]); rightValue = (Const*)DatumGetPointer(rightBoundary->max[i]); if (!PointerIsValid(rightValue) && PointerIsValid(leftValue)) { result->max[i] = PointerGetDatum(copyObject((void*)leftValue)); result->maxClose[i] = leftBoundary->maxClose[i]; } else if (!PointerIsValid(leftValue) && PointerIsValid(rightValue)) { result->max[i] = PointerGetDatum(copyObject((void*)rightValue)); result->maxClose[i] = rightBoundary->maxClose[i]; } else if (PointerIsValid(leftValue) && PointerIsValid(rightValue)) { compare = partitonKeyCompare(&leftValue, &rightValue, 1); if (compare > 0) { result->max[i] = PointerGetDatum(copyObject((void*)rightValue)); result->maxClose[i] = rightBoundary->maxClose[i]; } else if (compare < 0) { result->max[i] = PointerGetDatum(copyObject((void*)leftValue)); result->maxClose[i] = leftBoundary->maxClose[i]; } else { result->max[i] = PointerGetDatum(copyObject((void*)leftValue)); if (!leftBoundary->maxClose[i] || !rightBoundary->maxClose[i]) { result->maxClose[i] = false; } else { result->maxClose[i] = true; } } } leftValue = (Const*)DatumGetPointer(result->min[i]); rightValue = (Const*)DatumGetPointer(result->max[i]); if (leftValue != NULL && rightValue != NULL) { compare = partitonKeyCompare(&leftValue, &rightValue, 1); if (compare > 0 || (compare == 0 && !(result->minClose[i] && result->maxClose[i]))) { result->state = PRUNING_RESULT_EMPTY; break; } } } return result; } static void cleanPruningBottom(PruningContext *context, PartitionIdentifier* bottomSeq, Const* value) { int i = 0; RangePartitionMap* partMap = NULL; if (bottomSeq->partSeq < 0 || bottomSeq->partSeq >= ((RangePartitionMap*)GetPartitionMap(context))->rangeElementsNum || value == NULL) { return; } incre_partmap_refcount(GetPartitionMap(context)); partMap = (RangePartitionMap*)(GetPartitionMap(context)); for (i = bottomSeq->partSeq; i < partMap->rangeElementsNum; i++) { RangeElement* range = partMap->rangeElements + i; int compare = 0; compare = partitonKeyCompare(&value, range->boundary, 1); if (compare >= 0) { continue; } else { break; } } if (i >= partMap->rangeElementsNum) { i = -1; } bottomSeq->partSeq = i; decre_partmap_refcount(GetPartitionMap(context)); } static void cleanPruningTop(PruningContext *context, PartitionIdentifier* topSeq, Const* value) { int i = 0; RangePartitionMap* partMap = NULL; if (topSeq->partSeq < 0 || topSeq->partSeq >= ((RangePartitionMap*)GetPartitionMap(context))->rangeElementsNum || value == NULL) { return; } incre_partmap_refcount(GetPartitionMap(context)); partMap = (RangePartitionMap*)(GetPartitionMap(context)); for (i = topSeq->partSeq; i >= 0; i--) { RangeElement* range = partMap->rangeElements + i; int compare = 0; compare = partitonKeyCompare(&value, range->boundary, 1); if (compare <= 0) { continue; } else { break; } } if (i < topSeq->partSeq) { i++; } topSeq->partSeq = i; decre_partmap_refcount(GetPartitionMap(context)); } void destroyPruningResult(PruningResult* pruningResult) { if (!PointerIsValid(pruningResult)) { return; } if (PointerIsValid(pruningResult->bm_rangeSelectedPartitions)) { bms_free_ext(pruningResult->bm_rangeSelectedPartitions); pruningResult->bm_rangeSelectedPartitions = NULL; } if (PointerIsValid(pruningResult->intervalSelectedPartitions)) { bms_free_ext(pruningResult->intervalSelectedPartitions); pruningResult->intervalSelectedPartitions = NULL; } if (PointerIsValid(pruningResult->boundary)) { destroyPruningBoundary(pruningResult->boundary); pruningResult->boundary = NULL; } if (PointerIsValid(pruningResult->ls_rangeSelectedPartitions)) { list_free_ext(pruningResult->ls_rangeSelectedPartitions); pruningResult->ls_rangeSelectedPartitions = NIL; } if (PointerIsValid(pruningResult->expr)) { pfree(pruningResult->expr); pruningResult->expr = NULL; } if (PointerIsValid(pruningResult->exprPart)) { pfree(pruningResult->exprPart); pruningResult->exprPart = NULL; } if (PointerIsValid(pruningResult->paramArg)) { pfree(pruningResult->paramArg); pruningResult->paramArg = NULL; } if (PointerIsValid(pruningResult->partMap)) { DestroyPartitionMap(pruningResult->partMap); pruningResult->partMap = NULL; } pfree_ext(pruningResult); } static void destroyPruningResultList(List* resultList) { if (PointerIsValid(resultList)) { ListCell* cell = NULL; PruningResult* item = NULL; foreach (cell, resultList) { item = (PruningResult*)lfirst(cell); if (PointerIsValid(item)) { destroyPruningResult(item); } } list_free_ext(resultList); } return; } /* * @@GaussDB@@ * Target : data partition * Brief : get PartitionIdentifier for special SN in pruning result * Description : * Notes : start with 0 */ Oid getPartitionOidFromSequence(Relation relation, int partSeq, PartitionMap *oldmap) { /* if the partmap which copied before static pruning exists, it will replace the rel->partMap */ PartitionMap *partmap = oldmap ? oldmap : relation->partMap; Oid result = InvalidOid; AssertEreport(PointerIsValid(relation), MOD_OPT, "Unexpected NULL pointer for relation."); AssertEreport(PointerIsValid(partmap), MOD_OPT, "Unexpected NULL pointer for relation->partMap."); if (partmap->type == PART_TYPE_RANGE || partmap->type == PART_TYPE_INTERVAL) { int rangeElementsNum = ((RangePartitionMap*)partmap)->rangeElementsNum; if (partSeq < rangeElementsNum) { result = ((RangePartitionMap*)partmap)->rangeElements[partSeq].partitionOid; } else { ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("partSeq: %d out range of current relation partMap element num: %d.", partSeq, rangeElementsNum))); } /* do simple check, as rangeElements already be sorted */ if (partSeq > 0 && result == ((RangePartitionMap*)partmap)->rangeElements[partSeq - 1].partitionOid) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("Duplicate range partition map oids: %u, please try again.", result))); } } else if (PART_TYPE_LIST == partmap->type) { int listElementsNum = ((ListPartitionMap*)partmap)->listElementsNum; if (partSeq < listElementsNum) { result = ((ListPartitionMap*)partmap)->listElements[partSeq].partitionOid; } else { ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("partSeq: %d out range of current relation partMap element num: %d.", partSeq, listElementsNum))); } /* do simple check, as rangeElements already be sorted */ if (partSeq > 0 && result == ((ListPartitionMap*)partmap)->listElements[partSeq - 1].partitionOid) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("Duplicate range partition map oids: %u, please try again.", result))); } } else if (PART_TYPE_HASH == partmap->type) { int hashElementsNum = ((HashPartitionMap*)partmap)->hashElementsNum; if (partSeq < hashElementsNum) { result = ((HashPartitionMap*)partmap)->hashElements[partSeq].partitionOid; } else { ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("partSeq: %d out range of current relation partMap element num: %d.", partSeq, hashElementsNum))); } /* do simple check, as rangeElements already be sorted */ if (partSeq > 0 && result == ((HashPartitionMap*)partmap)->hashElements[partSeq - 1].partitionOid) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("Duplicate range partition map oids: %u, please try again.", result))); } } else { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Unupport partition strategy \"%d\"", partmap->type))); } return result; } /* * note that consts and constPointers are in and out parameter, and are provided by caller. */ void ConstructConstFromValues(Datum* datums, const bool* nulls, Oid* attrs, const int* colMap, int len, Const* consts, Const** constPointers) { HeapTuple tp; for (int i = 0; i < len; i++) { int col = colMap[i]; tp = SearchSysCache1((int)TYPEOID, ObjectIdGetDatum(attrs[col])); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp); consts[i].xpr.type = T_Const; consts[i].consttype = attrs[col]; consts[i].consttypmod = typtup->typtypmod; consts[i].constcollid = typtup->typcollation; consts[i].constlen = typtup->typlen; consts[i].constvalue = nulls[col] ? 0 : datums[col]; consts[i].constisnull = nulls[col]; consts[i].constbyval = typtup->typbyval; consts[i].location = -1; consts[i].ismaxvalue = false; constPointers[i] = &consts[i]; ReleaseSysCache(tp); } else { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("invalid type"))); } } return; } SubPartitionPruningResult* GetSubPartitionPruningResult(List* selectedSubPartitions, int partSeq) { ListCell* cell = NULL; foreach (cell, selectedSubPartitions) { SubPartitionPruningResult* subPartPruningResult = (SubPartitionPruningResult*)lfirst(cell); if (subPartPruningResult->partSeq == partSeq) { return subPartPruningResult; } } return NULL; } /* * @@GaussDB@@ * Brief : delete from partition (partition_name, subpartition_name, ...) * Description : eliminate partitions and subpartitions which not in the RTE (sub)partitionOidList. * return value : non-eliminated partitions and subpartitions. */ PruningResult* PartitionPruningForPartitionList(RangeTblEntry* rte, Relation rel) { Oid partOid; Oid subpartOid; PruningResult* pruningRes = NULL; if (rte->partitionOidList->length == 1) { partOid = list_nth_oid(rte->partitionOidList, 0); if (rte->isContainSubPartition) { subpartOid = list_nth_oid(rte->subpartitionOidList, 0); return SingleSubPartitionPruningForRestrictInfo(subpartOid, rel, partOid); } else { return singlePartitionPruningForRestrictInfo(partOid, rel); } } /* shouldn't happen */ if (!PointerIsValid(rel->partMap)) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("relation of oid=\"%u\" is not partitioned table", rel->rd_id))); } pruningRes = makeNode(PruningResult); pruningRes->state = PRUNING_RESULT_SUBSET; MergePartitionListsForPruning(rte, rel, pruningRes); return pruningRes; }