/* * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, * and the EPL 1.0 (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.expression; import org.h2.command.Parser; import org.h2.engine.Constants; import org.h2.engine.FunctionAlias; import org.h2.engine.Session; import org.h2.table.ColumnResolver; import org.h2.table.TableFilter; import org.h2.util.StatementBuilder; import org.h2.value.DataType; import org.h2.value.Value; import org.h2.value.ValueArray; import org.h2.value.ValueNull; import org.h2.value.ValueResultSet; /** * This class wraps a user-defined function. */ public class JavaFunction extends Expression implements FunctionCall { private final FunctionAlias functionAlias; private final FunctionAlias.JavaMethod javaMethod; private final Expression[] args; public JavaFunction(FunctionAlias functionAlias, Expression[] args) { this.functionAlias = functionAlias; this.javaMethod = functionAlias.findJavaMethod(args); this.args = args; } @Override public Value getValue(Session session) { return javaMethod.getValue(session, args, false); } @Override public int getType() { return javaMethod.getDataType(); } @Override public void mapColumns(ColumnResolver resolver, int level) { for (Expression e : args) { e.mapColumns(resolver, level); } } @Override public Expression optimize(Session session) { boolean allConst = isDeterministic(); for (int i = 0, len = args.length; i < len; i++) { Expression e = args[i].optimize(session); args[i] = e; allConst &= e.isConstant(); } if (allConst) { return ValueExpression.get(getValue(session)); } return this; } @Override public void setEvaluatable(TableFilter tableFilter, boolean b) { for (Expression e : args) { if (e != null) { e.setEvaluatable(tableFilter, b); } } } @Override public int getScale() { return DataType.getDataType(getType()).defaultScale; } @Override public long getPrecision() { return Integer.MAX_VALUE; } @Override public int getDisplaySize() { return Integer.MAX_VALUE; } @Override public String getSQL() { StatementBuilder buff = new StatementBuilder(); // TODO always append the schema once FUNCTIONS_IN_SCHEMA is enabled if (functionAlias.getDatabase().getSettings().functionsInSchema || !functionAlias.getSchema().getName().equals(Constants.SCHEMA_MAIN)) { buff.append( Parser.quoteIdentifier(functionAlias.getSchema().getName())) .append('.'); } buff.append(Parser.quoteIdentifier(functionAlias.getName())).append('('); for (Expression e : args) { buff.appendExceptFirst(", "); buff.append(e.getSQL()); } return buff.append(')').toString(); } @Override public void updateAggregate(Session session) { for (Expression e : args) { if (e != null) { e.updateAggregate(session); } } } @Override public String getName() { return functionAlias.getName(); } @Override public ValueResultSet getValueForColumnList(Session session, Expression[] argList) { Value v = javaMethod.getValue(session, argList, true); return v == ValueNull.INSTANCE ? null : (ValueResultSet) v; } @Override public Expression[] getArgs() { return args; } @Override public boolean isEverything(ExpressionVisitor visitor) { switch (visitor.getType()) { case ExpressionVisitor.DETERMINISTIC: if (!isDeterministic()) { return false; } // only if all parameters are deterministic as well break; case ExpressionVisitor.GET_DEPENDENCIES: visitor.addDependency(functionAlias); break; default: } for (Expression e : args) { if (e != null && !e.isEverything(visitor)) { return false; } } return true; } @Override public int getCost() { int cost = javaMethod.hasConnectionParam() ? 25 : 5; for (Expression e : args) { cost += e.getCost(); } return cost; } @Override public boolean isDeterministic() { return functionAlias.isDeterministic(); } @Override public Expression[] getExpressionColumns(Session session) { switch (getType()) { case Value.RESULT_SET: ValueResultSet rs = getValueForColumnList(session, getArgs()); return getExpressionColumns(session, rs.getResultSet()); case Value.ARRAY: return getExpressionColumns(session, (ValueArray) getValue(session)); } return super.getExpressionColumns(session); } @Override public boolean isBufferResultSetToLocalTemp() { return functionAlias.isBufferResultSetToLocalTemp(); } }