Search in sources :

Example 1 with SlidingWindowLogicalRel

use of com.hazelcast.jet.sql.impl.opt.logical.SlidingWindowLogicalRel in project hazelcast by hazelcast.

the class AggregateSlidingWindowPhysicalRule method onMatch.

@Override
public void onMatch(RelOptRuleCall call) {
    AggregateLogicalRel logicalAggregate = call.rel(0);
    assert logicalAggregate.getGroupType() == Group.SIMPLE;
    assert logicalAggregate.getGroupSets().size() == 1 && (logicalAggregate.getGroupSet() == null || logicalAggregate.getGroupSet().equals(logicalAggregate.getGroupSets().get(0)));
    List<RexNode> projections;
    RelDataType projectRowType;
    SlidingWindowLogicalRel windowRel;
    if (hasProject) {
        Project projectRel = call.rel(1);
        projections = new ArrayList<>(projectRel.getProjects());
        projectRowType = projectRel.getRowType();
        windowRel = call.rel(2);
    } else {
        windowRel = call.rel(1);
        // create an identity projection
        List<RelDataTypeField> fields = windowRel.getRowType().getFieldList();
        projections = new ArrayList<>(fields.size());
        for (int i = 0; i < fields.size(); i++) {
            RelDataTypeField field = fields.get(i);
            projections.add(call.builder().getRexBuilder().makeInputRef(field.getType(), i));
        }
        projectRowType = windowRel.getRowType();
    }
    // Our input hierarchy is, for example:
    // -Aggregate(group=[$0], EXPR$1=[AVG($1)])
    // --Project(rowType=[window_start, field1])
    // ---SlidingWindowRel(rowType=[field1, field2, timestamp, window_start, window_end])
    // 
    // We need to preserve the column used to generate window bounds and remove the window
    // bounds from the projection to get input projection such as this:
    // -SlidingWindowAggregatePhysicalRel(group=[$0], EXPR$1=[AVG($1)])
    // --Project(rowType=[timestamp, field1])
    // 
    // The group=[$0] we pass to SlidingWindowAggregatePhysicalRel' superclass isn't correct,
    // but it works for us for now - the superclass uses it only to calculate the output type.
    // And the timestamp and the window bound have the same type.
    int timestampIndex = windowRel.orderingFieldIndex();
    int windowStartIndex = windowRel.windowStartIndex();
    int windowEndIndex = windowRel.windowEndIndex();
    // Replace references to either window bound to timestamp in projection
    List<Integer> windowStartIndexes = new ArrayList<>();
    List<Integer> windowEndIndexes = new ArrayList<>();
    for (int i = 0; i < projections.size(); i++) {
        RexNode projection = projections.get(i);
        // we don't support any transformation of the window bound using an expression, it must be a direct input reference.
        if (projection instanceof RexInputRef) {
            int index = ((RexInputRef) projection).getIndex();
            if (index == windowStartIndex || index == windowEndIndex) {
                // todo [viliam] avoid multiple projections of the timestamp
                projection = call.builder().getRexBuilder().makeInputRef(projection.getType(), timestampIndex);
                projections.set(i, projection);
                if (index == windowStartIndex) {
                    windowStartIndexes.add(i);
                } else {
                    windowEndIndexes.add(i);
                }
            }
        } else if (hasInputRef(projection, windowStartIndex, windowEndIndex)) {
            throw QueryException.error(SqlErrorCode.PARSING, "In window aggregation, the window_start and window_end fields must be used" + " directly, without any transformation");
        }
    }
    RelNode input = windowRel.getInput();
    RelNode convertedInput = OptUtils.toPhysicalInput(input);
    Collection<RelNode> transformedInputs = OptUtils.extractPhysicalRelsFromSubset(convertedInput);
    for (RelNode transformedInput : transformedInputs) {
        // todo [viliam] change the name for window bound replaced with timestamps
        RelDataType newRowType = projectRowType;
        RelNode newProject = new ProjectPhysicalRel(transformedInput.getCluster(), transformedInput.getTraitSet(), transformedInput, projections, newRowType);
        RelNode transformedRel = transform(newProject, logicalAggregate, windowStartIndexes, windowEndIndexes, windowRel.windowPolicyProvider());
        if (transformedRel != null) {
            call.transformTo(transformedRel);
        }
    }
}
Also used : AggregateLogicalRel(com.hazelcast.jet.sql.impl.opt.logical.AggregateLogicalRel) ArrayList(java.util.ArrayList) RelDataType(org.apache.calcite.rel.type.RelDataType) Project(org.apache.calcite.rel.core.Project) RelDataTypeField(org.apache.calcite.rel.type.RelDataTypeField) RelNode(org.apache.calcite.rel.RelNode) SlidingWindowLogicalRel(com.hazelcast.jet.sql.impl.opt.logical.SlidingWindowLogicalRel) RexInputRef(org.apache.calcite.rex.RexInputRef) RexNode(org.apache.calcite.rex.RexNode)

Aggregations

AggregateLogicalRel (com.hazelcast.jet.sql.impl.opt.logical.AggregateLogicalRel)1 SlidingWindowLogicalRel (com.hazelcast.jet.sql.impl.opt.logical.SlidingWindowLogicalRel)1 ArrayList (java.util.ArrayList)1 RelNode (org.apache.calcite.rel.RelNode)1 Project (org.apache.calcite.rel.core.Project)1 RelDataType (org.apache.calcite.rel.type.RelDataType)1 RelDataTypeField (org.apache.calcite.rel.type.RelDataTypeField)1 RexInputRef (org.apache.calcite.rex.RexInputRef)1 RexNode (org.apache.calcite.rex.RexNode)1