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