1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.jdbc; |
7 | |
8 | import java.sql.Connection; |
9 | import java.sql.ResultSet; |
10 | import java.sql.SQLException; |
11 | import java.sql.SQLWarning; |
12 | import java.sql.Statement; |
13 | import java.util.ArrayList; |
14 | import org.h2.api.ErrorCode; |
15 | import org.h2.command.CommandInterface; |
16 | import org.h2.engine.SessionInterface; |
17 | import org.h2.engine.SysProperties; |
18 | import org.h2.message.DbException; |
19 | import org.h2.message.TraceObject; |
20 | import org.h2.result.ResultInterface; |
21 | import org.h2.util.New; |
22 | |
23 | /** |
24 | * Represents a statement. |
25 | */ |
26 | public class JdbcStatement extends TraceObject implements Statement { |
27 | |
28 | protected JdbcConnection conn; |
29 | protected SessionInterface session; |
30 | protected JdbcResultSet resultSet; |
31 | protected int maxRows; |
32 | protected int fetchSize = SysProperties.SERVER_RESULT_SET_FETCH_SIZE; |
33 | protected int updateCount; |
34 | protected final int resultSetType; |
35 | protected final int resultSetConcurrency; |
36 | protected final boolean closedByResultSet; |
37 | private CommandInterface executingCommand; |
38 | private int lastExecutedCommandType; |
39 | private ArrayList<String> batchCommands; |
40 | private boolean escapeProcessing = true; |
41 | private boolean cancelled; |
42 | |
43 | JdbcStatement(JdbcConnection conn, int id, int resultSetType, |
44 | int resultSetConcurrency, boolean closeWithResultSet) { |
45 | this.conn = conn; |
46 | this.session = conn.getSession(); |
47 | setTrace(session.getTrace(), TraceObject.STATEMENT, id); |
48 | this.resultSetType = resultSetType; |
49 | this.resultSetConcurrency = resultSetConcurrency; |
50 | this.closedByResultSet = closeWithResultSet; |
51 | } |
52 | |
53 | /** |
54 | * Executes a query (select statement) and returns the result set. |
55 | * If another result set exists for this statement, this will be closed |
56 | * (even if this statement fails). |
57 | * |
58 | * @param sql the SQL statement to execute |
59 | * @return the result set |
60 | */ |
61 | @Override |
62 | public ResultSet executeQuery(String sql) throws SQLException { |
63 | try { |
64 | int id = getNextId(TraceObject.RESULT_SET); |
65 | if (isDebugEnabled()) { |
66 | debugCodeAssign("ResultSet", TraceObject.RESULT_SET, id, |
67 | "executeQuery(" + quote(sql) + ")"); |
68 | } |
69 | synchronized (session) { |
70 | checkClosed(); |
71 | closeOldResultSet(); |
72 | sql = JdbcConnection.translateSQL(sql, escapeProcessing); |
73 | CommandInterface command = conn.prepareCommand(sql, fetchSize); |
74 | ResultInterface result; |
75 | boolean scrollable = resultSetType != ResultSet.TYPE_FORWARD_ONLY; |
76 | boolean updatable = resultSetConcurrency == ResultSet.CONCUR_UPDATABLE; |
77 | setExecutingStatement(command); |
78 | try { |
79 | result = command.executeQuery(maxRows, scrollable); |
80 | } finally { |
81 | setExecutingStatement(null); |
82 | } |
83 | command.close(); |
84 | resultSet = new JdbcResultSet(conn, this, result, id, |
85 | closedByResultSet, scrollable, updatable); |
86 | } |
87 | return resultSet; |
88 | } catch (Exception e) { |
89 | throw logAndConvert(e); |
90 | } |
91 | } |
92 | |
93 | /** |
94 | * Executes a statement (insert, update, delete, create, drop) |
95 | * and returns the update count. |
96 | * If another result set exists for this statement, this will be closed |
97 | * (even if this statement fails). |
98 | * |
99 | * If auto commit is on, this statement will be committed. |
100 | * If the statement is a DDL statement (create, drop, alter) and does not |
101 | * throw an exception, the current transaction (if any) is committed after |
102 | * executing the statement. |
103 | * |
104 | * @param sql the SQL statement |
105 | * @return the update count (number of row affected by an insert, |
106 | * update or delete, or 0 if no rows or the statement was a |
107 | * create, drop, commit or rollback) |
108 | * @throws SQLException if a database error occurred or a |
109 | * select statement was executed |
110 | */ |
111 | @Override |
112 | public int executeUpdate(String sql) throws SQLException { |
113 | try { |
114 | debugCodeCall("executeUpdate", sql); |
115 | return executeUpdateInternal(sql); |
116 | } catch (Exception e) { |
117 | throw logAndConvert(e); |
118 | } |
119 | } |
120 | |
121 | private int executeUpdateInternal(String sql) throws SQLException { |
122 | checkClosedForWrite(); |
123 | try { |
124 | closeOldResultSet(); |
125 | sql = JdbcConnection.translateSQL(sql, escapeProcessing); |
126 | CommandInterface command = conn.prepareCommand(sql, fetchSize); |
127 | synchronized (session) { |
128 | setExecutingStatement(command); |
129 | try { |
130 | updateCount = command.executeUpdate(); |
131 | } finally { |
132 | setExecutingStatement(null); |
133 | } |
134 | } |
135 | command.close(); |
136 | return updateCount; |
137 | } finally { |
138 | afterWriting(); |
139 | } |
140 | } |
141 | |
142 | /** |
143 | * Executes an arbitrary statement. If another result set exists for this |
144 | * statement, this will be closed (even if this statement fails). |
145 | * |
146 | * If the statement is a create or drop and does not throw an exception, the |
147 | * current transaction (if any) is committed after executing the statement. |
148 | * If auto commit is on, and the statement is not a select, this statement |
149 | * will be committed. |
150 | * |
151 | * @param sql the SQL statement to execute |
152 | * @return true if a result set is available, false if not |
153 | */ |
154 | @Override |
155 | public boolean execute(String sql) throws SQLException { |
156 | try { |
157 | debugCodeCall("execute", sql); |
158 | return executeInternal(sql); |
159 | } catch (Exception e) { |
160 | throw logAndConvert(e); |
161 | } |
162 | } |
163 | |
164 | private boolean executeInternal(String sql) throws SQLException { |
165 | int id = getNextId(TraceObject.RESULT_SET); |
166 | checkClosedForWrite(); |
167 | try { |
168 | closeOldResultSet(); |
169 | sql = JdbcConnection.translateSQL(sql, escapeProcessing); |
170 | CommandInterface command = conn.prepareCommand(sql, fetchSize); |
171 | boolean returnsResultSet; |
172 | synchronized (session) { |
173 | setExecutingStatement(command); |
174 | try { |
175 | if (command.isQuery()) { |
176 | returnsResultSet = true; |
177 | boolean scrollable = resultSetType != ResultSet.TYPE_FORWARD_ONLY; |
178 | boolean updatable = resultSetConcurrency == ResultSet.CONCUR_UPDATABLE; |
179 | ResultInterface result = command.executeQuery(maxRows, scrollable); |
180 | resultSet = new JdbcResultSet(conn, this, result, id, |
181 | closedByResultSet, scrollable, updatable); |
182 | } else { |
183 | returnsResultSet = false; |
184 | updateCount = command.executeUpdate(); |
185 | } |
186 | } finally { |
187 | setExecutingStatement(null); |
188 | } |
189 | } |
190 | command.close(); |
191 | return returnsResultSet; |
192 | } finally { |
193 | afterWriting(); |
194 | } |
195 | } |
196 | |
197 | /** |
198 | * Returns the last result set produces by this statement. |
199 | * |
200 | * @return the result set |
201 | */ |
202 | @Override |
203 | public ResultSet getResultSet() throws SQLException { |
204 | try { |
205 | checkClosed(); |
206 | if (resultSet != null) { |
207 | int id = resultSet.getTraceId(); |
208 | debugCodeAssign("ResultSet", TraceObject.RESULT_SET, id, "getResultSet()"); |
209 | } else { |
210 | debugCodeCall("getResultSet"); |
211 | } |
212 | return resultSet; |
213 | } catch (Exception e) { |
214 | throw logAndConvert(e); |
215 | } |
216 | } |
217 | |
218 | /** |
219 | * Returns the last update count of this statement. |
220 | * |
221 | * @return the update count (number of row affected by an insert, update or |
222 | * delete, or 0 if no rows or the statement was a create, drop, |
223 | * commit or rollback; -1 if the statement was a select). |
224 | * @throws SQLException if this object is closed or invalid |
225 | */ |
226 | @Override |
227 | public int getUpdateCount() throws SQLException { |
228 | try { |
229 | debugCodeCall("getUpdateCount"); |
230 | checkClosed(); |
231 | return updateCount; |
232 | } catch (Exception e) { |
233 | throw logAndConvert(e); |
234 | } |
235 | } |
236 | |
237 | /** |
238 | * Closes this statement. |
239 | * All result sets that where created by this statement |
240 | * become invalid after calling this method. |
241 | */ |
242 | @Override |
243 | public void close() throws SQLException { |
244 | try { |
245 | debugCodeCall("close"); |
246 | synchronized (session) { |
247 | closeOldResultSet(); |
248 | if (conn != null) { |
249 | conn = null; |
250 | } |
251 | } |
252 | } catch (Exception e) { |
253 | throw logAndConvert(e); |
254 | } |
255 | } |
256 | |
257 | /** |
258 | * Returns the connection that created this object. |
259 | * |
260 | * @return the connection |
261 | */ |
262 | @Override |
263 | public Connection getConnection() { |
264 | debugCodeCall("getConnection"); |
265 | return conn; |
266 | } |
267 | |
268 | /** |
269 | * Gets the first warning reported by calls on this object. |
270 | * This driver does not support warnings, and will always return null. |
271 | * |
272 | * @return null |
273 | */ |
274 | @Override |
275 | public SQLWarning getWarnings() throws SQLException { |
276 | try { |
277 | debugCodeCall("getWarnings"); |
278 | checkClosed(); |
279 | return null; |
280 | } catch (Exception e) { |
281 | throw logAndConvert(e); |
282 | } |
283 | } |
284 | |
285 | /** |
286 | * Clears all warnings. As this driver does not support warnings, |
287 | * this call is ignored. |
288 | */ |
289 | @Override |
290 | public void clearWarnings() throws SQLException { |
291 | try { |
292 | debugCodeCall("clearWarnings"); |
293 | checkClosed(); |
294 | } catch (Exception e) { |
295 | throw logAndConvert(e); |
296 | } |
297 | } |
298 | |
299 | /** |
300 | * Sets the name of the cursor. This call is ignored. |
301 | * |
302 | * @param name ignored |
303 | * @throws SQLException if this object is closed |
304 | */ |
305 | @Override |
306 | public void setCursorName(String name) throws SQLException { |
307 | try { |
308 | debugCodeCall("setCursorName", name); |
309 | checkClosed(); |
310 | } catch (Exception e) { |
311 | throw logAndConvert(e); |
312 | } |
313 | } |
314 | |
315 | /** |
316 | * Sets the fetch direction. |
317 | * This call is ignored by this driver. |
318 | * |
319 | * @param direction ignored |
320 | * @throws SQLException if this object is closed |
321 | */ |
322 | @Override |
323 | public void setFetchDirection(int direction) throws SQLException { |
324 | try { |
325 | debugCodeCall("setFetchDirection", direction); |
326 | checkClosed(); |
327 | } catch (Exception e) { |
328 | throw logAndConvert(e); |
329 | } |
330 | } |
331 | |
332 | /** |
333 | * Gets the fetch direction. |
334 | * |
335 | * @return FETCH_FORWARD |
336 | * @throws SQLException if this object is closed |
337 | */ |
338 | @Override |
339 | public int getFetchDirection() throws SQLException { |
340 | try { |
341 | debugCodeCall("getFetchDirection"); |
342 | checkClosed(); |
343 | return ResultSet.FETCH_FORWARD; |
344 | } catch (Exception e) { |
345 | throw logAndConvert(e); |
346 | } |
347 | } |
348 | |
349 | /** |
350 | * Gets the maximum number of rows for a ResultSet. |
351 | * |
352 | * @return the number of rows where 0 means no limit |
353 | * @throws SQLException if this object is closed |
354 | */ |
355 | @Override |
356 | public int getMaxRows() throws SQLException { |
357 | try { |
358 | debugCodeCall("getMaxRows"); |
359 | checkClosed(); |
360 | return maxRows; |
361 | } catch (Exception e) { |
362 | throw logAndConvert(e); |
363 | } |
364 | } |
365 | |
366 | /** |
367 | * Gets the maximum number of rows for a ResultSet. |
368 | * |
369 | * @param maxRows the number of rows where 0 means no limit |
370 | * @throws SQLException if this object is closed |
371 | */ |
372 | @Override |
373 | public void setMaxRows(int maxRows) throws SQLException { |
374 | try { |
375 | debugCodeCall("setMaxRows", maxRows); |
376 | checkClosed(); |
377 | if (maxRows < 0) { |
378 | throw DbException.getInvalidValueException("maxRows", maxRows); |
379 | } |
380 | this.maxRows = maxRows; |
381 | } catch (Exception e) { |
382 | throw logAndConvert(e); |
383 | } |
384 | } |
385 | |
386 | /** |
387 | * Sets the number of rows suggested to read in one step. |
388 | * This value cannot be higher than the maximum rows (setMaxRows) |
389 | * set by the statement or prepared statement, otherwise an exception |
390 | * is throws. Setting the value to 0 will set the default value. |
391 | * The default value can be changed using the system property |
392 | * h2.serverResultSetFetchSize. |
393 | * |
394 | * @param rows the number of rows |
395 | * @throws SQLException if this object is closed |
396 | */ |
397 | @Override |
398 | public void setFetchSize(int rows) throws SQLException { |
399 | try { |
400 | debugCodeCall("setFetchSize", rows); |
401 | checkClosed(); |
402 | if (rows < 0 || (rows > 0 && maxRows > 0 && rows > maxRows)) { |
403 | throw DbException.getInvalidValueException("rows", rows); |
404 | } |
405 | if (rows == 0) { |
406 | rows = SysProperties.SERVER_RESULT_SET_FETCH_SIZE; |
407 | } |
408 | fetchSize = rows; |
409 | } catch (Exception e) { |
410 | throw logAndConvert(e); |
411 | } |
412 | } |
413 | |
414 | /** |
415 | * Gets the number of rows suggested to read in one step. |
416 | * |
417 | * @return the current fetch size |
418 | * @throws SQLException if this object is closed |
419 | */ |
420 | @Override |
421 | public int getFetchSize() throws SQLException { |
422 | try { |
423 | debugCodeCall("getFetchSize"); |
424 | checkClosed(); |
425 | return fetchSize; |
426 | } catch (Exception e) { |
427 | throw logAndConvert(e); |
428 | } |
429 | } |
430 | |
431 | /** |
432 | * Gets the result set concurrency created by this object. |
433 | * |
434 | * @return the concurrency |
435 | */ |
436 | @Override |
437 | public int getResultSetConcurrency() throws SQLException { |
438 | try { |
439 | debugCodeCall("getResultSetConcurrency"); |
440 | checkClosed(); |
441 | return resultSetConcurrency; |
442 | } catch (Exception e) { |
443 | throw logAndConvert(e); |
444 | } |
445 | } |
446 | |
447 | /** |
448 | * Gets the result set type. |
449 | * |
450 | * @return the type |
451 | * @throws SQLException if this object is closed |
452 | */ |
453 | @Override |
454 | public int getResultSetType() throws SQLException { |
455 | try { |
456 | debugCodeCall("getResultSetType"); |
457 | checkClosed(); |
458 | return resultSetType; |
459 | } catch (Exception e) { |
460 | throw logAndConvert(e); |
461 | } |
462 | } |
463 | |
464 | /** |
465 | * Gets the maximum number of bytes for a result set column. |
466 | * |
467 | * @return always 0 for no limit |
468 | * @throws SQLException if this object is closed |
469 | */ |
470 | @Override |
471 | public int getMaxFieldSize() throws SQLException { |
472 | try { |
473 | debugCodeCall("getMaxFieldSize"); |
474 | checkClosed(); |
475 | return 0; |
476 | } catch (Exception e) { |
477 | throw logAndConvert(e); |
478 | } |
479 | } |
480 | |
481 | /** |
482 | * Sets the maximum number of bytes for a result set column. |
483 | * This method does currently do nothing for this driver. |
484 | * |
485 | * @param max the maximum size - ignored |
486 | * @throws SQLException if this object is closed |
487 | */ |
488 | @Override |
489 | public void setMaxFieldSize(int max) throws SQLException { |
490 | try { |
491 | debugCodeCall("setMaxFieldSize", max); |
492 | checkClosed(); |
493 | } catch (Exception e) { |
494 | throw logAndConvert(e); |
495 | } |
496 | } |
497 | |
498 | /** |
499 | * Enables or disables processing or JDBC escape syntax. |
500 | * See also Connection.nativeSQL. |
501 | * |
502 | * @param enable - true (default) or false (no conversion is attempted) |
503 | * @throws SQLException if this object is closed |
504 | */ |
505 | @Override |
506 | public void setEscapeProcessing(boolean enable) throws SQLException { |
507 | try { |
508 | if (isDebugEnabled()) { |
509 | debugCode("setEscapeProcessing("+enable+");"); |
510 | } |
511 | checkClosed(); |
512 | escapeProcessing = enable; |
513 | } catch (Exception e) { |
514 | throw logAndConvert(e); |
515 | } |
516 | } |
517 | |
518 | /** |
519 | * Cancels a currently running statement. |
520 | * This method must be called from within another |
521 | * thread than the execute method. |
522 | * Operations on large objects are not interrupted, |
523 | * only operations that process many rows. |
524 | * |
525 | * @throws SQLException if this object is closed |
526 | */ |
527 | @Override |
528 | public void cancel() throws SQLException { |
529 | try { |
530 | debugCodeCall("cancel"); |
531 | checkClosed(); |
532 | // executingCommand can be reset by another thread |
533 | CommandInterface c = executingCommand; |
534 | try { |
535 | if (c != null) { |
536 | c.cancel(); |
537 | cancelled = true; |
538 | } |
539 | } finally { |
540 | setExecutingStatement(null); |
541 | } |
542 | } catch (Exception e) { |
543 | throw logAndConvert(e); |
544 | } |
545 | } |
546 | |
547 | /** |
548 | * Check whether the statement was cancelled. |
549 | * |
550 | * @return true if yes |
551 | */ |
552 | public boolean wasCancelled() { |
553 | return cancelled; |
554 | } |
555 | |
556 | /** |
557 | * Gets the current query timeout in seconds. |
558 | * This method will return 0 if no query timeout is set. |
559 | * The result is rounded to the next second. |
560 | * For performance reasons, only the first call to this method |
561 | * will query the database. If the query timeout was changed in another |
562 | * way than calling setQueryTimeout, this method will always return |
563 | * the last value. |
564 | * |
565 | * @return the timeout in seconds |
566 | * @throws SQLException if this object is closed |
567 | */ |
568 | @Override |
569 | public int getQueryTimeout() throws SQLException { |
570 | try { |
571 | debugCodeCall("getQueryTimeout"); |
572 | checkClosed(); |
573 | return conn.getQueryTimeout(); |
574 | } catch (Exception e) { |
575 | throw logAndConvert(e); |
576 | } |
577 | } |
578 | |
579 | /** |
580 | * Sets the current query timeout in seconds. |
581 | * Changing the value will affect all statements of this connection. |
582 | * This method does not commit a transaction, |
583 | * and rolling back a transaction does not affect this setting. |
584 | * |
585 | * @param seconds the timeout in seconds - 0 means no timeout, values |
586 | * smaller 0 will throw an exception |
587 | * @throws SQLException if this object is closed |
588 | */ |
589 | @Override |
590 | public void setQueryTimeout(int seconds) throws SQLException { |
591 | try { |
592 | debugCodeCall("setQueryTimeout", seconds); |
593 | checkClosed(); |
594 | if (seconds < 0) { |
595 | throw DbException.getInvalidValueException("seconds", seconds); |
596 | } |
597 | conn.setQueryTimeout(seconds); |
598 | } catch (Exception e) { |
599 | throw logAndConvert(e); |
600 | } |
601 | } |
602 | |
603 | /** |
604 | * Adds a statement to the batch. |
605 | * |
606 | * @param sql the SQL statement |
607 | */ |
608 | @Override |
609 | public void addBatch(String sql) throws SQLException { |
610 | try { |
611 | debugCodeCall("addBatch", sql); |
612 | checkClosed(); |
613 | sql = JdbcConnection.translateSQL(sql, escapeProcessing); |
614 | if (batchCommands == null) { |
615 | batchCommands = New.arrayList(); |
616 | } |
617 | batchCommands.add(sql); |
618 | } catch (Exception e) { |
619 | throw logAndConvert(e); |
620 | } |
621 | } |
622 | |
623 | /** |
624 | * Clears the batch. |
625 | */ |
626 | @Override |
627 | public void clearBatch() throws SQLException { |
628 | try { |
629 | debugCodeCall("clearBatch"); |
630 | checkClosed(); |
631 | batchCommands = null; |
632 | } catch (Exception e) { |
633 | throw logAndConvert(e); |
634 | } |
635 | } |
636 | |
637 | /** |
638 | * Executes the batch. |
639 | * If one of the batched statements fails, this database will continue. |
640 | * |
641 | * @return the array of update counts |
642 | */ |
643 | @Override |
644 | public int[] executeBatch() throws SQLException { |
645 | try { |
646 | debugCodeCall("executeBatch"); |
647 | checkClosedForWrite(); |
648 | try { |
649 | if (batchCommands == null) { |
650 | // TODO batch: check what other database do if no commands |
651 | // are set |
652 | batchCommands = New.arrayList(); |
653 | } |
654 | int size = batchCommands.size(); |
655 | int[] result = new int[size]; |
656 | boolean error = false; |
657 | SQLException next = null; |
658 | for (int i = 0; i < size; i++) { |
659 | String sql = batchCommands.get(i); |
660 | try { |
661 | result[i] = executeUpdateInternal(sql); |
662 | } catch (Exception re) { |
663 | SQLException e = logAndConvert(re); |
664 | if (next == null) { |
665 | next = e; |
666 | } else { |
667 | e.setNextException(next); |
668 | next = e; |
669 | } |
670 | result[i] = Statement.EXECUTE_FAILED; |
671 | error = true; |
672 | } |
673 | } |
674 | batchCommands = null; |
675 | if (error) { |
676 | throw new JdbcBatchUpdateException(next, result); |
677 | } |
678 | return result; |
679 | } finally { |
680 | afterWriting(); |
681 | } |
682 | } catch (Exception e) { |
683 | throw logAndConvert(e); |
684 | } |
685 | } |
686 | |
687 | /** |
688 | * Return a result set that contains the last generated auto-increment key |
689 | * for this connection, if there was one. If no key was generated by the |
690 | * last modification statement, then an empty result set is returned. |
691 | * The returned result set only contains the data for the very last row. |
692 | * |
693 | * @return the result set with one row and one column containing the key |
694 | * @throws SQLException if this object is closed |
695 | */ |
696 | @Override |
697 | public ResultSet getGeneratedKeys() throws SQLException { |
698 | try { |
699 | int id = getNextId(TraceObject.RESULT_SET); |
700 | if (isDebugEnabled()) { |
701 | debugCodeAssign("ResultSet", TraceObject.RESULT_SET, id, "getGeneratedKeys()"); |
702 | } |
703 | checkClosed(); |
704 | return conn.getGeneratedKeys(this, id); |
705 | } catch (Exception e) { |
706 | throw logAndConvert(e); |
707 | } |
708 | } |
709 | |
710 | /** |
711 | * Moves to the next result set - however there is always only one result |
712 | * set. This call also closes the current result set (if there is one). |
713 | * Returns true if there is a next result set (that means - it always |
714 | * returns false). |
715 | * |
716 | * @return false |
717 | * @throws SQLException if this object is closed. |
718 | */ |
719 | @Override |
720 | public boolean getMoreResults() throws SQLException { |
721 | try { |
722 | debugCodeCall("getMoreResults"); |
723 | checkClosed(); |
724 | closeOldResultSet(); |
725 | return false; |
726 | } catch (Exception e) { |
727 | throw logAndConvert(e); |
728 | } |
729 | } |
730 | |
731 | /** |
732 | * Move to the next result set. |
733 | * This method always returns false. |
734 | * |
735 | * @param current Statement.CLOSE_CURRENT_RESULT, |
736 | * Statement.KEEP_CURRENT_RESULT, |
737 | * or Statement.CLOSE_ALL_RESULTS |
738 | * @return false |
739 | */ |
740 | @Override |
741 | public boolean getMoreResults(int current) throws SQLException { |
742 | try { |
743 | debugCodeCall("getMoreResults", current); |
744 | switch (current) { |
745 | case Statement.CLOSE_CURRENT_RESULT: |
746 | case Statement.CLOSE_ALL_RESULTS: |
747 | checkClosed(); |
748 | closeOldResultSet(); |
749 | break; |
750 | case Statement.KEEP_CURRENT_RESULT: |
751 | // nothing to do |
752 | break; |
753 | default: |
754 | throw DbException.getInvalidValueException("current", current); |
755 | } |
756 | return false; |
757 | } catch (Exception e) { |
758 | throw logAndConvert(e); |
759 | } |
760 | } |
761 | |
762 | /** |
763 | * Executes a statement and returns the update count. |
764 | * This method just calls executeUpdate(String sql) internally. |
765 | * The method getGeneratedKeys supports at most one columns and row. |
766 | * |
767 | * @param sql the SQL statement |
768 | * @param autoGeneratedKeys ignored |
769 | * @return the update count (number of row affected by an insert, |
770 | * update or delete, or 0 if no rows or the statement was a |
771 | * create, drop, commit or rollback) |
772 | * @throws SQLException if a database error occurred or a |
773 | * select statement was executed |
774 | */ |
775 | @Override |
776 | public int executeUpdate(String sql, int autoGeneratedKeys) |
777 | throws SQLException { |
778 | try { |
779 | if (isDebugEnabled()) { |
780 | debugCode("executeUpdate("+quote(sql)+", "+autoGeneratedKeys+");"); |
781 | } |
782 | return executeUpdateInternal(sql); |
783 | } catch (Exception e) { |
784 | throw logAndConvert(e); |
785 | } |
786 | } |
787 | |
788 | /** |
789 | * Executes a statement and returns the update count. |
790 | * This method just calls executeUpdate(String sql) internally. |
791 | * The method getGeneratedKeys supports at most one columns and row. |
792 | * |
793 | * @param sql the SQL statement |
794 | * @param columnIndexes ignored |
795 | * @return the update count (number of row affected by an insert, |
796 | * update or delete, or 0 if no rows or the statement was a |
797 | * create, drop, commit or rollback) |
798 | * @throws SQLException if a database error occurred or a |
799 | * select statement was executed |
800 | */ |
801 | @Override |
802 | public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { |
803 | try { |
804 | if (isDebugEnabled()) { |
805 | debugCode("executeUpdate("+quote(sql)+", "+quoteIntArray(columnIndexes)+");"); |
806 | } |
807 | return executeUpdateInternal(sql); |
808 | } catch (Exception e) { |
809 | throw logAndConvert(e); |
810 | } |
811 | } |
812 | |
813 | /** |
814 | * Executes a statement and returns the update count. |
815 | * This method just calls executeUpdate(String sql) internally. |
816 | * The method getGeneratedKeys supports at most one columns and row. |
817 | * |
818 | * @param sql the SQL statement |
819 | * @param columnNames ignored |
820 | * @return the update count (number of row affected by an insert, |
821 | * update or delete, or 0 if no rows or the statement was a |
822 | * create, drop, commit or rollback) |
823 | * @throws SQLException if a database error occurred or a |
824 | * select statement was executed |
825 | */ |
826 | @Override |
827 | public int executeUpdate(String sql, String[] columnNames) throws SQLException { |
828 | try { |
829 | if (isDebugEnabled()) { |
830 | debugCode("executeUpdate("+quote(sql)+", "+quoteArray(columnNames)+");"); |
831 | } |
832 | return executeUpdateInternal(sql); |
833 | } catch (Exception e) { |
834 | throw logAndConvert(e); |
835 | } |
836 | } |
837 | |
838 | /** |
839 | * Executes a statement and returns the update count. |
840 | * This method just calls execute(String sql) internally. |
841 | * The method getGeneratedKeys supports at most one columns and row. |
842 | * |
843 | * @param sql the SQL statement |
844 | * @param autoGeneratedKeys ignored |
845 | * @return the update count (number of row affected by an insert, |
846 | * update or delete, or 0 if no rows or the statement was a |
847 | * create, drop, commit or rollback) |
848 | * @throws SQLException if a database error occurred or a |
849 | * select statement was executed |
850 | */ |
851 | @Override |
852 | public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { |
853 | try { |
854 | if (isDebugEnabled()) { |
855 | debugCode("execute("+quote(sql)+", "+autoGeneratedKeys+");"); |
856 | } |
857 | return executeInternal(sql); |
858 | } catch (Exception e) { |
859 | throw logAndConvert(e); |
860 | } |
861 | } |
862 | |
863 | /** |
864 | * Executes a statement and returns the update count. |
865 | * This method just calls execute(String sql) internally. |
866 | * The method getGeneratedKeys supports at most one columns and row. |
867 | * |
868 | * @param sql the SQL statement |
869 | * @param columnIndexes ignored |
870 | * @return the update count (number of row affected by an insert, |
871 | * update or delete, or 0 if no rows or the statement was a |
872 | * create, drop, commit or rollback) |
873 | * @throws SQLException if a database error occurred or a |
874 | * select statement was executed |
875 | */ |
876 | @Override |
877 | public boolean execute(String sql, int[] columnIndexes) throws SQLException { |
878 | try { |
879 | if (isDebugEnabled()) { |
880 | debugCode("execute("+quote(sql)+", "+quoteIntArray(columnIndexes)+");"); |
881 | } |
882 | return executeInternal(sql); |
883 | } catch (Exception e) { |
884 | throw logAndConvert(e); |
885 | } |
886 | } |
887 | |
888 | /** |
889 | * Executes a statement and returns the update count. |
890 | * This method just calls execute(String sql) internally. |
891 | * The method getGeneratedKeys supports at most one columns and row. |
892 | * |
893 | * @param sql the SQL statement |
894 | * @param columnNames ignored |
895 | * @return the update count (number of row affected by an insert, |
896 | * update or delete, or 0 if no rows or the statement was a |
897 | * create, drop, commit or rollback) |
898 | * @throws SQLException if a database error occurred or a |
899 | * select statement was executed |
900 | */ |
901 | @Override |
902 | public boolean execute(String sql, String[] columnNames) throws SQLException { |
903 | try { |
904 | if (isDebugEnabled()) { |
905 | debugCode("execute("+quote(sql)+", "+quoteArray(columnNames)+");"); |
906 | } |
907 | return executeInternal(sql); |
908 | } catch (Exception e) { |
909 | throw logAndConvert(e); |
910 | } |
911 | } |
912 | |
913 | /** |
914 | * Gets the result set holdability. |
915 | * |
916 | * @return the holdability |
917 | */ |
918 | @Override |
919 | public int getResultSetHoldability() throws SQLException { |
920 | try { |
921 | debugCodeCall("getResultSetHoldability"); |
922 | checkClosed(); |
923 | return ResultSet.HOLD_CURSORS_OVER_COMMIT; |
924 | } catch (Exception e) { |
925 | throw logAndConvert(e); |
926 | } |
927 | } |
928 | |
929 | /** |
930 | * [Not supported] |
931 | */ |
932 | //## Java 1.7 ## |
933 | @Override |
934 | public void closeOnCompletion() { |
935 | // not supported |
936 | } |
937 | //*/ |
938 | |
939 | /** |
940 | * [Not supported] |
941 | */ |
942 | //## Java 1.7 ## |
943 | @Override |
944 | public boolean isCloseOnCompletion() { |
945 | return true; |
946 | } |
947 | //*/ |
948 | |
949 | // ============================================================= |
950 | |
951 | /** |
952 | * Check if this connection is closed. |
953 | * The next operation is a read request. |
954 | * |
955 | * @return true if the session was re-connected |
956 | * @throws DbException if the connection or session is closed |
957 | */ |
958 | boolean checkClosed() { |
959 | return checkClosed(false); |
960 | } |
961 | |
962 | /** |
963 | * Check if this connection is closed. |
964 | * The next operation may be a write request. |
965 | * |
966 | * @return true if the session was re-connected |
967 | * @throws DbException if the connection or session is closed |
968 | */ |
969 | boolean checkClosedForWrite() { |
970 | return checkClosed(true); |
971 | } |
972 | |
973 | /** |
974 | * INTERNAL. |
975 | * Check if the statement is closed. |
976 | * |
977 | * @param write if the next operation is possibly writing |
978 | * @return true if a reconnect was required |
979 | * @throws DbException if it is closed |
980 | */ |
981 | protected boolean checkClosed(boolean write) { |
982 | if (conn == null) { |
983 | throw DbException.get(ErrorCode.OBJECT_CLOSED); |
984 | } |
985 | conn.checkClosed(write); |
986 | SessionInterface s = conn.getSession(); |
987 | if (s != session) { |
988 | session = s; |
989 | trace = session.getTrace(); |
990 | return true; |
991 | } |
992 | return false; |
993 | } |
994 | |
995 | /** |
996 | * Called after each write operation. |
997 | */ |
998 | void afterWriting() { |
999 | if (conn != null) { |
1000 | conn.afterWriting(); |
1001 | } |
1002 | } |
1003 | |
1004 | /** |
1005 | * INTERNAL. |
1006 | * Close and old result set if there is still one open. |
1007 | */ |
1008 | protected void closeOldResultSet() throws SQLException { |
1009 | try { |
1010 | if (!closedByResultSet) { |
1011 | if (resultSet != null) { |
1012 | resultSet.closeInternal(); |
1013 | } |
1014 | } |
1015 | } finally { |
1016 | cancelled = false; |
1017 | resultSet = null; |
1018 | updateCount = -1; |
1019 | } |
1020 | } |
1021 | |
1022 | /** |
1023 | * INTERNAL. |
1024 | * Set the statement that is currently running. |
1025 | * |
1026 | * @param c the command |
1027 | */ |
1028 | protected void setExecutingStatement(CommandInterface c) { |
1029 | if (c == null) { |
1030 | conn.setExecutingStatement(null); |
1031 | } else { |
1032 | conn.setExecutingStatement(this); |
1033 | lastExecutedCommandType = c.getCommandType(); |
1034 | } |
1035 | executingCommand = c; |
1036 | } |
1037 | |
1038 | /** |
1039 | * INTERNAL. |
1040 | * Get the command type of the last executed command. |
1041 | */ |
1042 | public int getLastExecutedCommandType() { |
1043 | return lastExecutedCommandType; |
1044 | } |
1045 | |
1046 | /** |
1047 | * Returns whether this statement is closed. |
1048 | * |
1049 | * @return true if the statement is closed |
1050 | */ |
1051 | @Override |
1052 | public boolean isClosed() throws SQLException { |
1053 | try { |
1054 | debugCodeCall("isClosed"); |
1055 | return conn == null; |
1056 | } catch (Exception e) { |
1057 | throw logAndConvert(e); |
1058 | } |
1059 | } |
1060 | |
1061 | /** |
1062 | * Return an object of this class if possible. |
1063 | * |
1064 | * @param iface the class |
1065 | * @return this |
1066 | */ |
1067 | @Override |
1068 | @SuppressWarnings("unchecked") |
1069 | public <T> T unwrap(Class<T> iface) throws SQLException { |
1070 | if (isWrapperFor(iface)) { |
1071 | return (T) this; |
1072 | } |
1073 | throw DbException.getInvalidValueException("iface", iface); |
1074 | } |
1075 | |
1076 | /** |
1077 | * Checks if unwrap can return an object of this class. |
1078 | * |
1079 | * @param iface the class |
1080 | * @return whether or not the interface is assignable from this class |
1081 | */ |
1082 | @Override |
1083 | public boolean isWrapperFor(Class<?> iface) throws SQLException { |
1084 | return iface != null && iface.isAssignableFrom(getClass()); |
1085 | } |
1086 | |
1087 | /** |
1088 | * Returns whether this object is poolable. |
1089 | * @return false |
1090 | */ |
1091 | @Override |
1092 | public boolean isPoolable() { |
1093 | debugCodeCall("isPoolable"); |
1094 | return false; |
1095 | } |
1096 | |
1097 | /** |
1098 | * Requests that this object should be pooled or not. |
1099 | * This call is ignored. |
1100 | * |
1101 | * @param poolable the requested value |
1102 | */ |
1103 | @Override |
1104 | public void setPoolable(boolean poolable) { |
1105 | if (isDebugEnabled()) { |
1106 | debugCode("setPoolable("+poolable+");"); |
1107 | } |
1108 | } |
1109 | |
1110 | /** |
1111 | * INTERNAL |
1112 | */ |
1113 | @Override |
1114 | public String toString() { |
1115 | return getTraceObjectName(); |
1116 | } |
1117 | |
1118 | } |
1119 | |