Repository: GoogleCloudPlatform/pgadapter
Pull request #3103 (2025-03-27 15:03:42)

perf: use a stream mark to read null-terminated string
Prepared statements and portals can have names in the PG wire-protocol. These are sent as null-terminated strings. Read these strings by setting a mark in the stream and then scan for the null-terminator, instead of always creating a 128-byte buffer and read into that buffer. Using a stream mark will instead re-use the existing buffer in the existing input stream, and so reduce the amount of memory that is being reserved and released for each statement that is received.


Patch:
diff --git a/benchmarks/latency-comparison/golang/go.mod b/benchmarks/latency-comparison/golang/go.mod
index 65c09f83..f17ce672 100644
--- a/benchmarks/latency-comparison/golang/go.mod
+++ b/benchmarks/latency-comparison/golang/go.mod
@@ -6,11 +6,11 @@ toolchain go1.24.1
 
 require (
 	cloud.google.com/go/spanner v1.78.0
-	github.com/docker/docker v28.0.2+incompatible
+	github.com/docker/docker v28.0.4+incompatible
 	github.com/jackc/pgx/v4 v4.18.3
 	github.com/jackc/pgx/v5 v5.7.4
 	github.com/montanaflynn/stats v0.7.0
-	github.com/testcontainers/testcontainers-go v0.35.0
+	github.com/testcontainers/testcontainers-go v0.36.0
 	golang.org/x/oauth2 v0.28.0
 	google.golang.org/api v0.228.0
 )
@@ -22,7 +22,7 @@ require (
 	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
 	cloud.google.com/go/compute/metadata v0.6.0 // indirect
 	cloud.google.com/go/monitoring v1.24.1 // indirect
-	dario.cat/mergo v1.0.0 // indirect
+	dario.cat/mergo v1.0.1 // indirect
 	github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect
@@ -38,6 +38,7 @@ require (
 	github.com/distribution/reference v0.6.0 // indirect
 	github.com/docker/go-connections v0.5.0 // indirect
 	github.com/docker/go-units v0.5.0 // indirect
+	github.com/ebitengine/purego v0.8.2 // indirect
 	github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
 	github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -59,7 +60,7 @@ require (
 	github.com/jackc/pgtype v1.14.0 // indirect
 	github.com/klauspost/compress v1.17.4 // indirect
 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
-	github.com/magiconair/properties v1.8.7 // indirect
+	github.com/magiconair/properties v1.8.9 // indirect
 	github.com/moby/docker-image-spec v1.3.1 // indirect
 	github.com/moby/patternmatcher v0.6.0 // indirect
 	github.com/moby/sys/sequential v0.5.0 // indirect
@@ -68,18 +69,19 @@ require (
 	github.com/moby/term v0.5.0 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
-	github.com/opencontainers/image-spec v1.1.0 // indirect
+	github.com/opencontainers/image-spec v1.1.1 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 	github.com/shirou/gopsutil/v3 v3.23.12 // indirect
+	github.com/shirou/gopsutil/v4 v4.25.1 // indirect
 	github.com/shoenig/go-m1cpu v0.1.6 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/stretchr/testify v1.10.0 // indirect
 	github.com/tklauser/go-sysconf v0.3.12 // indirect
 	github.com/tklauser/numcpus v0.6.1 // indirect
-	github.com/yusufpapurcu/wmi v1.2.3 // indirect
+	github.com/yusufpapurcu/wmi v1.2.4 // indirect
 	go.opencensus.io v0.24.0 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 	go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect
diff --git a/benchmarks/latency-comparison/golang/go.sum b/benchmarks/latency-comparison/golang/go.sum
index 60f01685..82a3cb40 100644
--- a/benchmarks/latency-comparison/golang/go.sum
+++ b/benchmarks/latency-comparison/golang/go.sum
@@ -622,6 +622,8 @@ cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT
 cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
 dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
 dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
 git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
@@ -707,12 +709,16 @@ github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjr
 github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8=
 github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
+github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
+github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -986,6 +992,8 @@ github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuz
 github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
+github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -1015,6 +1023,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
 github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
 github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
 github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
 github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
@@ -1049,6 +1059,8 @@ github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZ
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
 github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
+github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
+github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
 github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
 github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
 github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -1086,6 +1098,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
 github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
+github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00=
+github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0=
 github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
 github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
@@ -1099,6 +1113,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
 github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
 github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
 github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
diff --git a/benchmarks/latency-comparison/java/pom.xml b/benchmarks/latency-comparison/java/pom.xml
index 77e74b5e..b2893fb3 100644
--- a/benchmarks/latency-comparison/java/pom.xml
+++ b/benchmarks/latency-comparison/java/pom.xml
@@ -43,7 +43,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>libraries-bom</artifactId>
-        <version>26.57.0</version>
+        <version>26.58.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
diff --git a/benchmarks/tpcc/pom.xml b/benchmarks/tpcc/pom.xml
index b9374db9..5ebeb7c0 100644
--- a/benchmarks/tpcc/pom.xml
+++ b/benchmarks/tpcc/pom.xml
@@ -21,7 +21,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>libraries-bom</artifactId>
-        <version>26.57.0</version>
+        <version>26.58.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
diff --git a/pom.xml b/pom.xml
index 8ddc2aa6..9ca0d1bf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,7 +85,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>libraries-bom</artifactId>
-        <version>26.57.0</version>
+        <version>26.58.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
diff --git a/samples/java/hibernate/pom.xml b/samples/java/hibernate/pom.xml
index b3d4af5c..83d79dbc 100644
--- a/samples/java/hibernate/pom.xml
+++ b/samples/java/hibernate/pom.xml
@@ -57,7 +57,7 @@
     <dependency>
       <groupId>io.hypersistence</groupId>
       <artifactId>hypersistence-utils-hibernate-63</artifactId>
-      <version>3.9.5</version>
+      <version>3.9.8</version>
     </dependency>
     <!-- Add testcontainers, so we can run a Docker container with PGAdapter+Spanner Emulator -->
     <dependency>
diff --git a/samples/java/spring-data-jpa/pom.xml b/samples/java/spring-data-jpa/pom.xml
index 0e055e40..355a3364 100644
--- a/samples/java/spring-data-jpa/pom.xml
+++ b/samples/java/spring-data-jpa/pom.xml
@@ -67,7 +67,7 @@
     <dependency>
       <groupId>io.hypersistence</groupId>
       <artifactId>hypersistence-utils-hibernate-63</artifactId>
-      <version>3.9.5</version>
+      <version>3.9.8</version>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
diff --git a/samples/snippets/golang-snippets/go.mod b/samples/snippets/golang-snippets/go.mod
index 891e95b4..66b1edad 100644
--- a/samples/snippets/golang-snippets/go.mod
+++ b/samples/snippets/golang-snippets/go.mod
@@ -1,17 +1,17 @@
 module golang-snippets
 
-go 1.22.0
+go 1.23.0
 
 toolchain go1.24.1
 
 require (
-	github.com/docker/docker v28.0.2+incompatible
+	github.com/docker/docker v28.0.4+incompatible
 	github.com/jackc/pgx/v5 v5.7.4
-	github.com/testcontainers/testcontainers-go v0.35.0
+	github.com/testcontainers/testcontainers-go v0.36.0
 )
 
 require (
-	dario.cat/mergo v1.0.0 // indirect
+	dario.cat/mergo v1.0.1 // indirect
 	github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -23,6 +23,7 @@ require (
 	github.com/distribution/reference v0.6.0 // indirect
 	github.com/docker/go-connections v0.5.0 // indirect
 	github.com/docker/go-units v0.5.0 // indirect
+	github.com/ebitengine/purego v0.8.2 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
@@ -33,7 +34,7 @@ require (
 	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
 	github.com/klauspost/compress v1.17.4 // indirect
 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
-	github.com/magiconair/properties v1.8.7 // indirect
+	github.com/magiconair/properties v1.8.9 // indirect
 	github.com/moby/docker-image-spec v1.3.1 // indirect
 	github.com/moby/patternmatcher v0.6.0 // indirect
 	github.com/moby/sys/sequential v0.5.0 // indirect
@@ -42,17 +43,18 @@ require (
 	github.com/moby/term v0.5.0 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
-	github.com/opencontainers/image-spec v1.1.0 // indirect
+	github.com/opencontainers/image-spec v1.1.1 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 	github.com/shirou/gopsutil/v3 v3.23.12 // indirect
+	github.com/shirou/gopsutil/v4 v4.25.1 // indirect
 	github.com/shoenig/go-m1cpu v0.1.6 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/stretchr/testify v1.10.0 // indirect
 	github.com/tklauser/go-sysconf v0.3.12 // indirect
 	github.com/tklauser/numcpus v0.6.1 // indirect
-	github.com/yusufpapurcu/wmi v1.2.3 // indirect
+	github.com/yusufpapurcu/wmi v1.2.4 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
 	go.opentelemetry.io/otel v1.35.0 // indirect
@@ -62,7 +64,7 @@ require (
 	go.opentelemetry.io/otel/trace v1.35.0 // indirect
 	golang.org/x/crypto v0.31.0 // indirect
 	golang.org/x/net v0.33.0 // indirect
-	golang.org/x/sys v0.28.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
 	golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
diff --git a/samples/snippets/golang-snippets/go.sum b/samples/snippets/golang-snippets/go.sum
index 270aff99..8fa46593 100644
--- a/samples/snippets/golang-snippets/go.sum
+++ b/samples/snippets/golang-snippets/go.sum
@@ -1,5 +1,7 @@
 dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
 dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
@@ -23,10 +25,14 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
 github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8=
 github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
+github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
+github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -69,6 +75,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
+github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
 github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
 github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
@@ -87,6 +95,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
 github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -97,6 +107,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
 github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
+github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
+github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
 github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
 github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
 github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -117,6 +129,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
 github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
+github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00=
+github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0=
 github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
 github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
@@ -125,6 +139,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
 github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
@@ -173,6 +189,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
 golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
 golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/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/parsers/LongParser.java b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/LongParser.java
index 45bcfe02..0d890c05 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/LongParser.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/LongParser.java
@@ -16,14 +16,17 @@ package com.google.cloud.spanner.pgadapter.parsers;
 
 import com.google.api.core.InternalApi;
 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 com.google.cloud.spanner.pgadapter.session.SessionState;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.nio.charset.StandardCharsets;
 import javax.annotation.Nonnull;
 import org.postgresql.util.ByteConverter;
 
@@ -95,18 +98,47 @@ public class LongParser extends Parser<Long> {
     return result;
   }
 
-  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 Long.toString(resultSet.getLong(position)).getBytes(StandardCharsets.UTF_8);
+        StringParser.writeToPG(sessionState, outputStream, getLongAsString(resultSet, position));
+        break;
       case POSTGRESQL_BINARY:
-        return convertToPG(resultSet.getLong(position));
+        byte[] value = convertToPG(resultSet.getLong(position));
+        outputStream.writeInt(value.length);
+        outputStream.write(value);
+        break;
       default:
         throw new IllegalArgumentException("unknown data format: " + format);
     }
   }
 
+  static String getLongAsString(ResultSet resultSet, int column) {
+    if (resultSet instanceof ProtobufResultSet
+        && ((ProtobufResultSet) resultSet).canGetProtobufValue(column)) {
+      return ((ProtobufResultSet) resultSet).getProtobufValue(column).getStringValue();
+    }
+    return Long.toString(resultSet.getLong(column));
+  }
+
   @Override
   public void bind(Statement.Builder statementBuilder, String name) {
     statementBuilder.bind(name).to(this.item);
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/StringParser.java b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/StringParser.java
index eb918ccd..bdd729c8 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/parsers/StringParser.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/parsers/StringParser.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.SpannerExceptionFactory;
 import com.google.cloud.spanner.Statement;
@@ -32,14 +33,14 @@ public class StringParser extends Parser<String> {
   private static final byte[] HEADER = new byte[0];
 
   StringParser(ResultSet item, int position) {
-    this.item = item.getString(position);
+    this.item = getString(item, position);
   }
 
   StringParser(Object item) {
     this.item = (String) item;
   }
 
-  StringParser(byte[] item, FormatCode formatCode) {
+  StringParser(byte[] item, FormatCode ignore) {
     if (item != null) {
       this.item = toString(item);
     }
@@ -60,12 +61,24 @@ public class StringParser extends Parser<String> {
     return this.item == null ? null : this.item.getBytes(StandardCharsets.UTF_8);
   }
 
+  /** Get the string from the result as efficiently as possible. */
+  static String getString(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();
+    } else {
+      return resultSet.getString(column);
+    }
+  }
+
   public static byte[] convertToPG(
       SessionState sessionState,
       DataOutputStream dataOutputStream,
       ResultSet resultSet,
       int position) {
-    writeToPG(sessionState, dataOutputStream, resultSet.getString(position));
+    writeToPG(sessionState, dataOutputStream, getString(resultSet, position));
     return null;
   }
 
@@ -77,13 +90,25 @@ public class StringParser extends Parser<String> {
   static void writeToPG(
       SessionState sessionState, DataOutputStream dataOutputStream, String value, byte[] header) {
     int bufferSize = sessionState.getStringConversionBufferSize();
-    int length = value.length();
+    // Skip getting the length if we don't need it.
+    int length = bufferSize <= 0 ? 0 : value.length();
     try {
       if (bufferSize <= 0 || length < bufferSize) {
-        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
-        dataOutputStream.writeInt(bytes.length + header.length);
-        dataOutputStream.write(header);
-        dataOutputStream.write(bytes);
+        // Just use the writeUTF method of DataOutputStream if there is no header that needs to be
+        // written.
+        if (header.length == 0) {
+          // PG expects the length be 4 bytes.
+          // writeUTF writes the length as 2 bytes.
+          // So in order to get the correct length written to the stream, we first write 2 bytes
+          // containing all zeros.
+          dataOutputStream.writeShort(0);
+          dataOutputStream.writeUTF(value);
+        } else {
+          byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
+          dataOutputStream.writeInt(bytes.length + header.length);
+          dataOutputStream.write(header);
+          dataOutputStream.write(bytes);
+        }
       } else {
         try (OutputStreamWriter writer =
             new OutputStreamWriter(dataOutputStream, StandardCharsets.UTF_8)) {
@@ -101,12 +126,6 @@ public class StringParser extends Parser<String> {
     }
   }
 
-  public static byte[] binaryParse(ResultSet resultSet, int position) {
-    return resultSet.isNull(position)
-        ? null
-        : resultSet.getString(position).getBytes(StandardCharsets.UTF_8);
-  }
-
   @Override
   public void bind(Statement.Builder statementBuilder, String name) {
     statementBuilder.bind(name).to(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..61e385e9 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
@@ -218,10 +218,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..e27aa4b7 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
@@ -27,7 +27,6 @@ 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;
@@ -89,10 +88,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);
   }
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 82e7454e..204ef2d6 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
@@ -120,6 +120,8 @@ public class Converter implements AutoCloseable {
                 : fixedFormat;
         byte[] column =
             Converter.convertToPG(outputStream, this.resultSet, column_index, format, sessionState);
+        // TODO: Remove this if statement and write statements once all data types are written
+        // directly to the output stream.
         if (column != null) {
           outputStream.writeInt(column.length);
           outputStream.write(column);
@@ -137,22 +139,24 @@ public class Converter implements AutoCloseable {
    * Return the data of the specified column of the {@link ResultSet} as a byte array. The column
    * may not contain a null value.
    *
+   * @param outputStream The output stream to write value to if the converter supports that.
    * @param result The {@link ResultSet} to read the data from.
    * @param position The column index.
    * @param format The {@link DataFormat} format to use to encode the data.
-   * @return a byte array containing the data in the specified format.
+   * @param sessionState The {@link SessionState} that should be used to determine how to format the
+   *     data (e.g. timezone).
+   * @return a byte array containing the data in the specified format or null if the data was
+   *     written directly to the output stream.
    */
-  public static byte[] convertToPG(
-      ResultSet result, int position, DataFormat format, SessionState sessionState) {
-    return convertToPG(null, result, position, format, sessionState);
-  }
-
+  // TODO: Rename this method to writeToPG and change return type to void once all data types write
+  // directly to the output stream.
   public static byte[] convertToPG(
       DataOutputStream outputStream,
       ResultSet result,
       int position,
       DataFormat format,
-      SessionState sessionState) {
+      SessionState sessionState)
+      throws IOException {
     Preconditions.checkArgument(!result.isNull(position), "Column may not contain a null value");
     Type type = result.getColumnType(position);
     switch (type.getCode()) {
@@ -168,7 +172,7 @@ public class Converter implements AutoCloseable {
         return DoubleParser.convertToPG(result, position, format);
       case INT64:
       case PG_OID:
-        return LongParser.convertToPG(result, position, format);
+        return LongParser.convertToPG(sessionState, outputStream, result, position, format);
       case PG_NUMERIC:
         return NumericParser.convertToPG(result, position, format);
       case STRING:
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/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);
   }
 
   /**
diff --git a/wrappers/golang/go.mod b/wrappers/golang/go.mod
index ab309325..ae7feb36 100644
--- a/wrappers/golang/go.mod
+++ b/wrappers/golang/go.mod
@@ -5,14 +5,14 @@ go 1.23.0
 toolchain go1.24.1
 
 require (
-	github.com/docker/docker v28.0.2+incompatible
-	github.com/testcontainers/testcontainers-go v0.35.0
+	github.com/docker/docker v28.0.4+incompatible
+	github.com/testcontainers/testcontainers-go v0.36.0
 	golang.org/x/oauth2 v0.28.0
 )
 
 require (
 	cloud.google.com/go/compute/metadata v0.3.0 // indirect
-	dario.cat/mergo v1.0.0 // indirect
+	dario.cat/mergo v1.0.1 // indirect
 	github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -24,6 +24,7 @@ require (
 	github.com/distribution/reference v0.6.0 // indirect
 	github.com/docker/go-connections v0.5.0 // indirect
 	github.com/docker/go-units v0.5.0 // indirect
+	github.com/ebitengine/purego v0.8.2 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
@@ -32,7 +33,7 @@ require (
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/klauspost/compress v1.17.4 // indirect
 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
-	github.com/magiconair/properties v1.8.7 // indirect
+	github.com/magiconair/properties v1.8.9 // indirect
 	github.com/moby/docker-image-spec v1.3.1 // indirect
 	github.com/moby/patternmatcher v0.6.0 // indirect
 	github.com/moby/sys/sequential v0.5.0 // indirect
@@ -41,17 +42,18 @@ require (
 	github.com/moby/term v0.5.0 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
-	github.com/opencontainers/image-spec v1.1.0 // indirect
+	github.com/opencontainers/image-spec v1.1.1 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 	github.com/shirou/gopsutil/v3 v3.23.12 // indirect
+	github.com/shirou/gopsutil/v4 v4.25.1 // indirect
 	github.com/shoenig/go-m1cpu v0.1.6 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/stretchr/testify v1.10.0 // indirect
 	github.com/tklauser/go-sysconf v0.3.12 // indirect
 	github.com/tklauser/numcpus v0.6.1 // indirect
-	github.com/yusufpapurcu/wmi v1.2.3 // indirect
+	github.com/yusufpapurcu/wmi v1.2.4 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
 	go.opentelemetry.io/otel v1.35.0 // indirect
@@ -61,7 +63,7 @@ require (
 	go.opentelemetry.io/otel/trace v1.35.0 // indirect
 	golang.org/x/crypto v0.35.0 // indirect
 	golang.org/x/net v0.36.0 // indirect
-	golang.org/x/sys v0.30.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
 	golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
diff --git a/wrappers/golang/go.sum b/wrappers/golang/go.sum
index 136a55ba..9465e4cd 100644
--- a/wrappers/golang/go.sum
+++ b/wrappers/golang/go.sum
@@ -2,6 +2,8 @@ cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2Qx
 cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
 dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
 dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
@@ -27,10 +29,14 @@ github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjr
 github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8=
 github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
+github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
+github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -63,6 +69,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
+github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
 github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
 github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
@@ -81,6 +89,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
 github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -91,6 +101,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
 github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
+github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
+github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
 github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
 github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
 github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -110,6 +122,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
 github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
+github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00=
+github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0=
 github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
 github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
@@ -118,6 +132,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
 github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
@@ -166,6 +182,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
 golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
 golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=


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