-
-
Notifications
You must be signed in to change notification settings - Fork 992
Description
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.noDataI 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.