diff --git a/README.md b/README.md
index 22c779b..4301858 100644
--- a/README.md
+++ b/README.md
@@ -329,8 +329,8 @@ $result3 = $connection3->Query("...");
 | --- | --- |
 | <strong>__construct</strong> | Create a new connection to a MonetDB database. <br><br><strong>@param</strong> <em>string</em> <strong>$host</strong> : The host of the database. Use '127.0.0.1' if the DB is on the same machine.<br><strong>@param</strong> <em>int</em> <strong>$port</strong> : The port of the database. For MonetDB this is usually 50000.<br><strong>@param</strong> <em>string</em> <strong>$user</strong> : The user name.<br><strong>@param</strong> <em>string</em> <strong>$password</strong> : The password of the user.<br><strong>@param</strong> <em>string</em> <strong>$database</strong> : The name of the database to connect to. Don't forget to release and start it.<br><strong>@param</strong> <em>string</em> <strong>$saltedHashAlgo</strong> <em>= "SHA1"</em> : Optional. The preferred hash algorithm to be used for exchanging the password. It has to be supported by both the server and PHP. This is only used for the salted hashing. Another stronger algorithm is used first (usually SHA512).<br><strong>@param</strong> <em>bool</em> <strong>$syncTimeZone</strong> <em>= true</em> : If true, then tells the clients time zone offset to the server, which will convert all timestamps is case there's a difference. If false, then the timestamps will end up on the server unmodified.<br><strong>@param</strong> <em>int</em> <strong>$maxReplySize</strong> <em>= 200</em> : The maximal number of tuples returned in a response. A higher value results in smaller number of memory allocations and string operations, but also in higher memory footprint. |
 | <strong>Close</strong> | Close the connection |
-| <strong>Query</strong> | Execute an SQL query and return its response. For 'select' queries the response can be iterated using a 'foreach' statement. You can pass an array as second parameter to execute the query as prepared statement, where the array contains the parameter values. SECURITY WARNING: For prepared statements in MonetDB, the parameter values are passed in a regular 'EXECUTE' command, using escaping. Therefore the same security considerations apply here as for using the Connection->Escape(...) method. Please read the comments for that method. <br><br><strong>@param</strong> <em>string</em> <strong>$sql</strong><br><strong>@param</strong> <em>array</em> <strong>$params</strong> <em>= null</em> : An optional array for prepared statement parameters. If not provided (or null), then a normal query is executed, instead of a prepared statement. The parameter values will retain their PHP type if possible. The following values won't be converted to string: null, true, false and numeric values.<br><strong>@return</strong> <em>Response</em> |
-| <strong>QueryFirst</strong> | Execute an SQL query and return only the first row as an associative array. If there is more data on the stream, then discard all. Returns null if the query has empty result. You can pass an array as second parameter to execute the query as prepared statement, where the array contains the parameter values. <br><br><strong>@param</strong> <em>string</em> <strong>$sql</strong><br><strong>@param</strong> <em>array</em> <strong>$params</strong> <em>= null</em> : An optional array for prepared statement parameters. If not provided (or null), then a normal query is executed, instead of a prepared statement. See the 'Query' method for more information about the parameter values.<br><strong>@return</strong> <em>string[] -or- null</em> |
+| <strong>Query</strong> | Execute an SQL query and return its response. For 'select' queries the response can be iterated using a 'foreach' statement. You can pass an array as second parameter to execute the query as prepared statement, where the array contains the parameter values. SECURITY WARNING: For prepared statements in MonetDB, the parameter values are passed in a regular 'EXECUTE' command, using escaping. Therefore the same security considerations apply here as for using the Connection->Escape(...) method. Please read the comments for that method. <br><br><strong>@param</strong> <em>string</em> <strong>$sql</strong><br><strong>@param</strong> <em>array</em> <strong>$params</strong> <em>= null</em> : An optional array for prepared statement parameters. If not provided (or null), then a normal query is executed instead of a prepared statement. The parameter values will retain their PHP type if possible. The following values won't be converted to string: null, true, false and numeric values.<br><strong>@return</strong> <em>Response</em> |
+| <strong>QueryFirst</strong> | Execute an SQL query and return only the first row as an associative array. If there is more data on the stream, then discard all. Returns null if the query has empty result. You can pass an array as second parameter to execute the query as prepared statement, where the array contains the parameter values. <br><br><strong>@param</strong> <em>string</em> <strong>$sql</strong><br><strong>@param</strong> <em>array</em> <strong>$params</strong> <em>= null</em> : An optional array for prepared statement parameters. If not provided (or null), then a normal query is executed instead of a prepared statement. See the 'Query' method for more information about the parameter values.<br><strong>@return</strong> <em>string[] -or- null</em> |
 | <strong>Command</strong> | Send a 'command' to MonetDB. Commands are used for configuring the database, for example setting the maximal response size, or for requesting unread parts of a query response ('export').<br><br><strong>@param</strong> <em>string</em> <strong>$command</strong><br><strong>@param</strong> <em>bool</em> <strong>$noResponse</strong> <em>= true</em> : If true, then returns NULL and makes no read to the underlying socket.<br><strong>@return</strong> <em>Response -or- null</em> |
 | <strong>Escape</strong> | Escape a string value, to be inserted into a query, inside single quotes. The following characters are escaped by this method: backslash, single quote, carriage return, line feed, tabulator, null character, CTRL+Z. As a security measure this library forces the use of multi-byte support and UTF-8 encoding, which is also used by MonetDB, avoiding the SQL-injection attacks, which play with differences between character encodings. <br><br><strong>@param</strong> <em>string</em> <strong>$value</strong><br><strong>@return</strong> <em>string</em> |
 | <strong>ClearPsCache</strong> | Clears the in-memory cache of prepared statements. This is called automatically when an error is received from MonetDB, because that also purges the prepared statements and all session state in this case. |
@@ -361,13 +361,20 @@ $result3 = $connection3->Query("...");
 | --- | --- |
 | <strong>GetQueryType</strong> | Returns a short string which identifies the type of the query.<br><br><strong>@return</strong> <em>string</em> |
 | <strong>GetDescription</strong> | Returns a user-friendly text which describes the effect of the query.<br><br><strong>@return</strong> <em>string</em> |
-| <strong>GetExecutionTime</strong> | The time the server spent on executing the query. In milliseconds.<br><br><strong>@return</strong> <em>float -or- null</em> |
-| <strong>GetQueryParsingTime</strong> | The time it took to parse and optimize the query. In milliseconds.<br><br><strong>@return</strong> <em>float -or- null</em> |
+| <strong>GetQueryTime</strong> | The time the server spent on executing the query. In milliseconds.<br><br><strong>@return</strong> <em>float -or- null</em> |
+| <strong>GetSqlOptimizerTime</strong> | SQL optimizer time in milliseconds.<br><br><strong>@return</strong> <em>float -or- null</em> |
+| <strong>GetMalOptimizerTime</strong> | MAL optimizer time in milliseconds.<br><br><strong>@return</strong> <em>float -or- null</em> |
 | <strong>GetAffectedRows</strong> | The number of rows updated or inserted.<br><br><strong>@return</strong> <em>integer -or- null</em> |
-| <strong>GetRowCount</strong> | The number of rows in the response.<br><br><strong>@return</strong> <em>integer -or- null</em> |
+| <strong>GetTotalRowCount</strong> | The total number of rows in the result set. This includes those rows too, which are not in the current response.<br><br><strong>@return</strong> <em>integer -or- null</em> |
 | <strong>GetAsText</strong> | Get a description of the status response in a human-readable format.<br><br><strong>@return</strong> <em>string</em> |
 | <strong>GetPreparedStatementID</strong> | Get the ID of a created prepared statement. This ID can be used in an 'EXECUTE' statement, but only in the same session.<br><br><strong>@return</strong> <em>integer -or- null</em> |
-| <strong>GetQueryID</strong> | Returns the ID of the query response that is returned in the result set.<br><br><strong>@return</strong> <em>integer -or- null</em> |
+| <strong>GetResultID</strong> | Returns the ID of the result set that is returned for a query. It is stored on the server for this session, and parts of it can be queried using the "export" command.<br><br><strong>@return</strong> <em>integer -or- null</em> |
+| <strong>GetAutoCommitState</strong> | Available after "start transaction", "commit" or "rollback". Tells whether the current session is in auto-commit mode or not.<br><br><strong>@return</strong> <em>boolean -or- null</em> |
+| <strong>GetRowCount</strong> | The number of rows (tuples) in the current response only.<br><br><strong>@return</strong> <em>integer -or- null</em> |
+| <strong>GetColumnCount</strong> | Column count. If the response contains tabular data, then this tells the number of columns.<br><br><strong>@return</strong> <em>integer -or- null</em> |
+| <strong>GetQueryID</strong> | Query ID. A global ID which is also used in functions such as sys.querylog_catalog().<br><br><strong>@return</strong> <em>integer -or- null</em> |
+| <strong>GetLastInsertID</strong> | The last automatically generated ID by an insert statement. (Usually auto_increment) NULL if none.<br><br><strong>@return</strong> <em>integer -or- null</em> |
+| <strong>GetExportOffset</strong> | The index (offset) of the first row in a block response. (For an "export" command.)<br><br><strong>@return</strong> <em>integer -or- null</em> |
 
 <hr><br>
 
diff --git a/protocol_doc/README.md b/protocol_doc/README.md
index f248af6..75934fe 100644
--- a/protocol_doc/README.md
+++ b/protocol_doc/README.md
@@ -151,9 +151,11 @@ password hashing, then the formula for getting the hash string is the following:
 Where the hash functions output hexadecimal values. After the client calculated the hash,
 it sends it in a message like the following:
 
-    LIT:monetdb:{SHA1}b8cb82cca07f379e25e99262e3b4b70054546136:sql:myDatabase:
+    LIT:monetdb:{SHA1}b8cb82cca07f379e25e99262e3b4b70054546136:sql:myDatabase:\n
 
-Which is also separated by colons and the meanings of the values are:
+The `\n` at the end means a line feed character. (It seems to work well without that too,
+but mclient puts a newline there.) The line consists of colon-separated
+values with the following meanings:
 
 | Value | Description |
 | --- | --- |
@@ -170,7 +172,7 @@ Optionally the client can allow the server the send/receive file
 transfer requests to/from the client, by adding a `FILETRANS` at the
 end, the following way:
 
-    LIT:monetdb:{SHA1}b8cb82cca07f379e25e99262e3b4b70054546136:sql:myDatabase:FILETRANS:
+    LIT:monetdb:{SHA1}b8cb82cca07f379e25e99262e3b4b70054546136:sql:myDatabase:FILETRANS:\n
 
 ## 3.1. Possible responses to an authentication request
 
@@ -194,8 +196,7 @@ After the client has sent the hashed password to the server, it can receive 3 ki
 ## 3.2. The Merovingian redirect
 
 The `Merovingian redirect` can be a real redirect, when the client is asked for connecting
-to a new host and port, but this case is not well documented and depend of special server
-configurations, therefore it will be ignored in this document.
+to a new host and port. For this see an example in section [Redirect](#51-redirect---).
 
 A more common case of the `Merovingian redirect` is a request for the repetition of the authentication
 process. It happens in the existing TCP connection. No new connections are created. This repetition is
@@ -289,12 +290,24 @@ if the field count is less than expected.
 
 ## 5.1. Redirect - **^**
 
-This response has been discussed already in chapter [The Merovingian redirect](#32-the-merovingian-redirect).
-Redirect messages always start with the `^` (caret) character. An example response:
+Redirect messages always start with the `^` (caret) character.
+This can be a real redirect, which instructs the client to close the current
+connection and open another one on a specific host/port. Example:
 
-    ^mapi:merovingian://proxy?database=myDatabase
+    ^mapi:monetdb://localhost:50001/test?lang=sql&user=monetdb
+
+| Sample value | Description |
+| --- | --- |
+| ^mapi:monetdb:// | This prefix identifies the redirect type |
+| localhost | Host name or IP address. (It can be IPv6) |
+| 50001 | Port. |
+| test | Database name. |
+| sql | Query language to request during the authentication. |
+| monetdb | User name to specify during the authentication. |
 
-?? Investigate if there are different kinds of redirects.
+Or it can mean a [Merovingian redirect](#32-the-merovingian-redirect). Example:
+
+    ^mapi:merovingian://proxy?database=myDatabase
 
 ## 5.2. Query response - **&**
 
@@ -425,9 +438,9 @@ The first line of the response consists of 5 space-separated values:
 | --- | --- | --- |
 | 0 | &5 | Identifies the response type. (Prepared statement creation) |
 | 1 | 15 | The ID of the created prepared statement. This can be used in an `EXECUTE` statement. |
-| 2 | 4 | Row count |
+| 2 | 4 | Total row count in the result set. |
 | 3 | 6 | Column count |
-| 4 | 4 | Tuple count |
+| 4 | 4 | Row count in current response only. |
 
 The original query requested only 3 columns, but this response returned 4 data rows, as the
 last one is for the `?` placeholder. The additional type information of the
diff --git a/src/Connection.php b/src/Connection.php
index abab4e2..646cbb0 100644
--- a/src/Connection.php
+++ b/src/Connection.php
@@ -207,19 +207,14 @@ private function Authenticate() {
             $pwHash = $challenge->HashPassword($this->password, $this->saltedHashAlgo);
             $upperSaltHash = strtoupper($this->saltedHashAlgo);
 
-            $this->Write("LIT:{$this->user}:{{$upperSaltHash}}{$pwHash}:sql:{$this->database}:");
+            $this->Write("LIT:{$this->user}:{{$upperSaltHash}}{$pwHash}:sql:{$this->database}:\n");
             
             $this->inputStream->LoadNextResponse();
             $inputStream = $this->inputStream->ReadNextLine();
             if (InputStream::IsResponse($inputStream, InputStream::MSG_REDIRECT, "mapi:merovingian:")) {
                 /*
-                    Only the main process of MonetDB opens ports to listen on, and it spawns
-                    separate sub-processes for each database.
-                    The main process acts as a proxy and it forwards the queries to the
-                    processes of the databases. For security reasons the user has to 
-                    authenticate not just at the main process, but also at the
-                    sub-process too. This repetition of the authentication process is
-                    called a "Merovingian redirect".
+                    See doc:
+                    https://github.com/MonetDB/MonetDB-PHP/tree/master/protocol_doc#32-the-merovingian-redirect
                 */
                 continue;
             }
diff --git a/src/Response.php b/src/Response.php
index 3fef545..aff0ad0 100644
--- a/src/Response.php
+++ b/src/Response.php
@@ -192,8 +192,8 @@ private function Parse() {
                         If not all rows did fit into the window,
                         request the next batch.
                     */
-                    $diff = $this->queryStatusRecord->GetRowCount() - $this->rowCount;
-                    $queryID = $this->queryStatusRecord->GetQueryID();
+                    $diff = $this->queryStatusRecord->GetTotalRowCount() - $this->rowCount;
+                    $queryID = $this->queryStatusRecord->GetResultID();
 
                     if ($diff > 0) {
                         $size = min($diff, $this->connection->GetMaxReplySize());
@@ -237,7 +237,7 @@ private function Parse() {
                         responses just get ignored.
                     */
                     if ($this->queryStatusRecord !== null) {
-                        if ($this->queryStatusRecord->GetQueryID() != $status->GetQueryID()) {
+                        if ($this->queryStatusRecord->GetResultID() != $status->GetResultID()) {
                             $this->ignoreTuples = true;
                         }
                     }
@@ -291,7 +291,7 @@ private function Parse() {
                         continue;
                     }
 
-                    if ($this->queryStatusRecord->GetQueryID() != $status->GetQueryID()) {
+                    if ($this->queryStatusRecord->GetResultID() != $status->GetResultID()) {
                         $this->ignoreTuples = true;
                         continue;
                     }
diff --git a/src/StatusRecord.php b/src/StatusRecord.php
index b5502b8..b28f9e4 100644
--- a/src/StatusRecord.php
+++ b/src/StatusRecord.php
@@ -42,26 +42,51 @@ class StatusRecord {
     private $queryTypeDescription = null;
 
     /**
-     * The ID of the query, which can be used for
+     * The ID of the result set, which can be used for
      * resuming it, using the "export" command.
+     * 
+     * @var int
+     */
+    private $resultID = null;
+
+    /**
+     * Query ID. A global ID which is also used in
+     * functions such as sys.querylog_catalog().
+     *
+     * @var int
      */
     private $queryID = null;
 
+    /**
+     * The last automatically generated ID by
+     * an insert statement. (Usually auto_increment)
+     * NULL if none.
+     *
+     * @var int
+     */
+    private $lastInsertID = null;
+
     /**
      * The time the server spent on executing
      * the query. In milliseconds.
      *
      * @var float
      */
-    private $executionTime = null;
+    private $queryTime = null;
 
     /**
-     * The time it took to parse and optimize
-     * the query.
+     * SQL optimizer time in milliseconds.
      *
      * @var float
      */
-    private $queryParsingTime = null;
+    private $sqlOptimizerTime = null;
+
+    /**
+     * MAL optimizer time in milliseconds.
+     *
+     * @var float
+     */
+    private $malOptimizerTime = null;
 
     /**
      * The number of rows updated or inserted.
@@ -71,7 +96,17 @@ class StatusRecord {
     private $affectedRows = null;
 
     /**
-     * The number of rows in the response.
+     * The total number of rows in the result set.
+     * This includes those rows too, which are not
+     * in the current response.
+     *
+     * @var int
+     */
+    private $totalRowCount = null;
+
+    /**
+     * The number of rows (tuples) in the current
+     * response only.
      *
      * @var int
      */
@@ -95,6 +130,22 @@ class StatusRecord {
      */
     private $autoCommitState = null;
 
+    /**
+     * Column count. If the response contains tabular data,
+     * then this tells the number of columns.
+     *
+     * @var int
+     */
+    private $columnCount = null;
+
+    /**
+     * The index (offset) of the first row in
+     * a block response. (For an "export" command.)
+     *
+     * @var int
+     */
+    private $exportOffset = null;
+
     /**
      * The server always responds with a status line to a query,
      * which tells data like the time spent on it, or the number
@@ -108,32 +159,44 @@ function __construct(int $queryType, string $line)
     {
         if ($queryType == InputStream::Q_TABLE) {
             $this->queryType = "table";
-            $this->queryTypeDescription = "Select query";
+            $this->queryTypeDescription = "Data response";
             $fields = $this->ParseFields($line, 8);
-            $this->queryID = (int)$fields[0];
-            $this->rowCount = (int)$fields[1];
-            $this->executionTime = $fields[4] / 1000;
-            $this->queryParsingTime = $fields[5] / 1000;
+            $this->resultID = (int)$fields[0];
+            $this->totalRowCount = (int)$fields[1];
+            $this->columnCount = (int)$fields[2];
+            $this->rowCount = (int)$fields[3];
+            $this->queryID  = (int)$fields[4];
+            $this->queryTime = $fields[5] / 1000;
+            $this->malOptimizerTime = $fields[6] / 1000;
+            $this->sqlOptimizerTime = $fields[7] / 1000;
         } else if ($queryType == InputStream::Q_BLOCK) {
             $this->queryType = "block";
-            $this->queryTypeDescription = "Continue a select query";
+            $this->queryTypeDescription = "Continue a data response";
             $fields = $this->ParseFields($line, 4);
-            $this->queryID = (int)$fields[0];
+            $this->resultID = (int)$fields[0];
+            $this->columnCount = (int)$fields[1];
             $this->rowCount = (int)$fields[2];
+            $this->exportOffset = (int)$fields[3];
         } else if ($queryType == InputStream::Q_CREATE) {
             // "SET TIME ZONE INTERVAL ..." returns this as well.
             $this->queryType = "schema";
-            $this->queryTypeDescription = "Modify schema";
+            $this->queryTypeDescription = "Stats only (schema)";
             $fields = $this->ParseFields($line, 2);
-            $this->executionTime = $fields[0] / 1000;
-            $this->queryParsingTime = $fields[1] / 1000;
+            $this->queryTime = $fields[0] / 1000;
+            $this->malOptimizerTime = $fields[1] / 1000;
         } else if ($queryType == InputStream::Q_UPDATE) {
             $this->queryType = "update";
             $this->queryTypeDescription = "Update or insert rows";
             $fields = $this->ParseFields($line, 6);
             $this->affectedRows = (int)$fields[0];
-            $this->executionTime = $fields[2] / 1000;
-            $this->queryParsingTime = $fields[3] / 1000;
+            $this->lastInsertID = (int)$fields[1];
+            if ($this->lastInsertID < 0) {
+                $this->lastInsertID = null;
+            }
+            $this->queryID = (int)$fields[2];
+            $this->queryTime = $fields[3] / 1000;
+            $this->malOptimizerTime = $fields[4] / 1000;
+            $this->sqlOptimizerTime = $fields[5] / 1000;
         } else if ($queryType == InputStream::Q_TRANSACTION) {
             $this->queryType = "transaction";
             $fields = $this->ParseFields($line, 1);
@@ -149,6 +212,9 @@ function __construct(int $queryType, string $line)
             $this->queryTypeDescription = "A prepared statement has been created.";
             $fields = $this->ParseFields($line, 4);
             $this->preparedStatementID = (int)$fields[0];
+            $this->totalRowCount = (int)$fields[1];
+            $this->columnCount = (int)$fields[2];
+            $this->rowCount = (int)$fields[3];
         } else {
             throw new MonetException("Unknown reply form MonetDB:\n{$line}\n");
         }
@@ -168,7 +234,9 @@ function __construct(int $queryType, string $line)
      */
     private function ParseFields(string $line, int $count): array {
         $parts = explode(" ", substr(trim($line), 3));
-        if (count($parts) != $count) {
+
+        // More fields is not a problem. Later protocol versions can add new ones.
+        if (count($parts) < $count) {
             throw new MonetException("Invalid response from MonetDB. Status response has invalid number of "
                 ."fields. '{$count}' is expected:\n{$line}\n");
         }
@@ -204,20 +272,29 @@ public function GetDescription(): string
      *
      * @return float|null
      */
-    public function GetExecutionTime(): ?float
+    public function GetQueryTime(): ?float
     {
-        return $this->executionTime;
+        return $this->queryTime;
     }
 
     /**
-     * The time it took to parse and optimize
-     * the query. In milliseconds.
+     * SQL optimizer time in milliseconds.
+     *
+     * @return float|null
+     */
+    public function GetSqlOptimizerTime(): ?float
+    {
+        return $this->sqlOptimizerTime;
+    }
+
+    /**
+     * MAL optimizer time in milliseconds.
      *
      * @return float|null
      */
-    public function GetQueryParsingTime(): ?float
+    public function GetMalOptimizerTime(): ?float
     {
-        return $this->queryParsingTime;
+        return $this->malOptimizerTime;
     }
 
     /**
@@ -231,13 +308,15 @@ public function GetAffectedRows(): ?int
     }
 
     /**
-     * The number of rows in the response.
+     * The total number of rows in the result set.
+     * This includes those rows too, which are not
+     * in the current response.
      *
      * @return integer|null
      */
-    public function GetRowCount(): ?int
+    public function GetTotalRowCount(): ?int
     {
-        return $this->rowCount;
+        return $this->totalRowCount;
     }
 
     /**
@@ -253,26 +332,58 @@ public function GetAsText(): string {
             $response[] = "Action: {$this->queryTypeDescription}";
         }
 
-        if ($this->executionTime !== null) {
-            $response[] = "Execution time: {$this->executionTime} ms";
+        if ($this->queryID !== null) {
+            $response[] = "Query ID (global): {$this->queryID}";
+        }
+
+        if ($this->resultID !== null) {
+            $response[] = "Result ID: {$this->resultID}";
+        }
+
+        if ($this->lastInsertID !== null) {
+            $response[] = "Last insert ID: {$this->lastInsertID}";
+        }
+
+        if ($this->queryTime !== null) {
+            $response[] = "Query time: {$this->queryTime} ms";
         }
 
-        if ($this->queryParsingTime !== null) {
-            $response[] = "Query parsing time: {$this->queryParsingTime} ms";
+        if ($this->sqlOptimizerTime !== null) {
+            $response[] = "SQL optimizer time: {$this->sqlOptimizerTime} ms";
+        }
+
+        if ($this->malOptimizerTime !== null) {
+            $response[] = "Mal optimizer time: {$this->malOptimizerTime} ms";
         }
 
         if ($this->affectedRows !== null) {
             $response[] = "Affected rows: {$this->affectedRows}";
         }
 
+        if ($this->totalRowCount !== null) {
+            $response[] = "Total rows: {$this->totalRowCount}";
+        }
+
         if ($this->rowCount !== null) {
-            $response[] = "Rows: {$this->rowCount}";
+            $response[] = "Rows in current response: {$this->rowCount}";
+        }
+
+        if ($this->columnCount !== null) {
+            $response[] = "Column count: {$this->columnCount}";
+        }
+
+        if ($this->autoCommitState !== null) {
+            $response[] = "Auto-commit state: {$this->autoCommitState}";
         }
 
         if ($this->preparedStatementID !== null) {
             $response[] = "Prepared statement ID: {$this->preparedStatementID}";
         }
 
+        if ($this->exportOffset !== null) {
+            $response[] = "Export offset: {$this->exportOffset}";
+        }
+
         return implode("\n", $response);
     }
 
@@ -300,13 +411,16 @@ public function GetPreparedStatementID(): ?int {
     }
 
     /**
-     * Returns the ID of the query response that
-     * is returned in the result set.
+     * Returns the ID of the result set that
+     * is returned for a query. It is stored on
+     * the server for this session, and parts
+     * of it can be queried using the "export"
+     * command.
      *
      * @return integer|null
      */
-    public function GetQueryID(): ?int {
-        return $this->queryID;
+    public function GetResultID(): ?int {
+        return $this->resultID;
     }
 
     /**
@@ -320,4 +434,60 @@ public function GetAutoCommitState(): ?bool
     {
         return $this->autoCommitState;
     }
+
+    /**
+     * The number of rows (tuples) in the current
+     * response only.
+     *
+     * @return integer|null
+     */
+    public function GetRowCount(): ?int
+    {
+        return $this->rowCount;
+    }
+
+    /**
+     * Column count. If the response contains tabular data,
+     * then this tells the number of columns.
+     *
+     * @return integer|null
+     */
+    public function GetColumnCount(): ?int
+    {
+        return $this->columnCount;
+    }
+
+    /**
+     * Query ID. A global ID which is also used in
+     * functions such as sys.querylog_catalog().
+     *
+     * @return integer|null
+     */
+    public function GetQueryID(): ?int
+    {
+        return $this->queryID;
+    }
+
+    /**
+     * The last automatically generated ID by
+     * an insert statement. (Usually auto_increment)
+     * NULL if none.
+     *
+     * @return integer|null
+     */
+    public function GetLastInsertID(): ?int
+    {
+        return $this->lastInsertID;
+    }
+
+    /**
+     * The index (offset) of the first row in
+     * a block response. (For an "export" command.)
+     *
+     * @return integer|null
+     */
+    public function GetExportOffset(): ?int
+    {
+        return $this->exportOffset;
+    }
 }