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.List;
import com.tesora.dve.sql.jg.CollapsedJoinGraph;
import com.tesora.dve.sql.jg.DGJoin;
import com.tesora.dve.sql.jg.JoinPosition;
import com.tesora.dve.sql.jg.UncollapsedJoinGraph;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.util.ListSet;
import com.tesora.dve.sql.util.ResizableArray;
// we may have some ordering issues - i.e. within a branch we may be required to
// execute either forwards or backwards. in the absence of dependency issues, we
// execute in whatever order is suitable. if there are dependency issues, we must
// execute branches left to right, and left to right within branches.
class JoinOrdering {
private CollapsedJoinGraph graph;
private ResizableArray<BranchOrder> branchOrders = new ResizableArray<BranchOrder>();
private JoinExecutionOrder overall = null;
public JoinOrdering(CollapsedJoinGraph jg) {
graph = jg;
List<DGJoin> allJoins = graph.getNaturalOrder();
for(Integer i : graph.getBranches().values())
branchOrders.set(i, new BranchOrder());
for(DGJoin dgj : allJoins) {
JoinPosition jp = dgj.getPosition();
if (jp.isInformal()) continue;
BranchOrder bo = branchOrders.get(jp.getBranch());
bo.addJoin(dgj);
if (jp.isFixed() || dgj.isMultijoin()) {
bo.setOrder(JoinExecutionOrder.LEFT);
// this is possibly overly conservative
overall = JoinExecutionOrder.LEFT;
}
}
}
public boolean canschedule(SchemaContext sc, JoinEntry je) {
DGJoin dgj = je.getJoin();
ListSet<DGJoin> clustered = je.getClusteredJoins();
// only true for cartesian joins
if (dgj == null) return true;
JoinPosition jp = dgj.getPosition();
if (jp.isInformal()) return true;
BranchOrder bo = branchOrders.get(jp.getBranch());
if (dgj.isInnerJoin() && !dgj.isMultijoin()) {
// I think inner joins, other than multijoins, can always be executed
return bo.matches(dgj, clustered, null);
}
if (overall == JoinExecutionOrder.LEFT) {
// we can only execute this join if it is the next in the natural order - and the natural order is the
// first branch order with anything in it
BranchOrder fbo = null;
for(int i = 0; i < branchOrders.size(); i++) {
BranchOrder ibo = branchOrders.get(i);
if (ibo == null) continue;
if (ibo.getNaturalOrder().isEmpty()) continue;
fbo = ibo;
break;
}
if (fbo != bo) return false;
return bo.matches(dgj, clustered, JoinExecutionOrder.LEFT);
} else if (bo.getOrder() == null) {
if (bo.hasOuterJoins()) {
// dgj must be either the first or the last - that will determine the order
if (bo.matches(dgj, clustered, JoinExecutionOrder.LEFT)) {
bo.setOrder(JoinExecutionOrder.LEFT);
return true;
} else if (bo.matches(dgj, clustered, JoinExecutionOrder.RIGHT)) {
bo.setOrder(JoinExecutionOrder.RIGHT);
return true;
}
} else {
// has no outer joins, order doesn't matter at all - so go ahead and schedule it
return bo.matches(dgj, clustered, null);
}
} else if (bo.matches(dgj, clustered, bo.getOrder())) {
return true;
}
return false;
}
public void describe(SchemaContext sc, PrintStream out) {
out.println("Uncollapsed join graph:");
UncollapsedJoinGraph ujg = new UncollapsedJoinGraph(sc, graph.getStatement());
out.println(ujg.describe(sc));
out.println("Collapsed join graph:");
out.println(graph.describe(sc));
out.println("Branch orders:");
for(int i = 0; i < branchOrders.size(); i++) {
out.println(" [" + i + "]");
for(DGJoin dgj : branchOrders.get(i).getNaturalOrder()) {
StringBuilder buf = new StringBuilder();
dgj.describe(sc,buf);
buf.append(" ").append("{").append(dgj.getLeftTables()).append("} <=> {").append(dgj.getRightTable()).append("}");
out.println(" " + buf.toString());
}
}
}
private static class BranchOrder {
private JoinExecutionOrder order;
private boolean hasOJs;
private ListSet<DGJoin> naturalOrderOnBranch;
public BranchOrder() {
order = null;
naturalOrderOnBranch = new ListSet<DGJoin>();
}
public void addJoin(DGJoin dgj) {
naturalOrderOnBranch.add(dgj);
if (!dgj.isInnerJoin())
hasOJs = true;
}
public boolean hasOuterJoins() {
return hasOJs;
}
public JoinExecutionOrder getOrder() {
return order;
}
public void setOrder(JoinExecutionOrder jeo) {
order = jeo;
}
public ListSet<DGJoin> getNaturalOrder() {
return naturalOrderOnBranch;
}
public boolean matches(DGJoin dgj, ListSet<DGJoin> clustered, JoinExecutionOrder jeo) {
if (jeo == JoinExecutionOrder.LEFT) {
if (matches(0,dgj,clustered))
return true;
} else if (jeo == JoinExecutionOrder.RIGHT) {
if (matches(naturalOrderOnBranch.size() - 1,dgj,clustered))
return true;
} else if (jeo == null) {
return naturalOrderOnBranch.remove(dgj);
}
return false;
}
private boolean matches(int branchIndex, DGJoin given, ListSet<DGJoin> clustered) {
DGJoin onBranch = naturalOrderOnBranch.get(branchIndex);
if (onBranch == given) {
naturalOrderOnBranch.remove(branchIndex);
return true;
} else if (clustered != null && clustered.contains(onBranch)) {
naturalOrderOnBranch.removeAll(clustered);
return true;
}
return false;
}
}
}