Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/jdbc/MonetStatement.java @ 391:f523727db392
Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.*
This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12) |
parents | src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java@02f353f62abe |
children | 00c6bb1d84ed |
comparison
equal
deleted
inserted
replaced
390:6199e0be3c6e | 391:f523727db392 |
---|---|
1 /* | |
2 * This Source Code Form is subject to the terms of the Mozilla Public | |
3 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 * | |
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V. | |
7 */ | |
8 | |
9 package org.monetdb.jdbc; | |
10 | |
11 import org.monetdb.mcl.net.MapiSocket; | |
12 import java.sql.BatchUpdateException; | |
13 import java.sql.Connection; | |
14 import java.sql.Statement; | |
15 import java.sql.ResultSet; | |
16 import java.sql.SQLException; | |
17 import java.sql.SQLFeatureNotSupportedException; | |
18 import java.sql.SQLWarning; | |
19 import java.util.ArrayList; | |
20 import java.util.concurrent.locks.ReentrantLock; | |
21 | |
22 /** | |
23 * A Statement suitable for the MonetDB database. | |
24 * | |
25 * The object used for executing a static SQL statement and returning | |
26 * the results it produces. | |
27 * | |
28 * By default, only one {@link ResultSet} object per Statement object can be | |
29 * open at the same time. Therefore, if the reading of one ResultSet | |
30 * object is interleaved with the reading of another, each must have | |
31 * been generated by different {@link Statement} objects. All execution methods | |
32 * in the Statement interface implicitly close a Statement's current | |
33 * ResultSet object if an open one exists. | |
34 * | |
35 * The current state of this Statement is that it only implements the | |
36 * executeQuery() which returns a ResultSet where from results can be | |
37 * read and executeUpdate() which returns the affected rows for DML. | |
38 * Commit and rollback are implemented, as is the autoCommit mechanism | |
39 * which relies on server side auto commit. | |
40 * Multi-result queries are supported using the getMoreResults() method. | |
41 * | |
42 * @author Fabian Groffen | |
43 * @author Martin van Dinther | |
44 * @version 0.9 | |
45 */ | |
46 public class MonetStatement | |
47 extends MonetWrapper | |
48 implements Statement, AutoCloseable | |
49 { | |
50 /** The parental Connection object */ | |
51 protected final MonetConnection connection; | |
52 /** The last ResponseList object this Statement produced */ | |
53 private MonetConnection.ResponseList lastResponseList; | |
54 /** The last Response that this object uses */ | |
55 MonetConnection.Response header; | |
56 /** The warnings this Statement object generated */ | |
57 private SQLWarning warnings; | |
58 /** Whether this Statement object is closed or not */ | |
59 protected boolean closed = false; | |
60 /** Whether the application wants this Statement object to be pooled */ | |
61 protected boolean poolable = false; | |
62 /** Whether this Statement should be closed if the last ResultSet closes */ | |
63 private boolean closeOnCompletion = false; | |
64 /** The timeout (in sec) for the query to return, 0 means no timeout */ | |
65 private int queryTimeout = 0; | |
66 /** The size of the blocks of results to ask for at the server */ | |
67 private int fetchSize = 0; | |
68 /** The maximum number of rows to return in a ResultSet, 0 indicates unlimited */ | |
69 private long maxRows = 0; | |
70 /** The type of ResultSet to produce; i.e. forward only, random access */ | |
71 private int resultSetType = MonetResultSet.DEF_RESULTSETTYPE; | |
72 /** The suggested direction of fetching data (implemented but not used) */ | |
73 private int fetchDirection = MonetResultSet.DEF_FETCHDIRECTION; | |
74 /** The concurrency of the ResultSet to produce */ | |
75 private int resultSetConcurrency = MonetResultSet.DEF_CONCURRENCY; | |
76 | |
77 /** A List to hold all queries of a batch */ | |
78 private ArrayList<String> batch = null; | |
79 private ReentrantLock batchLock = null; | |
80 | |
81 | |
82 /** | |
83 * MonetStatement constructor which checks the arguments for validity. | |
84 * This constructor is only accessible to classes from the jdbc package. | |
85 * | |
86 * @param connection the connection that created this Statement | |
87 * @param resultSetType type of ResultSet to produce | |
88 * @param resultSetConcurrency concurrency of ResultSet to produce | |
89 * @param resultSetHoldability holdability of ResultSet after commit | |
90 * @throws SQLException if an error occurs during login | |
91 * @throws IllegalArgumentException is one of the arguments null or empty | |
92 */ | |
93 MonetStatement( | |
94 final MonetConnection connection, | |
95 final int resultSetType, | |
96 final int resultSetConcurrency, | |
97 final int resultSetHoldability) | |
98 throws SQLException, IllegalArgumentException | |
99 { | |
100 if (connection == null) | |
101 throw new IllegalArgumentException("No Connection given!"); | |
102 | |
103 this.connection = connection; | |
104 this.queryTimeout = connection.lastSetQueryTimeout; | |
105 | |
106 this.resultSetType = resultSetType; | |
107 this.resultSetConcurrency = resultSetConcurrency; | |
108 | |
109 // check our limits, and generate warnings as appropriate | |
110 if (this.resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) { | |
111 addWarning("No concurrency mode other then read only is supported, continuing with concurrency level READ_ONLY", "01M13"); | |
112 this.resultSetConcurrency = ResultSet.CONCUR_READ_ONLY; | |
113 } | |
114 | |
115 // check type for supported mode | |
116 if (this.resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE) { | |
117 addWarning("Change sensitive scrolling ResultSet objects are not supported, continuing with a change non-sensitive scrollable cursor.", "01M14"); | |
118 this.resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; | |
119 } | |
120 | |
121 // check type for supported holdability | |
122 if (resultSetHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) { | |
123 addWarning("Close cursors at commit not supported, continuing with holdability to hold cursors open over commit.", "01M15"); | |
124 } | |
125 } | |
126 | |
127 //== methods of interface Statement | |
128 | |
129 /** | |
130 * Adds the given SQL command to the current list of commmands for this | |
131 * Statement object. The commands in this list can be executed as a | |
132 * batch by calling the method executeBatch. | |
133 * | |
134 * @param sql typically this is a static SQL INSERT or UPDATE statement | |
135 * @throws SQLException so the PreparedStatement can throw this exception | |
136 */ | |
137 @Override | |
138 public void addBatch(final String sql) throws SQLException { | |
139 if (batch == null) { | |
140 // create the ArrayList at first time use | |
141 batch = new ArrayList<String>(); | |
142 } | |
143 batch.add(sql); | |
144 } | |
145 | |
146 /** | |
147 * Empties this Statement object's current list of SQL commands. | |
148 */ | |
149 @Override | |
150 public void clearBatch() { | |
151 if (batch != null) | |
152 batch.clear(); | |
153 } | |
154 | |
155 /** | |
156 * Submits a batch of commands to the database for execution and if | |
157 * all commands execute successfully, returns an array of update | |
158 * counts. The int elements of the array that is returned are | |
159 * ordered to correspond to the commands in the batch, which are | |
160 * ordered according to the order in which they were added to the | |
161 * batch. The elements in the array returned by the method | |
162 * executeBatch may be one of the following: | |
163 * <ol> | |
164 * <li>A number greater than or equal to zero -- indicates that the | |
165 * command was processed successfully and is an update count giving | |
166 * the number of rows in the database that were affected by the | |
167 * command's execution</li> | |
168 * <li>A value of SUCCESS_NO_INFO -- indicates that the command was | |
169 * processed successfully but that the number of rows affected is | |
170 * unknown</li> | |
171 * </ol> | |
172 * If one of the commands in a batch update fails to execute | |
173 * properly, this method throws a BatchUpdateException, and a JDBC | |
174 * driver may or may not continue to process the remaining commands | |
175 * in the batch. However, the driver's behavior must be consistent | |
176 * with a particular DBMS, either always continuing to process | |
177 * commands or never continuing to process commands. | |
178 * | |
179 * MonetDB does continues after an error has occurred in the batch. | |
180 * If one of the commands attempts to return a result set, an | |
181 * SQLException is added to the SQLException list and thrown | |
182 * afterwards execution. Failing queries result in SQLExceptions | |
183 * too and may cause subparts of the batch to fail as well. | |
184 * | |
185 * @return an array of update counts containing one element for each | |
186 * command in the batch. The elements of the array are ordered | |
187 * according to the order in which commands were added to the | |
188 * batch. | |
189 * @throws SQLException if a database access error occurs. Throws | |
190 * BatchUpdateException (a subclass of SQLException) if one of the | |
191 * commands sent to the database fails to execute properly | |
192 */ | |
193 @Override | |
194 public int[] executeBatch() throws SQLException { | |
195 if (batch == null || batch.isEmpty()) { | |
196 return new int[0]; | |
197 } | |
198 | |
199 // this method is synchronized/locked to make sure no one gets in between the | |
200 // operations we execute below | |
201 if (batchLock == null) { | |
202 // create a ReentrantLock at first time use | |
203 batchLock = new ReentrantLock(); | |
204 } | |
205 batchLock.lock(); | |
206 try { | |
207 final int[] counts = new int[batch.size()]; | |
208 final String sep = connection.queryTempl[2]; | |
209 final int sepLen = sep.length(); | |
210 final BatchUpdateException e = new BatchUpdateException("Error(s) occurred while executing the batch, see next SQLExceptions for details", "22000", counts); | |
211 final StringBuilder tmpBatch = new StringBuilder(MapiSocket.BLOCK); | |
212 int offset = 0; | |
213 boolean first = true; | |
214 boolean error = false; | |
215 | |
216 for (int i = 0; i < batch.size(); i++) { | |
217 String tmp = batch.get(i); | |
218 if (sepLen + tmp.length() > MapiSocket.BLOCK) { | |
219 // The thing is too big. Way too big. Since it won't | |
220 // be optimal anyway, just add it to whatever we have | |
221 // and continue. | |
222 if (!first) | |
223 tmpBatch.append(sep); | |
224 tmpBatch.append(tmp); | |
225 // send and receive | |
226 error |= internalBatch(tmpBatch, counts, offset, i + 1, e); | |
227 offset = i; | |
228 tmpBatch.setLength(0); // clear the buffer | |
229 first = true; | |
230 continue; | |
231 } | |
232 if (tmpBatch.length() + sepLen + tmp.length() >= MapiSocket.BLOCK) { | |
233 // send and receive | |
234 error |= internalBatch(tmpBatch, counts, offset, i + 1, e); | |
235 offset = i; | |
236 tmpBatch.setLength(0); // clear the buffer | |
237 first = true; | |
238 } | |
239 if (first) | |
240 first = false; | |
241 else | |
242 tmpBatch.append(sep); | |
243 tmpBatch.append(tmp); | |
244 } | |
245 // send and receive | |
246 error |= internalBatch(tmpBatch, counts, offset, counts.length, e); | |
247 | |
248 // throw BatchUpdateException if it contains something | |
249 if (error) | |
250 throw e; | |
251 // otherwise just return the counts | |
252 return counts; | |
253 } finally { | |
254 batch.clear(); | |
255 batchLock.unlock(); | |
256 } | |
257 } | |
258 | |
259 private boolean internalBatch( | |
260 final StringBuilder batch, | |
261 final int[] counts, | |
262 int offset, | |
263 final int max, | |
264 final BatchUpdateException e) | |
265 throws BatchUpdateException | |
266 { | |
267 try { | |
268 boolean hasResultSet = internalExecute(batch.toString()); | |
269 int count = -1; | |
270 | |
271 if (!hasResultSet) | |
272 count = getUpdateCount(); | |
273 | |
274 do { | |
275 if (offset >= max) | |
276 throw new SQLException("Overflow: don't use multi statements when batching (" + max + ")", "M1M16"); | |
277 if (hasResultSet) { | |
278 e.setNextException( | |
279 new SQLException("Batch query produced a ResultSet! " + | |
280 "Ignoring and setting update count to " + | |
281 "value " + EXECUTE_FAILED, "M1M17")); | |
282 counts[offset] = EXECUTE_FAILED; | |
283 } else if (count >= 0) { | |
284 counts[offset] = count; | |
285 } | |
286 offset++; | |
287 } while ((hasResultSet = getMoreResults()) || (count = getUpdateCount()) != -1); | |
288 } catch (SQLException ex) { | |
289 e.setNextException(ex); | |
290 for (; offset < max; offset++) { | |
291 counts[offset] = EXECUTE_FAILED; | |
292 } | |
293 return true; | |
294 } | |
295 return false; | |
296 } | |
297 | |
298 /** | |
299 * Cancels this Statement object if both the DBMS and driver support aborting an SQL statement. | |
300 * This method can be used by one thread to cancel a statement that is being executed by another thread. | |
301 * | |
302 * @throws SQLException - if a database access error occurs or this method is called on a closed Statement | |
303 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this method | |
304 */ | |
305 @Override | |
306 public void cancel() throws SQLException { | |
307 throw new SQLFeatureNotSupportedException("Query cancelling is currently not supported by the driver.", "0A000"); | |
308 // a request to implement this is already logged, see: https://www.monetdb.org/bugzilla/show_bug.cgi?id=6222 | |
309 } | |
310 | |
311 /** | |
312 * Clears all warnings reported for this Statement object. After a call to | |
313 * this method, the method getWarnings returns null until a new warning is | |
314 * reported for this Statement object. | |
315 */ | |
316 @Override | |
317 public void clearWarnings() { | |
318 warnings = null; | |
319 } | |
320 | |
321 /** | |
322 * Releases this Statement object's database and JDBC resources immediately | |
323 * instead of waiting for this to happen when it is automatically closed. It | |
324 * is generally good practice to release resources as soon as you are | |
325 * finished with them to avoid tying up database resources. | |
326 * | |
327 * Calling the method close on a Statement object that is already closed has | |
328 * no effect. | |
329 * | |
330 * A Statement object is automatically closed when it is garbage collected. | |
331 * When a Statement object is closed, its current ResultSet object, if one | |
332 * exists, is also closed. | |
333 */ | |
334 @Override | |
335 public void close() { | |
336 // close previous ResultSet, if not closed already | |
337 if (lastResponseList != null) { | |
338 lastResponseList.close(); | |
339 lastResponseList = null; | |
340 } | |
341 closed = true; | |
342 } | |
343 | |
344 // Chapter 13.1.2.3 of Sun's JDBC 3.0 Specification | |
345 /** | |
346 * Executes the given SQL statement, which may return multiple results. In | |
347 * some (uncommon) situations, a single SQL statement may return multiple | |
348 * result sets and/or update counts. Normally you can ignore this unless | |
349 * you are (1) executing a stored procedure that you know may return | |
350 * multiple results or (2) you are dynamically executing an unknown SQL | |
351 * string. | |
352 * | |
353 * The execute method executes an SQL statement and indicates the form of | |
354 * the first result. You must then use the methods getResultSet or | |
355 * getUpdateCount to retrieve the result, and getMoreResults to move to any | |
356 * subsequent result(s). | |
357 * | |
358 * @param sql any SQL statement | |
359 * @return true if the first result is a ResultSet object; false if it is an | |
360 * update count or there are no results | |
361 * @throws SQLException if a database access error occurs | |
362 */ | |
363 @Override | |
364 public boolean execute(final String sql) throws SQLException { | |
365 return internalExecute(sql); | |
366 } | |
367 | |
368 /** | |
369 * Executes the given SQL statement, which may return multiple | |
370 * results, and signals the driver that any auto-generated keys | |
371 * should be made available for retrieval. The driver will ignore | |
372 * this signal if the SQL statement is not an INSERT statement. | |
373 * | |
374 * In some (uncommon) situations, a single SQL statement may return | |
375 * multiple result sets and/or update counts. Normally you can | |
376 * ignore this unless you are (1) executing a stored procedure that | |
377 * you know may return multiple results or (2) you are dynamically | |
378 * executing an unknown SQL string. | |
379 * | |
380 * The execute method executes an SQL statement and indicates the | |
381 * form of the first result. You must then use the methods | |
382 * getResultSet or getUpdateCount to retrieve the result, and | |
383 * getMoreResults to move to any subsequent result(s). | |
384 * | |
385 * @param sql any SQL statement | |
386 * @param autoGeneratedKeys a constant indicating whether | |
387 * auto-generated keys should be made available for retrieval | |
388 * using the method getGeneratedKeys; one of the following | |
389 * constants: Statement.RETURN_GENERATED_KEYS or | |
390 * Statement.NO_GENERATED_KEYS | |
391 * @return true if the first result is a ResultSet object; false if | |
392 * it is an update count or there are no results | |
393 * @throws SQLException - if a database access error occurs or the | |
394 * second parameter supplied to this method is not | |
395 * Statement.RETURN_GENERATED_KEYS or | |
396 * Statement.NO_GENERATED_KEYS. | |
397 */ | |
398 @Override | |
399 public boolean execute(final String sql, final int autoGeneratedKeys) | |
400 throws SQLException | |
401 { | |
402 if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && | |
403 autoGeneratedKeys != Statement.NO_GENERATED_KEYS) | |
404 throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); | |
405 | |
406 /* MonetDB has no way to disable this, so just do the normal thing ;) */ | |
407 return internalExecute(sql); | |
408 } | |
409 | |
410 /** | |
411 * Executes the given SQL statement, which may return multiple | |
412 * results, and signals the driver that the auto-generated keys | |
413 * indicated in the given array should be made available for | |
414 * retrieval. This array contains the indexes of the columns in the | |
415 * target table that contain the auto-generated keys that should be | |
416 * made available. The driver will ignore the array if the given SQL | |
417 * statement is not an INSERT statement. | |
418 * | |
419 * Under some (uncommon) situations, a single SQL statement may | |
420 * return multiple result sets and/or update counts. Normally you | |
421 * can ignore this unless you are (1) executing a stored procedure | |
422 * that you know may return multiple results or (2) you are | |
423 * dynamically executing an unknown SQL string. | |
424 * | |
425 * The execute method executes an SQL statement and indicates the | |
426 * form of the first result. You must then use the methods | |
427 * getResultSet or getUpdateCount to retrieve the result, and | |
428 * getMoreResults to move to any subsequent result(s). | |
429 * | |
430 * MonetDB only supports returing the generated key for one column, | |
431 * which will be the first column that has a serial. Hence, this | |
432 * method cannot work as required and the driver will fall back to | |
433 * executing with request to the database to return the generated | |
434 * key, if any. | |
435 * | |
436 * @param sql any SQL statement | |
437 * @param columnIndexes an array of the indexes of the columns in | |
438 * the inserted row that should be made available for | |
439 * retrieval by a call to the method getGeneratedKeys | |
440 * @return true if the first result is a ResultSet object; false if | |
441 * it is an update count or there are no results | |
442 * @throws SQLException if a database access error occurs or the | |
443 * elements in the int array passed to this method are not | |
444 * valid column indexes | |
445 */ | |
446 @Override | |
447 public boolean execute(final String sql, final int[] columnIndexes) | |
448 throws SQLException | |
449 { | |
450 addWarning("execute: generated keys for fixed set of columns not supported", "01M18"); | |
451 return execute(sql, Statement.RETURN_GENERATED_KEYS); | |
452 } | |
453 | |
454 /** | |
455 * Executes the given SQL statement, which may return multiple | |
456 * results, and signals the driver that the auto-generated keys | |
457 * indicated in the given array should be made available for | |
458 * retrieval. This array contains the names of the columns in the | |
459 * target table that contain the auto-generated keys that should be | |
460 * made available. The driver will ignore the array if the given SQL | |
461 * statement is not an INSERT statement. | |
462 * | |
463 * In some (uncommon) situations, a single SQL statement may return | |
464 * multiple result sets and/or update counts. Normally you can | |
465 * ignore this unless you are (1) executing a stored procedure that | |
466 * you know may return multiple results or (2) you are dynamically | |
467 * executing an unknown SQL string. | |
468 * | |
469 * The execute method executes an SQL statement and indicates the | |
470 * form of the first result. You must then use the methods | |
471 * getResultSet or getUpdateCount to retrieve the result, and | |
472 * getMoreResults to move to any subsequent result(s). | |
473 * | |
474 * MonetDB only supports returing the generated key for one column, | |
475 * which will be the first column that has a serial. Hence, this | |
476 * method cannot work as required and the driver will fall back to | |
477 * executing with request to the database to return the generated | |
478 * key, if any. | |
479 * | |
480 * @param sql any SQL statement | |
481 * @param columnNames an array of the names of the columns in the | |
482 * inserted row that should be made available for retrieval | |
483 * by a call to the method getGeneratedKeys | |
484 * @return true if the next result is a ResultSet object; false if | |
485 * it is an update count or there are no more results | |
486 * @throws SQLException if a database access error occurs or the | |
487 * elements of the String array passed to this method are | |
488 * not valid column names | |
489 */ | |
490 @Override | |
491 public boolean execute(final String sql, final String[] columnNames) | |
492 throws SQLException | |
493 { | |
494 addWarning("execute: generated keys for fixed set of columns not supported", "01M18"); | |
495 return execute(sql, Statement.RETURN_GENERATED_KEYS); | |
496 } | |
497 | |
498 /** | |
499 * Performs the steps to execute a given SQL statement. This method | |
500 * exists to allow the functionality of this function to be called | |
501 * from within this class only. The PreparedStatement for example | |
502 * overrides the execute() method to throw an SQLException, but it | |
503 * needs its functionality when the executeBatch method (which is | |
504 * inherited) is called. | |
505 * | |
506 * @param sql any SQL statement | |
507 * @return true if the first result is a ResultSet object; false if | |
508 * it is an update count or there are no results | |
509 * @throws SQLException if a database access error occurs | |
510 */ | |
511 private boolean internalExecute(final String sql) throws SQLException { | |
512 // close previous query, if not closed already | |
513 if (lastResponseList != null) { | |
514 lastResponseList.close(); | |
515 lastResponseList = null; | |
516 } | |
517 | |
518 if (queryTimeout != connection.lastSetQueryTimeout) { | |
519 // set requested/changed queryTimeout on the server side first | |
520 Statement st = null; | |
521 try { | |
522 st = connection.createStatement(); | |
523 final String callstmt = "CALL \"sys\".\"settimeout\"(" + queryTimeout + ")"; | |
524 // for debug: System.out.println("Before: " + callstmt); | |
525 st.execute(callstmt); | |
526 // for debug: System.out.println("After : " + callstmt); | |
527 connection.lastSetQueryTimeout = queryTimeout; | |
528 } | |
529 /* do not catch SQLException here, as we want to know it when it fails */ | |
530 finally { | |
531 if (st != null) { | |
532 try { | |
533 st.close(); | |
534 } catch (SQLException e) { /* ignore */ } | |
535 } | |
536 } | |
537 } | |
538 | |
539 // create a container for the result | |
540 lastResponseList = connection.new ResponseList( | |
541 fetchSize, | |
542 maxRows, | |
543 resultSetType, | |
544 resultSetConcurrency | |
545 ); | |
546 // fill the header list by processing the query | |
547 lastResponseList.processQuery(sql); | |
548 | |
549 return getMoreResults(); | |
550 } | |
551 | |
552 /** | |
553 * Executes the given SQL statement, which returns a single ResultSet | |
554 * object. | |
555 * | |
556 * @param sql an SQL statement to be sent to the database, typically a | |
557 * static SQL SELECT statement | |
558 * @return a ResultSet object that contains the data produced by the given | |
559 * query; never null | |
560 * @throws SQLException if a database access error occurs or the given SQL | |
561 * statement produces anything other than a single ResultSet object | |
562 */ | |
563 @Override | |
564 public ResultSet executeQuery(final String sql) throws SQLException { | |
565 if (execute(sql) != true) | |
566 throw new SQLException("Query did not produce a result set", "M1M19"); | |
567 | |
568 return getResultSet(); | |
569 } | |
570 | |
571 /** | |
572 * Executes the given SQL statement, which may be an INSERT, UPDATE, or | |
573 * DELETE statement or an SQL statement that returns nothing, such as an | |
574 * SQL DDL statement. | |
575 * | |
576 * @param sql an SQL INSERT, UPDATE or DELETE statement or an SQL statement | |
577 * that returns nothing | |
578 * @return either the row count for INSERT, UPDATE or DELETE statements, or | |
579 * 0 for SQL statements that return nothing | |
580 * @throws SQLException if a database access error occurs or the given SQL | |
581 * statement produces a ResultSet object | |
582 */ | |
583 @Override | |
584 public int executeUpdate(final String sql) throws SQLException { | |
585 if (execute(sql) != false) | |
586 throw new SQLException("Query produced a result set", "M1M17"); | |
587 | |
588 return getUpdateCount(); | |
589 } | |
590 | |
591 /** | |
592 * Executes the given SQL statement and signals the driver with the | |
593 * given flag about whether the auto-generated keys produced by this | |
594 * Statement object should be made available for retrieval. | |
595 * | |
596 * @param sql must be an SQL INSERT, UPDATE or DELETE statement or | |
597 * an SQL statement that returns nothing | |
598 * @param autoGeneratedKeys - a flag indicating whether | |
599 * auto-generated keys should be made available for | |
600 * retrieval; one of the following constants: | |
601 * Statement.RETURN_GENERATED_KEYS | |
602 * Statement.NO_GENERATED_KEYS | |
603 * @return either the row count for INSERT, UPDATE or DELETE | |
604 * statements, or 0 for SQL statements that return nothing | |
605 * @throws SQLException if a database access error occurs, the | |
606 * given SQL statement returns a ResultSet object, or the | |
607 * given constant is not one of those allowed | |
608 */ | |
609 @Override | |
610 public int executeUpdate(final String sql, final int autoGeneratedKeys) | |
611 throws SQLException | |
612 { | |
613 if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && | |
614 autoGeneratedKeys != Statement.NO_GENERATED_KEYS) | |
615 throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); | |
616 | |
617 /* MonetDB has no way to disable this, so just do the normal thing ;) */ | |
618 if (execute(sql) != false) | |
619 throw new SQLException("Query produced a result set", "M1M17"); | |
620 | |
621 return getUpdateCount(); | |
622 } | |
623 | |
624 /** | |
625 * Executes the given SQL statement and signals the driver that the | |
626 * auto-generated keys indicated in the given array should be made | |
627 * available for retrieval. The driver will ignore the array if the | |
628 * SQL statement is not an INSERT statement. | |
629 * | |
630 * MonetDB only supports returing the generated key for one column, | |
631 * which will be the first column that has a serial. Hence, this | |
632 * method cannot work as required and the driver will fall back to | |
633 * executing with request to the database to return the generated | |
634 * key, if any. | |
635 * | |
636 * @param sql an SQL INSERT, UPDATE or DELETE statement or an SQL | |
637 * statement that returns nothing, such as an SQL DDL statement | |
638 * @param columnIndexes an array of column indexes indicating the | |
639 * columns that should be returned from the inserted row | |
640 * @return either the row count for INSERT, UPDATE, or DELETE | |
641 * statements, or 0 for SQL statements that return nothing | |
642 * @throws SQLException if a database access error occurs, the SQL | |
643 * statement returns a ResultSet object, or the second | |
644 * argument supplied to this method is not an int array | |
645 * whose elements are valid column indexes | |
646 */ | |
647 @Override | |
648 public int executeUpdate(final String sql, final int[] columnIndexes) | |
649 throws SQLException | |
650 { | |
651 addWarning("executeUpdate: generated keys for fixed set of columns not supported", "01M18"); | |
652 return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); | |
653 } | |
654 | |
655 /** | |
656 * Executes the given SQL statement and signals the driver that the | |
657 * auto-generated keys indicated in the given array should be made | |
658 * available for retrieval. The driver will ignore the array if the | |
659 * SQL statement is not an INSERT statement. | |
660 * | |
661 * MonetDB only supports returing the generated key for one column, | |
662 * which will be the first column that has a serial. Hence, this | |
663 * method cannot work as required and the driver will fall back to | |
664 * executing with request to the database to return the generated | |
665 * key, if any. | |
666 * | |
667 * @param sql an SQL INSERT, UPDATE or DELETE statement or an SQL | |
668 * statement that returns nothing, such as an SQL DDL statement | |
669 * @param columnNames an array of the names of the columns that | |
670 * should be returned from the inserted row | |
671 * @return either the row count for INSERT, UPDATE, or DELETE | |
672 * statements, or 0 for SQL statements that return nothing | |
673 * @throws SQLException if a database access error occurs, the SQL | |
674 * statement returns a ResultSet object, or the second | |
675 * argument supplied to this method is not a String array | |
676 * whose elements are valid column names | |
677 */ | |
678 @Override | |
679 public int executeUpdate(final String sql, final String[] columnNames) | |
680 throws SQLException | |
681 { | |
682 addWarning("executeUpdate: generated keys for fixed set of columns not supported", "01M18"); | |
683 return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); | |
684 } | |
685 | |
686 /** | |
687 * Retrieves the Connection object that produced this Statement object. | |
688 * | |
689 * @return the connection that produced this statement | |
690 */ | |
691 @Override | |
692 public Connection getConnection() { | |
693 return connection; | |
694 } | |
695 | |
696 /** | |
697 * Retrieves the direction for fetching rows from database tables that is | |
698 * the default for result sets generated from this Statement object. If | |
699 * this Statement object has not set a fetch direction by calling the | |
700 * method setFetchDirection, the return value is ResultSet.FETCH_FORWARD. | |
701 * | |
702 * @return the default fetch direction for result sets generated from this | |
703 * Statement object | |
704 */ | |
705 @Override | |
706 public int getFetchDirection() { | |
707 return fetchDirection; | |
708 } | |
709 | |
710 /** | |
711 * Retrieves the number of result set rows that is the default fetch size | |
712 * for ResultSet objects generated from this Statement object. If this | |
713 * Statement object has not set a fetch size by calling the method | |
714 * setFetchSize, or the method setFetchSize was called as such to let | |
715 * the driver ignore the hint, 0 is returned. | |
716 * | |
717 * @return the default fetch size for result sets generated from this | |
718 * Statement object | |
719 */ | |
720 @Override | |
721 public int getFetchSize() { | |
722 return fetchSize; | |
723 } | |
724 | |
725 /** | |
726 * Retrieves any auto-generated keys created as a result of | |
727 * executing this Statement object. If this Statement object did not | |
728 * generate any keys, an empty ResultSet object is returned. | |
729 * | |
730 * @return a ResultSet object containing the auto-generated key(s) | |
731 * generated by the execution of this Statement object | |
732 * @throws SQLException - if a database access error occurs | |
733 */ | |
734 @Override | |
735 public ResultSet getGeneratedKeys() throws SQLException { | |
736 final String[] columns, types; | |
737 final String[][] results; | |
738 | |
739 columns = new String[1]; | |
740 types = new String[1]; | |
741 | |
742 columns[0] = "GENERATED_KEY"; | |
743 /* the generated key should be an integer, because (wait for it) other | |
744 * frameworks such as spring expect this. */ | |
745 types[0] = "BIGINT"; | |
746 | |
747 if (header != null && header instanceof MonetConnection.UpdateResponse) { | |
748 final String lastid = ((MonetConnection.UpdateResponse)header).lastid; | |
749 if (lastid.equals("-1")) { | |
750 results = new String[0][1]; | |
751 } else { | |
752 results = new String[1][1]; | |
753 results[0][0] = lastid; | |
754 } | |
755 } else { | |
756 results = new String[0][1]; | |
757 } | |
758 | |
759 try { | |
760 return new MonetVirtualResultSet(this, columns, types, results); | |
761 } catch (IllegalArgumentException e) { | |
762 throw new SQLException("Internal driver error: " + e.getMessage(), "M0M03"); | |
763 } | |
764 } | |
765 | |
766 /** | |
767 * Retrieves the maximum number of bytes that can be returned for | |
768 * character and binary column values in a ResultSet object produced | |
769 * by this Statement object. This limit applies only to BINARY, | |
770 * VARBINARY, LONGVARBINARY, CHAR, VARCHAR, and LONGVARCHAR | |
771 * columns. If the limit is exceeded, the excess data is silently | |
772 * discarded. | |
773 * | |
774 * The MonetDB JDBC driver currently doesn't support limiting | |
775 * fieldsizes, and hence always return 0 (unlimited). | |
776 * | |
777 * @return the current column size limit for columns storing | |
778 * character and binary values; zero means there is no limit | |
779 * @see #setMaxFieldSize(int max) | |
780 */ | |
781 @Override | |
782 public int getMaxFieldSize() { | |
783 return 2*1024*1024*1024 - 2; // MonetDB supports null terminated strings of max 2GB, see function: int UTF8_strlen() | |
784 } | |
785 | |
786 /** | |
787 * Retrieves the maximum number of rows that a ResultSet object produced by | |
788 * this Statement object can contain. If this limit is exceeded, the excess | |
789 * rows are silently dropped. | |
790 * | |
791 * @return the current maximum number of rows for a ResultSet object | |
792 * produced by this Statement object; zero means there is no limit | |
793 * @see #setMaxRows(int max) | |
794 */ | |
795 @Override | |
796 public int getMaxRows() { | |
797 if (maxRows >= Integer.MAX_VALUE) | |
798 return Integer.MAX_VALUE; | |
799 return (int)maxRows; | |
800 } | |
801 | |
802 /** | |
803 * Moves to this Statement object's next result, returns true if it is a | |
804 * ResultSet object, and implicitly closes any current ResultSet object(s) | |
805 * obtained with the method getResultSet. | |
806 * | |
807 * There are no more results when the following is true: | |
808 * (!getMoreResults() && (getUpdateCount() == -1) | |
809 * | |
810 * @return true if the next result is a ResultSet object; false if it is | |
811 * an update count or there are no more results | |
812 * @throws SQLException if a database access error occurs | |
813 * @see #getMoreResults(int current) | |
814 */ | |
815 @Override | |
816 public boolean getMoreResults() throws SQLException { | |
817 return getMoreResults(CLOSE_ALL_RESULTS); | |
818 } | |
819 | |
820 /** | |
821 * Moves to this Statement object's next result, deals with any current | |
822 * ResultSet object(s) according to the instructions specified by the given | |
823 * flag, and returns true if the next result is a ResultSet object. | |
824 * | |
825 * There are no more results when the following is true: | |
826 * (!getMoreResults() && (getUpdateCount() == -1) | |
827 * | |
828 * @param current one of the following Statement constants indicating what | |
829 * should happen to current ResultSet objects obtained using | |
830 * the method getResultSet: CLOSE_CURRENT_RESULT, | |
831 * KEEP_CURRENT_RESULT, or CLOSE_ALL_RESULTS | |
832 * @return true if the next result is a ResultSet object; false if it is | |
833 * an update count or there are no more results | |
834 * @throws SQLException if a database access error occurs | |
835 */ | |
836 @Override | |
837 public boolean getMoreResults(final int current) throws SQLException { | |
838 // protect against people calling this on an unitialised state | |
839 if (lastResponseList == null) { | |
840 header = null; | |
841 return false; | |
842 } | |
843 | |
844 if (current == CLOSE_CURRENT_RESULT) { | |
845 lastResponseList.closeCurrentResponse(); | |
846 } else if (current == CLOSE_ALL_RESULTS) { | |
847 lastResponseList.closeCurOldResponses(); | |
848 } | |
849 // we default to keep current result, which requires no action | |
850 header = lastResponseList.getNextResponse(); | |
851 | |
852 return (header != null && header instanceof MonetConnection.ResultSetResponse); | |
853 } | |
854 | |
855 /** | |
856 * Retrieves the number of seconds the driver will wait for a | |
857 * Statement object to execute. If the limit is exceeded, a | |
858 * SQLException is thrown. | |
859 * | |
860 * @return the current query timeout limit in seconds; zero means | |
861 * there is no limit | |
862 * @see #setQueryTimeout(int) | |
863 */ | |
864 @Override | |
865 public int getQueryTimeout() { | |
866 return queryTimeout; | |
867 } | |
868 | |
869 /** | |
870 * Retrieves the current result as a ResultSet object. This method | |
871 * should be called only once per result. | |
872 * | |
873 * @return the current result as a ResultSet object or null if the result | |
874 * is an update count or there are no more results | |
875 * @throws SQLException if a database access error occurs | |
876 */ | |
877 @Override | |
878 public ResultSet getResultSet() throws SQLException { | |
879 return (header != null && header instanceof MonetConnection.ResultSetResponse) | |
880 ? new MonetResultSet(this, | |
881 (MonetConnection.ResultSetResponse)header) | |
882 : null; | |
883 } | |
884 | |
885 /** | |
886 * Retrieves the result set concurrency for ResultSet objects generated | |
887 * by this Statement object. | |
888 * | |
889 * @return either ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE | |
890 */ | |
891 @Override | |
892 public int getResultSetConcurrency() { | |
893 return resultSetConcurrency; | |
894 } | |
895 | |
896 /** | |
897 * Retrieves the result set holdability for ResultSet objects | |
898 * generated by this Statement object. | |
899 * | |
900 * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or | |
901 * ResultSet.CLOSE_CURSORS_AT_COMMIT | |
902 */ | |
903 @Override | |
904 public int getResultSetHoldability() { | |
905 return ResultSet.HOLD_CURSORS_OVER_COMMIT; | |
906 } | |
907 | |
908 /** | |
909 * Retrieves the result set type for ResultSet objects generated by this | |
910 * Statement object. | |
911 * | |
912 * @return one of ResultSet.TYPE_FORWARD_ONLY, | |
913 * ResultSet.TYPE_SCROLL_INSENSITIVE, or | |
914 * ResultSet.TYPE_SCROLL_SENSITIVE | |
915 */ | |
916 @Override | |
917 public int getResultSetType() { | |
918 return resultSetType; | |
919 } | |
920 | |
921 /** | |
922 * Retrieves the current result as an update count; if the result is a | |
923 * ResultSet object or there are no more results, -1 is returned. This | |
924 * method should be called only once per result. | |
925 * | |
926 * @return the current result as an update count; -1 if the current result | |
927 * is a ResultSet object or there are no more results | |
928 * @throws SQLException if a database access error occurs | |
929 */ | |
930 @Override | |
931 public int getUpdateCount() throws SQLException { | |
932 long ret = getLargeUpdateCount(); | |
933 if (ret >= Integer.MAX_VALUE) | |
934 return Integer.MAX_VALUE; | |
935 return (int)ret; | |
936 } | |
937 | |
938 /** | |
939 * Retrieves the first warning reported by calls on this Statement object. | |
940 * If there is more than one warning, subsequent warnings will be chained to | |
941 * the first one and can be retrieved by calling the method | |
942 * SQLWarning.getNextWarning on the warning that was retrieved previously. | |
943 * | |
944 * This method may not be called on a closed statement; doing so will cause | |
945 * an SQLException to be thrown. | |
946 * | |
947 * Note: Subsequent warnings will be chained to this SQLWarning. | |
948 * | |
949 * @return the first SQLWarning object or null if there are none | |
950 * @throws SQLException if a database access error occurs or this method is | |
951 * called on a closed connection | |
952 */ | |
953 @Override | |
954 public SQLWarning getWarnings() throws SQLException { | |
955 if (closed) | |
956 throw new SQLException("Cannot call on closed Statement", "M1M20"); | |
957 | |
958 // if there are no warnings, this will be null, which fits with the | |
959 // specification. | |
960 return warnings; | |
961 } | |
962 | |
963 /** | |
964 * Sets the SQL cursor name to the given String, which will be used | |
965 * by subsequent Statement object execute methods. This name can | |
966 * then be used in SQL positioned update or delete statements to | |
967 * identify the current row in the ResultSet object generated by | |
968 * this statement. If the database does not support positioned | |
969 * update/delete, this method is a noop. To insure that a cursor has | |
970 * the proper isolation level to support updates, the cursor's | |
971 * SELECT statement should have the form SELECT FOR UPDATE. If FOR | |
972 * UPDATE is not present, positioned updates may fail. | |
973 * | |
974 * <b>Note:</b> By definition, the execution of positioned updates | |
975 * and deletes must be done by a different Statement object than the | |
976 * one that generated the ResultSet object being used for | |
977 * positioning. Also, cursor names must be unique within a | |
978 * connection. | |
979 * | |
980 * Since MonetDB does not support positioned update/delete, this | |
981 * method is a noop. | |
982 * | |
983 * @param name the new cursor name, which must be unique within a | |
984 * connection | |
985 * @throws SQLException if a database access error occurs | |
986 */ | |
987 @Override | |
988 public void setCursorName(final String name) throws SQLException { | |
989 addWarning("setCursorName: positioned updates/deletes not supported", "01M21"); | |
990 } | |
991 | |
992 /** | |
993 * Sets escape processing on or off. If escape scanning is on (the | |
994 * default), the driver will do escape substitution before sending | |
995 * the SQL statement to the database. Note: Since prepared | |
996 * statements have usually been parsed prior to making this call, | |
997 * disabling escape processing for PreparedStatements objects will | |
998 * have no effect. | |
999 * | |
1000 * The MonetDB JDBC driver implements no escape processing at all in | |
1001 * its current implementation because it is too expensive, and in | |
1002 * general should not be necessary given SQL standards compliance. | |
1003 * In this sense, this driver will ignore any call to this function. | |
1004 * | |
1005 * @param enable true to enable escape processing; false to disable | |
1006 * it | |
1007 * @throws SQLException if a database access error occurs | |
1008 */ | |
1009 @Override | |
1010 public void setEscapeProcessing(final boolean enable) throws SQLException { | |
1011 if (enable) | |
1012 addWarning("setEscapeProcessing: JDBC escape syntax is not supported by this driver", "01M22"); | |
1013 } | |
1014 | |
1015 /** | |
1016 * Gives the driver a hint as to the direction in which rows will be | |
1017 * processed in ResultSet objects created using this Statement object. | |
1018 * The default value is ResultSet.FETCH_FORWARD. | |
1019 * | |
1020 * Note that this method sets the default fetch direction for result sets | |
1021 * generated by this Statement object. Each result set has its own methods | |
1022 * for getting and setting its own fetch direction. | |
1023 * | |
1024 * @param direction the initial direction for processing rows | |
1025 * @throws SQLException if a database access error occurs or the given | |
1026 * direction is not one of ResultSet.FETCH_FORWARD, | |
1027 * ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN | |
1028 */ | |
1029 @Override | |
1030 public void setFetchDirection(final int direction) throws SQLException { | |
1031 if (direction == ResultSet.FETCH_FORWARD || | |
1032 direction == ResultSet.FETCH_REVERSE || | |
1033 direction == ResultSet.FETCH_UNKNOWN) | |
1034 { | |
1035 fetchDirection = direction; | |
1036 } else { | |
1037 throw new SQLException("Illegal direction: " + direction, "M1M05"); | |
1038 } | |
1039 } | |
1040 | |
1041 /** | |
1042 * Gives the JDBC driver a hint as to the number of rows that should be | |
1043 * fetched from the database when more rows are needed. The number of rows | |
1044 * specified affects only result sets created using this statement. If the | |
1045 * value specified is zero, then the hint is ignored. | |
1046 * | |
1047 * @param rows the number of rows to fetch | |
1048 * @throws SQLException if the condition 0 <= rows <= this.getMaxRows() | |
1049 * is not satisfied. | |
1050 */ | |
1051 @Override | |
1052 public void setFetchSize(final int rows) throws SQLException { | |
1053 if (rows >= 0 && !(getMaxRows() != 0 && rows > getMaxRows())) { | |
1054 fetchSize = rows; | |
1055 } else { | |
1056 throw new SQLException("Illegal fetch size value: " + rows, "M1M05"); | |
1057 } | |
1058 } | |
1059 | |
1060 /** | |
1061 * Sets the limit for the maximum number of bytes in a ResultSet | |
1062 * column storing character or binary values to the given number of | |
1063 * bytes. This limit applies only to BINARY, VARBINARY, | |
1064 * LONGVARBINARY, CHAR, VARCHAR, and LONGVARCHAR fields. If the | |
1065 * limit is exceeded, the excess data is silently discarded. For | |
1066 * maximum portability, use values greater than 256. | |
1067 * | |
1068 * MonetDB does not support any fieldsize limiting, and hence the | |
1069 * driver does not emulate it either, since it doesn't really lead | |
1070 * to memory reduction. | |
1071 * | |
1072 * @param max the new column size limit in bytes; zero means there | |
1073 * is no limit | |
1074 * @throws SQLException if a database access error occurs or the | |
1075 * condition max >= 0 is not satisfied | |
1076 * @see #getMaxFieldSize() | |
1077 */ | |
1078 @Override | |
1079 public void setMaxFieldSize(final int max) throws SQLException { | |
1080 if (max < 0) | |
1081 throw new SQLException("Illegal max value: " + max, "M1M05"); | |
1082 if (max > 0) | |
1083 addWarning("setMaxFieldSize: field size limitation not supported", "01M23"); | |
1084 } | |
1085 | |
1086 /** | |
1087 * Sets the limit for the maximum number of rows that any ResultSet object | |
1088 * can contain to the given number. If the limit is exceeded, the excess | |
1089 * rows are silently dropped. | |
1090 * | |
1091 * @param max the new max rows limit; zero means there is no limit | |
1092 * @throws SQLException if the condition max >= 0 is not satisfied | |
1093 * @see #getMaxRows() | |
1094 */ | |
1095 @Override | |
1096 public void setMaxRows(final int max) throws SQLException { | |
1097 if (max < 0) | |
1098 throw new SQLException("Illegal max value: " + max, "M1M05"); | |
1099 maxRows = max; | |
1100 } | |
1101 | |
1102 /** | |
1103 * Sets the number of seconds the driver will wait for a Statement | |
1104 * object to execute to the given number of seconds. If the limit is | |
1105 * exceeded, an SQLException is thrown. | |
1106 * | |
1107 * @param seconds the new query timeout limit in seconds; zero means | |
1108 * there is no limit | |
1109 * @throws SQLException if a database access error occurs or the | |
1110 * condition seconds >= 0 is not satisfied | |
1111 */ | |
1112 @Override | |
1113 public void setQueryTimeout(final int seconds) throws SQLException { | |
1114 if (seconds < 0) | |
1115 throw new SQLException("Illegal timeout value: " + seconds, "M1M05"); | |
1116 queryTimeout = seconds; | |
1117 } | |
1118 | |
1119 //== 1.6 methods (JDBC 4.0) | |
1120 | |
1121 /** | |
1122 * Retrieves whether this Statement object has been closed. A | |
1123 * Statement is closed if the method close has been called on it, or | |
1124 * if it is automatically closed. | |
1125 * | |
1126 * @return true if this Statement object is closed; false if it is | |
1127 * still open | |
1128 */ | |
1129 @Override | |
1130 public boolean isClosed() { | |
1131 return closed; | |
1132 } | |
1133 | |
1134 /** | |
1135 * Requests that a Statement be pooled or not pooled. The value | |
1136 * specified is a hint to the statement pool implementation | |
1137 * indicating whether the applicaiton wants the statement to be | |
1138 * pooled. It is up to the statement pool manager as to whether the | |
1139 * hint is used. | |
1140 * | |
1141 * The poolable value of a statement is applicable to both internal | |
1142 * statement caches implemented by the driver and external statement | |
1143 * caches implemented by application servers and other applications. | |
1144 * | |
1145 * By default, a Statement is not poolable when created, and a | |
1146 * PreparedStatement and CallableStatement are poolable when | |
1147 * created. | |
1148 * | |
1149 * @param poolable requests that the statement be pooled if true | |
1150 * and that the statement not be pooled if false | |
1151 */ | |
1152 @Override | |
1153 public void setPoolable(final boolean poolable) { | |
1154 this.poolable = poolable; | |
1155 } | |
1156 | |
1157 /** | |
1158 * Returns a value indicating whether the Statement is poolable or | |
1159 * not. | |
1160 * | |
1161 * @return true if the Statement is poolable; false otherwise | |
1162 */ | |
1163 @Override | |
1164 public boolean isPoolable() { | |
1165 return poolable; | |
1166 } | |
1167 | |
1168 //== 1.7 methods (JDBC 4.1) | |
1169 | |
1170 /** | |
1171 * Specifies that this Statement will be closed when all its | |
1172 * dependent result sets are closed. If execution of the Statement | |
1173 * does not produce any result sets, this method has no effect. | |
1174 * | |
1175 * @throws SQLException if this method is called on a closed Statement | |
1176 */ | |
1177 @Override | |
1178 public void closeOnCompletion() throws SQLException { | |
1179 if (closed) | |
1180 throw new SQLException("Cannot call on closed Statement", "M1M20"); | |
1181 closeOnCompletion = true; | |
1182 } | |
1183 | |
1184 /** | |
1185 * Returns a value indicating whether this Statement will be closed | |
1186 * when all its dependent result sets are closed. | |
1187 * | |
1188 * @return true if the Statement will be closed when all of its | |
1189 * dependent result sets are closed; false otherwise | |
1190 * @throws SQLException if this method is called on a closed Statement | |
1191 */ | |
1192 @Override | |
1193 public boolean isCloseOnCompletion() throws SQLException { | |
1194 if (closed) | |
1195 throw new SQLException("Cannot call on closed Statement", "M1M20"); | |
1196 return closeOnCompletion; | |
1197 } | |
1198 | |
1199 //== Java 1.8 methods (JDBC 4.2) | |
1200 | |
1201 /** | |
1202 * Retrieves the current result as an update count; | |
1203 * if the result is a ResultSet object or there are no more results, -1 is returned. | |
1204 * This method should be called only once per result. | |
1205 * | |
1206 * This method should be used when the returned row count may exceed Integer.MAX_VALUE. | |
1207 * The default implementation will throw UnsupportedOperationException | |
1208 * | |
1209 * @return the current result as an update count; -1 if the current | |
1210 * result is a ResultSet object or there are no more results | |
1211 * @throws SQLException if a database access error occurs or this | |
1212 * method is called on a closed Statement | |
1213 */ | |
1214 public long getLargeUpdateCount() throws SQLException { | |
1215 long ret = -1; | |
1216 if (header != null) { | |
1217 if (header instanceof MonetConnection.UpdateResponse) { | |
1218 ret = ((MonetConnection.UpdateResponse)header).count; | |
1219 } else if (header instanceof MonetConnection.SchemaResponse) { | |
1220 ret = ((MonetConnection.SchemaResponse)header).state; | |
1221 } | |
1222 } | |
1223 | |
1224 return ret; | |
1225 } | |
1226 | |
1227 /** | |
1228 * Sets the limit for the maximum number of rows that any ResultSet object | |
1229 * generated by this Statement object can contain to the given number. | |
1230 * If the limit is exceeded, the excess rows are silently dropped. | |
1231 * | |
1232 * This method should be used when the row limit may exceed Integer.MAX_VALUE. | |
1233 * The default implementation will throw UnsupportedOperationException | |
1234 * | |
1235 * @param max the new max rows limit; zero means there is no limit | |
1236 * @throws SQLException if a database access error occurs, | |
1237 * this method is called on a closed Statement or | |
1238 * the condition max >= 0 is not satisfied | |
1239 * @see #getLargeMaxRows() | |
1240 */ | |
1241 @Override | |
1242 public void setLargeMaxRows(final long max) throws SQLException { | |
1243 if (max < 0) | |
1244 throw new SQLException("Illegal max value: " + max, "M1M05"); | |
1245 maxRows = max; | |
1246 } | |
1247 | |
1248 /** | |
1249 * Retrieves the maximum number of rows that a ResultSet object produced by | |
1250 * this Statement object can contain. If this limit is exceeded, the excess | |
1251 * rows are silently dropped. | |
1252 * | |
1253 * This method should be used when the returned row limit may exceed Integer.MAX_VALUE. | |
1254 * The default implementation will return 0 | |
1255 * | |
1256 * @return the current maximum number of rows for a ResultSet object | |
1257 * produced by this Statement object; zero means there is no limit | |
1258 * @see #setLargeMaxRows(long max) | |
1259 */ | |
1260 @Override | |
1261 public long getLargeMaxRows() { | |
1262 return maxRows; | |
1263 } | |
1264 | |
1265 /** | |
1266 * Submits a batch of commands to the database for execution and if | |
1267 * all commands execute successfully, returns an array of update counts. | |
1268 * The long elements of the array that is returned are ordered to | |
1269 * correspond to the commands in the batch, which are ordered according | |
1270 * to the order in which they were added to the batch. | |
1271 * The elements in the array returned by the method executeLargeBatch | |
1272 * may be one of the following: | |
1273 * <ol> | |
1274 * <li>A number greater than or equal to zero -- indicates that the command | |
1275 * was processed successfully and is an update count giving the number of | |
1276 * rows in the database that were affected by the command's execution</li> | |
1277 * <li>A value of SUCCESS_NO_INFO -- indicates that the command was | |
1278 * processed successfully but that the number of rows affected is unknown</li> | |
1279 * If one of the commands in a batch update fails to execute properly, | |
1280 * this method throws a BatchUpdateException, and a JDBC driver may or | |
1281 * may not continue to process the remaining commands in the batch. | |
1282 * However, the driver's behavior must be consistent with a particular DBMS, | |
1283 * either always continuing to process commands or never continuing to process | |
1284 * commands. If the driver continues processing after a failure, the array | |
1285 * returned by the method BatchUpdateException.getLargeUpdateCounts will | |
1286 * contain as many elements as there are commands in the batch, and at | |
1287 * least one of the elements will be the following: | |
1288 * <li>A value of EXECUTE_FAILED -- indicates that the command failed to | |
1289 * execute successfully and occurs only if a driver continues to process | |
1290 * commands after a command fails</li> | |
1291 * </ol> | |
1292 * | |
1293 * This method should be used when the returned row count may exceed Integer.MAX_VALUE. | |
1294 * The default implementation will throw UnsupportedOperationException | |
1295 * | |
1296 * MonetDB does continues after an error has occurred in the batch. | |
1297 * If one of the commands attempts to return a result set, an | |
1298 * SQLException is added to the SQLException list and thrown | |
1299 * afterwards execution. Failing queries result in SQLExceptions | |
1300 * too and may cause subparts of the batch to fail as well. | |
1301 * | |
1302 * @return an array of update counts containing one element for each | |
1303 * command in the batch. The elements of the array are ordered | |
1304 * according to the order in which commands were added to the batch. | |
1305 * @throws SQLException if a database access error occurs, this method is called | |
1306 * on a closed Statement or the driver does not support batch statements. | |
1307 * Throws BatchUpdateException (a subclass of SQLException) if one of the | |
1308 * commands sent to the database fails to execute properly or attempts to return a result set. | |
1309 */ | |
1310 @Override | |
1311 public long[] executeLargeBatch() throws SQLException { | |
1312 if (batch == null || batch.isEmpty()) { | |
1313 return new long[0]; | |
1314 } | |
1315 | |
1316 final int[] ret = executeBatch(); | |
1317 // copy contents of int[] into new long[] | |
1318 final long[] counts = new long[ret.length]; | |
1319 for (int i = 0; i < ret.length; i++) { | |
1320 counts[i] = ret[i]; | |
1321 } | |
1322 return counts; | |
1323 } | |
1324 | |
1325 /** | |
1326 * Executes the given SQL statement, which may be an INSERT, UPDATE, or | |
1327 * DELETE statement or an SQL statement that returns nothing, such as an | |
1328 * SQL DDL statement. | |
1329 * | |
1330 * This method should be used when the returned row count may exceed Integer.MAX_VALUE. | |
1331 * Note: This method cannot be called on a PreparedStatement or CallableStatement. | |
1332 * The default implementation will throw SQLFeatureNotSupportedException | |
1333 * | |
1334 * @param sql an SQL Data Manipulation Language (DML) statement, such as | |
1335 * INSERT, UPDATE or DELETE; or an SQL statement that returns nothing, | |
1336 * such as a DDL statement. | |
1337 * @return either (1) the row count for SQL Data Manipulation Language (DML) statements | |
1338 * or (2) 0 for SQL statements that return nothing | |
1339 * @throws SQLException if a database access error occurs, this method is | |
1340 * called on a closed Statement, the given SQL statement produces a | |
1341 * ResultSet object, the method is called on a PreparedStatement or CallableStatement | |
1342 */ | |
1343 @Override | |
1344 public long executeLargeUpdate(final String sql) throws SQLException { | |
1345 if (execute(sql) != false) | |
1346 throw new SQLException("Query produced a result set", "M1M17"); | |
1347 | |
1348 return getLargeUpdateCount(); | |
1349 } | |
1350 | |
1351 /** | |
1352 * Executes the given SQL statement and signals the driver with the | |
1353 * given flag about whether the auto-generated keys produced by this | |
1354 * Statement object should be made available for retrieval. | |
1355 * The driver will ignore the flag if the SQL statement is not an INSERT | |
1356 * statement, or an SQL statement able to return auto-generated keys | |
1357 * (the list of such statements is vendor-specific). | |
1358 * | |
1359 * This method should be used when the returned row count may exceed Integer.MAX_VALUE. | |
1360 * Note: This method cannot be called on a PreparedStatement or CallableStatement. | |
1361 * The default implementation will throw SQLFeatureNotSupportedException | |
1362 * | |
1363 * @param sql an SQL Data Manipulation Language (DML) statement, such as | |
1364 * INSERT, UPDATE or DELETE; or an SQL statement that returns nothing, | |
1365 * such as a DDL statement. | |
1366 * @param autoGeneratedKeys - a flag indicating whether | |
1367 * auto-generated keys should be made available for | |
1368 * retrieval; one of the following constants: | |
1369 * Statement.RETURN_GENERATED_KEYS | |
1370 * Statement.NO_GENERATED_KEYS | |
1371 * @return either (1) the row count for SQL Data Manipulation Language (DML) statements | |
1372 * or (2) 0 for SQL statements that return nothing | |
1373 * @throws SQLException if a database access error occurs, this method is | |
1374 * called on a closed Statement, the given SQL statement produces a | |
1375 * ResultSet object, the given constant is not one of those allowed, | |
1376 * the method is called on a PreparedStatement or CallableStatement | |
1377 */ | |
1378 @Override | |
1379 public long executeLargeUpdate(final String sql, final int autoGeneratedKeys) | |
1380 throws SQLException | |
1381 { | |
1382 if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && | |
1383 autoGeneratedKeys != Statement.NO_GENERATED_KEYS) | |
1384 throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); | |
1385 | |
1386 /* MonetDB has no way to disable this, so just do the normal thing ;) */ | |
1387 if (execute(sql) != false) | |
1388 throw new SQLException("Query produced a result set", "M1M17"); | |
1389 | |
1390 return getLargeUpdateCount(); | |
1391 } | |
1392 | |
1393 /** | |
1394 * Executes the given SQL statement and signals the driver that the | |
1395 * auto-generated keys indicated in the given array should be made | |
1396 * available for retrieval. The driver will ignore the array if the | |
1397 * SQL statement is not an INSERT statement. | |
1398 * | |
1399 * This method should be used when the returned row count may exceed Integer.MAX_VALUE. | |
1400 * Note: This method cannot be called on a PreparedStatement or CallableStatement. | |
1401 * The default implementation will throw SQLFeatureNotSupportedException | |
1402 * | |
1403 * MonetDB only supports returing the generated key for one column, | |
1404 * which will be the first column that has a serial. Hence, this | |
1405 * method cannot work as required and the driver will fall back to | |
1406 * executing with request to the database to return the generated | |
1407 * key, if any. | |
1408 * | |
1409 * @param sql an SQL Data Manipulation Language (DML) statement, such as | |
1410 * INSERT, UPDATE or DELETE; or an SQL statement that returns nothing, | |
1411 * such as a DDL statement. | |
1412 * @param columnIndexes an array of column indexes indicating the | |
1413 * columns that should be returned from the inserted row | |
1414 * @return either (1) the row count for SQL Data Manipulation Language (DML) statements | |
1415 * or (2) 0 for SQL statements that return nothing | |
1416 * @throws SQLException if a database access error occurs, this method is | |
1417 * called on a closed Statement, the given SQL statement produces a | |
1418 * ResultSet object, the second argument supplied to this method is | |
1419 * not an int array whose elements are valid column indexes, | |
1420 * the method is called on a PreparedStatement or CallableStatement | |
1421 */ | |
1422 @Override | |
1423 public long executeLargeUpdate(final String sql, final int[] columnIndexes) | |
1424 throws SQLException | |
1425 { | |
1426 addWarning("executeLargeUpdate: generated keys for fixed set of columns not supported", "01M18"); | |
1427 return executeLargeUpdate(sql, Statement.RETURN_GENERATED_KEYS); | |
1428 } | |
1429 | |
1430 /** | |
1431 * Executes the given SQL statement and signals the driver that the | |
1432 * auto-generated keys indicated in the given array should be made | |
1433 * available for retrieval. The driver will ignore the array if the | |
1434 * SQL statement is not an INSERT statement. | |
1435 * | |
1436 * This method should be used when the returned row count may exceed Integer.MAX_VALUE. | |
1437 * Note: This method cannot be called on a PreparedStatement or CallableStatement. | |
1438 * The default implementation will throw SQLFeatureNotSupportedException | |
1439 * | |
1440 * MonetDB only supports returing the generated key for one column, | |
1441 * which will be the first column that has a serial. Hence, this | |
1442 * method cannot work as required and the driver will fall back to | |
1443 * executing with request to the database to return the generated | |
1444 * key, if any. | |
1445 * | |
1446 * @param sql an SQL Data Manipulation Language (DML) statement, such as | |
1447 * INSERT, UPDATE or DELETE; or an SQL statement that returns nothing, | |
1448 * such as a DDL statement. | |
1449 * @param columnNames an array of the names of the columns that | |
1450 * should be returned from the inserted row | |
1451 * @return either (1) the row count for SQL Data Manipulation Language (DML) statements | |
1452 * or (2) 0 for SQL statements that return nothing | |
1453 * @throws SQLException if a database access error occurs, this method is | |
1454 * called on a closed Statement, the given SQL statement produces a | |
1455 * ResultSet object, the second argument supplied to this method is | |
1456 * not a String array whose elements are valid column names, | |
1457 * the method is called on a PreparedStatement or CallableStatement | |
1458 */ | |
1459 @Override | |
1460 public long executeLargeUpdate(final String sql, final String[] columnNames) | |
1461 throws SQLException | |
1462 { | |
1463 addWarning("executeLargeUpdate: generated keys for fixed set of columns not supported", "01M18"); | |
1464 return executeLargeUpdate(sql, Statement.RETURN_GENERATED_KEYS); | |
1465 } | |
1466 | |
1467 //== end methods of interface Statement | |
1468 | |
1469 | |
1470 //== internal helper methods which do not belong to the JDBC interface | |
1471 | |
1472 /** | |
1473 * Adds a warning to the pile of warnings this Statement object has. If | |
1474 * there were no warnings (or clearWarnings was called) this warning will | |
1475 * be the first, otherwise this warning will get appended to the current | |
1476 * warning. | |
1477 * | |
1478 * @param reason the warning message | |
1479 */ | |
1480 private void addWarning(final String reason, final String sqlstate) { | |
1481 SQLWarning warng = new SQLWarning(reason, sqlstate); | |
1482 if (warnings == null) { | |
1483 warnings = warng; | |
1484 } else { | |
1485 warnings.setNextWarning(warng); | |
1486 } | |
1487 } | |
1488 | |
1489 /** | |
1490 * Closes this Statement if there are no more open ResultSets | |
1491 * (Responses). Called by MonetResultSet.close(). | |
1492 */ | |
1493 void closeIfCompletion() { | |
1494 if (!closeOnCompletion || lastResponseList == null) | |
1495 return; | |
1496 if (!lastResponseList.hasUnclosedResponses()) | |
1497 close(); | |
1498 } | |
1499 } | |
1500 | |
1501 /** | |
1502 * This internal subclass is not intended for normal use. Therefore it is restricted to | |
1503 * classes from the very same package only. | |
1504 * | |
1505 * Known issues with this class: some methods of the ResultSetMetaData object (obtained via getMetaData()) | |
1506 * require that its statement argument (accessed via getStatement()) has a valid Statement object set. | |
1507 * Instances of this subclass do not have a valid Statement (see special constructor), so | |
1508 * those metadata methods do not return the correct values. | |
1509 * Special checks are programmed to prevent NullPointerExceptions, see above. | |
1510 * | |
1511 * As of Jun2016 this class is only used by MonetStatement.getGeneratedKeys() | |
1512 * Note: to resolve a javac -Xlint warning, this class definition is moved to this file. | |
1513 * | |
1514 * TODO: try to eliminate the need for this class completely. | |
1515 */ | |
1516 final class MonetVirtualResultSet extends MonetResultSet { | |
1517 private String results[][]; | |
1518 private boolean closed; | |
1519 | |
1520 MonetVirtualResultSet( | |
1521 final Statement statement, | |
1522 final String[] columns, | |
1523 final String[] types, | |
1524 final String[][] results | |
1525 ) throws IllegalArgumentException { | |
1526 super(statement, columns, types, results.length); | |
1527 this.results = results; | |
1528 closed = false; | |
1529 } | |
1530 | |
1531 /** | |
1532 * This method is overridden in order to let it use the results array | |
1533 * instead of the cache in the Statement object that created it. | |
1534 * | |
1535 * @param row the number of the row to which the cursor should move. A | |
1536 * positive number indicates the row number counting from the | |
1537 * beginning of the result set; a negative number indicates the row | |
1538 * number counting from the end of the result set | |
1539 * @return true if the cursor is on the result set; false otherwise | |
1540 * @throws SQLException if a database error occurs | |
1541 */ | |
1542 @Override | |
1543 public boolean absolute(int row) throws SQLException { | |
1544 if (closed) | |
1545 throw new SQLException("ResultSet is closed!", "M1M20"); | |
1546 | |
1547 // first calculate what the JDBC row is | |
1548 if (row < 0) { | |
1549 // calculate the negatives... | |
1550 row = (int) tupleCount + row + 1; | |
1551 } | |
1552 // now place the row not farther than just before or after the result | |
1553 if (row < 0) | |
1554 row = 0; // before first | |
1555 else if (row > tupleCount + 1) | |
1556 row = (int) tupleCount + 1; // after last | |
1557 | |
1558 // store it | |
1559 curRow = row; | |
1560 | |
1561 // see if we have the row | |
1562 if (row < 1 || row > tupleCount) | |
1563 return false; | |
1564 | |
1565 for (int i = 0; i < results[row - 1].length; i++) { | |
1566 tlp.values[i] = results[row - 1][i]; | |
1567 } | |
1568 | |
1569 return true; | |
1570 } | |
1571 | |
1572 /** | |
1573 * Mainly here to prevent errors when the close method is called. There | |
1574 * is no real need for this object to close it. We simply remove our | |
1575 * resultset data. | |
1576 */ | |
1577 @Override | |
1578 public void close() { | |
1579 if (!closed) { | |
1580 closed = true; | |
1581 results = null; | |
1582 // types and columns are MonetResultSets private parts | |
1583 } | |
1584 } | |
1585 } |