Mercurial > hg > monetdb-java
changeset 287:2ad7f42f141d embedded
Merge with default.
author | Pedro Ferreira <pedro.ferreira@monetdbsolutions.com> |
---|---|
date | Fri, 26 Jul 2019 11:27:31 +0200 (2019-07-26) |
parents | 4face9f42efc (current diff) d430f8adbf1b (diff) |
children | 68401c1f10fa |
files | 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/MonetPreparedStatement.java src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java src/main/java/nl/cwi/monetdb/jdbc/MonetSavepoint.java src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java src/main/java/nl/cwi/monetdb/mcl/connection/SenderThread.java tests/build.xml |
diffstat | 10 files changed, 1223 insertions(+), 805 deletions(-) [+] |
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java @@ -11,7 +11,6 @@ package nl.cwi.monetdb.jdbc; import nl.cwi.monetdb.mcl.connection.ControlCommands; import nl.cwi.monetdb.mcl.connection.IMonetDBLanguage; import nl.cwi.monetdb.mcl.connection.MCLException; -import nl.cwi.monetdb.mcl.connection.SenderThread; import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.protocol.ServerResponses; @@ -50,8 +49,10 @@ import java.util.concurrent.Executor; * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira * @version 1.3 */ -public abstract class MonetConnection extends MonetWrapper implements Connection, AutoCloseable { - +public abstract class MonetConnection + extends MonetWrapper + implements Connection, AutoCloseable +{ /** The sequence counter */ private static int SeqCounter = 0; @@ -66,18 +67,22 @@ public abstract class MonetConnection ex /** The successful processed input properties */ protected final Properties conn_props; + /** The language to connect with */ protected IMonetDBLanguage language; + /** Authentication hash method */ protected final String hash; - /** An optional thread that is used for sending large queries */ - private SenderThread senderThread; + /** Whether this Connection is closed (and cannot be used anymore) */ private boolean closed; + /** Whether this Connection is in autocommit mode */ private boolean autoCommit = true; + /** The stack of warnings for this Connection object */ - private SQLWarning warnings; + private SQLWarning warnings = null; + /** The Connection specific mapping of user defined types to Java types */ private Map<String,Class<?>> typeMap = new HashMap<String,Class<?>>() { private static final long serialVersionUID = 1L; { @@ -86,25 +91,32 @@ public abstract class MonetConnection ex } }; - // See javadoc for documentation about WeakHashMap if you don't know what it does !!!NOW!!! - // (only when you deal with it of course) + // See javadoc for documentation about WeakHashMap if you don't know what + // it does !!!NOW!!! (only when you deal with it of course) /** A Map containing all (active) Statements created from this Connection */ - private Map<Statement,?> statements = new WeakHashMap<>(); + private Map<Statement,?> statements = new WeakHashMap<Statement, Object>(); + /** The number of results we receive from the server at once */ - private int curReplySize = -1; // the server by default uses -1 (all) + private int curReplySize = -1; // the server by default uses -1 (all) /** Whether or not BLOB is mapped to Types.VARBINARY instead of Types.BLOB within this connection */ - private final boolean treatBlobAsVarBinary; + private boolean treatBlobAsVarBinary = false; /** Whether or not CLOB is mapped to Types.VARCHAR instead of Types.CLOB within this connection */ - private final boolean treatClobAsVarChar; + private boolean treatClobAsVarChar = false; /** The underlying proticol provided by the connection (MAPI or embedded) */ protected AbstractProtocol protocol; /** Tells if the connection is embedded or not */ private final boolean isEmbedded; /** - * Constructor of a Connection for MonetDB. At this moment the current implementation limits itself to storing the - * given host, database, username and password for later use by the createStatement() call. This constructor is - * only accessible to classes from the jdbc package. + * Constructor of a Connection for MonetDB. At this moment the + * current implementation limits itself to storing the given host, + * database, username and password for later use by the + * createStatement() call. This constructor is only accessible to + * classes from the jdbc package. + * + * @param props a Property hashtable holding the properties needed for connecting + * @throws SQLException if a database error occurs + * @throws IllegalArgumentException is one of the arguments is null or empty */ public MonetConnection(Properties props, String hash, IMonetDBLanguage language, boolean blobIsBinary, boolean clobIsLongChar) { @@ -181,6 +193,7 @@ public abstract class MonetConnection ex /** The last set query timeout on the server as used by Statement, PreparedStatement and CallableStatement */ protected int lastSetQueryTimeout = 0; // 0 means no timeout, which is the default on the server + /** * Gets the initial value for the StringBuilder size. * @@ -239,10 +252,23 @@ public abstract class MonetConnection ex BatchUpdateException e) throws SQLException; /** - * Releases this Connection object's database and JDBC resources immediately instead of waiting for them to be - * automatically released. All Statements created from this Connection will be closed when this method is called. + * Clears all warnings reported for this Connection object. After a + * call to this method, the method getWarnings returns null until a + * new warning is reported for this Connection object. + */ + @Override + public void clearWarnings() { + warnings = null; + } + + /** + * Releases this Connection object's database and JDBC resources + * immediately instead of waiting for them to be automatically + * released. All Statements created from this Connection will be + * closed when this method is called. * - * Calling the method close on a Connection object that is already closed is a no-op. + * Calling the method close on a Connection object that is already + * closed is a no-op. */ @Override public void close() { @@ -259,11 +285,6 @@ public abstract class MonetConnection ex } catch (IOException e) { // ignore it } - // close active SendThread if any - if (senderThread != null) { - senderThread.shutdown(); - senderThread = null; - } // report ourselves as closed closed = true; } @@ -278,28 +299,21 @@ public abstract class MonetConnection ex super.finalize(); } - //== methods of interface Connection - /** - * Clears all warnings reported for this Connection object. After a call to this method, the method getWarnings - * returns null until a new warning is reported for this Connection object. - */ - @Override - public void clearWarnings() { - warnings = null; - } - - /** - * Makes all changes made since the previous commit/rollback permanent and releases any database locks currently - * held by this Connection object. This method should be used only when auto-commit mode has been disabled. + * Makes all changes made since the previous commit/rollback + * permanent and releases any database locks currently held by this + * Connection object. This method should be used only when + * auto-commit mode has been disabled. * - * @throws SQLException if a database access error occurs or this Connection object is in auto-commit mode + * @throws SQLException if a database access error occurs or this + * Connection object is in auto-commit mode * @see #setAutoCommit(boolean) */ @Override public void commit() throws SQLException { - // note: can't use sendIndependentCommand here because we need to process the auto_commit state the server gives - this.sendTransactionCommand("COMMIT"); + // note: can't use sendIndependentCommand here because we need + // to process the auto_commit state the server gives + sendTransactionCommand("COMMIT"); } /** @@ -400,9 +414,12 @@ public abstract class MonetConnection ex } /** - * Retrieves the current holdability of ResultSet objects created using this Connection object. + * Retrieves the current holdability of ResultSet objects created + * using this Connection object. * - * @return the holdability, one of ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT + * @return the holdability, one of + * ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT * @see #setHoldability(int) */ @Override @@ -424,16 +441,18 @@ public abstract class MonetConnection ex */ @Override public DatabaseMetaData getMetaData() throws SQLException { - if (!this.language.getRepresentation().equals("sql")) { + if (!this.language.getRepresentation().equals("sql")) throw new SQLException("This method is only supported in SQL mode", "M0M04"); - } + return new MonetDatabaseMetaData(this); } /** - * Retrieves this Connection object's current transaction isolation level. + * Retrieves this Connection object's current transaction isolation + * level. * - * @return the current transaction isolation level, which will be Connection.TRANSACTION_SERIALIZABLE + * @return the current transaction isolation level, which will be + * Connection.TRANSACTION_SERIALIZABLE */ @Override public int getTransactionIsolation() { @@ -441,10 +460,12 @@ public abstract class MonetConnection ex } /** - * Retrieves the Map object associated with this Connection object. Unless the application has added an entry, - * the type map returned will be empty. + * Retrieves the Map object associated with this Connection object. + * Unless the application has added an entry, the type map returned + * will be empty. * - * @return the java.util.Map object associated with this Connection object + * @return the java.util.Map object associated with this Connection + * object */ @Override public Map<String,Class<?>> getTypeMap() { @@ -458,19 +479,22 @@ public abstract class MonetConnection ex * the method SQLWarning.getNextWarning on the warning that was * retrieved previously. * - * This method may not be called on a closed connection; doing so will cause an SQLException to be thrown. + * This method may not be called on a closed connection; doing so + * will cause an SQLException to be thrown. * * Note: Subsequent warnings will be chained to this SQLWarning. * * @return the first SQLWarning object or null if there are none - * @throws SQLException if a database access error occurs or this method is called on a closed connection + * @throws SQLException if a database access error occurs or this method is + * called on a closed connection */ @Override public SQLWarning getWarnings() throws SQLException { - if (closed) { + if (closed) throw new SQLException("Cannot call on closed Connection", "M1M20"); - } - // if there are no warnings, this will be null, which fits with the specification. + + // if there are no warnings, this will be null, which fits with the + // specification. return warnings; } @@ -486,7 +510,8 @@ public abstract class MonetConnection ex * can determine that a connection is invalid by catching any * exceptions that might be thrown when an operation is attempted. * - * @return true if this Connection object is closed; false if it is still open + * @return true if this Connection object is closed; false if it is + * still open */ @Override public boolean isClosed() { @@ -688,10 +713,16 @@ public abstract class MonetConnection ex */ @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException { + throws SQLException + { try { - PreparedStatement ret = new MonetPreparedStatement(this, resultSetType, resultSetConcurrency, - resultSetHoldability, sql); + PreparedStatement ret = new MonetPreparedStatement( + this, + resultSetType, + resultSetConcurrency, + resultSetHoldability, + sql + ); // store it in the map for when we close... statements.put(ret, null); return ret; @@ -814,20 +845,20 @@ public abstract class MonetConnection ex */ @Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { - if (!(savepoint instanceof MonetSavepoint)) { + if (!(savepoint instanceof MonetSavepoint)) throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); - } - MonetSavepoint sp = (MonetSavepoint) savepoint; + + MonetSavepoint sp = (MonetSavepoint)savepoint; + // note: can't use sendIndependentCommand here because we need // to process the auto_commit state the server gives - this.sendTransactionCommand("RELEASE SAVEPOINT " + sp.getName()); + sendTransactionCommand("RELEASE SAVEPOINT " + sp.getName()); } /** * Undoes all changes made in the current transaction and releases - * any database locks currently held by this Connection object. This - * method should be used only when auto-commit mode has been - * disabled. + * any database locks currently held by this Connection object. + * This method should be used only when auto-commit mode has been disabled. * * @throws SQLException if a database access error occurs or this * Connection object is in auto-commit mode @@ -837,15 +868,13 @@ public abstract class MonetConnection ex public void rollback() throws SQLException { // note: can't use sendIndependentCommand here because we need // to process the auto_commit state the server gives - // create a container for the result - this.sendTransactionCommand("ROLLBACK"); + sendTransactionCommand("ROLLBACK"); } /** * Undoes all changes made after the given Savepoint object was set. * - * This method should be used only when auto-commit has been - * disabled. + * This method should be used only when auto-commit has been disabled. * * @param savepoint the Savepoint object to roll back to * @throws SQLException if a database access error occurs, the @@ -854,15 +883,14 @@ public abstract class MonetConnection ex */ @Override public void rollback(Savepoint savepoint) throws SQLException { - if (!(savepoint instanceof MonetSavepoint)) { + if (!(savepoint instanceof MonetSavepoint)) throw new SQLException("This driver can only handle savepoints it created itself", "M0M06"); - } MonetSavepoint sp = (MonetSavepoint)savepoint; + // note: can't use sendIndependentCommand here because we need // to process the auto_commit state the server gives - // create a container for the result - this.sendTransactionCommand("ROLLBACK TO SAVEPOINT " + sp.getName()); + sendTransactionCommand("ROLLBACK TO SAVEPOINT " + sp.getName()); } /** @@ -871,8 +899,7 @@ public abstract class MonetConnection ex * will be executed and committed as individual transactions. * Otherwise, its SQL statements are grouped into transactions that * are terminated by a call to either the method commit or the - * method rollback. By default, new connections are in auto-commit - * mode. + * method rollback. By default, new connections are in auto-commit mode. * * The commit occurs when the statement completes or the next * execute occurs, whichever comes first. In the case of statements @@ -939,9 +966,8 @@ public abstract class MonetConnection ex */ @Override public void setReadOnly(boolean readOnly) throws SQLException { - if (readOnly) { + if (readOnly == true) addWarning("cannot setReadOnly(true): read-only Connection mode not supported", "01M08"); - } } /** @@ -956,17 +982,16 @@ public abstract class MonetConnection ex public Savepoint setSavepoint() throws SQLException { // create a new Savepoint object MonetSavepoint sp = new MonetSavepoint(); + // note: can't use sendIndependentCommand here because we need // to process the auto_commit state the server gives - // create a container for the result - this.sendTransactionCommand("SAVEPOINT " + sp.getName()); + sendTransactionCommand("SAVEPOINT " + sp.getName()); return sp; } /** - * Creates a savepoint with the given name in the current - * transaction and returns the new Savepoint object that represents - * it. + * Creates a savepoint with the given name in the current transaction + * and returns the new Savepoint object that represents it. * * @param name a String containing the name of the savepoint * @return the new Savepoint object @@ -982,28 +1007,30 @@ public abstract class MonetConnection ex } catch (IllegalArgumentException e) { throw new SQLException(e.getMessage(), "M0M03"); } + // note: can't use sendIndependentCommand here because we need // to process the auto_commit state the server gives - // create a container for the result - this.sendTransactionCommand("SAVEPOINT " + sp.getName()); + sendTransactionCommand("SAVEPOINT " + sp.getName()); return sp; } /** * Attempts to change the transaction isolation level for this * Connection object to the one given. The constants defined in the - * interface Connection are the possible transaction isolation - * levels. + * interface Connection are the possible transaction isolation levels. * - * @param level one of the following Connection constants: Connection.TRANSACTION_READ_UNCOMMITTED, - * Connection.TRANSACTION_READ_COMMITTED, Connection.TRANSACTION_REPEATABLE_READ, or + * @param level one of the following Connection constants: + * Connection.TRANSACTION_READ_UNCOMMITTED, + * Connection.TRANSACTION_READ_COMMITTED, + * Connection.TRANSACTION_REPEATABLE_READ, or * Connection.TRANSACTION_SERIALIZABLE. */ @Override public void setTransactionIsolation(int level) { if (level != TRANSACTION_SERIALIZABLE) { addWarning("MonetDB only supports fully serializable " + - "transactions, continuing with transaction level raised to TRANSACTION_SERIALIZABLE", "01M09"); + "transactions, continuing with transaction level " + + "raised to TRANSACTION_SERIALIZABLE", "01M09"); } } @@ -1031,6 +1058,8 @@ public abstract class MonetConnection ex (closed ? "disconnected" : "connected"); } + //== Java 1.6 methods (JDBC 4.0) + /** * Factory method for creating Array objects. * @@ -1053,12 +1082,9 @@ public abstract class MonetConnection ex * type or a standard SQL type supported by this database. * This is the value returned by Array.getBaseTypeName * @return an Array object whose elements map to the specified SQL type - * @throws SQLException if a database error occurs, the JDBC type - * is not appropriate for the typeName and the conversion is - * not supported, the typeName is null or this method is - * called on a closed connection - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support this data type + * @throws SQLException - if a database error occurs, the JDBC type is not appropriate for the typeName and + * the conversion is not supported, the typeName is null or this method is called on a closed connection + * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type * @since 1.6 */ @Override @@ -1067,8 +1093,6 @@ public abstract class MonetConnection ex } - //== end methods of interface java.sql.Connection - /** * Constructs an object that implements the Clob interface. The * object returned initially contains no data. The setAsciiStream, @@ -1076,8 +1100,8 @@ public abstract class MonetConnection ex * may be used to add data to the Clob. * * @return a MonetClob instance - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support MonetClob objects that can be filled in + * @throws SQLException - if an object that implements the Clob interface can not be constructed, + * this method is called on a closed connection or a database access error occurs. * @since 1.6 */ @Override @@ -1092,13 +1116,14 @@ public abstract class MonetConnection ex * data to the Blob. * * @return a MonetBlob instance - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support MonetBlob objects that can be filled in + * @throws SQLException - if an object that implements the Blob interface can not be constructed, + * this method is called on a closed connection or a database access error occurs. * @since 1.6 */ @Override public java.sql.Blob createBlob() throws SQLException { - return new MonetBlob(new byte[1]); + byte[] buf = new byte[1]; + return new MonetBlob(buf); } /** @@ -1108,8 +1133,9 @@ public abstract class MonetConnection ex * may be used to add data to the NClob. * * @return an NClob instance - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support MonetNClob objects that can be filled in + * @throws SQLException - if an object that implements the NClob interface can not be constructed, + * this method is called on a closed connection or a database access error occurs. + * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type * @since 1.6 */ @Override @@ -1127,10 +1153,9 @@ public abstract class MonetConnection ex * @param attributes the attributes that populate the returned object * @return a Struct object that maps to the given SQL type and is * populated with the given attributes - * @throws SQLException if a database error occurs, the typeName - * is null or this method is called on a closed connection - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support this data type + * @throws SQLException - if an object that implements the Struct interface can not be constructed, + * this method is called on a closed connection or a database access error occurs. + * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type * @since 1.6 */ @Override @@ -1145,8 +1170,9 @@ public abstract class MonetConnection ex * interface may be used to add data to the SQLXML object. * * @return An object that implements the SQLXML interface - * @throws SQLFeatureNotSupportedException the JDBC driver does - * not support this data type + * @throws SQLException - if an object that implements the SQLXML interface can not be constructed, + * this method is called on a closed connection or a database access error occurs. + * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support this data type * @since 1.6 */ @Override @@ -1179,7 +1205,7 @@ public abstract class MonetConnection ex if (closed) return false; - // ping db using query: select 1; + // ping monetdb server using query: select 1; Statement stmt = null; ResultSet rs = null; boolean isValid = false; @@ -1325,6 +1351,7 @@ public abstract class MonetConnection ex name.equals("database") || name.equals("language") || name.equals("so_timeout") || + name.equals("debug") || name.equals("hash") || name.equals("treat_blob_as_binary") || name.equals("treat_clob_as_varchar") || @@ -1337,6 +1364,7 @@ public abstract class MonetConnection ex } else { addWarning("setClientInfo: " + name + "is not a recognised property", "01M07"); } + return; } /** @@ -1366,31 +1394,42 @@ public abstract class MonetConnection ex public void setClientInfo(Properties props) throws SQLClientInfoException { if (props != null) { for (Map.Entry<Object, Object> entry : props.entrySet()) { - setClientInfo(entry.getKey().toString(), entry.getValue().toString()); + setClientInfo(entry.getKey().toString(), + entry.getValue().toString()); } } } + + //== Java 1.7 methods (JDBC 4.1) + /** * Sets the given schema name to access. * * @param schema the name of a schema in which to work - * @throws SQLException if a database access error occurs or this method is called on a closed connection + * @throws SQLException if a database access error occurs or this + * method is called on a closed connection * @since 1.7 */ @Override public void setSchema(String schema) throws SQLException { if (closed) throw new SQLException("Cannot call on closed Connection", "M1M20"); - if (schema == null) + if (schema == null || schema.isEmpty()) throw new SQLException("Missing schema name", "M1M05"); - Statement st = createStatement(); - schema = schema.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'"); + Statement st = null; try { - st.execute("SET SCHEMA \"" + schema + "\""); + st = createStatement(); + if (st != null) + st.execute("SET SCHEMA \"" + schema + "\""); + // do not catch any Exception, just let it propagate } finally { - st.close(); + if (st != null) { + try { + st.close(); + } catch (SQLException e) { /* ignore */ } + } } } @@ -1398,26 +1437,42 @@ public abstract class MonetConnection ex * Retrieves this Connection object's current schema name. * * @return the current schema name or null if there is none - * @throws SQLException if a database access error occurs or this method is called on a closed connection + * @throws SQLException if a database access error occurs or this + * method is called on a closed connection + * @since 1.7 */ @Override public String getSchema() throws SQLException { - if (closed) { + if (closed) throw new SQLException("Cannot call on closed Connection", "M1M20"); - } - String cur_schema; - Statement st = createStatement(); + + String cur_schema = null; + Statement st = null; ResultSet rs = null; try { - rs = st.executeQuery("SELECT CURRENT_SCHEMA"); - if (!rs.next()) - throw new SQLException("Row expected", "02000"); - cur_schema = rs.getString(1); + st = createStatement(); + if (st != null) { + rs = st.executeQuery("SELECT CURRENT_SCHEMA"); + if (rs != null) { + if (rs.next()) + cur_schema = rs.getString(1); + } + } + // do not catch any Exception, just let it propagate } finally { - if (rs != null) - rs.close(); - st.close(); + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { /* ignore */ } + } + if (st != null) { + try { + st.close(); + } catch (SQLException e) { /* ignore */ } + } } + if (cur_schema == null) + throw new SQLException("Failed to fetch schema name", "02000"); return cur_schema; } @@ -1432,9 +1487,13 @@ public abstract class MonetConnection ex * Calling abort marks the connection closed and releases any * resources. Calling abort on a closed connection is a no-op. * - * @param executor The Executor implementation which will be used by abort - * @throws SQLException if a database access error occurs or the executor is null - * @throws SecurityException if a security manager exists and its checkPermission method denies calling abort + * @param executor The Executor implementation which will be used by + * abort + * @throws SQLException if a database access error occurs or the + * executor is null + * @throws SecurityException if a security manager exists and + * its checkPermission method denies calling abort + * @since 1.7 */ @Override public void abort(Executor executor) throws SQLException { @@ -1457,10 +1516,14 @@ public abstract class MonetConnection ex * isClosed or Connection.isValid methods, will result in a * SQLException. * - * @param executor The Executor implementation which will be used by setNetworkTimeout - * @param millis The time in milliseconds to wait for the database operation to complete - * @throws SQLException if a database access error occurs, this method is called on a closed connection, the - * executor is null, or the value specified for seconds is less than 0. + * @param executor The Executor implementation which will be used by + * setNetworkTimeout + * @param millis The time in milliseconds to wait for the + * database operation to complete + * @throws SQLException if a database access error occurs, this + * method is called on a closed connection, the executor is + * null, or the value specified for seconds is less than 0. + * @since 1.7 */ @Override public void setNetworkTimeout(Executor executor, int millis) throws SQLException { @@ -1501,6 +1564,7 @@ public abstract class MonetConnection ex } } + //== end methods of interface java.sql.Connection /** @@ -1604,10 +1668,12 @@ public abstract class MonetConnection ex } /** - * Sends the given string to MonetDB as regular statement, making sure there is a prompt after the command is sent. - * All possible returned information is discarded. Encountered errors are reported. + * Sends the given string to MonetDB as command/query using commandTempl or queryTempl + * Making sure there is a prompt after the command is sent. All possible + * returned information is discarded. Encountered errors are reported. * * @param command the exact string to send to MonetDB + * @param usequeryTempl send the command using a queryTempl? else it is send using commandTempl * @throws SQLException if an IO exception or a database error occurs */ void sendIndependentCommand(String command) throws SQLException { @@ -1628,8 +1694,10 @@ public abstract class MonetConnection ex } /** - * Adds a warning to the pile of warnings this Connection object has. If there were no warnings (or clearWarnings - * was called) this warning will be the first, otherwise this warning will get appended to the current warning. + * Adds a warning to the pile of warnings this Connection object + * has. If there were no warnings (or clearWarnings was called) + * this warning will be the first, otherwise this warning will get + * appended to the current warning. * * @param reason the warning message */ @@ -1643,9 +1711,11 @@ public abstract class MonetConnection ex } /** - * A list of Response objects. Responses are added to this list. Methods of this class are not synchronized. This is - * left as responsibility to the caller to prevent concurrent access. + * A list of Response objects. Responses are added to this list. + * Methods of this class are not synchronized. This is left as + * responsibility to the caller to prevent concurrent access. */ + // {{{ ResponseList class implementation public class ResponseList { /** The cache size (number of rows in a DataBlockResponse object) */ private final int cachesize; @@ -1657,28 +1727,39 @@ public abstract class MonetConnection ex private final int rsconcur; /** The sequence number of this ResponseList */ private final int seqnr; - /** A list of the Responses associated with the query, in the right order */ - private final List<IResponse> responses = new ArrayList<>(); - /** A map of ResultSetResponses, used for additional DataBlockResponse mapping */ + /** A list of the Responses associated with the query, + * in the right order */ + private final List<IResponse> responses; + /** A map of ResultSetResponses, used for additional + * DataBlockResponse mapping */ private Map<Integer, ResultSetResponse> rsresponses; + /** The current header returned by getNextResponse() */ - private int curResponse = -1; + private int curResponse; /** - * Main constructor. The query argument can either be a String or List. An SQLException is thrown if another - * object instance is supplied. + * Main constructor. The query argument can either be a String + * or List. An SQLException is thrown if another object + * instance is supplied. * * @param cachesize overall cachesize to use * @param maxrows maximum number of rows to allow in the set * @param rstype the type of result sets to produce * @param rsconcur the concurrency of result sets to produce */ - ResponseList(int cachesize, int maxrows, int rstype, int rsconcur) { + ResponseList( + int cachesize, + int maxrows, + int rstype, + int rsconcur + ) throws SQLException { this.cachesize = cachesize; this.maxrows = maxrows; this.rstype = rstype; this.rsconcur = rsconcur; - this.seqnr = SeqCounter++; + responses = new ArrayList<IResponse>(); + curResponse = -1; + seqnr = SeqCounter++; } public int getCachesize() { @@ -1698,7 +1779,8 @@ public abstract class MonetConnection ex } /** - * Retrieves the next available response, or null if there are no more responses. + * Retrieves the next available response, or null if there are + * no more responses. * * @return the next Response available or null */ @@ -1714,7 +1796,8 @@ public abstract class MonetConnection ex } curResponse++; if (curResponse >= responses.size()) { - // ResponseList is obviously completed so, there are no more responses + // ResponseList is obviously completed so, there are no + // more responses return null; } else { // return this response @@ -1723,7 +1806,7 @@ public abstract class MonetConnection ex } /** - * Closes the Response at index i, if not null. + * Closes the Reponse at index i, if not null. * * @param i the index position of the header to close */ @@ -1752,7 +1835,8 @@ public abstract class MonetConnection ex } /** - * Closes this ResponseList by closing all the Responses in this ResponseList. + * Closes this ResponseList by closing all the Responses in this + * ResponseList. */ public void close() { for (int i = 0; i < responses.size(); i++) { @@ -1761,7 +1845,8 @@ public abstract class MonetConnection ex } /** - * Returns whether this ResponseList has still unclosed Responses. + * Returns whether this ResponseList has still unclosed + * Responses. */ boolean hasUnclosedResponses() { for (IResponse r : responses) { @@ -1772,7 +1857,8 @@ public abstract class MonetConnection ex } /** - * Executes the query contained in this ResponseList, and stores the Responses resulting from this query in this + * Executes the query contained in this ResponseList, and + * stores the Responses resulting from this query in this * ResponseList. * * @throws SQLException if a database error occurs @@ -1785,24 +1871,28 @@ public abstract class MonetConnection ex * Internal executor of queries. * * @param templ the template to fill in - * @param query the query to execute + * @param the query to execute * @throws SQLException if a database error occurs */ @SuppressWarnings("fallthrough") - public void executeQuery(String[] templ, String query) throws SQLException { + public void executeQuery(String[] templ, String query) + throws SQLException + { String error = null; try { - // make sure we're ready to send query; read data till we have the prompt it is possible (and most - // likely) that we already have the prompt and do not have to skip any lines. Ignore errors from - // previous result sets. + // make sure we're ready to send query; read data till we + // have the prompt it is possible (and most likely) that we + // already have the prompt and do not have to skip any + // lines. Ignore errors from previous result sets. protocol.waitUntilPrompt(); // {{{ set reply size - /* - * Change the reply size of the server. If the given value is the same as the current value known - * to use, then ignore this call. If it is set to 0 we get a prompt after the server sent it's - * header. + /** + * Change the reply size of the server. If the given + * value is the same as the current value known to use, + * then ignore this call. If it is set to 0 we get a + * prompt after the server sent it's header. * * 2017: For now, in the embedded connection, the value set cachesize will be always the default one. */ @@ -1817,23 +1907,8 @@ public abstract class MonetConnection ex } // }}} set reply size - // If the query is larger than the TCP buffer size, use a special send thread to avoid deadlock with - // the server due to blocking behaviour when the buffer is full. Because the server will be writing - // back results to us, it will eventually block as well when its TCP buffer gets full, as we are - // blocking an not consuming from it. The result is a state where both client and server want to - // write, but block. - if (query.length() > getBlockSize()) { - // get a reference to the send thread - if (senderThread == null) { - senderThread = new SenderThread(protocol); - } - // tell it to do some work! - senderThread.runQuery(templ, query); - } else { - // this is a simple call, which is a lot cheaper and will always succeed for small queries. - protocol.writeNextQuery((templ[0] == null) ? "" : templ[0], query, - (templ[1] == null) ? "" : templ[1]); - } + // send query to the server + protocol.writeNextQuery((templ[0] == null) ? "" : templ[0], query, (templ[1] == null) ? "" : templ[1]); // go for new results protocol.fetchNextResponseData(); @@ -1896,12 +1971,14 @@ public abstract class MonetConnection ex nextResponse = protocol.getCurrentServerResponse(); break; } + // immediately handle errors after parsing the header (res may be null) if (error != null) { protocol.waitUntilPrompt(); nextResponse = protocol.getCurrentServerResponse(); break; } + // here we have a res object, which we can start filling if (res instanceof IIncompleteResponse) { IIncompleteResponse iter = (IIncompleteResponse) res; @@ -1918,28 +1995,29 @@ public abstract class MonetConnection ex } } } - - if (error != null) { + if (error != null) break; - } - // it is of no use to store DataBlockResponses, you never want to retrieve them directly - // anyway - if (!(res instanceof AbstractDataBlockResponse)) { + // it is of no use to store DataBlockReponses, you never want to + // retrieve them directly anyway + if (!(res instanceof AbstractDataBlockResponse)) responses.add(res); - } - // read the next line (can be prompt, new result, error, etc.) before we start the loop over + + // read the next line (can be prompt, new result, error, etc.) + // before we start the loop over protocol.fetchNextResponseData(); nextResponse = protocol.getCurrentServerResponse(); break; case ServerResponses.INFO: addWarning(protocol.getRemainingStringLine(0), "01000"); - // read the next line (can be prompt, new result, error, etc.) before we start the loop over + // read the next line (can be prompt, new result, error, etc.) + // before we start the loop over protocol.fetchNextResponseData(); nextResponse = protocol.getCurrentServerResponse(); break; case ServerResponses.ERROR: - // read everything till the prompt (should be error) we don't know if we ignore some + // read everything till the prompt (should be + // error) we don't know if we ignore some // garbage here... but the log should reveal that error = protocol.getRemainingStringLine(0); protocol.waitUntilPrompt(); @@ -1950,17 +2028,6 @@ public abstract class MonetConnection ex } } - // if we used the senderThread, make sure it has finished - if (senderThread != null) { - String tmp = senderThread.getErrors(); - if (tmp != null) { - if (error == null) { - error = "08000!" + tmp; - } else { - error += "\n08000!" + tmp; - } - } - } if (error != null) { SQLException ret = null; String[] errorsList = error.split("\n"); @@ -1982,7 +2049,7 @@ public abstract class MonetConnection ex throw ret; } } catch (SocketTimeoutException e) { - this.close(); // JDBC 4.1 semantics, abort() + close(); // JDBC 4.1 semantics, abort() throw new SQLNonTransientConnectionException("connection timed out", "08M33"); } catch (IOException e) { closed = true; @@ -1990,4 +2057,5 @@ public abstract class MonetConnection ex } } } + // }}} }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java @@ -26,7 +26,7 @@ import java.util.ArrayList; * @version 0.7 */ public class MonetDatabaseMetaData extends MonetWrapper implements DatabaseMetaData { - private Connection con; + private final Connection con; // Internal cache for 3 server environment values private String env_current_user; @@ -512,6 +512,8 @@ public class MonetDatabaseMetaData exten @Override public String getSystemFunctions() { + // Note: As of Apr2019 (11.33.3) release the system table systemfunctions is replaced by a view which queries functions.system + // TODO: Replace join to sys.systemfunctions with " AND \"system\" " but only if the server-version is >= 11.33.3 String wherePart = "\"id\" NOT IN (SELECT \"func_id\" FROM \"sys\".\"args\" WHERE \"number\" = 1)" + // without any args " AND \"id\" IN (SELECT \"function_id\" FROM \"sys\".\"systemfunctions\")" + // only functions marked as system
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in @@ -131,9 +131,11 @@ public final class MonetDriver implement * getPropertyInfo method. * * @param url the URL of the database to which to connect - * @param info a proposed list of tag/value pairs that will be sent on connect open - * @return an array of DriverPropertyInfo objects describing possible properties. This array may be an empty array - * if no properties are required. + * @param info a proposed list of tag/value pairs that will be sent on + * connect open + * @return an array of DriverPropertyInfo objects describing possible + * properties. This array may be an empty array if no properties + * are required. */ @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { @@ -260,10 +262,11 @@ public final class MonetDriver implement //== end methods of interface driver + /** A static Map containing the mapping between MonetDB types and Java SQL types */ /* use SELECT sqlname, * FROM sys.types order by 1, id; to view all MonetDB types */ /* see http://docs.oracle.com/javase/7/docs/api/java/sql/Types.html to view all supported java SQL types */ - private static Map<String, Integer> typeMap = new HashMap<>(); + private static java.util.Map<String, Integer> typeMap = new java.util.HashMap<String, Integer>(); static { // fill the typeMap once // typeMap.put("any", Types.???); @@ -319,17 +322,17 @@ public final class MonetDriver implement return Types.OTHER; } - private static String TypeMapppingSQL = null; // cache to optimise getSQLTypeMap() - /** * Returns a String usable in an SQL statement to map the server types * to values of java.sql.Types using the global static type map. * The returned string will be a SQL CASE x statement where the x is - * replaced with the given currentColumns name (or expression) string. + * replaced with the given column name (or expression) string. * - * @param column a String representing the value that should be evaluated in the SQL CASE statement + * @param column a String representing the value that should be evaluated + * in the SQL CASE statement * @return a SQL CASE statement */ + private static String TypeMapppingSQL = null; // cache to optimise getSQLTypeMap() static String getSQLTypeMap(String column) { if (TypeMapppingSQL == null) { // first time, compose TypeMappping SQL string
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java @@ -48,8 +48,10 @@ import java.util.Map; * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira * @version 0.5v */ -public class MonetPreparedStatement extends MonetStatement implements PreparedStatement, AutoCloseable { - +public class MonetPreparedStatement + extends MonetStatement + implements PreparedStatement, AutoCloseable +{ private final MonetConnection connection; private final String[] monetdbType; private final int[] javaType; @@ -61,17 +63,26 @@ public class MonetPreparedStatement exte private final int id; private final int size; private final int rscolcnt; + private final String[] values; - private final SimpleDateFormat mTimestampZ; - private final SimpleDateFormat mTimestamp; - private final SimpleDateFormat mTimeZ; - private final SimpleDateFormat mTime; - private final SimpleDateFormat mDate; + /* placeholders for date/time pattern formats created once (only when needed), used multiple times */ + /** Format of a timestamp with RFC822 time zone */ + private SimpleDateFormat mTimestampZ; + /** Format of a timestamp */ + private SimpleDateFormat mTimestamp; + /** Format of a time with RFC822 time zone */ + private SimpleDateFormat mTimeZ; + /** Format of a time */ + private SimpleDateFormat mTime; + /** Format of a date used by mserver */ + private SimpleDateFormat mDate; /** - * MonetPreparedStatement constructor which checks the arguments for validity. A MonetPreparedStatement is backed - * by a {@link MonetStatement}, which deals with most of the required stuff of this class. + * MonetPreparedStatement constructor which checks the arguments for + * validity. A MonetPreparedStatement is backed by a + * {@link MonetStatement}, which deals with most of the required stuff of + * this class. * * @param connection the connection that created this Statement * @param resultSetType type of {@link ResultSet} to produce @@ -80,9 +91,20 @@ public class MonetPreparedStatement exte * @throws SQLException if an error occurs during login * @throws IllegalArgumentException is one of the arguments is null or empty */ - MonetPreparedStatement(MonetConnection connection, int resultSetType, int resultSetConcurrency, - int resultSetHoldability, String prepareQuery) throws SQLException, IllegalArgumentException { - super(connection, resultSetType, resultSetConcurrency, resultSetHoldability); + MonetPreparedStatement( + MonetConnection connection, + int resultSetType, + int resultSetConcurrency, + int resultSetHoldability, + String prepareQuery) + throws SQLException, IllegalArgumentException + { + super( + connection, + resultSetType, + resultSetConcurrency, + resultSetHoldability + ); if (!super.execute("PREPARE " + prepareQuery)) throw new SQLException("Unexpected server response", "M0M10"); @@ -149,10 +171,50 @@ public class MonetPreparedStatement exte mDate = connection.getProtocol().getMonetDate(); } + /** + * Constructs an empty MonetPreparedStatement. This constructor is + * in particular useful for extensions of this class. + * + * @param connection the connection that created this Statement + * @param resultSetType type of ResultSet to produce + * @param resultSetConcurrency concurrency of ResultSet to produce + * @throws SQLException if an error occurs during login + */ + /* Disabled this constructor code as it is not part of the JDBC interface + It may be enabled again when a subclass is constructed which needs it. + MonetPreparedStatement( + MonetConnection connection, + int resultSetType, + int resultSetConcurrency, + int resultSetHoldability) + throws SQLException + { + super( + connection, + resultSetType, + resultSetConcurrency, + resultSetHoldability + ); + // initialise blank finals + monetdbType = null; + javaType = null; + digits = null; + scale = null; + schema = null; + table = null; + column = null; + values = null; + id = -1; + size = -1; + rscolcnt = -1; + } + */ + //== methods interface PreparedStatement /** - * Adds a set of parameters to this PreparedStatement object's batch of commands. + * Adds a set of parameters to this PreparedStatement object's batch + * of commands. * * @throws SQLException if a database access error occurs */ @@ -195,9 +257,10 @@ public class MonetPreparedStatement exte * getUpdateCount to retrieve the result; you must call * getMoreResults to move to any subsequent result(s). * - * @return true if the first result is a ResultSet object; false if the first result is an update count or there is - * no result - * @throws SQLException if a database access error occurs or an argument is supplied to this method + * @return true if the first result is a ResultSet object; false if the + * first result is an update count or there is no result + * @throws SQLException if a database access error occurs or an argument + * is supplied to this method */ @Override public boolean execute() throws SQLException { @@ -211,49 +274,55 @@ public class MonetPreparedStatement exte } /** - * Executes the SQL query in this PreparedStatement object and returns the ResultSet object generated by the query. + * Executes the SQL query in this PreparedStatement object and returns the + * ResultSet object generated by the query. * - * @return a ResultSet object that contains the data produced by the query never null - * @throws SQLException if a database access error occurs or the SQL statement does not return a ResultSet object + * @return a ResultSet object that contains the data produced by the query; + * never null + * @throws SQLException if a database access error occurs or the SQL + * statement does not return a ResultSet object */ @Override public ResultSet executeQuery() throws SQLException { - if (!execute()) + if (execute() != true) throw new SQLException("Query did not produce a result set", "M1M19"); return getResultSet(); } - /** override the executeQuery from the Statement to throw an SQLException */ + /** override the executeQuery from the Statement to throw an SQLException*/ @Override public ResultSet executeQuery(String q) throws SQLException { throw new SQLException("This method is not available in a PreparedStatement!", "M1M05"); } /** - * Executes the SQL statement in this PreparedStatement object, which must be an SQL INSERT, UPDATE or DELETE - * statement; or an SQL statement that returns nothing, such as a DDL statement. + * Executes the SQL statement in this PreparedStatement object, which must + * be an SQL INSERT, UPDATE or DELETE statement; or an SQL statement that + * returns nothing, such as a DDL statement. * * @return either (1) the row count for INSERT, UPDATE, or DELETE * statements or (2) 0 for SQL statements that return nothing - * @throws SQLException if a database access error occurs or the SQL statement returns a ResultSet object + * @throws SQLException if a database access error occurs or the SQL + * statement returns a ResultSet object */ @Override public int executeUpdate() throws SQLException { - if (execute()) + if (execute() != false) throw new SQLException("Query produced a result set", "M1M17"); + return getUpdateCount(); } - /** override the executeUpdate from the Statement to throw an SQLException */ + /** override the executeUpdate from the Statement to throw an SQLException*/ @Override public int executeUpdate(String q) throws SQLException { throw new SQLException("This method is not available in a PreparedStatement!", "M1M05"); } /** - * Returns the index (0..size-1) in the backing arrays for the given resultset column number or an SQLException - * when not found + * Returns the index (0..size-1) in the backing arrays for the given + * resultset column number or an SQLException when not found */ private int getColumnIdx(int colnr) throws SQLException { int curcol = 0; @@ -267,7 +336,6 @@ public class MonetPreparedStatement exte } throw new SQLException("No such column with index: " + colnr, "M1M05"); } - /** * Returns the index (0..size-1) in the backing arrays for the given * parameter number or an SQLException when not found @@ -285,6 +353,7 @@ public class MonetPreparedStatement exte throw new SQLException("No such parameter with index: " + paramnr, "M1M05"); } + /* helper for the anonymous class inside getMetaData */ private abstract class rsmdw extends MonetWrapper implements ResultSetMetaData {} /** @@ -301,6 +370,7 @@ public class MonetPreparedStatement exte * * @return the description of a ResultSet object's columns or null if the * driver cannot return a ResultSetMetaData object + * @throws SQLException if a database access error occurs */ @Override public ResultSetMetaData getMetaData() throws SQLException { @@ -312,7 +382,7 @@ public class MonetPreparedStatement exte /** * Returns the number of columns in this ResultSet object. * - * @return the number of columns + * @returns the number of columns */ @Override public int getColumnCount() { @@ -339,7 +409,7 @@ public class MonetPreparedStatement exte * query call to pull the IS_AUTOINCREMENT value for this column. * See also ResultSetMetaData.isAutoIncrement() */ - // For now we simply always return false. + // For now we simply allways return false. return false; } @@ -347,28 +417,37 @@ public class MonetPreparedStatement exte * Indicates whether a column's case matters. * * @param column the first column is 1, the second is 2, ... - * @return if the column is case sensitive + * @returns false */ @Override public boolean isCaseSensitive(int column) throws SQLException { switch (getColumnType(column)) { + case Types.CHAR: + case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness case Types.CLOB: - case Types.CHAR: + return true; case Types.VARCHAR: - case Types.LONGVARCHAR: + String monettype = getColumnTypeName(column); + if (monettype != null) { + // data of type inet or uuid is not case sensitive + if ("inet".equals(monettype) + || "uuid".equals(monettype)) + return false; + } return true; - default: - return false; } + + return false; } /** - * Indicates whether the designated column can be used in a where clause. + * Indicates whether the designated column can be used in a + * where clause. * * Returning true for all here, even for CLOB, BLOB. * * @param column the first column is 1, the second is 2, ... - * @return true + * @returns true */ @Override public boolean isSearchable(int column) { @@ -376,12 +455,14 @@ public class MonetPreparedStatement exte } /** - * Indicates whether the designated column is a cash value. From the MonetDB database perspective it is by - * definition unknown whether the value is a currency, because there are no currency datatypes such as - * MONEY. With this knowledge we can always return false here. + * Indicates whether the designated column is a cash value. + * From the MonetDB database perspective it is by definition + * unknown whether the value is a currency, because there are + * no currency datatypes such as MONEY. With this knowledge + * we can always return false here. * * @param column the first column is 1, the second is 2, ... - * @return false + * @returns false */ @Override public boolean isCurrency(int column) { @@ -389,7 +470,8 @@ public class MonetPreparedStatement exte } /** - * Indicates whether values in the designated column are signed numbers. + * Indicates whether values in the designated column are signed + * numbers. * Within MonetDB all numeric types (except oid and ptr) are signed. * * @param column the first column is 1, the second is 2, ... @@ -399,14 +481,22 @@ public class MonetPreparedStatement exte public boolean isSigned(int column) throws SQLException { // we can hardcode this, based on the colum type switch (getColumnType(column)) { + case Types.NUMERIC: + case Types.DECIMAL: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.REAL: + case Types.FLOAT: case Types.DOUBLE: + return true; case Types.BIGINT: - case Types.NUMERIC: - case Types.DECIMAL: + String monettype = getColumnTypeName(column); + if (monettype != null) { + if ("oid".equals(monettype) + || "ptr".equals(monettype)) + return false; + } return true; default: return false; @@ -414,10 +504,12 @@ public class MonetPreparedStatement exte } /** - * Indicates the designated column's normal maximum width in characters. + * Indicates the designated column's normal maximum width in + * characters. * * @param column the first column is 1, the second is 2, ... - * @return the normal maximum number of characters allowed as the width of the designated column + * @return the normal maximum number of characters allowed as the + * width of the designated column * @throws SQLException if there is no such column */ @Override @@ -461,8 +553,10 @@ public class MonetPreparedStatement exte } /** - * Get the designated column's number of decimal digits. This method is currently very expensive as it - * needs to retrieve the information from the database using an SQL query. + * Get the designated column's number of decimal digits. + * This method is currently very expensive as it needs to + * retrieve the information from the database using an SQL + * query. * * @param column the first column is 1, the second is 2, ... * @return precision @@ -478,8 +572,10 @@ public class MonetPreparedStatement exte } /** - * Gets the designated column's number of digits to right of the decimal point. This method is currently - * very expensive as it needs to retrieve the information from the database using an SQL query. + * Gets the designated column's number of digits to right of + * the decimal point. This method is currently very + * expensive as it needs to retrieve the information from + * the database using an SQL query. * * @param column the first column is 1, the second is 2, ... * @return scale @@ -495,8 +591,10 @@ public class MonetPreparedStatement exte } /** - * Indicates the nullability of values in the designated column. This method is currently very expensive as - * it needs to retrieve the information from the database using an SQL query. + * Indicates the nullability of values in the designated + * column. This method is currently very expensive as it + * needs to retrieve the information from the database using + * an SQL query. * * @param column the first column is 1, the second is 2, ... * @return nullability @@ -512,7 +610,8 @@ public class MonetPreparedStatement exte * MonetDB does not support the catalog naming concept as in: catalog.schema.table naming scheme * * @param column the first column is 1, the second is 2, ... - * @return the name of the catalog for the table in which the given column appears or "" if not applicable + * @return the name of the catalog for the table in which the given + * column appears or "" if not applicable */ @Override public String getCatalogName(int column) throws SQLException { @@ -520,8 +619,9 @@ public class MonetPreparedStatement exte } /** - * Indicates whether the designated column is definitely not writable. MonetDB does not support cursor - * updates, so nothing is writable. + * Indicates whether the designated column is definitely not + * writable. MonetDB does not support cursor updates, so + * nothing is writable. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -532,7 +632,8 @@ public class MonetPreparedStatement exte } /** - * Indicates whether it is possible for a write on the designated column to succeed. + * Indicates whether it is possible for a write on the + * designated column to succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -543,7 +644,8 @@ public class MonetPreparedStatement exte } /** - * Indicates whether a write on the designated column will definitely succeed. + * Indicates whether a write on the designated column will + * definitely succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -554,14 +656,18 @@ public class MonetPreparedStatement exte } /** - * Returns the fully-qualified name of the Java class whose instances are manufactured if the method - * ResultSet.getObject is called to retrieve a value from the column. ResultSet.getObject may return a - * subclass of the class returned by this method. + * Returns the fully-qualified name of the Java class whose + * instances are manufactured if the method + * ResultSet.getObject is called to retrieve a value from + * the column. ResultSet.getObject may return a subclass of + * the class returned by this method. * * @param column the first column is 1, the second is 2, ... - * @return the fully-qualified name of the class in the Java programming language that would be used by the - * method ResultSet.getObject to retrieve the value in the specified column. This is the class name used - * for custom mapping. + * @return the fully-qualified name of the class in the Java + * programming language that would be used by the method + * ResultSet.getObject to retrieve the value in the + * specified column. This is the class name used for custom + * mapping. * @throws SQLException if there is no such column */ @Override @@ -578,8 +684,9 @@ public class MonetPreparedStatement exte } /** - * Gets the designated column's suggested title for use in printouts and displays. This is currently equal - * to getColumnName(). + * Gets the designated column's suggested title for use in + * printouts and displays. This is currently equal to + * getColumnName(). * * @param column the first column is 1, the second is 2, ... * @return the suggested column title @@ -593,7 +700,7 @@ public class MonetPreparedStatement exte /** * Gets the designated column's name * - * @param colnr the first column is 1, the second is 2, ... + * @param column the first column is 1, the second is 2, ... * @return the column name * @throws SQLException if there is no such column */ @@ -626,8 +733,9 @@ public class MonetPreparedStatement exte * Retrieves the designated column's database-specific type name. * * @param column the first column is 1, the second is 2, ... - * @return type name used by the database. If the column type is a user-defined type, then a - * fully-qualified type name is returned. + * @return type name used by the database. If the column type is a + * user-defined type, then a fully-qualified type name is + * returned. * @throws SQLException if there is no such column */ @Override @@ -644,17 +752,20 @@ public class MonetPreparedStatement exte /* helper class for the anonymous class in getParameterMetaData */ private abstract class pmdw extends MonetWrapper implements ParameterMetaData {} /** - * Retrieves the number, types and properties of this PreparedStatement object's parameters. + * Retrieves the number, types and properties of this + * PreparedStatement object's parameters. * - * @return a ParameterMetaData object that contains information about the number, types and properties of this - * PreparedStatement object's parameters + * @return a ParameterMetaData object that contains information + * about the number, types and properties of this + * PreparedStatement object's parameters * @throws SQLException if a database access error occurs */ @Override public ParameterMetaData getParameterMetaData() throws SQLException { return new pmdw() { /** - * Retrieves the number of parameters in the PreparedStatement object for which this ParameterMetaData + * Retrieves the number of parameters in the + * PreparedStatement object for which this ParameterMetaData * object contains information. * * @return the number of parameters @@ -672,13 +783,16 @@ public class MonetPreparedStatement exte } /** - * Retrieves whether null values are allowed in the designated parameter. + * Retrieves whether null values are allowed in the + * designated parameter. * * This is currently always unknown for MonetDB/SQL. * * @param param the first parameter is 1, the second is 2, ... - * @return the nullability status of the given parameter; one of ParameterMetaData.parameterNoNulls, - * ParameterMetaData.parameterNullable, or ParameterMetaData.parameterNullableUnknown + * @return the nullability status of the given parameter; + * one of ParameterMetaData.parameterNoNulls, + * ParameterMetaData.parameterNullable, or + * ParameterMetaData.parameterNullableUnknown * @throws SQLException if a database access error occurs */ @Override @@ -687,7 +801,8 @@ public class MonetPreparedStatement exte } /** - * Retrieves whether values for the designated parameter can be signed numbers. + * Retrieves whether values for the designated parameter can + * be signed numbers. * * @param param the first parameter is 1, the second is 2, ... * @return true if so; false otherwise @@ -695,7 +810,7 @@ public class MonetPreparedStatement exte */ @Override public boolean isSigned(int param) throws SQLException { - // we can hardcode this, based on the column type + // we can hardcode this, based on the colum type switch (getParameterType(param)) { case Types.TINYINT: case Types.SMALLINT: @@ -810,7 +925,7 @@ public class MonetPreparedStatement exte Map<String,Class<?>> map = getConnection().getTypeMap(); Class<?> c; if (map.containsKey(typeName)) { - c = map.get(typeName); + c = (Class)map.get(typeName); } else { c = MonetResultSet.getClassForType(getParameterType(param)); } @@ -870,7 +985,9 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + public void setAsciiStream(int parameterIndex, InputStream x) + throws SQLException + { throw newSQLFeatureNotSupportedException("setAsciiStream"); } @@ -893,7 +1010,9 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + public void setAsciiStream(int parameterIndex, InputStream x, int length) + throws SQLException + { throw newSQLFeatureNotSupportedException("setAsciiStream"); } @@ -917,7 +1036,9 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + public void setAsciiStream(int parameterIndex, InputStream x, long length) + throws SQLException + { throw newSQLFeatureNotSupportedException("setAsciiStream"); } @@ -940,8 +1061,7 @@ public class MonetPreparedStatement exte // if precision is now greater than that of the db, throw an error: if (x.precision() > digits[i]) { - throw new SQLDataException("DECIMAL value exceeds allowed digits/scale: " + x.toPlainString() + - " (" + digits[i] + "/" + scale[i] + ")", "22003"); + throw new SQLDataException("DECIMAL value exceeds allowed digits/scale: " + x.toPlainString() + " (" + digits[i] + "/" + scale[i] + ")", "22003"); } // MonetDB doesn't like leading 0's, since it counts them as part of @@ -974,7 +1094,9 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + public void setBinaryStream(int parameterIndex, InputStream x) + throws SQLException + { throw newSQLFeatureNotSupportedException("setBinaryStream"); } @@ -996,7 +1118,9 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + public void setBinaryStream(int parameterIndex, InputStream x, int length) + throws SQLException + { throw newSQLFeatureNotSupportedException("setBinaryStream"); } @@ -1018,7 +1142,9 @@ public class MonetPreparedStatement exte * not support this method */ @Override - public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + public void setBinaryStream(int parameterIndex, InputStream x, long length) + throws SQLException + { throw newSQLFeatureNotSupportedException("setBinaryStream"); } @@ -1029,6 +1155,8 @@ public class MonetPreparedStatement exte * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x a Blob object that maps an SQL BLOB value * @throws SQLException if a database access error occurs + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this method */ @Override public void setBlob(int parameterIndex, InputStream x) throws SQLException { @@ -1079,7 +1207,8 @@ public class MonetPreparedStatement exte * should be sent to the server as a LONGVARBINARY or a BLOB. * * @param parameterIndex the first parameter is 1, the second is 2, ... - * @param is an object that contains the data to set the parameter value to + * @param is an object that contains the data to set the parameter + * value to * @param length the number of bytes in the parameter data * @throws SQLException if a database access error occurs */ @@ -1128,7 +1257,7 @@ public class MonetPreparedStatement exte setValue(parameterIndex, Byte.toString(x)); } - private static final String HEXES = "0123456789ABCDEF"; + static final String HEXES = "0123456789ABCDEF"; /** * Sets the designated parameter to the given Java array of bytes. The * driver converts this to an SQL VARBINARY or LONGVARBINARY (depending @@ -1147,8 +1276,11 @@ public class MonetPreparedStatement exte } StringBuilder hex = new StringBuilder(x.length * 2); - for (byte aX : x) { - hex.append(HEXES.charAt((aX & 0xF0) >> 4)).append(HEXES.charAt((aX & 0x0F))); + byte b; + for (int i = 0; i < x.length; i++) { + b = x[i]; + hex.append(HEXES.charAt((b & 0xF0) >> 4)) + .append(HEXES.charAt((b & 0x0F))); } setValue(parameterIndex, "blob '" + hex.toString() + "'"); } @@ -1170,7 +1302,12 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + public void setCharacterStream( + int parameterIndex, + Reader reader, + int length) + throws SQLException + { if (reader == null) { setNull(parameterIndex, -1); return; @@ -1201,7 +1338,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + public void setCharacterStream(int parameterIndex, Reader reader) + throws SQLException + { setCharacterStream(parameterIndex, reader, 0); } @@ -1222,7 +1361,12 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + public void setCharacterStream( + int parameterIndex, + Reader reader, + long length) + throws SQLException + { // given the implementation of the int-version, downcast is ok setCharacterStream(parameterIndex, reader, (int)length); } @@ -1302,7 +1446,7 @@ public class MonetPreparedStatement exte } // simply serialise the CLOB into a variable for now... far from // efficient, but might work for a few cases... - CharBuffer buf = CharBuffer.allocate((int) length); // have to down cast :( + CharBuffer buf = CharBuffer.allocate((int)length); // have to down cast :( try { reader.read(buf); } catch (IOException e) { @@ -1324,7 +1468,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setDate(int parameterIndex, Date x) throws SQLException { + public void setDate(int parameterIndex, java.sql.Date x) + throws SQLException + { setDate(parameterIndex, x, null); } @@ -1343,7 +1489,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + public void setDate(int parameterIndex, java.sql.Date x, Calendar cal) + throws SQLException + { if (x == null) { setNull(parameterIndex, -1); return; @@ -1436,7 +1584,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + public void setNCharacterStream(int parameterIndex, Reader value, long length) + throws SQLException + { setCharacterStream(parameterIndex, value, length); } @@ -1551,7 +1701,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + public void setNull(int parameterIndex, int sqlType, String typeName) + throws SQLException + { // MonetDB/SQL's NULL needs no type setNull(parameterIndex, sqlType); } @@ -1600,7 +1752,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + public void setObject(int parameterIndex, Object x, int targetSqlType) + throws SQLException + { setObject(parameterIndex, x, targetSqlType, 0); } @@ -1640,11 +1794,18 @@ public class MonetPreparedStatement exte * @see Types */ @Override - public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException { + public void setObject( + int parameterIndex, + Object x, + int targetSqlType, + int scale) + throws SQLException + { if (x == null) { setNull(parameterIndex, -1); return; } + // this is according to table B-5 if (x instanceof String) { setString(parameterIndex, (String)x); @@ -1660,13 +1821,13 @@ public class MonetPreparedStatement exte switch (targetSqlType) { case Types.TINYINT: setByte(parameterIndex, num.byteValue()); - break; + break; case Types.SMALLINT: setShort(parameterIndex, num.shortValue()); - break; + break; case Types.INTEGER: setInt(parameterIndex, num.intValue()); - break; + break; case Types.BIGINT: if (x instanceof BigDecimal) { BigDecimal bd = (BigDecimal)x; @@ -1674,23 +1835,23 @@ public class MonetPreparedStatement exte } else { setLong(parameterIndex, num.longValue()); } - break; + break; case Types.REAL: setFloat(parameterIndex, num.floatValue()); - break; + break; case Types.FLOAT: case Types.DOUBLE: setDouble(parameterIndex, num.doubleValue()); - break; + break; case Types.DECIMAL: case Types.NUMERIC: if (x instanceof BigDecimal) { setBigDecimal(parameterIndex, (BigDecimal)x); } else { setBigDecimal(parameterIndex, - new BigDecimal(num.doubleValue())); + new BigDecimal(num.doubleValue())); } - break; + break; case Types.BIT: case Types.BOOLEAN: if (num.doubleValue() != 0.0) { @@ -1698,13 +1859,13 @@ public class MonetPreparedStatement exte } else { setBoolean(parameterIndex, false); } - break; + break; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: setString(parameterIndex, x.toString()); - break; + break; default: throw new SQLException("Conversion not allowed", "M1M05"); } @@ -1713,23 +1874,23 @@ public class MonetPreparedStatement exte switch (targetSqlType) { case Types.TINYINT: setByte(parameterIndex, (byte)(val ? 1 : 0)); - break; + break; case Types.SMALLINT: setShort(parameterIndex, (short)(val ? 1 : 0)); - break; + break; case Types.INTEGER: setInt(parameterIndex, (val ? 1 : 0)); // do not cast to (int) as it generates a compiler warning - break; + break; case Types.BIGINT: setLong(parameterIndex, (long)(val ? 1 : 0)); - break; + break; case Types.REAL: setFloat(parameterIndex, (float)(val ? 1.0 : 0.0)); - break; + break; case Types.FLOAT: case Types.DOUBLE: setDouble(parameterIndex, (val ? 1.0 : 0.0)); // do no cast to (double) as it generates a compiler warning - break; + break; case Types.DECIMAL: case Types.NUMERIC: { @@ -1744,13 +1905,13 @@ public class MonetPreparedStatement exte case Types.BIT: case Types.BOOLEAN: setBoolean(parameterIndex, val); - break; + break; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: setString(parameterIndex, x.toString()); - break; + break; default: throw new SQLException("Conversion not allowed", "M1M05"); } @@ -1759,7 +1920,7 @@ public class MonetPreparedStatement exte switch (targetSqlType) { case Types.BIGINT: setLong(parameterIndex, num.longValue()); - break; + break; case Types.DECIMAL: case Types.NUMERIC: { @@ -1776,7 +1937,7 @@ public class MonetPreparedStatement exte case Types.LONGVARCHAR: case Types.CLOB: setString(parameterIndex, x.toString()); - break; + break; default: throw new SQLException("Conversion not allowed", "M1M05"); } @@ -1786,7 +1947,7 @@ public class MonetPreparedStatement exte case Types.VARBINARY: case Types.LONGVARBINARY: setBytes(parameterIndex, (byte[])x); - break; + break; default: throw new SQLException("Conversion not allowed", "M1M05"); } @@ -1883,8 +2044,8 @@ public class MonetPreparedStatement exte // with the actual sqltype the server expects, or we // will get an error back setValue( - paramnr, - sqltype + " '" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'" + paramnr, + sqltype + " '" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'" ); } @@ -2085,6 +2246,7 @@ public class MonetPreparedStatement exte setNull(parameterIndex, -1); return; } + int paramIdx = getParamIdx(parameterIndex); // this will throw a SQLException if parameter can not be found /* depending on the parameter data type (as expected by MonetDB) we @@ -2292,7 +2454,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + public void setTime(int parameterIndex, Time x, Calendar cal) + throws SQLException + { if (x == null) { setNull(parameterIndex, -1); return; @@ -2304,7 +2468,8 @@ public class MonetPreparedStatement exte // timezone shouldn't matter, since the server is timezone // aware in this case String RFC822 = mTimeZ.format(x); - setValue(parameterIndex, "timetz '" + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'"); + setValue(parameterIndex, "timetz '" + + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'"); } else { // server is not timezone aware for this field, and no // calendar given, since we told the server our timezone at @@ -2329,7 +2494,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + public void setTimestamp(int parameterIndex, Timestamp x) + throws SQLException + { setTimestamp(parameterIndex, x, null); } @@ -2350,7 +2517,9 @@ public class MonetPreparedStatement exte * @throws SQLException if a database access error occurs */ @Override - public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) + throws SQLException + { if (x == null) { setNull(parameterIndex, -1); return; @@ -2362,7 +2531,8 @@ public class MonetPreparedStatement exte // timezone shouldn't matter, since the server is timezone // aware in this case String RFC822 = mTimestampZ.format(x); - setValue(parameterIndex, "timestamptz '" + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'"); + setValue(parameterIndex, "timestamptz '" + + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'"); } else { // server is not timezone aware for this field, and no // calendar given, since we told the server our timezone at @@ -2400,7 +2570,9 @@ public class MonetPreparedStatement exte */ @Override @Deprecated - public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + public void setUnicodeStream(int parameterIndex, InputStream x, int length) + throws SQLException + { throw newSQLFeatureNotSupportedException("setUnicodeStream"); } @@ -2421,8 +2593,9 @@ public class MonetPreparedStatement exte } String val = x.toString(); - setValue(parameterIndex, "url '" + val.replaceAll("\\\\", "\\\\\\\\") - .replaceAll("'", "\\\\'") + "'"); + setValue(parameterIndex, + "url '" + val.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'" + ); } /** @@ -2451,7 +2624,8 @@ public class MonetPreparedStatement exte } /** - * Call close to release the server-sided handle for this PreparedStatement. + * Call close to release the server-sided handle for this + * PreparedStatement. */ @Override protected void finalize() { @@ -2461,8 +2635,9 @@ public class MonetPreparedStatement exte //== end methods interface PreparedStatement /** - * Sets the given index with the supplied value. If the given index is out of bounds, and SQLException is thrown. - * The given value should never be null. + * Sets the given index with the supplied value. If the given index is + * out of bounds, and SQLException is thrown. The given value should + * never be null. * * @param parameterIndex the parameter index * @param val the exact String representation to set @@ -2483,7 +2658,7 @@ public class MonetPreparedStatement exte */ private String transform() throws SQLException { StringBuilder buf = new StringBuilder(8 + 12 * size); - buf.append("execute ").append(id).append('('); + buf.append("exec ").append(id).append('('); // check if all columns are set and do a replace int col = 0; for (int i = 0; i < size; i++) {
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java @@ -44,8 +44,10 @@ import java.util.UUID; * @author Fabian Groffen, Martin van Dinther, Pedro Ferreira * @version 0.8 */ -public class MonetResultSet extends MonetWrapper implements ResultSet, AutoCloseable { - +public class MonetResultSet + extends MonetWrapper + implements ResultSet, AutoCloseable +{ /** The current position of the cursor for this ResultSet object */ int curRow = 0; // a blank final is immutable once assigned in the constructor @@ -78,6 +80,7 @@ public class MonetResultSet extends Mone * @param statement the statement which created this ResultSet * @param header a header containing the query, resultset type, etc. * @throws IllegalArgumentException if called with null or invalid value for one of the arguments + * @throws SQLException is a protocol error occurs */ MonetResultSet(Statement statement, ResultSetResponse header) throws IllegalArgumentException { if (statement == null) { @@ -109,6 +112,8 @@ public class MonetResultSet extends Mone * @param types the column types * @param results the number of rows in the ResultSet * @throws IllegalArgumentException if called with null or invalid value for one of the arguments + * @throws IOException if communicating with monet failed + * @throws SQLException is a protocol error occurs */ MonetResultSet(Statement statement, String[] columns, String[] types, int[] JdbcSQLTypes, int results) throws IllegalArgumentException { @@ -124,6 +129,7 @@ public class MonetResultSet extends Mone if (results < 0) { throw new IllegalArgumentException("Negative rowcount not allowed!"); } + this.statement = statement; this.header = null; this.fetchSize = 0; @@ -158,35 +164,41 @@ public class MonetResultSet extends Mone } } + //== methods of interface ResultSet // Chapter 14.2.2 Sun JDBC 3.0 Specification - /** * Moves the cursor to the given row number in this ResultSet object. * - * If the row number is positive, the cursor moves to the given row number with respect to the beginning of the - * result set. The first row is row 1, the second is row 2, and so on. + * If the row number is positive, the cursor moves to the given row number + * with respect to the beginning of the result set. The first row is row 1, + * the second is row 2, and so on. * - * If the given row number is negative, the cursor moves to an absolute row position with respect to the end of the - * result set. For example, calling the method absolute(-1) positions the cursor on the last row; calling the + * If the given row number is negative, the cursor moves to an absolute row + * position with respect to the end of the result set. For example, calling + * the method absolute(-1) positions the cursor on the last row; calling the * method absolute(-2) moves the cursor to the next-to-last row, and so on. * - * An attempt to position the cursor beyond the first/last row in the result set leaves the cursor before the first - * row or after the last row. - * Note: calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as calling last(). + * An attempt to position the cursor beyond the first/last row in the result + * set leaves the cursor before the first row or after the last row. + * Note: calling absolute(1) is the same as calling first(). Calling + * absolute(-1) is the same as calling last(). * - * @param row the number of the row to which the cursor should move. A positive number indicates the row number - * counting from the beginning of the result set; a negative number indicates the row number counting from - * the end of the result set + * @param row the number of the row to which the cursor should move. A + * positive number indicates the row number counting from the + * beginning of the result set; a negative number indicates the row + * number counting from the end of the result set * @return true if the cursor is on the result set; false otherwise - * @throws SQLException if a database access error occurs, or the result set type is TYPE_FORWARD_ONLY + * @throws SQLException if a database access error occurs, or the result set + * type is TYPE_FORWARD_ONLY */ @Override public boolean absolute(int row) throws SQLException { checkNotClosed(); if (row != curRow + 1 && type == TYPE_FORWARD_ONLY) - throw new SQLException("(Absolute) positioning not allowed on forward only result sets!", "M1M05"); + throw new SQLException("(Absolute) positioning not allowed on forward " + + " only result sets!", "M1M05"); // first calculate what the JDBC row is if (row < 0) { @@ -199,7 +211,9 @@ public class MonetResultSet extends Mone else if (row > tupleCount + 1) row = tupleCount + 1; // after last - this.curRow = row; + // store it + curRow = row; + boolean overlap = row <= this.tupleCount; if(overlap) this.currentBlock = header.getDataBlockCorrespondingToLine(row - 1); @@ -207,10 +221,11 @@ public class MonetResultSet extends Mone } /** - * Moves the cursor to the end of this ResultSet object, just after the last row. This method has no effect if the - * result set contains no rows. + * Moves the cursor to the end of this ResultSet object, just after the last + * row. This method has no effect if the result set contains no rows. * - * @throws SQLException if a database access error occurs or the result set type is TYPE_FORWARD_ONLY + * @throws SQLException if a database access error occurs or the result set + * type is TYPE_FORWARD_ONLY */ @Override public void afterLast() throws SQLException { @@ -218,10 +233,11 @@ public class MonetResultSet extends Mone } /** - * Moves the cursor to the front of this ResultSet object, just before the first row. This method has no effect - * if the result set contains no rows. + * Moves the cursor to the front of this ResultSet object, just before the + * first row. This method has no effect if the result set contains no rows. * - * @throws SQLException if a database access error occurs or the result set type is TYPE_FORWARD_ONLY + * @throws SQLException if a database access error occurs or the result set + * type is TYPE_FORWARD_ONLY */ @Override public void beforeFirst() throws SQLException { @@ -229,8 +245,9 @@ public class MonetResultSet extends Mone } /** - * Clears all warnings reported for this ResultSet object. After a call to this method, the method getWarnings - * returns null until a new warning is reported for this ResultSet object. + * Clears all warnings reported for this ResultSet object. After a call to + * this method, the method getWarnings returns null until a new warning is + * reported for this ResultSet object. */ @Override public void clearWarnings() { @@ -238,24 +255,24 @@ public class MonetResultSet extends Mone } /** - * Releases this ResultSet object's database (and JDBC) resources immediately instead of waiting for this to happen - * when it is automatically closed. + * Releases this ResultSet object's database (and JDBC) resources + * immediately instead of waiting for this to happen when it is + * automatically closed. */ @Override public void close() { if (header != null && !header.isClosed()) { header.close(); } - if (statement instanceof MonetStatement) { + if (statement instanceof MonetStatement) ((MonetStatement)statement).closeIfCompletion(); - } } // Chapter 14.2.3 from Sun JDBC 3.0 specification - /** - * Maps the given ResultSet column name to its ResultSet column index. Column names supplied to getter methods are - * case insensitive. If a select list contains the same column more than once, the first instance of the + * Maps the given ResultSet column name to its ResultSet column index. + * Column names supplied to getter methods are case insensitive. If a select + * list contains the same column more than once, the first instance of the * column will be returned. * * @param columnLabel the name of the column @@ -284,8 +301,10 @@ public class MonetResultSet extends Mone /** * Moves the cursor to the first row in this ResultSet object. * - * @return true if the cursor is on a valid row; false if there are no rows in the result set - * @throws SQLException - if a database access error occurs or the result set type is TYPE_FORWARD_ONLY + * @return true if the cursor is on a valid row; false if there are no rows + * in the result set + * @throws SQLException - if a database access error occurs or the result + * set type is TYPE_FORWARD_ONLY */ @Override public boolean first() throws SQLException { @@ -296,12 +315,12 @@ public class MonetResultSet extends Mone public Array getArray(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getArray"); } - @Override public Array getArray(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getArray"); } + /* Mapi doesn't allow something for streams at the moment, thus all not implemented for now */ @Override public InputStream getAsciiStream(int columnIndex) throws SQLException { try { @@ -334,7 +353,6 @@ public class MonetResultSet extends Mone throw newSQLInvalidColumnIndexException(columnIndex); } } - @Override public InputStream getAsciiStream(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getAsciiStream"); @@ -345,7 +363,6 @@ public class MonetResultSet extends Mone public InputStream getUnicodeStream(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getUnicodeStream"); } - @Override @Deprecated public InputStream getUnicodeStream(String columnLabel) throws SQLException { @@ -428,8 +445,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader - * object. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a java.io.Reader object. * * @param columnIndex the first column is 1, the second is 2, ... * @return a java.io.Reader object that contains the column value; @@ -453,12 +470,13 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader - * object. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a java.io.Reader object. * * @param columnLabel the name of the column - * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is - * null in the Java programming language. + * @return a java.io.Reader object that contains the column value; + * if the value is SQL NULL, the value returned is null in + * the Java programming language. * @throws SQLException if a database access error occurs */ @Override @@ -467,12 +485,15 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader - * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a java.io.Reader object. It is + * intended for use when accessing NCHAR,NVARCHAR and LONGNVARCHAR + * columns. * * @param columnIndex the first column is 1, the second is 2, ... - * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is - * null in the Java programming language. + * @return a java.io.Reader object that contains the column value; + * if the value is SQL NULL, the value returned is null in + * the Java programming language. * @throws SQLException if a database access error occurs */ @Override @@ -481,12 +502,15 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader - * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a java.io.Reader object. It is + * intended for use when accessing NCHAR,NVARCHAR and LONGNVARCHAR + * columns. * * @param columnLabel the name of the column - * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is - * null in the Java programming language. + * @return a java.io.Reader object that contains the column value; + * if the value is SQL NULL, the value returned is null in + * the Java programming language. * @throws SQLException if a database access error occurs */ @Override @@ -495,8 +519,9 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a Blob object in - * the Java programming language. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a Blob object in the Java programming + * language. * * @param columnIndex the first column is 1, the second is 2, ... * @return a Blob object representing the SQL BLOB value in the @@ -518,11 +543,14 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a Blob object in the - * Java programming language. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a Blob object in the Java programming + * language. * - * @param columnLabel the name of the column from which to retrieve the value - * @return a Blob object representing the SQL BLOB value in the specified column + * @param columnLabel the name of the column from which to retrieve + * the value + * @return a Blob object representing the SQL BLOB value in the + * specified column * @throws SQLException if a database access error occurs */ @Override @@ -531,7 +559,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a Clob object in the + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a Clob object in the * Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... @@ -554,11 +583,14 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a Clob object in the + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a Clob object in the * Java programming language. * - * @param columnLabel the name of the column from which to retrieve the value - * @return a Clob object representing the SQL CLOB value in the specified column + * @param columnLabel the name of the column from which to retrieve + * the value + * @return a Clob object representing the SQL CLOB value in the + * specified column * @throws SQLException if a database access error occurs */ @Override @@ -567,13 +599,16 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a NClob object in the + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a NClob object in the * Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... - * @return a NClob object representing the SQL NCLOB value in the specified column + * @return a NClob object representing the SQL NCLOB value in the + * specified column * @throws SQLException if a database access error occurs - * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this method */ @Override public NClob getNClob(int columnIndex) throws SQLException { @@ -581,13 +616,17 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a NClob object in the + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a NClob object in the * Java programming language. * - * @param columnLabel the name of the column from which to retrieve the value - * @return a NClob object representing the SQL NCLOB value in the specified column + * @param columnLabel the name of the column from which to retrieve + * the value + * @return a NClob object representing the SQL NCLOB value in the + * specified column * @throws SQLException if a database access error occurs - * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this method */ @Override public NClob getNClob(String columnLabel) throws SQLException { @@ -706,14 +745,16 @@ public class MonetResultSet extends Mone */ @Override @Deprecated - public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + public BigDecimal getBigDecimal(String columnLabel, int scale) + throws SQLException + { return getBigDecimal(findColumn(columnLabel), scale); } // See Sun JDBC Specification 3.0 Table B-6 /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a boolean in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a boolean in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned @@ -774,11 +815,12 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a boolean in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a boolean in the Java programming language. * * @param columnLabel the SQL name of the column - * @return the column value; if the value is SQL NULL, the value returned is false + * @return the column value; if the value is SQL NULL, the value returned + * is false * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override @@ -787,8 +829,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a byte in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a byte in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned @@ -901,11 +943,18 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a byte array in the - * Java programming language. The bytes represent the raw values returned by the driver. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a byte array in the Java programming language. The + * bytes represent the raw values returned by the driver. + * + * NOTE: Since the mapi protocol is ASCII-based, this method only returns + * Java byte representations of Strings, which is nothing more than + * an encoding into a sequence of bytes using the platform's default + * charset. * * @param columnLabel the SQL name of the column - * @return the column value; if the value is SQL NULL, the value returned is null + * @return the column value; if the value is SQL NULL, the value returned + * is null * @throws SQLException if a database access error occurs */ @Override @@ -914,12 +963,14 @@ public class MonetResultSet extends Mone } /** - * Retrieves the concurrency mode of this ResultSet object. The concurrency used is determined by the Statement - * object that created the result set. + * Retrieves the concurrency mode of this ResultSet object. The concurrency + * used is determined by the Statement object that created the result set. * - * NOTE: MonetDB only supports read-only result sets, and will always return ResultSet.CONCUR_READ_ONLY + * NOTE: MonetDB only supports read-only result sets, and will always return + * ResultSet.CONCUR_READ_ONLY * - * @return the concurrency type, either ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE + * @return the concurrency type, either ResultSet.CONCUR_READ_ONLY or + * ResultSet.CONCUR_UPDATABLE */ @Override public int getConcurrency() { @@ -940,8 +991,9 @@ public class MonetResultSet extends Mone * cursor used by a ResultSet object. The current row of a ResultSet object * is also the current row of this SQL cursor. * - * Note: If positioned update is not supported, a SQLException is thrown. MonetDB currently doesn't support updates, - * so the SQLException is thrown for now. + * Note: If positioned update is not supported, a SQLException is thrown. + * MonetDB currently doesn't support updates, so the SQLException is + * thrown for now. * * @return the SQL name for this ResultSet object's cursor * @throws SQLException if a database access error occurs @@ -1027,7 +1079,8 @@ public class MonetResultSet extends Mone /** * Retrieves the holdability of this ResultSet object. * - * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT + * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT * @throws SQLException if a database access error occurs */ @Override @@ -1057,13 +1110,13 @@ public class MonetResultSet extends Mone @Override public void setFetchDirection(int direction) throws SQLException { switch (direction) { - case ResultSet.FETCH_FORWARD: - break; - case ResultSet.FETCH_REVERSE: - case ResultSet.FETCH_UNKNOWN: - throw new SQLException("Not supported direction " + direction, "0A000"); - default: - throw new SQLException("Illegal direction: " + direction, "M1M05"); + case ResultSet.FETCH_FORWARD: + break; + case ResultSet.FETCH_REVERSE: + case ResultSet.FETCH_UNKNOWN: + throw new SQLException("Not supported direction " + direction, "0A000"); + default: + throw new SQLException("Illegal direction: " + direction, "M1M05"); } } @@ -1161,8 +1214,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a float in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a float in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 @@ -1174,8 +1227,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as an int in the - * Java programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as an int in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 @@ -1248,8 +1301,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as an int in the - * Java programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as an int in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 @@ -1261,8 +1314,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a long in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a long in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 @@ -1335,8 +1388,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a long in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a long in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 @@ -1347,16 +1400,17 @@ public class MonetResultSet extends Mone return getLong(findColumn(columnLabel)); } + /* helper for the anonymous class inside getMetaData */ private abstract class rsmdw extends MonetWrapper implements ResultSetMetaData {} - /** - * Retrieves the number, types and properties of this ResultSet object's columns. + * Retrieves the number, types and properties of this ResultSet object's + * columns. * * @return the description of this ResultSet object's columns */ @Override - public ResultSetMetaData getMetaData() { + public ResultSetMetaData getMetaData() throws SQLException { // return inner class which implements the ResultSetMetaData interface return new rsmdw() { // for the more expensive methods (getPrecision(), getScale(), isNullable()), we provide a simple cache @@ -1367,12 +1421,12 @@ public class MonetResultSet extends Mone private final int[] _scale = new int[array_size]; private final int[] _isNullable = new int[array_size]; private final boolean[] _isAutoincrement = new boolean[array_size]; - private Connection conn = null; - private DatabaseMetaData dbmd = null; + private final Connection conn = getStatement().getConnection(); + private final DatabaseMetaData dbmd = conn.getMetaData(); /** * A private utility method to check validity of column index number - * @throws SQLDataException when invalid column index number + * @throws an SQLDataException when invalid column index number */ private void checkColumnIndexValidity(int column) throws SQLDataException { if (column < 1 || column > columns.length) @@ -1402,14 +1456,6 @@ public class MonetResultSet extends Mone if (tblName != null && !tblName.isEmpty()) { String colName = getColumnName(column); if (colName != null && !colName.isEmpty()) { - if (conn == null) { - // first time, get a Connection object and cache it for all next columns - conn = getStatement().getConnection(); - } - if (conn != null && dbmd == null) { - // first time, get a MetaData object and cache it for all next columns - dbmd = conn.getMetaData(); - } if (dbmd != null) { // for precision, scale, isNullable and isAutoincrement we query the information from data dictionary ResultSet colInfo = dbmd.getColumns(null, schName, tblName, colName); @@ -1434,7 +1480,7 @@ public class MonetResultSet extends Mone /** * Returns the number of columns in this ResultSet object. * - * @return the number of columns + * @returns the number of columns */ @Override public int getColumnCount() { @@ -1452,7 +1498,7 @@ public class MonetResultSet extends Mone public boolean isAutoIncrement(int column) throws SQLException { checkColumnIndexValidity(column); try { - if (!_is_fetched[column]) { + if (_is_fetched[column] != true) { fetchColumnInfo(column); } return _isAutoincrement[column]; @@ -1465,7 +1511,7 @@ public class MonetResultSet extends Mone * Indicates whether a column's case matters. * * @param column the first column is 1, the second is 2, ... - * @return true for all character string columns else false + * @returns true for all character string columns else false */ @Override public boolean isCaseSensitive(int column) throws SQLException { @@ -1490,7 +1536,7 @@ public class MonetResultSet extends Mone * real column existing in a table or not... * * @param column the first column is 1, the second is 2, ... - * @return true + * @returns true */ @Override public boolean isSearchable(int column) throws SQLException { @@ -1506,7 +1552,7 @@ public class MonetResultSet extends Mone * we can always return false here. * * @param column the first column is 1, the second is 2, ... - * @return false + * @returns false */ @Override public boolean isCurrency(int column) throws SQLException { @@ -1515,7 +1561,8 @@ public class MonetResultSet extends Mone } /** - * Indicates whether values in the designated column are signed numbers. + * Indicates whether values in the designated column are signed + * numbers. * Within MonetDB all numeric types (except oid and ptr) are signed. * * @param column the first column is 1, the second is 2, ... @@ -1523,6 +1570,7 @@ public class MonetResultSet extends Mone */ @Override public boolean isSigned(int column) throws SQLException { + // we can hardcode this, based on the colum type switch (getColumnType(column)) { case Types.TINYINT: case Types.SMALLINT: @@ -1539,10 +1587,12 @@ public class MonetResultSet extends Mone } /** - * Indicates the designated column's normal maximum width in characters. + * Indicates the designated column's normal maximum width in + * characters. * * @param column the first column is 1, the second is 2, ... - * @return the normal maximum number of characters allowed as the width of the designated column + * @return the normal maximum number of characters allowed as the + * width of the designated column * @throws SQLException if there is no such column */ @Override @@ -1609,8 +1659,10 @@ public class MonetResultSet extends Mone } /** - * Get the designated column's number of decimal digits. This method is currently very expensive as it needs - * to retrieve the information from the database using an SQL query. + * Get the designated column's number of decimal digits. + * This method is currently very expensive as it needs to + * retrieve the information from the database using an SQL + * query. * * @param column the first column is 1, the second is 2, ... * @return precision @@ -1620,12 +1672,12 @@ public class MonetResultSet extends Mone public int getPrecision(int column) throws SQLException { checkColumnIndexValidity(column); try { - if (!_is_fetched[column]) { + if (_is_fetched[column] != true) { fetchColumnInfo(column); } if (_precision[column] == 0) { - // apparently no precision could be fetched use columnDisplaySize() value for variable - // length data types + // apparently no precision could be fetched + // use columnDisplaySize() value for variable length data types switch (getColumnType(column)) { case Types.CHAR: case Types.VARCHAR: @@ -1694,7 +1746,7 @@ public class MonetResultSet extends Mone public int getScale(int column) throws SQLException { checkColumnIndexValidity(column); try { - if (!_is_fetched[column]) { + if (_is_fetched[column] != true) { fetchColumnInfo(column); } return _scale[column]; @@ -1710,15 +1762,14 @@ public class MonetResultSet extends Mone * an SQL query. * * @param column the first column is 1, the second is 2, ... - * @return the nullability status of the given column; one of columnNoNulls, columnNullable or - * columnNullableUnknown + * @return the nullability status of the given column; one of columnNoNulls, columnNullable or columnNullableUnknown * @throws SQLException if a database access error occurs */ @Override public int isNullable(int column) throws SQLException { checkColumnIndexValidity(column); try { - if (!_is_fetched[column]) { + if (_is_fetched[column] != true) { fetchColumnInfo(column); } return _isNullable[column]; @@ -1739,11 +1790,13 @@ public class MonetResultSet extends Mone public String getCatalogName(int column) throws SQLException { checkColumnIndexValidity(column); return null; // MonetDB does NOT support catalogs + } /** - * Indicates whether the designated column is definitely not writable. MonetDB does not support - * cursor updates, so nothing is writable. + * Indicates whether the designated column is definitely not + * writable. MonetDB does not support cursor updates, so + * nothing is writable. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -1755,7 +1808,8 @@ public class MonetResultSet extends Mone } /** - * Indicates whether it is possible for a write on the designated column to succeed. + * Indicates whether it is possible for a write on the + * designated column to succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -1767,7 +1821,8 @@ public class MonetResultSet extends Mone } /** - * Indicates whether a write on the designated column will definitely succeed. + * Indicates whether a write on the designated column will + * definitely succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -1796,17 +1851,13 @@ public class MonetResultSet extends Mone @Override public String getColumnClassName(int column) throws SQLException { checkColumnIndexValidity(column); - if (conn == null) { - // first time, get a Connection object and cache it for all next columns - conn = getStatement().getConnection(); - } try { String MonetDBType = types[column - 1]; Class<?> type = null; if (conn != null) { Map<String,Class<?>> map = conn.getTypeMap(); if (map != null && map.containsKey(MonetDBType)) { - type = map.get(MonetDBType); + type = (Class)map.get(MonetDBType); } } if (type == null) { @@ -1823,8 +1874,9 @@ public class MonetResultSet extends Mone } /** - * Gets the designated column's suggested title for use in printouts and displays. This is currently equal - * to getColumnName(). + * Gets the designated column's suggested title for use in + * printouts and displays. This is currently equal to + * getColumnName(). * * @param column the first column is 1, the second is 2, ... * @return the suggested column title @@ -1836,7 +1888,7 @@ public class MonetResultSet extends Mone } /** - * Gets the designated column's name. + * Gets the designated column's name * * @param column the first column is 1, the second is 2, ... * @return the column name @@ -1873,8 +1925,9 @@ public class MonetResultSet extends Mone * Retrieves the designated column's database-specific type name. * * @param column the first column is 1, the second is 2, ... - * @return type name used by the database. If the column type is a user-defined type, then a - * fully-qualified type name is returned. + * @return type name used by the database. If the column type is a + * user-defined type, then a fully-qualified type name is + * returned. * @throws SQLException if there is no such column */ @Override @@ -1904,7 +1957,7 @@ public class MonetResultSet extends Mone * types. In the JDBC 2.0 API, the behavior of method getObject is extended * to materialize data of SQL user-defined types. When a column contains a * structured or distinct value, the behavior of this method is as if it - * were a call to: getObject(columnIndex, this.getStatement().getInternalConnection().getTypeMap()). + * were a call to: getObject(columnIndex, this.getStatement().getConnection().getTypeMap()). * * @param columnIndex the first column is 1, the second is 2, ... * @return a java.lang.Object holding the column value or null @@ -1915,6 +1968,7 @@ public class MonetResultSet extends Mone // Many generic JDBC programs use getObject(colnr) to retrieve value objects from a resultset // For speed the implementation should be as fast as possible, so avoid method calls (by inlining code) where possible checkNotClosed(); + final int JdbcType; try { JdbcType = JdbcSQLTypes[columnIndex - 1]; @@ -2003,8 +2057,8 @@ public class MonetResultSet extends Mone private boolean classImplementsSQLData(Class<?> cl) { Class<?>[] cls = cl.getInterfaces(); - for (Class<?> cl1 : cls) { - if (cl1 == SQLData.class) + for (int i = 0; i < cls.length; i++) { + if (cls[i] == SQLData.class) return true; } return false; @@ -2261,7 +2315,8 @@ public class MonetResultSet extends Mone * Additional conversions may be supported and are vendor defined. * * @param columnIndex the first column is 1, the second is 2, ... - * @param type Class representing the Java data type to convert the designated column to + * @param type Class representing the Java data type to convert the + * designated column to * @return an instance of type holding the column value * @throws SQLException if conversion is not supported, type is * null or another error occurs. The getCause() method of @@ -2290,9 +2345,11 @@ public class MonetResultSet extends Mone * supported. If the conversion is not supported or null is * specified for the type, a SQLException is thrown. * - * @param columnLabel the label for the column specified with the SQL AS clause. If the SQL AS clause was not - * specified, then the label is the name of the column - * @param type Class representing the Java data type to convert the designated column to + * @param columnLabel the label for the column specified with the + * SQL AS clause. If the SQL AS clause was not specified, + * then the label is the name of the column + * @param type Class representing the Java data type to convert the + * designated column to * @return an instance of type holding the column value * @throws SQLException if conversion is not supported, type is * null or another error occurs. The getCause() method of @@ -2307,16 +2364,19 @@ public class MonetResultSet extends Mone } /** - * Helper method to support the getObject and ResultsetMetaData.getColumnClassName JDBC methods. + * Helper method to support the getObject and + * ResultsetMetaData.getColumnClassName JDBC methods. * * @param type a value from java.sql.Types * @return a Class object from which an instance would be returned */ static Class<?> getClassForType(int type) { /** - * This switch returns the types as objects according to table B-3 from Oracle's JDBC specification 4.1 + * This switch returns the types as objects according to table B-3 from + * Oracle's JDBC specification 4.1 */ - switch(type) { // keep this switch regarding the returned classes aligned with getObject(int, Map) ! + // keep this switch regarding the returned classes aligned with getObject(int, Map) ! + switch(type) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: @@ -2367,7 +2427,8 @@ public class MonetResultSet extends Mone * built-in types specified in the JDBC specification. If the value is an * SQL NULL, the driver returns a Java null. * - * This method may also be used to read database-specific abstract data types. + * This method may also be used to read database-specific abstract data + * types. * * @param columnLabel the SQL name of the column * @return a java.lang.Object holding the column value @@ -2406,7 +2467,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the current row number. The first row is number 1, the second number 2, and so on. + * Retrieves the current row number. The first row is number 1, the second + * number 2, and so on. * * @return the current row number; 0 if there is no current row */ @@ -2421,9 +2483,11 @@ public class MonetResultSet extends Mone * programming language. * * @param columnIndex the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the value returned is null + * @return the column value; if the value is SQL NULL, the value returned + * is null * @throws SQLException if there is no such column - * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this method */ @Override public RowId getRowId(int columnIndex) throws SQLException { @@ -2431,13 +2495,16 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.RowId - * object in the Java programming language. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a java.sql.RowId object in the Java + * programming language. * * @param columnLabel the SQL name of the column - * @return the column value; if the value is SQL NULL, the value returned is null + * @return the column value; if the value is SQL NULL, the value returned + * is null * @throws SQLException if the ResultSet object does not contain columnLabel - * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this method */ @Override public RowId getRowId(String columnLabel) throws SQLException { @@ -2445,8 +2512,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a short in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a short in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 @@ -2504,8 +2571,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a short in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a short in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 @@ -2530,8 +2597,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a String in the Java - * programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a String in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is null @@ -2595,13 +2662,15 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet as a java.sql.SQLXML object - * in the Java programming language. + * Retrieves the value of the designated column in the current row + * of this ResultSet as a java.sql.SQLXML object in the Java + * programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return a SQLXML object that maps an SQL XML value * @throws SQLException if a database access error occurs - * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this method */ @Override public SQLXML getSQLXML(int columnIndex) throws SQLException { @@ -2609,14 +2678,17 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet as a java.sql.SQLXML object - * in the Java programming language. + * Retrieves the value of the designated column in the current row + * of this ResultSet as a java.sql.SQLXML object in the Java + * programming language. * - * @param columnLabel the label for the column specified with the SQL AS clause. If the SQL AS clause was not - * specified, then the label is the name of the column + * @param columnLabel the label for the column specified with the SQL AS + * clause. If the SQL AS clause was not specified, then the + * label is the name of the column * @return a SQLXML object that maps an SQL XML value * @throws SQLException if a database access error occurs - * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method + * @throws SQLFeatureNotSupportedException the JDBC driver does + * not support this method */ @Override public SQLXML getSQLXML(String columnLabel) throws SQLException { @@ -2741,8 +2813,9 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Time - * object in the Java programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a java.sql.Time object in the Java programming + * language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null @@ -2825,8 +2898,9 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Time - * object in the Java programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a java.sql.Time object in the Java programming + * language. * * @param columnLabel the SQL name of the column * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null @@ -2850,13 +2924,16 @@ public class MonetResultSet extends Mone * @throws SQLException if a database access error occurs */ @Override - public Time getTime(String columnLabel, Calendar cal) throws SQLException { + public Time getTime(String columnLabel, Calendar cal) + throws SQLException + { return getTime(findColumn(columnLabel), cal); } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp - * object in the Java programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a java.sql.Timestamp object in the Java programming + * language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null @@ -2959,8 +3036,9 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp - * object in the Java programming language. + * Retrieves the value of the designated column in the current row of this + * ResultSet object as a java.sql.Timestamp object in the Java programming + * language. * * @param columnLabel the SQL name of the column * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null @@ -2984,7 +3062,9 @@ public class MonetResultSet extends Mone * @throws SQLException if a database access error occurs */ @Override - public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + public Timestamp getTimestamp(String columnLabel, Calendar cal) + throws SQLException + { return getTimestamp(findColumn(columnLabel), cal); } @@ -2992,7 +3072,8 @@ public class MonetResultSet extends Mone * Retrieves the type of this ResultSet object. The type is determined by * the Statement object that created the result set. * - * @return ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE or ResultSet.TYPE_SCROLL_SENSITIVE + * @return ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + * or ResultSet.TYPE_SCROLL_SENSITIVE */ @Override public int getType() { @@ -3000,8 +3081,9 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.net.URL - * object in the Java programming language. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a java.net.URL object in the Java + * programming language. * * @param columnIndex the index of the column 1 is the first, 2 is the second,... * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null @@ -3038,8 +3120,9 @@ public class MonetResultSet extends Mone } /** - * Retrieves the value of the designated column in the current row of this ResultSet object as a java.net.URL object - * in the Java programming language. + * Retrieves the value of the designated column in the current row + * of this ResultSet object as a java.net.URL object in the Java + * programming language. * * @param columnLabel the SQL name of the column * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null @@ -3073,7 +3156,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves whether the cursor is after the last row in this ResultSet object. + * Retrieves whether the cursor is after the last row in this ResultSet + * object. * * @return true if the cursor is after the last row; false if the cursor is * at any other position or the result set contains no rows @@ -3086,7 +3170,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves whether the cursor is before the first row in this ResultSet object. + * Retrieves whether the cursor is before the first row in this ResultSet + * object. * * @return true if the cursor is before the first row; false if the cursor * is at any other position or the result set contains no rows @@ -3099,8 +3184,9 @@ public class MonetResultSet extends Mone } /** - * Retrieves whether this ResultSet object has been closed. A ResultSet is closed if the method close has been - * called on it, or if it is automatically closed. + * Retrieves whether this ResultSet object has been closed. A + * ResultSet is closed if the method close has been called on it, or + * if it is automatically closed. * * @return true if this ResultSet object is closed; false if it is still open * @throws SQLException if a database access error occurs or this method is called on a closed result set @@ -3111,7 +3197,8 @@ public class MonetResultSet extends Mone } /** - * Retrieves whether the cursor is on the first row of this ResultSet object. + * Retrieves whether the cursor is on the first row of this ResultSet + * object. * * @return true if the cursor is on the first row; false otherwise * @throws SQLException if a database access error occurs or this method is called on a closed result set @@ -3158,8 +3245,10 @@ public class MonetResultSet extends Mone * next will implicitly close it. A ResultSet object's warning chain is * cleared when a new row is read. * - * @return true if the new current row is valid; false if there are no more rows - * @throws SQLException if a database access error occurs or ResultSet is closed + * @return true if the new current row is valid; false if there are no + * more rows + * @throws SQLException if a database access error occurs or ResultSet is + * closed */ @Override public boolean next() throws SQLException { @@ -3169,9 +3258,10 @@ public class MonetResultSet extends Mone /** * Moves the cursor to the previous row in this ResultSet object. * - * @return true if the cursor is on a valid row; false if it is off the result set - * @throws SQLException if a database access error occurs or ResultSet is closed or the result set type is - * TYPE_FORWARD_ONLY + * @return true if the cursor is on a valid row; false if it is off + * the result set + * @throws SQLException if a database access error occurs or ResultSet is + * closed or the result set type is TYPE_FORWARD_ONLY */ @Override public boolean previous() throws SQLException { @@ -3245,7 +3335,7 @@ public class MonetResultSet extends Mone * * Note: Support for the rowUpdated method is optional with a result set concurrency of CONCUR_READ_ONLY * - * Returns: true if the current row is detected to have been visibly updated by the owner or another;false otherwise + * Returns: true if the current row is detected to have been visibly updated by the owner or another; false otherwise * * Throws: * SQLException - if a database access error occurs or this method is called on a closed result set @@ -3258,6 +3348,7 @@ public class MonetResultSet extends Mone return false; } + /* Next methods are all related to updateable result sets, which we do not support. * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method */ @@ -3291,6 +3382,7 @@ public class MonetResultSet extends Mone throw newSQLFeatureNotSupportedException("refreshRow"); } + @Override public void updateArray(int columnIndex, Array x) throws SQLException { throw newSQLFeatureNotSupportedException("updateArray"); @@ -3750,7 +3842,8 @@ public class MonetResultSet extends Mone /** * Small helper method that formats the "Invalid Column Index number ..." message - * and creates a new SQLDataException object whose SQLState is set to "22010": invalid indicator parameter value. + * and creates a new SQLDataException object whose SQLState is set + * to "22010": invalid indicator parameter value. * * @param colIdx the column index number * @return a new created SQLDataException object with SQLState 22010
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetSavepoint.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetSavepoint.java @@ -26,13 +26,13 @@ import java.util.concurrent.atomic.Atomi * * Because the IDs which get generated are a logical sequence, application * wide, two concurrent transactions are guaranteed to not to have the same - * save point identifiers. In this implementation the validaty of save points + * save point identifiers. In this implementation the validity of save points * is determined by the server, which makes this a light implementation. * * @author Fabian Groffen * @version 1.0 */ -public class MonetSavepoint implements Savepoint { +public final class MonetSavepoint implements Savepoint { /** The id of the last created Savepoint */ private static AtomicInteger highestId = new AtomicInteger(0); @@ -41,20 +41,29 @@ public class MonetSavepoint implements S /** The id of this Savepoint */ private final int id; + /** + * Creates a named MonetSavepoint object + */ public MonetSavepoint(String name) throws IllegalArgumentException { - if (name == null) - throw new IllegalArgumentException("Null name not allowed"); + if (name == null || name.isEmpty()) + throw new IllegalArgumentException("missing savepoint name string"); + this.id = getNextId(); this.name = name; } + /** + * Creates an unnamed MonetSavepoint object + */ public MonetSavepoint() { this.id = getNextId(); this.name = null; } + /** - * Retrieves the generated ID for the savepoint that this Savepoint object represents. + * Retrieves the generated ID for the savepoint that this Savepoint object + * represents. * * @return the numeric ID of this savepoint * @throws SQLException if this is a named savepoint @@ -63,11 +72,13 @@ public class MonetSavepoint implements S public int getSavepointId() throws SQLException { if (name != null) throw new SQLException("Cannot getID for named savepoint", "3B000"); - return getId(); + + return id; } /** - * Retrieves the name of the savepoint that this Savepoint object represents. + * Retrieves the name of the savepoint that this Savepoint object + * represents. * * @return the name of this savepoint * @throws SQLException if this is an un-named savepoint @@ -76,14 +87,16 @@ public class MonetSavepoint implements S public String getSavepointName() throws SQLException { if (name == null) throw new SQLException("Unable to retrieve name of unnamed savepoint", "3B000"); + return name; } //== end of methods from Savepoint interface /** - * Retrieves the savepoint id, like the getSavepointId method with the only difference that this method will always - * return the id, regardless of whether it is named or not. + * Retrieves the savepoint id, like the getSavepointId method with the only + * difference that this method will always return the id, regardless of + * whether it is named or not. * * @return the numeric ID of this savepoint */ @@ -92,8 +105,9 @@ public class MonetSavepoint implements S } /** - * Returns the name to use when referencing this save point to the MonetDB database. The returned value is - * guaranteed to be unique and consists of a prefix string and a sequence number. + * Returns the constructed internal name to use when referencing this save point to the + * MonetDB database. The returned value is guaranteed to be unique and consists of + * a prefix string and a sequence number. * * @return the unique savepoint name */ @@ -109,7 +123,8 @@ public class MonetSavepoint implements S * Therefore two successive calls to this method need not to have a * difference of 1. * - * @return the next int which is guaranteed to be higher than the one at the last call to this method. + * @return the next int which is guaranteed to be higher than the one + * at the last call to this method. */ private static int getNextId() { return highestId.incrementAndGet(); @@ -122,7 +137,7 @@ public class MonetSavepoint implements S * * @return the highest id returned by a call to getNextId() */ - private static int getHighestId() { - return highestId.get(); - } +// private static int getHighestId() { +// return highestId.get(); +// } }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java @@ -26,7 +26,7 @@ import java.util.concurrent.locks.Reentr /** * A Statement suitable for the MonetDB database. - * + * * The object used for executing a static SQL statement and returning * the results it produces. * @@ -36,7 +36,7 @@ import java.util.concurrent.locks.Reentr * been generated by different {@link Statement} objects. All execution methods * in the Statement interface implicitly close a Statement's current * ResultSet object if an open one exists. - * + * * The current state of this Statement is that it only implements the * executeQuery() which returns a ResultSet where from results can be * read and executeUpdate() which returns the affected rows for DML. @@ -47,9 +47,12 @@ import java.util.concurrent.locks.Reentr * @author Martin van Dinther * @version 0.7 */ -public class MonetStatement extends MonetWrapper implements Statement, AutoCloseable { +public class MonetStatement + extends MonetWrapper + implements Statement, AutoCloseable +{ /** The parental Connection object */ - private MonetConnection connection; + protected final MonetConnection connection; /** The last ResponseList object this Statement produced */ private MonetConnection.ResponseList lastResponseList; /** The last Response that this object uses */ @@ -74,8 +77,10 @@ public class MonetStatement extends Mone private int resultSetType = ResultSet.TYPE_FORWARD_ONLY; /** The concurrency of the ResultSet to produce */ private int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY; + /** A List to hold all queries of a batch */ - private List<String> batch = new ArrayList<>(); + private List<String> batch = new ArrayList<String>(); + /** * MonetStatement constructor which checks the arguments for validity, tries @@ -85,12 +90,18 @@ public class MonetStatement extends Mone * @param connection the connection that created this Statement * @param resultSetType type of ResultSet to produce * @param resultSetConcurrency concurrency of ResultSet to produce + * @throws SQLException if an error occurs during login * @throws IllegalArgumentException is one of the arguments is null or empty */ - MonetStatement(MonetConnection connection, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws IllegalArgumentException { - if (connection == null) throw - new IllegalArgumentException("No Connection given!"); + MonetStatement( + MonetConnection connection, + int resultSetType, + int resultSetConcurrency, + int resultSetHoldability) + throws SQLException, IllegalArgumentException + { + if (connection == null) + throw new IllegalArgumentException("No Connection given!"); this.connection = connection; this.resultSetType = resultSetType; @@ -100,13 +111,13 @@ public class MonetStatement extends Mone // check our limits, and generate warnings as appropriate if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) { addWarning("No concurrency mode other then read only is supported, continuing with concurrency level READ_ONLY", "01M13"); - this.resultSetConcurrency = ResultSet.CONCUR_READ_ONLY; + resultSetConcurrency = ResultSet.CONCUR_READ_ONLY; } // check type for supported mode if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE) { addWarning("Change sensitive scrolling ResultSet objects are not supported, continuing with a change non-sensitive scrollable cursor.", "01M14"); - this.resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; + resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; } // check type for supported holdability @@ -121,8 +132,9 @@ public class MonetStatement extends Mone //== methods of interface Statement /** - * Adds the given SQL command to the current list of commands for this Statement object. The commands in this list - * can be executed as a batch by calling the method executeBatch. + * Adds the given SQL command to the current list of commmands for this + * Statement object. The commands in this list can be executed as a + * batch by calling the method executeBatch. * * @param sql typically this is a static SQL INSERT or UPDATE statement * @throws SQLException so the PreparedStatement can throw this exception @@ -140,8 +152,8 @@ public class MonetStatement extends Mone batch.clear(); } - private Lock batchLock = new ReentrantLock(); - + Lock batchLock = new ReentrantLock(); + /** * Submits a batch of commands to the database for execution and if * all commands execute successfully, returns an array of update @@ -182,6 +194,9 @@ public class MonetStatement extends Mone */ @Override public int[] executeBatch() throws SQLException { + // this method is synchronized to make sure noone gets inbetween the + // operations we execute below + batchLock.lock(); try { // don't think long if there isn't much to do @@ -203,19 +218,14 @@ public class MonetStatement extends Mone } } - /** - * Please don't use this method!! It is just for internal use only!!! - * - * @param batch The next batch to execute - * @param counts The array of results of each batch - * @param offset The offset in the array of results - * @param max The max capacity in the array of results - * @param e An exception to be thrown if an error occurs - * @return If all queries in the batch executed successfully or not - * @throws BatchUpdateException if an IO exception or a database error occurs - */ - public boolean internalBatch(String batch, int[] counts, int offset, int max, BatchUpdateException e) - throws BatchUpdateException { + public boolean internalBatch( + String batch, + int[] counts, + int offset, + int max, + BatchUpdateException e) + throws BatchUpdateException + { try { boolean type = internalExecute(batch); int count = -1; @@ -225,8 +235,10 @@ public class MonetStatement extends Mone if (offset >= max) throw new SQLException("Overflow: don't use multi statements when batching (" + max + ")", "M1M16"); if (type) { - e.setNextException(new SQLException("Batch query produced a ResultSet! " + - "Ignoring and setting update count to value " + EXECUTE_FAILED, "M1M17")); + e.setNextException( + new SQLException("Batch query produced a ResultSet! " + + "Ignoring and setting update count to " + + "value " + EXECUTE_FAILED, "M1M17")); counts[offset] = EXECUTE_FAILED; } else if (count >= 0) { counts[offset] = count; @@ -343,10 +355,13 @@ public class MonetStatement extends Mone * Statement.NO_GENERATED_KEYS. */ @Override - public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { - if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && autoGeneratedKeys != Statement.NO_GENERATED_KEYS) { + public boolean execute(String sql, int autoGeneratedKeys) + throws SQLException + { + if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && + autoGeneratedKeys != Statement.NO_GENERATED_KEYS) throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); - } + /* MonetDB has no way to disable this, so just do the normal thing ;) */ return internalExecute(sql); } @@ -388,8 +403,10 @@ public class MonetStatement extends Mone * valid column indexes */ @Override - public boolean execute(String sql, int[] columnIndexes) throws SQLException { - this.addWarning("execute: generated keys for fixed set of columns not supported", "01M18"); + public boolean execute(String sql, int[] columnIndexes) + throws SQLException + { + addWarning("execute: generated keys for fixed set of columns not supported", "01M18"); return execute(sql, Statement.RETURN_GENERATED_KEYS); } @@ -430,7 +447,9 @@ public class MonetStatement extends Mone * not valid column names */ @Override - public boolean execute(String sql, String[] columnNames) throws SQLException { + public boolean execute(String sql, String[] columnNames) + throws SQLException + { addWarning("execute: generated keys for fixed set of columns not supported", "01M18"); return execute(sql, Statement.RETURN_GENERATED_KEYS); } @@ -469,13 +488,20 @@ public class MonetStatement extends Mone /* do not catch SQLException here, as we want to know it when it fails */ finally { if (st != null) { - st.close(); + try { + st.close(); + } catch (SQLException e) { /* ignore */ } } } } // create a container for the result - lastResponseList = connection.new ResponseList(fetchSize, maxRows, resultSetType, resultSetConcurrency); + lastResponseList = connection.new ResponseList( + fetchSize, + maxRows, + resultSetType, + resultSetConcurrency + ); // fill the header list by processing the query lastResponseList.processQuery(sql); @@ -495,7 +521,7 @@ public class MonetStatement extends Mone */ @Override public ResultSet executeQuery(String sql) throws SQLException { - if (!execute(sql)) + if (execute(sql) != true) throw new SQLException("Query did not produce a result set", "M1M19"); return getResultSet(); @@ -515,7 +541,7 @@ public class MonetStatement extends Mone */ @Override public int executeUpdate(String sql) throws SQLException { - if (execute(sql)) + if (execute(sql) != false) throw new SQLException("Query produced a result set", "M1M17"); return getUpdateCount(); @@ -540,12 +566,15 @@ public class MonetStatement extends Mone * given constant is not one of those allowed */ @Override - public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { - if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && autoGeneratedKeys != Statement.NO_GENERATED_KEYS) + public int executeUpdate(String sql, int autoGeneratedKeys) + throws SQLException + { + if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS && + autoGeneratedKeys != Statement.NO_GENERATED_KEYS) throw new SQLException("Invalid argument, expected RETURN_GENERATED_KEYS or NO_GENERATED_KEYS", "M1M05"); - + /* MonetDB has no way to disable this, so just do the normal thing ;) */ - if (execute(sql)) + if (execute(sql) != false) throw new SQLException("Query produced a result set", "M1M17"); return getUpdateCount(); @@ -575,7 +604,9 @@ public class MonetStatement extends Mone * whose elements are valid column indexes */ @Override - public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + public int executeUpdate(String sql, int[] columnIndexes) + throws SQLException + { addWarning("executeUpdate: generated keys for fixed set of columns not supported", "01M18"); return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); } @@ -604,7 +635,9 @@ public class MonetStatement extends Mone * whose elements are valid column names */ @Override - public int executeUpdate(String sql, String[] columnNames) throws SQLException { + public int executeUpdate(String sql, String[] columnNames) + throws SQLException + { addWarning("executeUpdate: generated keys for fixed set of columns not supported", "01M18"); return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); } @@ -640,7 +673,8 @@ public class MonetStatement extends Mone * setFetchSize, or the method setFetchSize was called as such to let * the driver ignore the hint, 0 is returned. * - * @return the default fetch size for result sets generated from this Statement object + * @return the default fetch size for result sets generated from this + * Statement object */ @Override public int getFetchSize() { @@ -652,8 +686,8 @@ public class MonetStatement extends Mone * executing this Statement object. If this Statement object did not * generate any keys, an empty ResultSet object is returned. * - * @return a ResultSet object containing the auto-generated key(s) generated by the execution of this Statement - * object + * @return a ResultSet object containing the auto-generated key(s) + * generated by the execution of this Statement object * @throws SQLException - if a database access error occurs */ @Override @@ -770,12 +804,14 @@ public class MonetStatement extends Mone } // we default to keep current result, which requires no action header = lastResponseList.getNextResponse(); + return (header != null && header instanceof ResultSetResponse); } /** - * Retrieves the number of seconds the driver will wait for a Statement object to execute. If the limit is exceeded, - * a SQLException is thrown. + * Retrieves the number of seconds the driver will wait for a + * Statement object to execute. If the limit is exceeded, a + * SQLException is thrown. * * @return the current query timeout limit in seconds; zero means * there is no limit @@ -787,10 +823,11 @@ public class MonetStatement extends Mone } /** - * Retrieves the current result as a ResultSet object. This method should be called only once per result. + * Retrieves the current result as a ResultSet object. This method + * should be called only once per result. * - * @return the current result as a ResultSet object or null if the result is an update count or there are no more - * results + * @return the current result as a ResultSet object or null if the result + * is an update count or there are no more results * @throws SQLException if a database access error occurs */ @Override @@ -800,7 +837,8 @@ public class MonetStatement extends Mone } /** - * Retrieves the result set concurrency for ResultSet objects generated by this Statement object. + * Retrieves the result set concurrency for ResultSet objects generated + * by this Statement object. * * @return either ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE */ @@ -810,7 +848,8 @@ public class MonetStatement extends Mone } /** - * Retrieves the result set holdability for ResultSet objects generated by this Statement object. + * Retrieves the result set holdability for ResultSet objects + * generated by this Statement object. * * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or * ResultSet.CLOSE_CURSORS_AT_COMMIT @@ -821,9 +860,12 @@ public class MonetStatement extends Mone } /** - * Retrieves the result set type for ResultSet objects generated by this Statement object. + * Retrieves the result set type for ResultSet objects generated by this + * Statement object. * - * @return one of ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE + * @return one of ResultSet.TYPE_FORWARD_ONLY, + * ResultSet.TYPE_SCROLL_INSENSITIVE, or + * ResultSet.TYPE_SCROLL_SENSITIVE */ @Override public int getResultSetType() { @@ -869,9 +911,9 @@ public class MonetStatement extends Mone */ @Override public SQLWarning getWarnings() throws SQLException { - if (closed) { + if (closed) throw new SQLException("Cannot call on closed Statement", "M1M20"); - } + // if there are no warnings, this will be null, which fits with the // specification. return warnings; @@ -925,9 +967,8 @@ public class MonetStatement extends Mone */ @Override public void setEscapeProcessing(boolean enable) throws SQLException { - if (enable) { + if (enable) addWarning("setEscapeProcessing: JDBC escape syntax is not supported by this driver", "01M22"); - } } /** @@ -946,8 +987,10 @@ public class MonetStatement extends Mone */ @Override public void setFetchDirection(int direction) throws SQLException { - if (direction == ResultSet.FETCH_FORWARD || direction == ResultSet.FETCH_REVERSE - || direction == ResultSet.FETCH_UNKNOWN) { + if (direction == ResultSet.FETCH_FORWARD || + direction == ResultSet.FETCH_REVERSE || + direction == ResultSet.FETCH_UNKNOWN) + { fetchDirection = direction; } else { throw new SQLException("Illegal direction: " + direction, "M1M05"); @@ -993,12 +1036,10 @@ public class MonetStatement extends Mone */ @Override public void setMaxFieldSize(int max) throws SQLException { - if (max < 0) { + if (max < 0) throw new SQLException("Illegal max value: " + max, "M1M05"); - } - if (max > 0) { + if (max > 0) addWarning("setMaxFieldSize: field size limitation not supported", "01M23"); - } } /** @@ -1012,9 +1053,8 @@ public class MonetStatement extends Mone */ @Override public void setMaxRows(int max) throws SQLException { - if (max < 0) { + if (max < 0) throw new SQLException("Illegal max value: " + max, "M1M05"); - } maxRows = max; } @@ -1053,7 +1093,7 @@ public class MonetStatement extends Mone /** * Requests that a Statement be pooled or not pooled. The value * specified is a hint to the statement pool implementation - * indicating whether the application wants the statement to be + * indicating whether the applicaiton wants the statement to be * pooled. It is up to the statement pool manager as to whether the * hint is used. * @@ -1074,7 +1114,8 @@ public class MonetStatement extends Mone } /** - * Returns a value indicating whether the Statement is poolable or not. + * Returns a value indicating whether the Statement is poolable or + * not. * * @return true if the Statement is poolable; false otherwise */ @@ -1084,7 +1125,7 @@ public class MonetStatement extends Mone } //== 1.7 methods (JDBC 4.1) - + /** * Specifies that this Statement will be closed when all its * dependent result sets are closed. If execution of the Statement @@ -1094,9 +1135,8 @@ public class MonetStatement extends Mone */ @Override public void closeOnCompletion() throws SQLException { - if (closed) { + if (closed) throw new SQLException("Cannot call on closed Statement", "M1M20"); - } closeOnCompletion = true; } @@ -1110,9 +1150,8 @@ public class MonetStatement extends Mone */ @Override public boolean isCloseOnCompletion() throws SQLException { - if (closed) { + if (closed) throw new SQLException("Cannot call on closed Statement", "M1M20"); - } return closeOnCompletion; } @@ -1170,24 +1209,25 @@ final class MonetVirtualResultSet extend throws IllegalArgumentException, IOException, SQLException { super(statement, columns, types, jdbcTypes, results.length); this.results = results; - this.closed = false; + closed = false; } /** - * This method is overridden in order to let it use the results array instead of the cache in the Statement object - * that created it. + * This method is overridden in order to let it use the results array + * instead of the cache in the Statement object that created it. * - * @param row the number of the row to which the cursor should move. A positive number indicates the row number - * counting from the beginning of the result set; a negative number indicates the row number counting from the end - * of the result set + * @param row the number of the row to which the cursor should move. A + * positive number indicates the row number counting from the + * beginning of the result set; a negative number indicates the row + * number counting from the end of the result set * @return true if the cursor is on the result set; false otherwise * @throws SQLException if a database error occurs */ @Override public boolean absolute(int row) throws SQLException { - if (closed) { + if (closed) throw new SQLException("ResultSet is closed!", "M1M20"); - } + // first calculate what the JDBC row is if (row < 0) { // calculate the negatives... @@ -1200,7 +1240,7 @@ final class MonetVirtualResultSet extend row = tupleCount + 1; // after last // store it - this.curRow = row; + curRow = row; // see if we have the row if (row < 1 || row > tupleCount) @@ -1230,8 +1270,9 @@ final class MonetVirtualResultSet extend } /** - * Mainly here to prevent errors when the close method is called. There is no real need for this object to close it. - * We simply remove our resultset data. + * Mainly here to prevent errors when the close method is called. There + * is no real need for this object to close it. We simply remove our + * resultset data. */ @Override public void close() {
deleted file mode 100644 --- a/src/main/java/nl/cwi/monetdb/mcl/connection/SenderThread.java +++ /dev/null @@ -1,147 +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 - 2019 MonetDB B.V. - */ - -package nl.cwi.monetdb.mcl.connection; - -import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * A thread to send a query to the server. When sending large amounts of data to a server, the output buffer of the - * underlying communication socket may overflow. In such case the sending process blocks. In order to prevent deadlock, - * it might be desirable that the driver as a whole does not block. This thread facilitates the prevention of such - * 'full block', because this separate thread only will block.<br /> This thread is designed for reuse, as thread - * creation costs are high. - * - * @author Fabian Groffen - */ -public class SenderThread extends Thread { - - /** The state WAIT represents this thread to be waiting for something to do */ - private static final int WAIT = 1; - /** The state QUERY represents this thread to be executing a query */ - private static final int QUERY = 2; - /** The state SHUTDOWN is the final state that ends this thread */ - private static final int SHUTDOWN = 3; - - private String[] templ; - private String query; - private AbstractProtocol protocol; - private String error; - private int state = SenderThread.WAIT; - private final Lock sendLock = new ReentrantLock(); - private final Condition queryAvailable = sendLock.newCondition(); - private final Condition waiting = sendLock.newCondition(); - - /** - * Constructor which immediately starts this thread and sets it into daemon mode. - * - * @param out the socket to write to - */ - public SenderThread(AbstractProtocol out) { - super("SendThread"); - this.setDaemon(true); - this.protocol = out; - this.start(); - } - - @Override - public void run() { - this.sendLock.lock(); - try { - while (true) { - while (this.state == SenderThread.WAIT) { - try { - this.queryAvailable.await(); - } catch (InterruptedException e) { - // woken up, eh? - } - } - if (this.state == SenderThread.SHUTDOWN) - break; - - // state is QUERY here - try { - this.protocol.writeNextQuery((templ[0] == null ? "" : templ[0]), query, - (templ[1] == null ? "" : templ[1])); - } catch (IOException e) { - this.error = e.getMessage(); - } - - // update our state, and notify, maybe someone is waiting for us in throwErrors - this.state = SenderThread.WAIT; - this.waiting.signal(); - } - } finally { - this.sendLock.unlock(); - } - } - - /** - * Starts sending the given query over the given socket. Beware that the thread should be finished (can be assured - * by calling throwErrors()) before this method is called! - * - * @param templ the query template - * @param query the query itself - * @throws SQLException if this SendThread is already in use - */ - public void runQuery(String[] templ, String query) throws SQLException { - this.sendLock.lock(); - try { - if (this.state != SenderThread.WAIT) { - throw new SQLException("Sender Thread already in use or shutting down!", "M0M03"); - } - this.templ = templ; - this.query = query; - // let the thread know there is some work to do - this.state = SenderThread.QUERY; - this.queryAvailable.signal(); - } finally { - this.sendLock.unlock(); - } - } - - /** - * Returns errors encountered during the sending process. - * - * @return the errors or null if none - */ - public String getErrors() { - this.sendLock.lock(); - try { - // make sure the thread is in WAIT state, not QUERY - while (this.state == SenderThread.QUERY) { - try { - this.waiting.await(); - } catch (InterruptedException e) { - // just try again - } - } - if (this.state == SenderThread.SHUTDOWN) - this.error = "SendThread is shutting down"; - } finally { - this.sendLock.unlock(); - } - return error; - } - - /** - * Requests this SendThread to stop. - */ - public void shutdown() { - sendLock.lock(); - state = SenderThread.SHUTDOWN; - sendLock.unlock(); - this.interrupt(); // break any wait conditions - } -}
new file mode 100644 --- /dev/null +++ b/tests/Bug_LargeQueries_6571_6693.java @@ -0,0 +1,161 @@ +/* + * 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 - 2019 MonetDB B.V. + */ + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; +import java.sql.SQLException; + +public class Bug_LargeQueries_6571_6693 { + final static String tbl_nm = "tbl6693"; + final static String largedata = createLargedata(98765); + + private static String createLargedata(int num) { + String repeatValue = "*"; + StringBuilder sb = new StringBuilder(num * repeatValue.length()); + for (int i = 0; i < num; i++) + sb.append(repeatValue); + String largedata = sb.toString(); + if (largedata.length() <= 8192) + System.out.println("Length (" + largedata.length() + ") of largedata value is too small! Should be larger than 8192!"); + return largedata; + } + + // To execute this test program: start a local MonetDB server (mserver5 process) and next execute command: + // java -cp monetdb-jdbc-2.29.jar:. Bug_LargeQueries_6571_6693 "jdbc:monetdb://localhost:50000/demo?user=monetdb&password=monetdb" + + public static void main(String[] args) throws SQLException { + int script_iterations = 10; + String conURL = args.length > 0 ? args[0] : ""; + + if (args.length > 1) { + try { + script_iterations = Integer.parseInt(args[1]); + } catch (NumberFormatException nfe) { + System.err.println("Cannot convert 2nd argumnent to an integer. Ignoring it."); + } + } + + try (Connection con = DriverManager.getConnection(conURL)) { + try (Statement stmt = con.createStatement()) { + // create a test table. + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + tbl_nm + " (attribute CLOB, value CLOB);"); + System.out.print("Created table: " + tbl_nm); + System.out.print(" Inserting rows. "); + String insertCmd = "INSERT INTO " + tbl_nm + " VALUES ('activeset_default_fiets', '" + largedata + "');"; + int ins = stmt.executeUpdate(insertCmd); + ins += stmt.executeUpdate(insertCmd); + ins += stmt.executeUpdate(insertCmd); + System.out.println(ins + " rows inserted"); + } + + run_tests(conURL, script_iterations); + + try (Statement stmt = con.createStatement()) { + System.out.println("Cleanup TABLE " + tbl_nm); + stmt.executeUpdate("DROP TABLE IF EXISTS " + tbl_nm); + } + } + + System.out.println("Test completed without hanging"); + } + + private static void run_tests(String conURL, int iterations) throws SQLException { + String script = + "delete from " + tbl_nm + " where attribute='activeset_default_fiets';\n" + + "insert into " + tbl_nm + " values ('activeset_default_fiets', '" + largedata + "');\n" + + "insert into " + tbl_nm + " values ('activeset_default_fiets', '" + largedata + "');\n" + + "insert into " + tbl_nm + " values ('activeset_default_fiets', '" + largedata + "');\n" + + "select value from " + tbl_nm + " where attribute='activeset_default_fiets';\n"; + System.out.println("Script size is " + script.length()); + + // first try to make the execution hang after many iterations of sending large data queries within one connection + System.out.println("First test repeat " + iterations + " times"); + try (Connection con = DriverManager.getConnection(conURL)) { + System.out.print("Iteration: "); + for (int i = 1; i <= iterations; i++) { + System.out.print(i + " "); + try (Statement stmt = con.createStatement()) { + process_script(stmt, script, 1, 3, 6); + } + } + System.out.println(); + } + System.out.println("Completed first test"); + + // also try to make the execution hang after many iterations of making connections (each their own socket) and sending large scripts + System.out.println("Second test repeat " + iterations + " times"); + System.out.print("Iteration: "); + for (int i = 1; i <= iterations; i++) { + try (Connection con = DriverManager.getConnection(conURL)) { + System.out.print(i + " "); + try (Statement stmt = con.createStatement()) { + process_script(stmt, script, 1, 3, 6); + process_script(stmt, script, 1, 3, 6); + process_script(stmt, script, 1, 3, 6); + process_script(stmt, script, 1, 3, 6); + } + } + } + System.out.println(); + System.out.println("Completed second test"); + + // next try to make the execution hang by sending very many queries combined in 1 large script + final int queries = 8765; + StringBuilder sb = new StringBuilder(queries * 13); + for (int i = 1; i <= queries; i++) + sb.append(" SELECT ").append(i).append(';'); + script = sb.toString(); + System.out.println("Script size is " + script.length()); + iterations = 3; + System.out.println("Third test repeat " + iterations + " times"); + try (Connection con = DriverManager.getConnection(conURL)) { + System.out.print("Iteration: "); + for (int i = 1; i <= iterations; i++) { + System.out.print(i + " "); + try (Statement stmt = con.createStatement()) { + process_script(stmt, script, queries, queries, 0); + } + } + System.out.println(); + } + System.out.println("Completed third test"); + } + + private static void process_script(Statement stmt, String script, + int expectedResults, int expectedTotalRows, int expectedUpdates) throws SQLException { + int results = 0; + int rows = 0; + int updates = 0; + stmt.execute(script); + do { + ResultSet rs = stmt.getResultSet(); + if (rs != null) { + results++; + while(rs.next()) { + String val = rs.getString(1); + rows++; + } + rs.close(); + } else { + int uc = stmt.getUpdateCount(); + if (uc > 0) + updates += uc; + } + } while (stmt.getMoreResults() || stmt.getUpdateCount() != -1); + + /* verify nr of processed resultsets and retrieved rows are as expected */ + if (results != expectedResults) + System.out.print(results + "!=" + expectedResults + " "); + if (rows != expectedTotalRows) + System.out.print(rows + "!=" + expectedTotalRows + " "); + if (updates != expectedUpdates) + System.out.print(updates + "!=" + expectedUpdates + " "); + } +}
--- a/tests/build.xml +++ b/tests/build.xml @@ -139,6 +139,7 @@ Copyright 1997 - July 2008 CWI, August 2 <antcall target="Bug_PrepStmtSetObject_CLOB_6349" /> <antcall target="Bug_Connect_as_voc_getMetaData_Failure_Bug_6388" /> <antcall target="Bug_PrepStmtSetString_6382" /> + <antcall target="Bug_LargeQueries_6571_6693" /> </target> <target name="test_class" depends="compile,jdbc"> @@ -418,4 +419,10 @@ Copyright 1997 - July 2008 CWI, August 2 </antcall> </target> + <target name="Bug_LargeQueries_6571_6693"> + <antcall target="test_class"> + <param name="test.class" value="Bug_LargeQueries_6571_6693" /> + </antcall> + </target> + </project>