blob: 72fd93969c1c4a012b45339a5944093bbc691de3 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tajo.plan.util;
import org.apache.tajo.algebra.*;
import org.apache.tajo.catalog.CatalogConstants;
import org.apache.tajo.catalog.Column;
import org.apache.tajo.common.TajoDataTypes.*;
import org.apache.tajo.datum.TimeDatum;
import org.apache.tajo.exception.TajoException;
import org.apache.tajo.exception.UnsupportedException;
import org.apache.tajo.plan.ExprAnnotator;
import org.apache.tajo.plan.visitor.SimpleAlgebraVisitor;
import org.apache.tajo.util.Pair;
import org.apache.tajo.util.TUtil;
import org.apache.tajo.util.datetime.DateTimeUtil;
import org.apache.tajo.util.datetime.TimeMeta;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.List;
import java.util.Stack;
import java.util.TimeZone;
/**
* This build SQL statements for getting partitions informs on CatalogStore with algebra expressions.
* This visitor assumes that all columns of algebra expressions are reserved for one table.
*
*/
public class PartitionFilterAlgebraVisitor extends SimpleAlgebraVisitor<Object, Expr> {
private String tableAlias;
private Column column;
private boolean isHiveCatalog = false;
private Stack<String> queries = new Stack();
private List<Pair<Type, Object>> parameters = TUtil.newList();
public String getTableAlias() {
return tableAlias;
}
public void setTableAlias(String tableAlias) {
this.tableAlias = tableAlias;
}
public Column getColumn() {
return column;
}
public void setColumn(Column column) {
this.column = column;
}
public boolean isHiveCatalog() {
return isHiveCatalog;
}
public void setIsHiveCatalog(boolean isHiveCatalog) {
this.isHiveCatalog = isHiveCatalog;
}
public List<Pair<Type, Object>> getParameters() {
return parameters;
}
public void setParameters(List<Pair<Type, Object>> parameters) {
this.parameters = parameters;
}
public void clearParameters() {
this.parameters.clear();
}
public String getResult() {
return queries.pop();
}
@Override
public Expr visit(Object ctx, Stack<Expr> stack, Expr expr) throws TajoException {
if (expr.getType() == OpType.LikePredicate) {
return visitLikePredicate(ctx, stack, (PatternMatchPredicate) expr);
} else if (expr.getType() == OpType.SimilarToPredicate) {
return visitSimilarToPredicate(ctx, stack, (PatternMatchPredicate) expr);
} else if (expr.getType() == OpType.Regexp) {
return visitRegexpPredicate(ctx, stack, (PatternMatchPredicate) expr);
}
return super.visit(ctx, stack, expr);
}
@Override
public Expr visitDateLiteral(Object ctx, Stack<Expr> stack, DateLiteral expr) throws TajoException {
StringBuilder sb = new StringBuilder();
if (!isHiveCatalog) {
sb.append("?").append(" )");
parameters.add(new Pair(Type.DATE, Date.valueOf(expr.toString())));
} else {
sb.append("\"").append(expr.toString()).append("\"");
}
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitTimestampLiteral(Object ctx, Stack<Expr> stack, TimestampLiteral expr) throws TajoException {
StringBuilder sb = new StringBuilder();
if (!isHiveCatalog) {
DateValue dateValue = expr.getDate();
TimeValue timeValue = expr.getTime();
int [] dates = ExprAnnotator.dateToIntArray(dateValue.getYears(),
dateValue.getMonths(),
dateValue.getDays());
int [] times = ExprAnnotator.timeToIntArray(timeValue.getHours(),
timeValue.getMinutes(),
timeValue.getSeconds(),
timeValue.getSecondsFraction());
long julianTimestamp;
if (timeValue.hasSecondsFraction()) {
julianTimestamp = DateTimeUtil.toJulianTimestamp(dates[0], dates[1], dates[2], times[0], times[1], times[2],
times[3] * 1000);
} else {
julianTimestamp = DateTimeUtil.toJulianTimestamp(dates[0], dates[1], dates[2], times[0], times[1], times[2], 0);
}
TimeMeta tm = new TimeMeta();
DateTimeUtil.toJulianTimeMeta(julianTimestamp, tm);
TimeZone tz = TimeZone.getDefault();
DateTimeUtil.toUTCTimezone(tm, tz);
sb.append("?").append(" )");
Timestamp timestamp = new Timestamp(DateTimeUtil.julianTimeToJavaTime(DateTimeUtil.toJulianTimestamp(tm)));
parameters.add(new Pair(Type.TIMESTAMP, timestamp));
} else {
sb.append("\"").append(expr.toString()).append("\"");
}
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitTimeLiteral(Object ctx, Stack<Expr> stack, TimeLiteral expr) throws TajoException {
StringBuilder sb = new StringBuilder();
if (!isHiveCatalog) {
TimeValue timeValue = expr.getTime();
int [] times = ExprAnnotator.timeToIntArray(timeValue.getHours(),
timeValue.getMinutes(),
timeValue.getSeconds(),
timeValue.getSecondsFraction());
long time;
if (timeValue.hasSecondsFraction()) {
time = DateTimeUtil.toTime(times[0], times[1], times[2], times[3] * 1000);
} else {
time = DateTimeUtil.toTime(times[0], times[1], times[2], 0);
}
TimeDatum timeDatum = new TimeDatum(time);
TimeMeta tm = timeDatum.asTimeMeta();
TimeZone tz = TimeZone.getDefault();
DateTimeUtil.toUTCTimezone(tm, tz);
sb.append("?").append(" )");
parameters.add(new Pair(Type.TIME, new Time(DateTimeUtil.toJavaTime(tm.hours, tm.minutes, tm.secs, tm.fsecs))));
} else {
sb.append("\"").append(expr.toString()).append("\"");
}
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitLiteral(Object ctx, Stack<Expr> stack, LiteralValue expr) throws TajoException {
StringBuilder sb = new StringBuilder();
if (!isHiveCatalog) {
sb.append("?").append(" )");
switch (expr.getValueType()) {
case Boolean:
parameters.add(new Pair(Type.BOOLEAN, Boolean.valueOf(expr.getValue())));
break;
case Unsigned_Float:
parameters.add(new Pair(Type.FLOAT8, Double.valueOf(expr.getValue())));
break;
case String:
parameters.add(new Pair(Type.TEXT, expr.getValue()));
break;
default:
parameters.add(new Pair(Type.INT8, Long.valueOf(expr.getValue())));
break;
}
} else {
switch (expr.getValueType()) {
case String:
sb.append("\"").append(expr.getValue()).append("\"");
break;
default:
sb.append(expr.getValue());
break;
}
}
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitValueListExpr(Object ctx, Stack<Expr> stack, ValueListExpr expr) throws TajoException {
StringBuilder sb = new StringBuilder();
if (!isHiveCatalog) {
sb.append("(");
for(int i = 0; i < expr.getValues().length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append("?");
stack.push(expr.getValues()[i]);
visit(ctx, stack, expr.getValues()[i]);
stack.pop();
}
sb.append(")");
sb.append(" )");
queries.push(sb.toString());
} else {
throw new UnsupportedException("IN Operator");
}
return expr;
}
@Override
public Expr visitColumnReference(Object ctx, Stack<Expr> stack, ColumnReferenceExpr expr) throws TajoException {
StringBuilder sb = new StringBuilder();
if (!isHiveCatalog) {
sb.append("( ").append(tableAlias).append(".").append(CatalogConstants.COL_COLUMN_NAME)
.append(" = '").append(expr.getName()).append("'")
.append(" AND ").append(tableAlias).append(".").append(CatalogConstants.COL_PARTITION_VALUE);
} else {
sb.append(expr.getName());
}
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitUnaryOperator(Object ctx, Stack<Expr> stack, UnaryOperator expr) throws TajoException {
stack.push(expr);
Expr child = visit(ctx, stack, expr.getChild());
stack.pop();
if (child.getType() == OpType.Literal) {
return new NullLiteral();
}
String childSql = queries.pop();
StringBuilder sb = new StringBuilder();
if (expr.getType() == OpType.IsNullPredicate) {
IsNullPredicate isNullPredicate = (IsNullPredicate) expr;
sb.append(childSql);
sb.append(" IS ");
if (isNullPredicate.isNot()) {
sb.append("NOT NULL");
} else {
sb.append("NULL");
}
}
if (!isHiveCatalog) {
sb.append(" )");
}
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitBetween(Object ctx, Stack<Expr> stack, BetweenPredicate expr) throws TajoException {
stack.push(expr);
visit(ctx, stack, expr.predicand());
String predicandSql = queries.pop();
visit(ctx, stack, expr.begin());
String beginSql= queries.pop();
if (!isHiveCatalog && beginSql.endsWith(")")) {
beginSql = beginSql.substring(0, beginSql.length()-1);
}
visit(ctx, stack, expr.end());
String endSql = queries.pop();
if (!isHiveCatalog && endSql.endsWith(")")) {
endSql = beginSql.substring(0, endSql.length()-1);
}
StringBuilder sb = new StringBuilder();
sb.append(predicandSql);
sb.append(" BETWEEN ");
sb.append(beginSql);
sb.append(" AND ");
sb.append(endSql);
if (!isHiveCatalog) {
sb.append(")");
}
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitCaseWhen(Object ctx, Stack<Expr> stack, CaseWhenPredicate expr) throws TajoException {
stack.push(expr);
StringBuilder sb = new StringBuilder();
sb.append("CASE ");
String condition, result;
for (CaseWhenPredicate.WhenExpr when : expr.getWhens()) {
visit(ctx, stack, when.getCondition());
condition = queries.pop();
visit(ctx, stack, when.getResult());
result = queries.pop();
String whenSql = condition + " " + result;
if (!isHiveCatalog && whenSql.endsWith(")")) {
whenSql = whenSql.substring(0, whenSql.length()-1);
}
sb.append(whenSql).append(" ");
}
if (expr.hasElseResult()) {
visit(ctx, stack, expr.getElseResult());
String elseSql = queries.pop();
if (!isHiveCatalog && elseSql.endsWith(")")) {
elseSql = elseSql.substring(0, elseSql.length()-1);
}
sb.append("ELSE ").append(elseSql).append(" END");
}
if (!isHiveCatalog) {
sb.append(")");
}
queries.push(sb.toString());
stack.pop();
return expr;
}
@Override
public Expr visitBinaryOperator(Object ctx, Stack<Expr> stack, BinaryOperator expr) throws TajoException {
stack.push(expr);
Expr lhs = visit(ctx, stack, expr.getLeft());
String leftSql = queries.pop();
Expr rhs = visit(ctx, stack, expr.getRight());
String rightSql = queries.pop();
stack.pop();
if (!expr.getLeft().equals(lhs)) {
expr.setLeft(lhs);
}
if (!expr.getRight().equals(rhs)) {
expr.setRight(rhs);
}
if (lhs.getType() == OpType.Literal && rhs.getType() == OpType.Literal) {
return new NullLiteral();
}
StringBuilder sb = new StringBuilder();
sb.append(leftSql);
sb.append(" ").append(getOperator(expr.getType())).append(" ");
sb.append(rightSql);
queries.push(sb.toString());
return expr;
}
private String getOperator(OpType type) {
String operator;
switch (type) {
case Not:
operator = "!";
break;
case And:
operator = "AND";
break;
case Or:
operator = "OR";
break;
case Equals:
operator = "=";
break;
case IsNullPredicate:
operator = "IS NULL";
break;
case NotEquals:
operator = "<>";
break;
case LessThan:
operator = "<";
break;
case LessThanOrEquals:
operator = "<=";
break;
case GreaterThan:
operator = ">";
break;
case GreaterThanOrEquals:
operator = ">=";
break;
case Plus:
operator = "+";
break;
case Minus:
operator = "-";
break;
case Modular:
operator = "%";
break;
case Multiply:
operator = "*";
break;
case Divide:
operator = "/";
break;
case LikePredicate:
operator = "LIKE";
break;
case SimilarToPredicate:
operator = "([.])";
break;
case InPredicate:
operator = "IN";
break;
case Asterisk:
operator = "*";
break;
//TODO: need to check more types.
default:
operator = type.name();
break;
}
return operator;
}
@Override
public Expr visitLikePredicate(Object ctx, Stack<Expr> stack, PatternMatchPredicate expr) throws TajoException {
stack.push(expr);
visit(ctx, stack, expr.getPredicand());
String predicand = queries.pop();
visit(ctx, stack, expr.getPattern());
String pattern = queries.pop();
stack.pop();
if(isHiveCatalog) {
if (pattern.startsWith("%") || pattern.endsWith("%")) {
throw new UnsupportedException("LIKE Operator with '%'");
}
}
StringBuilder sb = new StringBuilder();
sb.append(predicand);
if (expr.isNot()) {
sb.append(" NOT ");
}
if (expr.isCaseInsensitive()) {
sb.append(" ILIKE ");
} else {
sb.append(" LIKE ");
}
sb.append(pattern);
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitSimilarToPredicate(Object ctx, Stack<Expr> stack, PatternMatchPredicate expr) throws TajoException {
if (isHiveCatalog) {
throw new UnsupportedException("SIMILAR TO Operator");
}
stack.push(expr);
visit(ctx, stack, expr.getPredicand());
String predicand = queries.pop();
visit(ctx, stack, expr.getPattern());
String pattern = queries.pop();
stack.pop();
StringBuilder sb = new StringBuilder();
sb.append(predicand);
if (expr.isNot()) {
sb.append(" NOT ");
}
sb.append(" SIMILAR TO ");
sb.append(pattern);
queries.push(sb.toString());
return expr;
}
@Override
public Expr visitRegexpPredicate(Object ctx, Stack<Expr> stack, PatternMatchPredicate expr) throws TajoException {
if (isHiveCatalog) {
throw new UnsupportedException("REGEXP Operator");
}
stack.push(expr);
visit(ctx, stack, expr.getPredicand());
String predicand = queries.pop();
visit(ctx, stack, expr.getPattern());
String pattern = queries.pop();
stack.pop();
StringBuilder sb = new StringBuilder();
sb.append(predicand);
if (expr.isNot()) {
sb.append(" NOT ");
}
sb.append(" REGEXP ");
sb.append(pattern);
queries.push(sb.toString());
return expr;
}
}