comparison src/main/java/org/monetdb/jdbc/MonetStatement.java @ 391:f523727db392

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