Repository: GoogleCloudPlatform/pgadapter
Pull request #3098 (2025-03-27 13:24:40)

perf: get date values directly from protobuf value
Get date values directly from the protobuf value instead of first parsing and copying into a different buffer by calling ResultSet#getDate(int).


Patch:
diff --git a/samples/python/sqlalchemy2-sample/requirements.txt b/samples/python/sqlalchemy2-sample/requirements.txt
index ac519327..5dba3bf9 100644
--- a/samples/python/sqlalchemy2-sample/requirements.txt
+++ b/samples/python/sqlalchemy2-sample/requirements.txt
@@ -1,6 +1,6 @@
 psycopg[binary]==3.2.6
 pytz==2025.2
-sqlalchemy==2.0.39
+sqlalchemy==2.0.40
 google~=3.0.0
 google.auth~=2.38.0
 testcontainers~=4.9.0
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java b/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java
index 96e3384f..a7000b1b 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java
@@ -609,7 +609,8 @@ public class ConnectionHandler implements Runnable {
       new ErrorResponse(this, exception).send();
     } else {
       new ErrorResponse(this, exception).send();
-      new ReadyResponse(output, ReadyResponse.Status.IDLE).send();
+      ReadyResponse.sendIdleResponse(output);
+      output.flush();
     }
   }
 
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/BinaryParser.java b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/BinaryParser.java
index e642e8aa..085319b4 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/BinaryParser.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/BinaryParser.java
@@ -16,6 +16,7 @@ package com.google.cloud.spanner.pgadapter.parsers;
 
 import com.google.api.core.InternalApi;
 import com.google.cloud.ByteArray;
+import com.google.cloud.spanner.ProtobufResultSet;
 import com.google.cloud.spanner.ResultSet;
 import com.google.cloud.spanner.SpannerExceptionFactory;
 import com.google.cloud.spanner.Statement;
@@ -108,7 +109,7 @@ public class BinaryParser extends Parser<ByteArray> {
       DataFormat format) {
     int bufferSize = sessionState.getBinaryConversionBufferSize();
     try {
-      String base64 = resultSet.getValue(position).getAsString();
+      String base64 = getBase64(resultSet, position);
       switch (format) {
         case SPANNER:
         case POSTGRESQL_BINARY:
@@ -128,7 +129,7 @@ public class BinaryParser extends Parser<ByteArray> {
           }
           return null;
         case POSTGRESQL_TEXT:
-          return bytesToHex(resultSet.getBytes(position).toByteArray());
+          return bytesToHex(Base64.getDecoder().decode(base64));
         default:
           throw new IllegalArgumentException("unknown data format: " + format);
       }
@@ -137,6 +138,14 @@ public class BinaryParser extends Parser<ByteArray> {
     }
   }
 
+  static String getBase64(ResultSet resultSet, int column) {
+    if (resultSet instanceof ProtobufResultSet
+        && ((ProtobufResultSet) resultSet).canGetProtobufValue(column)) {
+      return ((ProtobufResultSet) resultSet).getProtobufValue(column).getStringValue();
+    }
+    return resultSet.getValue(column).getAsString();
+  }
+
   static int copy(int length, InputStream from, DataOutputStream to, int bufferSize)
       throws IOException {
     byte[] buf = new byte[Math.min(length, bufferSize)];
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/DateParser.java b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/DateParser.java
index 71bd8b30..b8d8b4a5 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/DateParser.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/DateParser.java
@@ -17,12 +17,15 @@ package com.google.cloud.spanner.pgadapter.parsers;
 import com.google.api.core.InternalApi;
 import com.google.cloud.Date;
 import com.google.cloud.spanner.ErrorCode;
+import com.google.cloud.spanner.ProtobufResultSet;
 import com.google.cloud.spanner.ResultSet;
 import com.google.cloud.spanner.SpannerExceptionFactory;
 import com.google.cloud.spanner.Statement;
 import com.google.cloud.spanner.pgadapter.ProxyServer.DataFormat;
 import com.google.cloud.spanner.pgadapter.error.PGExceptionFactory;
-import java.nio.charset.StandardCharsets;
+import com.google.cloud.spanner.pgadapter.session.SessionState;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.time.LocalDate;
 import javax.annotation.Nonnull;
 import org.postgresql.util.ByteConverter;
@@ -118,18 +121,47 @@ public class DateParser extends Parser<Date> {
     return IntegerParser.binaryParse(daysAsInt);
   }
 
-  public static byte[] convertToPG(ResultSet resultSet, int position, DataFormat format) {
+  public static byte[] convertToPG(
+      SessionState sessionState,
+      DataOutputStream outputStream,
+      ResultSet resultSet,
+      int position,
+      DataFormat format)
+      throws IOException {
+    writeToPG(sessionState, outputStream, resultSet, position, format);
+    return null;
+  }
+
+  static void writeToPG(
+      SessionState sessionState,
+      DataOutputStream outputStream,
+      ResultSet resultSet,
+      int position,
+      DataFormat format)
+      throws IOException {
     switch (format) {
       case SPANNER:
       case POSTGRESQL_TEXT:
-        return toString(resultSet.getDate(position)).getBytes(StandardCharsets.UTF_8);
+        StringParser.writeToPG(sessionState, outputStream, getDateAsString(resultSet, position));
+        break;
       case POSTGRESQL_BINARY:
-        return convertToPG(resultSet.getDate(position));
+        byte[] value = convertToPG(resultSet.getDate(position));
+        outputStream.writeInt(value.length);
+        outputStream.write(value);
+        break;
       default:
         throw new IllegalArgumentException("unknown data format: " + format);
     }
   }
 
+  static String getDateAsString(ResultSet resultSet, int column) {
+    if (resultSet instanceof ProtobufResultSet
+        && ((ProtobufResultSet) resultSet).canGetProtobufValue(column)) {
+      return ((ProtobufResultSet) resultSet).getProtobufValue(column).getStringValue();
+    }
+    return toString(resultSet.getDate(column));
+  }
+
   /**
    * Dates are stored as long, but technically cannot be longer than int. Here we ensure that is the
    * case.
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/JsonbParser.java b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/JsonbParser.java
index f6e6e913..8fed8bc1 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/JsonbParser.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/JsonbParser.java
@@ -15,6 +15,7 @@
 package com.google.cloud.spanner.pgadapter.parsers;
 
 import com.google.api.core.InternalApi;
+import com.google.cloud.spanner.ProtobufResultSet;
 import com.google.cloud.spanner.ResultSet;
 import com.google.cloud.spanner.Statement;
 import com.google.cloud.spanner.Value;
@@ -34,7 +35,7 @@ public class JsonbParser extends Parser<String> {
   private static final byte[] BINARY_HEADER = new byte[] {1};
 
   JsonbParser(ResultSet item, int position) {
-    this.item = item.getPgJsonb(position);
+    this.item = getJsonb(item, position);
   }
 
   JsonbParser(Object item) {
@@ -103,17 +104,28 @@ public class JsonbParser extends Parser<String> {
     switch (format) {
       case SPANNER:
       case POSTGRESQL_TEXT:
-        StringParser.writeToPG(sessionState, dataOutputStream, resultSet.getPgJsonb(position));
+        StringParser.writeToPG(sessionState, dataOutputStream, getJsonb(resultSet, position));
         return null;
       case POSTGRESQL_BINARY:
         StringParser.writeToPG(
-            sessionState, dataOutputStream, resultSet.getPgJsonb(position), BINARY_HEADER);
+            sessionState, dataOutputStream, getJsonb(resultSet, position), BINARY_HEADER);
         return null;
       default:
         throw new IllegalArgumentException("unknown data format: " + format);
     }
   }
 
+  /** Get the string from the result as efficiently as possible. */
+  static String getJsonb(ResultSet resultSet, int column) {
+    // If the result set is a ProtobufResultSet and the protobuf value is still present, then get
+    // the string directly from that.
+    if (resultSet instanceof ProtobufResultSet
+        && ((ProtobufResultSet) resultSet).canGetProtobufValue(column)) {
+      return ((ProtobufResultSet) resultSet).getProtobufValue(column).getStringValue();
+    }
+    return resultSet.getPgJsonb(column);
+  }
+
   @Override
   public void bind(Statement.Builder statementBuilder, String name) {
     statementBuilder.bind(name).to(Value.pgJsonb(this.item));
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java b/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java
index 7349e4e2..297d5ed5 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/session/SessionState.java
@@ -43,6 +43,8 @@ import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 
@@ -113,6 +115,39 @@ public class SessionState {
     initCopySettings(this.settings);
   }
 
+  /**
+   * These fields contain cached values for settings that are used often and/or expensive to
+   * calculate.
+   */
+  private final AtomicReference<RemoveEscapeClauseEnum> cachedRemoveEscapeClause =
+      new AtomicReference<>();
+
+  private final AtomicReference<ZoneId> cachedZoneId = new AtomicReference<>();
+  private final AtomicReference<Boolean> cachedReplaceForUpdateClause = new AtomicReference<>();
+  private final AtomicReference<Boolean> cachedReplacePgCatalogTables = new AtomicReference<>();
+  private final AtomicReference<Boolean> cachedEmulatePgClassTables = new AtomicReference<>();
+  private final AtomicReference<Integer> cachedBinaryConversionBufferSize = new AtomicReference<>();
+  private final AtomicReference<Integer> cachedStringConversionBufferSize = new AtomicReference<>();
+
+  private void invalidateCache() {
+    cachedRemoveEscapeClause.set(null);
+    cachedZoneId.set(null);
+    cachedReplaceForUpdateClause.set(null);
+    cachedReplacePgCatalogTables.set(null);
+    cachedEmulatePgClassTables.set(null);
+    cachedBinaryConversionBufferSize.set(null);
+    cachedStringConversionBufferSize.set(null);
+  }
+
+  private <T> T getCachedValue(Supplier<T> supplier, AtomicReference<T> cachedValue) {
+    if (cachedValue.get() != null) {
+      return cachedValue.get();
+    }
+    T value = supplier.get();
+    cachedValue.set(value);
+    return value;
+  }
+
   @VisibleForTesting
   Map<String, PGSetting> getSettings() {
     return this.settings;
@@ -242,7 +277,7 @@ public class SessionState {
 
   private void internalSet(
       String extension, String name, String setting, Map<String, PGSetting> currentSettings) {
-    clearCachedValues();
+    invalidateCache();
     String key = toKey(extension, name);
     PGSetting newSetting = currentSettings.get(key);
     if (newSetting == null) {
@@ -264,10 +299,6 @@ public class SessionState {
     currentSettings.put(key, newSetting);
   }
 
-  private void clearCachedValues() {
-    cachedZoneId = null;
-  }
-
   /** Returns the current value of the specified setting. */
   public PGSetting get(String extension, String name) {
     return internalGet(toKey(extension, name), true);
@@ -459,20 +490,25 @@ public class SessionState {
    */
   // TODO: Remove in the next major version update.
   public boolean isReplaceForUpdateClause() {
-    return getBoolSetting("spanner", "replace_for_update", false);
+    return getCachedValue(
+        () -> getBoolSetting("spanner", "replace_for_update", false), cachedReplaceForUpdateClause);
   }
 
   /** Returns whether LIKE ... ESCAPE clauses are removed. */
   public RemoveEscapeClauseEnum getRemoveEscapeClause() {
-    PGSetting setting = internalGet(toKey("spanner", "remove_escape_clause"), false);
-    if (setting == null) {
-      return RemoveEscapeClauseEnum.DEFAULT;
-    }
-    return tryGetFirstNonNull(
-        RemoveEscapeClauseEnum.DEFAULT,
-        () -> RemoveEscapeClauseEnum.valueOf(upper(setting.getSetting())),
-        () -> RemoveEscapeClauseEnum.valueOf(upper(setting.getResetVal())),
-        () -> RemoveEscapeClauseEnum.valueOf(upper(setting.getBootVal())));
+    return getCachedValue(
+        () -> {
+          PGSetting setting = internalGet(toKey("spanner", "remove_escape_clause"), false);
+          if (setting == null) {
+            return RemoveEscapeClauseEnum.DEFAULT;
+          }
+          return tryGetFirstNonNull(
+              RemoveEscapeClauseEnum.DEFAULT,
+              () -> RemoveEscapeClauseEnum.valueOf(upper(setting.getSetting())),
+              () -> RemoveEscapeClauseEnum.valueOf(upper(setting.getResetVal())),
+              () -> RemoveEscapeClauseEnum.valueOf(upper(setting.getBootVal())));
+        },
+        cachedRemoveEscapeClause);
   }
 
   private static String upper(String s) {
@@ -481,15 +517,19 @@ public class SessionState {
 
   /** Returns the current setting for replacing pg_catalog tables with common table expressions. */
   public boolean isReplacePgCatalogTables() {
-    PGSetting setting = internalGet(toKey("spanner", "replace_pg_catalog_tables"), false);
-    if (setting == null) {
-      return true;
-    }
-    return tryGetFirstNonNull(
-        true,
-        () -> BooleanParser.toBoolean(setting.getSetting()),
-        () -> BooleanParser.toBoolean(setting.getResetVal()),
-        () -> BooleanParser.toBoolean(setting.getBootVal()));
+    return getCachedValue(
+        () -> {
+          PGSetting setting = internalGet(toKey("spanner", "replace_pg_catalog_tables"), false);
+          if (setting == null) {
+            return true;
+          }
+          return tryGetFirstNonNull(
+              true,
+              () -> BooleanParser.toBoolean(setting.getSetting()),
+              () -> BooleanParser.toBoolean(setting.getResetVal()),
+              () -> BooleanParser.toBoolean(setting.getBootVal()));
+        },
+        cachedReplacePgCatalogTables);
   }
 
   /**
@@ -497,15 +537,19 @@ public class SessionState {
    * use the object name as OID.
    */
   public boolean isEmulatePgClassTables() {
-    PGSetting setting = internalGet(toKey("spanner", "emulate_pg_class_tables"), false);
-    if (setting == null) {
-      return false;
-    }
-    return tryGetFirstNonNull(
-        true,
-        () -> BooleanParser.toBoolean(setting.getSetting()),
-        () -> BooleanParser.toBoolean(setting.getResetVal()),
-        () -> BooleanParser.toBoolean(setting.getBootVal()));
+    return getCachedValue(
+        () -> {
+          PGSetting setting = internalGet(toKey("spanner", "emulate_pg_class_tables"), false);
+          if (setting == null) {
+            return false;
+          }
+          return tryGetFirstNonNull(
+              true,
+              () -> BooleanParser.toBoolean(setting.getSetting()),
+              () -> BooleanParser.toBoolean(setting.getResetVal()),
+              () -> BooleanParser.toBoolean(setting.getBootVal()));
+        },
+        cachedEmulatePgClassTables);
   }
 
   /** Returns the {@link DdlTransactionMode} that is used for this connection at this time. */
@@ -539,7 +583,11 @@ public class SessionState {
    * the PostgreSQL wire-format. Zero means no buffer and instead convert it directly in all cases.
    */
   public int getBinaryConversionBufferSize() {
-    return getIntegerSetting("spanner", "binary_conversion_buffer_size", 0);
+    return getCachedValue(
+        () -> {
+          return getIntegerSetting("spanner", "binary_conversion_buffer_size", 0);
+        },
+        cachedBinaryConversionBufferSize);
   }
 
   /**
@@ -547,7 +595,9 @@ public class SessionState {
    * means no buffer and instead convert it directly in all cases.
    */
   public int getStringConversionBufferSize() {
-    return getIntegerSetting("spanner", "string_conversion_buffer_size", 0);
+    return getCachedValue(
+        () -> getIntegerSetting("spanner", "string_conversion_buffer_size", 0),
+        cachedStringConversionBufferSize);
   }
 
   /**
@@ -570,25 +620,23 @@ public class SessionState {
     return getIntegerSetting("spanner", "dml_batch_update_count", 0);
   }
 
-  private ZoneId cachedZoneId;
-
   /** Returns the {@link ZoneId} of the current timezone for this session. */
   public ZoneId getTimezone() {
-    if (cachedZoneId != null) {
-      return cachedZoneId;
-    }
-    PGSetting setting = internalGet(toKey(null, "timezone"), false);
-    if (setting == null) {
-      return ZoneId.systemDefault();
-    }
-    String id =
-        tryGetFirstNonNull(
-            ZoneId.systemDefault().getId(),
-            setting::getSetting,
-            setting::getResetVal,
-            setting::getBootVal);
-
-    return (cachedZoneId = zoneIdFromString(id));
+    return getCachedValue(
+        () -> {
+          PGSetting setting = internalGet(toKey(null, "timezone"), false);
+          if (setting == null) {
+            return ZoneId.systemDefault();
+          }
+          String id =
+              tryGetFirstNonNull(
+                  ZoneId.systemDefault().getId(),
+                  setting::getSetting,
+                  setting::getResetVal,
+                  setting::getBootVal);
+          return zoneIdFromString(id);
+        },
+        cachedZoneId);
   }
 
   private ZoneId zoneIdFromString(String value) {
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/AbstractFetchOrMoveStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/AbstractFetchOrMoveStatement.java
index cb1a4005..867e160c 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/AbstractFetchOrMoveStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/AbstractFetchOrMoveStatement.java
@@ -25,7 +25,6 @@ import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexNa
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.Futures;
 import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.Future;
 
 abstract class AbstractFetchOrMoveStatement extends IntermediatePortalStatement {
@@ -129,8 +128,8 @@ abstract class AbstractFetchOrMoveStatement extends IntermediatePortalStatement
       String name,
       IntermediatePreparedStatement preparedStatement,
       byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes,
+      short[] parameterFormatCodes,
+      short[] resultFormatCodes,
       ParsedFetchOrMoveStatement fetchOrMoveStatement) {
     super(name, preparedStatement, parameters, parameterFormatCodes, resultFormatCodes);
     this.fetchOrMoveStatement = fetchOrMoveStatement;
@@ -186,10 +185,7 @@ abstract class AbstractFetchOrMoveStatement extends IntermediatePortalStatement
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // FETCH and MOVE do not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CloseStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CloseStatement.java
index 19c0b8dd..6ae537ba 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CloseStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CloseStatement.java
@@ -26,9 +26,7 @@ import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.cloud.spanner.pgadapter.statements.BackendConnection.NoResult;
 import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexName;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 public class CloseStatement extends IntermediatePortalStatement {
@@ -58,8 +56,8 @@ public class CloseStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.closeStatement = parse(originalStatement.getSql());
   }
 
@@ -98,10 +96,7 @@ public class CloseStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // CLOSE does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java
index 453e994f..adf4532a 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyStatement.java
@@ -129,8 +129,8 @@ public class CopyStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedCopyStatement = parsedCopyStatement;
   }
 
@@ -404,10 +404,7 @@ public class CopyStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // COPY does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyToStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyToStatement.java
index de2c852e..e63dc4da 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyToStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/CopyToStatement.java
@@ -37,9 +37,7 @@ import com.google.cloud.spanner.pgadapter.wireoutput.CopyDoneResponse;
 import com.google.cloud.spanner.pgadapter.wireoutput.CopyOutResponse;
 import com.google.cloud.spanner.pgadapter.wireoutput.WireOutput;
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
@@ -75,8 +73,8 @@ public class CopyToStatement extends IntermediatePortalStatement {
             createParsedStatement(parsedCopyStatement),
             createSelectStatement(parsedCopyStatement)),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedCopyStatement = parsedCopyStatement;
     if (parsedCopyStatement.format == CopyStatement.Format.BINARY) {
       this.csvFormat = null;
@@ -215,10 +213,7 @@ public class CopyToStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // COPY does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeclareStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeclareStatement.java
index 32d4a1f3..127a55cf 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeclareStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeclareStatement.java
@@ -35,9 +35,7 @@ import com.google.cloud.spanner.pgadapter.wireprotocol.BindMessage;
 import com.google.cloud.spanner.pgadapter.wireprotocol.ControlMessage.ManuallyCreatedToken;
 import com.google.cloud.spanner.pgadapter.wireprotocol.ParseMessage;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 @InternalApi
@@ -105,8 +103,8 @@ public class DeclareStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.declareStatement = parse(originalStatement.getSql());
   }
 
@@ -176,10 +174,7 @@ public class DeclareStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // DECLARE does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DiscardStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DiscardStatement.java
index 3a95dddc..ee18d106 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DiscardStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DiscardStatement.java
@@ -26,9 +26,7 @@ import com.google.cloud.spanner.pgadapter.statements.BackendConnection.Connectio
 import com.google.cloud.spanner.pgadapter.statements.BackendConnection.NoResult;
 import com.google.cloud.spanner.pgadapter.statements.DiscardStatement.ParsedDiscardStatement.DiscardType;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 public class DiscardStatement extends IntermediatePortalStatement {
@@ -65,8 +63,8 @@ public class DiscardStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.discardStatement = parse(originalStatement.getSql());
   }
 
@@ -120,10 +118,7 @@ public class DiscardStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // DISCARD does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExtendedQueryProtocolHandler.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExtendedQueryProtocolHandler.java
index 06da673e..1e7b955a 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExtendedQueryProtocolHandler.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExtendedQueryProtocolHandler.java
@@ -138,9 +138,7 @@ public class ExtendedQueryProtocolHandler {
    * received.
    */
   public void buffer(AbstractQueryProtocolMessage message) {
-    addEvent(
-        "Received message: '" + message.getIdentifier() + "'",
-        Attributes.of(DB_STATEMENT, message.getSql()));
+    addEvent(message.receivedEventDescription(), Attributes.of(DB_STATEMENT, message.getSql()));
     messages.add(message);
   }
 
@@ -218,10 +216,9 @@ public class ExtendedQueryProtocolHandler {
         throw PGExceptionFactory.newQueryCancelledException();
       }
       if (includeReadyResponse) {
-        new ReadyResponse(
-                connectionHandler.getConnectionMetadata().getOutputStream(),
-                getBackendConnection().getConnectionState().getReadyResponseStatus())
-            .send(false);
+        ReadyResponse.send(
+            connectionHandler.getConnectionMetadata().getOutputStream(),
+            getBackendConnection().getConnectionState().getReadyResponseStatus());
       }
     } catch (Throwable exception) {
       recordException(exception);
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/FetchStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/FetchStatement.java
index 21bfec3e..9d18e7d5 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/FetchStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/FetchStatement.java
@@ -22,7 +22,6 @@ import com.google.cloud.spanner.pgadapter.wireprotocol.ControlMessage.ManuallyCr
 import com.google.cloud.spanner.pgadapter.wireprotocol.ControlMessage.PreparedType;
 import com.google.cloud.spanner.pgadapter.wireprotocol.DescribeMessage;
 import com.google.cloud.spanner.pgadapter.wireprotocol.ExecuteMessage;
-import com.google.common.collect.ImmutableList;
 
 public class FetchStatement extends AbstractFetchOrMoveStatement {
 
@@ -42,8 +41,8 @@ public class FetchStatement extends AbstractFetchOrMoveStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of(),
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES,
         parse(originalStatement.getSql(), "fetch", ParsedFetchStatement.class));
   }
 
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePortalStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePortalStatement.java
index 42bceab7..e2a93416 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePortalStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePortalStatement.java
@@ -22,7 +22,6 @@ import com.google.cloud.spanner.pgadapter.parsers.Parser;
 import com.google.cloud.spanner.pgadapter.parsers.Parser.FormatCode;
 import com.google.cloud.spanner.pgadapter.statements.BackendConnection.NoResult;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 /**
@@ -32,18 +31,19 @@ import java.util.concurrent.Future;
 @InternalApi
 public class IntermediatePortalStatement extends IntermediatePreparedStatement {
   static final byte[][] NO_PARAMS = new byte[0][];
+  public static final short[] NO_FORMAT_CODES = new short[0];
 
   private final IntermediatePreparedStatement preparedStatement;
   private final byte[][] parameters;
-  protected final List<Short> parameterFormatCodes;
-  protected final List<Short> resultFormatCodes;
+  protected final short[] parameterFormatCodes;
+  protected final short[] resultFormatCodes;
 
   public IntermediatePortalStatement(
       String name,
       IntermediatePreparedStatement preparedStatement,
       byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      short[] parameterFormatCodes,
+      short[] resultFormatCodes) {
     super(
         preparedStatement.connectionHandler,
         preparedStatement.options,
@@ -62,23 +62,23 @@ public class IntermediatePortalStatement extends IntermediatePreparedStatement {
   }
 
   public short getParameterFormatCode(int index) {
-    if (this.parameterFormatCodes.size() == 0) {
+    if (this.parameterFormatCodes.length == 0) {
       return 0;
-    } else if (index >= this.parameterFormatCodes.size()) {
-      return this.parameterFormatCodes.get(0);
+    } else if (index >= this.parameterFormatCodes.length) {
+      return this.parameterFormatCodes[0];
     } else {
-      return this.parameterFormatCodes.get(index);
+      return this.parameterFormatCodes[index];
     }
   }
 
   @Override
   public short getResultFormatCode(int index) {
-    if (this.resultFormatCodes == null || this.resultFormatCodes.isEmpty()) {
+    if (this.resultFormatCodes == null || this.resultFormatCodes.length == 0) {
       return super.getResultFormatCode(index);
-    } else if (this.resultFormatCodes.size() == 1) {
-      return this.resultFormatCodes.get(0);
+    } else if (this.resultFormatCodes.length == 1) {
+      return this.resultFormatCodes[0];
     } else {
-      return this.resultFormatCodes.get(index);
+      return this.resultFormatCodes[index];
     }
   }
 
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePreparedStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePreparedStatement.java
index d3746212..875157a1 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePreparedStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediatePreparedStatement.java
@@ -26,8 +26,6 @@ import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
-import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import org.postgresql.core.Oid;
@@ -39,7 +37,6 @@ import org.postgresql.core.Oid;
 public class IntermediatePreparedStatement extends IntermediateStatement {
   static final int[] NO_PARAMETER_TYPES = new int[0];
 
-  private final String name;
   protected final int[] givenParameterDataTypes;
   protected Statement statement;
   private Future<DescribeResult> describeResult;
@@ -47,12 +44,12 @@ public class IntermediatePreparedStatement extends IntermediateStatement {
   public IntermediatePreparedStatement(
       ConnectionHandler connectionHandler,
       OptionsMetadata options,
+      // TODO: Remove name argument
       String name,
       int[] givenParameterDataTypes,
       ParsedStatement parsedStatement,
       Statement originalStatement) {
     super(connectionHandler, options, parsedStatement, originalStatement);
-    this.name = name;
     this.givenParameterDataTypes = givenParameterDataTypes;
     this.statement = originalStatement;
   }
@@ -89,10 +86,7 @@ public class IntermediatePreparedStatement extends IntermediateStatement {
    * @return An Intermediate Portal Statement (or rather a bound version of this statement)
    */
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     return new IntermediatePortalStatement(
         name, this, parameters, parameterFormatCodes, resultFormatCodes);
   }
@@ -147,8 +141,7 @@ public class IntermediatePreparedStatement extends IntermediateStatement {
         || hasOnlyNullValues(parameterValues)) {
       return;
     }
-    if (parameterValues.length != this.givenParameterDataTypes.length
-        || Arrays.stream(this.givenParameterDataTypes).anyMatch(p -> p == 0)) {
+    if (hasUnknownParams(parameterValues)) {
       // Note: We are only asking the backend to parse the types if there is at least one
       // parameter with unspecified type. Otherwise, we will rely on the types given in PARSE.
       // There is also no need to auto-describe if only null values are untyped.
@@ -179,6 +172,18 @@ public class IntermediatePreparedStatement extends IntermediateStatement {
     }
   }
 
+  private boolean hasUnknownParams(byte[][] parameterValues) {
+    if (parameterValues.length != this.givenParameterDataTypes.length) {
+      return true;
+    }
+    for (int givenParameterDataType : this.givenParameterDataTypes) {
+      if (givenParameterDataType == Oid.UNSPECIFIED) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   boolean hasOnlyNullValues(byte[][] parameterValues) {
     for (byte[] parameterValue : parameterValues) {
       if (parameterValue != null) {
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/InvalidStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/InvalidStatement.java
index c3b12977..c0847765 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/InvalidStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/InvalidStatement.java
@@ -19,7 +19,6 @@ import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStateme
 import com.google.cloud.spanner.pgadapter.ConnectionHandler;
 import com.google.cloud.spanner.pgadapter.error.PGExceptionFactory;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
-import com.google.common.collect.ImmutableList;
 
 public class InvalidStatement extends IntermediatePortalStatement {
 
@@ -40,8 +39,8 @@ public class InvalidStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     setException(PGExceptionFactory.toPGException(exception));
   }
 }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/MoveStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/MoveStatement.java
index 28d5908c..a5a3abd1 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/MoveStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/MoveStatement.java
@@ -20,7 +20,6 @@ import com.google.cloud.spanner.pgadapter.ConnectionHandler;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.cloud.spanner.pgadapter.wireprotocol.ControlMessage.ManuallyCreatedToken;
 import com.google.cloud.spanner.pgadapter.wireprotocol.ExecuteMessage;
-import com.google.common.collect.ImmutableList;
 
 /**
  * MOVE is the same as FETCH, except it just skips the results instead of actually sending the rows
@@ -45,8 +44,8 @@ public class MoveStatement extends AbstractFetchOrMoveStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of(),
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES,
         parse(originalStatement.getSql(), "move", ParsedMoveStatement.class));
   }
 
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/PrepareStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/PrepareStatement.java
index b76bbd71..300391e9 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/PrepareStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/PrepareStatement.java
@@ -124,8 +124,8 @@ public class PrepareStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.preparedStatement = parse(originalStatement.getSql());
   }
 
@@ -174,10 +174,7 @@ public class PrepareStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // PREPARE does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ReleaseStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ReleaseStatement.java
index 2d5ce642..144b5a9b 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ReleaseStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ReleaseStatement.java
@@ -26,9 +26,7 @@ import com.google.cloud.spanner.pgadapter.error.SQLState;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexName;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 public class ReleaseStatement extends IntermediatePortalStatement {
@@ -58,8 +56,8 @@ public class ReleaseStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedReleaseStatement = parse(originalStatement.getSql());
   }
 
@@ -112,10 +110,7 @@ public class ReleaseStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // RELEASE does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/RollbackToStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/RollbackToStatement.java
index 45b1368b..9c76a41a 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/RollbackToStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/RollbackToStatement.java
@@ -26,9 +26,7 @@ import com.google.cloud.spanner.pgadapter.error.SQLState;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexName;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 public class RollbackToStatement extends IntermediatePortalStatement {
@@ -58,8 +56,8 @@ public class RollbackToStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedRollbackToStatement = parse(originalStatement.getSql());
   }
 
@@ -123,10 +121,7 @@ public class RollbackToStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // ROLLBACK does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/SavepointStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/SavepointStatement.java
index a2f81e1a..a67c9fbe 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/SavepointStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/SavepointStatement.java
@@ -26,9 +26,7 @@ import com.google.cloud.spanner.pgadapter.error.SQLState;
 import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexName;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 public class SavepointStatement extends IntermediatePortalStatement {
@@ -58,8 +56,8 @@ public class SavepointStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedSavepointStatement = parse(originalStatement.getSql());
   }
 
@@ -111,10 +109,7 @@ public class SavepointStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // SAVEPOINT does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectCurrentSettingStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectCurrentSettingStatement.java
index 8b261baa..7c260269 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectCurrentSettingStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectCurrentSettingStatement.java
@@ -33,7 +33,6 @@ import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexNa
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.Future;
 
@@ -60,8 +59,8 @@ public class SelectCurrentSettingStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.showStatement = parse(originalStatement.getSql());
   }
 
@@ -128,10 +127,7 @@ public class SelectCurrentSettingStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     return this;
   }
 }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectSetConfigStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectSetConfigStatement.java
index 0029560e..d295f60c 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectSetConfigStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/SelectSetConfigStatement.java
@@ -34,7 +34,6 @@ import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexNa
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.Future;
 
@@ -61,8 +60,8 @@ public class SelectSetConfigStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.setStatement = parse(originalStatement.getSql());
   }
 
@@ -143,10 +142,7 @@ public class SelectSetConfigStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     return this;
   }
 }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShowDatabaseDdlStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShowDatabaseDdlStatement.java
index 6886c937..4ceb7eb9 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShowDatabaseDdlStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShowDatabaseDdlStatement.java
@@ -70,8 +70,8 @@ public class ShowDatabaseDdlStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedShowDatabaseDdlStatement = parse(originalStatement.getSql());
   }
 
@@ -150,10 +150,7 @@ public class ShowDatabaseDdlStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // SHOW DATABASE DDL does not support binding any parameters, so we just return the same
     // statement.
     return this;
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShutdownStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShutdownStatement.java
index 329b3e25..109ea28e 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShutdownStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ShutdownStatement.java
@@ -28,9 +28,7 @@ import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.cloud.spanner.pgadapter.statements.BackendConnection.NoResult;
 import com.google.cloud.spanner.pgadapter.statements.ShutdownStatement.ParsedShutdownStatement.Builder;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 /**
@@ -76,8 +74,8 @@ public class ShutdownStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedShutdownStatement = parse(originalStatement.getSql(), connectionHandler.getServer());
   }
 
@@ -125,10 +123,7 @@ public class ShutdownStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // SHUTDOWN does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/VacuumStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/VacuumStatement.java
index 1c0d2ee5..7f0e037f 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/VacuumStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/VacuumStatement.java
@@ -110,8 +110,8 @@ public class VacuumStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.parsedVacuumStatement = parse(originalStatement.getSql());
   }
 
@@ -148,10 +148,7 @@ public class VacuumStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // VACUUM does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/utils/Converter.java b/src/main/java/com/google/cloud/spanner/pgadapter/utils/Converter.java
index 204ef2d6..1c156f49 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/utils/Converter.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/utils/Converter.java
@@ -165,7 +165,7 @@ public class Converter implements AutoCloseable {
       case BYTES:
         return BinaryParser.convertToPG(sessionState, outputStream, result, position, format);
       case DATE:
-        return DateParser.convertToPG(result, position, format);
+        return DateParser.convertToPG(sessionState, outputStream, result, position, format);
       case FLOAT32:
         return FloatParser.convertToPG(result, position, format);
       case FLOAT64:
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/BindCompleteResponse.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/BindCompleteResponse.java
index b368abf5..7f716670 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/BindCompleteResponse.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/BindCompleteResponse.java
@@ -22,6 +22,12 @@ import java.text.MessageFormat;
 /** Assures to the client that a statement got bound to a portal successfully. */
 @InternalApi
 public class BindCompleteResponse extends WireOutput {
+  private static final byte[] RESPONSE = new byte[] {'2', 0, 0, 0, 4};
+
+  /** Send a standard BindComplete response message. */
+  public static void send(DataOutputStream output) throws IOException {
+    output.write(RESPONSE);
+  }
 
   public BindCompleteResponse(DataOutputStream output) {
     super(output, 4);
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CloseCompleteResponse.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CloseCompleteResponse.java
index 1edf8ef7..9a70a7bc 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CloseCompleteResponse.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CloseCompleteResponse.java
@@ -16,11 +16,18 @@ package com.google.cloud.spanner.pgadapter.wireoutput;
 
 import com.google.api.core.InternalApi;
 import java.io.DataOutputStream;
+import java.io.IOException;
 import java.text.MessageFormat;
 
 /** Assures to the client that a portal got closed successfully. */
 @InternalApi
 public class CloseCompleteResponse extends WireOutput {
+  private static final byte[] RESPONSE = new byte[] {'3', 0, 0, 0, 4};
+
+  /** Send a standard CloseResponse message. */
+  public static void send(DataOutputStream output) throws IOException {
+    output.write(RESPONSE);
+  }
 
   public CloseCompleteResponse(DataOutputStream output) {
     super(output, 4);
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CommandCompleteResponse.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CommandCompleteResponse.java
index 2390c841..780b9177 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CommandCompleteResponse.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/CommandCompleteResponse.java
@@ -17,22 +17,40 @@ package com.google.cloud.spanner.pgadapter.wireoutput;
 import com.google.api.core.InternalApi;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.text.MessageFormat;
 
 /** Signals to a client that an issued query is complete. */
 @InternalApi
 public class CommandCompleteResponse extends WireOutput {
-
+  private static final byte IDENTIFIER = 'C';
   private static final int HEADER_LENGTH = 4;
   private static final int NULL_TERMINATOR_LENGTH = 1;
 
   private static final byte NULL_TERMINATOR = 0;
 
+  /** Send a CommandComplete response. */
+  public static void send(DataOutputStream output, String command) throws IOException {
+    byte[] commandBytes = command.getBytes(StandardCharsets.UTF_8);
+    output.writeByte(IDENTIFIER);
+    output.writeInt(calculateLength(commandBytes));
+    output.write(commandBytes);
+    output.write(NULL_TERMINATOR);
+  }
+
+  private static int calculateLength(byte[] command) {
+    return HEADER_LENGTH + command.length + NULL_TERMINATOR_LENGTH;
+  }
+
   private final byte[] command;
 
   public CommandCompleteResponse(DataOutputStream output, String command) {
-    super(output, HEADER_LENGTH + command.getBytes(UTF8).length + NULL_TERMINATOR_LENGTH);
-    this.command = command.getBytes(UTF8);
+    this(output, command.getBytes(UTF8));
+  }
+
+  private CommandCompleteResponse(DataOutputStream output, byte[] command) {
+    super(output, HEADER_LENGTH + command.length + NULL_TERMINATOR_LENGTH);
+    this.command = command;
   }
 
   @Override
@@ -43,7 +61,7 @@ public class CommandCompleteResponse extends WireOutput {
 
   @Override
   public byte getIdentifier() {
-    return 'C';
+    return IDENTIFIER;
   }
 
   @Override
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ParseCompleteResponse.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ParseCompleteResponse.java
index dffe2ff4..0675e017 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ParseCompleteResponse.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ParseCompleteResponse.java
@@ -22,6 +22,12 @@ import java.text.MessageFormat;
 /** Signals the end of a parse transaction. */
 @InternalApi
 public class ParseCompleteResponse extends WireOutput {
+  private static final byte[] RESPONSE = new byte[] {'1', 0, 0, 0, 4};
+
+  /** Send a standard ParseComplete response. */
+  public static void send(DataOutputStream output) throws IOException {
+    output.write(RESPONSE);
+  }
 
   public ParseCompleteResponse(DataOutputStream output) {
     super(output, 4);
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/PortalSuspendedResponse.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/PortalSuspendedResponse.java
index 9872870f..8a3e7aaf 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/PortalSuspendedResponse.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/PortalSuspendedResponse.java
@@ -16,11 +16,18 @@ package com.google.cloud.spanner.pgadapter.wireoutput;
 
 import com.google.api.core.InternalApi;
 import java.io.DataOutputStream;
+import java.io.IOException;
 import java.text.MessageFormat;
 
 /** Signals that there are more rows available. */
 @InternalApi
 public class PortalSuspendedResponse extends WireOutput {
+  private static final byte[] RESPONSE = new byte[] {'s', 0, 0, 0, 4};
+
+  /** Send a standard PortalSuspended response. */
+  public static void send(DataOutputStream output) throws IOException {
+    output.write(RESPONSE);
+  }
 
   public PortalSuspendedResponse(DataOutputStream output) {
     super(output, 4);
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ReadyResponse.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ReadyResponse.java
index a3900b29..742caf86 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ReadyResponse.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireoutput/ReadyResponse.java
@@ -21,10 +21,44 @@ import java.io.IOException;
 import java.text.MessageFormat;
 
 /**
- * Signals readiness status to receieve messages (here we only tend to send Idle, which means ready)
+ * Signals readiness status to receive messages (here we only tend to send Idle, which means ready)
  */
 @InternalApi
 public class ReadyResponse extends WireOutput {
+  private static final byte[] IDLE_RESPONSE = new byte[] {'Z', 0, 0, 0, 5, (byte) Status.IDLE.c};
+  private static final byte[] TRANSACTION_RESPONSE =
+      new byte[] {'Z', 0, 0, 0, 5, (byte) Status.TRANSACTION.c};
+  private static final byte[] FAILED_RESPONSE =
+      new byte[] {'Z', 0, 0, 0, 5, (byte) Status.FAILED.c};
+
+  /** Send a ReadyResponse with the given status. */
+  public static void send(DataOutputStream output, Status status) throws IOException {
+    switch (status) {
+      case IDLE:
+        sendIdleResponse(output);
+        break;
+      case TRANSACTION:
+        sendTransactionResponse(output);
+        break;
+      case FAILED:
+        sendFailedResponse(output);
+        break;
+      default:
+        throw new IllegalArgumentException();
+    }
+  }
+
+  public static void sendIdleResponse(DataOutputStream output) throws IOException {
+    output.write(IDLE_RESPONSE);
+  }
+
+  public static void sendTransactionResponse(DataOutputStream output) throws IOException {
+    output.write(TRANSACTION_RESPONSE);
+  }
+
+  public static void sendFailedResponse(DataOutputStream output) throws IOException {
+    output.write(FAILED_RESPONSE);
+  }
 
   private final Status status;
 
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/AbstractQueryProtocolMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/AbstractQueryProtocolMessage.java
index f827ca8c..1b288fb8 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/AbstractQueryProtocolMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/AbstractQueryProtocolMessage.java
@@ -20,6 +20,8 @@ import com.google.cloud.spanner.pgadapter.ConnectionHandler.QueryMode;
 import com.google.cloud.spanner.pgadapter.statements.BackendConnection;
 import com.google.cloud.spanner.pgadapter.statements.ExtendedQueryProtocolHandler;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Abstract base class for extended query protocol messages. These can be buffered in memory locally
@@ -67,4 +69,16 @@ public abstract class AbstractQueryProtocolMessage extends ControlMessage {
     super.handleError(exception);
     this.returnedErrorResponse = true;
   }
+
+  private static final Map<Class<? extends AbstractQueryProtocolMessage>, String>
+      RECEIVED_EVENT_DESCRIPTIONS = new HashMap<>();
+
+  public String receivedEventDescription() {
+    String value = RECEIVED_EVENT_DESCRIPTIONS.get(getClass());
+    if (value == null) {
+      value = "Received message: '" + getIdentifier() + "'";
+      RECEIVED_EVENT_DESCRIPTIONS.put(getClass(), value);
+    }
+    return value;
+  }
 }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BindMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BindMessage.java
index 498b28c2..a1291671 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BindMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BindMessage.java
@@ -14,6 +14,8 @@
 
 package com.google.cloud.spanner.pgadapter.wireprotocol;
 
+import static com.google.cloud.spanner.pgadapter.statements.IntermediatePortalStatement.NO_FORMAT_CODES;
+
 import com.google.api.core.InternalApi;
 import com.google.cloud.spanner.pgadapter.ConnectionHandler;
 import com.google.cloud.spanner.pgadapter.statements.BackendConnection;
@@ -21,10 +23,8 @@ import com.google.cloud.spanner.pgadapter.statements.IntermediatePortalStatement
 import com.google.cloud.spanner.pgadapter.statements.IntermediatePreparedStatement;
 import com.google.cloud.spanner.pgadapter.wireoutput.BindCompleteResponse;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import java.text.MessageFormat;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * Message of type bind (meaning that it is a message called to a prepared statement to complete it
@@ -37,8 +37,8 @@ public class BindMessage extends AbstractQueryProtocolMessage {
 
   private final String portalName;
   private final String statementName;
-  private final List<Short> formatCodes;
-  private final List<Short> resultFormatCodes;
+  private final short[] formatCodes;
+  private final short[] resultFormatCodes;
   private final byte[][] parameters;
   private final IntermediatePortalStatement statement;
 
@@ -72,8 +72,8 @@ public class BindMessage extends AbstractQueryProtocolMessage {
     super(connection, 4, manuallyCreatedToken);
     this.portalName = portalName;
     this.statementName = statementName;
-    this.formatCodes = ImmutableList.of();
-    this.resultFormatCodes = ImmutableList.of();
+    this.formatCodes = NO_FORMAT_CODES;
+    this.resultFormatCodes = NO_FORMAT_CODES;
     this.parameters = Preconditions.checkNotNull(parameters);
     IntermediatePreparedStatement statement = connection.getStatement(statementName);
     this.statement =
@@ -107,7 +107,7 @@ public class BindMessage extends AbstractQueryProtocolMessage {
   public void flush() throws Exception {
     if (isExtendedProtocol()) {
       // The simple query protocol does not expect a BindComplete response.
-      new BindCompleteResponse(this.outputStream).send(false);
+      BindCompleteResponse.send(this.outputStream);
     }
   }
 
@@ -158,11 +158,11 @@ public class BindMessage extends AbstractQueryProtocolMessage {
     return this.parameters;
   }
 
-  public List<Short> getFormatCodes() {
+  public short[] getFormatCodes() {
     return this.formatCodes;
   }
 
-  public List<Short> getResultFormatCodes() {
+  public short[] getResultFormatCodes() {
     return this.resultFormatCodes;
   }
 }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BootstrapMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BootstrapMessage.java
index 68c7376d..85044f2b 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BootstrapMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/BootstrapMessage.java
@@ -22,7 +22,6 @@ import com.google.cloud.spanner.pgadapter.wireoutput.KeyDataResponse;
 import com.google.cloud.spanner.pgadapter.wireoutput.NoticeResponse;
 import com.google.cloud.spanner.pgadapter.wireoutput.ParameterStatusResponse;
 import com.google.cloud.spanner.pgadapter.wireoutput.ReadyResponse;
-import com.google.cloud.spanner.pgadapter.wireoutput.ReadyResponse.Status;
 import java.io.DataOutputStream;
 import java.nio.charset.StandardCharsets;
 import java.time.ZoneId;
@@ -171,6 +170,7 @@ public abstract class BootstrapMessage extends WireMessage {
     for (NoticeResponse noticeResponse : startupNotices) {
       noticeResponse.send(false);
     }
-    new ReadyResponse(output, Status.IDLE).send();
+    ReadyResponse.sendIdleResponse(output);
+    output.flush();
   }
 }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CloseMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CloseMessage.java
index d91094c6..d1c13f70 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CloseMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/CloseMessage.java
@@ -51,7 +51,7 @@ public class CloseMessage extends ControlMessage {
     this.statement = statement;
   }
 
-  /** Close the statement server-side and clean up by deleting their metdata locally. */
+  /** Close the statement server-side and clean up by deleting their metadata locally. */
   @Override
   protected void sendPayload() throws Exception {
     if (this.statement != null) {
@@ -62,7 +62,8 @@ public class CloseMessage extends ControlMessage {
         this.connection.closeStatement(this.name);
       }
     }
-    new CloseCompleteResponse(this.outputStream).send();
+    CloseCompleteResponse.send(this.outputStream);
+    this.outputStream.flush();
   }
 
   @Override
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java
index 1cfc71f0..a3a40fcc 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ControlMessage.java
@@ -15,6 +15,7 @@
 package com.google.cloud.spanner.pgadapter.wireprotocol;
 
 import static com.google.cloud.spanner.pgadapter.statements.BackendConnection.DB_STATEMENT;
+import static com.google.cloud.spanner.pgadapter.statements.IntermediatePortalStatement.NO_FORMAT_CODES;
 
 import com.google.api.core.InternalApi;
 import com.google.api.gax.grpc.GrpcCallContext;
@@ -207,11 +208,14 @@ public abstract class ControlMessage extends WireMessage {
    * @return A list of format codes.
    * @throws Exception If reading fails in any way.
    */
-  protected static List<Short> getFormatCodes(DataInputStream input) throws Exception {
-    List<Short> formatCodes = new ArrayList<>();
+  protected static short[] getFormatCodes(DataInputStream input) throws Exception {
     short numberOfFormatCodes = input.readShort();
+    if (numberOfFormatCodes == 0) {
+      return NO_FORMAT_CODES;
+    }
+    short[] formatCodes = new short[numberOfFormatCodes];
     for (int i = 0; i < numberOfFormatCodes; i++) {
-      formatCodes.add(input.readShort());
+      formatCodes[i] = input.readShort();
     }
     return formatCodes;
   }
@@ -266,11 +270,11 @@ public abstract class ControlMessage extends WireMessage {
       switch (statement.getStatementType()) {
         case DDL:
         case UNKNOWN:
-          new CommandCompleteResponse(this.outputStream, command).send(false);
+          CommandCompleteResponse.send(this.outputStream, command);
           break;
         case CLIENT_SIDE:
           if (statement.getStatementResult().getResultType() != ResultType.RESULT_SET) {
-            new CommandCompleteResponse(this.outputStream, command).send(false);
+            CommandCompleteResponse.send(this.outputStream, command);
             break;
           }
           // fallthrough to QUERY
@@ -280,13 +284,12 @@ public abstract class ControlMessage extends WireMessage {
             SendResultSetState state = sendResultSet(statement, mode, maxRows);
             statement.setHasMoreData(state.hasMoreRows());
             if (state.hasMoreRows() && mode == QueryMode.EXTENDED) {
-              new PortalSuspendedResponse(this.outputStream).send(false);
+              PortalSuspendedResponse.send(this.outputStream);
             } else {
               if (!state.hasMoreRows() && mode == QueryMode.EXTENDED) {
                 statement.close();
               }
-              new CommandCompleteResponse(this.outputStream, state.getCommandAndNumRows())
-                  .send(false);
+              CommandCompleteResponse.send(this.outputStream, state.getCommandAndNumRows());
             }
           } else {
             // For an INSERT command, the tag is INSERT oid rows, where rows is the number of rows
@@ -294,7 +297,7 @@ public abstract class ControlMessage extends WireMessage {
             // target table had OIDs, but OIDs system columns are not supported anymore; therefore
             // oid is always 0.
             command += ("INSERT".equals(command) ? " 0 " : " ") + statement.getUpdateCount();
-            new CommandCompleteResponse(this.outputStream, command).send(false);
+            CommandCompleteResponse.send(this.outputStream, command);
           }
           break;
         default:
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/FunctionCallMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/FunctionCallMessage.java
index cc05096d..bbdee289 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/FunctionCallMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/FunctionCallMessage.java
@@ -17,7 +17,6 @@ package com.google.cloud.spanner.pgadapter.wireprotocol;
 import com.google.api.core.InternalApi;
 import com.google.cloud.spanner.pgadapter.ConnectionHandler;
 import java.text.MessageFormat;
-import java.util.List;
 
 /**
  * Normally used to send a function call the back-end. Spanner does not currently support this, so
@@ -31,7 +30,7 @@ public class FunctionCallMessage extends ControlMessage {
   protected static final char IDENTIFIER = 'F';
 
   private final int functionID;
-  private final List<Short> argumentFormatCodes;
+  private final short[] argumentFormatCodes;
   private final byte[][] arguments;
   private final Short resultFormatCode;
 
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ParseMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ParseMessage.java
index fcdc30a9..762e4251 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ParseMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/ParseMessage.java
@@ -297,7 +297,7 @@ public class ParseMessage extends AbstractQueryProtocolMessage {
       handleError(statement.getException());
     } else if (isExtendedProtocol()) {
       // The simple query protocol does not need the ParseComplete response.
-      new ParseCompleteResponse(this.outputStream).send(false);
+      ParseCompleteResponse.send(this.outputStream);
     }
   }
 
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/WireMessage.java b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/WireMessage.java
index c6b83a25..7fb6060e 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/WireMessage.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/wireprotocol/WireMessage.java
@@ -23,7 +23,6 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.text.MessageFormat;
-import java.util.Arrays;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -149,30 +148,47 @@ public abstract class WireMessage {
   }
 
   /**
-   * Reads a null-terminated string from a {@link DataInputStream}. Note that though existing
-   * solutions for this exist, they are either not keyed exactly for our use case, or would lead to
-   * a more combersome addition to this codebase. Also note the 128 byte length is chosen from
-   * profiling and determining that it exceeds the 90th percentile size for inbound messages.
+   * The max number of characters to read when scanning for a null-terminator. Null-terminated
+   * strings are used in the PG wire-protocol for names of prepared statements and portals, but also
+   * for the SQL string of a statement. We therefore need to read potentially very long strings.
+   */
+  private static final int MARK_READ_LIMIT = 100_000_000;
+
+  /**
+   * Reads a null-terminated string from a {@link DataInputStream}.
    *
    * @return the string.
    * @throws IOException if an error occurs while reading from the stream, or if no null-terminator
    *     is found before the end of the stream.
    */
   public String readString() throws IOException {
-    byte[] buffer = new byte[128];
+    this.inputStream.mark(MARK_READ_LIMIT);
     int index = 0;
-    while (true) {
+    while (index < MARK_READ_LIMIT) {
       byte b = this.inputStream.readByte();
       if (b == 0) {
         break;
       }
-      buffer[index] = b;
       index++;
-      if (index == buffer.length) {
-        buffer = Arrays.copyOf(buffer, buffer.length * 2);
+      if (index == MARK_READ_LIMIT) {
+        throw new IOException("No null terminator found");
       }
     }
-    return new String(buffer, 0, index, StandardCharsets.UTF_8);
+    // Reset the stream to the mark and read the name (if any).
+    this.inputStream.reset();
+    if (index == 0) {
+      // No name, but we still need to skip the null-terminator.
+      //noinspection StatementWithEmptyBody
+      while (this.inputStream.skip(1) < 1) {}
+      return "";
+    }
+
+    byte[] result = new byte[index];
+    this.inputStream.readFully(result);
+    // Skip the null-terminator.
+    //noinspection StatementWithEmptyBody
+    while (this.inputStream.skip(1) < 1) {}
+    return new String(result, StandardCharsets.UTF_8);
   }
 
   /**


Test patch:
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeallocateStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeallocateStatement.java
index 43041d18..7cfc30c6 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeallocateStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/DeallocateStatement.java
@@ -26,9 +26,7 @@ import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
 import com.google.cloud.spanner.pgadapter.statements.BackendConnection.NoResult;
 import com.google.cloud.spanner.pgadapter.statements.SimpleParser.TableOrIndexName;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
-import java.util.List;
 import java.util.concurrent.Future;
 
 public class DeallocateStatement extends IntermediatePortalStatement {
@@ -58,8 +56,8 @@ public class DeallocateStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.deallocateStatement = parse(originalStatement.getSql());
   }
 
@@ -98,10 +96,7 @@ public class DeallocateStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // EXECUTE does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExecuteStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExecuteStatement.java
index e3dcdf01..479cb25a 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExecuteStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/ExecuteStatement.java
@@ -31,7 +31,6 @@ import com.google.cloud.spanner.pgadapter.wireprotocol.ControlMessage.PreparedTy
 import com.google.cloud.spanner.pgadapter.wireprotocol.DescribeMessage;
 import com.google.cloud.spanner.pgadapter.wireprotocol.ExecuteMessage;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
@@ -69,8 +68,8 @@ public class ExecuteStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.executeStatement = parse(originalStatement.getSql());
   }
 
@@ -119,10 +118,7 @@ public class ExecuteStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // EXECUTE does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/TruncateStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/TruncateStatement.java
index 5dec5b54..75589035 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/TruncateStatement.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/TruncateStatement.java
@@ -73,8 +73,8 @@ public class TruncateStatement extends IntermediatePortalStatement {
             parsedStatement,
             originalStatement),
         NO_PARAMS,
-        ImmutableList.of(),
-        ImmutableList.of());
+        NO_FORMAT_CODES,
+        NO_FORMAT_CODES);
     this.truncateStatement = parse(originalStatement.getSql());
   }
 
@@ -111,10 +111,7 @@ public class TruncateStatement extends IntermediatePortalStatement {
 
   @Override
   public IntermediatePortalStatement createPortal(
-      String name,
-      byte[][] parameters,
-      List<Short> parameterFormatCodes,
-      List<Short> resultFormatCodes) {
+      String name, byte[][] parameters, short[] parameterFormatCodes, short[] resultFormatCodes) {
     // TRUNCATE does not support binding any parameters, so we just return the same statement.
     return this;
   }
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/Psycopg3MockServerTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/Psycopg3MockServerTest.java
index 0158a11f..159e667e 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/Psycopg3MockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/python/psycopg3/Psycopg3MockServerTest.java
@@ -424,8 +424,8 @@ public class Psycopg3MockServerTest extends AbstractMockServerTest {
             .collect(Collectors.toList());
     assertEquals(1, bindMessages.size());
     BindMessage bindMessage = bindMessages.get(0);
-    assertEquals(1, bindMessage.getResultFormatCodes().size());
-    assertEquals(format.getCode(), bindMessage.getResultFormatCodes().get(0).shortValue());
+    assertEquals(1, bindMessage.getResultFormatCodes().length);
+    assertEquals(format.getCode(), bindMessage.getResultFormatCodes()[0]);
   }
 
   @Test
@@ -622,10 +622,8 @@ public class Psycopg3MockServerTest extends AbstractMockServerTest {
             .collect(Collectors.toList());
     assertEquals(1, bindMessages.size());
     BindMessage bindMessage = bindMessages.get(0);
-    assertEquals(11, bindMessage.getFormatCodes().size());
-    assertArrayEquals(
-        new Short[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
-        bindMessage.getFormatCodes().toArray(new Short[0]));
+    assertEquals(11, bindMessage.getFormatCodes().length);
+    assertArrayEquals(new short[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, bindMessage.getFormatCodes());
   }
 
   @Test
@@ -690,10 +688,8 @@ public class Psycopg3MockServerTest extends AbstractMockServerTest {
             .collect(Collectors.toList());
     assertEquals(1, bindMessages.size());
     BindMessage bindMessage = bindMessages.get(0);
-    assertEquals(11, bindMessage.getFormatCodes().size());
-    assertArrayEquals(
-        new Short[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-        bindMessage.getFormatCodes().toArray(new Short[0]));
+    assertEquals(11, bindMessage.getFormatCodes().length);
+    assertArrayEquals(new short[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, bindMessage.getFormatCodes());
   }
 
   @Test
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/statements/StatementTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/statements/StatementTest.java
index a5513ef5..bae11d0d 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/statements/StatementTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/statements/StatementTest.java
@@ -14,6 +14,7 @@
 
 package com.google.cloud.spanner.pgadapter.statements;
 
+import static com.google.cloud.spanner.pgadapter.statements.IntermediatePortalStatement.NO_FORMAT_CODES;
 import static com.google.cloud.spanner.pgadapter.statements.IntermediatePortalStatement.NO_PARAMS;
 import static com.google.cloud.spanner.pgadapter.statements.IntermediatePreparedStatement.NO_PARAMETER_TYPES;
 import static com.google.cloud.spanner.pgadapter.utils.ClientAutoDetector.EMPTY_LOCAL_STATEMENTS;
@@ -69,7 +70,6 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
@@ -126,8 +126,8 @@ public class StatementTest {
             new IntermediatePreparedStatement(
                 connectionHandler, options, "", NO_PARAMETER_TYPES, parse(sql), Statement.of(sql)),
             NO_PARAMS,
-            ImmutableList.of(),
-            ImmutableList.of());
+            NO_FORMAT_CODES,
+            NO_FORMAT_CODES);
 
     assertFalse(intermediateStatement.isExecuted());
     assertEquals("SELECT", intermediateStatement.getCommand());
@@ -155,8 +155,8 @@ public class StatementTest {
             new IntermediatePreparedStatement(
                 connectionHandler, options, "", NO_PARAMETER_TYPES, parse(sql), Statement.of(sql)),
             NO_PARAMS,
-            ImmutableList.of(),
-            ImmutableList.of());
+            NO_FORMAT_CODES,
+            NO_FORMAT_CODES);
 
     assertFalse(intermediateStatement.isExecuted());
     assertEquals("UPDATE", intermediateStatement.getCommand());
@@ -190,8 +190,8 @@ public class StatementTest {
             new IntermediatePreparedStatement(
                 connectionHandler, options, "", NO_PARAMETER_TYPES, parse(sql), Statement.of(sql)),
             NO_PARAMS,
-            ImmutableList.of(),
-            ImmutableList.of());
+            NO_FORMAT_CODES,
+            NO_FORMAT_CODES);
     BackendConnection backendConnection =
         new BackendConnection(
             NOOP_OTEL,
@@ -237,8 +237,8 @@ public class StatementTest {
             new IntermediatePreparedStatement(
                 connectionHandler, options, "", NO_PARAMETER_TYPES, parse(sql), Statement.of(sql)),
             NO_PARAMS,
-            ImmutableList.of(),
-            ImmutableList.of());
+            NO_FORMAT_CODES,
+            NO_FORMAT_CODES);
 
     assertFalse(intermediateStatement.isExecuted());
     assertEquals("CREATE", intermediateStatement.getCommand());
@@ -285,8 +285,8 @@ public class StatementTest {
             new IntermediatePreparedStatement(
                 connectionHandler, options, "", NO_PARAMETER_TYPES, parse(sql), Statement.of(sql)),
             NO_PARAMS,
-            ImmutableList.of(),
-            ImmutableList.of());
+            NO_FORMAT_CODES,
+            NO_FORMAT_CODES);
     BackendConnection backendConnection =
         new BackendConnection(
             NOOP_OTEL,
@@ -358,7 +358,7 @@ public class StatementTest {
     byte[][] parameters = {"userName".getBytes(), "20".getBytes(), "30".getBytes()};
     IntermediatePortalStatement intermediatePortalStatement =
         intermediateStatement.createPortal(
-            "", parameters, Arrays.asList((short) 0, (short) 0, (short) 0), new ArrayList<>());
+            "", parameters, new short[] {(short) 0, (short) 0, (short) 0}, NO_FORMAT_CODES);
     intermediatePortalStatement.bind(Statement.of(sqlStatement));
     intermediatePortalStatement.executeAsync(backendConnection);
     backendConnection.flush();
@@ -397,7 +397,7 @@ public class StatementTest {
     byte[][] parameters = {"{}".getBytes()};
 
     IntermediatePortalStatement portalStatement =
-        intermediateStatement.createPortal("", parameters, new ArrayList<>(), new ArrayList<>());
+        intermediateStatement.createPortal("", parameters, NO_FORMAT_CODES, NO_FORMAT_CODES);
     Statement boundStatement = portalStatement.bind(Statement.of(sqlStatement));
     assertEquals(
         Value.untyped(com.google.protobuf.Value.newBuilder().setStringValue("{}").build()),
@@ -441,8 +441,8 @@ public class StatementTest {
                 parse(sqlStatement),
                 Statement.of(sqlStatement)),
             NO_PARAMS,
-            ImmutableList.of(),
-            ImmutableList.of());
+            NO_FORMAT_CODES,
+            NO_FORMAT_CODES);
     BackendConnection backendConnection =
         new BackendConnection(
             NOOP_OTEL,
@@ -592,8 +592,8 @@ public class StatementTest {
             new IntermediatePreparedStatement(
                 connectionHandler, options, "", NO_PARAMETER_TYPES, parse(sql), Statement.of(sql)),
             NO_PARAMS,
-            ImmutableList.of(),
-            ImmutableList.of());
+            NO_FORMAT_CODES,
+            NO_FORMAT_CODES);
     BackendConnection backendConnection =
         new BackendConnection(
             NOOP_OTEL,
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/ProtocolTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/ProtocolTest.java
index 7a7f4505..273145ad 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/ProtocolTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/wireprotocol/ProtocolTest.java
@@ -14,6 +14,7 @@
 
 package com.google.cloud.spanner.pgadapter.wireprotocol;
 
+import static com.google.cloud.spanner.pgadapter.statements.IntermediatePortalStatement.NO_FORMAT_CODES;
 import static com.google.cloud.spanner.pgadapter.wireprotocol.ControlMessage.MAX_INVALID_MESSAGE_COUNT;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -72,11 +73,7 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import org.junit.Rule;
@@ -632,7 +629,7 @@ public class ProtocolTest {
         .thenReturn(intermediatePreparedStatement);
 
     byte[][] expectedParameters = {parameter};
-    List<Short> expectedFormatCodes = new ArrayList<>();
+    short[] expectedFormatCodes = NO_FORMAT_CODES;
     String expectedPortalName = "some portal";
     String expectedStatementName = "some statement";
 
@@ -651,8 +648,8 @@ public class ProtocolTest {
     assertEquals(expectedPortalName, ((BindMessage) message).getPortalName());
     assertEquals(expectedStatementName, ((BindMessage) message).getStatementName());
     assertArrayEquals(expectedParameters, ((BindMessage) message).getParameters());
-    assertEquals(expectedFormatCodes, ((BindMessage) message).getFormatCodes());
-    assertEquals(expectedFormatCodes, ((BindMessage) message).getResultFormatCodes());
+    assertArrayEquals(expectedFormatCodes, ((BindMessage) message).getFormatCodes());
+    assertArrayEquals(expectedFormatCodes, ((BindMessage) message).getResultFormatCodes());
     assertEquals("select * from foo", ((BindMessage) message).getSql());
     assertTrue(((BindMessage) message).hasParameterValues());
 
@@ -716,8 +713,8 @@ public class ProtocolTest {
             resultCodes);
 
     byte[][] expectedParameters = {firstParameter, secondParameter};
-    List<Short> expectedFormatCodes = Arrays.asList((short) 0, (short) 1);
-    List<Short> expectedResultFormatCodes = Collections.singletonList((short) 1);
+    short[] expectedFormatCodes = new short[] {(short) 0, (short) 1};
+    short[] expectedResultFormatCodes = new short[] {(short) 1};
     String expectedPortalName = "some portal";
     String expectedStatementName = "some statement";
 
@@ -737,8 +734,8 @@ public class ProtocolTest {
     assertEquals(expectedPortalName, ((BindMessage) message).getPortalName());
     assertEquals(expectedStatementName, ((BindMessage) message).getStatementName());
     assertArrayEquals(expectedParameters, ((BindMessage) message).getParameters());
-    assertEquals(expectedFormatCodes, ((BindMessage) message).getFormatCodes());
-    assertEquals(expectedResultFormatCodes, ((BindMessage) message).getResultFormatCodes());
+    assertArrayEquals(expectedFormatCodes, ((BindMessage) message).getFormatCodes());
+    assertArrayEquals(expectedResultFormatCodes, ((BindMessage) message).getResultFormatCodes());
   }
 
   @Test
@@ -791,8 +788,8 @@ public class ProtocolTest {
             resultCodes);
 
     byte[][] expectedParameters = {firstParameter, secondParameter};
-    List<Short> expectedFormatCodes = Collections.singletonList((short) 1);
-    List<Short> expectedResultFormatCodes = Collections.singletonList((short) 1);
+    short[] expectedFormatCodes = new short[] {(short) 1};
+    short[] expectedResultFormatCodes = new short[] {(short) 1};
     String expectedPortalName = "some portal";
     String expectedStatementName = "some statement";
 
@@ -812,8 +809,8 @@ public class ProtocolTest {
     assertEquals(expectedPortalName, ((BindMessage) message).getPortalName());
     assertEquals(expectedStatementName, ((BindMessage) message).getStatementName());
     assertArrayEquals(expectedParameters, ((BindMessage) message).getParameters());
-    assertEquals(expectedFormatCodes, ((BindMessage) message).getFormatCodes());
-    assertEquals(expectedResultFormatCodes, ((BindMessage) message).getResultFormatCodes());
+    assertArrayEquals(expectedFormatCodes, ((BindMessage) message).getFormatCodes());
+    assertArrayEquals(expectedResultFormatCodes, ((BindMessage) message).getResultFormatCodes());
   }
 
   @Test