Skip to content

Support for protocol version 3.2 #2489

@dbussink

Description

@dbussink

PostgreSQL 18 added a new protocol version, version 3.2. I'd like to add support to pgx for this. I'm happy to contribute a PR to add this.

I do have one significant question though. Having looked at the code, I see kinda two options here for implementing this which has different consequences for backwards compatibility and it raises the question if a certain change is safe enough for not.

Since it changes what a secret key for a cancel request looks like, the from scratch approach would be to make a change like this:

diff --git i/pgproto3/backend_key_data.go w/pgproto3/backend_key_data.go
index 23f5da67..126639cf 100644
--- i/pgproto3/backend_key_data.go
+++ w/pgproto3/backend_key_data.go
@@ -9,7 +9,7 @@ import (
 
 type BackendKeyData struct {
 	ProcessID uint32
-	SecretKey uint32
+	SecretKey []byte
 }
 
 // Backend identifies this message as sendable by the PostgreSQL backend.
diff --git i/pgproto3/cancel_request.go w/pgproto3/cancel_request.go
index 6b52dd97..c22bcd7d 100644
--- i/pgproto3/cancel_request.go
+++ w/pgproto3/cancel_request.go
@@ -12,7 +12,7 @@ const cancelRequestCode = 80877102
 
 type CancelRequest struct {
 	ProcessID uint32
-	SecretKey uint32
+	SecretKey []byte
 }
 
 // Frontend identifies this message as sendable by a PostgreSQL frontend.

This is though then a backwards compatibility breaking change. But I'm unsure if this is low risk enough or not. There's one precedent for a function signature change in pgproto3, where Encode was changed to also return an error.

The alternative is a change like this:

diff --git i/pgproto3/backend_key_data.go w/pgproto3/backend_key_data.go
index 23f5da67..90af31fb 100644
--- i/pgproto3/backend_key_data.go
+++ w/pgproto3/backend_key_data.go
@@ -2,14 +2,17 @@ package pgproto3

 import (
        "encoding/binary"
+       "encoding/hex"
        "encoding/json"

        "github.com/jackc/pgx/v5/internal/pgio"
 )

 type BackendKeyData struct {
-       ProcessID uint32
-       SecretKey uint32
+       ProtocolVersion uint32 // Set by caller before Decode to determine key format
+       ProcessID       uint32
+       SecretKey       uint32 // Populated for protocol 3.0
+       SecretKeyData   []byte // Populated for protocol 3.2+
 }

 // Backend identifies this message as sendable by the PostgreSQL backend.

This would be more backwards compatible. It poses a bit if a problem how to decode though, since Decode only gets []byte, but it could be done like this potentially:

diff --git i/pgproto3/frontend.go w/pgproto3/frontend.go
index 056e547c..20857671 100644
--- i/pgproto3/frontend.go
+++ w/pgproto3/frontend.go
@@ -358,6 +359,7 @@ func (f *Frontend) Receive() (BackendMessage, error) {
        case 'I':
                msg = &f.emptyQueryResponse
        case 'K':
+               f.backendKeyData.ProtocolVersion = f.protocolVersion
                msg = &f.backendKeyData
        case 'n':
                msg = &f.noData

I would consider these two options as the alternatives, but maybe I missed something of course 😄.

As for additional context, the reason I'd like to have 3.2 support is not just from a security perspective, but also because what is also described at https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-BACKENDKEYDATA:


The minimum and maximum key length are 4 and 256 bytes, respectively. The PostgreSQL server only sends keys up to 32 bytes, but the larger maximum size allows for future server versions, as well as connection poolers and other middleware, to use longer keys. One possible use case is augmenting the server's key with extra information. Middleware is therefore also encouraged to not use up all of the bytes, in case multiple middleware applications are layered on top of each other, each of which may wrap the key with extra data.


Specifically here the middleware part, we want to be able to use the larger room also with pgx for cancel request routing then.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions