PageRenderTime 31ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/EQEmuJSM/mysql-connector-java-5.1.13/src/com/mysql/jdbc/StatementImpl.java

http://cubbers-eqemu-utils.googlecode.com/
Java | 2330 lines | 1351 code | 443 blank | 536 comment | 346 complexity | 65431cd0d5b3185453b3403960574e3e MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0
  1. /*
  2. Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
  3. The MySQL Connector/J is licensed under the terms of the GPL,
  4. like most MySQL Connectors. There are special exceptions to the
  5. terms and conditions of the GPL as it is applied to this software,
  6. see the FLOSS License Exception available on mysql.com.
  7. This program is free software; you can redistribute it and/or
  8. modify it under the terms of the GNU General Public License as
  9. published by the Free Software Foundation; version 2 of the
  10. License.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18. 02110-1301 USA
  19. */
  20. package com.mysql.jdbc;
  21. import java.io.InputStream;
  22. import java.math.BigInteger;
  23. import java.sql.BatchUpdateException;
  24. import java.sql.ResultSet;
  25. import java.sql.SQLException;
  26. import java.sql.SQLWarning;
  27. import java.sql.Types;
  28. import java.util.ArrayList;
  29. import java.util.Calendar;
  30. import java.util.GregorianCalendar;
  31. import java.util.HashSet;
  32. import java.util.Iterator;
  33. import java.util.List;
  34. import java.util.Set;
  35. import java.util.TimerTask;
  36. import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
  37. import com.mysql.jdbc.exceptions.MySQLTimeoutException;
  38. import com.mysql.jdbc.profiler.ProfilerEvent;
  39. import com.mysql.jdbc.profiler.ProfilerEventHandler;
  40. /**
  41. * A Statement object is used for executing a static SQL statement and obtaining
  42. * the results produced by it.
  43. *
  44. * <p>
  45. * Only one ResultSet per Statement can be open at any point in time. Therefore,
  46. * if the reading of one ResultSet is interleaved with the reading of another,
  47. * each must have been generated by different Statements. All statement execute
  48. * methods implicitly close a statement's current ResultSet if an open one
  49. * exists.
  50. * </p>
  51. *
  52. * @author Mark Matthews
  53. * @version $Id: Statement.java 4624 2005-11-28 14:24:29 -0600 (Mon, 28 Nov
  54. * 2005) mmatthews $
  55. *
  56. * @see java.sql.Statement
  57. * @see ResultSetInternalMethods
  58. */
  59. public class StatementImpl implements Statement {
  60. protected static final String PING_MARKER = "/* ping */";
  61. /**
  62. * Thread used to implement query timeouts...Eventually we could be more
  63. * efficient and have one thread with timers, but this is a straightforward
  64. * and simple way to implement a feature that isn't used all that often.
  65. */
  66. class CancelTask extends TimerTask {
  67. long connectionId = 0;
  68. SQLException caughtWhileCancelling = null;
  69. StatementImpl toCancel;
  70. CancelTask(StatementImpl cancellee) throws SQLException {
  71. connectionId = connection.getIO().getThreadId();
  72. toCancel = cancellee;
  73. }
  74. public void run() {
  75. Thread cancelThread = new Thread() {
  76. public void run() {
  77. if (connection.getQueryTimeoutKillsConnection()) {
  78. try {
  79. toCancel.wasCancelled = true;
  80. toCancel.wasCancelledByTimeout = true;
  81. connection.realClose(false, false, true,
  82. new MySQLStatementCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout")));
  83. } catch (NullPointerException npe) {
  84. // not worth guarding against
  85. } catch (SQLException sqlEx) {
  86. caughtWhileCancelling = sqlEx;
  87. }
  88. } else {
  89. Connection cancelConn = null;
  90. java.sql.Statement cancelStmt = null;
  91. try {
  92. synchronized (cancelTimeoutMutex) {
  93. cancelConn = connection.duplicate();
  94. cancelStmt = cancelConn.createStatement();
  95. cancelStmt.execute("KILL QUERY " + connectionId);
  96. toCancel.wasCancelled = true;
  97. toCancel.wasCancelledByTimeout = true;
  98. }
  99. } catch (SQLException sqlEx) {
  100. caughtWhileCancelling = sqlEx;
  101. } catch (NullPointerException npe) {
  102. // Case when connection closed while starting to cancel
  103. // We can't easily synchronize this, because then one thread
  104. // can't cancel() a running query
  105. // ignore, we shouldn't re-throw this, because the connection's
  106. // already closed, so the statement has been timed out.
  107. } finally {
  108. if (cancelStmt != null) {
  109. try {
  110. cancelStmt.close();
  111. } catch (SQLException sqlEx) {
  112. throw new RuntimeException(sqlEx.toString());
  113. }
  114. }
  115. if (cancelConn != null) {
  116. try {
  117. cancelConn.close();
  118. } catch (SQLException sqlEx) {
  119. throw new RuntimeException(sqlEx.toString());
  120. }
  121. }
  122. toCancel = null;
  123. }
  124. }
  125. }
  126. };
  127. cancelThread.start();
  128. }
  129. }
  130. /** Mutex to prevent race between returning query results and noticing
  131. that we're timed-out or cancelled. */
  132. protected Object cancelTimeoutMutex = new Object();
  133. /** Used to generate IDs when profiling. */
  134. protected static int statementCounter = 1;
  135. public final static byte USES_VARIABLES_FALSE = 0;
  136. public final static byte USES_VARIABLES_TRUE = 1;
  137. public final static byte USES_VARIABLES_UNKNOWN = -1;
  138. protected boolean wasCancelled = false;
  139. protected boolean wasCancelledByTimeout = false;
  140. /** Holds batched commands */
  141. protected List batchedArgs;
  142. /** The character converter to use (if available) */
  143. protected SingleByteCharsetConverter charConverter = null;
  144. /** The character encoding to use (if available) */
  145. protected String charEncoding = null;
  146. /** The connection that created us */
  147. protected MySQLConnection connection = null;
  148. protected long connectionId = 0;
  149. /** The catalog in use */
  150. protected String currentCatalog = null;
  151. /** Should we process escape codes? */
  152. protected boolean doEscapeProcessing = true;
  153. /** If we're profiling, where should events go to? */
  154. protected ProfilerEventHandler eventSink = null;
  155. /** The number of rows to fetch at a time (currently ignored) */
  156. private int fetchSize = 0;
  157. /** Has this statement been closed? */
  158. protected boolean isClosed = false;
  159. /** The auto_increment value for the last insert */
  160. protected long lastInsertId = -1;
  161. /** The max field size for this statement */
  162. protected int maxFieldSize = MysqlIO.getMaxBuf();
  163. /**
  164. * The maximum number of rows to return for this statement (-1 means _all_
  165. * rows)
  166. */
  167. protected int maxRows = -1;
  168. /** Has someone changed this for this statement? */
  169. protected boolean maxRowsChanged = false;
  170. /** Set of currently-open ResultSets */
  171. protected Set openResults = new HashSet();
  172. /** Are we in pedantic mode? */
  173. protected boolean pedantic = false;
  174. /**
  175. * Where this statement was created, only used if profileSql or
  176. * useUsageAdvisor set to true.
  177. */
  178. protected Throwable pointOfOrigin;
  179. /** Should we profile? */
  180. protected boolean profileSQL = false;
  181. /** The current results */
  182. protected ResultSetInternalMethods results = null;
  183. /** The concurrency for this result set (updatable or not) */
  184. protected int resultSetConcurrency = 0;
  185. /** The type of this result set (scroll sensitive or in-sensitive) */
  186. protected int resultSetType = 0;
  187. /** Used to identify this statement when profiling. */
  188. protected int statementId;
  189. /** The timeout for a query */
  190. protected int timeoutInMillis = 0;
  191. /** The update count for this statement */
  192. protected long updateCount = -1;
  193. /** Should we use the usage advisor? */
  194. protected boolean useUsageAdvisor = false;
  195. /** The warnings chain. */
  196. protected SQLWarning warningChain = null;
  197. /**
  198. * Should this statement hold results open over .close() irregardless of
  199. * connection's setting?
  200. */
  201. protected boolean holdResultsOpenOverClose = false;
  202. protected ArrayList batchedGeneratedKeys = null;
  203. protected boolean retrieveGeneratedKeys = false;
  204. protected boolean continueBatchOnError = false;
  205. protected PingTarget pingTarget = null;
  206. protected boolean useLegacyDatetimeCode;
  207. private ExceptionInterceptor exceptionInterceptor;
  208. /** Whether or not the last query was of the form ON DUPLICATE KEY UPDATE */
  209. protected boolean lastQueryIsOnDupKeyUpdate = false;
  210. /**
  211. * Constructor for a Statement.
  212. *
  213. * @param c
  214. * the Connection instantation that creates us
  215. * @param catalog
  216. * the database name in use when we were created
  217. *
  218. * @throws SQLException
  219. * if an error occurs.
  220. */
  221. public StatementImpl(MySQLConnection c, String catalog) throws SQLException {
  222. if ((c == null) || c.isClosed()) {
  223. throw SQLError.createSQLException(
  224. Messages.getString("Statement.0"), //$NON-NLS-1$
  225. SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null); //$NON-NLS-1$ //$NON-NLS-2$
  226. }
  227. this.connection = c;
  228. this.connectionId = this.connection.getId();
  229. this.exceptionInterceptor = this.connection
  230. .getExceptionInterceptor();
  231. this.currentCatalog = catalog;
  232. this.pedantic = this.connection.getPedantic();
  233. this.continueBatchOnError = this.connection.getContinueBatchOnError();
  234. this.useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode();
  235. if (!this.connection.getDontTrackOpenResources()) {
  236. this.connection.registerStatement(this);
  237. }
  238. //
  239. // Adjust, if we know it
  240. //
  241. if (this.connection != null) {
  242. this.maxFieldSize = this.connection.getMaxAllowedPacket();
  243. int defaultFetchSize = this.connection.getDefaultFetchSize();
  244. if (defaultFetchSize != 0) {
  245. setFetchSize(defaultFetchSize);
  246. }
  247. }
  248. if (this.connection.getUseUnicode()) {
  249. this.charEncoding = this.connection.getEncoding();
  250. this.charConverter = this.connection.getCharsetConverter(this.charEncoding);
  251. }
  252. boolean profiling = this.connection.getProfileSql()
  253. || this.connection.getUseUsageAdvisor() || this.connection.getLogSlowQueries();
  254. if (this.connection.getAutoGenerateTestcaseScript() || profiling) {
  255. this.statementId = statementCounter++;
  256. }
  257. if (profiling) {
  258. this.pointOfOrigin = new Throwable();
  259. this.profileSQL = this.connection.getProfileSql();
  260. this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
  261. this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection);
  262. }
  263. int maxRowsConn = this.connection.getMaxRows();
  264. if (maxRowsConn != -1) {
  265. setMaxRows(maxRowsConn);
  266. }
  267. this.holdResultsOpenOverClose = this.connection.getHoldResultsOpenOverStatementClose();
  268. }
  269. /**
  270. * DOCUMENT ME!
  271. *
  272. * @param sql
  273. * DOCUMENT ME!
  274. *
  275. * @throws SQLException
  276. * DOCUMENT ME!
  277. */
  278. public synchronized void addBatch(String sql) throws SQLException {
  279. if (this.batchedArgs == null) {
  280. this.batchedArgs = new ArrayList();
  281. }
  282. if (sql != null) {
  283. this.batchedArgs.add(sql);
  284. }
  285. }
  286. /**
  287. * Cancels this Statement object if both the DBMS and driver support
  288. * aborting an SQL statement. This method can be used by one thread to
  289. * cancel a statement that is being executed by another thread.
  290. */
  291. public void cancel() throws SQLException {
  292. if (!this.isClosed &&
  293. this.connection != null &&
  294. this.connection.versionMeetsMinimum(5, 0, 0)) {
  295. Connection cancelConn = null;
  296. java.sql.Statement cancelStmt = null;
  297. try {
  298. cancelConn = this.connection.duplicate();
  299. cancelStmt = cancelConn.createStatement();
  300. cancelStmt.execute("KILL QUERY "
  301. + this.connection.getIO().getThreadId());
  302. this.wasCancelled = true;
  303. } finally {
  304. if (cancelStmt != null) {
  305. cancelStmt.close();
  306. }
  307. if (cancelConn != null) {
  308. cancelConn.close();
  309. }
  310. }
  311. }
  312. }
  313. // --------------------------JDBC 2.0-----------------------------
  314. /**
  315. * Checks if closed() has been called, and throws an exception if so
  316. *
  317. * @throws SQLException
  318. * if this statement has been closed
  319. */
  320. protected void checkClosed() throws SQLException {
  321. if (this.isClosed) {
  322. throw SQLError.createSQLException(Messages
  323. .getString("Statement.49"), //$NON-NLS-1$
  324. SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor()); //$NON-NLS-1$
  325. }
  326. }
  327. /**
  328. * Checks if the given SQL query with the given first non-ws char is a DML
  329. * statement. Throws an exception if it is.
  330. *
  331. * @param sql
  332. * the SQL to check
  333. * @param firstStatementChar
  334. * the UC first non-ws char of the statement
  335. *
  336. * @throws SQLException
  337. * if the statement contains DML
  338. */
  339. protected void checkForDml(String sql, char firstStatementChar)
  340. throws SQLException {
  341. if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
  342. || (firstStatementChar == 'D') || (firstStatementChar == 'A')
  343. || (firstStatementChar == 'C')) {
  344. String noCommentSql = StringUtils.stripComments(sql,
  345. "'\"", "'\"", true, false, true, true);
  346. if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") //$NON-NLS-1$
  347. || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") //$NON-NLS-1$
  348. || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") //$NON-NLS-1$
  349. || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") //$NON-NLS-1$
  350. || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") //$NON-NLS-1$
  351. || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER")) { //$NON-NLS-1$
  352. throw SQLError.createSQLException(Messages
  353. .getString("Statement.57"), //$NON-NLS-1$
  354. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
  355. }
  356. }
  357. }
  358. /**
  359. * Method checkNullOrEmptyQuery.
  360. *
  361. * @param sql
  362. * the SQL to check
  363. *
  364. * @throws SQLException
  365. * if query is null or empty.
  366. */
  367. protected void checkNullOrEmptyQuery(String sql) throws SQLException {
  368. if (sql == null) {
  369. throw SQLError.createSQLException(Messages
  370. .getString("Statement.59"), //$NON-NLS-1$
  371. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
  372. }
  373. if (sql.length() == 0) {
  374. throw SQLError.createSQLException(Messages
  375. .getString("Statement.61"), //$NON-NLS-1$
  376. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
  377. }
  378. }
  379. /**
  380. * JDBC 2.0 Make the set of commands in the current batch empty. This method
  381. * is optional.
  382. *
  383. * @exception SQLException
  384. * if a database-access error occurs, or the driver does not
  385. * support batch statements
  386. */
  387. public synchronized void clearBatch() throws SQLException {
  388. if (this.batchedArgs != null) {
  389. this.batchedArgs.clear();
  390. }
  391. }
  392. /**
  393. * After this call, getWarnings returns null until a new warning is reported
  394. * for this Statement.
  395. *
  396. * @exception SQLException
  397. * if a database access error occurs (why?)
  398. */
  399. public void clearWarnings() throws SQLException {
  400. this.warningChain = null;
  401. }
  402. /**
  403. * In many cases, it is desirable to immediately release a Statement's
  404. * database and JDBC resources instead of waiting for this to happen when it
  405. * is automatically closed. The close method provides this immediate
  406. * release.
  407. *
  408. * <p>
  409. * <B>Note:</B> A Statement is automatically closed when it is garbage
  410. * collected. When a Statement is closed, its current ResultSet, if one
  411. * exists, is also closed.
  412. * </p>
  413. *
  414. * @exception SQLException
  415. * if a database access error occurs
  416. */
  417. public synchronized void close() throws SQLException {
  418. realClose(true, true);
  419. }
  420. /**
  421. * Close any open result sets that have been 'held open'
  422. */
  423. protected synchronized void closeAllOpenResults() {
  424. if (this.openResults != null) {
  425. for (Iterator iter = this.openResults.iterator(); iter.hasNext();) {
  426. ResultSetInternalMethods element = (ResultSetInternalMethods) iter.next();
  427. try {
  428. element.realClose(false);
  429. } catch (SQLException sqlEx) {
  430. AssertionFailedException.shouldNotHappen(sqlEx);
  431. }
  432. }
  433. this.openResults.clear();
  434. }
  435. }
  436. public synchronized void removeOpenResultSet(ResultSet rs) {
  437. if (this.openResults != null) {
  438. this.openResults.remove(rs);
  439. }
  440. }
  441. public synchronized int getOpenResultSetCount() {
  442. if (this.openResults != null) {
  443. return this.openResults.size();
  444. }
  445. return 0;
  446. }
  447. /**
  448. * @param sql
  449. * @return
  450. */
  451. private ResultSetInternalMethods createResultSetUsingServerFetch(String sql)
  452. throws SQLException {
  453. java.sql.PreparedStatement pStmt = this.connection.prepareStatement(
  454. sql, this.resultSetType, this.resultSetConcurrency);
  455. pStmt.setFetchSize(this.fetchSize);
  456. if (this.maxRows > -1) {
  457. pStmt.setMaxRows(this.maxRows);
  458. }
  459. pStmt.execute();
  460. //
  461. // Need to be able to get resultset irrespective if we issued DML or
  462. // not to make this work.
  463. //
  464. ResultSetInternalMethods rs = ((com.mysql.jdbc.StatementImpl) pStmt)
  465. .getResultSetInternal();
  466. rs
  467. .setStatementUsedForFetchingRows((com.mysql.jdbc.PreparedStatement) pStmt);
  468. this.results = rs;
  469. return rs;
  470. }
  471. /**
  472. * We only stream result sets when they are forward-only, read-only, and the
  473. * fetch size has been set to Integer.MIN_VALUE
  474. *
  475. * @return true if this result set should be streamed row at-a-time, rather
  476. * than read all at once.
  477. */
  478. protected boolean createStreamingResultSet() {
  479. return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
  480. && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
  481. }
  482. private int originalResultSetType = 0;
  483. private int originalFetchSize = 0;
  484. /* (non-Javadoc)
  485. * @see com.mysql.jdbc.IStatement#enableStreamingResults()
  486. */
  487. public void enableStreamingResults() throws SQLException {
  488. this.originalResultSetType = this.resultSetType;
  489. this.originalFetchSize = this.fetchSize;
  490. setFetchSize(Integer.MIN_VALUE);
  491. setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
  492. }
  493. public void disableStreamingResults() throws SQLException {
  494. if (this.fetchSize == Integer.MIN_VALUE &&
  495. this.resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
  496. setFetchSize(this.originalFetchSize);
  497. setResultSetType(this.originalResultSetType);
  498. }
  499. }
  500. /**
  501. * Execute a SQL statement that may return multiple results. We don't have
  502. * to worry about this since we do not support multiple ResultSets. You can
  503. * use getResultSet or getUpdateCount to retrieve the result.
  504. *
  505. * @param sql
  506. * any SQL statement
  507. *
  508. * @return true if the next result is a ResulSet, false if it is an update
  509. * count or there are no more results
  510. *
  511. * @exception SQLException
  512. * if a database access error occurs
  513. */
  514. public boolean execute(String sql) throws SQLException {
  515. return execute(sql, false);
  516. }
  517. private boolean execute(String sql, boolean returnGeneratedKeys) throws SQLException {
  518. checkClosed();
  519. MySQLConnection locallyScopedConn = this.connection;
  520. synchronized (locallyScopedConn.getMutex()) {
  521. this.retrieveGeneratedKeys = returnGeneratedKeys;
  522. lastQueryIsOnDupKeyUpdate = false;
  523. if (returnGeneratedKeys)
  524. lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyInString(sql);
  525. resetCancelledState();
  526. checkNullOrEmptyQuery(sql);
  527. checkClosed();
  528. char firstNonWsChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql));
  529. boolean isSelect = true;
  530. if (firstNonWsChar != 'S') {
  531. isSelect = false;
  532. if (locallyScopedConn.isReadOnly()) {
  533. throw SQLError.createSQLException(Messages
  534. .getString("Statement.27") //$NON-NLS-1$
  535. + Messages.getString("Statement.28"), //$NON-NLS-1$
  536. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
  537. }
  538. }
  539. boolean doStreaming = createStreamingResultSet();
  540. // Adjust net_write_timeout to a higher value if we're
  541. // streaming result sets. More often than not, someone runs into
  542. // an issue where they blow net_write_timeout when using this
  543. // feature, and if they're willing to hold a result set open
  544. // for 30 seconds or more, one more round-trip isn't going to hurt
  545. //
  546. // This is reset by RowDataDynamic.close().
  547. if (doStreaming
  548. && locallyScopedConn.getNetTimeoutForStreamingResults() > 0) {
  549. executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
  550. + locallyScopedConn.getNetTimeoutForStreamingResults());
  551. }
  552. if (this.doEscapeProcessing) {
  553. Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
  554. locallyScopedConn.serverSupportsConvertFn(), locallyScopedConn);
  555. if (escapedSqlResult instanceof String) {
  556. sql = (String) escapedSqlResult;
  557. } else {
  558. sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
  559. }
  560. }
  561. if (this.results != null) {
  562. if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
  563. this.results.realClose(false);
  564. }
  565. }
  566. if (sql.charAt(0) == '/') {
  567. if (sql.startsWith(PING_MARKER)) {
  568. doPingInstead();
  569. return true;
  570. }
  571. }
  572. CachedResultSetMetaData cachedMetaData = null;
  573. ResultSetInternalMethods rs = null;
  574. // If there isn't a limit clause in the SQL
  575. // then limit the number of rows to return in
  576. // an efficient manner. Only do this if
  577. // setMaxRows() hasn't been used on any Statements
  578. // generated from the current Connection (saves
  579. // a query, and network traffic).
  580. this.batchedGeneratedKeys = null;
  581. if (useServerFetch()) {
  582. rs = createResultSetUsingServerFetch(sql);
  583. } else {
  584. CancelTask timeoutTask = null;
  585. String oldCatalog = null;
  586. try {
  587. if (locallyScopedConn.getEnableQueryTimeouts() &&
  588. this.timeoutInMillis != 0
  589. && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
  590. timeoutTask = new CancelTask(this);
  591. locallyScopedConn.getCancelTimer().schedule(timeoutTask,
  592. this.timeoutInMillis);
  593. }
  594. if (!locallyScopedConn.getCatalog().equals(
  595. this.currentCatalog)) {
  596. oldCatalog = locallyScopedConn.getCatalog();
  597. locallyScopedConn.setCatalog(this.currentCatalog);
  598. }
  599. //
  600. // Check if we have cached metadata for this query...
  601. //
  602. Field[] cachedFields = null;
  603. if (locallyScopedConn.getCacheResultSetMetadata()) {
  604. cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
  605. if (cachedMetaData != null) {
  606. cachedFields = cachedMetaData.fields;
  607. }
  608. }
  609. //
  610. // Only apply max_rows to selects
  611. //
  612. if (locallyScopedConn.useMaxRows()) {
  613. int rowLimit = -1;
  614. if (isSelect) {
  615. if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
  616. rowLimit = this.maxRows;
  617. } else {
  618. if (this.maxRows <= 0) {
  619. executeSimpleNonQuery(locallyScopedConn,
  620. "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
  621. } else {
  622. executeSimpleNonQuery(locallyScopedConn,
  623. "SET OPTION SQL_SELECT_LIMIT="
  624. + this.maxRows);
  625. }
  626. }
  627. } else {
  628. executeSimpleNonQuery(locallyScopedConn,
  629. "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
  630. }
  631. // Finally, execute the query
  632. rs = locallyScopedConn.execSQL(this, sql, rowLimit, null,
  633. this.resultSetType, this.resultSetConcurrency,
  634. doStreaming,
  635. this.currentCatalog, cachedFields);
  636. } else {
  637. rs = locallyScopedConn.execSQL(this, sql, -1, null,
  638. this.resultSetType, this.resultSetConcurrency,
  639. doStreaming,
  640. this.currentCatalog, cachedFields);
  641. }
  642. if (timeoutTask != null) {
  643. if (timeoutTask.caughtWhileCancelling != null) {
  644. throw timeoutTask.caughtWhileCancelling;
  645. }
  646. timeoutTask.cancel();
  647. timeoutTask = null;
  648. }
  649. synchronized (this.cancelTimeoutMutex) {
  650. if (this.wasCancelled) {
  651. SQLException cause = null;
  652. if (this.wasCancelledByTimeout) {
  653. cause = new MySQLTimeoutException();
  654. } else {
  655. cause = new MySQLStatementCancelledException();
  656. }
  657. resetCancelledState();
  658. throw cause;
  659. }
  660. }
  661. } finally {
  662. if (timeoutTask != null) {
  663. timeoutTask.cancel();
  664. locallyScopedConn.getCancelTimer().purge();
  665. }
  666. if (oldCatalog != null) {
  667. locallyScopedConn.setCatalog(oldCatalog);
  668. }
  669. }
  670. }
  671. if (rs != null) {
  672. this.lastInsertId = rs.getUpdateID();
  673. this.results = rs;
  674. rs.setFirstCharOfQuery(firstNonWsChar);
  675. if (rs.reallyResult()) {
  676. if (cachedMetaData != null) {
  677. locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
  678. this.results);
  679. } else {
  680. if (this.connection.getCacheResultSetMetadata()) {
  681. locallyScopedConn.initializeResultsMetadataFromCache(sql,
  682. null /* will be created */, this.results);
  683. }
  684. }
  685. }
  686. }
  687. return ((rs != null) && rs.reallyResult());
  688. }
  689. }
  690. protected synchronized void resetCancelledState() {
  691. if (this.cancelTimeoutMutex == null) {
  692. return;
  693. }
  694. synchronized (this.cancelTimeoutMutex) {
  695. this.wasCancelled = false;
  696. this.wasCancelledByTimeout = false;
  697. }
  698. }
  699. /**
  700. * @see StatementImpl#execute(String, int)
  701. */
  702. public boolean execute(String sql, int returnGeneratedKeys)
  703. throws SQLException {
  704. if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
  705. checkClosed();
  706. MySQLConnection locallyScopedConn = this.connection;
  707. synchronized (locallyScopedConn.getMutex()) {
  708. // If this is a 'REPLACE' query, we need to be able to parse
  709. // the 'info' message returned from the server to determine
  710. // the actual number of keys generated.
  711. boolean readInfoMsgState = this.connection
  712. .isReadInfoMsgEnabled();
  713. locallyScopedConn.setReadInfoMsgEnabled(true);
  714. try {
  715. return execute(sql, true);
  716. } finally {
  717. locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
  718. }
  719. }
  720. }
  721. return execute(sql);
  722. }
  723. /**
  724. * @see StatementImpl#execute(String, int[])
  725. */
  726. public boolean execute(String sql, int[] generatedKeyIndices)
  727. throws SQLException {
  728. if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
  729. checkClosed();
  730. MySQLConnection locallyScopedConn = this.connection;
  731. synchronized (locallyScopedConn.getMutex()) {
  732. this.retrieveGeneratedKeys = true;
  733. // If this is a 'REPLACE' query, we need to be able to parse
  734. // the 'info' message returned from the server to determine
  735. // the actual number of keys generated.
  736. boolean readInfoMsgState = locallyScopedConn
  737. .isReadInfoMsgEnabled();
  738. locallyScopedConn.setReadInfoMsgEnabled(true);
  739. try {
  740. return execute(sql, true);
  741. } finally {
  742. locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
  743. }
  744. }
  745. }
  746. return execute(sql);
  747. }
  748. /**
  749. * @see StatementImpl#execute(String, String[])
  750. */
  751. public boolean execute(String sql, String[] generatedKeyNames)
  752. throws SQLException {
  753. if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
  754. checkClosed();
  755. MySQLConnection locallyScopedConn = this.connection;
  756. synchronized (locallyScopedConn.getMutex()) {
  757. this.retrieveGeneratedKeys = true;
  758. // If this is a 'REPLACE' query, we need to be able to parse
  759. // the 'info' message returned from the server to determine
  760. // the actual number of keys generated.
  761. boolean readInfoMsgState = this.connection
  762. .isReadInfoMsgEnabled();
  763. locallyScopedConn.setReadInfoMsgEnabled(true);
  764. try {
  765. return execute(sql, true);
  766. } finally {
  767. locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
  768. }
  769. }
  770. }
  771. return execute(sql);
  772. }
  773. /**
  774. * JDBC 2.0 Submit a batch of commands to the database for execution. This
  775. * method is optional.
  776. *
  777. * @return an array of update counts containing one element for each command
  778. * in the batch. The array is ordered according to the order in
  779. * which commands were inserted into the batch
  780. *
  781. * @exception SQLException
  782. * if a database-access error occurs, or the driver does not
  783. * support batch statements
  784. * @throws java.sql.BatchUpdateException
  785. * DOCUMENT ME!
  786. */
  787. public synchronized int[] executeBatch() throws SQLException {
  788. checkClosed();
  789. MySQLConnection locallyScopedConn = this.connection;
  790. if (locallyScopedConn.isReadOnly()) {
  791. throw SQLError.createSQLException(Messages
  792. .getString("Statement.34") //$NON-NLS-1$
  793. + Messages.getString("Statement.35"), //$NON-NLS-1$
  794. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
  795. }
  796. if (this.results != null) {
  797. if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
  798. this.results.realClose(false);
  799. }
  800. }
  801. synchronized (locallyScopedConn.getMutex()) {
  802. if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
  803. return new int[0];
  804. }
  805. // we timeout the entire batch, not individual statements
  806. int individualStatementTimeout = this.timeoutInMillis;
  807. this.timeoutInMillis = 0;
  808. CancelTask timeoutTask = null;
  809. try {
  810. resetCancelledState();
  811. this.retrieveGeneratedKeys = true; // The JDBC spec doesn't forbid this, but doesn't provide for it either...we do..
  812. int[] updateCounts = null;
  813. if (this.batchedArgs != null) {
  814. int nbrCommands = this.batchedArgs.size();
  815. this.batchedGeneratedKeys = new ArrayList(this.batchedArgs.size());
  816. boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
  817. if (locallyScopedConn.versionMeetsMinimum(4, 1, 1) &&
  818. (multiQueriesEnabled ||
  819. (locallyScopedConn.getRewriteBatchedStatements() &&
  820. nbrCommands > 4))) {
  821. return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands, individualStatementTimeout);
  822. }
  823. if (locallyScopedConn.getEnableQueryTimeouts() &&
  824. individualStatementTimeout != 0
  825. && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
  826. timeoutTask = new CancelTask(this);
  827. locallyScopedConn.getCancelTimer().schedule(timeoutTask,
  828. individualStatementTimeout);
  829. }
  830. updateCounts = new int[nbrCommands];
  831. for (int i = 0; i < nbrCommands; i++) {
  832. updateCounts[i] = -3;
  833. }
  834. SQLException sqlEx = null;
  835. int commandIndex = 0;
  836. for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
  837. try {
  838. String sql = (String) this.batchedArgs.get(commandIndex);
  839. updateCounts[commandIndex] = executeUpdate(sql, true, true);
  840. // limit one generated key per OnDuplicateKey statement
  841. getBatchedGeneratedKeys(containsOnDuplicateKeyInString(sql) ? 1 : 0);
  842. } catch (SQLException ex) {
  843. updateCounts[commandIndex] = EXECUTE_FAILED;
  844. if (this.continueBatchOnError &&
  845. !(ex instanceof MySQLTimeoutException) &&
  846. !(ex instanceof MySQLStatementCancelledException) &&
  847. !hasDeadlockOrTimeoutRolledBackTx(ex)) {
  848. sqlEx = ex;
  849. } else {
  850. int[] newUpdateCounts = new int[commandIndex];
  851. if (hasDeadlockOrTimeoutRolledBackTx(ex)) {
  852. for (int i = 0; i < newUpdateCounts.length; i++) {
  853. newUpdateCounts[i] = Statement.EXECUTE_FAILED;
  854. }
  855. } else {
  856. System.arraycopy(updateCounts, 0,
  857. newUpdateCounts, 0, commandIndex);
  858. }
  859. throw new java.sql.BatchUpdateException(ex
  860. .getMessage(), ex.getSQLState(), ex
  861. .getErrorCode(), newUpdateCounts);
  862. }
  863. }
  864. }
  865. if (sqlEx != null) {
  866. throw new java.sql.BatchUpdateException(sqlEx
  867. .getMessage(), sqlEx.getSQLState(), sqlEx
  868. .getErrorCode(), updateCounts);
  869. }
  870. }
  871. if (timeoutTask != null) {
  872. if (timeoutTask.caughtWhileCancelling != null) {
  873. throw timeoutTask.caughtWhileCancelling;
  874. }
  875. timeoutTask.cancel();
  876. locallyScopedConn.getCancelTimer().purge();
  877. timeoutTask = null;
  878. }
  879. return (updateCounts != null) ? updateCounts : new int[0];
  880. } finally {
  881. if (timeoutTask != null) {
  882. timeoutTask.cancel();
  883. locallyScopedConn.getCancelTimer().purge();
  884. }
  885. resetCancelledState();
  886. this.timeoutInMillis = individualStatementTimeout;
  887. clearBatch();
  888. }
  889. }
  890. }
  891. protected final boolean hasDeadlockOrTimeoutRolledBackTx(SQLException ex) {
  892. int vendorCode = ex.getErrorCode();
  893. switch (vendorCode) {
  894. case MysqlErrorNumbers.ER_LOCK_DEADLOCK:
  895. case MysqlErrorNumbers.ER_LOCK_TABLE_FULL:
  896. return true;
  897. case MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT:
  898. try {
  899. return !this.connection.versionMeetsMinimum(5, 0, 13);
  900. } catch (SQLException sqlEx) {
  901. // won't actually be thrown in this case
  902. return false;
  903. }
  904. default:
  905. return false;
  906. }
  907. }
  908. /**
  909. * Rewrites batch into a single query to send to the server. This method
  910. * will constrain each batch to be shorter than max_allowed_packet on the
  911. * server.
  912. *
  913. * @return update counts in the same manner as executeBatch()
  914. * @throws SQLException
  915. */
  916. private int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
  917. int nbrCommands, int individualStatementTimeout) throws SQLException {
  918. MySQLConnection locallyScopedConn = this.connection;
  919. if (!multiQueriesEnabled) {
  920. locallyScopedConn.getIO().enableMultiQueries();
  921. }
  922. java.sql.Statement batchStmt = null;
  923. CancelTask timeoutTask = null;
  924. try {
  925. int[] updateCounts = new int[nbrCommands];
  926. for (int i = 0; i < nbrCommands; i++) {
  927. updateCounts[i] = -3;
  928. }
  929. int commandIndex = 0;
  930. StringBuffer queryBuf = new StringBuffer();
  931. batchStmt = locallyScopedConn.createStatement();
  932. if (locallyScopedConn.getEnableQueryTimeouts() &&
  933. individualStatementTimeout != 0
  934. && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
  935. timeoutTask = new CancelTask((StatementImpl)batchStmt);
  936. locallyScopedConn.getCancelTimer().schedule(timeoutTask,
  937. individualStatementTimeout);
  938. }
  939. int counter = 0;
  940. int numberOfBytesPerChar = 1;
  941. String connectionEncoding = locallyScopedConn.getEncoding();
  942. if (StringUtils.startsWithIgnoreCase(connectionEncoding, "utf")) {
  943. numberOfBytesPerChar = 3;
  944. } else if (CharsetMapping.isMultibyteCharset(connectionEncoding)) {
  945. numberOfBytesPerChar = 2;
  946. }
  947. int escapeAdjust = 1;
  948. batchStmt.setEscapeProcessing(this.doEscapeProcessing);
  949. if (this.doEscapeProcessing) {
  950. escapeAdjust = 2; /* We assume packet _could_ grow by this amount, as we're not
  951. sure how big statement will end up after
  952. escape processing */
  953. }
  954. SQLException sqlEx = null;
  955. int argumentSetsInBatchSoFar = 0;
  956. for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
  957. String nextQuery = (String) this.batchedArgs.get(commandIndex);
  958. if (((((queryBuf.length() + nextQuery.length())
  959. * numberOfBytesPerChar) + 1 /* for semicolon */
  960. + MysqlIO.HEADER_LENGTH) * escapeAdjust) + 32 > this.connection
  961. .getMaxAllowedPacket()) {
  962. try {
  963. batchStmt.execute(queryBuf.toString(), Statement.RETURN_GENERATED_KEYS);
  964. } catch (SQLException ex) {
  965. sqlEx = handleExceptionForBatch(commandIndex,
  966. argumentSetsInBatchSoFar, updateCounts, ex);
  967. }
  968. counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter,
  969. updateCounts);
  970. queryBuf = new StringBuffer();
  971. argumentSetsInBatchSoFar = 0;
  972. }
  973. queryBuf.append(nextQuery);
  974. queryBuf.append(";");
  975. argumentSetsInBatchSoFar++;
  976. }
  977. if (queryBuf.length() > 0) {
  978. try {
  979. batchStmt.execute(queryBuf.toString(), Statement.RETURN_GENERATED_KEYS);
  980. } catch (SQLException ex) {
  981. sqlEx = handleExceptionForBatch(commandIndex - 1,
  982. argumentSetsInBatchSoFar, updateCounts, ex);
  983. }
  984. counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter,
  985. updateCounts);
  986. }
  987. if (timeoutTask != null) {
  988. if (timeoutTask.caughtWhileCancelling != null) {
  989. throw timeoutTask.caughtWhileCancelling;
  990. }
  991. timeoutTask.cancel();
  992. locallyScopedConn.getCancelTimer().purge();
  993. timeoutTask = null;
  994. }
  995. if (sqlEx != null) {
  996. throw new java.sql.BatchUpdateException(sqlEx
  997. .getMessage(), sqlEx.getSQLState(), sqlEx
  998. .getErrorCode(), updateCounts);
  999. }
  1000. return (updateCounts != null) ? updateCounts : new int[0];
  1001. } finally {
  1002. if (timeoutTask != null) {
  1003. timeoutTask.cancel();
  1004. locallyScopedConn.getCancelTimer().purge();
  1005. }
  1006. resetCancelledState();
  1007. try {
  1008. if (batchStmt != null) {
  1009. batchStmt.close();
  1010. }
  1011. } finally {
  1012. if (!multiQueriesEnabled) {
  1013. locallyScopedConn.getIO().disableMultiQueries();
  1014. }
  1015. }
  1016. }
  1017. }
  1018. protected int processMultiCountsAndKeys(
  1019. StatementImpl batchedStatement,
  1020. int updateCountCounter, int[] updateCounts) throws SQLException {
  1021. updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
  1022. boolean doGenKeys = this.batchedGeneratedKeys != null;
  1023. byte[][] row = null;
  1024. if (doGenKeys) {
  1025. long generatedKey = batchedStatement.getLastInsertID();
  1026. row = new byte[1][];
  1027. row[0] = Long.toString(generatedKey).getBytes();
  1028. this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
  1029. }
  1030. while (batchedStatement.getMoreResults()
  1031. || batchedStatement.getUpdateCount() != -1) {
  1032. updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
  1033. if (doGenKeys) {
  1034. long generatedKey = batchedStatement.getLastInsertID();
  1035. row = new byte[1][];
  1036. row[0] = Long.toString(generatedKey).getBytes();
  1037. this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
  1038. }
  1039. }
  1040. return updateCountCounter;
  1041. }
  1042. protected SQLException handleExceptionForBatch(int endOfBatchIndex,
  1043. int numValuesPerBatch, int[] updateCounts, SQLException ex)
  1044. throws BatchUpdateException {
  1045. SQLException sqlEx;
  1046. for (int j = endOfBatchIndex; j > endOfBatchIndex - numValuesPerBatch; j--) {
  1047. updateCounts[j] = EXECUTE_FAILED;
  1048. }
  1049. if (this.continueBatchOnError &&
  1050. !(ex instanceof MySQLTimeoutException) &&
  1051. !(ex instanceof MySQLStatementCancelledException) &&
  1052. !hasDeadlockOrTimeoutRolledBackTx(ex)) {
  1053. sqlEx = ex;
  1054. } else {
  1055. int[] newUpdateCounts = new int[endOfBatchIndex];
  1056. System.arraycopy(updateCounts, 0,
  1057. newUpdateCounts, 0, endOfBatchIndex);
  1058. throw new java.sql.BatchUpdateException(ex
  1059. .getMessage(), ex.getSQLState(), ex
  1060. .getErrorCode(), newUpdateCounts);
  1061. }
  1062. return sqlEx;
  1063. }
  1064. /**
  1065. * Execute a SQL statement that retruns a single ResultSet
  1066. *
  1067. * @param sql
  1068. * typically a static SQL SELECT statement
  1069. *
  1070. * @return a ResulSet that contains the data produced by the query
  1071. *
  1072. * @exception SQLException
  1073. * if a database access error occurs
  1074. */
  1075. public java.sql.ResultSet executeQuery(String sql)
  1076. throws SQLException {
  1077. checkClosed();
  1078. MySQLConnection locallyScopedConn = this.connection;
  1079. synchronized (locallyScopedConn.getMutex()) {
  1080. this.retrieveGeneratedKeys = false;
  1081. resetCancelledState();
  1082. checkNullOrEmptyQuery(sql);
  1083. boolean doStreaming = createStreamingResultSet();
  1084. // Adjust net_write_timeout to a higher value if we're
  1085. // streaming result sets. More often than not, someone runs into
  1086. // an issue where they blow net_write_timeout when using this
  1087. // feature, and if they're willing to hold a result set open
  1088. // for 30 seconds or more, one more round-trip isn't going to hurt
  1089. //
  1090. // This is reset by RowDataDynamic.close().
  1091. if (doStreaming
  1092. && this.connection.getNetTimeoutForStreamingResults() > 0) {
  1093. executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
  1094. + this.connection.getNetTimeoutForStreamingResults());
  1095. }
  1096. if (this.doEscapeProcessing) {
  1097. Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
  1098. locallyScopedConn.serverSupportsConvertFn(), this.connection);
  1099. if (escapedSqlResult instanceof String) {
  1100. sql = (String) escapedSqlResult;
  1101. } else {
  1102. sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
  1103. }
  1104. }
  1105. char firstStatementChar = StringUtils.firstNonWsCharUc(sql,
  1106. findStartOfStatement(sql));
  1107. if (sql.charAt(0) == '/') {
  1108. if (sql.startsWith(PING_MARKER)) {
  1109. doPingInstead();
  1110. return this.results;
  1111. }
  1112. }
  1113. checkForDml(sql, firstStatementChar);
  1114. if (this.results != null) {
  1115. if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
  1116. this.results.realClose(false);
  1117. }
  1118. }
  1119. CachedResultSetMetaData cachedMetaData = null;
  1120. // If there isn't a limit clause in the SQL
  1121. // then limit the number of rows to return in
  1122. // an efficient manner. Only do this if
  1123. // setMaxRows() hasn't been used on any Statements
  1124. // generated from the current Connection (saves
  1125. // a query, and network traffic).
  1126. if (useServerFetch()) {
  1127. this.results = createResultSetUsingServerFetch(sql);
  1128. return this.results;
  1129. }
  1130. CancelTask timeoutTask = null;
  1131. String oldCatalog = null;
  1132. try {
  1133. if (locallyScopedConn.getEnableQueryTimeouts() &&
  1134. this.timeoutInMillis != 0
  1135. && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
  1136. timeoutTask = new CancelTask(this);
  1137. locallyScopedConn.getCancelTimer().schedule(timeoutTask,
  1138. this.timeoutInMillis);
  1139. }
  1140. if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
  1141. oldCatalog = locallyScopedConn.getCatalog();
  1142. locallyScopedConn.setCatalog(this.currentCatalog);
  1143. }
  1144. //
  1145. // Check if we have cached metadata for this query...
  1146. //
  1147. Field[] cachedFields = null;
  1148. if (locallyScopedConn.getCacheResultSetMetadata()) {
  1149. cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
  1150. if (cachedMetaData != null) {
  1151. cachedFields = cachedMetaData.fields;
  1152. }
  1153. }
  1154. if (locallyScopedConn.useMaxRows()) {
  1155. // We need to execute this all together
  1156. // So synchronize on the Connection's mutex (because
  1157. // even queries going through there synchronize
  1158. // on the connection
  1159. if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
  1160. this.results = locallyScopedConn.execSQL(this, sql,
  1161. this.maxRows, null, this.resultSetType,
  1162. this.resultSetConcurrency,
  1163. doStreaming,
  1164. this.currentCatalog, cachedFields);
  1165. } else {
  1166. if (this.maxRows <= 0) {
  1167. executeSimpleNonQuery(locallyScopedConn,
  1168. "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
  1169. } else {
  1170. executeSimpleNonQuery(locallyScopedConn,
  1171. "SET OPTION SQL_SELECT_LIMIT=" + this.maxRows);
  1172. }
  1173. this.results = locallyScopedConn.execSQL(this, sql, -1,
  1174. null, this.resultSetType,
  1175. this.resultSetConcurrency,
  1176. doStreaming,
  1177. this.currentCatalog, cachedFields);
  1178. if (oldCatalog != null) {
  1179. locallyScopedConn.setCatalog(oldCatalog);
  1180. }
  1181. }
  1182. } else {
  1183. this.results = locallyScopedConn.execSQL(this, sql, -1, null,
  1184. this.resultSetType, this.resultSetConcurrency,
  1185. doStreaming,
  1186. this.currentCatalog, cachedFields);
  1187. }
  1188. if (timeoutTask != null) {
  1189. if (timeoutTask.caughtWhileCancelling != null) {
  1190. throw timeoutTask.caughtWhileCancelling;
  1191. }
  1192. timeoutTask.cancel();
  1193. locallyScopedConn.getCancelTimer().purge();
  1194. timeoutTask = null;
  1195. }
  1196. synchronized (this.cancelTimeoutMutex) {
  1197. if (this.wasCancelled) {
  1198. SQLException cause = null;
  1199. if (this.wasCancelledByTimeout) {
  1200. cause = new MySQLTimeoutException();
  1201. } else {
  1202. cause = new MySQLStatementCancelledException();
  1203. }
  1204. resetCancelledState();
  1205. throw cause;
  1206. }
  1207. }
  1208. } finally {
  1209. if (timeoutTask != null) {
  1210. timeoutTask.cancel();
  1211. locallyScopedConn.getCancelTimer().purge();
  1212. }
  1213. if (oldCatalog != null) {
  1214. locallyScopedConn.setCatalog(oldCatalog);
  1215. }
  1216. }
  1217. this.lastInsertId = this.results.getUpdateID();
  1218. if (cachedMetaData != null) {
  1219. locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
  1220. this.results);
  1221. } else {
  1222. if (this.connection.getCacheResultSetMetadata()) {
  1223. locallyScopedConn.initializeResultsMetadataFromCache(sql,
  1224. null /* will be created */, this.results);
  1225. }
  1226. }
  1227. return this.results;
  1228. }
  1229. }
  1230. protected void doPingInstead() throws SQLException {
  1231. if (this.pingTarget != null) {
  1232. this.pingTarget.doPing();
  1233. } else {
  1234. this.connection.ping();
  1235. }
  1236. ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet();
  1237. this.results = fakeSelectOneResultSet;
  1238. }
  1239. protected ResultSetInternalMethods generatePingResultSet() throws SQLException {
  1240. Field[] fields = { new Field(null, "1", Types.BIGINT, 1) };
  1241. ArrayList rows = new ArrayList();
  1242. byte[] colVal = new byte[] { (byte) '1' };
  1243. rows.add(new ByteArrayRow(new byte[][] { colVal }, getExceptionInterceptor()));
  1244. return (ResultSetInternalMethods) DatabaseMetaData.buildResultSet(fields, rows,
  1245. this.connection);
  1246. }
  1247. protected void executeSimpleNonQuery(MySQLConnection c, String nonQuery)
  1248. throws SQLException {
  1249. c.execSQL(this, nonQuery,
  1250. -1, null, ResultSet.TYPE_FORWARD_ONLY,
  1251. ResultSet.CONCUR_READ_ONLY, false, this.currentCatalog,
  1252. null, false).close();
  1253. }
  1254. /**
  1255. * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
  1256. * statements that return nothing such as SQL DDL statements can be executed
  1257. * Any IDs generated for AUTO_INCREMENT fields can be retrieved by casting
  1258. * this Statement to org.gjt.mm.mysql.Statement and calling the
  1259. * getLastInsertID() method.
  1260. *
  1261. * @param sql
  1262. * a SQL statement
  1263. *
  1264. * @return either a row count, or 0 for SQL commands
  1265. *
  1266. * @exception SQLException
  1267. * if a database access error occurs
  1268. */
  1269. public int executeUpdate(String sql) throws SQLException {
  1270. return executeUpdate(sql, false, false);
  1271. }
  1272. protected int executeUpdate(String sql, boolean isBatch, boolean returnGeneratedKeys)
  1273. throws SQLException {
  1274. checkClosed();
  1275. MySQLConnection locallyScopedConn = this.connection;
  1276. char firstStatementChar = StringUtils.firstAlphaCharUc(sql,
  1277. findStartOfStatement(sql));
  1278. ResultSetInternalMethods rs = null;
  1279. synchronized (locallyScopedConn.getMutex()) {
  1280. this.retrieveGeneratedKeys = returnGeneratedKeys;
  1281. resetCancelledState();
  1282. checkNullOrEmptyQuery(sql);
  1283. if (this.doEscapeProcessing) {
  1284. Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
  1285. this.connection.serverSupportsConvertFn(), this.connection);
  1286. if (escapedSqlResult instanceof String) {
  1287. sql = (String) escapedSqlResult;
  1288. } else {
  1289. sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
  1290. }
  1291. }
  1292. if (locallyScopedConn.isReadOnly()) {
  1293. throw SQLError.createSQLException(Messages
  1294. .getString("Statement.42") //$NON-NLS-1$
  1295. + Messages.getString("Statement.43"), //$NON-NLS-1$
  1296. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
  1297. }
  1298. if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { //$NON-NLS-1$
  1299. throw SQLError.createSQLException(Messages
  1300. .getString("Statement.46"), //$NON-NLS-1$
  1301. "01S03", getExceptionInterceptor()); //$NON-NLS-1$
  1302. }
  1303. if (this.results != null) {
  1304. if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
  1305. this.results.realClose(false);
  1306. }
  1307. }
  1308. // The checking and changing of catalogs
  1309. // must happen in sequence, so synchronize
  1310. // on the same mutex that _conn is using
  1311. CancelTask timeoutTask = null;
  1312. String oldCatalog = null;
  1313. try {
  1314. if (locallyScopedConn.getEnableQueryTimeouts() &&
  1315. this.timeoutInMillis != 0
  1316. && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
  1317. timeoutTask = new CancelTask(this);
  1318. locallyScopedConn.getCancelTimer().schedule(timeoutTask,
  1319. this.timeoutInMillis);
  1320. }
  1321. if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
  1322. oldCatalog = locallyScopedConn.getCatalog();
  1323. locallyScopedConn.setCatalog(this.currentCatalog);
  1324. }
  1325. //
  1326. // Only apply max_rows to selects
  1327. //
  1328. if (locallyScopedConn.useMaxRows()) {
  1329. executeSimpleNonQuery(locallyScopedConn,
  1330. "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
  1331. }
  1332. rs = locallyScopedConn.execSQL(this, sql, -1, null,
  1333. java.sql.ResultSet.TYPE_FORWARD_ONLY,
  1334. java.sql.ResultSet.CONCUR_READ_ONLY, false,
  1335. this.currentCatalog,
  1336. null /* force read of field info on DML */,
  1337. isBatch);
  1338. if (timeoutTask != null) {
  1339. if (timeoutTask.caughtWhileCancelling != null) {
  1340. throw timeoutTask.caughtWhileCancelling;
  1341. }
  1342. timeoutTask.cancel();
  1343. locallyScopedConn.getCancelTimer().purge();
  1344. timeoutTask = null;
  1345. }
  1346. synchronized (this.cancelTimeoutMutex) {
  1347. if (this.wasCancelled) {
  1348. SQLException cause = null;
  1349. if (this.wasCancelledByTimeout) {
  1350. cause = new MySQLTimeoutException();
  1351. } else {
  1352. cause = new MySQLStatementCancelledException();
  1353. }
  1354. resetCancelledState();
  1355. throw cause;
  1356. }
  1357. }
  1358. } finally {
  1359. if (timeoutTask != null) {
  1360. timeoutTask.cancel();
  1361. locallyScopedConn.getCancelTimer().purge();
  1362. }
  1363. if (oldCatalog != null) {
  1364. locallyScopedConn.setCatalog(oldCatalog);
  1365. }
  1366. }
  1367. }
  1368. this.results = rs;
  1369. rs.setFirstCharOfQuery(firstStatementChar);
  1370. this.updateCount = rs.getUpdateCount();
  1371. int truncatedUpdateCount = 0;
  1372. if (this.updateCount > Integer.MAX_VALUE) {
  1373. truncatedUpdateCount = Integer.MAX_VALUE;
  1374. } else {
  1375. truncatedUpdateCount = (int) this.updateCount;
  1376. }
  1377. this.lastInsertId = rs.getUpdateID();
  1378. return truncatedUpdateCount;
  1379. }
  1380. /**
  1381. * @see StatementImpl#executeUpdate(String, int)
  1382. */
  1383. public int executeUpdate(String sql, int returnGeneratedKeys)
  1384. throws SQLException {
  1385. if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
  1386. checkClosed();
  1387. MySQLConnection locallyScopedConn = this.connection;
  1388. synchronized (locallyScopedConn.getMutex()) {
  1389. // If this is a 'REPLACE' query, we need to be able to parse
  1390. // the 'info' message returned from the server to determine
  1391. // the actual number of keys generated.
  1392. boolean readInfoMsgState = locallyScopedConn
  1393. .isReadInfoMsgEnabled();
  1394. locallyScopedConn.setReadInfoMsgEnabled(true);
  1395. try {
  1396. return executeUpdate(sql, false, true);
  1397. } finally {
  1398. locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
  1399. }
  1400. }
  1401. }
  1402. return executeUpdate(sql);
  1403. }
  1404. /**
  1405. * @see StatementImpl#executeUpdate(String, int[])
  1406. */
  1407. public int executeUpdate(String sql, int[] generatedKeyIndices)
  1408. throws SQLException {
  1409. if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
  1410. checkClosed();
  1411. MySQLConnection locallyScopedConn = this.connection;
  1412. synchronized (locallyScopedConn.getMutex()) {
  1413. // If this is a 'REPLACE' query, we need to be able to parse
  1414. // the 'info' message returned from the server to determine
  1415. // the actual number of keys generated.
  1416. boolean readInfoMsgState = locallyScopedConn
  1417. .isReadInfoMsgEnabled();
  1418. locallyScopedConn.setReadInfoMsgEnabled(true);
  1419. try {
  1420. return executeUpdate(sql, false, true);
  1421. } finally {
  1422. locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
  1423. }
  1424. }
  1425. }
  1426. return executeUpdate(sql);
  1427. }
  1428. /**
  1429. * @see StatementImpl#executeUpdate(String, String[])
  1430. */
  1431. public int executeUpdate(String sql, String[] generatedKeyNames)
  1432. throws SQLException {
  1433. if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
  1434. checkClosed();
  1435. MySQLConnection locallyScopedConn = this.connection;
  1436. synchronized (locallyScopedConn.getMutex()) {
  1437. // If this is a 'REPLACE' query, we need to be able to parse
  1438. // the 'info' message returned from the server to determine
  1439. // the actual number of keys generated.
  1440. boolean readInfoMsgState = this.connection
  1441. .isReadInfoMsgEnabled();
  1442. locallyScopedConn.setReadInfoMsgEnabled(true);
  1443. try {
  1444. return executeUpdate(sql, false, true);
  1445. } finally {
  1446. locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
  1447. }
  1448. }
  1449. }
  1450. return executeUpdate(sql);
  1451. }
  1452. /**
  1453. * Optimization to only use one calendar per-session, or calculate it for
  1454. * each call, depending on user configuration
  1455. */
  1456. protected Calendar getCalendarInstanceForSessionOrNew() {
  1457. if (this.connection != null) {
  1458. return this.connection.getCalendarInstanceForSessionOrNew();
  1459. } else {
  1460. // punt, no connection around
  1461. return new GregorianCalendar();
  1462. }
  1463. }
  1464. /**
  1465. * JDBC 2.0 Return the Connection that produced the Statement.
  1466. *
  1467. * @return the Connection that produced the Statement
  1468. *
  1469. * @throws SQLException
  1470. * if an error occurs
  1471. */
  1472. public java.sql.Connection getConnection() throws SQLException {
  1473. return this.connection;
  1474. }
  1475. /**
  1476. * JDBC 2.0 Determine the fetch direction.
  1477. *
  1478. * @return the default fetch direction
  1479. *
  1480. * @exception SQLException
  1481. * if a database-access error occurs
  1482. */
  1483. public int getFetchDirection() throws SQLException {
  1484. return java.sql.ResultSet.FETCH_FORWARD;
  1485. }
  1486. /**
  1487. * JDBC 2.0 Determine the default fetch size.
  1488. *
  1489. * @return the number of rows to fetch at a time
  1490. *
  1491. * @throws SQLException
  1492. * if an error occurs
  1493. */
  1494. public int getFetchSize() throws SQLException {
  1495. return this.fetchSize;
  1496. }
  1497. /**
  1498. * DOCUMENT ME!
  1499. *
  1500. * @return DOCUMENT ME!
  1501. *
  1502. * @throws SQLException
  1503. * DOCUMENT ME!
  1504. */
  1505. public synchronized java.sql.ResultSet getGeneratedKeys()
  1506. throws SQLException {
  1507. if (!this.retrieveGeneratedKeys) {
  1508. throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  1509. }
  1510. if (this.batchedGeneratedKeys == null) {
  1511. if (lastQueryIsOnDupKeyUpdate)
  1512. return getGeneratedKeysInternal(1);
  1513. else
  1514. return getGeneratedKeysInternal();
  1515. }
  1516. Field[] fields = new Field[1];
  1517. fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
  1518. fields[0].setConnection(this.connection);
  1519. return com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
  1520. new RowDataStatic(this.batchedGeneratedKeys), this.connection,
  1521. this, false);
  1522. }
  1523. /*
  1524. * Needed because there's no concept of super.super to get to this
  1525. * implementation from ServerPreparedStatement when dealing with batched
  1526. * updates.
  1527. */
  1528. protected java.sql.ResultSet getGeneratedKeysInternal()
  1529. throws SQLException {
  1530. int numKeys = getUpdateCount();
  1531. return getGeneratedKeysInternal(numKeys);
  1532. }
  1533. protected synchronized java.sql.ResultSet getGeneratedKeysInternal(int numKeys)
  1534. throws SQLException {
  1535. Field[] fields = new Field[1];
  1536. fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
  1537. fields[0].setConnection(this.connection);
  1538. fields[0].setUseOldNameMetadata(true);
  1539. ArrayList rowSet = new ArrayList();
  1540. long beginAt = getLastInsertID();
  1541. if (beginAt < 0) { // looking at an UNSIGNED BIGINT that has overflowed
  1542. fields[0].setUnsigned();
  1543. }
  1544. if (this.results != null) {
  1545. String serverInfo = this.results.getServerInfo();
  1546. //
  1547. // Only parse server info messages for 'REPLACE'
  1548. // queries
  1549. //
  1550. if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R')
  1551. && (serverInfo != null) && (serverInfo.length() > 0)) {
  1552. numKeys = getRecordCountFromInfo(serverInfo);
  1553. }
  1554. if ((beginAt != 0 /* BIGINT UNSIGNED can wrap the protocol representation */) && (numKeys > 0)) {
  1555. for (int i = 0; i < numKeys; i++) {
  1556. byte[][] row = new byte[1][];
  1557. if (beginAt > 0) {
  1558. row[0] = Long.toString(beginAt).getBytes();
  1559. } else {
  1560. byte[] asBytes = new byte[8];
  1561. asBytes[7] = (byte) (beginAt & 0xff);
  1562. asBytes[6] = (byte) (beginAt >>> 8);
  1563. asBytes[5] = (byte) (beginAt >>> 16);
  1564. asBytes[4] = (byte) (beginAt >>> 24);
  1565. asBytes[3] = (byte) (beginAt >>> 32);
  1566. asBytes[2] = (byte) (beginAt >>> 40);
  1567. asBytes[1] = (byte) (beginAt >>> 48);
  1568. asBytes[0] = (byte) (beginAt >>> 56);
  1569. BigInteger val = new BigInteger(1, asBytes);
  1570. row[0] = val.toString().getBytes();
  1571. }
  1572. rowSet.add(new ByteArrayRow(row, getExceptionInterceptor()));
  1573. beginAt += this.connection.getAutoIncrementIncrement();
  1574. }
  1575. }
  1576. }
  1577. com.mysql.jdbc.ResultSetImpl gkRs = com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
  1578. new RowDataStatic(rowSet), this.connection, this, false);
  1579. this.openResults.add(gkRs);
  1580. return gkRs;
  1581. }
  1582. /**
  1583. * Returns the id used when profiling
  1584. *
  1585. * @return the id used when profiling.
  1586. */
  1587. protected int getId() {
  1588. return this.statementId;
  1589. }
  1590. /**
  1591. * getLastInsertID returns the value of the auto_incremented key after an
  1592. * executeQuery() or excute() call.
  1593. *
  1594. * <p>
  1595. * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
  1596. * which is tied to the Connection that created this Statement, and
  1597. * therefore could have had many INSERTS performed before one gets a chance
  1598. * to call "select LAST_INSERT_ID()".
  1599. * </p>
  1600. *
  1601. * @return the last update ID.
  1602. */
  1603. public long getLastInsertID() {
  1604. return this.lastInsertId;
  1605. }
  1606. /**
  1607. * getLongUpdateCount returns the current result as an update count, if the
  1608. * result is a ResultSet or there are no more results, -1 is returned. It
  1609. * should only be called once per result.
  1610. *
  1611. * <p>
  1612. * This method returns longs as MySQL server versions newer than 3.22.4
  1613. * return 64-bit values for update counts
  1614. * </p>
  1615. *
  1616. * @return the current update count.
  1617. */
  1618. public long getLongUpdateCount() {
  1619. if (this.results == null) {
  1620. return -1;
  1621. }
  1622. if (this.results.reallyResult()) {
  1623. return -1;
  1624. }
  1625. return this.updateCount;
  1626. }
  1627. /**
  1628. * The maxFieldSize limit (in bytes) is the maximum amount of data returned
  1629. * for any column value; it only applies to BINARY, VARBINARY,
  1630. * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is
  1631. * exceeded, the excess data is silently discarded.
  1632. *
  1633. * @return the current max column size limit; zero means unlimited
  1634. *
  1635. * @exception SQLException
  1636. * if a database access error occurs
  1637. */
  1638. public int getMaxFieldSize() throws SQLException {
  1639. return this.maxFieldSize;
  1640. }
  1641. /**
  1642. * The maxRows limit is set to limit the number of rows that any ResultSet
  1643. * can contain. If the limit is exceeded, the excess rows are silently
  1644. * dropped.
  1645. *
  1646. * @return the current maximum row limit; zero means unlimited
  1647. *
  1648. * @exception SQLException
  1649. * if a database access error occurs
  1650. */
  1651. public int getMaxRows() throws SQLException {
  1652. if (this.maxRows <= 0) {
  1653. return 0;
  1654. }
  1655. return this.maxRows;
  1656. }
  1657. /**
  1658. * getMoreResults moves to a Statement's next result. If it returns true,
  1659. * this result is a ResulSet.
  1660. *
  1661. * @return true if the next ResultSet is valid
  1662. *
  1663. * @exception SQLException
  1664. * if a database access error occurs
  1665. */
  1666. public boolean getMoreResults() throws SQLException {
  1667. return getMoreResults(CLOSE_CURRENT_RESULT);
  1668. }
  1669. /**
  1670. * @see StatementImpl#getMoreResults(int)
  1671. */
  1672. public synchronized boolean getMoreResults(int current) throws SQLException {
  1673. if (this.results == null) {
  1674. return false;
  1675. }
  1676. boolean streamingMode = createStreamingResultSet();
  1677. if (streamingMode) {
  1678. if (this.results.reallyResult()) {
  1679. while (this.results.next()); // need to drain remaining rows to get to server status
  1680. // which tells us whether more results actually exist or not
  1681. }
  1682. }
  1683. ResultSetInternalMethods nextResultSet = this.results.getNextResultSet();
  1684. switch (current) {
  1685. case java.sql.Statement.CLOSE_CURRENT_RESULT:
  1686. if (this.results != null) {
  1687. if (!streamingMode) {
  1688. this.results.close();
  1689. }
  1690. this.results.clearNextResult();
  1691. }
  1692. break;
  1693. case java.sql.Statement.CLOSE_ALL_RESULTS:
  1694. if (this.results != null) {
  1695. if (!streamingMode) {
  1696. this.results.close();
  1697. }
  1698. this.results.clearNextResult();
  1699. }
  1700. closeAllOpenResults();
  1701. break;
  1702. case java.sql.Statement.KEEP_CURRENT_RESULT:
  1703. if (!this.connection.getDontTrackOpenResources()) {
  1704. this.openResults.add(this.results);
  1705. }
  1706. this.results.clearNextResult(); // nobody besides us should
  1707. // ever need this value...
  1708. break;
  1709. default:
  1710. throw SQLError.createSQLException(Messages
  1711. .getString("Statement.19"), //$NON-NLS-1$
  1712. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
  1713. }
  1714. this.results = nextResultSet;
  1715. if (this.results == null) {
  1716. this.updateCount = -1;
  1717. this.lastInsertId = -1;
  1718. } else if (this.results.reallyResult()) {
  1719. this.updateCount = -1;
  1720. this.lastInsertId = -1;
  1721. } else {
  1722. this.updateCount = this.results.getUpdateCount();
  1723. this.lastInsertId = this.results.getUpdateID();
  1724. }
  1725. return ((this.results != null) && this.results.reallyResult()) ? true
  1726. : false;
  1727. }
  1728. /**
  1729. * The queryTimeout limit is the number of seconds the driver will wait for
  1730. * a Statement to execute. If the limit is exceeded, a SQLException is
  1731. * thrown.
  1732. *
  1733. * @return the current query timeout limit in seconds; 0 = unlimited
  1734. *
  1735. * @exception SQLException
  1736. * if a database access error occurs
  1737. */
  1738. public int getQueryTimeout() throws SQLException {
  1739. return this.timeoutInMillis / 1000;
  1740. }
  1741. /**
  1742. * Parses actual record count from 'info' message
  1743. *
  1744. * @param serverInfo
  1745. * DOCUMENT ME!
  1746. *
  1747. * @return DOCUMENT ME!
  1748. */
  1749. private int getRecordCountFromInfo(String serverInfo) {
  1750. StringBuffer recordsBuf = new StringBuffer();
  1751. int recordsCount = 0;
  1752. int duplicatesCount = 0;
  1753. char c = (char) 0;
  1754. int length = serverInfo.length();
  1755. int i = 0;
  1756. for (; i < length; i++) {
  1757. c = serverInfo.charAt(i);
  1758. if (Character.isDigit(c)) {
  1759. break;
  1760. }
  1761. }
  1762. recordsBuf.append(c);
  1763. i++;
  1764. for (; i < length; i++) {
  1765. c = serverInfo.charAt(i);
  1766. if (!Character.isDigit(c)) {
  1767. break;
  1768. }
  1769. recordsBuf.append(c);
  1770. }
  1771. recordsCount = Integer.parseInt(recordsBuf.toString());
  1772. StringBuffer duplicatesBuf = new StringBuffer();
  1773. for (; i < length; i++) {
  1774. c = serverInfo.charAt(i);
  1775. if (Character.isDigit(c)) {
  1776. break;
  1777. }
  1778. }
  1779. duplicatesBuf.append(c);
  1780. i++;
  1781. for (; i < length; i++) {
  1782. c = serverInfo.charAt(i);
  1783. if (!Character.isDigit(c)) {
  1784. break;
  1785. }
  1786. duplicatesBuf.append(c);
  1787. }
  1788. duplicatesCount = Integer.parseInt(duplicatesBuf.toString());
  1789. return recordsCount - duplicatesCount;
  1790. }
  1791. /**
  1792. * getResultSet returns the current result as a ResultSet. It should only be
  1793. * called once per result.
  1794. *
  1795. * @return the current result set; null if there are no more
  1796. *
  1797. * @exception SQLException
  1798. * if a database access error occurs (why?)
  1799. */
  1800. public java.sql.ResultSet getResultSet() throws SQLException {
  1801. return ((this.results != null) && this.results.reallyResult()) ? (java.sql.ResultSet) this.results
  1802. : null;
  1803. }
  1804. /**
  1805. * JDBC 2.0 Determine the result set concurrency.
  1806. *
  1807. * @return CONCUR_UPDATABLE or CONCUR_READONLY
  1808. *
  1809. * @throws SQLException
  1810. * if an error occurs
  1811. */
  1812. public int getResultSetConcurrency() throws SQLException {
  1813. return this.resultSetConcurrency;
  1814. }
  1815. /**
  1816. * @see StatementImpl#getResultSetHoldability()
  1817. */
  1818. public int getResultSetHoldability() throws SQLException {
  1819. return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
  1820. }
  1821. protected ResultSetInternalMethods getResultSetInternal() {
  1822. return this.results;
  1823. }
  1824. /**
  1825. * JDBC 2.0 Determine the result set type.
  1826. *
  1827. * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
  1828. *
  1829. * @throws SQLException
  1830. * if an error occurs.
  1831. */
  1832. public int getResultSetType() throws SQLException {
  1833. return this.resultSetType;
  1834. }
  1835. /**
  1836. * getUpdateCount returns the current result as an update count, if the
  1837. * result is a ResultSet or there are no more results, -1 is returned. It
  1838. * should only be called once per result.
  1839. *
  1840. * @return the current result as an update count.
  1841. *
  1842. * @exception SQLException
  1843. * if a database access error occurs
  1844. */
  1845. public int getUpdateCount() throws SQLException {
  1846. if (this.results == null) {
  1847. return -1;
  1848. }
  1849. if (this.results.reallyResult()) {
  1850. return -1;
  1851. }
  1852. int truncatedUpdateCount = 0;
  1853. if (this.results.getUpdateCount() > Integer.MAX_VALUE) {
  1854. truncatedUpdateCount = Integer.MAX_VALUE;
  1855. } else {
  1856. truncatedUpdateCount = (int) this.results.getUpdateCount();
  1857. }
  1858. return truncatedUpdateCount;
  1859. }
  1860. /**
  1861. * The first warning reported by calls on this Statement is returned. A
  1862. * Statement's execute methods clear its java.sql.SQLWarning chain.
  1863. * Subsequent Statement warnings will be chained to this
  1864. * java.sql.SQLWarning.
  1865. *
  1866. * <p>
  1867. * The Warning chain is automatically cleared each time a statement is
  1868. * (re)executed.
  1869. * </p>
  1870. *
  1871. * <p>
  1872. * <B>Note:</B> If you are processing a ResultSet then any warnings
  1873. * associated with ResultSet reads will be chained on the ResultSet object.
  1874. * </p>
  1875. *
  1876. * @return the first java.sql.SQLWarning or null
  1877. *
  1878. * @exception SQLException
  1879. * if a database access error occurs
  1880. */
  1881. public java.sql.SQLWarning getWarnings() throws SQLException {
  1882. checkClosed();
  1883. if (this.connection != null && !th