@Override
 public String toString() {
   StringBuilder str = new StringBuilder(routine.getName().toString());
   str.append("(");
   boolean first = true;
   for (ExpressionNode operand : operands) {
     if (first) first = false;
     else str.append(",");
     str.append(operand);
   }
   str.append(")");
   return str.toString();
 }
 private TPreparedExpression assembleRoutine(
     ExpressionNode routineNode,
     Routine routine,
     List<ExpressionNode> operandNodes,
     ColumnExpressionContext columnContext,
     SubqueryOperatorAssembler subqueryAssembler) {
   List<TPreparedExpression> inputs =
       assembleExpressions(operandNodes, columnContext, subqueryAssembler);
   switch (routine.getCallingConvention()) {
     case JAVA:
       return new ServerJavaMethodTExpression(routine, inputs);
     case SCRIPT_FUNCTION_JAVA:
     case SCRIPT_FUNCTION_JSON:
       return new ScriptFunctionJavaRoutineTExpression(routine, inputs);
     case SCRIPT_BINDINGS:
     case SCRIPT_BINDINGS_JSON:
       return new ScriptBindingsRoutineTExpression(routine, inputs);
     default:
       throw new AkibanInternalException("Unimplemented routine " + routine.getName());
   }
 }
 @Override
 public int hashCode() {
   int hash = routine.getName().hashCode();
   hash += operands.hashCode();
   return hash;
 }