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(¶meterStatus{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(¶meterStatus{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(¶meterStatus{serverVersion: 90000}, buf, int64(10)) 669 buf = append(buf, '\t') 670 buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, 42.0000000001) 671 buf = append(buf, '\t') 672 buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, "hello\tworld") 673 buf = append(buf, '\t') 674 buf = appendEncodedText(¶meterStatus{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 }