blob: 4133f174a9d06908e7e75f523cc93c5ac5c304d3 [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.drill.common.expression;
import java.math.BigDecimal;
import java.util.List;
import org.apache.drill.common.expression.IfExpression.IfCondition;
import org.apache.drill.common.expression.ValueExpressions.BooleanExpression;
import org.apache.drill.common.expression.ValueExpressions.DateExpression;
import org.apache.drill.common.expression.ValueExpressions.Decimal18Expression;
import org.apache.drill.common.expression.ValueExpressions.Decimal28Expression;
import org.apache.drill.common.expression.ValueExpressions.Decimal38Expression;
import org.apache.drill.common.expression.ValueExpressions.Decimal9Expression;
import org.apache.drill.common.expression.ValueExpressions.DoubleExpression;
import org.apache.drill.common.expression.ValueExpressions.FloatExpression;
import org.apache.drill.common.expression.ValueExpressions.IntExpression;
import org.apache.drill.common.expression.ValueExpressions.IntervalDayExpression;
import org.apache.drill.common.expression.ValueExpressions.IntervalYearExpression;
import org.apache.drill.common.expression.ValueExpressions.LongExpression;
import org.apache.drill.common.expression.ValueExpressions.QuotedString;
import org.apache.drill.common.expression.ValueExpressions.TimeExpression;
import org.apache.drill.common.expression.ValueExpressions.TimeStampExpression;
import org.apache.drill.common.expression.ValueExpressions.VarDecimalExpression;
import org.apache.drill.common.expression.visitors.AbstractExprVisitor;
import org.apache.drill.common.types.TypeProtos.MajorType;
import org.joda.time.Period;
import com.google.common.collect.ImmutableList;
public class ExpressionStringBuilder extends AbstractExprVisitor<Void, StringBuilder, RuntimeException>{
static final ExpressionStringBuilder INSTANCE = new ExpressionStringBuilder();
public static String toString(LogicalExpression expr) {
StringBuilder sb = new StringBuilder();
expr.accept(INSTANCE, sb);
return sb.toString();
}
public static void toString(LogicalExpression expr, StringBuilder sb) {
expr.accept(INSTANCE, sb);
}
public static String escapeSingleQuote(String input) {
return input.replaceAll("(['\\\\])", "\\\\$1");
}
public static String escapeBackTick(String input) {
return input.replaceAll("([`\\\\])", "\\\\$1");
}
@Override
public Void visitFunctionCall(FunctionCall call, StringBuilder sb) throws RuntimeException {
List<LogicalExpression> args = call.args();
sb.append(call.getName());
sb.append("(");
for (int i = 0; i < args.size(); i++) {
if (i != 0) {
sb.append(", ");
}
args.get(i).accept(this, sb);
}
sb.append(") ");
return null;
}
@Override
public Void visitBooleanOperator(BooleanOperator op, StringBuilder sb) throws RuntimeException {
return visitFunctionCall(op, sb);
}
@Override
public Void visitFunctionHolderExpression(FunctionHolderExpression holder, StringBuilder sb) throws RuntimeException {
ImmutableList<LogicalExpression> args = holder.args;
sb.append(holder.getName());
sb.append("(");
for (int i = 0; i < args.size(); i++) {
if (i != 0) {
sb.append(", ");
}
args.get(i).accept(this, sb);
}
sb.append(") ");
return null;
}
@Override
public Void visitIfExpression(IfExpression ifExpr, StringBuilder sb) throws RuntimeException {
// serialize the if expression
sb.append(" ( ");
IfCondition c = ifExpr.ifCondition;
sb.append("if (");
c.condition.accept(this, sb);
sb.append(" ) then (");
c.expression.accept(this, sb);
sb.append(" ) ");
sb.append(" else (");
ifExpr.elseExpression.accept(this, sb);
sb.append(" ) ");
sb.append(" end ");
sb.append(" ) ");
return null;
}
@Override
public Void visitSchemaPath(SchemaPath path, StringBuilder sb) throws RuntimeException {
PathSegment seg = path.getRootSegment();
if (seg.isArray()) {
throw new IllegalStateException("Drill doesn't currently support top level arrays");
}
sb.append('`');
sb.append(escapeBackTick(seg.getNameSegment().getPath()));
sb.append('`');
while ( (seg = seg.getChild()) != null) {
if (seg.isNamed()) {
sb.append('.');
sb.append('`');
sb.append(escapeBackTick(seg.getNameSegment().getPath()));
sb.append('`');
} else {
sb.append('[');
sb.append(seg.getArraySegment().getIndex());
sb.append(']');
}
}
return null;
}
@Override
public Void visitLongConstant(LongExpression lExpr, StringBuilder sb) throws RuntimeException {
sb.append(lExpr.getLong());
return null;
}
@Override
public Void visitDateConstant(DateExpression lExpr, StringBuilder sb) throws RuntimeException {
sb.append("cast( ");
sb.append(lExpr.getDate());
sb.append(" as DATE)");
return null;
}
@Override
public Void visitTimeConstant(TimeExpression lExpr, StringBuilder sb) throws RuntimeException {
sb.append("cast( ");
sb.append(lExpr.getTime());
sb.append(" as TIME)");
return null;
}
@Override
public Void visitTimeStampConstant(TimeStampExpression lExpr, StringBuilder sb) throws RuntimeException {
sb.append("cast( ");
sb.append(lExpr.getTimeStamp());
sb.append(" as TIMESTAMP)");
return null;
}
@Override
public Void visitIntervalYearConstant(IntervalYearExpression lExpr, StringBuilder sb) throws RuntimeException {
sb.append("cast( '");
sb.append(Period.months(lExpr.getIntervalYear()).toString());
sb.append("' as INTERVALYEAR)");
return null;
}
@Override
public Void visitIntervalDayConstant(IntervalDayExpression lExpr, StringBuilder sb) throws RuntimeException {
sb.append("cast( '");
sb.append(Period.days(lExpr.getIntervalDay()).plusMillis(lExpr.getIntervalMillis()).toString());
sb.append("' as INTERVALDAY)");
return null;
}
@Override
public Void visitDecimal9Constant(Decimal9Expression decExpr, StringBuilder sb) throws RuntimeException {
BigDecimal value = new BigDecimal(decExpr.getIntFromDecimal());
sb.append((value.setScale(decExpr.getScale())).toString());
return null;
}
@Override
public Void visitDecimal18Constant(Decimal18Expression decExpr, StringBuilder sb) throws RuntimeException {
BigDecimal value = new BigDecimal(decExpr.getLongFromDecimal());
sb.append((value.setScale(decExpr.getScale())).toString());
return null;
}
@Override
public Void visitDecimal28Constant(Decimal28Expression decExpr, StringBuilder sb) throws RuntimeException {
sb.append(decExpr.toString());
return null;
}
@Override
public Void visitDecimal38Constant(Decimal38Expression decExpr, StringBuilder sb) throws RuntimeException {
sb.append(decExpr.getBigDecimal().toString());
return null;
}
@Override
public Void visitVarDecimalConstant(VarDecimalExpression decExpr, StringBuilder sb) throws RuntimeException {
sb.append(decExpr.getBigDecimal().toString());
return null;
}
@Override
public Void visitDoubleConstant(DoubleExpression dExpr, StringBuilder sb) throws RuntimeException {
sb.append(dExpr.getDouble());
return null;
}
@Override
public Void visitBooleanConstant(BooleanExpression e, StringBuilder sb) throws RuntimeException {
sb.append(e.getBoolean());
return null;
}
@Override
public Void visitQuotedStringConstant(QuotedString e, StringBuilder sb) throws RuntimeException {
sb.append("'");
sb.append(escapeSingleQuote(e.value));
sb.append("'");
return null;
}
@Override
public Void visitConvertExpression(ConvertExpression e, StringBuilder sb) throws RuntimeException {
sb.append(e.getConvertFunction()).append("(");
e.getInput().accept(this, sb);
sb.append(", '").append(e.getEncodingType()).append("')");
return null;
}
@Override
public Void visitAnyValueExpression(AnyValueExpression e, StringBuilder sb) throws RuntimeException {
sb.append("any(");
e.getInput().accept(this, sb);
sb.append(")");
return null;
}
@Override
public Void visitCastExpression(CastExpression e, StringBuilder sb) throws RuntimeException {
MajorType mt = e.getMajorType();
sb.append("cast( (");
e.getInput().accept(this, sb);
sb.append(" ) as ");
sb.append(mt.getMinorType().name());
switch(mt.getMinorType()) {
case FLOAT4:
case FLOAT8:
case BIT:
case INT:
case TINYINT:
case SMALLINT:
case BIGINT:
case UINT1:
case UINT2:
case UINT4:
case UINT8:
case DATE:
case TIMESTAMP:
case TIMESTAMPTZ:
case TIME:
case INTERVAL:
case INTERVALDAY:
case INTERVALYEAR:
// do nothing else.
break;
case VAR16CHAR:
case VARBINARY:
case VARCHAR:
case FIXED16CHAR:
case FIXEDBINARY:
case FIXEDCHAR:
// add size in parens
sb.append("(");
sb.append(mt.getPrecision());
sb.append(")");
break;
case DECIMAL9:
case DECIMAL18:
case DECIMAL28DENSE:
case DECIMAL28SPARSE:
case DECIMAL38DENSE:
case DECIMAL38SPARSE:
case VARDECIMAL:
// add scale and precision
sb.append("(");
sb.append(mt.getPrecision());
sb.append(", ");
sb.append(mt.getScale());
sb.append(")");
break;
default:
throw new UnsupportedOperationException(String.format("Unable to convert cast expression %s into string.", e));
}
sb.append(" )");
return null;
}
@Override
public Void visitFloatConstant(FloatExpression fExpr, StringBuilder sb) throws RuntimeException {
sb.append(fExpr.getFloat());
return null;
}
@Override
public Void visitIntConstant(IntExpression intExpr, StringBuilder sb) throws RuntimeException {
sb.append(intExpr.getInt());
return null;
}
@Override
public Void visitNullConstant(TypedNullConstant e, StringBuilder sb) throws RuntimeException {
sb.append("NULL");
return null;
}
@Override
public Void visitNullExpression(NullExpression e, StringBuilder sb) throws RuntimeException {
sb.append("NULL");
return null;
}
@Override
public Void visitUnknown(LogicalExpression e, StringBuilder sb) {
sb.append(e.toString());
return null;
}
}