Package org.apache.phoenix.compile

Source Code of org.apache.phoenix.compile.TrackOrderPreservingExpressionCompiler$Entry

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.phoenix.compile;

import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.LiteralExpression;
import org.apache.phoenix.expression.function.FunctionExpression;
import org.apache.phoenix.expression.function.FunctionExpression.OrderPreserving;
import org.apache.phoenix.parse.CaseParseNode;
import org.apache.phoenix.parse.ColumnParseNode;
import org.apache.phoenix.parse.DivideParseNode;
import org.apache.phoenix.parse.MultiplyParseNode;
import org.apache.phoenix.parse.SubtractParseNode;
import org.apache.phoenix.schema.ColumnRef;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.PTable.IndexType;
import org.apache.phoenix.util.SchemaUtil;

import com.google.common.collect.Lists;

/**
* Visitor that builds the expressions of a GROUP BY and ORDER BY clause. While traversing
* the parse node tree, the visitor also determines if the natural key order of the scan
* will match the order of the expressions. For GROUP BY, if order is preserved we can use
* an optimization during server-side aggregation to do the aggregation on-the-fly versus
* keeping track of each distinct group. We can only do this optimization if all the rows
* for each group will be contiguous. For ORDER BY, we can drop the ORDER BY statement if
* the order is preserved.
*
*/
public class TrackOrderPreservingExpressionCompiler extends ExpressionCompiler {
    public enum Ordering {ORDERED, UNORDERED};
   
    private final List<Entry> entries;
    private final Ordering ordering;
    private final int positionOffset;
    private OrderPreserving orderPreserving = OrderPreserving.YES;
    private ColumnRef columnRef;
    private boolean isOrderPreserving = true;
    private Boolean isReverse;
   
    TrackOrderPreservingExpressionCompiler(StatementContext context, GroupBy groupBy, int expectedEntrySize, Ordering ordering) {
        super(context, groupBy);
        PTable table = context.getResolver().getTables().get(0).getTable();
        boolean isSalted = table.getBucketNum() != null;
        boolean isMultiTenant = context.getConnection().getTenantId() != null && table.isMultiTenant();
        boolean isSharedViewIndex = table.getViewIndexId() != null;
        // TODO: util for this offset, as it's computed in numerous places
        positionOffset = (isSalted ? 1 : 0) + (isMultiTenant ? 1 : 0) + (isSharedViewIndex ? 1 : 0);
        entries = Lists.newArrayListWithExpectedSize(expectedEntrySize);
        this.ordering = ordering;
    }
   
    public Boolean isReverse() {
        return isReverse;
    }

    public boolean isOrderPreserving() {
        if (!isOrderPreserving) {
            return false;
        }
        if (ordering == Ordering.UNORDERED) {
            // Sort by position
            Collections.sort(entries, new Comparator<Entry>() {
                @Override
                public int compare(Entry o1, Entry o2) {
                    return o1.getPkPosition()-o2.getPkPosition();
                }
            });
        }
        // Determine if there are any gaps in the PK columns (in which case we don't need
        // to sort in the coprocessor because the keys will already naturally be in sorted
        // order.
        int prevPos = positionOffset - 1;
        OrderPreserving prevOrderPreserving = OrderPreserving.YES;
        for (int i = 0; i < entries.size() && isOrderPreserving; i++) {
            Entry entry = entries.get(i);
            int pos = entry.getPkPosition();
            isOrderPreserving &= (entry.getOrderPreserving() != OrderPreserving.NO) && (pos == prevPos || ((pos - 1 == prevPos) && (prevOrderPreserving == OrderPreserving.YES)));
            prevPos = pos;
            prevOrderPreserving = entries.get(i).getOrderPreserving();
        }
        return isOrderPreserving;
    }
   
    @Override
    protected Expression addExpression(Expression expression) {
        // TODO: have FunctionExpression visitor instead and remove this cast
        if (expression instanceof FunctionExpression) {
            // Keep the minimum value between this function and the current value,
            // so that we never increase OrderPreserving from NO or YES_IF_LAST.
            orderPreserving = OrderPreserving.values()[Math.min(orderPreserving.ordinal(), ((FunctionExpression)expression).preservesOrder().ordinal())];
        }
        return super.addExpression(expression);
    }

    @Override
    public boolean visitEnter(CaseParseNode node) throws SQLException {
        orderPreserving = OrderPreserving.NO;
        return super.visitEnter(node);
    }
   
    @Override
    public boolean visitEnter(DivideParseNode node) throws SQLException {
        // A divide expression may not preserve row order.
        // For example: GROUP BY 1/x
        orderPreserving = OrderPreserving.NO;
        return super.visitEnter(node);
    }

    @Override
    public boolean visitEnter(SubtractParseNode node) throws SQLException {
        // A subtract expression may not preserve row order.
        // For example: GROUP BY 10 - x
        orderPreserving = OrderPreserving.NO;
        return super.visitEnter(node);
    }

    @Override
    public boolean visitEnter(MultiplyParseNode node) throws SQLException {
        // A multiply expression may not preserve row order.
        // For example: GROUP BY -1 * x
        orderPreserving = OrderPreserving.NO;
        return super.visitEnter(node);
    }

    @Override
    public void reset() {
        super.reset();
        columnRef = null;
        orderPreserving = OrderPreserving.YES;
    }
   
    @Override
    protected ColumnRef resolveColumn(ColumnParseNode node) throws SQLException {
        ColumnRef ref = super.resolveColumn(node);
        // If we encounter any non PK column, then we can't aggregate on-the-fly
        // because the distinct groups have no correlation to the KV column value
        if (!SchemaUtil.isPKColumn(ref.getColumn())) {
            orderPreserving = OrderPreserving.NO;
        }
       
        if (columnRef == null) {
            columnRef = ref;
        } else if (!columnRef.equals(ref)) {
            // If we encounter more than one column reference in an expression,
            // we can't assume the result of the expression will be key ordered.
            // For example GROUP BY a * b
            orderPreserving = OrderPreserving.NO;
        }
        return ref;
    }

    public boolean addEntry(Expression expression) {
        if (expression instanceof LiteralExpression) {
            return false;
        }
        isOrderPreserving &= (orderPreserving != OrderPreserving.NO);
        entries.add(new Entry(expression, columnRef, orderPreserving));
        return true;
    }
   
    public boolean addEntry(Expression expression, SortOrder sortOrder) {
        // If the expression is sorted in a different order than the specified sort order
        // then the expressions are not order preserving.
        if (expression.getSortOrder() != sortOrder) {
            if (isReverse == null) {
                isReverse = true;
            } else if (!isReverse){
                orderPreserving = OrderPreserving.NO;
            }
        } else {
            if (isReverse == null) {
                isReverse = false;
            } else if (isReverse){
                orderPreserving = OrderPreserving.NO;
            }
        }
        return addEntry(expression);
    }
   
    public List<Entry> getEntries() {
        return entries;
    }

    public static class Entry {
        private final Expression expression;
        private final ColumnRef columnRef;
        private final OrderPreserving orderPreserving;
       
        private Entry(Expression expression, ColumnRef columnRef, OrderPreserving orderPreserving) {
            this.expression = expression;
            this.columnRef = columnRef;
            this.orderPreserving = orderPreserving;
        }

        public Expression getExpression() {
            return expression;
        }

        public int getPkPosition() {
            return columnRef.getPKSlotPosition();
        }

        public int getColumnPosition() {
            return columnRef.getColumnPosition();
        }

        public OrderPreserving getOrderPreserving() {
            return orderPreserving;
        }
    }
}
TOP

Related Classes of org.apache.phoenix.compile.TrackOrderPreservingExpressionCompiler$Entry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.