changeset 66:7307caacc2d5 embedded

Intermediary commit. Cross-implementation interface (for mapi connection with the old protocol, mapi connection with the new protocol and the embedded connection) almost done. The mcl layer processing is more memory efficient now.
author Pedro Ferreira <pedro.ferreira@monetdbsolutions.com>
date Tue, 06 Dec 2016 15:19:18 +0100 (2016-12-06)
parents e605cdd6373f
children 87ba760038b6
files src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.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/EmbeddedConnection.java src/main/java/nl/cwi/monetdb/mcl/connection/MapiConnection.java src/main/java/nl/cwi/monetdb/mcl/connection/MonetDBLanguage.java src/main/java/nl/cwi/monetdb/mcl/connection/SendThread.java src/main/java/nl/cwi/monetdb/mcl/io/SocketConnection.java src/main/java/nl/cwi/monetdb/mcl/parser/StartOfHeaderParser.java src/main/java/nl/cwi/monetdb/mcl/parser/socket/SocketHeaderLineParser.java src/main/java/nl/cwi/monetdb/mcl/parser/socket/SocketStartOfHeaderParser.java src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocolParser.java src/main/java/nl/cwi/monetdb/mcl/protocol/TableResultHeaders.java src/main/java/nl/cwi/monetdb/mcl/protocol/embedded/EmbeddedProtocol.java src/main/java/nl/cwi/monetdb/mcl/protocol/newmapi/NewMapiProtocol.java src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiConverter.java src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiServerResponseParser.java src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiStartOfHeaderParser.java src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java src/main/java/nl/cwi/monetdb/mcl/responses/AutoCommitResponse.java src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java src/main/java/nl/cwi/monetdb/mcl/responses/IIncompleteResponse.java src/main/java/nl/cwi/monetdb/mcl/responses/IResponse.java src/main/java/nl/cwi/monetdb/mcl/responses/ResultSetResponse.java src/main/java/nl/cwi/monetdb/mcl/responses/SchemaResponse.java src/main/java/nl/cwi/monetdb/mcl/responses/UpdateResponse.java src/main/java/nl/cwi/monetdb/merovingian/Control.java src/main/java/nl/cwi/monetdb/responses/AutoCommitResponse.java src/main/java/nl/cwi/monetdb/responses/DataBlockResponse.java src/main/java/nl/cwi/monetdb/responses/IResponse.java src/main/java/nl/cwi/monetdb/responses/ResponseList.java src/main/java/nl/cwi/monetdb/responses/ResultSetResponse.java src/main/java/nl/cwi/monetdb/responses/SchemaResponse.java src/main/java/nl/cwi/monetdb/responses/UpdateResponse.java
diffstat 43 files changed, 2245 insertions(+), 2516 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java
@@ -23,20 +23,20 @@ import java.io.*;
  * @author Fabian Groffen
  */
 public class MonetBlob implements Blob {
+
 	private byte[] buf;
 
-	public MonetBlob(byte[] data) {
-		buf = data;
+	MonetBlob(byte[] buf) {
+		this.buf = buf;
 	}
-	
-	static MonetBlob create(String in) {
+
+	MonetBlob(String in) {
 		int len = in.length() / 2;
-		byte[] buf = new byte[len];
-		for (int i = 0; i < len; i++)
-			buf[i] = (byte)Integer.parseInt(in.substring(2 * i, (2 * i) + 2), 16);
-		return new MonetBlob(buf);
+		this.buf = new byte[len];
+		for (int i = 0; i < len; i++) {
+			this.buf[i] = (byte) Integer.parseInt(in.substring(2 * i, (2 * i) + 2), 16);
+		}
 	}
-	
 
 	//== begin interface Blob
 	
@@ -93,9 +93,7 @@ public class MonetBlob implements Blob {
 	 *         not support this method
 	 */
 	@Override
-	public InputStream getBinaryStream(long pos, long length)
-		throws SQLException
-	{
+	public InputStream getBinaryStream(long pos, long length) throws SQLException {
 		if (buf == null)
 			throw new SQLException("This Blob object has been freed", "M1M20");
 		if (pos < 1)
@@ -261,9 +259,7 @@ public class MonetBlob implements Blob {
 	 *         BLOB value
 	 */
 	@Override
-	public int setBytes(long pos, byte[] bytes, int offset, int len)
-		throws SQLException
-	{
+	public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {
 		if (buf == null)
 			throw new SQLException("This Blob object has been freed", "M1M20");
 		try {
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java
@@ -26,7 +26,7 @@ public class MonetClob implements Clob {
 	
 	private StringBuilder buf;
 
-	public MonetClob(String in) {
+	MonetClob(String in) {
 		buf = new StringBuilder(in);
 	}
 
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
@@ -6,8 +6,12 @@ import nl.cwi.monetdb.mcl.connection.MCL
 import nl.cwi.monetdb.mcl.connection.Debugger;
 import nl.cwi.monetdb.mcl.connection.MonetDBLanguage;
 import nl.cwi.monetdb.mcl.parser.MCLParseException;
-import nl.cwi.monetdb.responses.ResponseList;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
+import nl.cwi.monetdb.mcl.protocol.ServerResponses;
+import nl.cwi.monetdb.mcl.responses.*;
 import nl.cwi.monetdb.mcl.connection.SendThread;
+import nl.cwi.monetdb.mcl.responses.DataBlockResponse;
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
 
 import java.io.*;
 import java.net.SocketTimeoutException;
@@ -42,6 +46,19 @@ import java.util.concurrent.Executor;
  */
 public abstract class MonetConnection extends MonetWrapper implements Connection {
 
+    /** the default number of rows that are (attempted to) read at once */
+    private static int DEF_FETCHSIZE = 250;
+    /** The sequence counter */
+    private static int SeqCounter = 0;
+
+    public static int GetDefFetchsize() {
+        return DEF_FETCHSIZE;
+    }
+
+    public static int GetSeqCounter() {
+        return SeqCounter;
+    }
+
     /** the successful processed input properties */
     protected final Properties conn_props;
     /** The language to connect with */
@@ -81,6 +98,8 @@ public abstract class MonetConnection ex
 
     protected Debugger ourSavior;
 
+    protected AbstractProtocol<?> protocol;
+
     /**
      * Constructor of a Connection for MonetDB. At this moment the
      * current implementation limits itself to storing the given host,
@@ -103,8 +122,8 @@ public abstract class MonetConnection ex
         return language;
     }
 
-    public void setLanguage(MonetDBLanguage language) {
-        this.language = language;
+    public void setClosed(boolean closed) {
+        this.closed = closed;
     }
 
     public void setDebugging(String filename) throws IOException {
@@ -135,6 +154,10 @@ public abstract class MonetConnection ex
 
     public abstract String getJDBCURL();
 
+    public AbstractProtocol<?> getProtocol() {
+        return this.protocol;
+    }
+
     /**
      * Releases this Connection object's database and JDBC resources
      * immediately instead of waiting for them to be automatically
@@ -199,6 +222,17 @@ public abstract class MonetConnection ex
         warnings = null;
     }
 
+    private void createResponseList(String query) throws SQLException {
+        // create a container for the result
+        ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY);
+        // send commit to the server
+        try {
+            l.processQuery(query);
+        } finally {
+            l.close();
+        }
+    }
+
     /**
      * Makes all changes made since the previous commit/rollback
      * permanent and releases any database locks currently held by this
@@ -213,15 +247,7 @@ public abstract class MonetConnection ex
     public void commit() 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
-        ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY);
-        // send commit to the server
-        try {
-            l.processQuery("COMMIT");
-        } finally {
-            l.close();
-        }
+        this.createResponseList("COMMIT");
     }
 
     /**
@@ -619,13 +645,7 @@ public abstract class MonetConnection ex
         // note: can't use sendIndependentCommand here because we need
         // to process the auto_commit state the server gives
         // create a container for the result
-        ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY);
-        // send the appropriate query string to the database
-        try {
-            l.processQuery("RELEASE SAVEPOINT " + sp.getName());
-        } finally {
-            l.close();
-        }
+        this.createResponseList("RELEASE SAVEPOINT " + sp.getName());
     }
 
     /**
@@ -643,13 +663,7 @@ public abstract class MonetConnection ex
         // note: can't use sendIndependentCommand here because we need
         // to process the auto_commit state the server gives
         // create a container for the result
-        ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY);
-        // send rollback to the server
-        try {
-            l.processQuery("ROLLBACK");
-        } finally {
-            l.close();
-        }
+        this.createResponseList("ROLLBACK");
     }
 
     /**
@@ -673,13 +687,7 @@ public abstract class MonetConnection ex
         // note: can't use sendIndependentCommand here because we need
         // to process the auto_commit state the server gives
         // create a container for the result
-        ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY);
-        // send the appropriate query string to the database
-        try {
-            l.processQuery("ROLLBACK TO SAVEPOINT " + sp.getName());
-        } finally {
-            l.close();
-        }
+        this.createResponseList("ROLLBACK TO SAVEPOINT " + sp.getName());
     }
 
     /**
@@ -710,7 +718,7 @@ public abstract class MonetConnection ex
     @Override
     public void setAutoCommit(boolean autoCommit) throws SQLException {
         if (this.autoCommit != autoCommit) {
-            sendControlCommand("auto_commit " + (autoCommit ? "1" : "0"));
+            this.sendControlCommand("auto_commit " + (autoCommit ? "1" : "0"));
             this.autoCommit = autoCommit;
         }
     }
@@ -774,13 +782,7 @@ public abstract class MonetConnection ex
         // note: can't use sendIndependentCommand here because we need
         // to process the auto_commit state the server gives
         // create a container for the result
-        ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY);
-        // send the appropriate query string to the database
-        try {
-            l.processQuery("SAVEPOINT " + sp.getName());
-        } finally {
-            l.close();
-        }
+        this.createResponseList("SAVEPOINT " + sp.getName());
         return sp;
     }
 
@@ -806,13 +808,7 @@ public abstract class MonetConnection ex
         // note: can't use sendIndependentCommand here because we need
         // to process the auto_commit state the server gives
         // create a container for the result
-        ResponseList l = new ResponseList(0, 0, ResultSet.FETCH_FORWARD, ResultSet.CONCUR_READ_ONLY);
-        // send the appropriate query string to the database
-        try {
-            l.processQuery("SAVEPOINT " + sp.getName());
-        } finally {
-            l.close();
-        }
+        this.createResponseList("SAVEPOINT " + sp.getName());
         return sp;
     }
 
@@ -893,9 +889,7 @@ public abstract class MonetConnection ex
      * @since 1.6
      */
     @Override
-    public java.sql.Array createArrayOf(String typeName, Object[] elements)
-            throws SQLException
-    {
+    public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException {
         throw new SQLFeatureNotSupportedException("createArrayOf() not supported", "0A000");
     }
 
@@ -965,9 +959,7 @@ public abstract class MonetConnection ex
      * @since 1.6
      */
     @Override
-    public java.sql.Struct createStruct(String typeName, Object[] attributes)
-            throws SQLException
-    {
+    public java.sql.Struct createStruct(String typeName, Object[] attributes) throws SQLException {
         throw new SQLFeatureNotSupportedException("createStruct() not supported", "0A000");
     }
 
@@ -1329,10 +1321,12 @@ public abstract class MonetConnection ex
     public void sendIndependentCommand(String command) throws SQLException {
         synchronized (this) {
             try {
-                out.writeLine(language.getQueryTemplateIndex(0) + command + language.getQueryTemplateIndex(1));
-                String error = in.waitForPrompt();
-                if (error != null)
+                protocol.writeNextCommand(language.getQueryTemplateIndex(0), command.getBytes(), language.getQueryTemplateIndex(1));
+                protocol.waitUntilPrompt();
+                if (protocol.getCurrentServerResponseHeader() == ServerResponses.ERROR) {
+                    String error = protocol.getRemainingStringLine(0);
                     throw new SQLException(error.substring(6), error.substring(0, 5));
+                }
             } catch (SocketTimeoutException e) {
                 close(); // JDBC 4.1 semantics: abort()
                 throw new SQLException("connection timed out", "08M33");
@@ -1355,10 +1349,12 @@ public abstract class MonetConnection ex
         // send X command
         synchronized (this) {
             try {
-                out.writeLine(language.getCommandTemplateIndex(0) + command + language.getCommandTemplateIndex(1));
-                String error = in.waitForPrompt();
-                if (error != null)
+                protocol.writeNextCommand(language.getCommandTemplateIndex(0), command.getBytes(), language.getCommandTemplateIndex(1));
+                protocol.waitUntilPrompt();
+                if (protocol.getCurrentServerResponseHeader() == ServerResponses.ERROR) {
+                    String error = protocol.getRemainingStringLine(0);
                     throw new SQLException(error.substring(6), error.substring(0, 5));
+                }
             } catch (SocketTimeoutException e) {
                 close(); // JDBC 4.1 semantics, abort()
                 throw new SQLException("connection timed out", "08M33");
@@ -1383,4 +1379,369 @@ public abstract class MonetConnection ex
             warnings.setNextWarning(new SQLWarning(reason, sqlstate));
         }
     }
+
+    /**
+     * A list of Response objects.  Responses are added to this list.
+     * Methods of this class are not synchronized.  This is left as
+     * responsibility to the caller to prevent concurrent access.
+     */
+    public class ResponseList {
+
+        /** The cache size (number of rows in a DataBlockResponse object) */
+        private final int cachesize;
+        /** The maximum number of results for this query */
+        private final int maxrows;
+        /** The ResultSet type to produce */
+        private final int rstype;
+        /** The ResultSet concurrency to produce */
+        private final int rsconcur;
+        /** The sequence number of this ResponseList */
+        private final int seqnr;
+        /** A list of the Responses associated with the query, in the right order */
+        private List<IResponse> responses = new ArrayList<>();
+        /** A map of ResultSetResponses, used for additional DataBlockResponse mapping */
+        private Map<Integer, ResultSetResponse> rsresponses;
+        /** The current header returned by getNextResponse() */
+        private int curResponse = -1;
+
+        /**
+         * 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
+         */
+        public 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++;
+        }
+
+        public int getCachesize() {
+            return cachesize;
+        }
+
+        public int getRstype() {
+            return rstype;
+        }
+
+        public int getRsconcur() {
+            return rsconcur;
+        }
+
+        public int getMaxrows() {
+            return maxrows;
+        }
+
+        /**
+         * Retrieves the next available response, or null if there are
+         * no more responses.
+         *
+         * @return the next Response available or null
+         */
+        IResponse getNextResponse() throws SQLException {
+            if (rstype == ResultSet.TYPE_FORWARD_ONLY) {
+                // free resources if we're running forward only
+                if (curResponse >= 0 && curResponse < responses.size()) {
+                    IResponse tmp = responses.get(curResponse);
+                    if (tmp != null) {
+                        tmp.close();
+                    }
+                    responses.set(curResponse, null);
+                }
+            }
+            curResponse++;
+            if (curResponse >= responses.size()) {
+                // ResponseList is obviously completed so, there are no
+                // more responses
+                return null;
+            } else {
+                // return this response
+                return responses.get(curResponse);
+            }
+        }
+
+        /**
+         * Closes the Response at index i, if not null.
+         *
+         * @param i the index position of the header to close
+         */
+        void closeResponse(int i) {
+            if (i < 0 || i >= responses.size()) return;
+            IResponse tmp = responses.set(i, null);
+            if (tmp != null)
+                tmp.close();
+        }
+
+        /**
+         * Closes the current response.
+         */
+        void closeCurrentResponse() {
+            closeResponse(curResponse);
+        }
+
+        /**
+         * Closes the current and previous responses.
+         */
+        void closeCurOldResponses() {
+            for (int i = curResponse; i >= 0; i--) {
+                closeResponse(i);
+            }
+        }
+
+        /**
+         * Closes this ResponseList by closing all the Responses in this
+         * ResponseList.
+         */
+        public void close() {
+            for (int i = 0; i < responses.size(); i++) {
+                closeResponse(i);
+            }
+        }
+
+        /**
+         * Returns whether this ResponseList has still unclosed
+         * Responses.
+         */
+        public boolean hasUnclosedResponses() {
+            for (IResponse r : responses) {
+                if (r != null)
+                    return true;
+            }
+            return false;
+        }
+
+        /**
+         * 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
+         */
+        public void processQuery(String query) throws SQLException {
+            this.executeQuery(language.getQueryTemplates(), query);
+        }
+
+        /**
+         * Internal executor of queries.
+         *
+         * @param templ the template to fill in
+         * @param query the query to execute
+         * @throws SQLException if a database error occurs
+         */
+        @SuppressWarnings({"fallthrough", "unchecked"})
+        public void executeQuery(byte[][] templ, String query) throws SQLException {
+            String error = null;
+
+            try {
+                // make sure we're ready to send query; read data till we
+                // have the prompt it is possible (and most likely) that we
+                // already have the prompt and do not have to skip any
+                // lines.  Ignore errors from previous result sets.
+                protocol.waitUntilPrompt();
+
+                // {{{ 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.
+                 */
+                int size = cachesize == 0 ? DEF_FETCHSIZE : cachesize;
+                size = maxrows != 0 ? Math.min(maxrows, size) : size;
+                // don't do work if it's not needed
+                if (language == MonetDBLanguage.LANG_SQL && size != curReplySize && !Arrays.deepEquals(templ, language.getCommandTemplates())) {
+                    sendControlCommand("reply_size " + size);
+
+                    // store the reply size after a successful change
+                    curReplySize = size;
+                }
+                // }}} 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 (sendThread == null) {
+                        sendThread = new SendThread(protocol);
+                    }
+                    // tell it to do some work!
+                    sendThread.runQuery(templ, query);
+                } else {
+                    // this is a simple call, which is a lot cheaper and will
+                    // always succeed for small queries.
+                    protocol.writeNextCommand((templ[0] == null) ? MonetDBLanguage.EmptyString : templ[0], query.getBytes(), (templ[1] == null) ? MonetDBLanguage.EmptyString : templ[1]);
+                }
+
+                // go for new results
+                protocol.fetchNextResponseData();
+                ServerResponses nextResponse = protocol.getCurrentServerResponseHeader();
+                IResponse res = null;
+                while (nextResponse != ServerResponses.PROMPT) {
+                    // each response should start with a start of header
+                    // (or error)
+                    switch (nextResponse) {
+                        case SOHEADER:
+                            // make the response object, and fill it
+                            try {
+                                switch (protocol.getNextStarterHeader()) {
+                                    case Q_PARSE:
+                                        throw new MCLParseException("Q_PARSE header not allowed here", 1);
+                                    case Q_TABLE:
+                                    case Q_PREPARE: {
+                                        res = protocol.getNextResultSetResponse(MonetConnection.this, ResponseList.this, seqnr);
+                                        ResultSetResponse<?> rsreponse = (ResultSetResponse<?>) res;
+                                        // only add this resultset to
+                                        // the hashmap if it can possibly
+                                        // have an additional datablock
+                                        if (rsreponse.getRowcount() < rsreponse.getTuplecount()) {
+                                            if (rsresponses == null) {
+                                                rsresponses = new HashMap<>();
+                                            }
+                                            rsresponses.put(rsreponse.getId(), rsreponse);
+                                        }
+                                    } break;
+                                    case Q_UPDATE:
+                                        res = protocol.getNextUpdateResponse();
+                                        break;
+                                    case Q_SCHEMA:
+                                        res = protocol.getNextSchemaResponse();
+                                        break;
+                                    case Q_TRANS:
+                                        res = protocol.getNextAutoCommitResponse();
+                                        boolean isAutoCommit = ((AutoCommitResponse)res).isAutocommit();
+
+                                        if (MonetConnection.this.getAutoCommit() && isAutoCommit) {
+                                            MonetConnection.this.addWarning("Server enabled auto commit mode while local state already was auto commit.", "01M11");
+                                        }
+                                        MonetConnection.this.autoCommit = isAutoCommit;
+                                        break;
+                                    case Q_BLOCK: {
+                                        DataBlockResponse<?> next = protocol.getNextDatablockResponse(rsresponses);
+                                        if (next == null) {
+                                            error = "M0M12!No ResultSetResponse for a DataBlock found";
+                                            break;
+                                        }
+                                        res = next;
+                                    } break;
+                                }
+                            } catch (MCLParseException e) {
+                                error = "M0M10!error while parsing start of header:\n" + e.getMessage() +
+                                        " found: '" + protocol.getRemainingStringLine(0).charAt(e.getErrorOffset()) + "'" +
+                                        " in: \"" +  protocol.getRemainingStringLine(0) + "\"" + " at pos: " + e.getErrorOffset();
+                                // flush all the rest
+                                protocol.waitUntilPrompt();
+                                nextResponse = protocol.getCurrentServerResponseHeader();
+                                break;
+                            }
+
+                            // immediately handle errors after parsing
+                            // the header (res may be null)
+                            if (error != null) {
+                               protocol.waitUntilPrompt();
+                               nextResponse = protocol.getCurrentServerResponseHeader();
+                               break;
+                            }
+
+                            // here we have a res object, which
+                            // we can start filling
+                            if(res instanceof IIncompleteResponse) {
+                                IIncompleteResponse iter = (IIncompleteResponse) res;
+                                while (iter.wantsMore()) {
+                                    try {
+                                        protocol.fetchNextResponseData();
+                                        iter.addLine(protocol.getCurrentServerResponseHeader(), protocol.getCurrentData());
+                                    } catch (MCLParseException ex) {
+                                        // right, some protocol violation,
+                                        // skip the rest of the result
+                                        error = "M0M10!" + ex.getMessage();
+                                        protocol.waitUntilPrompt();
+                                        nextResponse = protocol.getCurrentServerResponseHeader();
+                                        break;
+                                    }
+                                }
+                            }
+
+                            if (error != null) {
+                                break;
+                            }
+                            // it is of no use to store
+                            // DataBlockResponses, you never want to
+                            // retrieve them directly anyway
+                            if (!(res instanceof DataBlockResponse)) {
+                                responses.add(res);
+                            }
+
+                            // read the next line (can be prompt, new
+                            // result, error, etc.) before we start the
+                            // loop over
+                            protocol.fetchNextResponseData();
+                            nextResponse = protocol.getCurrentServerResponseHeader();
+                            break;
+                        case INFO:
+                            addWarning(protocol.getRemainingStringLine(1), "01000");
+
+                            // read the next line (can be prompt, new
+                            // result, error, etc.) before we start the
+                            // loop over
+                            protocol.fetchNextResponseData();
+                            nextResponse = protocol.getCurrentServerResponseHeader();
+                            break;
+                        case ERROR:
+                            // read everything till the prompt (should be
+                            // error) we don't know if we ignore some
+                            // garbage here... but the log should reveal
+                            // that
+                            protocol.waitUntilPrompt();
+                            nextResponse = protocol.getCurrentServerResponseHeader();
+                            error = protocol.getRemainingStringLine(1);
+                            break;
+                        default:
+                            throw new SQLException("!M0M10!protocol violation, unexpected line!");
+                    }
+                }
+
+                // if we used the sendThread, make sure it has finished
+                if (sendThread != null) {
+                    String tmp = sendThread.getErrors();
+                    if (tmp != null) {
+                        if (error == null) {
+                            error = "08000!" + tmp;
+                        } else {
+                            error += "\n08000!" + tmp;
+                        }
+                    }
+                }
+                if (error != null) {
+                    SQLException ret = null;
+                    String[] errors = error.split("\n");
+                    for (String error1 : errors) {
+                        if (ret == null) {
+                            ret = new SQLException(error1.substring(6), error1.substring(0, 5));
+                        } else {
+                            ret.setNextException(new SQLException(error1.substring(6), error1.substring(0, 5)));
+                        }
+                    }
+                    throw ret;
+                }
+            } catch (SocketTimeoutException e) {
+                this.close(); // JDBC 4.1 semantics, abort()
+                throw new SQLException("connection timed out", "08M33");
+            } catch (IOException e) {
+                closed = true;
+                throw new SQLException(e.getMessage() + " (mserver still alive?)", "08000");
+            }
+        }
+    }
 }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java
@@ -79,9 +79,7 @@ public class MonetDataSource extends Mon
 	 * @throws SQLException if connecting to the database fails
 	 */
 	@Override
-	public Connection getConnection(String username, String password)
-		throws SQLException
-	{
+	public Connection getConnection(String username, String password) throws SQLException {
 		if (loginTimeout > 0) {
 			/// could enable Socket.setSoTimeout(int timeout) here...
 		}
@@ -92,7 +90,6 @@ public class MonetDataSource extends Mon
 		return driver.connect(url, props);
 	}
 
-
 	/**
 	 * Gets the maximum time in seconds that this data source can wait while
 	 * attempting to connect to a database.
@@ -132,8 +129,7 @@ public class MonetDataSource extends Mon
 	 * @param out a PrintWriter - ignored
 	 */
 	@Override
-	public void setLogWriter(PrintWriter out) {
-	}
+	public void setLogWriter(PrintWriter out) {}
 
 	/**
 	 * Sets the password to use when connecting.  There is no getter
new file mode 100644
--- /dev/null
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java
@@ -0,0 +1,405 @@
+/*
+ * 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 - 2016 MonetDB B.V.
+ */
+
+package nl.cwi.monetdb.jdbc;
+
+import nl.cwi.monetdb.mcl.connection.MonetDBConnectionFactory;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Types;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
+/**
+ * A Driver suitable for the MonetDB database.
+ *
+ * This driver will be used by the DriverManager to determine if an URL
+ * is to be handled by this driver, and if it does, then this driver
+ * will supply a Connection suitable for MonetDB.
+ *
+ * This class has no explicit constructor, the default constructor
+ * generated by the Java compiler will be sufficient since nothing has
+ * to be set in order to use this driver.
+ *
+ * This Driver supports MonetDB database URLs. MonetDB URLs are defined
+ * as:
+ * <tt>jdbc:monetdb://&lt;host&gt;[:&lt;port&gt;]/&lt;database&gt;</tt>
+ * where [:&lt;port&gt;] denotes that a port is optional. If not
+ * given the default (@JDBC_DEF_PORT@) will be used.
+ *
+ * @author Fabian Groffen
+ * @version @JDBC_MAJOR@.@JDBC_MINOR@ (@JDBC_VER_SUFFIX@)
+ */
+final public class MonetDriver implements Driver {
+	// the url kind will be jdbc:monetdb://<host>[:<port>]/<database>
+	// Chapter 9.2.1 from Sun JDBC 3.0 specification
+	/** The prefix of a MonetDB url */
+	private static final String MONETURL = "jdbc:monetdb://";
+	/** Major version of this driver */
+	private static final int DRIVERMAJOR = 4;
+	/** Minor version of this driver */
+	private static final int DRIVERMINOR = 1;
+	/** Version suffix string */
+	private static final String DRIVERVERSIONSUFFIX =
+		"@JDBC_VER_SUFFIX@ based on MCL v@MCL_MAJOR@.@MCL_MINOR@";
+	// We're not fully compliant, but what we support is compliant
+	/** Whether this driver is JDBC compliant or not */
+	private static final boolean MONETJDBCCOMPLIANT = false;
+
+	/** MonetDB default port to connect to */
+	private static final String PORT = "50000";
+
+	public static String getPORT() {
+		return PORT;
+	}
+
+	// initialize this class: register it at the DriverManager
+	// Chapter 9.2 from Sun JDBC 3.0 specification
+	static {
+		try {
+			DriverManager.registerDriver(new MonetDriver());
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	//== methods of interface Driver
+
+	/**
+	 * Retrieves whether the driver thinks that it can open a connection to the
+	 * given URL. Typically drivers will return true if they understand the
+	 * subprotocol specified in the URL and false if they do not.
+	 *
+	 * @param url the URL of the database
+	 * @return true if this driver understands the given URL; false otherwise
+	 */
+	public boolean acceptsURL(String url) {
+		return url != null && url.startsWith(MONETURL);
+	}
+
+	/**
+	 * Attempts to make a database connection to the given URL. The driver
+	 * should return "null" if it realizes it is the wrong kind of driver to
+	 * connect to the given URL. This will be common, as when the JDBC driver
+	 * manager is asked to connect to a given URL it passes the URL to each
+	 * loaded driver in turn.
+	 *
+	 * The driver should throw an SQLException if it is the right driver to
+	 * connect to the given URL but has trouble connecting to the database.
+	 *
+	 * The java.util.Properties argument can be used to pass arbitrary string
+	 * tag/value pairs as connection arguments. Normally at least "user" and
+	 * "password" properties should be included in the Properties object.
+	 *
+	 * @param url the URL of the database to which to connect
+	 * @param info a list of arbitrary string tag/value pairs as connection
+	 *        arguments. Normally at least a "user" and "password" property
+	 *        should be included
+	 * @return a Connection object that represents a connection to the URL
+	 * @throws SQLException if a database access error occurs
+	 */
+	public Connection connect(String url, Properties info) throws SQLException {
+		int tmp;
+		Properties props = new Properties();
+		props.put("port", PORT);
+		props.putAll(info);
+		info = props;
+
+		// url should be of style jdbc:monetdb://<host>/<database>
+		if (!acceptsURL(url))
+			throw new SQLException("Invalid URL: it does not start with: " + MONETURL, "08M26");
+
+		// remove leading "jdbc:" so the rest is a valid hierarchical URI
+		URI uri;
+		try {
+			uri = new URI(url.substring(5));
+		} catch (URISyntaxException e) {
+			throw new SQLException(e.toString(), "08M26");
+		}
+
+		String uri_host = uri.getHost();
+		if (uri_host == null)
+			throw new SQLException("Invalid URL: no hostname given or unparsable in '" + url + "'", "08M26");
+		info.put("host", uri_host);
+
+		int uri_port = uri.getPort();
+		if (uri_port > 0)
+			info.put("port", "" + uri_port);
+
+		// check the database
+		String uri_path = uri.getPath();
+		if (uri_path != null && uri_path.length() != 0) {
+			uri_path = uri_path.substring(1);
+			if (!uri_path.trim().isEmpty())
+				info.put("database", uri_path);
+		}
+
+		String uri_query = uri.getQuery();
+		if (uri_query != null) {
+			// handle additional arguments
+			String args[] = uri_query.split("&");
+			for (String arg : args) {
+				tmp = arg.indexOf('=');
+				if (tmp > 0)
+					info.put(arg.substring(0, tmp), arg.substring(tmp + 1));
+			}
+		}
+
+		// finally return the Connection as requested
+		return MonetDBConnectionFactory.CreateMonetDBJDBCConnection(info);
+	}
+
+	/**
+	 * Retrieves the driver's major version number. Initially this should be 1.
+	 *
+	 * @return this driver's major version number
+	 */
+	public int getMajorVersion() {
+		return DRIVERMAJOR;
+	}
+
+	/**
+	 * Gets the driver's minor version number. Initially this should be 0.
+	 *
+	 * @return this driver's minor version number
+	 */
+	public int getMinorVersion() {
+		return DRIVERMINOR;
+	}
+
+	/**
+	 * Gets information about the possible properties for this driver.
+	 *
+	 * The getPropertyInfo method is intended to allow a generic GUI tool to
+	 * discover what properties it should prompt a human for in order to get
+	 * enough information to connect to a database. Note that depending on the
+	 * values the human has supplied so far, additional values may become
+	 * necessary, so it may be necessary to iterate though several calls to the
+	 * 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.
+	 */
+	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
+		if (!acceptsURL(url))
+			return null;
+
+		List<DriverPropertyInfo> props = new ArrayList<>();
+
+		DriverPropertyInfo prop = new DriverPropertyInfo("user", info.getProperty("user"));
+		prop.required = true;
+		prop.description = "The user loginname to use when authenticating on the database server";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("password", info.getProperty("password"));
+		prop.required = true;
+		prop.description = "The password to use when authenticating on the database server";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("debug", "false");
+		prop.required = false;
+		prop.description = "Whether or not to create a log file for debugging purposes";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("logfile", "");
+		prop.required = false;
+		prop.description = "The filename to write the debug log to. Only takes effect if debug is set to true. If the file exists, an incrementing number is added, till the filename is unique.";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("language", "sql");
+		prop.required = false;
+		prop.description = "What language to use for MonetDB conversations (experts only)";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("hash", "");
+		prop.required = false;
+		prop.description = "Force the use of the given hash algorithm during challenge response (one of SHA1, MD5, plain)";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("follow_redirects", "true");
+		prop.required = false;
+		prop.description = "Whether redirects issued by the server should be followed";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("treat_blob_as_binary", "false");
+		prop.required = false;
+		prop.description = "Whether BLOBs on the server should be treated as BINARY types, thus mapped to byte[]";
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("so_timeout", "0");
+		prop.required = false;
+		prop.description = "Defines the maximum time to wait in milliseconds on a blocking read socket call"; // this corresponds to the Connection.setNetworkTimeout() method introduced in JDBC 4.1
+		props.add(prop);
+
+		prop = new DriverPropertyInfo("embedded", "false");
+		prop.required = false;
+		prop.description = "Whether or not to use an embedded MonetDB connection";
+		props.add(prop);
+
+		DriverPropertyInfo[] dpi = new DriverPropertyInfo[props.size()];
+		return props.toArray(dpi);
+	}
+
+	/**
+	 * Reports whether this driver is a genuine JDBC Compliant&tm; driver. A
+	 * driver may only report true here if it passes the JDBC compliance tests;
+	 * otherwise it is required to return false.
+	 *
+	 * JDBC compliance requires full support for the JDBC API and full support
+	 * for SQL 92 Entry Level. It is expected that JDBC compliant drivers will
+	 * be available for all the major commercial databases.
+	 *
+	 * This method is not intended to encourage the development of non-JDBC
+	 * compliant drivers, but is a recognition of the fact that some vendors are
+	 * interested in using the JDBC API and framework for lightweight databases
+	 * that do not support full database functionality, or for special databases
+	 * such as document information retrieval where a SQL implementation may not
+	 * be feasible.
+	 *
+	 * @return true if this driver is JDBC Compliant; false otherwise
+	 */
+	public boolean jdbcCompliant() {
+		return MONETJDBCCOMPLIANT;
+	}
+
+	//== 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<>();
+	static {
+		// fill the typeMap once
+		// typeMap.put("any", Integer.valueOf(Types.???));
+		typeMap.put("bigint", Types.BIGINT);
+		typeMap.put("blob", Types.BLOB);
+		typeMap.put("boolean", Types.BOOLEAN);
+		typeMap.put("char", Types.CHAR);
+		typeMap.put("clob", Types.CLOB);
+		typeMap.put("date", Types.DATE);
+		typeMap.put("decimal", Types.DECIMAL);
+		typeMap.put("double", Types.DOUBLE);
+		typeMap.put("geometry", Types.VARCHAR);
+		typeMap.put("geometrya", Types.VARCHAR);
+		typeMap.put("hugeint", Types.NUMERIC);
+		typeMap.put("inet", Types.VARCHAR);
+		typeMap.put("int", Types.INTEGER);
+		typeMap.put("json", Types.VARCHAR);
+		// typeMap.put("mbr", Integer.valueOf(Types.???));
+		typeMap.put("month_interval", Types.INTEGER);
+		typeMap.put("oid", Types.BIGINT);
+		// typeMap.put("ptr", Integer.valueOf(Types.???));
+		typeMap.put("real", Types.REAL);
+		typeMap.put("sec_interval", Types.DECIMAL);
+		typeMap.put("smallint", Types.SMALLINT);
+		// typeMap.put("table", Integer.valueOf(Types.???));
+		typeMap.put("time", Types.TIME);
+		typeMap.put("timestamp", Types.TIMESTAMP);
+		typeMap.put("timestamptz", Types.TIMESTAMP);
+// new in Java 8: Types.TIMESTAMP_WITH_TIMEZONE (value 2014). Can't use it yet as we compile for java 7
+		typeMap.put("timetz", Types.TIME);
+// new in Java 8: Types.TIME_WITH_TIMEZONE (value 2013). Can't use it yet as we compile for java 7
+		typeMap.put("tinyint", Types.TINYINT);
+		typeMap.put("url", Types.VARCHAR);
+		typeMap.put("uuid", Types.VARCHAR);
+		typeMap.put("varchar", Types.VARCHAR);
+		typeMap.put("wrd", Types.BIGINT);
+	}
+
+	/**
+	 * Returns the java.sql.Types equivalent of the given MonetDB type.
+	 *
+	 * @param type the type as used by MonetDB
+	 * @return the mathing java.sql.Types constant or java.sql.Types.OTHER if
+	 *         nothing matched on the given string
+	 */
+	static int getJavaType(String type) {
+		// match the currentColumns type on a java.sql.Types constant
+		Integer tp = typeMap.get(type);
+		if (tp != null) {
+			return tp;
+		} else {
+			// this should not be able to happen
+			// do not assert, since maybe future versions introduce
+			// new types
+			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.
+	 *
+	 * @param column a String representing the value that should be evaluated
+	 *               in the SQL CASE statement
+	 * @return a SQL CASE statement
+	 */
+	static String getSQLTypeMap(String column) {
+		if (TypeMapppingSQL == null) {
+			// first time, compose TypeMappping SQL string
+			StringBuilder val = new StringBuilder((typeMap.size() * (7 + 7 + 7 + 4)) + 14);
+			for (Entry<String, Integer> entry : typeMap.entrySet()) {
+				val.append(" WHEN '").append(entry.getKey()).append("' THEN ").append(entry.getValue().toString());
+			}
+			val.append(" ELSE ").append(Types.OTHER).append(" END");
+			// as the typeMap is static, cache this SQL part for all next calls
+			TypeMapppingSQL = val.toString();
+		}
+		return "CASE " + column + TypeMapppingSQL;
+	}
+
+	/**
+	 * Returns a touched up identifying version string of this driver.
+	 *
+	 * @return the version string
+	 */
+	public static String getDriverVersion() {
+		return "" + DRIVERMAJOR + "." + DRIVERMINOR + " (" + DRIVERVERSIONSUFFIX + ")";
+	}
+
+	public static int getDriverMajorVersion() {
+		return DRIVERMAJOR;
+	}
+
+	public static int getDriverMinorVersion() {
+		return DRIVERMINOR;
+	}
+
+	/**
+	 * Return the parent Logger of all the Loggers used by this data
+	 * source.  This should be the Logger farthest from the root Logger
+	 * that is still an ancestor of all of the Loggers used by this data
+	 * source.  Configuring this Logger will affect all of the log
+	 * messages generated by the data source. In the worst case, this
+	 * may be the root Logger.
+	 *
+	 * @return the parent Logger for this data source
+	 * @throws SQLFeatureNotSupportedException if the data source does
+	 *         not use java.util.logging
+	 */
+	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+		throw new SQLFeatureNotSupportedException("java.util.logging not in use", "0A000");
+	}
+}
deleted file mode 100644
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in
+++ /dev/null
@@ -1,403 +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 - 2016 MonetDB B.V.
- */
-
-package nl.cwi.monetdb.jdbc;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.DriverPropertyInfo;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.logging.Logger;
-
-/**
- * A Driver suitable for the MonetDB database.
- *
- * This driver will be used by the DriverManager to determine if an URL
- * is to be handled by this driver, and if it does, then this driver
- * will supply a Connection suitable for MonetDB.
- *
- * This class has no explicit constructor, the default constructor
- * generated by the Java compiler will be sufficient since nothing has
- * to be set in order to use this driver.
- *
- * This Driver supports MonetDB database URLs. MonetDB URLs are defined
- * as:
- * <tt>jdbc:monetdb://&lt;host&gt;[:&lt;port&gt;]/&lt;database&gt;</tt>
- * where [:&lt;port&gt;] denotes that a port is optional. If not
- * given the default (@JDBC_DEF_PORT@) will be used.
- *
- * @author Fabian Groffen
- * @version @JDBC_MAJOR@.@JDBC_MINOR@ (@JDBC_VER_SUFFIX@)
- */
-final public class MonetDriver implements Driver {
-	// the url kind will be jdbc:monetdb://<host>[:<port>]/<database>
-	// Chapter 9.2.1 from Sun JDBC 3.0 specification
-	/** The prefix of a MonetDB url */
-	private static final String MONETURL = "jdbc:monetdb://";
-	/** Major version of this driver */
-	private static final int DRIVERMAJOR = @JDBC_MAJOR@;
-	/** Minor version of this driver */
-	private static final int DRIVERMINOR = @JDBC_MINOR@;
-	/** Version suffix string */
-	private static final String DRIVERVERSIONSUFFIX =
-		"@JDBC_VER_SUFFIX@ based on MCL v@MCL_MAJOR@.@MCL_MINOR@";
-	// We're not fully compliant, but what we support is compliant
-	/** Whether this driver is JDBC compliant or not */
-	private static final boolean MONETJDBCCOMPLIANT = false;
-
-	/** MonetDB default port to connect to */
-	private static final String PORT = "@JDBC_DEF_PORT@";
-
-
-	// initialize this class: register it at the DriverManager
-	// Chapter 9.2 from Sun JDBC 3.0 specification
-	static {
-		try {
-			DriverManager.registerDriver(new MonetDriver());
-		} catch (SQLException e) {
-			e.printStackTrace();
-		}
-	}
-
-	//== methods of interface Driver
-
-	/**
-	 * Retrieves whether the driver thinks that it can open a connection to the
-	 * given URL. Typically drivers will return true if they understand the
-	 * subprotocol specified in the URL and false if they do not.
-	 *
-	 * @param url the URL of the database
-	 * @return true if this driver understands the given URL; false otherwise
-	 */
-	public boolean acceptsURL(String url) {
-		return url != null && url.startsWith(MONETURL);
-	}
-
-	/**
-	 * Attempts to make a database connection to the given URL. The driver
-	 * should return "null" if it realizes it is the wrong kind of driver to
-	 * connect to the given URL. This will be common, as when the JDBC driver
-	 * manager is asked to connect to a given URL it passes the URL to each
-	 * loaded driver in turn.
-	 *
-	 * The driver should throw an SQLException if it is the right driver to
-	 * connect to the given URL but has trouble connecting to the database.
-	 *
-	 * The java.util.Properties argument can be used to pass arbitrary string
-	 * tag/value pairs as connection arguments. Normally at least "user" and
-	 * "password" properties should be included in the Properties object.
-	 *
-	 * @param url the URL of the database to which to connect
-	 * @param info a list of arbitrary string tag/value pairs as connection
-	 *        arguments. Normally at least a "user" and "password" property
-	 *        should be included
-	 * @return a Connection object that represents a connection to the URL
-	 * @throws SQLException if a database access error occurs
-	 */
-	public Connection connect(String url, Properties info)
-		throws SQLException
-	{
-		int tmp;
-		Properties props = new Properties();
-		// set the optional properties and their defaults here
-		props.put("port", PORT);
-		props.put("debug", "false");
-		props.put("language", "sql");	// mal, sql, <future>
-		props.put("so_timeout", "0");
-
-		props.putAll(info);
-		info = props;
-
-		// url should be of style jdbc:monetdb://<host>/<database>
-		if (!acceptsURL(url))
-			throw new SQLException("Invalid URL: it does not start with: " + MONETURL, "08M26");
-
-		// remove leading "jdbc:" so the rest is a valid hierarchical URI
-		URI uri;
-		try {
-			uri = new URI(url.substring(5));
-		} catch (URISyntaxException e) {
-			throw new SQLException(e.toString(), "08M26");
-		}
-
-		String uri_host = uri.getHost();
-		if (uri_host == null)
-			throw new SQLException("Invalid URL: no hostname given or unparsable in '" + url + "'", "08M26");
-		info.put("host", uri_host);
-
-		int uri_port = uri.getPort();
-		if (uri_port > 0)
-			info.put("port", "" + uri_port);
-
-		// check the database
-		String uri_path = uri.getPath();
-		if (uri_path != null && uri_path.length() != 0) {
-			uri_path = uri_path.substring(1);
-			if (!uri_path.trim().isEmpty())
-				info.put("database", uri_path);
-		}
-
-		String uri_query = uri.getQuery();
-		if (uri_query != null) {
-			// handle additional arguments
-			String args[] = uri_query.split("&");
-			for (int i = 0; i < args.length; i++) {
-				tmp = args[i].indexOf('=');
-				if (tmp > 0)
-					info.put(args[i].substring(0, tmp), args[i].substring(tmp + 1));
-			}
-		}
-
-		// finally return the Connection as requested
-		return new MonetConnection(info);
-	}
-
-	/**
-	 * Retrieves the driver's major version number. Initially this should be 1.
-	 *
-	 * @return this driver's major version number
-	 */
-	public int getMajorVersion() {
-		return DRIVERMAJOR;
-	}
-
-	/**
-	 * Gets the driver's minor version number. Initially this should be 0.
-	 *
-	 * @return this driver's minor version number
-	 */
-	public int getMinorVersion() {
-		return DRIVERMINOR;
-	}
-
-	/**
-	 * Gets information about the possible properties for this driver.
-	 *
-	 * The getPropertyInfo method is intended to allow a generic GUI tool to
-	 * discover what properties it should prompt a human for in order to get
-	 * enough information to connect to a database. Note that depending on the
-	 * values the human has supplied so far, additional values may become
-	 * necessary, so it may be necessary to iterate though several calls to the
-	 * 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.
-	 */
-	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
-		if (!acceptsURL(url))
-			return null;
-
-		List<DriverPropertyInfo> props = new ArrayList<DriverPropertyInfo>();
-
-		DriverPropertyInfo prop = new DriverPropertyInfo("user", info.getProperty("user"));
-		prop.required = true;
-		prop.description = "The user loginname to use when authenticating on the database server";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("password", info.getProperty("password"));
-		prop.required = true;
-		prop.description = "The password to use when authenticating on the database server";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("debug", "false");
-		prop.required = false;
-		prop.description = "Whether or not to create a log file for debugging purposes";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("logfile", "");
-		prop.required = false;
-		prop.description = "The filename to write the debug log to. Only takes effect if debug is set to true. If the file exists, an incrementing number is added, till the filename is unique.";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("language", "sql");
-		prop.required = false;
-		prop.description = "What language to use for MonetDB conversations (experts only)";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("hash", "");
-		prop.required = false;
-		prop.description = "Force the use of the given hash algorithm during challenge response (one of SHA1, MD5, plain)";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("follow_redirects", "true");
-		prop.required = false;
-		prop.description = "Whether redirects issued by the server should be followed";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("treat_blob_as_binary", "false");
-		prop.required = false;
-		prop.description = "Whether BLOBs on the server should be treated as BINARY types, thus mapped to byte[]";
-		props.add(prop);
-
-		prop = new DriverPropertyInfo("so_timeout", "0");
-		prop.required = false;
-		prop.description = "Defines the maximum time to wait in milliseconds on a blocking read socket call"; // this corresponds to the Connection.setNetworkTimeout() method introduced in JDBC 4.1
-		props.add(prop);
-
-		DriverPropertyInfo[] dpi = new DriverPropertyInfo[props.size()];
-		return props.toArray(dpi);
-	}
-
-	/**
-	 * Reports whether this driver is a genuine JDBC Compliant&tm; driver. A
-	 * driver may only report true here if it passes the JDBC compliance tests;
-	 * otherwise it is required to return false.
-	 *
-	 * JDBC compliance requires full support for the JDBC API and full support
-	 * for SQL 92 Entry Level. It is expected that JDBC compliant drivers will
-	 * be available for all the major commercial databases.
-	 *
-	 * This method is not intended to encourage the development of non-JDBC
-	 * compliant drivers, but is a recognition of the fact that some vendors are
-	 * interested in using the JDBC API and framework for lightweight databases
-	 * that do not support full database functionality, or for special databases
-	 * such as document information retrieval where a SQL implementation may not
-	 * be feasible.
-	 *
-	 * @return true if this driver is JDBC Compliant; false otherwise
-	 */
-	public boolean jdbcCompliant() {
-		return MONETJDBCCOMPLIANT;
-	}
-
-	//== 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 java.util.Map<String, Integer> typeMap = new java.util.HashMap<String, Integer>();
-	static {
-		// fill the typeMap once
-		// typeMap.put("any", Integer.valueOf(Types.???));
-		typeMap.put("bigint", Integer.valueOf(Types.BIGINT));
-		typeMap.put("blob", Integer.valueOf(Types.BLOB));
-		typeMap.put("boolean", Integer.valueOf(Types.BOOLEAN));
-		typeMap.put("char", Integer.valueOf(Types.CHAR));
-		typeMap.put("clob", Integer.valueOf(Types.CLOB));
-		typeMap.put("date", Integer.valueOf(Types.DATE));
-		typeMap.put("decimal", Integer.valueOf(Types.DECIMAL));
-		typeMap.put("double", Integer.valueOf(Types.DOUBLE));
-		// typeMap.put("geometry", Integer.valueOf(Types.???));
-		// typeMap.put("geometrya", Integer.valueOf(Types.???));
-		typeMap.put("hugeint", Integer.valueOf(Types.NUMERIC));
-		typeMap.put("inet", Integer.valueOf(Types.VARCHAR));
-		typeMap.put("int", Integer.valueOf(Types.INTEGER));
-		typeMap.put("json", Integer.valueOf(Types.VARCHAR));
-		// typeMap.put("mbr", Integer.valueOf(Types.???));
-		typeMap.put("month_interval", Integer.valueOf(Types.INTEGER));
-		typeMap.put("oid", Integer.valueOf(Types.BIGINT));
-		// typeMap.put("ptr", Integer.valueOf(Types.???));
-		typeMap.put("real", Integer.valueOf(Types.REAL));
-		typeMap.put("sec_interval", Integer.valueOf(Types.DECIMAL));
-		typeMap.put("smallint", Integer.valueOf(Types.SMALLINT));
-		// typeMap.put("table", Integer.valueOf(Types.???));
-		typeMap.put("time", Integer.valueOf(Types.TIME));
-		typeMap.put("timestamp", Integer.valueOf(Types.TIMESTAMP));
-		typeMap.put("timestamptz", Integer.valueOf(Types.TIMESTAMP));
-// new in Java 8: Types.TIMESTAMP_WITH_TIMEZONE (value 2014). Can't use it yet as we compile for java 7
-		typeMap.put("timetz", Integer.valueOf(Types.TIME));
-// new in Java 8: Types.TIME_WITH_TIMEZONE (value 2013). Can't use it yet as we compile for java 7
-		typeMap.put("tinyint", Integer.valueOf(Types.TINYINT));
-		typeMap.put("url", Integer.valueOf(Types.VARCHAR));
-		typeMap.put("uuid", Integer.valueOf(Types.VARCHAR));
-		typeMap.put("varchar", Integer.valueOf(Types.VARCHAR));
-		typeMap.put("wrd", Integer.valueOf(Types.BIGINT));
-	}
-
-	/**
-	 * Returns the java.sql.Types equivalent of the given MonetDB type.
-	 *
-	 * @param type the type as used by MonetDB
-	 * @return the mathing java.sql.Types constant or java.sql.Types.OTHER if
-	 *         nothing matched on the given string
-	 */
-	static int getJavaType(String type) {
-		// match the column type on a java.sql.Types constant
-		Integer tp = typeMap.get(type);
-		if (tp != null) {
-			return tp.intValue();
-		} else {
-			// this should not be able to happen
-			// do not assert, since maybe future versions introduce
-			// new types
-			return Types.OTHER;
-		}
-	}
-
-	/**
-	 * 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 column name (or expression) string.
-	 *
-	 * @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
-			StringBuilder val = new StringBuilder((typeMap.size() * (7 + 7 + 7 + 4)) + 14);
-			for (Entry<String, Integer> entry : typeMap.entrySet()) {
-				val.append(" WHEN '").append(entry.getKey()).append("' THEN ").append(entry.getValue().toString());
-			}
-			val.append(" ELSE ").append(Types.OTHER).append(" END");
-			// as the typeMap is static, cache this SQL part for all next calls
-			TypeMapppingSQL = val.toString();
-		}
-		return "CASE " + column + TypeMapppingSQL;
-	}
-
-	/**
-	 * Returns a touched up identifying version string of this driver.
-	 *
-	 * @return the version string
-	 */
-	public static String getDriverVersion() {
-		return "" + DRIVERMAJOR + "." + DRIVERMINOR + " (" + DRIVERVERSIONSUFFIX + ")";
-	}
-
-	public static int getDriverMajorVersion() {
-		return DRIVERMAJOR;
-	}
-
-	public static int getDriverMinorVersion() {
-		return DRIVERMINOR;
-	}
-
-	/**
-	 * Return the parent Logger of all the Loggers used by this data
-	 * source.  This should be the Logger farthest from the root Logger
-	 * that is still an ancestor of all of the Loggers used by this data
-	 * source.  Configuring this Logger will affect all of the log
-	 * messages generated by the data source. In the worst case, this
-	 * may be the root Logger.
-	 *
-	 * @return the parent Logger for this data source
-	 * @throws SQLFeatureNotSupportedException if the data source does
-	 *         not use java.util.logging
-	 */
-	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
-		throw new SQLFeatureNotSupportedException("java.util.logging not in use", "0A000");
-	}
-}
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java
@@ -8,6 +8,8 @@
 
 package nl.cwi.monetdb.jdbc;
 
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
+
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.IOException;
@@ -63,10 +65,7 @@ import java.util.Map;
  * @author Fabian Groffen, Martin van Dinther
  * @version 0.4
  */
-public class MonetPreparedStatement
-	extends MonetStatement
-	implements PreparedStatement
-{
+public class MonetPreparedStatement extends MonetStatement implements PreparedStatement {
 	private final String[] monetdbType;
 	private final int[] javaType;
 	private final int[] digits;
@@ -84,20 +83,15 @@ public class MonetPreparedStatement
 
 	/* only parse the date patterns once, use multiple times */
 	/** Format of a timestamp with RFC822 time zone */
-	final SimpleDateFormat mTimestampZ =
-		new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
+	private final SimpleDateFormat mTimestampZ = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
 	/** Format of a timestamp */
-	final SimpleDateFormat mTimestamp =
-		new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+	private final SimpleDateFormat mTimestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 	/** Format of a time with RFC822 time zone */
-	final SimpleDateFormat mTimeZ =
-		new SimpleDateFormat("HH:mm:ss.SSSZ");
+	private final SimpleDateFormat mTimeZ = new SimpleDateFormat("HH:mm:ss.SSSZ");
 	/** Format of a time */
-	final SimpleDateFormat mTime =
-		new SimpleDateFormat("HH:mm:ss.SSS");
+	private final SimpleDateFormat mTime = new SimpleDateFormat("HH:mm:ss.SSS");
 	/** Format of a date used by mserver */
-	final SimpleDateFormat mDate =
-		new SimpleDateFormat("yyyy-MM-dd");
+	private final SimpleDateFormat mDate = new SimpleDateFormat("yyyy-MM-dd");
 
 	/**
 	 * MonetPreparedStatement constructor which checks the arguments for
@@ -112,28 +106,17 @@ public class MonetPreparedStatement
 	 * @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");
 
 		// cheat a bit to get the ID and the number of columns
-		id = ((MonetConnection.ResultSetResponse)header).id;
-		size = ((MonetConnection.ResultSetResponse)header).tuplecount;
-		rscolcnt = ((MonetConnection.ResultSetResponse)header).columncount;
+		id = ((ResultSetResponse)header).getId();
+		size = ((ResultSetResponse)header).getTuplecount();
+		rscolcnt = ((ResultSetResponse)header).getColumncount();
 
 		// initialise blank finals
 		monetdbType = new String[size];
@@ -166,47 +149,6 @@ public class MonetPreparedStatement
 		poolable = true;
 	}
 
-	/**
-	 * 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;
-
-		this.connection = connection;
-	}
-	*/
-
 	//== methods interface PreparedStatement
 
 	/**
@@ -424,8 +366,7 @@ public class MonetPreparedStatement
 						String monettype = getColumnTypeName(column);
 						if (monettype != null) {
 							// data of type inet or uuid is not case sensitive
-							if ("inet".equals(monettype)
-							 || "uuid".equals(monettype))
+							if ("inet".equals(monettype) || "uuid".equals(monettype))
 								return false;
 						}
 						return true;
@@ -912,7 +853,7 @@ public class MonetPreparedStatement
 				Map<String,Class<?>> map = getConnection().getTypeMap();
 				Class<?> c;
 				if (map.containsKey(typeName)) {
-					c = (Class)map.get(typeName);
+					c = map.get(typeName);
 				} else {
 					c = MonetResultSet.getClassForType(getParameterType(param));
 				}
@@ -970,9 +911,7 @@ public class MonetPreparedStatement
 	 *         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");
 	}
 
@@ -993,9 +932,7 @@ public class MonetPreparedStatement
 	 * @throws SQLException if a database access error occurs
 	 */
 	@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");
 	}
 
@@ -1019,9 +956,7 @@ public class MonetPreparedStatement
 	 *         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");
 	}
 
@@ -1077,9 +1012,7 @@ public class MonetPreparedStatement
 	 *         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");
 	}
 
@@ -1101,9 +1034,7 @@ public class MonetPreparedStatement
 	 *         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");
 	}
 
@@ -1125,9 +1056,7 @@ public class MonetPreparedStatement
 	 *         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");
 	}
 
@@ -1254,12 +1183,7 @@ public class MonetPreparedStatement
 	 * @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;
@@ -1292,9 +1216,7 @@ public class MonetPreparedStatement
 	 *         not support this method
 	 */
 	@Override
-	public void setCharacterStream(int parameterIndex, Reader reader)
-		throws SQLException
-	{
+	public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
 		setCharacterStream(parameterIndex, reader, 0);
 	}
 
@@ -1315,12 +1237,7 @@ public class MonetPreparedStatement
 	 * @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);
 	}
@@ -1424,9 +1341,7 @@ public class MonetPreparedStatement
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public void setDate(int parameterIndex, java.sql.Date x)
-		throws SQLException
-	{
+	public void setDate(int parameterIndex, java.sql.Date x) throws SQLException {
 		setDate(parameterIndex, x, null);
 	}
 
@@ -1445,9 +1360,7 @@ public class MonetPreparedStatement
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public void setDate(int parameterIndex, java.sql.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;
@@ -1544,9 +1457,7 @@ public class MonetPreparedStatement
 	 *         not support this method
 	 */
 	@Override
-	public void setNCharacterStream(int i, Reader value, long length)
-		throws SQLException
-	{
+	public void setNCharacterStream(int i, Reader value, long length) throws SQLException {
 		throw newSQLFeatureNotSupportedException("setNCharacterStream");
 	}
 
@@ -1663,9 +1574,7 @@ public class MonetPreparedStatement
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public void setNull(int paramIndex, int sqlType, String typeName)
-		throws SQLException
-	{
+	public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException {
 		// MonetDB/SQL's NULL needs no type
 		setNull(paramIndex, sqlType);
 	}
@@ -1714,9 +1623,7 @@ public class MonetPreparedStatement
 	 * @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);
 	}
 
@@ -1754,13 +1661,7 @@ public class MonetPreparedStatement
 	 * @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 {
 		// this is according to table B-5
 		if (x instanceof String) {
 			switch (targetSqlType) {
@@ -1874,14 +1775,7 @@ public class MonetPreparedStatement
 				default:
 					throw new SQLException("Conversion not allowed", "M1M05");
 			}
-		} else if (x instanceof BigDecimal ||
-				x instanceof Byte ||
-				x instanceof Short ||
-				x instanceof Integer ||
-				x instanceof Long ||
-				x instanceof Float ||
-				x instanceof Double)
-		{
+		} else if (x instanceof BigDecimal || x instanceof Byte || x instanceof Short || x instanceof Integer || x instanceof Long || x instanceof Float || x instanceof Double) {
 			Number num = (Number)x;
 			switch (targetSqlType) {
 				case Types.TINYINT:
@@ -2002,12 +1896,7 @@ public class MonetPreparedStatement
 				default:
 					throw new SQLException("Conversion not allowed", "M1M05");
 			}
-		} else if (x instanceof java.sql.Date ||
-				x instanceof Timestamp ||
-				x instanceof Time ||
-				x instanceof Calendar ||
-				x instanceof java.util.Date)
-		{
+		} else if (x instanceof java.sql.Date || x instanceof Timestamp || x instanceof Time || x instanceof Calendar || x instanceof java.util.Date) {
 			switch (targetSqlType) {
 				case Types.CHAR:
 				case Types.VARCHAR:
@@ -2296,10 +2185,7 @@ public class MonetPreparedStatement
 			return;
 		}
 
-		setValue(
-			parameterIndex,
-			"'" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'"
-		);
+		setValue(parameterIndex, "'" + x.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'");
 	}
 
 	/**
@@ -2348,9 +2234,7 @@ public class MonetPreparedStatement
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public void setTime(int index, Time x, Calendar cal)
-		throws SQLException
-	{
+	public void setTime(int index, Time x, Calendar cal) throws SQLException {
 		if (x == null) {
 			setNull(index, -1);
 			return;
@@ -2361,8 +2245,7 @@ public class MonetPreparedStatement
 			// timezone shouldn't matter, since the server is timezone
 			// aware in this case
 			String RFC822 = mTimeZ.format(x);
-			setValue(index, "timetz '" +
-					RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'");
+			setValue(index, "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
@@ -2387,9 +2270,7 @@ public class MonetPreparedStatement
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public void setTimestamp(int index, Timestamp x)
-		throws SQLException
-	{
+	public void setTimestamp(int index, Timestamp x) throws SQLException {
 		setTimestamp(index, x, null);
 	}
 
@@ -2410,9 +2291,7 @@ public class MonetPreparedStatement
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public void setTimestamp(int index, Timestamp x, Calendar cal)
-		throws SQLException
-	{
+	public void setTimestamp(int index, Timestamp x, Calendar cal) throws SQLException {
 		if (x == null) {
 			setNull(index, -1);
 			return;
@@ -2423,8 +2302,7 @@ public class MonetPreparedStatement
 			// timezone shouldn't matter, since the server is timezone
 			// aware in this case
 			String RFC822 = mTimestampZ.format(x);
-			setValue(index, "timestamptz '" +
-					RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'");
+			setValue(index, "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
@@ -2461,9 +2339,7 @@ public class MonetPreparedStatement
 	 */
 	@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");
 	}
 
@@ -2526,7 +2402,7 @@ public class MonetPreparedStatement
 	 * @param val the exact String representation to set
 	 * @throws SQLException if the given index is out of bounds
 	 */
-	void setValue(int index, String val) throws SQLException {
+	private void setValue(int index, String val) throws SQLException {
 		values[getParamIdx(index)] = val;
 	}
 
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
@@ -8,9 +8,11 @@
 
 package nl.cwi.monetdb.jdbc;
 
+import nl.cwi.monetdb.jdbc.types.INET;
+import nl.cwi.monetdb.jdbc.types.URL;
 import nl.cwi.monetdb.mcl.parser.MCLParseException;
-import nl.cwi.monetdb.mcl.parser.TupleLineParser;
-import nl.cwi.monetdb.responses.ResultSetResponse;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -20,12 +22,12 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
 import java.net.MalformedURLException;
-import java.net.URL;
 import java.sql.Array;
 import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
+import java.sql.Date;
 import java.sql.NClob;
 import java.sql.Ref;
 import java.sql.ResultSet;
@@ -43,9 +45,7 @@ import java.sql.Timestamp;
 import java.sql.Types;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Map;
-import java.util.TimeZone;
+import java.util.*;
 
 /**
  * A ResultSet suitable for the MonetDB database.
@@ -69,9 +69,7 @@ import java.util.TimeZone;
  * @version 0.8
  */
 public class MonetResultSet extends MonetWrapper implements ResultSet {
-	// the following have default access modifier for the MonetVirtualResultSet subclass
-	/** The current line of the buffer split in columns */
-	final TupleLineParser tlp;
+
 	/** The current position of the cursor for this ResultSet object */
 	int curRow = 0;
 
@@ -101,6 +99,9 @@ public class MonetResultSet extends Mone
 	/** Just a dummy variable to keep store the fetchsize set. */
 	private int fetchSize;
 
+	final Object[] values;
+
+	private final AbstractProtocol<?> protocol;
 	/**
 	 * Main constructor backed by the given Header.
 	 *
@@ -127,14 +128,14 @@ public class MonetResultSet extends Mone
 
 		// throws SQLException on getters of Header, so we find out immediately
 		// if an error occurred for this query
-		columns = header.getNames();
-		types = header.getTypes();
-		tupleCount = header.tuplecount;
+		this.columns = header.getNames();
+		this.types = header.getTypes();
+		this.tupleCount = header.getTuplecount();
 
 		// create result array
-		tlp = ((MonetConnection)statement.getConnection()).getServer().getTupleLineParser(columns.length);
-
-		JdbcSQLTypes = new int[types.length];
+		this.protocol = ((MonetConnection)statement.getConnection()).getProtocol();
+		this.values = new Object[this.columns.length];
+		this.JdbcSQLTypes = new int[types.length];
 		populateJdbcSQLtypesArray();
 	}
 
@@ -171,12 +172,13 @@ public class MonetResultSet extends Mone
 		this.tupleCount = results;
 
 		try {
-			this.tlp = ((MonetConnection)statement.getConnection()).getServer().getTupleLineParser(columns.length);
+			this.protocol = ((MonetConnection)statement.getConnection()).getProtocol();
 		} catch (SQLException e) {
 			throw new IllegalArgumentException(e);
 		}
 
-		JdbcSQLTypes = new int[types.length];
+		this.values = new Object[this.columns.length];
+		this.JdbcSQLTypes = new int[types.length];
 		populateJdbcSQLtypesArray();
 	}
 
@@ -198,7 +200,6 @@ public class MonetResultSet extends Mone
 		}
 	}
 
-
 	//== methods of interface ResultSet
 
 	// Chapter 14.2.2 Sun JDBC 3.0 Specification
@@ -245,14 +246,14 @@ public class MonetResultSet extends Mone
 		if (row < 0) row = 0;	// before first
 		else if (row > tupleCount + 1) row = tupleCount + 1;	// after last
 
-		String tmpLine = header.getLine(row - 1);
+		Object tmpLine = header.getLine(row - 1);
 
 		// store it
 		curRow = row;
 
 		if (tmpLine == null) return false;
 		try {
-			tlp.parse(tmpLine);
+			protocol.parseTupleLine(tmpLine, this.values);
 		} catch (MCLParseException e) {
 			throw new SQLException(e.getMessage(), "M0M10");
 		}
@@ -462,13 +463,15 @@ public class MonetResultSet extends Mone
 	@Override
 	public Reader getCharacterStream(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
 			}
 			lastReadWasNull = false;
-			return new StringReader(val);
+			return new StringReader((String) val);
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -540,13 +543,15 @@ public class MonetResultSet extends Mone
 	@Override
 	public Blob getBlob(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
 			}
 			lastReadWasNull = false;
-			return MonetBlob.create(val);
+			return new MonetBlob((byte[]) val);
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -581,13 +586,15 @@ public class MonetResultSet extends Mone
 	@Override
 	public Clob getClob(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
 			}
 			lastReadWasNull = false;
-			return new MonetClob(val);
+			return new MonetClob((String) val);
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -656,15 +663,15 @@ public class MonetResultSet extends Mone
 	@Override
 	public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
 			}
 			lastReadWasNull = false;
-			return new BigDecimal(val);
-		} catch (NumberFormatException e) {
-			throw newSQLNumberFormatException(e);
+			return (BigDecimal) val;
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -682,22 +689,20 @@ public class MonetResultSet extends Mone
 	 */
 	@Override
 	@Deprecated
-	public BigDecimal getBigDecimal(int columnIndex, int scale)
-		throws SQLException
-	{
+	public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
 			}
 			lastReadWasNull = false;
 
-			BigDecimal bd = new BigDecimal(val);
+			BigDecimal bd = (BigDecimal) val;
 			bd.setScale(scale);
 			return bd;
-		} catch (NumberFormatException e) {
-			throw newSQLNumberFormatException(e);
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -729,9 +734,7 @@ public class MonetResultSet extends Mone
 	 */
 	@Override
 	@Deprecated
-	public BigDecimal getBigDecimal(String columnName, int scale)
-		throws SQLException
-	{
+	public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
 		return getBigDecimal(findColumn(columnName), scale);
 	}
 
@@ -748,28 +751,26 @@ public class MonetResultSet extends Mone
 	@Override
 	public boolean getBoolean(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return false;	// if the value is SQL NULL, the value returned is false
 			}
 			lastReadWasNull = false;
-
-			// match common cases first
-			if ("false".equalsIgnoreCase(val) || "0".equals(val))
-				return false;
-			if ("true".equalsIgnoreCase(val) || "1".equals(val))
-				return true;
-
 			// match type specific values
 			switch (JdbcSQLTypes[columnIndex - 1]) {
 				case Types.BOOLEAN:
+					return (Boolean) val;
 				case Types.CHAR:
 				case Types.VARCHAR:
 				case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
 				case Types.CLOB:
-					// check if string value equals "true" (case insensitive) or not
-					return Boolean.parseBoolean(val);
+					String other = (String) val;
+					if ("false".equalsIgnoreCase(other) || "0".equals(val))
+						return false;
+					if ("true".equalsIgnoreCase(other) || "1".equals(val))
+						return true;
+					throw newSQLInvalidColumnIndexException(columnIndex);
 				case Types.BIT: // MonetDB doesn't use type BinaryDigit, it's here for completeness
 				case Types.TINYINT:
 				case Types.SMALLINT:
@@ -787,6 +788,8 @@ public class MonetResultSet extends Mone
 				default:
 					throw new SQLException("Conversion from " + types[columnIndex - 1] + " to boolean type not supported", "M1M05");
 			}
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -818,15 +821,15 @@ public class MonetResultSet extends Mone
 	@Override
 	public byte getByte(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return (byte) 0;
 			}
 			lastReadWasNull = false;
-			return Byte.parseByte(val);
-		} catch (NumberFormatException e) {
-			throw newSQLNumberFormatException(e);
+			return (Byte) val;
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -859,7 +862,7 @@ public class MonetResultSet extends Mone
 	@Override
 	public byte[] getBytes(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
@@ -873,19 +876,12 @@ public class MonetResultSet extends Mone
 				case Types.VARBINARY:
 				case Types.LONGVARBINARY:
 					// unpack the HEX (BLOB) notation to real bytes
-					int len = val.length() / 2;
-					byte[] buf = new byte[len];
-					int offset;
-					for (int j = 0; j < len; j++) {
-						offset = j * 2;
-						buf[j] = (byte)Integer.parseInt(val.substring(offset, offset + 2), 16);
-					}
-					return buf;
+					return (byte[]) val;
 				default:
 					throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05");
 			}
-		} catch (NumberFormatException e) {
-			throw newSQLNumberFormatException(e);
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -950,7 +946,7 @@ public class MonetResultSet extends Mone
 	@Override
 	public String getCursorName() throws SQLException {
 		throw new SQLException("Positioned updates not supported for this " +
-				   "cursor (" + (header != null ? header.id : "") + ")", "0AM21");
+				   "cursor (" + (header != null ? header.getId() : "") + ")", "0AM21");
 	}
 
 	/**
@@ -965,15 +961,15 @@ public class MonetResultSet extends Mone
 	@Override
 	public double getDouble(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return 0;
 			}
 			lastReadWasNull = false;
-			return Double.parseDouble(val);
-		} catch (NumberFormatException e) {
-			throw newSQLNumberFormatException(e);
+			return (Double) val;
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -1085,15 +1081,15 @@ public class MonetResultSet extends Mone
 	@Override
 	public float getFloat(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return 0;
 			}
 			lastReadWasNull = false;
-			return Float.parseFloat(val);
-		} catch (NumberFormatException e) {
-			throw newSQLNumberFormatException(e);
+			return (Float) val;
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -1122,30 +1118,33 @@ public class MonetResultSet extends Mone
 	 */
 	@Override
 	public int getInt(int columnIndex) throws SQLException {
-		String val = "";
+		Object val;
 		try {
-			val = tlp.values[columnIndex - 1];
+			val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return 0;
 			}
 			lastReadWasNull = false;
-			return Integer.parseInt(val);
+			return (Integer) val;
 		} catch (NumberFormatException e) {
 			// The oid datatype values (as string) have a  @0  suffix in the string value.
-			// To allow succesful parsing and conversion to int, we need to remove the suffix first
+			// To allow successful parsing and conversion to int, we need to remove the suffix first
 			if ("oid".equals(types[columnIndex - 1])) {
-				int len = val.length();
-				if (len > 2 && val.endsWith("@0")) {
-					val = val.substring(0, len-2);
+				String str = (String) this.values[columnIndex - 1];
+				int len = str.length();
+				if (len > 2 && str.endsWith("@0")) {
+					str = str.substring(0, len-2);
 					try {
-						return Integer.parseInt(val);
+						return Integer.parseInt(str);
 					} catch (NumberFormatException nfe) {
 						throw newSQLNumberFormatException(nfe);
 					}
 				}
 			}
 			throw newSQLNumberFormatException(e);
+		} catch (ClassCastException ex) {
+			throw new SQLException(ex.getMessage());
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
 		}
@@ -1174,24 +1173,25 @@ public class MonetResultSet extends Mone
 	 */
 	@Override
 	public long getLong(int columnIndex) throws SQLException {
-		String val = "";
+		Object val;
 		try {
-			val = tlp.values[columnIndex - 1];
+			val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return 0;
 			}
 			lastReadWasNull = false;
-			return Long.parseLong(val);
+			return (Long) val;
 		} catch (NumberFormatException e) {
 			// The oid datatype values (as string) have a  @0  suffix in the string value.
 			// To allow succesful parsing and conversion to long, we need to remove the suffix first
 			if ("oid".equals(types[columnIndex - 1])) {
-				int len = val.length();
-				if (len > 2 && val.endsWith("@0")) {
-					val = val.substring(0, len-2);
+				String str = (String) this.values[columnIndex - 1];
+				int len = str.length();
+				if (len > 2 && str.endsWith("@0")) {
+					str = str.substring(0, len-2);
 					try {
-						return Long.parseLong(val);
+						return Long.parseLong(str);
 					} catch (NumberFormatException nfe) {
 						throw newSQLNumberFormatException(nfe);
 					}
@@ -1246,8 +1246,7 @@ public class MonetResultSet extends Mone
 			 * and cache the precision, scale, isNullable and isAutoincrement values in the above array chaches.
 			 * Also we only call md.getColumns() when we have a non empty schema name and table name and column name.
 			 */
-			private void fetchColumnInfo(int column) throws SQLException
-			{
+			private void fetchColumnInfo(int column) throws SQLException {
 				if (column <= 0 || column > columns.length)
 					throw newSQLInvalidColumnIndexException(column);
 
@@ -1339,13 +1338,11 @@ public class MonetResultSet extends Mone
 						String monettype = getColumnTypeName(column);
 						if (monettype != null && monettype.length() == 4) {
 							// data of type inet or uuid is not case sensitive
-							if ("inet".equals(monettype)
-							 || "uuid".equals(monettype))
+							if ("inet".equals(monettype) || "uuid".equals(monettype))
 								return false;
 						}
 						return true;
 				}
-
 				return false;
 			}
 
@@ -1406,8 +1403,7 @@ public class MonetResultSet extends Mone
 						String monettype = getColumnTypeName(column);
 						if (monettype != null && monettype.length() == 3) {
 							// data of type oid or ptr is not signed
-							if ("oid".equals(monettype)
-							 || "ptr".equals(monettype))
+							if ("oid".equals(monettype) || "ptr".equals(monettype))
 								return false;
 						}
 						return true;
@@ -1794,9 +1790,9 @@ 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
 		final int JdbcType;
-		final String val;
+		final Object val;
 		try {
-			val = tlp.values[columnIndex - 1];
+			val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
@@ -1811,48 +1807,16 @@ public class MonetResultSet extends Mone
 			case Types.BIT: // MonetDB doesn't use type BInary digiT, it's here for completeness
 			case Types.TINYINT:
 			case Types.SMALLINT:
-				try {
-					return Short.valueOf(val);
-				} catch (NumberFormatException e) {
-					return val;
-				}
 			case Types.INTEGER:
-				try {
-					return Integer.valueOf(val);
-				} catch (NumberFormatException e) {
-					return val;
-				}
 			case Types.BIGINT:
-				try {
-					return Long.valueOf(val);
-				} catch (NumberFormatException e) {
-					return val;
-				}
 			case Types.DOUBLE:
 			case Types.FLOAT:
-				try {
-					return Double.valueOf(val);
-				} catch (NumberFormatException e) {
-					return val;
-				}
 			case Types.REAL:
-				try {
-					return Float.valueOf(val);
-				} catch (NumberFormatException e) {
-					return val;
-				}
 			case Types.DECIMAL:
 			case Types.NUMERIC:
-				try {
-					return new BigDecimal(val);
-				} catch (NumberFormatException e) {
-					return val;
-				}
 			case Types.BOOLEAN:
-				return Boolean.valueOf(val);
-			case Types.VARCHAR:
-			{
-
+				return val;
+			case Types.VARCHAR: {
 				// The MonetDB types: inet, json, url and uuid are all mapped to Types.VARCHAR in MonetDriver.typeMap
 				// For these MonetDB types (except json, see comments below) we try to create objects of the corresponding class.
 				String MonetDBType = types[columnIndex - 1];
@@ -1860,12 +1824,9 @@ public class MonetResultSet extends Mone
 				case 3:
 					if ("url".equals(MonetDBType)) {
 						try {
-							nl.cwi.monetdb.jdbc.types.URL url_obj = new nl.cwi.monetdb.jdbc.types.URL();
-							url_obj.fromString(val);
+							URL url_obj = new URL();
+							url_obj.fromString((String) val);
 							return url_obj;
-						} catch (MalformedURLException exc) {
-							// ignore exception and just return the val String object
-							return val;
 						} catch (Exception exc) {
 							// ignore exception and just return the val String object
 							return val;
@@ -1875,8 +1836,8 @@ public class MonetResultSet extends Mone
 				case 4:
 					if ("inet".equals(MonetDBType)) {
 						try {
-							nl.cwi.monetdb.jdbc.types.INET inet_obj = new nl.cwi.monetdb.jdbc.types.INET();
-							inet_obj.fromString(val);
+							INET inet_obj = new INET();
+							inet_obj.fromString((String) val);
 							return inet_obj;
 						} catch (Exception exc) {
 							// ignore exception and just return the val String object
@@ -1885,7 +1846,7 @@ public class MonetResultSet extends Mone
 					} else
 					if ("uuid".equals(MonetDBType)) {
 						try {
-							return java.util.UUID.fromString(val);
+							return UUID.fromString((String) val);
 						} catch (IllegalArgumentException exc) {
 							// ignore exception and just return the val String object
 							return val;
@@ -1908,9 +1869,9 @@ public class MonetResultSet extends Mone
 			case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
 				return val;
 			case Types.CLOB:
-				return new MonetClob(val);
+				return new MonetClob((String) val);
 			case Types.BLOB:
-				return MonetBlob.create(val);
+				return new MonetBlob((String) val);
 			case Types.DATE:
 				return getDate(columnIndex);
 			case Types.TIME:
@@ -1969,13 +1930,11 @@ public class MonetResultSet extends Mone
 	 */
 	@Override
 	@SuppressWarnings("unchecked")
-	public Object getObject(int columnIndex, Map<String,Class<?>> map)
-		throws SQLException
-	{
-		final String val;
+	public Object getObject(int columnIndex, Map<String,Class<?>> map) throws SQLException {
+		final Object val;
 		final String MonetDBtype;
 		try {
-			val = tlp.values[columnIndex - 1];
+			val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
@@ -2026,8 +1985,7 @@ public class MonetResultSet extends Mone
 		} else if (classImplementsSQLData(type)) {
 			SQLData x;
 			try {
-				Constructor<? extends SQLData> ctor =
-					((Class)type).getConstructor();
+				Constructor<? extends SQLData> ctor = ((Class)type).getConstructor();
 				x = ctor.newInstance();
 			} catch (NoSuchMethodException | InstantiationException | InvocationTargetException | SecurityException | IllegalAccessException nsme) {
 				throw new SQLException(nsme.getMessage(), "M0M27");
@@ -2146,7 +2104,7 @@ public class MonetResultSet extends Mone
 				}
 
 				@Override
-				public URL readURL() throws SQLException {
+				public java.net.URL readURL() throws SQLException {
 					return getURL(colnum);
 				}
 
@@ -2225,9 +2183,7 @@ public class MonetResultSet extends Mone
 	 *         example, if a conversion error occurs
 	 */
 	@Override
-	public <T> T getObject(String columnLabel, Class<T> type)
-		throws SQLException
-	{
+	public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
 		return getObject(findColumn(columnLabel), type);
 	}
 
@@ -2394,13 +2350,13 @@ public class MonetResultSet extends Mone
 	@Override
 	public short getShort(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			Object val = this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return 0;
 			}
 			lastReadWasNull = false;
-			return Short.parseShort(val);
+			return (Short) val;
 		} catch (NumberFormatException e) {
 			throw newSQLNumberFormatException(e);
 		} catch (IndexOutOfBoundsException e) {
@@ -2447,7 +2403,7 @@ public class MonetResultSet extends Mone
 	@Override
 	public String getString(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			String val = (String) this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
@@ -2544,12 +2500,9 @@ public class MonetResultSet extends Mone
 	}
 
 	// This behaviour is according table B-6 of Sun JDBC Specification 3.0
-	private SimpleDateFormat ts =
-		new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-	private SimpleDateFormat t =
-		new SimpleDateFormat("HH:mm:ss");
-	private SimpleDateFormat d =
-		new SimpleDateFormat("yyyy-MM-dd");
+	private SimpleDateFormat ts = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+	private SimpleDateFormat t = new SimpleDateFormat("HH:mm:ss");
+	private SimpleDateFormat d = new SimpleDateFormat("yyyy-MM-dd");
 	/**
 	 * Helper method which parses the date/time value for columns of type
 	 * TIME, DATE and TIMESTAMP.  For the types CHAR, VARCHAR and
@@ -2567,17 +2520,15 @@ public class MonetResultSet extends Mone
 	 * @return the fractional seconds (nanos) or -1 if the value is NULL
 	 * @throws SQLException if a database error occurs
 	 */
-	private int getJavaDate(Calendar cal, int columnIndex, int type)
-		throws SQLException
-	{
-		if (cal == null) throw
-			new IllegalArgumentException("No Calendar object given!");
+	private int getJavaDate(Calendar cal, int columnIndex, int type) throws SQLException {
+		if (cal == null)
+			throw new IllegalArgumentException("No Calendar object given!");
 
 		final String monetDate;
 		final String MonetDBType;
 		int JdbcType;
 		try {
-			monetDate = tlp.values[columnIndex - 1];
+			monetDate = (String) this.values[columnIndex - 1];
 			if (monetDate == null) {
 				lastReadWasNull = true;
 				return -1;
@@ -2587,11 +2538,7 @@ public class MonetResultSet extends Mone
 			JdbcType = JdbcSQLTypes[columnIndex - 1];
 			// If we got a string type, set the JdbcType to the given type
 			// so we attempt to parse it as the caller thinks it is.
-			if (JdbcType == Types.CHAR ||
-			    JdbcType == Types.VARCHAR ||
-			    JdbcType == Types.LONGVARCHAR ||
-			    JdbcType == Types.CLOB)
-			{
+			if (JdbcType == Types.CHAR || JdbcType == Types.VARCHAR || JdbcType == Types.LONGVARCHAR || JdbcType == Types.CLOB) {
 				JdbcType = type;
 			}
 		} catch (IndexOutOfBoundsException e) {
@@ -2637,13 +2584,10 @@ public class MonetResultSet extends Mone
 			if (epos == -1) {
 				addWarning("parsing '" + monetDate + "' failed", "01M10");
 			} else if (epos < monetDate.length()) {
-				addWarning("parsing failed," +
-						 " found: '" + monetDate.charAt(epos) + "'" +
-						 " in: \"" + monetDate + "\"" +
+				addWarning("parsing failed, found: '" + monetDate.charAt(epos) + "' in: \"" + monetDate + "\"" +
 						 " at pos: " + ppos.getErrorIndex(), "01M10");
 			} else {
-				addWarning("parsing failed, expected more data after '" +
-						monetDate + "'", "01M10");
+				addWarning("parsing failed, expected more data after '" + monetDate + "'", "01M10");
 			}
 			// default value
 			cal.clear();
@@ -2661,15 +2605,10 @@ public class MonetResultSet extends Mone
 				int ctr;
 				try {
 					nanos = getIntrinsicValue(monDate[pos], pos++);
-					for (ctr = 1;
-							pos < monDate.length &&
-							monDate[pos] >= '0' &&
-							monDate[pos] <= '9';
-							ctr++)
-					{
+					for (ctr = 1; pos < monDate.length && monDate[pos] >= '0' && monDate[pos] <= '9'; ctr++) {
 						if (ctr < 9) {
 							nanos *= 10;
-							nanos += (getIntrinsicValue(monDate[pos], pos));
+							nanos += getIntrinsicValue(monDate[pos], pos);
 						}
 						if (ctr == 2)	// we have three at this point
 							cal.set(Calendar.MILLISECOND, nanos);
@@ -2678,10 +2617,8 @@ public class MonetResultSet extends Mone
 					while (ctr++ < 9)
 						nanos *= 10;
 				} catch(MCLParseException e) {
-					addWarning(e.getMessage() +
-							" found: '" + monDate[e.getErrorOffset()] + "'" +
-							" in: \"" + monetDate + "\"" +
-							" at pos: " + e.getErrorOffset(), "01M10");
+					addWarning(e.getMessage() + " found: '" + monDate[e.getErrorOffset()] + "'" +
+							" in: \"" + monetDate + "\" at pos: " + e.getErrorOffset(), "01M10");
 					// default value
 					cal.clear();
 					nanos = 0;
@@ -2701,9 +2638,7 @@ public class MonetResultSet extends Mone
 	 * @return the intrinsic value of the char
 	 * @throws MCLParseException if c is not a digit
 	 */
-	private final static int getIntrinsicValue(char c, int pos)
-		throws MCLParseException
-	{
+	private final static int getIntrinsicValue(char c, int pos) throws MCLParseException {
 		// note: don't use Character.isDigit() here, because
 		// we only want ISO-LATIN-1 digits
 		if (c >= '0' && c <= '9') {
@@ -2725,7 +2660,7 @@ public class MonetResultSet extends Mone
 	 * @see #getDate(int col, Calendar cal)
 	 */
 	@Override
-	public java.sql.Date getDate(int columnIndex) throws SQLException {
+	public Date getDate(int columnIndex) throws SQLException {
 		return getDate(columnIndex, Calendar.getInstance());
 	}
 
@@ -2743,9 +2678,7 @@ public class MonetResultSet extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public java.sql.Date getDate(int columnIndex, Calendar cal)
-		throws SQLException
-	{
+	public Date getDate(int columnIndex, Calendar cal) throws SQLException {
 		int ret = getJavaDate(cal, columnIndex, Types.DATE);
 		return ret == -1 ? null : new java.sql.Date(cal.getTimeInMillis());
 	}
@@ -2762,7 +2695,7 @@ public class MonetResultSet extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public java.sql.Date getDate(String columnName) throws SQLException {
+	public Date getDate(String columnName) throws SQLException {
 		return getDate(findColumn(columnName), Calendar.getInstance());
 	}
 
@@ -2781,9 +2714,7 @@ public class MonetResultSet extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public java.sql.Date getDate(String columnName, Calendar cal)
-		throws SQLException
-	{
+	public Date getDate(String columnName, Calendar cal) throws SQLException {
 		return getDate(findColumn(columnName), cal);
 	}
 
@@ -2818,9 +2749,7 @@ public class MonetResultSet extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public Time getTime(int columnIndex, Calendar cal)
-		throws SQLException
-	{
+	public Time getTime(int columnIndex, Calendar cal) throws SQLException {
 		int ret = getJavaDate(cal, columnIndex, Types.TIME);
 		return ret == -1 ? null : new Time(cal.getTimeInMillis());
 	}
@@ -2856,9 +2785,7 @@ public class MonetResultSet extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public Time getTime(String columnName, Calendar cal)
-		throws SQLException
-	{
+	public Time getTime(String columnName, Calendar cal) throws SQLException {
 		return getTime(findColumn(columnName), cal);
 	}
 
@@ -2893,9 +2820,7 @@ public class MonetResultSet extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public Timestamp getTimestamp(int columnIndex, Calendar cal)
-		throws SQLException
-	{
+	public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
 		int nanos = getJavaDate(cal, columnIndex, Types.TIMESTAMP);
 		if (nanos == -1)
 			return null;
@@ -2936,9 +2861,7 @@ public class MonetResultSet extends Mone
 	 * @throws SQLException if a database access error occurs
 	 */
 	@Override
-	public Timestamp getTimestamp(String columnName, Calendar cal)
-		throws SQLException
-	{
+	public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException {
 		return getTimestamp(findColumn(columnName), cal);
 	}
 
@@ -2968,16 +2891,16 @@ public class MonetResultSet extends Mone
 	 *         URL is malformed
 	 */
 	@Override
-	public URL getURL(int columnIndex) throws SQLException {
+	public java.net.URL getURL(int columnIndex) throws SQLException {
 		try {
-			String val = tlp.values[columnIndex - 1];
+			String val = (String) this.values[columnIndex - 1];
 			if (val == null) {
 				lastReadWasNull = true;
 				return null;
 			}
 			lastReadWasNull = false;
 			try {
-				return new URL(val);
+				return new java.net.URL(val);
 			} catch (MalformedURLException e) {
 				throw new SQLException(e.getMessage(), "M1M05");
 			}
@@ -2999,7 +2922,7 @@ public class MonetResultSet extends Mone
 	 *         URL is malformed
 	 */
 	@Override
-	public URL getURL(String columnName) throws SQLException {
+	public java.net.URL getURL(String columnName) throws SQLException {
 		return getURL(findColumn(columnName));
 	}
 
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetSavepoint.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetSavepoint.java
@@ -52,7 +52,6 @@ public class MonetSavepoint implements S
 		this.name = null;
 	}
 
-
 	/**
 	 * Retrieves the generated ID for the savepoint that this Savepoint object
 	 * represents.
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
@@ -8,7 +8,8 @@
 
 package nl.cwi.monetdb.jdbc;
 
-import nl.cwi.monetdb.responses.*;
+import nl.cwi.monetdb.mcl.responses.*;
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
 
 import java.sql.BatchUpdateException;
 import java.sql.Connection;
@@ -51,7 +52,7 @@ public class MonetStatement extends Mone
 	/** The parental Connection object */
 	private MonetConnection connection;
 	/** The last ResponseList object this Statement produced */
-	private ResponseList lastResponseList;
+	private MonetConnection.ResponseList lastResponseList;
 	/** The last Response that this object uses */
 	IResponse header;
 	/** The warnings this Statement object generated */
@@ -60,8 +61,7 @@ public class MonetStatement extends Mone
 	protected boolean closed;
 	/** Whether the application wants this Statement object to be pooled */
 	protected boolean poolable;
-	/** Whether this Statement should be closed if the last ResultSet
-	 * closes */
+	/** Whether this Statement should be closed if the last ResultSet closes */
 	private boolean closeOnCompletion = false;
 	/** The size of the blocks of results to ask for at the server */
 	private int fetchSize = 0;
@@ -73,7 +73,6 @@ 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<>();
 
@@ -198,19 +197,18 @@ public class MonetStatement extends Mone
 			boolean first = true;
 			boolean error = false;
 
-			MonetConnection server = connection.getServer();
-
 			BatchUpdateException e = new BatchUpdateException("Error(s) occurred while executing the batch, see next SQLExceptions for details", "22000", counts);
-			StringBuilder tmpBatch = new StringBuilder(server.getBlockSize());
-			String sep = server.getQueryTemplateHeader(2);
+			StringBuilder tmpBatch = new StringBuilder(connection.getBlockSize());
+			String sep = new String(connection.getLanguage().getQueryTemplateIndex(2));
 			for (int i = 0; i < batch.size(); i++) {
 				String tmp = batch.get(i);
-				if (sep.length() + tmp.length() > server.getBlockSize()) {
+				if (sep.length() + tmp.length() > connection.getBlockSize()) {
 					// The thing is too big.  Way too big.  Since it won't
 					// be optimal anyway, just add it to whatever we have
 					// and continue.
-					if (!first)
+					if (!first) {
 						tmpBatch.append(sep);
+					}
 					tmpBatch.append(tmp);
 					// send and receive
 					error |= internalBatch(tmpBatch.toString(), counts, offset, i + 1, e);
@@ -219,7 +217,7 @@ public class MonetStatement extends Mone
 					first = true;
 					continue;
 				}
-				if (tmpBatch.length() + sep.length() + tmp.length() >= server.getBlockSize()) {
+				if (tmpBatch.length() + sep.length() + tmp.length() >= connection.getBlockSize()) {
 					// send and receive
 					error |= internalBatch(tmpBatch.toString(), counts, offset, i + 1, e);
 					offset = i;
@@ -252,17 +250,14 @@ 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;
 				}
 				offset++;
-			} while ((type = getMoreResults()) ||
-					(count = getUpdateCount()) != -1);
+			} while ((type = getMoreResults()) || (count = getUpdateCount()) != -1);
 		} catch (SQLException ex) {
 			e.setNextException(ex);
 			for (; offset < max; offset++) {
@@ -460,9 +455,7 @@ 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);
 	}
@@ -488,7 +481,7 @@ public class MonetStatement extends Mone
 		}
 
 		// create a container for the result
-		lastResponseList = 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);
 
@@ -553,15 +546,11 @@ 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 ;) */
+		/* MonetDB has no way to disable this, so just do the normal thing ;) */
 		if (execute(sql))
 			throw new SQLException("Query produced a result set", "M1M17");
 
@@ -688,7 +677,7 @@ public class MonetStatement extends Mone
 		types[0] = "BIGINT";
 
 		if (header instanceof UpdateResponse) {
-			String lastid = ((UpdateResponse)header).lastid;
+			String lastid = ((UpdateResponse)header).getLastid();
 			if (lastid.equals("-1")) {
 				results = new String[0][1];
 			} else {
@@ -835,9 +824,7 @@ public class MonetStatement extends Mone
 	 */
 	@Override
 	public ResultSet getResultSet() throws SQLException{
-		return (header instanceof ResultSetResponse)
-			? new MonetResultSet(this, (ResultSetResponse)header)
-			: null;
+		return (header instanceof ResultSetResponse) ? new MonetResultSet(this, (ResultSetResponse)header) : null;
 	}
 
 	/**
@@ -890,9 +877,9 @@ public class MonetStatement extends Mone
 	public int getUpdateCount() throws SQLException {
 		int ret = -1;
 		if (header instanceof UpdateResponse) {
-			ret = ((UpdateResponse)header).count;
+			ret = ((UpdateResponse)header).getCount();
 		} else if (header instanceof SchemaResponse) {
-			ret = ((SchemaResponse)header).state;
+			ret = ((SchemaResponse)header).getState();
 		}
 
 		return ret;
@@ -1252,12 +1239,12 @@ final class MonetVirtualResultSet extend
 		else if (row > tupleCount + 1) row = tupleCount + 1;	// after last
 
 		// store it
-		curRow = row;
+		this.curRow = row;
 
 		// see if we have the row
 		if (row < 1 || row > tupleCount) return false;
 
-		System.arraycopy(results[row - 1], 0, tlp.values, 0, results[row - 1].length);
+		System.arraycopy(this.results[row - 1], 0, this.values, 0, this.results[row - 1].length);
 
 		return true;
 	}
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/EmbeddedConnection.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/EmbeddedConnection.java
@@ -4,8 +4,8 @@ import nl.cwi.monetdb.embedded.env.Monet
 import nl.cwi.monetdb.embedded.env.MonetDBEmbeddedDatabase;
 import nl.cwi.monetdb.embedded.env.MonetDBEmbeddedException;
 import nl.cwi.monetdb.jdbc.MonetConnection;
-import nl.cwi.monetdb.mcl.io.*;
 import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.embedded.EmbeddedProtocol;
 
 import java.io.*;
 import java.util.List;
@@ -18,8 +18,6 @@ public final class EmbeddedConnection ex
 
     private final String directory;
 
-    private JDBCEmbeddedConnection internalConnection;
-
     public EmbeddedConnection(Properties props, String database, String hash, String language, boolean blobIsBinary, boolean isDebugging, String directory) throws IOException {
         super(props, database, hash, language, blobIsBinary, isDebugging);
         this.directory = directory;
@@ -30,7 +28,7 @@ public final class EmbeddedConnection ex
     }
 
     public MonetDBEmbeddedConnection getAsMonetDBEmbeddedConnection() {
-        return internalConnection;
+        return ((EmbeddedProtocol)protocol).getEmbeddedConnection();
     }
 
     @Override
@@ -41,7 +39,7 @@ public final class EmbeddedConnection ex
             } else {
                 MonetDBEmbeddedDatabase.StartDatabase(this.directory, true, false);
             }
-            this.internalConnection = MonetDBEmbeddedDatabase.CreateJDBCEmbeddedConnection();
+            this.protocol = new EmbeddedProtocol(MonetDBEmbeddedDatabase.CreateJDBCEmbeddedConnection());
         } catch (MonetDBEmbeddedException ex) {
             throw new MCLException(ex);
         }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/MapiConnection.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/MapiConnection.java
@@ -5,6 +5,7 @@ import nl.cwi.monetdb.mcl.io.BufferedMCL
 import nl.cwi.monetdb.mcl.io.BufferedMCLWriter;
 import nl.cwi.monetdb.mcl.io.SocketConnection;
 import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
 import nl.cwi.monetdb.mcl.protocol.ServerResponses;
 import nl.cwi.monetdb.mcl.protocol.oldmapi.OldMapiProtocol;
 import nl.cwi.monetdb.util.ChannelSecurity;
@@ -78,8 +79,6 @@ public class MapiConnection extends Mone
     /** protocol version of the connection */
     protected int version;
 
-    protected OldMapiProtocol protocol;
-
     public MapiConnection(Properties props, String database, String hash, String language, boolean blobIsBinary, boolean isDebugging, String hostname, int port) throws IOException {
         super(props, database, hash, language, blobIsBinary, isDebugging);
         this.hostname = hostname;
@@ -146,13 +145,13 @@ public class MapiConnection extends Mone
 
     @Override
     public int getBlockSize() {
-        return protocol.getConnection().getBlockSize();
+        return ((OldMapiProtocol)protocol).getConnection().getBlockSize();
     }
 
     @Override
     public int getSoTimeout()  {
         try {
-            return protocol.getConnection().getSoTimeout();
+            return ((OldMapiProtocol)protocol).getConnection().getSoTimeout();
         } catch (SocketException e) {
             this.addWarning("The socket timeout could not be get", "M1M05");
         }
@@ -162,7 +161,7 @@ public class MapiConnection extends Mone
     @Override
     public void setSoTimeout(int s)  {
         try {
-            protocol.getConnection().setSoTimeout(s);
+            ((OldMapiProtocol)protocol).getConnection().setSoTimeout(s);
         } catch (SocketException e) {
             this.addWarning("The socket timeout could not be set", "M1M05");
         }
@@ -170,7 +169,7 @@ public class MapiConnection extends Mone
 
     @Override
     public void closeUnderlyingConnection() throws IOException {
-        protocol.getConnection().close();
+        ((OldMapiProtocol)protocol).getConnection().close();
     }
 
     @Override
@@ -182,6 +181,11 @@ public class MapiConnection extends Mone
     }
 
     @Override
+    public AbstractProtocol getProtocol() {
+        return this.protocol;
+    }
+
+    @Override
     public List<String> connect(String user, String pass) throws IOException, MCLParseException, MCLException {
         // Wrap around the internal connect that needs to know if it
         // should really make a TCP connection or not.
@@ -194,21 +198,28 @@ public class MapiConnection extends Mone
 
     private List<String> connect(String host, int port, String user, String pass, boolean makeConnection) throws IOException, MCLParseException, MCLException {
         if (ttl-- <= 0)
-            throw new MCLException("Maximum number of redirects reached, aborting connection attempt.  Sorry.");
+            throw new MCLException("Maximum number of redirects reached, aborting connection attempt. Sorry.");
+
+        AbstractProtocol<?> pro;
 
         if (makeConnection) {
-            this.protocol = new OldMapiProtocol(new SocketConnection(this.hostname, this.port));
-            this.protocol.getConnection().setTcpNoDelay(true);
+            pro = new OldMapiProtocol(new SocketConnection(this.hostname, this.port));
+            this.protocol = pro;
+            ((OldMapiProtocol)pro).getConnection().setTcpNoDelay(true);
 
             // set nodelay, as it greatly speeds up small messages (like we
             // often do)
             //TODO writer.registerReader(reader); ??
+        } else {
+            pro = this.protocol;
         }
 
-        this.protocol.getNextResponseHeader();
-        String test = this.getChallengeResponse(this.protocol.getEntireResponseLine(), user, pass,
-                this.language.getRepresentation(), this.database, this.hash);
-        this.protocol.writeNextLine(test.getBytes());
+        pro.fetchNextResponseData();
+        pro.waitUntilPrompt();
+        String firstLine = pro.getRemainingStringLine(0);
+
+        String test = this.getChallengeResponse(firstLine, user, pass, this.language.getRepresentation(), this.database, this.hash);
+        pro.writeNextCommand(MonetDBLanguage.EmptyString, test.getBytes(), MonetDBLanguage.EmptyString);
 
         List<String> redirects = new ArrayList<>();
         List<String> warns = new ArrayList<>();
@@ -216,15 +227,16 @@ public class MapiConnection extends Mone
         ServerResponses next;
 
         do {
-            next = this.protocol.getNextResponseHeader();
+            pro.fetchNextResponseData();
+            next = pro.getCurrentServerResponseHeader();
             switch (next) {
                 case ERROR:
-                    err += "\n" + this.protocol.getRemainingResponseLine(0);
+                    err += "\n" + pro.getRemainingStringLine(7);
                     break;
                 case INFO:
-                    warns.add(this.protocol.getRemainingResponseLine(0));
+                    warns.add(pro.getRemainingStringLine(1));
                 case REDIRECT:
-                    redirects.add(this.protocol.getRemainingResponseLine(0));
+                    redirects.add(pro.getRemainingStringLine(1));
             }
         } while (next != ServerResponses.PROMPT);
 
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/MonetDBLanguage.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/MonetDBLanguage.java
@@ -32,10 +32,20 @@ public enum MonetDBLanguage {
         return commandTemplates[index];
     }
 
+    public byte[][] getQueryTemplates() {
+        return queryTemplates;
+    }
+
+    public byte[][] getCommandTemplates() {
+        return commandTemplates;
+    }
+
     public String getRepresentation() {
         return representation;
     }
 
+    public static final byte[] EmptyString = "".getBytes();
+
     public static MonetDBLanguage GetLanguageFromString(String language) {
         switch (language) {
             case "sql":
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/SendThread.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/SendThread.java
@@ -1,6 +1,6 @@
 package nl.cwi.monetdb.mcl.connection;
 
-import nl.cwi.monetdb.mcl.io.AbstractMCLWriter;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
 
 import java.io.IOException;
 import java.sql.SQLException;
@@ -30,9 +30,9 @@ public class SendThread extends Thread {
         SHUTDOWN
     }
 
-    private String[] templ;
-    private String query;
-    private AbstractMCLWriter out;
+    private byte[][] templ;
+    private byte[] query;
+    private AbstractProtocol protocol;
     private String error;
     private SendThreadStatus state = SendThreadStatus.WAIT;
 
@@ -46,10 +46,10 @@ public class SendThread extends Thread {
      *
      * @param out the socket to write to
      */
-    public SendThread(AbstractMCLWriter out) {
+    public SendThread(AbstractProtocol out) {
         super("SendThread");
         this.setDaemon(true);
-        this.out = out;
+        this.protocol = out;
         this.start();
     }
 
@@ -70,7 +70,7 @@ public class SendThread extends Thread {
 
                 // state is QUERY here
                 try {
-                    out.writeLine((templ[0] == null ? "" : templ[0]) + query + (templ[1] == null ? "" : templ[1]));
+                    protocol.writeNextCommand((templ[0] == null ? MonetDBLanguage.EmptyString : templ[0]), query, (templ[1] == null ? MonetDBLanguage.EmptyString : templ[1]));
                 } catch (IOException e) {
                     error = e.getMessage();
                 }
@@ -94,14 +94,15 @@ public class SendThread extends Thread {
      * @param query the query itself
      * @throws SQLException if this SendThread is already in use
      */
-    public void runQuery(String[] templ, String query) throws SQLException {
+    public void runQuery(byte[][] templ, String query) throws SQLException {
         sendLock.lock();
         try {
-            if (state != SendThreadStatus.WAIT)
+            if (state != SendThreadStatus.WAIT) {
                 throw new SQLException("SendThread already in use or shutting down!", "M0M03");
+            }
 
             this.templ = templ;
-            this.query = query;
+            this.query = query.getBytes();
 
             // let the thread know there is some work to do
             state = SendThreadStatus.QUERY;
--- a/src/main/java/nl/cwi/monetdb/mcl/io/SocketConnection.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/io/SocketConnection.java
@@ -144,7 +144,6 @@ public class SocketConnection implements
     }
 
     public int readUntilChar(StringBuilder builder, char limit) throws IOException {
-        builder.setLength(0);
         boolean found = false;
 
         while(!found) {
@@ -160,19 +159,10 @@ public class SocketConnection implements
         return builder.length();
     }
 
-    public void writeNextLine(byte[] line) throws IOException {
-        bufferOut.clear();
-        this.writeNextBlock(line);
-        if (bufferOut.hasRemaining()) {
-            bufferOut.flip();
-            connection.write(this.bufferOut);
-        }
-    }
-
-    public void writeNextLine(byte[] prefix, String line, byte[] suffix) throws IOException {
+    public void writeNextLine(byte[] prefix, byte[] line, byte[] suffix) throws IOException {
         bufferOut.clear();
         this.writeNextBlock(prefix);
-        this.writeNextBlock(line.getBytes());
+        this.writeNextBlock(line);
         this.writeNextBlock(suffix);
         if (bufferOut.hasRemaining()) {
             bufferOut.flip();
deleted file mode 100644
--- a/src/main/java/nl/cwi/monetdb/mcl/parser/StartOfHeaderParser.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package nl.cwi.monetdb.mcl.parser;
-
-/**
- * Created by ferreira on 11/25/16.
- */
-public abstract class StartOfHeaderParser {
-
-    /* Query types (copied from sql_query.mx) */
-
-    /** A parse response (not handled) */
-    public final static int Q_PARSE    = '0';
-    /** A tabular response (typical ResultSet) */
-    public final static int Q_TABLE    = '1';
-    /** A response to an update statement, contains number of affected
-     * rows and generated key-id */
-    public final static int Q_UPDATE   = '2';
-    /** A response to a schema update */
-    public final static int Q_SCHEMA   = '3';
-    /** A response to a transaction statement (start, rollback, abort,
-     * commit) */
-    public final static int Q_TRANS    = '4';
-    /** A tabular response in response to a PREPARE statement containing
-     * information about the wildcard values that need to be supplied */
-    public final static int Q_PREPARE  = '5';
-    /** A tabular continuation response (for a ResultSet) */
-    public final static int Q_BLOCK    = '6';
-    /** An unknown and unsupported response */
-    public final static int Q_UNKNOWN  =  0;
-
-    protected int len;
-
-    protected int pos;
-
-    public abstract int parse(String in) throws MCLParseException;
-
-    public abstract int getNextAsInt() throws MCLParseException;
-
-    public abstract String getNextAsString() throws MCLParseException;
-
-    public final boolean hasNext() {
-        return pos < len;
-    }
-}
deleted file mode 100644
--- a/src/main/java/nl/cwi/monetdb/mcl/parser/socket/SocketHeaderLineParser.java
+++ /dev/null
@@ -1,178 +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 - 2016 MonetDB B.V.
- */
-
-package nl.cwi.monetdb.mcl.parser.socket;
-
-
-import nl.cwi.monetdb.mcl.parser.HeaderLineParser;
-import nl.cwi.monetdb.mcl.parser.MCLParseException;
-
-/**
- * The SocketHeaderLineParser is a generic MCLParser that extracts values from
- * a metadata header in the MCL protocol either as string or integer
- * values.
- *
- * @author Fabian Groffen <Fabian.Groffen>
- */
-public class SocketHeaderLineParser extends HeaderLineParser {
-
-	/**
-	 * Constructs a SocketHeaderLineParser which expects columncount columns.
-	 *
-	 * @param columncount the number of columns in the to be parsed string
-	 */
-	public SocketHeaderLineParser(int columncount) {
-		super(columncount);
-	}
-
-	/**
-	 * Parses the given String source as header line.  If source cannot
-	 * be parsed, an MCLParseException is thrown.  The columncount argument
-	 * given during construction is used for allocation of the backing
-	 * array.  Parsing a header line with has more fields will therefore
-	 * result in a crash.  While this seems illogical, the caller should
-	 * know this size, since the StartOfHeader contains this
-	 * information.
-	 *
-	 * @param source a String which should be parsed
-	 * @return the type of then parsed header line
-	 * @throws MCLParseException if an error occurs during parsing
-	 */
-	@Override
-	public int parse(String source) throws MCLParseException {
-		char[] chrLine = source.toCharArray();
-		int len = chrLine.length;
-		int pos = 0;
-		boolean foundChar = false;
-		boolean nameFound = false;
-		// find header name
-		for (int i = len - 1; i >= 0; i--) {
-			switch (chrLine[i]) {
-				case ' ':
-				case '\n':
-				case '\t':
-				case '\r':
-					if (!foundChar) {
-						len = i - 1;
-					} else {
-						pos = i + 1;
-					}
-					break;
-				case '#':
-					// found!
-					nameFound = true;
-					if (pos == 0) pos = i + 1;
-					i = 0;	// force the loop to terminate
-					break;
-				default:
-					foundChar = true;
-					pos = 0;
-					break;
-			}
-		}
-		if (!nameFound)
-			throw new MCLParseException("invalid header, no header name found", pos);
-
-		// depending on the name of the header, we continue
-		switch (chrLine[pos]) {
-			case 'n':
-				if (len - pos == 4 &&
-						source.regionMatches(pos + 1, "name", 1, 3))
-				{
-					getValues(chrLine, 2, pos - 3);
-					type = HeaderLineParser.NAME;
-				}
-				break;
-			case 'l':
-				if (len - pos == 6 &&
-						source.regionMatches(pos + 1, "length", 1, 5))
-				{
-					getIntValues(chrLine, 2, pos - 3);
-					type = HeaderLineParser.LENGTH;
-				}
-				break;
-			case 't':
-				if (len - pos == 4 &&
-						source.regionMatches(pos + 1, "type", 1, 3))
-				{
-					getValues(chrLine, 2, pos - 3);
-					type = HeaderLineParser.TYPE;
-				} else if (len - pos == 10 &&
-						source.regionMatches(pos + 1, "table_name", 1, 9))
-				{
-					getValues(chrLine, 2, pos - 3);
-					type = HeaderLineParser.TABLE;
-				}
-				break;
-			default:
-				throw new MCLParseException("unknown header: " +
-						(new String(chrLine, pos, len - pos)));
-		}
-
-		// adjust colno
-		reset();
-
-		return type;
-	}
-
-	/**
-	 * Returns an array of Strings containing the values between
-	 * ',\t' separators.  Note that no quoting/dequoting is done in this
-	 * method.
-	 *
-	 * @param chrLine a character array holding the input data
-	 * @param start where the relevant data starts
-	 * @param stop where the relevant data stops
-	 */
-	final private void getValues(char[] chrLine, int start, int stop) {
-		int elem = 0;
-
-		for (int i = start + 1; i < stop; i++) {
-			if (chrLine[i] == '\t' && chrLine[i - 1] == ',') {
-				values[elem++] =
-					new String(chrLine, start, i - 1 - start);
-				start = i + 1;
-			}
-		}
-		// add the left over part
-		values[elem + 1] = new String(chrLine, start, stop - start);
-	}
-
-	/**
-	 * Returns an array of ints containing the values between
-	 * ',\t' separators.
-	 *
-	 * @param chrLine a character array holding the input data
-	 * @param start where the relevant data starts
-	 * @param stop where the relevant data stops
-	 */
-	final private void getIntValues(char[] chrLine, int start, int stop)
-		throws MCLParseException
-	{
-		int elem = 0;
-		int tmp = 0;
-
-		for (int i = start; i < stop; i++) {
-			if (chrLine[i] == ',' && chrLine[i + 1] == '\t') {
-				intValues[elem++] = tmp;
-				tmp = 0;
-			} else {
-				tmp *= 10;
-				// note: don't use Character.isDigit() here, because
-				// we only want ISO-LATIN-1 digits
-				if (chrLine[i] >= '0' && chrLine[i] <= '9') {
-					tmp += (int)chrLine[i] - (int)'0';
-				} else {
-					throw new MCLParseException("expected a digit in " + new String(chrLine) + " at " + i);
-				}
-			}
-		}
-		// add the left over part
-		intValues[elem + 1] = tmp;
-	}
-}
deleted file mode 100644
--- a/src/main/java/nl/cwi/monetdb/mcl/parser/socket/SocketStartOfHeaderParser.java
+++ /dev/null
@@ -1,117 +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 - 2016 MonetDB B.V.
- */
-
-package nl.cwi.monetdb.mcl.parser.socket;
-
-import nl.cwi.monetdb.mcl.parser.StartOfHeaderParser;
-import nl.cwi.monetdb.mcl.parser.MCLParseException;
-
-import java.nio.CharBuffer;
-
-/**
- * The SocketStartOfHeaderParser allows easy examination of a start of header
- * line.  It does not fit into the general MCLParser framework because
- * it uses a different interface.  While the parser is very shallow, it
- * requires the caller to know about the header lines that are parsed.
- * All this parser does is detect the (valid) type of a soheader, and
- * allow to return the fields in it as integer or string.  An extra
- * bonus is that it can return if another field should be present in the
- * soheader.
- *
- * @author Fabian Groffen <Fabian.Groffen>
- */
-public class SocketStartOfHeaderParser extends StartOfHeaderParser {
-
-	private CharBuffer soh = null;
-
-	@Override
-	public final int parse(String in) throws MCLParseException {
-		soh = CharBuffer.wrap(in);
-		soh.get();	// skip the &
-		int type = soh.get();
-		switch (type) {
-			default:
-				throw new MCLParseException("invalid or unknown header", 1);
-			case Q_PARSE:
-			case Q_SCHEMA:
-				len = 0;
-			break;
-			case Q_TABLE:
-			case Q_PREPARE:
-				len = 4;
-				soh.get();
-			break;
-			case Q_UPDATE:
-				len = 2;
-				soh.get();
-			break;
-			case Q_TRANS:
-				len = 1;
-				soh.get();
-			break;
-			case Q_BLOCK:
-				len = 3;
-				soh.get();
-			break;
-		}
-		pos = 0;
-		return type;
-	}
-
-	/**
-	 * Returns the next token in the CharBuffer as integer. The value is
-	 * considered to end at the end of the CharBuffer or at a space.  If
-	 * a non-numeric character is encountered an MCLParseException is
-	 * thrown.
-	 *
-	 * @return The next token in the CharBuffer as integer
-	 * @throws MCLParseException if no numeric value could be read
-	 */
-	@Override
-	public final int getNextAsInt() throws MCLParseException {
-		pos++;
-		if (!soh.hasRemaining()) throw
-			new MCLParseException("unexpected end of string", soh.position() - 1);
-		int tmp;
-		char chr = soh.get();
-		// note: don't use Character.isDigit() here, because
-		// we only want ISO-LATIN-1 digits
-		if (chr >= '0' && chr <= '9') {
-			tmp = (int)chr - (int)'0';
-		} else {
-			throw new MCLParseException("expected a digit", soh.position() - 1);
-		}
-
-		while (soh.hasRemaining() && (chr = soh.get()) != ' ') {
-			tmp *= 10;
-			if (chr >= '0' && chr <= '9') {
-				tmp += (int)chr - (int)'0';
-			} else {
-				throw new MCLParseException("expected a digit", soh.position() - 1);
-			}
-		}
-
-		return tmp;
-	}
-
-	@Override
-	public final String getNextAsString() throws MCLParseException {
-		pos++;
-		if (!soh.hasRemaining()) throw
-			new MCLParseException("unexpected end of string", soh.position() - 1);
-		int cnt = 0;
-		soh.mark();
-		while (soh.hasRemaining() && soh.get() != ' ') {
-			cnt++;
-		}
-
-		soh.reset();
-
-		return soh.subSequence(0, cnt).toString();
-	}
-}
rename from src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocolParser.java
rename to src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocolParser.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java
@@ -1,46 +1,60 @@
 package nl.cwi.monetdb.mcl.protocol;
 
+import nl.cwi.monetdb.jdbc.MonetConnection;
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.responses.AutoCommitResponse;
+import nl.cwi.monetdb.mcl.responses.SchemaResponse;
+import nl.cwi.monetdb.mcl.responses.UpdateResponse;
+import nl.cwi.monetdb.mcl.responses.DataBlockResponse;
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
+
+import java.io.IOException;
+import java.util.Map;
+
 /**
  * Created by ferreira on 11/30/16.
  */
-public abstract class AbstractProtocolParser {
+public abstract class AbstractProtocol<T> {
 
     protected ServerResponses currentServerResponseHeader = ServerResponses.UNKNOWN;
 
-    protected StarterHeaders currentStarterHeader = StarterHeaders.Q_UNKNOWN;
+    public ServerResponses waitUntilPrompt() {
+        while(this.currentServerResponseHeader != ServerResponses.PROMPT) {
+           this.fetchNextResponseData();
+        }
+        return this.currentServerResponseHeader;
+    }
 
-    protected TableResultHeaders currentTableResultSetHeader = TableResultHeaders.UNKNOWN;
+    public abstract void fetchNextResponseData(); //UPDATE currentData!!!
 
     public ServerResponses getCurrentServerResponseHeader() {
         return currentServerResponseHeader;
     }
 
-    public StarterHeaders getCurrentStarterHeader() {
-        return currentStarterHeader;
-    }
+    public abstract T getCurrentData();
+
+    public abstract StarterHeaders getNextStarterHeader();
+
 
-    public TableResultHeaders getCurrentTableResultSetHeader() {
-        return currentTableResultSetHeader;
-    }
+    public abstract ResultSetResponse<T> getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, int seqnr) throws MCLParseException;
 
-    public ServerResponses getNextResponseHeader() {
-        this.currentServerResponseHeader = this.getNextResponseHeaderImplementation();
-        return this.currentServerResponseHeader;
+    public abstract UpdateResponse getNextUpdateResponse() throws MCLParseException;
+
+    public SchemaResponse getNextSchemaResponse() {
+        return new SchemaResponse();
     }
 
-    public StarterHeaders getNextStarterHeader() {
-        this.currentStarterHeader = this.getNextStarterHeaderImplementation();
-        return this.currentStarterHeader;
-    }
+    public abstract AutoCommitResponse getNextAutoCommitResponse() throws MCLParseException;
+
+    public abstract DataBlockResponse<T> getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) throws MCLParseException;
+
+
+    public abstract TableResultHeaders getNextTableHeader(Object line, String[] stringValues, int[] intValues) throws MCLParseException;
 
-    public TableResultHeaders getNextTableHeader() {
-        this.currentTableResultSetHeader = this.getNextTableHeaderImplementation();
-        return this.currentTableResultSetHeader;
-    }
+    public abstract void parseTupleLine(Object line, Object[] values) throws MCLParseException;
+
+    public abstract String getRemainingStringLine(int startIndex);
 
-    protected abstract ServerResponses getNextResponseHeaderImplementation();
+    public abstract void writeNextCommand(byte[] prefix, byte[] query, byte[] suffix) throws IOException;
 
-    protected abstract TableResultHeaders getNextTableHeaderImplementation();
-
-    protected abstract StarterHeaders getNextStarterHeaderImplementation();
 }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/TableResultHeaders.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/TableResultHeaders.java
@@ -4,9 +4,11 @@ package nl.cwi.monetdb.mcl.protocol;
  * Created by ferreira on 11/30/16.
  */
 public enum TableResultHeaders {
-    UNKNOWN,
+
+    /* Please don't change the order */
     NAME,
     LENGTH,
     TABLE,
-    TYPE
+    TYPE,
+    UNKNOWN
 }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/embedded/EmbeddedProtocol.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/embedded/EmbeddedProtocol.java
@@ -1,34 +1,75 @@
 package nl.cwi.monetdb.mcl.protocol.embedded;
 
-import nl.cwi.monetdb.mcl.io.JDBCEmbeddedConnection;
-import nl.cwi.monetdb.mcl.protocol.AbstractProtocolParser;
-import nl.cwi.monetdb.mcl.protocol.ServerResponses;
+import nl.cwi.monetdb.jdbc.MonetConnection;
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
 import nl.cwi.monetdb.mcl.protocol.StarterHeaders;
 import nl.cwi.monetdb.mcl.protocol.TableResultHeaders;
+import nl.cwi.monetdb.mcl.responses.AutoCommitResponse;
+import nl.cwi.monetdb.mcl.responses.DataBlockResponse;
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
+import nl.cwi.monetdb.mcl.responses.UpdateResponse;
+
+import java.io.IOException;
+import java.util.Map;
 
 /**
  * Created by ferreira on 11/30/16.
  */
-public class EmbeddedProtocol extends AbstractProtocolParser {
+public class EmbeddedProtocol extends AbstractProtocol<Object[]> {
 
-    private final JDBCEmbeddedConnection embeddedConnection;
+    @Override
+    public void fetchNextResponseData() {
 
-    public EmbeddedProtocol(JDBCEmbeddedConnection embeddedConnection) {
-        this.embeddedConnection = embeddedConnection;
     }
 
     @Override
-    public ServerResponses getNextResponseHeaderImplementation() {
+    public Object[] getCurrentData() {
+        return new Object[0];
+    }
+
+    @Override
+    public StarterHeaders getNextStarterHeader() {
+        return null;
+    }
+
+    @Override
+    public ResultSetResponse<Object[]> getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, int seqnr) throws MCLParseException {
+        return null;
+    }
+
+    @Override
+    public UpdateResponse getNextUpdateResponse() throws MCLParseException {
         return null;
     }
 
     @Override
-    public StarterHeaders getNextStarterHeaderImplementation() {
+    public AutoCommitResponse getNextAutoCommitResponse() throws MCLParseException {
+        return null;
+    }
+
+    @Override
+    public DataBlockResponse<Object[]> getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) throws MCLParseException {
+        return null;
+    }
+
+    @Override
+    public TableResultHeaders getNextTableHeader(Object line, String[] stringValues, int[] intValues) throws MCLParseException {
         return null;
     }
 
     @Override
-    public TableResultHeaders getNextTableHeaderImplementation() {
+    public void parseTupleLine(Object line, Object[] values) throws MCLParseException {
+
+    }
+
+    @Override
+    public String getRemainingStringLine(int startIndex) {
         return null;
     }
+
+    @Override
+    public void writeNextCommand(byte[] prefix, byte[] query, byte[] suffix) throws IOException {
+
+    }
 }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/newmapi/NewMapiProtocol.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/newmapi/NewMapiProtocol.java
@@ -1,34 +1,75 @@
 package nl.cwi.monetdb.mcl.protocol.newmapi;
 
-import nl.cwi.monetdb.mcl.io.SocketConnection;
-import nl.cwi.monetdb.mcl.protocol.AbstractProtocolParser;
-import nl.cwi.monetdb.mcl.protocol.ServerResponses;
+import nl.cwi.monetdb.jdbc.MonetConnection;
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
 import nl.cwi.monetdb.mcl.protocol.StarterHeaders;
 import nl.cwi.monetdb.mcl.protocol.TableResultHeaders;
+import nl.cwi.monetdb.mcl.responses.AutoCommitResponse;
+import nl.cwi.monetdb.mcl.responses.DataBlockResponse;
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
+import nl.cwi.monetdb.mcl.responses.UpdateResponse;
+
+import java.io.IOException;
+import java.util.Map;
 
 /**
  * Created by ferreira on 11/30/16.
  */
-public class NewMapiProtocol extends AbstractProtocolParser {
+public class NewMapiProtocol extends AbstractProtocol {
 
-    private final SocketConnection connection;
+    @Override
+    public void fetchNextResponseData() {
 
-    public NewMapiProtocol(SocketConnection con) {
-        this.connection = con;
     }
 
     @Override
-    public ServerResponses getNextResponseHeaderImplementation() {
+    public Object getCurrentData() {
+        return null;
+    }
+
+    @Override
+    public StarterHeaders getNextStarterHeader() {
+        return null;
+    }
+
+    @Override
+    public ResultSetResponse getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, int seqnr) throws MCLParseException {
+        return null;
+    }
+
+    @Override
+    public UpdateResponse getNextUpdateResponse() throws MCLParseException {
         return null;
     }
 
     @Override
-    public StarterHeaders getNextStarterHeaderImplementation() {
+    public AutoCommitResponse getNextAutoCommitResponse() throws MCLParseException {
+        return null;
+    }
+
+    @Override
+    public TableResultHeaders getNextTableHeader(Object line, String[] stringValues, int[] intValues) throws MCLParseException {
         return null;
     }
 
     @Override
-    public TableResultHeaders getNextTableHeaderImplementation() {
+    public void parseTupleLine(Object line, Object[] values) throws MCLParseException {
+
+    }
+
+    @Override
+    public String getRemainingStringLine(int startIndex) {
+        return null;
+    }
+
+    @Override
+    public void writeNextCommand(byte[] prefix, byte[] query, byte[] suffix) throws IOException {
+
+    }
+
+    @Override
+    public DataBlockResponse getNextDatablockResponse(Map rsresponses) throws MCLParseException {
         return null;
     }
 }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java
@@ -1,84 +1,137 @@
 package nl.cwi.monetdb.mcl.protocol.oldmapi;
 
+import nl.cwi.monetdb.jdbc.MonetConnection;
 import nl.cwi.monetdb.mcl.io.SocketConnection;
-import nl.cwi.monetdb.mcl.protocol.AbstractProtocolParser;
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
 import nl.cwi.monetdb.mcl.protocol.ServerResponses;
 import nl.cwi.monetdb.mcl.protocol.StarterHeaders;
 import nl.cwi.monetdb.mcl.protocol.TableResultHeaders;
+import nl.cwi.monetdb.mcl.responses.AutoCommitResponse;
+import nl.cwi.monetdb.mcl.responses.UpdateResponse;
+import nl.cwi.monetdb.mcl.responses.DataBlockResponse;
+import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
 
 import java.io.IOException;
+import java.sql.ResultSet;
+import java.util.Map;
 
 /**
  * Created by ferreira on 11/30/16.
  */
-public class OldMapiProtocol extends AbstractProtocolParser {
+public class OldMapiProtocol extends AbstractProtocol<StringBuilder> {
 
     private static final int STRING_BUILDER_INITIAL_SIZE = 128;
 
     private final SocketConnection connection;
 
-    private int builderPointer;
+    StringBuilder builder;
 
-    private final StringBuilder builder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE);
+    int currentPointer = 0;
 
     public OldMapiProtocol(SocketConnection con) {
         this.connection = con;
+        this.builder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE);
     }
 
     public SocketConnection getConnection() {
         return connection;
     }
 
+    boolean hasRemaining() {
+        return this.currentPointer < this.builder.length();
+    }
+
     @Override
-    public ServerResponses getNextResponseHeaderImplementation() {
-        ServerResponses res = ServerResponses.UNKNOWN;
+    public ServerResponses waitUntilPrompt() {
+        this.builder.setLength(0);
+        this.currentPointer = 0;
+        return super.waitUntilPrompt();
+    }
+
+    @Override
+    public void fetchNextResponseData() {
+        ServerResponses res;
         try {
-            while(res != ServerResponses.PROMPT) {
-                connection.readUntilChar(this.builder, '\n');
-                res = OldMapiConverter.GetNextResponseOnOldMapi(this.builder.charAt(0));
-                if(res == ServerResponses.ERROR && !this.builder.toString().matches("^![0-9A-Z]{5}!.+")) {
-                    this.builder.insert(1, "!22000!");
-                }
+            int bytesRead = connection.readUntilChar(this.builder, '\n');
+            res = OldMapiServerResponseParser.ParseOldMapiServerResponse(this);
+            if(res == ServerResponses.ERROR && !this.builder.substring(bytesRead).matches("^![0-9A-Z]{5}!.+")) {
+                this.builder.insert(bytesRead, "!22000!");
             }
-            this.builderPointer = 1;
         } catch (IOException e) {
             res = ServerResponses.ERROR;
             this.builder.setLength(0);
-            this.builderPointer = 0;
+            this.currentPointer = 0;
             this.builder.append("!22000!").append(e.getMessage());
         }
-        return res;
+        this.currentServerResponseHeader = res;
+    }
+
+    @Override
+    public StringBuilder getCurrentData() {
+        return this.builder;
+    }
+
+    @Override
+    public StarterHeaders getNextStarterHeader() {
+        return OldMapiStartOfHeaderParser.GetNextStartHeaderOnOldMapi(this);
+    }
+
+    @Override
+    public ResultSetResponse<StringBuilder> getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, int seqnr) throws MCLParseException {
+        int id = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        int tuplecount = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        int columncount = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        int rowcount = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        return new ResultSetResponse<>(con, list, seqnr, id, rowcount, tuplecount, columncount);
     }
 
-    public String getEntireResponseLine() {
-        String res = this.builder.toString();
-        this.builderPointer = this.builder.length();
-        return res;
+    @Override
+    public UpdateResponse getNextUpdateResponse() throws MCLParseException {
+        int count = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        String lastId = OldMapiStartOfHeaderParser.GetNextResponseDataAsString(this);
+        return new UpdateResponse(lastId, count);
+    }
+
+    @Override
+    public AutoCommitResponse getNextAutoCommitResponse() throws MCLParseException {
+        boolean ac = OldMapiStartOfHeaderParser.GetNextResponseDataAsString(this).equals("t");
+        return new AutoCommitResponse(ac);
     }
 
-    public String getRemainingResponseLine(int startIndex) {
-        String res = this.builder.substring(this.builderPointer + startIndex);
-        this.builderPointer = this.builder.length();
+    @Override
+    @SuppressWarnings("unchecked")
+    public DataBlockResponse<StringBuilder> getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) throws MCLParseException {
+        int id = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);	// pass the columncount --- Must do this!
+        int rowcount = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        int offset = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this);
+        ResultSetResponse<StringBuilder> resultSetResponse = rsresponses.get(id);
+        if (resultSetResponse == null) {
+            return null;
+        }
+        DataBlockResponse<StringBuilder> res = new DataBlockResponse<>(rowcount, resultSetResponse.getRSType() == ResultSet.TYPE_FORWARD_ONLY, StringBuilder.class);
+        resultSetResponse.addDataBlockResponse(offset, res);
         return res;
     }
 
     @Override
-    public StarterHeaders getNextStarterHeaderImplementation() {
-        try {
-            char nextToken = connection.readNextChar();
-            return OldMapiConverter.GetNextStartHeaderOnOldMapi(nextToken);
-        } catch (IOException e) {
-            return StarterHeaders.Q_UNKNOWN;
-        }
+    public TableResultHeaders getNextTableHeader(Object line, String[] stringValues, int[] intValues) throws MCLParseException {
+        return OldMapiTableHeaderParser.GetNextTableHeader((StringBuilder) line, stringValues, intValues);
+    }
+
+    @Override
+    public void parseTupleLine(Object line, Object[] values) throws MCLParseException {
+
     }
 
     @Override
-    public TableResultHeaders getNextTableHeaderImplementation() {
-        return null;
+    public String getRemainingStringLine(int startIndex) {
+        return this.builder.substring(startIndex);
     }
 
-
-    public void writeNextLine(byte[] line) throws IOException {
-        connection.writeNextLine(line);
+    @Override
+    public void writeNextCommand(byte[] prefix, byte[] query, byte[] suffix) throws IOException {
+        this.connection.writeNextLine(prefix, query, suffix);
     }
 }
rename from src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiConverter.java
rename to src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiServerResponseParser.java
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiConverter.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiServerResponseParser.java
@@ -1,56 +1,45 @@
 package nl.cwi.monetdb.mcl.protocol.oldmapi;
 
 import nl.cwi.monetdb.mcl.protocol.ServerResponses;
-import nl.cwi.monetdb.mcl.protocol.StarterHeaders;
 
 /**
  * Created by ferreira on 11/30/16.
  */
-public final class OldMapiConverter {
+public final class OldMapiServerResponseParser {
 
-    static ServerResponses GetNextResponseOnOldMapi(char nextChar) {
-        switch (nextChar) {
+    static ServerResponses ParseOldMapiServerResponse(OldMapiProtocol protocol) {
+        ServerResponses res;
+        switch (protocol.builder.charAt(protocol.currentPointer)) {
             case '!':
-                return ServerResponses.ERROR;
+                res = ServerResponses.ERROR;
+                break;
             case '&':
-                return ServerResponses.SOHEADER;
+                res = ServerResponses.SOHEADER;
+                break;
             case '%':
-                return ServerResponses.HEADER;
+                res = ServerResponses.HEADER;
+                break;
             case '[':
-                return ServerResponses.RESULT;
+                res = ServerResponses.RESULT;
+                break;
             case '=':
-                return ServerResponses.RESULT;
+                res = ServerResponses.RESULT;
+                break;
             case '^':
-                return ServerResponses.REDIRECT;
+                res = ServerResponses.REDIRECT;
+                break;
             case '#':
-                return ServerResponses.INFO;
+                res = ServerResponses.INFO;
+                break;
             case '.':
-                return ServerResponses.PROMPT;
+                res = ServerResponses.PROMPT;
+                break;
             case ',':
-                return ServerResponses.MORE;
+                res = ServerResponses.MORE;
+                break;
             default:
-                return ServerResponses.UNKNOWN;
+                res = ServerResponses.UNKNOWN;
         }
-    }
-
-    static StarterHeaders GetNextStartHeaderOnOldMapi(char nextChar) {
-        switch (nextChar) {
-            case '0':
-                return StarterHeaders.Q_PARSE;
-            case '1':
-                return StarterHeaders.Q_TABLE;
-            case '2':
-                return StarterHeaders.Q_UPDATE;
-            case '3':
-                return StarterHeaders.Q_SCHEMA;
-            case '4':
-                return StarterHeaders.Q_TRANS;
-            case '5':
-                return StarterHeaders.Q_PREPARE;
-            case '6':
-                return StarterHeaders.Q_BLOCK;
-            default:
-                return StarterHeaders.Q_UNKNOWN;
-        }
+        return res;
     }
 }
new file mode 100644
--- /dev/null
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiStartOfHeaderParser.java
@@ -0,0 +1,92 @@
+package nl.cwi.monetdb.mcl.protocol.oldmapi;
+
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.StarterHeaders;
+
+/**
+ * Created by ferreira on 12/6/16.
+ */
+public class OldMapiStartOfHeaderParser {
+
+    static StarterHeaders GetNextStartHeaderOnOldMapi(OldMapiProtocol protocol) {
+        StarterHeaders res;
+        switch (protocol.builder.charAt(protocol.currentPointer)) {
+            case '0':
+                res = StarterHeaders.Q_PARSE;
+                break;
+            case '1':
+                res = StarterHeaders.Q_TABLE;
+                break;
+            case '2':
+                res = StarterHeaders.Q_UPDATE;
+                break;
+            case '3':
+                res = StarterHeaders.Q_SCHEMA;
+                break;
+            case '4':
+                res = StarterHeaders.Q_TRANS;
+                break;
+            case '5':
+                res = StarterHeaders.Q_PREPARE;
+                break;
+            case '6':
+                res = StarterHeaders.Q_BLOCK;
+                break;
+            default:
+                res = StarterHeaders.Q_UNKNOWN;
+        }
+        return res;
+    }
+
+    static int GetNextResponseDataAsInt(OldMapiProtocol protocol) throws MCLParseException {
+        protocol.currentPointer++;
+        if (!protocol.hasRemaining()) {
+            throw new MCLParseException("unexpected end of string", protocol.currentPointer - 1);
+        }
+        int tmp;
+        char chr = protocol.builder.charAt(protocol.currentPointer);
+        // note: don't use Character.isDigit() here, because
+        // we only want ISO-LATIN-1 digits
+        if (chr >= '0' && chr <= '9') {
+            tmp = (int)chr - (int)'0';
+        } else {
+            throw new MCLParseException("expected a digit", protocol.currentPointer - 1);
+        }
+
+        while (protocol.hasRemaining()) {
+            chr = protocol.builder.charAt(protocol.currentPointer);
+            protocol.currentPointer++;
+            if(chr == ' ') {
+                break;
+            }
+            tmp *= 10;
+            if (chr >= '0' && chr <= '9') {
+                tmp += (int)chr - (int)'0';
+            } else {
+                throw new MCLParseException("expected a digit", protocol.currentPointer - 1);
+            }
+        }
+        return tmp;
+    }
+
+    static String GetNextResponseDataAsString(OldMapiProtocol protocol) throws MCLParseException {
+        protocol.currentPointer++;
+        if (!protocol.hasRemaining()) {
+            throw new MCLParseException("unexpected end of string", protocol.currentPointer - 1);
+        }
+        int cnt = 0, mark = protocol.currentPointer;
+        char chr;
+
+        while (protocol.hasRemaining()) {
+            chr = protocol.builder.charAt(protocol.currentPointer);
+            protocol.currentPointer++;
+            if(chr == ' ') {
+                break;
+            }
+            cnt++;
+        }
+
+        protocol.currentPointer = mark;
+        return protocol.builder.subSequence(0, cnt).toString();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java
@@ -0,0 +1,107 @@
+package nl.cwi.monetdb.mcl.protocol.oldmapi;
+
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.TableResultHeaders;
+
+/**
+ * Created by ferreira on 12/6/16.
+ */
+public class OldMapiTableHeaderParser {
+
+    static TableResultHeaders GetNextTableHeader(StringBuilder builder, String[] stringValues, int[] intValues) throws MCLParseException {
+        TableResultHeaders res = TableResultHeaders.UNKNOWN;
+        int len = builder.length(), pos = 0;
+        boolean foundChar = false, nameFound = false;
+
+        // find header name
+        for (int i = len - 1; i >= 0; i--) {
+            switch (builder.charAt(i)) {
+                case ' ':
+                case '\n':
+                case '\t':
+                case '\r':
+                    if (!foundChar) {
+                        len = i - 1;
+                    } else {
+                        pos = i + 1;
+                    }
+                    break;
+                case '#':
+                    // found!
+                    nameFound = true;
+                    if (pos == 0) pos = i + 1;
+                    i = 0;	// force the loop to terminate
+                    break;
+                default:
+                    foundChar = true;
+                    pos = 0;
+                    break;
+            }
+        }
+        if (!nameFound)
+            throw new MCLParseException("invalid header, no header name found", pos);
+
+        // depending on the name of the header, we continue
+        switch (builder.charAt(pos)) {
+            case 'n': //name
+                if (len - pos == 4) {
+                    GetStringValues(builder, pos - 3, stringValues);
+                    res = TableResultHeaders.NAME;
+                }
+                break;
+            case 'l': //length
+                if (len - pos == 6) {
+                    GetIntValues(builder, pos - 3, intValues);
+                    res = TableResultHeaders.LENGTH;
+                }
+                break;
+            case 't':
+                if (len - pos == 4) { //type
+                    GetStringValues(builder, pos - 3, stringValues);
+                    res = TableResultHeaders.TYPE;
+                } else if (len - pos == 10) { //table_name
+                    GetStringValues(builder, pos - 3, stringValues);
+                    res = TableResultHeaders.TABLE;
+                }
+                break;
+            default:
+                throw new MCLParseException("unknown header: " + builder.substring(pos, len - pos));
+        }
+        return res;
+    }
+
+    private static void GetStringValues(StringBuilder builder, int stop, String[] stringValues) {
+        int elem = 0, start = 2;
+
+        for (int i = start + 1; i < stop; i++) {
+            if (builder.charAt(i) == '\t' && builder.charAt(i - 1) == ',') {
+                stringValues[elem++] = builder.substring(start, i - 1 - start);
+                start = i + 1;
+            }
+        }
+        // add the left over part
+        stringValues[elem + 1] = builder.substring(start, stop - start);
+    }
+
+    private static void GetIntValues(StringBuilder builder, int stop, int[] intValues) throws MCLParseException {
+        int elem = 0, tmp = 0, start = 2;
+
+        for (int i = start; i < stop; i++) {
+            if (builder.charAt(i) == ',' && builder.charAt(i + 1) == '\t') {
+                intValues[elem++] = tmp;
+                tmp = 0;
+            } else {
+                tmp *= 10;
+                // note: don't use Character.isDigit() here, because
+                // we only want ISO-LATIN-1 digits
+                if (builder.charAt(i) >= '0' && builder.charAt(i) <= '9') {
+                    tmp += (int) builder.charAt(i) - (int)'0';
+                } else {
+                    throw new MCLParseException("expected a digit in " + builder.toString() + " at " + i);
+                }
+            }
+        }
+        // add the left over part
+        intValues[elem + 1] = tmp;
+    }
+}
rename from src/main/java/nl/cwi/monetdb/responses/AutoCommitResponse.java
rename to src/main/java/nl/cwi/monetdb/mcl/responses/AutoCommitResponse.java
--- a/src/main/java/nl/cwi/monetdb/responses/AutoCommitResponse.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/AutoCommitResponse.java
@@ -1,4 +1,4 @@
-package nl.cwi.monetdb.responses;
+package nl.cwi.monetdb.mcl.responses;
 
 /**
  * The AutoCommitResponse represents a transaction message.  It
@@ -7,10 +7,14 @@ package nl.cwi.monetdb.responses;
  */
 public class AutoCommitResponse extends SchemaResponse {
 
-    public final boolean autocommit;
+    private final boolean autocommit;
 
     public AutoCommitResponse(boolean ac) {
         // fill the blank final
         this.autocommit = ac;
     }
+
+    public boolean isAutocommit() {
+        return autocommit;
+    }
 }
rename from src/main/java/nl/cwi/monetdb/responses/DataBlockResponse.java
rename to src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java
--- a/src/main/java/nl/cwi/monetdb/responses/DataBlockResponse.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java
@@ -1,7 +1,9 @@
-package nl.cwi.monetdb.responses;
+package nl.cwi.monetdb.mcl.responses;
 
-import nl.cwi.monetdb.mcl.io.AbstractMCLReader;
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.ServerResponses;
 
+import java.lang.reflect.Array;
 import java.sql.SQLException;
 
 /**
@@ -21,10 +23,10 @@ import java.sql.SQLException;
  * rows from it.  When multiple threads will retrieve rows from this
  * object, it is possible for threads to get the same data.
  */
-public class DataBlockResponse implements IResponse {
+public class DataBlockResponse<T> implements IIncompleteResponse {
 
     /** The String array to keep the data in */
-    private final String[] data;
+    private final T[] data;
     /** The counter which keeps the current position in the data array */
     private int pos;
     /** Whether we can discard lines as soon as we have read them */
@@ -35,9 +37,10 @@ public class DataBlockResponse implement
      * @param size the size of the data array to create
      * @param forward whether this is a forward only result
      */
-    DataBlockResponse(int size, boolean forward) {
+    @SuppressWarnings("unchecked")
+    public DataBlockResponse(int size, boolean forward, Class<T> jClass) {
         this.pos = -1;
-        this.data = new String[size];
+        this.data = (T[]) Array.newInstance(jClass, size);
         this.forwardOnly = forward;
     }
 
@@ -48,23 +51,19 @@ public class DataBlockResponse implement
      * specified.
      *
      * @param line the header line as String
-     * @param linetype the line type according to the MAPI protocol
-     * @return a non-null String if the line is invalid,
-     *         or additional lines are not allowed.
+     * @param response the line type according to the MAPI protocol
+     * @throws MCLParseException If the result line is not expected
      */
-    @Override
-    public String addLine(String line, int linetype) {
-        if (linetype != AbstractMCLReader.RESULT)
-            return "protocol violation: unexpected line in data block: " + line;
+    @SuppressWarnings("unchecked")
+    public void addLine(ServerResponses response, Object line) throws MCLParseException {
+        if (response != ServerResponses.RESULT)
+            throw new MCLParseException("protocol violation: unexpected line in data block: " + line.toString());
         // add to the backing array
-        data[++pos] = line;
-
-        // all is well
-        return null;
+        data[++pos] = (T) line;
     }
 
     /**
-     * Returns whether this Reponse expects more lines to be added
+     * Returns whether this Response expects more lines to be added
      * to it.
      *
      * @return true if a next line should be added, false otherwise
@@ -93,7 +92,6 @@ public class DataBlockResponse implement
     /**
      * Instructs the Response implementation to close and do the
      * necessary clean up procedures.
-     *
      */
     @Override
     public void close() {
@@ -111,9 +109,9 @@ public class DataBlockResponse implement
      * @param line the row to retrieve
      * @return the requested row as String
      */
-    String getRow(int line) {
+    public T getRow(int line) {
         if (forwardOnly) {
-            String ret = data[line];
+            T ret = data[line];
             data[line] = null;
             return ret;
         } else {
new file mode 100644
--- /dev/null
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/IIncompleteResponse.java
@@ -0,0 +1,30 @@
+package nl.cwi.monetdb.mcl.responses;
+
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.ServerResponses;
+
+import java.sql.SQLException;
+
+/**
+ * Created by ferreira on 12/5/16.
+ */
+public interface IIncompleteResponse extends IResponse {
+    /**
+     * Returns whether this Response expects more lines to be added
+     * to it.
+     *
+     * @return true if a next line should be added, false otherwise
+     */
+    boolean wantsMore();
+
+    /**
+     * Indicates that no more header lines will be added to this
+     * Response implementation.
+     *
+     * @throws SQLException if the contents of the Response is not
+     *         consistent or sufficient.
+     */
+    void complete() throws SQLException;
+
+    void addLine(ServerResponses response, Object line) throws MCLParseException;
+}
rename from src/main/java/nl/cwi/monetdb/responses/IResponse.java
rename to src/main/java/nl/cwi/monetdb/mcl/responses/IResponse.java
--- a/src/main/java/nl/cwi/monetdb/responses/IResponse.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/IResponse.java
@@ -1,40 +1,10 @@
-package nl.cwi.monetdb.responses;
-
-import java.sql.SQLException;
+package nl.cwi.monetdb.mcl.responses;
 
 /**
  * A Response is a message sent by the server to indicate some
  * action has taken place, and possible results of that action.
  */
 public interface IResponse {
-
-    /**
-     * Adds a line to the underlying Response implementation.
-     *
-     * @param line the header line as String
-     * @param linetype the line type according to the MAPI protocol
-     * @return a non-null String if the line is invalid,
-     *         or additional lines are not allowed.
-     */
-    String addLine(String line, int linetype);
-
-    /**
-     * Returns whether this Response expects more lines to be added
-     * to it.
-     *
-     * @return true if a next line should be added, false otherwise
-     */
-    boolean wantsMore();
-
-    /**
-     * Indicates that no more header lines will be added to this
-     * Response implementation.
-     *
-     * @throws SQLException if the contents of the Response is not
-     *         consistent or sufficient.
-     */
-    void complete() throws SQLException;
-
     /**
      * Instructs the Response implementation to close and do the
      * necessary clean up procedures.
rename from src/main/java/nl/cwi/monetdb/responses/ResultSetResponse.java
rename to src/main/java/nl/cwi/monetdb/mcl/responses/ResultSetResponse.java
--- a/src/main/java/nl/cwi/monetdb/responses/ResultSetResponse.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/ResultSetResponse.java
@@ -1,10 +1,11 @@
-package nl.cwi.monetdb.responses;
+package nl.cwi.monetdb.mcl.responses;
 
 import nl.cwi.monetdb.jdbc.MonetConnection;
-import nl.cwi.monetdb.mcl.io.AbstractMCLReader;
-import nl.cwi.monetdb.mcl.parser.HeaderLineParser;
 import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.protocol.ServerResponses;
 
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
@@ -20,50 +21,54 @@ import java.sql.SQLException;
  * there the first line consists out of<br />
  * <tt>&amp;"qt" "id" "tc" "cc" "rc"</tt>.
  */
-public class ResultSetResponse implements IResponse {
+public class ResultSetResponse<T> implements IIncompleteResponse {
+
+    private static boolean CheckBooleanValuesAllTrue(boolean[] array) {
+        for (boolean anArray : array) {
+            if (!anArray) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /** The number of columns in this result */
-    public final int columncount;
+    private final int columncount;
+    /** The number of rows in the current block */
+    private final int rowcount;
     /** The total number of rows this result set has */
-    public final int tuplecount;
+    private final int tuplecount;
     /** The numbers of rows to retrieve per DataBlockResponse */
     private int cacheSize;
     /** The table ID of this result */
-    public final int id;
+    private final int id;
     /** The names of the columns in this result */
-    private String[] name;
+    private final String[] name;
     /** The types of the columns in this result */
-    private String[] type;
+    private final String[] type;
     /** The max string length for each column in this result */
-    private int[] columnLengths;
+    private final int[] columnLengths;
     /** The table for each column in this result */
-    private String[] tableNames;
+    private final String[] tableNames;
     /** The query sequence number */
     private final int seqnr;
-    /** A List of result blocks (chunks of size fetchSize/cacheSize) */
-    private DataBlockResponse[] resultBlocks;
-
     /** A bitmap telling whether the headers are set or not */
-    private boolean[] isSet;
+    private boolean[] isSet = new boolean[4];
     /** Whether this Response is closed */
     private boolean closed;
 
+    /** The connection belonging for this ResultSetResponse */
+    private MonetConnection con;
     /** The Connection that we should use when requesting a new block */
-    private ResponseList parent;
+    private MonetConnection.ResponseList parent;
     /** Whether the fetchSize was explitly set by the user */
     private boolean cacheSizeSetExplicitly = false;
-    /** Whether we should send an Xclose command to the server
-     *  if we close this Response */
+    /** Whether we should send an Xclose command to the server if we close this Response */
     private boolean destroyOnClose;
     /** the offset to be used on Xexport queries */
     private int blockOffset = 0;
-
-    /** A parser for header lines */
-    HeaderLineParser hlp;
-
-    private final static int NAMES	= 0;
-    private final static int TYPES	= 1;
-    private final static int TABLES	= 2;
-    private final static int LENS	= 3;
+    /** A List of result blocks (chunks of size fetchSize/cacheSize) */
+    private DataBlockResponse<T>[] resultBlocks;
 
     /**
      * Sole constructor, which requires a MonetConnection parent to
@@ -77,91 +82,51 @@ public class ResultSetResponse implement
      *               supply new result blocks when necessary
      * @param seq the query sequence number
      */
-    ResultSetResponse(int id, int tuplecount, int columncount, int rowcount, ResponseList parent, int seq) throws SQLException {
-        isSet = new boolean[7];
+    @SuppressWarnings("unchecked")
+    public ResultSetResponse(MonetConnection con, MonetConnection.ResponseList parent, int id, int seq, int rowcount, int tuplecount, int columncount) {
+        this.con = con;
         this.parent = parent;
-        if (parent.cachesize == 0) {
-				/* Below we have to calculate how many "chunks" we need
-				 * to allocate to store the entire result.  However, if
-				 * the user didn't set a cache size, as in this case, we
-				 * need to stick to our defaults. */
-            cacheSize = ResponseList.DEF_FETCHSIZE;
+        if (parent.getCachesize() == 0) {
+            /* Below we have to calculate how many "chunks" we need
+             * to allocate to store the entire result.  However, if
+             * the user didn't set a cache size, as in this case, we
+             * need to stick to our defaults. */
+            cacheSize = MonetConnection.GetDefFetchsize();
             cacheSizeSetExplicitly = false;
         } else {
-            cacheSize = parent.cachesize;
+            cacheSize = parent.getCachesize();
             cacheSizeSetExplicitly = true;
         }
-			/* So far, so good.  Now the problem with EXPLAIN, DOT, etc
-			 * queries is, that they don't support any block fetching,
-			 * so we need to always fetch everything at once.  For that
-			 * reason, the cache size is here set to the rowcount if
-			 * it's larger, such that we do a full fetch at once.
-			 * (Because we always set a reply_size, we can only get a
-			 * larger rowcount from the server if it doesn't paginate,
-			 * because it's a pseudo SQL result.) */
-        if (rowcount > cacheSize)
-            cacheSize = rowcount;
-        seqnr = seq;
-        closed = false;
-        destroyOnClose = id > 0 && tuplecount > rowcount;
-
-        this.id = id;
-        this.tuplecount = tuplecount;
-        this.columncount = columncount;
-        this.resultBlocks = new DataBlockResponse[(tuplecount / cacheSize) + 1];
-
-        hlp = server.getHeaderLineParser(columncount);
-
-        resultBlocks[0] = new DataBlockResponse(rowcount, parent.rstype == ResultSet.TYPE_FORWARD_ONLY);
-    }
 
-    /**
-     * Parses the given string and changes the value of the matching
-     * header appropriately, or passes it on to the underlying
-     * DataResponse.
-     *
-     * @param tmpLine the string that contains the header
-     * @return a non-null String if the header cannot be parsed or
-     *         is unknown
-     */
-    // {{{ addLine
-    @Override
-    public String addLine(String tmpLine, int linetype) {
-        if (isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES]) {
-            return resultBlocks[0].addLine(tmpLine, linetype);
+        /* So far, so good.  Now the problem with EXPLAIN, DOT, etc
+         * queries is, that they don't support any block fetching,
+         * so we need to always fetch everything at once.  For that
+         * reason, the cache size is here set to the rowcount if
+         * it's larger, such that we do a full fetch at once.
+         * (Because we always set a reply_size, we can only get a
+         * larger rowcount from the server if it doesn't paginate,
+         * because it's a pseudo SQL result.) */
+        if (rowcount > cacheSize) {
+            cacheSize = rowcount;
         }
-
-        if (linetype != AbstractMCLReader.HEADER)
-            return "header expected, got: " + tmpLine;
+        this.seqnr = seq;
+        this.destroyOnClose = id > 0 && tuplecount > rowcount;
+        this.id = id;
+        this.rowcount = rowcount;
 
-        // depending on the name of the header, we continue
-        try {
-            switch (hlp.parse(tmpLine)) {
-                case HeaderLineParser.NAME:
-                    name = hlp.values.clone();
-                    isSet[NAMES] = true;
-                    break;
-                case HeaderLineParser.LENGTH:
-                    columnLengths = hlp.intValues.clone();
-                    isSet[LENS] = true;
-                    break;
-                case HeaderLineParser.TYPE:
-                    type = hlp.values.clone();
-                    isSet[TYPES] = true;
-                    break;
-                case HeaderLineParser.TABLE:
-                    tableNames = hlp.values.clone();
-                    isSet[TABLES] = true;
-                    break;
-            }
-        } catch (MCLParseException e) {
-            return e.getMessage();
-        }
+        int maxrows = parent.getMaxrows();
+        this.tuplecount = (maxrows != 0 && tuplecount > maxrows) ? maxrows : tuplecount;
+        this.columncount = columncount;
 
-        // all is well
-        return null;
+        this.name = new String[this.columncount];
+        this.type = new String[this.columncount];
+        this.tableNames = new String[this.columncount];
+        this.columnLengths = new int[this.columncount];
+
+        Class<T> persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+        this.resultBlocks = (DataBlockResponse<T>[]) Array.newInstance(DataBlockResponse.class, (tuplecount / cacheSize) + 1);
+        this.resultBlocks[0] = new DataBlockResponse<>(rowcount, parent.getRstype() == ResultSet.TYPE_FORWARD_ONLY, persistentClass);
     }
-    // }}}
 
     /**
      * Returns whether this ResultSetResponse needs more lines.
@@ -170,33 +135,7 @@ public class ResultSetResponse implement
      */
     @Override
     public boolean wantsMore() {
-        return !(isSet[LENS] && isSet[TYPES] && isSet[TABLES] && isSet[NAMES]) || resultBlocks[0].wantsMore();
-    }
-
-    /**
-     * Returns an array of Strings containing the values between
-     * ',\t' separators.
-     *
-     * @param chrLine a character array holding the input data
-     * @param start where the relevant data starts
-     * @param stop where the relevant data stops
-     * @return an array of Strings
-     */
-    final private String[] getValues(char[] chrLine, int start, int stop) {
-        int elem = 0;
-        String[] values = new String[columncount];
-
-        for (int i = start; i < stop; i++) {
-            if (chrLine[i] == '\t' && chrLine[i - 1] == ',') {
-                values[elem++] =
-                        new String(chrLine, start, i - 1 - start);
-                start = i + 1;
-            }
-        }
-        // at the left over part
-        values[elem++] = new String(chrLine, start, stop - start);
-
-        return values;
+        return !CheckBooleanValuesAllTrue(isSet) || resultBlocks[0].wantsMore();
     }
 
     /**
@@ -206,7 +145,7 @@ public class ResultSetResponse implement
      * @param offset the offset number of rows for this block
      * @param rr the DataBlockResponse to add
      */
-    public void addDataBlockResponse(int offset, DataBlockResponse rr) {
+    public void addDataBlockResponse(int offset, DataBlockResponse<T> rr) {
         int block = (offset - blockOffset) / cacheSize;
         resultBlocks[block] = rr;
     }
@@ -216,18 +155,34 @@ public class ResultSetResponse implement
      * needs to be consistent with regard to its internal data.
      *
      * @throws SQLException if the data currently in this Response is not
-     *                      sufficient to be consistant
+     *                      sufficient to be consistent
      */
     @Override
     public void complete() throws SQLException {
         String error = "";
-        if (!isSet[NAMES]) error += "name header missing\n";
-        if (!isSet[TYPES]) error += "type header missing\n";
-        if (!isSet[TABLES]) error += "table name header missing\n";
-        if (!isSet[LENS]) error += "column width header missing\n";
+        if (!isSet[0]) error += "name header missing\n";
+        if (!isSet[1]) error += "column width header missing\n";
+        if (!isSet[2]) error += "table name header missing\n";
+        if (!isSet[3]) error += "type header missing\n";
         if (!error.equals("")) throw new SQLException(error, "M0M10");
     }
 
+    public int getId() {
+        return id;
+    }
+
+    public int getColumncount() {
+        return columncount;
+    }
+
+    public int getTuplecount() {
+        return tuplecount;
+    }
+
+    public int getRowcount() {
+        return rowcount;
+    }
+
     /**
      * Returns the names of the columns
      *
@@ -288,7 +243,7 @@ public class ResultSetResponse implement
      * @return the ResultSet type
      */
     public int getRSType() {
-        return parent.rstype;
+        return parent.getRstype();
     }
 
     /**
@@ -297,12 +252,48 @@ public class ResultSetResponse implement
      * @return the ResultSet concurrency
      */
     public int getRSConcur() {
-        return parent.rsconcur;
+        return parent.getRsconcur();
+    }
+
+    /**
+     * Parses the given string and changes the value of the matching
+     * header appropriately, or passes it on to the underlying
+     * DataResponse.
+     *
+     * @param line the string that contains the header
+     * @throws MCLParseException if has a wrong header
+     */
+    @SuppressWarnings("unchecked")
+    public void addLine(ServerResponses response, Object line) throws MCLParseException {
+        if (CheckBooleanValuesAllTrue(isSet)) {
+            resultBlocks[0].addLine(response, line);
+        }
+        if (response != ServerResponses.HEADER) {
+            throw new MCLParseException("header expected, got: " + response.toString());
+        } else {
+            //we will always pass the tableNames pointer
+            switch (con.getProtocol().getNextTableHeader(line, this.tableNames, this.columnLengths)) {
+                case NAME:
+                    System.arraycopy(this.tableNames, 0, this.name, 0, this.columncount);
+                    isSet[0] = true;
+                    break;
+                case LENGTH:
+                    isSet[1] = true;
+                    break;
+                case TYPE:
+                    System.arraycopy(this.tableNames, 0, this.type, 0, this.columncount);
+                    isSet[2] = true;
+                    break;
+                case TABLE:
+                    isSet[3] = true;
+                    break;
+            }
+        }
     }
 
     /**
      * Returns a line from the cache. If the line is already present in the
-     * cache, it is returned, if not apropriate actions are taken to make
+     * cache, it is returned, if not appropriate actions are taken to make
      * sure the right block is being fetched and as soon as the requested
      * line is fetched it is returned.
      *
@@ -311,7 +302,8 @@ public class ResultSetResponse implement
      *         is out of the scope of the result set
      * @throws SQLException if an database error occurs
      */
-    public String getLine(int row) throws SQLException {
+    @SuppressWarnings("unchecked")
+    public T getLine(int row) throws SQLException {
         if (row >= tuplecount || row < 0)
             return null;
 
@@ -319,21 +311,21 @@ public class ResultSetResponse implement
         int blockLine = (row - blockOffset) % cacheSize;
 
         // do we have the right block loaded? (optimistic try)
-        DataBlockResponse rawr;
+        DataBlockResponse<T> rawr;
         // load block if appropriate
         if ((rawr = resultBlocks[block]) == null) {
             /// TODO: ponder about a maximum number of blocks to keep
             ///       in memory when dealing with random access to
             ///       reduce memory blow-up
 
-            // if we're running forward only, we can discard the oldmapi
+            // if we're running forward only, we can discard the resultset
             // block loaded
-            if (parent.rstype == ResultSet.TYPE_FORWARD_ONLY) {
+            if (parent.getRstype() == ResultSet.TYPE_FORWARD_ONLY) {
                 for (int i = 0; i < block; i++)
                     resultBlocks[i] = null;
 
-                if (ResponseList.SeqCounter - 1 == seqnr && !cacheSizeSetExplicitly &&
-                        tuplecount - row > cacheSize && cacheSize < ResponseList.DEF_FETCHSIZE * 10) {
+                if (MonetConnection.GetSeqCounter() - 1 == seqnr && !cacheSizeSetExplicitly &&
+                        tuplecount - row > cacheSize && cacheSize < MonetConnection.GetDefFetchsize() * 10) {
                     // there has no query been issued after this
                     // one, so we can consider this an uninterrupted
                     // continuation request.  Let's once increase
@@ -363,11 +355,11 @@ public class ResultSetResponse implement
             }
 
             // ok, need to fetch cache block first
-            parent.executeQuery(server.getCommandHeaderTemplates(),
-                    "export " + id + " " + ((block * cacheSize) + blockOffset) + " " + cacheSize);
+            parent.executeQuery(con.getLanguage().getCommandTemplates(), "export " + id + " " + ((block * cacheSize) + blockOffset) + " " + cacheSize);
             rawr = resultBlocks[block];
-            if (rawr == null) throw
-                    new AssertionError("block " + block + " should have been fetched by now :(");
+            if (rawr == null) {
+                throw new AssertionError("block " + block + " should have been fetched by now :(");
+            }
         }
 
         return rawr.getRow(blockLine);
@@ -384,7 +376,9 @@ public class ResultSetResponse implement
         // result only if we had an ID in the header and this result
         // was larger than the reply size
         try {
-            if (destroyOnClose) sendControlCommand("close " + id);
+            if (destroyOnClose) {
+                con.sendControlCommand("close " + id);
+            }
         } catch (SQLException e) {
             // probably a connection error...
         }
rename from src/main/java/nl/cwi/monetdb/responses/SchemaResponse.java
rename to src/main/java/nl/cwi/monetdb/mcl/responses/SchemaResponse.java
--- a/src/main/java/nl/cwi/monetdb/responses/SchemaResponse.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/SchemaResponse.java
@@ -1,4 +1,4 @@
-package nl.cwi.monetdb.responses;
+package nl.cwi.monetdb.mcl.responses;
 
 import java.sql.Statement;
 
@@ -13,21 +13,10 @@ import java.sql.Statement;
  */
 public class SchemaResponse implements IResponse {
 
-    public final int state = Statement.SUCCESS_NO_INFO;
-
-    @Override
-    public String addLine(String line, int linetype) {
-        return "Header lines are not supported for a SchemaResponse";
-    }
+    private final int state = Statement.SUCCESS_NO_INFO;
 
-    @Override
-    public boolean wantsMore() {
-        return false;
-    }
-
-    @Override
-    public void complete() {
-        // empty, because there is nothing to check
+    public int getState() {
+        return state;
     }
 
     @Override
rename from src/main/java/nl/cwi/monetdb/responses/UpdateResponse.java
rename to src/main/java/nl/cwi/monetdb/mcl/responses/UpdateResponse.java
--- a/src/main/java/nl/cwi/monetdb/responses/UpdateResponse.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/UpdateResponse.java
@@ -1,4 +1,4 @@
-package nl.cwi.monetdb.responses;
+package nl.cwi.monetdb.mcl.responses;
 
 /**
  * The UpdateResponse represents an update statement response.  It
@@ -9,28 +9,23 @@ package nl.cwi.monetdb.responses;
  * <tt>&amp;2 0 -1</tt>
  */
 public class UpdateResponse implements IResponse {
-    public final int count;
-    public final String lastid;
+
+    private final String lastid;
+
+    private final int count;
 
-    public UpdateResponse(int cnt, String id) {
+    public UpdateResponse(String id, int cnt) {
         // fill the blank finals
+        this.lastid = id;
         this.count = cnt;
-        this.lastid = id;
     }
 
-    @Override
-    public String addLine(String line, int linetype) {
-        return "Header lines are not supported for an UpdateResponse";
+    public String getLastid() {
+        return lastid;
     }
 
-    @Override
-    public boolean wantsMore() {
-        return false;
-    }
-
-    @Override
-    public void complete() {
-        // empty, because there is nothing to check
+    public int getCount() {
+        return count;
     }
 
     @Override
--- a/src/main/java/nl/cwi/monetdb/merovingian/Control.java
+++ b/src/main/java/nl/cwi/monetdb/merovingian/Control.java
@@ -131,7 +131,7 @@ public class Control {
 		} catch (AssertionError e) { // mcl panics
 			ms.close();
 			
-			// Try oldmapi protocol instead
+			// Try resultset protocol instead
 			Socket s;
 			PrintStream out;
 			BufferedReader in;
deleted file mode 100644
--- a/src/main/java/nl/cwi/monetdb/responses/ResponseList.java
+++ /dev/null
@@ -1,403 +0,0 @@
-package nl.cwi.monetdb.responses;
-
-import nl.cwi.monetdb.jdbc.MonetConnection;
-import nl.cwi.monetdb.mcl.connection.SendThread;
-import nl.cwi.monetdb.mcl.io.AbstractMCLReader;
-import nl.cwi.monetdb.mcl.parser.MCLParseException;
-import nl.cwi.monetdb.mcl.parser.StartOfHeaderParser;
-
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A list of Response objects.  Responses are added to this list.
- * Methods of this class are not synchronized.  This is left as
- * responsibility to the caller to prevent concurrent access.
- */
-public class ResponseList {
-
-    /** the default number of rows that are (attempted to) read at once */
-    protected final static int DEF_FETCHSIZE = 250;
-    /** The sequence counter */
-    protected static int SeqCounter = 0;
-
-    /** The cache size (number of rows in a DataBlockResponse object) */
-    private final int cachesize;
-    /** The maximum number of results for this query */
-    private final int maxrows;
-    /** The ResultSet type to produce */
-    final int rstype;
-    /** The ResultSet concurrency to produce */
-    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 List<IResponse> responses = new ArrayList<>();
-    /** A map of ResultSetResponses, used for additional
-     *  DataBlockResponse mapping */
-    private Map<Integer, ResultSetResponse> rsresponses;
-    /** The current header returned by getNextResponse() */
-    private int curResponse = -1;
-
-    /**
-     * 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
-     */
-    public 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++;
-    }
-
-    /**
-     * Retrieves the next available response, or null if there are
-     * no more responses.
-     *
-     * @return the next Response available or null
-     */
-    public IResponse getNextResponse() throws SQLException {
-        if (rstype == ResultSet.TYPE_FORWARD_ONLY) {
-            // free resources if we're running forward only
-            if (curResponse >= 0 && curResponse < responses.size()) {
-                IResponse tmp = responses.get(curResponse);
-                if (tmp != null) tmp.close();
-                responses.set(curResponse, null);
-            }
-        }
-        curResponse++;
-        if (curResponse >= responses.size()) {
-            // ResponseList is obviously completed so, there are no
-            // more responses
-            return null;
-        } else {
-            // return this response
-            return responses.get(curResponse);
-        }
-    }
-
-    /**
-     * Closes the Response at index i, if not null.
-     *
-     * @param i the index position of the header to close
-     */
-    public void closeResponse(int i) {
-        if (i < 0 || i >= responses.size()) return;
-        IResponse tmp = responses.set(i, null);
-        if (tmp != null)
-            tmp.close();
-    }
-
-    /**
-     * Closes the current response.
-     */
-    public void closeCurrentResponse() {
-        closeResponse(curResponse);
-    }
-
-    /**
-     * Closes the current and previous responses.
-     */
-    public void closeCurOldResponses() {
-        for (int i = curResponse; i >= 0; i--) {
-            closeResponse(i);
-        }
-    }
-
-    /**
-     * Closes this ResponseList by closing all the Responses in this
-     * ResponseList.
-     */
-    public void close() {
-        for (int i = 0; i < responses.size(); i++) {
-            closeResponse(i);
-        }
-    }
-
-    /**
-     * Returns whether this ResponseList has still unclosed
-     * Responses.
-     */
-    public boolean hasUnclosedResponses() {
-        for (IResponse r : responses) {
-            if (r != null)
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * 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
-     */
-    public void processQuery(String query) throws SQLException {
-        executeQuery(server.getQueryHeaderTemplates(), query);
-    }
-
-    /**
-     * Internal executor of queries.
-     *
-     * @param templ the template to fill in
-     * @param query the query to execute
-     * @throws SQLException if a database error occurs
-     */
-    @SuppressWarnings("fallthrough")
-    public void executeQuery(String[] templ, String query) throws SQLException {
-        boolean sendThreadInUse = false;
-        String error = null;
-
-        try {
-            synchronized (server) {
-                // 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.
-                in.waitForPrompt();
-
-                // {{{ 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.
-                 */
-                int size = cachesize == 0 ? DEF_FETCHSIZE : cachesize;
-                size = maxrows != 0 ? Math.min(maxrows, size) : size;
-                // don't do work if it's not needed
-                if (server.getLang() == MonetConnection.LANG_SQL && size != curReplySize && templ != server.getCommandHeaderTemplates()) {
-                    sendControlCommand("reply_size " + size);
-
-                    // store the reply size after a successful change
-                    curReplySize = size;
-                }
-                // }}} 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() > server.getBlockSize()) {
-                    // get a reference to the send thread
-                    if (sendThread == null)
-                        sendThread = new SendThread(out);
-                    // tell it to do some work!
-                    sendThread.runQuery(templ, query);
-                    sendThreadInUse = true;
-                } else {
-                    // this is a simple call, which is a lot cheaper and will
-                    // always succeed for small queries.
-                    out.writeLine((templ[0] == null ? "" : templ[0] + query + templ[1] == null ? "" : templ[1]));
-                }
-
-                // go for new results
-                String tmpLine = in.readLine();
-                int linetype = in.getLineType();
-                IResponse res = null;
-                while (linetype != AbstractMCLReader.PROMPT) {
-                    // each response should start with a start of header
-                    // (or error)
-                    switch (linetype) {
-                        case AbstractMCLReader.SOHEADER:
-                            // make the response object, and fill it
-                            try {
-                                switch (sohp.parse(tmpLine)) {
-                                    case StartOfHeaderParser.Q_PARSE:
-                                        throw new MCLParseException("Q_PARSE header not allowed here", 1);
-                                    case StartOfHeaderParser.Q_TABLE:
-                                    case StartOfHeaderParser.Q_PREPARE: {
-                                        int id = sohp.getNextAsInt();
-                                        int tuplecount = sohp.getNextAsInt();
-                                        int columncount = sohp.getNextAsInt();
-                                        int rowcount = sohp.getNextAsInt();
-                                        // enforce the maxrows setting
-                                        if (maxrows != 0 && tuplecount > maxrows)
-                                            tuplecount = maxrows;
-                                        res = new ResultSetResponse(id, tuplecount, columncount, rowcount,
-                                                this, seqnr);
-                                        // only add this resultset to
-                                        // the hashmap if it can possibly
-                                        // have an additional datablock
-                                        if (rowcount < tuplecount) {
-                                            if (rsresponses == null)
-                                                rsresponses = new HashMap<>();
-                                            rsresponses.put(id, (ResultSetResponse) res);
-                                        }
-                                    } break;
-                                    case StartOfHeaderParser.Q_UPDATE:
-                                        res = new UpdateResponse(
-                                                sohp.getNextAsInt(),   // count
-                                                sohp.getNextAsString() // key-id
-                                        );
-                                        break;
-                                    case StartOfHeaderParser.Q_SCHEMA:
-                                        res = new SchemaResponse();
-                                        break;
-                                    case StartOfHeaderParser.Q_TRANS:
-                                        boolean ac = sohp.getNextAsString().equals("t");
-                                        if (autoCommit && ac) {
-                                            addWarning("Server enabled auto commit " +
-                                                    "mode while local state " +
-                                                    "already was auto commit.", "01M11"
-                                            );
-                                        }
-                                        autoCommit = ac;
-                                        res = new AutoCommitResponse(ac);
-                                        break;
-                                    case StartOfHeaderParser.Q_BLOCK: {
-                                        // a new block of results for a
-                                        // response...
-                                        int id = sohp.getNextAsInt();
-                                        sohp.getNextAsInt();	// columncount
-                                        int rowcount = sohp.getNextAsInt();
-                                        int offset = sohp.getNextAsInt();
-                                        ResultSetResponse t = rsresponses.get(id);
-                                        if (t == null) {
-                                            error = "M0M12!no ResultSetResponse with id " + id + " found";
-                                            break;
-                                        }
-
-                                        DataBlockResponse r = new DataBlockResponse(rowcount,
-                                                t.getRSType() == ResultSet.TYPE_FORWARD_ONLY);
-
-                                        t.addDataBlockResponse(offset, r);
-                                        res = r;
-                                    } break;
-                                }
-                            } catch (MCLParseException e) {
-                                error = "M0M10!error while parsing start of header:\n" +
-                                        e.getMessage() +
-                                        " found: '" + tmpLine.charAt(e.getErrorOffset()) + "'" +
-                                        " in: \"" + tmpLine + "\"" +
-                                        " at pos: " + e.getErrorOffset();
-                                // flush all the rest
-                                in.waitForPrompt();
-                                linetype = in.getLineType();
-                                break;
-                            }
-
-                            // immediately handle errors after parsing
-                            // the header (res may be null)
-                            if (error != null) {
-                                in.waitForPrompt();
-                                linetype = in.getLineType();
-                                break;
-                            }
-
-                            // here we have a res object, which
-                            // we can start filling
-                            while (res.wantsMore()) {
-                                error = res.addLine(
-                                        in.readLine(),
-                                        in.getLineType()
-                                );
-                                if (error != null) {
-                                    // right, some protocol violation,
-                                    // skip the rest of the result
-                                    error = "M0M10!" + error;
-                                    in.waitForPrompt();
-                                    linetype = in.getLineType();
-                                    break;
-                                }
-                            }
-                            if (error != null)
-                                break;
-                            // it is of no use to store
-                            // DataBlockReponses, you never want to
-                            // retrieve them directly anyway
-                            if (!(res instanceof DataBlockResponse))
-                                responses.add(res);
-
-                            // read the next line (can be prompt, new
-                            // result, error, etc.) before we start the
-                            // loop over
-                            tmpLine = in.readLine();
-                            linetype = in.getLineType();
-                            break;
-                        case AbstractMCLReader.INFO:
-                            addWarning(tmpLine.substring(1), "01000");
-
-                            // read the next line (can be prompt, new
-                            // result, error, etc.) before we start the
-                            // loop over
-                            tmpLine = in.readLine();
-                            linetype = in.getLineType();
-                            break;
-                        default:	// Yeah... in Java this is correct!
-                            // we have something we don't
-                            // expect/understand, let's make it an error
-                            // message
-                            tmpLine = "!M0M10!protocol violation, unexpected line: " + tmpLine;
-                            // don't break; fall through...
-                        case AbstractMCLReader.ERROR:
-                            // read everything till the prompt (should be
-                            // error) we don't know if we ignore some
-                            // garbage here... but the log should reveal
-                            // that
-                            error = in.waitForPrompt();
-                            linetype = in.getLineType();
-                            if (error != null) {
-                                error = tmpLine.substring(1) + "\n" + error;
-                            } else {
-                                error = tmpLine.substring(1);
-                            }
-                            break;
-                    }
-                }
-            }
-
-            // if we used the sendThread, make sure it has finished
-            if (sendThreadInUse) {
-                String tmp = sendThread.getErrors();
-                if (tmp != null) {
-                    if (error == null) {
-                        error = "08000!" + tmp;
-                    } else {
-                        error += "\n08000!" + tmp;
-                    }
-                }
-            }
-            if (error != null) {
-                SQLException ret = null;
-                String[] errors = error.split("\n");
-                for (String error1 : errors) {
-                    if (ret == null) {
-                        ret = new SQLException(error1.substring(6),
-                                error1.substring(0, 5));
-                    } else {
-                        ret.setNextException(new SQLException(
-                                error1.substring(6),
-                                error1.substring(0, 5)));
-                    }
-                }
-                throw ret;
-            }
-        } catch (SocketTimeoutException e) {
-            close(); // JDBC 4.1 semantics, abort()
-            throw new SQLException("connection timed out", "08M33");
-        } catch (IOException e) {
-            closed = true;
-            throw new SQLException(e.getMessage() + " (mserver still alive?)", "08000");
-        }
-    }
-}