diff --git a/pgconn/config.go b/pgconn/config.go index dff550953..0177d22c5 100644 --- a/pgconn/config.go +++ b/pgconn/config.go @@ -454,23 +454,37 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con minProto, err := parseProtocolVersion(settings["min_protocol_version"]) if err != nil { - return nil, &ParseConfigError{ConnString: connString, msg: "invalid min_protocol_version", err: err} + return nil, &ParseConfigError{ConnString: connString, msg: fmt.Sprintf("invalid min_protocol_version: %q", settings["min_protocol_version"]), err: err} } maxProto, err := parseProtocolVersion(settings["max_protocol_version"]) if err != nil { - return nil, &ParseConfigError{ConnString: connString, msg: "invalid max_protocol_version", err: err} - } - if minProto > maxProto { - return nil, &ParseConfigError{ConnString: connString, msg: "min_protocol_version cannot be greater than max_protocol_version"} + return nil, &ParseConfigError{ConnString: connString, msg: fmt.Sprintf("invalid max_protocol_version: %q", settings["max_protocol_version"]), err: err} } config.MinProtocolVersion = settings["min_protocol_version"] config.MaxProtocolVersion = settings["max_protocol_version"] + if config.MinProtocolVersion == "" { config.MinProtocolVersion = "3.0" } + + // When max_protocol_version is not explicitly set, default based on + // min_protocol_version. This matches libpq behavior: if min > 3.0, + // default max to latest; otherwise default to 3.0 for compatibility + // with older servers/poolers that don't support NegotiateProtocolVersion. if config.MaxProtocolVersion == "" { - config.MaxProtocolVersion = "3.0" + if minProto > pgproto3.ProtocolVersion30 { + config.MaxProtocolVersion = "latest" + } else { + config.MaxProtocolVersion = "3.0" + } + } + + // Only error when max_protocol_version was explicitly set and conflicts + // with min_protocol_version. When max_protocol_version is not explicitly + // set, the auto-raise logic above already ensures a valid default. + if minProto > maxProto && settings["max_protocol_version"] != "" { + return nil, &ParseConfigError{ConnString: connString, msg: "min_protocol_version cannot be greater than max_protocol_version"} } switch channelBinding := settings["channel_binding"]; channelBinding { diff --git a/pgconn/config_test.go b/pgconn/config_test.go index 57898eb88..e6120aad3 100644 --- a/pgconn/config_test.go +++ b/pgconn/config_test.go @@ -1228,6 +1228,12 @@ func TestParseConfigProtocolVersion(t *testing.T) { expectedMin: "3.2", expectedMax: "3.2", }, + { + name: "min_protocol_version=latest auto-raises max", + connString: "postgres://localhost/test?min_protocol_version=latest", + expectedMin: "latest", + expectedMax: "latest", + }, { name: "max_protocol_version=latest", connString: "postgres://localhost/test?max_protocol_version=latest", @@ -1235,7 +1241,7 @@ func TestParseConfigProtocolVersion(t *testing.T) { expectedMax: "latest", }, { - name: "min and max = latest", + name: "min_protocol_version and max_protocol_version = latest", connString: "postgres://localhost/test?min_protocol_version=latest&max_protocol_version=latest", expectedMin: "latest", expectedMax: "latest", @@ -1244,27 +1250,39 @@ func TestParseConfigProtocolVersion(t *testing.T) { name: "invalid min_protocol_version", connString: "postgres://localhost/test?min_protocol_version=2.0", expectError: true, - expectedErrContain: "invalid min_protocol_version", + expectedErrContain: `invalid min_protocol_version: "2.0"`, }, { name: "invalid max_protocol_version", connString: "postgres://localhost/test?max_protocol_version=4.0", expectError: true, - expectedErrContain: "invalid max_protocol_version", + expectedErrContain: `invalid max_protocol_version: "4.0"`, }, { - name: "min > max", + name: "min_protocol_version > max_protocol_version", connString: "postgres://localhost/test?min_protocol_version=3.2&max_protocol_version=3.0", expectError: true, expectedErrContain: "min_protocol_version cannot be greater than max_protocol_version", }, { - name: "environment variable PGMINPROTOCOLVERSION without matching max fails", - connString: "postgres://localhost/test", - envMin: "3.2", + name: "min_protocol_version (latest) > max_protocol_version", + connString: "postgres://localhost/test?min_protocol_version=latest&max_protocol_version=3.0", expectError: true, expectedErrContain: "min_protocol_version cannot be greater than max_protocol_version", }, + { + name: "min_protocol_version=3.2 auto-raises max", + connString: "postgres://localhost/test?min_protocol_version=3.2", + expectedMin: "3.2", + expectedMax: "latest", + }, + { + name: "environment variable PGMINPROTOCOLVERSION auto-raises max", + connString: "postgres://localhost/test", + envMin: "3.2", + expectedMin: "3.2", + expectedMax: "latest", + }, { name: "environment variables PGMINPROTOCOLVERSION and PGMAXPROTOCOLVERSION together", connString: "postgres://localhost/test", diff --git a/pgproto3/startup_message.go b/pgproto3/startup_message.go index 6caab3ee4..eb48f72bf 100644 --- a/pgproto3/startup_message.go +++ b/pgproto3/startup_message.go @@ -13,6 +13,7 @@ import ( const ( ProtocolVersion30 = 196608 // 3.0 ProtocolVersion32 = 196610 // 3.2 + ProtocolVersionLatest = ProtocolVersion32 // Latest is 3.2 ProtocolVersionNumber = ProtocolVersion30 // Default is still 3.0 )