encode_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. package pq
  2. import (
  3. "github.com/lib/pq/oid"
  4. "bytes"
  5. "fmt"
  6. "testing"
  7. "time"
  8. )
  9. func TestScanTimestamp(t *testing.T) {
  10. var nt NullTime
  11. tn := time.Now()
  12. nt.Scan(tn)
  13. if !nt.Valid {
  14. t.Errorf("Expected Valid=false")
  15. }
  16. if nt.Time != tn {
  17. t.Errorf("Time value mismatch")
  18. }
  19. }
  20. func TestScanNilTimestamp(t *testing.T) {
  21. var nt NullTime
  22. nt.Scan(nil)
  23. if nt.Valid {
  24. t.Errorf("Expected Valid=false")
  25. }
  26. }
  27. var timeTests = []struct {
  28. str string
  29. timeval time.Time
  30. }{
  31. {"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
  32. {"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
  33. {"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
  34. {"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))},
  35. {"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))},
  36. {"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))},
  37. {"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))},
  38. {"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))},
  39. {"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
  40. {"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))},
  41. {"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
  42. {"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))},
  43. {"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))},
  44. {"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))},
  45. {"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000,
  46. time.FixedZone("", -7*60*60))},
  47. {"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
  48. time.FixedZone("", -7*60*60))},
  49. {"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0,
  50. time.FixedZone("", -(7*60*60+42*60)))},
  51. {"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0,
  52. time.FixedZone("", -(7*60*60+30*60+9)))},
  53. {"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
  54. time.FixedZone("", 7*60*60))},
  55. {"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
  56. {"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
  57. {"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000,
  58. time.FixedZone("", -7*60*60))},
  59. {"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
  60. {"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)},
  61. {"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
  62. {"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)},
  63. {"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
  64. {"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
  65. {"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
  66. }
  67. // Helper function for the two tests below
  68. func tryParse(str string) (t time.Time, err error) {
  69. defer func() {
  70. if p := recover(); p != nil {
  71. err = fmt.Errorf("%v", p)
  72. return
  73. }
  74. }()
  75. t = parseTs(nil, str)
  76. return
  77. }
  78. // Test that parsing the string results in the expected value.
  79. func TestParseTs(t *testing.T) {
  80. for i, tt := range timeTests {
  81. val, err := tryParse(tt.str)
  82. if err != nil {
  83. t.Errorf("%d: got error: %v", i, err)
  84. } else if val.String() != tt.timeval.String() {
  85. t.Errorf("%d: expected to parse %q into %q; got %q",
  86. i, tt.str, tt.timeval, val)
  87. }
  88. }
  89. }
  90. // Now test that sending the value into the database and parsing it back
  91. // returns the same time.Time value.
  92. func TestEncodeAndParseTs(t *testing.T) {
  93. db, err := openTestConnConninfo("timezone='Etc/UTC'")
  94. if err != nil {
  95. t.Fatal(err)
  96. }
  97. defer db.Close()
  98. for i, tt := range timeTests {
  99. var dbstr string
  100. err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr)
  101. if err != nil {
  102. t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err)
  103. continue
  104. }
  105. val, err := tryParse(dbstr)
  106. if err != nil {
  107. t.Errorf("%d: could not parse value %q: %s", i, dbstr, err)
  108. continue
  109. }
  110. val = val.In(tt.timeval.Location())
  111. if val.String() != tt.timeval.String() {
  112. t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val)
  113. }
  114. }
  115. }
  116. var formatTimeTests = []struct {
  117. time time.Time
  118. expected string
  119. }{
  120. {time.Time{}, "0001-01-01T00:00:00Z"},
  121. {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"},
  122. {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"},
  123. {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"},
  124. {time.Date(1, time.January, 1, 0, 0, 0, 0, time.FixedZone("", 19*60+32)), "0001-01-01T00:00:00+00:19:32"},
  125. {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"},
  126. }
  127. func TestFormatTs(t *testing.T) {
  128. for i, tt := range formatTimeTests {
  129. val := string(formatTs(tt.time))
  130. if val != tt.expected {
  131. t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected)
  132. }
  133. }
  134. }
  135. func TestTimestampWithTimeZone(t *testing.T) {
  136. db := openTestConn(t)
  137. defer db.Close()
  138. tx, err := db.Begin()
  139. if err != nil {
  140. t.Fatal(err)
  141. }
  142. defer tx.Rollback()
  143. // try several different locations, all included in Go's zoneinfo.zip
  144. for _, locName := range []string{
  145. "UTC",
  146. "America/Chicago",
  147. "America/New_York",
  148. "Australia/Darwin",
  149. "Australia/Perth",
  150. } {
  151. loc, err := time.LoadLocation(locName)
  152. if err != nil {
  153. t.Logf("Could not load time zone %s - skipping", locName)
  154. continue
  155. }
  156. // Postgres timestamps have a resolution of 1 microsecond, so don't
  157. // use the full range of the Nanosecond argument
  158. refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc)
  159. for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} {
  160. // Switch Postgres's timezone to test different output timestamp formats
  161. _, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone))
  162. if err != nil {
  163. t.Fatal(err)
  164. }
  165. var gotTime time.Time
  166. row := tx.QueryRow("select $1::timestamp with time zone", refTime)
  167. err = row.Scan(&gotTime)
  168. if err != nil {
  169. t.Fatal(err)
  170. }
  171. if !refTime.Equal(gotTime) {
  172. t.Errorf("timestamps not equal: %s != %s", refTime, gotTime)
  173. }
  174. // check that the time zone is set correctly based on TimeZone
  175. pgLoc, err := time.LoadLocation(pgTimeZone)
  176. if err != nil {
  177. t.Logf("Could not load time zone %s - skipping", pgLoc)
  178. continue
  179. }
  180. translated := refTime.In(pgLoc)
  181. if translated.String() != gotTime.String() {
  182. t.Errorf("timestamps not equal: %s != %s", translated, gotTime)
  183. }
  184. }
  185. }
  186. }
  187. func TestTimestampWithOutTimezone(t *testing.T) {
  188. db := openTestConn(t)
  189. defer db.Close()
  190. test := func(ts, pgts string) {
  191. r, err := db.Query("SELECT $1::timestamp", pgts)
  192. if err != nil {
  193. t.Fatalf("Could not run query: %v", err)
  194. }
  195. n := r.Next()
  196. if n != true {
  197. t.Fatal("Expected at least one row")
  198. }
  199. var result time.Time
  200. err = r.Scan(&result)
  201. if err != nil {
  202. t.Fatalf("Did not expect error scanning row: %v", err)
  203. }
  204. expected, err := time.Parse(time.RFC3339, ts)
  205. if err != nil {
  206. t.Fatalf("Could not parse test time literal: %v", err)
  207. }
  208. if !result.Equal(expected) {
  209. t.Fatalf("Expected time to match %v: got mismatch %v",
  210. expected, result)
  211. }
  212. n = r.Next()
  213. if n != false {
  214. t.Fatal("Expected only one row")
  215. }
  216. }
  217. test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00")
  218. // Test higher precision time
  219. test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033")
  220. }
  221. func TestStringWithNul(t *testing.T) {
  222. db := openTestConn(t)
  223. defer db.Close()
  224. hello0world := string("hello\x00world")
  225. _, err := db.Query("SELECT $1::text", &hello0world)
  226. if err == nil {
  227. t.Fatal("Postgres accepts a string with nul in it; " +
  228. "injection attacks may be plausible")
  229. }
  230. }
  231. func TestByteaToText(t *testing.T) {
  232. db := openTestConn(t)
  233. defer db.Close()
  234. b := []byte("hello world")
  235. row := db.QueryRow("SELECT $1::text", b)
  236. var result []byte
  237. err := row.Scan(&result)
  238. if err != nil {
  239. t.Fatal(err)
  240. }
  241. if string(result) != string(b) {
  242. t.Fatalf("expected %v but got %v", b, result)
  243. }
  244. }
  245. func TestTextToBytea(t *testing.T) {
  246. db := openTestConn(t)
  247. defer db.Close()
  248. b := "hello world"
  249. row := db.QueryRow("SELECT $1::bytea", b)
  250. var result []byte
  251. err := row.Scan(&result)
  252. if err != nil {
  253. t.Fatal(err)
  254. }
  255. if !bytes.Equal(result, []byte(b)) {
  256. t.Fatalf("expected %v but got %v", b, result)
  257. }
  258. }
  259. func TestByteaOutputFormatEncoding(t *testing.T) {
  260. input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
  261. want := []byte("\\x5c78000102fffe6162636465666730313233")
  262. got := encode(&parameterStatus{serverVersion: 90000}, input, oid.T_bytea)
  263. if !bytes.Equal(want, got) {
  264. t.Errorf("invalid hex bytea output, got %v but expected %v", got, want)
  265. }
  266. want = []byte("\\\\x\\000\\001\\002\\377\\376abcdefg0123")
  267. got = encode(&parameterStatus{serverVersion: 84000}, input, oid.T_bytea)
  268. if !bytes.Equal(want, got) {
  269. t.Errorf("invalid escape bytea output, got %v but expected %v", got, want)
  270. }
  271. }
  272. func TestByteaOutputFormats(t *testing.T) {
  273. db := openTestConn(t)
  274. defer db.Close()
  275. if getServerVersion(t, db) < 90000 {
  276. // skip
  277. return
  278. }
  279. testByteaOutputFormat := func(f string) {
  280. expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08")
  281. sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')"
  282. var data []byte
  283. // use a txn to avoid relying on getting the same connection
  284. txn, err := db.Begin()
  285. if err != nil {
  286. t.Fatal(err)
  287. }
  288. defer txn.Rollback()
  289. _, err = txn.Exec("SET LOCAL bytea_output TO " + f)
  290. if err != nil {
  291. t.Fatal(err)
  292. }
  293. // use Query; QueryRow would hide the actual error
  294. rows, err := txn.Query(sqlQuery)
  295. if err != nil {
  296. t.Fatal(err)
  297. }
  298. if !rows.Next() {
  299. if rows.Err() != nil {
  300. t.Fatal(rows.Err())
  301. }
  302. t.Fatal("shouldn't happen")
  303. }
  304. err = rows.Scan(&data)
  305. if err != nil {
  306. t.Fatal(err)
  307. }
  308. err = rows.Close()
  309. if err != nil {
  310. t.Fatal(err)
  311. }
  312. if !bytes.Equal(data, expectedData) {
  313. t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData)
  314. }
  315. }
  316. testByteaOutputFormat("hex")
  317. testByteaOutputFormat("escape")
  318. }
  319. func TestAppendEncodedText(t *testing.T) {
  320. var buf []byte
  321. buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, int64(10))
  322. buf = append(buf, '\t')
  323. buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, float32(42.0000000001))
  324. buf = append(buf, '\t')
  325. buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, 42.0000000001)
  326. buf = append(buf, '\t')
  327. buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, "hello\tworld")
  328. buf = append(buf, '\t')
  329. buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255})
  330. if string(buf) != "10\t42\t42.0000000001\thello\\tworld\t\\\\x0080ff" {
  331. t.Fatal(string(buf))
  332. }
  333. }
  334. func TestAppendEscapedText(t *testing.T) {
  335. if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" {
  336. t.Fatal(string(esc))
  337. }
  338. if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" {
  339. t.Fatal(string(esc))
  340. }
  341. if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" {
  342. t.Fatal(string(esc))
  343. }
  344. }
  345. func TestAppendEscapedTextExistingBuffer(t *testing.T) {
  346. var buf []byte
  347. buf = []byte("123\t")
  348. if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" {
  349. t.Fatal(string(esc))
  350. }
  351. buf = []byte("123\t")
  352. if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" {
  353. t.Fatal(string(esc))
  354. }
  355. buf = []byte("123\t")
  356. if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" {
  357. t.Fatal(string(esc))
  358. }
  359. }
  360. func BenchmarkAppendEscapedText(b *testing.B) {
  361. longString := ""
  362. for i := 0; i < 100; i++ {
  363. longString += "123456789\n"
  364. }
  365. for i := 0; i < b.N; i++ {
  366. appendEscapedText(nil, longString)
  367. }
  368. }
  369. func BenchmarkAppendEscapedTextNoEscape(b *testing.B) {
  370. longString := ""
  371. for i := 0; i < 100; i++ {
  372. longString += "1234567890"
  373. }
  374. for i := 0; i < b.N; i++ {
  375. appendEscapedText(nil, longString)
  376. }
  377. }