package com.tesora.dve.sql.node.structural;
/*
* #%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.HashMap;
import java.util.Map;
import com.tesora.dve.sql.ParserException.Pass;
import com.tesora.dve.sql.SchemaException;
public final class JoinSpecification {
public enum OuterJoin {
LEFT("LEFT OUTER"),
RIGHT("RIGHT OUTER"),
FULL("FULL OUTER");
private final String sql;
private OuterJoin(String sql) {
this.sql = sql;
}
public String getSQL() {
return this.sql;
}
}
// if null, then this is an inner join
private OuterJoin outerJoinKind;
// true if this is a cartesian join
private boolean cross;
// true if this is a natural join
private boolean natural;
// default is inner
private JoinSpecification() {
outerJoinKind = null;
cross = false;
natural = false;
}
private JoinSpecification(boolean isCross, boolean isNatural) {
if (isCross && isNatural) {
throw new IllegalArgumentException("A join cannot be both cross and natural at the same time.");
}
outerJoinKind = null;
this.cross = isCross;
this.natural = isNatural;
}
private JoinSpecification(OuterJoin variety) {
outerJoinKind = variety;
this.cross = false;
this.natural = false;
}
private JoinSpecification(OuterJoin variety, boolean isNatural) {
outerJoinKind = variety;
this.cross = false;
this.natural = isNatural;
}
public boolean isInnerJoin() { return outerJoinKind == null; }
public boolean isCrossJoin() { return isInnerJoin() && this.cross; }
public boolean isNaturalJoin() { return this.natural; }
public OuterJoin getOuterJoinKind() { return outerJoinKind; }
public boolean isLeftOuterJoin() { return outerJoinKind == OuterJoin.LEFT; }
public boolean isRightOuterJoin() { return outerJoinKind == OuterJoin.RIGHT; }
public boolean isFullOuterJoin() { return outerJoinKind == OuterJoin.FULL; }
public boolean isOuterJoin() { return outerJoinKind != null; }
public static final JoinSpecification INNER_JOIN = new JoinSpecification();
// note: this is mysql specific!
public static final JoinSpecification CROSS_JOIN = INNER_JOIN;
public static final JoinSpecification LEFT_OUTER_JOIN = new JoinSpecification(OuterJoin.LEFT);
public static final JoinSpecification NATURAL_JOIN = new JoinSpecification(false, true);
public static final JoinSpecification NATURAL_LEFT_OUTER_JOIN = new JoinSpecification(OuterJoin.LEFT, true);
private static Map<String, JoinSpecification> supported = buildSupportedJoins();
private static Map<String, JoinSpecification> buildSupportedJoins() {
HashMap<String, JoinSpecification> ret = new HashMap<String, JoinSpecification>();
ret.put("INNER", INNER_JOIN);
ret.put("LEFT OUTER", LEFT_OUTER_JOIN);
ret.put("RIGHT OUTER", new JoinSpecification(OuterJoin.RIGHT));
ret.put("FULL OUTER", new JoinSpecification(OuterJoin.FULL));
ret.put("LEFT", LEFT_OUTER_JOIN);
ret.put("RIGHT", new JoinSpecification(OuterJoin.RIGHT));
ret.put("FULL", new JoinSpecification(OuterJoin.FULL));
ret.put("CROSS",INNER_JOIN);
ret.put("NATURAL", NATURAL_JOIN);
ret.put("NATURAL LEFT", NATURAL_LEFT_OUTER_JOIN);
ret.put("NATURAL LEFT OUTER", NATURAL_LEFT_OUTER_JOIN);
return ret;
}
public static JoinSpecification makeJoinSpecification(String joinWords) {
JoinSpecification js = supported.get(joinWords.trim());
if (js == null)
throw new SchemaException(Pass.SECOND, "No support for join type : " + joinWords);
return js;
}
public String getSQL() {
if (outerJoinKind == null) {
if (natural) {
return "NATURAL";
}
return "INNER";
}
final String outerJoinSql = outerJoinKind.getSQL();
return (this.natural) ? "NATURAL " + outerJoinSql : outerJoinSql;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (cross ? 1231 : 1237);
result = prime * result + (natural ? 1231 : 1237);
result = prime * result
+ ((outerJoinKind == null) ? 0 : outerJoinKind.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
JoinSpecification other = (JoinSpecification) obj;
if (cross != other.cross)
return false;
if (natural != other.natural)
return false;
if (outerJoinKind != other.outerJoinKind)
return false;
return true;
}
}