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.util.ArrayList; import java.util.List; import com.tesora.dve.sql.expression.ExpressionUtils; import com.tesora.dve.sql.expression.TableKey; import com.tesora.dve.sql.jg.DPart; import com.tesora.dve.sql.jg.JoinEdge; import com.tesora.dve.sql.node.expression.ColumnInstance; import com.tesora.dve.sql.node.expression.ExpressionNode; import com.tesora.dve.sql.node.expression.FunctionCall; import com.tesora.dve.sql.node.test.EngineConstant; import com.tesora.dve.sql.schema.SchemaContext; import com.tesora.dve.sql.statement.dml.SelectStatement; import com.tesora.dve.sql.transform.ColumnInstanceCollector; import com.tesora.dve.sql.transform.FunCollector; import com.tesora.dve.sql.transform.NullFunCollector; import com.tesora.dve.sql.util.Functional; import com.tesora.dve.sql.util.ListSet; import com.tesora.dve.sql.util.UnaryPredicate; class OriginalWhereBuffer extends FinalBuffer { protected PartitionLookup partitionLookup; public OriginalWhereBuffer(Buffer prev, PartitionLookup pl) { super(BufferKind.WC, prev,pl); partitionLookup = pl; } @Override public void adapt(final SchemaContext sc, SelectStatement stmt) { P2ProjectionBuffer proj = (P2ProjectionBuffer) getBuffer(BufferKind.P2); // see if we have any outer joins ListSet<TableKey> allOuterJoinedTables = new ListSet<TableKey>(); ListSet<TableKey> outerJoinedTables = new ListSet<TableKey>(); for(JoinEdge je : partitionLookup.jg.getJoins()) { if (je.getJoin().isInnerJoin()) continue; allOuterJoinedTables.add(je.getLHSTab()); allOuterJoinedTables.add(je.getRHSTab()); if (je.getJoinType().isLeftOuterJoin()) outerJoinedTables.add(je.getRHSTab()); else if (je.getJoinType().isRightOuterJoin()) outerJoinedTables.add(je.getLHSTab()); else { outerJoinedTables.add(je.getLHSTab()); outerJoinedTables.add(je.getRHSTab()); } } List<ExpressionNode> clauses = ExpressionUtils.decomposeAndClause(stmt.getWhereClause()); // so, null handling: // there are two cases where we need to delay processing of the where clause to after // the join has been performed: // [1] is null, is not null // [2] where clauses utilizing columns in the outer joined table // in the first case, we're going to delay processing the predicate until after the tables are joined // in the second case we're going to process the predicate twice: once for the partition query and again // after the join is done. for(ExpressionNode en : clauses) { partitionLookup.getRestrictionManager().take(en); ListSet<ColumnInstance> columns = ColumnInstanceCollector.getColumnInstances(en); ListSet<TableKey> tabs = ColumnInstanceCollector.getTableKeys(columns); ListSet<TableKey> both = null; ListSet<FunctionCall> funs = FunCollector.collectFuns(en); if (!allOuterJoinedTables.isEmpty()) { List<FunctionCall> nullFuns = Functional.select(funs, NullFunCollector.isNullFun); if (!nullFuns.isEmpty()) { // we're going to add all the outer joined stuff to the dependencies, just to be safe // this means we'll delay doing the is null checks until the joins are planned tabs.addAll(allOuterJoinedTables); } else { ListSet<ColumnInstance> cols = ColumnInstanceCollector.getColumnInstances(en); ListSet<TableKey> tks = ColumnInstanceCollector.getTableKeys(cols); for(TableKey tk : tks) { if (outerJoinedTables.contains(tk)) { both = allOuterJoinedTables; break; } } } } List<FunctionCall> eqjs = Functional.select(funs, new UnaryPredicate<FunctionCall>() { @Override public boolean test(FunctionCall ln) { Boolean value = ln.getDerivedAttribute(EngineConstant.EQUIJOIN, sc); if (value == null) return false; return value.booleanValue(); } }); if (both != null) { ListSet<DPart> partitions = partitionLookup.getPartitionsFor(tabs); if (partitions.size() <= 1) addNonBridging(partitions,en); partitions = partitionLookup.getPartitionsFor(both); addBridgingDependency(partitions, en, columns, proj, eqjs, sc); } else { ListSet<DPart> partitions = partitionLookup.getPartitionsFor(tabs); if (partitions.size() > 1) { addBridgingDependency(partitions, en, columns, proj, eqjs, sc); } else { addNonBridging(partitions,en); } } } } private void addBridgingDependency(ListSet<DPart> partitions, ExpressionNode en, ListSet<ColumnInstance> columns, P2ProjectionBuffer proj, List<FunctionCall> eqjs, SchemaContext sc) { // figure out if we have any redist entries List<ExpressionNode> redistEntries = new ArrayList<ExpressionNode>(); for(FunctionCall fc : eqjs) { for(ExpressionNode p : fc.getParametersEdge()) { if (!(p instanceof ColumnInstance)) { redistEntries.add(p); } } } ListSet<ExpressionNode> bits = new ListSet<ExpressionNode>(); ListSet<ColumnInstance> splodeyCols = new ListSet<ColumnInstance>(); if (redistEntries.isEmpty()) { for(ColumnInstance ci : columns) bits.add(ci); splodeyCols = columns; } else { for(ColumnInstance ci : columns) { if (ci.ifAncestor(redistEntries) == null) { bits.add(ci); splodeyCols.add(ci); } } bits.addAll(redistEntries); } // bridging, we need to decompose // but we also need to add entries to the projection buffer, which is what was passed in BufferEntry be = new ExplodingBufferEntry(en, bits); for(ExpressionNode p : redistEntries) { BufferEntry nbe = proj.addForJoin(p, true); be.addDependency(nbe); nbe.registerCompoundRedist(p, (RedistBufferEntry) nbe); proj.partitionInfo.add(nbe,partitions); } for(ColumnInstance ci : splodeyCols) { BufferEntry de = proj.add(ci); be.addDependency(de); } add(be); partitionInfo.add(be, partitions); } private void addNonBridging(ListSet<DPart> partitions, ExpressionNode en) { BufferEntry be = new BufferEntry(en); add(be); partitionInfo.add(be, partitions); } }