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() &amp;&amp; (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() &amp;&amp; (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 &lt;= rows &lt;= 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 &gt;= 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 &gt;= 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 }