Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ unreleased
----------


v1.12.3 (2026-04-03)
--------------------
- Send datestyle startup parameter, improving compatbility with database engines
that use a different default datestyle such as EnterpriseDB ([#1312]).

[#1312]: https://github.com/lib/pq/pull/1312

v1.12.2 (2026-04-02)
--------------------

Expand Down
4 changes: 4 additions & 0 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,10 @@ func (cn *conn) startup(cfg Config) error {
w.string("client_encoding")
w.string(cfg.ClientEncoding)
}
if cfg.Datestyle != "" {
w.string("datestyle")
w.string(cfg.Datestyle)
}
for k, v := range cfg.Runtime {
w.string(k)
w.string(v)
Expand Down
73 changes: 51 additions & 22 deletions connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,28 +113,6 @@ func TestNewConnector(t *testing.T) {
defer db.Close()
useConn(t, db)
})

t.Run("database=", func(t *testing.T) {
// Cockroach allows connecting to any database name, although it does
// give an error on DDL statements (but not "select;" / Ping()). Pretty
// weird, but not really important to run this test there so whatever.
pqtest.SkipCockroach(t)

want1, want2 := `or:pq: database "err" does not exist (3D000)|pq: no such database: err (08P01)`,
`or:pq: database "two" does not exist (3D000)|pq: no such database: two (08P01)`

// Make sure database= consistently take precedence over dbname=
for i := 0; i < 10; i++ {
_, err := pqtest.DB(t, "database=err")
if !pqtest.ErrorContains(err, want1) {
t.Errorf("wrong error:\nhave: %s\nwant: %s", err, want1)
}
_, err = pqtest.DB(t, "dbname=one database=two")
if !pqtest.ErrorContains(err, want2) {
t.Errorf("wrong error:\nhave: %s\nwant: %s", err, want2)
}
}
})
}

// TODO: this can be merged with TestNewConfig, I think?
Expand Down Expand Up @@ -758,6 +736,57 @@ func TestConnectionTargetSessionAttrs(t *testing.T) {
}
}

func TestStartupParameters(t *testing.T) {
tests := []struct {
dsn string
want map[string]string
}{
{"", map[string]string{"client_encoding": "UTF8", "database": "pqgo", "datestyle": "ISO, MDY", "user": "pqgo"}},

// Make sure database= works, and takes pre secede over dbname=
{"dbname=A", map[string]string{"client_encoding": "UTF8", "database": "A", "datestyle": "ISO, MDY", "user": "pqgo"}},
{"database=B", map[string]string{"client_encoding": "UTF8", "database": "B", "datestyle": "ISO, MDY", "user": "pqgo"}},
{"dbname=A database=B", map[string]string{"client_encoding": "UTF8", "database": "B", "datestyle": "ISO, MDY", "user": "pqgo"}},
}

pqtest.Unsetenv(t, "PGAPPNAME")
for _, tt := range tests {
t.Run("", func(t *testing.T) {
var have map[string]string
f := pqtest.NewFake(t, func(f pqtest.Fake, cn net.Conn) {
_, params, ok := f.ReadStartup(cn)
if !ok {
return
}
have = params

f.WriteMsg(cn, proto.AuthenticationRequest, "\x00\x00\x00\x00")
f.WriteMsg(cn, proto.ReadyForQuery, "I")
for {
code, _, ok := f.ReadMsg(cn)
if !ok {
return
}
switch code {
case proto.Query:
f.WriteMsg(cn, proto.EmptyQueryResponse, "")
f.WriteMsg(cn, proto.ReadyForQuery, "I")
case proto.Terminate:
cn.Close()
return
}
}
})
defer f.Close()

pqtest.MustDB(t, f.DSN()+" "+tt.dsn).Close()
if !reflect.DeepEqual(have, tt.want) {
t.Errorf("\nhave: %#v\nwant: %#v", have, tt.want)
}
})
}
}

func TestProtocolVersion(t *testing.T) {
var (
key30 = []byte{1, 2, 3, 4}
Expand Down