package com.tesora.dve.sql.transform.strategy.join; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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/>. * #L% */ import java.io.PrintStream; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.tesora.dve.common.MultiMap; import com.tesora.dve.sql.expression.ColumnKey; import com.tesora.dve.sql.expression.ExpressionUtils; import com.tesora.dve.sql.expression.TableKey; import com.tesora.dve.sql.jg.CollapsedJoinGraph; import com.tesora.dve.sql.jg.DPart; import com.tesora.dve.sql.node.expression.ExpressionNode; import com.tesora.dve.sql.node.expression.FunctionCall; import com.tesora.dve.sql.transform.ColumnInstanceCollector; import com.tesora.dve.sql.transform.NullFunCollector; import com.tesora.dve.sql.util.Functional; import com.tesora.dve.sql.util.ListOfPairs; import com.tesora.dve.sql.util.ListSet; import com.tesora.dve.sql.util.Pair; public class RestrictionManager { private MultiMap<ColumnKey,ExpressionNode> restrictions; private MultiMap<ColumnKey,ColumnKey> restrictionForwarding; private ListSet<TableKey> nullTested; private boolean pruned; public RestrictionManager(CollapsedJoinGraph jg) { restrictions = new MultiMap<ColumnKey,ExpressionNode>(); restrictionForwarding = jg.getRestrictionPropagationMap(); nullTested = new ListSet<TableKey>(); pruned = false; } public void takeNullTested(TableKey tk) { nullTested.add(tk); } public void take(ExpressionNode expr) { List<ExpressionNode> decomp = ExpressionUtils.decomposeAndClause(expr); for(ExpressionNode en : decomp) { // if the node is an is null - we can't use it ListSet<FunctionCall> nullFuns = NullFunCollector.collectNullFuns(en); if (!nullFuns.isEmpty()) { // has null funs - examine them for(FunctionCall fc : nullFuns) { ListSet<ColumnKey> ncks = ColumnInstanceCollector.getColumnKeys(fc); for(ColumnKey ick : ncks) nullTested.add(ick.getTableKey()); } continue; } ListSet<ColumnKey> cks = ColumnInstanceCollector.getColumnKeys(en); if (cks.size() != 1) continue; restrictions.put(cks.get(0), en); } } public ListOfPairs<ColumnKey,ExpressionNode> findPropagatedRestrictions(DPart partition) { prune(); // first find all columns that are in play by looking at the forwarding. ListSet<ColumnKey> columnsForPartition = new ListSet<ColumnKey>(); ListSet<TableKey> tablesForPartition = partition.getTables(); for(ColumnKey ck : restrictionForwarding.keySet()) { if (tablesForPartition.contains(ck.getTableKey())) columnsForPartition.add(ck); } if (columnsForPartition.isEmpty()) return null; // this is a slice of the full multimap. the keys are in partition; the values are elsewhere. LinkedHashMap<ColumnKey,Set<ColumnKey>> slice = new LinkedHashMap<ColumnKey,Set<ColumnKey>>(); for(ColumnKey ck : columnsForPartition) { ListSet<ColumnKey> others = new ListSet<ColumnKey>(); others.addAll(restrictionForwarding.get(ck)); // now we have to go to those and all of theirs, unless they already exist or are in partition. boolean done = false; while(!done) { HashSet<ColumnKey> more = new HashSet<ColumnKey>(); for(ColumnKey ick : others) { Set<ColumnKey> partners = (Set<ColumnKey>) restrictionForwarding.get(ick); for(ColumnKey ock : partners) { if (tablesForPartition.contains(ock.getTableKey())) continue; more.add(ock); } } done = !others.addAll(more); } slice.put(ck, others); } if (slice.isEmpty()) return null; // finally for column key in the partition, find expressions that can be mapped to it. ListOfPairs<ColumnKey,ExpressionNode> out = new ListOfPairs<ColumnKey,ExpressionNode>(); for(Map.Entry<ColumnKey, Set<ColumnKey>> me : slice.entrySet()) { for(ColumnKey ock : me.getValue()) { Collection<ExpressionNode> exprs = restrictions.get(ock); if (exprs == null || exprs.isEmpty()) continue; for(ExpressionNode en : exprs) out.add(me.getKey(),en); } } return out; } public ListOfPairs<ColumnKey,ExpressionNode> findNonPropagatingRestrictions(DPart part) { prune(); ListOfPairs<ColumnKey,ExpressionNode> out = new ListOfPairs<ColumnKey,ExpressionNode>(); for(ColumnKey ck : restrictions.keySet()) { if (part.getTables().contains(ck.getTableKey())) { for(ExpressionNode en : restrictions.get(ck)) out.add(ck, en); } } return out; } public void describe(PrintStream ps) { ps.println("****** Restrictions **********"); ps.println("Column forwarding:"); HashSet<Pair<ColumnKey,ColumnKey>> processed = new HashSet<Pair<ColumnKey,ColumnKey>>(); for(ColumnKey lk : restrictionForwarding.keySet()) { Set<ColumnKey> rks = (Set<ColumnKey>) restrictionForwarding.get(lk); for(ColumnKey rk : rks) { ColumnKey l, r; if (lk.getTableKey().getNode() < rk.getTableKey().getNode()) { l = lk; r = rk; } else { l = rk; r = lk; } Pair<ColumnKey,ColumnKey> proc = new Pair<ColumnKey,ColumnKey>(l,r); if (processed.add(proc)) { ps.println(" " + l + " <==> " + r); } } } ps.println(); ps.println("Column restrictions: "); for(ColumnKey ck : restrictions.keySet()) { ps.println(" " + ck); for(ExpressionNode en : restrictions.get(ck)) { ps.println(" " + en); } } ps.println(); } private void prune() { if (pruned) return; pruned = true; // any table that is null tested - remove from the restrictions if (nullTested.isEmpty()) return; List<ColumnKey> cks = Functional.toList(restrictions.keySet()); for(ColumnKey ck : cks) { if (nullTested.contains(ck.getTableKey())) restrictions.remove(ck); } } }