Package com.foundationdb.sql.optimizer.rule

Source Code of com.foundationdb.sql.optimizer.rule.ColumnEquivalenceFinder$ColumnEquivalenceVisitor

/**
* 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.rule;

import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.ForeignKey;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.ais.model.Table;
import com.foundationdb.ais.model.TableIndex;
import com.foundationdb.server.types.texpressions.Comparison;
import com.foundationdb.sql.optimizer.plan.BaseQuery;
import com.foundationdb.sql.optimizer.plan.ColumnExpression;
import com.foundationdb.sql.optimizer.plan.ComparisonCondition;
import com.foundationdb.sql.optimizer.plan.ConditionExpression;
import com.foundationdb.sql.optimizer.plan.ExpressionNode;
import com.foundationdb.sql.optimizer.plan.ExpressionVisitor;
import com.foundationdb.sql.optimizer.plan.JoinNode;
import com.foundationdb.sql.optimizer.plan.Joinable;
import com.foundationdb.sql.optimizer.plan.PlanNode;
import com.foundationdb.sql.optimizer.plan.PlanVisitor;
import com.foundationdb.sql.optimizer.plan.Select;
import com.foundationdb.sql.optimizer.plan.TableNode;
import com.foundationdb.sql.optimizer.plan.TableSource;
import com.foundationdb.sql.optimizer.plan.TableTree;
import com.foundationdb.sql.optimizer.rule.JoinAndIndexPicker.JoinEnumerator;
import com.foundationdb.sql.optimizer.rule.JoinAndIndexPicker.JoinsFinder;
import com.foundationdb.sql.optimizer.rule.JoinAndIndexPicker.Picker;
import com.foundationdb.sql.types.DataTypeDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

public final class ColumnEquivalenceFinder extends BaseRule {
    private static final Logger logger = LoggerFactory.getLogger(ColumnEquivalenceFinder.class);
   
    @Override
    protected Logger getLogger() {
        return logger;
    }
   
    private static class ColumnEquivalenceVisitor implements PlanVisitor, ExpressionVisitor {

        private ColumnEquivalenceStack equivs = new ColumnEquivalenceStack();

        @Override
        public boolean visitEnter(PlanNode n) {
            equivs.enterNode(n);
            return visit(n);
        }

        @Override
        public boolean visitLeave(PlanNode n) {
            equivs.leaveNode(n);
            return true;
        }

        @Override
        public boolean visit(PlanNode n) {
            if (n instanceof JoinNode) {
                JoinNode joinNode = (JoinNode)n;
                if (joinNode.isInnerJoin())
                    equivalenceConditions(joinNode.getJoinConditions());
            }
            else if (n instanceof Select) {
                Select select = (Select) n;
                equivalenceConditions(select.getConditions());
            }
            return true;
        }

        private void equivalenceConditions(List<ConditionExpression> conditions) {
            if (conditions != null) {
                for (ConditionExpression condition : conditions)
                    equivalenceCondition(condition);
            }
        }

        @Override
        public boolean visitEnter(ExpressionNode n) {
            return visit(n);
        }

        @Override
        public boolean visitLeave(ExpressionNode n) {
            return true;
        }

        @Override
        public boolean visit(ExpressionNode n) {
            return true;
        }

        private void equivalenceCondition(ConditionExpression condition) {
            if (condition instanceof ComparisonCondition) {
                ComparisonCondition comparison = (ComparisonCondition) condition;
                if (comparison.getOperation().equals(Comparison.EQ)
                        && (comparison.getLeft() instanceof ColumnExpression)
                        && (comparison.getRight() instanceof ColumnExpression)
                        ) {
                    ColumnExpression left = (ColumnExpression) comparison.getLeft();
                    ColumnExpression right = (ColumnExpression) comparison.getRight();
                    if (!left.equals(right)) {
                        markNotNull(left);
                        markNotNull(right);
                        equivs.get().markEquivalent(left, right); // also implies right.equivalentTo(left)
                    }
                }
            }
        }
    }

    private static void markNotNull(ColumnExpression columnExpression) {
        DataTypeDescriptor sqLtype = columnExpression.getSQLtype();
        if (sqLtype.isNullable()) {
            DataTypeDescriptor notNullable = sqLtype.getNullabilityType(false);
            columnExpression.setSQLtype(notNullable);
        }
    }

    @Override
    public void apply(PlanContext plan) {
        // Do basic column equivalence finding
        plan.getPlan().accept(new ColumnEquivalenceVisitor());
       
        // Do FK table equivalence finding
        // Loop through all the tables, and find any possible FK Parents
        // Add these to the possible Equivalences
        BaseQuery basePlan = (BaseQuery)(plan.getPlan());
        List<Picker> pickers = new JoinsFinder(plan).find();
        for (Picker picker : pickers) {
            addFKEquivsFromJoins (picker.rootJoin(), picker.query.getFKEquivalencies());
            picker.query.getFKEquivalencies().copyEquivalences(picker.query.getColumnEquivalencies());
        }
    }

    private void addFKEquivsFromJoins(Joinable joins, EquivalenceFinder<ColumnExpression> equivalencies) {
        List<Joinable> tables = new ArrayList<>();
        JoinEnumerator.addTables(joins, tables);
        Map<Table, TableSource> sources = new HashMap<>();

        for (Joinable table: tables) {
            if (table instanceof TableSource) {
                TableSource tableSource = (TableSource)table;
                sources.put(tableSource.getTable().getTable(), tableSource);
            }
        }
       
        for (Joinable table: tables) {
            if (table instanceof TableSource) {
                TableSource tableSource = (TableSource) table;
                checkFKParents (tableSource.getTable().getTable(), tableSource, sources, equivalencies);
            }
        }
    }
   
    private void checkFKParents(Table child, TableSource tableSource,  Map<Table, TableSource> sources, EquivalenceFinder<ColumnExpression> equivelances) {
        for (ForeignKey key : child.getReferencingForeignKeys()) {
            if (checkParentFKIsPK (key, child)) {
                TableSource parentSource = sources.get(key.getReferencedTable());
                if (parentSource == null) {
                    parentSource = generateTableSource(key.getReferencedTable());
                    sources.put(key.getReferencedTable(), parentSource);
                }
                checkFKParents (key.getReferencedTable(), parentSource, sources, equivelances);
                for (int i = 0; i < key.getReferencedColumns().size(); i++) {
                    ColumnExpression one = expressionFromColumn(key.getReferencingColumns().get(i), tableSource);
                    ColumnExpression two = expressionFromColumn(key.getReferencedColumns().get(i), parentSource);
                    equivelances.markEquivalent(one, two);
                }
            }
        }
    }
   
    private boolean checkParentFKIsPK (ForeignKey key, Table child) {
        if (key.getReferencingTable().equals(child) && // TODO: This is temporary redundant.
            key.getReferencedIndex().isPrimaryKey()) {

            // Check the child table FK columns are the child table PK as well.
            // TODO: We may be able to relax this with further testing.
            List<Column> fkColumns = key.getReferencingColumns();
            TableIndex childPKIndex = key.getReferencingTable().getIndex(Index.PRIMARY);
            // this can occur if the child table has no declared primary key.
            if (childPKIndex == null) { return false; }
            List<IndexColumn> pkColumns = childPKIndex.getKeyColumns();
            if (fkColumns.size() != pkColumns.size()) return false;
            for (int i = 0; i < fkColumns.size(); i++) {
                if (!fkColumns.get(i).equals(pkColumns.get(i).getColumn())) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
   
    private TableSource generateTableSource (Table table) {
        return new TableSource (new TableNode (table, new TableTree()), true, table.getName().toString());
    }
    private ColumnExpression expressionFromColumn(Column col, TableSource source) {
       
        if (source == null) {
            Table table = col.getTable();
            TableNode node = new TableNode(table, new TableTree());
            source = new TableSource(node, true, table.getName().toString());
        }
        return new ColumnExpression(source, col);
    }
   
}
TOP

Related Classes of com.foundationdb.sql.optimizer.rule.ColumnEquivalenceFinder$ColumnEquivalenceVisitor

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.