pgx/conn_test.go
Rick Snyder 6413491657 Add support for specifying sslmode in connection strings
Add tests for sslmode parameter when calling ParseURI.  Fix existing tests to work since default sslmode is 'prefer'

Make sure we default to prefer if sslmode is not provided in ParseDSN

Fix existing tests for ParseDSN to expect TLS configuration for prefer since prefer is the default sslmode; also, add tests for ParseDSN when specifying sslmode parameter on connection string
2015-08-19 11:33:12 -04:00

1028 lines
24 KiB
Go

package pgx_test
import (
"crypto/tls"
"fmt"
"net"
"os"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/jackc/pgx"
)
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(&currentDB)
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 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 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 TestConnectWithTLS(t *testing.T) {
t.Parallel()
if tlsConnConfig == nil {
return
}
conn, err := pgx.Connect(*tlsConnConfig)
if err != nil {
t.Fatal("Unable to establish connection: " + err.Error())
}
err = conn.Close()
if err != nil {
t.Fatal("Unable to close connection")
}
}
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 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 TestConnectWithMD5Password(t *testing.T) {
t.Parallel()
if md5ConnConfig == nil {
return
}
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 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 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 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 TestParseURI(t *testing.T) {
t.Parallel()
tests := []struct {
url string
connParams pgx.ConnConfig
}{
{
url: "postgres://jack:secret@localhost:5432/mydb?sslmode=prefer",
connParams: pgx.ConnConfig{
User: "jack",
Password: "secret",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
url: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable",
connParams: pgx.ConnConfig{
User: "jack",
Password: "secret",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: nil,
UseFallbackTLS: false,
FallbackTLSConfig: nil,
},
},
{
url: "postgres://jack:secret@localhost:5432/mydb",
connParams: pgx.ConnConfig{
User: "jack",
Password: "secret",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
url: "postgresql://jack:secret@localhost:5432/mydb",
connParams: pgx.ConnConfig{
User: "jack",
Password: "secret",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
url: "postgres://jack@localhost:5432/mydb",
connParams: pgx.ConnConfig{
User: "jack",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
url: "postgres://jack@localhost/mydb",
connParams: pgx.ConnConfig{
User: "jack",
Host: "localhost",
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
}
for i, tt := range tests {
connParams, err := pgx.ParseURI(tt.url)
if err != nil {
t.Errorf("%d. Unexpected error from pgx.ParseURL(%q) => %v", i, tt.url, err)
continue
}
if !reflect.DeepEqual(connParams, tt.connParams) {
t.Errorf("%d. expected %#v got %#v", i, tt.connParams, connParams)
}
}
}
func TestParseDSN(t *testing.T) {
t.Parallel()
tests := []struct {
url string
connParams pgx.ConnConfig
}{
{
url: "user=jack password=secret host=localhost port=5432 dbname=mydb sslmode=disable",
connParams: pgx.ConnConfig{
User: "jack",
Password: "secret",
Host: "localhost",
Port: 5432,
Database: "mydb",
},
},
{
url: "user=jack password=secret host=localhost port=5432 dbname=mydb sslmode=prefer",
connParams: pgx.ConnConfig{
User: "jack",
Password: "secret",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
url: "user=jack password=secret host=localhost port=5432 dbname=mydb",
connParams: pgx.ConnConfig{
User: "jack",
Password: "secret",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
url: "user=jack host=localhost port=5432 dbname=mydb",
connParams: pgx.ConnConfig{
User: "jack",
Host: "localhost",
Port: 5432,
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
url: "user=jack host=localhost dbname=mydb",
connParams: pgx.ConnConfig{
User: "jack",
Host: "localhost",
Database: "mydb",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
}
for i, tt := range tests {
connParams, err := pgx.ParseDSN(tt.url)
if err != nil {
t.Errorf("%d. Unexpected error from pgx.ParseDSN(%q) => %v", i, tt.url, err)
continue
}
if !reflect.DeepEqual(connParams, tt.connParams) {
t.Errorf("%d. expected %#v got %#v", i, tt.connParams, connParams)
}
}
}
func TestParseEnvLibpq(t *testing.T) {
pgEnvvars := []string{"PGHOST", "PGPORT", "PGDATABASE", "PGUSER", "PGPASSWORD"}
savedEnv := make(map[string]string)
for _, n := range pgEnvvars {
savedEnv[n] = os.Getenv(n)
}
defer func() {
for k, v := range savedEnv {
err := os.Setenv(k, v)
if err != nil {
t.Fatalf("Unable to restore environment:", err)
}
}
}()
tests := []struct {
name string
envvars map[string]string
config pgx.ConnConfig
}{
{
name: "No environment",
envvars: map[string]string{},
config: pgx.ConnConfig{
TLSConfig: &tls.Config{InsecureSkipVerify: true},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
name: "Normal PG vars",
envvars: map[string]string{
"PGHOST": "123.123.123.123",
"PGPORT": "7777",
"PGDATABASE": "foo",
"PGUSER": "bar",
"PGPASSWORD": "baz",
},
config: pgx.ConnConfig{
Host: "123.123.123.123",
Port: 7777,
Database: "foo",
User: "bar",
Password: "baz",
TLSConfig: &tls.Config{InsecureSkipVerify: true},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
name: "sslmode=disable",
envvars: map[string]string{
"PGSSLMODE": "disable",
},
config: pgx.ConnConfig{
TLSConfig: nil,
UseFallbackTLS: false,
},
},
{
name: "sslmode=allow",
envvars: map[string]string{
"PGSSLMODE": "allow",
},
config: pgx.ConnConfig{
TLSConfig: nil,
UseFallbackTLS: true,
FallbackTLSConfig: &tls.Config{InsecureSkipVerify: true},
},
},
{
name: "sslmode=prefer",
envvars: map[string]string{
"PGSSLMODE": "prefer",
},
config: pgx.ConnConfig{
TLSConfig: &tls.Config{InsecureSkipVerify: true},
UseFallbackTLS: true,
FallbackTLSConfig: nil,
},
},
{
name: "sslmode=require",
envvars: map[string]string{
"PGSSLMODE": "require",
},
config: pgx.ConnConfig{
TLSConfig: &tls.Config{},
UseFallbackTLS: false,
},
},
{
name: "sslmode=verify-ca",
envvars: map[string]string{
"PGSSLMODE": "verify-ca",
},
config: pgx.ConnConfig{
TLSConfig: &tls.Config{},
UseFallbackTLS: false,
},
},
{
name: "sslmode=verify-full",
envvars: map[string]string{
"PGSSLMODE": "verify-full",
},
config: pgx.ConnConfig{
TLSConfig: &tls.Config{},
UseFallbackTLS: false,
},
},
{
name: "sslmode=verify-full with host",
envvars: map[string]string{
"PGHOST": "pgx.example",
"PGSSLMODE": "verify-full",
},
config: pgx.ConnConfig{
Host: "pgx.example",
TLSConfig: &tls.Config{
ServerName: "pgx.example",
},
UseFallbackTLS: false,
},
},
}
for _, tt := range tests {
for _, n := range pgEnvvars {
err := os.Unsetenv(n)
if err != nil {
t.Fatalf("%s: Unable to clear environment:", tt.name, err)
}
}
for k, v := range tt.envvars {
err := os.Setenv(k, v)
if err != nil {
t.Fatalf("%s: Unable to set environment:", tt.name, err)
}
}
config, err := pgx.ParseEnvLibpq()
if err != nil {
t.Errorf("%s: Unexpected error from pgx.ParseLibpq() => %v", tt.name, err)
continue
}
if config.Host != tt.config.Host {
t.Errorf("%s: expected Host to be %v got %v", tt.name, tt.config.Host, config.Host)
}
if config.Port != tt.config.Port {
t.Errorf("%s: expected Port to be %v got %v", tt.name, tt.config.Port, config.Port)
}
if config.Port != tt.config.Port {
t.Errorf("%s: expected Port to be %v got %v", tt.name, tt.config.Port, config.Port)
}
if config.User != tt.config.User {
t.Errorf("%s: expected User to be %v got %v", tt.name, tt.config.User, config.User)
}
if config.Password != tt.config.Password {
t.Errorf("%s: expected Password to be %v got %v", tt.name, tt.config.Password, config.Password)
}
tlsTests := []struct {
name string
expected *tls.Config
actual *tls.Config
}{
{
name: "TLSConfig",
expected: tt.config.TLSConfig,
actual: config.TLSConfig,
},
{
name: "FallbackTLSConfig",
expected: tt.config.FallbackTLSConfig,
actual: config.FallbackTLSConfig,
},
}
for _, tlsTest := range tlsTests {
name := tlsTest.name
expected := tlsTest.expected
actual := tlsTest.actual
if expected == nil && actual != nil {
t.Errorf("%s / %s: expected nil, but it was set", tt.name, name)
} else if expected != nil && actual == nil {
t.Errorf("%s / %s: expected to be set, but got nil", tt.name, name)
} else if expected != nil && actual != nil {
if actual.InsecureSkipVerify != expected.InsecureSkipVerify {
t.Errorf("%s / %s: expected InsecureSkipVerify to be %v got %v", tt.name, name, expected.InsecureSkipVerify, actual.InsecureSkipVerify)
}
if actual.ServerName != expected.ServerName {
t.Errorf("%s / %s: expected ServerName to be %v got %v", tt.name, name, expected.ServerName, actual.ServerName)
}
}
}
if config.UseFallbackTLS != tt.config.UseFallbackTLS {
t.Errorf("%s: expected UseFallbackTLS to be %v got %v", tt.name, tt.config.UseFallbackTLS, config.UseFallbackTLS)
}
}
}
func TestExec(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
if results := mustExec(t, conn, "create temporary table foo(id integer primary key);"); results != "CREATE TABLE" {
t.Error("Unexpected results from Exec")
}
// Accept parameters
if results := mustExec(t, conn, "insert into foo(id) values($1)", 1); results != "INSERT 0 1" {
t.Errorf("Unexpected results from Exec: %v", results)
}
if results := mustExec(t, conn, "drop table foo;"); results != "DROP TABLE" {
t.Error("Unexpected results from Exec")
}
// Multiple statements can be executed -- last command tag is returned
if results := mustExec(t, conn, "create temporary table foo(id serial primary key); drop table foo;"); results != "DROP TABLE" {
t.Error("Unexpected results from Exec")
}
// Can execute longer SQL strings than sharedBufferSize
if results := mustExec(t, conn, strings.Repeat("select 42; ", 1000)); results != "SELECT 1" {
t.Errorf("Unexpected results from Exec: %v", results)
}
// Exec no-op which does not return a command tag
if results := mustExec(t, conn, "--;"); results != "" {
t.Errorf("Unexpected results from Exec: %v", results)
}
}
func TestExecFailure(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
if _, err := conn.Exec("selct;"); err == nil {
t.Fatal("Expected SQL syntax error")
}
rows, _ := conn.Query("select 1")
rows.Close()
if rows.Err() != nil {
t.Fatalf("Exec failure appears to have broken connection: %v", rows.Err())
}
}
func TestPrepare(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
_, err := conn.Prepare("test", "select $1::varchar")
if err != nil {
t.Errorf("Unable to prepare statement: %v", err)
return
}
var s string
err = conn.QueryRow("test", "hello").Scan(&s)
if err != nil {
t.Errorf("Executing prepared statement failed: %v", err)
}
if s != "hello" {
t.Errorf("Prepared statement did not return expected value: %v", s)
}
err = conn.Deallocate("test")
if err != nil {
t.Errorf("conn.Deallocate failed: %v", err)
}
// Create another prepared statement to ensure Deallocate left the connection
// in a working state and that we can reuse the prepared statement name.
_, err = conn.Prepare("test", "select $1::integer")
if err != nil {
t.Errorf("Unable to prepare statement: %v", err)
return
}
var n int32
err = conn.QueryRow("test", int32(1)).Scan(&n)
if err != nil {
t.Errorf("Executing prepared statement failed: %v", err)
}
if n != 1 {
t.Errorf("Prepared statement did not return expected value: %v", s)
}
err = conn.Deallocate("test")
if err != nil {
t.Errorf("conn.Deallocate failed: %v", err)
}
}
func TestPrepareBadSQLFailure(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
if _, err := conn.Prepare("badSQL", "select foo"); err == nil {
t.Fatal("Prepare should have failed with syntax error")
}
ensureConnValid(t, conn)
}
func TestPrepareQueryManyParameters(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
tests := []struct {
count int
succeed bool
}{
{
count: 65534,
succeed: true,
},
{
count: 65535,
succeed: true,
},
{
count: 65536,
succeed: false,
},
{
count: 65537,
succeed: false,
},
}
for i, tt := range tests {
params := make([]string, 0, tt.count)
args := make([]interface{}, 0, tt.count)
for j := 0; j < tt.count; j++ {
params = append(params, fmt.Sprintf("($%d::text)", j+1))
args = append(args, strconv.FormatInt(int64(j), 10))
}
sql := "values" + strings.Join(params, ", ")
psName := fmt.Sprintf("manyParams%d", i)
_, err := conn.Prepare(psName, sql)
if err != nil {
if tt.succeed {
t.Errorf("%d. %v", i, err)
}
continue
}
if !tt.succeed {
t.Errorf("%d. Expected error but succeeded", i)
continue
}
rows, err := conn.Query(psName, args...)
if err != nil {
t.Errorf("conn.Query failed: %v", err)
continue
}
for rows.Next() {
var s string
rows.Scan(&s)
}
if rows.Err() != nil {
t.Errorf("Reading query result failed: %v", err)
}
}
ensureConnValid(t, conn)
}
func TestListenNotify(t *testing.T) {
t.Parallel()
listener := mustConnect(t, *defaultConnConfig)
defer closeConn(t, listener)
if err := listener.Listen("chat"); err != nil {
t.Fatalf("Unable to start listening: %v", err)
}
notifier := mustConnect(t, *defaultConnConfig)
defer closeConn(t, notifier)
mustExec(t, notifier, "notify chat")
// when notification is waiting on the socket to be read
notification, err := listener.WaitForNotification(time.Second)
if err != nil {
t.Fatalf("Unexpected error on WaitForNotification: %v", err)
}
if notification.Channel != "chat" {
t.Errorf("Did not receive notification on expected channel: %v", notification.Channel)
}
// when notification has already been read during previous query
mustExec(t, notifier, "notify chat")
rows, _ := listener.Query("select 1")
rows.Close()
if rows.Err() != nil {
t.Fatalf("Unexpected error on Query: %v", rows.Err())
}
notification, err = listener.WaitForNotification(0)
if err != nil {
t.Fatalf("Unexpected error on WaitForNotification: %v", err)
}
if notification.Channel != "chat" {
t.Errorf("Did not receive notification on expected channel: %v", notification.Channel)
}
// when timeout occurs
notification, err = listener.WaitForNotification(time.Millisecond)
if err != pgx.ErrNotificationTimeout {
t.Errorf("WaitForNotification returned the wrong kind of error: %v", err)
}
if notification != nil {
t.Errorf("WaitForNotification returned an unexpected notification: %v", notification)
}
// listener can listen again after a timeout
mustExec(t, notifier, "notify chat")
notification, err = listener.WaitForNotification(time.Second)
if err != nil {
t.Fatalf("Unexpected error on WaitForNotification: %v", err)
}
if notification.Channel != "chat" {
t.Errorf("Did not receive notification on expected channel: %v", notification.Channel)
}
}
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 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 TestCommandTag(t *testing.T) {
t.Parallel()
var tests = []struct {
commandTag pgx.CommandTag
rowsAffected int64
}{
{commandTag: "INSERT 0 5", rowsAffected: 5},
{commandTag: "UPDATE 0", rowsAffected: 0},
{commandTag: "UPDATE 1", rowsAffected: 1},
{commandTag: "DELETE 0", rowsAffected: 0},
{commandTag: "DELETE 1", rowsAffected: 1},
{commandTag: "CREATE TABLE", rowsAffected: 0},
{commandTag: "ALTER TABLE", rowsAffected: 0},
{commandTag: "DROP TABLE", rowsAffected: 0},
}
for i, tt := range tests {
actual := tt.commandTag.RowsAffected()
if tt.rowsAffected != actual {
t.Errorf(`%d. "%s" should have affected %d rows but it was %d`, i, tt.commandTag, tt.rowsAffected, actual)
}
}
}
func TestInsertBoolArray(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
if results := mustExec(t, conn, "create temporary table foo(spice bool[]);"); results != "CREATE TABLE" {
t.Error("Unexpected results from Exec")
}
// Accept parameters
if results := mustExec(t, conn, "insert into foo(spice) values($1)", []bool{true, false, true}); results != "INSERT 0 1" {
t.Errorf("Unexpected results from Exec: %v", results)
}
}
func TestInsertTimestampArray(t *testing.T) {
t.Parallel()
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
if results := mustExec(t, conn, "create temporary table foo(spice timestamp[]);"); results != "CREATE TABLE" {
t.Error("Unexpected results from Exec")
}
// Accept parameters
if results := mustExec(t, conn, "insert into foo(spice) values($1)", []time.Time{time.Unix(1419143667, 0), time.Unix(1419143672, 0)}); results != "INSERT 0 1" {
t.Errorf("Unexpected results from Exec: %v", results)
}
}