github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/lib/pq/encode_test.go (about)

     1  package pq
     2  
     3  import (
     4  	"bytes"
     5  	"database/sql"
     6  	"fmt"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/insionng/yougam/libraries/lib/pq/oid"
    12  )
    13  
    14  func TestScanTimestamp(t *testing.T) {
    15  	var nt NullTime
    16  	tn := time.Now()
    17  	nt.Scan(tn)
    18  	if !nt.Valid {
    19  		t.Errorf("Expected Valid=false")
    20  	}
    21  	if nt.Time != tn {
    22  		t.Errorf("Time value mismatch")
    23  	}
    24  }
    25  
    26  func TestScanNilTimestamp(t *testing.T) {
    27  	var nt NullTime
    28  	nt.Scan(nil)
    29  	if nt.Valid {
    30  		t.Errorf("Expected Valid=false")
    31  	}
    32  }
    33  
    34  var timeTests = []struct {
    35  	str     string
    36  	timeval time.Time
    37  }{
    38  	{"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
    39  	{"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
    40  	{"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
    41  	{"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))},
    42  	{"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))},
    43  	{"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))},
    44  	{"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))},
    45  	{"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))},
    46  	{"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
    47  	{"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))},
    48  	{"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
    49  	{"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))},
    50  	{"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))},
    51  	{"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))},
    52  	{"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000,
    53  		time.FixedZone("", -7*60*60))},
    54  	{"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
    55  		time.FixedZone("", -7*60*60))},
    56  	{"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0,
    57  		time.FixedZone("", -(7*60*60+42*60)))},
    58  	{"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0,
    59  		time.FixedZone("", -(7*60*60+30*60+9)))},
    60  	{"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
    61  		time.FixedZone("", 7*60*60))},
    62  	{"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
    63  	{"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
    64  	{"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000,
    65  		time.FixedZone("", -7*60*60))},
    66  	{"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
    67  	{"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
    68  	{"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
    69  	{"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
    70  	{"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
    71  	{"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
    72  	{"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
    73  }
    74  
    75  // Test that parsing the string results in the expected value.
    76  func TestParseTs(t *testing.T) {
    77  	for i, tt := range timeTests {
    78  		val, err := ParseTimestamp(nil, tt.str)
    79  		if err != nil {
    80  			t.Errorf("%d: got error: %v", i, err)
    81  		} else if val.String() != tt.timeval.String() {
    82  			t.Errorf("%d: expected to parse %q into %q; got %q",
    83  				i, tt.str, tt.timeval, val)
    84  		}
    85  	}
    86  }
    87  
    88  var timeErrorTests = []string{
    89  	"2001",
    90  	"2001-2-03",
    91  	"2001-02-3",
    92  	"2001-02-03 ",
    93  	"2001-02-03 04",
    94  	"2001-02-03 04:",
    95  	"2001-02-03 04:05",
    96  	"2001-02-03 04:05:",
    97  	"2001-02-03 04:05:6",
    98  	"2001-02-03 04:05:06.123 B",
    99  }
   100  
   101  // Test that parsing the string results in an error.
   102  func TestParseTsErrors(t *testing.T) {
   103  	for i, tt := range timeErrorTests {
   104  		_, err := ParseTimestamp(nil, tt)
   105  		if err == nil {
   106  			t.Errorf("%d: expected an error from parsing: %v", i, tt)
   107  		}
   108  	}
   109  }
   110  
   111  // Now test that sending the value into the database and parsing it back
   112  // returns the same time.Time value.
   113  func TestEncodeAndParseTs(t *testing.T) {
   114  	db, err := openTestConnConninfo("timezone='Etc/UTC'")
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	defer db.Close()
   119  
   120  	for i, tt := range timeTests {
   121  		var dbstr string
   122  		err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr)
   123  		if err != nil {
   124  			t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err)
   125  			continue
   126  		}
   127  
   128  		val, err := ParseTimestamp(nil, dbstr)
   129  		if err != nil {
   130  			t.Errorf("%d: could not parse value %q: %s", i, dbstr, err)
   131  			continue
   132  		}
   133  		val = val.In(tt.timeval.Location())
   134  		if val.String() != tt.timeval.String() {
   135  			t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val)
   136  		}
   137  	}
   138  }
   139  
   140  var formatTimeTests = []struct {
   141  	time     time.Time
   142  	expected string
   143  }{
   144  	{time.Time{}, "0001-01-01T00:00:00Z"},
   145  	{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"},
   146  	{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"},
   147  	{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"},
   148  	{time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"},
   149  
   150  	{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z"},
   151  	{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00"},
   152  	{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00"},
   153  
   154  	{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z BC"},
   155  	{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00 BC"},
   156  	{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00 BC"},
   157  
   158  	{time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09"},
   159  	{time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09 BC"},
   160  }
   161  
   162  func TestFormatTs(t *testing.T) {
   163  	for i, tt := range formatTimeTests {
   164  		val := string(formatTs(tt.time))
   165  		if val != tt.expected {
   166  			t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected)
   167  		}
   168  	}
   169  }
   170  
   171  func TestTimestampWithTimeZone(t *testing.T) {
   172  	db := openTestConn(t)
   173  	defer db.Close()
   174  
   175  	tx, err := db.Begin()
   176  	if err != nil {
   177  		t.Fatal(err)
   178  	}
   179  	defer tx.Rollback()
   180  
   181  	// try several different locations, all included in Go's zoneinfo.zip
   182  	for _, locName := range []string{
   183  		"UTC",
   184  		"America/Chicago",
   185  		"America/New_York",
   186  		"Australia/Darwin",
   187  		"Australia/Perth",
   188  	} {
   189  		loc, err := time.LoadLocation(locName)
   190  		if err != nil {
   191  			t.Logf("Could not load time zone %s - skipping", locName)
   192  			continue
   193  		}
   194  
   195  		// Postgres timestamps have a resolution of 1 microsecond, so don't
   196  		// use the full range of the Nanosecond argument
   197  		refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc)
   198  
   199  		for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} {
   200  			// Switch Postgres's timezone to test different output timestamp formats
   201  			_, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone))
   202  			if err != nil {
   203  				t.Fatal(err)
   204  			}
   205  
   206  			var gotTime time.Time
   207  			row := tx.QueryRow("select $1::timestamp with time zone", refTime)
   208  			err = row.Scan(&gotTime)
   209  			if err != nil {
   210  				t.Fatal(err)
   211  			}
   212  
   213  			if !refTime.Equal(gotTime) {
   214  				t.Errorf("timestamps not equal: %s != %s", refTime, gotTime)
   215  			}
   216  
   217  			// check that the time zone is set correctly based on TimeZone
   218  			pgLoc, err := time.LoadLocation(pgTimeZone)
   219  			if err != nil {
   220  				t.Logf("Could not load time zone %s - skipping", pgLoc)
   221  				continue
   222  			}
   223  			translated := refTime.In(pgLoc)
   224  			if translated.String() != gotTime.String() {
   225  				t.Errorf("timestamps not equal: %s != %s", translated, gotTime)
   226  			}
   227  		}
   228  	}
   229  }
   230  
   231  func TestTimestampWithOutTimezone(t *testing.T) {
   232  	db := openTestConn(t)
   233  	defer db.Close()
   234  
   235  	test := func(ts, pgts string) {
   236  		r, err := db.Query("SELECT $1::timestamp", pgts)
   237  		if err != nil {
   238  			t.Fatalf("Could not run query: %v", err)
   239  		}
   240  
   241  		n := r.Next()
   242  
   243  		if n != true {
   244  			t.Fatal("Expected at least one row")
   245  		}
   246  
   247  		var result time.Time
   248  		err = r.Scan(&result)
   249  		if err != nil {
   250  			t.Fatalf("Did not expect error scanning row: %v", err)
   251  		}
   252  
   253  		expected, err := time.Parse(time.RFC3339, ts)
   254  		if err != nil {
   255  			t.Fatalf("Could not parse test time literal: %v", err)
   256  		}
   257  
   258  		if !result.Equal(expected) {
   259  			t.Fatalf("Expected time to match %v: got mismatch %v",
   260  				expected, result)
   261  		}
   262  
   263  		n = r.Next()
   264  		if n != false {
   265  			t.Fatal("Expected only one row")
   266  		}
   267  	}
   268  
   269  	test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00")
   270  
   271  	// Test higher precision time
   272  	test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033")
   273  }
   274  
   275  func TestInfinityTimestamp(t *testing.T) {
   276  	db := openTestConn(t)
   277  	defer db.Close()
   278  	var err error
   279  	var resultT time.Time
   280  
   281  	expectedErrorStrPrefix := `sql: Scan error on column index 0: unsupported`
   282  	type testCases []struct {
   283  		Query                string
   284  		Param                string
   285  		ExpectedErrStrPrefix string
   286  		ExpectedVal          interface{}
   287  	}
   288  	tc := testCases{
   289  		{"SELECT $1::timestamp", "-infinity", expectedErrorStrPrefix, "-infinity"},
   290  		{"SELECT $1::timestamptz", "-infinity", expectedErrorStrPrefix, "-infinity"},
   291  		{"SELECT $1::timestamp", "infinity", expectedErrorStrPrefix, "infinity"},
   292  		{"SELECT $1::timestamptz", "infinity", expectedErrorStrPrefix, "infinity"},
   293  	}
   294  	// try to assert []byte to time.Time
   295  	for _, q := range tc {
   296  		err = db.QueryRow(q.Query, q.Param).Scan(&resultT)
   297  		if !strings.HasPrefix(err.Error(), q.ExpectedErrStrPrefix) {
   298  			t.Errorf("Scanning -/+infinity, expected error to have prefix %q, got %q", q.ExpectedErrStrPrefix, err)
   299  		}
   300  	}
   301  	// yield []byte
   302  	for _, q := range tc {
   303  		var resultI interface{}
   304  		err = db.QueryRow(q.Query, q.Param).Scan(&resultI)
   305  		if err != nil {
   306  			t.Errorf("Scanning -/+infinity, expected no error, got %q", err)
   307  		}
   308  		result, ok := resultI.([]byte)
   309  		if !ok {
   310  			t.Errorf("Scanning -/+infinity, expected []byte, got %#v", resultI)
   311  		}
   312  		if string(result) != q.ExpectedVal {
   313  			t.Errorf("Scanning -/+infinity, expected %q, got %q", q.ExpectedVal, result)
   314  		}
   315  	}
   316  
   317  	y1500 := time.Date(1500, time.January, 1, 0, 0, 0, 0, time.UTC)
   318  	y2500 := time.Date(2500, time.January, 1, 0, 0, 0, 0, time.UTC)
   319  	EnableInfinityTs(y1500, y2500)
   320  
   321  	err = db.QueryRow("SELECT $1::timestamp", "infinity").Scan(&resultT)
   322  	if err != nil {
   323  		t.Errorf("Scanning infinity, expected no error, got %q", err)
   324  	}
   325  	if !resultT.Equal(y2500) {
   326  		t.Errorf("Scanning infinity, expected %q, got %q", y2500, resultT)
   327  	}
   328  
   329  	err = db.QueryRow("SELECT $1::timestamptz", "infinity").Scan(&resultT)
   330  	if err != nil {
   331  		t.Errorf("Scanning infinity, expected no error, got %q", err)
   332  	}
   333  	if !resultT.Equal(y2500) {
   334  		t.Errorf("Scanning Infinity, expected time %q, got %q", y2500, resultT.String())
   335  	}
   336  
   337  	err = db.QueryRow("SELECT $1::timestamp", "-infinity").Scan(&resultT)
   338  	if err != nil {
   339  		t.Errorf("Scanning -infinity, expected no error, got %q", err)
   340  	}
   341  	if !resultT.Equal(y1500) {
   342  		t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
   343  	}
   344  
   345  	err = db.QueryRow("SELECT $1::timestamptz", "-infinity").Scan(&resultT)
   346  	if err != nil {
   347  		t.Errorf("Scanning -infinity, expected no error, got %q", err)
   348  	}
   349  	if !resultT.Equal(y1500) {
   350  		t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
   351  	}
   352  
   353  	y_1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC)
   354  	y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC)
   355  	var s string
   356  	err = db.QueryRow("SELECT $1::timestamp::text", y_1500).Scan(&s)
   357  	if err != nil {
   358  		t.Errorf("Encoding -infinity, expected no error, got %q", err)
   359  	}
   360  	if s != "-infinity" {
   361  		t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
   362  	}
   363  	err = db.QueryRow("SELECT $1::timestamptz::text", y_1500).Scan(&s)
   364  	if err != nil {
   365  		t.Errorf("Encoding -infinity, expected no error, got %q", err)
   366  	}
   367  	if s != "-infinity" {
   368  		t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
   369  	}
   370  
   371  	err = db.QueryRow("SELECT $1::timestamp::text", y11500).Scan(&s)
   372  	if err != nil {
   373  		t.Errorf("Encoding infinity, expected no error, got %q", err)
   374  	}
   375  	if s != "infinity" {
   376  		t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
   377  	}
   378  	err = db.QueryRow("SELECT $1::timestamptz::text", y11500).Scan(&s)
   379  	if err != nil {
   380  		t.Errorf("Encoding infinity, expected no error, got %q", err)
   381  	}
   382  	if s != "infinity" {
   383  		t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
   384  	}
   385  
   386  	disableInfinityTs()
   387  
   388  	var panicErrorString string
   389  	func() {
   390  		defer func() {
   391  			panicErrorString, _ = recover().(string)
   392  		}()
   393  		EnableInfinityTs(y2500, y1500)
   394  	}()
   395  	if panicErrorString != infinityTsNegativeMustBeSmaller {
   396  		t.Errorf("Expected error, %q, got %q", infinityTsNegativeMustBeSmaller, panicErrorString)
   397  	}
   398  }
   399  
   400  func TestStringWithNul(t *testing.T) {
   401  	db := openTestConn(t)
   402  	defer db.Close()
   403  
   404  	hello0world := string("hello\x00world")
   405  	_, err := db.Query("SELECT $1::text", &hello0world)
   406  	if err == nil {
   407  		t.Fatal("Postgres accepts a string with nul in it; " +
   408  			"injection attacks may be plausible")
   409  	}
   410  }
   411  
   412  func TestByteSliceToText(t *testing.T) {
   413  	db := openTestConn(t)
   414  	defer db.Close()
   415  
   416  	b := []byte("hello world")
   417  	row := db.QueryRow("SELECT $1::text", b)
   418  
   419  	var result []byte
   420  	err := row.Scan(&result)
   421  	if err != nil {
   422  		t.Fatal(err)
   423  	}
   424  
   425  	if string(result) != string(b) {
   426  		t.Fatalf("expected %v but got %v", b, result)
   427  	}
   428  }
   429  
   430  func TestStringToBytea(t *testing.T) {
   431  	db := openTestConn(t)
   432  	defer db.Close()
   433  
   434  	b := "hello world"
   435  	row := db.QueryRow("SELECT $1::bytea", b)
   436  
   437  	var result []byte
   438  	err := row.Scan(&result)
   439  	if err != nil {
   440  		t.Fatal(err)
   441  	}
   442  
   443  	if !bytes.Equal(result, []byte(b)) {
   444  		t.Fatalf("expected %v but got %v", b, result)
   445  	}
   446  }
   447  
   448  func TestTextByteSliceToUUID(t *testing.T) {
   449  	db := openTestConn(t)
   450  	defer db.Close()
   451  
   452  	b := []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
   453  	row := db.QueryRow("SELECT $1::uuid", b)
   454  
   455  	var result string
   456  	err := row.Scan(&result)
   457  	if forceBinaryParameters() {
   458  		pqErr := err.(*Error)
   459  		if pqErr == nil {
   460  			t.Errorf("Expected to get error")
   461  		} else if pqErr.Code != "22P03" {
   462  			t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
   463  		}
   464  	} else {
   465  		if err != nil {
   466  			t.Fatal(err)
   467  		}
   468  
   469  		if result != string(b) {
   470  			t.Fatalf("expected %v but got %v", b, result)
   471  		}
   472  	}
   473  }
   474  
   475  func TestBinaryByteSlicetoUUID(t *testing.T) {
   476  	db := openTestConn(t)
   477  	defer db.Close()
   478  
   479  	b := []byte{'\xa0', '\xee', '\xbc', '\x99',
   480  		'\x9c', '\x0b',
   481  		'\x4e', '\xf8',
   482  		'\xbb', '\x00', '\x6b',
   483  		'\xb9', '\xbd', '\x38', '\x0a', '\x11'}
   484  	row := db.QueryRow("SELECT $1::uuid", b)
   485  
   486  	var result string
   487  	err := row.Scan(&result)
   488  	if forceBinaryParameters() {
   489  		if err != nil {
   490  			t.Fatal(err)
   491  		}
   492  
   493  		if result != string("a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11") {
   494  			t.Fatalf("expected %v but got %v", b, result)
   495  		}
   496  	} else {
   497  		pqErr := err.(*Error)
   498  		if pqErr == nil {
   499  			t.Errorf("Expected to get error")
   500  		} else if pqErr.Code != "22021" {
   501  			t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
   502  		}
   503  	}
   504  }
   505  
   506  func TestStringToUUID(t *testing.T) {
   507  	db := openTestConn(t)
   508  	defer db.Close()
   509  
   510  	s := "a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11"
   511  	row := db.QueryRow("SELECT $1::uuid", s)
   512  
   513  	var result string
   514  	err := row.Scan(&result)
   515  	if err != nil {
   516  		t.Fatal(err)
   517  	}
   518  
   519  	if result != s {
   520  		t.Fatalf("expected %v but got %v", s, result)
   521  	}
   522  }
   523  
   524  func TestTextByteSliceToInt(t *testing.T) {
   525  	db := openTestConn(t)
   526  	defer db.Close()
   527  
   528  	expected := 12345678
   529  	b := []byte(fmt.Sprintf("%d", expected))
   530  	row := db.QueryRow("SELECT $1::int", b)
   531  
   532  	var result int
   533  	err := row.Scan(&result)
   534  	if forceBinaryParameters() {
   535  		pqErr := err.(*Error)
   536  		if pqErr == nil {
   537  			t.Errorf("Expected to get error")
   538  		} else if pqErr.Code != "22P03" {
   539  			t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
   540  		}
   541  	} else {
   542  		if err != nil {
   543  			t.Fatal(err)
   544  		}
   545  		if result != expected {
   546  			t.Fatalf("expected %v but got %v", expected, result)
   547  		}
   548  	}
   549  }
   550  
   551  func TestBinaryByteSliceToInt(t *testing.T) {
   552  	db := openTestConn(t)
   553  	defer db.Close()
   554  
   555  	expected := 12345678
   556  	b := []byte{'\x00', '\xbc', '\x61', '\x4e'}
   557  	row := db.QueryRow("SELECT $1::int", b)
   558  
   559  	var result int
   560  	err := row.Scan(&result)
   561  	if forceBinaryParameters() {
   562  		if err != nil {
   563  			t.Fatal(err)
   564  		}
   565  		if result != expected {
   566  			t.Fatalf("expected %v but got %v", expected, result)
   567  		}
   568  	} else {
   569  		pqErr := err.(*Error)
   570  		if pqErr == nil {
   571  			t.Errorf("Expected to get error")
   572  		} else if pqErr.Code != "22021" {
   573  			t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
   574  		}
   575  	}
   576  }
   577  
   578  func TestByteaOutputFormatEncoding(t *testing.T) {
   579  	input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
   580  	want := []byte("\\x5c78000102fffe6162636465666730313233")
   581  	got := encode(&parameterStatus{serverVersion: 90000}, input, oid.T_bytea)
   582  	if !bytes.Equal(want, got) {
   583  		t.Errorf("invalid hex bytea output, got %v but expected %v", got, want)
   584  	}
   585  
   586  	want = []byte("\\\\x\\000\\001\\002\\377\\376abcdefg0123")
   587  	got = encode(&parameterStatus{serverVersion: 84000}, input, oid.T_bytea)
   588  	if !bytes.Equal(want, got) {
   589  		t.Errorf("invalid escape bytea output, got %v but expected %v", got, want)
   590  	}
   591  }
   592  
   593  func TestByteaOutputFormats(t *testing.T) {
   594  	db := openTestConn(t)
   595  	defer db.Close()
   596  
   597  	if getServerVersion(t, db) < 90000 {
   598  		// skip
   599  		return
   600  	}
   601  
   602  	testByteaOutputFormat := func(f string, usePrepared bool) {
   603  		expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08")
   604  		sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')"
   605  
   606  		var data []byte
   607  
   608  		// use a txn to avoid relying on getting the same connection
   609  		txn, err := db.Begin()
   610  		if err != nil {
   611  			t.Fatal(err)
   612  		}
   613  		defer txn.Rollback()
   614  
   615  		_, err = txn.Exec("SET LOCAL bytea_output TO " + f)
   616  		if err != nil {
   617  			t.Fatal(err)
   618  		}
   619  		var rows *sql.Rows
   620  		var stmt *sql.Stmt
   621  		if usePrepared {
   622  			stmt, err = txn.Prepare(sqlQuery)
   623  			if err != nil {
   624  				t.Fatal(err)
   625  			}
   626  			rows, err = stmt.Query()
   627  		} else {
   628  			// use Query; QueryRow would hide the actual error
   629  			rows, err = txn.Query(sqlQuery)
   630  		}
   631  		if err != nil {
   632  			t.Fatal(err)
   633  		}
   634  		if !rows.Next() {
   635  			if rows.Err() != nil {
   636  				t.Fatal(rows.Err())
   637  			}
   638  			t.Fatal("shouldn't happen")
   639  		}
   640  		err = rows.Scan(&data)
   641  		if err != nil {
   642  			t.Fatal(err)
   643  		}
   644  		err = rows.Close()
   645  		if err != nil {
   646  			t.Fatal(err)
   647  		}
   648  		if stmt != nil {
   649  			err = stmt.Close()
   650  			if err != nil {
   651  				t.Fatal(err)
   652  			}
   653  		}
   654  		if !bytes.Equal(data, expectedData) {
   655  			t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData)
   656  		}
   657  	}
   658  
   659  	testByteaOutputFormat("hex", false)
   660  	testByteaOutputFormat("escape", false)
   661  	testByteaOutputFormat("hex", true)
   662  	testByteaOutputFormat("escape", true)
   663  }
   664  
   665  func TestAppendEncodedText(t *testing.T) {
   666  	var buf []byte
   667  
   668  	buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, int64(10))
   669  	buf = append(buf, '\t')
   670  	buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, 42.0000000001)
   671  	buf = append(buf, '\t')
   672  	buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, "hello\tworld")
   673  	buf = append(buf, '\t')
   674  	buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255})
   675  
   676  	if string(buf) != "10\t42.0000000001\thello\\tworld\t\\\\x0080ff" {
   677  		t.Fatal(string(buf))
   678  	}
   679  }
   680  
   681  func TestAppendEscapedText(t *testing.T) {
   682  	if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" {
   683  		t.Fatal(string(esc))
   684  	}
   685  	if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" {
   686  		t.Fatal(string(esc))
   687  	}
   688  	if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" {
   689  		t.Fatal(string(esc))
   690  	}
   691  }
   692  
   693  func TestAppendEscapedTextExistingBuffer(t *testing.T) {
   694  	var buf []byte
   695  	buf = []byte("123\t")
   696  	if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" {
   697  		t.Fatal(string(esc))
   698  	}
   699  	buf = []byte("123\t")
   700  	if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" {
   701  		t.Fatal(string(esc))
   702  	}
   703  	buf = []byte("123\t")
   704  	if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" {
   705  		t.Fatal(string(esc))
   706  	}
   707  }
   708  
   709  func BenchmarkAppendEscapedText(b *testing.B) {
   710  	longString := ""
   711  	for i := 0; i < 100; i++ {
   712  		longString += "123456789\n"
   713  	}
   714  	for i := 0; i < b.N; i++ {
   715  		appendEscapedText(nil, longString)
   716  	}
   717  }
   718  
   719  func BenchmarkAppendEscapedTextNoEscape(b *testing.B) {
   720  	longString := ""
   721  	for i := 0; i < 100; i++ {
   722  		longString += "1234567890"
   723  	}
   724  	for i := 0; i < b.N; i++ {
   725  		appendEscapedText(nil, longString)
   726  	}
   727  }