changeset 255:96057ee68017 embedded

Merge with default.
author Pedro Ferreira <pedro.ferreira@monetdbsolutions.com>
date Fri, 09 Nov 2018 12:18:51 +0100 (2018-11-09)
parents 14e6f812b43c (current diff) 27b7166860c8 (diff)
children 4face9f42efc
files example/SQLcopyinto.java src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.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/MonetStatement.java tests/SQLcopyinto.java tests/build.xml
diffstat 9 files changed, 242 insertions(+), 213 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,39 +1,12 @@
 # ChangeLog file for monetdb-java
 # This file is updated with Maddlog
 
-* Wed May 23 2018 Sjoerd Mullender <sjoerd@acm.org>
-- Compiled and released new jars: monetdb-jdbc-2.28.jar, monetdb-mcl-1.17.jar
-  and updated jdbcclient.jar
-
-* Thu Apr 26 2018 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
-- Corrected and extended output of DatabaseMetaData methods
-  getTimeDateFunctions() and getSystemFunctions().  The Date/Time functions
-  (curdate, current_date, current_time, current_timestamp, curtime,
-  local_timezone, localtime, localtimestamp) were returned by
-  getSystemFunctions() but are now returned by getTimeDateFunctions().
-  getTimeDateFunctions() now also lists functions: date_to_str, extract, now,
-  str_to_date, str_to_time, str_to_timestamp, time_to_str and timestamp_to_str.
-- Improved DatabaseMetaData methods getTablePrivileges() and
-  getColumnPrivileges() by returning also any combination of privileges
-  for the table or column in the PRIVILEGE result column. Previously only
-  single privileges (SELECT or UPDATE or INSERT or DELETE or EXECUTE or
-  GRANT) would be returned.
+* Thu Sep 20 2018 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
+- Improved example program SQLcopyinto.java and moved it to tests directory
+  for automatic testing.
 
-* Thu Apr 19 2018 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
-- Corrected method DatabaseMetaData.getFunctions() for result column
-  FUNCTION_TYPE.  It used to return DatabaseMetaData.functionResultUnknown
-  value for Analytic (functions.type 6) and Loader function (functions type 7).
-  It now returns DatabaseMetaData.functionNoTable for Analytic functions and
-  DatabaseMetaData.functionReturnsTable for Loader functions.
-- DatabaseMetaData methods getTables(), getColumns(), getProcedures() and
-  getFunctions() now return the comment in the REMARKS result column when a
-  comment has been set for the table / view / column / procedure / function
-  via the SQL command COMMENT ON <db-object type> <qname> IS 'comment-text'.
+* Thu Jun 28 2018 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
+- Corrected return values of DatabaseMetaData methods
+  allTablesAreSelectable() and allProceduresAreCallable().
+  They used to return true but now return false.
 
-* Thu Dec 14 2017 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
-- Fixed a problem with retrieving Dates and Timestamps which contained a
-  year value less than 1000. It would throw an SQLDataException with message:
-   Could not convert value to a Date. Expected JDBC date escape format
-   yyyy-[m]m-[d]d.
-  See also: https://www.monetdb.org/bugzilla/show_bug.cgi?id=6468
-
deleted file mode 100644
--- a/example/SQLcopyinto.java
+++ /dev/null
@@ -1,103 +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 - 2018 MonetDB B.V.
- */
-
-import java.sql.*;
-import java.io.*;
-import java.util.*;
-
-import nl.cwi.monetdb.mcl.connection.mapi.MapiConnection;
-import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
-
-/**
- * This example demonstrates how the MonetDB JDBC driver can facilitate
- * in performing COPY INTO sequences.  This is mainly meant to show how
- * a quick load can be performed from Java.
- *
- * @author Fabian Groffen
- */
-
-public class SQLcopyinto {
-	public static void main(String[] args) throws Exception {
-		// request a connection suitable for Monet from the driver manager
-		// note that the database specifier is currently not implemented, for
-		// Monet itself can't access multiple databases.
-		// turn on debugging
-		Connection con = DriverManager.getConnection("jdbc:monetdb://localhost/database", "monetdb", "monetdb");
-
-		// get a statement to execute on
-		Statement stmt = con.createStatement();
-
-		String query = "CREATE TABLE example (id int, val varchar(24))";
-		try {
-			stmt.execute(query);
-		} catch (SQLException e) {
-			System.out.println(query + ": " + e.getMessage());
-			System.exit(1);
-		}
-
-		// now create a connection manually to perform a load, this can
-		// of course also be done simultaneously with the JDBC
-		// connection being kept connected
-
-		MapiConnection server = new MapiConnection(null, null, "sql", false, true,"localhost", 50000, "database");
-
-		try {
-			List warning = server.connect("monetdb", "monetdb");
-			if (warning != null) {
-				for (Object aWarning : warning) {
-					System.out.println(aWarning.toString());
-				}
-			}
-			AbstractProtocol oldmMapiProtocol = server.getProtocol();
-
-			oldmMapiProtocol.waitUntilPrompt();
-			String error = oldmMapiProtocol.getRemainingStringLine(0);
-			if (error != null)
-				throw new Exception(error);
-
-			query = "COPY INTO example FROM STDIN USING DELIMITERS ',','\\n';";
-			// the leading 's' is essential, since it is a protocol
-			// marker that should not be omitted, likewise the
-			// trailing semicolon
-			oldmMapiProtocol.writeNextQuery("s", query, "\n");
-
-			for (int i = 0; i < 100; i++) {
-				oldmMapiProtocol.writeNextQuery(null, "" + i + ",val_" + i, "\n");
-			}
-			oldmMapiProtocol.waitUntilPrompt();
-			error = oldmMapiProtocol.getRemainingStringLine(0);
-			if (error != null)
-				throw new Exception(error);
-			// disconnect from server
-			server.close();
-		} catch (IOException e) {
-			System.err.println("unable to connect: " + e.getMessage());
-			System.exit(-1);
-		} catch (Exception e) {
-			System.err.println(e.getMessage());
-			System.exit(-1);
-		}
-
-		query = "SELECT COUNT(*) FROM example";
-		ResultSet rs = null;
-		try {
-			rs = stmt.executeQuery(query);
-		} catch (SQLException e) {
-			System.out.println(query + ": " + e.getMessage());
-			System.exit(1);
-		}
-		if (rs != null && rs.next())
-			System.out.println(rs.getString(1));
-
-		// free resources, close the statement
-		stmt.close();
-		// close the connection with the database
-		con.close();
-
-	}
-}
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
@@ -178,6 +178,8 @@ public abstract class MonetConnection ex
 	private boolean queriedCommentsTable = false;
 	private boolean hasCommentsTable = false;
 
+	/** The last set query timeout on the server as used by Statement and PreparedStatement (and CallableStatement in future) */
+	protected int lastSetQueryTimeout = 0;	// 0 means no timeout, which is the default on the server
 
 	/**
 	 * Gets the initial value for the StringBuilder size.
@@ -1026,7 +1028,8 @@ public abstract class MonetConnection ex
 	 */
 	@Override
 	public String toString() {
-		return "MonetDB Connection (" + this.getJDBCURL() + ") " + (closed ? "disconnected" : "connected");
+		return "MonetDB Connection (" + getJDBCURL() + ") " +
+				(closed ? "disconnected" : "connected");
 	}
 
 	/**
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java
@@ -107,25 +107,27 @@ public class MonetDatabaseMetaData exten
 	}
 
 	/**
-	 * Can all the procedures returned by getProcedures be called
-	 * by the current user?
+	 * Retrieves whether the current user can call all the procedures
+	 * returned by the method getProcedures.
 	 *
-	 * @return true if so
+	 * @return false because we currently return all procedures from sys.functions
+	 *    and do not filter on EXECUTE privilege or procedure ownership.
 	 */
 	@Override
 	public boolean allProceduresAreCallable() {
-		return true;
+		return false;
 	}
 
 	/**
-	 * Can all the tables returned by getTable be SELECTed by
-	 * the current user?
+	 * Retrieves whether the current user can use all the tables
+	 * returned by the method getTables in a SELECT statement.
 	 *
-	 * @return true because we only have one user a.t.m.
+	 * @return false because we currently return all tables from sys.tables
+	 *    and do not filter on SELECT privilege or table ownership.
 	 */
 	@Override
 	public boolean allTablesAreSelectable() {
-		return true;
+		return false;
 	}
 
 	/**
@@ -1698,6 +1700,7 @@ public class MonetDatabaseMetaData exten
 	 *	"" retrieves those without a schema;
 	 *	null means that the schema name should not be used to narrow the search
 	 * @param procedureNamePattern - a procedure name pattern; must match the procedure name as it is stored in the database
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet - each row is a procedure description
 	 * @throws SQLException if a database access error occurs
 	 */
@@ -1807,7 +1810,9 @@ public class MonetDatabaseMetaData exten
 	 *	"" retrieves those without a schema;
 	 *	null means that the schema name should not be used to narrow the search
 	 * @param procedureNamePattern - a procedure name pattern; must match the procedure name as it is stored in the database
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param columnNamePattern - a column name pattern; must match the column name as it is stored in the database
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet - each row describes a stored procedure parameter or column
 	 * @throws SQLException if a database-access error occurs
 	 * @see #getSearchStringEscape
@@ -1877,7 +1882,7 @@ public class MonetDatabaseMetaData exten
 
 	/**
 	 * Returns a SQL match part string where depending on the input value we
-	 * compose an exact match (use =) or match with wildcards (use LIKE)
+	 * compose an exact match (use =) or match with wildcards (use LIKE) or IS NULL
 	 *
 	 * @param in the string to match
 	 * @return the SQL match part string
@@ -1941,6 +1946,7 @@ public class MonetDatabaseMetaData exten
 	 *	null means that the schema name should not be used to narrow the search
 	 * @param tableNamePattern - a table name pattern; must match the table name as it is stored in the database
 	 *	For all tables this should be "%"
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param types - a list of table types, which must be from the list of table types returned
 	 *	from getTableTypes(),to include; null returns all types
 	 * @return ResultSet - each row is a table description
@@ -2199,7 +2205,9 @@ public class MonetDatabaseMetaData exten
 	 *	null means that the schema name should not be used to narrow the search
 	 * @param tableNamePattern - a table name pattern; must match the table name as it is stored in the database
 	 *	For all tables this should be "%"
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param columnNamePattern - a column name pattern; must match the column name as it is stored in the database
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet - each row is a column description
 	 * @throws SQLException if a database error occurs
 	 * @see #getSearchStringEscape
@@ -2295,7 +2303,9 @@ public class MonetDatabaseMetaData exten
 	 * @param catalog a catalog name; "" retrieves those without a catalog
 	 * @param schemaPattern a schema name; "" retrieves those without a schema
 	 * @param tableNamePattern a table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param columnNamePattern a column name pattern
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet each row is a column privilege description
 	 * @see #getSearchStringEscape
 	 * @throws SQLException if a database error occurs
@@ -2394,6 +2404,7 @@ public class MonetDatabaseMetaData exten
 	 * @param catalog a catalog name; "" retrieves those without a catalog
 	 * @param schemaPattern a schema name pattern; "" retrieves those without a schema
 	 * @param tableNamePattern a table name pattern
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet each row is a table privilege description
 	 * @see #getSearchStringEscape
 	 * @throws SQLException if a database error occurs
@@ -2491,6 +2502,7 @@ public class MonetDatabaseMetaData exten
 	 * @param catalog a catalog name; "" retrieves those without a catalog
 	 * @param schema a schema name; "" retrieves those without a schema
 	 * @param table a table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param scope the scope of interest; use same values as SCOPE
 	 * @param nullable include columns that are nullable?
 	 * @return ResultSet each row is a column description
@@ -2577,6 +2589,7 @@ public class MonetDatabaseMetaData exten
 	 * @param catalog a catalog name; "" retrieves those without a catalog
 	 * @param schema a schema name; "" retrieves those without a schema
 	 * @param table a table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet each row is a column description
 	 * @throws SQLException if a database error occurs
 	 */
@@ -2617,9 +2630,9 @@ public class MonetDatabaseMetaData exten
 	 *	</OL>
 	 *
 	 * @param catalog a catalog name; "" retrieves those without a catalog
-	 * @param schema a schema name pattern; "" retrieves those
-	 * without a schema
+	 * @param schema a schema name pattern; "" retrieves those without a schema
 	 * @param table a table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet each row is a primary key column description
 	 * @throws SQLException if a database error occurs
 	 */
@@ -2754,6 +2767,7 @@ public class MonetDatabaseMetaData exten
 	 * @param catalog a catalog name; "" retrieves those without a catalog
 	 * @param schema a schema name pattern; "" retrieves those without a schema
 	 * @param table a table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet each row is a primary key column description
 	 * @see #getExportedKeys
 	 * @throws SQLException if a database error occurs
@@ -2838,6 +2852,7 @@ public class MonetDatabaseMetaData exten
 	 * @param catalog a catalog name; "" retrieves those without a catalog
 	 * @param schema a schema name pattern; "" retrieves those without a schema
 	 * @param table a table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet each row is a foreign key column description
 	 * @see #getImportedKeys
 	 * @throws SQLException if a database error occurs
@@ -2925,9 +2940,11 @@ public class MonetDatabaseMetaData exten
 	 * @param pcatalog primary key catalog name; "" retrieves those without a catalog
 	 * @param pschema primary key schema name pattern; "" retrieves those without a schema
 	 * @param ptable primary key table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param fcatalog foreign key catalog name; "" retrieves those without a catalog
 	 * @param fschema foreign key schema name pattern; "" retrieves those without a schema
 	 * @param ftable koreign key table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet each row is a foreign key column description
 	 * @throws SQLException if a database error occurs
 	 * @see #getImportedKeys
@@ -3102,6 +3119,7 @@ public class MonetDatabaseMetaData exten
 	 * @param catalog a catalog name; "" retrieves those without a catalog
 	 * @param schema a schema name pattern; "" retrieves those without a schema
 	 * @param table a table name
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param unique when true, return only indices for unique values;
 	 *	   when false, return indices regardless of whether unique or not
 	 * @param approximate when true, result is allowed to reflect approximate
@@ -3468,10 +3486,8 @@ public class MonetDatabaseMetaData exten
 	 *
 	 * @param catalog a catalog name; "" retrieves those without a catalog;
 	 *		  <code>null</code> means drop catalog name from the selection criteria
-	 * @param schemaPattern a schema name pattern; "" retrieves those
-	 *		  without a schema
-	 * @param typeNamePattern a UDT name pattern; may be a fully-qualified
-	 *		  name
+	 * @param schemaPattern a schema name pattern; "" retrieves those without a schema
+	 * @param typeNamePattern a UDT name pattern; may be a fully-qualified name
 	 * @return a <code>ResultSet</code> object in which a row gives information
 	 *		   about the designated UDT
 	 * @throws SQLException if a database access error occurs
@@ -3516,10 +3532,8 @@ public class MonetDatabaseMetaData exten
 	 *
 	 * @param catalog a catalog name; "" retrieves those without a catalog;
 	 *		  <code>null</code> means drop catalog name from the selection criteria
-	 * @param schemaPattern a schema name pattern; "" retrieves those
-	 *		  without a schema
-	 * @param tableNamePattern a table name pattern; may be a fully-qualified
-	 *		  name
+	 * @param schemaPattern a schema name pattern; "" retrieves those without a schema
+	 * @param tableNamePattern a table name pattern; may be a fully-qualified name
 	 * @return a <code>ResultSet</code> object in which each row is a type description
 	 * @throws SQLException if a database access error occurs
 	 */
@@ -3905,6 +3919,7 @@ public class MonetDatabaseMetaData exten
 	 *        not be used to narrow the search
 	 * @param functionNamePattern a function name pattern; must match
 	 *        the function name as it is stored in the database
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet - each row is a function description
 	 * @throws SQLException if a database access error occurs
 	 */
@@ -4008,8 +4023,10 @@ public class MonetDatabaseMetaData exten
 	 *        not be used to narrow the search
 	 * @param functionNamePattern a procedure name pattern; must match the
 	 *        function name as it is stored in the database
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @param columnNamePattern a parameter name pattern; must match the
 	 *        parameter or column name as it is stored in the database
+	 *   Note that our implementation allows this param to be null also (for efficiency as no extra LIKE "%" condition is added to be evaluated).
 	 * @return ResultSet - each row describes a user function parameter,
 	 *         column or return type
 	 * @throws SQLException - if a database access error occurs
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in
@@ -386,7 +386,7 @@ public final class MonetDriver implement
 		Properties props = new Properties();
 
 		if (!acceptsURL(url))
-			throw new SQLException("Invalid URL: it does not start with: " + MONETURL, "08M26");
+			return null;
 
 		if(!url.startsWith("jdbc:monetdb:embedded:")) {
 			// url should be of style jdbc:monetdb://<host>/<database>
@@ -404,12 +404,12 @@ public final class MonetDriver implement
 			try {
 				uri = new URI(url.substring(5));
 			} catch (URISyntaxException e) {
-				throw new SQLNonTransientConnectionException(e.toString(), "08M26");
+				return null;
 			}
 
 			String uri_host = uri.getHost();
 			if (uri_host == null)
-				throw new SQLNonTransientConnectionException("Invalid URL: no hostname given or unparsable in '" + url + "'", "08M26");
+				return null;
 			info.put("host", uri_host);
 
 			int uri_port = uri.getPort();
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
@@ -59,9 +59,11 @@ public class MonetStatement extends Mone
 	/** Whether this Statement object is closed or not */
 	protected boolean closed;
 	/** Whether the application wants this Statement object to be pooled */
-	boolean poolable;
+	protected boolean poolable;
 	/** Whether this Statement should be closed if the last ResultSet closes */
 	private boolean closeOnCompletion = false;
+	/** The timeout (in sec) for the query to return, 0 means no timeout */
+	private int queryTimeout = 0;
 	/** The size of the blocks of results to ask for at the server */
 	private int fetchSize = 0;
 	/** The maximum number of rows to return in a ResultSet, 0 indicates unlimited */
@@ -93,6 +95,7 @@ public class MonetStatement extends Mone
 		this.connection = connection;
 		this.resultSetType = resultSetType;
 		this.resultSetConcurrency = resultSetConcurrency;
+		this.queryTimeout = connection.lastSetQueryTimeout;
 
 		// check our limits, and generate warnings as appropriate
 		if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
@@ -216,7 +219,8 @@ public class MonetStatement extends Mone
 		try {
 			boolean type = internalExecute(batch);
 			int count = -1;
-			if (!type) count = getUpdateCount();
+			if (!type)
+				count = getUpdateCount();
 			do {
 				if (offset >= max)
 					throw new SQLException("Overflow: don't use multi statements when batching (" + max + ")", "M1M16");
@@ -451,6 +455,25 @@ public class MonetStatement extends Mone
 			lastResponseList = null;
 		}
 
+		if (queryTimeout != connection.lastSetQueryTimeout) {
+			// set requested/changed queryTimeout on the server side first
+			Statement st = null;
+			try {
+				st = connection.createStatement();
+				String callstmt = "CALL \"sys\".\"settimeout\"(" + queryTimeout + ")";
+				// for debug: System.out.println("Before: " + callstmt);
+				st.execute(callstmt);
+				// for debug: System.out.println("After : " + callstmt);
+				connection.lastSetQueryTimeout = queryTimeout;
+			}
+			/* do not catch SQLException here, as we want to know it when it fails */
+			finally {
+				if (st != null) {
+					 st.close();
+				}
+			}
+		}
+
 		// create a container for the result
 		lastResponseList = connection.new ResponseList(fetchSize, maxRows, resultSetType, resultSetConcurrency);
 		// fill the header list by processing the query
@@ -681,7 +704,7 @@ public class MonetStatement extends Mone
 	 * @see #setMaxFieldSize(int max)
 	 */
 	@Override
-	public int getMaxFieldSize() throws SQLException {
+	public int getMaxFieldSize() {
 		return 2*1024*1024*1024 - 2;	// MonetDB supports null terminated strings of max 2GB, see function: int UTF8_strlen()
 	}
 
@@ -696,7 +719,7 @@ public class MonetStatement extends Mone
 	 * @see #setMaxRows(int max)
 	 */
 	@Override
-	public int getMaxRows() throws SQLException {
+	public int getMaxRows() {
 		return maxRows;
 	}
 
@@ -762,27 +785,8 @@ public class MonetStatement extends Mone
 	 * @see #setQueryTimeout(int)
 	 */
 	@Override
-	public int getQueryTimeout() throws SQLException {
-		Statement st = null;
-		ResultSet rs = null;
-		try {
-			st = connection.createStatement();
-			rs = st.executeQuery("SELECT \"querytimeout\" FROM \"sys\".\"sessions\"() WHERE \"active\"");
-			if (rs != null && rs.next()) {
-				// MonetDB stores querytimeout in a bigint, so correctly deal with big int values
-				long timeout = rs.getLong(1);
-				return (timeout > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) timeout;
-			}
-		/* do not catch SQLException here, as we want to know it when it fails */
-		} finally {
-			if (rs != null) {
-				rs.close();
-			}
-			if (st != null) {
-				 st.close();
-			}
-		}
-		return 0;	// in case the query didn't return a result (which should normally never happen)
+	public int getQueryTimeout() {
+		return queryTimeout;
 	}
 
 	/**
@@ -815,7 +819,7 @@ public class MonetStatement extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public int getResultSetHoldability() throws SQLException {
+	public int getResultSetHoldability() {
 		return ResultSet.HOLD_CURSORS_OVER_COMMIT;
 	}
 
@@ -1027,20 +1031,9 @@ public class MonetStatement extends Mone
 	 */
 	@Override
 	public void setQueryTimeout(int seconds) throws SQLException {
-		if (seconds < 0) {
+		if (seconds < 0)
 			throw new SQLException("Illegal timeout value: " + seconds, "M1M05");
-		}
-		Statement st = null;
-		try {
-			st = connection.createStatement();
-			// CALL "sys"."settimeout"(int_value)
-			st.execute("CALL \"sys\".\"settimeout\"(" + seconds + ")");
-		/* do not catch SQLException here, as we want to know it when it fails */
-		} finally {
-			if (st != null) {
-				 st.close();
-			}
-		}
+		queryTimeout = seconds;
 	}
 
 	//== 1.6 methods (JDBC 4.0)
@@ -1202,14 +1195,19 @@ final class MonetVirtualResultSet extend
 			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
+		if (row < 0)
+			row = 0;	// before first
+		else if (row > tupleCount + 1)
+			row = tupleCount + 1;	// after last
 
 		// store it
 		this.curRow = row;
 
 		// see if we have the row
-		return !(row < 1 || row > tupleCount);
+		if (row < 1 || row > tupleCount)
+			return false;
+
+		return true;
 	}
 
 	@Override
--- a/tests/BugSetQueryTimeout_Bug_3357.java
+++ b/tests/BugSetQueryTimeout_Bug_3357.java
@@ -16,17 +16,12 @@ public class BugSetQueryTimeout_Bug_3357
 		try {
 			System.out.println("QueryTimeout = " + st.getQueryTimeout());
 
-			st.setQueryTimeout(123);
-			System.out.println("QueryTimeout = " + st.getQueryTimeout());
-
-			st.setQueryTimeout(2134567890);
-			System.out.println("QueryTimeout = " + st.getQueryTimeout());
-
-			st.setQueryTimeout(0);
-			System.out.println("QueryTimeout = " + st.getQueryTimeout());
-
-			st.setQueryTimeout(-1);	// to generate an SQLException as negative timeouts are invalid
-			System.out.println("QueryTimeout = " + st.getQueryTimeout());
+			testTimeout(st, 123);
+			testTimeout(st, 123);
+			testTimeout(st, 2134567890);
+			testTimeout(st, 0);
+			testTimeout(st, 0);
+			testTimeout(st, -1);	// to generate an SQLException as negative timeouts are invalid
 		} catch (SQLException se) {
 			System.out.println("setQueryTimeout(timeout_value) throws: " + se);
 		} finally {
@@ -34,4 +29,13 @@ public class BugSetQueryTimeout_Bug_3357
 		}
 		con.close();
 	}
+
+	private static void testTimeout(Statement st, int secs) throws SQLException {
+		st.setQueryTimeout(secs);
+		// as the call to set the timeout is delayed till a statement is executed, issue a select statment
+		ResultSet rs = st.executeQuery("SELECT " + secs);
+		if (rs != null)
+			rs.close();
+		System.out.println("QueryTimeout = " + st.getQueryTimeout());
+	}
 }
new file mode 100644
--- /dev/null
+++ b/tests/SQLcopyinto.java
@@ -0,0 +1,130 @@
+/*
+ * 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 - 2018 MonetDB B.V.
+ */
+
+import java.sql.*;
+import java.io.*;
+import java.util.*;
+import nl.cwi.monetdb.mcl.connection.mapi.MapiConnection;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
+
+/**
+ * This program demonstrates how the MonetDB JDBC driver can facilitate
+ * in performing COPY INTO ... FROM STDIN sequences.
+ * It shows how a data stream via MapiSocket to STDIN can be performed.
+ *
+ * @author Fabian Groffen, Martin van Dinther
+ */
+
+public class SQLcopyinto {
+	final private static String tablenm = "exampleSQLCopyInto";
+
+	public static void main(String[] args) throws Exception {
+		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.28.jar:. SQLcopyinto \"jdbc:monetdb://localhost:50000/demo?user=monetdb&password=monetdb\"");
+			System.exit(-1);
+		}
+		String jdbc_url = args[0];
+
+		// request a connection to MonetDB server via the driver manager
+		Connection conn = DriverManager.getConnection(jdbc_url);
+		System.out.println("Connected to MonetDB server");
+
+		Statement stmt = null;
+		ResultSet rs = null;
+		try {
+			stmt = conn.createStatement();
+			stmt.execute("CREATE TABLE IF NOT EXISTS " + tablenm + " (id int, val varchar(24))");
+
+			fillTableUsingCopyIntoSTDIN(conn);
+
+			// check content of the table populated via COPY INTO ... FROM STDIN
+			System.out.println("Listing uploaded data:");
+			rs = stmt.executeQuery("SELECT * FROM " + tablenm);
+			if (rs != null) {
+				while (rs.next()) {
+					System.out.println("Row data: " + rs.getString(1) + ", " + rs.getString(2));
+				}
+				rs.close();
+				rs = null;
+			}
+
+			stmt.execute("DROP TABLE " + tablenm);
+
+			System.out.println("SQLcopyinto completed");
+		} catch (SQLException e) {
+			System.err.println("SQLException: " + e.getMessage());
+		} catch (Exception e) {
+			System.err.println("Exception: " + e.getMessage());
+		} finally {
+			// free resources
+			if (rs != null)
+				rs.close();
+			if (stmt != null)
+				stmt.close();
+			// close the JDBC connection to MonetDB server
+			if (conn != null)
+				conn.close();
+		}
+	}
+
+	public static void fillTableUsingCopyIntoSTDIN(Connection mcon) throws Exception {
+		System.out.println();
+		System.out.println("CopyInto STDIN begin");
+
+		MapiConnection server = new MapiConnection(null, null, "sql", false, true,"localhost", 50000, "database");
+		try {
+			server.setSoTimeout(60);
+
+			// extract from MonetConnection object the used connection properties
+			String host = mcon.getClientInfo("host");
+			int port = Integer.parseInt(mcon.getClientInfo("port"));
+			String login = mcon.getClientInfo("user");
+			String passw = mcon.getClientInfo("password");
+			// System.out.println("host: " + host + " port: " + port + " login: " + login + " passwd: " + passw);
+
+			System.out.println("Before connecting to MonetDB server via MapiSocket");
+			List warning =  server.connect("monetdb", "monetdb");
+			if (warning != null) {
+				for (Iterator it = warning.iterator(); it.hasNext(); ) {
+					System.out.println("Warning: " + it.next().toString());
+				}
+			}
+			System.out.println("Connected to MonetDB server via MapiSocket");
+			AbstractProtocol oldmMapiProtocol = server.getProtocol();
+
+			String error = oldmMapiProtocol.getRemainingStringLine(0);
+			if (error != null)
+				System.err.println("Received start error: " + error);
+
+			System.out.println("Before sending data to STDIN");
+
+			// the leading 's' is essential, since it is a protocol marker
+			// that should not be omitted, likewise the trailing semicolon
+			oldmMapiProtocol.writeNextQuery("s", "COPY INTO " + tablenm + " FROM STDIN USING DELIMITERS ',','\\n';", "\n");
+			// now write the row data values as csv data lines to the STDIN stream
+			for (int i = 0; i < 40; i++) {
+				oldmMapiProtocol.writeNextQuery(null, "" + i + ",val_" + i, "\n");
+			}
+			oldmMapiProtocol.waitUntilPrompt();
+			error = oldmMapiProtocol.getRemainingStringLine(0);
+			if (error != null)
+				System.err.println("Received error: " + error);
+			System.out.println("Completed sending data via STDIN");
+		} catch (Exception e) {
+			System.err.println("Mapi Exception: " + e.getMessage());
+		} finally {
+			// close connection to MonetDB server
+			server.close();
+		}
+
+		System.out.println("CopyInto STDIN end");
+		System.out.println();
+	}
+}
--- a/tests/build.xml
+++ b/tests/build.xml
@@ -95,6 +95,7 @@ Copyright 1997 - July 2008 CWI, August 2
 
   <!-- Run tests -->
   <target name="test">
+    <antcall target="SQLcopyinto" />
     <antcall target="Test_Cautocommit" />
     <!-- <antcall target="Test_Cforkbomb" /> -->
     <antcall target="Test_CisValid" />
@@ -152,6 +153,12 @@ Copyright 1997 - July 2008 CWI, August 2
 
   <!-- convenience targets for the outside caller to specify which
   test(s) should be run -->
+  <target name="SQLcopyinto">
+    <antcall target="test_class">
+      <param name="test.class" value="SQLcopyinto" />
+    </antcall>
+  </target>
+
   <target name="Test_Cautocommit">
     <antcall target="test_class">
       <param name="test.class" value="Test_Cautocommit" />