/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.hive;
import static org.teiid.language.SQLConstants.Reserved.*;
import java.util.List;
import org.teiid.core.util.StringUtil;
import org.teiid.language.*;
import org.teiid.language.Join.JoinType;
import org.teiid.language.SQLConstants.Tokens;
import org.teiid.translator.TypeFacility;
import org.teiid.translator.jdbc.SQLConversionVisitor;
public class HiveSQLConversionVisitor extends SQLConversionVisitor {
BaseHiveExecutionFactory baseHiveExecutionFactory;
public HiveSQLConversionVisitor(BaseHiveExecutionFactory hef) {
super(hef);
this.baseHiveExecutionFactory = hef;
}
@Override
public void visit(Join obj) {
TableReference leftItem = obj.getLeftItem();
TableReference rightItem = obj.getRightItem();
JoinType joinType = obj.getJoinType();
//impala only supports a left linear join
if (baseHiveExecutionFactory.requiresLeftLinearJoin() && rightItem instanceof Join) {
if (leftItem instanceof Join) {
//TODO: this may need to be handled in the engine to inhibit pushdown
throw new AssertionError("A left linear join structure is required: " + obj); //$NON-NLS-1$
}
//swap
TableReference tr = leftItem;
leftItem = rightItem;
rightItem = tr;
if (joinType == JoinType.RIGHT_OUTER_JOIN) {
joinType = JoinType.LEFT_OUTER_JOIN;
} else if (joinType == JoinType.LEFT_OUTER_JOIN) {
joinType = JoinType.RIGHT_OUTER_JOIN;
}
}
if(useParensForJoins() && leftItem instanceof Join) {
buffer.append(Tokens.LPAREN);
append(leftItem);
buffer.append(Tokens.RPAREN);
} else {
append(leftItem);
}
buffer.append(Tokens.SPACE);
switch(joinType) {
case CROSS_JOIN:
// Hive just works with "JOIN" keyword no inner or cross
// fixed in - https://issues.apache.org/jira/browse/HIVE-2549
buffer.append(CROSS);
break;
case FULL_OUTER_JOIN:
buffer.append(FULL)
.append(Tokens.SPACE)
.append(OUTER);
break;
case INNER_JOIN:
// Hive just works with "JOIN" keyword no inner or cross
//buffer.append(INNER);
break;
case LEFT_OUTER_JOIN:
buffer.append(LEFT)
.append(Tokens.SPACE)
.append(OUTER);
break;
case RIGHT_OUTER_JOIN:
buffer.append(RIGHT)
.append(Tokens.SPACE)
.append(OUTER);
break;
default: buffer.append(UNDEFINED);
}
buffer.append(Tokens.SPACE)
.append(JOIN)
.append(Tokens.SPACE);
if(rightItem instanceof Join && (useParensForJoins() || obj.getJoinType() == Join.JoinType.CROSS_JOIN)) {
buffer.append(Tokens.LPAREN);
append(rightItem);
buffer.append(Tokens.RPAREN);
} else {
append(rightItem);
}
final Condition condition = obj.getCondition();
if (condition != null) {
buffer.append(Tokens.SPACE)
.append(ON)
.append(Tokens.SPACE);
append(condition);
}
}
public void addColumns(List<DerivedColumn> items) {
if (items != null && items.size() != 0) {
addColumn(items.get(0));
for (int i = 1; i < items.size(); i++) {
buffer.append(Tokens.COMMA)
.append(Tokens.SPACE);
addColumn(items.get(i));
}
}
}
private void addColumn(DerivedColumn dc) {
if (dc.getAlias() != null) {
buffer.append(dc.getAlias());
}
else {
Expression expr = dc.getExpression();
if (expr instanceof ColumnReference) {
buffer.append(((ColumnReference)expr).getName());
}
else {
append(expr);
}
}
}
@Override
public void visit(SetQuery obj) {
//TODO: with hive 1.2, this handling is not necessary
//even with hive 0.13 it's only partially necessary - for distinct
if (obj.getWith() != null) {
append(obj.getWith());
}
Select select = obj.getProjectedQuery();
startInlineView(select, !obj.isAll());
appendSetChild(obj, obj.getLeftQuery(), false);
appendSetOp(obj);
appendSetChild(obj, obj.getRightQuery(), true);
endInlineView(obj);
}
private void endInlineView(QueryExpression obj) {
buffer.append(Tokens.RPAREN);
buffer.append(Tokens.SPACE);
buffer.append("X__"); //$NON-NLS-1$
OrderBy orderBy = obj.getOrderBy();
if(orderBy != null) {
buffer.append(Tokens.SPACE);
append(orderBy);
}
Limit limit = obj.getLimit();
if(limit != null) {
buffer.append(Tokens.SPACE);
append(limit);
}
}
private void startInlineView(Select select, boolean distinct) {
buffer.append(SELECT).append(Tokens.SPACE);
if(distinct) {
buffer.append(DISTINCT).append(Tokens.SPACE);
}
addColumns(select.getDerivedColumns());
buffer.append(Tokens.SPACE);
buffer.append(FROM).append(Tokens.SPACE);
buffer.append(Tokens.LPAREN);
}
private void appendSetOp(SetQuery obj) {
buffer.append(Tokens.SPACE);
appendSetOperation(obj.getOperation());
// UNION "ALL" always
buffer.append(Tokens.SPACE);
buffer.append(ALL);
buffer.append(Tokens.SPACE);
}
private void appendSetChild(SetQuery obj, QueryExpression child, boolean right) {
if (child instanceof Select || shouldNestSetChild(obj, child, right)) {
appendSetQuery(obj, child, right);
} else {
//non-nested set op
SetQuery setQuery = (SetQuery)child;
append(setQuery.getLeftQuery());
appendSetOp(setQuery);
appendSetChild(setQuery, setQuery.getRightQuery(), true);
}
}
@Override
public void visit(Select obj) {
if (obj.getOrderBy() != null) {
//hive does not like order by using the full column references
//this should be fine even with joins as the engine should alias the select columns
for (SortSpecification spec : obj.getOrderBy().getSortSpecifications()) {
if (spec.getExpression() instanceof ColumnReference) {
ColumnReference cr = (ColumnReference)spec.getExpression();
cr.setTable(null);
}
}
}
if (obj.isDistinct() && obj.getGroupBy() != null) {
if (obj.getWith() != null) {
append(obj.getWith());
}
if (obj.getOrderBy() == null) {
boolean needsAliasing = false;
List<DerivedColumn> derivedColumns = obj.getDerivedColumns();
for (int i = 0; i < derivedColumns.size(); i++) {
DerivedColumn dc = derivedColumns.get(i);
if (dc.getAlias() == null) {
needsAliasing = true;
break;
}
}
if (needsAliasing) {
for (int i = 0; i < derivedColumns.size(); i++) {
DerivedColumn dc = derivedColumns.get(i);
dc.setAlias("c_" + i); //$NON-NLS-1$
}
}
}
startInlineView(obj, obj.isDistinct());
//remove the distinct from the inline view
obj.setDistinct(false);
super.visit(obj);
endInlineView(obj);
return;
}
super.visit(obj);
}
@Override
protected void translateSQLType(Class<?> type, Object obj,
StringBuilder valuesbuffer) {
if (obj != null && type == TypeFacility.RUNTIME_TYPES.STRING) {
String val = obj.toString();
valuesbuffer.append(Tokens.QUOTE)
.append(StringUtil.replaceAll(StringUtil.replaceAll(val, "\\", "\\\\"), "'", "\\'")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
.append(Tokens.QUOTE);
} else {
super.translateSQLType(type, obj, valuesbuffer);
}
}
}