/** * Translates {@link org.eigenbase.rex.RexNode REX expressions} to {@link * net.hydromatic.linq4j.expressions.Expression linq4j expressions}. * * @author jhyde */ public class RexToLixTranslator { public static final Map<Method, SqlOperator> JAVA_TO_SQL_METHOD_MAP = Util.<Method, SqlOperator>mapOf( findMethod(String.class, "toUpperCase"), upperFunc, findMethod(SqlFunctions.class, "substring", String.class, Integer.TYPE, Integer.TYPE), substringFunc); private static final Map<SqlOperator, ExpressionType> SQL_TO_LINQ_OPERATOR_MAP = Util.<SqlOperator, ExpressionType>mapOf( andOperator, AndAlso, orOperator, OrElse, lessThanOperator, LessThan, lessThanOrEqualOperator, LessThanOrEqual, greaterThanOperator, GreaterThan, greaterThanOrEqualOperator, GreaterThanOrEqual, equalsOperator, Equal, notEqualsOperator, NotEqual, notOperator, Not); public static final Map<SqlOperator, Method> SQL_OP_TO_JAVA_METHOD_MAP = new HashMap<SqlOperator, Method>(); static { for (Map.Entry<Method, SqlOperator> entry : JAVA_TO_SQL_METHOD_MAP.entrySet()) { SQL_OP_TO_JAVA_METHOD_MAP.put(entry.getValue(), entry.getKey()); } } private final Map<RexNode, Slot> map = new HashMap<RexNode, Slot>(); private final JavaTypeFactory typeFactory; private final RexProgram program; private final List<Slot> inputSlots = new ArrayList<Slot>(); /** * Set of expressions which are to be translated inline. That is, they should not be assigned to * variables on first use. At present, the algorithm is to use a first pass to determine how many * times each expression is used, and expressions are marked for inlining if they are used at most * once. */ private final Set<RexNode> inlineRexSet = new HashSet<RexNode>(); private List<Statement> list; private static Method findMethod(Class<?> clazz, String name, Class... parameterTypes) { try { return clazz.getMethod(name, parameterTypes); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } private RexToLixTranslator( RexProgram program, JavaTypeFactory typeFactory, List<Expression> inputs) { this.program = program; this.typeFactory = typeFactory; for (Expression input : inputs) { inputSlots.add(new Slot(null, input)); } } /** * Translates a {@link RexProgram} to a sequence of expressions and declarations. * * @param inputs Variables holding the current record of each input relational expression * @param program Program to be translated * @return Sequence of expressions, optional condition */ public static List<Expression> translateProjects( List<Expression> inputs, RexProgram program, JavaTypeFactory typeFactory, List<Statement> list) { return new RexToLixTranslator(program, typeFactory, inputs) .translate(list, program.getProjectList()); } private Expression translate(RexNode expr) { Slot slot = map.get(expr); if (slot == null) { Expression expression = translate0(expr); assert expression != null; final ParameterExpression parameter; if (!inlineRexSet.contains(expr) && !(expr instanceof RexLocalRef)) { parameter = Expressions.parameter(expression.getType(), "v" + map.size()); } else { parameter = null; } slot = new Slot(parameter, expression); if (parameter != null && list != null) { list.add(Expressions.declare(Modifier.FINAL, slot.parameterExpression, slot.expression)); } map.put(expr, slot); } slot.count++; return slot.parameterExpression != null ? slot.parameterExpression : slot.expression; } private Expression translate0(RexNode expr) { if (expr instanceof RexInputRef) { // TODO: multiple inputs, e.g. joins final Expression input = getInput(0); final int index = ((RexInputRef) expr).getIndex(); final List<RelDataTypeField> fields = program.getInputRowType().getFieldList(); final RelDataTypeField field = fields.get(index); if (fields.size() == 1) { return input; } else if (input.getType() == Object[].class) { return Expressions.convert_( Expressions.arrayIndex(input, Expressions.constant(field.getIndex())), Types.box(JavaRules.EnumUtil.javaClass(typeFactory, field.getType()))); } else { return Expressions.field(input, field.getName()); } } if (expr instanceof RexLocalRef) { return translate(program.getExprList().get(((RexLocalRef) expr).getIndex())); } if (expr instanceof RexLiteral) { return Expressions.constant( ((RexLiteral) expr).getValue(), typeFactory.getJavaClass(expr.getType())); } if (expr instanceof RexCall) { final RexCall call = (RexCall) expr; final SqlOperator operator = call.getOperator(); final ExpressionType expressionType = SQL_TO_LINQ_OPERATOR_MAP.get(operator); if (expressionType != null) { switch (operator.getSyntax()) { case Binary: return Expressions.makeBinary( expressionType, translate(call.getOperands()[0]), translate(call.getOperands()[1])); case Postfix: case Prefix: return Expressions.makeUnary(expressionType, translate(call.getOperands()[0])); default: throw new RuntimeException("unknown syntax " + operator.getSyntax()); } } Method method = SQL_OP_TO_JAVA_METHOD_MAP.get(operator); if (method != null) { List<Expression> exprs = translateList(Arrays.asList(call.operands)); return !Modifier.isStatic(method.getModifiers()) ? Expressions.call(exprs.get(0), method, exprs.subList(1, exprs.size())) : Expressions.call(method, exprs); } switch (expr.getKind()) { default: throw new RuntimeException("cannot translate expression " + expr); } } throw new RuntimeException("cannot translate expression " + expr); } /** * Gets the expression for an input and counts it. * * @param index Input ordinal * @return Expression to which an input should be translated */ private Expression getInput(int index) { Slot slot = inputSlots.get(index); if (list == null) { slot.count++; } else { if (slot.count > 1 && slot.parameterExpression == null) { slot.parameterExpression = Expressions.parameter(slot.expression.type, "current" + index); list.add(Expressions.declare(Modifier.FINAL, slot.parameterExpression, slot.expression)); } } return slot.parameterExpression != null ? slot.parameterExpression : slot.expression; } private List<Expression> translateList(List<RexNode> operandList) { final List<Expression> list = new ArrayList<Expression>(); for (RexNode rex : operandList) { list.add(translate(rex)); } return list; } private List<Expression> translate(List<Statement> list, List<RexLocalRef> rexList) { // First pass. Count how many times each sub-expression is used. this.list = null; for (RexNode rexExpr : rexList) { translate(rexExpr); } // Mark expressions as inline if they are not used more than once. for (Map.Entry<RexNode, Slot> entry : map.entrySet()) { if (entry.getValue().count < 2 || entry.getKey() instanceof RexLiteral) { inlineRexSet.add(entry.getKey()); } } // Second pass. When translating each expression, if it is used more // than once, the first time it is encountered, add a declaration to the // list and set its usage count to 0. this.list = list; this.map.clear(); List<Expression> translateds = new ArrayList<Expression>(); for (RexNode rexExpr : rexList) { translateds.add(translate(rexExpr)); } return translateds; } public static Expression translateCondition( List<Expression> inputs, RexProgram program, JavaTypeFactory typeFactory, List<Statement> list) { List<Expression> x = new RexToLixTranslator(program, typeFactory, inputs) .translate(list, Collections.singletonList(program.getCondition())); assert x.size() == 1; return x.get(0); } private static class Slot { ParameterExpression parameterExpression; Expression expression; int count; public Slot(ParameterExpression parameterExpression, Expression expression) { this.parameterExpression = parameterExpression; this.expression = expression; } } }