package com.tesora.dve.sql.transform.strategy.joinsimplification;
/*
* #%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.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.sql.expression.TableKey;
import com.tesora.dve.sql.node.Edge;
import com.tesora.dve.sql.node.LanguageNode;
import com.tesora.dve.sql.node.Traversal;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.node.structural.JoinSpecification;
import com.tesora.dve.sql.node.structural.JoinedTable;
import com.tesora.dve.sql.node.test.EngineConstant;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.statement.dml.DMLStatement;
public class NullRejectionSimplifier extends Simplifier {
@Override
public boolean applies(SchemaContext sc, DMLStatement stmt) throws PEException {
if (EngineConstant.FROMCLAUSE.has(stmt)) {
AnyOuterJoinTraversal any = new AnyOuterJoinTraversal();
any.traverse(EngineConstant.FROMCLAUSE.getEdge(stmt));
if (any.hasLeftOuterJoins()) return true;
}
return false;
}
@Override
public DMLStatement simplify(SchemaContext sc, DMLStatement in, JoinSimplificationTransformFactory parent) throws PEException {
String before = (parent.emitting() ? in.getSQL(sc) : null);
if (process(sc, in)) {
if (parent.emitting()) {
parent.emit(" NR in: " + before);
parent.emit(" NR out: " + in.getSQL(sc));
}
return in;
}
return null;
}
public static boolean process(SchemaContext sc, DMLStatement stmt) {
boolean mods = false;
boolean done = false;
NRBuilder wcbuilder = null;
do {
// build the first otab set
JoinsTraversal ott = new JoinsTraversal();
ott.traverse(EngineConstant.FROMCLAUSE.getEdge(stmt));
Map<TableKey,JoinedTable> otabs = ott.getOuterTables();
List<TableKey> oj = new ArrayList<TableKey>();
for(Map.Entry<TableKey,JoinedTable> me : otabs.entrySet()) {
if (me.getValue().getJoinType().isLeftOuterJoin())
oj.add(me.getKey());
}
if (oj.isEmpty())
return mods;
if (wcbuilder == null) {
wcbuilder = new NRBuilder(sc,true,(ExpressionNode)EngineConstant.WHERECLAUSE.get(stmt));
if (wcbuilder.isUnsupported())
return false;
}
List<TableKey> any = wcbuilder.required(oj);
if (any.isEmpty()) {
// next up, join conditions
List<ExpressionNode> roots = new ArrayList<ExpressionNode>();
for(Map.Entry<TableKey, JoinedTable> me : otabs.entrySet()) {
if (me.getValue().getJoinType().isInnerJoin()) {
if (me.getValue().getJoinOn() != null)
roots.add(me.getValue().getJoinOn());
}
}
if (!roots.isEmpty()) {
NRBuilder jb = new NRBuilder(sc,false,roots);
if (!jb.isUnsupported())
any = jb.required(oj);
}
}
if (!any.isEmpty()) {
for(TableKey tk : any) {
JoinedTable enclosing = otabs.get(tk);
if (enclosing.getJoinType().isLeftOuterJoin())
enclosing.setJoinType(JoinSpecification.INNER_JOIN);
}
mods = true;
}
else
done = true;
} while(!done);
return mods;
}
private static class JoinsTraversal extends Traversal {
LinkedHashMap<TableKey,JoinedTable> tables = new LinkedHashMap<TableKey,JoinedTable>();
public JoinsTraversal() {
super(Order.PREORDER, ExecStyle.ONCE);
}
public Map<TableKey,JoinedTable> getOuterTables() {
return tables;
}
@Override
public LanguageNode action(LanguageNode in) {
if (in instanceof JoinedTable) {
JoinedTable jt = (JoinedTable) in;
if (jt.getJoinedToTable() != null)
tables.put(jt.getJoinedToTable().getTableKey(), jt);
}
return in;
}
}
private static class AnyOuterJoinTraversal extends Traversal {
private boolean any = false;
public AnyOuterJoinTraversal() {
super(Order.PREORDER,ExecStyle.ONCE);
}
@Override
public boolean allow(Edge<?,?> e) {
return !any;
}
@Override
public boolean allow(LanguageNode ln) {
return !any;
}
public boolean hasLeftOuterJoins() {
return any;
}
@Override
public LanguageNode action(LanguageNode in) {
if (in instanceof JoinedTable) {
JoinedTable jt = (JoinedTable) in;
if (jt.getJoinType().isLeftOuterJoin()) {
any = true;
}
}
return in;
}
}
}