Skip to content

Commit 852fb3b

Browse files
authored
Run tests parallel (#1221)
Speeds up test runner from ~10.5 seconds to ~2.8 seconds, which is not a bad win.
1 parent 6696a19 commit 852fb3b

File tree

18 files changed

+788
-799
lines changed

18 files changed

+788
-799
lines changed

array_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"database/sql"
66
"database/sql/driver"
7+
"fmt"
78
"math/rand"
89
"reflect"
910
"strings"
@@ -1654,3 +1655,57 @@ func TestArrayValueBackend(t *testing.T) {
16541655
}
16551656
}
16561657
}
1658+
1659+
func TestArrayArg(t *testing.T) {
1660+
db := pqtest.MustDB(t)
1661+
defer db.Close()
1662+
1663+
tests := []struct {
1664+
pgType string
1665+
in, out any
1666+
}{
1667+
{"int[]", []int{245, 231}, []int64{245, 231}},
1668+
{"int[]", &[]int{245, 231}, []int64{245, 231}},
1669+
{"int[]", []int64{245, 231}, nil},
1670+
{"int[]", &[]int64{245, 231}, []int64{245, 231}},
1671+
{"varchar[]", []string{"hello", "world"}, nil},
1672+
{"varchar[]", &[]string{"hello", "world"}, []string{"hello", "world"}},
1673+
}
1674+
1675+
for _, tt := range tests {
1676+
t.Run("", func(t *testing.T) {
1677+
if tt.out == nil {
1678+
tt.out = tt.in
1679+
}
1680+
1681+
r, err := db.Query(fmt.Sprintf("SELECT $1::%s", tt.pgType), tt.in)
1682+
if err != nil {
1683+
t.Fatal(err)
1684+
}
1685+
defer r.Close()
1686+
1687+
if !r.Next() {
1688+
if r.Err() != nil {
1689+
t.Fatal(r.Err())
1690+
}
1691+
t.Fatal("expected row")
1692+
}
1693+
1694+
defer func() {
1695+
if r.Next() {
1696+
t.Fatal("unexpected row")
1697+
}
1698+
}()
1699+
1700+
have := reflect.New(reflect.TypeOf(tt.out))
1701+
if err := r.Scan(Array(have.Interface())); err != nil {
1702+
t.Fatal(err)
1703+
}
1704+
1705+
if !reflect.DeepEqual(tt.out, have.Elem().Interface()) {
1706+
t.Errorf("\nhave: %v\nwant %v", have, tt.out)
1707+
}
1708+
})
1709+
}
1710+
1711+
}

auth/kerberos/krb.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@ import (
55
"strings"
66
)
77

8-
/*
9-
* Find the A record associated with a hostname
10-
* In general, hostnames supplied to the driver should be
11-
* canonicalized because the KDC usually only has one
12-
* principal and not one per potential alias of a host.
13-
*/
8+
// Find the A record associated with a hostname In general, hostnames supplied
9+
// to the driver should be canonicalized because the KDC usually only has one
10+
// principal and not one per potential alias of a host.
1411
func canonicalizeHostname(host string) (string, error) {
1512
canon := host
1613

auth/kerberos/krb_unix.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//go:build !windows
22

3+
// UNIX Kerberos support, using jcmturner's pure-go implementation
4+
35
package kerberos
46

57
import (
@@ -14,11 +16,6 @@ import (
1416
"github.com/jcmturner/gokrb5/v8/spnego"
1517
)
1618

17-
/*
18-
* UNIX Kerberos support, using jcmturner's pure-go
19-
* implementation
20-
*/
21-
2219
// GSS implements the pq.GSS interface.
2320
type GSS struct {
2421
cli *client.Client

cmd/pqlisten/main.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Command pqlisten is a self-contained Go program which uses the LISTEN /
2+
// NOTIFY mechanism to avoid polling the database while waiting for more work to
3+
// arrive.
4+
//
5+
// You can see the program in action by defining a function similar to the
6+
// following:
7+
//
8+
// CREATE OR REPLACE FUNCTION public.get_work()
9+
// RETURNS bigint
10+
// LANGUAGE sql
11+
// AS $$
12+
// SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END
13+
// $$
14+
// ;
15+
package main
16+
17+
import (
18+
"database/sql"
19+
"fmt"
20+
"os"
21+
"strings"
22+
"time"
23+
24+
"github.com/lib/pq"
25+
)
26+
27+
func main() {
28+
db, err := sql.Open("postgres", strings.Join(os.Args[1:], " "))
29+
if err != nil {
30+
panic(err)
31+
}
32+
33+
reportProblem := func(ev pq.ListenerEventType, err error) {
34+
if err != nil {
35+
fmt.Fprintln(os.Stderr, err.Error())
36+
}
37+
}
38+
39+
var (
40+
minReconn = 10 * time.Second
41+
maxReconn = time.Minute
42+
listener = pq.NewListener("", minReconn, maxReconn, reportProblem)
43+
)
44+
err = listener.Listen("getwork")
45+
if err != nil {
46+
panic(err)
47+
}
48+
49+
fmt.Println("entering main loop")
50+
for {
51+
// process all available work before waiting for notifications
52+
getWork(db)
53+
waitForNotification(listener)
54+
}
55+
}
56+
57+
func doWork(db *sql.DB, work int64) {
58+
// work here
59+
fmt.Println("doing work...")
60+
}
61+
62+
func getWork(db *sql.DB) {
63+
for {
64+
// get work from the database here
65+
var work sql.NullInt64
66+
err := db.QueryRow("SELECT get_work()").Scan(&work)
67+
if err != nil {
68+
fmt.Println("call to get_work() failed: ", err)
69+
time.Sleep(10 * time.Second)
70+
continue
71+
}
72+
if !work.Valid {
73+
// no more work to do
74+
fmt.Println("ran out of work")
75+
return
76+
}
77+
78+
fmt.Println("starting work on ", work.Int64)
79+
go doWork(db, work.Int64)
80+
}
81+
}
82+
83+
func waitForNotification(l *pq.Listener) {
84+
select {
85+
case <-l.Notify:
86+
fmt.Println("received notification, new work available")
87+
case <-time.After(90 * time.Second):
88+
go l.Ping()
89+
// Check if there's more work available, just in case it takes a while
90+
// for the Listener to notice connection loss and reconnect.
91+
fmt.Println("received no work for 90 seconds, checking for new work")
92+
}
93+
}

conn.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"os"
1818
"os/user"
1919
"path/filepath"
20+
"reflect"
2021
"runtime"
2122
"strconv"
2223
"strings"
@@ -45,8 +46,13 @@ var (
4546

4647
// Compile time validation that our types implement the expected interfaces
4748
var (
48-
_ driver.Driver = Driver{}
49-
_ driver.Validator = &conn{}
49+
_ driver.Driver = Driver{}
50+
_ driver.Validator = (*conn)(nil)
51+
_ driver.NamedValueChecker = (*conn)(nil)
52+
_ driver.Pinger = (*conn)(nil)
53+
_ driver.SessionResetter = (*conn)(nil)
54+
_ driver.ExecerContext = (*conn)(nil)
55+
_ driver.QueryerContext = (*conn)(nil)
5056
)
5157

5258
// Driver is the Postgres database driver.
@@ -924,6 +930,31 @@ func toNamedValue(v []driver.Value) []driver.NamedValue {
924930
return v2
925931
}
926932

933+
// CheckNamedValue implements [driver.NamedValueChecker].
934+
func (c *conn) CheckNamedValue(nv *driver.NamedValue) error {
935+
if _, ok := nv.Value.(driver.Valuer); ok {
936+
// Ignore Valuer, for backward compatibility with pq.Array().
937+
return driver.ErrSkip
938+
}
939+
940+
// Ignoring []byte / []uint8.
941+
if _, ok := nv.Value.([]uint8); ok {
942+
return driver.ErrSkip
943+
}
944+
945+
v := reflect.ValueOf(nv.Value)
946+
if v.Kind() == reflect.Ptr {
947+
v = v.Elem()
948+
}
949+
if v.Kind() == reflect.Slice {
950+
var err error
951+
nv.Value, err = Array(v.Interface()).Value()
952+
return err
953+
}
954+
955+
return driver.ErrSkip
956+
}
957+
927958
// Implement the "Queryer" interface
928959
func (cn *conn) Query(query string, args []driver.Value) (driver.Rows, error) {
929960
return cn.query(query, toNamedValue(args))
@@ -2141,11 +2172,6 @@ func alnumLowerASCII(ch rune) rune {
21412172
return -1 // discard
21422173
}
21432174

2144-
// The database/sql/driver package says:
2145-
// All Conn implementations should implement the following interfaces: Pinger, SessionResetter, and Validator.
2146-
var _ driver.Pinger = &conn{}
2147-
var _ driver.SessionResetter = &conn{}
2148-
21492175
func (cn *conn) ResetSession(ctx context.Context) error {
21502176
// Ensure bad connections are reported: From database/sql/driver:
21512177
// If a connection is never returned to the connection pool but immediately reused, then

conn_go18.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import (
99
"time"
1010
)
1111

12-
const (
13-
watchCancelDialContextTimeout = time.Second * 10
14-
)
12+
const watchCancelDialContextTimeout = 10 * time.Second
1513

1614
// Implement the "QueryerContext" interface
1715
func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {

conn_go19.go

Lines changed: 0 additions & 34 deletions
This file was deleted.

conn_go19_test.go

Lines changed: 0 additions & 67 deletions
This file was deleted.

0 commit comments

Comments
 (0)