Mercurial > hg > monetdb-java
changeset 487:35592dc59ecb
Merge remote-tracking branch 'github/master'
author | GIT repo for MonetDB <monetgit@dev.monetdb.org> |
---|---|
date | Wed, 16 Jun 2021 17:19:07 +0200 (2021-06-16) |
parents | 8e3fbb725faf (diff) 65d3f718fe25 (current diff) |
children | 59309e3e6daa |
files | |
diffstat | 17 files changed, 546 insertions(+), 465 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags +++ b/.hgtags @@ -5,3 +5,4 @@ fe8170e2b549c22ceb2d96301022b9304f62424d 6423fb0bf9ebba64167ca1b40b79aeacbb8e7c47 v2.28 70808ab71d2ad997d7b7fa38d675fc90f71a059c v2.29 bc39810b3faa4c52ce63b29147075e91266a3e84 v3.0 +a80b5db244b8ceeea936770b9c5f314d46251fb3 v3.1
--- a/ChangeLog +++ b/ChangeLog @@ -1,179 +1,37 @@ # ChangeLog file for monetdb-java # This file is updated with Maddlog +* Mon Jun 14 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Compiled and released new jar files: monetdb-jdbc-3.1.jre8.jar, + monetdb-mcl-1.20.jre8.jar and jdbcclient.jre8.jar + +* Thu Apr 29 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Improved performance of ResultSetMetaData methods getPrecision(), + getScale(), isNullable() and isAutoIncrement(). The data is fetched + from the server by sending a query. This used to be one query for + each column of the ResultSet. Now these metadata queries are combined + into one query fetching this meta data for up to 50 columns in one query. + This reduces the number of queries sent to the server significantly. + This is noticable for instance when using generic JDBC query tools + such as SQuirreL, DBeaver, which now respond much faster. + * Wed Mar 3 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> - Implemented PreparedStatement.toString() as requested by https://github.com/MonetDB/monetdb-java/issues/8 * Wed Mar 3 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> - Implemented fix for released monetdb-jdbc-3.0.jre8.jar and - monetdb-mcl-1.19.jre8.jar when it is was run using java build - 1.8.0_###. It would throw: java.lang.NoSuchMethodError: - java.nio.CharBuffer.mark()Ljava/nio/CharBuffer; at - org.monetdb.mcl.parser.StartOfHeaderParser.getNextAsString(Unknown - Source) at - org.monetdb.jdbc.MonetConnection$ResponseList.executeQuery(Unknown - Source) at - org.monetdb.jdbc.MonetConnection$ResponseList.processQuery(Unknown - Source) at org.monetdb.jdbc.MonetStatement.internalExecute(Unknown - Source) at org.monetdb.jdbc.MonetStatement.execute(Unknown Source) + monetdb-mcl-1.19.jre8.jar when it is was run using java build 1.8.0_###. + It would throw: + java.lang.NoSuchMethodError: java.nio.CharBuffer.mark()Ljava/nio/CharBuffer; + at org.monetdb.mcl.parser.StartOfHeaderParser.getNextAsString(Unknown Source) + at org.monetdb.jdbc.MonetConnection$ResponseList.executeQuery(Unknown Source) + at org.monetdb.jdbc.MonetConnection$ResponseList.processQuery(Unknown Source) + at org.monetdb.jdbc.MonetStatement.internalExecute(Unknown Source) + at org.monetdb.jdbc.MonetStatement.execute(Unknown Source) The problem is caused by a change in java.nio.CharBuffer API (return types of methods mark() and reset() have changed from Buffer to CharBuffer) from Java 8 to Java 9+. -* Wed Feb 17 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Compiled and released new jar files: monetdb-jdbc-3.0.jre8.jar, - monetdb-mcl-1.19.jre8.jar and jdbcclient.jre8.jar - - monetdb-jdbc-3.0.jre8.jar is a new major release of the MonetDB JDBC driver. - The MonetDB JDBC Driver is now compliant with the Javaâ„¢ Database - Connectivity (JDBC) 4.2 specification as defined in Java 8 and requires - Java 8 runtime (profile compact2) as minimum version. - - Important: the MonetDB JDBC driver class name has also been changed in - this release to: org.monetdb.jdbc.MonetDriver. The old driver class - (nl.cwi.monetdb.jdbc.MonetDriver) is also included in the jar file, but - only to ease the transition for existing deployments. It will be removed - in a future release of this JDBC driver. Please use the new driver - class name if this is used in your configuration files or Java code. - - The JdbcClient program (jdbcclient.jre8.jar) has been extended with - functionality to validate the integrity of the system tables (\vsci) or - to validate the integrity of data in tables of a specific schema (\vsi xyz) - based on defined declarative constraints (pkey, fkey, not null, etc.). - This will be usefull to find and report inconsistencies in your database. - This functionality is a beta release. Please let us know if you - encounter any issues running it. See below for more information. - - Besides a few bug fixes also performance has been improved in multiple areas. - -* Wed Feb 3 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Added support for escaped wildcards (\% en \_) in String arguments of - DatabaseMetaData methods which return a ResultSet, such as getTables(), - getColumns(), etc. When you do not want the characters % or _ to be - interpreted as wildcards but as normal characters you can prefix them - with a backslash (so \% and \_). Note: be sure all wildcards characters - in the String argument are escaped else the search must still use a - LIKE operator instead of an = comparison operator. - This fixes: https://github.com/MonetDB/monetdb-java/issues/3 - -* Thu Jan 28 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Corrected the ordering of the output of DatabaseMetaData methods - getImportedKeys(), getExportedKeys() and getCrossReference(). In cases - where a table would have multiple fks to the same external table, - the output was not as expected. This has been corrected, so the columns - now appear in the order as defined in the creation of the fks. - -* Thu Jan 28 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- The dumping of table definitions from JdbcClient program has been - improved. It now includes the ON UPDATE and ON DELETE rules for foreign - key constraints. Also it no longer generates CREATE INDEX statements - for foreign key constraints whose name is not system generated but - user specified. - -* Thu Jan 14 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Improved DatabaseMetaData.getTypeInfo() output for temporal data - types: sec_interval, day_interval, month_interval, date, time, timetz, - timestamp and timestamptz. - -* Wed Jan 6 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Corrected output of resultset columns UPDATE_RULE and DELETE_RULE - when calling DatabaseMetaData API methods getImportedKeys() or - getExportedKeys() or getCrossReference(). These columns used to - always return DatabaseMetaData.importedKeyNoAction but now they - can also report the other values when set: - DatabaseMetaData.importedKeyCascade - or DatabaseMetaData.importedKeyRestrict - or DatabaseMetaData.importedKeySetNull - or DatabaseMetaData.importedKeySetDefault. - -* Thu Nov 12 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Moved Java classes from packages starting with nl.cwi.monetdb.* - to package org.monetdb.* This naming complies to the Java Package - Naming convention as MonetDB's main website is www.monetdb.org. - To prevent problems with existing Java programs and JDBC driver - configurations we still support usage of the following classes: - nl.cwi.monetdb.jdbc.MonetDriver - nl.cwi.monetdb.jdbc.types.INET - nl.cwi.monetdb.jdbc.types.URL - nl.cwi.monetdb.mcl.net.MapiSocket - nl.cwi.monetdb.client.JdbcClient - They are implemented as simple wrappers of their org.monetdb.* equivalents. - Note: These nl.cwi.monetdb.* classes are now marked as deprecated and may - be removed in a future release. If you still use them in your Java code or - configuration files, update them to use the new package names. - -* Thu Oct 29 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Extended JdbcClient program with 3 new commands to quickly validate - data integrity: - \vsci validate sql system catalog integrity - \vsi <schema> validate integrity of data in the given schema - \vdbi validate integrity of data in all user schemas in the database - The current validations include: - - Primary Key uniqueness - - Primary Key column(s) being NOT NULL (currently only for \vsci) - - Unique constraint uniqueness - - Foreign Key referential integrity - - Column NOT NULL constraint - - Varchar(n) max length constraint - - Idem for char(n), clob(n), blob(n), json(n) and url(n). - It can be usefull to run \vsci before and after an upgrade of MonetDB server. - Use \vsi my_schema to validate data in all tables of a specific schema. - Use \vdbi to validate integrity of data in all user schemas in - the database. Note: this can take a while, depending on your number - of user schemas, tables, columns and rows. Despite being tested on several - internal dbs the functionality is still beta, so you can get false - errors reported. If you encounter any let us know asap. - -* Thu Oct 8 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Improved performance of ResultSetMetaData methods isAutoIncrement(), - getPrecision() and getScale() significantly for columns of specific data - types as in some cases no costly meta data query is executed anymore. - -* Thu Oct 8 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- The connection properties treat_clob_as_varchar and treat_blob_as_binary - are now set to true by default within the JDBC driver. This is done - as it results by default in less memory usage, (much) faster response - and better user experience for many generic JDBC applications (like - SQuirreL SQL, DBeaver, etc) when fetching data from CLOB or BLOB result - columns. See release.txt for more information and how you can turn - it off to get the old JDBC driver behavior if you require it. - -* Wed Oct 7 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Added support for new Java 8 java.sql.Types: Types.TIME_WITH_TIMEZONE and - Types.TIMESTAMP_WITH_TIMEZONE. - -* Wed Sep 23 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Updated JDBC driver to comply with JDBC 4.2 interface now we compile - for Java 8. This includes: - - adding 8 methods to MonetCallableStatement - - adding 2 methods to MonetDatabaseMetaData - - adding 3 methods to MonetPreparedStatement - - adding 4 methods to MonetResultSet - - adding 8 methods to MonetStatement - -* Wed Sep 23 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Corrected MonetDatabaseMetaData.getTypeInfo() - - The LITERAL_PREFIX column now includes the required casting name for - types: clob, inet, json, url, uuid and blob. - - The SEARCHABLE column now returns typePredBasic instead of typeSearchable - for type: blob. - - The AUTO_INCREMENT column now returns false for types: hugeint, decimal, - oid and wrd. - -* Thu Sep 10 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Removed support for deprecated MD5 encryption algorithm in MapiSocket. - -* Wed Sep 9 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Corrected Statement.executeBatch() method. It now implicitly clears the - batch buffer, ready to accept new addBatch() calls without the need for - an explicit clearBatch() call. - See also https://www.monetdb.org/bugzilla/show_bug.cgi?id=6953 - -* Wed Feb 19 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- Corrected the return value of getCatalogTerm() to "cat". - -* Wed Feb 12 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> -- As Java 7 is no longer supported we now compile for Java 8 as the - minimum required JVM version (profile compact2). - +For a complete list of changes in previous monetdb-java releases see: + https://www.monetdb.org/downloads/Java/archive/ChangeLog-Archive
--- a/ChangeLog-Archive +++ b/ChangeLog-Archive @@ -2,6 +2,38 @@ # This file contains all past monetdb-java ChangeLog entries # For every new release the ChangeLog is prepended to this file. +* Mon Jun 14 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Compiled and released new jar files: monetdb-jdbc-3.1.jre8.jar, + monetdb-mcl-1.20.jre8.jar and jdbcclient.jre8.jar + +* Thu Apr 29 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Improved performance of ResultSetMetaData methods getPrecision(), + getScale(), isNullable() and isAutoIncrement(). The data is fetched + from the server by sending a query. This used to be one query for + each column of the ResultSet. Now these metadata queries are combined + into one query fetching this meta data for up to 50 columns in one query. + This reduces the number of queries sent to the server significantly. + This is noticable for instance when using generic JDBC query tools + such as SQuirreL, DBeaver, which now respond much faster. + +* Wed Mar 3 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Implemented PreparedStatement.toString() as requested by + https://github.com/MonetDB/monetdb-java/issues/8 + +* Wed Mar 3 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Implemented fix for released monetdb-jdbc-3.0.jre8.jar and + monetdb-mcl-1.19.jre8.jar when it is was run using java build 1.8.0_###. + It would throw: + java.lang.NoSuchMethodError: java.nio.CharBuffer.mark()Ljava/nio/CharBuffer; + at org.monetdb.mcl.parser.StartOfHeaderParser.getNextAsString(Unknown Source) + at org.monetdb.jdbc.MonetConnection$ResponseList.executeQuery(Unknown Source) + at org.monetdb.jdbc.MonetConnection$ResponseList.processQuery(Unknown Source) + at org.monetdb.jdbc.MonetStatement.internalExecute(Unknown Source) + at org.monetdb.jdbc.MonetStatement.execute(Unknown Source) + The problem is caused by a change in java.nio.CharBuffer API (return + types of methods mark() and reset() have changed from Buffer to + CharBuffer) from Java 8 to Java 9+. + * Wed Feb 17 2021 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> - Compiled and released new jar files: monetdb-jdbc-3.0.jre8.jar, monetdb-mcl-1.19.jre8.jar and jdbcclient.jre8.jar
--- a/build.properties +++ b/build.properties @@ -9,7 +9,7 @@ # major release number MCL_MAJOR=1 # minor release number -MCL_MINOR=19 +MCL_MINOR=20 ## @@ -19,7 +19,7 @@ MCL_MINOR=19 # major release number JDBC_MAJOR=3 # minor release number -JDBC_MINOR=0 +JDBC_MINOR=1 # an additional identifying string JDBC_VER_SUFFIX=Liberica # the default port to connect on, if no port given when using SQL
--- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>monetdb</groupId> <artifactId>monetdb-jdbc</artifactId> - <version>3.0</version> + <version>3.1</version> <name>${project.groupId}:${project.artifactId}</name> <description>MonetDB JDBC driver</description> <url>https://www.monetdb.org</url>
--- a/release.txt +++ b/release.txt @@ -1,6 +1,6 @@ RELEASE NOTES -MonetDB JDBC driver version 3.0 (Liberica/MCL-1.19) -Release date: 2021-02-17 +MonetDB JDBC driver version 3.1 (Liberica/MCL-1.20) +Release date: 2021-06-14 The Java Database Connectivity (JDBC) API provides universal data access from the Java programming language. @@ -11,6 +11,10 @@ For more information see https://www.mon The latest MonetDB JDBC driver can be downloaded from https://www.monetdb.org/downloads/Java/ +The sources for this JDBC driver and related Java programs can be found at: +https://dev.monetdb.org/hg/monetdb-java/file/tip + + The MonetDB JDBC connection URL format to use is: jdbc:monetdb://<hostname>[:<portnr>]/<databasename>[?<property>=<value>[&<property>=<value>]] @@ -58,6 +62,14 @@ For example: See also: https://www.monetdb.org/Documentation/SQLreference/Programming/JDBC +The MonetDB JDBC driver class name is: org.monetdb.jdbc.MonetDriver +This has been changed as of release 3.0 (monetdb-jdbc-3.0.jre8.jar). +The old driver class (nl.cwi.monetdb.jdbc.MonetDriver) has been deprecated. +It is still included in the jar file to ease the transition for existing deployments. +However it will be removed in a future release of the MonetDB JDBC driver. +Please use the new driver class name asap in your configuration files or Java code. + + JDBC COMPLIANCE The MonetDB JDBC driver is a type 4 driver (100% pure Java) and complies to JDBC 4.2 definition, see @@ -79,6 +91,8 @@ please let us know at our bugtracker: Currently implemented JDBC 4.2 interfaces include: * java.sql.Driver + The next method is NOT useable/supported: + - getParentLogger * java.sql.Connection The next features/methods are NOT useable/supported: @@ -127,7 +141,11 @@ Currently implemented JDBC 4.2 interface - getAsciiStream, getUnicodeStream - getNClob - getRef, getRowId, getSQLXML - - All methods related to updateable result sets + - getObject(column, Class<T> type) + - moveToCurrentRow, moveToInsertRow, + - All methods related to updateable result sets such as: + updateArray ... updateTimestamp, cancelRowUpdates, + deleteRow, insertRow, refreshRow * java.sql.ResultSetMetaData @@ -151,6 +169,8 @@ Currently implemented JDBC 4.2 interface and by class: org.monetdb.jdbc.types.URL * javax.sql.DataSource (not tested) + The next method is NOT useable/supported: + - getParentLogger The following java.sql.* interfaces are NOT implemented:
--- a/src/main/java/org/monetdb/client/JdbcClient.java +++ b/src/main/java/org/monetdb/client/JdbcClient.java @@ -18,6 +18,7 @@ import org.monetdb.util.XMLExporter; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.Console; import java.io.IOException; import java.io.InputStreamReader; import java.io.File; @@ -257,7 +258,11 @@ public class JdbcClient { /* cannot (yet // we need the password from the user, fetch it with a pseudo // password protector if (pass == null) { - final char[] tmp = System.console().readPassword("password: "); + final Console syscon = System.console(); + char[] tmp = null; + if (syscon != null) { + tmp = syscon.readPassword("password: "); + } if (tmp == null) { System.err.println("Invalid password!"); System.exit(1); @@ -332,26 +337,27 @@ public class JdbcClient { /* cannot (yet dbmd = null; } + stmt = con.createStatement(); // is used by processInteractive(), processBatch(), doDump() + in = new BufferedReader(new InputStreamReader(System.in)); out = new PrintWriter(new BufferedWriter(new java.io.OutputStreamWriter(System.out))); - stmt = con.createStatement(); // is used by doDump - // see if we will have to perform a database dump (only in SQL mode) if ("sql".equals(lang) && copts.getOption("dump").isPresent() && dbmd != null) { + final int argcount = copts.getOption("dump").getArgumentCount(); + // use the given file for writing oc = copts.getOption("file"); if (oc.isPresent()) out = new PrintWriter(new BufferedWriter(new java.io.FileWriter(oc.getArgument()))); - // we only want user tables and views to be dumped, unless a specific table is requested + // we only want user tables and views to be dumped (DDL and optional data), unless a specific table is requested final String[] types = {"TABLE","VIEW","MERGE TABLE","REMOTE TABLE","REPLICA TABLE","STREAM TABLE"}; // Future: fetch all type names using dbmd.getTableTypes() and construct String[] with all // table type names excluding the SYSTEM ... ones and LOCAL TEMPORARY TABLE ones. - // request the list of tables available in the current schema in the database - ResultSet tbl = dbmd.getTables(null, con.getSchema(), null, - (copts.getOption("dump").getArgumentCount() == 0) ? types : null); + // request the list of tables/views available in the current schema in the database + ResultSet tbl = dbmd.getTables(null, con.getSchema(), null, (argcount == 0) ? types : null); // fetch all tables and store them in a LinkedList of Table objects final LinkedList<Table> tables = new LinkedList<Table>(); while (tbl.next()) { @@ -379,7 +385,7 @@ public class JdbcClient { /* cannot (yet out.println("START TRANSACTION;\n"); // dump specific table(s) or not? - if (copts.getOption("dump").getArgumentCount() > 0) { // yes we do + if (argcount > 0) { // yes we do final String[] dumpers = copts.getOption("dump").getArguments(); for (int i = 0; i < tables.size(); i++) { Table ttmp = tables.get(i); @@ -813,6 +819,8 @@ public class JdbcClient { /* cannot (yet MDBvalidator.validateDBIntegrity(con, true); } else if (command.equals("\\vdbi_noheader")) { // used only for internal automated testing MDBvalidator.validateDBIntegrity(con, false); + } else { + doProcess = true; } } else if (command.startsWith("\\l") || command.startsWith("\\i")) { String object = command.substring(2).trim(); @@ -903,7 +911,7 @@ public class JdbcClient { /* cannot (yet // execute the query, let the driver decide what type it is int aff = -1; - boolean nextRslt = stmt.execute(query, Statement.RETURN_GENERATED_KEYS); + boolean nextRslt = stmt.execute(query, Statement.RETURN_GENERATED_KEYS); if (!nextRslt) aff = stmt.getUpdateCount(); do { @@ -959,8 +967,7 @@ public class JdbcClient { /* cannot (yet } while ((nextRslt = stmt.getMoreResults()) || (aff = stmt.getUpdateCount()) != -1); - // if there were warnings for this statement, - // and/or connection show them! + // if there were warnings for this statement show them! warn = stmt.getWarnings(); while (warn != null) { System.err.println("Statement warning: " + warn.getMessage()); @@ -968,6 +975,7 @@ public class JdbcClient { /* cannot (yet } stmt.clearWarnings(); + // if there were warnings for this connection show them! warn = con.getWarnings(); while (warn != null) { // suppress warning when issueing a "set schema xyz;" command @@ -1036,11 +1044,12 @@ public class JdbcClient { /* cannot (yet exporter.dumpSchema(dbmd, tableType, table.getSchem(), table.getName()); out.println(); - // only dump data from real tables, not from views / MERGE / REMOTE / REPLICA tables + // only dump data from real tables, not from VIEWs / MERGE / REMOTE / REPLICA / STREAM tables if (tableType.contains("TABLE") && !tableType.equals("MERGE TABLE") && !tableType.equals("REMOTE TABLE") - && !tableType.equals("REPLICA TABLE")) { + && !tableType.equals("REPLICA TABLE") + && !tableType.equals("STREAM TABLE")) { final ResultSet rs = stmt.executeQuery("SELECT * FROM " + table.getFqnameQ()); if (rs != null) { exporter.dumpResultSet(rs);
--- a/src/main/java/org/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/org/monetdb/jdbc/MonetConnection.java @@ -140,13 +140,16 @@ public class MonetConnection private final int lang; /** Whether or not BLOB is mapped to Types.VARBINARY instead of Types.BLOB within this connection */ - private boolean treatBlobAsVarBinary = true; // turned on by default for optimal performance (from JDBC Driver release 2.30 onwards) + private boolean treatBlobAsVarBinary = true; // turned on by default for optimal performance (from JDBC Driver release 3.0 onwards) /** Whether or not CLOB is mapped to Types.VARCHAR instead of Types.CLOB within this connection */ - private boolean treatClobAsVarChar = true; // turned on by default for optimal performance (from JDBC Driver release 2.30 onwards) + private boolean treatClobAsVarChar = true; // turned on by default for optimal performance (from JDBC Driver release 3.0 onwards) /** The last set query timeout on the server as used by Statement, PreparedStatement and CallableStatement */ protected int lastSetQueryTimeout; // 0 means no timeout, which is the default on the server + /** A cache to reduce the number of DatabaseMetaData objects created by getMetaData() to maximum 1 per connection */ + private DatabaseMetaData dbmd; + /** * Constructor of a Connection for MonetDB. At this moment the @@ -586,10 +589,14 @@ public class MonetConnection */ @Override public DatabaseMetaData getMetaData() throws SQLException { + // for debug: System.out.println("calling getMetaData()"); if (lang != LANG_SQL) throw new SQLException("This method is only supported in SQL mode", "M0M04"); - - return new MonetDatabaseMetaData(this); + if (dbmd == null) { + // first use, construct it once and reuse it for all next calls + dbmd = new MonetDatabaseMetaData(this); + } + return dbmd; } /** @@ -1193,8 +1200,8 @@ public class MonetConnection */ @Override public String toString() { - return "MonetDB Connection (" + getJDBCURL() + ") " + - (closed ? "disconnected" : "connected"); + return "MonetDB Connection (" + getJDBCURL() + + (closed ? ") disconnected" : ") connected"); } //== Java 1.6 methods (JDBC 4.0) @@ -1687,10 +1694,12 @@ public class MonetConnection checkNotClosed(); Statement st = null; try { + final String callstmt; // as of release Jun2020 (11.37.7) the function sys.settimeout(bigint) is deprecated and replaced by new sys.setquerytimeout(int) - final boolean postJun2020 = (getDatabaseMajorVersion() >=11) && (getDatabaseMinorVersion() >= 37); - final String callstmt = postJun2020 ? "CALL sys.\"setquerytimeout\"(" + millis + ")" - : "CALL sys.\"settimeout\"(" + millis + ")"; + if ((getDatabaseMajorVersion() == 11) && (getDatabaseMinorVersion() < 37)) + callstmt = "CALL sys.\"settimeout\"(" + millis + ")"; + else + callstmt = "CALL sys.\"setquerytimeout\"(" + millis + ")"; // for debug: System.out.println("Before: " + callstmt); st = createStatement(); st.execute(callstmt); @@ -1860,8 +1869,8 @@ public class MonetConnection if (env_monet_version != null) { try { // from version string such as 11.33.9 extract number: 11 - final int start = env_monet_version.indexOf('.'); - databaseMajorVersion = Integer.parseInt((start >= 0) ? env_monet_version.substring(0, start) : env_monet_version); + final int end = env_monet_version.indexOf('.'); + databaseMajorVersion = Integer.parseInt((end >= 0) ? env_monet_version.substring(0, end) : env_monet_version); } catch (NumberFormatException nfe) { // ignore } @@ -2036,8 +2045,10 @@ public class MonetConnection private void sendCommand(final String command, final boolean usequeryTempl) throws SQLException { synchronized (server) { try { - out.writeLine(usequeryTempl ? (queryTempl[0] + command + queryTempl[1]) - : (commandTempl[0] + command + commandTempl[1]) ); + if (usequeryTempl) + out.writeLine(queryTempl[0] + command + queryTempl[1]); + else + out.writeLine(commandTempl[0] + command + commandTempl[1]); final String error = in.waitForPrompt(); if (error != null) throw new SQLException(error.substring(6), error.substring(0, 5)); @@ -2959,7 +2970,9 @@ public class MonetConnection * then ignore this call. If it is set to 0 we get a * prompt after the server sent it's header. */ - int size = (cachesize == 0 ? defaultFetchSize : cachesize); + int size = cachesize; + if (size == 0) + size = defaultFetchSize; if (maxrows > 0 && maxrows < size) size = (int)maxrows; // don't do work if it's not needed @@ -2972,7 +2985,7 @@ public class MonetConnection // }}} set reply size // send query to the server - out.writeLine( (templ[0] == null ? "" : templ[0]) + query + (templ[1] == null ? "" : templ[1]) ); + out.writeLine(templ[0] + query + templ[1]); // go for new results String tmpLine = in.readLine(); @@ -3013,7 +3026,7 @@ public class MonetConnection res = new SchemaResponse(); break; case StartOfHeaderParser.Q_TRANS: - final boolean ac = sohp.getNextAsString().equals("t") ? true : false; + final boolean ac = sohp.getNextAsString().equals("t"); if (autoCommit && ac) { addWarning("Server enabled auto commit mode " + "while local state already was auto commit.", "01M11"); @@ -3027,7 +3040,11 @@ public class MonetConnection sohp.getNextAsInt(); // columncount final int rowcount = sohp.getNextAsInt(); final int offset = sohp.getNextAsInt(); - final ResultSetResponse t = (rsresponses != null) ? rsresponses.get(Integer.valueOf(id)) : null; + final ResultSetResponse t; + if (rsresponses != null) + t = rsresponses.get(Integer.valueOf(id)); + else + t = null; if (t == null) { error = "M0M12!no ResultSetResponse with id " + id + " found"; break; @@ -3038,11 +3055,12 @@ public class MonetConnection } break; } // end of switch (sohp.parse(tmpLine)) } catch (MCLParseException e) { + final int offset = e.getErrorOffset(); error = "M0M10!error while parsing start of header:\n" + e.getMessage() + - " found: '" + tmpLine.charAt(e.getErrorOffset()) + "'" + - " in: \"" + tmpLine + "\"" + - " at pos: " + e.getErrorOffset(); + " found: '" + tmpLine.charAt(offset) + + "' in: \"" + tmpLine + + "\" at pos: " + offset; // flush all the rest in.waitForPrompt(); linetype = in.getLineType();
--- a/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java +++ b/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java @@ -2131,7 +2131,9 @@ public class MonetDatabaseMetaData "WHEN c.\"type\" IN ('int','smallint','tinyint','bigint','hugeint','float','real','double','oid','wrd') THEN 2 " + "ELSE 0 END AS int) AS \"NUM_PREC_RADIX\", " + "cast(CASE c.\"null\" WHEN true THEN ").append(ResultSetMetaData.columnNullable) - .append(" WHEN false THEN ").append(ResultSetMetaData.columnNoNulls).append(" END AS int) AS \"NULLABLE\", ") + .append(" WHEN false THEN ").append(ResultSetMetaData.columnNoNulls) + .append(" ELSE ").append(ResultSetMetaData.columnNullableUnknown) + .append(" END AS int) AS \"NULLABLE\", ") .append(useCommentsTable ? "cm.\"remark\"" : "cast(null AS varchar(9999))").append(" AS \"REMARKS\", " + "c.\"default\" AS \"COLUMN_DEF\", " + "cast(0 as int) AS \"SQL_DATA_TYPE\", " +
--- a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java +++ b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java @@ -80,7 +80,8 @@ public class MonetPreparedStatement private final int size; private final int rscolcnt; - private final String[] values; + private int paramCount = 0; + private final String[] paramValues; /* placeholders for date/time pattern formats created once (only when needed), used multiple times */ /** Format of a timestamp with RFC822 time zone */ @@ -139,7 +140,6 @@ public class MonetPreparedStatement schema = new String[size]; table = new String[size]; column = new String[size]; - values = new String[size]; // fill the arrays final ResultSet rs = super.getResultSet(); @@ -169,13 +169,17 @@ public class MonetPreparedStatement schema[i] = rs.getString(schema_colnr); table[i] = rs.getString(table_colnr); column[i] = rs.getString(column_colnr); + // System.out.println("column " + i + " has value: " + column[i]); /* 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]); + if (column[i] == null) + paramCount++; } rs.close(); } + paramValues = new String[paramCount + 1]; // parameters start from 1 + // PreparedStatements are by default poolable poolable = true; } @@ -212,7 +216,7 @@ public class MonetPreparedStatement schema = null; table = null; column = null; - values = null; + paramValues = null; id = -1; size = -1; rscolcnt = -1; @@ -249,8 +253,8 @@ public class MonetPreparedStatement */ @Override public void clearParameters() { - for (int i = 0; i < size; i++) { - values[i] = null; + for (int param = 1; param <= paramCount; param++) { + paramValues[param] = null; } } @@ -785,17 +789,10 @@ public class MonetPreparedStatement * object contains information. * * @return the number of parameters - * @throws SQLException if a database access error occurs */ @Override - public int getParameterCount() throws SQLException { - int cnt = 0; - - for (int i = 0; i < size; i++) { - if (column[i] == null) - cnt++; - } - return cnt; + public int getParameterCount() { + return paramCount; } /** @@ -809,10 +806,9 @@ public class MonetPreparedStatement * one of ParameterMetaData.parameterNoNulls, * ParameterMetaData.parameterNullable, or * ParameterMetaData.parameterNullableUnknown - * @throws SQLException if a database access error occurs */ @Override - public int isNullable(final int param) throws SQLException { + public int isNullable(final int param) { return ParameterMetaData.parameterNullableUnknown; } @@ -962,10 +958,9 @@ public class MonetPreparedStatement * ParameterMetaData.parameterModeOut, or * ParameterMetaData.parameterModeInOut * ParameterMetaData.parameterModeUnknown. - * @throws SQLException if a database access error occurs */ @Override - public int getParameterMode(final int param) throws SQLException { + public int getParameterMode(final int param) { return ParameterMetaData.parameterModeIn; } }; @@ -1263,7 +1258,7 @@ public class MonetPreparedStatement @Override public void setBytes(final int parameterIndex, final byte[] x) throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -1358,7 +1353,7 @@ public class MonetPreparedStatement @Override public void setClob(final int parameterIndex, final Clob x) throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -1379,7 +1374,7 @@ public class MonetPreparedStatement @Override public void setClob(final int parameterIndex, final Reader reader) throws SQLException { if (reader == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -1417,7 +1412,7 @@ public class MonetPreparedStatement @Override public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { if (reader == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } if (length < 0 || length > Integer.MAX_VALUE) { @@ -1471,7 +1466,7 @@ public class MonetPreparedStatement throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -1780,7 +1775,7 @@ public class MonetPreparedStatement throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -2218,7 +2213,7 @@ public class MonetPreparedStatement @Override public void setString(final int parameterIndex, final String x) throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -2530,7 +2525,7 @@ public class MonetPreparedStatement throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -2600,7 +2595,7 @@ public class MonetPreparedStatement throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -2674,7 +2669,7 @@ public class MonetPreparedStatement @Override public void setURL(final int parameterIndex, final URL x) throws SQLException { if (x == null) { - setNull(parameterIndex, -1); + setValue(parameterIndex, "NULL"); return; } @@ -2725,14 +2720,15 @@ public class MonetPreparedStatement */ @Override public String toString() { - final StringBuilder sb = new StringBuilder(256); + final StringBuilder sb = new StringBuilder(128 + paramCount * 64); sb.append("Prepared SQL: ").append(sqlStatement).append("\n"); int param = 1; 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) { - sb.append(" parameter ").append(param++).append(" ").append(monetdbType[i]); - sb.append(", set value: ").append((values[i] != null) ? values[i] : "<null>").append("\n"); + sb.append(" parameter ").append(param).append(" ").append(monetdbType[i]); + sb.append(", set value: ").append((paramValues[param] != null) ? paramValues[param] : "<null>").append("\n"); + param++; } } return sb.toString(); @@ -2788,37 +2784,41 @@ public class MonetPreparedStatement * @throws SQLException if the given index is out of bounds */ private final void setValue(final int parameterIndex, final String val) throws SQLException { - values[getParamIdx(parameterIndex)] = (val == null ? "NULL" : val); + if (parameterIndex < 1 || parameterIndex > paramCount) + throw new SQLException("No such parameter with index: " + parameterIndex, "M1M05"); + + if (val != null) + paramValues[parameterIndex] = val; + else + paramValues[parameterIndex] = "NULL"; } /** - * Transforms the prepare query into a simple SQL query by replacing - * the ?'s with the given column contents. - * Mind that the JDBC specs allow `reuse' of a value for a column over - * multiple executes. + * Constructs an "exec ##(paramval, ...)" statement string for the current parameter values. + * Mind that the JDBC specs allow 'reuse' of a value for a parameter over multiple executes. * - * @return the simple SQL string for the prepare query - * @throws SQLException if not all columns are set + * @return the "exec ##(...)" string + * @throws SQLException if not all parameters are set with a value */ + private StringBuilder execStmt; // created once, re-used multiple times so much less objects are created and gc-ed private final String transform() throws SQLException { - final StringBuilder buf = new StringBuilder(8 + 12 * size); - 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++) { - if (column[i] != null) - continue; - col++; - if (col > 1) - buf.append(','); - if (values[i] == null) - throw new SQLException("Cannot execute, parameter " + col + " is missing.", "M1M05"); + if (execStmt == null) + // first time use, create it once + execStmt = new StringBuilder(32 + paramCount * 32); + else + execStmt.setLength(0); // clear the buffer - buf.append(values[i]); + execStmt.append("exec ").append(id).append('('); + // check if all parameters are set and add the parameter values + for (int param = 1; param <= paramCount; param++) { + if (paramValues[param] == null) + throw new SQLException("Cannot execute, parameter " + param + " is missing.", "M1M05"); + if (param > 1) + execStmt.append(','); + execStmt.append(paramValues[param]); } - buf.append(')'); - - return buf.toString(); + execStmt.append(')'); + return execStmt.toString(); } /**
--- a/src/main/java/org/monetdb/jdbc/MonetResultSet.java +++ b/src/main/java/org/monetdb/jdbc/MonetResultSet.java @@ -263,7 +263,10 @@ public class MonetResultSet // store it curRow = row; - final String tmpLine = (header != null) ? header.getLine(row - 1) : null; + if (header == null) + return false; + + final String tmpLine = header.getLine(row - 1); if (tmpLine == null) return false; @@ -434,7 +437,7 @@ public class MonetResultSet return null; return new java.io.ByteArrayInputStream(bte); } - throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05"); + throw new SQLException("Cannot operate on type: " + types[columnIndex - 1], "M1M05"); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } @@ -904,7 +907,7 @@ public class MonetResultSet case Types.LONGVARBINARY: return MonetBlob.hexStrToByteArray(val); default: - throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05"); + throw new SQLException("Cannot operate on type: " + types[columnIndex - 1], "M1M05"); } } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); @@ -971,8 +974,8 @@ public class MonetResultSet */ @Override public String getCursorName() throws SQLException { - throw new SQLException("Positioned updates not supported for this " + - "cursor (" + (header != null ? header.id : "") + ")", "0AM21"); + throw new SQLException("Positioned updates not supported for this cursor (" + + (header != null ? header.id + ")" : ")"), "0AM21"); } /** @@ -1052,7 +1055,7 @@ public class MonetResultSet break; case ResultSet.FETCH_REVERSE: case ResultSet.FETCH_UNKNOWN: - throw new SQLException("Not supported direction " + direction, "0A000"); + throw new SQLException("Not supported direction: " + direction, "0A000"); default: throw new SQLException("Illegal direction: " + direction, "M1M05"); } @@ -1250,13 +1253,16 @@ public class MonetResultSet private final String[] schemas = (header != null) ? header.getSchemaNames() : null; private final String[] tables = (header != null) ? header.getTableNames() : null; private final MonetConnection conn = (MonetConnection)getStatement().getConnection(); - // for the more expensive methods (getPrecision(), getScale(), isNullable(), isAutoIncrement()), we - // use caches to store precision, scale and isNullable values from getColumnInfo() combined per fully qualified column. + + // for the methods: getPrecision(), getScale(), isNullable() and isAutoIncrement(), we use + // caches to store precision, scale, isNullable and isAutoincrement values for each resultset column + // so they do not need to queried and fetched from the server again and again. private final int array_size = columns.length + 1; // add 1 as in JDBC columns start from 1 (array from 0). + private final boolean[] _is_queried = new boolean[array_size]; private final boolean[] _is_fetched = new boolean[array_size]; private final int[] _precision = new int[array_size]; private final int[] _scale = new int[array_size]; - private final int[] _isNullable = new int[array_size]; + private final int[] _isNullable = new int[array_size]; private final boolean[] _isAutoincrement = new boolean[array_size]; /** @@ -1269,81 +1275,161 @@ public class MonetResultSet } /** - * A private method to fetch the precision, scale, isNullable and isAutoincrement value for a fully qualified column. - * As getColumnInfo() is an expensive method we call it only once per column and store - * the precision, scale, isNullable and isAutoincrement values in the above array caches. - * Also we only call getColumnInfo() when we have a non empty schema name and table name and column name. + * A private method to fetch the precision, scale, isNullable and isAutoincrement values + * combined for a specific column. + * The fetched values are stored in the above array caches. */ - private final void fetchColumnInfo(final int column) throws SQLException - { + private final void fetchColumnInfo(final int column) throws SQLException { + // for debug: System.out.println("fetchColumnInfo(" + column + ")"); checkColumnIndexValidity(column); - _is_fetched[column] = true; + if (_is_fetched[column] != true) { + // fetch column info for multiple columns combined in one go, starting at 1 + fetchManyColumnsInfo(1); + if (_is_fetched[column] != true) { + // fetch info for column x if it was not fetched by the previous call + fetchManyColumnsInfo(column); + } + } + + if (_is_fetched[column]) + return; + + // apparently no data could be fetched for this resultset column, fall back to defaults _precision[column] = 0; _scale[column] = 0; _isNullable[column] = columnNullableUnknown; _isAutoincrement[column] = false; - - // we will only call getColumnInfo() when we have a specific schema name, table name and column name - final String schName = getSchemaName(column); - if (schName != null && !schName.isEmpty()) { - final String tblName = getTableName(column); - if (tblName != null && !tblName.isEmpty()) { - final String colName = getColumnName(column); - if (colName != null && !colName.isEmpty()) { - // for precision, scale, isNullable and isAutoincrement we query the information from data dictionary - final ResultSet colInfo = getColumnInfo(schName, tblName, colName); - if (colInfo != null) { - // we expect exactly one row in the resultset - if (colInfo.next()) { - _precision[column] = colInfo.getInt(1); // col 1 (was 7) is "COLUMN_SIZE" - _scale[column] = colInfo.getInt(2); // col 2 (was 9) is "DECIMAL_DIGITS" - _isNullable[column] = colInfo.getInt(3); // col 3 (was 11) is "NULLABLE" - _isAutoincrement[column] = colInfo.getBoolean(4); // col 4 (was 23) is "IS_AUTOINCREMENT" + } + + /** + * A private method to fetch the precision, scale, isNullable and isAutoincrement values + * for many fully qualified columns combined in one SQL query to reduce the number of queries sent. + * As fetching this meta information from the server per column is costly we combine the querying of + * the precision, scale, isNullable and isAutoincrement values and cache it in internal arrays. + * We also do this for many (up to 50) columns combined in one query to reduce + * the number of queries needed for fetching this metadata for all resultset columns. + * Many generic JDBC database tools (e.g. SQuirreL) request this meta data for each column of each resultset, + * so these optimisations reduces the number of meta data queries significantly. + */ + private final void fetchManyColumnsInfo(final int column) throws SQLException { + // for debug: System.out.println("fetchManyColumnsInfo(" + column + ")"); + + // Most queries have less than 50 resultset columns + // So 50 is a good balance between speedup (up to 49x) and size of query sent to server + final int MAX_COLUMNS_PER_QUERY = 50; + + final StringBuilder query = new StringBuilder(600 + (MAX_COLUMNS_PER_QUERY * 150)); + /* next SQL query is a simplified version of query in MonetDatabaseMetaData.getColumns(), to fetch only the needed attributes of a column */ + query.append("SELECT " + + "s.\"name\" AS schnm, " + + "t.\"name\" AS tblnm, " + + "c.\"name\" AS colnm, " + + "c.\"type_digits\", " + + "c.\"type_scale\", " + + "cast(CASE c.\"null\" WHEN true THEN ").append(ResultSetMetaData.columnNullable) + .append(" WHEN false THEN ").append(ResultSetMetaData.columnNoNulls) + .append(" ELSE ").append(ResultSetMetaData.columnNullableUnknown) + .append(" END AS int) AS nullable, ").append( + "cast(CASE WHEN c.\"default\" IS NOT NULL AND c.\"default\" LIKE 'next value for %' THEN true ELSE false END AS boolean) AS isautoincrement " + + "FROM \"sys\".\"columns\" c " + + "JOIN \"sys\".\"tables\" t ON c.\"table_id\" = t.\"id\" " + + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + + "WHERE "); + + /* combine the conditions for multiple (up to 50) columns into the WHERE-clause */ + String schName = null; + String tblName = null; + String colName = null; + int queriedcolcount = 0; + for (int col = column; col < array_size && queriedcolcount < MAX_COLUMNS_PER_QUERY; col++) { + if (_is_fetched[col] != true) { + if (_is_queried[col] != true) { + _precision[col] = 0; + _scale[col] = 0; + _isNullable[col] = columnNullableUnknown; + _isAutoincrement[col] = false; + schName = getSchemaName(col); + if (schName != null && !schName.isEmpty()) { + tblName = getTableName(col); + if (tblName != null && !tblName.isEmpty()) { + colName = getColumnName(col); + if (colName != null && !colName.isEmpty()) { + if (queriedcolcount > 0) + query.append(" OR "); + query.append("(s.\"name\" = ").append(MonetWrapper.sq(schName)); + query.append(" AND t.\"name\" = ").append(MonetWrapper.sq(tblName)); + query.append(" AND c.\"name\" = ").append(MonetWrapper.sq(colName)); + query.append(")"); + _is_queried[col] = true; // flag it + queriedcolcount++; + } } - colInfo.close(); // close the resultset to release resources + } + if (_is_queried[col] != true) { + // make sure we do not try to query it again next time as it is not queryable + _is_fetched[col] = true; } } } } - } - - /* private simplified copy of MonetDatabaseMetaData.getColumns() method to fetch only 4 needed attributes of a specific column */ - private final ResultSet getColumnInfo(final String schemaName, final String tableName, final String columnName) throws SQLException - { - final StringBuilder query = new StringBuilder(700); - query.append("SELECT " + - "c.\"type_digits\" AS \"COLUMN_SIZE\", " + - "c.\"type_scale\" AS \"DECIMAL_DIGITS\", " + - "cast(CASE c.\"null\" WHEN true THEN ").append(ResultSetMetaData.columnNullable) - .append(" WHEN false THEN ").append(ResultSetMetaData.columnNoNulls) - .append(" ELSE ").append(columnNullableUnknown) - .append(" END AS int) AS \"NULLABLE\", ").append( - "cast(CASE WHEN c.\"default\" IS NOT NULL AND c.\"default\" LIKE 'next value for %' THEN true ELSE false END AS boolean) AS \"IS_AUTOINCREMENT\" " + - // ", s.\"name\" AS \"TABLE_SCHEM\", t.\"name\" AS \"TABLE_NAME\", c.\"name\" AS \"COLUMN_NAME\" " + - "FROM \"sys\".\"columns\" c " + - "JOIN \"sys\".\"tables\" t ON c.\"table_id\" = t.\"id\" " + - "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" "); - - query.append("WHERE s.\"name\" = ").append(MonetWrapper.sq(schemaName)); - query.append(" AND t.\"name\" = ").append(MonetWrapper.sq(tableName)); - query.append(" AND c.\"name\" = ").append(MonetWrapper.sq(columnName)); - // query.append(" ORDER BY \"TABLE_SCHEM\", \"TABLE_NAME\""); - - ResultSet rs = null; + + if (queriedcolcount == 0) + return; + + // execute query to get information on queriedcolcount (or less) columns. final Statement stmt = conn.createStatement(); if (stmt != null) { // for debug: System.out.println("SQL (len " + query.length() + "): " + query.toString()); - rs = stmt.executeQuery(query.toString()); + final ResultSet rs = stmt.executeQuery(query.toString()); if (rs != null) { - /* we want the statement object to be closed also when the resultset is closed by the caller */ - stmt.closeOnCompletion(); - } else { - /* failed to produce a resultset, so release resources for created statement object now */ - stmt.close(); + String rsSchema = null; + String rsTable = null; + String rsColumn = null; + while (rs.next()) { + rsSchema = rs.getString(1); // col 1 is schnm + rsTable = rs.getString(2); // col 2 is tblnm + rsColumn = rs.getString(3); // col 3 is colnm + // find the matching schema.table.column entry in the array + for (int col = 1; col < array_size; col++) { + if (_is_fetched[col] != true && _is_queried[col]) { + colName = getColumnName(col); + if (colName != null && colName.equals(rsColumn)) { + tblName = getTableName(col); + if (tblName != null && tblName.equals(rsTable)) { + schName = getSchemaName(col); + if (schName != null && schName.equals(rsSchema)) { + // found matching entry + // for debug: System.out.println("Found match at [" + col + "] for " + schName + "." + tblName + "." + colName); + _precision[col] = rs.getInt(4); // col 4 is "type_digits" (or "COLUMN_SIZE") + _scale[col] = rs.getInt(5); // col 5 is "type_scale" (or "DECIMAL_DIGITS") + _isNullable[col] = rs.getInt(6); // col 6 is nullable (or "NULLABLE") + _isAutoincrement[col] = rs.getBoolean(7); // col 7 is isautoincrement (or "IS_AUTOINCREMENT") + _is_fetched[col] = true; + queriedcolcount--; + // we found the match, exit the for-loop + col = array_size; + } + } + } + } + } + } + rs.close(); + } + stmt.close(); + } + + if (queriedcolcount != 0) { + // not all queried columns have resulted in a returned data row. + // make sure we do not match those columns again next run + for (int col = column; col < array_size; col++) { + if (_is_fetched[col] != true && _is_queried[col]) { + _is_fetched[col] = true; + // for debug: System.out.println("Found NO match at [" + col + "] for " + getSchemaName(col) + "." + getTableName(col) + "." + getColumnName(col)); + } } } - return rs; } /** @@ -2735,19 +2821,18 @@ public class MonetResultSet } if (pdate == null) { // parsing failed - final String errMsg; + final StringBuilder errMsg = new StringBuilder(128); final int epos = ppos.getErrorIndex(); if (epos == -1) { - errMsg = "parsing '" + monetDateStr + "' failed"; + errMsg.append("parsing '").append(monetDateStr).append("' failed"); } else if (epos < monetDate.length()) { - errMsg = "parsing failed," + - " found: '" + monetDate.charAt(epos) + "'" + - " in: \"" + monetDateStr + "\"" + - " at pos: " + (epos + (negativeYear ? 2 : 1)); + errMsg.append("parsing failed at pos ").append(epos + (negativeYear ? 2 : 1)) + .append(" found: '").append(monetDate.charAt(epos)) + .append("' in '").append(monetDateStr).append("'"); } else { - errMsg = "parsing failed, expected more data after '" + monetDateStr + "'"; + errMsg.append("parsing failed, expected more data after '").append(monetDateStr).append("'"); } - throw new SQLException(errMsg, "01M10"); + throw new SQLException(errMsg.toString(), "01M10"); } cal.setTime(pdate); @@ -2788,10 +2873,11 @@ public class MonetResultSet while (ctr++ < 9) nanos *= 10; } catch (MCLParseException e) { + final int offset = e.getErrorOffset(); addWarning(e.getMessage() + - " found: '" + monDate[e.getErrorOffset()] + "'" + - " in: \"" + monetDate + "\"" + - " at pos: " + e.getErrorOffset(), "01M10"); + " found: '" + monDate[offset] + + "' in: \"" + monetDate + + "\" at pos: " + offset, "01M10"); // default value nanos = 0; } @@ -2874,8 +2960,9 @@ public class MonetResultSet } cal = Calendar.getInstance(); } - final int ret = getJavaDate(cal, columnIndex, Types.DATE); - return ret == -1 ? null : new java.sql.Date(cal.getTimeInMillis()); + if (getJavaDate(cal, columnIndex, Types.DATE) == -1) + return null; + return new java.sql.Date(cal.getTimeInMillis()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } @@ -2963,8 +3050,9 @@ public class MonetResultSet } cal = Calendar.getInstance(); } - final int ret = getJavaDate(cal, columnIndex, Types.TIME); - return ret == -1 ? null : new Time(cal.getTimeInMillis()); + if (getJavaDate(cal, columnIndex, Types.TIME) == -1) + return null; + return new Time(cal.getTimeInMillis()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); }
--- a/src/main/java/org/monetdb/jdbc/MonetStatement.java +++ b/src/main/java/org/monetdb/jdbc/MonetStatement.java @@ -206,7 +206,10 @@ public class MonetStatement // copy contents of long[] into new int[] final int[] counts = new int[ret.length]; for (int i = 0; i < ret.length; i++) { - counts[i] = (ret[i] >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)ret[i]; + if (ret[i] >= Integer.MAX_VALUE) + counts[i] = Integer.MAX_VALUE; + else + counts[i] = (int)ret[i]; } return counts; } @@ -776,10 +779,9 @@ public class MonetStatement */ @Override public ResultSet getResultSet() throws SQLException { - return (header != null && header instanceof MonetConnection.ResultSetResponse) - ? new MonetResultSet(this, - (MonetConnection.ResultSetResponse)header) - : null; + if (header != null && header instanceof MonetConnection.ResultSetResponse) + return new MonetResultSet(this, (MonetConnection.ResultSetResponse)header); + return null; } /** @@ -1111,17 +1113,16 @@ public class MonetStatement * @throws SQLException if a database access error occurs or this * method is called on a closed Statement */ + @Override public long getLargeUpdateCount() throws SQLException { - long ret = -1; if (header != null) { if (header instanceof MonetConnection.UpdateResponse) { - ret = ((MonetConnection.UpdateResponse)header).count; + return ((MonetConnection.UpdateResponse)header).count; } else if (header instanceof MonetConnection.SchemaResponse) { - ret = ((MonetConnection.SchemaResponse)header).state; + return ((MonetConnection.SchemaResponse)header).state; } } - - return ret; + return -1; } /** @@ -1570,9 +1571,7 @@ final class MonetVirtualResultSet extend */ @Override public void close() { - if (!closed) { - closed = true; - // types and columns are MonetResultSets private parts - } + closed = true; + // types and columns are MonetResultSets private parts } }
--- a/src/main/java/org/monetdb/util/CmdLineOpts.java +++ b/src/main/java/org/monetdb/util/CmdLineOpts.java @@ -39,7 +39,7 @@ public final class CmdLineOpts { final String defaulta, final String descriptiona) throws OptionsException { - OptionContainer oc = + final OptionContainer oc = new OptionContainer( shorta, longa, @@ -54,7 +54,7 @@ public final class CmdLineOpts { } public void removeOption(final String name) { - OptionContainer oc = opts.get(name); + final OptionContainer oc = opts.get(name); if (oc != null) { opts.remove(oc.shorta); opts.remove(oc.longa); @@ -62,7 +62,7 @@ public final class CmdLineOpts { } public OptionContainer getOption(final String key) throws OptionsException { - OptionContainer ret = opts.get(key); + final OptionContainer ret = opts.get(key); if (ret == null) throw new OptionsException("No such option: " + key); @@ -80,9 +80,11 @@ public final class CmdLineOpts { in.close(); } + String key; + OptionContainer option = null; for (java.util.Enumeration<?> e = prop.propertyNames(); e.hasMoreElements(); ) { - String key = (String) e.nextElement(); - OptionContainer option = opts.get(key); + key = (String) e.nextElement(); + option = opts.get(key); if (option == null) throw new OptionsException("Unknown option: " + key); option.resetArguments(); @@ -346,9 +348,8 @@ public final class CmdLineOpts { public void addArgument(final String val) throws OptionsException { if (cardinality == CAR_ZERO) { throw new OptionsException("option " + name + " does not allow arguments"); - } else if ((cardinality == CAR_ONE || - cardinality == CAR_ZERO_ONE) && - values.size() >= 1) { + } + if ((cardinality == CAR_ONE || cardinality == CAR_ZERO_ONE) && values.size() >= 1) { throw new OptionsException("option " + name + " does at max allow only one argument"); } // we can add it
--- a/src/main/java/org/monetdb/util/MDBvalidator.java +++ b/src/main/java/org/monetdb/util/MDBvalidator.java @@ -1123,11 +1123,10 @@ public final class MDBvalidator { {"querylog_history", "id", "id", "querylog_catalog", null}, {"querylog_history", "owner", "name", "users", null}, {"querylog_history", "pipe", "name", "optimizers", null}, - {"queue WHERE tag > cast(0 as oid) AND ", "tag", "tag", "queue", null}, - {"queue WHERE tag > cast(0 as oid) AND ", "tag", "cast(tag as oid)", "queue", null}, - {"queue", "tag", "cast(tag as oid)", "queue", null}, +// not a fk: {"queue", "sessionid", "sessionid", "sessions", "37"}, // as queue contains a historical list, the session may have been closed in the meantime, so not a real persistent fk // not a fk: {"queue", "\"username\"", "name", "users", null}, // as queue contains a historical list, the user may have been removed in the meantime, so not a real persistent fk {"sessions", "\"username\"", "name", "users", null}, + {"sessions", "optimizer", "name", "optimizers", "37"}, {"statistics", "column_id", "id", "(SELECT id FROM sys._columns UNION ALL SELECT id FROM tmp._columns) as c", null}, {"statistics", "type", "sqlname", "types", null}, {"storage()", "schema", "name", "schemas", null},
--- a/tests/JDBC_API_Tester.java +++ b/tests/JDBC_API_Tester.java @@ -32,7 +32,7 @@ import org.monetdb.jdbc.types.URL; * to only one time instead of 40+ times. * Also all output is no longer send to system out/err but collected in a StringBuilder. * The contents of it is compared with the expected output at the end of each test. - * Only when it deviates the output is sent to system out, see compareExpectedOutput(). + * Only when it deviates the output is sent to system err, see compareExpectedOutput(). * * @author Martin van Dinther * @version 0.2 @@ -41,6 +41,7 @@ final public class JDBC_API_Tester { StringBuilder sb; // buffer to collect the test output final static int sbInitLen = 3712; Connection con; // main connection shared by all tests + boolean foundDifferences = false; public static void main(String[] args) throws Exception { String con_URL = args[0]; @@ -96,8 +97,13 @@ final public class JDBC_API_Tester { jt.BugResultSetMetaData_Bug_6183(); jt.BugSetQueryTimeout_Bug_3357(); jt.SQLcopyinto(); + /* run next long running test (11 minutes) only before a new release */ + /* jt.Test_PSlargeamount(); */ jt.closeConx(jt.con); + + if (jt.foundDifferences) + System.exit(-1); } private void Test_Cautocommit(String arg0) { @@ -823,10 +829,13 @@ final public class JDBC_API_Tester { stmt = con.createStatement(); // query sys.types to find out if sql datatype hugeint is supported rs = stmt.executeQuery("SELECT sqlname from sys.types where sqlname = 'hugeint';"); - if (rs != null && rs.next()) { - String sqlname = rs.getString(1); - if ("hugeint".equals(sqlname)) - supportsHugeInt = true; + if (rs != null) { + if (rs.next()) { + if ("hugeint".equals(rs.getString(1))) + supportsHugeInt = true; + } + rs.close(); + rs = null; } } catch (SQLException e) { sb.append("FAILED: ").append(e.getMessage()).append("\n"); @@ -1525,6 +1534,90 @@ final public class JDBC_API_Tester { "100, true\n"); } + /* Create a lot of PreparedStatements, to emulate webloads such as those from Hibernate. */ + /* this test is same as Test_PSsomeamount() but for many more PreparedStatements to stress the server */ + private void Test_PSlargeamount() { + sb.setLength(0); // clear the output log buffer + + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + // >> true: auto commit should be on + sb.append("0. true\t").append(con.getAutoCommit()).append("\n"); + + sb.append("1. Preparing and executing a unique statement\n"); + for (int i = 0; i < 50001; i++) { + pstmt = con.prepareStatement("select " + i + ", " + i + " = ?"); + pstmt.setInt(1, i); + rs = pstmt.executeQuery(); + if (rs.next() && i % 1000 == 0) { + sb.append(rs.getInt(1)).append(", ").append(rs.getBoolean(2)).append("\n"); + } + /* next call should cause resources on the server to be freed */ + pstmt.close(); + } + } catch (SQLException e) { + sb.append("FAILED: ").append(e.getMessage()).append("\n"); + } + + closeStmtResSet(pstmt, rs); + + compareExpectedOutput("Test_PSlargeamount", + "0. true true\n" + + "1. Preparing and executing a unique statement\n" + + "0, true\n" + + "1000, true\n" + + "2000, true\n" + + "3000, true\n" + + "4000, true\n" + + "5000, true\n" + + "6000, true\n" + + "7000, true\n" + + "8000, true\n" + + "9000, true\n" + + "10000, true\n" + + "11000, true\n" + + "12000, true\n" + + "13000, true\n" + + "14000, true\n" + + "15000, true\n" + + "16000, true\n" + + "17000, true\n" + + "18000, true\n" + + "19000, true\n" + + "20000, true\n" + + "21000, true\n" + + "22000, true\n" + + "23000, true\n" + + "24000, true\n" + + "25000, true\n" + + "26000, true\n" + + "27000, true\n" + + "28000, true\n" + + "29000, true\n" + + "30000, true\n" + + "31000, true\n" + + "32000, true\n" + + "33000, true\n" + + "34000, true\n" + + "35000, true\n" + + "36000, true\n" + + "37000, true\n" + + "38000, true\n" + + "39000, true\n" + + "40000, true\n" + + "41000, true\n" + + "42000, true\n" + + "43000, true\n" + + "44000, true\n" + + "45000, true\n" + + "46000, true\n" + + "47000, true\n" + + "48000, true\n" + + "49000, true\n" + + "50000, true\n"); + } + private void Test_PSsqldata() { sb.setLength(0); // clear the output log buffer @@ -1811,36 +1904,37 @@ final public class JDBC_API_Tester { // The zoneless fields will show differences since the time // is inserted translated to the given timezones, and // retrieved as in they were given in those timezones. - // When the insert zone matches the retrieve zone, Java should + // When the insert zone matches the retrieve zone, Java should // eventually see 1st Jan 1970. while (rs.next()) { - sb.append("retrieved row (String):\n").append( - rs.getString("ts")).append(" | ").append( - rs.getString("tsz")).append(" | ").append( - rs.getString("t")).append(" | ").append( - rs.getString("tz")).append("\n"); + sb.append("retrieved row (String):\n") + .append(rs.getString("ts")).append(" | ") + // .append(rs.getString("tsz")).append(" | ") -- this values changes when summer or wintertime changes so no stable output + .append(rs.getString("t")).append(" | ") + // .append(rs.getString("tz")) -- this values changes when summer or wintertime changes so no stable output + .append("\n"); tsz.setTimeZone(TimeZone.getDefault()); tz.setTimeZone(tsz.getTimeZone()); - sb.append("default (").append(tsz.getTimeZone().getID()).append("):\n").append( - tsz.format(rs.getTimestamp("ts"))).append(" | ").append( - tsz.format(rs.getTimestamp("tsz"))).append(" | ").append( - tz.format(rs.getTime("t"))).append(" | ").append( - tz.format(rs.getTime("tz"))).append("\n"); + sb.append("default (").append(tsz.getTimeZone().getID()).append("):\n") + .append(tsz.format(rs.getTimestamp("ts"))).append(" | ") + .append(tsz.format(rs.getTimestamp("tsz"))).append(" | ") + .append(tz.format(rs.getTime("t"))).append(" | ") + .append(tz.format(rs.getTime("tz"))).append("\n"); c.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); - sb.append(c.getTimeZone().getID()).append(":\n").append( - rs.getTimestamp("ts", c)).append(" | ").append( - rs.getTimestamp("tsz", c)).append(" | ").append( - rs.getTime("t", c)).append(" | ").append( - rs.getTime("tz", c)).append("\n"); + sb.append(c.getTimeZone().getID()).append(":\n") + .append(rs.getTimestamp("ts", c)).append(" | ") + .append(rs.getTimestamp("tsz", c)).append(" | ") + .append(rs.getTime("t", c)).append(" | ") + .append(rs.getTime("tz", c)).append("\n"); c.setTimeZone(TimeZone.getTimeZone("Africa/Windhoek")); - sb.append(c.getTimeZone().getID()).append(":\n").append( - rs.getTimestamp("ts", c)).append(" | ").append( - rs.getTimestamp("tsz", c)).append(" | ").append( - rs.getTime("t", c)).append(" | ").append( - rs.getTime("tz", c)).append("\n"); + sb.append(c.getTimeZone().getID()).append(":\n") + .append(rs.getTimestamp("ts", c)).append(" | ") + .append(rs.getTimestamp("tsz", c)).append(" | ") + .append(rs.getTime("t", c)).append(" | ") + .append(rs.getTime("tz", c)).append("\n"); SQLWarning w = rs.getWarnings(); while (w != null) { @@ -1872,7 +1966,8 @@ final public class JDBC_API_Tester { "3. closing PreparedStatement... passed\n" + "4. selecting records... passed\n" + "retrieved row (String):\n" + - "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + +// old output "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + + "1970-01-01 00:00:00.000000 | 00:00:00 | \n" + "default (UTC):\n" + "1970-01-01 00:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 00:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + @@ -1880,7 +1975,8 @@ final public class JDBC_API_Tester { "Africa/Windhoek:\n" + "1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" + "retrieved row (String):\n" + - "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + +// old output "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + + "1970-01-01 00:00:00.000000 | 00:00:00 | \n" + "default (UTC):\n" + "1970-01-01 00:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 00:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + @@ -1888,7 +1984,8 @@ final public class JDBC_API_Tester { "Africa/Windhoek:\n" + "1969-12-31 22:00:00.0 | 1970-01-01 00:00:00.0 | 22:00:00 | 00:00:00\n" + "retrieved row (String):\n" + - "1969-12-31 16:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 16:00:00 | 01:00:00+01:00\n" + +// old output "1969-12-31 16:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 16:00:00 | 01:00:00+01:00\n" + + "1969-12-31 16:00:00.000000 | 16:00:00 | \n" + "default (UTC):\n" + "1969-12-31 16:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 16:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + @@ -1896,7 +1993,8 @@ final public class JDBC_API_Tester { "Africa/Windhoek:\n" + "1969-12-31 14:00:00.0 | 1970-01-01 00:00:00.0 | 14:00:00 | 00:00:00\n" + "retrieved row (String):\n" + - "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + +// old output "1970-01-01 00:00:00.000000 | 1970-01-01 01:00:00.000000+01:00 | 00:00:00 | 01:00:00+01:00\n" + + "1970-01-01 00:00:00.000000 | 00:00:00 | \n" + "default (UTC):\n" + "1970-01-01 00:00:00.000+0000 | 1970-01-01 00:00:00.000+0000 | 00:00:00.000+0000 | 00:00:00.000+0000\n" + "America/Los_Angeles:\n" + @@ -2754,13 +2852,13 @@ final public class JDBC_API_Tester { "3. d 2004-04-24 to tm: 00:00:00\n" + "3. d 2004-04-24 to dt: 2004-04-24\n" + "4. vc 2004-04-24 11:43:53.654321 to ts: 2004-04-24 11:43:53.654321\n" + - "4. vc 2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed, found: '-' in: \"2004-04-24 11:43:53.654321\" at pos: 5\n" + + "4. vc 2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 5 found: '-' in '2004-04-24 11:43:53.654321'\n" + "4. vc 2004-04-24 11:43:53.654321 to dt: 2004-04-24\n" + - "5. vc 11:43:53 to ts: rs.getTimestamp(colnm) failed with error: parsing failed, found: ':' in: \"11:43:53\" at pos: 3\n" + + "5. vc 11:43:53 to ts: rs.getTimestamp(colnm) failed with error: parsing failed at pos 3 found: ':' in '11:43:53'\n" + "5. vc 11:43:53 to tm: 11:43:53\n" + - "5. vc 11:43:53 to dt: rs.getDate(colnm) failed with error: parsing failed, found: ':' in: \"11:43:53\" at pos: 3\n" + + "5. vc 11:43:53 to dt: rs.getDate(colnm) failed with error: parsing failed at pos 3 found: ':' in '11:43:53'\n" + "6. vc 2004-04-24 to ts: 2004-04-24 00:00:00.0\n" + - "6. vc 2004-04-24 to tm: rs.getTime(colnm) failed with error: parsing failed, found: '-' in: \"2004-04-24\" at pos: 5\n" + + "6. vc 2004-04-24 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 5 found: '-' in '2004-04-24'\n" + "6. vc 2004-04-24 to dt: 2004-04-24\n" + "11. ts 904-04-24 11:43:53.567000 to ts: 0904-04-24 11:43:53.567\n" + "11. ts 904-04-24 11:43:53.567000 to tm: 11:43:53\n" + @@ -2781,13 +2879,13 @@ final public class JDBC_API_Tester { "16. d 4-04-24 to tm: 00:00:00\n" + "16. d 4-04-24 to dt: 0004-04-24\n" + "17. vc 904-04-24 11:43:53.567 to ts: 0904-04-24 11:43:53.567\n" + - "17. vc 904-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed, found: '-' in: \"904-04-24 11:43:53.567\" at pos: 4\n" + + "17. vc 904-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 4 found: '-' in '904-04-24 11:43:53.567'\n" + "17. vc 904-04-24 11:43:53.567 to dt: 0904-04-24\n" + "18. vc 74-04-24 11:43:53.567 to ts: 0074-04-24 11:43:53.567\n" + - "18. vc 74-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed, found: '-' in: \"74-04-24 11:43:53.567\" at pos: 3\n" + + "18. vc 74-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 3 found: '-' in '74-04-24 11:43:53.567'\n" + "18. vc 74-04-24 11:43:53.567 to dt: 0074-04-24\n" + "19. vc 4-04-24 11:43:53.567 to ts: 0004-04-24 11:43:53.567\n" + - "19. vc 4-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed, found: '-' in: \"4-04-24 11:43:53.567\" at pos: 2\n" + + "19. vc 4-04-24 11:43:53.567 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 2 found: '-' in '4-04-24 11:43:53.567'\n" + "19. vc 4-04-24 11:43:53.567 to dt: 0004-04-24\n" + "21. ts -4-04-24 11:43:53.567000 to ts: 0004-04-24 11:43:53.567\n" + "21. ts -4-04-24 11:43:53.567000 to tm: 11:43:53\n" + @@ -2802,10 +2900,10 @@ final public class JDBC_API_Tester { "24. d -3004-04-24 to tm: 00:00:00\n" + "24. d -3004-04-24 to dt: 3004-04-24\n" + "25. vc -2004-04-24 11:43:53.654321 to ts: 2004-04-24 11:43:53.654321\n" + - "25. vc -2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed, found: '-' in: \"-2004-04-24 11:43:53.654321\" at pos: 6\n" + + "25. vc -2004-04-24 11:43:53.654321 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 6 found: '-' in '-2004-04-24 11:43:53.654321'\n" + "25. vc -2004-04-24 11:43:53.654321 to dt: 2004-04-24\n" + "26. vc -3004-04-24 to ts: 3004-04-24 00:00:00.0\n" + - "26. vc -3004-04-24 to tm: rs.getTime(colnm) failed with error: parsing failed, found: '-' in: \"-3004-04-24\" at pos: 6\n" + + "26. vc -3004-04-24 to tm: rs.getTime(colnm) failed with error: parsing failed at pos 6 found: '-' in '-3004-04-24'\n" + "26. vc -3004-04-24 to dt: 3004-04-24\n" + "0. true true\n"); } @@ -4028,7 +4126,7 @@ final public class JDBC_API_Tester { "Script size is 83256\n" + "First test repeat 10 times. Iteration: 1 2 3 4 5 6 7 8 9 10 \n" + "Completed first test\n" + - "Second test repeat 10 times. Iteration: 1 2 3 4 5 6 7 8 9 10 \n" + + "Second test repeat 10 times. Iteration: 1 2 3 4 5 6 7 8 9 10 \n" + "Completed second test\n" + "Script size is 3012\n" + "Third test repeat 9 times.\n" + @@ -5043,19 +5141,20 @@ final public class JDBC_API_Tester { private void compareExpectedOutput(String testname, String expected) { if (!expected.equals(sb.toString())) { - System.out.print("Test '"); - System.out.print(testname); + foundDifferences = true; + System.err.print("Test '"); + System.err.print(testname); if (!testname.endsWith(")")) - System.out.print("()"); - System.out.println("' produced different output!"); - System.out.println("Expected:"); - System.out.println(expected); - System.out.println("Gotten:"); - System.out.println(sb); - System.out.println(); + System.err.print("()"); + System.err.println("' produced different output!"); + System.err.println("Expected:"); + System.err.println(expected); + System.err.println("Gotten:"); + System.err.println(sb); + System.err.println(); } if (sb.length() > sbInitLen) { - System.out.println("Test '" + testname + System.err.println("Test '" + testname + "' produced output > " + sbInitLen + " chars! Enlarge sbInitLen to: " + sb.length()); }
--- a/tests/SQLcopyinto.java +++ b/tests/SQLcopyinto.java @@ -28,7 +28,7 @@ public class SQLcopyinto { System.out.println("SQLcopyinto started"); if (args.length == 0) { System.err.println("Error: missing startup argument: the jdbc connection url !"); - System.err.println("Usage: java -cp monetdb-jdbc-2.29.jre7.jar:. SQLcopyinto \"jdbc:monetdb://localhost:50000/demo?user=monetdb&password=monetdb\""); + System.err.println("Usage: java -cp monetdb-jdbc-3.0.jre8.jar:. SQLcopyinto \"jdbc:monetdb://localhost:50000/demo?user=monetdb&password=monetdb\""); System.exit(-1); } String jdbc_url = args[0]; @@ -118,14 +118,13 @@ public class SQLcopyinto { mclOut.write("" + i + ",val_" + i); mclOut.newLine(); } + mclOut.writeLine(""); // need this one for synchronisation over flush() - error = mclIn.waitForPrompt(); if (error != null) System.err.println("Received error: " + error); mclOut.writeLine(""); // need this one for synchronisation over flush() - error = mclIn.waitForPrompt(); if (error != null) System.err.println("Received finish error: " + error); @@ -134,7 +133,7 @@ public class SQLcopyinto { } catch (Exception e) { System.err.println("Mapi Exception: " + e.getMessage()); } finally { - // close connection to MonetDB server + // close MapiSocket connection to MonetDB server server.close(); }
deleted file mode 100644 --- a/tests/Test_PSlargeamount.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 - 2021 MonetDB B.V. - */ - -import java.sql.*; - -/* Create a lot of PreparedStatements, to emulate webloads such as those - * from Hibernate. */ - -public class Test_PSlargeamount { - public static void main(String[] args) throws Exception { - Connection con = DriverManager.getConnection(args[0]); - Statement stmt = con.createStatement(); - PreparedStatement pstmt; - - // >> true: auto commit should be on - System.out.println("0. true\t" + con.getAutoCommit()); - - try { - System.out.println("1. Preparing and executing a unique statement"); - for (int i = 0; i < 50000; i++) { - pstmt = con.prepareStatement("select " + i + ", " + i + " = ?"); - pstmt.setInt(1, i); - ResultSet rs = pstmt.executeQuery(); - if (rs.next() && i % 1000 == 0) { - System.out.println(rs.getInt(1) + ", " + rs.getBoolean(2)); - } - /* this call should cause resources on the server to be - * freed */ - pstmt.close(); - } - } catch (SQLException e) { - System.out.println("FAILED :( "+ e.getMessage()); - System.out.println("ABORTING TEST!!!"); - } - - stmt.close(); - con.close(); - } -}