/* Copyright (c) 2001-2010, 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 org.hsqldb.ParserDQL.CompileContext; import org.hsqldb.error.Error; import org.hsqldb.error.ErrorCode; import org.hsqldb.lib.ArraySort; import org.hsqldb.lib.OrderedHashSet; import org.hsqldb.result.Result; import org.hsqldb.result.ResultMetaData; import org.hsqldb.store.ValuePool; import org.hsqldb.types.Type; /** * Implementation of Statement for callable procedures.<p> * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 1.9.0 * @since 1.9.0 */ public class StatementProcedure extends StatementDMQL { /** Expression to evaluate */ Expression expression; /** Routine to execute */ Routine procedure; /** arguments to Routine */ Expression[] arguments = Expression.emptyArray; ResultMetaData resultMetaData; /** * Constructor for CALL statements for expressions. */ StatementProcedure(Session session, Expression expression, CompileContext compileContext) { super(StatementTypes.CALL, StatementTypes.X_SQL_DATA, session.getCurrentSchemaHsqlName()); this.expression = expression; setDatabseObjects(session, compileContext); checkAccessRights(session); if (procedure != null) { session.getGrantee().checkAccess(procedure); } } /** * Constructor for CALL statements for procedures. */ StatementProcedure(Session session, Routine procedure, Expression[] arguments, CompileContext compileContext) { super(StatementTypes.CALL, StatementTypes.X_SQL_DATA, session.getCurrentSchemaHsqlName()); this.procedure = procedure; this.arguments = arguments; setDatabseObjects(session, compileContext); checkAccessRights(session); } Result getResult(Session session) { return expression == null ? getProcedureResult(session) : getExpressionResult(session); } Result getProcedureResult(Session session) { Object[] data = ValuePool.emptyObjectArray; if (arguments.length > 0) { data = new Object[arguments.length]; } for (int i = 0; i < arguments.length; i++) { Expression e = arguments[i]; Object value = e.getValue(session); if (e != null) { Type targetType = procedure.getParameter(i).getDataType(); data[i] = targetType.convertToType(session, value, e.getDataType()); } } session.sessionContext.push(); session.sessionContext.routineArguments = data; session.sessionContext.routineVariables = ValuePool.emptyObjectArray; Result result = Result.updateZeroResult; if (procedure.isPSM()) { result = executePSMProcedure(session); } else { result = executeJavaProcedure(session); } Object[] callArguments = session.sessionContext.routineArguments; session.sessionContext.pop(); if (result.isError()) { return result; } if (result.isSimpleValue()) { result = Result.updateZeroResult; } boolean returnParams = false; for (int i = 0; i < procedure.getParameterCount(); i++) { ColumnSchema param = procedure.getParameter(i); int mode = param.getParameterMode(); if (mode != SchemaObject.ParameterModes.PARAM_IN) { if (this.arguments[i].isDynamicParam()) { int paramIndex = arguments[i].parameterIndex; session.sessionContext.dynamicArguments[paramIndex] = callArguments[i]; returnParams = true; } else { int varIndex = arguments[i].getColumnIndex(); session.sessionContext.routineVariables[varIndex] = callArguments[i]; } } } if (returnParams) { result = Result.newCallResponse( this.getParametersMetaData().getParameterTypes(), this.id, session.sessionContext.dynamicArguments); } return result; } Result executePSMProcedure(Session session) { int variableCount = procedure.getVariableCount(); if (variableCount > 0) { session.sessionContext.routineVariables = new Object[variableCount]; } Result result = procedure.statement.execute(session); if (!result.isError()) { result = Result.updateZeroResult; } return result; } Result executeJavaProcedure(Session session) { Result result = Result.updateZeroResult; int extraArg = procedure.javaMethodWithConnection ? 1 : 0; Object[] callArguments = session.sessionContext.routineArguments; Object[] data = new Object[callArguments.length + extraArg]; data = procedure.convertArgsToJava(session, callArguments); if (procedure.javaMethodWithConnection) { data[0] = session.getInternalConnection(); } result = procedure.invokeJavaMethod(session, data); procedure.convertArgsToSQL(session, callArguments, data); return result; } Result getExpressionResult(Session session) { Object o; // expression return value Result r; session.sessionData.startRowProcessing(); o = expression.getValue(session); if (o instanceof Result) { return (Result) o; } if (resultMetaData == null) { getResultMetaData(); } r = Result.newSingleColumnResult(resultMetaData); Object[] row; if (expression.getDataType().isArrayType()) { row = new Object[1]; row[0] = o; } else if (o instanceof Object[]) { row = (Object[]) o; } else { row = new Object[1]; row[0] = o; } r.getNavigator().add(row); return r; } SubQuery[] getSubqueries(Session session) { OrderedHashSet subQueries = null; if (expression != null) { subQueries = expression.collectAllSubqueries(subQueries); } for (int i = 0; i < arguments.length; i++) { subQueries = arguments[i].collectAllSubqueries(subQueries); } if (subQueries == null || subQueries.size() == 0) { return SubQuery.emptySubqueryArray; } SubQuery[] subQueryArray = new SubQuery[subQueries.size()]; subQueries.toArray(subQueryArray); ArraySort.sort(subQueryArray, 0, subQueryArray.length, subQueryArray[0]); for (int i = 0; i < subqueries.length; i++) { subQueryArray[i].prepareTable(session); } return subQueryArray; } public ResultMetaData getResultMetaData() { if (resultMetaData != null) { return resultMetaData; } switch (type) { case StatementTypes.CALL : { if (expression == null) { return ResultMetaData.emptyResultMetaData; } // TODO: // // 1.) standard to register metadata for columns of // the primary result set, if any, generated by call // // 2.) Represent the return value, if any (which is // not, in truth, a result set), as an OUT parameter // // For now, I've reverted a bunch of code I had in place // and instead simply reflect things as the are, describing // a single column result set that communicates // the return value. If the expression generating the // return value has a void return type, a result set // is described whose single column is of type NULL ResultMetaData md = ResultMetaData.newResultMetaData(1); ColumnBase column = new ColumnBase(null, null, null, StatementDMQL.RETURN_COLUMN_NAME); column.setType(expression.getDataType()); md.columns[0] = column; md.prepareData(); resultMetaData = md; return md; } default : throw Error.runtimeError(ErrorCode.U_S0500, "StatementProcedure"); } } /** * Returns the metadata for the placeholder parameters. */ public ResultMetaData getParametersMetaData() { /** @todo - change the auto-names to the names of params */ return super.getParametersMetaData(); } void collectTableNamesForRead(OrderedHashSet set) { if (expression == null) { set.addAll(procedure.getTableNamesForRead()); } else { for (int i = 0; i < subqueries.length; i++) { if (subqueries[i].queryExpression != null) { subqueries[i].queryExpression.getBaseTableNames(set); } } for (int i = 0; i < routines.length; i++) { set.addAll(routines[i].getTableNamesForRead()); } } } void collectTableNamesForWrite(OrderedHashSet set) { if (expression == null) { set.addAll(procedure.getTableNamesForWrite()); } } }