func TestFatalTxError(t *testing.T) { t.Parallel() conn := mustConnect(t, *defaultConnConfig) defer closeConn(t, conn) otherConn, err := pgx.Connect(*defaultConnConfig) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } defer otherConn.Close() _, err = otherConn.Exec("select pg_terminate_backend($1)", conn.Pid) if err != nil { t.Fatalf("Unable to kill backend PostgreSQL process: %v", err) } _, err = conn.Query("select 1") if err == nil { t.Fatal("Expected error but none occurred") } if conn.IsAlive() { t.Fatal("Connection should not be live but was") } }
func TestFatalRxError(t *testing.T) { t.Parallel() conn := mustConnect(t, *defaultConnConfig) defer closeConn(t, conn) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() var n int32 var s string err := conn.QueryRow("select 1::int4, pg_sleep(10)::varchar").Scan(&n, &s) if err, ok := err.(pgx.PgError); !ok || err.Severity != "FATAL" { t.Fatalf("Expected QueryRow Scan to return fatal PgError, but instead received %v", err) } }() otherConn, err := pgx.Connect(*defaultConnConfig) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } defer otherConn.Close() if _, err := otherConn.Exec("select pg_terminate_backend($1)", conn.Pid); err != nil { t.Fatalf("Unable to kill backend PostgreSQL process: %v", err) } wg.Wait() if conn.IsAlive() { t.Fatal("Connection should not be live but was") } }
func TestConnectWithTLSFallback(t *testing.T) { t.Parallel() if tlsConnConfig == nil { return } connConfig := *tlsConnConfig connConfig.TLSConfig = &tls.Config{ServerName: "bogus.local"} // bogus ServerName should ensure certificate validation failure conn, err := pgx.Connect(connConfig) if err == nil { t.Fatal("Expected failed connection, but succeeded") } connConfig.UseFallbackTLS = true connConfig.FallbackTLSConfig = &tls.Config{InsecureSkipVerify: true} conn, err = pgx.Connect(connConfig) if err != nil { t.Fatal("Unable to establish connection: " + err.Error()) } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func TestConnectCustomDialer(t *testing.T) { t.Parallel() if customDialerConnConfig == nil { return } dialled := false conf := *customDialerConnConfig conf.Dial = func(network, address string) (net.Conn, error) { dialled = true return net.Dial(network, address) } conn, err := pgx.Connect(conf) if err != nil { t.Fatalf("Unable to establish connection: %s", err) } if !dialled { t.Fatal("Connect did not use custom dialer") } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func mustConnect(t testing.TB, config pgx.ConnConfig) *pgx.Conn { conn, err := pgx.Connect(config) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } return conn }
func SetupPostgres(dbname string) error { if os.Getenv("PGDATABASE") != "" { dbname = os.Getenv("PGDATABASE") } else { os.Setenv("PGDATABASE", dbname) } if os.Getenv("PGSSLMODE") == "" { os.Setenv("PGSSLMODE", "disable") } connConfig := pgx.ConnConfig{ Host: "/var/run/postgresql", Database: "postgres", } db, err := pgx.Connect(connConfig) if err != nil { return err } defer db.Close() if _, err := db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", dbname)); err != nil { return err } if _, err := db.Exec(fmt.Sprintf("CREATE DATABASE %s", dbname)); err != nil { return err } return nil }
func Example_CustomType() { conn, err := pgx.Connect(*defaultConnConfig) if err != nil { fmt.Printf("Unable to establish connection: %v", err) return } var p NullPoint err = conn.QueryRow("select null::point").Scan(&p) if err != nil { fmt.Println(err) return } fmt.Println(p) err = conn.QueryRow("select point(1.5,2.5)").Scan(&p) if err != nil { fmt.Println(err) return } fmt.Println(p) // Output: // null point // 1.5, 2.5 }
func TestConnectWithRuntimeParams(t *testing.T) { t.Parallel() connConfig := *defaultConnConfig connConfig.RuntimeParams = map[string]string{ "application_name": "pgxtest", "search_path": "myschema", } conn, err := pgx.Connect(connConfig) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } defer conn.Close() var s string err = conn.QueryRow("show application_name").Scan(&s) if err != nil { t.Fatalf("QueryRow Scan unexpectedly failed: %v", err) } if s != "pgxtest" { t.Errorf("Expected application_name to be %s, but it was %s", "pgxtest", s) } err = conn.QueryRow("show search_path").Scan(&s) if err != nil { t.Fatalf("QueryRow Scan unexpectedly failed: %v", err) } if s != "myschema" { t.Errorf("Expected search_path to be %s, but it was %s", "myschema", s) } }
func TestStressTLSConnection(t *testing.T) { t.Parallel() if tlsConnConfig == nil { t.Skip("Skipping due to undefined tlsConnConfig") } if testing.Short() { t.Skip("Skipping due to testing -short") } conn, err := pgx.Connect(*tlsConnConfig) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } defer conn.Close() for i := 0; i < 50; i++ { sql := `select * from generate_series(1, $1)` rows, err := conn.Query(sql, 2000000) if err != nil { t.Fatal(err) } var n int32 for rows.Next() { rows.Scan(&n) } if rows.Err() != nil { t.Fatalf("queryCount: %d, Row number: %d. %v", i, n, rows.Err()) } } }
func main() { var err error conn, err = pgx.Connect(extractConfig()) if err != nil { fmt.Fprintf(os.Stderr, "Unable to connection to database: %v\n", err) os.Exit(1) } if len(os.Args) == 1 { printHelp() os.Exit(0) } switch os.Args[1] { case "list": err = listTasks() if err != nil { fmt.Fprintf(os.Stderr, "Unable to list tasks: %v\n", err) os.Exit(1) } case "add": err = addTask(os.Args[2]) if err != nil { fmt.Fprintf(os.Stderr, "Unable to add task: %v\n", err) os.Exit(1) } case "update": n, err := strconv.ParseInt(os.Args[2], 10, 32) if err != nil { fmt.Fprintf(os.Stderr, "Unable convert task_num into int32: %v\n", err) os.Exit(1) } err = updateTask(int32(n), os.Args[3]) if err != nil { fmt.Fprintf(os.Stderr, "Unable to update task: %v\n", err) os.Exit(1) } case "remove": n, err := strconv.ParseInt(os.Args[2], 10, 32) if err != nil { fmt.Fprintf(os.Stderr, "Unable convert task_num into int32: %v\n", err) os.Exit(1) } err = removeTask(int32(n)) if err != nil { fmt.Fprintf(os.Stderr, "Unable to remove task: %v\n", err) os.Exit(1) } default: fmt.Fprintln(os.Stderr, "Invalid command") printHelp() os.Exit(1) } }
func connect(c *C, n int, db string) *pgx.Conn { conn, err := pgx.Connect(pgx.ConnConfig{ Host: "127.0.0.1", Port: 54320 + uint16(n), User: "******", Password: "******", Database: db, }) c.Assert(err, IsNil) return conn }
func connect(c *C, s state.Database, db string) *pgx.Conn { port, _ := strconv.Atoi(s.(*Postgres).port) conn, err := pgx.Connect(pgx.ConnConfig{ Host: "127.0.0.1", Port: uint16(port), User: "******", Password: "******", Database: db, }) c.Assert(err, IsNil) return conn }
func TestConnectWithConnectionRefused(t *testing.T) { t.Parallel() // Presumably nothing is listening on 127.0.0.1:1 bad := *defaultConnConfig bad.Host = "127.0.0.1" bad.Port = 1 _, err := pgx.Connect(bad) if err == nil { t.Fatal("Expected error establishing connection to bad port") } }
func TestConnectWithInvalidUser(t *testing.T) { t.Parallel() if invalidUserConnConfig == nil { return } _, err := pgx.Connect(*invalidUserConnConfig) pgErr, ok := err.(pgx.PgError) if !ok { t.Fatalf("Expected to receive a PgError with code 28000, instead received: %v", err) } if pgErr.Code != "28000" && pgErr.Code != "28P01" { t.Fatalf("Expected to receive a PgError with code 28000 or 28P01, instead received: %v", pgErr) } }
func TestConnectWithMD5Password(t *testing.T) { t.Parallel() if md5ConnConfig == nil { t.Skip("Skipping due to undefined md5ConnConfig") } conn, err := pgx.Connect(*md5ConnConfig) if err != nil { t.Fatal("Unable to establish connection: " + err.Error()) } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func TestConnectWithTcp(t *testing.T) { t.Parallel() if tcpConnConfig == nil { return } conn, err := pgx.Connect(*tcpConnConfig) if err != nil { t.Fatal("Unable to establish connection: " + err.Error()) } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func TestConnectWithPlainTextPassword(t *testing.T) { t.Parallel() if plainPasswordConnConfig == nil { return } conn, err := pgx.Connect(*plainPasswordConnConfig) if err != nil { t.Fatal("Unable to establish connection: " + err.Error()) } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func TestConnectWithUnixSocketDirectory(t *testing.T) { t.Parallel() // /.s.PGSQL.5432 if unixSocketConnConfig == nil { return } conn, err := pgx.Connect(*unixSocketConnConfig) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func TestConnectWithUnixSocketFile(t *testing.T) { t.Parallel() if unixSocketConnConfig == nil { return } connParams := *unixSocketConnConfig connParams.Host = connParams.Host + "/.s.PGSQL.5432" conn, err := pgx.Connect(connParams) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func TestConnect(t *testing.T) { t.Parallel() conn, err := pgx.Connect(*defaultConnConfig) if err != nil { t.Fatalf("Unable to establish connection: %v", err) } if _, present := conn.RuntimeParams["server_version"]; !present { t.Error("Runtime parameters not stored") } if conn.Pid == 0 { t.Error("Backend PID not stored") } if conn.SecretKey == 0 { t.Error("Backend secret key not stored") } var currentDB string err = conn.QueryRow("select current_database()").Scan(¤tDB) if err != nil { t.Fatalf("QueryRow Scan unexpectedly failed: %v", err) } if currentDB != defaultConnConfig.Database { t.Errorf("Did not connect to specified database (%v)", defaultConnConfig.Database) } var user string err = conn.QueryRow("select current_user").Scan(&user) if err != nil { t.Fatalf("QueryRow Scan unexpectedly failed: %v", err) } if user != defaultConnConfig.User { t.Errorf("Did not connect as specified user (%v)", defaultConnConfig.User) } err = conn.Close() if err != nil { t.Fatal("Unable to close connection") } }
func (d *Driver) Open(name string) (driver.Conn, error) { if d.Pool != nil { conn, err := d.Pool.Acquire() if err != nil { return nil, err } return &Conn{conn: conn, pool: d.Pool}, nil } connConfig, err := pgx.ParseURI(name) if err != nil { return nil, err } conn, err := pgx.Connect(connConfig) if err != nil { return nil, err } c := &Conn{conn: conn} return c, nil }
func Example_JSON() { conn, err := pgx.Connect(*defaultConnConfig) if err != nil { fmt.Printf("Unable to establish connection: %v", err) return } if _, ok := conn.PgTypes[pgx.JsonOid]; !ok { // No JSON type -- must be running against very old PostgreSQL // Pretend it works fmt.Println("John", 42) return } type person struct { Name string `json:"name"` Age int `json:"age"` } input := person{ Name: "John", Age: 42, } var output person err = conn.QueryRow("select $1::json", input).Scan(&output) if err != nil { fmt.Println(err) return } fmt.Println(output.Name, output.Age) // Output: // John 42 }
func (s *S) TestHTTPResync(c *C) { var connPids []int32 var cmu sync.Mutex poolConfig := newPgxConnPoolConfig() poolConfig.AfterConnect = func(conn *pgx.Conn) error { cmu.Lock() defer cmu.Unlock() connPids = append(connPids, conn.Pid) return nil } pgxpool, err := pgx.NewConnPool(poolConfig) if err != nil { c.Fatal(err) } l := &HTTPListener{ Addr: "127.0.0.1:0", ds: NewPostgresDataStore("http", pgxpool), discoverd: s.discoverd, } if err := l.Start(); err != nil { c.Fatal(err) } defer l.Close() srv := httptest.NewServer(httpTestHandler("1")) srv2 := httptest.NewServer(httpTestHandler("2")) defer srv.Close() defer srv2.Close() route := addRoute(c, l, router.HTTPRoute{ Domain: "example.com", Service: "example-com", }.ToRoute()) discoverdRegisterHTTPService(c, l, "example-com", srv.Listener.Addr().String()) assertGet(c, "http://"+l.Addr, "example.com", "1") // testing hooks presyncc := make(chan struct{}) l.preSync = func() { <-presyncc } postsyncc := make(chan struct{}) l.postSync = func(startc <-chan struct{}) { <-startc close(postsyncc) } terminateAllPids := func() { cmu.Lock() defer cmu.Unlock() // grab a fresh conn conn, err := pgx.Connect(poolConfig.ConnConfig) if err != nil { c.Fatal(err) } defer conn.Close() // terminate all conns from the pool for _, pid := range connPids { if _, err := conn.Exec("select pg_terminate_backend($1)", pid); err != nil { c.Fatalf("Unable to kill backend PostgreSQL process: %v", err) } } connPids = connPids[:0] } terminateAllPids() // Because we terminated all of the connections some will be dead, others // will become dead when we try to write to them. attempts := 0 for { if attempts > 5 { c.Fatal(fmt.Errorf("Unable to remove route after disconnecting sync")) } err = l.RemoveRoute(route.ID) if e, ok := err.(*net.OpError); ok { if ee, ok := e.Err.(*os.SyscallError); ok && ee.Err == syscall.EPIPE || e.Err == syscall.EPIPE { attempts++ continue } } if err == pgx.ErrDeadConn { attempts++ continue } c.Assert(err, IsNil) break } // Also add a new route err = l.AddRoute(router.HTTPRoute{ Domain: "example.org", Service: "example-org", }.ToRoute()) c.Assert(err, IsNil) // trigger the reconnect close(presyncc) // wait for the sync to complete <-postsyncc // ensure that route was actually removed res, err := httpClient.Do(newReq("http://"+l.Addr, "example.com")) c.Assert(err, IsNil) c.Assert(res.StatusCode, Equals, 404) res.Body.Close() // ensure that new route was added and traffic being directed discoverdRegisterHTTPService(c, l, "example-org", srv2.Listener.Addr().String()) assertGet(c, "http://"+l.Addr, "example.org", "2") }
// Test the race condition in LockJob func TestLockJobAdvisoryRace(t *testing.T) { c := openTestClientMaxConns(t, 2) defer truncateAndClose(c.pool) // *pgx.ConnPool doesn't support pools of only one connection. Make sure // the other one is busy so we know which backend will be used by LockJob // below. unusedConn, err := c.pool.Acquire() if err != nil { t.Fatal(err) } // We use two jobs: the first one is concurrently deleted, and the second // one is returned by LockJob after recovering from the race condition. for i := 0; i < 2; i++ { if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil { t.Fatal(err) } } // helper functions newConn := func() *pgx.Conn { conn, err := pgx.Connect(testConnConfig) if err != nil { panic(err) } return conn } getBackendID := func(conn *pgx.Conn) int32 { var backendID int32 err := conn.QueryRow(` SELECT backendid FROM pg_stat_get_backend_idset() psgb(backendid) WHERE pg_stat_get_backend_pid(psgb.backendid) = pg_backend_pid() `).Scan(&backendID) if err != nil { panic(err) } return backendID } waitUntilBackendIsWaiting := func(backendID int32, name string) { conn := newConn() i := 0 for { var waiting bool err := conn.QueryRow(`SELECT pg_stat_get_backend_waiting($1)`, backendID).Scan(&waiting) if err != nil { panic(err) } if waiting { break } else { i++ if i >= 10000/50 { panic(fmt.Sprintf("timed out while waiting for %s", name)) } time.Sleep(50 * time.Millisecond) } } } // Reproducing the race condition is a bit tricky. The idea is to form a // lock queue on the relation that looks like this: // // AccessExclusive <- AccessShare <- AccessExclusive ( <- AccessShare ) // // where the leftmost AccessShare lock is the one implicitly taken by the // sqlLockJob query. Once we release the leftmost AccessExclusive lock // without releasing the rightmost one, the session holding the rightmost // AccessExclusiveLock can run the necessary DELETE before the sqlCheckJob // query runs (since it'll be blocked behind the rightmost AccessExclusive // Lock). // deletedJobIDChan := make(chan int64, 1) lockJobBackendIDChan := make(chan int32) secondAccessExclusiveBackendIDChan := make(chan int32) go func() { conn := newConn() defer conn.Close() tx, err := conn.Begin() if err != nil { panic(err) } _, err = tx.Exec(`LOCK TABLE que_jobs IN ACCESS EXCLUSIVE MODE`) if err != nil { panic(err) } // first wait for LockJob to appear behind us backendID := <-lockJobBackendIDChan waitUntilBackendIsWaiting(backendID, "LockJob") // then for the AccessExclusive lock to appear behind that one backendID = <-secondAccessExclusiveBackendIDChan waitUntilBackendIsWaiting(backendID, "second access exclusive lock") err = tx.Rollback() if err != nil { panic(err) } }() go func() { conn := newConn() defer conn.Close() // synchronization point secondAccessExclusiveBackendIDChan <- getBackendID(conn) tx, err := conn.Begin() if err != nil { panic(err) } _, err = tx.Exec(`LOCK TABLE que_jobs IN ACCESS EXCLUSIVE MODE`) if err != nil { panic(err) } // Fake a concurrent transaction grabbing the job var jid int64 err = tx.QueryRow(` DELETE FROM que_jobs WHERE job_id = (SELECT min(job_id) FROM que_jobs) RETURNING job_id `).Scan(&jid) if err != nil { panic(err) } deletedJobIDChan <- jid err = tx.Commit() if err != nil { panic(err) } }() conn, err := c.pool.Acquire() if err != nil { panic(err) } ourBackendID := getBackendID(conn) c.pool.Release(conn) // synchronization point lockJobBackendIDChan <- ourBackendID // release the unused connection once the locked job has acquired the // other connection so it can be used by the job's LockedUntil goroutine. go func() { for { if c.pool.Stat().AvailableConnections == 0 { c.pool.Release(unusedConn) return } time.Sleep(50 * time.Millisecond) } }() job, err := c.LockJob("") if err != nil { panic(err) } defer job.Done() deletedJobID := <-deletedJobIDChan t.Logf("Got id %d", job.ID) t.Logf("Concurrently deleted id %d", deletedJobID) if deletedJobID >= job.ID { t.Fatalf("deleted job id %d must be smaller than job.ID %d", deletedJobID, job.ID) } }
func TestLargeObjects(t *testing.T) { t.Parallel() conn, err := pgx.Connect(*defaultConnConfig) if err != nil { t.Fatal(err) } tx, err := conn.Begin() if err != nil { t.Fatal(err) } lo, err := tx.LargeObjects() if err != nil { t.Fatal(err) } id, err := lo.Create(0) if err != nil { t.Fatal(err) } obj, err := lo.Open(id, pgx.LargeObjectModeRead|pgx.LargeObjectModeWrite) if err != nil { t.Fatal(err) } n, err := obj.Write([]byte("testing")) if err != nil { t.Fatal(err) } if n != 7 { t.Errorf("Expected n to be 7, got %d", n) } pos, err := obj.Seek(1, 0) if err != nil { t.Fatal(err) } if pos != 1 { t.Errorf("Expected pos to be 1, got %d", pos) } res := make([]byte, 6) n, err = obj.Read(res) if err != nil { t.Fatal(err) } if string(res) != "esting" { t.Errorf(`Expected res to be "esting", got %q`, res) } if n != 6 { t.Errorf("Expected n to be 6, got %d", n) } n, err = obj.Read(res) if err != io.EOF { t.Error("Expected io.EOF, go nil") } if n != 0 { t.Errorf("Expected n to be 0, got %d", n) } pos, err = obj.Tell() if err != nil { t.Fatal(err) } if pos != 7 { t.Errorf("Expected pos to be 7, got %d", pos) } err = obj.Truncate(1) if err != nil { t.Fatal(err) } pos, err = obj.Seek(-1, 2) if err != nil { t.Fatal(err) } if pos != 0 { t.Errorf("Expected pos to be 0, got %d", pos) } res = make([]byte, 2) n, err = obj.Read(res) if err != io.EOF { t.Errorf("Expected err to be io.EOF, got %v", err) } if n != 1 { t.Errorf("Expected n to be 1, got %d", n) } if res[0] != 't' { t.Errorf("Expected res[0] to be 't', got %v", res[0]) } err = obj.Close() if err != nil { t.Fatal(err) } err = lo.Unlink(id) if err != nil { t.Fatal(err) } _, err = lo.Open(id, pgx.LargeObjectModeRead) if e, ok := err.(pgx.PgError); !ok || e.Code != "42704" { t.Errorf("Expected undefined_object error (42704), got %#v", err) } }