0001: /**
0002: * Sequoia: Database clustering technology.
0003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
0004: * Science And Control (INRIA).
0005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
0006: * Copyright (C) 2005-2006 Continuent, Inc.
0007: * Contact: sequoia@continuent.org
0008: *
0009: * Licensed under the Apache License, Version 2.0 (the "License");
0010: * you may not use this file except in compliance with the License.
0011: * You may obtain a copy of the License at
0012: *
0013: * http://www.apache.org/licenses/LICENSE-2.0
0014: *
0015: * Unless required by applicable law or agreed to in writing, software
0016: * distributed under the License is distributed on an "AS IS" BASIS,
0017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0018: * See the License for the specific language governing permissions and
0019: * limitations under the License.
0020: *
0021: * Initial developer(s): Emmanuel Cecchet.
0022: * Contributor(s): Vadim Kassin, Jean-Bernard van Zuylen.
0023: */package org.continuent.sequoia.driver;
0024:
0025: import java.sql.BatchUpdateException;
0026: import java.sql.ResultSet;
0027: import java.sql.ResultSetMetaData;
0028: import java.sql.SQLException;
0029: import java.sql.SQLWarning;
0030: import java.util.ArrayList;
0031: import java.util.Iterator;
0032: import java.util.LinkedList;
0033: import java.util.Vector;
0034: import java.util.regex.Matcher;
0035:
0036: import org.continuent.sequoia.common.exceptions.NotImplementedException;
0037: import org.continuent.sequoia.common.protocol.Field;
0038: import org.continuent.sequoia.common.sql.Request;
0039: import org.continuent.sequoia.common.sql.RequestWithResultSetParameters;
0040:
0041: /**
0042: * A <code>Statement</code> object is used for executing a static SQL
0043: * statement and obtaining the results produced by it.
0044: * <p>
0045: * Only one <code>ResultSet</code> per <code>Statement</code> can be open at
0046: * any point in time. Therefore, if the reading of one <code>ResultSet</code>
0047: * is interleaved with the reading of another, each must have been generated by
0048: * different <code>Statements</code>. All <code>Statements</code> execute
0049: * methods implicitly close a statement's current <code>ResultSet</code> if an
0050: * open one exists.
0051: *
0052: * @see java.sql.Statement
0053: * @see DriverResultSet
0054: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
0055: * @author <a href="mailto:vadim@kase.kz">Vadim Kassin </a>
0056: * @author <a href="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
0057: * </a>
0058: * @version 1.0
0059: */
0060: public class Statement implements java.sql.Statement {
0061: /** The Driver used to create connections */
0062: protected Driver driver;
0063:
0064: /** The connection that created us */
0065: protected Connection connection = null;
0066:
0067: /** Vector for batch commands */
0068: protected Vector batch = null;
0069:
0070: /** The warnings chain - package private */
0071: SQLWarning warnings = null;
0072:
0073: /** The current result for a read request */
0074: protected ResultSet result = null;
0075:
0076: /** List of updateCount/ResultSet */
0077: protected LinkedList resultList = null;
0078: protected Iterator resultListIterator = null;
0079:
0080: /** The update count for a write request */
0081: protected int updateCount = -1;
0082:
0083: /** Query timeout in seconds (0 means no timeout) */
0084: protected int timeout = 0;
0085:
0086: /** Default ResultSet fetch size */
0087: private int fetchSize = 0;
0088: /** Cursor name used jointly with fetch size */
0089: private String cursorName;
0090:
0091: /** Type of the ResultSet defaults to TYPE_FORWARD_ONLY */
0092: private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
0093:
0094: /** ResultSet Concurrency defaults to CONCUR_READ_ONLY */
0095: private int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
0096:
0097: /** Maximum field size (unused) */
0098: private int maxFieldSize = 0;
0099:
0100: /** Maximum number of rows */
0101: private int maxRows = 0;
0102:
0103: /**
0104: * Direction for fetching rows from ResultSet (note that this hint is
0105: * currently ignored
0106: */
0107: private int fetchDirection = ResultSet.FETCH_FORWARD;
0108:
0109: /**
0110: * Should the driver to escape processing before sending to the DB?
0111: */
0112: protected boolean escapeProcessing = true;
0113:
0114: /** Auto generated keys */
0115: protected ResultSet generatedKeys = null;
0116: protected int generatedKeysFlag = java.sql.Statement.NO_GENERATED_KEYS;
0117:
0118: /**
0119: * Creates a new <code>Statement</code> instance.
0120: *
0121: * @param c the <code>Connection</code> that created us
0122: * @param driver tje <code>Driver</code> used to create connections
0123: */
0124: Statement(Connection c, Driver driver) {
0125: connection = c;
0126: this .driver = driver;
0127: }
0128:
0129: /**
0130: * Closes the ResultSet attached to this statement
0131: *
0132: * @see #close()
0133: */
0134: protected void finalize() throws Throwable {
0135: this .close();
0136: super .finalize();
0137: }
0138:
0139: /**
0140: * Adds sql to the current list of commands.
0141: *
0142: * @param sql an SQL statement that returns an update count (INSERT or UPDATE)
0143: * @exception SQLException if an error occurs
0144: */
0145: public synchronized void addBatch(String sql) throws SQLException {
0146: if (batch == null)
0147: batch = new Vector();
0148: batch.addElement(sql.trim());
0149: }
0150:
0151: /**
0152: * Could be use by one thread to cancel a statement that is being executed by
0153: * another thread. We don't support that for instance.
0154: *
0155: * @exception SQLException if an error occurs
0156: */
0157: public void cancel() throws SQLException {
0158: throw new NotImplementedException("cancel()");
0159: }
0160:
0161: /**
0162: * Empties the current list of commands.
0163: *
0164: * @exception SQLException if an error occurs
0165: */
0166: public void clearBatch() throws SQLException {
0167: if (batch != null)
0168: batch.removeAllElements();
0169: }
0170:
0171: /**
0172: * After this call, <code>getWarnings</code> returns <code>null</code>
0173: * until a new warning is reported for this <code>Statement</code>.
0174: *
0175: * @exception SQLException if a database access error occurs (why?)
0176: */
0177: public void clearWarnings() throws SQLException {
0178: warnings = null;
0179: }
0180:
0181: /**
0182: * Utility to add warnings to a given warning chain.
0183: *
0184: * @param addMe (in) warnings to be added
0185: * @param toThis (in/out) reference chain on which to add addMe. If null, will
0186: * be equal to addMe after the function returns
0187: */
0188: protected void addWarningTo(SQLWarning addMe, SQLWarning toThis) {
0189: if (toThis != null)
0190: toThis.setNextWarning(warnings);
0191: else
0192: toThis = warnings;
0193: }
0194:
0195: /**
0196: * Execute a batch of commands
0197: *
0198: * @return an array containing update count that corresponding to the commands
0199: * that executed successfully
0200: * @exception BatchUpdateException if an error occurs on one statement (the
0201: * number of updated rows for the successfully executed
0202: * statements can be found in
0203: * BatchUpdateException.getUpdateCounts())
0204: */
0205: public int[] executeBatch() throws BatchUpdateException {
0206: if (batch == null || batch.isEmpty())
0207: return new int[0];
0208:
0209: ArrayList generatedKeysData = null;
0210: Field generatedKeysField = null;
0211: if (connection.isAlwaysGettingGeneratedKeys())
0212: generatedKeysData = new ArrayList();
0213:
0214: int size = batch.size();
0215: int[] batchResult = new int[size];
0216: int i = 0;
0217: // must keep warning in a separate place otherwise they will be erased at
0218: // each call to executeUpdate()
0219: SQLWarning allWarnings = null;
0220: try {
0221: for (i = 0; i < size; i++) {
0222: batchResult[i] = this .executeUpdate((String) batch
0223: .elementAt(i));
0224: if (warnings != null)
0225: addWarningTo(warnings, allWarnings);
0226: if (connection.isAlwaysGettingGeneratedKeys()) {
0227: if (generatedKeys != null) {
0228: while (generatedKeys.next()) {
0229: generatedKeysData.add(generatedKeys
0230: .getObject(1));
0231: if (generatedKeysField == null) {
0232: ResultSetMetaData metaData = generatedKeys
0233: .getMetaData();
0234: generatedKeysField = new Field(metaData
0235: .getTableName(1), metaData
0236: .getColumnName(1), metaData
0237: .getColumnDisplaySize(1),
0238: metaData.getColumnType(1),
0239: metaData.getColumnTypeName(1),
0240: metaData.getColumnClassName(1));
0241: }
0242: }
0243: }
0244: }
0245: }
0246: // make one chain with all generated warnings
0247: warnings = allWarnings;
0248: generatedKeys = new DriverResultSet(connection,
0249: generatedKeysData, generatedKeysField);
0250: return batchResult;
0251: } catch (SQLException e) {
0252: String message = "Batch failed for request " + i + ": "
0253: + batch.elementAt(i) + " (" + e + ")";
0254:
0255: int[] updateCounts = new int[i];
0256: System.arraycopy(batchResult, 0, updateCounts, 0, i);
0257:
0258: throw new BatchUpdateException(message, updateCounts);
0259: } finally {
0260: batch.removeAllElements();
0261: }
0262: }
0263:
0264: /**
0265: * In many cases, it is desirable to immediately release a Statement's
0266: * database and JDBC resources instead of waiting for this to happen when it
0267: * is automatically closed. The close method provides this immediate release.
0268: * <p>
0269: * <B>Note: </B> A Statement is automatically closed when it is garbage
0270: * collected. When a Statement is closed, its current ResultSet, if one
0271: * exists, is also closed.
0272: *
0273: * @exception SQLException if a database access error occurs (why?)
0274: */
0275: public void close() throws SQLException {
0276: // Force the ResultSet to close
0277: if (result != null)
0278: try {
0279: result.close();
0280: } catch (SQLException ignore) {
0281: }
0282:
0283: // Disasociate it from us (For Garbage Collection)
0284: result = null;
0285: connection = null;
0286: }
0287:
0288: /**
0289: * Check if the statement is closed and cleanup the last results if any
0290: * (update count is reset and last result is closed).
0291: *
0292: * @throws SQLException if the Statement is closed.
0293: */
0294: private void checkIfClosedAndCleanupLastResult()
0295: throws SQLException {
0296: if (isClosed())
0297: throw new SQLException(
0298: "Unable to execute query on a closed statement");
0299:
0300: updateCount = -1; // invalidate the last write result
0301: if (result != null) { // Discard the previous result
0302: try {
0303: result.close();
0304: } catch (Exception ignore) {
0305: }
0306: result = null;
0307: }
0308: if (generatedKeys != null) { // Discard the previous generated keys
0309: try {
0310: generatedKeys.close();
0311: } catch (Exception ignore) {
0312: }
0313: generatedKeys = null;
0314: }
0315: resultList = null;
0316: resultListIterator = null;
0317: clearWarnings();
0318: }
0319:
0320: /**
0321: * Returns true if the given SQL query can be mapped to a JDBC call. This is
0322: * the case for example of BEGIN, COMMIT or ROLLBACK that can be mapped to the
0323: * corresponding JDBC calls.
0324: *
0325: * @param sqlQuery the SQL statement to try to map
0326: * @return true if the SQL has been mapped to a JDBC call
0327: */
0328: private boolean sqlHasBeenMappedToJDBCCall(String sqlQuery)
0329: throws SQLException {
0330: final JDBCRegExp jdbcRegExp = driver.getJDBCRegExp();
0331: if (jdbcRegExp.getBeginPattern().matcher(sqlQuery).matches()) {
0332: connection.setAutoCommit(false);
0333: return true;
0334: }
0335: if (jdbcRegExp.getCommitPattern().matcher(sqlQuery).matches()) {
0336: connection.commit();
0337: return true;
0338: }
0339: Matcher m = jdbcRegExp.getRollbackToSavepointPattern().matcher(
0340: sqlQuery);
0341: if (m.matches()) {
0342: // Extract the savepoint name after the pattern
0343: connection.rollback(new Savepoint(sqlQuery.substring(
0344: m.end(), sqlQuery.length()).trim()));
0345: return true;
0346: }
0347: m = jdbcRegExp.getReleaseSavepointPattern().matcher(sqlQuery);
0348: if (m.matches()) {
0349: // Extract the savepoint name after the pattern
0350: connection.releaseSavepoint(new Savepoint(sqlQuery
0351: .substring(m.end(), sqlQuery.length()).trim()));
0352: return true;
0353: }
0354: if (jdbcRegExp.getRollbackPattern().matcher(sqlQuery).matches()) {
0355: connection.rollback();
0356: return true;
0357: }
0358: if (jdbcRegExp.getSetReadOnlyTransactionPattern().matcher(
0359: sqlQuery).matches()) {
0360: connection.setReadOnly(true);
0361: return true;
0362: }
0363: m = jdbcRegExp.getSetSavepointPattern().matcher(sqlQuery);
0364: if (m.matches()) {
0365: // Extract the savepoint name after the pattern
0366: connection.setSavepoint(sqlQuery.substring(m.end(),
0367: sqlQuery.length()).trim());
0368: return true;
0369: }
0370: m = jdbcRegExp.getSetAutocommit1Pattern().matcher(sqlQuery);
0371: if (m.matches()) {
0372: connection.setAutoCommit(true);
0373: return true;
0374: }
0375: m = jdbcRegExp.getSetIsolationLevelPattern().matcher(sqlQuery);
0376: if (m.matches()) {
0377: String isolationLevel = sqlQuery.substring(m.end(),
0378: sqlQuery.length()).toLowerCase();
0379: if (isolationLevel.startsWith("read commited")) {
0380: connection
0381: .setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
0382: return true;
0383: }
0384: if (isolationLevel.startsWith("read uncommited")) {
0385: connection
0386: .setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
0387: return true;
0388: }
0389: if (isolationLevel.startsWith("repeatable read")) {
0390: connection
0391: .setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
0392: return true;
0393: }
0394: if (isolationLevel.startsWith("serializable")) {
0395: connection
0396: .setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
0397:
0398: return true;
0399: }
0400: }
0401: return false;
0402: }
0403:
0404: /**
0405: * Execute a SQL statement that may return multiple results.
0406: *
0407: * @param sql any SQL statement
0408: * @return true if the result is a ResultSet or false if it is an integer
0409: * @exception SQLException if an error occurs
0410: */
0411: public boolean execute(String sql) throws SQLException {
0412: if (connection.isAlwaysGettingGeneratedKeys()
0413: && sql.toLowerCase().trim().startsWith("insert"))
0414: return execute(sql, RETURN_GENERATED_KEYS);
0415: return execute(sql.trim(), (String) null);
0416: }
0417:
0418: /**
0419: * Execute a SQL statement that returns a single ResultSet
0420: *
0421: * @param sqlSkeleton the SQL request squeleton or null
0422: * @param parameters PreparedStaement parameters
0423: * @return a ResulSet that contains the data produced by the query
0424: * @throws SQLException if a database access error occurs or if this statement
0425: * is closed
0426: */
0427: protected boolean execute(String sqlSkeleton, String parameters)
0428: throws SQLException {
0429: checkIfClosedAndCleanupLastResult();
0430:
0431: if (sqlHasBeenMappedToJDBCCall(sqlSkeleton))
0432: return false;
0433:
0434: RequestWithResultSetParameters request = new RequestWithResultSetParameters(
0435: sqlSkeleton, parameters, escapeProcessing, timeout);
0436: setReadRequestParameters(request);
0437: ResultAndWarnings resultAndWarns = connection
0438: .statementExecute(request);
0439: this .warnings = resultAndWarns.getStatementWarnings();
0440: resultList = (LinkedList) resultAndWarns.getResultList();
0441: resultListIterator = resultList.iterator();
0442: generatedKeys = new DriverResultSet(this .connection, true);
0443: return getMoreResults();
0444: }
0445:
0446: /**
0447: * Execute a SQL statement that returns a single ResultSet
0448: *
0449: * @param sql typically a static SQL <code>SELECT</code> statement
0450: * @return a ResulSet that contains the data produced by the query
0451: * @exception SQLException if a database access error occurs
0452: */
0453: public java.sql.ResultSet executeQuery(String sql)
0454: throws SQLException {
0455: return executeQuery(sql.trim(), null);
0456: }
0457:
0458: /**
0459: * Execute a SQL statement that returns a single ResultSet
0460: *
0461: * @param sqlSkeleton the SQL request squeleton or null
0462: * @param parameters PreparedStaement parameters
0463: * @return a ResulSet that contains the data produced by the query
0464: * @exception SQLException if a database access error occurs or if this
0465: * statement is closed
0466: */
0467: protected java.sql.ResultSet executeQuery(String sqlSkeleton,
0468: String parameters) throws SQLException {
0469: checkIfClosedAndCleanupLastResult();
0470:
0471: if (sqlHasBeenMappedToJDBCCall(sqlSkeleton))
0472: // In theory executeQuery() is not allowed to return null,
0473: // but after all we are here catering for some user's mistake in the
0474: // first place (i.e., committing using executeQuery()).
0475: // We can hardly imagine he will go as far as _using_ this resultset!
0476: return null;
0477:
0478: RequestWithResultSetParameters request = new RequestWithResultSetParameters(
0479: sqlSkeleton, parameters, escapeProcessing, timeout);
0480: setReadRequestParameters(request);
0481: DriverResultSet drs = connection.statementExecuteQuery(request);
0482: drs.setStatement(this );
0483: this .warnings = drs.getStatementWarnings();
0484: this .result = drs;
0485:
0486: return result;
0487: }
0488:
0489: protected void setReadRequestParameters(
0490: RequestWithResultSetParameters request) {
0491: request.setMaxRows(maxRows);
0492: request.setFetchSize(fetchSize);
0493: request.setCursorName(cursorName);
0494: }
0495:
0496: /**
0497: * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
0498: * statements that return nothing such as SQL DDL statements can be executed
0499: *
0500: * @param sql a SQL statement
0501: * @return either a row count, or 0 for SQL commands
0502: * @exception SQLException if a database access error occurs
0503: */
0504: public int executeUpdate(String sql) throws SQLException {
0505: if (connection.isAlwaysGettingGeneratedKeys())
0506: generatedKeysFlag = RETURN_GENERATED_KEYS;
0507: return executeUpdateWithSkeleton(sql.trim(), null);
0508: }
0509:
0510: /**
0511: * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
0512: * statements that return nothing such as SQL DDL statements can be executed
0513: *
0514: * @param sqlSkeleton the SQL request squeleton or null
0515: * @param parameters PreparedStaement parameters
0516: * @return either a row count, or 0 for SQL commands
0517: * @exception SQLException if a database access error occurs or if this
0518: * statement is closed
0519: */
0520: protected int executeUpdateWithSkeleton(String sqlSkeleton,
0521: String parameters) throws SQLException {
0522: checkIfClosedAndCleanupLastResult();
0523:
0524: if (sqlHasBeenMappedToJDBCCall(sqlSkeleton))
0525: return 0;
0526:
0527: Request request = new Request(sqlSkeleton, parameters,
0528: escapeProcessing, timeout);
0529:
0530: if (generatedKeysFlag == java.sql.Statement.RETURN_GENERATED_KEYS) { // Get the auto generated key back
0531: DriverGeneratedKeysResult answer = connection
0532: .statementExecuteUpdateWithKeys(request);
0533: generatedKeys = answer.getDriverResultSet();
0534: updateCount = answer.getUpdateCount();
0535: warnings = answer.getWarnings();
0536: return answer.getUpdateCount();
0537: } else { // No generated keys
0538: ResultAndWarnings resultAndWarns = connection
0539: .statementExecuteUpdate(request);
0540: this .warnings = resultAndWarns.getStatementWarnings();
0541: updateCount = resultAndWarns.getUpdateCount();
0542: generatedKeys = new DriverResultSet(this .connection, true);
0543: return updateCount;
0544: }
0545: }
0546:
0547: /**
0548: * Retrieve the connection that created this Statement object
0549: *
0550: * @return a <code>java.sql.Connection</code> object
0551: * @exception SQLException never
0552: */
0553: public java.sql.Connection getConnection() throws SQLException {
0554: return connection;
0555: }
0556:
0557: /**
0558: * @see java.sql.Statement#getFetchDirection()
0559: */
0560: public int getFetchDirection() throws SQLException {
0561: return fetchDirection;
0562: }
0563:
0564: /**
0565: * @see java.sql.Statement#getFetchSize()
0566: */
0567: public int getFetchSize() throws SQLException {
0568: return fetchSize;
0569: }
0570:
0571: /**
0572: * The maxFieldSize limit (in bytes) is the maximum amount of data returned
0573: * for any column value; it only applies to <code>BINARY</code>,
0574: * <code>VARBINARY</code>,<code>LONGVARBINARY</code>,<code>CHAR</code>,
0575: * <code>VARCHAR</code> and <code>LONGVARCHAR</code> columns. If the limit
0576: * is exceeded, the excess data is silently discarded.
0577: * <p>
0578: * <b>Note: </b> We don't do anything with this value yet.
0579: *
0580: * @return the current max column size limit; zero means unlimited
0581: * @exception SQLException if a database access error occurs
0582: */
0583: public int getMaxFieldSize() throws SQLException {
0584: return maxFieldSize;
0585: }
0586:
0587: /**
0588: * The maxRows limit is set to limit the number of rows that any
0589: * <code>ResultSet</code> can contain. If the limit is exceeded, the excess
0590: * rows are silently dropped.
0591: *
0592: * @return the current maximum row limit; zero means unlimited
0593: * @exception SQLException if a database access error occurs
0594: */
0595: public int getMaxRows() throws SQLException {
0596: return maxRows;
0597: }
0598:
0599: /**
0600: * Return the true if next available result is a ResultSet or false it this is
0601: * an update count. If the result is false and getUpdateCount() returns -1
0602: * then there is no more result
0603: * <p>
0604: * Any open ResultSet is implicitly closed.
0605: *
0606: * @return true for a ResultSet, false otherwise
0607: * @exception SQLException if an error occurs
0608: */
0609: public boolean getMoreResults() throws SQLException {
0610: return getMoreResults(CLOSE_CURRENT_RESULT);
0611: }
0612:
0613: /**
0614: * The queryTimeout limit is the number of seconds the driver will wait for a
0615: * Statement to execute. If the limit is exceeded, a <code>SQLException</code>
0616: * is thrown.
0617: *
0618: * @return the current query timeout limit in seconds; 0 = unlimited
0619: * @exception SQLException if a database access error occurs
0620: */
0621: public int getQueryTimeout() throws SQLException {
0622: return timeout;
0623: }
0624:
0625: /**
0626: * Returns the current result as a <code>ResultSet</code>.
0627: *
0628: * @return the current result set; null if there are no more
0629: * @exception SQLException never
0630: */
0631: public java.sql.ResultSet getResultSet() throws SQLException {
0632: return result;
0633: }
0634:
0635: /**
0636: * Retrieve the concurrency mode for the <code>ResultSet</code>.
0637: *
0638: * @return <code>CONCUR_READ_ONLY</code> or <code>CONCUR_UPDATABLE</code>
0639: * @exception SQLException never
0640: */
0641: public int getResultSetConcurrency() throws SQLException {
0642: return resultSetConcurrency;
0643: }
0644:
0645: /**
0646: * Retrieve the type of the generated <code>ResultSet</code>.
0647: *
0648: * @return one of <code>TYPE_FORWARD_ONLY</code> or
0649: * <code>TYPE_SCROLL_INSENSITIVE</code>
0650: * @exception SQLException never
0651: */
0652: public int getResultSetType() throws SQLException {
0653: return resultSetType;
0654: }
0655:
0656: /**
0657: * Returns the current result as an update count, if the result is a
0658: * <code>ResultSet</code> or there are no more results, -1 is returned. It
0659: * should only be called once per result.
0660: *
0661: * @return the current result as an update count.
0662: * @exception SQLException if a database access error occurs
0663: */
0664: public int getUpdateCount() throws SQLException {
0665: return updateCount;
0666: }
0667:
0668: /**
0669: * The first warning reported by calls on this Statement is returned. A
0670: * Statement's execute methods clear its SQLWarning chain. Subsequent
0671: * <code>Statement</code> warnings will be chained to this SQLWarning.
0672: * <p>
0673: * The Warning chain is automatically cleared each time a statement is
0674: * (re)executed.
0675: * <p>
0676: * <B>Note: </B> if you are processing a <code>ResultSet</code> then any
0677: * warnings associated with <code>ResultSet</code> reads will be chained on
0678: * the <code>ResultSet</code> object.
0679: *
0680: * @return the first SQLWarning on null
0681: * @exception SQLException if a database access error occurs or this method is
0682: * called on a closed statement
0683: */
0684: public SQLWarning getWarnings() throws SQLException {
0685: if (isClosed())
0686: throw new SQLException(
0687: "Unable to get warnings on a closed statement");
0688: return warnings;
0689: }
0690:
0691: /**
0692: * Defines the SQL cursor name that will be used by subsequent execute
0693: * methods. This name can then be used in SQL positioned update/delete
0694: * statements to identify the current row in the ResultSet generated by this
0695: * statement. If a database doesn't support positioned update/delete, this
0696: * method is a no-op.
0697: * <p>
0698: *
0699: * @param name the new cursor name
0700: * @exception SQLException not supported
0701: */
0702: public void setCursorName(String name) throws SQLException {
0703: cursorName = name;
0704: }
0705:
0706: /**
0707: * If escape scanning is on (the default), the driver will do escape
0708: * substitution before sending the SQL to the database.
0709: *
0710: * @param enable true to enable; false to disable
0711: * @exception SQLException if a database access error occurs
0712: */
0713: public void setEscapeProcessing(boolean enable) throws SQLException {
0714: escapeProcessing = enable;
0715: }
0716:
0717: /**
0718: * @see java.sql.Statement#setFetchDirection(int)
0719: */
0720: public void setFetchDirection(int direction) throws SQLException {
0721: if ((direction == ResultSet.FETCH_FORWARD)
0722: || (direction == ResultSet.FETCH_REVERSE)
0723: || (direction == ResultSet.FETCH_UNKNOWN))
0724: this .fetchDirection = direction;
0725: else
0726: throw new SQLException("Unsupported direction " + direction
0727: + " in setFetchDirection");
0728: }
0729:
0730: /**
0731: * Set the default fetch size for the produced ResultSet.
0732: *
0733: * @param rows number of rows that should be fetched from the database
0734: * @exception SQLException if a database access error occurs or the condition
0735: * 0 <= size <= this.getMaxRows is not satisfied
0736: */
0737: public void setFetchSize(int rows) throws SQLException {
0738: if (rows < 0
0739: // The spec forgets the case maxRows = 0.
0740: || 0 < maxRows && maxRows < rows) {
0741: throw new SQLException("Invalid fetch size value: " + rows);
0742: }
0743: // It also forgets the case where maxRows is set < fetchSize AFTERwards,
0744: // but we don't care about it.
0745:
0746: fetchSize = rows;
0747: }
0748:
0749: /**
0750: * Sets the <code>maxFieldSize</code>.
0751: *
0752: * @param max the new max column size limit; 0 means unlimited
0753: * @exception SQLException if a database access error occurs or the condition
0754: * max >= 0 is not satisfied
0755: */
0756: public void setMaxFieldSize(int max) throws SQLException {
0757: if (max < 0) {
0758: throw new SQLException("Invalid max field size value: "
0759: + max);
0760: }
0761: maxFieldSize = max;
0762: }
0763:
0764: /**
0765: * Sets the maximum number of rows that any <code>ResultSet</code> can
0766: * contain.
0767: *
0768: * @param max the new max rows limit; 0 means unlimited
0769: * @exception SQLException if a database access error occurs or the condition
0770: * max >= 0 is not satisfied
0771: */
0772: public void setMaxRows(int max) throws SQLException {
0773: if (max < 0) {
0774: throw new SQLException("Invalid max rows limit: " + max);
0775: }
0776: // this may break fetchSize <= maxRows
0777: maxRows = max;
0778: }
0779:
0780: /**
0781: * Sets the number of seconds the driver will wait for a
0782: * <code>Statement</code> object to execute.
0783: *
0784: * @param seconds the new query timeout limit in seconds; 0 means no timeout
0785: * @exception SQLException if a database access error occurs or the condition
0786: * seconds >= 0 is not satisfied
0787: */
0788: public void setQueryTimeout(int seconds) throws SQLException {
0789: if (seconds < 0) {
0790: throw new SQLException("Invalid query timeout value: "
0791: + seconds);
0792: }
0793: timeout = seconds;
0794: }
0795:
0796: /**
0797: * @param value an <code>int</code> value
0798: * @exception SQLException if an error occurs
0799: */
0800: public void setResultSetConcurrency(int value) throws SQLException {
0801: switch (value) {
0802: case ResultSet.CONCUR_READ_ONLY:
0803: case ResultSet.CONCUR_UPDATABLE:
0804: resultSetConcurrency = value;
0805: break;
0806: default:
0807: throw new SQLException("Invalid ResultSet "
0808: + "concurrency mode: " + value);
0809: }
0810: }
0811:
0812: /**
0813: * @param value an <code>int</code> value
0814: * @exception SQLException if an error occurs
0815: */
0816: public void setResultSetType(int value) throws SQLException {
0817: switch (value) {
0818: case ResultSet.TYPE_FORWARD_ONLY:
0819: case ResultSet.TYPE_SCROLL_INSENSITIVE:
0820: resultSetType = value;
0821: break;
0822: case ResultSet.TYPE_SCROLL_SENSITIVE:
0823: throw new SQLException(
0824: "TYPE_SCROLL_SENSITIVE is not a supported ResultSet type");
0825: default:
0826: throw new SQLException("Invalid ResultSet type");
0827: }
0828: }
0829:
0830: // --------------------------JDBC 3.0-----------------------------
0831:
0832: /**
0833: * Moves to this <code>Statement</code> object's next result, deals with any
0834: * current <code>ResultSet</code> object(s) according to the instructions
0835: * specified by the given flag, and returns <code>true</code> if the next
0836: * result is a <code>ResultSet</code> object.
0837: * <p>
0838: * There are no more results when the following is <code>true</code>:
0839: *
0840: * <pre>(!getMoreResults() && (getUpdateCount() == -1)</pre>
0841: *
0842: * @param current one of the following <code>Statement</code> constants
0843: * indicating what should happen to current <code>ResultSet</code>
0844: * objects obtained using the method
0845: * <code>getResultSet</code: <code>CLOSE_CURRENT_RESULT</code>,
0846: * <code>KEEP_CURRENT_RESULT</code>, or <code>CLOSE_ALL_RESULTS</code>
0847: * @return <code>true</code> if the next result is a <code>ResultSet</code>
0848: * object; <code>false</code> if it is an update count or there are
0849: * no more results
0850: * @exception SQLException if a database access error occurs
0851: * @since JDK 1.4
0852: * @see #execute(String)
0853: */
0854: public boolean getMoreResults(int current) throws SQLException {
0855: // TODO: actually support CLOSE_ALL_RESULTS by calling result.close()
0856: // on ALL previous resultsets.
0857: if (current != KEEP_CURRENT_RESULT) {
0858: if (result != null)
0859: try {
0860: result.close();
0861: } catch (SQLException ignore) {
0862: }
0863: }
0864:
0865: // Reset results so getResultSet() will return null if this is an
0866: // update count, and getUpdateCount() will return -1 otherwise
0867: updateCount = -1;
0868: result = null;
0869:
0870: if (!resultListIterator.hasNext()) {
0871: // End of list, return false
0872: return false;
0873: }
0874:
0875: Object nextResult = resultListIterator.next();
0876: if (nextResult instanceof DriverResultSet) {
0877: // The previous ResultSet was NOT being streamed, this is currently
0878: // incompatible with the generic LinkList this.execute() by design:
0879: // VirtualDatabaseWorkerThread always returns FULL resultsets when using
0880: // .execute()
0881: result = (ResultSet) nextResult;
0882:
0883: // FIXME: why did we miss this at construction time and left a half-built
0884: // DriverResultSet?
0885: ((DriverResultSet) result).setStatement(this );
0886: return true;
0887: }
0888: if (nextResult instanceof Integer) {
0889: updateCount = ((Integer) nextResult).intValue();
0890: return false;
0891: }
0892: throw new SQLException(
0893: "Unexpected result type in getMoreResults ("
0894: + nextResult + ")");
0895: }
0896:
0897: /**
0898: * Retrieves any auto-generated keys created as a result of executing this
0899: * <code>Statement</code> object. If this <code>Statement</code> object
0900: * did not generate any keys, an empty <code>ResultSet</code> object is
0901: * returned.
0902: *
0903: * @return a <code>ResultSet</code> object containing the auto-generated
0904: * key(s) generated by the execution of this <code>Statement</code>
0905: * object
0906: * @exception SQLException if a database access error occurs
0907: * @since JDK 1.4
0908: */
0909: public java.sql.ResultSet getGeneratedKeys() throws SQLException {
0910: return generatedKeys;
0911: }
0912:
0913: /**
0914: * Executes the given SQL statement and signals the driver with the given flag
0915: * about whether the auto-generated keys produced by this
0916: * <code>Statement</code> object should be made available for retrieval.
0917: *
0918: * @param sql must be an SQL <code>INSERT</code>,<code>UPDATE</code> or
0919: * <code>DELETE</code> statement or an SQL statement that returns
0920: * nothing
0921: * @param autoGeneratedKeys a flag indicating whether auto-generated keys
0922: * should be made available for retrieval; one of the following
0923: * constants: <code>Statement.RETURN_GENERATED_KEYS</code>
0924: * <code>Statement.NO_GENERATED_KEYS</code>
0925: * @return either the row count for <code>INSERT</code>,
0926: * <code>UPDATE</code> or <code>DELETE</code> statements, or
0927: * <code>0</code> for SQL statements that return nothing
0928: * @exception SQLException if a database access error occurs, the given SQL
0929: * statement returns a <code>ResultSet</code> object, or the
0930: * given constant is not one of those allowed
0931: * @since JDK 1.4
0932: */
0933: public int executeUpdate(String sql, int autoGeneratedKeys)
0934: throws SQLException {
0935: if (!connection.isAlwaysGettingGeneratedKeys())
0936: generatedKeysFlag = autoGeneratedKeys;
0937: else
0938: generatedKeysFlag = RETURN_GENERATED_KEYS;
0939: int executeUpdateResult = executeUpdate(sql);
0940: if (!connection.isAlwaysGettingGeneratedKeys())
0941: generatedKeysFlag = NO_GENERATED_KEYS;
0942: return executeUpdateResult;
0943: }
0944:
0945: /**
0946: * Executes the given SQL statement and signals the driver that the
0947: * auto-generated keys indicated in the given array should be made available
0948: * for retrieval. The driver will ignore the array if the SQL statement is not
0949: * an <code>INSERT</code> statement.
0950: *
0951: * @param sql an SQL <code>INSERT</code>,<code>UPDATE</code> or
0952: * <code>DELETE</code> statement or an SQL statement that returns
0953: * nothing, such as an SQL DDL statement
0954: * @param columnIndexes an array of column indexes indicating the columns that
0955: * should be returned from the inserted row
0956: * @return either the row count for <code>INSERT</code>,
0957: * <code>UPDATE</code>, or <code>DELETE</code> statements, or 0
0958: * for SQL statements that return nothing
0959: * @exception SQLException if a database access error occurs or the SQL
0960: * statement returns a <code>ResultSet</code> object
0961: * @since JDK 1.4
0962: */
0963: public int executeUpdate(String sql, int[] columnIndexes)
0964: throws SQLException {
0965: return executeUpdate(sql, RETURN_GENERATED_KEYS);
0966: }
0967:
0968: /**
0969: * Executes the given SQL statement and signals the driver that the
0970: * auto-generated keys indicated in the given array should be made available
0971: * for retrieval. The driver will ignore the array if the SQL statement is not
0972: * an <code>INSERT</code> statement.
0973: *
0974: * @param sql an SQL <code>INSERT</code>,<code>UPDATE</code> or
0975: * <code>DELETE</code> statement or an SQL statement that returns
0976: * nothing
0977: * @param columnNames an array of the names of the columns that should be
0978: * returned from the inserted row
0979: * @return either the row count for <code>INSERT</code>,
0980: * <code>UPDATE</code>, or <code>DELETE</code> statements, or 0
0981: * for SQL statements that return nothing
0982: * @exception SQLException if a database access error occurs
0983: * @since JDK 1.4
0984: */
0985: public int executeUpdate(String sql, String[] columnNames)
0986: throws SQLException {
0987: return executeUpdate(sql, RETURN_GENERATED_KEYS);
0988: }
0989:
0990: /**
0991: * Executes the given SQL statement, which may return multiple results, and
0992: * signals the driver that any auto-generated keys should be made available
0993: * for retrieval. The driver will ignore this signal if the SQL statement is
0994: * not an <code>INSERT</code> statement.
0995: * <p>
0996: * In some (uncommon) situations, a single SQL statement may return multiple
0997: * result sets and/or update counts. Normally you can ignore this unless you
0998: * are (1) executing a stored procedure that you know may return multiple
0999: * results or (2) you are dynamically executing an unknown SQL string.
1000: * <p>
1001: * The <code>execute</code> method executes an SQL statement and indicates
1002: * the form of the first result. You must then use the methods
1003: * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve
1004: * the result, and <code>getMoreResults</code> to move to any subsequent
1005: * result(s).
1006: *
1007: * @param sql any SQL statement
1008: * @param autoGeneratedKeys a constant indicating whether auto-generated keys
1009: * should be made available for retrieval using the method
1010: * <code>getGeneratedKeys</code>; one of the following constants:
1011: * <code>Statement.RETURN_GENERATED_KEYS</code> or
1012: * <code>Statement.NO_GENERATED_KEYS</code>
1013: * @return <code>true</code> if the first result is a <code>ResultSet</code>
1014: * object; <code>false</code> if it is an update count or there are
1015: * no results
1016: * @exception SQLException if a database access error occurs
1017: * @see #getResultSet
1018: * @see #getUpdateCount
1019: * @see #getMoreResults()
1020: * @see #getGeneratedKeys
1021: * @since JDK 1.4
1022: */
1023: public boolean execute(String sql, int autoGeneratedKeys)
1024: throws SQLException {
1025: if (connection.isAlwaysGettingGeneratedKeys()
1026: && sql.toLowerCase().trim().startsWith("insert"))
1027: generatedKeysFlag = RETURN_GENERATED_KEYS;
1028: else
1029: generatedKeysFlag = autoGeneratedKeys;
1030:
1031: if (autoGeneratedKeys == RETURN_GENERATED_KEYS) {
1032: int executeUpdateResult = executeUpdate(sql);
1033: resultList = new LinkedList();
1034: resultList.add(new Integer(executeUpdateResult));
1035: resultListIterator = resultList.iterator();
1036: if (!connection.isAlwaysGettingGeneratedKeys())
1037: autoGeneratedKeys = NO_GENERATED_KEYS;
1038: return getMoreResults();
1039: }
1040: return execute(sql);
1041: }
1042:
1043: /**
1044: * Executes the given SQL statement, which may return multiple results, and
1045: * signals the driver that the auto-generated keys indicated in the given
1046: * array should be made available for retrieval. This array contains the
1047: * indexes of the columns in the target table that contain the auto-generated
1048: * keys that should be made available. The driver will ignore the array if the
1049: * given SQL statement is not an <code>INSERT</code> statement.
1050: * <p>
1051: * Under some (uncommon) situations, a single SQL statement may return
1052: * multiple result sets and/or update counts. Normally you can ignore this
1053: * unless you are (1) executing a stored procedure that you know may return
1054: * multiple results or (2) you are dynamically executing an unknown SQL
1055: * string.
1056: * <p>
1057: * The <code>execute</code> method executes an SQL statement and indicates
1058: * the form of the first result. You must then use the methods
1059: * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve
1060: * the result, and <code>getMoreResults</code> to move to any subsequent
1061: * result(s).
1062: *
1063: * @param sql any SQL statement
1064: * @param columnIndexes an array of the indexes of the columns in the inserted
1065: * row that should be made available for retrieval by a call to the
1066: * method <code>getGeneratedKeys</code>
1067: * @return <code>true</code> if the first result is a <code>ResultSet</code>
1068: * object; <code>false</code> if it is an update count or there are
1069: * no results
1070: * @exception SQLException if a database access error occurs
1071: * @see #getResultSet
1072: * @see #getUpdateCount
1073: * @see #getMoreResults()
1074: * @since JDK 1.4
1075: */
1076: public boolean execute(String sql, int[] columnIndexes)
1077: throws SQLException {
1078: return execute(sql, RETURN_GENERATED_KEYS);
1079: }
1080:
1081: /**
1082: * Executes the given SQL statement, which may return multiple results, and
1083: * signals the driver that the auto-generated keys indicated in the given
1084: * array should be made available for retrieval. This array contains the names
1085: * of the columns in the target table that contain the auto-generated keys
1086: * that should be made available. The driver will ignore the array if the
1087: * given SQL statement is not an <code>INSERT</code> statement.
1088: * <p>
1089: * In some (uncommon) situations, a single SQL statement may return multiple
1090: * result sets and/or update counts. Normally you can ignore this unless you
1091: * are (1) executing a stored procedure that you know may return multiple
1092: * results or (2) you are dynamically executing an unknown SQL string.
1093: * <p>
1094: * The <code>execute</code> method executes an SQL statement and indicates
1095: * the form of the first result. You must then use the methods
1096: * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve
1097: * the result, and <code>getMoreResults</code> to move to any subsequent
1098: * result(s).
1099: *
1100: * @param sql any SQL statement
1101: * @param columnNames an array of the names of the columns in the inserted row
1102: * that should be made available for retrieval by a call to the
1103: * method <code>getGeneratedKeys</code>
1104: * @return <code>true</code> if the next result is a <code>ResultSet</code>
1105: * object; <code>false</code> if it is an update count or there are
1106: * no more results
1107: * @exception SQLException if a database access error occurs
1108: * @see #getResultSet
1109: * @see #getUpdateCount
1110: * @see #getMoreResults()
1111: * @see #getGeneratedKeys
1112: * @since JDK 1.4
1113: */
1114: public boolean execute(String sql, String[] columnNames)
1115: throws SQLException {
1116: return execute(sql, RETURN_GENERATED_KEYS);
1117: }
1118:
1119: /**
1120: * Retrieves the result set holdability for <code>ResultSet</code> objects
1121: * generated by this <code>Statement</code> object.
1122: *
1123: * @return either <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
1124: * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
1125: * @exception SQLException if a database access error occurs
1126: * @since JDK 1.4
1127: */
1128: public int getResultSetHoldability() throws SQLException {
1129: return connection.getHoldability();
1130: }
1131:
1132: /**
1133: * Test if this statement is closed.
1134: *
1135: * @return <code>true</code> if this statement is closed
1136: */
1137: protected boolean isClosed() {
1138: return (connection == null);
1139: }
1140: }
|