Package com.foundationdb.sql.optimizer

Source Code of com.foundationdb.sql.optimizer.DistinctEliminator

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.foundationdb.sql.optimizer;

import com.foundationdb.sql.parser.*;

import com.foundationdb.sql.StandardException;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.ais.model.Table;

import java.util.*;

/** Eliminate DISTINCT from SELECT when result is already distinct.
*
* Derby has a somewhat different version of this, but it does not try
* multiple result tables or outer joins.
*
* It would be nicer if this could be an actual rule, but it really
* has to run before ASTStatementLoader to keep from trying to sort on
* all the extra columns.
*
* Likewise, if this runs before SubqueryFlattener, it can remove a
* DISTINCT from a derived table and allow it to flatten. If it runs
* after, it will not be stopped by derived tables that do get
* flattened.  Really, the two need to cooperate more closely.
*/
public class DistinctEliminator
{
    private SQLParserContext parserContext;
    private FromBaseTable unjoinedTable;
    public DistinctEliminator(SQLParserContext parserContext) {
        this.parserContext = parserContext;
    }

    public DMLStatementNode eliminate(DMLStatementNode stmt) throws StandardException {
        ResultSetNode resultSet = stmt.getResultSetNode();
        if (resultSet.getNodeType() == NodeTypes.SELECT_NODE) {
            selectNode((SelectNode)resultSet);
        }
        return stmt;
    }

    protected void selectNode(SelectNode selectNode) throws StandardException {
        boolean foundSubquery = false;
        for (FromTable fromTable : selectNode.getFromList()) {
            if (fromTable instanceof FromSubquery) {
                ResultSetNode subquery = ((FromSubquery)fromTable).getSubquery();
                if (subquery instanceof SelectNode) {
                    selectNode((SelectNode)subquery);
                }
                foundSubquery = true;
            }
        }
        // May have eliminated from subquery, but can't from main one.
        if (foundSubquery) return;
       
        // Nothing more to do if not distinct or if grouped.
        if (!selectNode.isDistinct() ||
            (selectNode.getGroupByList() != null))
            return;

        unjoinedTable = null;

        ResultColumnList resultColumns = selectNode.getResultColumns();
        AndNode whereConditions = (AndNode)selectNode.getWhereClause();
        for (FromTable fromTable : selectNode.getFromList()) {
            if (!isTableDistinct(fromTable, resultColumns, whereConditions, null)) {
                return;
            }
        }

        // Some table needs to not just be joined, so that don't get fooled by
        // SELECT 1 FROM t1,t2 WHERE t1.pk = t2.pk
        if (unjoinedTable != null)
            // Everything looks distinct already.
            selectNode.clearDistinct();
    }

    protected boolean isTableDistinct(FromTable fromTable,
                                      ResultColumnList resultColumns,
                                      AndNode whereConditions, AndNode joinConditions)
            throws StandardException {
        if (fromTable instanceof FromBaseTable) {
            TableBinding binding = (TableBinding)fromTable.getUserData();
            if (binding == null) return false;

            return isTableDistinct((FromBaseTable)fromTable, binding,
                                   resultColumns, whereConditions, joinConditions);
        }
        else if (fromTable instanceof JoinNode) {
            return isJoinDistinct((JoinNode)fromTable, resultColumns, whereConditions);
        }
        else
            return false;
    }

    protected boolean isJoinDistinct(JoinNode join,
                                     ResultColumnList resultColumns,
                                     AndNode whereConditions
            throws StandardException {
        ResultSetNode left = join.getLeftResultSet();
        ResultSetNode right = join.getRightResultSet();
        if (!((left instanceof FromTable) && (right instanceof FromTable)))
            return false;
        FromTable leftTable = (FromTable)left;
        FromTable rightTable = (FromTable)right;
        ValueNode joinClause = join.getJoinClause();
        if ((joinClause != null) && !(joinClause instanceof AndNode))
            return false;
        AndNode joinConditions = (AndNode)joinClause;
        if (join instanceof HalfOuterJoinNode) {
            if (((HalfOuterJoinNode)join).isRightOuterJoin()) {
                return (isTableDistinct(leftTable, resultColumns,
                                        null, joinConditions) &&
                        isTableDistinct(rightTable, resultColumns,
                                        whereConditions, null));
            }
            else {
                return (isTableDistinct(leftTable, resultColumns,
                                        whereConditions, null) &&
                        isTableDistinct(rightTable, resultColumns,
                                        null, joinConditions));
            }
        }
        else
            return (isTableDistinct(leftTable, resultColumns,
                                    whereConditions, joinConditions) &&
                    isTableDistinct(rightTable, resultColumns,
                                    whereConditions, joinConditions));
    }

    protected boolean isTableDistinct(FromBaseTable table, TableBinding binding,
                                      ResultColumnList resultColumns,
                                      AndNode whereConditions, AndNode joinConditions)
            throws StandardException {
        boolean found = false;
        for (Index index : ((Table)binding.getTable()).getIndexes()) {
            if (!index.isUnique()) continue;
            Set<FromTable> joinTables = null;
            Set<FromTable> columnJoinTables = new HashSet<>();
            boolean handled = true, joined = false;
            for (IndexColumn indexColumn : index.getKeyColumns()) {
                Column column = indexColumn.getColumn();
                // A table's contribution is distinct if every column
                // in some unique index is not nullable and appears in
                // the select list. More joining (with the same
                // condition) won't introduce duplicates.
                if (!binding.isNullable() &&
                    !column.getNullable() &&
                    columnInResult(column, resultColumns)) {
                    continue;
                }
                // A table is unique (occurs zero or one times) if
                // every column of some unique index participates in
                // an equality constraint either with a constant or
                // with a single other table.

                // At least some cases of unique index columns joined
                // to two or more other tables don't keep it unique,
                // such as:
                //   (1,2) (2,3) (3,1)
                //   (1,2) (2,4) (4,1)
                // So don't bother with those cases.
                columnJoinTables.clear();
                if (!(columnInConditions(column, whereConditions, columnJoinTables) ||
                      columnInConditions(column, joinConditions, columnJoinTables))) {
                    columnJoinTables.remove(table);
                    if (columnJoinTables.isEmpty()) {
                        handled = false;
                        break;
                    }
                    joined = true;
                    if (joinTables == null)
                        joinTables = new HashSet<>(columnJoinTables);
                    else {
                        joinTables.retainAll(columnJoinTables);
                        if (joinTables.isEmpty()) {
                            handled = false;
                            break;
                        }
                    }
                }
            }
            if (handled) {
                found = true;
                if (!joined && (unjoinedTable == null))
                    unjoinedTable = table;
                if (unjoinedTable != null)
                    break;
                // If needed to join, try other indexes; they might not with same table.
            }
        }
        return found;
    }

    // Does the given column appear in the result set directly?
    protected boolean columnInResult(Column column, ResultColumnList resultColumns)
            throws StandardException {
        for (ResultColumn resultColumn : resultColumns) {
            if (isColumnReference(resultColumn.getExpression(), column)) {
                return true;
            }
        }
        return false;
    }

    // Is there some equality condition on this column in these conditions,
    // either to a constant (return true) or some other table(s) (return them)?
    protected boolean columnInConditions(Column column, AndNode conditions,
                                         Set<FromTable> joinTables)
            throws StandardException {
        if (conditions != null) {
            while (true) {
                ValueNode leftOperand = conditions.getLeftOperand();
                ValueNode rightOperand = conditions.getRightOperand();
                if (leftOperand.getNodeType() == NodeTypes.BINARY_EQUALS_OPERATOR_NODE) {
                    BinaryComparisonOperatorNode equals = (BinaryComparisonOperatorNode)leftOperand;
                    ValueNode otherOperand = null;
                    if (isColumnReference(equals.getLeftOperand(), column))
                        otherOperand = equals.getRightOperand();
                    else if (isColumnReference(equals.getRightOperand(), column))
                        otherOperand = equals.getLeftOperand();
                    if ((otherOperand instanceof ConstantNode) ||
                        (otherOperand instanceof ParameterNode))
                        return true;
                    else if (otherOperand instanceof ColumnReference) {
                        ColumnBinding columnBinding = (ColumnBinding)
                            ((ColumnReference)otherOperand).getUserData();
                        if (columnBinding != null)
                            joinTables.add(columnBinding.getFromTable());
                    }
                }
                if (rightOperand instanceof AndNode)
                    conditions = (AndNode)rightOperand;
                else
                    break;
            }
        }
        return false;
    }

    // This is a reference to the given column?
    protected boolean isColumnReference(ValueNode value, Column column)
            throws StandardException {
        if (value instanceof ColumnReference) {
            ColumnBinding columnBinding = (ColumnBinding)
                ((ColumnReference)value).getUserData();
            if ((columnBinding != null) &&
                (column == columnBinding.getColumn())) {
                return true;
            }
        }
        return false;
    }

}
TOP

Related Classes of com.foundationdb.sql.optimizer.DistinctEliminator

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.