Mercurial > hg > monetdb-java
changeset 172:60063c67f9e7 embedded
Merged with default
author | Pedro Ferreira <pedro.ferreira@monetdbsolutions.com> |
---|---|
date | Tue, 19 Sep 2017 13:49:34 +0200 (2017-09-19) |
parents | 0f95fee3cf29 (current diff) 296c4a16ef9f (diff) |
children | 89c285fc0a49 |
files | build.properties pom.xml src/main/java/nl/cwi/monetdb/client/JdbcClient.java src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java tests/Bug_PrepStmtSetString_6382.java tests/Test_PSmetadata.java tests/build.xml |
diffstat | 25 files changed, 3094 insertions(+), 2485 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags +++ b/.hgtags @@ -1,1 +1,3 @@ 80de05f07508fec938845b4a6e05f600bf0b48c0 v2.24 +c43c293f3d5841517cbe0d858108c4da5fb1ec0c v2.26 +a6a2f4ee2d42d7e192f9d8d37f79ea99178d7f2c v2.25
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ # ChangeLog file for monetdb-java # This file is updated with Maddlog +* Thu Sep 7 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Implemented PreparedStatement methods setNCharacterStream(int, Reader) + and setNCharacterStream(int, Reader, long). + +* Thu Aug 31 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Corrected PreparedStatement methods setString(int, String) + and setObject(int, Object, ...) in case the target parameter + data type was json or inet or url or uuid. See also + https://www.monetdb.org/bugzilla/show_bug.cgi?id=6382 + +* Thu Aug 24 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Implemented PreparedStatement method setURL(int, URL). +- Implemented PreparedStatement method setNString(int, String). +- The MonetDB JDBC driver code and jdbcclient program are now compiled + without debug info and with optimise flag enabled. The new jar files are + now smaller in size. + +* Thu Aug 17 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Implemented ResultSet method getNCharacterStream(). +- In class MonetClob implemented methods getCharacterStream() + and getCharacterStream(long pos, long length). Method + getCharacterStream() is called by DBeaver to fetch the Clob value. + It used to throw a SQLFeatureNotSupportedException with message: + "Method getCharacterStream() currently not supported". This caused + DBeaver to log the exception and show NULL as the value on screen, + which is incorrect. This has been fixed. + +* Fri Jul 28 2017 Sjoerd Mullender <sjoerd@acm.org> +- Compiled and released new jars: monetdb-jdbc-2.26.jar and updated jdbcclient.jar + +* Thu Jul 13 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Corrected implementation of PreparedStatement method + setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) + for the case the targetSqlType is Types.CLOB. + See also: https://www.monetdb.org/bugzilla/show_bug.cgi?id=6349 +
--- a/ChangeLog-Archive +++ b/ChangeLog-Archive @@ -1,6 +1,25 @@ # DO NOT EDIT THIS FILE -- MAINTAINED AUTOMATICALLY # This file contains past monetdb-java ChangeLog entries +* Fri Jul 28 2017 Sjoerd Mullender <sjoerd@acm.org> +- Compiled and released new jars: monetdb-jdbc-2.26.jar and updated jdbcclient.jar + +* Thu Jul 13 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Corrected implementation of PreparedStatement method + setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) + for the case the targetSqlType is Types.CLOB. + See also: https://www.monetdb.org/bugzilla/show_bug.cgi?id=6349 + +* Thu Mar 30 2017 Sjoerd Mullender <sjoerd@acm.org> +- Compiled and released new jars: monetdb-jdbc-2.25.jar, monetdb-mcl-1.15.jar + and updated jdbcclient.jar + +* Thu Mar 9 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Corrected ResultSetMetaData methods getColumnLabel(int), getColomnName(int), + getTableName(int) and getSchemaName(int) for names which contain special + characters such as a space, a tab, a comma, a double quote, etc. + See also: https://www.monetdb.org/bugzilla/show_bug.cgi?id=6183 + * Wed Dec 21 2016 Sjoerd Mullender <sjoerd@acm.org> - Compiled and released new jars: monetdb-jdbc-2.24.jar, monetdb-mcl-1.14.jar and updated jdbcclient.jar
--- a/SQLSTATEs +++ b/SQLSTATEs @@ -1,56 +1,71 @@ -08M01 opening logfile failed +Following SQLStates are MonetDB JDBC driver specific: 01M02 redirect warning -M0M03 illegal arguments (invalid call of internal function) 01M03 illegal arguments (invalid call of internal function) -M0M04 only supported in SQL mode -M1M05 invalid argument (user supplied) -M0M06 savepoint is not MonetSavepoint 01M07 unrecognised clientinfo property 01M08 read-only conection mode not supported 01M09 transaction mode not supported -M0M10 protocol violation/unexpected server response 01M10 unexpected server output 01M11 server-client autocommit state mismatch -M0M12 matching client handle referenced by server not found 01M13 concurrency mode not supported 01M14 scrolling mode not supported 01M15 holdability mode not supported -M1M16 multistatements not supported in batches -M1M17 result set not expected for DML or DDL-statement 01M18 generated keys for columns not supported -M1M19 response is not a result set -M1M20 object closed 01M21 cursors not supported -0AM21 cursors not supported 01M22 JDBC escape syntax not supported 01M23 field size limitation not supported 01M24 query time out not supported -M1M25 failed reading from/writing to object stream + +08M01 opening logfile failed 08M26 invalid URI -M0M27 unknown error +08M33 connection timed out + +0AM21 cursors not supported +0AM34 Java generics not supported + 22M28 invalid BLOB format -M0M29 assert +22M29 invalid inet format +22M30 invalid url format +22M31 invalid uuid format +22M32 invalid json format + +2BM37 dependent objects still exist +2DM30 autocommit mode active 3BM30 autocommit mode active -2DM30 autocommit mode active + 42M31 user/role already exists 42M32 user/role not found -08M33 connection timeout -0AM34 Java generics not supported 42M35 sequence not found 42M36 cannot restart sequence with NULL -2BM37 dependent objects still exist + +M0M03 illegal arguments (invalid call of internal function) +M0M04 only supported in SQL mode +M0M06 savepoint is not MonetSavepoint +M0M10 protocol violation/unexpected server response +M0M12 matching client handle referenced by server not found +M0M27 unknown error +M0M29 assert + +M1M05 invalid argument (user supplied) +M1M16 multistatements not supported in batches +M1M17 result set not expected for DML or DDL-statement +M1M19 response is not a result set +M1M20 object closed +M1M25 failed reading from/writing to object stream -TODO: -JDBC 4.1 suggests the following SQLException subclass mapping: -NonTransientSQLExeceptions (fails when same operation executed again) -0A SQLFeatureNotSupportedException -08 SQLNonTransientConnectionException -22 SQLDataException -23 SQLIntegrityConstraintViolationException -28 SQLInvalidAuthorizationException -42 SQLSyntaxErrorException -TransientSQLExeceptions (retry of same operation might succeed) -08 SQLTransientConnectionException -40 SQLTransactionRollbackException - SQLTimeoutException +SQLState codes are used in SQLExceptions. +JDBC 4.1 defines the following SQLException subclass mappings: + NonTransientSQLExceptions (fails when same operation executed again) + 0A SQLFeatureNotSupportedException + 08 SQLNonTransientConnectionException + 22 SQLDataException + 23 SQLIntegrityConstraintViolationException + 28 SQLInvalidAuthorizationSpecException + 42 SQLSyntaxErrorException + TransientSQLExeceptions (retry of same operation might succeed) + 08 SQLTransientConnectionException + 40 SQLTransactionRollbackException + null SQLTimeoutException +See also: http://docs.oracle.com/javase/7/docs/api/java/sql/SQLException.html +See also: https://en.wikibooks.org/wiki/Structured_Query_Language/SQLSTATE +
--- a/release.txt +++ b/release.txt @@ -1,6 +1,6 @@ RELEASE NOTES -MonetDB JDBC driver version 2.24 (Liberica/MCL-1.14) -Release date: 2016-12-15 +MonetDB JDBC driver version 2.26 (Liberica/MCL-1.15) +Release date: 2017-07-28 This JDBC driver is designed for use with MonetDB, a main-memory database. For more information see https://www.monetdb.org/. @@ -55,8 +55,8 @@ Currently implemented JDBC 4.1 interface - setArray - setAsciiStream, setBinaryStream, setUnicodeStream - setBlob - - setNCharacterStream, setNClob, setNString - - setRef, setRowId, setSQLXML, setURL + - setNClob + - setRef, setRowId, setSQLXML * java.sql.ParameterMetaData @@ -64,9 +64,9 @@ Currently implemented JDBC 4.1 interface The next features/methods are NOT implemented: - getArray - getAsciiStream, getUnicodeStream - - getNCharacterStream, getNClob, getNString + - getNClob - getRef, getRowId, getSQLXML - - all methods related to updateable result sets + - All methods related to updateable result sets * java.sql.ResultSetMetaData @@ -83,10 +83,13 @@ Currently implemented JDBC 4.1 interface A simple implementation using a StringBuilder to store the whole CLOB The next features/methods are NOT implemented: - getAsciiStream - - getCharacterStream - setAsciiStream - setCharacterStream + * java.sql.SQLData + implemented by class: nl.cwi.monetdb.jdbc.types.INET + and by class: nl.cwi.monetdb.jdbc.types.URL + * javax.sql.DataSource (not tested) @@ -96,7 +99,6 @@ The next java.sql.* interfaces are NOT i * java.sql.NClob * java.sql.Ref * java.sql.Rowid - * java.sql.SQLData * java.sql.SQLInput * java.sql.SQLOutput * java.sql.SQLXML
--- a/src/main/java/nl/cwi/monetdb/client/JdbcClient.java +++ b/src/main/java/nl/cwi/monetdb/client/JdbcClient.java @@ -54,11 +54,11 @@ import java.util.zip.GZIPInputStream; public final class JdbcClient { private static Connection con; + private static DatabaseMetaData dbmd; private static Statement stmt; private static BufferedReader in; private static PrintWriter out; private static Exporter exporter; - private static DatabaseMetaData dbmd; public static void main(String[] args) throws Exception { CmdLineOpts copts = new CmdLineOpts(); @@ -158,20 +158,21 @@ public final class JdbcClient { System.exit(1); } // we can actually compare pointers (objects) here - if (!Objects.equals(user, copts.getOption("user").getArgument())) pass = null; + if (user != copts.getOption("user").getArgument()) + pass = null; if (copts.getOption("help").isPresent()) { System.out.print( "Usage java -jar jdbcclient.jar\n" + - " [-h host[:port]] [-p port] [-f file] [-u user]\n" + - " [-l language] [-d database] [-e] [-D [table]]\n" + - " [-X<opt>]\n" + + "\t\t[-h host[:port]] [-p port] [-f file] [-u user]\n" + + "\t\t[-l language] [-d database] [-e] [-D [table]]\n" + + "\t\t[-X<opt>]\n" + "or using long option equivalents --host --port --file --user --language\n" + "--dump --echo --database.\n" + "Arguments may be written directly after the option like -p50000.\n" + "\n" + - "If no host and port are given, localhost and 50000 are assumed. An .monetdb\n" + - "file may exist in the user's home directory. This file can contain\n" + + "If no host and port are given, localhost and 50000 are assumed.\n" + + "An .monetdb file may exist in the user's home directory. This file can contain\n" + "preferences to use each time JdbcClient is started. Options given on the\n" + "command line override the preferences file. The .monetdb file syntax is\n" + "<option>=<value> where option is one of the options host, port, file, mode\n" + @@ -219,7 +220,8 @@ public final class JdbcClient { host = host + ":" + copts.getOption("port").getArgument(); } - //Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); + // make sure the driver is loaded + // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers // build the extra arguments of the JDBC connect string String attr = "?"; @@ -227,12 +229,15 @@ public final class JdbcClient { String lang = oc.getArgument(); if (oc.isPresent()) attr += "language=" + lang + "&"; + +/* Xquery is no longer functional or supported // set some behaviour based on the language XQuery if (lang.equals("xquery")) { scolonterm = false; // no ; to end a statement if (!copts.getOption("Xoutput").isPresent()) xmlMode = true; // the user will like xml results, most probably } +*/ oc = copts.getOption("Xdebug"); if (oc.isPresent()) { attr += "debug=true&"; @@ -273,6 +278,7 @@ public final class JdbcClient { // SQL language dbmd = null; } + stmt = con.createStatement(); // see if we will have to perform a database dump (only in SQL mode) @@ -295,9 +301,9 @@ public final class JdbcClient { List<Table> tables = new LinkedList<>(); while (tbl.next()) { tables.add(new Table( - tbl.getString("TABLE_SCHEM"), - tbl.getString("TABLE_NAME"), - tbl.getString("TABLE_TYPE"))); + tbl.getString(2), // 2 = "TABLE_SCHEM" + tbl.getString(3), // 3 = "TABLE_NAME" + tbl.getString(4))); // 4 = "TABLE_TYPE" } tbl.close(); @@ -329,23 +335,21 @@ public final class JdbcClient { } } } else { - /* this returns everything, so including SYSTEM TABLE - * constraints */ + /* this returns everything, so including SYSTEM TABLE constraints */ tbl = dbmd.getImportedKeys(null, null, null); while (tbl.next()) { - // find FK table object - Table fk = Table.findTable(tbl.getString("FKTABLE_SCHEM") + "." + tbl.getString("FKTABLE_NAME"), tables); + // find FK table object 6 = "FKTABLE_SCHEM", 7 = "FKTABLE_NAME" + Table fk = Table.findTable(tbl.getString(6) + "." + tbl.getString(7), tables); - // find PK table object - Table pk = Table.findTable(tbl.getString("PKTABLE_SCHEM") + "." + tbl.getString("PKTABLE_NAME"), tables); + // find PK table object 2 = "PKTABLE_SCHEM", 3 = "PKTABLE_NAME" + Table pk = Table.findTable(tbl.getString(2) + "." + tbl.getString(3), tables); - // this happens when a system table has referential - // constraints + // this happens when a system table has referential constraints if (fk == null || pk == null) continue; - // add PK table dependancy to FK table - fk.addDependancy(pk); + // add PK table dependency to FK table + fk.addDependency(pk); } tbl.close(); @@ -473,8 +477,7 @@ public final class JdbcClient { * * @param uri URL or filename as String * @return a BufferedReader for the uri - * @throws Exception if uri cannot be identified as a valid URL or - * file + * @throws Exception if uri cannot be identified as a valid URL or file */ static BufferedReader getReader(String uri) throws Exception { BufferedReader ret = null; @@ -611,40 +614,54 @@ public final class JdbcClient { out.println("\\q quits this program"); out.println("\\h this help screen"); if (dbmd != null) - out.println("\\d list available tables and views"); + out.println("\\d list available tables and views in current schema"); out.println("\\d<obj> describes the given table or view"); out.println("\\l<uri> executes the contents of the given file or URL"); out.println("\\i<uri> batch executes the inserts from the given file or URL"); } else if (dbmd != null && command.startsWith("\\d")) { + ResultSet tbl = null; try { - String object = command.substring(2).trim().toLowerCase(); + String object = command.substring(2).trim(); if (scolonterm && object.endsWith(";")) object = object.substring(0, object.length() - 1); - if (!object.isEmpty()) { + if (object.isEmpty()) { + // list available tables and views in current schema + String current_schema = con.getSchema(); + tbl = dbmd.getTables(null, current_schema, null, null); + + // give us a list of all non-system tables and views (including temp ones) + while (tbl.next()) { + String tableType = tbl.getString(4); // 4 = "TABLE_TYPE" + if (tableType != null && !tableType.startsWith("SYSTEM ")) + out.println(tableType + "\t" + + tbl.getString(2) + "." + // 2 = "TABLE_SCHEM" + tbl.getString(3)); // 3 = "TABLE_NAME" + } + tbl.close(); + tbl = null; + } else { + // describes the given table or view String schema; + String obj_nm = object; + boolean found = false; int dot = object.indexOf("."); - if (dot != -1) { - // use provided schema + if (dot > 0) { + // use specified schema schema = object.substring(0, dot); - object = object.substring(dot + 1); + obj_nm = object.substring(dot + 1); } else { // use current schema schema = con.getSchema(); } - ResultSet tbl = dbmd.getTables(null, schema, null, null); - - // we have an object, see if we can find it - boolean found = false; - while (tbl.next()) { - String tableName = tbl.getString("TABLE_NAME"); - String schemaName = tbl.getString("TABLE_SCHEM"); - if ((dot == -1 && tableName.equalsIgnoreCase(object)) || - (schemaName + "." + tableName).equalsIgnoreCase(object)) - { + tbl = dbmd.getTables(null, schema, obj_nm, null); + while (tbl.next() && !found) { + String tableName = tbl.getString(3); // 3 = "TABLE_NAME" + String schemaName = tbl.getString(2); // 2 = "TABLE_SCHEM" + if (obj_nm.equals(tableName) && schema.equals(schemaName)) { // we found it, describe it exporter.dumpSchema(dbmd, - tbl.getString("TABLE_TYPE"), - tbl.getString("TABLE_CAT"), + tbl.getString(4), // 4 = "TABLE_TYPE" + tbl.getString(1), // 1 = "TABLE_CAT" schemaName, tableName); @@ -653,22 +670,10 @@ public final class JdbcClient { } } tbl.close(); + tbl = null; if (!found) System.err.println("Unknown table or view: " + object); - } else { - String current_schema = con.getSchema(); - ResultSet tbl = dbmd.getTables(null, current_schema, null, null); - - // give us a list of all non-system tables and views (including temp ones) - while (tbl.next()) { - String tableType = tbl.getString("TABLE_TYPE"); - if (tableType != null && !tableType.startsWith("SYSTEM ")) - out.println(tableType + "\t" + - tbl.getString("TABLE_SCHEM") + "." + - tbl.getString("TABLE_NAME")); - } - tbl.close(); } } catch (SQLException e) { out.flush(); @@ -676,6 +681,9 @@ public final class JdbcClient { System.err.println("Error [" + e.getSQLState() + "]: " + e.getMessage()); // print all error messages in the chain (if any) } while ((e = e.getNextException()) != null); + } finally { + if (tbl != null) + tbl.close(); } } else if (command.startsWith("\\l") || command.startsWith("\\i")) { String object = command.substring(2).trim(); @@ -845,7 +853,7 @@ public final class JdbcClient { * is sent to the server as-is. * * @param batchSize the number of items to store in the batch before - * sending them to the database for execution. + * sending them to the database for execution. * @throws IOException if an IO exception occurs. */ public static void processBatch(int batchSize) throws IOException { @@ -1120,12 +1128,12 @@ class Table { this.fqname = schem + "." + name; } - void addDependancy(Table dependsOn) throws Exception { + void addDependency(Table dependsOn) throws Exception { if (this.fqname.equals(dependsOn.fqname)) - throw new Exception("Cyclic dependancy graphs are not supported (foreign key relation references self)"); + throw new Exception("Cyclic dependency graphs are not supported (foreign key relation references self)"); if (dependsOn.needs.contains(this)) - throw new Exception("Cyclic dependancy graphs are not supported (foreign key relation a->b and b->a)"); + throw new Exception("Cyclic dependency graphs are not supported (foreign key relation a->b and b->a)"); if (!needs.contains(dependsOn)) needs.add(dependsOn); @@ -1148,18 +1156,10 @@ class Table { return schem; } - String getSchemQ() { - return JdbcClient.dq(schem); - } - String getName() { return name; } - String getNameQ() { - return JdbcClient.dq(name); - } - String getType() { return type; } @@ -1169,7 +1169,7 @@ class Table { } String getFqnameQ() { - return getSchemQ() + "." + getNameQ(); + return JdbcClient.dq(schem) + "." + JdbcClient.dq(name); } public String toString() { @@ -1190,7 +1190,7 @@ class Table { for (int i = 0; i < table.needs.size(); i++) { Table child = table.needs.get(i); if (parents.contains(child)) - throw new Exception("Cyclic dependancy graphs are not supported (cycle detected for " + child.fqname + ")"); + throw new Exception("Cyclic dependency graphs are not supported (cycle detected for " + child.fqname + ")"); checkForLoop(child, parents); } }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java @@ -39,6 +39,24 @@ public class MonetBlob implements Blob, return buffer; } + static MonetBlob create(String in) { + // unpack the HEX (BLOB) notation to real bytes + int len = in.length() / 2; + byte[] buf = new byte[len]; + int offset; + for (int i = 0; i < len; i++) { + offset = 2 * i; + buf[i] = (byte)Integer.parseInt(in.substring(offset, offset + 2), 16); + } + return new MonetBlob(buf); + } + + /* internal utility method */ + private void checkBufIsNotNull() throws SQLException { + if (buffer == null) + throw new SQLException("This MonetBlob has been freed", "M1M20"); + } + //== begin interface Blob /** @@ -61,8 +79,7 @@ public class MonetBlob implements Blob, */ @Override public InputStream getBinaryStream() throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); + checkBufIsNotNull(); return new ByteArrayInputStream(buffer); } @@ -79,8 +96,7 @@ public class MonetBlob implements Blob, */ @Override public InputStream getBinaryStream(long pos, long length) throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); + checkBufIsNotNull(); if (pos < 1) throw new SQLException("pos is less than 1", "M1M05"); if (pos - 1 > buffer.length) @@ -103,10 +119,9 @@ public class MonetBlob implements Blob, */ @Override public byte[] getBytes(long pos, int length) throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); + checkBufIsNotNull(); try { - return Arrays.copyOfRange(buffer, (int) pos - 1, (int) pos - 1 + length); + return java.util.Arrays.copyOfRange(buffer, (int) pos - 1, (int) pos - 1 + length); } catch (IndexOutOfBoundsException e) { throw new SQLException(e.getMessage(), "M0M10"); } @@ -120,9 +135,8 @@ public class MonetBlob implements Blob, */ @Override public long length() throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); - return (long) buffer.length; + checkBufIsNotNull(); + return (long)buffer.length; } /** @@ -150,8 +164,7 @@ public class MonetBlob implements Blob, */ @Override public long position(byte[] pattern, long start) throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); + checkBufIsNotNull(); try { for (int i = (int)(start - 1); i < buffer.length - pattern.length; i++) { int j; @@ -184,12 +197,7 @@ public class MonetBlob implements Blob, */ @Override public OutputStream setBinaryStream(long pos) throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); - if (pos < 1) - throw new SQLException("pos is less than 1", "M1M05"); - throw new SQLFeatureNotSupportedException("Operation setBinaryStream(long pos) currently not supported", - "0A000"); + throw new SQLFeatureNotSupportedException("Method setBinaryStream(long pos) not supported", "0A000"); } /** @@ -222,8 +230,7 @@ public class MonetBlob implements Blob, */ @Override public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); + checkBufIsNotNull(); try { /* transactions? what are you talking about? */ System.arraycopy(bytes, offset - 1 + (int) pos, buffer, (int) pos, len - (int) pos); @@ -241,8 +248,7 @@ public class MonetBlob implements Blob, */ @Override public void truncate(long len) throws SQLException { - if (buffer == null) - throw new SQLException("This Blob object has been freed", "M1M20"); + checkBufIsNotNull(); if (buffer.length > len) { byte[] newbuf = new byte[(int)len]; System.arraycopy(buffer, 0, newbuf, 0, (int) len);
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java @@ -11,6 +11,7 @@ package nl.cwi.monetdb.jdbc; import java.io.*; import java.sql.Clob; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; /** * The MonetClob class implements the {@link java.sql.Clob} interface. Because @@ -24,19 +25,28 @@ import java.sql.SQLException; * @author Fabian Groffen */ public class MonetClob implements Clob, Serializable, Comparable<MonetClob> { - + private final StringBuilder buffer; public MonetClob(String in) { - buffer = new StringBuilder(in); + if(in != null) + buffer = new StringBuilder(in); + else + buffer = null; } public MonetClob(char[] toParse, int startPosition, int count) { buffer = new StringBuilder(new String(toParse, startPosition, count)); } + /* internal utility method */ + private void checkBufIsNotNull() throws SQLException { + if (buffer == null) + throw new SQLException("This MonetClob has been freed", "M1M20"); + } + //== begin interface Clob - + /** * This method frees the Clob object and releases the resources the resources that it holds. The object is invalid * once the free method is called. @@ -46,7 +56,8 @@ public class MonetClob implements Clob, */ @Override public void free() { - buffer.setLength(0); + if (buffer != null) + buffer.setLength(0); } /** @@ -57,6 +68,7 @@ public class MonetClob implements Clob, */ @Override public InputStream getAsciiStream() throws SQLException { + checkBufIsNotNull(); if (buffer.length() == 0) throw new SQLException("This Clob has been freed", "M1M20"); return new ByteArrayInputStream(buffer.toString().getBytes()); @@ -71,9 +83,8 @@ public class MonetClob implements Clob, */ @Override public Reader getCharacterStream() throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); - return new StringReader(buffer.toString()); + checkBufIsNotNull(); + return new StringReader(buffer.toString()); } /** @@ -88,9 +99,8 @@ public class MonetClob implements Clob, */ @Override public Reader getCharacterStream(long pos, long length) throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); - return new StringReader(buffer.substring((int)(pos - 1), (int)(pos - 1 + length))); + checkBufIsNotNull(); + return new StringReader(getSubString(pos, (int)length)); } /** @@ -104,8 +114,7 @@ public class MonetClob implements Clob, */ @Override public String getSubString(long pos, int length) throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); + checkBufIsNotNull(); try { return buffer.substring((int)(pos - 1), (int)(pos - 1 + length)); } catch (IndexOutOfBoundsException e) { @@ -121,9 +130,8 @@ public class MonetClob implements Clob, */ @Override public long length() throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); - return (long) buffer.length(); + checkBufIsNotNull(); + return (long)buffer.length(); } /** @@ -137,6 +145,7 @@ public class MonetClob implements Clob, */ @Override public long position(Clob searchstr, long start) throws SQLException { + checkBufIsNotNull(); return position(searchstr.getSubString(1L, (int)(searchstr.length())), start); } @@ -151,23 +160,18 @@ public class MonetClob implements Clob, */ @Override public long position(String searchstr, long start) throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); + checkBufIsNotNull(); return (long)(buffer.indexOf(searchstr, (int)(start - 1))); } @Override public OutputStream setAsciiStream(long pos) throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); - throw new SQLException("Operation setAsciiStream(long pos) currently not supported", "0A000"); + throw new SQLFeatureNotSupportedException("Method setAsciiStream(long pos) not supported", "0A000"); } @Override public Writer setCharacterStream(long pos) throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); - throw new SQLException("Operation setCharacterStream(long pos) currently not supported", "0A000"); + throw new SQLFeatureNotSupportedException("Method setCharacterStream(long pos) not supported", "0A000"); } /** @@ -195,12 +199,9 @@ public class MonetClob implements Clob, */ @Override public int setString(long pos, String str, int offset, int len) throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); - + checkBufIsNotNull(); int buflen = buffer.length(); int retlen = Math.min(buflen, (int)(pos - 1 + len)); - if (retlen > 0) { buffer.replace((int)(pos - 1), (int)(pos + retlen), str.substring(offset - 1, (offset + len))); return retlen; @@ -217,8 +218,7 @@ public class MonetClob implements Clob, */ @Override public void truncate(long len) throws SQLException { - if (buffer.length() == 0) - throw new SQLException("This Clob has been freed", "M1M20"); + checkBufIsNotNull(); buffer.setLength((int) len); } @@ -230,8 +230,8 @@ public class MonetClob implements Clob, */ @Override public String toString() { - if (buffer.length() == 0) - return "<a freed MonetClob instance>"; + if (buffer == null || buffer.length() == 0) + return "null"; return buffer.toString(); } @@ -247,7 +247,11 @@ public class MonetClob implements Clob, * Overriding the hashCode method for the byte array. */ @Override - public int hashCode() { return this.buffer.toString().hashCode(); } + public int hashCode() { + if (buffer == null || buffer.length() == 0) + return 0; + return this.buffer.toString().hashCode(); + } /** * Adding the compare to method.
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java @@ -11,6 +11,7 @@ import nl.cwi.monetdb.mcl.protocol.Start import nl.cwi.monetdb.mcl.responses.*; import java.io.IOException; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.sql.*; import java.util.*; @@ -41,1654 +42,1769 @@ import java.util.concurrent.Executor; * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira * @version 1.3 */ -public abstract class MonetConnection extends MonetWrapper implements Connection { +public abstract class MonetConnection extends MonetWrapper implements Connection, AutoCloseable { - /** The sequence counter */ - private static int SeqCounter = 0; + /** The sequence counter */ + private static int SeqCounter = 0; - /** - * Gets the current sequence counter. - * - * @return The current sequence counter - */ - public static int getSeqCounter() { - return SeqCounter; - } + /** + * Gets the current sequence counter. + * + * @return The current sequence counter + */ + public static int getSeqCounter() { + return SeqCounter; + } - /** The successful processed input properties */ - protected final Properties conn_props; - /** The language to connect with */ - protected IMonetDBLanguage language; - /** Authentication hash method */ - protected final String hash; - /** An optional thread that is used for sending large queries */ - private SenderThread senderThread; - /** Whether this Connection is closed (and cannot be used anymore) */ - private boolean closed; - /** Whether this Connection is in autocommit mode */ - private boolean autoCommit = true; - /** The stack of warnings for this Connection object */ - private SQLWarning warnings; - /** The Connection specific mapping of user defined types to Java types */ - private Map<String,Class<?>> typeMap = new HashMap<String,Class<?>>() { - private static final long serialVersionUID = 1L; { - put("inet", MonetINET.class); - put("url", MonetURL.class); - } - }; + /** The successful processed input properties */ + protected final Properties conn_props; + /** The language to connect with */ + protected IMonetDBLanguage language; + /** Authentication hash method */ + protected final String hash; + /** An optional thread that is used for sending large queries */ + private SenderThread senderThread; + /** Whether this Connection is closed (and cannot be used anymore) */ + private boolean closed; + /** Whether this Connection is in autocommit mode */ + private boolean autoCommit = true; + /** The stack of warnings for this Connection object */ + private SQLWarning warnings; + /** The Connection specific mapping of user defined types to Java types */ + private Map<String,Class<?>> typeMap = new HashMap<String,Class<?>>() { + private static final long serialVersionUID = 1L; { + put("inet", MonetINET.class); + put("url", MonetURL.class); + } + }; + + // See javadoc for documentation about WeakHashMap if you don't know what it does !!!NOW!!! + // (only when you deal with it of course) + /** A Map containing all (active) Statements created from this Connection */ + private Map<Statement,?> statements = new WeakHashMap<>(); + /** The number of results we receive from the server at once */ + private int curReplySize = -1; // the server by default uses -1 (all) + /** Whether or not BLOB is mapped to LONGVARBINARY within the driver */ + private final boolean blobIsBinary; + /** Whether or not CLOB is mapped to LONGVARCHAR within the driver */ + private final boolean clobIsLongChar; + /** The underlying proticol provided by the connection (MAPI or embedded) */ + protected AbstractProtocol protocol; + /** Tells if the connection is embedded or not */ + private final boolean isEmbedded; - // See javadoc for documentation about WeakHashMap if you don't know what it does !!!NOW!!! - // (only when you deal with it of course) - /** A Map containing all (active) Statements created from this Connection */ - private Map<Statement,?> statements = new WeakHashMap<>(); - /** The number of results we receive from the server at once */ - private int curReplySize = -1; // the server by default uses -1 (all) - /** Whether or not BLOB is mapped to LONGVARBINARY within the driver */ - private final boolean blobIsBinary; - /** Whether or not CLOB is mapped to LONGVARCHAR within the driver */ - private final boolean clobIsLongChar; - /** The underlying proticol provided by the connection (MAPI or embedded) */ - protected AbstractProtocol protocol; - /** Tells if the connection is embedded or not */ - private final boolean isEmbedded; + /** + * Constructor of a Connection for MonetDB. At this moment the current implementation limits itself to storing the + * given host, database, username and password for later use by the createStatement() call. This constructor is + * only accessible to classes from the jdbc package. + */ + public MonetConnection(Properties props, String hash, IMonetDBLanguage language, boolean blobIsBinary, + boolean clobIsLongChar) { + this.conn_props = props; + this.hash = hash; + this.language = language; + this.blobIsBinary = blobIsBinary; + this.clobIsLongChar = clobIsLongChar; + //"instance of" should be cleanner, but this is faster. + this.isEmbedded = props.getProperty("embedded", "false").equals("true"); + } + + /** + * Checks if the conection is embedded or not + * + * @return If the connection is embedded + */ + public boolean isEmbedded() { + return isEmbedded; + } - /** - * Constructor of a Connection for MonetDB. At this moment the current implementation limits itself to storing the - * given host, database, username and password for later use by the createStatement() call. This constructor is - * only accessible to classes from the jdbc package. - * - * @throws IOException if an error occurs - */ - public MonetConnection(Properties props, String hash, IMonetDBLanguage language, boolean blobIsBinary, - boolean clobIsLongChar) throws IOException { - this.conn_props = props; - this.hash = hash; - this.language = language; - this.blobIsBinary = blobIsBinary; - this.clobIsLongChar = clobIsLongChar; - //"instance of" should be cleanner, but this is faster. - this.isEmbedded = props.getProperty("embedded", "false").equals("true"); - } + /** + * Gets the connection's language data. + * + * @return The connection's language data + */ + public IMonetDBLanguage getLanguage() { + return language; + } - /** - * Checks if the conection is embedded or not - * - * @return If the connection is embedded - */ - public boolean isEmbedded() { - return isEmbedded; - } + /** + * Gets the connection's protocol. + * + * @return The connection's protocol + */ + public AbstractProtocol getProtocol() { + return this.protocol; + } - /** - * Gets the connection's language data. - * - * @return The connection's language data - */ - public IMonetDBLanguage getLanguage() { - return language; - } - - /** - * Gets the connection's protocol. - * - * @return The connection's protocol - */ - public AbstractProtocol getProtocol() { - return this.protocol; - } + /** + * Connects to the server, authenticating the user. + * + * @param user The user name to authenticate + * @param pass The user's password + * @return A List with informational (warning) messages. If this list is empty; then there are no warnings. + * @throws IOException if an I/O error occurs when creating the socket + * @throws ProtocolException if bogus data is received + * @throws MCLException if an MCL related error occurs + */ + public abstract List<String> connect(String user, String pass) throws IOException, ProtocolException, MCLException; - /** - * Connects to the server, authenticating the user. - * - * @param user The user name to authenticate - * @param pass The user's password - * @return A List with informational (warning) messages. If this list is empty; then there are no warnings. - * @throws IOException if an I/O error occurs when creating the socket - * @throws ProtocolException if bogus data is received - * @throws MCLException if an MCL related error occurs - */ - public abstract List<String> connect(String user, String pass) throws IOException, ProtocolException, MCLException; + /** + * Gets the underlying connection block size length. + * + * @return The block size length + */ + public abstract int getBlockSize(); - /** - * Gets the underlying connection block size length. - * - * @return The block size length - */ - public abstract int getBlockSize(); + /** + * Gets the underlying connection default fetch size for DataBlock responses. + * + * @return The default fetch size + */ + public abstract int getDefFetchsize(); + + /** + * Gets the initial value for the StringBuilder size. + * + * @return The initial value for the StringBuilder size + */ + public abstract int initialStringBuilderSize(); - /** - * Gets the underlying connection default fetch size for DataBlock responses. - * - * @return The default fetch size - */ - public abstract int getDefFetchsize(); + /** + * Gets the underlying connection socket timeout. + * + * @return The underlying connection socket timeout + */ + public abstract int getSoTimeout() throws SocketException; - /** - * Gets the initial value for the StringBuilder size. - * - * @return The initial value for the StringBuilder size - */ - public abstract int initialStringBuilderSize(); + /** + * Sets the underlying connection socket timeout. + * + * @param timeout The specified timeout, in milliseconds. A timeout of zero is interpreted as an infinite timeout + */ + public abstract void setSoTimeout(int timeout) throws SocketException; - /** - * Gets the underlying connection socket timeout. - * - * @return The underlying connection socket timeout - */ - public abstract int getSoTimeout(); + /** + * Closes the underlying connection implementation. + * + * @throws IOException if an I/O error occurs while closing the connection + */ + public abstract void closeUnderlyingConnection() throws IOException; - /** - * Sets the underlying connection socket timeout. - * - * @param timeout The specified timeout, in milliseconds. A timeout of zero is interpreted as an infinite timeout - */ - public abstract void setSoTimeout(int timeout); + /** + * Gets the underlying connection JDBC String URL. + * + * @return The underlying connection JDBC String URL + */ + public abstract String getJDBCURL(); - /** - * Closes the underlying connection implementation. - * - * @throws IOException if an I/O error occurs while closing the connection - */ - public abstract void closeUnderlyingConnection() throws IOException; - - /** - * Gets the underlying connection JDBC String URL. - * - * @return The underlying connection JDBC String URL - */ - public abstract String getJDBCURL(); - - /** - * Sends a control command to the server. - * - * @param commandID the command identifier according to {@link ControlCommands} listing - * @param data The integer to send according to the control command - * @throws SQLException if an IO exception or a database error occurs - */ - public abstract void sendControlCommand(int commandID, int data) throws SQLException; + /** + * Sends a control command to the server. + * + * @param commandID the command identifier according to {@link ControlCommands} listing + * @param data The integer to send according to the control command + * @throws SQLException if an IO exception or a database error occurs + */ + public abstract void sendControlCommand(int commandID, int data) throws SQLException; - /** - * Releases this Connection object's database and JDBC resources immediately instead of waiting for them to be - * automatically released. All Statements created from this Connection will be closed when this method is called. - * - * Calling the method close on a Connection object that is already closed is a no-op. - */ - @Override - public void close() { - for (Statement st : statements.keySet()) { - try { - st.close(); - } catch (SQLException e) { - // better luck next time! - } - } - // close the socket or the embedded server - try { - this.closeUnderlyingConnection(); - } catch (IOException e) { - // ignore it - } - // close active SendThread if any - if (senderThread != null) { - senderThread.shutdown(); - senderThread = null; - } - // report ourselves as closed - closed = true; - } + /** + * Releases this Connection object's database and JDBC resources immediately instead of waiting for them to be + * automatically released. All Statements created from this Connection will be closed when this method is called. + * + * Calling the method close on a Connection object that is already closed is a no-op. + */ + @Override + public void close() { + for (Statement st : statements.keySet()) { + try { + st.close(); + } catch (SQLException e) { + // better luck next time! + } + } + // close the socket or the embedded server + try { + this.closeUnderlyingConnection(); + } catch (IOException e) { + // ignore it + } + // close active SendThread if any + if (senderThread != null) { + senderThread.shutdown(); + senderThread = null; + } + // report ourselves as closed + closed = true; + } + + /** + * Destructor called by garbage collector before destroying this object tries to disconnect the MonetDB connection + * if it has not been disconnected already. + */ + @Override + protected void finalize() throws Throwable { + this.close(); + super.finalize(); + } + + //== methods of interface Connection + + /** + * Clears all warnings reported for this Connection object. After a call to this method, the method getWarnings + * returns null until a new warning is reported for this Connection object. + */ + @Override + public void clearWarnings() { + warnings = null; + } - /** - * Destructor called by garbage collector before destroying this object tries to disconnect the MonetDB connection - * if it has not been disconnected already. - */ - @Override - protected void finalize() throws Throwable { - this.close(); - super.finalize(); - } - - //== methods of interface Connection + /** + * Makes all changes made since the previous commit/rollback permanent and releases any database locks currently + * held by this Connection object. This method should be used only when auto-commit mode has been disabled. + * + * @throws SQLException if a database access error occurs or this Connection object is in auto-commit mode + * @see #setAutoCommit(boolean) + */ + @Override + public void commit() throws SQLException { + // note: can't use sendIndependentCommand here because we need to process the auto_commit state the server gives + this.sendTransactionCommand("COMMIT"); + } - /** - * Clears all warnings reported for this Connection object. After a call to this method, the method getWarnings - * returns null until a new warning is reported for this Connection object. - */ - @Override - public void clearWarnings() { - warnings = null; - } + /** + * Creates a Statement object for sending SQL statements to the + * database. SQL statements without parameters are normally + * executed using Statement objects. If the same SQL statement is + * executed many times, it may be more efficient to use a + * PreparedStatement object. + * + * Result sets created using the returned Statement object will by + * default be type TYPE_FORWARD_ONLY and have a concurrency level of + * CONCUR_READ_ONLY. + * + * @return a new default Statement object + * @throws SQLException if a database access error occurs + */ + @Override + public Statement createStatement() throws SQLException { + return createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + } - private void createResponseList(String query) throws SQLException { - // create a container for the result - ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY); - // send commit to the server - try { - l.processQuery(query); - } finally { - l.close(); - } - } + /** + * Creates a Statement object that will generate ResultSet objects + * with the given type and concurrency. This method is the same as + * the createStatement method above, but it allows the default + * result set type and concurrency to be overridden. + * + * @param resultSetType a result set type; one of + * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + * or ResultSet.TYPE_SCROLL_SENSITIVE + * @param resultSetConcurrency a concurrency type; one of + * ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE + * @return a new Statement object that will generate ResultSet objects with + * the given type and concurrency + * @throws SQLException if a database access error occurs + */ + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return createStatement(resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT); + } - /** - * Makes all changes made since the previous commit/rollback permanent and releases any database locks currently - * held by this Connection object. This method should be used only when auto-commit mode has been disabled. - * - * @throws SQLException if a database access error occurs or this Connection object is in auto-commit mode - * @see #setAutoCommit(boolean) - */ - @Override - public void commit() throws SQLException { - // note: can't use sendIndependentCommand here because we need to process the auto_commit state the server gives - this.createResponseList("COMMIT"); - } + /** + * Creates a Statement object that will generate ResultSet objects + * with the given type, concurrency, and holdability. This method + * is the same as the createStatement method above, but it allows + * the default result set type, concurrency, and holdability to be + * overridden. + * + * @param resultSetType one of the following ResultSet constants: + * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + * or ResultSet.TYPE_SCROLL_SENSITIVE + * @param resultSetConcurrency one of the following ResultSet + * constants: ResultSet.CONCUR_READ_ONLY or + * ResultSet.CONCUR_UPDATABLE + * @param resultSetHoldability one of the following ResultSet + * constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT + * + * @return a new Statement object that will generate ResultSet + * objects with the given type, concurrency, and holdability + * @throws SQLException if a database access error occurs or the + * given parameters are not ResultSet constants indicating type, + * concurrency, and holdability + */ + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + try { + Statement ret = new MonetStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability); + // store it in the map for when we close... + statements.put(ret, null); + return ret; + } catch (IllegalArgumentException e) { + throw new SQLException(e.toString(), "M0M03"); + } + // we don't have to catch SQLException because that is declared to + // be thrown + } - /** - * Creates a Statement object for sending SQL statements to the database. SQL statements without parameters are - * normally executed using Statement objects. If the same SQL statement is executed many times, it may be more - * efficient to use a PreparedStatement object. - * - * Result sets created using the returned Statement object will by default be type TYPE_FORWARD_ONLY and have a - * concurrency level of CONCUR_READ_ONLY. - * - * @return a new default Statement object - * @throws SQLException if a database access error occurs - */ - @Override - public Statement createStatement() throws SQLException { - return createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, - ResultSet.HOLD_CURSORS_OVER_COMMIT); - } + /** + * Retrieves the current auto-commit mode for this Connection object. + * + * @return the current state of this Connection object's auto-commit mode + * @see #setAutoCommit(boolean) + */ + @Override + public boolean getAutoCommit() throws SQLException { + return autoCommit; + } - /** - * Creates a Statement object that will generate ResultSet objects with the given type and concurrency. This method - * is the same as the createStatement method above, but it allows the default result set type and concurrency to be - * overridden. - * - * @param resultSetType a result set type; one of ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, - * or ResultSet.TYPE_SCROLL_SENSITIVE - * @param resultSetConcurrency a concurrency type; one of ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE - * @return a new Statement object that will generate ResultSet objects with the given type and concurrency - * @throws SQLException if a database access error occurs - */ - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { - return createStatement(resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT); - } + /** + * Retrieves this Connection object's current catalog name. + * + * @return the current catalog name or null if there is none + * @throws SQLException if a database access error occurs or the current language is not SQL + */ + @Override + public String getCatalog() throws SQLException { + // MonetDB does NOT support catalogs + return null; + } - /** - * Creates a Statement object that will generate ResultSet objects - * with the given type, concurrency, and holdability. This method - * is the same as the createStatement method above, but it allows - * the default result set type, concurrency, and holdability to be - * overridden. - * - * @param resultSetType one of the following ResultSet constants: - * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, - * or ResultSet.TYPE_SCROLL_SENSITIVE - * @param resultSetConcurrency one of the following ResultSet - * constants: ResultSet.CONCUR_READ_ONLY or - * ResultSet.CONCUR_UPDATABLE - * @param resultSetHoldability one of the following ResultSet - * constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or - * ResultSet.CLOSE_CURSORS_AT_COMMIT - * - * @return a new Statement object that will generate ResultSet - * objects with the given type, concurrency, and holdability - * @throws SQLException if a database access error occurs or the - * given parameters are not ResultSet constants indicating type, - * concurrency, and holdability - */ - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException { - try { - Statement ret = new MonetStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability); - // store it in the map for when we close... - statements.put(ret, null); - return ret; - } catch (IllegalArgumentException e) { - throw new SQLException(e.toString(), "M0M03"); - } - // we don't have to catch SQLException because that is declared to - // be thrown - } + /** + * Retrieves the current holdability of ResultSet objects created using this Connection object. + * + * @return the holdability, one of ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT + */ + @Override + public int getHoldability() { + // TODO: perhaps it is better to have the server implement + // CLOSE_CURSORS_AT_COMMIT + return ResultSet.HOLD_CURSORS_OVER_COMMIT; + } - /** - * Retrieves the current auto-commit mode for this Connection object. - * - * @return the current state of this Connection object's auto-commit mode - * @see #setAutoCommit(boolean) - */ - @Override - public boolean getAutoCommit() throws SQLException { - return autoCommit; - } + /** + * Retrieves a DatabaseMetaData object that contains metadata about + * the database to which this Connection object represents a + * connection. The metadata includes information about the + * database's tables, its supported SQL grammar, its stored + * procedures, the capabilities of this connection, and so on. + * + * @throws SQLException if the current language is not SQL + * @return a DatabaseMetaData object for this Connection object + */ + @Override + public DatabaseMetaData getMetaData() throws SQLException { + if (!this.language.getRepresentation().equals("sql")) { + throw new SQLException("This method is only supported in SQL mode", "M0M04"); + } + return new MonetDatabaseMetaData(this); + } - /** - * Retrieves this Connection object's current catalog name. - * - * @return the current catalog name or null if there is none - * @throws SQLException if a database access error occurs or the current language is not SQL - */ - @Override - public String getCatalog() throws SQLException { - // MonetDB does NOT support catalogs - return null; - } + /** + * Retrieves this Connection object's current transaction isolation level. + * + * @return the current transaction isolation level, which will be Connection.TRANSACTION_SERIALIZABLE + */ + @Override + public int getTransactionIsolation() { + return TRANSACTION_SERIALIZABLE; + } + + /** + * Retrieves the Map object associated with this Connection object. Unless the application has added an entry, + * the type map returned will be empty. + * + * @return the java.util.Map object associated with this Connection object + */ + @Override + public Map<String,Class<?>> getTypeMap() { + return typeMap; + } - /** - * Retrieves the current holdability of ResultSet objects created using this Connection object. - * - * @return the holdability, one of ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT - */ - @Override - public int getHoldability() { - // TODO: perhaps it is better to have the server implement - // CLOSE_CURSORS_AT_COMMIT - return ResultSet.HOLD_CURSORS_OVER_COMMIT; - } - - /** - * Retrieves a DatabaseMetaData object that contains metadata about - * the database to which this Connection object represents a - * connection. The metadata includes information about the - * database's tables, its supported SQL grammar, its stored - * procedures, the capabilities of this connection, and so on. - * - * @throws SQLException if the current language is not SQL - * @return a DatabaseMetaData object for this Connection object - */ - @Override - public DatabaseMetaData getMetaData() throws SQLException { - if (!this.language.getRepresentation().equals("sql")) { - throw new SQLException("This method is only supported in SQL mode", "M0M04"); - } - return new MonetDatabaseMetaData(this); - } + /** + * Retrieves the first warning reported by calls on this Connection + * object. If there is more than one warning, subsequent warnings + * will be chained to the first one and can be retrieved by calling + * the method SQLWarning.getNextWarning on the warning that was + * retrieved previously. + * + * This method may not be called on a closed connection; doing so will cause an SQLException to be thrown. + * + * Note: Subsequent warnings will be chained to this SQLWarning. + * + * @return the first SQLWarning object or null if there are none + * @throws SQLException if a database access error occurs or this method is called on a closed connection + */ + @Override + public SQLWarning getWarnings() throws SQLException { + if (closed) { + throw new SQLException("Cannot call on closed Connection", "M1M20"); + } + // if there are no warnings, this will be null, which fits with the specification. + return warnings; + } - /** - * Retrieves this Connection object's current transaction isolation level. - * - * @return the current transaction isolation level, which will be Connection.TRANSACTION_SERIALIZABLE - */ - @Override - public int getTransactionIsolation() { - return TRANSACTION_SERIALIZABLE; - } - - /** - * Retrieves the Map object associated with this Connection object. Unless the application has added an entry, - * the type map returned will be empty. - * - * @return the java.util.Map object associated with this Connection object - */ - @Override - public Map<String,Class<?>> getTypeMap() { - return typeMap; - } + /** + * Retrieves whether this Connection object has been closed. A + * connection is closed if the method close has been called on it or + * if certain fatal errors have occurred. This method is guaranteed + * to return true only when it is called after the method + * Connection.close has been called. + * + * This method generally cannot be called to determine whether a + * connection to a database is valid or invalid. A typical client + * can determine that a connection is invalid by catching any + * exceptions that might be thrown when an operation is attempted. + * + * @return true if this Connection object is closed; false if it is still open + */ + @Override + public boolean isClosed() { + return closed; + } - /** - * Retrieves the first warning reported by calls on this Connection - * object. If there is more than one warning, subsequent warnings - * will be chained to the first one and can be retrieved by calling - * the method SQLWarning.getNextWarning on the warning that was - * retrieved previously. - * - * This method may not be called on a closed connection; doing so will cause an SQLException to be thrown. - * - * Note: Subsequent warnings will be chained to this SQLWarning. - * - * @return the first SQLWarning object or null if there are none - * @throws SQLException if a database access error occurs or this method is called on a closed connection - */ - @Override - public SQLWarning getWarnings() throws SQLException { - if (closed) { - throw new SQLException("Cannot call on closed Connection", "M1M20"); - } - // if there are no warnings, this will be null, which fits with the specification. - return warnings; - } + /** + * Retrieves whether this Connection object is in read-only mode. + * MonetDB currently doesn't support updateable result sets, but + * updates are possible. Hence the Connection object is never in + * read-only mode. + * + * @return true if this Connection object is read-only; false otherwise + */ + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public String nativeSQL(String sql) { + /* there is currently no way to get the native MonetDB rewritten SQL string back, so just return the original string */ + /* in future we may replace/remove the escape sequences { <escape-type> ...} before sending it to the server */ + return sql; + } - /** - * Retrieves whether this Connection object has been closed. A - * connection is closed if the method close has been called on it or - * if certain fatal errors have occurred. This method is guaranteed - * to return true only when it is called after the method - * Connection.close has been called. - * - * This method generally cannot be called to determine whether a - * connection to a database is valid or invalid. A typical client - * can determine that a connection is invalid by catching any - * exceptions that might be thrown when an operation is attempted. - * - * @return true if this Connection object is closed; false if it is still open - */ - @Override - public boolean isClosed() { - return closed; - } + @Override + public CallableStatement prepareCall(String sql) { + /* not implemented yet */ + return null; + } - /** - * Retrieves whether this Connection object is in read-only mode. - * MonetDB currently doesn't support updateable result sets, but - * updates are possible. Hence the Connection object is never in - * read-only mode. - * - * @return true if this Connection object is read-only; false otherwise - */ - @Override - public boolean isReadOnly() { - return false; - } + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) { + /* not implemented yet */ + return null; + } - @Override - public String nativeSQL(String sql) {return sql;} - - @Override - public CallableStatement prepareCall(String sql) {return null;} + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { + /* not implemented yet */ + return null; + } - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) {return null;} - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, - int resultSetHoldability) {return null;} + /** + * Creates a PreparedStatement object for sending parameterized SQL + * statements to the database. + * + * A SQL statement with or without IN parameters can be pre-compiled + * and stored in a PreparedStatement object. This object can then be + * used to efficiently execute this statement multiple times. + * + * Note: This method is optimized for handling parametric SQL + * statements that benefit from precompilation. If the driver + * supports precompilation, the method prepareStatement will send + * the statement to the database for precompilation. Some drivers + * may not support precompilation. In this case, the statement may + * not be sent to the database until the PreparedStatement object is + * executed. This has no direct effect on users; however, it does + * affect which methods throw certain SQLException objects. + * + * Result sets created using the returned PreparedStatement object + * will by default be type TYPE_FORWARD_ONLY and have a concurrency + * level of CONCUR_READ_ONLY. + * + * @param sql an SQL statement that may contain one or more '?' IN + * parameter placeholders + * @return a new default PreparedStatement object containing the + * pre-compiled SQL statement + * @throws SQLException if a database access error occurs + */ + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + } - /** - * Creates a PreparedStatement object for sending parameterized SQL - * statements to the database. - * - * A SQL statement with or without IN parameters can be pre-compiled - * and stored in a PreparedStatement object. This object can then be - * used to efficiently execute this statement multiple times. - * - * Note: This method is optimized for handling parametric SQL - * statements that benefit from precompilation. If the driver - * supports precompilation, the method prepareStatement will send - * the statement to the database for precompilation. Some drivers - * may not support precompilation. In this case, the statement may - * not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does - * affect which methods throw certain SQLException objects. - * - * Result sets created using the returned PreparedStatement object - * will by default be type TYPE_FORWARD_ONLY and have a concurrency - * level of CONCUR_READ_ONLY. - * - * @param sql an SQL statement that may contain one or more '?' IN - * parameter placeholders - * @return a new default PreparedStatement object containing the - * pre-compiled SQL statement - * @throws SQLException if a database access error occurs - */ - @Override - public PreparedStatement prepareStatement(String sql) throws SQLException { - return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, - ResultSet.HOLD_CURSORS_OVER_COMMIT); - } + /** + * Creates a PreparedStatement object that will generate ResultSet + * objects with the given type and concurrency. This method is the + * same as the prepareStatement method above, but it allows the + * default result set type and concurrency to be overridden. + * + * @param sql a String object that is the SQL statement to be sent to the + * database; may contain one or more ? IN parameters + * @param resultSetType a result set type; one of + * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + * or ResultSet.TYPE_SCROLL_SENSITIVE + * @param resultSetConcurrency a concurrency type; one of + * ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE + * @return a new PreparedStatement object containing the pre-compiled SQL + * statement that will produce ResultSet objects with the given + * type and concurrency + * @throws SQLException if a database access error occurs or the given + * parameters are not ResultSet constants indicating + * type and concurrency + */ + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return prepareStatement(sql, resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT); + } - /** - * Creates a PreparedStatement object that will generate ResultSet - * objects with the given type and concurrency. This method is the - * same as the prepareStatement method above, but it allows the - * default result set type and concurrency to be overridden. - * - * @param sql a String object that is the SQL statement to be sent to the - * database; may contain one or more ? IN parameters - * @param resultSetType a result set type; one of - * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, - * or ResultSet.TYPE_SCROLL_SENSITIVE - * @param resultSetConcurrency a concurrency type; one of - * ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE - * @return a new PreparedStatement object containing the pre-compiled SQL - * statement that will produce ResultSet objects with the given - * type and concurrency - * @throws SQLException if a database access error occurs or the given - * parameters are not ResultSet constants indicating - * type and concurrency - */ - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException { - return prepareStatement(sql, resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT); - } + /** + * Creates a PreparedStatement object that will generate ResultSet + * objects with the given type, concurrency, and holdability. + * + * This method is the same as the prepareStatement method above, but + * it allows the default result set type, concurrency, and + * holdability to be overridden. + * + * @param sql a String object that is the SQL statement to be sent + * to the database; may contain one or more ? IN parameters + * @param resultSetType one of the following ResultSet constants: + * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + * or ResultSet.TYPE_SCROLL_SENSITIVE + * @param resultSetConcurrency one of the following ResultSet + * constants: ResultSet.CONCUR_READ_ONLY or + * ResultSet.CONCUR_UPDATABLE + * @param resultSetHoldability one of the following ResultSet + * constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT + * @return a new PreparedStatement object, containing the + * pre-compiled SQL statement, that will generate ResultSet objects + * with the given type, concurrency, and holdability + * @throws SQLException if a database access error occurs or the + * given parameters are not ResultSet constants indicating type, + * concurrency, and holdability + */ + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + try { + PreparedStatement ret = new MonetPreparedStatement(this, resultSetType, resultSetConcurrency, + resultSetHoldability, sql); + // store it in the map for when we close... + statements.put(ret, null); + return ret; + } catch (IllegalArgumentException e) { + throw new SQLException(e.toString(), "M0M03"); + } + // we don't have to catch SQLException because that is declared to + // be thrown + } - /** - * Creates a PreparedStatement object that will generate ResultSet - * objects with the given type, concurrency, and holdability. - * - * This method is the same as the prepareStatement method above, but - * it allows the default result set type, concurrency, and - * holdability to be overridden. - * - * @param sql a String object that is the SQL statement to be sent - * to the database; may contain one or more ? IN parameters - * @param resultSetType one of the following ResultSet constants: - * ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, - * or ResultSet.TYPE_SCROLL_SENSITIVE - * @param resultSetConcurrency one of the following ResultSet - * constants: ResultSet.CONCUR_READ_ONLY or - * ResultSet.CONCUR_UPDATABLE - * @param resultSetHoldability one of the following ResultSet - * constants: ResultSet.HOLD_CURSORS_OVER_COMMIT or - * ResultSet.CLOSE_CURSORS_AT_COMMIT - * @return a new PreparedStatement object, containing the - * pre-compiled SQL statement, that will generate ResultSet objects - * with the given type, concurrency, and holdability - * @throws SQLException if a database access error occurs or the - * given parameters are not ResultSet constants indicating type, - * concurrency, and holdability - */ - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, - int resultSetHoldability) throws SQLException { - try { - PreparedStatement ret = new MonetPreparedStatement(this, resultSetType, resultSetConcurrency, - resultSetHoldability, sql); - // store it in the map for when we close... - statements.put(ret, null); - return ret; - } catch (IllegalArgumentException e) { - throw new SQLException(e.toString(), "M0M03"); - } - // we don't have to catch SQLException because that is declared to - // be thrown - } + /** + * Creates a default PreparedStatement object that has the + * capability to retrieve auto-generated keys. The given constant + * tells the driver whether it should make auto-generated keys + * available for retrieval. This parameter is ignored if the SQL + * statement is not an INSERT statement. + * + * Note: This method is optimized for handling parametric SQL + * statements that benefit from precompilation. If the driver + * supports precompilation, the method prepareStatement will send + * the statement to the database for precompilation. Some drivers + * may not support precompilation. In this case, the statement may + * not be sent to the database until the PreparedStatement object is + * executed. This has no direct effect on users; however, it does + * affect which methods throw certain SQLExceptions. + * + * Result sets created using the returned PreparedStatement object + * will by default be type TYPE_FORWARD_ONLY and have a concurrency + * level of CONCUR_READ_ONLY. + * + * @param sql an SQL statement that may contain one or more '?' IN + * parameter placeholders + * @param autoGeneratedKeys a flag indicating whether auto-generated + * keys should be returned; one of + * Statement.RETURN_GENERATED_KEYS or + * Statement.NO_GENERATED_KEYS + * @return a new PreparedStatement object, containing the + * pre-compiled SQL statement, that will have the capability + * of returning auto-generated keys + * @throws SQLException - if a database access error occurs or the + * given parameter is not a Statement constant indicating + * whether auto-generated keys should be returned + */ + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && + autoGeneratedKeys != Statement.NO_GENERATED_KEYS) + throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); - /** - * Creates a default PreparedStatement object that has the - * capability to retrieve auto-generated keys. The given constant - * tells the driver whether it should make auto-generated keys - * available for retrieval. This parameter is ignored if the SQL - * statement is not an INSERT statement. - * - * Note: This method is optimized for handling parametric SQL - * statements that benefit from precompilation. If the driver - * supports precompilation, the method prepareStatement will send - * the statement to the database for precompilation. Some drivers - * may not support precompilation. In this case, the statement may - * not be sent to the database until the PreparedStatement object is - * executed. This has no direct effect on users; however, it does - * affect which methods throw certain SQLExceptions. - * - * Result sets created using the returned PreparedStatement object - * will by default be type TYPE_FORWARD_ONLY and have a concurrency - * level of CONCUR_READ_ONLY. - * - * @param sql an SQL statement that may contain one or more '?' IN - * parameter placeholders - * @param autoGeneratedKeys a flag indicating whether auto-generated - * keys should be returned; one of - * Statement.RETURN_GENERATED_KEYS or - * Statement.NO_GENERATED_KEYS - * @return a new PreparedStatement object, containing the - * pre-compiled SQL statement, that will have the capability - * of returning auto-generated keys - * @throws SQLException - if a database access error occurs or the - * given parameter is not a Statement constant indicating - * whether auto-generated keys should be returned - */ - @Override - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { - if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && autoGeneratedKeys != Statement.NO_GENERATED_KEYS) { - throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); - } - /* MonetDB has no way to disable this, so just do the normal thing ;) */ - return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - } + /* MonetDB has no way to disable this, so just do the normal + * thing ;) */ + return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + } - @Override - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) {return null;} - - @Override - public PreparedStatement prepareStatement(String sql, String[] columnNames) {return null;} + /** + * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. + * This array contains the indexes of the columns in the target table that contain the auto-generated keys that should be made available. + * The driver will ignore the array if the SQL statement is not an INSERT statement, or an SQL statement able to + * return auto-generated keys (the list of such statements is vendor-specific). + * + * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. + * This object can then be used to efficiently execute this statement multiple times. + * + * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. + * If the driver supports precompilation, the method prepareStatement will send the statement to the database for precompilation. + * Some drivers may not support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement + * object is executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. + * + * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have + * a concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling getHoldability(). + * + * Parameters: + * sql - an SQL statement that may contain one or more '?' IN parameter placeholders + * columnIndexes - an array of column indexes indicating the columns that should be returned from the inserted row or rows + * Returns: + * a new PreparedStatement object, containing the pre-compiled statement, that is capable of + * returning the auto-generated keys designated by the given array of column indexes + * Throws: + * SQLException - if a database access error occurs or this method is called on a closed connection + * SQLFeatureNotSupportedException - if the JDBC driver does not support this method + */ + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + throw new SQLFeatureNotSupportedException("prepareStatement(String sql, int[] columnIndexes) not supported", "0A000"); + } - /** - * Removes the given Savepoint object from the current transaction. - * Any reference to the savepoint after it have been removed will - * cause an SQLException to be thrown. - * - * @param savepoint the Savepoint object to be removed - * @throws SQLException if a database access error occurs or the given - * Savepoint object is not a valid savepoint in the current - * transaction - */ - @Override - public void releaseSavepoint(Savepoint savepoint) throws SQLException { - if (!(savepoint instanceof MonetSavepoint)) { - throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); - } - MonetSavepoint sp = (MonetSavepoint) savepoint; - // note: can't use sendIndependentCommand here because we need - // to process the auto_commit state the server gives - // create a container for the result - this.createResponseList("RELEASE SAVEPOINT " + sp.getName()); - } - - /** - * Undoes all changes made in the current transaction and releases - * any database locks currently held by this Connection object. This - * method should be used only when auto-commit mode has been - * disabled. - * - * @throws SQLException if a database access error occurs or this - * Connection object is in auto-commit mode - * @see #setAutoCommit(boolean) - */ - @Override - public void rollback() throws SQLException { - // note: can't use sendIndependentCommand here because we need - // to process the auto_commit state the server gives - // create a container for the result - this.createResponseList("ROLLBACK"); - } + /** + * Creates a default PreparedStatement object capable of returning the auto-generated keys designated by the given array. + * This array contains the names of the columns in the target table that contain the auto-generated keys that should be returned. + * The driver will ignore the array if the SQL statement is not an INSERT statement, or an SQL statement able to + * return auto-generated keys (the list of such statements is vendor-specific). + * + * An SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. + * This object can then be used to efficiently execute this statement multiple times. + * + * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. + * If the driver supports precompilation, the method prepareStatement will send the statement to the database for precompilation. + * Some drivers may not support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement + * object is executed. This has no direct effect on users; however, it does affect which methods throw certain SQLExceptions. + * + * Result sets created using the returned PreparedStatement object will by default be type TYPE_FORWARD_ONLY and have + * a concurrency level of CONCUR_READ_ONLY. The holdability of the created result sets can be determined by calling getHoldability(). + * + * Parameters: + * sql - an SQL statement that may contain one or more '?' IN parameter placeholders + * columnNames - an array of column names indicating the columns that should be returned from the inserted row or rows + * Returns: + * a new PreparedStatement object, containing the pre-compiled statement, that is capable of + * returning the auto-generated keys designated by the given array of column names + * Throws: + * SQLException - if a database access error occurs or this method is called on a closed connection + * SQLFeatureNotSupportedException - if the JDBC driver does not support this method + */ + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + throw new SQLFeatureNotSupportedException("prepareStatement(String sql, String[] columnNames) not supported", "0A000"); + } - /** - * Undoes all changes made after the given Savepoint object was set. - * - * This method should be used only when auto-commit has been - * disabled. - * - * @param savepoint the Savepoint object to roll back to - * @throws SQLException if a database access error occurs, the - * Savepoint object is no longer valid, or this Connection - * object is currently in auto-commit mode - */ - @Override - public void rollback(Savepoint savepoint) throws SQLException { - if (!(savepoint instanceof MonetSavepoint)) { - throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); - } - - MonetSavepoint sp = (MonetSavepoint)savepoint; - // note: can't use sendIndependentCommand here because we need - // to process the auto_commit state the server gives - // create a container for the result - this.createResponseList("ROLLBACK TO SAVEPOINT " + sp.getName()); - } + /** + * Removes the given Savepoint object from the current transaction. + * Any reference to the savepoint after it have been removed will + * cause an SQLException to be thrown. + * + * @param savepoint the Savepoint object to be removed + * @throws SQLException if a database access error occurs or the given + * Savepoint object is not a valid savepoint in the current + * transaction + */ + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + if (!(savepoint instanceof MonetSavepoint)) { + throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); + } + MonetSavepoint sp = (MonetSavepoint) savepoint; + // note: can't use sendIndependentCommand here because we need + // to process the auto_commit state the server gives + this.sendTransactionCommand("RELEASE SAVEPOINT " + sp.getName()); + } - /** - * Sets this connection's auto-commit mode to the given state. If a - * connection is in auto-commit mode, then all its SQL statements - * will be executed and committed as individual transactions. - * Otherwise, its SQL statements are grouped into transactions that - * are terminated by a call to either the method commit or the - * method rollback. By default, new connections are in auto-commit - * mode. - * - * The commit occurs when the statement completes or the next - * execute occurs, whichever comes first. In the case of statements - * returning a ResultSet object, the statement completes when the - * last row of the ResultSet object has been retrieved or the - * ResultSet object has been closed. In advanced cases, a single - * statement may return multiple results as well as output parameter - * values. In these cases, the commit occurs when all results and - * output parameter values have been retrieved. - * - * NOTE: If this method is called during a transaction, the - * transaction is committed. - * - * @param autoCommit true to enable auto-commit mode; false to disable it - * @throws SQLException if a database access error occurs - * @see #getAutoCommit() - */ - @Override - public void setAutoCommit(boolean autoCommit) throws SQLException { - if (this.autoCommit != autoCommit) { - this.sendControlCommand(ControlCommands.AUTO_COMMIT, (autoCommit ? 1 : 0)); - this.autoCommit = autoCommit; - } - } + /** + * Undoes all changes made in the current transaction and releases + * any database locks currently held by this Connection object. This + * method should be used only when auto-commit mode has been + * disabled. + * + * @throws SQLException if a database access error occurs or this + * Connection object is in auto-commit mode + * @see #setAutoCommit(boolean) + */ + @Override + public void rollback() throws SQLException { + // note: can't use sendIndependentCommand here because we need + // to process the auto_commit state the server gives + // create a container for the result + this.sendTransactionCommand("ROLLBACK"); + } - /** - * Sets the given catalog name in order to select a subspace of this - * Connection object's database in which to work. If the driver - * does not support catalogs, it will silently ignore this request. - */ - @Override - public void setCatalog(String catalog) throws SQLException { - throw new SQLFeatureNotSupportedException("setCatalog(String catalog) not supported", "0A000"); - } + /** + * Undoes all changes made after the given Savepoint object was set. + * + * This method should be used only when auto-commit has been + * disabled. + * + * @param savepoint the Savepoint object to roll back to + * @throws SQLException if a database access error occurs, the + * Savepoint object is no longer valid, or this Connection + * object is currently in auto-commit mode + */ + @Override + public void rollback(Savepoint savepoint) throws SQLException { + if (!(savepoint instanceof MonetSavepoint)) { + throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); + } + + MonetSavepoint sp = (MonetSavepoint)savepoint; + // note: can't use sendIndependentCommand here because we need + // to process the auto_commit state the server gives + // create a container for the result + this.sendTransactionCommand("ROLLBACK TO SAVEPOINT " + sp.getName()); + } - /** - * Changes the default holdability of ResultSet objects created using this - * Connection object to the given holdability. The default holdability of - * ResultSet objects can be be determined by invoking DatabaseMetaData.getResultSetHoldability(). - * - * @param holdability - a ResultSet holdability constant; one of - * ResultSet.HOLD_CURSORS_OVER_COMMIT or - * ResultSet.CLOSE_CURSORS_AT_COMMIT - * @see #getHoldability() - */ - @Override - public void setHoldability(int holdability) throws SQLException { - // we only support ResultSet.HOLD_CURSORS_OVER_COMMIT - if (holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT) - throw new SQLFeatureNotSupportedException("setHoldability(CLOSE_CURSORS_AT_COMMIT) not supported", "0A000"); - } + /** + * Sets this connection's auto-commit mode to the given state. If a + * connection is in auto-commit mode, then all its SQL statements + * will be executed and committed as individual transactions. + * Otherwise, its SQL statements are grouped into transactions that + * are terminated by a call to either the method commit or the + * method rollback. By default, new connections are in auto-commit + * mode. + * + * The commit occurs when the statement completes or the next + * execute occurs, whichever comes first. In the case of statements + * returning a ResultSet object, the statement completes when the + * last row of the ResultSet object has been retrieved or the + * ResultSet object has been closed. In advanced cases, a single + * statement may return multiple results as well as output parameter + * values. In these cases, the commit occurs when all results and + * output parameter values have been retrieved. + * + * NOTE: If this method is called during a transaction, the + * transaction is committed. + * + * @param autoCommit true to enable auto-commit mode; false to disable it + * @throws SQLException if a database access error occurs + * @see #getAutoCommit() + */ + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + if (this.autoCommit != autoCommit) { + this.sendControlCommand(ControlCommands.AUTO_COMMIT, (autoCommit ? 1 : 0)); + this.autoCommit = autoCommit; + } + } - /** - * Puts this connection in read-only mode as a hint to the driver to - * enable database optimizations. MonetDB doesn't support any mode - * here, hence an SQLWarning is generated if attempted to set - * to true here. - * - * @param readOnly true enables read-only mode; false disables it - * @throws SQLException if a database access error occurs or this - * method is called during a transaction. - */ - @Override - public void setReadOnly(boolean readOnly) throws SQLException { - if (readOnly) { - addWarning("cannot setReadOnly(true): read-only Connection mode not supported", "01M08"); - } - } + /** + * Sets the given catalog name in order to select a subspace of this + * Connection object's database in which to work. If the driver + * does not support catalogs, it will silently ignore this request. + */ + @Override + public void setCatalog(String catalog) throws SQLException { + throw new SQLFeatureNotSupportedException("setCatalog(String catalog) not supported", "0A000"); + } - /** - * Creates an unnamed savepoint in the current transaction and - * returns the new Savepoint object that represents it. - * - * @return the new Savepoint object - * @throws SQLException if a database access error occurs or this Connection - * object is currently in auto-commit mode - */ - @Override - public Savepoint setSavepoint() throws SQLException { - // create a new Savepoint object - MonetSavepoint sp = new MonetSavepoint(); - // note: can't use sendIndependentCommand here because we need - // to process the auto_commit state the server gives - // create a container for the result - this.createResponseList("SAVEPOINT " + sp.getName()); - return sp; - } + /** + * Changes the default holdability of ResultSet objects created using this + * Connection object to the given holdability. The default holdability of + * ResultSet objects can be be determined by invoking DatabaseMetaData.getResultSetHoldability(). + * + * @param holdability - a ResultSet holdability constant; one of + * ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT + * @see #getHoldability() + */ + @Override + public void setHoldability(int holdability) throws SQLException { + // we only support ResultSet.HOLD_CURSORS_OVER_COMMIT + if (holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT) + throw new SQLFeatureNotSupportedException("setHoldability(CLOSE_CURSORS_AT_COMMIT) not supported", "0A000"); + } - /** - * Creates a savepoint with the given name in the current - * transaction and returns the new Savepoint object that represents - * it. - * - * @param name a String containing the name of the savepoint - * @return the new Savepoint object - * @throws SQLException if a database access error occurs or this Connection - * object is currently in auto-commit mode - */ - @Override - public Savepoint setSavepoint(String name) throws SQLException { - // create a new Savepoint object - MonetSavepoint sp; - try { - sp = new MonetSavepoint(name); - } catch (IllegalArgumentException e) { - throw new SQLException(e.getMessage(), "M0M03"); - } - // note: can't use sendIndependentCommand here because we need - // to process the auto_commit state the server gives - // create a container for the result - this.createResponseList("SAVEPOINT " + sp.getName()); - return sp; - } + /** + * Puts this connection in read-only mode as a hint to the driver to + * enable database optimizations. MonetDB doesn't support any mode + * here, hence an SQLWarning is generated if attempted to set + * to true here. + * + * @param readOnly true enables read-only mode; false disables it + * @throws SQLException if a database access error occurs or this + * method is called during a transaction. + */ + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + if (readOnly) { + addWarning("cannot setReadOnly(true): read-only Connection mode not supported", "01M08"); + } + } + + /** + * Creates an unnamed savepoint in the current transaction and + * returns the new Savepoint object that represents it. + * + * @return the new Savepoint object + * @throws SQLException if a database access error occurs or this Connection + * object is currently in auto-commit mode + */ + @Override + public Savepoint setSavepoint() throws SQLException { + // create a new Savepoint object + MonetSavepoint sp = new MonetSavepoint(); + // note: can't use sendIndependentCommand here because we need + // to process the auto_commit state the server gives + // create a container for the result + this.sendTransactionCommand("SAVEPOINT " + sp.getName()); + return sp; + } - /** - * Attempts to change the transaction isolation level for this - * Connection object to the one given. The constants defined in the - * interface Connection are the possible transaction isolation - * levels. - * - * @param level one of the following Connection constants: Connection.TRANSACTION_READ_UNCOMMITTED, - * Connection.TRANSACTION_READ_COMMITTED, Connection.TRANSACTION_REPEATABLE_READ, or - * Connection.TRANSACTION_SERIALIZABLE. - */ - @Override - public void setTransactionIsolation(int level) { - if (level != TRANSACTION_SERIALIZABLE) { - addWarning("MonetDB only supports fully serializable " + - "transactions, continuing with transaction level raised to TRANSACTION_SERIALIZABLE", "01M09"); - } - } + /** + * Creates a savepoint with the given name in the current + * transaction and returns the new Savepoint object that represents + * it. + * + * @param name a String containing the name of the savepoint + * @return the new Savepoint object + * @throws SQLException if a database access error occurs or this Connection + * object is currently in auto-commit mode + */ + @Override + public Savepoint setSavepoint(String name) throws SQLException { + // create a new Savepoint object + MonetSavepoint sp; + try { + sp = new MonetSavepoint(name); + } catch (IllegalArgumentException e) { + throw new SQLException(e.getMessage(), "M0M03"); + } + // note: can't use sendIndependentCommand here because we need + // to process the auto_commit state the server gives + // create a container for the result + this.sendTransactionCommand("SAVEPOINT " + sp.getName()); + return sp; + } - /** - * Installs the given TypeMap object as the type map for this - * Connection object. The type map will be used for the custom - * mapping of SQL structured types and distinct types. - * - * @param map the java.util.Map object to install as the replacement for - * this Connection object's default type map - */ - @Override - public void setTypeMap(Map<String, Class<?>> map) { - typeMap = map; - } + /** + * Attempts to change the transaction isolation level for this + * Connection object to the one given. The constants defined in the + * interface Connection are the possible transaction isolation + * levels. + * + * @param level one of the following Connection constants: Connection.TRANSACTION_READ_UNCOMMITTED, + * Connection.TRANSACTION_READ_COMMITTED, Connection.TRANSACTION_REPEATABLE_READ, or + * Connection.TRANSACTION_SERIALIZABLE. + */ + @Override + public void setTransactionIsolation(int level) { + if (level != TRANSACTION_SERIALIZABLE) { + addWarning("MonetDB only supports fully serializable " + + "transactions, continuing with transaction level raised to TRANSACTION_SERIALIZABLE", "01M09"); + } + } + + /** + * Installs the given TypeMap object as the type map for this + * Connection object. The type map will be used for the custom + * mapping of SQL structured types and distinct types. + * + * @param map the java.util.Map object to install as the replacement for + * this Connection object's default type map + */ + @Override + public void setTypeMap(Map<String, Class<?>> map) { + typeMap = map; + } - /** - * Returns a string identifying this Connection to the MonetDB server. - * - * @return a String representing this Object - */ - @Override - public String toString() { - return "MonetDB Connection (" + this.getJDBCURL() + ") " + (closed ? "disconnected" : "connected"); - } - - //== Java 1.6 methods (JDBC 4.0) + /** + * Returns a string identifying this Connection to the MonetDB server. + * + * @return a String representing this Object + */ + @Override + public String toString() { + return "MonetDB Connection (" + this.getJDBCURL() + ") " + (closed ? "disconnected" : "connected"); + } - /** - * Factory method for creating Array objects. - * - * Note: When createArrayOf is used to create an array object that - * maps to a primitive data type, then it is implementation-defined - * whether the Array object is an array of that primitive data type - * or an array of Object. - * - * Note: The JDBC driver is responsible for mapping the elements - * Object array to the default JDBC SQL type defined in - * java.sql.Types for the given class of Object. The default mapping - * is specified in Appendix B of the JDBC specification. If the - * resulting JDBC type is not the appropriate type for the given - * typeName then it is implementation defined whether an - * SQLException is thrown or the driver supports the resulting conversion. - * - * @param typeName the SQL name of the type the elements of the - * array map to. The typeName is a database-specific name - * which may be the name of a built-in type, a user-defined - * type or a standard SQL type supported by this database. - * This is the value returned by Array.getBaseTypeName - * @return an Array object whose elements map to the specified SQL type - * @throws SQLException if a database error occurs, the JDBC type - * is not appropriate for the typeName and the conversion is - * not supported, the typeName is null or this method is - * called on a closed connection - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support this data type - * @since 1.6 - */ - @Override - public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { - throw new SQLFeatureNotSupportedException("createArrayOf() not supported", "0A000"); - } + /** + * Factory method for creating Array objects. + * + * Note: When createArrayOf is used to create an array object that + * maps to a primitive data type, then it is implementation-defined + * whether the Array object is an array of that primitive data type + * or an array of Object. + * + * Note: The JDBC driver is responsible for mapping the elements + * Object array to the default JDBC SQL type defined in + * java.sql.Types for the given class of Object. The default mapping + * is specified in Appendix B of the JDBC specification. If the + * resulting JDBC type is not the appropriate type for the given + * typeName then it is implementation defined whether an + * SQLException is thrown or the driver supports the resulting conversion. + * + * @param typeName the SQL name of the type the elements of the + * array map to. The typeName is a database-specific name + * which may be the name of a built-in type, a user-defined + * type or a standard SQL type supported by this database. + * This is the value returned by Array.getBaseTypeName + * @return an Array object whose elements map to the specified SQL type + * @throws SQLException if a database error occurs, the JDBC type + * is not appropriate for the typeName and the conversion is + * not supported, the typeName is null or this method is + * called on a closed connection + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this data type + * @since 1.6 + */ + @Override + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + throw new SQLFeatureNotSupportedException("createArrayOf() not supported", "0A000"); + } + + + //== end methods of interface java.sql.Connection - /** - * Constructs an object that implements the Clob interface. The - * object returned initially contains no data. The setAsciiStream, - * setCharacterStream and setString methods of the Clob interface - * may be used to add data to the Clob. - * - * @return a MonetClob instance - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support MonetClob objects that can be filled in - * @since 1.6 - */ - @Override - public java.sql.Clob createClob() throws SQLException { - return new MonetClob(""); - } + /** + * Constructs an object that implements the Clob interface. The + * object returned initially contains no data. The setAsciiStream, + * setCharacterStream and setString methods of the Clob interface + * may be used to add data to the Clob. + * + * @return a MonetClob instance + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support MonetClob objects that can be filled in + * @since 1.6 + */ + @Override + public java.sql.Clob createClob() throws SQLException { + return new MonetClob(""); + } - /** - * Constructs an object that implements the Blob interface. The - * object returned initially contains no data. The setBinaryStream - * and setBytes methods of the Blob interface may be used to add - * data to the Blob. - * - * @return a MonetBlob instance - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support MonetBlob objects that can be filled in - * @since 1.6 - */ - @Override - public java.sql.Blob createBlob() throws SQLException { - return new MonetBlob(new byte[1]); - } + /** + * Constructs an object that implements the Blob interface. The + * object returned initially contains no data. The setBinaryStream + * and setBytes methods of the Blob interface may be used to add + * data to the Blob. + * + * @return a MonetBlob instance + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support MonetBlob objects that can be filled in + * @since 1.6 + */ + @Override + public java.sql.Blob createBlob() throws SQLException { + return new MonetBlob(new byte[1]); + } - /** - * Constructs an object that implements the NClob interface. The - * object returned initially contains no data. The setAsciiStream, - * setCharacterStream and setString methods of the NClob interface - * may be used to add data to the NClob. - * - * @return an NClob instance - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support MonetNClob objects that can be filled in - * @since 1.6 - */ - @Override - public java.sql.NClob createNClob() throws SQLException { - throw new SQLFeatureNotSupportedException("createNClob() not supported", "0A000"); - } + /** + * Constructs an object that implements the NClob interface. The + * object returned initially contains no data. The setAsciiStream, + * setCharacterStream and setString methods of the NClob interface + * may be used to add data to the NClob. + * + * @return an NClob instance + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support MonetNClob objects that can be filled in + * @since 1.6 + */ + @Override + public java.sql.NClob createNClob() throws SQLException { + throw new SQLFeatureNotSupportedException("createNClob() not supported", "0A000"); + } - /** - * Factory method for creating Struct objects. - * - * @param typeName the SQL type name of the SQL structured type that - * this Struct object maps to. The typeName is the name of a - * user-defined type that has been defined for this database. - * It is the value returned by Struct.getSQLTypeName. - * @param attributes the attributes that populate the returned object - * @return a Struct object that maps to the given SQL type and is - * populated with the given attributes - * @throws SQLException if a database error occurs, the typeName - * is null or this method is called on a closed connection - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support this data type - * @since 1.6 - */ - @Override - public java.sql.Struct createStruct(String typeName, Object[] attributes) throws SQLException { - throw new SQLFeatureNotSupportedException("createStruct() not supported", "0A000"); - } + /** + * Factory method for creating Struct objects. + * + * @param typeName the SQL type name of the SQL structured type that + * this Struct object maps to. The typeName is the name of a + * user-defined type that has been defined for this database. + * It is the value returned by Struct.getSQLTypeName. + * @param attributes the attributes that populate the returned object + * @return a Struct object that maps to the given SQL type and is + * populated with the given attributes + * @throws SQLException if a database error occurs, the typeName + * is null or this method is called on a closed connection + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this data type + * @since 1.6 + */ + @Override + public java.sql.Struct createStruct(String typeName, Object[] attributes) throws SQLException { + throw new SQLFeatureNotSupportedException("createStruct() not supported", "0A000"); + } - /** - * Constructs an object that implements the SQLXML interface. The - * object returned initially contains no data. The - * createXmlStreamWriter object and setString method of the SQLXML - * interface may be used to add data to the SQLXML object. - * - * @return An object that implements the SQLXML interface - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support this data type - * @since 1.6 - */ - @Override - public java.sql.SQLXML createSQLXML() throws SQLException { - throw new SQLFeatureNotSupportedException("createSQLXML() not supported", "0A000"); - } + /** + * Constructs an object that implements the SQLXML interface. The + * object returned initially contains no data. The + * createXmlStreamWriter object and setString method of the SQLXML + * interface may be used to add data to the SQLXML object. + * + * @return An object that implements the SQLXML interface + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this data type + * @since 1.6 + */ + @Override + public java.sql.SQLXML createSQLXML() throws SQLException { + throw new SQLFeatureNotSupportedException("createSQLXML() not supported", "0A000"); + } - /** - * Returns true if the connection has not been closed and is still - * valid. The driver shall submit a query on the connection or use - * some other mechanism that positively verifies the connection is - * still valid when this method is called. - * - * The query submitted by the driver to validate the connection - * shall be executed in the context of the current transaction. - * - * @param timeout The time in seconds to wait for the database - * operation used to validate the connection to complete. If - * the timeout period expires before the operation completes, - * this method returns false. A value of 0 indicates a - * timeout is not applied to the database operation. - * @return true if the connection is valid, false otherwise - * @throws SQLException if the value supplied for timeout is less than 0 - * @since 1.6 - */ - @Override - public boolean isValid(int timeout) throws SQLException { - if (timeout < 0) - throw new SQLException("timeout is less than 0", "M1M05"); - if (closed) - return false; + /** + * Returns true if the connection has not been closed and is still + * valid. The driver shall submit a query on the connection or use + * some other mechanism that positively verifies the connection is + * still valid when this method is called. + * + * The query submitted by the driver to validate the connection + * shall be executed in the context of the current transaction. + * + * @param timeout The time in seconds to wait for the database + * operation used to validate the connection to complete. If + * the timeout period expires before the operation completes, + * this method returns false. A value of 0 indicates a + * timeout is not applied to the database operation. + * @return true if the connection is valid, false otherwise + * @throws SQLException if the value supplied for timeout is less than 0 + * @since 1.6 + */ + @Override + public boolean isValid(int timeout) throws SQLException { + if (timeout < 0) + throw new SQLException("timeout is less than 0", "M1M05"); + if (closed) + return false; - // ping db using query: select 1; - Statement stmt = null; - ResultSet rs = null; - try { - stmt = createStatement(); - stmt.setQueryTimeout(timeout); - rs = stmt.executeQuery("SELECT 1"); - rs.close(); - rs = null; - stmt.close(); - return true; - } catch (Exception e) { - if (rs != null) { - try { - rs.close(); - } catch (Exception ignored) {} - } - if (stmt != null) { - try { - stmt.close(); - } catch (Exception ignored) {} - } - } - return false; - } - - /** - * Returns the value of the client info property specified by name. - * This method may return null if the specified client info property - * has not been set and does not have a default value. - * This method will also return null if the specified client info - * property name is not supported by the driver. - * Applications may use the DatabaseMetaData.getClientInfoProperties method - * to determine the client info properties supported by the driver. - * - * @param name - The name of the client info property to retrieve - * @return The value of the client info property specified or null - * @throws SQLException - if the database server returns an error - * when fetching the client info value from the database - * or this method is called on a closed connection - * @since 1.6 - */ - @Override - public String getClientInfo(String name) throws SQLException { - if (name == null || name.isEmpty()) - return null; - return conn_props.getProperty(name); - } + // ping db using query: select 1; + Statement stmt = null; + ResultSet rs = null; + boolean isValid = false; + try { + stmt = createStatement(); + if (stmt != null) { + int original_timeout = stmt.getQueryTimeout(); + if (timeout > 0 && original_timeout != timeout) { + // we need to change the requested timeout for this test query + stmt.setQueryTimeout(timeout); + } + rs = stmt.executeQuery("SELECT 1"); + if (rs != null && rs.next()) { + isValid = true; + } + if (timeout > 0 && original_timeout != timeout) { + // restore the original server timeout value + stmt.setQueryTimeout(original_timeout); + } + } + } catch (SQLException se) { + String msg = se.getMessage(); + if (msg != null && msg.equals("Current transaction is aborted (please ROLLBACK)")) { + isValid = true; + } + /* ignore stmt errors/exceptions, we are only testing if the connection is still alive and usable */ + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception e2) { /* ignore error */ } + } + if (stmt != null) { + try { + stmt.close(); + } catch (Exception e2) { /* ignore error */ } + } + } + return isValid; + } - /** - * Returns a list containing the name and current value of each client info - * property supported by the driver. The value of a client info property may - * be null if the property has not been set and does not have a default value. - * - * @return A Properties object that contains the name and current value - * of each of the client info properties supported by the driver. - * @throws SQLException - if the database server returns an error - * when fetching the client info value from the database - * or this method is called on a closed connection - * @since 1.6 - */ - @Override - public Properties getClientInfo() throws SQLException { - // return a clone of the connection properties object - return new Properties(conn_props); - } + /** + * Returns the value of the client info property specified by name. + * This method may return null if the specified client info property + * has not been set and does not have a default value. + * This method will also return null if the specified client info + * property name is not supported by the driver. + * Applications may use the DatabaseMetaData.getClientInfoProperties method + * to determine the client info properties supported by the driver. + * + * @param name - The name of the client info property to retrieve + * @return The value of the client info property specified or null + * @throws SQLException - if the database server returns an error + * when fetching the client info value from the database + * or this method is called on a closed connection + * @since 1.6 + */ + @Override + public String getClientInfo(String name) throws SQLException { + if (name == null || name.isEmpty()) + return null; + return conn_props.getProperty(name); + } + + /** + * Returns a list containing the name and current value of each client info + * property supported by the driver. The value of a client info property may + * be null if the property has not been set and does not have a default value. + * + * @return A Properties object that contains the name and current value + * of each of the client info properties supported by the driver. + * @throws SQLException - if the database server returns an error + * when fetching the client info value from the database + * or this method is called on a closed connection + * @since 1.6 + */ + @Override + public Properties getClientInfo() throws SQLException { + // return a clone of the connection properties object + return new Properties(conn_props); + } - /** - * Sets the value of the client info property specified by name to the value specified by value. - * Applications may use the DatabaseMetaData.getClientInfoProperties method to determine - * the client info properties supported by the driver and the maximum length that may be specified - * for each property. - * - * The driver stores the value specified in a suitable location in the database. For example - * in a special register, session parameter, or system table column. For efficiency the driver - * may defer setting the value in the database until the next time a statement is executed - * or prepared. Other than storing the client information in the appropriate place in the - * database, these methods shall not alter the behavior of the connection in anyway. - * The values supplied to these methods are used for accounting, diagnostics and debugging purposes only. - * - * The driver shall generate a warning if the client info name specified is not recognized by the driver. - * - * If the value specified to this method is greater than the maximum length for the property - * the driver may either truncate the value and generate a warning or generate a SQLClientInfoException. - * If the driver generates a SQLClientInfoException, the value specified was not set on the connection. - * - * The following are standard client info properties. Drivers are not required to support these - * properties however if the driver supports a client info property that can be described by one - * of the standard properties, the standard property name should be used. - * - * ApplicationName - The name of the application currently utilizing the connection - * ClientUser - The name of the user that the application using the connection is performing work for. - * This may not be the same as the user name that was used in establishing the connection. - * ClientHostname - The hostname of the computer the application using the connection is running on. - * - * @param name - The name of the client info property to set - * @param value - The value to set the client info property to. If the - * value is null, the current value of the specified property is cleared. - * @throws SQLClientInfoException - if the database server returns an error - * while setting the clientInfo values on the database server - * or this method is called on a closed connection - * @since 1.6 - */ - @Override - public void setClientInfo(String name, String value) throws java.sql.SQLClientInfoException { - if (name == null || name.isEmpty()) { - addWarning("setClientInfo: missing property name", "01M07"); - return; - } - // If the value is null, the current value of the specified property is cleared. - if (value == null) { - if (conn_props.containsKey(name)) - conn_props.remove(name); - return; - } - // only set value for supported property names - if (name.equals("host") || name.equals("port") || name.equals("user") || name.equals("password") || - name.equals("database") || name.equals("language") || name.equals("so_timeout") || - name.equals("hash") || name.equals("treat_blob_as_binary") || name.equals("follow_redirects") || - name.equals("treat_clob_as_longvarchar") || name.equals("embedded") || name.equals("directory")) { - conn_props.setProperty(name, value); - } else { - addWarning("setClientInfo: " + name + "is not a recognised property", "01M07"); - } - } + /** + * Sets the value of the client info property specified by name to the value specified by value. + * Applications may use the DatabaseMetaData.getClientInfoProperties method to determine + * the client info properties supported by the driver and the maximum length that may be specified + * for each property. + * + * The driver stores the value specified in a suitable location in the database. For example + * in a special register, session parameter, or system table column. For efficiency the driver + * may defer setting the value in the database until the next time a statement is executed + * or prepared. Other than storing the client information in the appropriate place in the + * database, these methods shall not alter the behavior of the connection in anyway. + * The values supplied to these methods are used for accounting, diagnostics and debugging purposes only. + * + * The driver shall generate a warning if the client info name specified is not recognized by the driver. + * + * If the value specified to this method is greater than the maximum length for the property + * the driver may either truncate the value and generate a warning or generate a SQLClientInfoException. + * If the driver generates a SQLClientInfoException, the value specified was not set on the connection. + * + * The following are standard client info properties. Drivers are not required to support these + * properties however if the driver supports a client info property that can be described by one + * of the standard properties, the standard property name should be used. + * + * ApplicationName - The name of the application currently utilizing the connection + * ClientUser - The name of the user that the application using the connection is performing work for. + * This may not be the same as the user name that was used in establishing the connection. + * ClientHostname - The hostname of the computer the application using the connection is running on. + * + * @param name - The name of the client info property to set + * @param value - The value to set the client info property to. If the + * value is null, the current value of the specified property is cleared. + * @throws SQLClientInfoException - if the database server returns an error + * while setting the clientInfo values on the database server + * or this method is called on a closed connection + * @since 1.6 + */ + @Override + public void setClientInfo(String name, String value) throws java.sql.SQLClientInfoException { + if (name == null || name.isEmpty()) { + addWarning("setClientInfo: missing property name", "01M07"); + return; + } + // If the value is null, the current value of the specified property is cleared. + if (value == null) { + if (conn_props.containsKey(name)) + conn_props.remove(name); + return; + } + // only set value for supported property names + if (name.equals("host") || name.equals("port") || name.equals("user") || name.equals("password") || + name.equals("database") || name.equals("language") || name.equals("so_timeout") || + name.equals("hash") || name.equals("treat_blob_as_binary") || name.equals("follow_redirects") || + name.equals("treat_clob_as_longvarchar") || name.equals("embedded") || name.equals("directory")) { + conn_props.setProperty(name, value); + } else { + addWarning("setClientInfo: " + name + "is not a recognised property", "01M07"); + } + } - /** - * Sets the value of the connection's client info properties. - * The Properties object contains the names and values of the client info - * properties to be set. The set of client info properties contained in the - * properties list replaces the current set of client info properties on the connection. - * If a property that is currently set on the connection is not present in the - * properties list, that property is cleared. Specifying an empty properties list - * will clear all of the properties on the connection. - * See setClientInfo (String, String) for more information. - * - * If an error occurs in setting any of the client info properties, a - * SQLClientInfoException is thrown. The SQLClientInfoException contains information - * indicating which client info properties were not set. The state of the client - * information is unknown because some databases do not allow multiple client info - * properties to be set atomically. For those databases, one or more properties may - * have been set before the error occurred. - * - * @param props - The list of client info properties to set - * @throws SQLClientInfoException - if the database server returns an error - * while setting the clientInfo values on the database server - * or this method is called on a closed connection - * @since 1.6 - */ - @Override - public void setClientInfo(Properties props) throws java.sql.SQLClientInfoException { - if (props != null) { - for (Map.Entry<Object, Object> entry : props.entrySet()) { - setClientInfo(entry.getKey().toString(), entry.getValue().toString()); - } - } - } + /** + * Sets the value of the connection's client info properties. + * The Properties object contains the names and values of the client info + * properties to be set. The set of client info properties contained in the + * properties list replaces the current set of client info properties on the connection. + * If a property that is currently set on the connection is not present in the + * properties list, that property is cleared. Specifying an empty properties list + * will clear all of the properties on the connection. + * See setClientInfo (String, String) for more information. + * + * If an error occurs in setting any of the client info properties, a + * SQLClientInfoException is thrown. The SQLClientInfoException contains information + * indicating which client info properties were not set. The state of the client + * information is unknown because some databases do not allow multiple client info + * properties to be set atomically. For those databases, one or more properties may + * have been set before the error occurred. + * + * @param props - The list of client info properties to set + * @throws SQLClientInfoException - if the database server returns an error + * while setting the clientInfo values on the database server + * or this method is called on a closed connection + * @since 1.6 + */ + @Override + public void setClientInfo(Properties props) throws java.sql.SQLClientInfoException { + if (props != null) { + for (Map.Entry<Object, Object> entry : props.entrySet()) { + setClientInfo(entry.getKey().toString(), entry.getValue().toString()); + } + } + } - //== Java 1.7 methods (JDBC 4.1) + /** + * Sets the given schema name to access. + * + * @param schema the name of a schema in which to work + * @throws SQLException if a database access error occurs or this method is called on a closed connection + * @since 1.7 + */ + @Override + public void setSchema(String schema) throws SQLException { + if (closed) + throw new SQLException("Cannot call on closed Connection", "M1M20"); + if (schema == null) + throw new SQLException("Missing schema name", "M1M05"); - /** - * Sets the given schema name to access. - * - * @param schema the name of a schema in which to work - * @throws SQLException if a database access error occurs or this method is called on a closed connection - * @since 1.7 - */ - @Override - public void setSchema(String schema) throws SQLException { - if (closed) - throw new SQLException("Cannot call on closed Connection", "M1M20"); - if (schema == null) - throw new SQLException("Missing schema name", "M1M05"); - - try (Statement st = createStatement()) { - st.execute("SET SCHEMA \"" + schema + "\""); - } - } + Statement st = createStatement(); + schema = schema.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'"); + try { + st.execute("SET SCHEMA \"" + schema + "\""); + } finally { + st.close(); + } + } - /** - * Retrieves this Connection object's current schema name. - * - * @return the current schema name or null if there is none - * @throws SQLException if a database access error occurs or this method is called on a closed connection - */ - @Override - public String getSchema() throws SQLException { - if (closed) { - throw new SQLException("Cannot call on closed Connection", "M1M20"); - } - String cur_schema; - Statement st = createStatement(); - ResultSet rs = null; - try { - rs = st.executeQuery("SELECT CURRENT_SCHEMA"); - if (!rs.next()) - throw new SQLException("Row expected", "02000"); - cur_schema = rs.getString(1); - } finally { - if (rs != null) - rs.close(); - st.close(); - } - return cur_schema; - } + /** + * Retrieves this Connection object's current schema name. + * + * @return the current schema name or null if there is none + * @throws SQLException if a database access error occurs or this method is called on a closed connection + */ + @Override + public String getSchema() throws SQLException { + if (closed) { + throw new SQLException("Cannot call on closed Connection", "M1M20"); + } + String cur_schema; + Statement st = createStatement(); + ResultSet rs = null; + try { + rs = st.executeQuery("SELECT CURRENT_SCHEMA"); + if (!rs.next()) + throw new SQLException("Row expected", "02000"); + cur_schema = rs.getString(1); + } finally { + if (rs != null) + rs.close(); + st.close(); + } + return cur_schema; + } - /** - * Terminates an open connection. Calling abort results in: - * * The connection marked as closed - * * Closes any physical connection to the database - * * Releases resources used by the connection - * * Insures that any thread that is currently accessing the - * connection will either progress to completion or throw an - * SQLException. - * Calling abort marks the connection closed and releases any - * resources. Calling abort on a closed connection is a no-op. - * - * @param executor The Executor implementation which will be used by abort - * @throws SQLException if a database access error occurs or the executor is null - * @throws SecurityException if a security manager exists and its checkPermission method denies calling abort - */ - @Override - public void abort(Executor executor) throws SQLException { - if (closed) - return; - if (executor == null) - throw new SQLException("executor is null", "M1M05"); - // this is really the simplest thing to do, it destroys - // everything (in particular the server connection) - close(); - } + /** + * Terminates an open connection. Calling abort results in: + * * The connection marked as closed + * * Closes any physical connection to the database + * * Releases resources used by the connection + * * Insures that any thread that is currently accessing the + * connection will either progress to completion or throw an + * SQLException. + * Calling abort marks the connection closed and releases any + * resources. Calling abort on a closed connection is a no-op. + * + * @param executor The Executor implementation which will be used by abort + * @throws SQLException if a database access error occurs or the executor is null + * @throws SecurityException if a security manager exists and its checkPermission method denies calling abort + */ + @Override + public void abort(Executor executor) throws SQLException { + if (closed) + return; + if (executor == null) + throw new SQLException("executor is null", "M1M05"); + // this is really the simplest thing to do, it destroys + // everything (in particular the server connection) + close(); + } - /** - * Sets the maximum period a Connection or objects created from the - * Connection will wait for the database to reply to any one - * request. If any request remains unanswered, the waiting method - * will return with a SQLException, and the Connection or objects - * created from the Connection will be marked as closed. Any - * subsequent use of the objects, with the exception of the close, - * isClosed or Connection.isValid methods, will result in a - * SQLException. - * - * @param executor The Executor implementation which will be used by setNetworkTimeout - * @param millis The time in milliseconds to wait for the database operation to complete - * @throws SQLException if a database access error occurs, this method is called on a closed connection, the - * executor is null, or the value specified for seconds is less than 0. - */ - @Override - public void setNetworkTimeout(Executor executor, int millis) throws SQLException { - if (closed) { - throw new SQLException("Cannot call on closed Connection", "M1M20"); - } - if (executor == null) - throw new SQLException("executor is null", "M1M05"); - if (millis < 0) - throw new SQLException("milliseconds is less than zero", "M1M05"); - this.setSoTimeout(millis); - } + /** + * Sets the maximum period a Connection or objects created from the + * Connection will wait for the database to reply to any one + * request. If any request remains unanswered, the waiting method + * will return with a SQLException, and the Connection or objects + * created from the Connection will be marked as closed. Any + * subsequent use of the objects, with the exception of the close, + * isClosed or Connection.isValid methods, will result in a + * SQLException. + * + * @param executor The Executor implementation which will be used by setNetworkTimeout + * @param millis The time in milliseconds to wait for the database operation to complete + * @throws SQLException if a database access error occurs, this method is called on a closed connection, the + * executor is null, or the value specified for seconds is less than 0. + */ + @Override + public void setNetworkTimeout(Executor executor, int millis) throws SQLException { + if (closed) + throw new SQLException("Cannot call on closed Connection", "M1M20"); + if (executor == null) + throw new SQLException("executor is null", "M1M05"); + if (millis < 0) + throw new SQLException("milliseconds is less than zero", "M1M05"); + + try { + this.setSoTimeout(millis); + } catch (SocketException e) { + throw new SQLNonTransientConnectionException(e.getMessage(), "08000"); + } + } - /** - * Retrieves the number of milliseconds the driver will wait for a database request to complete. If the limit is - * exceeded, a SQLException is thrown. - * - * @return the current timeout limit in milliseconds; zero means there is no limit - * @throws SQLException if a database access error occurs or this method is called on a closed Connection - */ - @Override - public int getNetworkTimeout() throws SQLException { - if (closed) { - throw new SQLException("Cannot call on closed Connection", "M1M20"); - } - return this.getSoTimeout(); - } + /** + * Retrieves the number of milliseconds the driver will wait for a + * database request to complete. If the limit is exceeded, a + * SQLException is thrown. + * + * @return the current timeout limit in milliseconds; zero means + * there is no limit + * @throws SQLException if a database access error occurs or + * this method is called on a closed Connection + * @since 1.7 + */ + @Override + public int getNetworkTimeout() throws SQLException { + if (closed) + throw new SQLException("Cannot call on closed Connection", "M1M20"); - //== end methods of interface Connection + try { + return this.getSoTimeout(); + } catch (SocketException e) { + throw new SQLNonTransientConnectionException(e.getMessage(), "08000"); + } + } + + //== end methods of interface Connection - /** - * Returns whether the BLOB type should be mapped to BINARY type. - */ - public boolean getBlobAsBinary() { - return blobIsBinary; - } + /** + * Returns whether the BLOB type should be mapped to BINARY type. + */ + public boolean getBlobAsBinary() { + return blobIsBinary; + } - /** - * Returns whether the CLOB type should be mapped to LONGVARCHAR type. - */ - public boolean getClobAsLongChar() { - return clobIsLongChar; - } + /** + * Returns whether the CLOB type should be mapped to LONGVARCHAR type. + */ + public boolean getClobAsLongChar() { + return clobIsLongChar; + } - /** - * Sends the given string to MonetDB as regular statement, making sure there is a prompt after the command is sent. - * All possible returned information is discarded. Encountered errors are reported. - * - * @param command the exact string to send to MonetDB - * @throws SQLException if an IO exception or a database error occurs - */ - void sendIndependentCommand(String command) throws SQLException { - try { - protocol.writeNextQuery(language.getQueryTemplateIndex(0), command, language.getQueryTemplateIndex(1)); - protocol.waitUntilPrompt(); - int csrh = protocol.getCurrentServerResponse(); - if (csrh == ServerResponses.ERROR) { - String error = protocol.getRemainingStringLine(0); - throw new SQLException(error.substring(6), error.substring(0, 5)); - } - } catch (SocketTimeoutException e) { - close(); // JDBC 4.1 semantics: abort() - throw new SQLException("connection timed out", "08M33"); - } catch (IOException e) { - throw new SQLException(e.getMessage(), "08000"); - } - } + /** + * Sends the given string to MonetDB as special transaction command. + * All possible returned information is discarded. + * Encountered errors are reported. + * + * @param command the exact string to send to MonetDB + * @throws SQLException if an IO exception or a database error occurs + */ + private void sendTransactionCommand(String command) throws SQLException { + // create a container for the result + ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY); + // send the appropriate query string to the database + try { + l.processQuery(command); + } finally { + l.close(); + } + } - /** - * Adds a warning to the pile of warnings this Connection object has. If there were no warnings (or clearWarnings - * was called) this warning will be the first, otherwise this warning will get appended to the current warning. - * - * @param reason the warning message - */ - protected void addWarning(String reason, String sqlstate) { - if (warnings == null) { - warnings = new SQLWarning(reason, sqlstate); - } else { - warnings.setNextWarning(new SQLWarning(reason, sqlstate)); - } - } + /** + * Sends the given string to MonetDB as regular statement, making sure there is a prompt after the command is sent. + * All possible returned information is discarded. Encountered errors are reported. + * + * @param command the exact string to send to MonetDB + * @throws SQLException if an IO exception or a database error occurs + */ + void sendIndependentCommand(String command) throws SQLException { + try { + protocol.writeNextQuery(language.getQueryTemplateIndex(0), command, language.getQueryTemplateIndex(1)); + protocol.waitUntilPrompt(); + int csrh = protocol.getCurrentServerResponse(); + if (csrh == ServerResponses.ERROR) { + String error = protocol.getRemainingStringLine(0); + throw new SQLException(error.substring(6), error.substring(0, 5)); + } + } catch (SocketTimeoutException e) { + close(); // JDBC 4.1 semantics: abort() + throw new SQLNonTransientConnectionException("connection timed out", "08M33"); + } catch (IOException e) { + throw new SQLNonTransientConnectionException(e.getMessage(), "08000"); + } + } - /** - * A list of Response objects. Responses are added to this list. Methods of this class are not synchronized. This is - * left as responsibility to the caller to prevent concurrent access. - */ - public class ResponseList { + /** + * Adds a warning to the pile of warnings this Connection object has. If there were no warnings (or clearWarnings + * was called) this warning will be the first, otherwise this warning will get appended to the current warning. + * + * @param reason the warning message + */ + void addWarning(String reason, String sqlstate) { + if (warnings == null) { + warnings = new SQLWarning(reason, sqlstate); + } else { + warnings.setNextWarning(new SQLWarning(reason, sqlstate)); + } + } - /** The cache size (number of rows in a DataBlockResponse object) */ - private final int cachesize; - /** The maximum number of results for this query */ - private final int maxrows; - /** The ResultSet type to produce */ - private final int rstype; - /** The ResultSet concurrency to produce */ - private final int rsconcur; - /** The sequence number of this ResponseList */ - private final int seqnr; - /** A list of the Responses associated with the query, in the right order */ - private final List<IResponse> responses = new ArrayList<>(); - /** A map of ResultSetResponses, used for additional DataBlockResponse mapping */ - private Map<Integer, ResultSetResponse> rsresponses; - /** The current header returned by getNextResponse() */ - private int curResponse = -1; + /** + * A list of Response objects. Responses are added to this list. Methods of this class are not synchronized. This is + * left as responsibility to the caller to prevent concurrent access. + */ + public class ResponseList { - /** - * Main constructor. The query argument can either be a String or List. An SQLException is thrown if another - * object instance is supplied. - * - * @param cachesize overall cachesize to use - * @param maxrows maximum number of rows to allow in the set - * @param rstype the type of result sets to produce - * @param rsconcur the concurrency of result sets to produce - */ - ResponseList(int cachesize, int maxrows, int rstype, int rsconcur) { - this.cachesize = cachesize; - this.maxrows = maxrows; - this.rstype = rstype; - this.rsconcur = rsconcur; - this.seqnr = SeqCounter++; - } + /** The cache size (number of rows in a DataBlockResponse object) */ + private final int cachesize; + /** The maximum number of results for this query */ + private final int maxrows; + /** The ResultSet type to produce */ + private final int rstype; + /** The ResultSet concurrency to produce */ + private final int rsconcur; + /** The sequence number of this ResponseList */ + private final int seqnr; + /** A list of the Responses associated with the query, in the right order */ + private final List<IResponse> responses = new ArrayList<>(); + /** A map of ResultSetResponses, used for additional DataBlockResponse mapping */ + private Map<Integer, ResultSetResponse> rsresponses; + /** The current header returned by getNextResponse() */ + private int curResponse = -1; - public int getCachesize() { - return cachesize; - } - - public int getRstype() { - return rstype; - } + /** + * Main constructor. The query argument can either be a String or List. An SQLException is thrown if another + * object instance is supplied. + * + * @param cachesize overall cachesize to use + * @param maxrows maximum number of rows to allow in the set + * @param rstype the type of result sets to produce + * @param rsconcur the concurrency of result sets to produce + */ + ResponseList(int cachesize, int maxrows, int rstype, int rsconcur) { + this.cachesize = cachesize; + this.maxrows = maxrows; + this.rstype = rstype; + this.rsconcur = rsconcur; + this.seqnr = SeqCounter++; + } - public int getRsconcur() { - return rsconcur; - } + public int getCachesize() { + return cachesize; + } + + public int getRstype() { + return rstype; + } - public int getMaxrows() { - return maxrows; - } + public int getRsconcur() { + return rsconcur; + } + + public int getMaxrows() { + return maxrows; + } - /** - * Retrieves the next available response, or null if there are no more responses. - * - * @return the next Response available or null - */ - IResponse getNextResponse() throws SQLException { - if (rstype == ResultSet.TYPE_FORWARD_ONLY) { - // free resources if we're running forward only - if (curResponse >= 0 && curResponse < responses.size()) { - IResponse tmp = responses.get(curResponse); - if (tmp != null) { - tmp.close(); - } - responses.set(curResponse, null); - } - } - curResponse++; - if (curResponse >= responses.size()) { - // ResponseList is obviously completed so, there are no more responses - return null; - } else { - // return this response - return responses.get(curResponse); - } - } + /** + * Retrieves the next available response, or null if there are no more responses. + * + * @return the next Response available or null + */ + IResponse getNextResponse() throws SQLException { + if (rstype == ResultSet.TYPE_FORWARD_ONLY) { + // free resources if we're running forward only + if (curResponse >= 0 && curResponse < responses.size()) { + IResponse tmp = responses.get(curResponse); + if (tmp != null) { + tmp.close(); + } + responses.set(curResponse, null); + } + } + curResponse++; + if (curResponse >= responses.size()) { + // ResponseList is obviously completed so, there are no more responses + return null; + } else { + // return this response + return responses.get(curResponse); + } + } - /** - * Closes the Response at index i, if not null. - * - * @param i the index position of the header to close - */ - void closeResponse(int i) { - if (i < 0 || i >= responses.size()) return; - IResponse tmp = responses.set(i, null); - if (tmp != null) - tmp.close(); - } - - /** - * Closes the current response. - */ - void closeCurrentResponse() { - closeResponse(curResponse); - } + /** + * Closes the Response at index i, if not null. + * + * @param i the index position of the header to close + */ + void closeResponse(int i) { + if (i < 0 || i >= responses.size()) return; + IResponse tmp = responses.set(i, null); + if (tmp != null) + tmp.close(); + } - /** - * Closes the current and previous responses. - */ - void closeCurOldResponses() { - for (int i = curResponse; i >= 0; i--) { - closeResponse(i); - } - } + /** + * Closes the current response. + */ + void closeCurrentResponse() { + closeResponse(curResponse); + } - /** - * Closes this ResponseList by closing all the Responses in this ResponseList. - */ - public void close() { - for (int i = 0; i < responses.size(); i++) { - closeResponse(i); - } - } + /** + * Closes the current and previous responses. + */ + void closeCurOldResponses() { + for (int i = curResponse; i >= 0; i--) { + closeResponse(i); + } + } - /** - * Returns whether this ResponseList has still unclosed Responses. - */ - boolean hasUnclosedResponses() { - for (IResponse r : responses) { - if (r != null) - return true; - } - return false; - } + /** + * Closes this ResponseList by closing all the Responses in this ResponseList. + */ + public void close() { + for (int i = 0; i < responses.size(); i++) { + closeResponse(i); + } + } - /** - * Executes the query contained in this ResponseList, and stores the Responses resulting from this query in this - * ResponseList. - * - * @throws SQLException if a database error occurs - */ - void processQuery(String query) throws SQLException { - this.executeQuery(language.getQueryTemplates(), query); - } - - /** - * Internal executor of queries. - * - * @param templ the template to fill in - * @param query the query to execute - * @throws SQLException if a database error occurs - */ - @SuppressWarnings("fallthrough") - public void executeQuery(String[] templ, String query) throws SQLException { - String error = null; + /** + * Returns whether this ResponseList has still unclosed Responses. + */ + boolean hasUnclosedResponses() { + for (IResponse r : responses) { + if (r != null) + return true; + } + return false; + } - try { - // make sure we're ready to send query; read data till we have the prompt it is possible (and most - // likely) that we already have the prompt and do not have to skip any lines. Ignore errors from - // previous result sets. - protocol.waitUntilPrompt(); + /** + * Executes the query contained in this ResponseList, and stores the Responses resulting from this query in this + * ResponseList. + * + * @throws SQLException if a database error occurs + */ + void processQuery(String query) throws SQLException { + this.executeQuery(language.getQueryTemplates(), query); + } - // {{{ set reply size - /* - * Change the reply size of the server. If the given value is the same as the current value known - * to use, then ignore this call. If it is set to 0 we get a prompt after the server sent it's - * header. - * - * 2017: For now, in the embedded connection, the value set cachesize will be always the default one. - */ - int size = (cachesize != 0 && !isEmbedded) ? cachesize : MonetConnection.this.getDefFetchsize(); - size = maxrows != 0 ? Math.min(maxrows, size) : size; - // don't do work if it's not needed - if (!isEmbedded && language.getRepresentation().equals("sql") && size != curReplySize && - !Arrays.deepEquals(templ, language.getCommandTemplates())) { - sendControlCommand(ControlCommands.REPLY_SIZE, size); - // store the reply size after a successful change - curReplySize = size; - } - // }}} set reply size + /** + * Internal executor of queries. + * + * @param templ the template to fill in + * @param query the query to execute + * @throws SQLException if a database error occurs + */ + @SuppressWarnings("fallthrough") + public void executeQuery(String[] templ, String query) throws SQLException { + String error = null; + + try { + // make sure we're ready to send query; read data till we have the prompt it is possible (and most + // likely) that we already have the prompt and do not have to skip any lines. Ignore errors from + // previous result sets. + protocol.waitUntilPrompt(); - // If the query is larger than the TCP buffer size, use a special send thread to avoid deadlock with - // the server due to blocking behaviour when the buffer is full. Because the server will be writing - // back results to us, it will eventually block as well when its TCP buffer gets full, as we are - // blocking an not consuming from it. The result is a state where both client and server want to - // write, but block. - if (query.length() > getBlockSize()) { - // get a reference to the send thread - if (senderThread == null) { - senderThread = new SenderThread(protocol); - } - // tell it to do some work! - senderThread.runQuery(templ, query); - } else { - // this is a simple call, which is a lot cheaper and will always succeed for small queries. - protocol.writeNextQuery((templ[0] == null) ? "" : templ[0], query, - (templ[1] == null) ? "" : templ[1]); - } + // {{{ set reply size + /* + * Change the reply size of the server. If the given value is the same as the current value known + * to use, then ignore this call. If it is set to 0 we get a prompt after the server sent it's + * header. + * + * 2017: For now, in the embedded connection, the value set cachesize will be always the default one. + */ + int size = (cachesize != 0 && !isEmbedded) ? cachesize : MonetConnection.this.getDefFetchsize(); + size = maxrows != 0 ? Math.min(maxrows, size) : size; + // don't do work if it's not needed + if (!isEmbedded && language.getRepresentation().equals("sql") && size != curReplySize && + !Arrays.deepEquals(templ, language.getCommandTemplates())) { + sendControlCommand(ControlCommands.REPLY_SIZE, size); + // store the reply size after a successful change + curReplySize = size; + } + // }}} set reply size - // go for new results - protocol.fetchNextResponseData(); - int nextResponse = protocol.getCurrentServerResponse(); - IResponse res = null; - while (nextResponse != ServerResponses.PROMPT) { - // each response should start with a start of header (or error) - switch (nextResponse) { - case ServerResponses.SOHEADER: - // make the response object, and fill it - int nextStartHeader = protocol.getNextStarterHeader(); - try { - switch (nextStartHeader) { - case StarterHeaders.Q_PARSE: - throw new ProtocolException("Q_PARSE header not allowed here"); - case StarterHeaders.Q_TABLE: - case StarterHeaders.Q_PREPARE: { - res = protocol.getNextResultSetResponse(MonetConnection.this, - ResponseList.this, this.seqnr, this.maxrows); - ResultSetResponse rsreponse = (ResultSetResponse) res; - if (rsresponses == null) { - rsresponses = new HashMap<>(); - } - rsresponses.put(rsreponse.getId(), rsreponse); - } - break; - case StarterHeaders.Q_UPDATE: - res = protocol.getNextUpdateResponse(); - break; - case StarterHeaders.Q_SCHEMA: - res = protocol.getNextSchemaResponse(); - break; - case StarterHeaders.Q_TRANS: - res = protocol.getNextAutoCommitResponse(); - boolean isAutoCommit = ((AutoCommitResponse) res).isAutocommit(); + // If the query is larger than the TCP buffer size, use a special send thread to avoid deadlock with + // the server due to blocking behaviour when the buffer is full. Because the server will be writing + // back results to us, it will eventually block as well when its TCP buffer gets full, as we are + // blocking an not consuming from it. The result is a state where both client and server want to + // write, but block. + if (query.length() > getBlockSize()) { + // get a reference to the send thread + if (senderThread == null) { + senderThread = new SenderThread(protocol); + } + // tell it to do some work! + senderThread.runQuery(templ, query); + } else { + // this is a simple call, which is a lot cheaper and will always succeed for small queries. + protocol.writeNextQuery((templ[0] == null) ? "" : templ[0], query, + (templ[1] == null) ? "" : templ[1]); + } + + // go for new results + protocol.fetchNextResponseData(); + int nextResponse = protocol.getCurrentServerResponse(); + IResponse res = null; + while (nextResponse != ServerResponses.PROMPT) { + // each response should start with a start of header (or error) + switch (nextResponse) { + case ServerResponses.SOHEADER: + // make the response object, and fill it + int nextStartHeader = protocol.getNextStarterHeader(); + try { + switch (nextStartHeader) { + case StarterHeaders.Q_PARSE: + throw new ProtocolException("Q_PARSE header not allowed here"); + case StarterHeaders.Q_TABLE: + case StarterHeaders.Q_PREPARE: { + res = protocol.getNextResultSetResponse(MonetConnection.this, + ResponseList.this, this.seqnr, this.maxrows); + ResultSetResponse rsreponse = (ResultSetResponse) res; + if (rsresponses == null) { + rsresponses = new HashMap<>(); + } + rsresponses.put(rsreponse.getId(), rsreponse); + } + break; + case StarterHeaders.Q_UPDATE: + res = protocol.getNextUpdateResponse(); + break; + case StarterHeaders.Q_SCHEMA: + res = protocol.getNextSchemaResponse(); + break; + case StarterHeaders.Q_TRANS: + res = protocol.getNextAutoCommitResponse(); + boolean isAutoCommit = ((AutoCommitResponse) res).isAutocommit(); - if (MonetConnection.this.getAutoCommit() && isAutoCommit) { - MonetConnection.this.addWarning("Server enabled auto commit mode " + - "while local state already was auto commit.", "01M11"); - } - MonetConnection.this.autoCommit = isAutoCommit; - break; - case StarterHeaders.Q_BLOCK: { - AbstractDataBlockResponse next = protocol.getNextDatablockResponse(rsresponses); - if (next == null) { - error = "M0M12!No ResultSetResponse for a DataBlock found"; - break; - } - res = next; - } - break; - } - } catch (ProtocolException e) { - error = "M0M10!error while parsing start of header:\n" + e.getMessage() + " found: '" - + protocol.getRemainingStringLine(0).charAt(e.getErrorOffset()) + "'" + - " in: \"" + protocol.getRemainingStringLine(0) + "\"" + " at pos: " - + e.getErrorOffset(); - // flush all the rest - protocol.waitUntilPrompt(); - nextResponse = protocol.getCurrentServerResponse(); - break; - } + if (MonetConnection.this.getAutoCommit() && isAutoCommit) { + MonetConnection.this.addWarning("Server enabled auto commit mode " + + "while local state already was auto commit.", "01M11"); + } + MonetConnection.this.autoCommit = isAutoCommit; + break; + case StarterHeaders.Q_BLOCK: { + AbstractDataBlockResponse next = protocol.getNextDatablockResponse(rsresponses); + if (next == null) { + error = "M0M12!No ResultSetResponse for a DataBlock found"; + break; + } + res = next; + } + break; + } + } catch (ProtocolException e) { + error = "M0M10!error while parsing start of header:\n" + e.getMessage() + " found: '" + + protocol.getRemainingStringLine(0).charAt(e.getErrorOffset()) + "'" + + " in: \"" + protocol.getRemainingStringLine(0) + "\"" + " at pos: " + + e.getErrorOffset(); + // flush all the rest + protocol.waitUntilPrompt(); + nextResponse = protocol.getCurrentServerResponse(); + break; + } + // immediately handle errors after parsing the header (res may be null) + if (error != null) { + protocol.waitUntilPrompt(); + nextResponse = protocol.getCurrentServerResponse(); + break; + } + // here we have a res object, which we can start filling + if (res instanceof IIncompleteResponse) { + IIncompleteResponse iter = (IIncompleteResponse) res; + while (iter.wantsMore()) { + try { + protocol.fetchNextResponseData(); + iter.addLines(protocol); + } catch (ProtocolException ex) { + // right, some protocol violation, skip the rest of the result + error = "M0M10!" + ex.getMessage(); + protocol.waitUntilPrompt(); + nextResponse = protocol.getCurrentServerResponse(); + break; + } + } + } - // immediately handle errors after parsing the header (res may be null) - if (error != null) { - protocol.waitUntilPrompt(); - nextResponse = protocol.getCurrentServerResponse(); - break; - } - - // here we have a res object, which we can start filling - if (res instanceof IIncompleteResponse) { - IIncompleteResponse iter = (IIncompleteResponse) res; - while (iter.wantsMore()) { - try { - protocol.fetchNextResponseData(); - iter.addLines(protocol); - } catch (ProtocolException ex) { - // right, some protocol violation, skip the rest of the result - error = "M0M10!" + ex.getMessage(); - protocol.waitUntilPrompt(); - nextResponse = protocol.getCurrentServerResponse(); - break; - } - } - } - - if (error != null) { - break; - } + if (error != null) { + break; + } - // it is of no use to store DataBlockResponses, you never want to retrieve them directly - // anyway - if (!(res instanceof AbstractDataBlockResponse)) { - responses.add(res); - } - // read the next line (can be prompt, new result, error, etc.) before we start the loop over - protocol.fetchNextResponseData(); - nextResponse = protocol.getCurrentServerResponse(); - break; - case ServerResponses.INFO: - addWarning(protocol.getRemainingStringLine(0), "01000"); - // read the next line (can be prompt, new result, error, etc.) before we start the loop over - protocol.fetchNextResponseData(); - nextResponse = protocol.getCurrentServerResponse(); - break; - case ServerResponses.ERROR: - // read everything till the prompt (should be error) we don't know if we ignore some - // garbage here... but the log should reveal that - error = protocol.getRemainingStringLine(0); - protocol.waitUntilPrompt(); - nextResponse = protocol.getCurrentServerResponse(); - break; - default: - throw new SQLException("Protocol violation, unexpected line!", "M0M10"); - } - } + // it is of no use to store DataBlockResponses, you never want to retrieve them directly + // anyway + if (!(res instanceof AbstractDataBlockResponse)) { + responses.add(res); + } + // read the next line (can be prompt, new result, error, etc.) before we start the loop over + protocol.fetchNextResponseData(); + nextResponse = protocol.getCurrentServerResponse(); + break; + case ServerResponses.INFO: + addWarning(protocol.getRemainingStringLine(0), "01000"); + // read the next line (can be prompt, new result, error, etc.) before we start the loop over + protocol.fetchNextResponseData(); + nextResponse = protocol.getCurrentServerResponse(); + break; + case ServerResponses.ERROR: + // read everything till the prompt (should be error) we don't know if we ignore some + // garbage here... but the log should reveal that + error = protocol.getRemainingStringLine(0); + protocol.waitUntilPrompt(); + nextResponse = protocol.getCurrentServerResponse(); + break; + default: + throw new SQLException("Protocol violation, unexpected line!", "M0M10"); + } + } - // if we used the senderThread, make sure it has finished - if (senderThread != null) { - String tmp = senderThread.getErrors(); - if (tmp != null) { - if (error == null) { - error = "08000!" + tmp; - } else { - error += "\n08000!" + tmp; - } - } - } - if (error != null) { - SQLException ret = null; - String[] errorsList = error.split("\n"); - for (String singleError : errorsList) { - String reason = isEmbedded() ? singleError : singleError.substring(6); - String sqlState = isEmbedded() ? "M0M10" : singleError.substring(0, 5); - if (ret == null) { - ret = new SQLException(reason, sqlState); - } else { - ret.setNextException(new SQLException(reason, sqlState)); - } - } - throw ret; - } - } catch (SocketTimeoutException e) { - this.close(); // JDBC 4.1 semantics, abort() - throw new SQLException("connection timed out", "08M33"); - } catch (IOException e) { - closed = true; - throw new SQLException(e.getMessage() + " (mserver still alive?)", "08000"); - } - } - } + // if we used the senderThread, make sure it has finished + if (senderThread != null) { + String tmp = senderThread.getErrors(); + if (tmp != null) { + if (error == null) { + error = "08000!" + tmp; + } else { + error += "\n08000!" + tmp; + } + } + } + if (error != null) { + SQLException ret = null; + String[] errorsList = error.split("\n"); + for (String singleError : errorsList) { + SQLException newErr; + String reason = isEmbedded() ? singleError : singleError.substring(6); + String sqlState = isEmbedded() ? "M0M10" : singleError.substring(0, 5); + if (singleError.length() >= 6) { + newErr = new SQLException(reason, sqlState); + } else { + newErr = new SQLNonTransientConnectionException(singleError, "08000"); + } + if (ret == null) { + ret = newErr; + } else { + ret.setNextException(newErr); + } + } + throw ret; + } + } catch (SocketTimeoutException e) { + this.close(); // JDBC 4.1 semantics, abort() + throw new SQLNonTransientConnectionException("connection timed out", "08M33"); + } catch (IOException e) { + closed = true; + throw new SQLNonTransientConnectionException(e.getMessage() + " (mserver5 still alive?)", "08006"); + } + } + } }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java @@ -34,11 +34,11 @@ public class MonetDataSource extends Mon private String user; private String password; // insecure, but how to do it better? - private String description = "MonetDB database"; + private String description = "MonetDB database"; private String url = "jdbc:monetdb://localhost/"; - private int loginTimeout; - private String directory; - private final MonetDriver driver = new MonetDriver(); + private int loginTimeout; + private String directory; + private final MonetDriver driver = new MonetDriver(); // the following properties are also standard: // private String dataSourceName; @@ -46,7 +46,7 @@ public class MonetDataSource extends Mon // private String serverName; // private String role; - public MonetDataSource() {} + public MonetDataSource() {} /** * Attempts to establish a connection with the data source that this DataSource object represents. @@ -72,13 +72,13 @@ public class MonetDataSource extends Mon Properties props = new Properties(); props.put("user", username); props.put("password", password); - if (loginTimeout > 0) { - props.put("so_timeout", Integer.toString(loginTimeout)); - } + if (loginTimeout > 0) { + props.put("so_timeout", Integer.toString(loginTimeout)); + } if(directory != null) { - props.put("embedded", "true"); + props.put("embedded", "true"); props.put("directory", directory); - } + } return driver.connect(url, props); } @@ -184,34 +184,34 @@ public class MonetDataSource extends Mon this.description = description; } - /** - * Gets the directory value - * - * @return the directory value - */ - public String getDirectory() { - return directory; - } + /** + * Gets the directory value + * + * @return the directory value + */ + public String getDirectory() { + return directory; + } - /** - * Sets the directory value, meaning it wil start an embedded connection - * - * @param directory The directory location - */ - public void setDirectory(String directory) { - this.directory = directory; - } + /** + * Sets the directory value, meaning it wil start an embedded connection + * + * @param directory The directory location + */ + public void setDirectory(String directory) { + this.directory = directory; + } - /** - * Gets the embedded connection directory. If not, then a MAPI connection will be created instead. - * - * @return If the connection will be embedded. If not, then a MAPI connection will be created instead. - */ - public boolean isEmbedded() { - return directory != null; - } + /** + * Gets the embedded connection directory. If not, then a MAPI connection will be created instead. + * + * @return If the connection will be embedded. If not, then a MAPI connection will be created instead. + */ + public boolean isEmbedded() { + return directory != null; + } - /** + /** * Return the parent Logger of all the Loggers used by this data * source. This should be the Logger farthest from the root Logger * that is still an ancestor of all of the Loggers used by this data
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java @@ -46,7 +46,7 @@ public class MonetDatabaseMetaData exten try { st = con.createStatement(); rs = st.executeQuery( - "SELECT \"name\", \"value\" FROM \"sys\".\"environment\"" + + "SELECT \"name\", \"value\" FROM \"sys\".\"env\"()" + " WHERE \"name\" IN ('monet_version', 'max_clients')" + " UNION SELECT 'current_user' as \"name\", current_user as \"value\""); if (rs != null) { @@ -2195,7 +2195,7 @@ public class MonetDatabaseMetaData exten "cast(null AS varchar(1)) AS \"SCOPE_CATALOG\", " + "cast(null AS varchar(1)) AS \"SCOPE_SCHEMA\", " + "cast(null AS varchar(1)) AS \"SCOPE_TABLE\", " + - "cast(").append(MonetDriver.getJavaType("other")).append(" AS smallint) AS \"SOURCE_DATA_TYPE\", " + + "cast(null AS smallint) AS \"SOURCE_DATA_TYPE\", " + "cast(CASE WHEN \"columns\".\"default\" IS NOT NULL AND \"columns\".\"default\" LIKE 'next value for %' THEN 'YES' ELSE 'NO' END AS varchar(3)) AS \"IS_AUTOINCREMENT\", " + "cast('NO' AS varchar(3)) AS \"IS_GENERATEDCOLUMN\" " + "FROM \"sys\".\"columns\", " +
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in @@ -15,6 +15,7 @@ import nl.cwi.monetdb.mcl.protocol.Proto import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.sql.Connection; @@ -23,6 +24,7 @@ import java.sql.DriverManager; import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLNonTransientConnectionException; import java.sql.Types; import java.util.*; import java.util.Map.Entry; @@ -91,6 +93,7 @@ public final class MonetDriver implement * @param url the URL of the database * @return true if this driver understands the given URL; false otherwise */ + @Override public boolean acceptsURL(String url) { return url != null && url.startsWith(MONETURL); } @@ -100,6 +103,7 @@ public final class MonetDriver implement * * @return this driver's major version number */ + @Override public int getMajorVersion() { return DRIVERMAJOR; } @@ -109,6 +113,7 @@ public final class MonetDriver implement * * @return this driver's minor version number */ + @Override public int getMinorVersion() { return DRIVERMINOR; } @@ -128,6 +133,7 @@ public final class MonetDriver implement * @return an array of DriverPropertyInfo objects describing possible properties. This array may be an empty array * if no properties are required. */ + @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { if (!acceptsURL(url)) return null; @@ -196,6 +202,7 @@ public final class MonetDriver implement * * @return true if this driver is JDBC Compliant; false otherwise */ + @Override public boolean jdbcCompliant() { return MONETJDBCCOMPLIANT; } @@ -302,16 +309,17 @@ public final class MonetDriver implement } /** - * Return the parent Logger of all the Loggers used by this data - * source. This should be the Logger farthest from the root Logger - * that is still an ancestor of all of the Loggers used by this data - * source. Configuring this Logger will affect all of the log - * messages generated by the data source. In the worst case, this - * may be the root Logger. + * Return the parent Logger of all the Loggers used by this data source. + * This should be the Logger farthest from the root Logger that is + * still an ancestor of all of the Loggers used by this data source. + * Configuring this Logger will affect all of the log messages + * generated by the data source. In the worst case, this may be the root Logger. * * @return the parent Logger for this data source * @throws SQLFeatureNotSupportedException if the data source does not use java.util.logging + * @since 1.7 */ + @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException("java.util.logging not in use", "0A000"); } @@ -331,9 +339,8 @@ public final class MonetDriver implement * "password" properties should be included in the Properties object. * * @param url the URL of the database to which to connect - * @param info a list of arbitrary string tag/value pairs as connection - * arguments. Normally at least a "user" and "password" property - * should be included + * @param info a list of arbitrary string tag/value pairs as connection arguments. Normally at least a "user" and + * "password" property should be included * @return a Connection object that represents a connection to the URL * @throws SQLException if a database access error occurs */ @@ -415,7 +422,7 @@ public final class MonetDriver implement if(embeddedConnectionClass == null) { embeddedConnectionClass = Class.forName("nl.cwi.monetdb.embedded.jdbc.EmbeddedConnection"); if(embeddedConnectionClass == null) { //if it is still null then there is a problem! - throw new SQLException("EmbeddedConnection Class not found! Please add monetdb-java-lite jar to the CLASSPATH"); + throw new SQLNonTransientConnectionException("EmbeddedConnection Class not found! Please add monetdb-java-lite jar to the CLASSPATH"); } } res = (MonetConnection) embeddedConnectionClass @@ -423,7 +430,7 @@ public final class MonetDriver implement .newInstance(props, hash, language, directory); } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) { - throw new SQLException(e); + throw new SQLNonTransientConnectionException(e); } } else { String hostname = props.getProperty("host"); @@ -468,11 +475,7 @@ public final class MonetDriver implement sockTimeout = 0; props.setProperty("so_timeout", "0"); } - try { - res = new MapiConnection(props, hash, language, blobIsBinary, clobIsLongChar, hostname, port, database); - } catch (IOException e) { - throw new SQLException(e); - } + res = new MapiConnection(props, hash, language, blobIsBinary, clobIsLongChar, hostname, port, database); if(failedparse1) { res.addWarning("Unable to parse port number from: " + port, "M1M05"); } @@ -485,7 +488,11 @@ public final class MonetDriver implement if(negative2) { res.addWarning("Negative socket timeout not allowed. Value ignored", "M1M05"); } - res.setSoTimeout(sockTimeout); + try { + res.setSoTimeout(sockTimeout); + } catch(SocketException ex) { + res.addWarning("Failed to set socket timeout: " + ex.getMessage(), "M1M05"); + } } try { //attempt to connect and authenticate the user @@ -505,15 +512,15 @@ public final class MonetDriver implement throw new SQLException("Unable to connect (" + con.getHostname() + ":" + con.getPort() + "): " + e.getMessage(), "08006"); } else { - throw new SQLException("Unable to connect: " + e.getMessage(), "08006"); + throw new SQLNonTransientConnectionException("Unable to connect: " + e.getMessage(), "08006"); } } catch (ProtocolException e) { - throw new SQLException(e.getMessage(), "08001"); + throw new SQLNonTransientConnectionException(e.getMessage(), "08001"); } catch (MCLException e) { String[] connex = e.getMessage().split("\n"); - SQLException sqle = new SQLException(connex[0], "08001", e); + SQLException sqle = new SQLNonTransientConnectionException(connex[0], "08001", e); for (int i = 1; i < connex.length; i++) { - sqle.setNextException(new SQLException(connex[1], "08001")); + sqle.setNextException(new SQLNonTransientConnectionException(connex[1], "08001")); } throw sqle; }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java @@ -46,9 +46,9 @@ import java.util.Map; * </pre> * * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira - * @version 0.4 + * @version 0.5v */ -public class MonetPreparedStatement extends MonetStatement implements PreparedStatement { +public class MonetPreparedStatement extends MonetStatement implements PreparedStatement, AutoCloseable { private final MonetConnection connection; private final String[] monetdbType; @@ -106,18 +106,30 @@ public class MonetPreparedStatement exte // fill the arrays ResultSet rs = super.getResultSet(); - for (int i = 0; rs.next(); i++) { - monetdbType[i] = rs.getString("type"); - javaType[i] = MonetDriver.getJavaType(monetdbType[i]); - digits[i] = rs.getInt("digits"); - scale[i] = rs.getInt("scale"); - if (rscolcnt == 3) - continue; - schema[i] = rs.getString("schema"); - table[i] = rs.getString("table"); - column[i] = rs.getString("column"); + if (rs != null) { + // System.out.println("After super.getResultSet();"); + int type_colnr = rs.findColumn("type"); + int digits_colnr = rs.findColumn("digits"); + int scale_colnr = rs.findColumn("scale"); + int schema_colnr = rs.findColumn("schema"); + int table_colnr = rs.findColumn("table"); + int column_colnr = rs.findColumn("column"); + for (int i = 0; rs.next(); i++) { + monetdbType[i] = rs.getString(type_colnr); + javaType[i] = MonetDriver.getJavaType(monetdbType[i]); + digits[i] = rs.getInt(digits_colnr); + scale[i] = rs.getInt(scale_colnr); + if (rscolcnt == 3) + continue; + schema[i] = rs.getString(schema_colnr); + table[i] = rs.getString(table_colnr); + column[i] = rs.getString(column_colnr); + /* when column[i] != null it is a result column of the prepared query, see getColumnIdx(int), + when column[i] == null it is a parameter for the prepared statement, see getParamIdx(int). */ + // System.out.println("column " + i + " has value: " + column[i]); + } + rs.close(); } - rs.close(); // PreparedStatements are by default poolable poolable = true; @@ -238,6 +250,7 @@ public class MonetPreparedStatement exte private int getColumnIdx(int colnr) throws SQLException { int curcol = 0; for (int i = 0; i < size; i++) { + /* when column[i] == null it is a parameter, when column[i] != null it is a result column of the prepared query */ if (column[i] == null) continue; curcol++; @@ -254,6 +267,7 @@ public class MonetPreparedStatement exte private int getParamIdx(int paramnr) throws SQLException { int curparam = 0; for (int i = 0; i < size; i++) { + /* when column[i] == null it is a parameter, when column[i] != null it is a result column of the prepared query */ if (column[i] != null) continue; curparam++; @@ -800,13 +814,13 @@ public class MonetPreparedStatement exte * Sets the designated parameter to the given Array object. The * driver converts this to an SQL ARRAY value when it sends it to * the database. - * - * @param i the first parameter is 1, the second is 2, ... + * + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x an Array object that maps an SQL ARRAY value * @throws SQLException if a database access error occurs */ @Override - public void setArray(int i, Array x) throws SQLException { + public void setArray(int parameterIndex, Array x) throws SQLException { throw newSQLFeatureNotSupportedException("setArray"); } @@ -882,14 +896,14 @@ public class MonetPreparedStatement exte * The driver converts this to an SQL NUMERIC value when it sends it to the * database. * - * @param idx the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @throws SQLException if a database access error occurs */ @Override - public void setBigDecimal(int idx, BigDecimal x) throws SQLException { + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { // get array position - int i = getParamIdx(idx); + int i = getParamIdx(parameterIndex); // round to the scale of the DB: x = x.setScale(scale[i], RoundingMode.HALF_UP); @@ -910,7 +924,7 @@ public class MonetPreparedStatement exte xStr = xStr.substring(0, Math.min(xStr.length(), dot + 1 + scale[i])); while (xStr.startsWith("0") && xStr.length() > 1) xStr = xStr.substring(1); - setValue(idx, xStr); + setValue(parameterIndex, xStr); } /** @@ -983,12 +997,12 @@ public class MonetPreparedStatement exte * converts this to an SQL BLOB value when it sends it to the database. * * @param parameterIndex the first parameter is 1, the second is 2, ... - * @param stream an object that contains the data to set the parameter value to + * @param x a Blob object that maps an SQL BLOB value * @throws SQLException if a database access error occurs */ @Override - public void setBlob(int parameterIndex, InputStream stream) throws SQLException { - if (stream == null) { + public void setBlob(int parameterIndex, InputStream x) throws SQLException { + if (x == null) { setNull(parameterIndex, -1); return; } @@ -997,7 +1011,7 @@ public class MonetPreparedStatement exte ByteArrayOutputStream buf = new ByteArrayOutputStream(); int numChars; try { - while ((numChars = stream.read(arr, 0, arr.length)) > 0) { + while ((numChars = x.read(arr, 0, arr.length)) > 0) { buf.write(arr, 0, numChars); } setBytes(parameterIndex, buf.toByteArray()); @@ -1035,13 +1049,13 @@ public class MonetPreparedStatement exte * should be sent to the server as a LONGVARBINARY or a BLOB. * * @param parameterIndex the first parameter is 1, the second is 2, ... - * @param stream an object that contains the data to set the parameter value to + * @param is an object that contains the data to set the parameter value to * @param length the number of bytes in the parameter data * @throws SQLException if a database access error occurs */ @Override - public void setBlob(int parameterIndex, InputStream stream, long length) throws SQLException { - if (stream == null) { + public void setBlob(int parameterIndex, InputStream is, long length) throws SQLException { + if (is == null) { setNull(parameterIndex, -1); return; } @@ -1049,7 +1063,7 @@ public class MonetPreparedStatement exte byte[] arr = new byte[(int) length]; ByteArrayOutputStream buf = new ByteArrayOutputStream((int) length); - int numChars = stream.read(arr, 0, (int) length); + int numChars = is.read(arr, 0, (int) length); buf.write(arr, 0, numChars); setBytes(parameterIndex, buf.toByteArray()); } catch (IOException e) { @@ -1187,36 +1201,36 @@ public class MonetPreparedStatement exte * Sets the designated parameter to the given Clob object. The driver * converts this to an SQL CLOB value when it sends it to the database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x a Clob object that maps an SQL CLOB value * @throws SQLException if a database access error occurs */ @Override - public void setClob(int i, Clob x) throws SQLException { + public void setClob(int parameterIndex, Clob x) throws SQLException { if (x == null) { - setNull(i, -1); + setNull(parameterIndex, -1); return; } // simply serialise the CLOB into a variable for now... far from // efficient, but might work for a few cases... // be on your marks: we have to cast the length down! - setString(i, x.getSubString(1L, (int)(x.length()))); + setString(parameterIndex, x.getSubString(1L, (int)(x.length()))); } /** * Sets the designated parameter to the given Clob object. The driver * converts this to an SQL CLOB value when it sends it to the database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param reader an object that contains the data to set the parameter * value to * @throws SQLException if a database access error occurs */ @Override - public void setClob(int i, Reader reader) throws SQLException { + public void setClob(int parameterIndex, Reader reader) throws SQLException { if (reader == null) { - setNull(i, -1); + setNull(parameterIndex, -1); return; } // Some buffer. Size of 8192 is default for BufferedReader, so... @@ -1227,7 +1241,7 @@ public class MonetPreparedStatement exte while ((numChars = reader.read(arr, 0, arr.length)) > 0) { buf.append(arr, 0, numChars); } - setString(i, buf.toString()); + setString(parameterIndex, buf.toString()); } catch (IOException e) { throw new SQLException(e); } @@ -1244,16 +1258,16 @@ public class MonetPreparedStatement exte * extra work to determine whether the parameter data should be sent * to the server as a LONGVARCHAR or a CLOB. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param reader An object that contains the data to set the * parameter value to. * @param length the number of characters in the parameter data. * @throws SQLException if a database access error occurs */ @Override - public void setClob(int i, Reader reader, long length) throws SQLException { + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { if (reader == null || length < 0) { - setNull(i, -1); + setNull(parameterIndex, -1); return; } // simply serialise the CLOB into a variable for now... far from @@ -1267,7 +1281,7 @@ public class MonetPreparedStatement exte } // We have to rewind the buffer, because otherwise toString() returns "". buf.rewind(); - setString(i, buf.toString()); + setString(parameterIndex, buf.toString()); } /** @@ -1371,15 +1385,15 @@ public class MonetPreparedStatement exte * necessary conversion from Java character format to the national * character set in the database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param value the parameter value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override - public void setNCharacterStream(int i, Reader value) throws SQLException { - throw newSQLFeatureNotSupportedException("setNCharacterStream"); + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + setCharacterStream(parameterIndex, value, 0); } /** @@ -1388,7 +1402,7 @@ public class MonetPreparedStatement exte * necessary conversion from Java character format to the national * character set in the database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param value the parameter value * @param length the number of characters in the parameter data. * @throws SQLException if a database access error occurs @@ -1396,8 +1410,8 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setNCharacterStream(int i, Reader value, long length) throws SQLException { - throw newSQLFeatureNotSupportedException("setNCharacterStream"); + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + setCharacterStream(parameterIndex, value, length); } /** @@ -1405,14 +1419,14 @@ public class MonetPreparedStatement exte * driver converts this to a SQL NCLOB value when it sends it to the * database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param value the parameter value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override - public void setNClob(int i, Reader value) throws SQLException { + public void setNClob(int parameterIndex, Reader value) throws SQLException { throw newSQLFeatureNotSupportedException("setNClob"); } @@ -1421,14 +1435,14 @@ public class MonetPreparedStatement exte * driver converts this to a SQL NCLOB value when it sends it to the * database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param value the parameter value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override - public void setNClob(int i, NClob value) throws SQLException { + public void setNClob(int parameterIndex, NClob value) throws SQLException { throw newSQLFeatureNotSupportedException("setNClob"); } @@ -1443,7 +1457,7 @@ public class MonetPreparedStatement exte * extra work to determine whether the parameter data should be sent * to the server as a LONGNVARCHAR or a NCLOB. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param r An object that contains the data to set the parameter * value to * @param length the number of characters in the parameter data @@ -1452,7 +1466,7 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setNClob(int i, Reader r, long length) throws SQLException { + public void setNClob(int parameterIndex, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("setNClob"); } @@ -1462,15 +1476,15 @@ public class MonetPreparedStatement exte * value (depending on the argument's size relative to the driver's * limits on NVARCHAR values) when it sends it to the database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param value the parameter value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override - public void setNString(int i, String value) throws SQLException { - throw newSQLFeatureNotSupportedException("setNString"); + public void setNString(int parameterIndex, String value) throws SQLException { + setString(parameterIndex, value); } /** @@ -1505,7 +1519,7 @@ public class MonetPreparedStatement exte * parameter of any JDBC type. If the parameter does not have a * user-defined or REF type, the given typeName is ignored. * - * @param paramIndex the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param sqlType a value from java.sql.Types * @param typeName the fully-qualified name of an SQL user-defined type; * ignored if the parameter is not a user-defined type or @@ -1513,9 +1527,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException { + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { // MonetDB/SQL's NULL needs no type - setNull(paramIndex, sqlType); + setNull(parameterIndex, sqlType); } /** @@ -1540,14 +1554,14 @@ public class MonetPreparedStatement exte * example, if the object is of a class implementing more than one * of the interfaces named above. * - * @param index the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the object containing the input parameter value * @throws SQLException if a database access error occurs or the type of * the given object is ambiguous */ @Override - public void setObject(int index, Object x) throws SQLException { - setObject(index, x, javaType[getParamIdx(index)]); + public void setObject(int parameterIndex, Object x) throws SQLException { + setObject(parameterIndex, x, javaType[getParamIdx(parameterIndex)], 0); } /** @@ -1600,127 +1614,14 @@ public class MonetPreparedStatement exte * @see Types */ @Override - public void setObject( - int parameterIndex, - Object x, - int targetSqlType, - int scale) - throws SQLException - { + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException { + if (x == null) { + setNull(parameterIndex, -1); + return; + } // this is according to table B-5 if (x instanceof String) { - switch (targetSqlType) { - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.CLOB: - setString(parameterIndex, (String)x); - break; - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - { - int val; - try { - val = Integer.parseInt((String)x); - } catch (NumberFormatException e) { - val = 0; - } - setInt(parameterIndex, val); - } break; - case Types.BIGINT: - { - long val; - try { - val = Long.parseLong((String)x); - } catch (NumberFormatException e) { - val = 0; - } - setLong(parameterIndex, val); - } break; - case Types.REAL: - { - float val; - try { - val = Float.parseFloat((String)x); - } catch (NumberFormatException e) { - val = 0; - } - setFloat(parameterIndex, val); - } break; - case Types.FLOAT: - case Types.DOUBLE: - { - double val; - try { - val = Double.parseDouble((String)x); - } catch (NumberFormatException e) { - val = 0; - } - setDouble(parameterIndex, val); - } break; - case Types.DECIMAL: - case Types.NUMERIC: - { - BigDecimal val; - try { - val = new BigDecimal((String)x); - } catch (NumberFormatException e) { - try { - val = new BigDecimal(0.0); - } catch (NumberFormatException ex) { - throw new SQLException("Internal error: unable to create template BigDecimal: " + ex.getMessage(), "M0M03"); - } - } - val = val.setScale(scale, BigDecimal.ROUND_HALF_UP); - setBigDecimal(parameterIndex, val); - } break; - case Types.BIT: - case Types.BOOLEAN: - setBoolean(parameterIndex, (Boolean.valueOf((String)x)).booleanValue()); - break; - case Types.BINARY: - case Types.VARBINARY: - case Types.LONGVARBINARY: - setBytes(parameterIndex, ((String)x).getBytes()); - break; - case Types.DATE: - { - java.sql.Date val; - try { - val = java.sql.Date.valueOf((String)x); - } catch (IllegalArgumentException e) { - val = new java.sql.Date(0L); - } - setDate(parameterIndex, val); - } break; - case Types.TIME: - { - Time val; - try { - val = Time.valueOf((String)x); - } catch (IllegalArgumentException e) { - val = new Time(0L); - } - setTime(parameterIndex, val); - } break; - case Types.TIMESTAMP: - { - Timestamp val; - try { - val = Timestamp.valueOf((String)x); - } catch (IllegalArgumentException e) { - val = new Timestamp(0L); - } - setTimestamp(parameterIndex, val); - } break; - case Types.NCHAR: - case Types.NVARCHAR: - case Types.LONGNVARCHAR: - throw newSQLFeatureNotSupportedException("setObject() with targetType N[VAR]CHAR"); - default: - throw new SQLException("Conversion not allowed", "M1M05"); - } + setString(parameterIndex, (String)x); } else if (x instanceof BigDecimal || x instanceof Byte || x instanceof Short || @@ -1879,13 +1780,13 @@ public class MonetPreparedStatement exte setDate(parameterIndex, new java.sql.Date( ((java.util.Date)x).getTime())); } else if (x instanceof Calendar) { - setDate(parameterIndex, new java.sql.Date( - ((Calendar)x).getTimeInMillis())); + setDate(parameterIndex, new java.sql.Date(((Calendar)x).getTimeInMillis())); } else { throw new SQLException("Conversion not allowed", "M1M05"); } break; case Types.TIME: + case 2013: //Types.TIME_WITH_TIMEZONE: if (x instanceof Time) { setTime(parameterIndex, (Time)x); } else if (x instanceof Timestamp) { @@ -1894,13 +1795,13 @@ public class MonetPreparedStatement exte setTime(parameterIndex, new java.sql.Time( ((java.util.Date)x).getTime())); } else if (x instanceof Calendar) { - setTime(parameterIndex, new java.sql.Time( - ((Calendar)x).getTimeInMillis())); + setTime(parameterIndex, new java.sql.Time(((Calendar)x).getTimeInMillis())); } else { throw new SQLException("Conversion not allowed", "M1M05"); } break; case Types.TIMESTAMP: + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: if (x instanceof Timestamp) { setTimestamp(parameterIndex, (Timestamp)x); } else if (x instanceof java.sql.Date) { @@ -1909,8 +1810,7 @@ public class MonetPreparedStatement exte setTimestamp(parameterIndex, new java.sql.Timestamp( ((java.util.Date)x).getTime())); } else if (x instanceof Calendar) { - setTimestamp(parameterIndex, new java.sql.Timestamp( - ((Calendar)x).getTimeInMillis())); + setTimestamp(parameterIndex, new java.sql.Timestamp(((Calendar)x).getTimeInMillis())); } else { throw new SQLException("Conversion not allowed", "M1M05"); } @@ -1926,9 +1826,9 @@ public class MonetPreparedStatement exte } } else if (x instanceof Array) { setArray(parameterIndex, (Array)x); - } else if (x instanceof Blob) { + } else if (x instanceof Blob || x instanceof MonetBlob) { setBlob(parameterIndex, (Blob)x); - } else if (x instanceof Clob) { + } else if (x instanceof Clob || x instanceof MonetClob) { setClob(parameterIndex, (Clob)x); } else if (x instanceof Struct) { // I have no idea how to do this... @@ -1937,12 +1837,14 @@ public class MonetPreparedStatement exte setRef(parameterIndex, (Ref)x); } else if (x instanceof java.net.URL) { setURL(parameterIndex, (java.net.URL)x); + } else if (x instanceof java.util.UUID) { + setString(parameterIndex, x.toString()); } else if (x instanceof RowId) { setRowId(parameterIndex, (RowId)x); } else if (x instanceof NClob) { - throw newSQLFeatureNotSupportedException("setObject() with object of type NClob"); + setNClob(parameterIndex, (NClob)x); } else if (x instanceof SQLXML) { - throw newSQLFeatureNotSupportedException("setObject() with object of type SQLXML"); + setSQLXML(parameterIndex, (SQLXML)x); } else if (x instanceof SQLData) { // not in JDBC4.1??? SQLData sx = (SQLData)x; final int paramnr = parameterIndex; @@ -2022,7 +1924,7 @@ public class MonetPreparedStatement exte @Override public void writeCharacterStream(Reader x) throws SQLException { - setCharacterStream(paramnr, x); + setCharacterStream(paramnr, x, 0); } @Override @@ -2092,7 +1994,7 @@ public class MonetPreparedStatement exte }; sx.writeSQL(out); } else { // java Class - throw newSQLFeatureNotSupportedException("setObject() with object of type Class"); + throw newSQLFeatureNotSupportedException("setObject() with object of type Class " + x.getClass().getName()); } } @@ -2101,14 +2003,14 @@ public class MonetPreparedStatement exte * The driver converts this to an SQL REF value when it sends it to the * database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x an SQL REF value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override - public void setRef(int i, Ref x) throws SQLException { + public void setRef(int parameterIndex, Ref x) throws SQLException { throw newSQLFeatureNotSupportedException("setRef"); } @@ -2117,14 +2019,14 @@ public class MonetPreparedStatement exte * The driver converts this to a SQL ROWID value when it sends it to * the database. * - * @param i the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override - public void setRowId(int i, RowId x) throws SQLException { + public void setRowId(int parameterIndex, RowId x) throws SQLException { throw newSQLFeatureNotSupportedException("setRowId"); } @@ -2157,7 +2059,164 @@ public class MonetPreparedStatement exte setNull(parameterIndex, -1); return; } - setValue(parameterIndex, "'" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'"); + int paramIdx = getParamIdx(parameterIndex); // this will throw a SQLException if parameter can not be found + + /* depending on the parameter data type (as expected by MonetDB) we + may need to add the data type as cast prefix to the parameter value */ + int paramJdbcType = javaType[paramIdx]; + String paramMonetdbType = monetdbType[paramIdx]; + + switch (paramJdbcType) { + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.CLOB: + case Types.NCHAR: + case Types.NVARCHAR: + case Types.LONGNVARCHAR: + { + String castprefix = ""; + switch (paramMonetdbType) { + // some MonetDB specific data types require a cast prefix + case "inet": + try { + // check if x represents a valid inet string to prevent + // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 + nl.cwi.monetdb.jdbc.MonetINET inet_obj = new nl.cwi.monetdb.jdbc.MonetINET(x); + } catch (Exception se) { + throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + se.getMessage(), "22M29"); + } + castprefix = "inet "; + break; + case "json": + // There is no support for JSON in standard java class libraries. + // Possibly we could use org.json.simple.JSONObject or other/faster libs + // javax.json.Json is not released yet (see https://json-processing-spec.java.net/) + // see also https://github.com/fabienrenaud/java-json-benchmark + // Note that it would make our JDBC driver dependent of an external jar + // and we don't want that. + + // do simplistic check if x represents a valid json string to prevent + // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 + if (x.isEmpty() || + (x.startsWith("{") && !x.endsWith("}")) || + (x.startsWith("[") && !x.endsWith("]")) + // TODO check completely if x represents a valid json string + ) + throw new SQLDataException("Invalid json string. It does not start with { or [ and end with } or ]", "22M32"); + + // TODO check completely if x represents a valid json string + + castprefix = "json "; + break; + case "url": + try { + // also check if x represents a valid url string to prevent + // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 + java.net.URL url_obj = new java.net.URL(x); + } catch (java.net.MalformedURLException mue) { + throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + mue.getMessage(), "22M30"); + } + castprefix = "url "; + break; + case "uuid": + try { + // also check if x represents a valid uuid string to prevent + // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 + java.util.UUID uuid_obj = java.util.UUID.fromString(x); + } catch (IllegalArgumentException iae) { + throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + iae.getMessage(), "22M31"); + } + castprefix = "uuid "; + break; + } + /* in specific cases prefix the string with: inet or json or url or uuid */ + setValue(parameterIndex, + castprefix + "'" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'"); + break; + } + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + try { + // check (by calling parse) if the string represents a valid number to prevent + // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 + if (paramJdbcType == Types.TINYINT) { + int number = Byte.parseByte(x); + } else if (paramJdbcType == Types.SMALLINT ) { + int number = Short.parseShort(x); + } else if (paramJdbcType == Types.INTEGER) { + int number = Integer.parseInt(x); + } else if (paramJdbcType == Types.BIGINT) { + long number = Long.parseLong(x); + } else if (paramJdbcType == Types.REAL || paramJdbcType == Types.DOUBLE || paramJdbcType == Types.FLOAT) { + double number = Double.parseDouble(x); + } else { + BigDecimal number = new BigDecimal(x); + } + } catch (NumberFormatException nfe) { + throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + nfe.getMessage(), "22003"); + } + setValue(parameterIndex, x); + break; + case Types.BIT: + case Types.BOOLEAN: + if (x.equalsIgnoreCase("false") || x.equalsIgnoreCase("true") || x.equals("0") || x.equals("1")) { + setValue(parameterIndex, x); + } else { + throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed", "22000"); + } + break; + case Types.DATE: + case Types.TIME: + case 2013: //Types.TIME_WITH_TIMEZONE: + case Types.TIMESTAMP: + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: + try { + // check if the string represents a valid calendar date or time or timestamp to prevent + // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 + if (paramJdbcType == Types.DATE) { + java.sql.Date datum = java.sql.Date.valueOf(x); + } else if (paramJdbcType == Types.TIME || paramJdbcType == 2013) { + Time tijdstip = Time.valueOf(x); + } else { + Timestamp tijdstip = Timestamp.valueOf(x); + } + } catch (IllegalArgumentException iae) { + throw new SQLDataException("Conversion of string: " + x + " to parameter data type " + paramMonetdbType + " failed. " + iae.getMessage(), "22007"); + } + /* prefix the string with: date or time or timetz or timestamp or timestamptz */ + setValue(parameterIndex, paramMonetdbType + " '" + x + "'"); + break; + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + case Types.BLOB: + // check if the string x contains pairs of hex chars to prevent + // failing exec #(..., ...) calls which destroy the prepared statement, see bug 6351 + int xlen = x.length(); + for (int i = 0; i < xlen; i++) { + char c = x.charAt(i); + if (c < '0' || c > '9') { + if (c < 'A' || c > 'F') { + if (c < 'a' || c > 'f') { + throw new SQLDataException("Invalid string for parameter data type " + paramMonetdbType + ". The string may contain only hex chars", "22M28"); + } + } + } + } + /* prefix the string with: blob */ + setValue(parameterIndex, "blob '" + x + "'"); + break; + default: + throw new SQLException("Conversion of string to parameter data type " + paramMonetdbType + " is not (yet) supported", "M1M05"); + } } /** @@ -2181,13 +2240,13 @@ public class MonetPreparedStatement exte * The driver converts this to an SQL TIME value when it sends it to * the database. * - * @param index the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @throws SQLException if a database access error occurs */ @Override - public void setTime(int index, Time x) throws SQLException { - setTime(index, x, null); + public void setTime(int parameterIndex, Time x) throws SQLException { + setTime(parameterIndex, x, null); } /** @@ -2200,34 +2259,35 @@ public class MonetPreparedStatement exte * timezone, which is that of the virtual machine running the * application. * - * @param index the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @param cal the Calendar object the driver will use to construct the time * @throws SQLException if a database access error occurs */ @Override - public void setTime(int index, Time x, Calendar cal) throws SQLException { + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { if (x == null) { - setNull(index, -1); + setNull(parameterIndex, -1); return; } - boolean hasTimeZone = monetdbType[getParamIdx(index)].endsWith("tz"); + String MonetDBType = monetdbType[getParamIdx(parameterIndex)]; + boolean hasTimeZone = ("timetz".equals(MonetDBType) || "timestamptz".equals(MonetDBType)); if (hasTimeZone) { // timezone shouldn't matter, since the server is timezone // aware in this case String RFC822 = mTimeZ.format(x); - setValue(index, "timetz '" + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'"); + setValue(parameterIndex, "timetz '" + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'"); } else { // server is not timezone aware for this field, and no // calendar given, since we told the server our timezone at // connection creation, we can just write a plain timestamp // here if (cal == null) { - setValue(index, "time '" + x.toString() + "'"); + setValue(parameterIndex, "time '" + x.toString() + "'"); } else { mTime.setTimeZone(cal.getTimeZone()); - setValue(index, "time '" + mTime.format(x) + "'"); + setValue(parameterIndex, "time '" + mTime.format(x) + "'"); } } } @@ -2237,13 +2297,13 @@ public class MonetPreparedStatement exte * value. The driver converts this to an SQL TIMESTAMP value when * it sends it to the database. * - * @param index the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @throws SQLException if a database access error occurs */ @Override - public void setTimestamp(int index, Timestamp x) throws SQLException { - setTimestamp(index, x, null); + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + setTimestamp(parameterIndex, x, null); } /** @@ -2256,35 +2316,35 @@ public class MonetPreparedStatement exte * default timezone, which is that of the virtual machine running * the application. * - * @param index the first parameter is 1, the second is 2, ... + * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @param cal the Calendar object the driver will use to construct the * timestamp * @throws SQLException if a database access error occurs */ @Override - public void setTimestamp(int index, Timestamp x, Calendar cal) throws SQLException { + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { if (x == null) { - setNull(index, -1); + setNull(parameterIndex, -1); return; } - boolean hasTimeZone = monetdbType[getParamIdx(index)].endsWith("tz"); + String MonetDBType = monetdbType[getParamIdx(parameterIndex)]; + boolean hasTimeZone = ("timestamptz".equals(MonetDBType) || "timetz".equals(MonetDBType)); if (hasTimeZone) { // timezone shouldn't matter, since the server is timezone // aware in this case String RFC822 = mTimestampZ.format(x); - setValue(index, "timestamptz '" + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'"); + setValue(parameterIndex, "timestamptz '" + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'"); } else { // server is not timezone aware for this field, and no // calendar given, since we told the server our timezone at - // connection creation, we can just write a plain timestamp - // here + // connection creation, we can just write a plain timestamp here if (cal == null) { - setValue(index, "timestamp '" + x.toString() + "'"); + setValue(parameterIndex, "timestamp '" + x.toString() + "'"); } else { mTimestamp.setTimeZone(cal.getTimeZone()); - setValue(index, "timestamp '" + mTimestamp.format(x) + "'"); + setValue(parameterIndex, "timestamp '" + mTimestamp.format(x) + "'"); } } } @@ -2326,8 +2386,14 @@ public class MonetPreparedStatement exte */ @Override public void setURL(int parameterIndex, URL x) throws SQLException { - setString(parameterIndex, x.toString()); - values[getParamIdx(parameterIndex)] = "url " + values[getParamIdx(parameterIndex)]; + if (x == null) { + setNull(parameterIndex, -1); + return; + } + + String val = x.toString(); + setValue(parameterIndex, "url '" + val.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("'", "\\\\'") + "'"); } /** @@ -2369,12 +2435,12 @@ public class MonetPreparedStatement exte * Sets the given index with the supplied value. If the given index is out of bounds, and SQLException is thrown. * The given value should never be null. * - * @param index the parameter index + * @param parameterIndex the parameter index * @param val the exact String representation to set * @throws SQLException if the given index is out of bounds */ - private void setValue(int index, String val) throws SQLException { - values[getParamIdx(index)] = val; + private void setValue(int parameterIndex, String val) throws SQLException { + values[getParamIdx(parameterIndex)] = (val == null ? "NULL" : val); } /** @@ -2388,9 +2454,7 @@ public class MonetPreparedStatement exte */ private String transform() throws SQLException { StringBuilder buf = new StringBuilder(8 + 12 * size); - buf.append("exec "); - buf.append(id); - buf.append('('); + buf.append("exec ").append(id).append('('); // check if all columns are set and do a replace int col = 0; for (int i = 0; i < size; i++) { @@ -2399,8 +2463,8 @@ public class MonetPreparedStatement exte col++; if (col > 1) buf.append(','); - if (values[i] == null) throw - new SQLException("Cannot execute, parameter " + col + " is missing.", "M1M05"); + if (values[i] == null) + throw new SQLException("Cannot execute, parameter " + col + " is missing.", "M1M05"); buf.append(values[i]); } @@ -2411,19 +2475,20 @@ public class MonetPreparedStatement exte /** * Small helper method that formats the "Invalid Parameter Index number ..." message - * and creates a new SQLException object whose SQLState is set to "M1M05". + * and creates a new SQLDataException object whose SQLState is set + * to "22010": invalid indicator parameter value. * * @param paramIdx the parameter index number - * @return a new created SQLException object with SQLState M1M05 + * @return a new created SQLDataException object with SQLState 22010 */ - private static SQLException newSQLInvalidParameterIndexException(int paramIdx) { - return new SQLException("Invalid Parameter Index number: " + paramIdx, "M1M05"); + private static SQLDataException newSQLInvalidParameterIndexException(int paramIdx) { + return new SQLDataException("Invalid Parameter Index number: " + paramIdx, "22010"); } /** * Small helper method that formats the "Method ... not implemented" message * and creates a new SQLFeatureNotSupportedException object - * whose SQLState is set to "0A000". + * whose SQLState is set to "0A000": feature not supported. * * @param name the method name * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java @@ -12,10 +12,7 @@ import nl.cwi.monetdb.mcl.protocol.Proto import nl.cwi.monetdb.mcl.responses.AbstractDataBlockResponse; import nl.cwi.monetdb.mcl.responses.ResultSetResponse; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; +import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; @@ -47,7 +44,7 @@ import java.util.UUID; * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira * @version 0.8 */ -public class MonetResultSet extends MonetWrapper implements ResultSet { +public class MonetResultSet extends MonetWrapper implements ResultSet, AutoCloseable { /** The current position of the cursor for this ResultSet object */ int curRow = 0; @@ -80,9 +77,9 @@ public class MonetResultSet extends Mone * * @param statement the statement which created this ResultSet * @param header a header containing the query, resultset type, etc. - * @throws SQLException is a protocol error occurs + * @throws IllegalArgumentException if called with null or invalid value for one of the arguments */ - MonetResultSet(Statement statement, ResultSetResponse header) throws SQLException { + MonetResultSet(Statement statement, ResultSetResponse header) throws IllegalArgumentException { if (statement == null) { throw new IllegalArgumentException("Statement may not be null!"); } @@ -111,7 +108,7 @@ public class MonetResultSet extends Mone * @param columns the column names * @param types the column types * @param results the number of rows in the ResultSet - * @throws IllegalArgumentException if communicating with monet failed + * @throws IllegalArgumentException if called with null or invalid value for one of the arguments */ MonetResultSet(Statement statement, String[] columns, String[] types, int[] JdbcSQLTypes, int results) throws IllegalArgumentException { @@ -144,14 +141,14 @@ public class MonetResultSet extends Mone * Moves the cursor to the given row number in this ResultSet object. * * If the row number is positive, the cursor moves to the given row number with respect to the beginning of the - * result set. The first row is row 1, the second is row 2, and so on. + * result set. The first row is row 1, the second is row 2, and so on. * * If the given row number is negative, the cursor moves to an absolute row position with respect to the end of the - * result set. For example, calling the method absolute(-1) positions the cursor on the last row; calling the + * result set. For example, calling the method absolute(-1) positions the cursor on the last row; calling the * method absolute(-2) moves the cursor to the next-to-last row, and so on. * * An attempt to position the cursor beyond the first/last row in the result set leaves the cursor before the first - * row or after the last row. + * row or after the last row. * Note: calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as calling last(). * * @param row the number of the row to which the cursor should move. A positive number indicates the row number @@ -174,12 +171,11 @@ public class MonetResultSet extends Mone row = tupleCount + row + 1; } // now place the row not farther than just before or after the result - if (row < 0) { - row = 0; // before first - } else if (row > tupleCount + 1) { - row = tupleCount + 1; // after last - } - // store it + if (row < 0) + row = 0; // before first + else if (row > tupleCount + 1) + row = tupleCount + 1; // after last + this.curRow = row; this.currentBlock = header.getDataBlockCorrespondingToLine(row - 1); return this.curRow <= this.tupleCount; @@ -237,25 +233,25 @@ public class MonetResultSet extends Mone * case insensitive. If a select list contains the same column more than once, the first instance of the * column will be returned. * - * @param columnName the name of the column + * @param columnLabel the name of the column * @return the column index of the given column name - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public int findColumn(String columnName) throws SQLException { - if (columnName != null) { + public int findColumn(String columnLabel) throws SQLException { + if (columnLabel != null) { final int array_size = columns.length; for (int i = 0; i < array_size; i++) { - if (columnName.equals(columns[i])) + if (columnLabel.equals(columns[i])) return i + 1; } /* if an exact match did not succeed try a case insensitive match */ for (int i = 0; i < array_size; i++) { - if (columnName.equalsIgnoreCase(columns[i])) + if (columnLabel.equalsIgnoreCase(columns[i])) return i + 1; } } - throw new SQLException("No such column name: " + columnName, "M1M05"); + throw new SQLException("No such column name: " + columnLabel, "M1M05"); } /** @@ -275,46 +271,46 @@ public class MonetResultSet extends Mone } @Override - public Array getArray(String colName) throws SQLException { + public Array getArray(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getArray"); } @Override public InputStream getAsciiStream(int columnIndex) throws SQLException { - try { + try { InputStream res = null; - switch (JdbcSQLTypes[columnIndex - 1]) { - case Types.CLOB: + switch (JdbcSQLTypes[columnIndex - 1]) { + case Types.CLOB: Clob cl = getClob(columnIndex); - if(cl != null) { - res = cl.getAsciiStream(); - } + if(cl != null) { + res = cl.getAsciiStream(); + } break; - case Types.BLOB: - case Types.LONGVARBINARY: - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - byte[] bytes = getBytes(columnIndex); - if(bytes != null) { - res = new ByteArrayInputStream(getBytes(columnIndex)); - } + case Types.BLOB: + case Types.LONGVARBINARY: + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + byte[] bytes = getBytes(columnIndex); + if(bytes != null) { + res = new ByteArrayInputStream(getBytes(columnIndex)); + } break; - default: - throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to ascii stream not supported", "M1M05"); - } - return res; - } catch (ClassCastException ex) { - throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { - throw newSQLInvalidColumnIndexException(columnIndex); - } + default: + throw new SQLException("Conversion from " + types[columnIndex - 1] + + " to ascii stream not supported", "M1M05"); + } + return res; + } catch (ClassCastException ex) { + throw new SQLException(ex.getMessage()); + } catch (IndexOutOfBoundsException e) { + throw newSQLInvalidColumnIndexException(columnIndex); + } } @Override - public InputStream getAsciiStream(String columnName) throws SQLException { - return getAsciiStream(findColumn(columnName)); + public InputStream getAsciiStream(String columnLabel) throws SQLException { + throw newSQLFeatureNotSupportedException("getAsciiStream"); } @Override @@ -325,73 +321,73 @@ public class MonetResultSet extends Mone @Override @Deprecated - public InputStream getUnicodeStream(String columnName) throws SQLException { + public InputStream getUnicodeStream(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getUnicodeStream"); } /** * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of - * uninterpreted bytes. The value can then be read in chunks from the stream. This method is particularly suitable - * for retrieving large LONGVARBINARY values. + * uninterpreted bytes. The value can then be read in chunks from the stream. This method is particularly suitable + * for retrieving large LONGVARBINARY values. * <br/><br/> * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next - * call to a getter method implicitly closes the stream. Also, a stream may return 0 when the method - * InputStream.available is called whether there is data available or not. + * call to a getter method implicitly closes the stream. Also, a stream may return 0 when the method + * InputStream.available is called whether there is data available or not. * * @param columnIndex the first column is 1, the second is 2, ... * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the - * value is SQL NULL, the value returned is null + * value is SQL NULL, the value returned is null * @throws SQLException if the columnIndex is not valid; if a database access error occurs or this method is called - * on a closed result set + * on a closed result set */ @Override public InputStream getBinaryStream(int columnIndex) throws SQLException { - try { + try { InputStream res = null; - switch (JdbcSQLTypes[columnIndex - 1]) { - case Types.BLOB: - Blob cl = getBlob(columnIndex); - if(cl != null) { - res = cl.getBinaryStream(); - } - break; - case Types.LONGVARBINARY: - byte[] bytes = getBytes(columnIndex); - if(bytes != null) { - res = new ByteArrayInputStream(getBytes(columnIndex)); - } - break; - default: - throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to binary stream not supported", "M1M05"); - } + switch (JdbcSQLTypes[columnIndex - 1]) { + case Types.BLOB: + Blob cl = getBlob(columnIndex); + if(cl != null) { + res = cl.getBinaryStream(); + } + break; + case Types.LONGVARBINARY: + byte[] bytes = getBytes(columnIndex); + if(bytes != null) { + res = new ByteArrayInputStream(getBytes(columnIndex)); + } + break; + default: + throw new SQLException("Conversion from " + types[columnIndex - 1] + + " to binary stream not supported", "M1M05"); + } return res; - } catch (ClassCastException ex) { - throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { - throw newSQLInvalidColumnIndexException(columnIndex); - } + } catch (ClassCastException ex) { + throw new SQLException(ex.getMessage()); + } catch (IndexOutOfBoundsException e) { + throw newSQLInvalidColumnIndexException(columnIndex); + } } - + /** * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of - * uninterpreted bytes. The value can then be read in chunks from the stream. This method is particularly suitable - * for retrieving large LONGVARBINARY values. - * <br/><br/> + * uninterpreted bytes. The value can then be read in chunks from the stream. This method is particularly suitable + * for retrieving large LONGVARBINARY values. + * <br/><br/> * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next - * call to a getter method implicitly closes the stream. Also, a stream may return 0 when the method available is - * called whether there is data available or not. + * call to a getter method implicitly closes the stream. Also, a stream may return 0 when the method available is + * called whether there is data available or not. * - * @param columnName the label for the column specified with the SQL AS clause. If the SQL AS clause was not - * specified, then the label is the name of the column + * @param columnLabel the label for the column specified with he SQL AS clause. If the SQL AS clause was not + * specified, then the label is the name of the column * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the - * value is SQL NULL, the result is null + * value is SQL NULL, the result is null * @throws SQLException if the columnLabel is not valid; if a database access error occurs or this method is called - * on a closed result set + * on a closed result set */ @Override - public InputStream getBinaryStream(String columnName) throws SQLException { - return getBinaryStream(findColumn(columnName)); + public InputStream getBinaryStream(String columnLabel) throws SQLException { + return getBinaryStream(findColumn(columnLabel)); } /** @@ -407,12 +403,12 @@ public class MonetResultSet extends Mone public Reader getCharacterStream(int columnIndex) throws SQLException { try { String ss = currentBlock.getValueAsString(columnIndex - 1); - return (ss == null) ? null : new StringReader(ss); + return (ss == null) ? null : new StringReader(ss); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -421,45 +417,45 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader * object. * - * @param columnName the name of the column + * @param columnLabel the name of the column * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is - * null in the Java programming language. + * null in the Java programming language. * @throws SQLException if a database access error occurs */ @Override - public Reader getCharacterStream(String columnName) throws SQLException { - return getCharacterStream(findColumn(columnName)); + public Reader getCharacterStream(String columnLabel) throws SQLException { + return getCharacterStream(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader - * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns. + * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns. * * @param columnIndex the first column is 1, the second is 2, ... * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is - * null in the Java programming language. + * null in the Java programming language. * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override public Reader getNCharacterStream(int columnIndex) throws SQLException { - throw newSQLFeatureNotSupportedException("getNCharacterStream"); + return getCharacterStream(columnIndex); } /** * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader - * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns. + * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns. * - * @param columnName the name of the column + * @param columnLabel the name of the column * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is - * null in the Java programming language. + * null in the Java programming language. * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override - public Reader getNCharacterStream(String columnName) throws SQLException { - throw newSQLFeatureNotSupportedException("getNCharacterStream"); + public Reader getNCharacterStream(String columnLabel) throws SQLException { + return getCharacterStream(findColumn(columnLabel)); } /** @@ -477,23 +473,23 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this ResultSet object as a Blob object in the - * Java programming language. + * Java programming language. * - * @param colName the name of the column from which to retrieve the value + * @param columnLabel the name of the column from which to retrieve the value * @return a Blob object representing the SQL BLOB value in the specified column * @throws SQLException if a database access error occurs */ @Override - public Blob getBlob(String colName) throws SQLException { - return getBlob(findColumn(colName)); + public Blob getBlob(String columnLabel) throws SQLException { + return getBlob(findColumn(columnLabel)); } /** @@ -511,8 +507,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -521,26 +517,26 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a Clob object in the * Java programming language. * - * @param colName the name of the column from which to retrieve the value + * @param columnLabel the name of the column from which to retrieve the value * @return a Clob object representing the SQL CLOB value in the specified column * @throws SQLException if a database access error occurs */ @Override - public Clob getClob(String colName) throws SQLException { - return getClob(findColumn(colName)); + public Clob getClob(String columnLabel) throws SQLException { + return getClob(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this ResultSet object as a NClob object in the * Java programming language. * - * @param i the first column is 1, the second is 2, ... + * @param columnIndex the first column is 1, the second is 2, ... * @return a NClob object representing the SQL NCLOB value in the specified column * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override - public NClob getNClob(int i) throws SQLException { + public NClob getNClob(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getNClob"); } @@ -548,13 +544,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a NClob object in the * Java programming language. * - * @param colName the name of the column from which to retrieve the value + * @param columnLabel the name of the column from which to retrieve the value * @return a NClob object representing the SQL NCLOB value in the specified column * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override - public NClob getNClob(String colName) throws SQLException { + public NClob getNClob(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getNClob"); } @@ -601,7 +597,7 @@ public class MonetResultSet extends Mone case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - String ss = currentBlock.getValueAsString(columnIndex - 1); + String ss = currentBlock.getValueAsString(columnIndex - 1); return (ss == null) ? null : new BigDecimal(ss); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + @@ -610,8 +606,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -646,21 +642,21 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.math.BigDecimal with full precision. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value (full precision); if the value is SQL NULL, * the value returned is null in the Java programming language. * @throws SQLException if a database access error occurs */ @Override - public BigDecimal getBigDecimal(String columnName) throws SQLException { - return getBigDecimal(findColumn(columnName)); + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + return getBigDecimal(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.math.BigDecimal with full precision. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @param scale the number of digits to the right of the decimal point * @return the column value (full precision); if the value is SQL NULL, * the value returned is null in the Java programming language. @@ -668,14 +664,14 @@ public class MonetResultSet extends Mone */ @Override @Deprecated - public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { - return getBigDecimal(findColumn(columnName), scale); + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + return getBigDecimal(findColumn(columnLabel), scale); } // See Sun JDBC Specification 3.0 Table B-6 /** * Retrieves the value of the designated column in the current row of this ResultSet object as a boolean in the Java - * programming language. + * programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is false @@ -707,27 +703,27 @@ public class MonetResultSet extends Mone case Types.DOUBLE: double dou = currentBlock.getDoubleValue(columnIndex - 1); return !currentBlock.isLastReadWasNull() && dou != 0.0d; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.CLOB: - case Types.BLOB: - case Types.LONGVARBINARY: - String val = currentBlock.getValueAsString(columnIndex - 1); - return val != null && !"0".equals(val) && ("1".equals(val) || Boolean.parseBoolean(val)); - case Types.NUMERIC: - case Types.DECIMAL: - BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec != null && bigdec.compareTo(BigDecimal.ZERO) != 0; - default: //OTHERS, BLOB, LONGVARBINARY, TIME... - throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to boolean type not supported", "M1M05"); - } + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.CLOB: + case Types.BLOB: + case Types.LONGVARBINARY: + String val = currentBlock.getValueAsString(columnIndex - 1); + return val != null && !"0".equals(val) && ("1".equals(val) || Boolean.parseBoolean(val)); + case Types.NUMERIC: + case Types.DECIMAL: + BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); + return bigdec != null && bigdec.compareTo(BigDecimal.ZERO) != 0; + default: //OTHERS, BLOB, LONGVARBINARY, TIME... + throw new SQLException("Conversion from " + types[columnIndex - 1] + + " to boolean type not supported", "M1M05"); + } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -736,13 +732,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a boolean in the Java * programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is false - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public boolean getBoolean(String columnName) throws SQLException { - return getBoolean(findColumn(columnName)); + public boolean getBoolean(String columnLabel) throws SQLException { + return getBoolean(findColumn(columnLabel)); } /** @@ -797,8 +793,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -807,14 +803,14 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this * ResultSet object as a byte in the Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned * is 0 * @throws SQLException if a database access error occurs */ @Override - public byte getByte(String columnName) throws SQLException { - return getByte(findColumn(columnName)); + public byte getByte(String columnLabel) throws SQLException { + return getByte(findColumn(columnLabel)); } /** @@ -849,8 +845,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -859,13 +855,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a byte array in the * Java programming language. The bytes represent the raw values returned by the driver. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override - public byte[] getBytes(String columnName) throws SQLException { - return getBytes(findColumn(columnName)); + public byte[] getBytes(String columnLabel) throws SQLException { + return getBytes(findColumn(columnLabel)); } /** @@ -959,8 +955,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -969,13 +965,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this * ResultSet object as a double in the Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public double getDouble(String columnName) throws SQLException { - return getDouble(findColumn(columnName)); + public double getDouble(String columnLabel) throws SQLException { + return getDouble(findColumn(columnLabel)); } /** @@ -1109,8 +1105,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -1119,13 +1115,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a float in the Java * programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public float getFloat(String columnName) throws SQLException { - return getFloat(findColumn(columnName)); + public float getFloat(String columnLabel) throws SQLException { + return getFloat(findColumn(columnLabel)); } /** @@ -1180,8 +1176,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -1190,13 +1186,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as an int in the * Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public int getInt(String columnName) throws SQLException { - return getInt(findColumn(columnName)); + public int getInt(String columnLabel) throws SQLException { + return getInt(findColumn(columnLabel)); } /** @@ -1251,8 +1247,8 @@ public class MonetResultSet extends Mone } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { - throw new SQLException(e); - } catch (IndexOutOfBoundsException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -1261,13 +1257,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a long in the Java * programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public long getLong(String columnName) throws SQLException { - return getLong(findColumn(columnName)); + public long getLong(String columnLabel) throws SQLException { + return getLong(findColumn(columnLabel)); } /* helper for the anonymous class inside getMetaData */ @@ -1569,9 +1565,11 @@ public class MonetResultSet extends Mone _precision[column] = 10; break; case Types.TIME: + case 2013: //Types.TIME_WITH_TIMEZONE: _precision[column] = 8; break; case Types.TIMESTAMP: + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: _precision[column] = 19; break; default: @@ -1832,8 +1830,10 @@ public class MonetResultSet extends Mone case Types.DATE: return getDate(columnIndex); case Types.TIME: + case 2013: //Types.TIME_WITH_TIMEZONE: return getTime(columnIndex); case Types.TIMESTAMP: + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: return getTimestamp(columnIndex); case Types.OTHER: { // The MonetDB types: inet, json, url and uuid are all mapped to Types.OTHER in MonetDriver.typeMap @@ -1889,8 +1889,8 @@ public class MonetResultSet extends Mone } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } catch (ProtocolException e) { - throw new SQLException(e); - } + throw new SQLException(e); + } } private boolean classImplementsSQLData(Class<?> cl) { @@ -2160,6 +2160,8 @@ public class MonetResultSet extends Mone @Override @SuppressWarnings("unchecked") public <T> T getObject(int columnIndex, Class<T> type) throws SQLException { + if (type == null) + throw new SQLException("type is null", "M1M05"); try { return (T) getObjectFromClass(columnIndex, type); } catch (IndexOutOfBoundsException e) { @@ -2223,8 +2225,10 @@ public class MonetResultSet extends Mone case Types.DATE: return Date.class; case Types.TIME: + case 2013: //Types.TIME_WITH_TIMEZONE: return Time.class; case Types.TIMESTAMP: + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: return Timestamp.class; case Types.CLOB: return Clob.class; @@ -2247,13 +2251,13 @@ public class MonetResultSet extends Mone * * This method may also be used to read database-specific abstract data types. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return a java.lang.Object holding the column value * @throws SQLException if a database access error occurs */ @Override - public Object getObject(String columnName) throws SQLException { - return getObject(findColumn(columnName)); + public Object getObject(String columnLabel) throws SQLException { + return getObject(findColumn(columnLabel)); } /** @@ -2262,24 +2266,24 @@ public class MonetResultSet extends Mone * value is an SQL NULL, the driver returns a Java null. This method uses * the specified Map object for custom mapping if appropriate. * - * @param colName the name of the column from which to retrieve the value + * @param columnLabel the name of the column from which to retrieve the value * @param map a java.util.Map object that contains the mapping from SQL * type names to classes in the Java programming language * @return an Object representing the SQL value in the specified column * @throws SQLException if a database access error occurs */ @Override - public Object getObject(String colName, Map<String,Class<?>> map) throws SQLException { - return getObject(findColumn(colName), map); + public Object getObject(String columnLabel, Map<String,Class<?>> map) throws SQLException { + return getObject(findColumn(columnLabel), map); } @Override - public Ref getRef(int i) throws SQLException { + public Ref getRef(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getRef"); } @Override - public Ref getRef(String colName) throws SQLException { + public Ref getRef(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getRef"); } @@ -2312,13 +2316,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.RowId * object in the Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override - public RowId getRowId(String columnName) throws SQLException { + public RowId getRowId(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getRowId"); } @@ -2384,13 +2388,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a short in the Java * programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public short getShort(String columnName) throws SQLException { - return getShort(findColumn(columnName)); + public short getShort(String columnLabel) throws SQLException { + return getShort(findColumn(columnLabel)); } /** @@ -2430,13 +2434,13 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this * ResultSet object as a String in the Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override - public String getString(String columnName) throws SQLException { - return getString(findColumn(columnName)); + public String getString(String columnLabel) throws SQLException { + return getString(findColumn(columnLabel)); } /** @@ -2461,27 +2465,27 @@ public class MonetResultSet extends Mone * language. It is intended for use when accessing NCHAR,NVARCHAR * and LONGNVARCHAR columns. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null - * @throws SQLException if the ResultSet object does not contain columnName + * @throws SQLException if the ResultSet object does not contain columnLabel * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override - public String getNString(String columnName) throws SQLException { - return getNString(findColumn(columnName)); + public String getNString(String columnLabel) throws SQLException { + return getString(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this ResultSet as a java.sql.SQLXML object * in the Java programming language. * - * @param i the first column is 1, the second is 2, ... + * @param columnIndex the first column is 1, the second is 2, ... * @return a SQLXML object that maps an SQL XML value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override - public SQLXML getSQLXML(int i) throws SQLException { + public SQLXML getSQLXML(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getSQLXML"); } @@ -2489,14 +2493,14 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet as a java.sql.SQLXML object * in the Java programming language. * - * @param colName the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, - * then the label is the name of the column + * @param columnLabel the label for the column specified with the SQL AS clause. If the SQL AS clause was not + * specified, then the label is the name of the column * @return a SQLXML object that maps an SQL XML value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override - public SQLXML getSQLXML(String colName) throws SQLException { + public SQLXML getSQLXML(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getSQLXML"); } @@ -2531,44 +2535,44 @@ public class MonetResultSet extends Mone public Date getDate(int columnIndex, Calendar cal) throws SQLException { try { Calendar res; - long millis; - switch (JdbcSQLTypes[columnIndex - 1]) { - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: + long millis; + switch (JdbcSQLTypes[columnIndex - 1]) { + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + if(res == null) { + return null; + } + millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + break; + case 2013: //Types.TIME_WITH_TIMEZONE: + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); if(res == null) { return null; } - millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + millis = res.getTimeInMillis(); break; - case 2013: //Types.TIME_WITH_TIMEZONE: - case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: - res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.CLOB: + case Types.BLOB: + case Types.LONGVARBINARY: + res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.DATE); if(res == null) { return null; } - millis = res.getTimeInMillis(); - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.CLOB: - case Types.BLOB: - case Types.LONGVARBINARY: - res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.DATE); - if(res == null) { - return null; - } - millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); - break; - default: - this.addWarning("unsupported data type", "01M03"); - cal.clear(); - millis = 0; - } - return new Date(millis); - } catch (ClassCastException ex) { + millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + break; + default: + this.addWarning("unsupported data type", "01M03"); + cal.clear(); + millis = 0; + } + return new Date(millis); + } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (ProtocolException e) { throw new SQLException(e); @@ -2582,13 +2586,13 @@ public class MonetResultSet extends Mone * ResultSet object as a java.sql.Date object in the Java programming * language. * - * @param columnName the SQL name of the column from which to retrieve the value + * @param columnLabel the SQL name of the column from which to retrieve the value * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override - public Date getDate(String columnName) throws SQLException { - return getDate(findColumn(columnName), Calendar.getInstance()); + public Date getDate(String columnLabel) throws SQLException { + return getDate(findColumn(columnLabel), Calendar.getInstance()); } /** @@ -2598,14 +2602,14 @@ public class MonetResultSet extends Mone * millisecond value for the date if the underlying database does not store * timezone information. * - * @param columnName the SQL name of the column from which to retrieve the value + * @param columnLabel the SQL name of the column from which to retrieve the value * @param cal the java.util.Calendar object to use in constructing the date * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override - public Date getDate(String columnName, Calendar cal) throws SQLException { - return getDate(findColumn(columnName), cal); + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + return getDate(findColumn(columnLabel), cal); } /** @@ -2618,7 +2622,7 @@ public class MonetResultSet extends Mone */ @Override public Time getTime(int columnIndex) throws SQLException { - return getTime(columnIndex, Calendar.getInstance()); + return getTime(columnIndex, null); } /** @@ -2630,71 +2634,71 @@ public class MonetResultSet extends Mone * * @param columnIndex the first column is 1, the second is 2, ... * @param cal the java.util.Calendar object to use in constructing the timestamp - * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null in - * the Java programming language + * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null in the + * Java programming language * @throws SQLException if a database access error occurs */ @Override public Time getTime(int columnIndex, Calendar cal) throws SQLException { - try { - Calendar res; - long millis; - switch (JdbcSQLTypes[columnIndex - 1]) { - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: + try { + Calendar res; + long millis; + switch (JdbcSQLTypes[columnIndex - 1]) { + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); if(res == null) { return null; } millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; - case 2013: //Types.TIME_WITH_TIMEZONE: - case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: + case 2013: //Types.TIME_WITH_TIMEZONE: + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); if(res == null) { return null; } millis = res.getTimeInMillis(); break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.CLOB: + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.TIME); + res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.TIME); if(res == null) { return null; } - millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); - break; - default: - this.addWarning("unsupported data type", "01M03"); - cal.clear(); - millis = 0; - } - return new Time(millis); - } catch (ClassCastException ex) { - throw new SQLException(ex.getMessage()); - } catch (ProtocolException e) { + millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + break; + default: + this.addWarning("unsupported data type", "01M03"); + cal.clear(); + millis = 0; + } + return new Time(millis); + } catch (ClassCastException ex) { + throw new SQLException(ex.getMessage()); + } catch (ProtocolException e) { throw new SQLException(e); } catch (IndexOutOfBoundsException e) { - throw newSQLInvalidColumnIndexException(columnIndex); - } + throw newSQLInvalidColumnIndexException(columnIndex); + } } /** * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Time * object in the Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override - public Time getTime(String columnName) throws SQLException { - return getTime(findColumn(columnName), Calendar.getInstance()); + public Time getTime(String columnLabel) throws SQLException { + return getTime(findColumn(columnLabel), null); } /** @@ -2704,15 +2708,15 @@ public class MonetResultSet extends Mone * millisecond value for the time if the underlying database does not store * timezone information. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @param cal the java.util.Calendar object to use in constructing the timestamp - * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null - * in the Java programming language + * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null in the + * Java programming language * @throws SQLException if a database access error occurs */ @Override - public Time getTime(String columnName, Calendar cal) throws SQLException { - return getTime(findColumn(columnName), cal); + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + return getTime(findColumn(columnLabel), cal); } /** @@ -2725,7 +2729,7 @@ public class MonetResultSet extends Mone */ @Override public Timestamp getTimestamp(int columnIndex) throws SQLException { - return getTimestamp(columnIndex, Calendar.getInstance()); + return getTimestamp(columnIndex, null); } /** @@ -2743,26 +2747,26 @@ public class MonetResultSet extends Mone */ @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { - try { - Calendar res; + try { + Calendar res; long millis; int nanos = 0; - switch (JdbcSQLTypes[columnIndex - 1]) { - case Types.DATE: - case Types.TIME: - res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + switch (JdbcSQLTypes[columnIndex - 1]) { + case Types.DATE: + case Types.TIME: + res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); if(res == null) { return null; } - millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); - break; - case 2013: //Types.TIME_WITH_TIMEZONE: - res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + break; + case 2013: //Types.TIME_WITH_TIMEZONE: + res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); if(res == null) { return null; } - millis = res.getTimeInMillis(); - break; + millis = res.getTimeInMillis(); + break; case Types.TIMESTAMP: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); if(res == null) { @@ -2779,46 +2783,46 @@ public class MonetResultSet extends Mone nanos = currentBlock.getLastNanos(); millis = res.getTimeInMillis(); break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.CLOB: + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.TIMESTAMP); + res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.TIMESTAMP); if(res == null) { return null; } - millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); - break; - default: - this.addWarning("unsupported data type", "01M03"); - cal.clear(); - millis = 0; - } - Timestamp result = new Timestamp(millis); - result.setNanos(nanos); - return result; - } catch (ClassCastException ex) { - throw new SQLException(ex.getMessage()); - } catch (ProtocolException e) { + millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + break; + default: + this.addWarning("unsupported data type", "01M03"); + cal.clear(); + millis = 0; + } + Timestamp result = new Timestamp(millis); + result.setNanos(nanos); + return result; + } catch (ClassCastException ex) { + throw new SQLException(ex.getMessage()); + } catch (ProtocolException e) { throw new SQLException(e); } catch (IndexOutOfBoundsException e) { - throw newSQLInvalidColumnIndexException(columnIndex); - } + throw newSQLInvalidColumnIndexException(columnIndex); + } } /** * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp * object in the Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override - public Timestamp getTimestamp(String columnName) throws SQLException { - return getTimestamp(findColumn(columnName), Calendar.getInstance()); + public Timestamp getTimestamp(String columnLabel) throws SQLException { + return getTimestamp(findColumn(columnLabel), null); } /** @@ -2828,15 +2832,15 @@ public class MonetResultSet extends Mone * millisecond value for the timestamp if the underlying database does not * store timezone information. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @param cal the java.util.Calendar object to use in constructing the timestamp * @return the column value as a java.sql.Timestamp object; if the value is * SQL NULL, the value returned is null in the Java programming language * @throws SQLException if a database access error occurs */ @Override - public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { - return getTimestamp(findColumn(columnName), cal); + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + return getTimestamp(findColumn(columnLabel), cal); } /** @@ -2892,14 +2896,14 @@ public class MonetResultSet extends Mone * Retrieves the value of the designated column in the current row of this ResultSet object as a java.net.URL object * in the Java programming language. * - * @param columnName the SQL name of the column + * @param columnLabel the SQL name of the column * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null in the * Java programming language * @throws SQLException if a database access error occurs, or if a URL is malformed */ @Override - public URL getURL(String columnName) throws SQLException { - return getURL(findColumn(columnName)); + public URL getURL(String columnLabel) throws SQLException { + return getURL(findColumn(columnLabel)); } /** @@ -3087,7 +3091,7 @@ public class MonetResultSet extends Mone * * Note: Support for the rowUpdated method is optional with a result set concurrency of CONCUR_READ_ONLY * - * Returns: true if the current row is detected to have been visibly updated by the owner or another; false otherwise + * Returns: true if the current row is detected to have been visibly updated by the owner or another;false otherwise * * Throws: * SQLException - if a database access error occurs or this method is called on a closed result set @@ -3100,21 +3104,21 @@ public class MonetResultSet extends Mone return false; } - /** - * Adds a warning to the pile of warnings this ResultSet object has. If - * there were no warnings (or clearWarnings was called) this warning will - * be the first, otherwise this warning will get appended to the current - * warning. - * - * @param reason the warning message - */ - public void addWarning(String reason, String sqlstate) { - if (warnings == null) { - warnings = new SQLWarning(reason, sqlstate); - } else { - warnings.setNextWarning(new SQLWarning(reason, sqlstate)); - } - } + /** + * Adds a warning to the pile of warnings this ResultSet object has. If + * there were no warnings (or clearWarnings was called) this warning will + * be the first, otherwise this warning will get appended to the current + * warning. + * + * @param reason the warning message + */ + public void addWarning(String reason, String sqlstate) { + if (warnings == null) { + warnings = new SQLWarning(reason, sqlstate); + } else { + warnings.setNextWarning(new SQLWarning(reason, sqlstate)); + } + } /* the next methods are all related to updateable result sets, which we currently do not support */ @Override @@ -3153,7 +3157,7 @@ public class MonetResultSet extends Mone } @Override - public void updateArray(String columnName, Array x) throws SQLException { + public void updateArray(String columnLabel, Array x) throws SQLException { throw newSQLFeatureNotSupportedException("updateArray"); } @@ -3173,17 +3177,17 @@ public class MonetResultSet extends Mone } @Override - public void updateAsciiStream(String columnName, InputStream x) throws SQLException { + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override - public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException { + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override - public void updateAsciiStream(String columnName, InputStream x, long length) throws SQLException { + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @@ -3193,7 +3197,7 @@ public class MonetResultSet extends Mone } @Override - public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBigDecimal"); } @@ -3213,17 +3217,17 @@ public class MonetResultSet extends Mone } @Override - public void updateBinaryStream(String columnName, InputStream x) throws SQLException { + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override - public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException { + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override - public void updateBinaryStream(String columnName, InputStream x, long length) throws SQLException { + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @@ -3243,17 +3247,17 @@ public class MonetResultSet extends Mone } @Override - public void updateBlob(String columnName, Blob x) throws SQLException { + public void updateBlob(String columnLabel, Blob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override - public void updateBlob(String columnName, InputStream s) throws SQLException { + public void updateBlob(String columnLabel, InputStream s) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override - public void updateBlob(String columnName, InputStream s, long length) throws SQLException { + public void updateBlob(String columnLabel, InputStream s, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @@ -3263,7 +3267,7 @@ public class MonetResultSet extends Mone } @Override - public void updateBoolean(String columnName, boolean x) throws SQLException { + public void updateBoolean(String columnLabel, boolean x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBoolean"); } @@ -3273,7 +3277,7 @@ public class MonetResultSet extends Mone } @Override - public void updateByte(String columnName, byte x) throws SQLException { + public void updateByte(String columnLabel, byte x) throws SQLException { throw newSQLFeatureNotSupportedException("updateByte"); } @@ -3283,7 +3287,7 @@ public class MonetResultSet extends Mone } @Override - public void updateBytes(String columnName, byte[] x) throws SQLException { + public void updateBytes(String columnLabel, byte[] x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBytes"); } @@ -3303,17 +3307,17 @@ public class MonetResultSet extends Mone } @Override - public void updateCharacterStream(String columnName, Reader reader) throws SQLException { + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override - public void updateCharacterStream(String columnName, Reader reader, int length) throws SQLException { + public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override - public void updateCharacterStream(String columnName, Reader reader, long length) throws SQLException { + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @@ -3328,12 +3332,12 @@ public class MonetResultSet extends Mone } @Override - public void updateNCharacterStream(String columnName, Reader reader) throws SQLException { + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { throw newSQLFeatureNotSupportedException("updateNCharacterStream"); } @Override - public void updateNCharacterStream(String columnName, Reader reader, long length) throws SQLException { + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateNCharacterStream"); } @@ -3353,17 +3357,17 @@ public class MonetResultSet extends Mone } @Override - public void updateClob(String columnName, Clob x) throws SQLException { + public void updateClob(String columnLabel, Clob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override - public void updateClob(String columnName, Reader r) throws SQLException { + public void updateClob(String columnLabel, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override - public void updateClob(String columnName, Reader r, long length) throws SQLException { + public void updateClob(String columnLabel, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @@ -3383,17 +3387,17 @@ public class MonetResultSet extends Mone } @Override - public void updateNClob(String columnName, NClob x) throws SQLException { + public void updateNClob(String columnLabel, NClob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override - public void updateNClob(String columnName, Reader r) throws SQLException { + public void updateNClob(String columnLabel, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override - public void updateNClob(String columnName, Reader r, long length) throws SQLException { + public void updateNClob(String columnLabel, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @@ -3403,7 +3407,7 @@ public class MonetResultSet extends Mone } @Override - public void updateDate(String columnName, java.sql.Date x) throws SQLException { + public void updateDate(String columnLabel, java.sql.Date x) throws SQLException { throw newSQLFeatureNotSupportedException("updateDate"); } @@ -3413,7 +3417,7 @@ public class MonetResultSet extends Mone } @Override - public void updateDouble(String columnName, double x) throws SQLException { + public void updateDouble(String columnLabel, double x) throws SQLException { throw newSQLFeatureNotSupportedException("updateDouble"); } @@ -3423,7 +3427,7 @@ public class MonetResultSet extends Mone } @Override - public void updateFloat(String columnName, float x) throws SQLException { + public void updateFloat(String columnLabel, float x) throws SQLException { throw newSQLFeatureNotSupportedException("updateFloat"); } @@ -3433,7 +3437,7 @@ public class MonetResultSet extends Mone } @Override - public void updateInt(String columnName, int x) throws SQLException { + public void updateInt(String columnLabel, int x) throws SQLException { throw newSQLFeatureNotSupportedException("updateInt"); } @@ -3443,7 +3447,7 @@ public class MonetResultSet extends Mone } @Override - public void updateLong(String columnName, long x) throws SQLException { + public void updateLong(String columnLabel, long x) throws SQLException { throw newSQLFeatureNotSupportedException("updateLong"); } @@ -3453,7 +3457,7 @@ public class MonetResultSet extends Mone } @Override - public void updateNull(String columnName) throws SQLException { + public void updateNull(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("updateNull"); } @@ -3468,12 +3472,12 @@ public class MonetResultSet extends Mone } @Override - public void updateObject(String columnName, Object x) throws SQLException { + public void updateObject(String columnLabel, Object x) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override - public void updateObject(String columnName, Object x, int scale) throws SQLException { + public void updateObject(String columnLabel, Object x, int scale) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @@ -3483,7 +3487,7 @@ public class MonetResultSet extends Mone } @Override - public void updateRef(String columnName, Ref x) throws SQLException { + public void updateRef(String columnLabel, Ref x) throws SQLException { throw newSQLFeatureNotSupportedException("updateRef"); } @@ -3508,7 +3512,7 @@ public class MonetResultSet extends Mone } @Override - public void updateShort(String columnName, short x) throws SQLException { + public void updateShort(String columnLabel, short x) throws SQLException { throw newSQLFeatureNotSupportedException("updateShort"); } @@ -3518,7 +3522,7 @@ public class MonetResultSet extends Mone } @Override - public void updateString(String columnName, String x) throws SQLException { + public void updateString(String columnLabel, String x) throws SQLException { throw newSQLFeatureNotSupportedException("updateString"); } @@ -3528,12 +3532,12 @@ public class MonetResultSet extends Mone } @Override - public void updateNString(String columnName, String x) throws SQLException { + public void updateNString(String columnLabel, String x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNString"); } @Override - public void updateSQLXML(String columnName, SQLXML x) throws SQLException { + public void updateSQLXML(String columnLabel, SQLXML x) throws SQLException { throw newSQLFeatureNotSupportedException("updateSQLXML"); } @@ -3548,7 +3552,7 @@ public class MonetResultSet extends Mone } @Override - public void updateTime(String columnName, Time x) throws SQLException { + public void updateTime(String columnLabel, Time x) throws SQLException { throw newSQLFeatureNotSupportedException("updateTime"); } @@ -3558,7 +3562,7 @@ public class MonetResultSet extends Mone } @Override - public void updateTimestamp(String columnName, Timestamp x) throws SQLException { + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { throw newSQLFeatureNotSupportedException("updateTimestamp"); } @@ -3580,19 +3584,18 @@ public class MonetResultSet extends Mone /** * Small helper method that formats the "Invalid Column Index number ..." message - * and creates a new SQLException object whose SQLState is set to "M1M05". + * and creates a new SQLDataException object whose SQLState is set to "22010": invalid indicator parameter value. * * @param colIdx the column index number - * @return a new created SQLException object with SQLState M1M05 + * @return a new created SQLDataException object with SQLState 22010 */ - static SQLException newSQLInvalidColumnIndexException(int colIdx) { - return new SQLException("Invalid Column Index number: " + colIdx, "M1M05"); + static SQLDataException newSQLInvalidColumnIndexException(int colIdx) { + return new SQLDataException("Invalid Column Index number: " + colIdx, "22010"); } /** * Small helper method that formats the "Method ... not implemented" message - * and creates a new SQLFeatureNotSupportedException object - * whose SQLState is set to "0A000". + * and creates a new SQLFeatureNotSupportedException object whose SQLState is set to "0A000": feature not supported. * * @param name the method name * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java @@ -11,6 +11,7 @@ package nl.cwi.monetdb.jdbc; import nl.cwi.monetdb.mcl.responses.*; import nl.cwi.monetdb.mcl.responses.ResultSetResponse; +import java.io.IOException; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.Statement; @@ -45,7 +46,7 @@ import java.util.concurrent.locks.Reentr * @author Martin van Dinther * @version 0.7 */ -public class MonetStatement extends MonetWrapper implements Statement { +public class MonetStatement extends MonetWrapper implements Statement, AutoCloseable { /** The parental Connection object */ private MonetConnection connection; /** The last ResponseList object this Statement produced */ @@ -81,11 +82,10 @@ public class MonetStatement extends Mone * @param connection the connection that created this Statement * @param resultSetType type of ResultSet to produce * @param resultSetConcurrency concurrency of ResultSet to produce - * @throws SQLException if an error occurs during login * @throws IllegalArgumentException is one of the arguments is null or empty */ MonetStatement(MonetConnection connection, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException, IllegalArgumentException { + throws IllegalArgumentException { if (connection == null) throw new IllegalArgumentException("No Connection given!"); @@ -241,8 +241,8 @@ public class MonetStatement extends Mone int count = -1; if (!type) count = getUpdateCount(); do { - if (offset >= max) throw - new SQLException("Overflow: don't use multi statements when batching (" + max + ")", "M1M16"); + if (offset >= max) + throw new SQLException("Overflow: don't use multi statements when batching (" + max + ")", "M1M16"); if (type) { e.setNextException(new SQLException("Batch query produced a ResultSet! " + "Ignoring and setting update count to value " + EXECUTE_FAILED, "M1M17")); @@ -330,7 +330,7 @@ public class MonetStatement extends Mone public boolean execute(String sql) throws SQLException { return internalExecute(sql); } - + /** * Executes the given SQL statement, which may return multiple * results, and signals the driver that any auto-generated keys @@ -679,7 +679,7 @@ public class MonetStatement extends Mone try { return new MonetVirtualResultSet(this, columns, types, jdbcTypes, results); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException | IOException e) { throw new SQLException("Internal driver error: " + e.getMessage(), "M0M03"); } } @@ -1188,7 +1188,7 @@ final class MonetVirtualResultSet extend private boolean closed; MonetVirtualResultSet(Statement statement, String[] columns, String[] types, int[] jdbcTypes, Object[] results) - throws IllegalArgumentException { + throws IllegalArgumentException, IOException, SQLException { super(statement, columns, types, jdbcTypes, results.length); this.results = results; this.closed = false;
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java @@ -24,6 +24,7 @@ import java.net.URISyntaxException; import java.nio.ByteOrder; import java.sql.Connection; import java.sql.SQLException; +import java.sql.SQLNonTransientConnectionException; import java.util.*; /** @@ -56,7 +57,7 @@ public class MapiConnection extends Mone private ByteOrder serverEndianness; public MapiConnection(Properties props, String hash, String language, boolean blobIsBinary, boolean clobIsLongChar, - String hostname, int port, String database) throws IOException { + String hostname, int port, String database) { super(props, hash, MapiLanguage.getLanguageFromString(language), blobIsBinary, clobIsLongChar); this.hostname = hostname; this.port = port; @@ -97,16 +98,11 @@ public class MapiConnection extends Mone * @return The currently in use timeout in milliseconds */ @Override - public int getSoTimeout() { - try { - if(protocol != null) { - this.soTimeout = ((OldMapiProtocol)protocol).getSocket().getSoTimeout(); - } - return this.soTimeout; - } catch (SocketException e) { - this.addWarning("The socket timeout could not be get", "M1M05"); + public int getSoTimeout() throws SocketException { + if(protocol != null) { + this.soTimeout = ((OldMapiProtocol)protocol).getSocket().getSoTimeout(); } - return -1; + return this.soTimeout; } /** @@ -117,18 +113,14 @@ public class MapiConnection extends Mone * @param timeout The specified timeout, in milliseconds. A timeout of zero is interpreted as an infinite timeout */ @Override - public void setSoTimeout(int timeout) { + public void setSoTimeout(int timeout) throws SocketException { if (timeout < 0) { throw new IllegalArgumentException("Timeout can't be negative"); } - try { - if(protocol != null) { - ((OldMapiProtocol)protocol).getSocket().setSoTimeout(timeout); - } - this.soTimeout = timeout; - } catch (SocketException e) { - this.addWarning("The socket timeout could not be set", "M1M05"); + if(protocol != null) { + ((OldMapiProtocol)protocol).getSocket().setSoTimeout(timeout); } + this.soTimeout = timeout; } /** @@ -253,9 +245,9 @@ public class MapiConnection extends Mone } } catch (SocketTimeoutException e) { close(); // JDBC 4.1 semantics, abort() - throw new SQLException("connection timed out", "08M33"); + throw new SQLNonTransientConnectionException("connection timed out", "08M33"); } catch (IOException e) { - throw new SQLException(e.getMessage(), "08000"); + throw new SQLNonTransientConnectionException(e.getMessage(), "08000"); } }
--- a/tests/BugDecimalRound_Bug_3561.java +++ b/tests/BugDecimalRound_Bug_3561.java @@ -13,22 +13,28 @@ public class BugDecimalRound_Bug_3561 { public static void main(String[] args) throws Exception { // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers Connection con = DriverManager.getConnection(args[0]); + Statement stmt1 = con.createStatement(); - PreparedStatement st; - Statement stmt2; - ResultSet rs; - BigDecimal bd = new BigDecimal("112.125"); + stmt1.executeUpdate("CREATE TABLE bug3561 (d decimal(14,4))"); - stmt1.executeUpdate("CREATE TABLE bug3561 (d decimal(14,4))"); - st = con.prepareStatement("INSERT INTO bug3561 VALUES (?)"); - st.setBigDecimal(1, bd); + PreparedStatement st = con.prepareStatement("INSERT INTO bug3561 VALUES (?)"); + st.setBigDecimal(1, new BigDecimal("112.125")); + st.executeUpdate(); + st.setBigDecimal(1, new BigDecimal("212.12345")); st.executeUpdate(); - stmt2 = con.createStatement(); - rs = stmt2.executeQuery("SELECT d FROM bug3561"); + st.setBigDecimal(1, new BigDecimal("0.012345")); + st.executeUpdate(); + st.close(); + + Statement stmt2 = con.createStatement(); + ResultSet rs = stmt2.executeQuery("SELECT d FROM bug3561"); while (rs.next()) System.out.println(rs.getString(1)); rs.close(); + stmt2.close(); + stmt1.executeUpdate("DROP TABLE bug3561"); + stmt1.close(); con.close(); } }
--- a/tests/BugExecuteUpdate_Bug_3350.java +++ b/tests/BugExecuteUpdate_Bug_3350.java @@ -16,15 +16,23 @@ public class BugExecuteUpdate_Bug_3350 { final Statement stmt = con.createStatement(); try { - executeDML(stmt, "INSERT INTO sys.keywords VALUES ('Bug_3350')"); // should insert 1 row - executeDML(stmt, "INSERT INTO sys.keywords VALUES ('Bug_3350')"); // this will result in an SQLException due to PK uniqueness violation + stmt.execute("CREATE TABLE t3350 (keyword VARCHAR(30) PRIMARY KEY)"); + con.commit(); + + executeDML(stmt, "INSERT INTO t3350 VALUES ('Bug_3350')"); // should insert 1 row + executeDML(stmt, "INSERT INTO t3350 VALUES ('Bug_3350')"); // this will result in an SQLException due to PK uniqueness violation con.rollback(); - executeDML(stmt, "INSERT INTO sys.keywords VALUES ('Bug_3350')"); // should insert 1 row - executeDML(stmt, "DELETE FROM sys.keywords WHERE \"keyword\" = 'Bug_3350'"); // should delete 1 row - executeDML(stmt, "DELETE FROM sys.keywords WHERE \"keyword\" = 'Bug_3350'"); // should delete 0 rows + executeDML(stmt, "INSERT INTO t3350 VALUES ('Bug_3350')"); // should insert 1 row + executeDML(stmt, "INSERT INTO t3350 VALUES ('1'), ('x'), ('3'), ('y')"); // should insert 4 rows + executeDML(stmt, "DELETE FROM t3350 WHERE \"keyword\" = 'Bug_3350'"); // should delete 1 row + executeDML(stmt, "DELETE FROM t3350 WHERE \"keyword\" = 'Bug_3350'"); // should delete 0 rows + executeDML(stmt, "UPDATE t3350 set \"keyword\" = keyword||'_ext'"); // should update 4 rows + executeDML(stmt, "DELETE FROM t3350"); // should delete 4 rows + con.commit(); - con.rollback(); + stmt.execute("DROP TABLE t3350"); + con.commit(); } catch (SQLException se) { System.out.println(se.getMessage()); } finally {
--- a/tests/BugResultSetMetaData_Bug_6183.java +++ b/tests/BugResultSetMetaData_Bug_6183.java @@ -10,7 +10,8 @@ import java.sql.*; public class BugResultSetMetaData_Bug_6183 { static final String dqTblName = "\"my dq_table\""; - static final String[] dqColNames = {"\"my space\"", "\"my, comma_space\"", "\"my$dollar\"", "\"my#hash\"", "\"my tab\"", "\"my ,tab_comma\"", "\"my, comma_tab\"", "\"my\\\"backslash_doublequote\""}; + static final String[] dqColNames = { "\"my space\"", "\"my, comma_space\"", "\"my$dollar\"", "\"my#hash\"", "\"my tab\"" + , "\"my ,tab_comma\"", "\"my, comma_tab\"", "\"my\\\"backslash_doublequote\"", "\"Abc\"", "\" \"", "\"123\"" }; public static void main(String[] args) throws Exception { // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers
new file mode 100644 --- /dev/null +++ b/tests/Bug_Connect_as_voc_getMetaData_Failure_Bug_6388.java @@ -0,0 +1,117 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 1997 - July 2008 CWI, August 2008 - 2017 MonetDB B.V. + */ + +import java.sql.*; + +public class Bug_Connect_as_voc_getMetaData_Failure_Bug_6388 { + public static void main(String[] args) throws SQLException + { + Connection con1 = null; + Statement stmt1 = null; + + // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers + con1 = DriverManager.getConnection(args[0]); + stmt1 = con1.createStatement(); + + // test the creation of a table with concurrent clients + try { + System.out.println("1. CREATE USER voc"); + stmt1.executeUpdate("CREATE USER \"voc\" WITH PASSWORD 'voc' NAME 'VOC Explorer' SCHEMA \"sys\""); + System.out.println("2. CREATE SCHEMA voc"); + stmt1.executeUpdate("CREATE SCHEMA \"voc\" AUTHORIZATION \"voc\""); + System.out.println("3. ALTER USER voc"); + stmt1.executeUpdate("ALTER USER \"voc\" SET SCHEMA \"voc\""); + System.out.println("creation succeeded :)"); + System.out.println(); + + login_as_voc_and_get_MetaData(args[0].replace("=monetdb", "=voc")); + + System.out.println(); + System.out.println("Cleanup created objects"); + System.out.println("5. ALTER USER voc"); + stmt1.executeUpdate("ALTER USER \"voc\" SET SCHEMA \"sys\""); + System.out.println("6. DROP SCHEMA voc"); + stmt1.executeUpdate("DROP SCHEMA \"voc\""); + System.out.println("7. DROP USER voc"); + stmt1.executeUpdate("DROP USER \"voc\""); + System.out.println("cleanup succeeded :)"); + } catch (SQLException e) { + System.out.println("FAILED creating user and schema voc. " + e.getMessage()); + } finally { + stmt1.close(); + con1.close(); + } + } + + private static void login_as_voc_and_get_MetaData(String connectString) { + Connection con2 = null; + + try { + System.out.println("4.1. connect as user: voc"); + con2 = DriverManager.getConnection(connectString); + System.out.println("connected :)"); + } catch (SQLException e) { + System.out.println("FAILED to connect as user voc. " + e.getMessage()); + return; + } + + try { + DatabaseMetaData dbmd = con2.getMetaData(); + + System.out.println("4.2. getUserName()"); + System.out.println("UserName = " + dbmd.getUserName()); + + System.out.println("4.3. getMaxConnections()"); + System.out.println("MaxConnections = " + dbmd.getMaxConnections()); + + System.out.println("4.4. getDatabaseProductVersion()"); + String dbmsVersion = dbmd.getDatabaseProductVersion(); // should be 11.27.1 or higher + boolean postJul2017 = ("11.27.1".compareTo(dbmsVersion) <= 0); + System.out.println("DatabaseProductVersion = " + (postJul2017 ? "11.27.+" : dbmsVersion)); + + System.out.println("4.5. getDatabaseMajorVersion()"); + System.out.println("DatabaseMajorVersion = " + dbmd.getDatabaseMajorVersion()); // should be 11 + + System.out.println("4.6. getDatabaseMinorVersion()"); + int dbmsMinorVersion = dbmd.getDatabaseMinorVersion(); // should be 27 or higher + System.out.println("DatabaseMinorVersion = " + (dbmsMinorVersion >= 27 ? "27+" : dbmsMinorVersion)); + + System.out.println("4.7. getTables(null, 'tmp', null, null)"); + ResultSet rs1 = dbmd.getTables(null, "tmp", null, null); + if (rs1 != null) { + System.out.println("List Tables in schema tmp:"); + while (rs1.next()) { + System.out.println(rs1.getString(3)); + } + rs1.close(); + } + System.out.println("completed listing Tables in schema tmp"); + + System.out.println("4.8. getTableTypes()"); + rs1 = dbmd.getTableTypes(); + if (rs1 != null) { + System.out.println("List TableTypes:"); + while (rs1.next()) { + System.out.println(rs1.getString(1)); + } + rs1.close(); + } + System.out.println("completed listing TableTypes"); + + System.out.println("voc meta data Test completed successfully"); + } catch (SQLException e) { + System.out.println("FAILED fetching MonetDatabaseMetaData. " + e.getMessage()); + } finally { + try { + con2.close(); + } catch (SQLException e) { + System.out.println("FAILED to close voc connection. " + e.getMessage()); + } + } + } +}
new file mode 100644 --- /dev/null +++ b/tests/Bug_PrepStmtSetObject_CLOB_6349.java @@ -0,0 +1,60 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 1997 - July 2008 CWI, August 2008 - 2017 MonetDB B.V. + */ + +import java.sql.*; + +public class Bug_PrepStmtSetObject_CLOB_6349 { + public static void main(String[] args) throws Exception { + // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers + Connection con = DriverManager.getConnection(args[0]); + Statement stmt = con.createStatement(); + PreparedStatement pstmt = null; + ParameterMetaData pmd = null; + ResultSet rs = null; + ResultSetMetaData rsmd = null; + + System.out.println("0. true\t" + con.getAutoCommit()); + + try { + stmt.executeUpdate("CREATE TABLE PrepStmtSetObject_CLOB (myint INT, myvarchar VARCHAR(15), myclob CLOB)"); + stmt.executeUpdate("INSERT INTO PrepStmtSetObject_CLOB VALUES (123, 'A string', 'A longer string')"); + stmt.executeUpdate("INSERT INTO PrepStmtSetObject_CLOB VALUES (NULL, NULL, NULL)"); // all NULLs + + pstmt = con.prepareStatement("SELECT myclob, myvarchar, myint FROM PrepStmtSetObject_CLOB WHERE myclob = ?"); + pmd = pstmt.getParameterMetaData(); + System.out.println("Prepared Query has " + pmd.getParameterCount() + " parameters. Type of first is: " + pmd.getParameterTypeName(1)); + rsmd = pstmt.getMetaData(); + System.out.println("Prepared Query has " + rsmd.getColumnCount() + " columns. Type of first is: " + rsmd.getColumnTypeName(1)); + + pstmt.setObject(1, "A longer string"); + rs = pstmt.executeQuery(); + rsmd = rs.getMetaData(); + System.out.println("Query ResultSet has " + rsmd.getColumnCount() + " columns. Type of first is: " + rsmd.getColumnTypeName(1)); + + boolean has_row = rs.next(); + boolean has_rows = rs.next(); + if (has_row == false || has_rows == true) + System.out.println("Fetching Query ResultSet failed"); + + stmt.executeUpdate("DROP TABLE PrepStmtSetObject_CLOB"); + + } catch (SQLException e) { + System.out.println("FAILED :( "+ e.getMessage()); + while ((e = e.getNextException()) != null) + System.out.println("FAILED :( " + e.getMessage()); + System.out.println("ABORTING TEST!!!"); + } finally { + if (rs != null) rs.close(); + if (pstmt != null) pstmt.close(); + stmt.close(); + } + + con.close(); + } +} +
new file mode 100644 --- /dev/null +++ b/tests/Bug_PrepStmtSetString_6382.java @@ -0,0 +1,145 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 1997 - July 2008 CWI, August 2008 - 2017 MonetDB B.V. + */ + +import java.sql.*; + +import nl.cwi.monetdb.jdbc.MonetINET; +import nl.cwi.monetdb.jdbc.MonetURL; + +public class Bug_PrepStmtSetString_6382 { + public static void main(String[] args) throws Exception { + // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers + Connection con = DriverManager.getConnection(args[0]); + System.out.println("0. true\t" + con.getAutoCommit()); + + Statement stmt = null; + PreparedStatement pstmt = null; + ParameterMetaData pmd = null; + ResultSet rs = null; + ResultSetMetaData rsmd = null; + try { + stmt = con.createStatement(); + String tableName = "PrepStmtSetString_6382"; + System.out.println("Creating table " + tableName); + stmt.executeUpdate("CREATE TABLE " + tableName + " (myint INT, myvarchar VARCHAR(15), myjson JSON, myuuid UUID, myurl URL, myinet INET)"); + + System.out.println("Inserting row 1"); + stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (1, 'row 1', '{}', uuid '34c8deb5-e608-406b-beda-6a951f73d455', 'https://www.monetdb.org/', '128.0.0.1')"); + + System.out.println("Inserting row 2"); + stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (2, 'row 2', '[]', NULL, NULL, NULL)"); + + + System.out.println("Creating a prepared statement with 6 parameters and inserting rows using setInt(), setString(), setNull(), setNString(), setURL(), setObject()."); + pstmt = con.prepareStatement("INSERT INTO " + tableName + " VALUES (?,?, ? ,?,? , ?)"); + pmd = pstmt.getParameterMetaData(); + int pcount = pmd.getParameterCount(); + System.out.println("Prepared Statement has " + pcount + " parameters:" + (pcount != 6 ? " ERROR: Expected 6 parameters!" : "")); + for (int p = 1; p <= pcount; p++) { + System.out.println(" Parameter " + p + " type is: " + pmd.getParameterTypeName(p) + ". JDBC SQL type: " + pmd.getParameterType(p)); + } + + int row = 3; + pstmt.setInt(1, row); + pstmt.setString(2, "row " + row); + pstmt.setString(3, "{\"menu\": {\n \"id\": \"file\",\n \"value\": \"File\",\n \"popup\": {\n \"menuitem\": [\n {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n ]\n }\n}}"); + pstmt.setNull(4, 0); + pstmt.setNull(5, 0); + pstmt.setNull(6, 0); + System.out.println("Inserting row " + row); + int inserted = pstmt.executeUpdate(); + System.out.println("Inserted " + inserted + " row"); + + row++; // row 4 + pstmt.setShort(1, (short)row); + pstmt.setNString(2, "row " + row); + pstmt.setNull(3, 0); + pstmt.setString(4, "4a148b7d-8d47-4e1e-a21e-09a71abf2215"); + System.out.println("Inserting row " + row); + inserted = pstmt.executeUpdate(); + System.out.println("Inserted " + inserted + " row"); + + row++; // row 5 + pstmt.setLong(1, row); + pstmt.setString(2, "row " + row); + pstmt.setNull(4, 0); + pstmt.setURL(5, new java.net.URL("https://www.cwi.nl/")); + System.out.println("Inserting row " + row); + inserted = pstmt.executeUpdate(); + System.out.println("Inserted " + inserted + " row"); + + row++; // row 6 + pstmt.setBigDecimal(1, new java.math.BigDecimal(row)); + pstmt.setNString(2, "row " + row); + pstmt.setNull(5, 0); + pstmt.setString(6, "127.255.255.255"); + System.out.println("Inserting row " + row); + inserted = pstmt.executeUpdate(); + System.out.println("Inserted " + inserted + " row"); + + /* also test generic setObject(int, String) */ + row++; // row 7 + pstmt.setObject(1, new Integer(row)); + pstmt.setObject(2, "row " + row); + pstmt.setObject(3, "{\"menu\": {\n \"header\": \"SVG Viewer\",\n \"items\": [\n {\"id\": \"Open\"},\n {\"id\": \"OpenNew\", \"label\": \"Open New\"},\n null,\n {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n {\"id\": \"OriginalView\", \"label\": \"Original View\"},\n null,\n {\"id\": \"Quality\"},\n {\"id\": \"Pause\"},\n {\"id\": \"Mute\"},\n null,\n {\"id\": \"Help\"},\n {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n ]\n}}"); + pstmt.setObject(4, "b39dc76e-4faf-4fd9-bc1e-17df48acf764"); + pstmt.setObject(5, "https://en.wikipedia.org/wiki/IP_address"); + pstmt.setObject(6, "223.255.255.255"); + System.out.println("Inserting row " + row); + inserted = pstmt.executeUpdate(); + System.out.println("Inserted " + inserted + " row"); + + row++; // row 8 + pstmt.setObject(1, new java.math.BigDecimal(row)); + pstmt.setObject(2, "row " + row); + pstmt.setObject(3, null); + pstmt.setObject(4, java.util.UUID.fromString("ff125769-b63c-4c3c-859f-5b84a9349e24")); + MonetURL myURL = new MonetURL("https://en.wikipedia.org/wiki/IP_address"); + pstmt.setObject(5, myURL); + MonetINET myINET = new MonetINET("223.234.245.255"); + pstmt.setObject(6, myINET); + System.out.println("Inserting row " + row); + inserted = pstmt.executeUpdate(); + System.out.println("Inserted " + inserted + " row"); + + + System.out.println("List contents of TABLE " + tableName + " after " + row + " rows inserted"); + rs = stmt.executeQuery("SELECT * FROM " + tableName + " ORDER BY 1"); + rsmd = rs.getMetaData(); + int colcount = rsmd.getColumnCount(); + System.out.println("Query has " + colcount + " output columns." + (colcount != 6 ? " ERROR: Expected 6 columns!" : "")); + row = 0; + while (rs.next()) { + System.out.print("row " + ++row); + for (int c = 1; c <= colcount; c++) { + System.out.print("\t" + rs.getString(c)); + } + System.out.println(); + } + + System.out.println(); + System.out.println("Cleanup TABLE " + tableName); + stmt.executeUpdate("DROP TABLE " + tableName); + System.out.println("Test completed successfully"); + } catch (SQLException e) { + System.err.println("FAILED :( "+ e.getMessage()); + while ((e = e.getNextException()) != null) + System.err.println("FAILED :( " + e.getMessage()); + System.err.println("ABORTING TEST!!!"); + } finally { + if (rs != null) + rs.close(); + if (pstmt != null) + pstmt.close(); + if (stmt != null) + stmt.close(); + con.close(); + } + } +} +
--- a/tests/Test_Dobjects.java +++ b/tests/Test_Dobjects.java @@ -7,37 +7,36 @@ */ import java.sql.*; -import java.util.*; public class Test_Dobjects { private static void dumpResultSet(ResultSet rs) throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); - System.out.println("Resultset with " + rsmd.getColumnCount() + " columns"); - for (int col = 1; col <= rsmd.getColumnCount(); col++) { + int columnCount = rsmd.getColumnCount(); + System.out.println("Resultset with " + columnCount + " columns"); + for (int col = 1; col <= columnCount; col++) { System.out.print(rsmd.getColumnName(col) + "\t"); } System.out.println(); while (rs.next()) { - for (int col = 1; col <= rsmd.getColumnCount(); col++) { + for (int col = 1; col <= columnCount; col++) { System.out.print(rs.getString(col) + "\t"); } System.out.println(); } + rs.close(); } public static void main(String[] args) throws Exception { // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers Connection con = DriverManager.getConnection(args[0]); - Statement stmt = con.createStatement(); - PreparedStatement pstmt; DatabaseMetaData dbmd = con.getMetaData(); - try { // inspect the catalog by use of dbmd functions dumpResultSet(dbmd.getCatalogs()); // dumpResultSet(dbmd.getSchemas()); // this produces different outputs on different platforms due to dependency on SAMTOOLS and NETCDF. so exclude it dumpResultSet(dbmd.getSchemas(null, "sys")); - dumpResultSet(dbmd.getTables(null, "sys", null, null)); +// dumpResultSet(dbmd.getTables(null, "sys", null, null)); // this produces different outputs on different platforms due to dependency on Geom and NETCDF. + dumpResultSet(dbmd.getTables(null, "tmp", null, null)); // schema tmp has 6 tables dumpResultSet(dbmd.getUDTs(null, "sys", null, null)); int[] UDTtypes = { Types.STRUCT, Types.DISTINCT }; dumpResultSet(dbmd.getUDTs(null, "sys", null, UDTtypes)); @@ -45,7 +44,6 @@ public class Test_Dobjects { System.out.println("FAILED :( "+ e.getMessage()); System.out.println("ABORTING TEST!!!"); } - con.close(); } }
--- a/tests/Test_PSmetadata.java +++ b/tests/Test_PSmetadata.java @@ -77,19 +77,4 @@ public class Test_PSmetadata { con.rollback(); con.close(); } - - private static String isInstance(Object obj, String type) { - if (obj == null) - return("(null)"); - try { - Class c = Class.forName(type); - if (c.isInstance(obj)) { - return(obj.getClass().getName() + " is an instance of " + type); - } else { - return(obj.getClass().getName() + " is NOT an instance of " + type); - } - } catch (ClassNotFoundException e) { - return("No such class: " + type); - } - } }
--- a/tests/build.xml +++ b/tests/build.xml @@ -18,7 +18,7 @@ Copyright 1997 - July 2008 CWI, August 2 <project name="JDBCTests" default="compile" basedir="."> <property file="build.properties" /> - <property file="../build.properties"/> <!-- included for version --> + <property file="../build.properties" /> <!-- included for version --> <!-- set global properties for this build --> <property name="srcdir" value="." /> @@ -30,7 +30,8 @@ Copyright 1997 - July 2008 CWI, August 2 value="jdbc:monetdb://localhost/?user=monetdb&password=monetdb${debug}" /> <property name="jdbctests-jar" value="${jardir}/jdbctests.jar" /> - <property name="jvm.version" value="1.7"/> + <property name="jvm.version" value="1.7" /> + <property name="javac.flags" value="-Xlint:-options" /> <!-- Prepares the build directory --> <target name="prepare"> @@ -94,13 +95,13 @@ Copyright 1997 - July 2008 CWI, August 2 <!-- Run tests --> <target name="test"> <antcall target="Test_Cautocommit" /> - <antcall target="Test_Cforkbomb" /> + <!-- <antcall target="Test_Cforkbomb" /> --> <antcall target="Test_CisValid" /> <antcall target="Test_Clargequery" /> <antcall target="Test_Cmanycon" /> <antcall target="Test_Creplysize" /> <antcall target="Test_Csavepoints" /> - <antcall target="Test_Csendthread" /> + <!-- <antcall target="Test_Csendthread" /> --> <antcall target="Test_Ctransaction" /> <antcall target="Test_Dobjects" /> <antcall target="Test_FetchSize" /> @@ -132,6 +133,9 @@ Copyright 1997 - July 2008 CWI, August 2 <antcall target="BugExecuteUpdate_Bug_3350" /> <antcall target="BugSetQueryTimeout_Bug_3357" /> <antcall target="BugResultSetMetaData_Bug_6183" /> + <antcall target="Bug_PrepStmtSetObject_CLOB_6349" /> + <antcall target="Bug_Connect_as_voc_getMetaData_Failure_Bug_6388" /> + <antcall target="Bug_PrepStmtSetString_6382" /> </target> <target name="test_class" depends="compile,jdbc"> @@ -381,4 +385,22 @@ Copyright 1997 - July 2008 CWI, August 2 </antcall> </target> + <target name="Bug_PrepStmtSetObject_CLOB_6349"> + <antcall target="test_class"> + <param name="test.class" value="Bug_PrepStmtSetObject_CLOB_6349" /> + </antcall> + </target> + + <target name="Bug_Connect_as_voc_getMetaData_Failure_Bug_6388"> + <antcall target="test_class"> + <param name="test.class" value="Bug_Connect_as_voc_getMetaData_Failure_Bug_6388" /> + </antcall> + </target> + + <target name="Bug_PrepStmtSetString_6382"> + <antcall target="test_class"> + <param name="test.class" value="Bug_PrepStmtSetString_6382" /> + </antcall> + </target> + </project>