Search in sources :

Example 11 with ExpressionType

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);
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) TupleValueExpression(org.voltdb.expressions.TupleValueExpression) AbstractReceivePlanNode(org.voltdb.plannodes.AbstractReceivePlanNode) HashAggregatePlanNode(org.voltdb.plannodes.HashAggregatePlanNode) AggregatePlanNode(org.voltdb.plannodes.AggregatePlanNode) PartialAggregatePlanNode(org.voltdb.plannodes.PartialAggregatePlanNode) AbstractReceivePlanNode(org.voltdb.plannodes.AbstractReceivePlanNode) MergeReceivePlanNode(org.voltdb.plannodes.MergeReceivePlanNode) ReceivePlanNode(org.voltdb.plannodes.ReceivePlanNode) PartialAggregatePlanNode(org.voltdb.plannodes.PartialAggregatePlanNode) HashAggregatePlanNode(org.voltdb.plannodes.HashAggregatePlanNode) SchemaColumn(org.voltdb.plannodes.SchemaColumn) AggregateExpression(org.voltdb.expressions.AggregateExpression) Constraint(org.voltdb.catalog.Constraint) AbstractExpression(org.voltdb.expressions.AbstractExpression) ExpressionType(org.voltdb.types.ExpressionType) NodeSchema(org.voltdb.plannodes.NodeSchema)

Example 12 with ExpressionType

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);
            }
        }
    }
}
Also used : ExpressionType(org.voltdb.types.ExpressionType) Constraint(org.voltdb.catalog.Constraint)

Example 13 with ExpressionType

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);
}
Also used : AbstractExpression(org.voltdb.expressions.AbstractExpression) ExpressionType(org.voltdb.types.ExpressionType)

Example 14 with ExpressionType

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);
}
Also used : IndexLookupType(org.voltdb.types.IndexLookupType) ArrayList(java.util.ArrayList) JSONException(org.json_voltpatches.JSONException) AbstractExpression(org.voltdb.expressions.AbstractExpression) VoltType(org.voltdb.VoltType) ConstantValueExpression(org.voltdb.expressions.ConstantValueExpression) ColumnRef(org.voltdb.catalog.ColumnRef) ExpressionType(org.voltdb.types.ExpressionType)

Example 15 with ExpressionType

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;
}
Also used : TupleValueExpression(org.voltdb.expressions.TupleValueExpression) ArrayList(java.util.ArrayList) JSONException(org.json_voltpatches.JSONException) ComparisonExpression(org.voltdb.expressions.ComparisonExpression) AbstractExpression(org.voltdb.expressions.AbstractExpression) Column(org.voltdb.catalog.Column) OperatorExpression(org.voltdb.expressions.OperatorExpression) ColumnRef(org.voltdb.catalog.ColumnRef) ExpressionType(org.voltdb.types.ExpressionType)

Aggregations

ExpressionType (org.voltdb.types.ExpressionType)30 AbstractExpression (org.voltdb.expressions.AbstractExpression)16 ArrayList (java.util.ArrayList)6 ComparisonExpression (org.voltdb.expressions.ComparisonExpression)6 JSONException (org.json_voltpatches.JSONException)5 VoltType (org.voltdb.VoltType)5 TupleValueExpression (org.voltdb.expressions.TupleValueExpression)5 Constraint (org.voltdb.catalog.Constraint)4 VoltXMLElement (org.hsqldb_voltpatches.VoltXMLElement)3 Column (org.voltdb.catalog.Column)3 AggregateExpression (org.voltdb.expressions.AggregateExpression)3 ColumnRef (org.voltdb.catalog.ColumnRef)2 MaterializedViewHandlerInfo (org.voltdb.catalog.MaterializedViewHandlerInfo)2 MaterializedViewInfo (org.voltdb.catalog.MaterializedViewInfo)2 OperatorExpression (org.voltdb.expressions.OperatorExpression)2 SelectSubqueryExpression (org.voltdb.expressions.SelectSubqueryExpression)2 WindowFunctionExpression (org.voltdb.expressions.WindowFunctionExpression)2 AbstractPlanNode (org.voltdb.plannodes.AbstractPlanNode)2 AggregatePlanNode (org.voltdb.plannodes.AggregatePlanNode)2 HashAggregatePlanNode (org.voltdb.plannodes.HashAggregatePlanNode)2