001: /*
002:
003: Derby - Class org.apache.derby.iapi.jdbc.BrokeredStatement
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.jdbc;
023:
024: import org.apache.derby.iapi.reference.JDBC30Translation;
025: import org.apache.derby.iapi.reference.SQLState;
026:
027: import org.apache.derby.iapi.error.StandardException;
028: import org.apache.derby.iapi.error.PublicAPI;
029: import org.apache.derby.iapi.services.info.JVMInfo;
030: import org.apache.derby.impl.jdbc.Util;
031:
032: import java.sql.Connection;
033: import java.sql.ResultSet;
034: import java.sql.SQLException;
035: import java.sql.SQLWarning;
036: import java.sql.Statement;
037:
038: import java.lang.reflect.*;
039:
040: /**
041: A Statement implementation that forwards all of its requests to an underlying Statement.
042: */
043: public class BrokeredStatement implements EngineStatement {
044:
045: /**
046: My control. Use the controlCheck() method to obtain the control
047: when calling a check method. This will result in the correct exception
048: being thrown if the statement is already closed.
049: */
050: final BrokeredStatementControl control;
051:
052: final int jdbcLevel;
053: final int resultSetType;
054: final int resultSetConcurrency;
055: final int resultSetHoldability;
056:
057: /**
058: My state
059: */
060: private String cursorName;
061: private Boolean escapeProcessing;
062:
063: BrokeredStatement(BrokeredStatementControl control, int jdbcLevel)
064: throws SQLException {
065: this .control = control;
066: this .jdbcLevel = jdbcLevel;
067:
068: // save the state of the Statement while we are pretty much guaranteed the
069: // underlying statement is open.
070: resultSetType = getResultSetType();
071: resultSetConcurrency = getResultSetConcurrency();
072:
073: resultSetHoldability = getResultSetHoldability();
074: }
075:
076: public final void addBatch(String sql) throws SQLException {
077: getStatement().addBatch(sql);
078: }
079:
080: public final void clearBatch() throws SQLException {
081: getStatement().clearBatch();
082: }
083:
084: public final int[] executeBatch() throws SQLException {
085: return getStatement().executeBatch();
086: }
087:
088: public final void cancel() throws SQLException {
089: getStatement().cancel();
090: }
091:
092: public final boolean execute(String sql) throws SQLException {
093: return getStatement().execute(sql);
094: }
095:
096: public final ResultSet executeQuery(String sql) throws SQLException {
097: return wrapResultSet(getStatement().executeQuery(sql));
098: }
099:
100: public final int executeUpdate(String sql) throws SQLException {
101: return getStatement().executeUpdate(sql);
102: }
103:
104: /**
105: * In many cases, it is desirable to immediately release a
106: * Statements's database and JDBC resources instead of waiting for
107: * this to happen when it is automatically closed; the close
108: * method provides this immediate release.
109: *
110: * <P><B>Note:</B> A Statement is automatically closed when it is
111: * garbage collected. When a Statement is closed its current
112: * ResultSet, if one exists, is also closed.
113: * @exception SQLException thrown on failure.
114: */
115: public final void close() throws SQLException {
116: getStatement().close();
117: }
118:
119: public final Connection getConnection() throws SQLException {
120: return getStatement().getConnection();
121: }
122:
123: public final int getFetchDirection() throws SQLException {
124: return getStatement().getFetchDirection();
125: }
126:
127: public final int getFetchSize() throws SQLException {
128: return getStatement().getFetchSize();
129: }
130:
131: public final int getMaxFieldSize() throws SQLException {
132: return getStatement().getMaxFieldSize();
133: }
134:
135: public final int getMaxRows() throws SQLException {
136: return getStatement().getMaxRows();
137: }
138:
139: public final int getResultSetConcurrency() throws SQLException {
140: return getStatement().getResultSetConcurrency();
141: }
142:
143: /**
144: * The maxFieldSize limit (in bytes) is set to limit the size of
145: * data that can be returned for any column value; it only applies
146: * to BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR, and
147: * LONGVARCHAR fields. If the limit is exceeded, the excess data
148: * is silently discarded.
149: *
150: * @param max the new max column size limit; zero means unlimited
151: * @exception SQLException thrown on failure.
152: */
153: public final void setMaxFieldSize(int max) throws SQLException {
154: getStatement().setMaxFieldSize(max);
155: }
156:
157: /**
158: * The maxRows limit is set to limit the number of rows that any
159: * ResultSet can contain. If the limit is exceeded, the excess
160: * rows are silently dropped.
161: *
162: * @param max the new max rows limit; zero means unlimited
163: * @exception SQLException thrown on failure.
164: */
165: public final void setMaxRows(int max) throws SQLException {
166: getStatement().setMaxRows(max);
167: }
168:
169: /**
170: * If escape scanning is on (the default) the driver will do
171: * escape substitution before sending the SQL to the database.
172: *
173: * @param enable true to enable; false to disable
174: * @exception SQLException thrown on failure.
175: */
176: public final void setEscapeProcessing(boolean enable)
177: throws SQLException {
178: getStatement().setEscapeProcessing(enable);
179: escapeProcessing = enable ? Boolean.TRUE : Boolean.FALSE;
180: }
181:
182: /**
183: * The first warning reported by calls on this Statement is
184: * returned. A Statment's execute methods clear its SQLWarning
185: * chain. Subsequent Statement warnings will be chained to this
186: * SQLWarning.
187: *
188: * <p>The warning chain is automatically cleared each time
189: * a statement is (re)executed.
190: *
191: * <P><B>Note:</B> If you are processing a ResultSet then any
192: * warnings associated with ResultSet reads will be chained on the
193: * ResultSet object.
194: *
195: * @return the first SQLWarning or null
196: * @exception SQLException thrown on failure.
197: */
198: public final SQLWarning getWarnings() throws SQLException {
199: return getStatement().getWarnings();
200: }
201:
202: /**
203: * After this call getWarnings returns null until a new warning is
204: * reported for this Statement.
205: * @exception SQLException thrown on failure.
206: */
207: public final void clearWarnings() throws SQLException {
208: getStatement().clearWarnings();
209: }
210:
211: /**
212: * setCursorName defines the SQL cursor name that will be used by
213: * subsequent Statement execute methods. This name can then be
214: * used in SQL positioned update/delete statements to identify the
215: * current row in the ResultSet generated by this getStatement(). If
216: * the database doesn't support positioned update/delete, this
217: * method is a noop.
218: *
219: * <P><B>Note:</B> By definition, positioned update/delete
220: * execution must be done by a different Statement than the one
221: * which generated the ResultSet being used for positioning. Also,
222: * cursor names must be unique within a Connection.
223: *
224: * @param name the new cursor name.
225: */
226: public final void setCursorName(String name) throws SQLException {
227: getStatement().setCursorName(name);
228: cursorName = name;
229: }
230:
231: /**
232: * getResultSet returns the current result as a ResultSet. It
233: * should only be called once per result.
234: *
235: * @return the current result as a ResultSet; null if the result
236: * is an update count or there are no more results or the statement
237: * was closed.
238: * @see #execute
239: */
240: public final ResultSet getResultSet() throws SQLException {
241: return wrapResultSet(getStatement().getResultSet());
242: }
243:
244: /**
245: * getUpdateCount returns the current result as an update count;
246: * if the result is a ResultSet or there are no more results -1
247: * is returned. It should only be called once per result.
248: *
249: * <P>The only way to tell for sure that the result is an update
250: * count is to first test to see if it is a ResultSet. If it is
251: * not a ResultSet it is either an update count or there are no
252: * more results.
253: *
254: * @return the current result as an update count; -1 if it is a
255: * ResultSet or there are no more results
256: * @see #execute
257: */
258: public final int getUpdateCount() throws SQLException {
259: return getStatement().getUpdateCount();
260: }
261:
262: /**
263: * getMoreResults moves to a Statement's next result. It returns true if
264: * this result is a ResultSet. getMoreResults also implicitly
265: * closes any current ResultSet obtained with getResultSet.
266: *
267: * There are no more results when (!getMoreResults() &&
268: * (getUpdateCount() == -1)
269: *
270: * @return true if the next result is a ResultSet; false if it is
271: * an update count or there are no more results
272: * @see #execute
273: * @exception SQLException thrown on failure.
274: */
275: public final boolean getMoreResults() throws SQLException {
276: return getStatement().getMoreResults();
277: }
278:
279: /**
280: * JDBC 2.0
281: *
282: * Determine the result set type.
283: *
284: * @exception SQLException Feature not implemented for now.
285: */
286: public final int getResultSetType() throws SQLException {
287: return getStatement().getResultSetType();
288: }
289:
290: /**
291: * JDBC 2.0
292: *
293: * Give a hint as to the direction in which the rows in a result set
294: * will be processed. The hint applies only to result sets created
295: * using this Statement object. The default value is
296: * ResultSet.FETCH_FORWARD.
297: *
298: * @param direction the initial direction for processing rows
299: * @exception SQLException if a database-access error occurs or direction
300: * is not one of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or
301: * ResultSet.FETCH_UNKNOWN
302: */
303: public final void setFetchDirection(int direction)
304: throws SQLException {
305: getStatement().setFetchDirection(direction);
306: }
307:
308: /**
309: * JDBC 2.0
310: *
311: * Give the JDBC driver a hint as to the number of rows that should
312: * be fetched from the database when more rows are needed. The number
313: * of rows specified only affects result sets created using this
314: * getStatement(). If the value specified is zero, then the hint is ignored.
315: * The default value is zero.
316: *
317: * @param rows the number of rows to fetch
318: * @exception SQLException if a database-access error occurs, or the
319: * condition 0 <= rows <= this.getMaxRows() is not satisfied.
320: */
321: public final void setFetchSize(int rows) throws SQLException {
322: getStatement().setFetchSize(rows);
323: }
324:
325: public final int getQueryTimeout() throws SQLException {
326: return getStatement().getQueryTimeout();
327: }
328:
329: public final void setQueryTimeout(int seconds) throws SQLException {
330: getStatement().setQueryTimeout(seconds);
331: }
332:
333: /*
334: ** JDBC 3.0 methods
335: */
336: public final boolean execute(String sql, int autoGeneratedKeys)
337: throws SQLException {
338:
339: return getStatement().execute(sql, autoGeneratedKeys);
340: }
341:
342: public final boolean execute(String sql, int[] columnIndexes)
343: throws SQLException {
344: return getStatement().execute(sql, columnIndexes);
345: }
346:
347: public final boolean execute(String sql, String[] columnNames)
348: throws SQLException {
349: return getStatement().execute(sql, columnNames);
350: }
351:
352: public final int executeUpdate(String sql, int autoGeneratedKeys)
353: throws SQLException {
354: int retVal = getStatement().executeUpdate(sql,
355: autoGeneratedKeys);
356: return retVal;
357: }
358:
359: public final int executeUpdate(String sql, int[] columnIndexes)
360: throws SQLException {
361: return getStatement().executeUpdate(sql, columnIndexes);
362: }
363:
364: public final int executeUpdate(String sql, String[] columnNames)
365: throws SQLException {
366:
367: return getStatement().executeUpdate(sql, columnNames);
368: }
369:
370: /**
371: * JDBC 3.0
372: *
373: * Moves to this Statement obect's next result, deals with any current ResultSet
374: * object(s) according to the instructions specified by the given flag, and
375: * returns true if the next result is a ResultSet object
376: *
377: * @param current - one of the following Statement constants indicating what
378: * should happen to current ResultSet objects obtained using the method
379: * getResultSetCLOSE_CURRENT_RESULT, KEEP_CURRENT_RESULT, or CLOSE_ALL_RESULTS
380: * @return true if the next result is a ResultSet; false if it is
381: * an update count or there are no more results
382: * @see #execute
383: * @exception SQLException thrown on failure.
384: */
385: public final boolean getMoreResults(int current)
386: throws SQLException {
387: return ((EngineStatement) getStatement())
388: .getMoreResults(current);
389: }
390:
391: /**
392: * JDBC 3.0
393: *
394: * Retrieves any auto-generated keys created as a result of executing this
395: * Statement object. If this Statement object did not generate any keys, an empty
396: * ResultSet object is returned. If this Statement is a non-insert statement,
397: * an exception will be thrown.
398: *
399: * @return a ResultSet object containing the auto-generated key(s) generated by
400: * the execution of this Statement object
401: * @exception SQLException if a database access error occurs
402: */
403: public final ResultSet getGeneratedKeys() throws SQLException {
404: return wrapResultSet(getStatement().getGeneratedKeys());
405: }
406:
407: /**
408: * Return the holdability of ResultSets created by this Statement.
409: * If this Statement is active in a global transaction the
410: * CLOSE_CURSORS_ON_COMMIT will be returned regardless of
411: * the holdability it was created with. In a local transaction
412: * the original create holdabilty will be returned.
413: */
414: public final int getResultSetHoldability() throws SQLException {
415: int holdability = ((EngineStatement) getStatement())
416: .getResultSetHoldability();
417:
418: // Holdability might be downgraded.
419: return controlCheck().checkHoldCursors(holdability);
420: }
421:
422: /*
423: ** Control methods
424: */
425:
426: public Statement createDuplicateStatement(Connection conn,
427: Statement oldStatement) throws SQLException {
428:
429: Statement newStatement;
430:
431: if (jdbcLevel == 2)
432: newStatement = conn.createStatement(resultSetType,
433: resultSetConcurrency);
434: else
435: newStatement = conn.createStatement(resultSetType,
436: resultSetConcurrency, resultSetHoldability);
437:
438: setStatementState(oldStatement, newStatement);
439:
440: return newStatement;
441: }
442:
443: void setStatementState(Statement oldStatement,
444: Statement newStatement) throws SQLException {
445: if (cursorName != null)
446: newStatement.setCursorName(cursorName);
447: if (escapeProcessing != null)
448: newStatement.setEscapeProcessing(escapeProcessing
449: .booleanValue());
450:
451: newStatement
452: .setFetchDirection(oldStatement.getFetchDirection());
453: newStatement.setFetchSize(oldStatement.getFetchSize());
454: newStatement.setMaxFieldSize(oldStatement.getMaxFieldSize());
455: newStatement.setMaxRows(oldStatement.getMaxRows());
456: newStatement.setQueryTimeout(oldStatement.getQueryTimeout());
457: }
458:
459: public Statement getStatement() throws SQLException {
460: return control.getRealStatement();
461: }
462:
463: /**
464: * Provide the control access to every ResultSet we return.
465: * If required the control can wrap the ResultSet, but
466: * it (the control) must ensure a underlying ResultSet is
467: * only wrapped once, if say java.sql.Statement.getResultSet
468: * is returned twice.
469: *
470: * @param rs ResultSet being returned, can be null.
471: */
472: final ResultSet wrapResultSet(ResultSet rs) {
473: return control.wrapResultSet(this , rs);
474: }
475:
476: /**
477: Get the BrokeredStatementControl in order to perform a check.
478: Obtained indirectly to ensure that the correct exception is
479: thrown if the Statement has been closed.
480: */
481: final BrokeredStatementControl controlCheck() throws SQLException {
482: // simplest method that will throw an exception if the Statement is closed
483: getStatement().getConnection();
484: return control;
485: }
486:
487: /**
488: * Returns false unless <code>iface</code> is implemented
489: *
490: * @param iface a Class defining an interface.
491: * @return true if this implements the interface or
492: * directly or indirectly wraps an object
493: * that does.
494: * @throws java.sql.SQLException if an error occurs while determining
495: * whether this is a wrapper for an object
496: * with the given interface.
497: */
498: public boolean isWrapperFor(Class iface) throws SQLException {
499: checkIfClosed();
500: return iface.isInstance(this );
501: }
502:
503: /**
504: * Checks if the statement is closed. Not implemented for this
505: * class since <code>isClosed()</code> is a new method in JDBC
506: * 4.0. The JDBC 4.0 sub-classes should override this method.
507: *
508: * @return <code>true</code> if the statement is closed,
509: * <code>false</code> otherwise
510: * @exception SQLException not-implemented exception
511: */
512: protected boolean isClosed() throws SQLException {
513: // Not implemented since we cannot forward the call to a JDBC
514: // 4.0 method from this class. This dummy implementation is
515: // provided here so that checkIfClosed() can be implemented
516: // once in this class instead of once in each of the
517: // Brokered*Statement40 classes.
518: throw Util.notImplemented();
519: }
520:
521: /**
522: * Checks if the statement is closed and throws an exception if it
523: * is. This method relies on the <code>isClosed()</code> method
524: * and therefore only works with JDBC 4.0.
525: *
526: * @exception SQLException if the statement is closed
527: */
528: protected final void checkIfClosed() throws SQLException {
529: if (isClosed()) {
530: throw Util.generateCsSQLException(SQLState.ALREADY_CLOSED,
531: "Statement");
532: }
533: }
534: }
|