📄 jtdspreparedstatement.java
字号:
// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
package net.sourceforge.jtds.jdbc;
import java.io.InputStream;
import java.io.Reader;
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.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.ArrayList;
import java.util.Collection;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.text.NumberFormat;
/**
* jTDS implementation of the java.sql.PreparedStatement interface.
* <p>
* Implementation notes:
* <ol>
* <li>Generally a simple subclass of Statement mainly adding support for the
* setting of parameters.
* <li>The stream logic is taken over from the work Brian did to add Blob support
* to the original jTDS.
* <li>Use of Statement specific method calls eg executeQuery(sql) is blocked by
* this version of the driver. This is unlike the original jTDS but inline
* with all the other JDBC drivers that I have been able to test.
* </ol>
*
* @author Mike Hutchinson
* @author Brian Heineman
* @version $Id: JtdsPreparedStatement.java,v 1.63 2007/07/12 21:03:23 bheineman Exp $
*/
public class JtdsPreparedStatement extends JtdsStatement implements PreparedStatement {
/** The SQL statement being prepared. */
protected final String sql;
/** The first SQL keyword in the SQL string.*/
protected String sqlWord;
/** The procedure name for CallableStatements. */
protected String procName;
/** The parameter list for the call. */
protected ParamInfo[] parameters;
/** True to return generated keys. */
private boolean returnKeys;
/** The cached parameter meta data. */
protected ParamInfo[] paramMetaData;
/** Used to format numeric values when scale is specified. */
private final static NumberFormat f = NumberFormat.getInstance();
/** Collection of handles used by this statement */
Collection handles;
/**
* Construct a new preparedStatement object.
*
* @param connection The parent connection.
* @param sql The SQL statement to prepare.
* @param resultSetType The result set type eg SCROLLABLE etc.
* @param concurrency The result set concurrency eg READONLY.
* @param returnKeys True if generated keys should be returned.
* @throws SQLException
*/
JtdsPreparedStatement(ConnectionJDBC2 connection,
String sql,
int resultSetType,
int concurrency,
boolean returnKeys)
throws SQLException {
super(connection, resultSetType, concurrency);
// Parse the SQL looking for escapes and parameters
if (this instanceof JtdsCallableStatement) {
sql = normalizeCall(sql);
}
ArrayList params = new ArrayList();
String[] parsedSql = SQLParser.parse(sql, params, connection, false);
if (parsedSql[0].length() == 0) {
throw new SQLException(Messages.get("error.prepare.nosql"), "07000");
}
if (parsedSql[1].length() > 1) {
if (this instanceof JtdsCallableStatement) {
// Procedure call
this.procName = parsedSql[1];
}
}
sqlWord = parsedSql[2];
if (returnKeys && "insert".equals(sqlWord)) {
if (connection.getServerType() == Driver.SQLSERVER
&& connection.getDatabaseMajorVersion() >= 8) {
this.sql = parsedSql[0] + " SELECT SCOPE_IDENTITY() AS ID";
} else {
this.sql = parsedSql[0] + " SELECT @@IDENTITY AS ID";
}
this.returnKeys = true;
} else {
this.sql = parsedSql[0];
this.returnKeys = false;
}
parameters = (ParamInfo[]) params.toArray(new ParamInfo[params.size()]);
}
/**
* This method converts native call syntax into (hopefully) valid JDBC
* escape syntax.
* <p/>
* <b>Note:</b> This method is required for backwards compatibility with
* previous versions of jTDS. Strictly speaking only the JDBC syntax needs
* to be recognised, constructions such as "?=#testproc ?,?" are neither
* valid native syntax nor valid escapes. All the substrings and trims
* below are not as bad as they look. The objects created all refer back to
* the original sql string it is just the start and length positions which
* change.
*
* @param sql the SQL statement to process
* @return the SQL, possibly in original form
*/
protected static String normalizeCall(String sql) {
String original = sql;
sql = sql.trim();
if (sql.length() > 0 && sql.charAt(0) == '{') {
return original; // Assume already escaped
}
if (sql.length() > 4 && sql.substring(0, 5).equalsIgnoreCase("exec ")) {
sql = sql.substring(4).trim();
} else if (sql.length() > 7 && sql.substring(0, 8).equalsIgnoreCase("execute ")){
sql = sql.substring(7).trim();
}
if (sql.length() > 1 && sql.charAt(0) == '?') {
sql = sql.substring(1).trim();
if (sql.length() < 1 || sql.charAt(0) != '=') {
return original; // Give up, error will be reported elsewhere
}
sql = sql.substring(1).trim();
// OK now reconstruct as JDBC escaped call
return "{?=call " + sql + '}';
}
return "{call " + sql + '}';
}
/**
* Check that this statement is still open.
*
* @throws SQLException if statement closed.
*/
protected void checkOpen() throws SQLException {
if (closed) {
throw new SQLException(
Messages.get("error.generic.closed", "PreparedStatement"), "HY010");
}
}
/**
* Report that user tried to call a method not supported on this type of statement.
*
* @param method The method name to report in the error message.
* @throws SQLException
*/
protected void notSupported(String method) throws SQLException {
throw new SQLException(
Messages.get("error.generic.notsup", method), "HYC00");
}
/**
* Execute the SQL batch on a MS server.
* <p/>
* When running with <code>prepareSQL=1</code> or <code>3</code>, the driver will first prepare temporary stored
* procedures or statements for each parameter combination found in the batch. The handles to these pre-preared
* statements will then be used to execute the actual batch statements.
*
* @param size the total size of the batch
* @param executeSize the maximum number of statements to send in one request
* @param counts the returned update counts
* @return chained exceptions linked to a <code>SQLException</code>
* @throws SQLException if a serious error occurs during execution
*/
protected SQLException executeMSBatch(int size, int executeSize, ArrayList counts)
throws SQLException {
if (parameters.length == 0) {
// There are no parameters, each SQL call is the same so execute as a simple batch
return super.executeMSBatch(size, executeSize, counts);
}
SQLException sqlEx = null;
String procHandle[] = null;
// Prepare any statements before executing the batch
if (connection.getPrepareSql() == TdsCore.TEMPORARY_STORED_PROCEDURES ||
connection.getPrepareSql() == TdsCore.PREPARE) {
procHandle = new String[size];
for (int i = 0; i < size; i++) {
// Prepare the statement
procHandle[i] = connection.prepareSQL(this, sql, (ParamInfo[]) batchValues.get(i), false, false);
}
}
for (int i = 0; i < size;) {
Object value = batchValues.get(i);
if (procHandle != null) {
procName = procHandle[i];
}
++i;
// Execute batch now if max size reached or end of batch
boolean executeNow = (i % executeSize == 0) || i == size;
tds.startBatch();
tds.executeSQL(sql, procName, (ParamInfo[]) value, false, 0, -1, -1, executeNow);
// If the batch has been sent, process the results
if (executeNow) {
sqlEx = tds.getBatchCounts(counts, sqlEx);
// If a serious error then we stop execution now as count
// is too small.
if (sqlEx != null && counts.size() != i) {
break;
}
}
}
return sqlEx;
}
/**
* Execute the SQL batch on a Sybase server.
* <p/>
* Sybase needs to have the SQL concatenated into one TDS language packet followed by up to 1000 parameters. This
* method will be overriden for <code>CallableStatements</code>.
*
* @param size the total size of the batch
* @param executeSize the maximum number of statements to send in one request
* @param counts the returned update counts
* @return chained exceptions linked to a <code>SQLException</code>
* @throws SQLException if a serious error occurs during execution
*/
protected SQLException executeSybaseBatch(int size, int executeSize, ArrayList counts) throws SQLException {
if (parameters.length == 0) {
// There are no parameters each SQL call is the same so
// execute as a simple batch
return super.executeSybaseBatch(size, executeSize, counts);
}
// Revise the executeSize down if too many parameters will be required.
// Be conservative the actual maximums are 256 for older servers and 2048.
int maxParams = (connection.getDatabaseMajorVersion() < 12 ||
(connection.getDatabaseMajorVersion() == 12 && connection.getDatabaseMinorVersion() < 50)) ?
200 : 1000;
StringBuffer sqlBuf = new StringBuffer(size * 32);
SQLException sqlEx = null;
if (parameters.length * executeSize > maxParams) {
executeSize = maxParams / parameters.length;
if (executeSize == 0) {
executeSize = 1;
}
}
ArrayList paramList = new ArrayList();
for (int i = 0; i < size;) {
Object value = batchValues.get(i);
++i;
// Execute batch now if max size reached or end of batch
boolean executeNow = (i % executeSize == 0) || i == size;
int offset = sqlBuf.length();
sqlBuf.append(sql).append(' ');
for (int n = 0; n < parameters.length; n++) {
ParamInfo p = ((ParamInfo[]) value)[n];
// Allow for the position of the '?' marker in the buffer
p.markerPos += offset;
paramList.add(p);
}
if (executeNow) {
ParamInfo args[];
args = (ParamInfo[]) paramList.toArray(new ParamInfo[paramList.size()]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -