nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: boardgameio/boardgame.io
Pull request #977 (2021-08-07 20:22:11)

fix(master): Don’t crash on missing chatMessage
This line was throwing unhandled promise rejections when I was testing invalid packets

#### Checklist

- [ this was edited in github] Use a separate branch in your local repo (not `main`).
- [no ] Test coverage is 100% (or you have a story for why it's ok)


nopeless: @delucis yeah I figured it out, thanks. I was thinking about this the other night about having a schema validation like validate.js. But then I realized this chat packet is something different from the other events (for example it is not handled by redux). So I guess this simple fix will suffice
nopeless: wait, let me fix a few other stuff
nopeless: Actually while fixing some other stuff, I realized that the other stuff should be a separate PR. I pinged you on gitter.

Patch:
diff --git a/src/master/master.ts b/src/master/master.ts
index 2ce78e7..229a979 100644
--- a/src/master/master.ts
+++ b/src/master/master.ts
@@ -456,6 +456,9 @@ export class Master {
           metadata: true,
         }
       );
+      if (!(chatMessage && typeof chatMessage.sender === 'string')) {
+        return { error: 'unauthorized' };
+      }
       const isAuthentic = await this.auth.authenticateCredentials({
         playerID: chatMessage.sender,
         credentials,


Test patch:
diff --git a/src/master/master.test.ts b/src/master/master.test.ts
index 8b8de95..9000ad6 100644
--- a/src/master/master.test.ts
+++ b/src/master/master.test.ts
@@ -966,6 +966,19 @@ describe('authentication', () => {
 
     beforeEach(resetTestEnvironment);
 
+    test('auth success', async () => {
+      const authenticateCredentials = () => true;
+      const master = new Master(
+        game,
+        storage,
+        TransportAPI(send, sendAll),
+        new Auth({ authenticateCredentials })
+      );
+      const ret = await master.onChatMessage(matchID, chatMessage, undefined);
+      expect(ret).toBeUndefined();
+      expect(sendAll).toHaveBeenCalled();
+    });
+
     test('auth failure', async () => {
       const authenticateCredentials = () => false;
       const master = new Master(
@@ -979,7 +992,7 @@ describe('authentication', () => {
       expect(sendAll).not.toHaveBeenCalled();
     });
 
-    test('auth success', async () => {
+    test('invalid packet', async () => {
       const authenticateCredentials = () => true;
       const master = new Master(
         game,
@@ -987,9 +1000,9 @@ describe('authentication', () => {
         TransportAPI(send, sendAll),
         new Auth({ authenticateCredentials })
       );
-      const ret = await master.onChatMessage(matchID, chatMessage, undefined);
-      expect(ret).toBeUndefined();
-      expect(sendAll).toHaveBeenCalled();
+      const ret = await master.onChatMessage(matchID, undefined, undefined);
+      expect(ret && ret.error).toBe('unauthorized');
+      expect(sendAll).not.toHaveBeenCalled();
     });
 
     test('default', async () => {
Repository: boardgameio/boardgame.io
Pull request #1060 (2022-02-20 01:25:56)

fix(lobby): block creation of matches with invalid player counts
#### Checklist

- [x] Use a separate branch in your local repo (not `main`).
- [x] Test coverage is 100% (or you have a story for why it's ok).

## Description

Fixes https://github.com/boardgameio/boardgame.io/issues/1055. Prevents a user from creating a match with an invalid number of players (only applicable if `minPlayers` and/or `maxPlayers` are defined).

## Testing

I tried to imitate the style and verbosity of the existing tests in `api.test.ts` with the tests I introduced, ~but technically some edge cases are not addressed by tests (eg. player count too low). If verbosity is preferred I'd be happy to add a few more test cases.~ but feel free to let me know if there's anywhere I can make improvements.


Patch:
diff --git a/src/server/api.ts b/src/server/api.ts
index c0e276b..d49855a 100644
--- a/src/server/api.ts
+++ b/src/server/api.ts
@@ -121,6 +121,15 @@ export const configureRouter = ({
     const game = games.find((g) => g.name === gameName);
     if (!game) ctx.throw(404, 'Game ' + gameName + ' not found');
 
+    if (
+      ctx.request.body.numPlayers !== undefined &&
+      (Number.isNaN(numPlayers) ||
+        (game.minPlayers && numPlayers < game.minPlayers) ||
+        (game.maxPlayers && numPlayers > game.maxPlayers))
+    ) {
+      ctx.throw(400, 'Invalid numPlayers');
+    }
+
     const matchID = await CreateMatch({
       ctx,
       db,


Test patch:
diff --git a/src/server/api.test.ts b/src/server/api.test.ts
index 9d514f2..a5e1b45 100644
--- a/src/server/api.test.ts
+++ b/src/server/api.test.ts
@@ -125,6 +125,8 @@ describe('.configureRouter', () => {
             numPlayers == 2 && setupData.tokens !== 2
               ? 'Two player games must use two tokens'
               : undefined,
+          minPlayers: 2,
+          maxPlayers: 2,
         },
       ];
     });
@@ -193,6 +195,32 @@ describe('.configureRouter', () => {
         });
       });
 
+      describe('with invalid numPlayers', () => {
+        test('not enough players fails', async () => {
+          response = await request(app.callback())
+            .post('/games/validate/create')
+            .send({ numPlayers: 1 });
+
+          expect(response.status).toEqual(400);
+        });
+
+        test('too many players fails', async () => {
+          response = await request(app.callback())
+            .post('/games/validate/create')
+            .send({ numPlayers: 3 });
+
+          expect(response.status).toEqual(400);
+        });
+
+        test('invalid type fails', async () => {
+          response = await request(app.callback())
+            .post('/games/validate/create')
+            .send({ numPlayers: 'hello' });
+
+          expect(response.status).toEqual(400);
+        });
+      });
+
       describe('for an unknown game name', () => {
         beforeEach(async () => {
           response = await request(app.callback()).post('/games/bar/create');
Repository: jackc/pgx
Pull request #1839 (2023-12-19 17:28:38)

feat(pgproto3/backend): add a SetMaxBodyLen to limit the max body length for the receive
Create a pull request for the discussion: https://github.com/jackc/pgx/discussions/1836




Patch:
diff --git a/pgproto3/backend.go b/pgproto3/backend.go
index 6db77e4a..efa909c3 100644
--- a/pgproto3/backend.go
+++ b/pgproto3/backend.go
@@ -38,6 +38,7 @@ type Backend struct {
 	terminate      Terminate
 
 	bodyLen    int
+	maxBodyLen int // maxBodyLen is the maximum length of a message body in octets. If a message body exceeds this length, Receive will return an error.
 	msgType    byte
 	partialMsg bool
 	authType   uint32
@@ -158,6 +159,9 @@ func (b *Backend) Receive() (FrontendMessage, error) {
 
 		b.msgType = header[0]
 		b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
+		if b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {
+			return nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}
+		}
 		b.partialMsg = true
 	}
 
@@ -260,3 +264,12 @@ func (b *Backend) SetAuthType(authType uint32) error {
 
 	return nil
 }
+
+// SetMaxBodyLen sets the maximum length of a message body in octets. If a message body exceeds this length, Receive will return
+// an error. This is useful for protecting against malicious clients that send large messages with the intent of
+// causing memory exhaustion.
+// The default value is 0.
+// If maxBodyLen is 0, then no maximum is enforced.
+func (b *Backend) SetMaxBodyLen(maxBodyLen int) {
+	b.maxBodyLen = maxBodyLen
+}
diff --git a/pgproto3/pgproto3.go b/pgproto3/pgproto3.go
index ef5a5489..04be291c 100644
--- a/pgproto3/pgproto3.go
+++ b/pgproto3/pgproto3.go
@@ -70,6 +70,15 @@ func (e *writeError) Unwrap() error {
 	return e.err
 }
 
+type ExceededMaxBodyLenErr struct {
+	maxExpectedBodyLen int
+	actualBodyLen      int
+}
+
+func (e *ExceededMaxBodyLenErr) Error() string {
+	return fmt.Sprintf("invalid body length: expected at most %d, but got %d", e.maxExpectedBodyLen, e.actualBodyLen)
+}
+
 // getValueFromJSON gets the value from a protocol message representation in JSON.
 func getValueFromJSON(v map[string]string) ([]byte, error) {
 	if v == nil {


Test patch:
diff --git a/pgproto3/backend_test.go b/pgproto3/backend_test.go
index 596245dd..5655122a 100644
--- a/pgproto3/backend_test.go
+++ b/pgproto3/backend_test.go
@@ -120,3 +120,21 @@ func TestStartupMessage(t *testing.T) {
 		}
 	})
 }
+
+func TestBackendReceiveExceededMaxBodyLen(t *testing.T) {
+	t.Parallel()
+
+	server := &interruptReader{}
+	server.push([]byte{'Q', 0, 0, 10, 10})
+
+	backend := pgproto3.NewBackend(server, nil)
+
+	// Set max body len to 5
+	backend.SetMaxBodyLen(5)
+
+	// Receive regular msg
+	msg, err := backend.Receive()
+	assert.Nil(t, msg)
+	var invalidBodyLenErr *pgproto3.ExceededMaxBodyLenErr
+	assert.ErrorAs(t, err, &invalidBodyLenErr)
+}
Repository: jackc/pgx
Pull request #2112 (2024-08-21 19:07:14)

support text scanner for binary format for uint32


Patch:
diff --git a/pgtype/uint32.go b/pgtype/uint32.go
index 098c516c..c0134109 100644
--- a/pgtype/uint32.go
+++ b/pgtype/uint32.go
@@ -205,6 +205,8 @@ func (Uint32Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPl
 			return scanPlanBinaryUint32ToUint32{}
 		case Uint32Scanner:
 			return scanPlanBinaryUint32ToUint32Scanner{}
+		case TextScanner:
+			return scanPlanBinaryUint32ToTextScanner{}
 		}
 	case TextFormatCode:
 		switch target.(type) {
@@ -282,6 +284,22 @@ func (scanPlanBinaryUint32ToUint32Scanner) Scan(src []byte, dst any) error {
 	return s.ScanUint32(Uint32{Uint32: n, Valid: true})
 }
 
+type scanPlanBinaryUint32ToTextScanner struct{}
+
+func (scanPlanBinaryUint32ToTextScanner) Scan(src []byte, dst any) error {
+	s, ok := (dst).(TextScanner)
+	if !ok {
+		return ErrScanTargetTypeChanged
+	}
+
+	if src == nil {
+		return s.ScanText(Text{})
+	}
+
+	n := uint64(binary.BigEndian.Uint32(src))
+	return s.ScanText(Text{String: strconv.FormatUint(n, 10), Valid: true})
+}
+
 type scanPlanTextAnyToUint32Scanner struct{}
 
 func (scanPlanTextAnyToUint32Scanner) Scan(src []byte, dst any) error {


Test patch:
diff --git a/pgtype/uint32_test.go b/pgtype/uint32_test.go
index 842de643..efa4e273 100644
--- a/pgtype/uint32_test.go
+++ b/pgtype/uint32_test.go
@@ -17,5 +17,6 @@ func TestUint32Codec(t *testing.T) {
 		},
 		{pgtype.Uint32{}, new(pgtype.Uint32), isExpectedEq(pgtype.Uint32{})},
 		{nil, new(pgtype.Uint32), isExpectedEq(pgtype.Uint32{})},
+		{"1147", new(string), isExpectedEq("1147")},
 	})
 }
Repository: jackc/pgx
Pull request #2145 (2024-10-09 11:23:12)

Implement pgtype.UUID.String()
https://github.com/jackc/pgx/issues/2055


Patch:
diff --git a/pgtype/uuid.go b/pgtype/uuid.go
index d57c0f2f..0628f193 100644
--- a/pgtype/uuid.go
+++ b/pgtype/uuid.go
@@ -96,6 +96,14 @@ func (src UUID) Value() (driver.Value, error) {
 	return encodeUUID(src.Bytes), nil
 }
 
+func (src UUID) String() string {
+	if !src.Valid {
+		return ""
+	}
+
+	return encodeUUID(src.Bytes)
+}
+
 func (src UUID) MarshalJSON() ([]byte, error) {
 	if !src.Valid {
 		return []byte("null"), nil


Test patch:
diff --git a/pgtype/uuid_test.go b/pgtype/uuid_test.go
index 1c701e74..255bd92f 100644
--- a/pgtype/uuid_test.go
+++ b/pgtype/uuid_test.go
@@ -63,6 +63,38 @@ func TestUUIDCodec(t *testing.T) {
 	})
 }
 
+func TestUUID_String(t *testing.T) {
+	tests := []struct {
+		name string
+		src  pgtype.UUID
+		want string
+	}{
+		{
+			name: "first",
+			src: pgtype.UUID{
+				Bytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122},
+				Valid: true,
+			},
+			want: "1d485a7a-6d18-4599-8c6c-34425616887a",
+		},
+		{
+			name: "third",
+			src: pgtype.UUID{
+				Bytes: [16]byte{},
+			},
+			want: "",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := tt.src.String()
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("MarshalJSON() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
 func TestUUID_MarshalJSON(t *testing.T) {
 	tests := []struct {
 		name string
Repository: jackc/pgx
Pull request #1160 (2022-03-02 13:09:27)

Fix single line comment for line endings in mac when sanitizing.
Line endings on macs are \r, so sanitizing single-line comments did not correctly parse query arguments up to \n


Patch:
diff --git a/internal/sanitize/sanitize.go b/internal/sanitize/sanitize.go
index 2dba3b81..a7a94e93 100644
--- a/internal/sanitize/sanitize.go
+++ b/internal/sanitize/sanitize.go
@@ -246,7 +246,7 @@ func oneLineCommentState(l *sqlLexer) stateFn {
 		case '\\':
 			_, width = utf8.DecodeRuneInString(l.src[l.pos:])
 			l.pos += width
-		case '\n':
+		case '\n', '\r':
 			return rawState
 		case utf8.RuneError:
 			if l.pos-l.start > 0 {


Test patch:
diff --git a/internal/sanitize/sanitize_test.go b/internal/sanitize/sanitize_test.go
index 344c46b0..bbac84b0 100644
--- a/internal/sanitize/sanitize_test.go
+++ b/internal/sanitize/sanitize_test.go
@@ -77,12 +77,16 @@ func TestNewQuery(t *testing.T) {
 			expected: sanitize.Query{Parts: []sanitize.Part{"select -- a baby's toy\n'barbie', ", 1}},
 		},
 		{
-			sql:      "select 42 -- is a Thinker's favorite number",
-			expected: sanitize.Query{Parts: []sanitize.Part{"select 42 -- is a Thinker's favorite number"}},
+			sql:      "select 42 -- is a Deep Thought's favorite number",
+			expected: sanitize.Query{Parts: []sanitize.Part{"select 42 -- is a Deep Thought's favorite number"}},
 		},
 		{
-			sql:      "select 42, -- \\nis a Thinker's favorite number\n$1",
-			expected: sanitize.Query{Parts: []sanitize.Part{"select 42, -- \\nis a Thinker's favorite number\n", 1}},
+			sql:      "select 42, -- \\nis a Deep Thought's favorite number\n$1",
+			expected: sanitize.Query{Parts: []sanitize.Part{"select 42, -- \\nis a Deep Thought's favorite number\n", 1}},
+		},
+		{
+			sql:      "select 42, -- \\nis a Deep Thought's favorite number\r$1",
+			expected: sanitize.Query{Parts: []sanitize.Part{"select 42, -- \\nis a Deep Thought's favorite number\r", 1}},
 		},
 	}
Repository: jackc/pgx
Pull request #1216 (2022-05-31 17:21:15)

Add Hijack from v5


Patch:
diff --git a/pgxpool/conn.go b/pgxpool/conn.go
index 0b59d741..036c728a 100644
--- a/pgxpool/conn.go
+++ b/pgxpool/conn.go
@@ -46,6 +46,22 @@ func (c *Conn) Release() {
 	}()
 }
 
+// Hijack assumes ownership of the connection from the pool. Caller is responsible for closing the connection. Hijack
+// will panic if called on an already released or hijacked connection.
+func (c *Conn) Hijack() *pgx.Conn {
+	if c.res == nil {
+		panic("cannot hijack already released or hijacked connection")
+	}
+
+	conn := c.Conn()
+	res := c.res
+	c.res = nil
+
+	res.Hijack()
+
+	return conn
+}
+
 func (c *Conn) Exec(ctx context.Context, sql string, arguments ...interface{}) (pgconn.CommandTag, error) {
 	return c.Conn().Exec(ctx, sql, arguments...)
 }


Test patch:
diff --git a/pgxpool/pool_test.go b/pgxpool/pool_test.go
index 072ca053..42e029e1 100644
--- a/pgxpool/pool_test.go
+++ b/pgxpool/pool_test.go
@@ -115,6 +115,32 @@ func TestPoolAcquireAndConnRelease(t *testing.T) {
 	c.Release()
 }
 
+func TestPoolAcquireAndConnHijack(t *testing.T) {
+	t.Parallel()
+
+	ctx := context.Background()
+
+	pool, err := pgxpool.Connect(ctx, os.Getenv("PGX_TEST_DATABASE"))
+	require.NoError(t, err)
+	defer pool.Close()
+
+	c, err := pool.Acquire(ctx)
+	require.NoError(t, err)
+
+	connsBeforeHijack := pool.Stat().TotalConns()
+
+	conn := c.Hijack()
+	defer conn.Close(ctx)
+
+	connsAfterHijack := pool.Stat().TotalConns()
+	require.Equal(t, connsBeforeHijack-1, connsAfterHijack)
+
+	var n int32
+	err = conn.QueryRow(ctx, `select 1`).Scan(&n)
+	require.NoError(t, err)
+	require.Equal(t, int32(1), n)
+}
+
 func TestPoolAcquireFunc(t *testing.T) {
 	t.Parallel()
Repository: jackc/pgx
Pull request #1232 (2022-06-21 09:33:14)

Add logger func wrapper for adapter creation
This PR adds a convenience logger function wrapper to make logger adapter creation easier. This solution is analogous to how a logger is added to an AWS config using aws-sdk-go-v2, see https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/config#WithLogger. Implementation is based on https://github.com/aws/smithy-go/blob/main/logging/logger.go.


Patch:
diff --git a/logger.go b/logger.go
index 89fd5af5..19a74123 100644
--- a/logger.go
+++ b/logger.go
@@ -47,6 +47,14 @@ type Logger interface {
 	Log(ctx context.Context, level LogLevel, msg string, data map[string]interface{})
 }
 
+// LoggerFunc is a wrapper around a function to satisfy the pgx.Logger interface
+type LoggerFunc func(ctx context.Context, level LogLevel, msg string, data map[string]interface{})
+
+// Log delegates the logging request to the wrapped function
+func (f LoggerFunc) Log(ctx context.Context, level LogLevel, msg string, data map[string]interface{}) {
+	f(ctx, level, msg, data)
+}
+
 // LogLevelFromString converts log level string to constant
 //
 // Valid levels:


Test patch:
diff --git a/conn_test.go b/conn_test.go
index e34662ae..467f6ecc 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -1,7 +1,9 @@
 package pgx_test
 
 import (
+	"bytes"
 	"context"
+	"log"
 	"os"
 	"strings"
 	"sync"
@@ -837,6 +839,37 @@ func TestLogPassesContext(t *testing.T) {
 	}
 }
 
+func TestLoggerFunc(t *testing.T) {
+	t.Parallel()
+
+	const testMsg = "foo"
+
+	buf := bytes.Buffer{}
+	logger := log.New(&buf, "", 0)
+
+	createAdapterFn := func(logger *log.Logger) pgx.LoggerFunc {
+		return func(ctx context.Context, level pgx.LogLevel, msg string, data map[string]interface{}) {
+			logger.Printf("%s", testMsg)
+		}
+	}
+
+	config := mustParseConfig(t, os.Getenv("PGX_TEST_DATABASE"))
+	config.Logger = createAdapterFn(logger)
+
+	conn := mustConnect(t, config)
+	defer closeConn(t, conn)
+
+	buf.Reset() // Clear logs written when establishing connection
+
+	if _, err := conn.Exec(context.TODO(), ";"); err != nil {
+		t.Fatal(err)
+	}
+
+	if strings.TrimSpace(buf.String()) != testMsg {
+		t.Errorf("Expected logger function to return '%s', but it was '%s'", testMsg, buf.String())
+	}
+}
+
 func TestIdentifierSanitize(t *testing.T) {
 	t.Parallel()
Repository: jackc/pgx
Pull request #1484 (2023-01-30 00:19:23)

Fix for passing the context in pgxpool.ConnectConfig to BeforeConnect
Fixes bug where the context passed to a `config.BeforeConnect` function (of type `func(context.Context, *pgx.ConnConfig) error`) is not the same as the context passed to the invoking `pgxpool.ConnectConfig(ctx, config)` function.  This happens when `config.MinConns` is greater than 0.

This bug was introduced in 4.17 from this commit line: https://github.com/jackc/pgx/pull/1213/files#diff-dfb45ae842e6d6603aae7f6631cd38ee1c56d2053289b1914aff4bef35e39d2dR243


Patch:
diff --git a/pgxpool/pool.go b/pgxpool/pool.go
index 8e88ecaf..e83d8a5d 100644
--- a/pgxpool/pool.go
+++ b/pgxpool/pool.go
@@ -265,7 +265,7 @@ func ConnectConfig(ctx context.Context, config *Config) (*Pool, error) {
 	)
 
 	if !config.LazyConnect {
-		if err := p.checkMinConns(); err != nil {
+		if err := p.checkMinConnsWithContext(ctx); err != nil {
 			// Couldn't create resources for minpool size. Close unhealthy pool.
 			p.Close()
 			return nil, err
@@ -485,17 +485,21 @@ func (p *Pool) checkConnsHealth() bool {
 	return destroyed
 }
 
-func (p *Pool) checkMinConns() error {
+func (p *Pool) checkMinConnsWithContext(ctx context.Context) error {
 	// TotalConns can include ones that are being destroyed but we should have
 	// sleep(500ms) around all of the destroys to help prevent that from throwing
 	// off this check
 	toCreate := p.minConns - p.Stat().TotalConns()
 	if toCreate > 0 {
-		return p.createIdleResources(context.Background(), int(toCreate))
+		return p.createIdleResources(ctx, int(toCreate))
 	}
 	return nil
 }
 
+func (p *Pool) checkMinConns() error {
+	return p.checkMinConnsWithContext(context.Background())
+}
+
 func (p *Pool) createIdleResources(parentCtx context.Context, targetResources int) error {
 	ctx, cancel := context.WithCancel(parentCtx)
 	defer cancel()


Test patch:
diff --git a/pgxpool/pool_test.go b/pgxpool/pool_test.go
index 4e712016..4346d6f5 100644
--- a/pgxpool/pool_test.go
+++ b/pgxpool/pool_test.go
@@ -73,6 +73,26 @@ func TestLazyConnect(t *testing.T) {
 	assert.Equal(t, context.Canceled, err)
 }
 
+func TestBeforeConnectWithContextWithValueAndOneMinConn(t *testing.T) {
+	t.Parallel()
+
+	config, err := pgxpool.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
+	assert.NoError(t, err)
+	config.MinConns = 1
+	config.BeforeConnect = func(ctx context.Context, config *pgx.ConnConfig) error {
+		val := ctx.Value("key")
+		if val == nil {
+			return errors.New("no value found with key 'key'")
+		}
+		return nil
+	}
+
+	ctx := context.WithValue(context.Background(), "key", "value")
+
+	_, err = pgxpool.ConnectConfig(ctx, config)
+	assert.NoError(t, err)
+}
+
 func TestConstructorIgnoresContext(t *testing.T) {
 	t.Parallel()
Repository: jackc/pgx
Pull request #1497 (2023-02-10 17:58:50)

Register pgx driver using major version
Fixed: #1480


Patch:
diff --git a/stdlib/sql.go b/stdlib/sql.go
index da377ece..1c46e278 100644
--- a/stdlib/sql.go
+++ b/stdlib/sql.go
@@ -58,6 +58,7 @@ import (
 	"math"
 	"math/rand"
 	"reflect"
+	"sort"
 	"strconv"
 	"strings"
 	"sync"
@@ -84,7 +85,13 @@ func init() {
 		configs: make(map[string]*pgx.ConnConfig),
 	}
 	fakeTxConns = make(map[*pgx.Conn]*sql.Tx)
-	sql.Register("pgx", pgxDriver)
+
+	drivers := sql.Drivers()
+	// if pgx driver was already registered by different pgx major version then we skip registration under the default name.
+	if i := sort.SearchStrings(sql.Drivers(), "pgx"); len(drivers) >= i || drivers[i] != "pgx" {
+		sql.Register("pgx", pgxDriver)
+	}
+	sql.Register("pgx/v4", pgxDriver)
 
 	databaseSQLResultFormats = pgx.QueryResultFormatsByOID{
 		pgtype.BoolOID:        1,


Test patch:
diff --git a/stdlib/sql_test.go b/stdlib/sql_test.go
index 099320c0..e0aa50ca 100644
--- a/stdlib/sql_test.go
+++ b/stdlib/sql_test.go
@@ -157,9 +157,22 @@ func closeStmt(t *testing.T, stmt *sql.Stmt) {
 }
 
 func TestSQLOpen(t *testing.T) {
-	db, err := sql.Open("pgx", os.Getenv("PGX_TEST_DATABASE"))
-	require.NoError(t, err)
-	closeDB(t, db)
+	tests := []struct {
+		driverName string
+	}{
+		{driverName: "pgx"},
+		{driverName: "pgx/v4"},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.driverName, func(t *testing.T) {
+			db, err := sql.Open(tt.driverName, os.Getenv("PGX_TEST_DATABASE"))
+			require.NoError(t, err)
+			closeDB(t, db)
+		})
+	}
 }
 
 func TestNormalLifeCycle(t *testing.T) {