/* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/J is licensed under the terms of the GPLv2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception <http://www.mysql.com/about/legal/licensing/foss-exception.html>. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mysql.jdbc; import java.io.InputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.ParameterMetaData; import java.sql.Ref; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Representation of stored procedures for JDBC * * @author Mark Matthews * @version $Id: CallableStatement.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews * Exp $ */ public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement { protected final static Constructor<?> JDBC_4_CSTMT_2_ARGS_CTOR; protected final static Constructor<?> JDBC_4_CSTMT_4_ARGS_CTOR; static { if (Util.isJdbc4()) { try { JDBC_4_CSTMT_2_ARGS_CTOR = Class.forName( "com.mysql.jdbc.JDBC4CallableStatement") .getConstructor( new Class[] { MySQLConnection.class, CallableStatementParamInfo.class }); JDBC_4_CSTMT_4_ARGS_CTOR = Class.forName( "com.mysql.jdbc.JDBC4CallableStatement") .getConstructor( new Class[] { MySQLConnection.class, String.class, String.class, Boolean.TYPE }); } catch (SecurityException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } else { JDBC_4_CSTMT_4_ARGS_CTOR = null; JDBC_4_CSTMT_2_ARGS_CTOR = null; } } protected static class CallableStatementParam { int desiredJdbcType; int index; int inOutModifier; boolean isIn; boolean isOut; int jdbcType; short nullability; String paramName; int precision; int scale; String typeName; CallableStatementParam(String name, int idx, boolean in, boolean out, int jdbcType, String typeName, int precision, int scale, short nullability, int inOutModifier) { this.paramName = name; this.isIn = in; this.isOut = out; this.index = idx; this.jdbcType = jdbcType; this.typeName = typeName; this.precision = precision; this.scale = scale; this.nullability = nullability; this.inOutModifier = inOutModifier; } /* * (non-Javadoc) * * @see java.lang.Object#clone() */ protected Object clone() throws CloneNotSupportedException { return super.clone(); } } protected class CallableStatementParamInfo { String catalogInUse; boolean isFunctionCall; String nativeSql; int numParameters; List<CallableStatementParam> parameterList; Map<String, CallableStatementParam> parameterMap; /** * synchronized externally in checkReadOnlyProcedure() */ boolean isReadOnlySafeProcedure = false; /** * synchronized externally in checkReadOnlyProcedure() */ boolean isReadOnlySafeChecked = false; /** * Constructor that converts a full list of parameter metadata into one * that only represents the placeholders present in the {CALL ()}. * * @param fullParamInfo the metadata for all parameters for this stored * procedure or function. */ CallableStatementParamInfo(CallableStatementParamInfo fullParamInfo) { this.nativeSql = originalSql; this.catalogInUse = currentCatalog; isFunctionCall = fullParamInfo.isFunctionCall; @SuppressWarnings("synthetic-access") int[] localParameterMap = placeholderToParameterIndexMap; int parameterMapLength = localParameterMap.length; this.isReadOnlySafeProcedure = fullParamInfo.isReadOnlySafeProcedure; this.isReadOnlySafeChecked = fullParamInfo.isReadOnlySafeChecked; parameterList = new ArrayList<CallableStatementParam>(fullParamInfo.numParameters); parameterMap = new HashMap<String, CallableStatementParam>(fullParamInfo.numParameters); if (isFunctionCall) { // Take the return value parameterList.add(fullParamInfo.parameterList.get(0)); } int offset = isFunctionCall ? 1 : 0; for (int i = 0; i < parameterMapLength; i++) { if (localParameterMap[i] != 0) { CallableStatementParam param = fullParamInfo.parameterList.get(localParameterMap[i] + offset); parameterList.add(param); parameterMap.put(param.paramName, param); } } this.numParameters = parameterList.size(); } @SuppressWarnings("synthetic-access") CallableStatementParamInfo(java.sql.ResultSet paramTypesRs) throws SQLException { boolean hadRows = paramTypesRs.last(); this.nativeSql = originalSql; this.catalogInUse = currentCatalog; isFunctionCall = callingStoredFunction; if (hadRows) { this.numParameters = paramTypesRs.getRow(); this.parameterList = new ArrayList<CallableStatementParam>(this.numParameters); this.parameterMap = new HashMap<String, CallableStatementParam>(this.numParameters); paramTypesRs.beforeFirst(); addParametersFromDBMD(paramTypesRs); } else { this.numParameters = 0; } if (isFunctionCall) { this.numParameters += 1; } } private void addParametersFromDBMD(java.sql.ResultSet paramTypesRs) throws SQLException { int i = 0; while (paramTypesRs.next()) { String paramName = paramTypesRs.getString(4); int inOutModifier = paramTypesRs.getInt(5); boolean isOutParameter = false; boolean isInParameter = false; if (i == 0 && isFunctionCall) { isOutParameter = true; isInParameter = false; } else if (inOutModifier == DatabaseMetaData.procedureColumnInOut) { isOutParameter = true; isInParameter = true; } else if (inOutModifier == DatabaseMetaData.procedureColumnIn) { isOutParameter = false; isInParameter = true; } else if (inOutModifier == DatabaseMetaData.procedureColumnOut) { isOutParameter = true; isInParameter = false; } int jdbcType = paramTypesRs.getInt(6); String typeName = paramTypesRs.getString(7); int precision = paramTypesRs.getInt(8); int scale = paramTypesRs.getInt(10); short nullability = paramTypesRs.getShort(12); CallableStatementParam paramInfoToAdd = new CallableStatementParam( paramName, i++, isInParameter, isOutParameter, jdbcType, typeName, precision, scale, nullability, inOutModifier); this.parameterList.add(paramInfoToAdd); this.parameterMap.put(paramName, paramInfoToAdd); } } protected void checkBounds(int paramIndex) throws SQLException { int localParamIndex = paramIndex - 1; if ((paramIndex < 0) || (localParamIndex >= this.numParameters)) { throw SQLError.createSQLException( Messages.getString("CallableStatement.11") + paramIndex //$NON-NLS-1$ + Messages.getString("CallableStatement.12") + numParameters //$NON-NLS-1$ + Messages.getString("CallableStatement.13"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ } } /* * (non-Javadoc) * * @see java.lang.Object#clone() */ protected Object clone() throws CloneNotSupportedException { return super.clone(); } CallableStatementParam getParameter(int index) { return this.parameterList.get(index); } CallableStatementParam getParameter(String name) { return this.parameterMap.get(name); } public String getParameterClassName(int arg0) throws SQLException { String mysqlTypeName = getParameterTypeName(arg0); boolean isBinaryOrBlob = StringUtils.indexOfIgnoreCase(mysqlTypeName, "BLOB") != -1 || StringUtils.indexOfIgnoreCase(mysqlTypeName, "BINARY") != -1; boolean isUnsigned = StringUtils.indexOfIgnoreCase(mysqlTypeName, "UNSIGNED") != -1; int mysqlTypeIfKnown = 0; if (StringUtils.startsWithIgnoreCase(mysqlTypeName, "MEDIUMINT")) { mysqlTypeIfKnown = MysqlDefs.FIELD_TYPE_INT24; } return ResultSetMetaData.getClassNameForJavaType(getParameterType(arg0), isUnsigned, mysqlTypeIfKnown, isBinaryOrBlob, false); } public int getParameterCount() throws SQLException { if (this.parameterList == null) { return 0; } return this.parameterList.size(); } public int getParameterMode(int arg0) throws SQLException { checkBounds(arg0); return getParameter(arg0 - 1).inOutModifier; } public int getParameterType(int arg0) throws SQLException { checkBounds(arg0); return getParameter(arg0 - 1).jdbcType; } public String getParameterTypeName(int arg0) throws SQLException { checkBounds(arg0); return getParameter(arg0 - 1).typeName; } public int getPrecision(int arg0) throws SQLException { checkBounds(arg0); return getParameter(arg0 - 1).precision; } public int getScale(int arg0) throws SQLException { checkBounds(arg0); return getParameter(arg0 - 1).scale; } public int isNullable(int arg0) throws SQLException { checkBounds(arg0); return getParameter(arg0 - 1).nullability; } public boolean isSigned(int arg0) throws SQLException { checkBounds(arg0); return false; } Iterator<CallableStatementParam> iterator() { return this.parameterList.iterator(); } int numberOfParameters() { return this.numParameters; } } /** * Can't implement this directly, as then you can't use callable statements * on JDK-1.3.1, which unfortunately isn't EOL'd yet, and still present * quite a bit out there in the wild (Websphere, FreeBSD, anyone?) */ protected class CallableStatementParamInfoJDBC3 extends CallableStatementParamInfo implements ParameterMetaData { CallableStatementParamInfoJDBC3(java.sql.ResultSet paramTypesRs) throws SQLException { super(paramTypesRs); } public CallableStatementParamInfoJDBC3(CallableStatementParamInfo paramInfo) { super(paramInfo); } /** * Returns true if this either implements the interface argument or is directly or indirectly a wrapper * for an object that does. Returns false otherwise. If this implements the interface then return true, * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped * object. If this does not implement the interface and is not a wrapper, return false. * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method * returns true then calling <code>unwrap</code> with the same argument should succeed. * * @param interfaces a Class defining an interface. * @return true if this implements the interface or directly or indirectly wraps an object that does. * @throws java.sql.SQLException if an error occurs while determining whether this is a wrapper * for an object with the given interface. * @since 1.6 */ public boolean isWrapperFor(Class<?> iface) throws SQLException { checkClosed(); // This works for classes that aren't actually wrapping // anything return iface.isInstance(this); } /** * Returns an object that implements the given interface to allow access to non-standard methods, * or standard methods not exposed by the proxy. * The result may be either the object found to implement the interface or a proxy for that object. * If the receiver implements the interface then that is the object. If the receiver is a wrapper * and the wrapped object implements the interface then that is the object. Otherwise the object is * the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown. * * @param iface A Class defining an interface that the result must implement. * @return an object that implements the interface. May be a proxy for the actual implementing object. * @throws java.sql.SQLException If no object found that implements the interface * @since 1.6 */ public Object unwrap(Class<?> iface) throws java.sql.SQLException { try { // This works for classes that aren't actually wrapping // anything return Util.cast(iface, this); } catch (ClassCastException cce) { throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } } } private final static int NOT_OUTPUT_PARAMETER_INDICATOR = Integer.MIN_VALUE; private final static String PARAMETER_NAMESPACE_PREFIX = "@com_mysql_jdbc_outparam_"; //$NON-NLS-1$ private static String mangleParameterName(String origParameterName) { //Fixed for 5.5+ in callers if (origParameterName == null) { return null; } int offset = 0; if (origParameterName.length() > 0 && origParameterName.charAt(0) == '@') { offset = 1; } StringBuffer paramNameBuf = new StringBuffer(PARAMETER_NAMESPACE_PREFIX .length() + origParameterName.length()); paramNameBuf.append(PARAMETER_NAMESPACE_PREFIX); paramNameBuf.append(origParameterName.substring(offset)); return paramNameBuf.toString(); } private boolean callingStoredFunction = false; private ResultSetInternalMethods functionReturnValueResults; private boolean hasOutputParams = false; // private List parameterList; // private Map parameterMap; private ResultSetInternalMethods outputParameterResults; protected boolean outputParamWasNull = false; private int[] parameterIndexToRsIndex; protected CallableStatementParamInfo paramInfo; private CallableStatementParam returnValueParam; /** * Creates a new CallableStatement * * @param conn * the connection creating this statement * @param paramInfo * the SQL to prepare * * @throws SQLException * if an error occurs */ public CallableStatement(MySQLConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { super(conn, paramInfo.nativeSql, paramInfo.catalogInUse); this.paramInfo = paramInfo; this.callingStoredFunction = this.paramInfo.isFunctionCall; if (this.callingStoredFunction) { this.parameterCount += 1; } this.retrieveGeneratedKeys = true; // not provided for in the JDBC spec } /** * Creates a callable statement instance -- We need to provide factory-style methods * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise * the class verifier complains when it tries to load JDBC4-only interface * classes that are present in JDBC4 method signatures. */ protected static CallableStatement getInstance(MySQLConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { if (!Util.isJdbc4()) { return new CallableStatement(conn, sql, catalog, isFunctionCall); } return (CallableStatement) Util.handleNewInstance( JDBC_4_CSTMT_4_ARGS_CTOR, new Object[] { conn, sql, catalog, Boolean.valueOf(isFunctionCall) }, conn.getExceptionInterceptor()); } /** * Creates a callable statement instance -- We need to provide factory-style methods * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise * the class verifier complains when it tries to load JDBC4-only interface * classes that are present in JDBC4 method signatures. */ protected static CallableStatement getInstance(MySQLConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { if (!Util.isJdbc4()) { return new CallableStatement(conn, paramInfo); } return (CallableStatement) Util.handleNewInstance( JDBC_4_CSTMT_2_ARGS_CTOR, new Object[] { conn, paramInfo }, conn.getExceptionInterceptor()); } private int[] placeholderToParameterIndexMap; private void generateParameterMap() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.paramInfo == null) { return; } // if the user specified some parameters as literals, we need to // provide a map from the specified placeholders to the actual // parameter numbers int parameterCountFromMetaData = this.paramInfo.getParameterCount(); // Ignore the first ? if this is a stored function, it doesn't count if (this.callingStoredFunction) { parameterCountFromMetaData--; } if (this.paramInfo != null && this.parameterCount != parameterCountFromMetaData) { this.placeholderToParameterIndexMap = new int[this.parameterCount]; int startPos = this.callingStoredFunction ? StringUtils.indexOfIgnoreCase(this.originalSql, "SELECT") : StringUtils.indexOfIgnoreCase(this.originalSql, "CALL"); if (startPos != -1) { int parenOpenPos = this.originalSql.indexOf('(', startPos + 4); if (parenOpenPos != -1) { int parenClosePos = StringUtils.indexOfIgnoreCaseRespectQuotes(parenOpenPos, this.originalSql, ")", '\'', true); if (parenClosePos != -1) { List<?> parsedParameters = StringUtils.split(this.originalSql.substring(parenOpenPos + 1, parenClosePos), ",", "'\"", "'\"", true); int numParsedParameters = parsedParameters.size(); // sanity check if (numParsedParameters != this.parameterCount) { // bail? } int placeholderCount = 0; for (int i = 0; i < numParsedParameters; i++) { if (((String)parsedParameters.get(i)).equals("?")) { this.placeholderToParameterIndexMap[placeholderCount++] = i; } } } } } } } } /** * Creates a new CallableStatement * * @param conn * the connection creating this statement * @param sql * the SQL to prepare * @param catalog * the current catalog * * @throws SQLException * if an error occurs */ public CallableStatement(MySQLConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { super(conn, sql, catalog); this.callingStoredFunction = isFunctionCall; if (!this.callingStoredFunction) { if (!StringUtils.startsWithIgnoreCaseAndWs(sql, "CALL")) { // not really a stored procedure call fakeParameterTypes(false); } else { determineParameterTypes(); } generateParameterMap(); } else { determineParameterTypes(); generateParameterMap(); this.parameterCount += 1; } this.retrieveGeneratedKeys = true; // not provided for in the JDBC spec } /* * (non-Javadoc) * * @see java.sql.PreparedStatement#addBatch() */ public void addBatch() throws SQLException { setOutParams(); super.addBatch(); } private CallableStatementParam checkIsOutputParam(int paramIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.callingStoredFunction) { if (paramIndex == 1) { if (this.returnValueParam == null) { this.returnValueParam = new CallableStatementParam("", 0, false, true, Types.VARCHAR, "VARCHAR", 0, 0, DatabaseMetaData.attributeNullableUnknown, DatabaseMetaData.procedureColumnReturn); } return this.returnValueParam; } // Move to position in output result set paramIndex--; } checkParameterIndexBounds(paramIndex); int localParamIndex = paramIndex - 1; if (this.placeholderToParameterIndexMap != null) { localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; } CallableStatementParam paramDescriptor = this.paramInfo .getParameter(localParamIndex); // We don't have reliable metadata in this case, trust // the caller if (this.connection.getNoAccessToProcedureBodies()) { paramDescriptor.isOut = true; paramDescriptor.isIn = true; paramDescriptor.inOutModifier = DatabaseMetaData.procedureColumnInOut; } else if (!paramDescriptor.isOut) { throw SQLError.createSQLException( Messages.getString("CallableStatement.9") + paramIndex //$NON-NLS-1$ + Messages.getString("CallableStatement.10"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } this.hasOutputParams = true; return paramDescriptor; } } /** * DOCUMENT ME! * * @param paramIndex * * @throws SQLException */ private void checkParameterIndexBounds(int paramIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { this.paramInfo.checkBounds(paramIndex); } } /** * Checks whether or not this statement is supposed to be providing * streamable result sets...If output parameters are registered, the driver * can not stream the results. * * @throws SQLException * DOCUMENT ME! */ private void checkStreamability() throws SQLException { if (this.hasOutputParams && createStreamingResultSet()) { throw SQLError.createSQLException(Messages.getString("CallableStatement.14"), //$NON-NLS-1$ SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); } } public void clearParameters() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { super.clearParameters(); try { if (this.outputParameterResults != null) { this.outputParameterResults.close(); } } finally { this.outputParameterResults = null; } } } /** * Used to fake up some metadata when we don't have access to * SHOW CREATE PROCEDURE or mysql.proc. * * @throws SQLException if we can't build the metadata. */ private void fakeParameterTypes(boolean isReallyProcedure) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { Field[] fields = new Field[13]; fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0); fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0); fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0); fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 0); fields[4] = new Field("", "COLUMN_TYPE", Types.CHAR, 0); fields[5] = new Field("", "DATA_TYPE", Types.SMALLINT, 0); fields[6] = new Field("", "TYPE_NAME", Types.CHAR, 0); fields[7] = new Field("", "PRECISION", Types.INTEGER, 0); fields[8] = new Field("", "LENGTH", Types.INTEGER, 0); fields[9] = new Field("", "SCALE", Types.SMALLINT, 0); fields[10] = new Field("", "RADIX", Types.SMALLINT, 0); fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0); fields[12] = new Field("", "REMARKS", Types.CHAR, 0); String procName = isReallyProcedure ? extractProcedureName() : null; byte[] procNameAsBytes = null; try { procNameAsBytes = procName == null ? null : StringUtils.getBytes(procName, "UTF-8"); } catch (UnsupportedEncodingException ueEx) { procNameAsBytes = StringUtils.s2b(procName, this.connection); } ArrayList<ResultSetRow> resultRows = new ArrayList<ResultSetRow>(); for (int i = 0; i < this.parameterCount; i++) { byte[][] row = new byte[13][]; row[0] = null; // PROCEDURE_CAT row[1] = null; // PROCEDURE_SCHEM row[2] = procNameAsBytes; // PROCEDURE/NAME row[3] = StringUtils.s2b(String.valueOf(i), this.connection); // COLUMN_NAME row[4] = StringUtils.s2b(String .valueOf(DatabaseMetaData.procedureColumnIn), this.connection); row[5] = StringUtils.s2b(String.valueOf(Types.VARCHAR), this.connection); // DATA_TYPE row[6] = StringUtils.s2b("VARCHAR", this.connection); // TYPE_NAME row[7] = StringUtils.s2b(Integer.toString(65535), this.connection); // PRECISION row[8] = StringUtils.s2b(Integer.toString(65535), this.connection); // LENGTH row[9] = StringUtils.s2b(Integer.toString(0), this.connection); // SCALE row[10] = StringUtils.s2b(Integer.toString(10), this.connection); // RADIX row[11] = StringUtils.s2b(Integer .toString(DatabaseMetaData.procedureNullableUnknown), this.connection); // nullable row[12] = null; resultRows.add(new ByteArrayRow(row, getExceptionInterceptor())); } java.sql.ResultSet paramTypesRs = DatabaseMetaData.buildResultSet( fields, resultRows, this.connection); convertGetProcedureColumnsToInternalDescriptors(paramTypesRs); } } private void determineParameterTypes() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { java.sql.ResultSet paramTypesRs = null; try { //Bug#57022, we need to check for db.SPname notation first // and pass on only SPname String procName = extractProcedureName(); String quotedId = ""; try { quotedId = this.connection.supportsQuotedIdentifiers() ? this.connection.getMetaData().getIdentifierQuoteString() : ""; } catch (SQLException sqlEx) { // Forced by API, never thrown from getIdentifierQuoteString() in // this implementation. AssertionFailedException.shouldNotHappen(sqlEx); } List<?> parseList = StringUtils.splitDBdotName(procName, "", quotedId , this.connection.isNoBackslashEscapesSet()); String tmpCatalog = ""; //There *should* be 2 rows, if any. if (parseList.size() == 2) { tmpCatalog = (String) parseList.get(0); procName = (String) parseList.get(1); } else { //keep values as they are } java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); boolean useCatalog = false; if (tmpCatalog.length() <= 0) { useCatalog = true; } paramTypesRs = dbmd.getProcedureColumns(this.connection .versionMeetsMinimum(5, 0, 2) && useCatalog ? this.currentCatalog : tmpCatalog/*null*/, null, procName, "%"); //$NON-NLS-1$ boolean hasResults = false; try { if (paramTypesRs.next()) { paramTypesRs.previous(); hasResults = true; } } catch (Exception e) { // paramTypesRs is empty, proceed with fake params. swallow, was expected } if (hasResults){ convertGetProcedureColumnsToInternalDescriptors(paramTypesRs); } else { fakeParameterTypes(true); } } finally { SQLException sqlExRethrow = null; if (paramTypesRs != null) { try { paramTypesRs.close(); } catch (SQLException sqlEx) { sqlExRethrow = sqlEx; } paramTypesRs = null; } if (sqlExRethrow != null) { throw sqlExRethrow; } } } } private void convertGetProcedureColumnsToInternalDescriptors(java.sql.ResultSet paramTypesRs) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.connection.isRunningOnJDK13()) { this.paramInfo = new CallableStatementParamInfoJDBC3( paramTypesRs); } else { this.paramInfo = new CallableStatementParamInfo(paramTypesRs); } } } /* * (non-Javadoc) * * @see java.sql.PreparedStatement#execute() */ public boolean execute() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { boolean returnVal = false; checkStreamability(); setInOutParamsOnServer(); setOutParams(); returnVal = super.execute(); if (this.callingStoredFunction) { this.functionReturnValueResults = this.results; this.functionReturnValueResults.next(); this.results = null; } retrieveOutParams(); if (!this.callingStoredFunction) { return returnVal; } // Functions can't return results return false; } } /* * (non-Javadoc) * * @see java.sql.PreparedStatement#executeQuery() */ public java.sql.ResultSet executeQuery() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { checkStreamability(); java.sql.ResultSet execResults = null; setInOutParamsOnServer(); setOutParams(); execResults = super.executeQuery(); retrieveOutParams(); return execResults; } } /* * (non-Javadoc) * * @see java.sql.PreparedStatement#executeUpdate() */ public int executeUpdate() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { int returnVal = -1; checkStreamability(); if (this.callingStoredFunction) { execute(); return -1; } setInOutParamsOnServer(); setOutParams(); returnVal = super.executeUpdate(); retrieveOutParams(); return returnVal; } } private String extractProcedureName() throws SQLException { String sanitizedSql = StringUtils.stripComments(this.originalSql, "`\"'", "`\"'", true, false, true, true); // TODO: Do this with less memory allocation int endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql, "CALL "); //$NON-NLS-1$ int offset = 5; if (endCallIndex == -1) { endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql, "SELECT "); offset = 7; } if (endCallIndex != -1) { StringBuffer nameBuf = new StringBuffer(); String trimmedStatement = sanitizedSql.substring( endCallIndex + offset).trim(); int statementLength = trimmedStatement.length(); for (int i = 0; i < statementLength; i++) { char c = trimmedStatement.charAt(i); if (Character.isWhitespace(c) || (c == '(') || (c == '?')) { break; } nameBuf.append(c); } return nameBuf.toString(); } throw SQLError.createSQLException(Messages.getString("CallableStatement.1"), //$NON-NLS-1$ SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } /** * Adds 'at' symbol to beginning of parameter names if needed. * * @param paramNameIn * the parameter name to 'fix' * * @return the parameter name with an 'a' prepended, if needed * * @throws SQLException * if the parameter name is null or empty. */ protected String fixParameterName(String paramNameIn) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { //Fixed for 5.5+ if (((paramNameIn == null) || (paramNameIn.length() == 0)) && (!hasParametersView())) { throw SQLError.createSQLException( ((Messages.getString("CallableStatement.0") + paramNameIn) == null) //$NON-NLS-1$ ? Messages.getString("CallableStatement.15") : Messages.getString("CallableStatement.16"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$ } if ((paramNameIn == null) && (hasParametersView())) { paramNameIn = "nullpn"; }; if (this.connection.getNoAccessToProcedureBodies()) { throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } return mangleParameterName(paramNameIn); } } /** * @see java.sql.CallableStatement#getArray(int) */ public Array getArray(int i) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(i); Array retValue = rs.getArray(mapOutputParameterIndexToRsIndex(i)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getArray(java.lang.String) */ public Array getArray(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Array retValue = rs.getArray(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBigDecimal(int) */ public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); BigDecimal retValue = rs .getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * DOCUMENT ME! * * @param parameterIndex * DOCUMENT ME! * @param scale * DOCUMENT ME! * * @return DOCUMENT ME! * * @throws SQLException * DOCUMENT ME! * * @see java.sql.CallableStatement#getBigDecimal(int, int) * @deprecated */ public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); BigDecimal retValue = rs.getBigDecimal( mapOutputParameterIndexToRsIndex(parameterIndex), scale); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBigDecimal(java.lang.String) */ public BigDecimal getBigDecimal(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= BigDecimal retValue = rs.getBigDecimal(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBlob(int) */ public Blob getBlob(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Blob retValue = rs .getBlob(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBlob(java.lang.String) */ public Blob getBlob(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Blob retValue = rs.getBlob(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBoolean(int) */ public boolean getBoolean(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); boolean retValue = rs .getBoolean(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBoolean(java.lang.String) */ public boolean getBoolean(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= boolean retValue = rs.getBoolean(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getByte(int) */ public byte getByte(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); byte retValue = rs .getByte(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getByte(java.lang.String) */ public byte getByte(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= byte retValue = rs.getByte(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBytes(int) */ public byte[] getBytes(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); byte[] retValue = rs .getBytes(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getBytes(java.lang.String) */ public byte[] getBytes(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= byte[] retValue = rs.getBytes(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getClob(int) */ public Clob getClob(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Clob retValue = rs .getClob(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getClob(java.lang.String) */ public Clob getClob(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Clob retValue = rs.getClob(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getDate(int) */ public Date getDate(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Date retValue = rs .getDate(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getDate(int, java.util.Calendar) */ public Date getDate(int parameterIndex, Calendar cal) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Date retValue = rs.getDate( mapOutputParameterIndexToRsIndex(parameterIndex), cal); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getDate(java.lang.String) */ public Date getDate(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Date retValue = rs.getDate(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getDate(java.lang.String, * java.util.Calendar) */ public Date getDate(String parameterName, Calendar cal) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Date retValue = rs.getDate(fixParameterName(parameterName), cal); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getDouble(int) */ public double getDouble(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); double retValue = rs .getDouble(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getDouble(java.lang.String) */ public double getDouble(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= double retValue = rs.getDouble(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getFloat(int) */ public float getFloat(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); float retValue = rs .getFloat(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getFloat(java.lang.String) */ public float getFloat(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= float retValue = rs.getFloat(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getInt(int) */ public int getInt(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); int retValue = rs .getInt(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getInt(java.lang.String) */ public int getInt(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= int retValue = rs.getInt(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getLong(int) */ public long getLong(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); long retValue = rs .getLong(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getLong(java.lang.String) */ public long getLong(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= long retValue = rs.getLong(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } protected int getNamedParamIndex(String paramName, boolean forOut) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.connection.getNoAccessToProcedureBodies()) { throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } //Fixed for 5.5+ in callers if ((paramName == null) || (paramName.length() == 0)) { throw SQLError.createSQLException(Messages.getString("CallableStatement.2"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } if (this.paramInfo == null) { throw SQLError.createSQLException( Messages.getString("CallableStatement.3") + paramName + Messages.getString("CallableStatement.4"), //$NON-NLS-1$ //$NON-NLS-2$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } CallableStatementParam namedParamInfo = this.paramInfo .getParameter(paramName); if (forOut && !namedParamInfo.isOut) { throw SQLError.createSQLException( Messages.getString("CallableStatement.5") + paramName //$NON-NLS-1$ + Messages.getString("CallableStatement.6"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } if (this.placeholderToParameterIndexMap == null) { return namedParamInfo.index + 1; // JDBC indices are 1-based } for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { if (this.placeholderToParameterIndexMap[i] == namedParamInfo.index) { return i + 1; } } throw SQLError.createSQLException("Can't find local placeholder mapping for parameter named \"" + paramName + "\".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } } /** * @see java.sql.CallableStatement#getObject(int) */ public Object getObject(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex); ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Object retVal = rs.getObjectStoredProc( mapOutputParameterIndexToRsIndex(parameterIndex), paramDescriptor.desiredJdbcType); this.outputParamWasNull = rs.wasNull(); return retVal; } } /** * @see java.sql.CallableStatement#getObject(int, java.util.Map) */ public Object getObject(int parameterIndex, Map<String, Class<?>> map) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Object retVal = rs.getObject( mapOutputParameterIndexToRsIndex(parameterIndex), map); this.outputParamWasNull = rs.wasNull(); return retVal; } } /** * @see java.sql.CallableStatement#getObject(java.lang.String) */ public Object getObject(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Object retValue = rs.getObject(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getObject(java.lang.String, * java.util.Map) */ public Object getObject(String parameterName, Map<String, Class<?>> map) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Object retValue = rs.getObject(fixParameterName(parameterName), map); this.outputParamWasNull = rs.wasNull(); return retValue; } } // JDBC-4.1 public <T> T getObject(int parameterIndex, Class<T> type) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); // remove cast once 1.5, 1.6 EOL'd T retVal = ((ResultSetImpl)rs).getObject( mapOutputParameterIndexToRsIndex(parameterIndex), type); this.outputParamWasNull = rs.wasNull(); return retVal; } } public <T> T getObject(String parameterName, Class<T> type) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= T retValue = ((ResultSetImpl)rs).getObject(fixParameterName(parameterName), type); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * Returns the ResultSet that holds the output parameters, or throws an * appropriate exception if none exist, or they weren't returned. * * @return the ResultSet that holds the output parameters * * @throws SQLException * if no output parameters were defined, or if no output * parameters were returned. */ protected ResultSetInternalMethods getOutputParameters(int paramIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { this.outputParamWasNull = false; if (paramIndex == 1 && this.callingStoredFunction && this.returnValueParam != null) { return this.functionReturnValueResults; } if (this.outputParameterResults == null) { if (this.paramInfo.numberOfParameters() == 0) { throw SQLError.createSQLException(Messages .getString("CallableStatement.7"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } throw SQLError.createSQLException(Messages.getString("CallableStatement.8"), //$NON-NLS-1$ SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } return this.outputParameterResults; } } public ParameterMetaData getParameterMetaData() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.placeholderToParameterIndexMap == null) { return (CallableStatementParamInfoJDBC3) this.paramInfo; } return new CallableStatementParamInfoJDBC3(this.paramInfo); } } /** * @see java.sql.CallableStatement#getRef(int) */ public Ref getRef(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Ref retValue = rs .getRef(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getRef(java.lang.String) */ public Ref getRef(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Ref retValue = rs.getRef(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getShort(int) */ public short getShort(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); short retValue = rs .getShort(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getShort(java.lang.String) */ public short getShort(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= short retValue = rs.getShort(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getString(int) */ public String getString(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); String retValue = rs .getString(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getString(java.lang.String) */ public String getString(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= String retValue = rs.getString(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTime(int) */ public Time getTime(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Time retValue = rs .getTime(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTime(int, java.util.Calendar) */ public Time getTime(int parameterIndex, Calendar cal) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Time retValue = rs.getTime( mapOutputParameterIndexToRsIndex(parameterIndex), cal); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTime(java.lang.String) */ public Time getTime(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Time retValue = rs.getTime(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTime(java.lang.String, * java.util.Calendar) */ public Time getTime(String parameterName, Calendar cal) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Time retValue = rs.getTime(fixParameterName(parameterName), cal); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTimestamp(int) */ public Timestamp getTimestamp(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Timestamp retValue = rs .getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar) */ public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Timestamp retValue = rs.getTimestamp( mapOutputParameterIndexToRsIndex(parameterIndex), cal); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTimestamp(java.lang.String) */ public Timestamp getTimestamp(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getTimestamp(java.lang.String, * java.util.Calendar) */ public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName), cal); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getURL(int) */ public URL getURL(int parameterIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); URL retValue = rs .getURL(mapOutputParameterIndexToRsIndex(parameterIndex)); this.outputParamWasNull = rs.wasNull(); return retValue; } } /** * @see java.sql.CallableStatement#getURL(java.lang.String) */ public URL getURL(String parameterName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be // from ?= URL retValue = rs.getURL(fixParameterName(parameterName)); this.outputParamWasNull = rs.wasNull(); return retValue; } } protected int mapOutputParameterIndexToRsIndex(int paramIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.returnValueParam != null && paramIndex == 1) { return 1; } checkParameterIndexBounds(paramIndex); int localParamIndex = paramIndex - 1; if (this.placeholderToParameterIndexMap != null) { localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; } int rsIndex = this.parameterIndexToRsIndex[localParamIndex]; if (rsIndex == NOT_OUTPUT_PARAMETER_INDICATOR) { throw SQLError.createSQLException( Messages.getString("CallableStatement.21") + paramIndex //$NON-NLS-1$ + Messages.getString("CallableStatement.22"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } return rsIndex + 1; } } /** * @see java.sql.CallableStatement#registerOutParameter(int, int) */ public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex); paramDescriptor.desiredJdbcType = sqlType; } /** * @see java.sql.CallableStatement#registerOutParameter(int, int, int) */ public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { registerOutParameter(parameterIndex, sqlType); } /** * @see java.sql.CallableStatement#registerOutParameter(int, int, * java.lang.String) */ public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { checkIsOutputParam(parameterIndex); } /** * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, * int) */ public void registerOutParameter(String parameterName, int sqlType) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { registerOutParameter(getNamedParamIndex(parameterName, true), sqlType); } } /** * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, * int, int) */ public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { registerOutParameter(getNamedParamIndex(parameterName, true), sqlType); } /** * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, * int, java.lang.String) */ public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { registerOutParameter(getNamedParamIndex(parameterName, true), sqlType, typeName); } /** * Issues a second query to retrieve all output parameters. * * @throws SQLException * if an error occurs. */ private void retrieveOutParams() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { int numParameters = this.paramInfo.numberOfParameters(); this.parameterIndexToRsIndex = new int[numParameters]; for (int i = 0; i < numParameters; i++) { this.parameterIndexToRsIndex[i] = NOT_OUTPUT_PARAMETER_INDICATOR; } int localParamIndex = 0; if (numParameters > 0) { StringBuffer outParameterQuery = new StringBuffer("SELECT "); //$NON-NLS-1$ boolean firstParam = true; boolean hadOutputParams = false; for (Iterator<CallableStatementParam> paramIter = this.paramInfo.iterator(); paramIter .hasNext();) { CallableStatementParam retrParamInfo = paramIter .next(); if (retrParamInfo.isOut) { hadOutputParams = true; this.parameterIndexToRsIndex[retrParamInfo.index] = localParamIndex++; if ((retrParamInfo.paramName == null) && (hasParametersView())) { retrParamInfo.paramName = "nullnp" + retrParamInfo.index; } String outParameterName = mangleParameterName(retrParamInfo.paramName); if (!firstParam) { outParameterQuery.append(","); //$NON-NLS-1$ } else { firstParam = false; } if (!outParameterName.startsWith("@")) { //$NON-NLS-1$ outParameterQuery.append('@'); } outParameterQuery.append(outParameterName); } } if (hadOutputParams) { // We can't use 'ourself' to execute this query, or any // pending result sets would be overwritten java.sql.Statement outParameterStmt = null; java.sql.ResultSet outParamRs = null; try { outParameterStmt = this.connection.createStatement(); outParamRs = outParameterStmt .executeQuery(outParameterQuery.toString()); this.outputParameterResults = ((com.mysql.jdbc.ResultSetInternalMethods) outParamRs) .copy(); if (!this.outputParameterResults.next()) { this.outputParameterResults.close(); this.outputParameterResults = null; } } finally { if (outParameterStmt != null) { outParameterStmt.close(); } } } else { this.outputParameterResults = null; } } else { this.outputParameterResults = null; } } } /** * @see java.sql.CallableStatement#setAsciiStream(java.lang.String, * java.io.InputStream, int) */ public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { setAsciiStream(getNamedParamIndex(parameterName, false), x, length); } /** * @see java.sql.CallableStatement#setBigDecimal(java.lang.String, * java.math.BigDecimal) */ public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { setBigDecimal(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setBinaryStream(java.lang.String, * java.io.InputStream, int) */ public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { setBinaryStream(getNamedParamIndex(parameterName, false), x, length); } /** * @see java.sql.CallableStatement#setBoolean(java.lang.String, boolean) */ public void setBoolean(String parameterName, boolean x) throws SQLException { setBoolean(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setByte(java.lang.String, byte) */ public void setByte(String parameterName, byte x) throws SQLException { setByte(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setBytes(java.lang.String, byte[]) */ public void setBytes(String parameterName, byte[] x) throws SQLException { setBytes(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setCharacterStream(java.lang.String, * java.io.Reader, int) */ public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { setCharacterStream(getNamedParamIndex(parameterName, false), reader, length); } /** * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date) */ public void setDate(String parameterName, Date x) throws SQLException { setDate(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date, * java.util.Calendar) */ public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { setDate(getNamedParamIndex(parameterName, false), x, cal); } /** * @see java.sql.CallableStatement#setDouble(java.lang.String, double) */ public void setDouble(String parameterName, double x) throws SQLException { setDouble(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setFloat(java.lang.String, float) */ public void setFloat(String parameterName, float x) throws SQLException { setFloat(getNamedParamIndex(parameterName, false), x); } /** * */ private void setInOutParamsOnServer() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.paramInfo.numParameters > 0) { for (Iterator<CallableStatementParam> paramIter = this.paramInfo.iterator(); paramIter .hasNext();) { CallableStatementParam inParamInfo = paramIter .next(); //Fix for 5.5+ if (inParamInfo.isOut && inParamInfo.isIn) { if ((inParamInfo.paramName == null) && (hasParametersView())) { inParamInfo.paramName = "nullnp" + inParamInfo.index; }; String inOutParameterName = mangleParameterName(inParamInfo.paramName); StringBuffer queryBuf = new StringBuffer( 4 + inOutParameterName.length() + 1 + 1); queryBuf.append("SET "); //$NON-NLS-1$ queryBuf.append(inOutParameterName); queryBuf.append("=?"); //$NON-NLS-1$ PreparedStatement setPstmt = null; try { setPstmt = (PreparedStatement) this.connection .clientPrepareStatement(queryBuf.toString()); byte[] parameterAsBytes = getBytesRepresentation( inParamInfo.index); if (parameterAsBytes != null) { if (parameterAsBytes.length > 8 && parameterAsBytes[0] == '_' && parameterAsBytes[1] == 'b' && parameterAsBytes[2] == 'i' && parameterAsBytes[3] == 'n' && parameterAsBytes[4] == 'a' && parameterAsBytes[5] == 'r' && parameterAsBytes[6] == 'y' && parameterAsBytes[7] == '\'') { setPstmt.setBytesNoEscapeNoQuotes(1, parameterAsBytes); } else { int sqlType = inParamInfo.desiredJdbcType; switch (sqlType) { case Types.BIT: case Types.BINARY: case Types.BLOB: case Types.JAVA_OBJECT: case Types.LONGVARBINARY: case Types.VARBINARY: setPstmt.setBytes(1, parameterAsBytes); break; default: // the inherited PreparedStatement methods // have already escaped and quoted these parameters setPstmt.setBytesNoEscape(1, parameterAsBytes); } } } else { setPstmt.setNull(1, Types.NULL); } setPstmt.executeUpdate(); } finally { if (setPstmt != null) { setPstmt.close(); } } } } } } } /** * @see java.sql.CallableStatement#setInt(java.lang.String, int) */ public void setInt(String parameterName, int x) throws SQLException { setInt(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setLong(java.lang.String, long) */ public void setLong(String parameterName, long x) throws SQLException { setLong(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setNull(java.lang.String, int) */ public void setNull(String parameterName, int sqlType) throws SQLException { setNull(getNamedParamIndex(parameterName, false), sqlType); } /** * @see java.sql.CallableStatement#setNull(java.lang.String, int, * java.lang.String) */ public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { setNull(getNamedParamIndex(parameterName, false), sqlType, typeName); } /** * @see java.sql.CallableStatement#setObject(java.lang.String, * java.lang.Object) */ public void setObject(String parameterName, Object x) throws SQLException { setObject(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setObject(java.lang.String, * java.lang.Object, int) */ public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { setObject(getNamedParamIndex(parameterName, false), x, targetSqlType); } /** * @see java.sql.CallableStatement#setObject(java.lang.String, * java.lang.Object, int, int) */ public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { } private void setOutParams() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.paramInfo.numParameters > 0) { for (Iterator<CallableStatementParam> paramIter = this.paramInfo.iterator(); paramIter .hasNext();) { CallableStatementParam outParamInfo = paramIter .next(); if (!this.callingStoredFunction && outParamInfo.isOut) { if ((outParamInfo.paramName == null) && (hasParametersView())) { outParamInfo.paramName = "nullnp" + outParamInfo.index; }; String outParameterName = mangleParameterName(outParamInfo.paramName); int outParamIndex = 0; if (this.placeholderToParameterIndexMap == null) { outParamIndex = outParamInfo.index + 1; } else { // Find it, todo: remove this linear search boolean found = false; for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { if (this.placeholderToParameterIndexMap[i] == outParamInfo.index) { outParamIndex = i + 1; /* JDBC is 1-based */ found = true; break; } } if (!found) { throw SQLError.createSQLException("boo!", "S1000", this.connection.getExceptionInterceptor()); } } this.setBytesNoEscapeNoQuotes(outParamIndex, StringUtils.getBytes(outParameterName, this.charConverter, this.charEncoding, this.connection .getServerCharacterEncoding(), this.connection.parserKnowsUnicode(), getExceptionInterceptor())); } } } } } /** * @see java.sql.CallableStatement#setShort(java.lang.String, short) */ public void setShort(String parameterName, short x) throws SQLException { setShort(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setString(java.lang.String, * java.lang.String) */ public void setString(String parameterName, String x) throws SQLException { setString(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time) */ public void setTime(String parameterName, Time x) throws SQLException { setTime(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time, * java.util.Calendar) */ public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { setTime(getNamedParamIndex(parameterName, false), x, cal); } /** * @see java.sql.CallableStatement#setTimestamp(java.lang.String, * java.sql.Timestamp) */ public void setTimestamp(String parameterName, Timestamp x) throws SQLException { setTimestamp(getNamedParamIndex(parameterName, false), x); } /** * @see java.sql.CallableStatement#setTimestamp(java.lang.String, * java.sql.Timestamp, java.util.Calendar) */ public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { setTimestamp(getNamedParamIndex(parameterName, false), x, cal); } /** * @see java.sql.CallableStatement#setURL(java.lang.String, java.net.URL) */ public void setURL(String parameterName, URL val) throws SQLException { setURL(getNamedParamIndex(parameterName, false), val); } /** * @see java.sql.CallableStatement#wasNull() */ public boolean wasNull() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { return this.outputParamWasNull; } } public int[] executeBatch() throws SQLException { if (this.hasOutputParams) { throw SQLError.createSQLException("Can't call executeBatch() on CallableStatement with OUTPUT parameters", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } return super.executeBatch(); } protected int getParameterIndexOffset() { if (this.callingStoredFunction) { return -1; } return super.getParameterIndexOffset(); } public void setAsciiStream(String parameterName, InputStream x) throws SQLException { setAsciiStream(getNamedParamIndex(parameterName, false), x); } public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { setAsciiStream(getNamedParamIndex(parameterName, false), x, length); } public void setBinaryStream(String parameterName, InputStream x) throws SQLException { setBinaryStream(getNamedParamIndex(parameterName, false), x); } public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { setBinaryStream(getNamedParamIndex(parameterName, false), x, length); } public void setBlob(String parameterName, Blob x) throws SQLException { setBlob(getNamedParamIndex(parameterName, false), x); } public void setBlob(String parameterName, InputStream inputStream) throws SQLException { setBlob(getNamedParamIndex(parameterName, false), inputStream); } public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { setBlob(getNamedParamIndex(parameterName, false), inputStream, length); } public void setCharacterStream(String parameterName, Reader reader) throws SQLException { setCharacterStream(getNamedParamIndex(parameterName, false), reader); } public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { setCharacterStream(getNamedParamIndex(parameterName, false), reader, length); } public void setClob(String parameterName, Clob x) throws SQLException { setClob(getNamedParamIndex(parameterName, false), x); } public void setClob(String parameterName, Reader reader) throws SQLException { setClob(getNamedParamIndex(parameterName, false), reader); } public void setClob(String parameterName, Reader reader, long length) throws SQLException { setClob(getNamedParamIndex(parameterName, false), reader, length); } public void setNCharacterStream(String parameterName, Reader value) throws SQLException { setNCharacterStream(getNamedParamIndex(parameterName, false), value); } public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { setNCharacterStream(getNamedParamIndex(parameterName, false), value, length); } /** * Check whether the stored procedure alters any data or is safe for read-only usage. * * @return true if procedure does not alter data * @throws SQLException */ private boolean checkReadOnlyProcedure() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.connection.getNoAccessToProcedureBodies()) { return false; } if (this.paramInfo.isReadOnlySafeChecked) { return this.paramInfo.isReadOnlySafeProcedure; } ResultSet rs = null; java.sql.PreparedStatement ps = null; try { String procName = extractProcedureName(); String catalog = this.currentCatalog; if (procName.indexOf(".") != -1) { catalog = procName.substring(0, procName.indexOf(".")); if (StringUtils.startsWithIgnoreCaseAndWs(catalog, "`") && catalog.trim().endsWith("`")) { catalog = catalog.substring(1, catalog.length() - 1); } procName = procName.substring(procName.indexOf(".") + 1); procName = StringUtils.toString(StringUtils.stripEnclosure( StringUtils.getBytes(procName), "`", "`")); } ps = this.connection .prepareStatement("SELECT SQL_DATA_ACCESS FROM " + " information_schema.routines " + " WHERE routine_schema = ? " + " AND routine_name = ?"); ps.setMaxRows(0); ps.setFetchSize(0); ps.setString(1, catalog); ps.setString(2, procName); rs = ps.executeQuery(); if (rs.next()) { String sqlDataAccess = rs.getString(1); if ("READS SQL DATA".equalsIgnoreCase(sqlDataAccess) || "NO SQL".equalsIgnoreCase(sqlDataAccess)) { synchronized (this.paramInfo) { this.paramInfo.isReadOnlySafeChecked = true; this.paramInfo.isReadOnlySafeProcedure = true; } return true; } } } catch (SQLException e) { // swallow the Exception } finally { if(rs != null){ rs.close(); } if(ps != null){ ps.close(); } } this.paramInfo.isReadOnlySafeChecked = false; this.paramInfo.isReadOnlySafeProcedure = false; } return false; } protected boolean checkReadOnlySafeStatement() throws SQLException { return (super.checkReadOnlySafeStatement() || this.checkReadOnlyProcedure()); } private boolean hasParametersView() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { try { if (this.connection.versionMeetsMinimum(5, 5, 0)) { java.sql.DatabaseMetaData dbmd1 = new DatabaseMetaDataUsingInfoSchema(this.connection, this.connection.getCatalog()); return ((DatabaseMetaDataUsingInfoSchema)dbmd1).gethasParametersView(); } return false; } catch (SQLException e) { return false; } } } }