changeset 74:17365ed26611 embedded

In Java, you cannot get a pointer to a String, in order to make a faster memory copy. So I had to change a StringBuilder to a CharBuffer which allows to retrieve the pointer and make a faster processing.
author Pedro Ferreira <pedro.ferreira@monetdbsolutions.com>
date Thu, 15 Dec 2016 11:25:04 +0100 (2016-12-15)
parents 953422c41194
children 0ae34196c54e
files src/main/java/nl/cwi/monetdb/client/JMonetDB.java src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.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/ChannelSecurity.java src/main/java/nl/cwi/monetdb/mcl/connection/Debugger.java src/main/java/nl/cwi/monetdb/mcl/connection/SendThread.java src/main/java/nl/cwi/monetdb/mcl/connection/SenderThread.java src/main/java/nl/cwi/monetdb/mcl/connection/embedded/EmbeddedConnection.java src/main/java/nl/cwi/monetdb/mcl/connection/helpers/BufferReallocator.java src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java src/main/java/nl/cwi/monetdb/mcl/connection/helpers/Debugger.java src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.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/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/protocol/oldmapi/OldMapiTupleLineParser.java src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java
diffstat 24 files changed, 750 insertions(+), 684 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/client/JMonetDB.java
+++ b/src/main/java/nl/cwi/monetdb/client/JMonetDB.java
@@ -119,7 +119,7 @@ copts.produceHelpMessage()
 		}
 
 		String[] commands = copts.getOption("command").getArguments();
-		if (commands[0].equals("status")) {
+		/*if (commands[0].equals("status")) {
 			List<SabaothDB> sdbs;
 			if (commands.length == 1) {
 				sdbs = ctl.getAllStatuses();
@@ -131,6 +131,6 @@ copts.produceHelpMessage()
 			for (SabaothDB sdb : sdbs) {
 				System.out.println(sdb.getName() + " " + sdb.getURI());
 			}
-		}
+		}*/
 	}
 }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
@@ -3,6 +3,8 @@ package nl.cwi.monetdb.jdbc;
 import nl.cwi.monetdb.jdbc.types.INET;
 import nl.cwi.monetdb.jdbc.types.URL;
 import nl.cwi.monetdb.mcl.connection.*;
+import nl.cwi.monetdb.mcl.connection.helpers.Debugger;
+import nl.cwi.monetdb.mcl.connection.SenderThread;
 import nl.cwi.monetdb.mcl.connection.mapi.MapiLanguage;
 import nl.cwi.monetdb.mcl.protocol.ProtocolException;
 import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
@@ -66,7 +68,7 @@ public abstract class MonetConnection ex
     /** Authentication hash method */
     protected final String hash;
     /** An optional thread that is used for sending large queries */
-    private SendThread sendThread;
+    private SenderThread senderThread;
     /** Whether this Connection is closed (and cannot be used anymore) */
     private boolean closed;
     /** Whether this Connection is in autocommit mode */
@@ -96,7 +98,7 @@ public abstract class MonetConnection ex
 
     private Debugger ourSavior;
 
-    protected AbstractProtocol<?> protocol;
+    protected AbstractProtocol protocol;
 
     /**
      * Constructor of a Connection for MonetDB. At this moment the current implementation limits itself to storing the
@@ -156,7 +158,7 @@ public abstract class MonetConnection ex
 
     public abstract String getJDBCURL();
 
-    public AbstractProtocol<?> getProtocol() {
+    public AbstractProtocol getProtocol() {
         return this.protocol;
     }
 
@@ -170,36 +172,34 @@ public abstract class MonetConnection ex
      */
     @Override
     public void close() {
-        synchronized(protocol) {
-            for (Statement st : statements.keySet()) {
-                try {
-                    st.close();
-                } catch (SQLException e) {
-                    // better luck next time!
-                }
-            }
-            //close the debugger
+        for (Statement st : statements.keySet()) {
             try {
-                if (ourSavior != null) {
-                    ourSavior.close();
-                }
-            } catch (IOException e) {
-                // ignore it
+                st.close();
+            } catch (SQLException e) {
+                // better luck next time!
+            }
+        }
+        //close the debugger
+        try {
+            if (ourSavior != null) {
+                ourSavior.close();
             }
-            // close the socket or the embedded server
-            try {
-                this.closeUnderlyingConnection();
-            } catch (IOException e) {
-                // ignore it
-            }
-            // close active SendThread if any
-            if (sendThread != null) {
-                sendThread.shutdown();
-                sendThread = null;
-            }
-            // report ourselves as closed
-            closed = true;
+        } catch (IOException e) {
+            // ignore it
         }
+        // close the socket or the embedded server
+        try {
+            this.closeUnderlyingConnection();
+        } catch (IOException e) {
+            // ignore it
+        }
+        // close active SendThread if any
+        if (senderThread != null) {
+            senderThread.shutdown();
+            senderThread = null;
+        }
+        // report ourselves as closed
+        closed = true;
     }
 
     /**
@@ -1310,20 +1310,18 @@ public abstract class MonetConnection ex
      * @throws SQLException if an IO exception or a database error occurs
      */
     public void sendIndependentCommand(String command) throws SQLException {
-        synchronized (protocol) {
-            try {
-                protocol.writeNextQuery(language.getQueryTemplateIndex(0), command, 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");
-            } catch (IOException e) {
-                throw new SQLException(e.getMessage(), "08000");
+        try {
+            protocol.writeNextQuery(language.getQueryTemplateIndex(0), command, 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");
+        } catch (IOException e) {
+            throw new SQLException(e.getMessage(), "08000");
         }
     }
 
@@ -1495,192 +1493,190 @@ public abstract class MonetConnection ex
             String error = null;
 
             try {
-                synchronized (protocol) {
-                    // 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();
+                // 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 == MapiLanguage.LANG_SQL && size != curReplySize &&
-                            !Arrays.deepEquals(templ, language.getCommandTemplates())) {
-                        sendControlCommand(ControlCommands.REPLY_SIZE, size);
+                // {{{ 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 == MapiLanguage.LANG_SQL && size != curReplySize &&
+                        !Arrays.deepEquals(templ, language.getCommandTemplates())) {
+                    sendControlCommand(ControlCommands.REPLY_SIZE, size);
 
-                        // store the reply size after a successful change
-                        curReplySize = size;
-                    }
-                    // }}} set reply size
+                    // 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.writeNextQuery((templ[0] == null) ? "" : templ[0], query,
-                                (templ[1] == null) ? "" : templ[1]);
+                // If the query is larger than the TCP buffer size, use a special send thread to avoid deadlock with
+                // the server due to blocking behaviour when the buffer is full. Because the server will be writing
+                // back results to us, it will eventually block as well when its TCP buffer gets full, as we are
+                // blocking an not consuming from it. The result is a state where both client and server want to
+                // write, but block.
+                if (query.length() > getBlockSize()) {
+                    // get a reference to the send thread
+                    if (senderThread == null) {
+                        senderThread = new SenderThread(protocol);
                     }
+                    // tell it to do some work!
+                    senderThread.runQuery(templ, query);
+                } else {
+                    // this is a simple call, which is a lot cheaper and will
+                    // always succeed for small queries.
+                    protocol.writeNextQuery((templ[0] == null) ? "" : templ[0], query,
+                            (templ[1] == null) ? "" : templ[1]);
+                }
 
-                    // go for new results
-                    protocol.fetchNextResponseData();
-                    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 ProtocolException("Q_PARSE header not allowed here", 1);
-                                        case Q_TABLE:
-                                        case Q_PREPARE: {
-                                            res = protocol.getNextResultSetResponse(MonetConnection.this,
-                                                    ResponseList.this, 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);
+                // 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 ProtocolException("Q_PARSE header not allowed here", 1);
+                                    case Q_TABLE:
+                                    case Q_PREPARE: {
+                                        res = protocol.getNextResultSetResponse(MonetConnection.this,
+                                                ResponseList.this, 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_UPDATE:
-                                            res = protocol.getNextUpdateResponse();
+                                    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;
-                                        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;
+                                        res = next;
                                     }
-                                } catch (ProtocolException e) {
-                                    error = "M0M10!error while parsing start of header:\n" + e.getMessage() + " found: '"
-                                            + protocol.getRemainingStringLine(0).charAt(e.getErrorOffset()) + "'" +
-                                            " in: \"" + protocol.getRemainingStringLine(0) + "\"" + " at pos: "
-                                            + e.getErrorOffset();
-                                    // flush all the rest
-                                    protocol.waitUntilPrompt();
-                                    nextResponse = protocol.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 (ProtocolException 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
-                                error = protocol.getRemainingStringLine(1);
+                            } catch (ProtocolException e) {
+                                error = "M0M10!error while parsing start of header:\n" + e.getMessage() + " found: '"
+                                        + protocol.getRemainingStringLine(0).charAt(e.getErrorOffset()) + "'" +
+                                        " in: \"" + protocol.getRemainingStringLine(0) + "\"" + " at pos: "
+                                        + e.getErrorOffset();
+                                // flush all the rest
                                 protocol.waitUntilPrompt();
                                 nextResponse = protocol.getCurrentServerResponseHeader();
                                 break;
-                            default:
-                                throw new SQLException("Protocol violation, unexpected line!", "M0M10");
+                            }
+
+                            // 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 (ProtocolException 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(0), "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
+                            error = protocol.getRemainingStringLine(0);
+                            protocol.waitUntilPrompt();
+                            nextResponse = protocol.getCurrentServerResponseHeader();
+                            break;
+                        default:
+                            throw new SQLException("Protocol violation, unexpected line!", "M0M10");
+                    }
+                }
+
+                // if we used the senderThread, make sure it has finished
+                if (senderThread != null) {
+                    String tmp = senderThread.getErrors();
+                    if (tmp != null) {
+                        if (error == null) {
+                            error = "08000!" + tmp;
+                        } else {
+                            error += "\n08000!" + tmp;
                         }
                     }
-
-                    // if 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)));
                         }
                     }
-                    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;
-                    }
+                    throw ret;
                 }
             } catch (SocketTimeoutException e) {
                 this.close(); // JDBC 4.1 semantics, abort()
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetSavepoint.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetSavepoint.java
@@ -53,8 +53,7 @@ public class MonetSavepoint implements S
 	}
 
 	/**
-	 * Retrieves the generated ID for the savepoint that this Savepoint object
-	 * represents.
+	 * Retrieves the generated ID for the savepoint that this Savepoint object represents.
 	 *
 	 * @return the numeric ID of this savepoint
 	 * @throws SQLException if this is a named savepoint
@@ -68,8 +67,7 @@ public class MonetSavepoint implements S
 	}
 
 	/**
-	 * Retrieves the name of the savepoint that this Savepoint object
-	 * represents.
+	 * Retrieves the name of the savepoint that this Savepoint object represents.
 	 *
 	 * @return the name of this savepoint
 	 * @throws SQLException if this is an un-named savepoint
@@ -85,9 +83,8 @@ public class MonetSavepoint implements S
 	//== end of methods from Savepoint interface
 
 	/**
-	 * Retrieves the savepoint id, like the getSavepointId method with the only
-	 * difference that this method will always return the id, regardless of
-	 * whether it is named or not.
+	 * Retrieves the savepoint id, like the getSavepointId method with the only difference that this method will always
+	 * return the id, regardless of whether it is named or not.
 	 *
 	 * @return the numeric ID of this savepoint
 	 */
@@ -96,9 +93,8 @@ public class MonetSavepoint implements S
 	}
 
 	/**
-	 * Returns the name to use when referencing this save point to the MonetDB
-	 * database. The returned value is guaranteed to be unique and consists of
-	 * a prefix string and a sequence number.
+	 * Returns the name to use when referencing this save point to the MonetDB database. The returned value is
+	 * guaranteed to be unique and consists of a prefix string and a sequence number.
 	 *
 	 * @return the unique savepoint name
 	 */
@@ -114,8 +110,7 @@ public class MonetSavepoint implements S
 	 * Therefore two successive calls to this method need not to have a
 	 * difference of 1.
 	 *
-	 * @return the next int which is guaranteed to be higher than the one
-	 *         at the last call to this method.
+	 * @return the next int which is guaranteed to be higher than the one at the last call to this method.
 	 */
 	private static int getNextId() {
 		return highestId.incrementAndGet();
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
@@ -120,9 +120,8 @@ public class MonetStatement extends Mone
 	//== methods of interface Statement
 
 	/**
-	 * Adds the given SQL command to the current list of commmands for this
-	 * Statement object.  The commands in this list can be executed as a
-	 * batch by calling the method executeBatch.
+	 * Adds the given SQL command to the current list of commands for this Statement object. The commands in this list
+	 * can be executed as a batch by calling the method executeBatch.
 	 *
 	 * @param sql typically this is a static SQL INSERT or UPDATE statement
 	 * @throws SQLException so the PreparedStatement can throw this exception
@@ -183,9 +182,6 @@ public class MonetStatement extends Mone
 	 */
 	@Override
 	public int[] executeBatch() throws SQLException {
-		// this method is synchronized to make sure none gets in between the
-		// operations we execute below
-
 		batchLock.lock();
 		try {
 			// don't think long if there isn't much to do
rename from src/main/java/nl/cwi/monetdb/mcl/connection/SendThread.java
rename to src/main/java/nl/cwi/monetdb/mcl/connection/SenderThread.java
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/SendThread.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/SenderThread.java
@@ -19,7 +19,7 @@ import java.util.concurrent.locks.Reentr
  * This thread is designed for reuse, as thread creation costs are
  * high.
  */
-public class SendThread extends Thread {
+public class SenderThread extends Thread {
 
     private enum SendThreadStatus {
         /** The state WAIT represents this thread to be waiting for something to do */
@@ -44,7 +44,7 @@ public class SendThread extends Thread {
      *
      * @param out the socket to write to
      */
-    public SendThread(AbstractProtocol out) {
+    public SenderThread(AbstractProtocol out) {
         super("SendThread");
         this.setDaemon(true);
         this.protocol = out;
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/embedded/EmbeddedConnection.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/embedded/EmbeddedConnection.java
@@ -79,8 +79,5 @@ public final class EmbeddedConnection ex
 
     @Override
     public void sendControlCommand(ControlCommands con, int data) throws SQLException {
-        synchronized (protocol) {
-
-        }
     }
 }
new file mode 100644
--- /dev/null
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/BufferReallocator.java
@@ -0,0 +1,36 @@
+package nl.cwi.monetdb.mcl.connection.helpers;
+
+import java.nio.CharBuffer;
+
+/**
+ * Created by ferreira on 12/14/16.
+ */
+public class BufferReallocator {
+
+    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+    private static int GetNewCapacity(CharBuffer oldBuffer) {
+        int minCapacity = oldBuffer.capacity() << 1;
+        int newCapacity = (oldBuffer.capacity() << 1) + 2;
+        if (newCapacity - minCapacity < 0) {
+            newCapacity = minCapacity;
+        }
+
+        if(newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) {
+            if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
+                throw new OutOfMemoryError();
+            }
+            return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE;
+        } else {
+            return newCapacity;
+        }
+    }
+
+    public static CharBuffer ReallocateBuffer(CharBuffer oldBuffer) {
+        int newCapacity = GetNewCapacity(oldBuffer);
+        CharBuffer newBuffer = CharBuffer.allocate(newCapacity);
+        oldBuffer.flip();
+        newBuffer.put(oldBuffer.array());
+        return newBuffer;
+    }
+}
rename from src/main/java/nl/cwi/monetdb/mcl/connection/ChannelSecurity.java
rename to src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/ChannelSecurity.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java
@@ -1,4 +1,4 @@
-package nl.cwi.monetdb.mcl.connection;
+package nl.cwi.monetdb.mcl.connection.helpers;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
rename from src/main/java/nl/cwi/monetdb/mcl/connection/Debugger.java
rename to src/main/java/nl/cwi/monetdb/mcl/connection/helpers/Debugger.java
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/Debugger.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/Debugger.java
@@ -1,4 +1,4 @@
-package nl.cwi.monetdb.mcl.connection;
+package nl.cwi.monetdb.mcl.connection.helpers;
 
 import java.io.*;
 
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java
@@ -1,5 +1,7 @@
 package nl.cwi.monetdb.mcl.connection.mapi;
 
+import nl.cwi.monetdb.mcl.connection.helpers.BufferReallocator;
+
 import java.io.Closeable;
 import java.io.IOException;
 import java.net.Socket;
@@ -32,9 +34,7 @@ public abstract class AbstractSocket imp
 
     private final CharsetDecoder asciiDecoder = StandardCharsets.UTF_8.newDecoder();
 
-    private boolean hasFinished;
-
-    public AbstractSocket(String hostname, int port, MapiConnection connection) throws IOException {
+    AbstractSocket(String hostname, int port, MapiConnection connection) throws IOException {
         this.socket = new Socket(hostname, port);
         this.connection = connection;
         this.bufferIn = ByteBuffer.wrap(new byte[getBlockSize()]);
@@ -44,19 +44,19 @@ public abstract class AbstractSocket imp
         this.stringsDecoded.flip();
     }
 
-    public int getSoTimeout() throws SocketException {
+    int getSoTimeout() throws SocketException {
         return socket.getSoTimeout();
     }
 
-    public void setSoTimeout(int s) throws SocketException {
+    void setSoTimeout(int s) throws SocketException {
         socket.setSoTimeout(s);
     }
 
-    public void setTcpNoDelay(boolean on) throws SocketException {
+    void setTcpNoDelay(boolean on) throws SocketException {
         socket.setTcpNoDelay(on);
     }
 
-    public void setSocketChannelEndianness(ByteOrder bo) {
+    void setSocketChannelEndianness(ByteOrder bo) {
         this.bufferIn.order(bo);
         this.bufferOut.order(bo);
     }
@@ -72,8 +72,7 @@ public abstract class AbstractSocket imp
     private void readToBuffer() throws IOException {
         int read = this.readToBufferIn(this.bufferIn);
         if(read == 0) {
-            this.hasFinished = true;
-            throw new IOException("Done!");
+            throw new IOException("The server has reached EOF!");
         }
         this.stringsDecoded.clear();
         this.asciiDecoder.reset();
@@ -82,27 +81,37 @@ public abstract class AbstractSocket imp
         this.stringsDecoded.flip();
     }
 
-    public int readLine(StringBuilder builder) throws IOException {
-        builder.setLength(0);
+    public CharBuffer readLine(CharBuffer lineBuffer) throws IOException {
+        lineBuffer.clear();
         boolean found = false;
-        char[] array = this.stringsDecoded.array();
-        int position = this.stringsDecoded.position();
+        char[] sourceArray = this.stringsDecoded.array();
+        int sourcePosition = this.stringsDecoded.position();
+        char[] destinationArray = lineBuffer.array();
+        int destinationPosition = 0;
+        int destinationLimit = lineBuffer.limit();
 
         while(!found) {
             if(!this.stringsDecoded.hasRemaining()) {
                 this.readToBuffer();
-                array = this.stringsDecoded.array();
-                position = 0;
+                sourceArray = this.stringsDecoded.array();
+                sourcePosition = 0;
             }
-            char c = array[position++];
+            char c = sourceArray[sourcePosition++];
             if(c == '\n') {
                 found = true;
             } else {
-                builder.append(c);
+                if(destinationPosition + 1 >= destinationLimit) {
+                    lineBuffer = BufferReallocator.ReallocateBuffer(lineBuffer);
+                    destinationArray = lineBuffer.array();
+                    destinationLimit = lineBuffer.limit();
+                }
+                destinationArray[destinationPosition++] = c;
             }
         }
-        this.stringsDecoded.position(position);
-        return builder.length();
+        this.stringsDecoded.position(sourcePosition);
+        lineBuffer.position(destinationPosition);
+        lineBuffer.flip();
+        return lineBuffer;
     }
 
     private void flushOutputCharBuffer() throws IOException {
@@ -113,8 +122,7 @@ public abstract class AbstractSocket imp
         this.stringsEncoded.clear();
         int written = this.writeFromBufferOut(this.bufferOut);
         if(written == 0) {
-            this.hasFinished = true;
-            throw new IOException("Done!");
+            throw new IOException("The query could not be sent to the server!");
         } else {
             this.flush();
         }
@@ -122,12 +130,18 @@ public abstract class AbstractSocket imp
 
     private void writeNextBlock(String line) throws IOException {
         int limit = line.length();
+        int destinationPosition = this.stringsEncoded.position();
+        char[] destinationArray = this.stringsEncoded.array();
+
         for (int i = 0; i < limit; i++) {
             if (!this.stringsEncoded.hasRemaining()) {
                 this.flushOutputCharBuffer();
+                destinationArray = this.stringsEncoded.array();
+                destinationPosition = 0;
             }
-            this.stringsEncoded.put(line.charAt(i));
+            destinationArray[destinationPosition++] = line.charAt(i);
         }
+        this.stringsEncoded.position(destinationPosition);
     }
 
     public void writeNextLine(String prefix, String line, String suffix) throws IOException {
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java
@@ -1,7 +1,7 @@
 package nl.cwi.monetdb.mcl.connection.mapi;
 
 import nl.cwi.monetdb.jdbc.MonetConnection;
-import nl.cwi.monetdb.mcl.connection.ChannelSecurity;
+import nl.cwi.monetdb.mcl.connection.helpers.ChannelSecurity;
 import nl.cwi.monetdb.mcl.connection.ControlCommands;
 import nl.cwi.monetdb.mcl.connection.MCLException;
 import nl.cwi.monetdb.mcl.protocol.ProtocolException;
@@ -194,20 +194,18 @@ public class MapiConnection extends Mone
             case CLOSE:
                 command = "close " + data;
         }
-        synchronized (protocol) {
-            try {
-                protocol.writeNextQuery(language.getCommandTemplateIndex(0), command, 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");
-            } catch (IOException e) {
-                throw new SQLException(e.getMessage(), "08000");
+        try {
+            protocol.writeNextQuery(language.getCommandTemplateIndex(0), command, 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");
+        } catch (IOException e) {
+            throw new SQLException(e.getMessage(), "08000");
         }
     }
 
@@ -233,7 +231,7 @@ public class MapiConnection extends Mone
         }
 
         this.protocol.fetchNextResponseData();
-        String nextLine = this.protocol.getCurrentData().toString();
+        String nextLine = this.protocol.getRemainingStringLine(0);
         this.protocol.waitUntilPrompt();
         String test = this.getChallengeResponse(nextLine, user, pass, this.language.getRepresentation(),
                 this.database, this.hash);
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
 public class OldMapiSocket extends AbstractSocket {
 
     /** The blocksize (hardcoded in compliance with stream.mx) */
-    private final static int BLOCK = 8 * 1024 - 2;
+    public final static int BLOCK = 8 * 1024 - 2;
 
     /**
      * A short in two bytes for holding the block size in bytes
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java
@@ -13,7 +13,7 @@ import java.util.Map;
 /**
  * Created by ferreira on 11/30/16.
  */
-public abstract class AbstractProtocol<T> {
+public abstract class AbstractProtocol {
 
     protected ServerResponses currentServerResponseHeader = ServerResponses.UNKNOWN;
 
@@ -25,7 +25,7 @@ public abstract class AbstractProtocol<T
         return currentServerResponseHeader;
     }
 
-    public abstract T getCurrentData();
+    public abstract Object getCurrentData();
 
     public abstract StarterHeaders getNextStarterHeader();
 
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/embedded/EmbeddedProtocol.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/embedded/EmbeddedProtocol.java
@@ -14,7 +14,7 @@ import java.util.Map;
 /**
  * Created by ferreira on 11/30/16.
  */
-public class EmbeddedProtocol extends AbstractProtocol<Object[]> {
+public class EmbeddedProtocol extends AbstractProtocol {
 
     private final JDBCEmbeddedConnection connection;
 
@@ -37,7 +37,7 @@ public class EmbeddedProtocol extends Ab
     }
 
     @Override
-    public Object[] getCurrentData() {
+    public Object getCurrentData() {
         return new Object[0];
     }
 
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/newmapi/NewMapiProtocol.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/newmapi/NewMapiProtocol.java
@@ -13,7 +13,7 @@ import java.util.Map;
 /**
  * Created by ferreira on 11/30/16.
  */
-public class NewMapiProtocol extends AbstractProtocol<Object[]> {
+public class NewMapiProtocol extends AbstractProtocol {
 
     @Override
     public ServerResponses waitUntilPrompt() throws IOException {
@@ -26,7 +26,7 @@ public class NewMapiProtocol extends Abs
     }
 
     @Override
-    public Object[] getCurrentData() {
+    public Object getCurrentData() {
         return new Object[0];
     }
 
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java
@@ -13,47 +13,41 @@ import nl.cwi.monetdb.mcl.responses.Data
 import nl.cwi.monetdb.mcl.responses.ResultSetResponse;
 
 import java.io.IOException;
+import java.nio.CharBuffer;
 import java.util.Map;
 
 /**
  * Created by ferreira on 11/30/16.
  */
-public class OldMapiProtocol extends AbstractProtocol<StringBuilder> {
-
-    private static final int STRING_BUILDER_INITIAL_SIZE = 128;
+public class OldMapiProtocol extends AbstractProtocol {
 
     private final OldMapiSocket socket;
 
-    final StringBuilder builder;
-
-    int currentPointer = 0;
+    CharBuffer lineBuffer;
 
     private final StringBuilder tupleLineBuilder;
 
     public OldMapiProtocol(OldMapiSocket socket) {
         this.socket = socket;
-        this.builder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE);
-        this.tupleLineBuilder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE);
+        this.lineBuffer = CharBuffer.wrap(new char[OldMapiSocket.BLOCK]);
+        this.tupleLineBuilder = new StringBuilder(OldMapiSocket.BLOCK);
     }
 
     public OldMapiSocket getSocket() {
         return socket;
     }
 
-    boolean hasRemaining() {
-        return this.currentPointer < this.builder.length();
-    }
-
     @Override
     public ServerResponses waitUntilPrompt() throws IOException {
         while(this.currentServerResponseHeader != ServerResponses.PROMPT) {
-            if(this.socket.readLine(this.builder) == 0) {
+            this.lineBuffer = this.socket.readLine(this.lineBuffer);
+            if(this.lineBuffer.limit() == 0) {
                 throw new IOException("Connection to server lost!");
             }
-            this.currentPointer = 0;
             this.currentServerResponseHeader = OldMapiServerResponseParser.ParseOldMapiServerResponse(this);
+            this.lineBuffer.position(0);
             if (this.currentServerResponseHeader == ServerResponses.ERROR) {
-                this.currentPointer = 1;
+                this.lineBuffer.position(1);
             }
         }
         return this.currentServerResponseHeader;
@@ -61,25 +55,30 @@ public class OldMapiProtocol extends Abs
 
     @Override
     public void fetchNextResponseData() throws IOException { //readLine equivalent
-        this.socket.readLine(this.builder);
-        this.currentPointer = 0;
+        this.lineBuffer = this.socket.readLine(this.lineBuffer);
+        if(this.lineBuffer.limit() == 0) {
+            throw new IOException("Connection to server lost!");
+        }
         this.currentServerResponseHeader = OldMapiServerResponseParser.ParseOldMapiServerResponse(this);
-        if (this.currentServerResponseHeader == ServerResponses.ERROR && !this.builder.toString().matches("^![0-9A-Z]{5}!.+")) {
-            //this.builder.deleteCharAt(0);
-            this.builder.insert(0, "!22000!");
+        if (this.currentServerResponseHeader == ServerResponses.ERROR && !this.lineBuffer.toString().matches("^[0-9A-Z]{5}!.+")) {
+            CharBuffer newbuffer = CharBuffer.wrap(new char[this.lineBuffer.capacity() + 7]);
+            newbuffer.put("!22000!");
+            newbuffer.put(this.lineBuffer.array());
+            newbuffer.flip();
+            this.lineBuffer = newbuffer;
         }
-        this.currentPointer = 1;
+        this.lineBuffer.position(1);
     }
 
     @Override
-    public StringBuilder getCurrentData() {
-        return this.builder;
+    public CharBuffer getCurrentData() {
+        return this.lineBuffer;
     }
 
     @Override
     public StarterHeaders getNextStarterHeader() {
         StarterHeaders res = OldMapiStartOfHeaderParser.GetNextStartHeaderOnOldMapi(this);
-        this.currentPointer += 2;
+        this.lineBuffer.position(this.lineBuffer.position() + 1);
         return res;
     }
 
@@ -124,19 +123,19 @@ public class OldMapiProtocol extends Abs
     @Override
     public TableResultHeaders getNextTableHeader(Object line, String[] stringValues, int[] intValues)
             throws ProtocolException {
-        return OldMapiTableHeaderParser.GetNextTableHeader((StringBuilder) line, stringValues, intValues);
+        return OldMapiTableHeaderParser.GetNextTableHeader((CharBuffer) line, stringValues, intValues);
     }
 
     @Override
     public int parseTupleLine(int lineNumber, Object line, int[] typesMap, Object[] data, boolean[] nulls)
             throws ProtocolException {
-        return OldMapiTupleLineParser.OldMapiParseTupleLine(lineNumber, (StringBuilder) line,
+        return OldMapiTupleLineParser.OldMapiParseTupleLine(lineNumber, (CharBuffer) line,
                 this.tupleLineBuilder, typesMap, data, nulls);
     }
 
     @Override
     public String getRemainingStringLine(int startIndex) {
-        return this.builder.substring(startIndex);
+        return new String(this.lineBuffer.array(), startIndex, this.lineBuffer.limit() - startIndex);
     }
 
     @Override
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiServerResponseParser.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiServerResponseParser.java
@@ -9,7 +9,7 @@ final class OldMapiServerResponseParser 
 
     static ServerResponses ParseOldMapiServerResponse(OldMapiProtocol protocol) {
         ServerResponses res;
-        switch (protocol.builder.charAt(protocol.currentPointer)) {
+        switch (protocol.lineBuffer.get()) {
             case '!':
                 res = ServerResponses.ERROR;
                 break;
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiStartOfHeaderParser.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiStartOfHeaderParser.java
@@ -10,7 +10,7 @@ final class OldMapiStartOfHeaderParser {
 
     static StarterHeaders GetNextStartHeaderOnOldMapi(OldMapiProtocol protocol) {
         StarterHeaders res;
-        switch (protocol.builder.charAt(protocol.currentPointer)) {
+        switch (protocol.lineBuffer.get()) {
             case '0':
                 res = StarterHeaders.Q_PARSE;
                 break;
@@ -39,23 +39,24 @@ final class OldMapiStartOfHeaderParser {
     }
 
     static int GetNextResponseDataAsInt(OldMapiProtocol protocol) throws ProtocolException {
-        if (!protocol.hasRemaining()) {
-            throw new ProtocolException("unexpected end of string", protocol.currentPointer - 1);
+        int currentPointer = protocol.lineBuffer.position();
+        int limit = protocol.lineBuffer.limit();
+        char[] array = protocol.lineBuffer.array();
+
+        if (currentPointer >= limit) {
+            throw new ProtocolException("unexpected end of string", currentPointer - 1);
         }
         int tmp;
-        char chr = protocol.builder.charAt(protocol.currentPointer);
-        protocol.currentPointer++;
-        // note: don't use Character.isDigit() here, because
-        // we only want ISO-LATIN-1 digits
+        char chr = array[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 ProtocolException("expected a digit", protocol.currentPointer - 1);
+            throw new ProtocolException("expected a digit", currentPointer - 1);
         }
 
-        while (protocol.hasRemaining()) {
-            chr = protocol.builder.charAt(protocol.currentPointer);
-            protocol.currentPointer++;
+        while (currentPointer < limit) {
+            chr = array[currentPointer++];
             if(chr == ' ') {
                 break;
             }
@@ -63,29 +64,33 @@ final class OldMapiStartOfHeaderParser {
             if (chr >= '0' && chr <= '9') {
                 tmp += (int)chr - (int)'0';
             } else {
-                throw new ProtocolException("expected a digit", protocol.currentPointer - 1);
+                throw new ProtocolException("expected a digit", currentPointer - 1);
             }
         }
+        protocol.lineBuffer.position(currentPointer);
         return tmp;
     }
 
     static String GetNextResponseDataAsString(OldMapiProtocol protocol) throws ProtocolException {
-        if (!protocol.hasRemaining()) {
-            throw new ProtocolException("unexpected end of string", protocol.currentPointer - 1);
+        int currentPointer = protocol.lineBuffer.position();
+        int limit = protocol.lineBuffer.limit();
+        char[] array = protocol.lineBuffer.array();
+
+        if (currentPointer >= limit) {
+            throw new ProtocolException("unexpected end of string", currentPointer - 1);
         }
-        int cnt = 0, mark = protocol.currentPointer;
+        int cnt = 0, mark = currentPointer;
         char chr;
 
-        while (protocol.hasRemaining()) {
-            chr = protocol.builder.charAt(protocol.currentPointer);
-            protocol.currentPointer++;
+        while (currentPointer < limit) {
+            chr = array[currentPointer++];
             if(chr == ' ') {
                 break;
             }
             cnt++;
         }
 
-        protocol.currentPointer = mark;
-        return protocol.builder.subSequence(0, cnt).toString();
+        protocol.lineBuffer.position(mark);
+        return new String(array, 0, cnt);
     }
 }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java
@@ -3,25 +3,31 @@ package nl.cwi.monetdb.mcl.protocol.oldm
 import nl.cwi.monetdb.mcl.protocol.ProtocolException;
 import nl.cwi.monetdb.mcl.protocol.TableResultHeaders;
 
+import java.nio.CharBuffer;
+
 /**
  * Created by ferreira on 12/6/16.
  */
 final class OldMapiTableHeaderParser {
 
-    static TableResultHeaders GetNextTableHeader(StringBuilder builder, String[] stringValues, int[] intValues) throws ProtocolException {
+    static TableResultHeaders GetNextTableHeader(CharBuffer lineBuffer, String[] stringValues, int[] intValues)
+            throws ProtocolException {
         TableResultHeaders res = TableResultHeaders.UNKNOWN;
-        int len = builder.length(), pos = 0;
+        int currentLength = lineBuffer.limit();
+        char[] array = lineBuffer.array();
+
+        int pos = 0;
         boolean foundChar = false, nameFound = false;
 
         // find header name
-        for (int i = len - 1; i >= 0; i--) {
-            switch (builder.charAt(i)) {
+        for (int i = currentLength - 1; i >= 0; i--) {
+            switch (array[i]) {
                 case ' ':
                 case '\n':
                 case '\t':
                 case '\r':
                     if (!foundChar) {
-                        len = i - 1;
+                        currentLength = i - 1;
                     } else {
                         pos = i + 1;
                     }
@@ -42,62 +48,62 @@ final class OldMapiTableHeaderParser {
             throw new ProtocolException("invalid header, no header name found", pos);
 
         // depending on the name of the header, we continue
-        switch (builder.charAt(pos)) {
+        switch (array[pos]) {
             case 'n': //name
-                if (len - pos == 4) {
-                    GetStringValues(builder, pos - 3, stringValues);
+                if (currentLength - pos == 4) {
+                    GetStringValues(array, pos - 3, stringValues);
                     res = TableResultHeaders.NAME;
                 }
                 break;
             case 'l': //length
-                if (len - pos == 6) {
-                    GetIntValues(builder, pos - 3, intValues);
+                if (currentLength - pos == 6) {
+                    GetIntValues(array, pos - 3, intValues);
                     res = TableResultHeaders.LENGTH;
                 }
                 break;
             case 't':
-                if (len - pos == 4) { //type
-                    GetStringValues(builder, pos - 3, stringValues);
+                if (currentLength - pos == 4) { //type
+                    GetStringValues(array, pos - 3, stringValues);
                     res = TableResultHeaders.TYPE;
-                } else if (len - pos == 10) { //table_name
-                    GetStringValues(builder, pos - 3, stringValues);
+                } else if (currentLength - pos == 10) { //table_name
+                    GetStringValues(array, pos - 3, stringValues);
                     res = TableResultHeaders.TABLE;
                 }
                 break;
             default:
-                throw new ProtocolException("unknown header: " + builder.substring(pos, len));
+                throw new ProtocolException("unknown header: " + new String(array, pos, currentLength - pos));
         }
         return res;
     }
 
-    private static void GetStringValues(StringBuilder builder, int stop, String[] stringValues) {
+    private static void GetStringValues(char[] array, 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);
+            if (array[i] == '\t' && array[i - 1] == ',') {
+                stringValues[elem++] = new String(array, start, i - 1 - start);
                 start = i + 1;
             }
         }
         // add the left over part
-        stringValues[elem] = builder.substring(start, stop);
+        stringValues[elem] = new String(array, start, stop - start);
     }
 
-    private static void GetIntValues(StringBuilder builder, int stop, int[] intValues) throws ProtocolException {
+    private static void GetIntValues(char[] array, int stop, int[] intValues) throws ProtocolException {
         int elem = 0, tmp = 0, start = 2;
 
         for (int i = start; i < stop; i++) {
-            if (builder.charAt(i) == ',' && builder.charAt(i + 1) == '\t') {
+            if (array[i] == ',' && array[i + 1] == '\t') {
                 intValues[elem++] = tmp;
                 tmp = 0;
                 i++;
             } 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';
+                if (array[i] >= '0' && array[i] <= '9') {
+                    tmp += (int) array[i] - (int)'0';
                 } else {
-                    throw new ProtocolException("expected a digit in " + builder.toString() + " at " + i);
+                    throw new ProtocolException("expected a digit in " + new String(array) + " at " + i);
                 }
             }
         }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParser.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParser.java
@@ -6,10 +6,9 @@ import nl.cwi.monetdb.mcl.protocol.Proto
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.sql.Date;
+import java.nio.CharBuffer;
 import java.sql.Types;
 import java.text.ParseException;
-import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 
 /**
@@ -17,17 +16,49 @@ import java.text.SimpleDateFormat;
  */
 final class OldMapiTupleLineParser {
 
-    static int OldMapiParseTupleLine(int lineNumber, StringBuilder line, StringBuilder helper, int[] typesMap,
+    private static final char[] NULL_STRING = "NULL".toCharArray();
+
+    private static int CharIndexOf(char[] source, int sourceCount, char[] target, int targetCount) {
+        if (targetCount == 0) {
+            return 0;
+        }
+
+        char first = target[0];
+        int max = sourceCount - targetCount;
+
+        for (int i = 0; i <= max; i++) {
+            /* Look for first character. */
+            if (source[i] != first) {
+                while (++i <= max && source[i] != first);
+            }
+
+            /* Found first character, now look at the rest of v2 */
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + targetCount - 1;
+                for (int k = 1; j < end && source[j] == target[k]; j++, k++);
+
+                if (j == end) {
+                    /* Found whole string. */
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    static int OldMapiParseTupleLine(int lineNumber, CharBuffer lineBuffer, StringBuilder helper, int[] typesMap,
                                      Object[] values, boolean[] nulls) throws ProtocolException {
-        int len = line.length();
+        int len = lineBuffer.limit();
+        char[] array = lineBuffer.array();
 
         // first detect whether this is a single value line (=) or a real tuple ([)
-        if (line.charAt(0) == '=') {
+        if (array[0] == '=') {
             if (typesMap.length != 1) {
                 throw new ProtocolException(typesMap.length + " columns expected, but only single value found");
             }
             // return the whole string but the leading =
-            OldMapiStringToJavaObjectConverter(line.substring(1), lineNumber, values[0], typesMap[0]);
+            OldMapiStringToJavaObjectConverter(new String(array, 1, len - 1), lineNumber, values[0], typesMap[0]);
             return 1;
         }
 
@@ -35,7 +66,7 @@ final class OldMapiTupleLineParser {
         boolean inString = false, escaped = false;
         int cursor = 2, column = 0, i = 2;
         for (; i < len; i++) {
-            switch(line.charAt(i)) {
+            switch(array[i]) {
                 default:
                     escaped = false;
                     break;
@@ -44,17 +75,13 @@ final class OldMapiTupleLineParser {
                     break;
                 case '"':
                     /**
-                     * If all strings are wrapped between two quotes, a \" can
-                     * never exist outside a string. Thus if we believe that we
-                     * are not within a string, we can safely assume we're about
-                     * to enter a string if we find a quote.
-                     * If we are in a string we should stop being in a string if
-                     * we find a quote which is not prefixed by a \, for that
-                     * would be an escaped quote. However, a nasty situation can
-                     * occur where the string is like "test \\" as obvious, a
-                     * test for a \ in front of a " doesn't hold here for all
-                     * cases. Because "test \\\"" can exist as well, we need to
-                     * know if a quote is prefixed by an escaping slash or not.
+                     * If all strings are wrapped between two quotes, a \" can never exist outside a string. Thus if we
+                     * believe that we are not within a string, we can safely assume we're about to enter a string if we
+                     * find a quote. If we are in a string we should stop being in a string if we find a quote which is
+                     * not prefixed by a \, for that would be an escaped quote. However, a nasty situation can occur
+                     * where the string is like "test \\" as obvious, a test for a \ in front of a " doesn't hold here
+                     * for all cases. Because "test \\\"" can exist as well, we need to know if a quote is prefixed by
+                     * an escaping slash or not.
                      */
                     if (!inString) {
                         inString = true;
@@ -66,19 +93,18 @@ final class OldMapiTupleLineParser {
                     escaped = false;
                     break;
                 case '\t':
-                    if (!inString && (i > 0 && line.charAt(i - 1) == ',') || (i + 1 == len - 1 && line.charAt(++i) == ']')) { // dirty
+                    if (!inString && (i > 0 && array[i - 1] == ',') || (i + 1 == len - 1 && array[++i] == ']')) { // dirty
                         // split!
-                        if (line.charAt(cursor) == '"' && line.charAt(i - 2) == '"') {
+                        if (array[cursor] == '"' && array[i - 2] == '"') {
                             // reuse the StringBuilder by cleaning it
                             helper.setLength(0);
                             // prevent capacity increases
                             helper.ensureCapacity((i - 2) - (cursor + 1));
                             for (int pos = cursor + 1; pos < i - 2; pos++) {
-                                if (line.charAt(pos) == '\\' && pos + 1 < i - 2) {
+                                if (array[cursor] == '\\' && pos + 1 < i - 2) {
                                     pos++;
-                                    // strToStr and strFromStr in gdk_atoms.mx only
-                                    // support \t \n \\ \" and \377
-                                    switch (line.charAt(pos)) {
+                                    // strToStr and strFromStr in gdk_atoms.mx only support \t \n \\ \" and \377
+                                    switch (array[pos]) {
                                         case '\\':
                                             helper.append('\\');
                                             break;
@@ -93,12 +119,11 @@ final class OldMapiTupleLineParser {
                                             break;
                                         case '0': case '1': case '2': case '3':
                                             // this could be an octal number, let's check it out
-                                            if (pos + 2 < i - 2 &&
-                                                    line.charAt(pos + 1) >= '0' && line.charAt(pos + 1) <= '7' &&
-                                                    line.charAt(pos + 2) >= '0' && line.charAt(pos + 2) <= '7') {
+                                            if (pos + 2 < i - 2 && array[pos + 1] >= '0' && array[pos + 1] <= '7' &&
+                                                    array[pos + 2] >= '0' && array[pos + 2] <= '7') {
                                                 // we got the number!
                                                 try {
-                                                    helper.append((char)(Integer.parseInt("" + line.charAt(pos) + line.charAt(pos + 1) + line.charAt(pos + 2), 8)));
+                                                    helper.append((char)(Integer.parseInt("" + array[pos] + array[pos + 1] + array[pos + 2], 8)));
                                                     pos += 2;
                                                 } catch (NumberFormatException e) {
                                                     // hmmm, this point should never be reached actually...
@@ -106,27 +131,26 @@ final class OldMapiTupleLineParser {
                                                 }
                                             } else {
                                                 // do default action if number seems not to be correct
-                                                helper.append(line.charAt(pos));
+                                                helper.append(array[pos]);
                                             }
                                             break;
                                         default:
                                             // this is wrong, just ignore the escape, and print the char
-                                            helper.append(line.charAt(pos));
+                                            helper.append(array[pos]);
                                             break;
                                     }
                                 } else {
-                                    helper.append(line.charAt(pos));
+                                    helper.append(array[pos]);
                                 }
                             }
-
                             // put the unescaped string in the right place
                             OldMapiStringToJavaObjectConverter(helper.toString(), lineNumber, values[column], typesMap[column]);
                             nulls[column] = false;
-                        } else if ((i - 1) - cursor == 4 && line.indexOf("NULL", cursor) == cursor) {
+                        } else if ((i - 1) - cursor == 4 && CharIndexOf(array, array.length, NULL_STRING, NULL_STRING.length) == cursor) {
                             SetNullValue(lineNumber, values[column], typesMap[column]);
                             nulls[column] = true;
                         } else {
-                            OldMapiStringToJavaObjectConverter(line.substring(cursor, i - 1), lineNumber, values[column], typesMap[column]);
+                            OldMapiStringToJavaObjectConverter(new String(array, cursor, i - 1 - cursor), lineNumber, values[column], typesMap[column]);
                             nulls[column] = false;
                         }
                         column++;
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java
@@ -31,7 +31,7 @@ public class DataBlockResponse implement
     /** The counter which keeps the current position in the data array */
     private int pos;
     /** The connection protocol to parse the tuple lines */
-    private final AbstractProtocol<?> protocol;
+    private final AbstractProtocol protocol;
     /** The JdbcSQLTypes mapping */
     private final int[] jdbcSQLTypes;
     /** A mapping of null values of the current Row */
@@ -46,7 +46,7 @@ public class DataBlockResponse implement
      * @param columncount the number of columns
      * @param forward whether this is a forward only result
      */
-    DataBlockResponse(int rowcount, int columncount, boolean forward, AbstractProtocol<?> protocol, int[] JdbcSQLTypes) {
+    DataBlockResponse(int rowcount, int columncount, boolean forward, AbstractProtocol protocol, int[] JdbcSQLTypes) {
         this.pos = -1;
         this.data = new Object[columncount];
         this.nullMappings = new boolean[rowcount][columncount];
@@ -154,7 +154,7 @@ public class DataBlockResponse implement
     }
 
     public Object[] getData() { /* For VirtualResultSet :( */
-        return data;
+        return this.data;
     }
 
     public boolean checkValueIsNull(int column) {