/* * 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 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.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 org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ExpressionStringBuilder.class); 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 { ImmutableList<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 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 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: // 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; } }