📄 jtdsstatement.java
字号:
* Implements the common functionality for plain statement {@link #execute}
* and {#link #executeUpdate}: basic checks, cleaning up of previous
* results, setting up and executing the query and loading the first
* results.
*
* @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
* <code>DELETE</code> statement or an SQL statement that
* returns nothing, such as an SQL DDL statement
* @param autoGeneratedKeys a flag indicating whether auto-generated keys
* should be made available for retrieval
* @param update boolean flag indicating whether the caller is
* {@link #executeUpdate} -- in this case an exception is
* thrown if the first result is not an update count and no
* cursor is created (direct execution)
* @return <code>true</code> if the first result is a
* <code>ResultSet</code>, <code>false</code> if it's an update
* count
* @see #execute
* @see #executeUpdate
*/
private boolean executeImpl(String sql, int autoGeneratedKeys, boolean update)
throws SQLException {
initialize();
if (sql == null || sql.length() == 0) {
throw new SQLException(Messages.get("error.generic.nosql"), "HY000");
}
boolean returnKeys;
String sqlWord = "";
if (escapeProcessing) {
String tmp[] = SQLParser.parse(sql, null, connection, false);
if (tmp[1].length() != 0) {
throw new SQLException(
Messages.get("error.statement.badsql"), "07000");
}
sql = tmp[0];
sqlWord = tmp[2];
} else {
// Escape processing turned off so
// see if we can extract "insert" from start of statement
sql = sql.trim();
if (sql.length() > 5) {
sqlWord = sql.substring(0,6).toLowerCase();
}
}
if (autoGeneratedKeys == RETURN_GENERATED_KEYS) {
returnKeys = "insert".equals(sqlWord);
} else if (autoGeneratedKeys == NO_GENERATED_KEYS) {
returnKeys = false;
} else {
throw new SQLException(
Messages.get("error.generic.badoption",
Integer.toString(autoGeneratedKeys),
"autoGeneratedKeys"),
"HY092");
}
if (returnKeys) {
if (connection.getServerType() == Driver.SQLSERVER
&& connection.getDatabaseMajorVersion() >= 8) {
sql += " SELECT SCOPE_IDENTITY() AS ID";
} else {
sql += " SELECT @@IDENTITY AS ID";
}
}
return executeSQL(sql, null, null, returnKeys, update,
!update && useCursor(returnKeys, sqlWord));
}
/**
* Determines whether a cursor should be used based on the requested result
* set type and concurrency, whether a cursor name has been set, the
* <code>useCursors</code> connection property has been set, the first
* word in the SQL query is either SELECT or EXEC/EXECUTE and no generated
* keys are returned.
*
* @param returnKeys indicates whether keys will be returned by the query
* @param sqlWord the first word in the SQL query; can be
* <code>null</code> if the caller is
* {@link #executeQuery}
* @return <code>true</code> if a cursor should be used, <code>false</code>
* if not
*/
protected boolean useCursor(boolean returnKeys, String sqlWord) {
return (resultSetType != ResultSet.TYPE_FORWARD_ONLY
|| resultSetConcurrency != ResultSet.CONCUR_READ_ONLY
|| connection.getUseCursors()
|| cursorName != null)
&& !returnKeys
&& (sqlWord == null || "select".equals(sqlWord) || sqlWord.startsWith("exec"));
}
/**
* Retrieve the default fetch size for this statement.
*
* @return the default fetch size for a new <code>ResultSet</code>
*/
int getDefaultFetchSize() {
return (0 < this.maxRows && this.maxRows < DEFAULT_FETCH_SIZE)
? this.maxRows : DEFAULT_FETCH_SIZE;
}
// ------------------ java.sql.Statement methods ----------------------
public int getFetchDirection() throws SQLException {
checkOpen();
return this.fetchDirection;
}
public int getFetchSize() throws SQLException {
checkOpen();
return this.fetchSize;
}
public int getMaxFieldSize() throws SQLException {
checkOpen();
return this.maxFieldSize;
}
public int getMaxRows() throws SQLException {
checkOpen();
return this.maxRows;
}
public int getQueryTimeout() throws SQLException {
checkOpen();
return this.queryTimeout;
}
public int getResultSetConcurrency() throws SQLException {
checkOpen();
return this.resultSetConcurrency;
}
public int getResultSetHoldability() throws SQLException {
checkOpen();
return JtdsResultSet.HOLD_CURSORS_OVER_COMMIT;
}
public int getResultSetType() throws SQLException {
checkOpen();
return resultSetType;
}
public int getUpdateCount() throws SQLException {
checkOpen();
return updateCount;
}
public void cancel() throws SQLException {
checkOpen();
if (tds != null) {
tds.cancel(false);
}
}
public void clearBatch() throws SQLException {
checkOpen();
if (batchValues != null) {
batchValues.clear();
}
}
public void clearWarnings() throws SQLException {
checkOpen();
messages.clearWarnings();
}
public void close() throws SQLException {
if (!closed) {
SQLException closeEx = null;
try {
closeAllResultSets();
} catch (SQLException ex) {
if (!"HYT00".equals(ex.getSQLState())
&& !"HY008".equals(ex.getSQLState())) {
// Only throw exceptions not caused by cancels or timeouts
closeEx = ex;
}
} finally {
SQLException releaseEx = null;
try {
if (!connection.isClosed()) {
connection.releaseTds(tds);
}
// Check for server side errors
tds.getMessages().checkErrors();
} catch (SQLException ex) {
// Remember any exception thrown
releaseEx = ex;
// Queue up any result set close exceptions
if (closeEx != null) {
releaseEx.setNextException(closeEx);
}
} finally {
// Clean up everything
closed = true;
tds = null;
connection.removeStatement(this);
connection = null;
// Re-throw any caught exception
if (releaseEx != null) {
throw releaseEx;
}
}
}
// Throw any exception caught during result set close
if (closeEx != null) {
throw closeEx;
}
}
}
public boolean getMoreResults() throws SQLException {
checkOpen();
return getMoreResults(CLOSE_ALL_RESULTS);
}
/**
* Execute batch of SQL Statements.
* <p/>
* The JDBC3 standard says that the behaviour of this method must be
* consistent for any DBMS. As Sybase (and to a lesser extent SQL Server)
* will sometimes continue after a batch execution error, the only way to
* comply with the standard is to always return an array of update counts
* the same size as the batch list. Slots in the array beyond the last
* executed statement are set to <code>EXECUTE_FAILED</code>.
*
* @return update counts as an <code>int[]</code>
*/
public int[] executeBatch()
throws SQLException, BatchUpdateException {
checkOpen();
initialize();
if (batchValues == null || batchValues.size() == 0) {
return new int[0];
}
int size = batchValues.size();
int executeSize = connection.getBatchSize();
executeSize = (executeSize == 0) ? Integer.MAX_VALUE : executeSize;
SQLException sqlEx;
ArrayList counts = new ArrayList(size);
try {
// Lock the connection, making sure the batch executes atomically. This is especially important in the
// case of prepared statement batches (where we don't want the prepares rolled back before being executed)
// but should also provide some level of sanity in the general case.
synchronized (connection) {
if (connection.getServerType() == Driver.SYBASE
&& connection.getTdsVersion() == Driver.TDS50) {
sqlEx = executeSybaseBatch(size, executeSize, counts);
} else {
sqlEx = executeMSBatch(size, executeSize, counts);
}
}
// Ensure array is the same size as the original statement list
int updateCounts[] = new int[size];
// Copy the update counts into the int array
int results = counts.size();
for (int i = 0; i < results; i++) {
updateCounts[i] = ((Integer) counts.get(i)).intValue();
}
// Pad any remaining slots with EXECUTE_FAILED
for (int i = results; i < updateCounts.length; i++) {
updateCounts[i] = EXECUTE_FAILED.intValue();
}
// See if we should return an exception
if (sqlEx != null) {
BatchUpdateException batchEx =
new BatchUpdateException(sqlEx.getMessage(),
sqlEx.getSQLState(),
sqlEx.getErrorCode(),
updateCounts);
// Chain any other exceptions
batchEx.setNextException(sqlEx.getNextException());
throw batchEx;
}
return updateCounts;
} catch (BatchUpdateException ex) {
// If it's a BatchUpdateException let it go
throw ex;
} catch (SQLException ex) {
// An SQLException can only occur while sending the batch
// (getBatchCounts() doesn't throw SQLExceptions), so we have to
// end the batch and return the partial results
// FIXME What should we send here to flush out the batch?
// Come to think of it, is there any circumstance under which this
// could actually happen without the connection getting closed?
// No counts will have been returned either as last packet will not
// have been sent.
throw new BatchUpdateException(ex.getMessage(), ex.getSQLState(),
ex.getErrorCode(), new int[0]);
} finally {
clearBatch();
}
}
public void setFetchDirection(int direction) throws SQLException {
checkOpen();
switch (direction) {
case ResultSet.FETCH_UNKNOWN:
case ResultSet.FETCH_REVERSE:
case ResultSet.FETCH_FORWARD:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -