/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.sql.server;
import com.foundationdb.server.types.value.Value;
import com.foundationdb.server.types.value.ValueSource;
import com.foundationdb.server.types.value.ValueSources;
import com.foundationdb.sql.parser.ConstantNode;
import com.foundationdb.sql.parser.JavaValueNode;
import com.foundationdb.sql.parser.ParameterNode;
import com.foundationdb.sql.parser.SQLToJavaValueNode;
import com.foundationdb.sql.parser.StaticMethodCallNode;
import com.foundationdb.sql.parser.ValueNode;
import com.foundationdb.ais.model.Routine;
import com.foundationdb.ais.model.TableName;
import com.foundationdb.qp.operator.QueryBindings;
import com.foundationdb.server.error.NoSuchRoutineException;
import com.foundationdb.server.error.UnsupportedSQLException;
import com.foundationdb.server.types.TClass;
import com.foundationdb.server.types.TExecutionContext;
import com.foundationdb.server.types.TInstance;
import java.util.Arrays;
public class ServerCallInvocation extends ServerRoutineInvocation
{
private final Object[] constantArgs;
private final int[] parameterArgs;
protected ServerCallInvocation(Routine routine,
Object[] constantArgs,
int[] parameterArgs) {
super(routine);
this.constantArgs = constantArgs;
this.parameterArgs = parameterArgs;
}
public static ServerCallInvocation of(ServerSession server,
StaticMethodCallNode methodCall) {
String schemaName, routineName;
if (methodCall.getProcedureName() == null) {
schemaName = null;
routineName = methodCall.getMethodName();
}
else {
schemaName = methodCall.getProcedureName().getSchemaName();
routineName = methodCall.getProcedureName().getTableName();
}
if (schemaName == null) {
schemaName = server.getDefaultSchemaName();
}
Routine routine = server.getAIS().getRoutine(schemaName, routineName);
if (routine == null)
throw new NoSuchRoutineException(schemaName, routineName);
Object[] constantArgs = null;
int[] parameterArgs = null;
JavaValueNode[] margs = methodCall.getMethodParameters();
if (margs != null) {
constantArgs = new Object[margs.length];
parameterArgs = new int[margs.length];
Arrays.fill(parameterArgs, -1);
for (int i = 0; i < margs.length; i++) {
JavaValueNode marg = margs[i];
if (marg instanceof SQLToJavaValueNode) {
ValueNode sqlArg = ((SQLToJavaValueNode)marg).getSQLValueNode();
if (sqlArg instanceof ConstantNode) {
constantArgs[i] = ((ConstantNode)sqlArg).getValue();
continue;
}
else if (sqlArg instanceof ParameterNode) {
parameterArgs[i] = ((ParameterNode)sqlArg).getParameterNumber();
continue;
}
}
throw new UnsupportedSQLException("CALL parameter must be constant",
marg);
}
}
return new ServerCallInvocation(routine, constantArgs, parameterArgs);
}
public static ServerCallInvocation of(ServerSession server, TableName routineName) {
Routine routine = server.getAIS().getRoutine(routineName);
if (routine == null)
throw new NoSuchRoutineException(routineName);
int nparams = routine.getParameters().size();
Object[] constantArgs = new Object[nparams];
int[] parameterArgs = new int[nparams];
for (int i = 0; i < nparams; i++) {
parameterArgs[i] = i;
}
return new ServerCallInvocation(routine, constantArgs, parameterArgs);
}
public int parameterUsage(int param) {
if (parameterArgs != null) {
for (int i = 0; i < parameterArgs.length; i++) {
if (parameterArgs[i] == param) {
return i;
}
}
}
return -1;
}
public boolean hasParameters() {
for (int index : parameterArgs) {
if (index != -1) {
return true;
}
}
return false;
}
public boolean parametersInOrder() {
for (int i = 0; i < parameterArgs.length; i++) {
if (i != parameterArgs[i]) {
return false;
}
}
return true;
}
public void copyParameters(QueryBindings source, QueryBindings target) {
for (int i = 0; i < parameterArgs.length; i++) {
if (parameterArgs[i] < 0) {
TInstance type = null;
if (constantArgs[i] == null)
type = this.getType(i);
target.setValue(i, ValueSources.valuefromObject(constantArgs[i], type));
}
else {
target.setValue(i, source.getValue(parameterArgs[i]));
}
}
}
@Override
public int size() {
if (constantArgs == null)
return 0;
else
return constantArgs.length;
}
public int getParameterNumber(int i) {
return parameterArgs[i];
}
public Object getConstantValue(int i) {
return constantArgs[i];
}
@Override
public ServerJavaValues asValues(ServerQueryContext context, QueryBindings bindings) {
return new Values(context, bindings);
}
protected class Values extends ServerJavaValues {
private ServerQueryContext context;
private QueryBindings bindings;
protected Values(ServerQueryContext context, QueryBindings bindings) {
this.context = context;
this.bindings = bindings;
}
@Override
protected int size() {
return getRoutine().getParameters().size();
}
@Override
protected ServerQueryContext getContext() {
return context;
}
@Override
protected ValueSource getValue(int index) {
TInstance type = getType(index);
TClass tclass = type.typeClass();
ValueSource source;
if (parameterArgs[index] < 0) {
source = ValueSources.valuefromObject(constantArgs[index], type);
if (source.getType().typeClass().equals(tclass))
return source; // Literal value matches.
}
else {
source = bindings.getValue(parameterArgs[index]);
}
// Constants passed or parameters bound may not be of the
// type specified in the signature.
Value value = new Value(type);
TExecutionContext executionContext =
new TExecutionContext(null, null, type,
context, null, null, null);
tclass.fromObject(executionContext, source, value);
return value;
}
@Override
protected TInstance getType(int index) {
return ServerCallInvocation.this.getType(index);
}
@Override
protected void setValue(int index, ValueSource source) {
if (parameterArgs[index] < 0) {
// An INOUT passed as a constant; do not overwrite it.
}
else {
bindings.setValue(parameterArgs[index], source);
}
}
@Override
protected java.sql.ResultSet toResultSet(int index, Object resultSet) {
throw new UnsupportedOperationException();
}
}
}