提交 5cecfcd1 编写于 作者: F foyzur 提交者: GitHub

Rule based partition selection for list (sub)partitions (#2076)

GPDB supports range and list partitions. Range partitions are represented as a set of rules. Each rule defines the boundaries of a part. E.g., a rule might say that a part contains all values between (0, 5], where left bound is 0 exclusive, but the right bound is 5, inclusive. List partitions are defined by a list of values that the part will contain. 

ORCA uses the above rule definition to generate expressions that determine which partitions need to be scanned. These expressions are of the following types:

1. Equality predicate as in PartitionSelectorState->levelEqExpressions: If we have a simple equality on partitioning key (e.g., part_key = 1).

2. General predicate as in PartitionSelectorState->levelExpressions: If we need more complex composition, including non-equality such as part_key > 1.

Note:  We also have residual predicate, which the optimizer currently doesn't use. We are planning to remove this dead code soon.

Prior to  this PR, ORCA was treating both range and list partitions as range partitions. This meant that each list part will be converted to a set of list values and each of these values will become a single point range partition.

E.g., consider the DDL:

```sql
CREATE TABLE DATE_PARTS (id int, year int, month int, day int, region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
    SUBPARTITION BY LIST (month)
       SUBPARTITION TEMPLATE (
        SUBPARTITION Q1 VALUES (1, 2, 3), 
        SUBPARTITION Q2 VALUES (4 ,5 ,6),
        SUBPARTITION Q3 VALUES (7, 8, 9),
        SUBPARTITION Q4 VALUES (10, 11, 12),
        DEFAULT SUBPARTITION other_months )
( START (2002) END (2012) EVERY (1), 
  DEFAULT PARTITION outlying_years );
```

Here we partition the months as list partition using quarters. So, each of the list part will contain three months. Now consider a query on this table:

```sql
select * from DATE_PARTS where month between 1 and 3;
```

Prior to this ORCA generated plan would consider each value of the Q1 as a separate range part with just one point range. I.e., we will have 3 virtual parts to evaluate for just one Q1: [1], [2], [3]. This approach is inefficient. The problem is further exacerbated when we have multi-level partitioning. Consider the list part of the above example. We have only 4 rules for 4 different quarters, but we will have 12 different virtual rule (aka constraints). For each such constraint, we will then evaluate the entire subtree of partitions.

After this PR, we no longer decompose rules into constraints for list parts and then derive single point virtual range partitions based on those constraints. Rather, the new ORCA changes will use ScalarArrayOp to express selectivity on a list of values. So, the expression for the above SQL will look like 1 <= ANY {month_part} AND 3 >= ANY {month_part}, where month_part will be substituted at runtime with different list of values for each of quarterly partitions. We will end up evaluating that expressions 4 times with the following list of values:

Q1: 1 <= ANY {1,2,3} AND 3 >= ANY {1,2,3}
Q2: 1 <= ANY {4,5,6} AND 3 >= ANY {4,5,6}
...

Compare this to the previous approach, where we will end up evaluating 12 different expressions, each time for a single point value:

First constraint of Q1: 1 <= 1 AND 3 >= 1
Second constraint of Q1: 1 <= 2 AND 3 >= 2
Third constraint of Q1: 1 <= 3 AND 3 >= 3
First constraint of Q2: 1 <= 4 AND 3 >= 4
...

The ScalarArrayOp depends on a new type of expression PartListRuleExpr that can convert a list rule to an array of values. ORCA specific changes can be found here: https://github.com/greenplum-db/gporca/pull/149
上级 5b2ea684
......@@ -120,7 +120,7 @@ sync_tools: opt_write_test /opt/releng/apache-ant
-Divyrepo.user=$(IVYREPO_USER) -Divyrepo.passwd="$(IVYREPO_PASSWD)" resolve);
@echo "Resolve finished";
LD_LIBRARY_PATH='' wget -O - https://github.com/greenplum-db/gporca/releases/download/v2.16.0/bin_orca_centos5_release.tar.gz | tar zxf - -C $(BLD_TOP)/ext/$(BLD_ARCH)
LD_LIBRARY_PATH='' wget -O - https://github.com/greenplum-db/gporca/releases/download/v2.17.0/bin_orca_centos5_release.tar.gz | tar zxf - -C $(BLD_TOP)/ext/$(BLD_ARCH)
clean_tools: opt_write_test
@cd releng/make/dependencies; \
......
......@@ -375,12 +375,26 @@ rel_partition_key_attrs(Oid relid)
*/
List *
rel_partition_keys_ordered(Oid relid)
{
List *pkeys = NIL;
rel_partition_keys_kinds_ordered(relid, &pkeys, NULL);
return pkeys;
}
/*
* Output a list of lists representing the partitioning keys and a list representing
* the partitioning kinds of the partitioned table identified by the relid or NIL.
* The keys and kinds are in the order of partitioning levels.
*/
void
rel_partition_keys_kinds_ordered(Oid relid, List **pkeys, List **pkinds)
{
Relation partrel;
ScanKeyData scankey;
SysScanDesc sscan;
List *levels = NIL;
List *keysUnordered = NIL;
List *kindsUnordered = NIL;
int nlevels = 0;
HeapTuple tuple = NULL;
......@@ -410,7 +424,12 @@ rel_partition_keys_ordered(Oid relid)
nlevels++;
levels = lappend_int(levels, p->parlevel);
keysUnordered = lappend(keysUnordered, levelkeys);
if (pkeys != NULL)
keysUnordered = lappend(keysUnordered, levelkeys);
if (pkinds != NULL)
kindsUnordered = lappend_int(kindsUnordered, p->parkind);
}
systable_endscan(sscan);
heap_close(partrel, AccessShareLock);
......@@ -418,22 +437,31 @@ rel_partition_keys_ordered(Oid relid)
if (1 == nlevels)
{
list_free(levels);
return keysUnordered;
if (pkeys != NULL)
*pkeys = keysUnordered;
if (pkinds != NULL)
*pkinds = kindsUnordered;
return;
}
// now order the keys by level
List *pkeys = NIL;
// now order the keys and kinds by level
for (int i = 0; i< nlevels; i++)
{
int pos = list_find_int(levels, i);
Assert (0 <= pos);
pkeys = lappend(pkeys, list_nth(keysUnordered, pos));
if (pkeys != NULL)
*pkeys = lappend(*pkeys, list_nth(keysUnordered, pos));
if (pkinds != NULL)
*pkinds = lappend_int(*pkinds, list_nth_int(kindsUnordered, pos));
}
list_free(levels);
list_free(keysUnordered);
return pkeys;
list_free(kindsUnordered);
}
/*
......
......@@ -52,168 +52,12 @@ eval_propagation_expression(PartitionSelectorState *node, Oid part_oid)
return DatumGetInt32(result);
}
/* ----------------------------------------------------------------
* construct_partition_constraints_range
*
* construct a PartitionConstraints node given a PartitionRule for
* partition by range
*
* caller is responsible for free the PartitionConstraints generated
*
* ----------------------------------------------------------------
*/
static PartitionConstraints *
construct_part_constraints_range(PartitionRule *rule)
{
Assert (NULL != rule);
PartitionConstraints *constraint = makeNode(PartitionConstraints);
Assert (NULL != constraint);
constraint->pRule = rule;
constraint->defaultPart = rule->parisdefault;
if (constraint->defaultPart)
{
return constraint;
}
/* retrieve boundary information */
if (NULL == rule->parrangestart && NULL == rule->parrangeend)
{
/* partition with only the NULL value */
constraint->lowerBound = NULL;
constraint->lbInclusive = true;
constraint->lbOpen = false;
constraint->upperBound = NULL;
constraint->upInclusive = true;
constraint->upOpen = false;
return constraint;
}
if (NULL == rule->parrangestart)
{
/* open lower bound */
constraint->lbOpen = true;
constraint->lowerBound = NULL;
constraint->lbInclusive = false;
}
else
{
List *parrangeStart = (List *) rule->parrangestart;
Assert (1 == list_length(parrangeStart));
Node *lowerBound = (Node *) linitial(parrangeStart);
Assert (IsA(lowerBound, Const));
constraint->lowerBound = (Const *) lowerBound;
constraint->lbInclusive = rule->parrangestartincl;
constraint->lbOpen = false;
}
if (NULL == rule->parrangeend)
{
/* open upper bound */
constraint->upOpen = true;
constraint->upperBound = NULL;
constraint->upInclusive = false;
}
else
{
List *parrangeEnd = (List *) rule->parrangeend;
Assert (1 == list_length(parrangeEnd));
Node *upperBound = (Node *) linitial(parrangeEnd);
Assert (IsA(upperBound, Const));
constraint->upperBound = (Const *) upperBound;
constraint->upInclusive = rule->parrangeendincl;
constraint->upOpen = false;
}
Assert (!constraint->upOpen || !constraint->lbOpen);
return constraint;
}
/* ----------------------------------------------------------------
* construct_partition_constraints_list
*
* construct a list of PartitionConstraints node given a PartitionRule
* for partition by list
*
* caller is responsible for free the PartitionConstraintss generated
*
* ----------------------------------------------------------------
*/
static List *
construct_part_constraints_list(PartitionRule *rule)
{
List *result = NIL;
/* default part */
if (NULL == rule->parlistvalues || rule->parisdefault)
{
PartitionConstraints *constraint = makeNode(PartitionConstraints);
Assert (NULL != constraint);
constraint->pRule = rule;
constraint->defaultPart = true;
result = lappend(result, constraint);
return result;
}
ListCell *lc = NULL;
foreach (lc, rule->parlistvalues)
{
List *values = (List *) lfirst(lc);
/* make sure it is single-column partition */
Assert (1 == list_length(values));
Node *value = (Node *) lfirst(list_nth_cell(values, 0));
Assert (IsA(value, Const));
PartitionConstraints *constraint = makeNode(PartitionConstraints);
Assert (NULL != constraint);
constraint->pRule = rule;
constraint->defaultPart = false;
constraint->lowerBound = (Const *) value;
constraint->lbInclusive = true;
constraint->lbOpen = false;
constraint->upperBound = (Const *) value;
constraint->upInclusive = true;
constraint->upOpen = false;
result = lappend(result, constraint);
}
return result;
}
/* ----------------------------------------------------------------
* construct_partition_constraints
*
* construct a list of PartitionConstraints node given a PartitionRule
* and its partition type
*
* caller is responsible for free the PartitionConstraintss generated
*
* ----------------------------------------------------------------
*/
static List *
construct_part_constraints(PartitionRule *rule, char parkind)
{
List *result = NIL;
switch(parkind)
{
case 'r':
result = lappend(result, construct_part_constraints_range(rule));
break;
case 'l':
result = construct_part_constraints_list(rule);
break;
default:
elog(ERROR,"unrecognized partitioning kind '%c'", parkind);
break;
}
return result;
}
/* ----------------------------------------------------------------
* eval_part_qual
*
* Evaluate a qualification expression that consists of
* PartDefaultExpr, PartBoundExpr, PartBoundInclusionExpr, PartBoundOpenExpr
* PartDefaultExpr, PartBoundExpr, PartBoundInclusionExpr, PartBoundOpenExpr,
* PartListRuleExpr and PartListNullTestExpr.
*
* Return true is passed, otherwise false.
*
......@@ -281,141 +125,64 @@ partition_selection(PartitionNode *pn, PartitionAccessMethods *accessMethods, Oi
}
/* ----------------------------------------------------------------
* partition_constraints_range
* partition_rules_for_general_predicate
*
* Returns a list of PartitionConstraints of all children PartitionRules
* with their constraints for a given partition-by-range
* PartitionNode
* Returns a list of PartitionRule for the general predicate
* of current partition level
*
* ----------------------------------------------------------------
*/
static List *
partition_constraints_range(PartitionNode *pn)
partition_rules_for_general_predicate(PartitionSelectorState *node, int level,
TupleTableSlot *inputTuple, PartitionNode *parentNode)
{
Assert (NULL != pn && 'r' == pn->part->parkind);
List *result = NIL;
ListCell *lc;
foreach (lc, pn->rules)
{
PartitionRule *rule = (PartitionRule *) lfirst(lc);
PartitionConstraints *constraint = construct_part_constraints_range(rule);
result = lappend(result, constraint);
}
return result;
}
Assert (NULL != node);
Assert (NULL != parentNode);
/* ----------------------------------------------------------------
* partition_constraints_list
*
* Returns a list of PartitionConstraints of all children PartitionRules
* with their constraints for a given partition-by-list
* PartitionNode
*
* It generates one PartitionConstraints for each partition value in one
* PartitionRule
*
* ----------------------------------------------------------------
*/
static List *
partition_constraints_list(PartitionNode *pn)
{
Assert (NULL != pn && 'l' == pn->part->parkind);
List *result = NIL;
ListCell *lc = NULL;
foreach (lc, pn->rules)
foreach (lc, parentNode->rules)
{
PartitionRule *rule = (PartitionRule *) lfirst(lc);
result = list_concat(result, construct_part_constraints_list(rule));
}
return result;
}
/* ----------------------------------------------------------------
* partition_constraints
*
* Returns a list of PartitionConstraints of all children PartitionRules
* with their constraints for a given parent PartitionNode
*
* ----------------------------------------------------------------
*/
static List *
partition_constraints(PartitionNode *pn)
{
Assert (NULL != pn);
Partition *part = pn->part;
List *result = NIL;
switch(part->parkind)
{
case 'r':
result = partition_constraints_range(pn);
break;
case 'l':
result = partition_constraints_list(pn);
break;
default:
elog(ERROR,"unrecognized partitioning kind '%c'",
part->parkind);
break;
}
/* We need to register it to allLevelParts to evaluate the current predicate */
node->levelPartRules[level] = rule;
/* add default part if exists */
if (NULL != pn->default_part)
{
PartitionConstraints *constraint = makeNode(PartitionConstraints);
constraint->pRule = pn->default_part;
constraint->defaultPart = true;
result = lappend(result, constraint);
/* evaluate generalPredicate */
ExprState *exprstate = (ExprState *) lfirst(list_nth_cell(node->levelExprStates, level));
if (eval_part_qual(exprstate, node, inputTuple))
{
result = lappend(result, rule);
}
}
return result;
}
/* ----------------------------------------------------------------
* partition_constraints_for_general_predicate
*
* Return list of PartitionConstraints for the general predicate
* of current partition level
*
* ----------------------------------------------------------------
*/
static List *
partition_constraints_for_general_predicate(PartitionSelectorState *node, int level,
TupleTableSlot *inputTuple, PartitionNode *parentNode)
{
Assert (NULL != node);
Assert (NULL != parentNode);
List *partConstraints = partition_constraints(parentNode);
List *result = NIL;
ListCell *lc = NULL;
foreach (lc, partConstraints)
if (parentNode->default_part)
{
PartitionConstraints *constraints = (PartitionConstraints *) lfirst(lc);
/* We need to register it to allLevelParts to evaluate the current predicate */
node->levelPartConstraints[level] = constraints;
node->levelPartRules[level] = parentNode->default_part;
/* evaluate generalPredicate */
ExprState *exprstate = (ExprState *) lfirst(list_nth_cell(node->levelExprStates, level));
if (eval_part_qual(exprstate, node, inputTuple))
{
result = lappend(result, constraints);
result = lappend(result, parentNode->default_part);
}
}
/* reset allLevelPartConstraints */
node->levelPartConstraints[level] = NULL;
node->levelPartRules[level] = NULL;
return result;
}
/* ----------------------------------------------------------------
* partition_constraints_for_equality_predicate
* partition_rules_for_equality_predicate
*
* Return list of PartitionConstraints for the equality predicate
* Return the PartitionRule for the equality predicate
* of current partition level
*
* ----------------------------------------------------------------
*/
static List *
partition_constraints_for_equality_predicate(PartitionSelectorState *node, int level,
static PartitionRule *
partition_rules_for_equality_predicate(PartitionSelectorState *node, int level,
TupleTableSlot *inputTuple, PartitionNode *parentNode)
{
Assert (NULL != node);
......@@ -442,12 +209,7 @@ partition_constraints_for_equality_predicate(PartitionSelectorState *node, int l
* to choose the correct comparator.
*/
Oid exprTypid = exprType((Node *) exprState->expr);
PartitionRule *partRule = partition_selection(parentNode, node->accessMethods, ps->relid, value, exprTypid, isNull);
if (NULL != partRule)
{
return construct_part_constraints(partRule, parentNode->part->parkind);
}
return NIL;
return partition_selection(parentNode, node->accessMethods, ps->relid, value, exprTypid, isNull);
}
/* ----------------------------------------------------------------
......@@ -492,46 +254,65 @@ processLevel(PartitionSelectorState *node, int level, TupleTableSlot *inputTuple
PartitionNode *parentNode = node->rootPartitionNode;
if (0 != level)
{
Assert (NULL != node->levelPartConstraints[level - 1]);
parentNode = node->levelPartConstraints[level - 1]->pRule->children;
Assert (NULL != node->levelPartRules[level - 1]);
parentNode = node->levelPartRules[level - 1]->children;
}
/* list of PartitionConstraints that satisfied the predicates */
List *satisfiedPartConstraints = NULL;
/* list of PartitionRule that satisfied the predicates */
List *satisfiedRules = NIL;
/* If equalityPredicate exists */
if (NULL != equalityPredicate)
{
Assert (NULL == generalPredicate);
List *partConstraints = partition_constraints_for_equality_predicate(node, level, inputTuple, parentNode);
satisfiedPartConstraints = list_concat(satisfiedPartConstraints, partConstraints);
PartitionRule *chosenRule = partition_rules_for_equality_predicate(node, level, inputTuple, parentNode);
if (chosenRule != NULL)
{
satisfiedRules = lappend(satisfiedRules, chosenRule);
}
}
/* If generalPredicate exists */
else if (NULL != generalPredicate)
{
List *partConstraints = partition_constraints_for_general_predicate(node, level, inputTuple, parentNode);
satisfiedPartConstraints = list_concat(satisfiedPartConstraints, partConstraints);
List *chosenRules = partition_rules_for_general_predicate(node, level, inputTuple, parentNode);
satisfiedRules = list_concat(satisfiedRules, chosenRules);
}
/* None of the predicate exists */
else
{
/*
* Neither equality predicate nor general predicate
* exists. Return all the next level PartitionConstraintss.
* 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.
*/
satisfiedPartConstraints = partition_constraints(parentNode);
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, satisfiedPartConstraints)
foreach (lc, satisfiedRules)
{
PartitionConstraints *partConstraint = (PartitionConstraints *) lfirst(lc);
node->levelPartConstraints[level] = partConstraint;
bool freeConstraint = true;
PartitionRule *rule = (PartitionRule *) lfirst(lc);
node->levelPartRules[level] = rule;
/* If we already in the leaf level */
if (level == ps->nLevels - 1)
......@@ -550,23 +331,20 @@ processLevel(PartitionSelectorState *node, int level, TupleTableSlot *inputTuple
{
if (NULL != ps->propagationExpression)
{
if (!list_member_oid(selparts->partOids, partConstraint->pRule->parchildrelid))
if (!list_member_oid(selparts->partOids, rule->parchildrelid))
{
selparts->partOids = lappend_oid(selparts->partOids, partConstraint->pRule->parchildrelid);
int scanId = eval_propagation_expression(node, partConstraint->pRule->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
{
/*
* We'll need this partConstraint to evaluate the PartOidExpr of the
* PartitionSelector operator's target list. Save it in node->acceptedLeafPart.
* PartOidExprState.acceptedLeafPart also points to this partConstraint,
* so we must save it here (GPSQL-2956).
* We'll need the oid of this rule to evaluate the PartOidExpr of the
* PartitionSelector operator's target list. Save it in node->acceptedLeafOid.
*/
*node->acceptedLeafPart = partConstraint;
freeConstraint = false;
node->acceptedLeafOid = rule->parchildrelid;
}
}
}
......@@ -578,17 +356,12 @@ processLevel(PartitionSelectorState *node, int level, TupleTableSlot *inputTuple
selparts->scanIds = list_concat(selparts->scanIds, selpartsChild->scanIds);
pfree(selpartsChild);
}
if (freeConstraint)
{
pfree(partConstraint);
}
}
list_free(satisfiedPartConstraints);
list_free(satisfiedRules);
/* After finish iteration, reset this level's PartitionConstraints */
node->levelPartConstraints[level] = NULL;
/* After finish iteration, reset this level's PartitionRule */
node->levelPartRules[level] = NULL;
return selparts;
}
......@@ -610,7 +383,7 @@ initPartitionSelection(PartitionSelector *node, EState *estate)
psstate = makeNode(PartitionSelectorState);
psstate->ps.plan = (Plan *) node;
psstate->ps.state = estate;
psstate->levelPartConstraints = (PartitionConstraints**) palloc0(node->nLevels * sizeof(PartitionConstraints*));
psstate->levelPartRules = (PartitionRule**) palloc0(node->nLevels * sizeof(PartitionRule*));
/* ExprContext initialization */
ExecAssignExprContext(estate, &psstate->ps);
......@@ -630,8 +403,6 @@ initPartitionSelection(PartitionSelector *node, EState *estate)
ExecInitExpr(generalExpr, (PlanState *) psstate));
}
psstate->acceptedLeafPart = (PartitionConstraints **) palloc0(sizeof(void *));
psstate->residualPredicateExprState = ExecInitExpr((Expr *) node->residualPredicate,
(PlanState *) psstate);
psstate->propagationExprState = ExecInitExpr((Expr *) node->propagationExpression,
......
......@@ -209,6 +209,13 @@ static Datum ExecEvalPartBoundInclusionExpr(PartBoundInclusionExprState *exprsta
static Datum ExecEvalPartBoundOpenExpr(PartBoundOpenExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalPartListRuleExpr(PartListRuleExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalPartListNullTestExpr(PartListNullTestExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static bool ExecIsExprUnsafeToConst_walker(Node *node, void *context);
static bool ExecIsExprUnsafeToConst(Node *node);
......@@ -4653,11 +4660,10 @@ static Datum ExecEvalPartOidExpr(PartOidExprState *exprstate,
*isDone = ExprSingleResult;
}
PartitionConstraints *constraint = *exprstate->acceptedLeafPart;
if (NULL != constraint)
if (InvalidOid != *exprstate->acceptedLeafOid)
{
*isNull = false;
return UInt32GetDatum(constraint->pRule->parchildrelid);
return UInt32GetDatum(*exprstate->acceptedLeafOid);
}
*isNull = true;
......@@ -4678,8 +4684,8 @@ static Datum ExecEvalPartDefaultExpr(PartDefaultExprState *exprstate,
Assert(NULL != isNull);
PartDefaultExpr *expr = (PartDefaultExpr *) exprstate->xprstate.expr;
PartitionConstraints *constraint = (PartitionConstraints *) exprstate->levelPartConstraints[expr->level];
Assert (NULL != constraint);
PartitionRule *rule = exprstate->selector->levelPartRules[expr->level];
Assert (NULL != rule);
if (isDone)
{
......@@ -4687,7 +4693,7 @@ static Datum ExecEvalPartDefaultExpr(PartDefaultExprState *exprstate,
}
*isNull = false;
return BoolGetDatum(constraint->defaultPart);
return BoolGetDatum(rule->parisdefault);
}
/* ----------------------------------------------------------------
......@@ -4704,12 +4710,24 @@ static Datum ExecEvalPartBoundExpr(PartBoundExprState *exprstate,
Assert(NULL != isNull);
PartBoundExpr *expr = (PartBoundExpr *) exprstate->xprstate.expr;
PartitionConstraints *constraint = (PartitionConstraints *) exprstate->levelPartConstraints[expr->level];
Assert (NULL != constraint);
Const *con = constraint->upperBound;
if (expr->isLowerBound)
PartitionSelectorState *selector = exprstate->selector;
PartitionRule *rule = selector->levelPartRules[expr->level];
Assert (NULL != rule);
ASSERT_RANGE_PART(selector, expr->level);
List *parBoundary = NULL;
parBoundary = expr->isLowerBound ? (List *) rule->parrangestart : (List *) rule->parrangeend;
/* The constant boundary to return */
Const *con = NULL;
if (parBoundary != NULL)
{
con = constraint->lowerBound;
Assert (1 == list_length(parBoundary));
Node *bound = (Node *) linitial(parBoundary);
Assert (IsA(bound, Const));
con = (Const *) bound;
}
if (isDone)
......@@ -4742,18 +4760,24 @@ static Datum ExecEvalPartBoundInclusionExpr(PartBoundInclusionExprState *exprsta
Assert(NULL != isNull);
PartBoundInclusionExpr *expr = (PartBoundInclusionExpr *) exprstate->xprstate.expr;
PartitionConstraints *constraint = (PartitionConstraints *) exprstate->levelPartConstraints[expr->level];
Assert (NULL != constraint);
PartitionSelectorState *selector = exprstate->selector;
PartitionRule *rule = selector->levelPartRules[expr->level];
Assert (NULL != rule);
ASSERT_RANGE_PART(selector, expr->level);
bool isIncluded = rule->parrangeendincl;
if (expr->isLowerBound)
{
isIncluded = rule->parrangestartincl;
}
if (isDone)
{
*isDone = ExprSingleResult;
}
*isNull = false;
if (expr->isLowerBound)
{
return BoolGetDatum(constraint->lbInclusive);
}
return BoolGetDatum(constraint->upInclusive);
return BoolGetDatum(isIncluded);
}
/* ----------------------------------------------------------------
......@@ -4770,18 +4794,178 @@ static Datum ExecEvalPartBoundOpenExpr(PartBoundOpenExprState *exprstate,
Assert(NULL != isNull);
PartBoundOpenExpr *expr = (PartBoundOpenExpr *) exprstate->xprstate.expr;
PartitionConstraints *constraint = (PartitionConstraints *) exprstate->levelPartConstraints[expr->level];
Assert (NULL != constraint);
PartitionSelectorState *selector = exprstate->selector;
PartitionRule *rule = selector->levelPartRules[expr->level];
Assert (NULL != rule);
ASSERT_RANGE_PART(selector, expr->level);
bool isOpen = (rule->parrangeend == NULL);
if (expr->isLowerBound)
{
isOpen = (rule->parrangestart == NULL);
}
if (isDone)
{
*isDone = ExprSingleResult;
}
*isNull = false;
if (expr->isLowerBound)
return BoolGetDatum(isOpen);
}
/* ----------------------------------------------------------------
* ExecEvalPartListRuleExpr
*
* Evaluate a PartListRuleExpr
* ----------------------------------------------------------------
*/
static Datum ExecEvalPartListRuleExpr(PartListRuleExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
Assert(NULL != exprstate);
Assert(NULL != isNull);
PartListRuleExpr *expr = (PartListRuleExpr *) exprstate->xprstate.expr;
PartitionSelectorState *selector = exprstate->selector;
PartitionRule *rule = selector->levelPartRules[expr->level];
Assert (NULL != rule);
ASSERT_LIST_PART(selector, expr->level);
ListCell *lc = NULL;
size_t numVal = rule->parlistvalues ? rule->parlistvalues->length : 0;
Datum *array_values = NULL;
int16 typlen = 0;
bool typbyval = false;
char typalign = 'i';
Oid consttype = expr->elementtype;
get_typlenbyvalalign(consttype, &typlen, &typbyval, &typalign);
bool *is_null = NULL;
if (numVal > 0)
{
return BoolGetDatum(constraint->lbOpen);
is_null = palloc0(sizeof(bool) * numVal);
#ifdef USE_ASSERT_CHECKING
/* The type of the first attribute should match the underlying table col's type */
Oid valOid = ((Const *) linitial(lfirst(rule->parlistvalues->head)))->consttype;
Assert(valOid == consttype);
#endif
array_values = palloc0(numVal * sizeof(Datum));
int datumIdx = 0;
Const *con = NULL;
foreach (lc, rule->parlistvalues)
{
List *values = (List *) lfirst(lc);
/*
* Make sure it is single-column partition.
* Optimizer currently doesn't support multi-column partitions.
*/
Assert (1 == list_length(values));
Node *value = (Node *) lfirst(list_nth_cell(values, 0));
Assert (IsA(value, Const));
con = (Const *) value;
array_values[datumIdx] = con->constvalue;
if (con->constisnull)
{
is_null[datumIdx] = true;
}
++datumIdx;
}
}
return BoolGetDatum(constraint->upOpen);
/*
* If we have an empty list of values in the rule (for default part, or for whatever other reason),
* we will just create an empty array. Returning null might return NULL from parent operator, such
* as ExecEvalScalarArrayOp
*/
int dims[1];
int lbs[1];
dims[0] = numVal;
lbs[0] = 1;
ArrayType *array = construct_md_array(array_values, is_null, 1, dims, lbs,
consttype, typlen, typbyval, typalign);
if (array_values)
{
Assert(NULL != array);
pfree(array_values);
Assert(NULL != is_null);
pfree(is_null);
}
if (isDone)
{
*isDone = ExprSingleResult;
}
*isNull = false;
return PointerGetDatum(array);
}
/* ----------------------------------------------------------------
* ExecEvalPartListNullTestExpr
*
* Evaluate a PartListNullTestExpr
* ----------------------------------------------------------------
*/
static Datum ExecEvalPartListNullTestExpr(PartListNullTestExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
Assert(NULL != exprstate);
Assert(NULL != isNull);
PartListNullTestExpr *expr = (PartListNullTestExpr *) exprstate->xprstate.expr;
PartitionSelectorState *selector = exprstate->selector;
PartitionRule *rule = selector->levelPartRules[expr->level];
Assert (NULL != rule);
ASSERT_LIST_PART(selector, expr->level);
ListCell *lc = NULL;
size_t numVal = rule->parlistvalues ? rule->parlistvalues->length : 0;
*isNull = false;
if (numVal > 0)
{
Const *con = NULL;
foreach (lc, rule->parlistvalues)
{
List *values = (List *) lfirst(lc);
/* make sure it is single-column partition */
Assert (1 == list_length(values));
Node *value = (Node *) lfirst(list_nth_cell(values, 0));
Assert (IsA(value, Const));
con = (Const *) value;
if (con->constisnull && expr->nulltesttype == IS_NULL)
{
/* Found at least on null value when we were asked to find one */
return BoolGetDatum(true);
}
else if (!con->constisnull && expr->nulltesttype == IS_NOT_NULL)
{
return BoolGetDatum(true);
}
}
}
return BoolGetDatum(false);
}
/* ----------------------------------------------------------------
......@@ -5746,11 +5930,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
exprstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalPartOidExpr;
/*
* exprstate->acceptedLeafPart is a double pointer, pointing
* to the field in the PartitionSelector state that will
* be holding the actual PartitionConstraints value (GPSQL-2956)
* computed for each tuple.
* to the field in the PartitionSelectorState that will
* be holding the actual acceptedLeafOid.
*/
exprstate->acceptedLeafPart = psstate->acceptedLeafPart;
exprstate->acceptedLeafOid = &(psstate->acceptedLeafOid);
state = (ExprState *) exprstate;
}
......@@ -5761,7 +5944,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
PartitionSelectorState *psstate = (PartitionSelectorState *) parent;
PartDefaultExprState *exprstate = makeNode(PartDefaultExprState);
exprstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalPartDefaultExpr;
exprstate->levelPartConstraints = psstate->levelPartConstraints;
exprstate->selector = psstate;
state = (ExprState *) exprstate;
}
......@@ -5772,7 +5955,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
PartitionSelectorState *psstate = (PartitionSelectorState *) parent;
PartBoundExprState *exprstate = makeNode(PartBoundExprState);
exprstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalPartBoundExpr;
exprstate->levelPartConstraints = psstate->levelPartConstraints;
exprstate->selector = psstate;
state = (ExprState *) exprstate;
}
......@@ -5783,7 +5966,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
PartitionSelectorState *psstate = (PartitionSelectorState *) parent;
PartBoundInclusionExprState *exprstate = makeNode(PartBoundInclusionExprState);
exprstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalPartBoundInclusionExpr;
exprstate->levelPartConstraints = psstate->levelPartConstraints;
exprstate->selector = psstate;
state = (ExprState *) exprstate;
}
......@@ -5794,11 +5977,35 @@ ExecInitExpr(Expr *node, PlanState *parent)
PartitionSelectorState *psstate = (PartitionSelectorState *) parent;
PartBoundOpenExprState *exprstate = makeNode(PartBoundOpenExprState);
exprstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalPartBoundOpenExpr;
exprstate->levelPartConstraints = psstate->levelPartConstraints;
exprstate->selector = psstate;
state = (ExprState *) exprstate;
}
break;
case T_PartListRuleExpr:
{
Insist(parent && IsA(parent, PartitionSelectorState));
PartitionSelectorState *psstate = (PartitionSelectorState *) parent;
PartListRuleExprState *exprstate = makeNode(PartListRuleExprState);
exprstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalPartListRuleExpr;
exprstate->selector = psstate;
state = (ExprState *) exprstate;
}
break;
case T_PartListNullTestExpr:
{
Insist(parent && IsA(parent, PartitionSelectorState));
PartitionSelectorState *psstate = (PartitionSelectorState *) parent;
PartListNullTestExprState *exprstate = makeNode(PartListNullTestExprState);
exprstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalPartListNullTestExpr;
exprstate->selector = psstate;
state = (ExprState *) exprstate;
}
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
......
......@@ -21,7 +21,8 @@
#include "executor/nodePartitionSelector.h"
#include "utils/memutils.h"
static void partition_propagation(EState *estate, List *partOids, List *scanIds, int32 selectorId);
static void
partition_propagation(EState *estate, List *partOids, List *scanIds, int32 selectorId);
/* PartitionSelector Slots */
#define PARTITIONSELECTOR_NSLOTS 1
......@@ -121,7 +122,7 @@ ExecPartitionSelector(PartitionSelectorState *node)
{
/* propagate the part oids obtained via static partition selection */
partition_propagation(estate, ps->staticPartOids, ps->staticScanIds, ps->selectorId);
*node->acceptedLeafPart = NULL;
node->acceptedLeafOid = InvalidOid;
return NULL;
}
......@@ -185,12 +186,7 @@ ExecPartitionSelector(PartitionSelectorState *node)
Assert (ExprSingleResult == isDone);
}
/* reset acceptedLeafPart */
if (NULL != *node->acceptedLeafPart)
{
pfree(*node->acceptedLeafPart);
*node->acceptedLeafPart = NULL;
}
node->acceptedLeafOid = InvalidOid;
return candidateOutputSlot;
}
......@@ -206,11 +202,11 @@ ExecReScanPartitionSelector(PartitionSelectorState *node, ExprContext *exprCtxt)
/* reset PartitionSelectorState */
PartitionSelector *ps = (PartitionSelector *) node->ps.plan;
Assert (NULL == *node->acceptedLeafPart);
Assert (InvalidOid == node->acceptedLeafOid);
for(int iter = 0; iter < ps->nLevels; iter++)
{
node->levelPartConstraints[iter] = NULL;
node->levelPartRules[iter] = NULL;
}
/* free result tuple slot */
......
......@@ -1252,6 +1252,22 @@ gpdb::PlPartitionAttrs
return NIL;
}
void
gpdb::GetOrderedPartKeysAndKinds
(
Oid oid,
List **pkeys,
List **pkinds
)
{
GP_WRAP_START;
{
/* catalog tables: pg_partition */
rel_partition_keys_kinds_ordered(oid, pkeys, pkinds);
}
GP_WRAP_END;
}
PartitionNode *
gpdb::PpnParts
(
......
......@@ -118,6 +118,8 @@ CTranslatorDXLToScalar::PexprFromDXLNodeScalar
{EdxlopScalarPartBound, &CTranslatorDXLToScalar::PexprPartBound},
{EdxlopScalarPartBoundInclusion, &CTranslatorDXLToScalar::PexprPartBoundInclusion},
{EdxlopScalarPartBoundOpen, &CTranslatorDXLToScalar::PexprPartBoundOpen},
{EdxlopScalarPartListValues, &CTranslatorDXLToScalar::PexprPartListValues},
{EdxlopScalarPartListNullTest, &CTranslatorDXLToScalar::PexprPartListNullTest},
};
const ULONG ulTranslators = GPOS_ARRAY_SIZE(rgTranslators);
......@@ -1811,6 +1813,55 @@ CTranslatorDXLToScalar::PexprPartBoundOpen
return (Expr *) pexpr;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorDXLToScalar::PexprPartListValues
//
// @doc:
// Translates a DXL part list values into a GPDB part list values
//
//---------------------------------------------------------------------------
Expr *
CTranslatorDXLToScalar::PexprPartListValues
(
const CDXLNode *pdxlnPartListValues,
CMappingColIdVar * //pmapcidvar
)
{
CDXLScalarPartListValues *pdxlop = CDXLScalarPartListValues::PdxlopConvert(pdxlnPartListValues->Pdxlop());
PartListRuleExpr *pexpr = MakeNode(PartListRuleExpr);
pexpr->level = pdxlop->UlLevel();
pexpr->resulttype = CMDIdGPDB::PmdidConvert(pdxlop->PmdidResult())->OidObjectId();
pexpr->elementtype = CMDIdGPDB::PmdidConvert(pdxlop->PmdidElement())->OidObjectId();
return (Expr *) pexpr;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorDXLToScalar::PexprPartListNullTest
//
// @doc:
// Translates a DXL part list values into a GPDB part list null test
//
//---------------------------------------------------------------------------
Expr *
CTranslatorDXLToScalar::PexprPartListNullTest
(
const CDXLNode *pdxlnPartListNullTest,
CMappingColIdVar * //pmapcidvar
)
{
CDXLScalarPartListNullTest *pdxlop = CDXLScalarPartListNullTest::PdxlopConvert(pdxlnPartListNullTest->Pdxlop());
PartListNullTestExpr *pexpr = MakeNode(PartListNullTestExpr);
pexpr->level = pdxlop->UlLevel();
pexpr->nulltesttype = pdxlop->FIsNull() ? IS_NULL : IS_NOT_NULL;
return (Expr *) pexpr;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorDXLToScalar::PexprFromDXLNodeScId
......
......@@ -497,6 +497,7 @@ CTranslatorRelcacheToDXL::Pmdrel
DrgPmdid *pdrgpmdidIndexes = NULL;
DrgPmdid *pdrgpmdidTriggers = NULL;
DrgPul *pdrgpulPartKeys = NULL;
DrgPsz *pdrgpszPartTypes = NULL;
ULONG ulLeafPartitions = 0;
BOOL fConvertHashToRandom = false;
DrgPdrgPul *pdrgpdrgpulKeys = NULL;
......@@ -540,7 +541,7 @@ CTranslatorRelcacheToDXL::Pmdrel
// get partition keys
if (IMDRelation::ErelstorageExternal != erelstorage)
{
pdrgpulPartKeys = PdrgpulPartKeys(pmp, rel, oid);
GetPartKeysAndTypes(pmp, rel, oid, &pdrgpulPartKeys, &pdrgpszPartTypes);
}
BOOL fPartitioned = (NULL != pdrgpulPartKeys && 0 < pdrgpulPartKeys->UlLength());
......@@ -629,6 +630,7 @@ CTranslatorRelcacheToDXL::Pmdrel
pdrgpmdcol,
pdrpulDistrCols,
pdrgpulPartKeys,
pdrgpszPartTypes,
ulLeafPartitions,
fConvertHashToRandom,
pdrgpdrgpulKeys,
......@@ -2947,19 +2949,21 @@ CTranslatorRelcacheToDXL::Erelstorage
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpulPartKeys
// CTranslatorRelcacheToDXL::GetPartKeysAndTypes
//
// @doc:
// Get partition keys for relation or NULL if relation not partitioned.
// Get partition keys and types for relation or NULL if relation not partitioned.
// Caller responsible for closing the relation if an exception is raised
//
//---------------------------------------------------------------------------
DrgPul *
CTranslatorRelcacheToDXL::PdrgpulPartKeys
void
CTranslatorRelcacheToDXL::GetPartKeysAndTypes
(
IMemoryPool *pmp,
Relation rel,
OID oid
OID oid,
DrgPul **pdrgpulPartKeys,
DrgPsz **pdrgpszPartTypes
)
{
GPOS_ASSERT(NULL != rel);
......@@ -2967,12 +2971,15 @@ CTranslatorRelcacheToDXL::PdrgpulPartKeys
if (!gpdb::FRelPartIsRoot(oid))
{
// not a partitioned table
return NULL;
*pdrgpulPartKeys = NULL;
*pdrgpszPartTypes = NULL;
return;
}
// TODO: Feb 23, 2012; support intermediate levels
DrgPul *pdrgpulPartKeys = GPOS_NEW(pmp) DrgPul(pmp);
*pdrgpulPartKeys = GPOS_NEW(pmp) DrgPul(pmp);
*pdrgpszPartTypes = GPOS_NEW(pmp) DrgPsz(pmp);
PartitionNode *pn = gpdb::PpnParts(oid, 0 /*level*/, 0 /*parent*/, false /*inctemplate*/, true /*includesubparts*/);
GPOS_ASSERT(NULL != pn);
......@@ -2982,12 +2989,15 @@ CTranslatorRelcacheToDXL::PdrgpulPartKeys
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Hash partitioning"));
}
List *plPartKeys = gpdb::PlPartitionAttrs(oid);
List *plPartKeys = NIL;
List *plPartTypes = NIL;
gpdb::GetOrderedPartKeysAndKinds(oid, &plPartKeys, &plPartTypes);
ListCell *plc = NULL;
ForEach (plc, plPartKeys)
ListCell *plcKey = NULL;
ListCell *plcType = NULL;
ForBoth (plcKey, plPartKeys, plcType, plPartTypes)
{
List *plPartKey = (List *) lfirst(plc);
List *plPartKey = (List *) lfirst(plcKey);
if (1 < gpdb::UlListLength(plPartKey))
{
......@@ -2995,11 +3005,14 @@ CTranslatorRelcacheToDXL::PdrgpulPartKeys
}
INT iAttno = linitial_int(plPartKey);
CHAR partType = (CHAR) lfirst_int(plcType);
GPOS_ASSERT(0 < iAttno);
pdrgpulPartKeys->Append(GPOS_NEW(pmp) ULONG(iAttno - 1));
(*pdrgpulPartKeys)->Append(GPOS_NEW(pmp) ULONG(iAttno - 1));
(*pdrgpszPartTypes)->Append(GPOS_NEW(pmp) CHAR(partType));
}
return pdrgpulPartKeys;
gpdb::FreeList(plPartKeys);
gpdb::FreeList(plPartTypes);
}
......
......@@ -2742,6 +2742,27 @@ _copyPartBoundOpenExpr(const PartBoundOpenExpr *from)
return newnode;
}
static PartListRuleExpr *
_copyPartListRuleExpr(const PartListRuleExpr *from)
{
PartListRuleExpr *newnode = makeNode(PartListRuleExpr);
COPY_SCALAR_FIELD(level);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(elementtype);
return newnode;
}
static PartListNullTestExpr *
_copyPartListNullTestExpr(const PartListNullTestExpr *from)
{
PartListNullTestExpr *newnode = makeNode(PartListNullTestExpr);
COPY_SCALAR_FIELD(level);
COPY_SCALAR_FIELD(nulltesttype);
return newnode;
}
static Query *
_copyQuery(Query *from)
{
......@@ -5205,6 +5226,12 @@ copyObject(void *from)
case T_PartBoundOpenExpr:
retval = _copyPartBoundOpenExpr(from);
break;
case T_PartListRuleExpr:
retval = _copyPartListRuleExpr(from);
break;
case T_PartListNullTestExpr:
retval = _copyPartListNullTestExpr(from);
break;
case T_RangeTblEntry:
retval = _copyRangeTblEntry(from);
break;
......
......@@ -1945,6 +1945,14 @@ _outNode(StringInfo str, void *obj)
_outPartBoundOpenExpr(str, obj);
break;
case T_PartListRuleExpr:
_outPartListRuleExpr(str, obj);
break;
case T_PartListNullTestExpr:
_outPartListNullTestExpr(str, obj);
break;
case T_CreateTrigStmt:
_outCreateTrigStmt(str, obj);
break;
......
......@@ -3269,6 +3269,25 @@ _outPartBoundOpenExpr(StringInfo str, PartBoundOpenExpr *node)
WRITE_BOOL_FIELD(isLowerBound);
}
static void
_outPartListRuleExpr(StringInfo str, PartListRuleExpr *node)
{
WRITE_NODE_TYPE("PARTLISTRULEEXPR");
WRITE_INT_FIELD(level);
WRITE_OID_FIELD(resulttype);
WRITE_OID_FIELD(elementtype);
}
static void
_outPartListNullTestExpr(StringInfo str, PartListNullTestExpr *node)
{
WRITE_NODE_TYPE("PARTLISTNULLTESTEXPR");
WRITE_INT_FIELD(level);
WRITE_ENUM_FIELD(nulltesttype, NullTestType);
}
#ifndef COMPILING_BINARY_FUNCS
static void
_outColumnDef(StringInfo str, ColumnDef *node)
......@@ -5088,6 +5107,14 @@ _outNode(StringInfo str, void *obj)
_outPartBoundOpenExpr(str, obj);
break;
case T_PartListRuleExpr:
_outPartListRuleExpr(str, obj);
break;
case T_PartListNullTestExpr:
_outPartListNullTestExpr(str, obj);
break;
case T_CreateTrigStmt:
_outCreateTrigStmt(str, obj);
break;
......
......@@ -343,6 +343,29 @@ _readPartBoundOpenExpr(void)
READ_DONE();
}
static PartListRuleExpr *
_readPartListRuleExpr(void)
{
READ_LOCALS(PartListRuleExpr);
READ_INT_FIELD(level);
READ_OID_FIELD(resulttype);
READ_OID_FIELD(elementtype);
READ_DONE();
}
static PartListNullTestExpr *
_readPartListNullTestExpr(void)
{
READ_LOCALS(PartListNullTestExpr);
READ_INT_FIELD(level);
READ_ENUM_FIELD(nulltesttype, NullTestType);
READ_DONE();
}
/*
* Stuff from primnodes.h.
*/
......@@ -3304,6 +3327,12 @@ readNodeBinary(void)
case T_PartBoundOpenExpr:
return_value = _readPartBoundOpenExpr();
break;
case T_PartListRuleExpr:
return_value = _readPartListRuleExpr();
break;
case T_PartListNullTestExpr:
return_value = _readPartListNullTestExpr();
break;
case T_RowMarkClause:
return_value = _readRowMarkClause();
break;
......
......@@ -3988,6 +3988,8 @@ expression_tree_mutator(Node *node,
case T_PartBoundExpr:
case T_PartBoundInclusionExpr:
case T_PartBoundOpenExpr:
case T_PartListRuleExpr:
case T_PartListNullTestExpr:
return (Node *) copyObject(node);
case T_Aggref:
{
......
......@@ -143,6 +143,8 @@ expression_tree_walker(Node *node,
case T_PartBoundExpr:
case T_PartBoundInclusionExpr:
case T_PartBoundOpenExpr:
case T_PartListRuleExpr:
case T_PartListNullTestExpr:
/* primitive node types with no expression subnodes */
break;
case T_Aggref:
......@@ -1184,6 +1186,8 @@ plan_tree_walker(Node *node,
case T_PartBoundExpr:
case T_PartBoundInclusionExpr:
case T_PartBoundOpenExpr:
case T_PartListRuleExpr:
case T_PartListNullTestExpr:
case T_WindowFrame:
case T_WindowFrameEdge:
case T_WindowKey:
......
......@@ -2889,6 +2889,12 @@ exprType(Node *expr)
case T_PartBoundOpenExpr:
type = BOOLOID;
break;
case T_PartListRuleExpr:
type = ((PartListRuleExpr *) expr)->resulttype;
break;
case T_PartListNullTestExpr:
type = BOOLOID;
break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
type = InvalidOid; /* keep compiler quiet */
......
......@@ -67,6 +67,8 @@ extern List *rel_partition_key_attrs(Oid relid);
extern List *rel_partition_keys_ordered(Oid relid);
extern void rel_partition_keys_kinds_ordered(Oid relid, List **pkeys, List **pkinds);
extern bool rel_has_external_partition(Oid relid);
extern bool query_has_external_partition(Query *query);
......
......@@ -14,24 +14,6 @@
#include "nodes/relation.h"
#include "nodes/execnodes.h"
/* ----------------
* PartitionConstraints node
* used during PartitionSelector
* ----------------
*/
typedef struct PartitionConstraints
{
NodeTag type;
PartitionRule * pRule;
bool defaultPart;
Const *lowerBound;
Const *upperBound;
bool lbInclusive;
bool upInclusive;
bool lbOpen;
bool upOpen;
} PartitionConstraints;
/* ----------------
* SelectedParts node
* This is the result of partition selection. It has a list of leaf part oids
......@@ -54,4 +36,14 @@ extern AttrNumber *varattnos_map(TupleDesc oldtd, TupleDesc newtd);
extern void change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
extern void change_varattnos_of_a_varno(Node *node, const AttrNumber *newattno, Index varno);
#define ASSERT_RANGE_PART(selector, level) \
AssertImply(0 == level, selector->rootPartitionNode->part->parkind == 'r'); \
AssertImply(0 < level, selector->levelPartRules[level - 1] != NULL && \
selector->levelPartRules[level - 1]->children->part->parkind == 'r'); \
#define ASSERT_LIST_PART(selector, level) \
AssertImply(0 == level, selector->rootPartitionNode->part->parkind == 'l'); \
AssertImply(0 < level, selector->levelPartRules[level - 1] != NULL && \
selector->levelPartRules[level - 1]->children->part->parkind == 'l'); \
#endif /* PARTITIONSELECTION_H */
......@@ -294,6 +294,9 @@ namespace gpdb {
// partition attributes
List *PlPartitionAttrs(Oid oid);
// get partition keys and kinds ordered by partition level
void GetOrderedPartKeysAndKinds(Oid oid, List **pkeys, List **pkinds);
// parts of a partitioned table
PartitionNode *PpnParts(Oid relid, int2 level, Oid parent, bool inctemplate, bool includesubparts);
......
......@@ -379,6 +379,20 @@ namespace gpdxl
CMappingColIdVar *pmapcidvar
);
// translate a scalar part list values into an Expr
Expr *PexprPartListValues
(
const CDXLNode *pdxlnPartListValues,
CMappingColIdVar *pmapcidvar
);
// translate a scalar part list null test into an Expr
Expr *PexprPartListNullTest
(
const CDXLNode *pdxlnPartListNullTest,
CMappingColIdVar *pmapcidvar
);
// translate a scalar ident into an Expr
Expr *PexprFromDXLNodeScId
(
......
......@@ -256,9 +256,9 @@ namespace gpdxl
ULONG ulNumHistValues
);
// get partition keys for a relation
// get partition keys and types for a relation
static
DrgPul *PdrgpulPartKeys(IMemoryPool *pmp, Relation rel, OID oid);
void GetPartKeysAndTypes(IMemoryPool *pmp, Relation rel, OID oid, DrgPul **pdrgpulPartKeys, DrgPsz **pdrgpszPartTypes);
// get keysets for relation
static
......
......@@ -1001,8 +1001,16 @@ typedef struct PartOidExprState
{
ExprState xprstate;
/* accepted leaf PartitionConstraints for current tuple */
struct PartitionConstraints **acceptedLeafPart;
/*
* Pointer to the accepted leaf OID stored in PartitionSelectorState.
* Note: the other partition selector expressions refer to
* PartitionSelectorState directly to extract information from the currently
* selected rule. However, a PartOidExpr is different from those as this one
* is used after the selection is done and rules list are freed to project
* a partition oid output. Therefore, we cannot rely on reading part oid
* from the currently selected leaf rule, stored inside levelPartRules.
*/
Oid *acceptedLeafOid;
} PartOidExprState;
/* ----------------
......@@ -1013,8 +1021,8 @@ typedef struct PartDefaultExprState
{
ExprState xprstate;
/* accepted partitions for all levels */
struct PartitionConstraints **levelPartConstraints;
/* PartitionSelectorState where expression evaluator can look for rules */
struct PartitionSelectorState *selector;
} PartDefaultExprState;
/* ----------------
......@@ -1025,8 +1033,8 @@ typedef struct PartBoundExprState
{
ExprState xprstate;
/* accepted partitions for all levels */
struct PartitionConstraints **levelPartConstraints;
/* PartitionSelectorState where expression evaluator can look for rules */
struct PartitionSelectorState *selector;
} PartBoundExprState;
/* ----------------
......@@ -1037,8 +1045,8 @@ typedef struct PartBoundInclusionExprState
{
ExprState xprstate;
/* accepted partitions for all levels */
struct PartitionConstraints **levelPartConstraints;
/* PartitionSelectorState where expression evaluator can look for rules */
struct PartitionSelectorState *selector;
} PartBoundInclusionExprState;
/* ----------------
......@@ -1049,10 +1057,34 @@ typedef struct PartBoundOpenExprState
{
ExprState xprstate;
/* accepted partitions for all levels */
struct PartitionConstraints **levelPartConstraints;
/* PartitionSelectorState where expression evaluator can look for rules */
struct PartitionSelectorState *selector;
} PartBoundOpenExprState;
/* ----------------
* PartListRuleExprState node
* ----------------
*/
typedef struct PartListRuleExprState
{
ExprState xprstate;
/* PartitionSelectorState where expression evaluator can look for rules */
struct PartitionSelectorState *selector;
} PartListRuleExprState;
/* ----------------
* PartListNullTestExprState node
* ----------------
*/
typedef struct PartListNullTestExprState
{
ExprState xprstate;
/* PartitionSelectorState where expression evaluator can look for rules */
struct PartitionSelectorState *selector;
} PartListNullTestExprState;
/* ----------------
* SubPlanState node
* ----------------
......@@ -2631,8 +2663,8 @@ struct PartitionSelectorState
PlanState ps; /* its first field is NodeTag */
PartitionNode *rootPartitionNode; /* PartitionNode for root table */
PartitionAccessMethods *accessMethods; /* Access method for partition */
struct PartitionConstraints **levelPartConstraints; /* accepted partitions for all levels */
struct PartitionConstraints **acceptedLeafPart; /* accepted leaf PartitionConstraints for current tuple */
struct PartitionRule **levelPartRules; /* accepted partitions for all levels */
Oid acceptedLeafOid; /* accepted leaf OID for current tuple */
List *levelEqExprStates; /* ExprState for equality expressions for all levels */
List *levelExprStates; /* ExprState for general expressions for all levels */
ExprState *residualPredicateExprState; /* ExprState for evaluating residual predicate */
......
......@@ -220,6 +220,8 @@ typedef enum NodeTag
T_PartBoundExpr,
T_PartBoundInclusionExpr,
T_PartBoundOpenExpr,
T_PartListRuleExpr,
T_PartListNullTestExpr,
T_TableOidInfo,
/*
......@@ -261,6 +263,8 @@ typedef enum NodeTag
T_PartBoundExprState,
T_PartBoundInclusionExprState,
T_PartBoundOpenExprState,
T_PartListRuleExprState,
T_PartListNullTestExprState,
/*
* TAGS FOR PLANNER NODES (relation.h)
......@@ -509,7 +513,6 @@ typedef enum NodeTag
T_FormatterData, /* in access/formatter.h */
T_ExtProtocolData, /* in access/extprotocol.h */
T_ExtProtocolValidatorData, /* in access/extprotocol.h */
T_PartitionConstraints, /* in executor/nodePartitionSelector.h */
T_SelectedParts, /* in executor/nodePartitionSelector.h */
/* CDB: tags for random other stuff */
......
......@@ -1539,8 +1539,38 @@ typedef struct PartBoundInclusionExpr
typedef struct PartBoundOpenExpr
{
Expr xpr;
int level; /* partitioning level */
int level; /* partitioning level */
bool isLowerBound; /* lower (min) or upper (max) bound */
} PartBoundOpenExpr;
/*
* PartListRuleExpr
* Represents the expression that converts the current rule for
* level "level" to a list of constants. It only appears in
* levelExpressions (which is non-equality predicates) for
* the partition selector.
*/
typedef struct PartListRuleExpr
{
Expr xpr;
int level; /* partitioning level */
Oid resulttype; /* the result type of expr - array type of part key */
Oid elementtype; /* the element type of partition list values */
} PartListRuleExpr;
/*
* PartListNullTestExpr
*
* Represents whether the list values of a partition for the specified level
* contains NULL or not, if nulltesttype is IS_NULL.
*
* NOTE: This expr only works for list partition.
*/
typedef struct PartListNullTestExpr
{
Expr xpr;
int level; /* partitioning level */
NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
} PartListNullTestExpr;
#endif /* PRIMNODES_H */
......@@ -14,6 +14,9 @@ set enable_seqscan=off;
set enable_bitmapscan=on;
set enable_indexscan=on;
create schema partition_pruning;
set search_path to partition_pruning, public;
-- Set up common test tables.
CREATE TABLE pt_lt_tab
(
......@@ -582,11 +585,13 @@ create language plpythonu;
create or replace function get_selected_parts(explain_query text) returns text as
$$
rv = plpy.execute('explain ' + explain_query)
rv = plpy.execute(explain_query)
search_text = 'Partition Selector'
result = []
result.append(0)
result.append(0)
selected = 0
out_of = 0
for i in range(len(rv)):
cur_line = rv[i]['QUERY PLAN']
if search_text.lower() in cur_line.lower():
......@@ -599,8 +604,10 @@ for i in range(len(rv)):
temp_line = rv[j]['QUERY PLAN']
if temp_line.find('Partitions selected:') != -1:
result[0] = int(temp_line[temp_line.index('selected: ')+10:temp_line.index(' (out')])
result[1] = int(temp_line[temp_line.index('out of')+6:temp_line.index(')')])
selected += int(temp_line[temp_line.index('selected: ')+10:temp_line.index(' (out')])
out_of += int(temp_line[temp_line.index('out of')+6:temp_line.index(')')])
result[0] = selected
result[1] = out_of
return result
$$
language plpythonu;
......@@ -610,23 +617,200 @@ create table partprune_foo(a int, b int, c int) partition by range (b) (start (1
insert into partprune_foo select generate_series(1,5), generate_series(1,100), generate_series(1,10);
analyze partprune_foo;
select get_selected_parts(' select * from partprune_foo;');
select get_selected_parts('explain select * from partprune_foo;');
select * from partprune_foo;
select get_selected_parts(' select * from partprune_foo where b = 35;');
select get_selected_parts('explain select * from partprune_foo where b = 35;');
select * from partprune_foo where b = 35;
select get_selected_parts(' select * from partprune_foo where b < 35;');
select get_selected_parts('explain select * from partprune_foo where b < 35;');
select * from partprune_foo where b < 35;
select get_selected_parts(' select * from partprune_foo where b in (5, 6, 14, 23);');
select get_selected_parts('explain select * from partprune_foo where b in (5, 6, 14, 23);');
select * from partprune_foo where b in (5, 6, 14, 23);
select get_selected_parts(' select * from partprune_foo where b < 15 or b > 60;');
select get_selected_parts('explain select * from partprune_foo where b < 15 or b > 60;');
select * from partprune_foo where b < 15 or b > 60;
select get_selected_parts(' select * from partprune_foo where b = 150;');
select get_selected_parts('explain select * from partprune_foo where b = 150;');
select * from partprune_foo where b = 150;
select get_selected_parts(' select * from partprune_foo where b = a*5;');
select get_selected_parts('explain select * from partprune_foo where b = a*5;');
select * from partprune_foo where b = a*5;
-- Check for all the different number of partition selections
DROP TABLE IF EXISTS DATE_PARTS;
CREATE TABLE DATE_PARTS (id int, year int, month int, day int, region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
SUBPARTITION BY LIST (month)
SUBPARTITION TEMPLATE (
SUBPARTITION Q1 VALUES (1, 2, 3),
SUBPARTITION Q2 VALUES (4 ,5 ,6),
SUBPARTITION Q3 VALUES (7, 8, 9),
SUBPARTITION Q4 VALUES (10, 11, 12),
DEFAULT SUBPARTITION other_months )
SUBPARTITION BY RANGE(day)
SUBPARTITION TEMPLATE (
START (1) END (31) EVERY (10),
DEFAULT SUBPARTITION other_days)
( START (2002) END (2012) EVERY (4),
DEFAULT PARTITION outlying_years );
insert into DATE_PARTS select i, extract(year from dt), extract(month from dt), extract(day from dt), NULL from (select i, '2002-01-01'::date + i * interval '1 day' day as dt from generate_series(1, 3650) as i) as t;
-- Expected total parts => 4 * 1 * 4 => 16:
-- TODO #141973839: we selected extra parts because of disjunction: 32 parts: 4 * 2 * 4
select get_selected_parts('explain analyze select * from DATE_PARTS where month between 1 and 3;');
-- Expected total parts => 4 * 2 * 4 => 32:
-- TODO #141973839: we selected extra parts because of disjunction: 48 parts: 4 * 3 * 4
select get_selected_parts('explain analyze select * from DATE_PARTS where month between 1 and 4;');
-- Expected total parts => 1 * 2 * 4 => 8:
-- TODO #141973839: we selected extra parts because of disjunction: 24 parts: 2 * 3 * 4
select get_selected_parts('explain analyze select * from DATE_PARTS where year = 2003 and month between 1 and 4;');
-- 1 :: 5 :: 4 => 20 // Only default for year
select get_selected_parts('explain analyze select * from DATE_PARTS where year = 1999;');
-- 4 :: 1 :: 4 => 16 // Only default for month
select get_selected_parts('explain analyze select * from DATE_PARTS where month = 13;');
-- 1 :: 1 :: 4 => 4 // Default for both year and month
select get_selected_parts('explain analyze select * from DATE_PARTS where year = 1999 and month = 13;');
-- 4 :: 5 :: 1 => 20 // Only default part for day
select get_selected_parts('explain analyze select * from DATE_PARTS where day = 40;');
-- General predicate
-- TODO #141973839. We expected 112 parts: (month = 1) => 4 * 1 * 4 => 16, month > 3 => 4 * 4 * 4 => 64, month in (0, 1, 2) => 4 * 1 * 4 => 16, month is NULL => 4 * 1 * 4 => 16.
-- However, we selected 128 parts: (month = 1) => 4 * 1 * 4 => 16, month > 3 => 4 * 4 * 4 => 64, month in (0, 1, 2) => 4 * 2 * 4 => 32, month is NULL => 4 * 1 * 4 => 16.
select get_selected_parts('explain analyze select * from DATE_PARTS where month = 1 union all select * from DATE_PARTS where month > 3 union all select * from DATE_PARTS where month in (0,1,2) union all select * from DATE_PARTS where month is null;');
-- Equality predicate
-- 16 partitions => 4 from year x 1 from month x 4 from days.
select get_selected_parts('explain analyze select * from DATE_PARTS where month = 3;'); -- Not working (it only produces general)
-- More Equality and General Predicates ---
drop table if exists foo;
create table foo(a int, b int)
partition by list (b)
(partition p1 values(1,3), partition p2 values(4,2), default partition other);
-- General predicate
-- Total 6 parts. b = 1: 1 part, b > 3: 2 parts, b in (0, 1): 2 parts. b is null: 1 part
select get_selected_parts('explain analyze select * from foo where b = 1 union all select * from foo where b > 3 union all select * from foo where b in (0,1) union all select * from foo where b is null;');
drop table if exists pt;
CREATE TABLE pt (id int, gender varchar(2))
DISTRIBUTED BY (id)
PARTITION BY LIST (gender)
( PARTITION girls VALUES ('F', NULL),
PARTITION boys VALUES ('M'),
DEFAULT PARTITION other );
-- General filter
-- TODO #141916623. Expecting 6 parts, but optimizer plan selects 7 parts. The 6 parts breakdown is: gender = 'F': 1 part, gender < 'M': 2 parts (including default), gender in ('F', F'M'): 2 parts, gender is null => 1 part
select get_selected_parts('explain analyze select * from pt where gender = ''F'' union all select * from pt where gender < ''M'' union all select * from pt where gender in (''F'', ''FM'') union all select * from pt where gender is null;');
-- DML
-- Non-default part
insert into DATE_PARTS values (-1, 2004, 11, 30, NULL);
select * from date_parts_1_prt_2_2_prt_q4_3_prt_4 where id < 0;
-- Default year
insert into DATE_PARTS values (-2, 1999, 11, 30, NULL);
select * from date_parts_1_prt_outlying_years_2_prt_q4_3_prt_4 where id < 0;
-- Default month
insert into DATE_PARTS values (-3, 2004, 20, 30, NULL);
select * from date_parts_1_prt_2_2_prt_other_months where id < 0;
-- Default day
insert into DATE_PARTS values (-4, 2004, 10, 50, NULL);
select * from date_parts_1_prt_2_2_prt_q4_3_prt_other_days where id < 0;
-- Default everything
insert into DATE_PARTS values (-5, 1999, 20, 50, NULL);
select * from date_parts_1_prt_outlying_years_2_prt_other_mo_3_prt_other_days where id < 0;
-- Default month + day but not year
insert into DATE_PARTS values (-6, 2002, 20, 50, NULL);
select * from date_parts_1_prt_2_2_prt_other_months_3_prt_other_days where id < 0;
-- Dropped columns with exchange
drop table if exists sales;
CREATE TABLE sales (trans_id int, to_be_dropped1 int, date date, amount
decimal(9,2), to_be_dropped2 int, region text)
DISTRIBUTED BY (trans_id)
PARTITION BY RANGE (date)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE
( SUBPARTITION usa VALUES ('usa'),
SUBPARTITION asia VALUES ('asia'),
SUBPARTITION europe VALUES ('europe'),
DEFAULT SUBPARTITION other_regions)
(START (date '2011-01-01') INCLUSIVE
END (date '2012-01-01') EXCLUSIVE
EVERY (INTERVAL '3 month'),
DEFAULT PARTITION outlying_dates );
-- This will introduce different column numbers in subsequent part tables
alter table sales drop column to_be_dropped1;
alter table sales drop column to_be_dropped2;
-- Create the exchange candidate without dropped columns
drop table if exists sales_exchange_part;
create table sales_exchange_part (trans_id int, date date, amount
decimal(9,2), region text);
-- Insert some data
insert into sales_exchange_part values(1, '2011-01-01', 10.1, 'usa');
-- Exchange
ALTER TABLE sales
ALTER PARTITION FOR (RANK(1))
EXCHANGE PARTITION FOR ('usa') WITH TABLE sales_exchange_part ;
-- TODO: #141973839. Expected 10 parts, currently selecting 15 parts. First level: 4 parts + 1 default. Second level 2 parts. Total 10 parts.
select get_selected_parts('explain analyze select * from sales where region = ''usa'' or region = ''asia'';');
select * from sales where region = 'usa' or region = 'asia';
-- Updating partition key
select * from sales_1_prt_2_2_prt_usa;
select * from sales_1_prt_2_2_prt_europe;
update sales set region = 'europe' where trans_id = 1;
select * from sales_1_prt_2_2_prt_europe;
select * from sales_1_prt_2_2_prt_usa;
select * from sales;
-- Distinct From
drop table if exists bar;
CREATE TABLE bar (i INTEGER, j decimal)
partition by list (j)
subpartition by range (i) subpartition template (start(1) end(4) every(2))
(partition p1 values(0.2,2.8, NULL), partition p2 values(1.7,3.1),
partition p3 values(5.6), default partition other);
insert into bar values(1, 0.2); --p1
insert into bar values(1, 1.7); --p2
insert into bar values(1, 2.1); --default
insert into bar values(1, 5.6); --default
insert into bar values(1, NULL); --p1
-- In-equality
-- 8 parts: All 4 parts on first level and each will have 2 range parts
select get_selected_parts('explain analyze select * from bar where j>0.02;');
-- 6 parts: Excluding 1 list parts at first level. So, 3 at first level and each has 2 at second level.
select get_selected_parts('explain analyze select * from bar where j>2.8;');
-- Distinct From
-- TODO: #141722947 6 parts: Everything except 1 part that contains 5.6.
select get_selected_parts('explain analyze select * from bar where j is distinct from 5.6;');
-- 8 parts: NULL is shared with others on p1. So, all 8 parts.
select get_selected_parts('explain analyze select * from bar where j is distinct from NULL;');
RESET ALL;
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册