From caafb63085b079baa6d5657bb04be74d555007f4 Mon Sep 17 00:00:00 2001 From: itxaiohanglover <1531137510@qq.com> Date: Thu, 2 Jul 2026 01:02:36 +0800 Subject: [PATCH 1/2] fix: add Conn and *Conn to mapperFor and isUnsafe type switches Conn was missing from the type switches in mapperFor() and isUnsafe(), causing custom Mapper and unsafe flag to not propagate when creating Stmts from *sqlx.Conn. Fixes #975 --- sqlx.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sqlx.go b/sqlx.go index 8259a4f..95f4669 100644 --- a/sqlx.go +++ b/sqlx.go @@ -136,6 +136,10 @@ func isUnsafe(i interface{}) bool { return v.unsafe case *Tx: return v.unsafe + case Conn: + return v.unsafe + case *Conn: + return v.unsafe case sql.Rows, *sql.Rows: return false default: @@ -153,6 +157,10 @@ func mapperFor(i interface{}) *reflectx.Mapper { return i.Mapper case *Tx: return i.Mapper + case Conn: + return i.Mapper + case *Conn: + return i.Mapper default: return mapper() } From aaa28b8e2e25f8c7bfc6d464db30d69f74763d0a Mon Sep 17 00:00:00 2001 From: August Vi Date: Sat, 10 Oct 2026 21:05:45 -0700 Subject: [PATCH 2/2] Add the test that a Stmt prepared on a Conn keeps the Conn's Mapper and unsafe flag. --- conn_preparex_test.go | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 conn_preparex_test.go diff --git a/conn_preparex_test.go b/conn_preparex_test.go new file mode 100644 index 0000000..fd3cc15 --- /dev/null +++ b/conn_preparex_test.go @@ -0,0 +1,63 @@ +package sqlx + +import ( + "context" + "strings" + "testing" + + "github.com/secondwindoss/sqlx/reflectx" +) + +// A Stmt prepared on a Conn inherits the Conn's Mapper and unsafe flag +// (upstream #975). +func TestConnPreparexKeepsMapperAndUnsafe(t *testing.T) { + RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T, now string) { + loadDefaultFixture(db, t) + ctx := context.Background() + + conn, err := db.Unsafe().Connx(ctx) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + if !conn.unsafe { + t.Fatal("precondition: conn from db.Unsafe() should be unsafe") + } + stmt, err := conn.PreparexContext(ctx, conn.Rebind("SELECT * FROM person WHERE first_name=?")) + if err != nil { + t.Fatal(err) + } + defer stmt.Close() + if stmt.unsafe != conn.unsafe { + t.Errorf("unsafe: Stmt.unsafe=%v, want %v (conn.unsafe)", stmt.unsafe, conn.unsafe) + } + var partial struct { + FirstName string `db:"first_name"` + } + if err := stmt.Get(&partial, "Jason"); err != nil { + t.Errorf("unsafe: Get into partial struct through conn-prepared Stmt: %v", err) + } + + conn2, err := db.Connx(ctx) + if err != nil { + t.Fatal(err) + } + defer conn2.Close() + conn2.Mapper = reflectx.NewMapperFunc("json", strings.ToLower) + stmt2, err := conn2.PreparexContext(ctx, conn2.Rebind("SELECT first_name, last_name FROM person WHERE first_name=?")) + if err != nil { + t.Fatal(err) + } + defer stmt2.Close() + if stmt2.Mapper != conn2.Mapper { + t.Errorf("mapper: Stmt.Mapper was not taken from Conn.Mapper") + } + var p struct { + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + } + if err := stmt2.Get(&p, "Jason"); err != nil { + t.Errorf("mapper: Get with json-tag mapper set on Conn: %v", err) + } + }) +}