use of org.voltdb.types.ExpressionType in project voltdb by VoltDB.
the class PlanAssembler method handleAggregationOperators.
private AbstractPlanNode handleAggregationOperators(AbstractPlanNode root) {
/*
* "Select A from T group by A" is grouped but has no aggregate operator
* expressions. Catch that case by checking the grouped flag
*/
if (m_parsedSelect.hasAggregateOrGroupby()) {
AggregatePlanNode aggNode = null;
// i.e., on the coordinator
AggregatePlanNode topAggNode = null;
IndexGroupByInfo gbInfo = new IndexGroupByInfo();
if (root instanceof AbstractReceivePlanNode) {
// for distinct that does not group by partition column
if (!m_parsedSelect.hasAggregateDistinct() || m_parsedSelect.hasPartitionColumnInGroupby()) {
AbstractPlanNode candidate = root.getChild(0).getChild(0);
gbInfo.m_multiPartition = true;
switchToIndexScanForGroupBy(candidate, gbInfo);
}
} else if (switchToIndexScanForGroupBy(root, gbInfo)) {
root = gbInfo.m_indexAccess;
}
boolean needHashAgg = gbInfo.needHashAggregator(root, m_parsedSelect);
// Construct the aggregate nodes
if (needHashAgg) {
if (m_parsedSelect.m_mvFixInfo.needed()) {
// TODO: may optimize this edge case in future
aggNode = new HashAggregatePlanNode();
} else {
if (gbInfo.isChangedToSerialAggregate()) {
assert (root instanceof ReceivePlanNode);
aggNode = new AggregatePlanNode();
} else if (gbInfo.isChangedToPartialAggregate()) {
aggNode = new PartialAggregatePlanNode(gbInfo.m_coveredGroupByColumns);
} else {
aggNode = new HashAggregatePlanNode();
}
topAggNode = new HashAggregatePlanNode();
}
} else {
aggNode = new AggregatePlanNode();
if (!m_parsedSelect.m_mvFixInfo.needed()) {
topAggNode = new AggregatePlanNode();
}
}
NodeSchema agg_schema = new NodeSchema();
NodeSchema top_agg_schema = new NodeSchema();
for (int outputColumnIndex = 0; outputColumnIndex < m_parsedSelect.m_aggResultColumns.size(); outputColumnIndex += 1) {
ParsedColInfo col = m_parsedSelect.m_aggResultColumns.get(outputColumnIndex);
AbstractExpression rootExpr = col.expression;
AbstractExpression agg_input_expr = null;
SchemaColumn schema_col = null;
SchemaColumn top_schema_col = null;
if (rootExpr instanceof AggregateExpression) {
ExpressionType agg_expression_type = rootExpr.getExpressionType();
agg_input_expr = rootExpr.getLeft();
// A bit of a hack: ProjectionNodes after the
// aggregate node need the output columns here to
// contain TupleValueExpressions (effectively on a temp table).
// So we construct one based on the output of the
// aggregate expression, the column alias provided by HSQL,
// and the offset into the output table schema for the
// aggregate node that we're computing.
// Oh, oh, it's magic, you know..
TupleValueExpression tve = new TupleValueExpression(AbstractParsedStmt.TEMP_TABLE_NAME, AbstractParsedStmt.TEMP_TABLE_NAME, "", col.alias, rootExpr, outputColumnIndex);
tve.setDifferentiator(col.differentiator);
boolean is_distinct = ((AggregateExpression) rootExpr).isDistinct();
aggNode.addAggregate(agg_expression_type, is_distinct, outputColumnIndex, agg_input_expr);
schema_col = new SchemaColumn(AbstractParsedStmt.TEMP_TABLE_NAME, AbstractParsedStmt.TEMP_TABLE_NAME, "", col.alias, tve, outputColumnIndex);
top_schema_col = new SchemaColumn(AbstractParsedStmt.TEMP_TABLE_NAME, AbstractParsedStmt.TEMP_TABLE_NAME, "", col.alias, tve, outputColumnIndex);
/*
* Special case count(*), count(), sum(), min() and max() to
* push them down to each partition. It will do the
* push-down if the select columns only contains the listed
* aggregate operators and other group-by columns. If the
* select columns includes any other aggregates, it will not
* do the push-down. - nshi
*/
if (topAggNode != null) {
ExpressionType top_expression_type = agg_expression_type;
/*
* For count(*), count() and sum(), the pushed-down
* aggregate node doesn't change. An extra sum()
* aggregate node is added to the coordinator to sum up
* the numbers from all the partitions. The input schema
* and the output schema of the sum() aggregate node is
* the same as the output schema of the push-down
* aggregate node.
*
* If DISTINCT is specified, don't do push-down for
* count() and sum() when not group by partition column.
* An exception is the aggregation arguments are the
* partition column (ENG-4980).
*/
if (agg_expression_type == ExpressionType.AGGREGATE_COUNT_STAR || agg_expression_type == ExpressionType.AGGREGATE_COUNT || agg_expression_type == ExpressionType.AGGREGATE_SUM) {
if (is_distinct && !(m_parsedSelect.hasPartitionColumnInGroupby() || canPushDownDistinctAggregation((AggregateExpression) rootExpr))) {
topAggNode = null;
} else {
// for aggregate distinct when group by
// partition column, the top aggregate node
// will be dropped later, thus there is no
// effect to assign the top_expression_type.
top_expression_type = ExpressionType.AGGREGATE_SUM;
}
} else /*
* For min() and max(), the pushed-down aggregate node
* doesn't change. An extra aggregate node of the same
* type is added to the coordinator. The input schema
* and the output schema of the top aggregate node is
* the same as the output schema of the pushed-down
* aggregate node.
*
* APPROX_COUNT_DISTINCT can be similarly pushed down, but
* must be split into two different functions, which is
* done later, from pushDownAggregate().
*/
if (agg_expression_type != ExpressionType.AGGREGATE_MIN && agg_expression_type != ExpressionType.AGGREGATE_MAX && agg_expression_type != ExpressionType.AGGREGATE_APPROX_COUNT_DISTINCT) {
/*
* Unsupported aggregate for push-down (AVG for example).
*/
topAggNode = null;
}
if (topAggNode != null) {
/*
* Input column of the top aggregate node is the
* output column of the push-down aggregate node
*/
boolean topDistinctFalse = false;
topAggNode.addAggregate(top_expression_type, topDistinctFalse, outputColumnIndex, tve);
}
}
// end if we have a top agg node
} else {
// has already been broken down.
assert (!rootExpr.hasAnySubexpressionOfClass(AggregateExpression.class));
/*
* These columns are the pass through columns that are not being
* aggregated on. These are the ones from the SELECT list. They
* MUST already exist in the child node's output. Find them and
* add them to the aggregate's output.
*/
schema_col = new SchemaColumn(col.tableName, col.tableAlias, col.columnName, col.alias, col.expression, outputColumnIndex);
AbstractExpression topExpr = null;
if (col.groupBy) {
topExpr = m_parsedSelect.m_groupByExpressions.get(col.alias);
} else {
topExpr = col.expression;
}
top_schema_col = new SchemaColumn(col.tableName, col.tableAlias, col.columnName, col.alias, topExpr, outputColumnIndex);
}
agg_schema.addColumn(schema_col);
top_agg_schema.addColumn(top_schema_col);
}
for (ParsedColInfo col : m_parsedSelect.groupByColumns()) {
aggNode.addGroupByExpression(col.expression);
if (topAggNode != null) {
topAggNode.addGroupByExpression(m_parsedSelect.m_groupByExpressions.get(col.alias));
}
}
aggNode.setOutputSchema(agg_schema);
if (topAggNode != null) {
if (m_parsedSelect.hasComplexGroupby()) {
topAggNode.setOutputSchema(top_agg_schema);
} else {
topAggNode.setOutputSchema(agg_schema);
}
}
// Never push down aggregation for MV fix case.
root = pushDownAggregate(root, aggNode, topAggNode, m_parsedSelect);
}
return handleDistinctWithGroupby(root);
}
use of org.voltdb.types.ExpressionType in project voltdb by VoltDB.
the class PlanAssembler method fixDistributedApproxCountDistinct.
/**
* This function is called once it's been determined that we can push down
* an aggregation plan node.
*
* If an APPROX_COUNT_DISTINCT aggregate is distributed, then we need to
* convert the distributed aggregate function to VALS_TO_HYPERLOGLOG,
* and the coordinating aggregate function to HYPERLOGLOGS_TO_CARD.
*
* @param distNode The aggregate node executed on each partition
* @param coordNode The aggregate node executed on the coordinator
*/
private static void fixDistributedApproxCountDistinct(AggregatePlanNode distNode, AggregatePlanNode coordNode) {
assert (distNode != null);
assert (coordNode != null);
// Patch up any APPROX_COUNT_DISTINCT on the distributed node.
List<ExpressionType> distAggTypes = distNode.getAggregateTypes();
boolean hasApproxCountDistinct = false;
for (int i = 0; i < distAggTypes.size(); ++i) {
ExpressionType et = distAggTypes.get(i);
if (et == ExpressionType.AGGREGATE_APPROX_COUNT_DISTINCT) {
hasApproxCountDistinct = true;
distNode.updateAggregate(i, ExpressionType.AGGREGATE_VALS_TO_HYPERLOGLOG);
}
}
if (hasApproxCountDistinct) {
// Now, patch up any APPROX_COUNT_DISTINCT on the coordinating node.
List<ExpressionType> coordAggTypes = coordNode.getAggregateTypes();
for (int i = 0; i < coordAggTypes.size(); ++i) {
ExpressionType et = coordAggTypes.get(i);
if (et == ExpressionType.AGGREGATE_APPROX_COUNT_DISTINCT) {
coordNode.updateAggregate(i, ExpressionType.AGGREGATE_HYPERLOGLOGS_TO_CARD);
}
}
}
}
use of org.voltdb.types.ExpressionType in project voltdb by VoltDB.
the class AggregatePlanNode method isTableNonDistinctCountConstant.
public boolean isTableNonDistinctCountConstant() {
if (!isTableNonDistinctCount()) {
return false;
}
AbstractExpression aggArgument = m_aggregateExpressions.get(0);
ExpressionType argumentType = aggArgument.getExpressionType();
// Is the expression a constant?
return argumentType.equals(ExpressionType.VALUE_PARAMETER) || argumentType.equals(ExpressionType.VALUE_CONSTANT);
}
use of org.voltdb.types.ExpressionType in project voltdb by VoltDB.
the class IndexCountPlanNode method createOrNull.
// Create an IndexCountPlanNode that replaces the parent aggregate and child
// indexscan IF the indexscan's end expressions are a form that can be
// modeled with an end key.
// The supported forms for end expression are:
// - null
// - one filter expression per index key component (ANDed together)
// as "combined" for the IndexScan.
// - fewer filter expressions than index key components with one of the
// (the last) being a LT comparison.
// - 1 fewer filter expressions than index key components,
// but all ANDed equality filters
// The LT restriction comes because when index key prefixes are identical
// to the prefix-only end key, the entire index key sorts greater than the
// prefix-only end-key, because it is always longer.
// These prefix-equal cases would be missed in an EQ or LTE filter,
// causing undercounts.
// A prefix-only LT filter discards prefix-equal cases, so it is allowed.
// @return the IndexCountPlanNode or null if one is not possible.
public static IndexCountPlanNode createOrNull(IndexScanPlanNode isp, AggregatePlanNode apn) {
// add support for reverse scan
// for ASC scan, check endExpression;
// for DESC scan (isReverseScan()), check the searchkeys
List<AbstractExpression> endKeys = new ArrayList<>();
// Translate the index scan's end condition into a list of end key
// expressions and note the comparison operand of the last one.
// Initially assume it to be an equality filter.
IndexLookupType endType = IndexLookupType.EQ;
List<AbstractExpression> endComparisons = ExpressionUtil.uncombinePredicate(isp.getEndExpression());
for (AbstractExpression ae : endComparisons) {
// LT or LTE expression that resets the end type.
assert (endType == IndexLookupType.EQ);
ExpressionType exprType = ae.getExpressionType();
if (exprType == ExpressionType.COMPARE_LESSTHAN) {
endType = IndexLookupType.LT;
} else if (exprType == ExpressionType.COMPARE_LESSTHANOREQUALTO) {
endType = IndexLookupType.LTE;
} else {
assert (exprType == ExpressionType.COMPARE_EQUAL || exprType == ExpressionType.COMPARE_NOTDISTINCT);
}
// PlanNodes all need private deep copies of expressions
// so that the resolveColumnIndexes results
// don't get bashed by other nodes or subsequent planner runs
endKeys.add(ae.getRight().clone());
}
int indexSize = 0;
String jsonstring = isp.getCatalogIndex().getExpressionsjson();
List<ColumnRef> indexedColRefs = null;
List<AbstractExpression> indexedExprs = null;
if (jsonstring.isEmpty()) {
indexedColRefs = CatalogUtil.getSortedCatalogItems(isp.getCatalogIndex().getColumns(), "index");
indexSize = indexedColRefs.size();
} else {
try {
indexedExprs = AbstractExpression.fromJSONArrayString(jsonstring, isp.getTableScan());
indexSize = indexedExprs.size();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
int searchKeySize = isp.getSearchKeyExpressions().size();
int endKeySize = endKeys.size();
if (!isp.isReverseScan() && endType != IndexLookupType.LT && endKeySize > 0 && endKeySize < indexSize) {
// That is, when a prefix-only key exists and does not use LT.
if (endType != IndexLookupType.EQ || searchKeySize != indexSize || endKeySize < indexSize - 1) {
return null;
}
// To use an index count for an equality search of da compound key,
// both the search key and end key must have a component for each
// index component.
// If the search key is long enough but the end key is one component
// short, it can be patched with a type-appropriate max key value
// (if one exists for the type), but the end key comparison needs to
// change from EQ to LTE to compensate.
VoltType missingEndKeyType;
// and get the missing key component's indexed expression.
if (jsonstring.isEmpty()) {
int lastIndex = indexedColRefs.get(endKeySize).getColumn().getIndex();
for (AbstractExpression expr : endComparisons) {
if (((TupleValueExpression) (expr.getLeft())).getColumnIndex() == lastIndex) {
return null;
}
}
int catalogTypeCode = indexedColRefs.get(endKeySize).getColumn().getType();
missingEndKeyType = VoltType.get((byte) catalogTypeCode);
} else {
AbstractExpression lastIndexedExpr = indexedExprs.get(endKeySize);
for (AbstractExpression expr : endComparisons) {
if (expr.getLeft().bindingToIndexedExpression(lastIndexedExpr) != null) {
return null;
}
}
missingEndKeyType = lastIndexedExpr.getValueType();
}
String maxValueForType = missingEndKeyType.getMaxValueForKeyPadding();
// for which all legal values are less than or equal to it.
if (maxValueForType == null) {
return null;
}
ConstantValueExpression maxKey = new ConstantValueExpression();
maxKey.setValueType(missingEndKeyType);
maxKey.setValue(maxValueForType);
maxKey.setValueSize(missingEndKeyType.getLengthInBytesForFixedTypes());
endType = IndexLookupType.LTE;
endKeys.add(maxKey);
}
// DESC case
if (searchKeySize > 0 && searchKeySize < indexSize) {
return null;
}
return new IndexCountPlanNode(isp, apn, endType, endKeys);
}
use of org.voltdb.types.ExpressionType in project voltdb by VoltDB.
the class IndexScanPlanNode method buildSkipNullPredicate.
public static AbstractExpression buildSkipNullPredicate(int nullExprIndex, Index catalogIndex, StmtTableScan tableScan, List<AbstractExpression> searchkeyExpressions, List<Boolean> compareNotDistinct) {
String exprsjson = catalogIndex.getExpressionsjson();
List<AbstractExpression> indexedExprs = null;
if (exprsjson.isEmpty()) {
indexedExprs = new ArrayList<>();
List<ColumnRef> indexedColRefs = CatalogUtil.getSortedCatalogItems(catalogIndex.getColumns(), "index");
assert (nullExprIndex < indexedColRefs.size());
for (int i = 0; i <= nullExprIndex; i++) {
ColumnRef colRef = indexedColRefs.get(i);
Column col = colRef.getColumn();
TupleValueExpression tve = new TupleValueExpression(tableScan.getTableName(), tableScan.getTableAlias(), col, col.getIndex());
indexedExprs.add(tve);
}
} else {
try {
indexedExprs = AbstractExpression.fromJSONArrayString(exprsjson, tableScan);
assert (nullExprIndex < indexedExprs.size());
} catch (JSONException e) {
e.printStackTrace();
assert (false);
}
}
// For a partial index extract all TVE expressions from it predicate if it's NULL-rejecting expression
// These TVEs do not need to be added to the skipNUll predicate because it's redundant.
AbstractExpression indexPredicate = null;
Set<TupleValueExpression> notNullTves = null;
String indexPredicateJson = catalogIndex.getPredicatejson();
if (!StringUtil.isEmpty(indexPredicateJson)) {
try {
indexPredicate = AbstractExpression.fromJSONString(indexPredicateJson, tableScan);
assert (indexPredicate != null);
} catch (JSONException e) {
e.printStackTrace();
assert (false);
}
if (ExpressionUtil.isNullRejectingExpression(indexPredicate, tableScan.getTableAlias())) {
notNullTves = new HashSet<>();
notNullTves.addAll(ExpressionUtil.getTupleValueExpressions(indexPredicate));
}
}
AbstractExpression nullExpr = indexedExprs.get(nullExprIndex);
AbstractExpression skipNullPredicate = null;
if (notNullTves == null || !notNullTves.contains(nullExpr)) {
List<AbstractExpression> exprs = new ArrayList<>();
for (int i = 0; i < nullExprIndex; i++) {
AbstractExpression idxExpr = indexedExprs.get(i);
ExpressionType exprType = ExpressionType.COMPARE_EQUAL;
if (i < compareNotDistinct.size() && compareNotDistinct.get(i)) {
exprType = ExpressionType.COMPARE_NOTDISTINCT;
}
AbstractExpression expr = new ComparisonExpression(exprType, idxExpr, searchkeyExpressions.get(i).clone());
exprs.add(expr);
}
// then we add "nullExpr IS NULL" to the expression for matching tuples to skip. (ENG-11096)
if (nullExprIndex == searchkeyExpressions.size() || compareNotDistinct.get(nullExprIndex) == false) {
// nullExprIndex == m_searchkeyExpressions.size() - 1
AbstractExpression expr = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, nullExpr, null);
exprs.add(expr);
} else {
return null;
}
skipNullPredicate = ExpressionUtil.combinePredicates(exprs);
skipNullPredicate.finalizeValueTypes();
}
return skipNullPredicate;
}
Aggregations