1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.jdbc.EmbedStatement |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.jdbc; |
22 | |
23 | import org.apache.derby.iapi.reference.JDBC20Translation; |
24 | import org.apache.derby.iapi.reference.JDBC30Translation; |
25 | import org.apache.derby.iapi.reference.SQLState; |
26 | |
27 | import org.apache.derby.iapi.services.sanity.SanityManager; |
28 | |
29 | import org.apache.derby.iapi.sql.Activation; |
30 | import org.apache.derby.iapi.sql.PreparedStatement; |
31 | import org.apache.derby.iapi.sql.ResultSet; |
32 | import org.apache.derby.iapi.sql.ParameterValueSet; |
33 | import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; |
34 | import org.apache.derby.iapi.error.StandardException; |
35 | import org.apache.derby.iapi.jdbc.EngineStatement; |
36 | |
37 | import java.sql.SQLException; |
38 | import java.sql.SQLWarning; |
39 | import java.util.Vector; |
40 | |
41 | /* |
42 | We would import these, but have name-overlap |
43 | import java.sql.Statement; |
44 | import java.sql.ResultSet; |
45 | */ |
46 | |
47 | /** |
48 | * |
49 | * EmbedStatement is a local JDBC statement. |
50 | * |
51 | <P><B>Supports</B> |
52 | <UL> |
53 | <LI> JSR169 - no subsetting for java.sql.Statement |
54 | <LI> JDBC 2.0 |
55 | <LI> JDBC 3.0 - no new dependencies on new JDBC 3.0 or JDK 1.4 classes, |
56 | new methods can safely be added into implementation. |
57 | </UL> |
58 | |
59 | * @author ames |
60 | */ |
61 | public class EmbedStatement extends ConnectionChild |
62 | implements EngineStatement { |
63 | |
64 | private final java.sql.Connection applicationConnection; |
65 | |
66 | /** |
67 | * Statement reference the application is using to execute |
68 | * this Statement. Normally set to this, but if this was |
69 | * created by a Connection from an XAConnection then this |
70 | * will be a reference to the BrokeredStatement. |
71 | * |
72 | * Making it protected to allow access from EmbedPreparedStatement40 |
73 | * to be used for StatementEvents |
74 | * |
75 | */ |
76 | protected EngineStatement applicationStatement; |
77 | |
78 | int updateCount = -1; |
79 | java.sql.ResultSet results; |
80 | //for jdbc3.0 feature, where you can get a resultset of rows inserted |
81 | //for auto generated columns after an insert |
82 | private java.sql.ResultSet autoGeneratedKeysResultSet; |
83 | private String cursorName; |
84 | |
85 | private final boolean forMetaData; |
86 | final int resultSetType; |
87 | private final int resultSetConcurrency; |
88 | private final int resultSetHoldability; |
89 | final LanguageConnectionContext lcc; |
90 | |
91 | private SQLWarning warnings; |
92 | String SQLText; |
93 | |
94 | private int fetchSize = 1; |
95 | private int fetchDirection = JDBC20Translation.FETCH_FORWARD; |
96 | int MaxFieldSize; |
97 | private int timeoutSeconds; |
98 | |
99 | //the state of this statement, set to false when close() is called |
100 | private boolean active = true; |
101 | |
102 | //in case of batch update, save the individual statements in the batch in this vector |
103 | //this is only used by JDBC 2.0 |
104 | Vector batchStatements; |
105 | |
106 | // The maximum # of rows to return per result set. |
107 | // (0 means no limit.) |
108 | int maxRows; |
109 | |
110 | private ParameterValueSet pvs; |
111 | |
112 | // An EmbedStatement is NOT poolable by default. The constructor for |
113 | // PreparedStatement overrides this. |
114 | protected boolean isPoolable = false; |
115 | |
116 | // |
117 | // constructor |
118 | // |
119 | public EmbedStatement (EmbedConnection connection, boolean forMetaData, |
120 | int resultSetType, int resultSetConcurrency, int resultSetHoldability) |
121 | { |
122 | super(connection); |
123 | this.forMetaData = forMetaData; |
124 | this.resultSetType = resultSetType; |
125 | this.resultSetConcurrency = resultSetConcurrency; |
126 | this.resultSetHoldability = resultSetHoldability; |
127 | |
128 | lcc = getEmbedConnection().getLanguageConnection(); |
129 | applicationConnection = getEmbedConnection().getApplicationConnection(); |
130 | applicationStatement = this; |
131 | |
132 | // By default, no statements time out. |
133 | // Timeout is set explicitly with setQueryTimeout(). |
134 | timeoutSeconds = 0; |
135 | } |
136 | |
137 | // |
138 | // java.sql.Statement interface |
139 | // the comments are those from the JDBC interface, |
140 | // so we know what we're supposed to to. |
141 | |
142 | /** |
143 | * Execute a SQL statement that returns a single ResultSet. |
144 | * |
145 | * @param sql typically this is a static SQL SELECT statement |
146 | * @return a ResultSet that contains the data produced by the |
147 | * query; never null |
148 | * @exception SQLException thrown on failure. |
149 | */ |
150 | public java.sql.ResultSet executeQuery(String sql) |
151 | throws SQLException |
152 | { |
153 | execute(sql, true, false, JDBC30Translation.NO_GENERATED_KEYS, null, null); |
154 | |
155 | if (SanityManager.DEBUG) { |
156 | if (results == null) |
157 | SanityManager.THROWASSERT("no results returned on executeQuery()"); |
158 | } |
159 | |
160 | return results; |
161 | } |
162 | |
163 | /** |
164 | * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, |
165 | * SQL statements that return nothing such as SQL DDL statements |
166 | * can be executed. |
167 | * |
168 | * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL |
169 | * statement that returns nothing |
170 | * @return either the row count for INSERT, UPDATE or DELETE; or 0 |
171 | * for SQL statements that return nothing |
172 | * @exception SQLException thrown on failure. |
173 | */ |
174 | public int executeUpdate(String sql) throws SQLException |
175 | { |
176 | execute(sql, false, true, JDBC30Translation.NO_GENERATED_KEYS, null, null); |
177 | return updateCount; |
178 | } |
179 | |
180 | /** |
181 | * JDBC 3.0 |
182 | * |
183 | * Execute the given SQL statement and signals the driver with the given flag |
184 | * about whether the auto-generated keys produced by this Statement object |
185 | * should be made available for retrieval. |
186 | * |
187 | * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL |
188 | * statement that returns nothing |
189 | * @param autoGeneratedKeys - a flag indicating whether auto-generated keys |
190 | * should be made available for retrieval; one of the following constants: |
191 | * Statement.RETURN_GENERATED_KEYS Statement.NO_GENERATED_KEYS |
192 | * @return either the row count for INSERT, UPDATE or DELETE; or 0 |
193 | * for SQL statements that return nothing |
194 | * @exception SQLException if a database access error occurs |
195 | */ |
196 | public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException |
197 | { |
198 | execute(sql, false, true, autoGeneratedKeys, null, null); |
199 | return updateCount; |
200 | } |
201 | |
202 | /** |
203 | * JDBC 3.0 |
204 | * |
205 | * Executes the given SQL statement and signals the driver that the |
206 | * auto-generated keys indicated in the given array should be made |
207 | * available for retrieval. The driver will ignore the array if the SQL |
208 | * statement is not an INSERT statement |
209 | * |
210 | * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL |
211 | * statement that returns nothing |
212 | * @param columnIndexes - an array of column indexes indicating the |
213 | * columns that should be returned from the inserted row |
214 | * @return either the row count for INSERT, UPDATE or DELETE; or 0 |
215 | * for SQL statements that return nothing |
216 | * @exception SQLException if a database access error occurs |
217 | */ |
218 | public int executeUpdate(String sql, int[] columnIndexes) throws SQLException |
219 | { |
220 | throw Util.notImplemented("executeUpdate(String, int[])"); |
221 | } |
222 | |
223 | /** |
224 | * JDBC 3.0 |
225 | * |
226 | * Executes the given SQL statement and signals the driver that the |
227 | * auto-generated keys indicated in the given array should be made |
228 | * available for retrieval. The driver will ignore the array if the SQL |
229 | * statement is not an INSERT statement |
230 | * |
231 | * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL |
232 | * statement that returns nothing |
233 | * @param columnNames - an array of the names of the columns |
234 | * that should be returned from the inserted row |
235 | * @return either the row count for INSERT, UPDATE or DELETE; or 0 |
236 | * for SQL statements that return nothing |
237 | * @exception SQLException if a database access error occurs |
238 | */ |
239 | public int executeUpdate(String sql, String[] columnNames) throws SQLException |
240 | { |
241 | throw Util.notImplemented("executeUpdate(String, String[])"); |
242 | } |
243 | |
244 | final void checkIfInMiddleOfBatch() throws SQLException { |
245 | /* If batchStatements is not null then we are in the middle |
246 | * of a batch. That's an invalid state. We need to finish the |
247 | * batch either by clearing the batch or executing the batch. |
248 | * executeUpdate is not allowed inside the batch. |
249 | */ |
250 | if (batchStatements != null) |
251 | throw newSQLException(SQLState.MIDDLE_OF_BATCH); |
252 | } |
253 | |
254 | /** |
255 | * Tell whether this statment has been closed or not. |
256 | * |
257 | * @return <code>true</code> is closed, <code>false</code> otherwise. |
258 | * @exception SQLException if a database access error occurs. |
259 | */ |
260 | public boolean isClosed() throws SQLException { |
261 | // If active, verify state by consulting parent connection. |
262 | if (active) { |
263 | try { |
264 | checkExecStatus(); |
265 | } catch (SQLException sqle) { |
266 | } |
267 | } |
268 | return !active; |
269 | } |
270 | |
271 | /** |
272 | * In many cases, it is desirable to immediately release a |
273 | * Statements's database and JDBC resources instead of waiting for |
274 | * this to happen when it is automatically closed; the close |
275 | * method provides this immediate release. |
276 | * |
277 | * <P><B>Note:</B> A Statement is automatically closed when it is |
278 | * garbage collected. When a Statement is closed its current |
279 | * ResultSet, if one exists, is also closed. |
280 | * @exception SQLException thrown on failure. |
281 | */ |
282 | public final void close() throws SQLException { |
283 | |
284 | /* The close() method is the only method |
285 | * that is allowed to be called on a closed |
286 | * Statement, as per Jon Ellis. |
287 | */ |
288 | if (!active) |
289 | { |
290 | return; |
291 | } |
292 | |
293 | synchronized (getConnectionSynchronization()) { |
294 | |
295 | closeActions(); |
296 | |
297 | //we first set the status |
298 | active = false; |
299 | |
300 | //first, clear the resutl set |
301 | clearResultSets(); |
302 | |
303 | //next, release other resource |
304 | cursorName = null; |
305 | warnings = null; |
306 | SQLText = null; |
307 | batchStatements = null; |
308 | } |
309 | } |
310 | |
311 | // allow sub-classes to execute additional close |
312 | // logic while holding the synchronization. |
313 | void closeActions() throws SQLException { |
314 | } |
315 | |
316 | //---------------------------------------------------------------------- |
317 | |
318 | /** |
319 | * The maxFieldSize limit (in bytes) is the maximum amount of data |
320 | * returned for any column value; it only applies to BINARY, |
321 | * VARBINARY, LONGVARBINARY, CHAR, VARCHAR, and LONGVARCHAR |
322 | * columns. If the limit is exceeded, the excess data is silently |
323 | * discarded. |
324 | * |
325 | * @return the current max column size limit; zero means unlimited |
326 | * @exception SQLException thrown on failure. |
327 | */ |
328 | public int getMaxFieldSize() throws SQLException { |
329 | checkStatus(); |
330 | |
331 | return MaxFieldSize; |
332 | } |
333 | |
334 | /** |
335 | * The maxFieldSize limit (in bytes) is set to limit the size of |
336 | * data that can be returned for any column value; it only applies |
337 | * to BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR, and |
338 | * LONGVARCHAR fields. If the limit is exceeded, the excess data |
339 | * is silently discarded. |
340 | * |
341 | * @param max the new max column size limit; zero means unlimited |
342 | * @exception SQLException thrown on failure. |
343 | */ |
344 | public void setMaxFieldSize(int max) throws SQLException { |
345 | checkStatus(); |
346 | |
347 | if (max < 0) |
348 | { |
349 | throw newSQLException(SQLState.INVALID_MAXFIELD_SIZE, new Integer(max)); |
350 | } |
351 | this.MaxFieldSize = max; |
352 | } |
353 | |
354 | /** |
355 | * The maxRows limit is the maximum number of rows that a |
356 | * ResultSet can contain. If the limit is exceeded, the excess |
357 | * rows are silently dropped. |
358 | * |
359 | * @return the current max row limit; zero means unlimited |
360 | * @exception SQLException thrown on failure. |
361 | */ |
362 | public int getMaxRows() throws SQLException |
363 | { |
364 | checkStatus(); |
365 | return maxRows; |
366 | } |
367 | |
368 | /** |
369 | * The maxRows limit is set to limit the number of rows that any |
370 | * ResultSet can contain. If the limit is exceeded, the excess |
371 | * rows are silently dropped. |
372 | * |
373 | * @param max the new max rows limit; zero means unlimited |
374 | * @exception SQLException thrown on failure. |
375 | */ |
376 | public void setMaxRows(int max) throws SQLException |
377 | { |
378 | checkStatus(); |
379 | if (max < 0) |
380 | { |
381 | throw newSQLException(SQLState.INVALID_MAX_ROWS_VALUE, new Integer(max)); |
382 | } |
383 | this.maxRows = max; |
384 | } |
385 | |
386 | /** |
387 | * If escape scanning is on (the default) the driver will do |
388 | * escape substitution before sending the SQL to the database. |
389 | * |
390 | * @param enable true to enable; false to disable |
391 | * @exception SQLException thrown on failure. |
392 | */ |
393 | public void setEscapeProcessing(boolean enable) throws SQLException { |
394 | checkStatus(); |
395 | // Nothing to do in our server , just ignore it. |
396 | |
397 | } |
398 | |
399 | /** |
400 | * The queryTimeout limit is the number of seconds the driver will |
401 | * wait for a Statement to execute. If the limit is exceeded a |
402 | * SQLException is thrown. |
403 | * |
404 | * @return the current query timeout limit in seconds; zero means unlimited |
405 | * @exception SQLException thrown on failure. |
406 | */ |
407 | public final int getQueryTimeout() throws SQLException { |
408 | checkStatus(); |
409 | return timeoutSeconds; |
410 | } |
411 | |
412 | /** |
413 | * The queryTimeout limit is the number of seconds the driver will |
414 | * wait for a Statement to execute. If the limit is exceeded a |
415 | * SQLException is thrown. |
416 | * |
417 | * @param seconds the new query timeout limit in seconds; zero means unlimited |
418 | * @exception SQLException thrown on failure. |
419 | */ |
420 | public final void setQueryTimeout(int seconds) throws SQLException { |
421 | checkStatus(); |
422 | if (seconds < 0) { |
423 | throw newSQLException(SQLState.INVALID_QUERYTIMEOUT_VALUE, |
424 | new Integer(seconds)); |
425 | } |
426 | timeoutSeconds = seconds; |
427 | } |
428 | |
429 | /** |
430 | * Cancel can be used by one thread to cancel a statement that |
431 | * is being executed by another thread. |
432 | * @exception SQLException thrown on failure. |
433 | */ |
434 | public void cancel() throws SQLException { |
435 | throw Util.notImplemented("cancel"); |
436 | } |
437 | |
438 | /** |
439 | * The first warning reported by calls on this Statement is |
440 | * returned. A Statment's execute methods clear its SQLWarning |
441 | * chain. Subsequent Statement warnings will be chained to this |
442 | * SQLWarning. |
443 | * |
444 | * <p>The warning chain is automatically cleared each time |
445 | * a statement is (re)executed. |
446 | * |
447 | * <P><B>Note:</B> If you are processing a ResultSet then any |
448 | * warnings associated with ResultSet reads will be chained on the |
449 | * ResultSet object. |
450 | * |
451 | * @return the first SQLWarning or null |
452 | * @exception SQLException thrown on failure. |
453 | */ |
454 | public SQLWarning getWarnings() throws SQLException { |
455 | checkStatus(); |
456 | return warnings; |
457 | } |
458 | |
459 | /** |
460 | * After this call getWarnings returns null until a new warning is |
461 | * reported for this Statement. |
462 | * @exception SQLException thrown on failure. |
463 | */ |
464 | public void clearWarnings() throws SQLException { |
465 | checkStatus(); |
466 | warnings = null; |
467 | } |
468 | |
469 | /** |
470 | * setCursorName defines the SQL cursor name that will be used by |
471 | * subsequent Statement execute methods. This name can then be |
472 | * used in SQL positioned update/delete statements to identify the |
473 | * current row in the ResultSet generated by this statement. If |
474 | * the database doesn't support positioned update/delete, this |
475 | * method is a noop. |
476 | * |
477 | * <P><B>Note:</B> By definition, positioned update/delete |
478 | * execution must be done by a different Statement than the one |
479 | * which generated the ResultSet being used for positioning. Also, |
480 | * cursor names must be unique within a Connection. |
481 | * |
482 | * @param name the new cursor name. |
483 | */ |
484 | public void setCursorName(String name) throws SQLException { |
485 | checkStatus(); |
486 | cursorName = name; |
487 | } |
488 | |
489 | //----------------------- Multiple Results -------------------------- |
490 | |
491 | /** |
492 | * Execute a SQL statement that may return multiple results. |
493 | * Under some (uncommon) situations a single SQL statement may return |
494 | * multiple result sets and/or update counts. Normally you can ignore |
495 | * this, unless you're executing a stored procedure that you know may |
496 | * return multiple results, or unless you're dynamically executing an |
497 | * unknown SQL string. The "execute", "getMoreResults", "getResultSet" |
498 | * and "getUpdateCount" methods let you navigate through multiple results. |
499 | * |
500 | * The "execute" method executes a SQL statement and indicates the |
501 | * form of the first result. You can then use getResultSet or |
502 | * getUpdateCount to retrieve the result, and getMoreResults to |
503 | * move to any subsequent result(s). |
504 | * |
505 | * @param sql any SQL statement |
506 | * |
507 | * @return true if the first result is a ResultSet; false if it is an integer |
508 | * @see #getResultSet |
509 | * @see #getUpdateCount |
510 | * @see #getMoreResults |
511 | * @exception SQLException thrown on failure |
512 | */ |
513 | public boolean execute(String sql) |
514 | throws SQLException |
515 | { |
516 | return execute(sql, false, false, JDBC30Translation.NO_GENERATED_KEYS, null, null); |
517 | } |
518 | |
519 | /** |
520 | * Execute a SQL statement that may return multiple results. |
521 | * Under some (uncommon) situations a single SQL statement may return |
522 | * multiple result sets and/or update counts. Normally you can ignore |
523 | * this, unless you're executing a stored procedure that you know may |
524 | * return multiple results, or unless you're dynamically executing an |
525 | * unknown SQL string. The "execute", "getMoreResults", "getResultSet" |
526 | * and "getUpdateCount" methods let you navigate through multiple results. |
527 | * |
528 | * The "execute" method executes a SQL statement and indicates the |
529 | * form of the first result. You can then use getResultSet or |
530 | * getUpdateCount to retrieve the result, and getMoreResults to |
531 | * move to any subsequent result(s). |
532 | * |
533 | * @param sql any SQL statement |
534 | * @param executeQuery caller is executeQuery() |
535 | * @param executeUpdate caller is executeUpdate() |
536 | * @param autoGeneratedKeys |
537 | * @param columnIndexes |
538 | * @param columnNames |
539 | * |
540 | * @return true if the first result is a ResultSet; false if it is an integer |
541 | * @see #getResultSet |
542 | * @see #getUpdateCount |
543 | * @see #getMoreResults |
544 | * @exception SQLException thrown on failure |
545 | */ |
546 | private boolean execute(String sql, boolean executeQuery, boolean executeUpdate, |
547 | int autoGeneratedKeys, int[] columnIndexes, String[] columnNames) throws SQLException |
548 | { |
549 | synchronized (getConnectionSynchronization()) { |
550 | |
551 | checkExecStatus(); |
552 | if (sql == null) { |
553 | throw newSQLException(SQLState.NULL_SQL_TEXT); |
554 | } |
555 | checkIfInMiddleOfBatch(); |
556 | clearResultSets(); // release the last statement executed, if any. |
557 | |
558 | setupContextStack(); // make sure there's context |
559 | |
560 | |
561 | // try to remember the SQL statement in case anybody asks for it |
562 | SQLText = sql; |
563 | |
564 | try { |
565 | Activation activation; |
566 | try { |
567 | PreparedStatement preparedStatement = lcc.prepareInternalStatement |
568 | (lcc.getDefaultSchema(), sql, resultSetConcurrency==JDBC20Translation.CONCUR_READ_ONLY, false); |
569 | activation = |
570 | preparedStatement.getActivation(lcc, resultSetType == JDBC20Translation.TYPE_SCROLL_INSENSITIVE); |
571 | checkRequiresCallableStatement(activation); |
572 | } catch (Throwable t) { |
573 | throw handleException(t); |
574 | } |
575 | |
576 | |
577 | // this is for a Statement execution |
578 | activation.setSingleExecution(); |
579 | |
580 | //bug 4838 - save the auto-generated key information in activation. keeping this |
581 | //information in lcc will not work work it can be tampered by a nested trasaction |
582 | if (autoGeneratedKeys == JDBC30Translation.RETURN_GENERATED_KEYS) |
583 | activation.setAutoGeneratedKeysResultsetInfo(columnIndexes, columnNames); |
584 | return executeStatement(activation, executeQuery, executeUpdate); |
585 | } finally { |
586 | restoreContextStack(); |
587 | } |
588 | } |
589 | } |
590 | |
591 | /** |
592 | * JDBC 3.0 |
593 | * |
594 | * Executes the given SQL statement, which may return multiple |
595 | * results, and signals the driver that any auto-generated keys |
596 | * should be made available for retrieval. The driver will ignore |
597 | * this signal if the SQL statement is not an INSERT statement. |
598 | * |
599 | * @param sql any SQL statement |
600 | * @param autoGeneratedKeys - a constant indicating whether |
601 | * auto-generated keys should be made available for retrieval using |
602 | * the method getGeneratedKeys; one of the following constants: |
603 | * Statement.RETURN_GENERATED_KEYS or Statement.NO_GENERATED_KEYS |
604 | * @return rue if the first result is a ResultSet object; false if |
605 | * it is an update count or there are no results |
606 | * @exception SQLException if a database access error occurs |
607 | */ |
608 | public boolean execute(String sql, int autoGeneratedKeys) throws SQLException |
609 | { |
610 | return execute(sql, false, false, autoGeneratedKeys, null, null); |
611 | } |
612 | |
613 | /** |
614 | * JDBC 3.0 |
615 | * |
616 | * Executes the given SQL statement, which may return multiple |
617 | * results, and signals the driver that the auto-generated keys |
618 | * indicated in the given array should be made available for retrieval. |
619 | * This array contains the indexes of the columns in the target table |
620 | * that contain the auto-generated keys that should be made available. |
621 | * The driver will ignore the array if the given SQL statement is not an |
622 | * INSERT statement. |
623 | * |
624 | * @param sql any SQL statement |
625 | * @param columnIndexes - an array of the indexes of the columns in the |
626 | * inserted row that should be made available for retrieval by a call to |
627 | * the method getGeneratedKeys |
628 | * @return rue if the first result is a ResultSet object; false if |
629 | * it is an update count or there are no results |
630 | * @exception SQLException if a database access error occurs |
631 | */ |
632 | public boolean execute(String sql, int[] columnIndexes) throws SQLException |
633 | { |
634 | throw Util.notImplemented("execute(String, int[])"); |
635 | } |
636 | |
637 | /** |
638 | * JDBC 3.0 |
639 | * |
640 | * Executes the given SQL statement, which may return multiple |
641 | * results, and signals the driver that the auto-generated keys |
642 | * indicated in the given array should be made available for retrieval. |
643 | * This array contains the names of the columns in the target table |
644 | * that contain the auto-generated keys that should be made available. |
645 | * The driver will ignore the array if the given SQL statement is not an |
646 | * INSERT statement. |
647 | * |
648 | * @param sql any SQL statement |
649 | * @param columnNames - an array of the names of the columns in the |
650 | * inserted row that should be made available for retrieval by a call to |
651 | * the method getGeneratedKeys |
652 | * @return rue if the first result is a ResultSet object; false if |
653 | * it is an update count or there are no results |
654 | * @exception SQLException if a database access error occurs |
655 | */ |
656 | public boolean execute(String sql, String[] columnNames) throws SQLException |
657 | { |
658 | throw Util.notImplemented("execute(String, String[])"); |
659 | } |
660 | |
661 | /** |
662 | * getResultSet returns the current result as a ResultSet. It |
663 | * should only be called once per result. |
664 | * |
665 | * @return the current result as a ResultSet; null if the result |
666 | * is an update count or there are no more results or the statement |
667 | * was closed. |
668 | * @see #execute |
669 | */ |
670 | public final java.sql.ResultSet getResultSet() throws SQLException { |
671 | checkStatus(); |
672 | |
673 | return results; |
674 | } |
675 | |
676 | /** |
677 | * getUpdateCount returns the current result as an update count; |
678 | * if the result is a ResultSet or there are no more results -1 |
679 | * is returned. It should only be called once per result. |
680 | * |
681 | * <P>The only way to tell for sure that the result is an update |
682 | * count is to first test to see if it is a ResultSet. If it is |
683 | * not a ResultSet it is either an update count or there are no |
684 | * more results. |
685 | * |
686 | * @return the current result as an update count; -1 if it is a |
687 | * ResultSet or there are no more results |
688 | * @see #execute |
689 | */ |
690 | public final int getUpdateCount() throws SQLException { |
691 | checkStatus(); |
692 | return updateCount; |
693 | } |
694 | |
695 | /** |
696 | * getMoreResults moves to a Statement's next result. It returns true if |
697 | * this result is a ResultSet. getMoreResults also implicitly |
698 | * closes any current ResultSet obtained with getResultSet. |
699 | * |
700 | * There are no more results when (!getMoreResults() && |
701 | * (getUpdateCount() == -1) |
702 | * |
703 | * @return true if the next result is a ResultSet; false if it is |
704 | * an update count or there are no more results |
705 | * @see #execute |
706 | * @exception SQLException thrown on failure. |
707 | */ |
708 | public final boolean getMoreResults() throws SQLException { |
709 | return getMoreResults(JDBC30Translation.CLOSE_ALL_RESULTS); |
710 | } |
711 | |
712 | ///////////////////////////////////////////////////////////////////////// |
713 | // |
714 | // JDBC 2.0 methods that are implemented here because EmbedPreparedStatement |
715 | // and EmbedCallableStatement in Local20 need access to them, and those |
716 | // classes extend their peer classes in Local, instead of EmbedStatement |
717 | // in Local20 |
718 | // |
719 | // We do the same of JDBC 3.0 methods. |
720 | ///////////////////////////////////////////////////////////////////////// |
721 | |
722 | /** |
723 | * JDBC 2.0 |
724 | * |
725 | * Determine the result set type. |
726 | * |
727 | * @exception SQLException Feature not implemented for now. |
728 | */ |
729 | public final int getResultSetType() |
730 | throws SQLException |
731 | { |
732 | checkStatus(); |
733 | return resultSetType; |
734 | } |
735 | |
736 | |
737 | /** |
738 | * JDBC 2.0 |
739 | * |
740 | * Give a hint as to the direction in which the rows in a result set |
741 | * will be processed. The hint applies only to result sets created |
742 | * using this Statement object. The default value is |
743 | * ResultSet.FETCH_FORWARD. |
744 | * |
745 | * @param direction the initial direction for processing rows |
746 | * @exception SQLException if a database-access error occurs or direction |
747 | * is not one of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or |
748 | * ResultSet.FETCH_UNKNOWN |
749 | */ |
750 | public void setFetchDirection(int direction) throws SQLException { |
751 | |
752 | checkStatus(); |
753 | /* fetch direction is meaningless to us. we just save |
754 | * it off if it is valid and return the current value if asked. |
755 | */ |
756 | if (direction == JDBC20Translation.FETCH_FORWARD || |
757 | direction == JDBC20Translation.FETCH_REVERSE || |
758 | direction == JDBC20Translation.FETCH_UNKNOWN ) |
759 | { |
760 | fetchDirection = direction; |
761 | }else |
762 | throw newSQLException(SQLState.INVALID_FETCH_DIRECTION, |
763 | new Integer(direction)); |
764 | } |
765 | |
766 | /** |
767 | * JDBC 2.0 |
768 | * |
769 | * Determine the fetch direction. |
770 | * |
771 | * @return the default fetch direction |
772 | * @exception SQLException if a database-access error occurs |
773 | */ |
774 | public int getFetchDirection() throws SQLException { |
775 | checkStatus(); |
776 | return fetchDirection; |
777 | } |
778 | |
779 | |
780 | /** |
781 | * JDBC 2.0 |
782 | * |
783 | * Give the JDBC driver a hint as to the number of rows that should |
784 | * be fetched from the database when more rows are needed. The number |
785 | * of rows specified only affects result sets created using this |
786 | * statement. If the value specified is zero, then the hint is ignored. |
787 | * The default value is zero. |
788 | * |
789 | * @param rows the number of rows to fetch |
790 | * @exception SQLException if a database-access error occurs, or the |
791 | * condition 0 <= rows <= this.getMaxRows() is not satisfied. |
792 | */ |
793 | public void setFetchSize(int rows) throws SQLException { |
794 | checkStatus(); |
795 | if (rows < 0 || (this.getMaxRows() != 0 && |
796 | rows > this.getMaxRows())) |
797 | { |
798 | throw newSQLException(SQLState.INVALID_ST_FETCH_SIZE, new Integer(rows)); |
799 | }else if ( rows > 0 ) // ignore the call if the value is zero |
800 | fetchSize = rows; |
801 | } |
802 | |
803 | /** |
804 | * JDBC 2.0 |
805 | * |
806 | * Determine the default fetch size. |
807 | * @exception SQLException if a database-access error occurs |
808 | * |
809 | */ |
810 | public int getFetchSize() throws SQLException { |
811 | checkStatus(); |
812 | return fetchSize; |
813 | } |
814 | |
815 | /** |
816 | * JDBC 2.0 |
817 | * |
818 | * Determine the result set concurrency. |
819 | * |
820 | * @exception SQLException Feature not implemented for now. |
821 | */ |
822 | public int getResultSetConcurrency() throws SQLException { |
823 | checkStatus(); |
824 | return resultSetConcurrency; |
825 | } |
826 | |
827 | /** |
828 | * JDBC 3.0 |
829 | * |
830 | * Retrieves the result set holdability for ResultSet objects |
831 | * generated by this Statement object. |
832 | * |
833 | * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or |
834 | * ResultSet.CLOSE_CURSORS_AT_COMMIT |
835 | * @exception SQLException Feature not implemented for now. |
836 | */ |
837 | public final int getResultSetHoldability() throws SQLException { |
838 | checkStatus(); |
839 | return resultSetHoldability; |
840 | } |
841 | |
842 | /** |
843 | * JDBC 2.0 |
844 | * |
845 | * Adds a SQL command to the current batch of commmands for the statement. |
846 | * This method is optional. |
847 | * |
848 | * @param sql typically this is a static SQL INSERT or UPDATE statement |
849 | * @exception SQLException if a database-access error occurs, or the |
850 | * driver does not support batch statements |
851 | */ |
852 | public void addBatch( String sql ) throws SQLException { |
853 | checkStatus(); |
854 | synchronized (getConnectionSynchronization()) { |
855 | if (batchStatements == null) |
856 | batchStatements = new Vector(); |
857 | batchStatements.addElement(sql); |
858 | } |
859 | } |
860 | |
861 | /** |
862 | * JDBC 2.0 |
863 | * |
864 | * Make the set of commands in the current batch empty. |
865 | * This method is optional. |
866 | * |
867 | * @exception SQLException if a database-access error occurs, or the |
868 | * driver does not support batch statements |
869 | */ |
870 | public final void clearBatch() throws SQLException { |
871 | checkStatus(); |
872 | synchronized (getConnectionSynchronization()) { |
873 | batchStatements = null; |
874 | } |
875 | } |
876 | |
877 | /** |
878 | * JDBC 2.0 |
879 | * |
880 | * Submit a batch of commands to the database for execution. |
881 | * This method is optional. |
882 | * |
883 | * Moving jdbc2.0 batch related code in this class because |
884 | * callableStatement in jdbc 20 needs this code too and it doesn't derive |
885 | * from prepared statement in jdbc 20 in our implementation. |
886 | * BatchUpdateException is the only new class from jdbc 20 which is being |
887 | * referenced here and in order to avoid any jdk11x problems, using |
888 | * reflection code to make an instance of that class. |
889 | * |
890 | * @return an array of update counts containing one element for each |
891 | * command in the batch. The array is ordered according |
892 | * to the order in which commands were inserted into the batch |
893 | * @exception SQLException if a database-access error occurs, or the |
894 | * driver does not support batch statements |
895 | */ |
896 | public int[] executeBatch() throws SQLException { |
897 | checkExecStatus(); |
898 | synchronized (getConnectionSynchronization()) |
899 | { |
900 | setupContextStack(); |
901 | int i = 0; |
902 | // As per the jdbc 2.0 specs, close the statement object's current resultset |
903 | // if one is open. |
904 | // Are there results? |
905 | // outside of the lower try/finally since results will |
906 | // setup and restore themselves. |
907 | clearResultSets(); |
908 | |
909 | Vector stmts = batchStatements; |
910 | batchStatements = null; |
911 | int size; |
912 | if (stmts == null) |
913 | size = 0; |
914 | else |
915 | size = stmts.size(); |
916 | |
917 | int[] returnUpdateCountForBatch = new int[size]; |
918 | |
919 | SQLException sqle; |
920 | try { |
921 | for (; i< size; i++) |
922 | { |
923 | if (executeBatchElement(stmts.elementAt(i))) |
924 | throw newSQLException(SQLState.RESULTSET_RETURN_NOT_ALLOWED); |
925 | returnUpdateCountForBatch[i] = getUpdateCount(); |
926 | } |
927 | return returnUpdateCountForBatch; |
928 | } |
929 | catch (StandardException se) { |
930 | |
931 | sqle = handleException(se); |
932 | } |
933 | catch (SQLException sqle2) |
934 | { |
935 | sqle = sqle2; |
936 | } |
937 | finally |
938 | { |
939 | restoreContextStack(); |
940 | } |
941 | |
942 | int successfulUpdateCount[] = new int[i]; |
943 | for (int j=0; j<i; j++) |
944 | { |
945 | successfulUpdateCount[j] = returnUpdateCountForBatch[j]; |
946 | } |
947 | |
948 | SQLException batch = |
949 | new java.sql.BatchUpdateException(sqle.getMessage(), sqle.getSQLState(), |
950 | sqle.getErrorCode(), successfulUpdateCount); |
951 | |
952 | batch.setNextException(sqle); |
953 | throw batch; |
954 | } |
955 | } |
956 | |
957 | /** |
958 | Execute a single element of the batch. Overridden by EmbedPreparedStatement |
959 | */ |
960 | boolean executeBatchElement(Object batchElement) throws SQLException, StandardException { |
961 | return execute((String)batchElement, false, true, JDBC30Translation.NO_GENERATED_KEYS, null, null); |
962 | } |
963 | |
964 | /** |
965 | * JDBC 2.0 |
966 | * |
967 | * Return the Connection that produced the Statement. |
968 | * |
969 | * @exception SQLException Exception if it cannot find the connection |
970 | * associated to this statement. |
971 | */ |
972 | public final java.sql.Connection getConnection() throws SQLException { |
973 | checkStatus(); |
974 | |
975 | java.sql.Connection appConn = getEmbedConnection().getApplicationConnection(); |
976 | if ((appConn != applicationConnection) || (appConn == null)) { |
977 | |
978 | throw Util.noCurrentConnection(); |
979 | } |
980 | return appConn; |
981 | } |
982 | |
983 | /** |
984 | * JDBC 3.0 |
985 | * |
986 | * Moves to this Statement obect's next result, deals with any current ResultSet |
987 | * object(s) according to the instructions specified by the given flag, and |
988 | * returns true if the next result is a ResultSet object |
989 | * |
990 | * @param current - one of the following Statement constants indicating what |
991 | * should happen to current ResultSet objects obtained using the method |
992 | * getResultSetCLOSE_CURRENT_RESULT, KEEP_CURRENT_RESULT, or CLOSE_ALL_RESULTS |
993 | * @return true if the next result is a ResultSet; false if it is |
994 | * an update count or there are no more results |
995 | * @see #execute |
996 | * @exception SQLException thrown on failure. |
997 | */ |
998 | public final boolean getMoreResults(int current) throws SQLException { |
999 | checkExecStatus(); |
1000 | |
1001 | synchronized (getConnectionSynchronization()) { |
1002 | if (dynamicResults == null) { |
1003 | // we only have the one resultset, so this is |
1004 | // simply a close for us. |
1005 | clearResultSets(); |
1006 | return false; |
1007 | } |
1008 | |
1009 | int startingClose; |
1010 | switch (current) { |
1011 | default: |
1012 | case JDBC30Translation.CLOSE_ALL_RESULTS: |
1013 | startingClose = 0; |
1014 | break; |
1015 | case JDBC30Translation.CLOSE_CURRENT_RESULT: |
1016 | // just close the current result set. |
1017 | startingClose = currentDynamicResultSet; |
1018 | break; |
1019 | case JDBC30Translation.KEEP_CURRENT_RESULT: |
1020 | // make the close loop a no-op. |
1021 | startingClose = dynamicResults.length; |
1022 | break; |
1023 | } |
1024 | |
1025 | // Close loop. |
1026 | SQLException se = null; |
1027 | for (int i = startingClose; i <= currentDynamicResultSet && i < dynamicResults.length; i++) { |
1028 | EmbedResultSet lrs = dynamicResults[i]; |
1029 | if (lrs == null) |
1030 | continue; |
1031 | |
1032 | |
1033 | try { |
1034 | lrs.close(); |
1035 | } catch (SQLException sqle) { |
1036 | if (se == null) |
1037 | se = sqle; |
1038 | else |
1039 | se.setNextException(sqle); |
1040 | } finally { |
1041 | dynamicResults[i] = null; |
1042 | } |
1043 | } |
1044 | |
1045 | if (se != null) { |
1046 | // leave positioned on the current result set (?) |
1047 | throw se; |
1048 | } |
1049 | |
1050 | updateCount = -1; |
1051 | |
1052 | while (++currentDynamicResultSet < dynamicResults.length) { |
1053 | |
1054 | EmbedResultSet lrs = dynamicResults[currentDynamicResultSet]; |
1055 | if (lrs != null) { |
1056 | if (lrs.isClosed) { |
1057 | dynamicResults[currentDynamicResultSet] = null; |
1058 | continue; |
1059 | } |
1060 | |
1061 | results = lrs; |
1062 | |
1063 | return true; |
1064 | } |
1065 | } |
1066 | |
1067 | results = null; |
1068 | return false; |
1069 | } |
1070 | } |
1071 | |
1072 | /** |
1073 | * JDBC 3.0 |
1074 | * |
1075 | * Retrieves any auto-generated keys created as a result of executing this |
1076 | * Statement object. If this Statement is a non-insert statement, |
1077 | * a null ResultSet object is returned. |
1078 | * |
1079 | * @return a ResultSet object containing the auto-generated key(s) generated by |
1080 | * the execution of this Statement object |
1081 | * @exception SQLException if a database access error occurs |
1082 | */ |
1083 | public final java.sql.ResultSet getGeneratedKeys() throws SQLException { |
1084 | checkStatus(); |
1085 | if (autoGeneratedKeysResultSet == null) |
1086 | return null; |
1087 | else { |
1088 | execute("VALUES IDENTITY_VAL_LOCAL()", true, false, JDBC30Translation.NO_GENERATED_KEYS, null, null); |
1089 | return results; |
1090 | } |
1091 | } |
1092 | |
1093 | ///////////////////////////////////////////////////////////////////////// |
1094 | // |
1095 | // Implementation specific methods |
1096 | // |
1097 | ///////////////////////////////////////////////////////////////////////// |
1098 | |
1099 | /** |
1100 | Execute the current statement. |
1101 | @exception SQLException thrown on failure. |
1102 | */ |
1103 | boolean executeStatement(Activation a, |
1104 | boolean executeQuery, boolean executeUpdate) |
1105 | throws SQLException { |
1106 | |
1107 | // we don't differentiate the update from the resultset case. |
1108 | // so, there could be a result set. |
1109 | |
1110 | // note: the statement interface will paste together |
1111 | // an activation and make sure the prepared statement |
1112 | // is still valid, so it is preferrable, for now, |
1113 | // to creating our own activation and stuffing it in |
1114 | // the prepared statement. |
1115 | |
1116 | synchronized (getConnectionSynchronization()) { |
1117 | setupContextStack(); // make sure there's context |
1118 | boolean retval; |
1119 | |
1120 | pvs = a.getParameterValueSet(); |
1121 | |
1122 | try { |
1123 | // The following is from the javadoc for java.sql.Statement |
1124 | // Only one ResultSet per Statement can be open at any point in time. |
1125 | // Therefore, if the reading of one ResultSet is interleaved with the |
1126 | // reading of another, each must have been generated by different Statements. |
1127 | // All statement execute methods implicitly close a |
1128 | // statment's current ResultSet if an open one exists. |
1129 | if (results != null) { |
1130 | results.close(); |
1131 | results = null; |
1132 | } |
1133 | |
1134 | clearWarnings(); |
1135 | |
1136 | if (! forMetaData) { |
1137 | commitIfNeeded(); // commit the last statement if needed |
1138 | needCommit(); |
1139 | } else { |
1140 | |
1141 | |
1142 | if (lcc.getActivationCount() > 1) { |
1143 | // we do not want to commit here as there seems to be other |
1144 | // statements/resultSets currently opened for this connection. |
1145 | } else { |
1146 | commitIfNeeded(); // we can legitimately commit |
1147 | needCommit(); |
1148 | } |
1149 | } |
1150 | |
1151 | // if this was a prepared statement, this just |
1152 | // gets it for us, it won't recompile unless it is invalid. |
1153 | PreparedStatement ps = a.getPreparedStatement(); |
1154 | ps.rePrepare(lcc); |
1155 | addWarning(ps.getCompileTimeWarnings()); |
1156 | |
1157 | |
1158 | /* |
1159 | ** WARNING WARNING |
1160 | ** |
1161 | ** Any state set in the activation before execution *must* be copied |
1162 | ** to the new activation in GenericActivationHolder.execute() when |
1163 | ** the statement has been recompiled. State such as |
1164 | ** singleExecution, cursorName, holdability, maxRows. |
1165 | */ |
1166 | |
1167 | if (cursorName != null) |
1168 | { |
1169 | a.setCursorName(cursorName); |
1170 | } |
1171 | |
1172 | boolean executeHoldable = getExecuteHoldable(); |
1173 | |
1174 | a.setResultSetHoldability(executeHoldable); |
1175 | |
1176 | //reset the activation to clear warnings |
1177 | //and clear existing result sets in case this has been cached |
1178 | a.reset(); |
1179 | a.setMaxRows(maxRows); |
1180 | long timeoutMillis = (long)timeoutSeconds * 1000L; |
1181 | ResultSet resultsToWrap = ps.execute(a, |
1182 | false, |
1183 | timeoutMillis); |
1184 | addWarning(a.getWarnings()); |
1185 | |
1186 | |
1187 | if (resultsToWrap.returnsRows()) { |
1188 | |
1189 | // The statement returns rows, so calling it with |
1190 | // executeUpdate() is not allowed. |
1191 | if (executeUpdate) { |
1192 | throw StandardException.newException( |
1193 | SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE); |
1194 | } |
1195 | |
1196 | EmbedResultSet lresults = factory.newEmbedResultSet(getEmbedConnection(), resultsToWrap, forMetaData, this, ps.isAtomic()); |
1197 | results = lresults; |
1198 | |
1199 | |
1200 | // Set up the finalization of the ResultSet to |
1201 | // mark the activation as unused. It will be |
1202 | // closed sometime later by the connection |
1203 | // outside of finalization. |
1204 | if (a.isSingleExecution()) |
1205 | lresults.finalizeActivation = a; |
1206 | |
1207 | updateCount = -1; |
1208 | retval = true; |
1209 | } |
1210 | else { |
1211 | |
1212 | // Only applipable for an insert statement, which does not return rows. |
1213 | //the auto-generated keys resultset will be null if used for non-insert statement |
1214 | if (a.getAutoGeneratedKeysResultsetMode() && (resultsToWrap.getAutoGeneratedKeysResultset() != null)) |
1215 | { |
1216 | resultsToWrap.getAutoGeneratedKeysResultset().open(); |
1217 | autoGeneratedKeysResultSet = factory.newEmbedResultSet(getEmbedConnection(), |
1218 | resultsToWrap.getAutoGeneratedKeysResultset(), false, this, ps.isAtomic()); |
1219 | } |
1220 | |
1221 | updateCount = resultsToWrap.modifiedRowCount(); |
1222 | |
1223 | resultsToWrap.finish(); // Don't need the result set any more |
1224 | results = null; // note that we have none. |
1225 | |
1226 | int dynamicResultCount = 0; |
1227 | if (a.getDynamicResults() != null) { |
1228 | dynamicResultCount = |
1229 | processDynamicResults(a.getDynamicResults(), |
1230 | a.getMaxDynamicResults()); |
1231 | } |
1232 | |
1233 | // executeQuery() is not allowed if the statement |
1234 | // doesn't return exactly one ResultSet. |
1235 | if (executeQuery && dynamicResultCount != 1) { |
1236 | throw StandardException.newException( |
1237 | SQLState.LANG_INVALID_CALL_TO_EXECUTE_QUERY); |
1238 | } |
1239 | |
1240 | // executeUpdate() is not allowed if the statement |
1241 | // returns ResultSets. |
1242 | if (executeUpdate && dynamicResultCount > 0) { |
1243 | throw StandardException.newException( |
1244 | SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE); |
1245 | } |
1246 | |
1247 | if (dynamicResultCount == 0) { |
1248 | if (a.isSingleExecution()) { |
1249 | a.close(); |
1250 | } |
1251 | |
1252 | if (!forMetaData) |
1253 | commitIfNeeded(); |
1254 | else { |
1255 | |
1256 | if (lcc.getActivationCount() > 1) { |
1257 | // we do not want to commit here as there seems to be other |
1258 | // statements/resultSets currently opened for this connection. |
1259 | } else { |
1260 | commitIfNeeded(); // we can legitimately commit |
1261 | } |
1262 | } |
1263 | } |
1264 | |
1265 | retval = (dynamicResultCount > 0); |
1266 | } |
1267 | } catch (Throwable t) { |
1268 | if (a.isSingleExecution()) { |
1269 | try { a.close(); } catch (Throwable tt) {;} |
1270 | } |
1271 | throw handleException(t); |
1272 | } finally { |
1273 | restoreContextStack(); |
1274 | } |
1275 | return retval; |
1276 | } |
1277 | } |
1278 | |
1279 | /** |
1280 | * Add a SQLWarning to this Statement object. |
1281 | * If the Statement already has a SQLWarning then it |
1282 | * is added to the end of the chain. |
1283 | * |
1284 | * @see #getWarnings() |
1285 | */ |
1286 | final void addWarning(SQLWarning sw) |
1287 | { |
1288 | if (sw != null) { |
1289 | if (warnings == null) |
1290 | warnings = sw; |
1291 | else |
1292 | warnings.setNextException(sw); |
1293 | } |
1294 | } |
1295 | |
1296 | |
1297 | /* package */ |
1298 | public String getSQLText() |
1299 | { |
1300 | // no need to synchronize - accessing a reference is atomic |
1301 | // synchronized (getConnectionSynchronization()) |
1302 | return SQLText; |
1303 | } |
1304 | |
1305 | public ParameterValueSet getParameterValueSet() |
1306 | { |
1307 | return pvs; |
1308 | } |
1309 | |
1310 | /** |
1311 | * Throw an exception if this Statement has been closed explictly |
1312 | * or it has noticed it has been closed implicitly. |
1313 | * JDBC specifications require nearly all methods throw a SQLException |
1314 | * if the Statement has been closed, thus most methods call this |
1315 | * method or checkExecStatus first. |
1316 | * |
1317 | * @exception SQLException Thrown if the statement is marked as closed. |
1318 | * |
1319 | * @see #checkExecStatus() |
1320 | */ |
1321 | final void checkStatus() throws SQLException { |
1322 | |
1323 | if (!active) |
1324 | throw newSQLException(SQLState.ALREADY_CLOSED, "Statement"); |
1325 | } |
1326 | |
1327 | /** |
1328 | A heavier weight version of checkStatus() that ensures the application's Connection |
1329 | object is still open. This is to stop errors or unexpected behaviour when a [Prepared]Statement |
1330 | object is used after the application has been closed. In particular to ensure that |
1331 | a Statement obtained from a PooledConnection cannot be used after the application has closed |
1332 | its connection (as the underlying Connection is still active). |
1333 | To avoid this heavier weight check on every method of [Prepared]Statement it is only used |
1334 | on those methods that would end up using the database's connection to read or modify data. |
1335 | E.g. execute*(), but not setXXX, etc. |
1336 | <BR> |
1337 | If this Statement's Connection is closed an exception will |
1338 | be thrown and the active field will be set to false, |
1339 | completely marking the Statement as closed. |
1340 | <BR> |
1341 | If the Statement is not currently connected to an active |
1342 | transaction, i.e. a suspended global transaction, then |
1343 | this method will throw a SQLException but the Statement |
1344 | will remain open. The Statement is open but unable to |
1345 | process any new requests until its global transaction |
1346 | is resumed. |
1347 | <BR> |
1348 | Upon return from the method, with or without a SQLException |
1349 | the field active will correctly represent the open state of |
1350 | the Statement. |
1351 | |
1352 | @exception SQLException Thrown if the statement is marked as closed |
1353 | or the Statement's transaction is suspended. |
1354 | |
1355 | @see #checkStatus() |
1356 | */ |
1357 | final void checkExecStatus() throws SQLException { |
1358 | // getConnection() checks if the Statement is closed |
1359 | if (!getConnection().isClosed()) |
1360 | return; |
1361 | |
1362 | // Now this connection is closed for all |
1363 | // future use. |
1364 | active = false; |
1365 | |
1366 | throw Util.noCurrentConnection(); |
1367 | } |
1368 | |
1369 | /** |
1370 | Close and clear all result sets associated with this statement |
1371 | from the last execution. |
1372 | */ |
1373 | void clearResultSets() throws SQLException { |
1374 | |
1375 | SQLException sqle = null; |
1376 | |
1377 | try { |
1378 | // Are there results? |
1379 | // outside of the lower try/finally since results will |
1380 | // setup and restore themselves. |
1381 | if (results != null) { |
1382 | results.close(); |
1383 | results = null; |
1384 | } |
1385 | } catch (SQLException s1) { |
1386 | sqle = s1; |
1387 | } |
1388 | |
1389 | try { |
1390 | if (autoGeneratedKeysResultSet != null) { |
1391 | autoGeneratedKeysResultSet.close(); |
1392 | autoGeneratedKeysResultSet = null; |
1393 | } |
1394 | } catch (SQLException sauto) { |
1395 | if (sqle == null) |
1396 | sqle = sauto; |
1397 | else |
1398 | sqle.setNextException(sauto); |
1399 | } |
1400 | |
1401 | // close all the dynamic result sets. |
1402 | if (dynamicResults != null) { |
1403 | for (int i = 0; i < dynamicResults.length; i++) { |
1404 | EmbedResultSet lrs = dynamicResults[i]; |
1405 | if (lrs == null) |
1406 | continue; |
1407 | |
1408 | try { |
1409 | lrs.close(); |
1410 | } catch (SQLException sdynamic) { |
1411 | if (sqle == null) |
1412 | sqle = sdynamic; |
1413 | else |
1414 | sqle.setNextException(sdynamic); |
1415 | } |
1416 | } |
1417 | dynamicResults = null; |
1418 | } |
1419 | |
1420 | /* |
1421 | We don't reset statement to null because PreparedStatement |
1422 | relies on it being there for subsequent (post-close) execution |
1423 | requests. There is no close method on database statement objects. |
1424 | */ |
1425 | |
1426 | updateCount = -1; // reset field |
1427 | |
1428 | if (sqle != null) |
1429 | throw sqle; |
1430 | } |
1431 | |
1432 | /** |
1433 | Check to see if a statement requires to be executed via a callable statement. |
1434 | */ |
1435 | void checkRequiresCallableStatement(Activation activation) throws SQLException { |
1436 | |
1437 | ParameterValueSet pvs = activation.getParameterValueSet(); |
1438 | |
1439 | if (pvs == null) |
1440 | return; |
1441 | |
1442 | if (pvs.checkNoDeclaredOutputParameters()) { |
1443 | try { |
1444 | activation.close(); |
1445 | } catch (StandardException se) { |
1446 | } |
1447 | throw newSQLException(SQLState.REQUIRES_CALLABLE_STATEMENT, SQLText); |
1448 | } |
1449 | } |
1450 | |
1451 | /** |
1452 | Transfer my batch of Statements to a newly created Statement. |
1453 | */ |
1454 | public void transferBatch(EmbedStatement other) throws SQLException { |
1455 | |
1456 | synchronized (getConnectionSynchronization()) { |
1457 | other.batchStatements = batchStatements; |
1458 | batchStatements = null; |
1459 | } |
1460 | } |
1461 | |
1462 | /** |
1463 | * Set the application statement for this Statement. |
1464 | */ |
1465 | public final void setApplicationStatement(EngineStatement s) { |
1466 | this.applicationStatement = s; |
1467 | } |
1468 | |
1469 | private EmbedResultSet[] dynamicResults; |
1470 | private int currentDynamicResultSet; |
1471 | |
1472 | /** |
1473 | * Go through a holder of dynamic result sets, remove those that |
1474 | * should not be returned, and sort the result sets according to |
1475 | * their creation. |
1476 | * |
1477 | * @param holder a holder of dynamic result sets |
1478 | * @param maxDynamicResultSets the maximum number of result sets |
1479 | * to be returned |
1480 | * @return the actual number of result sets |
1481 | * @exception SQLException if an error occurs |
1482 | */ |
1483 | private int processDynamicResults(java.sql.ResultSet[][] holder, |
1484 | int maxDynamicResultSets) |
1485 | throws SQLException |
1486 | { |
1487 | |
1488 | EmbedResultSet[] sorted = new EmbedResultSet[holder.length]; |
1489 | |
1490 | int actualCount = 0; |
1491 | for (int i = 0; i < holder.length; i++) { |
1492 | |
1493 | java.sql.ResultSet[] param = holder[i]; |
1494 | |
1495 | if (param[0] == null) |
1496 | continue; |
1497 | |
1498 | java.sql.ResultSet rs = param[0]; |
1499 | param[0] = null; |
1500 | |
1501 | // ignore non-cloudscape result sets or results sets from another connection |
1502 | if (!(rs instanceof EmbedResultSet)) |
1503 | continue; |
1504 | |
1505 | EmbedResultSet lrs = (EmbedResultSet) rs; |
1506 | |
1507 | if (lrs.getEmbedConnection().rootConnection != getEmbedConnection().rootConnection) |
1508 | continue; |
1509 | |
1510 | // ignore closed result sets. |
1511 | if (lrs.isClosed) |
1512 | continue; |
1513 | |
1514 | lrs.setDynamicResultSet(this); |
1515 | sorted[actualCount++] = lrs; |
1516 | } |
1517 | |
1518 | if (actualCount != 0) { |
1519 | |
1520 | // results are defined to be ordered according to their creation |
1521 | if (actualCount != 1) { |
1522 | java.util.Arrays.sort(sorted, 0, actualCount); |
1523 | } |
1524 | |
1525 | dynamicResults = sorted; |
1526 | |
1527 | if (actualCount > maxDynamicResultSets) { |
1528 | addWarning(StandardException.newWarning(SQLState.LANG_TOO_MANY_DYNAMIC_RESULTS_RETURNED)); |
1529 | |
1530 | for (int i = maxDynamicResultSets; i < actualCount; i++) { |
1531 | sorted[i].close(); |
1532 | sorted[i] = null; |
1533 | } |
1534 | |
1535 | actualCount = maxDynamicResultSets; |
1536 | } |
1537 | |
1538 | |
1539 | updateCount = -1; |
1540 | results = sorted[0]; |
1541 | currentDynamicResultSet = 0; |
1542 | |
1543 | // 0100C is not returned for procedures written in Java, from the SQL2003 spec. |
1544 | // getWarnings(StandardException.newWarning(SQLState.LANG_DYNAMIC_RESULTS_RETURNED)); |
1545 | } |
1546 | |
1547 | |
1548 | return actualCount; |
1549 | } |
1550 | |
1551 | /** |
1552 | Callback on the statement when one of its result sets is closed. |
1553 | This allows the statement to control when it completes and hence |
1554 | when it commits in auto commit mode. |
1555 | |
1556 | Must have connection synchronization and setupContextStack(), this |
1557 | is required for the call to commitIfNeeded(). |
1558 | */ |
1559 | void resultSetClosing(EmbedResultSet closingLRS) throws SQLException { |
1560 | |
1561 | // If the Connection is not in auto commit then this statement completion |
1562 | // cannot cause a commit. |
1563 | if (!getEmbedConnection().autoCommit) |
1564 | return; |
1565 | |
1566 | // If we have dynamic results, see if there is another result set open. |
1567 | // If so, then no commit. The last result set to close will close the statement. |
1568 | if (dynamicResults != null) { |
1569 | for (int i = 0; i < dynamicResults.length; i++) { |
1570 | EmbedResultSet lrs = dynamicResults[i]; |
1571 | if (lrs == null) |
1572 | continue; |
1573 | if (lrs.isClosed) |
1574 | continue; |
1575 | if (lrs == closingLRS) |
1576 | continue; |
1577 | |
1578 | // at least one still open so no commit now. |
1579 | return; |
1580 | } |
1581 | } |
1582 | |
1583 | // new Throwable("COMMIT ON " + SQLText).printStackTrace(System.out); |
1584 | |
1585 | // beetle 5383. Force a commit in autocommit always. Before this |
1586 | // change if client in autocommit opened a result set, did a commit, |
1587 | // then next then close a commit would not be forced on the close. |
1588 | commitIfAutoCommit(); |
1589 | } |
1590 | |
1591 | /** |
1592 | * Get the execute time holdability for the Statement. |
1593 | * When in a global transaction holdabilty defaults to false. |
1594 | * @throws SQLException Error from getResultSetHoldability. |
1595 | */ |
1596 | private boolean getExecuteHoldable() throws SQLException |
1597 | { |
1598 | if (resultSetHoldability == JDBC30Translation.CLOSE_CURSORS_AT_COMMIT) |
1599 | return false; |
1600 | |
1601 | // Simple non-XA case |
1602 | if (applicationStatement == this) |
1603 | return true; |
1604 | |
1605 | return applicationStatement.getResultSetHoldability() == |
1606 | JDBC30Translation.HOLD_CURSORS_OVER_COMMIT; |
1607 | } |
1608 | |
1609 | /** |
1610 | * Returns the value of the EmbedStatement's poolable hint, |
1611 | * indicating whether pooling is requested. |
1612 | * |
1613 | * @return The value of the poolable hint. |
1614 | * @throws SQLException if the Statement has been closed. |
1615 | */ |
1616 | |
1617 | public boolean isPoolable() throws SQLException { |
1618 | // Assert the statement is still active (not closed) |
1619 | checkStatus(); |
1620 | |
1621 | return isPoolable; |
1622 | } |
1623 | |
1624 | /** |
1625 | * Requests that an EmbedStatement be pooled or not. |
1626 | * |
1627 | * @param poolable requests that the EmbedStatement be pooled if true |
1628 | * and not be pooled if false. |
1629 | * @throws SQLException if the EmbedStatement has been closed. |
1630 | */ |
1631 | |
1632 | public void setPoolable(boolean poolable) throws SQLException { |
1633 | // Assert the statement is still active (not closed) |
1634 | checkStatus(); |
1635 | |
1636 | isPoolable = poolable; |
1637 | } |
1638 | } |
1639 | |