org.hsqldb.Routine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sqltool Show documentation
Show all versions of sqltool Show documentation
HSQLDB - Lightweight 100% Java SQL Database Engine
/* Copyright (c) 2001-2021, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.HsqlNameManager.SimpleName;
import org.hsqldb.ParserBase.Recorder;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.jdbc.JDBCResultSet;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.OrderedHashMap;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.map.BitMap;
import org.hsqldb.map.ValuePool;
import org.hsqldb.persist.HsqlDatabaseProperties;
import org.hsqldb.result.Result;
import org.hsqldb.rights.Grantee;
import org.hsqldb.types.RowType;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;
/**
* Implementation of specific routine
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
*
* @version 2.6.0
* @since 1.9.0
*/
public class Routine implements SchemaObject, RangeGroup, Cloneable {
public static final int NO_SQL = 1;
public static final int CONTAINS_SQL = 2;
public static final int READS_SQL = 3;
public static final int MODIFIES_SQL = 4;
//
public static final int LANGUAGE_JAVA = 1;
public static final int LANGUAGE_SQL = 2;
//
public static final int PARAM_STYLE_JAVA = 1;
public static final int PARAM_STYLE_SQL = 2;
//
static final Routine[] emptyArray = new Routine[]{};
//
RoutineSchema routineSchema;
private HsqlName name;
private HsqlName specificName;
Type[] parameterTypes;
int typeGroups;
Type returnType;
Table returnTable;
final int routineType;
int language = LANGUAGE_SQL;
int dataImpact = CONTAINS_SQL;
int parameterStyle;
boolean isDeterministic;
boolean isNullInputOutput;
boolean isNewSavepointLevel = true;
int maxDynamicResults = 0;
boolean isRecursive;
boolean returnsTable;
Statement statement;
//
boolean isAggregate;
boolean isIndex;
boolean isSearch;
//
private String methodName;
Method javaMethod;
boolean javaMethodWithConnection;
private boolean isLibraryRoutine;
//
OrderedHashMap parameterList = new OrderedHashMap();
RangeVariable[] ranges = RangeVariable.emptyArray;
//
int variableCount;
int cursorCount;
//
OrderedHashSet references;
//
Table triggerTable;
int triggerType;
int triggerOperation;
public Routine(int type) {
routineType = type;
returnType = Type.SQL_ALL_TYPES;
ranges = new RangeVariable[]{
new RangeVariable(parameterList, null, false,
RangeVariable.PARAMETER_RANGE) };
}
public Routine(Table table, RangeVariable[] ranges, int impact,
int triggerType, int operationType) {
routineType = SchemaObject.TRIGGER;
returnType = Type.SQL_ALL_TYPES;
dataImpact = impact;
this.ranges = ranges;
this.triggerTable = table;
this.triggerType = triggerType;
this.triggerOperation = operationType;
}
public int getType() {
return routineType;
}
public HsqlName getName() {
return name;
}
public HsqlName getSchemaName() {
if (routineType == SchemaObject.TRIGGER) {
return triggerTable.getSchemaName();
}
return name.schema;
}
public HsqlName getCatalogName() {
return name.schema.schema;
}
public Grantee getOwner() {
return name.schema.owner;
}
public OrderedHashSet getReferences() {
return references;
}
public OrderedHashSet getComponents() {
return null;
}
public void compile(Session session, SchemaObject parentObject) {
ParserRoutine p = new ParserRoutine(session,
new Scanner(session,
statement.getSQL()));
session.sessionContext.pushRoutineTables();
try {
p.read();
Recorder recorder = p.startRecording();
Statement newStatement = p.compileSQLProcedureStatementOrNull(this,
null);
String sql = recorder.getSQL();
newStatement.setSQL(sql);
setProcedure(newStatement);
resolve(session);
} finally {
session.sessionContext.popRoutineTables();
}
}
public String getSQL() {
return getDefinitionSQL(true);
}
public String getSQLAlter() {
StringBuilder sb = new StringBuilder();
sb.append(Tokens.T_ALTER).append(' ').append(Tokens.T_SPECIFIC);
sb.append(' ').append(Tokens.T_ROUTINE).append(' ');
sb.append(specificName.getSchemaQualifiedStatementName());
sb.append(' ').append(Tokens.T_BODY);
sb.append(' ').append(statement.getSQL());
return sb.toString();
}
public String getSQLDeclaration() {
return getDefinitionSQL(false);
}
private String getDefinitionSQL(boolean withBody) {
StringBuilder sb = new StringBuilder();
sb.append(Tokens.T_CREATE).append(' ');
if (isAggregate) {
sb.append(Tokens.T_AGGREGATE).append(' ');
}
if (routineType == SchemaObject.PROCEDURE) {
sb.append(Tokens.T_PROCEDURE);
} else {
sb.append(Tokens.T_FUNCTION);
}
sb.append(' ');
sb.append(name.getSchemaQualifiedStatementName());
sb.append('(');
for (int i = 0; i < parameterList.size(); i++) {
if (i > 0) {
sb.append(',');
}
ColumnSchema param = (ColumnSchema) parameterList.get(i);
// in - out
sb.append(param.getSQL());
}
sb.append(')');
sb.append(' ');
if (routineType == SchemaObject.FUNCTION) {
sb.append(Tokens.T_RETURNS);
sb.append(' ');
if (returnsTable) {
sb.append(Tokens.T_TABLE);
sb.append(returnTable.getColumnListWithTypeSQL());
} else {
sb.append(returnType.getTypeDefinition());
}
sb.append(' ');
}
// SPECIFIC
if (specificName != null) {
sb.append(Tokens.T_SPECIFIC);
sb.append(' ');
sb.append(specificName.getStatementName());
sb.append(' ');
}
//
sb.append(Tokens.T_LANGUAGE);
sb.append(' ');
if (language == LANGUAGE_JAVA) {
sb.append(Tokens.T_JAVA);
} else {
sb.append(Tokens.T_SQL);
}
sb.append(' ');
//
if (!isDeterministic) {
sb.append(Tokens.T_NOT);
sb.append(' ');
}
sb.append(Tokens.T_DETERMINISTIC);
sb.append(' ');
//
sb.append(getDataImpactString());
sb.append(' ');
//
if (routineType == SchemaObject.FUNCTION) {
if (isNullInputOutput) {
sb.append(Tokens.T_RETURNS).append(' ').append(Tokens.T_NULL);
} else {
sb.append(Tokens.T_CALLED);
}
sb.append(' ').append(Tokens.T_ON).append(' ');
sb.append(Tokens.T_NULL).append(' ').append(Tokens.T_INPUT);
sb.append(' ');
} else {
if (isNewSavepointLevel) {
sb.append(Tokens.T_NEW);
} else {
sb.append(Tokens.T_OLD);
}
sb.append(' ').append(Tokens.T_SAVEPOINT).append(' ');
sb.append(Tokens.T_LEVEL).append(' ');
if (maxDynamicResults != 0) {
sb.append(' ').append(Tokens.T_DYNAMIC).append(' ');
sb.append(Tokens.T_RESULT).append(' ').append(Tokens.T_SETS);
sb.append(' ').append(maxDynamicResults).append(' ');
}
}
if (language == LANGUAGE_JAVA) {
sb.append(Tokens.T_EXTERNAL).append(' ').append(Tokens.T_NAME);
sb.append(' ').append('\'').append(methodName).append('\'');
} else {
if (withBody) {
sb.append(statement.getSQL());
} else {
sb.append(Tokens.T_SIGNAL).append(' ');
sb.append(Tokens.T_SQLSTATE).append(' ');
sb.append('\'').append("45000").append('\'');
}
}
return sb.toString();
}
public String getSQLBodyDefinition() {
StringBuilder sb = new StringBuilder();
if (language == LANGUAGE_JAVA) {
sb.append(Tokens.T_EXTERNAL).append(' ').append(Tokens.T_NAME);
sb.append(' ').append('\'').append(methodName).append('\'');
} else {
sb.append(statement.getSQL());
}
return sb.toString();
}
public String getExternalName() {
if (language == LANGUAGE_JAVA) {
return methodName;
} else {
return null;
}
}
public long getChangeTimestamp() {
return 0;
}
public void addParameter(ColumnSchema param) {
HsqlName name = param.getName();
String paramName =
name == null
? HsqlNameManager.getAutoNoNameColumnString(parameterList.size())
: name.name;
boolean result = parameterList.add(paramName, param);
if (!result) {
throw Error.error(ErrorCode.X_42614);
}
}
public void setLanguage(int lang) {
language = lang;
}
public int getLanguage() {
return language;
}
boolean isPSM() {
return language == LANGUAGE_SQL;
}
public void setDataImpact(int impact) {
dataImpact = impact;
}
public int getDataImpact() {
return dataImpact;
}
public String getDataImpactString() {
StringBuilder sb = new StringBuilder();
switch (dataImpact) {
case NO_SQL :
sb.append(Tokens.T_NO).append(' ').append(Tokens.T_SQL);
break;
case CONTAINS_SQL :
sb.append(Tokens.T_CONTAINS).append(' ').append(Tokens.T_SQL);
break;
case READS_SQL :
sb.append(Tokens.T_READS).append(' ').append(
Tokens.T_SQL).append(' ').append(Tokens.T_DATA);
break;
case MODIFIES_SQL :
sb.append(Tokens.T_MODIFIES).append(' ').append(
Tokens.T_SQL).append(' ').append(Tokens.T_DATA);
break;
}
return sb.toString();
}
public void setReturnType(Type type) {
returnType = type;
}
public Type getReturnType() {
return returnType;
}
public Table getTable() {
return returnTable;
}
public void setProcedure(Statement statement) {
this.statement = statement;
}
public Statement getProcedure() {
return statement;
}
public void setSpecificName(HsqlName name) {
specificName = name;
}
public int getMaxDynamicResults() {
return maxDynamicResults;
}
public void setName(HsqlName name) {
this.name = name;
}
public HsqlName getSpecificName() {
return specificName;
}
public void setDeterministic(boolean value) {
isDeterministic = value;
}
public boolean isDeterministic() {
return isDeterministic;
}
public void setNullInputOutput(boolean value) {
isNullInputOutput = value;
}
public boolean isNullInputOutput() {
return isNullInputOutput;
}
public void setNewSavepointLevel(boolean value) {
isNewSavepointLevel = value;
}
public void setMaxDynamicResults(int value) {
maxDynamicResults = value;
}
public void setParameterStyle(int style) {
parameterStyle = style;
}
public void setMethodURL(String url) {
this.methodName = url;
}
public Method getMethod() {
return javaMethod;
}
public void setMethod(Method method) {
this.javaMethod = method;
}
public void setReturnTable(TableDerived table) {
this.returnTable = table;
this.returnsTable = true;
SimpleName[] names = new SimpleName[table.getColumnCount()];
Type[] types = table.getColumnTypes();
returnType = new RowType(types);
}
public boolean returnsTable() {
return returnsTable;
}
public void setAggregate(boolean isAggregate) {
this.isAggregate = isAggregate;
}
public boolean isAggregate() {
return isAggregate;
}
public void resolve(Session session) {
setLanguage(language);
if (language == Routine.LANGUAGE_SQL) {
if (dataImpact == NO_SQL) {
throw Error.error(ErrorCode.X_42604, "CONTAINS SQL");
}
if (parameterStyle == PARAM_STYLE_JAVA) {
throw Error.error(ErrorCode.X_42604, "PARAMETER STYLE");
}
}
if (language == Routine.LANGUAGE_SQL) {
if (parameterStyle != 0 && parameterStyle != PARAM_STYLE_SQL) {
throw Error.error(ErrorCode.X_42604, "PARAMETER STYLE");
}
}
parameterTypes = new Type[parameterList.size()];
typeGroups = 0;
for (int i = 0; i < parameterTypes.length; i++) {
ColumnSchema param = (ColumnSchema) parameterList.get(i);
parameterTypes[i] = param.dataType;
if (i < 4) {
typeGroups =
BitMap.setByte(typeGroups,
(byte) param.dataType.typeComparisonGroup,
i * 8);
}
}
if (isAggregate) {
if (parameterTypes.length != 4) {
throw Error.error(ErrorCode.X_42610);
}
boolean check = parameterTypes[1].typeCode == Types.BOOLEAN;
//
ColumnSchema param = (ColumnSchema) parameterList.get(0);
check &= param.getParameterMode()
== SchemaObject.ParameterModes.PARAM_IN;
param = (ColumnSchema) parameterList.get(1);
check &= param.getParameterMode()
== SchemaObject.ParameterModes.PARAM_IN;
param = (ColumnSchema) parameterList.get(2);
check &= param.getParameterMode()
== SchemaObject.ParameterModes.PARAM_INOUT;
param = (ColumnSchema) parameterList.get(3);
check &= param.getParameterMode()
== SchemaObject.ParameterModes.PARAM_INOUT;
if (!check) {
throw Error.error(ErrorCode.X_42610);
}
}
resolveReferences(session);
}
void resolveReferences(Session session) {
if (statement != null) {
statement.resolve(session);
checkSQLData(session);
}
if (methodName != null && javaMethod == null) {
boolean[] hasConnection = new boolean[1];
javaMethod = getMethod(methodName, this, hasConnection,
returnsTable);
if (javaMethod == null) {
throw Error.error(ErrorCode.X_46103);
}
javaMethodWithConnection = hasConnection[0];
String className = javaMethod.getDeclaringClass().getName();
if (className.equals("java.lang.Math")) {
isLibraryRoutine = true;
}
}
setReferences();
}
private void setReferences() {
OrderedHashSet set = new OrderedHashSet();
for (int i = 0; i < parameterTypes.length; i++) {
ColumnSchema param = (ColumnSchema) parameterList.get(i);
OrderedHashSet refs = param.getReferences();
if (refs != null) {
set.addAll(refs);
}
}
if (statement != null) {
set.addAll(statement.getReferences());
}
isRecursive = false;
if (set.contains(getSpecificName())) {
set.remove(getSpecificName());
isRecursive = true;
}
references = set;
}
void checkSQLData(Session session) {
OrderedHashSet set = statement.getReferences();
for (int i = 0; i < set.size(); i++) {
HsqlName name = (HsqlName) set.get(i);
if (name.type == SchemaObject.SPECIFIC_ROUTINE) {
Routine routine =
(Routine) session.database.schemaManager.getSchemaObject(
name);
if (routine.dataImpact == Routine.READS_SQL) {
if (dataImpact == Routine.CONTAINS_SQL) {
throw Error.error(ErrorCode.X_42608,
Tokens.T_READS + ' ' + Tokens.T_SQL
+ ' ' + Tokens.T_DATA);
}
} else if (routine.dataImpact == Routine.MODIFIES_SQL) {
if (dataImpact == Routine.CONTAINS_SQL
|| dataImpact == Routine.READS_SQL) {
throw Error.error(ErrorCode.X_42608,
Tokens.T_MODIFIES + ' '
+ Tokens.T_SQL + ' '
+ Tokens.T_DATA);
}
}
}
}
if (dataImpact == Routine.CONTAINS_SQL
|| dataImpact == Routine.READS_SQL) {
HsqlName[] names = statement.getTableNamesForWrite();
for (int i = 0; i < names.length; i++) {
if (names[i].schema != SqlInvariants.SYSTEM_SCHEMA_HSQLNAME) {
throw Error.error(ErrorCode.X_42608,
Tokens.T_MODIFIES + ' ' + Tokens.T_SQL
+ ' ' + Tokens.T_DATA);
}
}
}
if (dataImpact == Routine.CONTAINS_SQL) {
HsqlName[] names = statement.getTableNamesForRead();
for (int i = 0; i < names.length; i++) {
if (names[i].schema != SqlInvariants.SYSTEM_SCHEMA_HSQLNAME) {
throw Error.error(ErrorCode.X_42608,
Tokens.T_READS + ' ' + Tokens.T_SQL
+ ' ' + Tokens.T_DATA);
}
}
}
}
public boolean isTrigger() {
return routineType == SchemaObject.TRIGGER;
}
public boolean isProcedure() {
return routineType == SchemaObject.PROCEDURE;
}
public boolean isFunction() {
return routineType == SchemaObject.FUNCTION;
}
public ColumnSchema getParameter(int i) {
return (ColumnSchema) parameterList.get(i);
}
Type[] getParameterTypes() {
return parameterTypes;
}
int getParameterSignature() {
return typeGroups;
}
public int getParameterCount() {
return parameterTypes.length;
}
public int getParameterCount(int type) {
int count = 0;
for (int i = 0; i < parameterList.size(); i++) {
ColumnSchema col = (ColumnSchema) parameterList.get(i);
if (col.getParameterMode() == type) {
count++;
}
}
return count;
}
public int getParameterIndex(String name) {
return parameterList.getIndex(name);
}
public RangeVariable[] getRangeVariables() {
return ranges;
}
public void setCorrelated() {
//
}
public boolean isVariable() {
return true;
}
public int getVariableCount() {
return variableCount;
}
public int getCursorCount() {
return cursorCount;
}
public boolean isLibraryRoutine() {
return isLibraryRoutine;
}
public HsqlName[] getTableNamesForRead() {
if (statement == null) {
return HsqlName.emptyArray;
}
return statement.getTableNamesForRead();
}
public HsqlName[] getTableNamesForWrite() {
if (statement == null) {
return HsqlName.emptyArray;
}
return statement.getTableNamesForWrite();
}
public void resetAlteredRoutineSettings() {
if (isPSM()) {
methodName = null;
javaMethod = null;
javaMethodWithConnection = false;
parameterStyle = PARAM_STYLE_SQL;
if (dataImpact == NO_SQL) {
dataImpact = CONTAINS_SQL;
}
} else {
statement = null;
references = null;
variableCount = 0;
cursorCount = 0;
ranges = RangeVariable.emptyArray;
}
}
public void setAsAlteredRoutine(Routine routine) {
language = routine.language;
dataImpact = routine.dataImpact;
parameterStyle = routine.parameterStyle;
isDeterministic = routine.isDeterministic;
isNullInputOutput = routine.isNullInputOutput;
maxDynamicResults = routine.maxDynamicResults;
isRecursive = routine.isRecursive;
javaMethod = routine.javaMethod;
//
isRecursive = routine.isRecursive;
javaMethodWithConnection = routine.javaMethodWithConnection;
methodName = routine.methodName;
statement = routine.statement;
references = routine.references;
variableCount = routine.variableCount;
cursorCount = routine.cursorCount;
ranges = routine.ranges;
}
Object[] convertArgsToJava(Session session, Object[] callArguments) {
int extraArg = javaMethodWithConnection ? 1
: 0;
Object[] data = new Object[javaMethod.getParameterTypes().length];
Type[] types = getParameterTypes();
int i = 0;
for (; i < types.length; i++) {
Object value = callArguments[i];
ColumnSchema param = getParameter(i);
if (param.parameterMode == SchemaObject.ParameterModes.PARAM_IN) {
data[i + extraArg] = types[i].convertSQLToJava(session, value);
} else {
Object jdbcValue = types[i].convertSQLToJava(session, value);
Class cl = types[i].getJDBCClass();
Object array = java.lang.reflect.Array.newInstance(cl, 1);
java.lang.reflect.Array.set(array, 0, jdbcValue);
data[i + extraArg] = array;
}
}
for (; i + extraArg < data.length; i++) {
data[i + extraArg] = new java.sql.ResultSet[1];
}
return data;
}
void convertArgsToSQL(Session session, Object[] callArguments,
Object[] data) {
int extraArg = javaMethodWithConnection ? 1
: 0;
Type[] types = getParameterTypes();
int i = 0;
for (; i < types.length; i++) {
Object value = data[i + extraArg];
ColumnSchema param = getParameter(i);
if (param.parameterMode != SchemaObject.ParameterModes.PARAM_IN) {
value = java.lang.reflect.Array.get(value, 0);
}
callArguments[i] = types[i].convertJavaToSQL(session, value);
}
Result head = null;
for (; i + extraArg < data.length; i++) {
ResultSet rs = ((ResultSet[]) data[i + extraArg])[0];
if (rs != null) {
if (rs instanceof JDBCResultSet) {
Result r = ((JDBCResultSet) rs).result;
if (head == null) {
callArguments[i] = r;
head = r;
} else {
head.addChainedResult(r);
}
} else {
throw Error.error(ErrorCode.X_46000,
"ResultSet not native");
}
}
}
}
public Result invokeJavaMethodDirect(Object[] data) {
Result result;
try {
Object returnValue = javaMethod.invoke(null, data);
returnValue = returnType.convertJavaToSQL(null, returnValue);
result = Result.newPSMResult(returnValue);
} catch (Throwable t) {
result = Result.newErrorResult(Error.error(t, ErrorCode.X_46000,
getName().name));
}
return result;
}
Result invokeJavaMethod(Session session, Object[] data) {
Result result;
HsqlName oldSessionSchema = session.getCurrentSchemaHsqlName();
try {
if (dataImpact == Routine.NO_SQL) {
session.sessionContext.isReadOnly = Boolean.TRUE;
session.setNoSQL();
} else if (dataImpact == Routine.CONTAINS_SQL) {
session.sessionContext.isReadOnly = Boolean.TRUE;
} else if (dataImpact == Routine.READS_SQL) {
session.sessionContext.isReadOnly = Boolean.TRUE;
}
session.setCurrentSchemaHsqlName(getSchemaName());
Object returnValue = javaMethod.invoke(null, data);
if (returnsTable()) {
if (returnValue instanceof JDBCResultSet) {
result = ((JDBCResultSet) returnValue).result;
} else {
// convert ResultSet to table
throw Error.runtimeError(ErrorCode.U_S0500,
"FunctionSQLInvoked");
}
} else {
returnValue = returnType.convertJavaToSQL(session,
returnValue);
result = Result.newPSMResult(returnValue);
}
} catch (InvocationTargetException e) {
result = Result.newErrorResult(Error.error(e, ErrorCode.X_46000,
getName().name));
} catch (IllegalAccessException e) {
result = Result.newErrorResult(Error.error(e, ErrorCode.X_46000,
getName().name));
} catch (Throwable e) {
result = Result.newErrorResult(Error.error(e, ErrorCode.X_46000,
getName().name));
}
session.setCurrentSchemaHsqlName(oldSessionSchema);
return result;
}
public Result invoke(Session session, Object[] data,
Object[] aggregateData, boolean push) {
Result result;
if (push) {
session.sessionContext.pushRoutineInvocation();
}
if (isPSM()) {
try {
session.sessionContext.routineArguments = data;
session.sessionContext.routineVariables =
ValuePool.emptyObjectArray;
if (variableCount > 0) {
session.sessionContext.routineVariables =
new Object[variableCount];
}
session.sessionContext.routineCursors = Result.emptyArray;
if (cursorCount > 0) {
session.sessionContext.routineCursors =
new Result[cursorCount];
}
result = statement.execute(session);
if (aggregateData != null) {
for (int i = 0; i < aggregateData.length; i++) {
aggregateData[i] = data[i + 1];
}
}
} catch (Throwable e) {
result = Result.newErrorResult(e);
}
} else {
if (isAggregate) {
data = convertArgsToJava(session, data);
}
result = invokeJavaMethod(session, data);
if (isAggregate) {
Object[] callResult = new Object[data.length];
convertArgsToSQL(session, callResult, data);
for (int i = 0; i < aggregateData.length; i++) {
aggregateData[i] = callResult[i + 1];
}
}
}
if (push) {
session.sessionContext.popRoutineInvocation();
}
return result;
}
public Routine duplicate() {
try {
return (Routine) super.clone();
} catch (CloneNotSupportedException e) {
throw Error.runtimeError(ErrorCode.U_S0500, "Type");
}
}
static Method getMethod(String name, Routine routine,
boolean[] hasConnection, boolean returnsTable) {
int i = name.indexOf(':');
if (i != -1) {
if (!name.substring(0, i).equals(SqlInvariants.CLASSPATH_NAME)) {
throw Error.error(ErrorCode.X_46102, name);
}
name = name.substring(i + 1);
}
Method[] methods = getMethods(name);
int firstMismatch = -1;
for (i = 0; i < methods.length; i++) {
int offset = 0;
hasConnection[0] = false;
Method method = methods[i];
Class[] params = method.getParameterTypes();
int matchedParamCount;
if (params.length > 0
&& params[0].equals(java.sql.Connection.class)) {
offset = 1;
hasConnection[0] = true;
}
matchedParamCount = params.length - offset;
if (routine.isProcedure()) {
for (int j = offset; j < params.length; j++) {
if (params[j].isArray()
&& java.sql.ResultSet.class.isAssignableFrom(
params[j].getComponentType())) {
matchedParamCount = j - offset;
break;
}
}
}
if (matchedParamCount != routine.parameterTypes.length) {
continue;
}
if (returnsTable) {
if (!java.sql.ResultSet.class.isAssignableFrom(
method.getReturnType())) {
continue;
}
} else {
Type methodReturnType =
Types.getParameterSQLType(method.getReturnType());
if (methodReturnType == null) {
continue;
}
if (!routine.returnType.canBeAssignedFrom(methodReturnType)) {
continue;
}
if (!methodReturnType.isLobType()
&& (methodReturnType.isBinaryType()
|| methodReturnType.isCharacterType())) {
//
} else {
int routineRetType = routine.returnType.typeCode;
if (routineRetType == Types.SQL_NUMERIC) {
routineRetType = Types.SQL_DECIMAL;
}
if (methodReturnType.typeCode != routineRetType) {
continue;
}
}
}
for (int j = 0; j < routine.parameterTypes.length; j++) {
boolean isInOut = false;
Class param = params[j + offset];
if (param.isArray()) {
if (!byte[].class.equals(param)) {
param = param.getComponentType();
if (param.isPrimitive()) {
method = null;
break;
}
isInOut = true;
}
}
Type methodParamType = Types.getParameterSQLType(param);
if (methodParamType == null) {
method = null;
break;
}
boolean result = routine.parameterTypes[j].typeComparisonGroup
== methodParamType.typeComparisonGroup;
// exact type for number
if (result && routine.parameterTypes[j].isNumberType()) {
int routineParamType = routine.parameterTypes[j].typeCode;
if (routineParamType == Types.SQL_NUMERIC) {
routineParamType = Types.SQL_DECIMAL;
}
result = routineParamType == methodParamType.typeCode;
}
if (isInOut
&& routine.getParameter(j).parameterMode
== SchemaObject.ParameterModes.PARAM_IN) {
result = false;
}
if (!result) {
method = null;
if (j + offset > firstMismatch) {
firstMismatch = j + offset;
}
break;
}
}
if (method != null) {
for (int j = 0; j < routine.parameterTypes.length; j++) {
routine.getParameter(j).setNullable(
!params[j + offset].isPrimitive());
}
return method;
}
}
if (firstMismatch >= 0) {
ColumnSchema param = routine.getParameter(firstMismatch);
throw Error.error(ErrorCode.X_46511, param.getNameString());
}
return null;
}
static Method[] getMethods(String name) {
int i = name.lastIndexOf('.');
if (i == -1) {
throw Error.error(ErrorCode.X_42501, name);
}
String className = name.substring(0, i);
String methodname = name.substring(i + 1);
Class cl;
Method[] methods = null;
if (!HsqlDatabaseProperties.supportsJavaMethod(name)) {
throw Error.error(ErrorCode.X_42501, className);
}
try {
cl = Class.forName(className, true,
Thread.currentThread().getContextClassLoader());
} catch (Throwable t1) {
try {
cl = Class.forName(className);
} catch (Throwable t) {
throw Error.error(t, ErrorCode.X_42501,
ErrorCode.M_Message_Pair, new String[] {
t.toString(), className
});
}
}
try {
methods = cl.getMethods();
} catch (Throwable t) {
throw Error.error(t, ErrorCode.X_42501, ErrorCode.M_Message_Pair,
new String[] {
t.toString(), className
});
}
HsqlArrayList list = new HsqlArrayList();
for (i = 0; i < methods.length; i++) {
int offset = 0;
int endIndex = Integer.MAX_VALUE;
Method method = methods[i];
int modifiers = method.getModifiers();
if (!method.getName().equals(methodname)
|| !Modifier.isStatic(modifiers)
|| !Modifier.isPublic(modifiers)) {
continue;
}
Class[] params = methods[i].getParameterTypes();
if (params.length > 0
&& params[0].equals(java.sql.Connection.class)) {
offset = 1;
}
for (int j = offset; j < params.length; j++) {
Class param = params[j];
if (param.isArray()) {
if (!byte[].class.equals(param)) {
param = param.getComponentType();
if (param.isPrimitive()) {
method = null;
break;
}
if (java.sql.ResultSet.class.isAssignableFrom(param)) {
if (endIndex > j) {
endIndex = j;
}
}
}
if (j >= endIndex) {
if (java.sql.ResultSet.class.isAssignableFrom(param)) {
continue;
} else {
method = null;
break;
}
}
} else {
if (j > endIndex) {
method = null;
break;
}
}
Type methodParamType = Types.getParameterSQLType(param);
if (methodParamType == null) {
method = null;
break;
}
}
if (method == null) {
continue;
}
if (java.sql.ResultSet.class.isAssignableFrom(
method.getReturnType())) {
list.add(methods[i]);
} else {
Type methodReturnType =
Types.getParameterSQLType(method.getReturnType());
if (methodReturnType != null) {
list.add(methods[i]);
}
}
}
methods = new Method[list.size()];
list.toArray(methods);
return methods;
}
public static Routine[] newRoutines(Session session, Method[] methods) {
Routine[] routines = new Routine[methods.length];
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
routines[i] = newRoutine(session, method);
}
return routines;
}
/**
* Returns a new function Routine object based solely on a Java Method object.
*/
public static Routine newRoutine(Session session, Method method) {
Routine routine = new Routine(SchemaObject.FUNCTION);
int offset = 0;
Class[] params = method.getParameterTypes();
String className = method.getDeclaringClass().getName();
StringBuilder sb = new StringBuilder();
sb.append("CLASSPATH:");
sb.append(method.getDeclaringClass().getName()).append('.');
sb.append(method.getName());
if (params.length > 0 && params[0].equals(java.sql.Connection.class)) {
offset = 1;
}
String name = sb.toString();
if (className.equals("java.lang.Math")) {
routine.isLibraryRoutine = true;
}
for (int j = offset; j < params.length; j++) {
Type methodParamType = Types.getParameterSQLType(params[j]);
HsqlName colName = session.database.nameManager.newHsqlName("C"
+ (j - offset + 1), false, SchemaObject.PARAMETER);
ColumnSchema param = new ColumnSchema(colName, methodParamType,
!params[j].isPrimitive(),
false, null);
routine.addParameter(param);
}
routine.setLanguage(Routine.LANGUAGE_JAVA);
routine.setMethod(method);
routine.setMethodURL(name);
routine.setDataImpact(Routine.NO_SQL);
Type methodReturnType =
Types.getParameterSQLType(method.getReturnType());
routine.javaMethodWithConnection = offset == 1;
routine.setReturnType(methodReturnType);
routine.resolve(session);
return routine;
}
/**
* simply named function only - requires return type
*/
public static void createRoutines(Session session, HsqlName schema,
String name) {
Method[] methods = Routine.getMethods(name);
Routine[] routines = Routine.newRoutines(session, methods);
HsqlName routineName = session.database.nameManager.newHsqlName(schema,
name, true, SchemaObject.FUNCTION);
for (int i = 0; i < routines.length; i++) {
if (routines[i].getReturnType() != Type.SQL_ALL_TYPES) {
routines[i].setName(routineName);
session.database.schemaManager.addSchemaObject(routines[i]);
}
}
}
}