result_binary_test.go 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  1. package pgserver
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/binary"
  6. "math"
  7. "testing"
  8. )
  9. // extendedResultConnection returns a connection whose protocol output is
  10. // captured in memory, so Bind/Execute responses can be decoded.
  11. func extendedResultConnection(t *testing.T) (*Connection, *countingConn) {
  12. t.Helper()
  13. c, _ := newDataConnection(t)
  14. conn := &countingConn{}
  15. c.conn = conn
  16. c.writer = bufio.NewWriter(conn)
  17. return c, conn
  18. }
  19. func parseMessage(name, sql string) *Message {
  20. mb := NewMessageBuilder()
  21. mb.WriteString(name)
  22. mb.WriteString(sql)
  23. mb.WriteInt16(0) // no parameter OIDs
  24. return &Message{Type: MsgParse, Data: mb.Bytes()}
  25. }
  26. // bindResultMessage binds a portal with text parameters and the given result
  27. // format list.
  28. func bindResultMessage(portal, statement string, params []string, resultFormats []int16) *Message {
  29. mb := NewMessageBuilder()
  30. mb.WriteString(portal)
  31. mb.WriteString(statement)
  32. mb.WriteInt16(0) // all-text parameter formats
  33. mb.WriteInt16(int16(len(params)))
  34. for _, param := range params {
  35. mb.WriteInt32(int32(len(param)))
  36. mb.WriteBytes([]byte(param))
  37. }
  38. mb.WriteInt16(int16(len(resultFormats)))
  39. for _, format := range resultFormats {
  40. mb.WriteInt16(format)
  41. }
  42. return &Message{Type: MsgBind, Data: mb.Bytes()}
  43. }
  44. func executeMessage(portal string) *Message {
  45. mb := NewMessageBuilder()
  46. mb.WriteString(portal)
  47. mb.WriteInt32(0)
  48. return &Message{Type: MsgExecute, Data: mb.Bytes()}
  49. }
  50. func sendExtended(t *testing.T, c *Connection, messages ...*Message) {
  51. t.Helper()
  52. for _, message := range messages {
  53. if err := c.handleMessage(message); err != nil {
  54. t.Fatalf("handle %c: %v", message.Type, err)
  55. }
  56. }
  57. }
  58. // dataRowRawValues splits a DataRow into its raw field bytes; nil means SQL
  59. // NULL.
  60. func dataRowRawValues(t *testing.T, msg *Message) [][]byte {
  61. t.Helper()
  62. if msg.Type != MsgDataRow {
  63. t.Fatalf("message type = %c, want DataRow", msg.Type)
  64. }
  65. if len(msg.Data) < 2 {
  66. t.Fatal("short DataRow")
  67. }
  68. count := int(binary.BigEndian.Uint16(msg.Data[:2]))
  69. pos := 2
  70. values := make([][]byte, 0, count)
  71. for i := 0; i < count; i++ {
  72. if pos+4 > len(msg.Data) {
  73. t.Fatal("short DataRow field length")
  74. }
  75. length := int32(binary.BigEndian.Uint32(msg.Data[pos : pos+4]))
  76. pos += 4
  77. if length == -1 {
  78. values = append(values, nil)
  79. continue
  80. }
  81. if length < 0 || pos+int(length) > len(msg.Data) {
  82. t.Fatalf("field %d length %d out of range", i, length)
  83. }
  84. values = append(values, msg.Data[pos:pos+int(length)])
  85. pos += int(length)
  86. }
  87. if pos != len(msg.Data) {
  88. t.Fatalf("DataRow has %d trailing bytes", len(msg.Data)-pos)
  89. }
  90. return values
  91. }
  92. func dataRowsRaw(t *testing.T, messages []*Message) [][][]byte {
  93. t.Helper()
  94. var rows [][][]byte
  95. for _, message := range messages {
  96. if message.Type == MsgDataRow {
  97. rows = append(rows, dataRowRawValues(t, message))
  98. }
  99. }
  100. return rows
  101. }
  102. func rowDescriptionFormats(t *testing.T, data []byte) []int16 {
  103. t.Helper()
  104. if len(data) < 2 {
  105. t.Fatal("row description too short")
  106. }
  107. count := int(binary.BigEndian.Uint16(data[:2]))
  108. pos := 2
  109. formats := make([]int16, 0, count)
  110. for i := 0; i < count; i++ {
  111. end := bytes.IndexByte(data[pos:], 0)
  112. if end < 0 {
  113. t.Fatal("unterminated column name")
  114. }
  115. pos += end + 1 // name
  116. pos += 4 // table OID
  117. pos += 2 // column attribute number
  118. pos += 4 // type OID
  119. pos += 2 // type size
  120. pos += 4 // type modifier
  121. if pos+2 > len(data) {
  122. t.Fatal("truncated row description")
  123. }
  124. formats = append(formats, int16(binary.BigEndian.Uint16(data[pos:pos+2])))
  125. pos += 2
  126. }
  127. return formats
  128. }
  129. func messageTypes(messages []*Message) string {
  130. var types []byte
  131. for _, message := range messages {
  132. types = append(types, message.Type)
  133. }
  134. return string(types)
  135. }
  136. func prepareBinaryResultTable(t *testing.T, c *Connection) {
  137. t.Helper()
  138. for _, sql := range []string{
  139. `CREATE TABLE t (id INTEGER PRIMARY KEY, flag BOOLEAN, name TEXT, amount REAL, score DOUBLE, doc JSONB, data BLOB, tags TEXT[], nums NUMERIC[], flags BOOLEAN[])`,
  140. `INSERT INTO t VALUES (1, 1, 'alpha', 1.5, 2.25, '{"a": 1}', X'00ff', '{a,"b c"}', '{1.10,12345678901234567890}', '{t,f}')`,
  141. `INSERT INTO t VALUES (2, 0, NULL, NULL, NULL, NULL, NULL, '{}', '{NULL,2}', '{f,NULL}')`,
  142. } {
  143. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  144. t.Fatalf("%s: %v", sql, err)
  145. }
  146. }
  147. }
  148. const binaryResultSelect = `SELECT id, flag, name, amount, score, doc, data, tags, nums, flags FROM t ORDER BY id`
  149. // An empty Bind result format list keeps every column in text format, and the
  150. // RowDescription reports format 0 for all of them.
  151. func TestBinaryResultsAllTextByDefault(t *testing.T) {
  152. c, conn := extendedResultConnection(t)
  153. prepareBinaryResultTable(t, c)
  154. sendExtended(t, c,
  155. parseMessage("s", binaryResultSelect),
  156. bindResultMessage("p", "s", nil, nil),
  157. executeMessage("p"),
  158. &Message{Type: MsgSync},
  159. )
  160. messages := readAllMessages(t, conn.bytes())
  161. if msg := findError(messages); msg != nil {
  162. t.Fatalf("text result failed: %s", errorCode(msg))
  163. }
  164. rd := findRowDescription(t, messages)
  165. for i, format := range rowDescriptionFormats(t, rd.Data) {
  166. if format != 0 {
  167. t.Fatalf("column %d format = %d, want text", i, format)
  168. }
  169. }
  170. rows := dataRowsRaw(t, messages)
  171. if len(rows) != 2 {
  172. t.Fatalf("got %d rows, want 2", len(rows))
  173. }
  174. if string(rows[0][2]) != "alpha" || string(rows[0][3]) != "1.5" {
  175. t.Fatalf("text row = %q", rows[0])
  176. }
  177. }
  178. // A single result format code repeats for every column, so one binary entry
  179. // makes the whole result binary. Every supported scalar and array type is
  180. // checked against its exact wire representation.
  181. func TestBinaryResultsSingleFormatRepeats(t *testing.T) {
  182. c, conn := extendedResultConnection(t)
  183. prepareBinaryResultTable(t, c)
  184. sendExtended(t, c,
  185. parseMessage("s", binaryResultSelect),
  186. bindResultMessage("p", "s", nil, []int16{1}),
  187. executeMessage("p"),
  188. &Message{Type: MsgSync},
  189. )
  190. messages := readAllMessages(t, conn.bytes())
  191. if msg := findError(messages); msg != nil {
  192. t.Fatalf("binary result failed: %s", errorCode(msg))
  193. }
  194. rd := findRowDescription(t, messages)
  195. formats := rowDescriptionFormats(t, rd.Data)
  196. if len(formats) != 10 {
  197. t.Fatalf("RowDescription has %d columns, want 10", len(formats))
  198. }
  199. for i, format := range formats {
  200. if format != 1 {
  201. t.Fatalf("column %d format = %d, want binary", i, format)
  202. }
  203. }
  204. // The OIDs do not change with the format.
  205. oids := rowDescriptionOIDs(t, rd.Data)
  206. wantOIDs := []int32{23, 16, 25, 700, 701, 3802, 17, 1009, 1231, 1000}
  207. for i, want := range wantOIDs {
  208. if oids[i] != want {
  209. t.Errorf("column %d OID = %d, want %d", i, oids[i], want)
  210. }
  211. }
  212. rows := dataRowsRaw(t, messages)
  213. if len(rows) != 2 {
  214. t.Fatalf("got %d rows, want 2", len(rows))
  215. }
  216. row := rows[0]
  217. assertBytes(t, "int4", row[0], []byte{0, 0, 0, 1})
  218. assertBytes(t, "bool", row[1], []byte{1})
  219. assertBytes(t, "text", row[2], []byte("alpha"))
  220. assertBytes(t, "float4", row[3], binary.BigEndian.AppendUint32(nil, math.Float32bits(1.5)))
  221. assertBytes(t, "float8", row[4], binary.BigEndian.AppendUint64(nil, math.Float64bits(2.25)))
  222. if len(row[5]) == 0 || row[5][0] != 1 {
  223. t.Fatalf("jsonb value %v does not start with the version byte", row[5])
  224. }
  225. if !bytes.Contains(row[5][1:], []byte(`"a"`)) {
  226. t.Fatalf("jsonb value %q does not carry the JSON text", row[5])
  227. }
  228. assertBytes(t, "bytea", row[6], []byte{0x00, 0xff})
  229. // text[]: one dimension, two elements, lower bound 1.
  230. assertArrayHeader(t, "text[]", row[7], 25, 2, false)
  231. if got := arrayElements(t, row[7]); len(got) != 2 || string(got[0]) != "a" || string(got[1]) != "b c" {
  232. t.Fatalf("text[] elements = %q", got)
  233. }
  234. // numeric[]: 1.10 keeps its scale, and the 20-digit value stays exact.
  235. assertArrayHeader(t, "numeric[]", row[8], 1700, 2, false)
  236. numericElements := arrayElements(t, row[8])
  237. assertNumericBinary(t, "1.10", numericElements[0], 2, 0, 2, []uint16{1, 1000})
  238. assertNumericBinary(t, "12345678901234567890", numericElements[1], 5, 4, 0, []uint16{1234, 5678, 9012, 3456, 7890})
  239. // bool[]: t/f.
  240. assertArrayHeader(t, "bool[]", row[9], 16, 2, false)
  241. if got := arrayElements(t, row[9]); len(got) != 2 || got[0][0] != 1 || got[1][0] != 0 {
  242. t.Fatalf("bool[] elements = %v", got)
  243. }
  244. // Row 2 exercises NULL columns, an empty array, and NULL array elements.
  245. second := rows[1]
  246. assertBytes(t, "int4 second row", second[0], []byte{0, 0, 0, 2})
  247. assertBytes(t, "bool second row", second[1], []byte{0})
  248. for _, index := range []int{2, 3, 4, 5, 6} {
  249. if second[index] != nil {
  250. t.Errorf("column %d = %v, want NULL", index, second[index])
  251. }
  252. }
  253. // An empty array has no dimension header.
  254. assertBytes(t, "empty text[]", second[7], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25})
  255. assertArrayHeader(t, "numeric[] with NULL", second[8], 1700, 2, true)
  256. numericSecond := arrayElements(t, second[8])
  257. if numericSecond[0] != nil {
  258. t.Fatalf("numeric[] first element = %v, want NULL", numericSecond[0])
  259. }
  260. assertNumericBinary(t, "2", numericSecond[1], 1, 0, 0, []uint16{2})
  261. assertArrayHeader(t, "bool[] with NULL", second[9], 16, 2, true)
  262. boolSecond := arrayElements(t, second[9])
  263. if boolSecond[0] == nil || boolSecond[0][0] != 0 || boolSecond[1] != nil {
  264. t.Fatalf("bool[] elements = %v, want [f NULL]", boolSecond)
  265. }
  266. }
  267. // Mixed lists choose text and binary per column in one result.
  268. func TestBinaryResultsMixedPerColumnFormats(t *testing.T) {
  269. c, conn := extendedResultConnection(t)
  270. prepareBinaryResultTable(t, c)
  271. // id text, flag binary, name text, amount binary, score text, doc binary,
  272. // data text, tags binary, nums text, flags binary.
  273. formats := []int16{0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
  274. sendExtended(t, c,
  275. parseMessage("s", binaryResultSelect),
  276. bindResultMessage("p", "s", nil, formats),
  277. executeMessage("p"),
  278. &Message{Type: MsgSync},
  279. )
  280. messages := readAllMessages(t, conn.bytes())
  281. if msg := findError(messages); msg != nil {
  282. t.Fatalf("mixed result failed: %s", errorCode(msg))
  283. }
  284. rd := findRowDescription(t, messages)
  285. if got := rowDescriptionFormats(t, rd.Data); !equalInt16(got, formats) {
  286. t.Fatalf("RowDescription formats = %v, want %v", got, formats)
  287. }
  288. rows := dataRowsRaw(t, messages)
  289. if len(rows) != 2 {
  290. t.Fatalf("got %d rows, want 2", len(rows))
  291. }
  292. row := rows[0]
  293. // Text columns carry their text spelling.
  294. assertBytes(t, "text id", row[0], []byte("1"))
  295. assertBytes(t, "text name", row[2], []byte("alpha"))
  296. assertBytes(t, "text score", row[4], []byte("2.25"))
  297. assertBytes(t, "text data", row[6], []byte(`\x00ff`))
  298. assertBytes(t, "text nums", row[8], []byte("{1.10,12345678901234567890}"))
  299. // Binary columns carry their binary representation.
  300. assertBytes(t, "binary flag", row[1], []byte{1})
  301. assertBytes(t, "binary amount", row[3], binary.BigEndian.AppendUint32(nil, math.Float32bits(1.5)))
  302. if len(row[5]) == 0 || row[5][0] != 1 {
  303. t.Fatalf("jsonb value %v does not start with the version byte", row[5])
  304. }
  305. assertArrayHeader(t, "binary tags", row[7], 25, 2, false)
  306. assertArrayHeader(t, "binary flags", row[9], 16, 2, false)
  307. }
  308. // Formats belong to the portal, not the statement: two portals over one
  309. // prepared statement can use different formats, and re-binding a closed portal
  310. // starts from the new list.
  311. func TestBinaryResultsArePerPortal(t *testing.T) {
  312. c, conn := extendedResultConnection(t)
  313. prepareBinaryResultTable(t, c)
  314. sql := `SELECT id, flag FROM t ORDER BY id`
  315. sendExtended(t, c,
  316. parseMessage("s", sql),
  317. bindResultMessage("text_portal", "s", nil, nil),
  318. bindResultMessage("binary_portal", "s", nil, []int16{1}),
  319. executeMessage("text_portal"),
  320. executeMessage("binary_portal"),
  321. &Message{Type: MsgClose, Data: append([]byte("Ptext_portal"), 0)},
  322. bindResultMessage("text_portal", "s", nil, []int16{0}),
  323. executeMessage("text_portal"),
  324. &Message{Type: MsgSync},
  325. )
  326. messages := readAllMessages(t, conn.bytes())
  327. if msg := findError(messages); msg != nil {
  328. t.Fatalf("per-portal result failed: %s", errorCode(msg))
  329. }
  330. var descriptions [][]int16
  331. var rows [][][]byte
  332. for _, message := range messages {
  333. switch message.Type {
  334. case MsgRowDescription:
  335. descriptions = append(descriptions, rowDescriptionFormats(t, message.Data))
  336. case MsgDataRow:
  337. rows = append(rows, dataRowRawValues(t, message))
  338. }
  339. }
  340. if len(descriptions) != 3 {
  341. t.Fatalf("got %d RowDescriptions, want 3", len(descriptions))
  342. }
  343. if !equalInt16(descriptions[0], []int16{0, 0}) {
  344. t.Fatalf("text portal formats = %v", descriptions[0])
  345. }
  346. if !equalInt16(descriptions[1], []int16{1, 1}) {
  347. t.Fatalf("binary portal formats = %v", descriptions[1])
  348. }
  349. if !equalInt16(descriptions[2], []int16{0, 0}) {
  350. t.Fatalf("re-bound text portal formats = %v", descriptions[2])
  351. }
  352. // The two text portals put "1" and "1" in the first row; the binary portal
  353. // puts four bytes.
  354. if len(rows) != 6 {
  355. t.Fatalf("got %d rows, want 6", len(rows))
  356. }
  357. if string(rows[0][0]) != "1" || string(rows[1][0]) != "2" {
  358. t.Fatalf("text portal rows = %q %q", rows[0][0], rows[1][0])
  359. }
  360. assertBytes(t, "binary portal id", rows[2][0], []byte{0, 0, 0, 1})
  361. if string(rows[4][0]) != "1" || string(rows[5][0]) != "2" {
  362. t.Fatalf("re-bound text portal rows = %q %q", rows[4][0], rows[5][0])
  363. }
  364. }
  365. // A format list whose length is neither one nor the column count is a protocol
  366. // violation, and no RowDescription or DataRow is sent before the error.
  367. func TestBinaryResultsFormatCountMismatch(t *testing.T) {
  368. c, conn := extendedResultConnection(t)
  369. prepareBinaryResultTable(t, c)
  370. sendExtended(t, c,
  371. parseMessage("s", `SELECT id, flag FROM t`),
  372. bindResultMessage("p", "s", nil, []int16{0, 1, 0}),
  373. executeMessage("p"),
  374. &Message{Type: MsgSync},
  375. )
  376. messages := readAllMessages(t, conn.bytes())
  377. if got := messageTypes(messages); got != "12"+string(MsgErrorResponse)+string(MsgReadyForQuery) {
  378. t.Fatalf("messages = %q", got)
  379. }
  380. if code := errorCode(findError(messages)); code != ErrCodeProtocolViolation {
  381. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeProtocolViolation)
  382. }
  383. if c.extendedFailed {
  384. t.Fatal("Sync did not clear the extended-failure state")
  385. }
  386. // The connection is reusable after the rejected cycle.
  387. conn.reset()
  388. sendExtended(t, c,
  389. parseMessage("s2", `SELECT id FROM t ORDER BY id`),
  390. bindResultMessage("p2", "s2", nil, []int16{1}),
  391. executeMessage("p2"),
  392. &Message{Type: MsgSync},
  393. )
  394. messages = readAllMessages(t, conn.bytes())
  395. if msg := findError(messages); msg != nil {
  396. t.Fatalf("query after format error failed: %s", errorCode(msg))
  397. }
  398. rows := dataRowsRaw(t, messages)
  399. if len(rows) != 2 || !bytes.Equal(rows[0][0], []byte{0, 0, 0, 1}) {
  400. t.Fatalf("rows after format error = %v", rows)
  401. }
  402. }
  403. // A binary request for a type the wire layer cannot encode fails with 0A000
  404. // before any result frame, including when the statement has rows to return.
  405. func TestBinaryResultsUnsupportedOID(t *testing.T) {
  406. c, conn := extendedResultConnection(t)
  407. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE stamps (id INTEGER PRIMARY KEY, created_at TIMESTAMP)`)); err != nil {
  408. t.Fatal(err)
  409. }
  410. if _, err := c.executor.Execute(parseStmt(t, `INSERT INTO stamps VALUES (1, '2024-01-02 03:04:05')`)); err != nil {
  411. t.Fatal(err)
  412. }
  413. sendExtended(t, c,
  414. parseMessage("s", `SELECT id, created_at FROM stamps`),
  415. bindResultMessage("p", "s", nil, []int16{1}),
  416. executeMessage("p"),
  417. &Message{Type: MsgSync},
  418. )
  419. messages := readAllMessages(t, conn.bytes())
  420. if got := messageTypes(messages); got != "12"+string(MsgErrorResponse)+string(MsgReadyForQuery) {
  421. t.Fatalf("messages = %q", got)
  422. }
  423. if code := errorCode(findError(messages)); code != ErrCodeFeatureNotSupported {
  424. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeFeatureNotSupported)
  425. }
  426. // Text format for the same statement still works.
  427. conn.reset()
  428. sendExtended(t, c,
  429. parseMessage("s2", `SELECT created_at FROM stamps`),
  430. bindResultMessage("p2", "s2", nil, nil),
  431. executeMessage("p2"),
  432. &Message{Type: MsgSync},
  433. )
  434. messages = readAllMessages(t, conn.bytes())
  435. if msg := findError(messages); msg != nil {
  436. t.Fatalf("text query after unsupported binary failed: %s", errorCode(msg))
  437. }
  438. if rows := dataRowsRaw(t, messages); len(rows) != 1 || string(rows[0][0]) != "2024-01-02 03:04:05" {
  439. t.Fatalf("text rows = %q", rows)
  440. }
  441. }
  442. // A rejected format inside an explicit transaction block marks the transaction
  443. // failed, as any statement error does, and no rows are emitted.
  444. func TestBinaryResultsErrorFailsTransaction(t *testing.T) {
  445. c, conn := extendedResultConnection(t)
  446. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)); err != nil {
  447. t.Fatal(err)
  448. }
  449. sendExtended(t, c,
  450. &Message{Type: MsgQuery, Data: []byte("BEGIN\x00")},
  451. parseMessage("s", `SELECT id FROM t`),
  452. bindResultMessage("p", "s", nil, []int16{0, 1}),
  453. executeMessage("p"),
  454. &Message{Type: MsgSync},
  455. )
  456. messages := readAllMessages(t, conn.bytes())
  457. ready := messages[len(messages)-1]
  458. if ready.Type != MsgReadyForQuery || ready.Data[0] != TxStatusFailed {
  459. t.Fatalf("transaction status = %q, want failed", messageTypes(messages))
  460. }
  461. if code := errorCode(findError(messages)); code != ErrCodeProtocolViolation {
  462. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeProtocolViolation)
  463. }
  464. // While failed, the next statement is refused with 25P02.
  465. conn.reset()
  466. sendExtended(t, c,
  467. parseMessage("s2", `SELECT id FROM t`),
  468. bindResultMessage("p2", "s2", nil, nil),
  469. executeMessage("p2"),
  470. &Message{Type: MsgSync},
  471. )
  472. messages = readAllMessages(t, conn.bytes())
  473. if code := errorCode(findError(messages)); code != ErrCodeTransactionAborted {
  474. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeTransactionAborted)
  475. }
  476. }
  477. // appendNumericText encodes exact decimal text into base-10000 groups, keeping
  478. // the display scale and rejecting values that are not decimal numbers.
  479. func TestAppendNumericTextEncoding(t *testing.T) {
  480. cases := []struct {
  481. text string
  482. ndigits uint16
  483. weight int16
  484. sign uint16
  485. dscale uint16
  486. digits []uint16
  487. }{
  488. {"0", 0, 0, 0, 0, nil},
  489. {"1.10", 2, 0, 0, 2, []uint16{1, 1000}},
  490. {"-2.5", 2, 0, 0x4000, 1, []uint16{2, 5000}},
  491. {"12345678901234567890", 5, 4, 0, 0, []uint16{1234, 5678, 9012, 3456, 7890}},
  492. {"1e5", 1, 1, 0, 0, []uint16{10}},
  493. {"0.00001", 1, -2, 0, 5, []uint16{1000}},
  494. {"-0.000", 0, 0, 0, 3, nil},
  495. }
  496. for _, tc := range cases {
  497. t.Run(tc.text, func(t *testing.T) {
  498. encoded, err := appendNumericText(nil, tc.text)
  499. if err != nil {
  500. t.Fatalf("appendNumericText(%q): %v", tc.text, err)
  501. }
  502. assertNumericBinary(t, tc.text, encoded, tc.ndigits, tc.weight, tc.dscale, tc.digits, tc.sign)
  503. })
  504. }
  505. for _, text := range []string{"", "abc", "1.2.3", "NaN", "1e999999"} {
  506. if _, err := appendNumericText(nil, text); err == nil {
  507. t.Errorf("appendNumericText(%q) succeeded, want an error", text)
  508. }
  509. }
  510. }
  511. // assertNumericBinary checks a binary numeric payload.
  512. func assertNumericBinary(t *testing.T, label string, data []byte, ndigits uint16, weight int16, dscale uint16, digits []uint16, sign ...uint16) {
  513. t.Helper()
  514. wantSign := uint16(0)
  515. if len(sign) > 0 {
  516. wantSign = sign[0]
  517. }
  518. if len(data) != 8+2*len(digits) {
  519. t.Fatalf("%s numeric length = %d, want %d (%v)", label, len(data), 8+2*len(digits), data)
  520. }
  521. if got := binary.BigEndian.Uint16(data[0:2]); got != ndigits {
  522. t.Errorf("%s ndigits = %d, want %d", label, got, ndigits)
  523. }
  524. if got := int16(binary.BigEndian.Uint16(data[2:4])); got != weight {
  525. t.Errorf("%s weight = %d, want %d", label, got, weight)
  526. }
  527. if got := binary.BigEndian.Uint16(data[4:6]); got != wantSign {
  528. t.Errorf("%s sign = %#x, want %#x", label, got, wantSign)
  529. }
  530. if got := binary.BigEndian.Uint16(data[6:8]); got != dscale {
  531. t.Errorf("%s dscale = %d, want %d", label, got, dscale)
  532. }
  533. for i, want := range digits {
  534. if got := binary.BigEndian.Uint16(data[8+2*i : 10+2*i]); got != want {
  535. t.Errorf("%s digit %d = %d, want %d", label, i, got, want)
  536. }
  537. }
  538. }
  539. func assertArrayHeader(t *testing.T, label string, data []byte, elementOID int32, count int, hasNull bool) {
  540. t.Helper()
  541. if len(data) < 12 {
  542. t.Fatalf("%s array is %d bytes, want a header", label, len(data))
  543. }
  544. if ndim := int32(binary.BigEndian.Uint32(data[0:4])); ndim != 1 {
  545. t.Fatalf("%s ndim = %d, want 1", label, ndim)
  546. }
  547. wantNull := uint32(0)
  548. if hasNull {
  549. wantNull = 1
  550. }
  551. if got := binary.BigEndian.Uint32(data[4:8]); got != wantNull {
  552. t.Errorf("%s hasnull = %d, want %d", label, got, wantNull)
  553. }
  554. if got := int32(binary.BigEndian.Uint32(data[8:12])); got != elementOID {
  555. t.Errorf("%s element OID = %d, want %d", label, got, elementOID)
  556. }
  557. if len(data) < 20 {
  558. t.Fatalf("%s array is %d bytes, want a dimension header", label, len(data))
  559. }
  560. if got := int32(binary.BigEndian.Uint32(data[12:16])); got != int32(count) {
  561. t.Errorf("%s dimension length = %d, want %d", label, got, count)
  562. }
  563. if got := int32(binary.BigEndian.Uint32(data[16:20])); got != 1 {
  564. t.Errorf("%s lower bound = %d, want 1", label, got)
  565. }
  566. }
  567. // arrayElements splits the element payloads of a one-dimensional binary array.
  568. func arrayElements(t *testing.T, data []byte) [][]byte {
  569. t.Helper()
  570. if len(data) < 20 {
  571. t.Fatalf("array is %d bytes, want a dimension header", len(data))
  572. }
  573. count := int(binary.BigEndian.Uint32(data[12:16]))
  574. pos := 20
  575. elements := make([][]byte, 0, count)
  576. for i := 0; i < count; i++ {
  577. if pos+4 > len(data) {
  578. t.Fatalf("array element %d is truncated", i)
  579. }
  580. length := int32(binary.BigEndian.Uint32(data[pos : pos+4]))
  581. pos += 4
  582. if length == -1 {
  583. elements = append(elements, nil)
  584. continue
  585. }
  586. if length < 0 || pos+int(length) > len(data) {
  587. t.Fatalf("array element %d length %d out of range", i, length)
  588. }
  589. elements = append(elements, data[pos:pos+int(length)])
  590. pos += int(length)
  591. }
  592. if pos != len(data) {
  593. t.Fatalf("array has %d trailing bytes", len(data)-pos)
  594. }
  595. return elements
  596. }
  597. func assertBytes(t *testing.T, label string, got, want []byte) {
  598. t.Helper()
  599. if !bytes.Equal(got, want) {
  600. t.Fatalf("%s = %q (%v), want %q (%v)", label, got, got, want, want)
  601. }
  602. }
  603. func equalInt16(left, right []int16) bool {
  604. if len(left) != len(right) {
  605. return false
  606. }
  607. for i := range left {
  608. if left[i] != right[i] {
  609. return false
  610. }
  611. }
  612. return true
  613. }
  614. // Describe never exposes the portal's bound formats: statement and portal
  615. // Describe both return NoData, and the formats travel with the RowDescription
  616. // emitted at Execute.
  617. func TestBinaryResultsDescribeStaysNoData(t *testing.T) {
  618. c, conn := extendedResultConnection(t)
  619. prepareBinaryResultTable(t, c)
  620. sendExtended(t, c,
  621. parseMessage("s", `SELECT id, flag FROM t`),
  622. bindResultMessage("p", "s", nil, []int16{1}),
  623. &Message{Type: MsgDescribe, Data: []byte{'S', 's', 0}},
  624. &Message{Type: MsgDescribe, Data: []byte{'P', 'p', 0}},
  625. &Message{Type: MsgSync},
  626. )
  627. messages := readAllMessages(t, conn.bytes())
  628. if got := messageTypes(messages); got != "12tnnZ" {
  629. t.Fatalf("messages = %q, want ParseComplete/BindComplete/ParameterDescription/NoData/NoData/ReadyForQuery", got)
  630. }
  631. }
  632. // Simple-query results keep text format; only a Bind result format list can
  633. // request binary columns.
  634. func TestSimpleQueryResultsStayText(t *testing.T) {
  635. c, conn := extendedResultConnection(t)
  636. prepareBinaryResultTable(t, c)
  637. messages := runSimple(t, c, conn, `SELECT id, flag, tags FROM t ORDER BY id`)
  638. if msg := findError(messages); msg != nil {
  639. t.Fatalf("simple query failed: %s", errorCode(msg))
  640. }
  641. rd := findRowDescription(t, messages)
  642. for i, format := range rowDescriptionFormats(t, rd.Data) {
  643. if format != 0 {
  644. t.Fatalf("column %d format = %d, want text", i, format)
  645. }
  646. }
  647. rows := dataRowsRaw(t, messages)
  648. if len(rows) != 2 || string(rows[0][2]) != `{a,"b c"}` {
  649. t.Fatalf("simple rows = %q", rows)
  650. }
  651. }
  652. // VARCHAR arrays report OID 1015 and carry element OID 1043.
  653. func TestBinaryResultsVarcharArray(t *testing.T) {
  654. c, conn := extendedResultConnection(t)
  655. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE v (id INTEGER PRIMARY KEY, names VARCHAR(10)[])`)); err != nil {
  656. t.Fatal(err)
  657. }
  658. if _, err := c.executor.Execute(parseStmt(t, `INSERT INTO v VALUES (1, '{a,"b c"}')`)); err != nil {
  659. t.Fatal(err)
  660. }
  661. sendExtended(t, c,
  662. parseMessage("s", `SELECT names FROM v`),
  663. bindResultMessage("p", "s", nil, []int16{1}),
  664. executeMessage("p"),
  665. &Message{Type: MsgSync},
  666. )
  667. messages := readAllMessages(t, conn.bytes())
  668. if msg := findError(messages); msg != nil {
  669. t.Fatalf("varchar[] result failed: %s", errorCode(msg))
  670. }
  671. rd := findRowDescription(t, messages)
  672. if oid := rowDescriptionOIDs(t, rd.Data)[0]; oid != 1015 {
  673. t.Fatalf("varchar[] OID = %d, want 1015", oid)
  674. }
  675. rows := dataRowsRaw(t, messages)
  676. assertArrayHeader(t, "varchar[]", rows[0][0], 1043, 2, false)
  677. elements := arrayElements(t, rows[0][0])
  678. if len(elements) != 2 || string(elements[0]) != "a" || string(elements[1]) != "b c" {
  679. t.Fatalf("varchar[] elements = %q", elements)
  680. }
  681. }
  682. // TestAppendBinaryResultValue covers the encoders that no SQL type reaches
  683. // directly, such as int2 results and int2 arrays.
  684. func TestAppendBinaryResultValue(t *testing.T) {
  685. cases := []struct {
  686. name string
  687. oid int32
  688. value interface{}
  689. want []byte
  690. }{
  691. {"bool true", 16, bool(true), []byte{1}},
  692. {"bool from int64", 16, int64(0), []byte{0}},
  693. {"bool from text", 16, "yes", []byte{1}},
  694. {"int2", 21, int64(-2), []byte{0xff, 0xfe}},
  695. {"int2 from text", 21, "12", []byte{0, 12}},
  696. {"int4", 23, int64(1), []byte{0, 0, 0, 1}},
  697. {"int8", 20, int64(-1), []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
  698. {"float4", 700, float64(1.5), binary.BigEndian.AppendUint32(nil, math.Float32bits(1.5))},
  699. {"float8", 701, int64(2), binary.BigEndian.AppendUint64(nil, math.Float64bits(2))},
  700. {"text", 25, "alpha", []byte("alpha")},
  701. {"varchar", 1043, "beta", []byte("beta")},
  702. {"bytea bytes", 17, []byte{0x00, 0xff}, []byte{0x00, 0xff}},
  703. {"bytea string", 17, "ab", []byte("ab")},
  704. {"numeric text", 1700, "1.10", []byte{0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0x03, 0xe8}},
  705. {"numeric int", 1700, int64(2), []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 2}},
  706. {"jsonb", 3802, `{"a": 1}`, []byte(`{"a": 1}`)},
  707. }
  708. for _, tc := range cases {
  709. t.Run(tc.name, func(t *testing.T) {
  710. got, err := appendBinaryResultValue(nil, tc.oid, tc.value)
  711. if err != nil {
  712. t.Fatalf("appendBinaryResultValue(%d, %v): %v", tc.oid, tc.value, err)
  713. }
  714. want := tc.want
  715. if tc.oid == 3802 {
  716. want = append([]byte{1}, tc.want...)
  717. }
  718. assertBytes(t, tc.name, got, want)
  719. })
  720. }
  721. for _, tc := range []struct {
  722. name string
  723. oid int32
  724. value interface{}
  725. }{
  726. {"int4 from text", 23, "abc"},
  727. {"int2 out of range", 21, int64(70000)},
  728. {"int4 out of range", 23, int64(1) << 40},
  729. {"bool from text", 16, "maybe"},
  730. {"float from text", 701, "abc"},
  731. {"unsupported oid", 1184, "2024-01-02 03:04:05"},
  732. {"numeric from bool", 1700, true},
  733. } {
  734. t.Run(tc.name, func(t *testing.T) {
  735. if _, err := appendBinaryResultValue(nil, tc.oid, tc.value); err == nil {
  736. t.Fatalf("%s: encoding succeeded, want an error", tc.name)
  737. }
  738. })
  739. }
  740. }
  741. // TestBinaryResultArrayOIDs covers every array OID in the encoding table,
  742. // including the int2/varchar forms the engine reports for declared arrays.
  743. func TestBinaryResultArrayOIDs(t *testing.T) {
  744. cases := []struct {
  745. name string
  746. arrayOID int32
  747. elementOID int32
  748. text string
  749. want [][]byte
  750. }{
  751. {"bool[]", 1000, 16, "{t,f}", [][]byte{{1}, {0}}},
  752. {"int2[]", 1005, 21, "{1,-2}", [][]byte{{0, 1}, {0xff, 0xfe}}},
  753. {"int4[]", 1007, 23, "{1,2}", [][]byte{{0, 0, 0, 1}, {0, 0, 0, 2}}},
  754. {"text[]", 1009, 25, `{a,"b c"}`, [][]byte{[]byte("a"), []byte("b c")}},
  755. {"varchar[]", 1015, 1043, "{a}", [][]byte{[]byte("a")}},
  756. {"int8[]", 1016, 20, "{1}", [][]byte{{0, 0, 0, 0, 0, 0, 0, 1}}},
  757. {"float4[]", 1021, 700, "{1.5}", [][]byte{binary.BigEndian.AppendUint32(nil, math.Float32bits(1.5))}},
  758. {"float8[]", 1022, 701, "{1.5}", [][]byte{binary.BigEndian.AppendUint64(nil, math.Float64bits(1.5))}},
  759. {"numeric[]", 1231, 1700, "{1.10}", [][]byte{{0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0x03, 0xe8}}},
  760. }
  761. for _, tc := range cases {
  762. t.Run(tc.name, func(t *testing.T) {
  763. got, err := appendBinaryResultValue(nil, tc.arrayOID, tc.text)
  764. if err != nil {
  765. t.Fatalf("encode %s: %v", tc.name, err)
  766. }
  767. assertArrayHeader(t, tc.name, got, tc.elementOID, len(tc.want), false)
  768. elements := arrayElements(t, got)
  769. if len(elements) != len(tc.want) {
  770. t.Fatalf("%s elements = %d, want %d", tc.name, len(elements), len(tc.want))
  771. }
  772. for i := range tc.want {
  773. assertBytes(t, tc.name+" element", elements[i], tc.want[i])
  774. }
  775. })
  776. }
  777. // Unsupported array OIDs are not encodable.
  778. for _, oid := range []int32{1001 /* bytea[] */, 1185 /* timestamptz[] */} {
  779. if canEncodeBinaryResult(oid) {
  780. t.Errorf("canEncodeBinaryResult(%d) = true", oid)
  781. }
  782. }
  783. for _, oid := range []int32{16, 17, 20, 21, 23, 25, 700, 701, 1043, 1700, 3802, 1000, 1005, 1007, 1009, 1015, 1016, 1021, 1022, 1231} {
  784. if !canEncodeBinaryResult(oid) {
  785. t.Errorf("canEncodeBinaryResult(%d) = false", oid)
  786. }
  787. }
  788. for _, oid := range []int32{1082, 1184} {
  789. if canEncodeBinaryResult(oid) {
  790. t.Errorf("canEncodeBinaryResult(%d) = true", oid)
  791. }
  792. }
  793. }
  794. // A statement that writes and returns rows executes before its Bind result
  795. // formats are resolved, because the row metadata only exists at Execute. The
  796. // rejected binary request therefore sends no rows but does not undo the write;
  797. // this ordering is documented in the capability document.
  798. func TestBinaryResultsRejectAfterDMLExecution(t *testing.T) {
  799. c, conn := extendedResultConnection(t)
  800. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE w (id INTEGER PRIMARY KEY, created_at TIMESTAMP)`)); err != nil {
  801. t.Fatal(err)
  802. }
  803. sendExtended(t, c,
  804. parseMessage("s", `INSERT INTO w VALUES (1, '2024-01-02 03:04:05') RETURNING created_at`),
  805. bindResultMessage("p", "s", nil, []int16{1}),
  806. executeMessage("p"),
  807. &Message{Type: MsgSync},
  808. )
  809. messages := readAllMessages(t, conn.bytes())
  810. if got := messageTypes(messages); got != "12"+string(MsgErrorResponse)+string(MsgReadyForQuery) {
  811. t.Fatalf("messages = %q, want no RowDescription or DataRow", got)
  812. }
  813. if code := errorCode(findError(messages)); code != ErrCodeFeatureNotSupported {
  814. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeFeatureNotSupported)
  815. }
  816. result, err := c.executor.Execute(parseStmt(t, `SELECT COUNT(*) FROM w`))
  817. if err != nil {
  818. t.Fatal(err)
  819. }
  820. if result.Rows[0][0] != int64(1) {
  821. t.Fatalf("write was rolled back: %v", result.Rows)
  822. }
  823. }
  824. // Catalog emulation results use the same per-portal formats as executor
  825. // results: their text and boolean columns encode as binary.
  826. func TestBinaryResultsCatalogQuery(t *testing.T) {
  827. c, conn := extendedResultConnection(t)
  828. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)); err != nil {
  829. t.Fatal(err)
  830. }
  831. sendExtended(t, c,
  832. parseMessage("s", `SELECT tablename, hasindexes FROM pg_tables`),
  833. bindResultMessage("p", "s", nil, []int16{1}),
  834. executeMessage("p"),
  835. &Message{Type: MsgSync},
  836. )
  837. messages := readAllMessages(t, conn.bytes())
  838. if msg := findError(messages); msg != nil {
  839. t.Fatalf("binary catalog query failed: %s", errorCode(msg))
  840. }
  841. rd := findRowDescription(t, messages)
  842. if got := rowDescriptionFormats(t, rd.Data); !equalInt16(got, []int16{1, 1}) {
  843. t.Fatalf("catalog formats = %v", got)
  844. }
  845. rows := dataRowsRaw(t, messages)
  846. if len(rows) != 1 || string(rows[0][0]) != "t" {
  847. t.Fatalf("catalog rows = %q", rows)
  848. }
  849. // hasindexes is a binary boolean.
  850. if len(rows[0][1]) != 1 || rows[0][1][0] != 0 {
  851. t.Fatalf("hasindexes = %v, want false", rows[0][1])
  852. }
  853. }
  854. // A stored value the requested binary type cannot represent fails mid-result:
  855. // the RowDescription and rows already written stay written, the failing DataRow
  856. // is never emitted, the transaction is marked failed, and the connection stays
  857. // usable after ROLLBACK.
  858. func TestBinaryResultsEncodeFailureMidResult(t *testing.T) {
  859. c, conn := extendedResultConnection(t)
  860. for _, sql := range []string{
  861. `CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER)`,
  862. `INSERT INTO t VALUES (1, 5), (2, 'abc'), (3, 7)`,
  863. } {
  864. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  865. t.Fatalf("%s: %v", sql, err)
  866. }
  867. }
  868. if msg := findError(runSimple(t, c, conn, "BEGIN")); msg != nil {
  869. t.Fatalf("BEGIN failed: %s", errorCode(msg))
  870. }
  871. conn.reset()
  872. sendExtended(t, c,
  873. parseMessage("s", `SELECT id, x FROM t ORDER BY id`),
  874. bindResultMessage("p", "s", nil, []int16{1, 1}),
  875. executeMessage("p"),
  876. &Message{Type: MsgSync},
  877. )
  878. messages := readAllMessages(t, conn.bytes())
  879. if got := messageTypes(messages); got != "12TDEZ" {
  880. t.Fatalf("messages = %q, want Parse/Bind/RowDescription/DataRow/Error/ReadyForQuery", got)
  881. }
  882. rows := dataRowsRaw(t, messages)
  883. if len(rows) != 1 {
  884. t.Fatalf("got %d DataRows, want only the rows before the failure", len(rows))
  885. }
  886. assertBytes(t, "row before the failure", rows[0][0], []byte{0, 0, 0, 1})
  887. assertBytes(t, "int4 before the failure", rows[0][1], []byte{0, 0, 0, 5})
  888. if code := errorCode(findError(messages)); code != ErrCodeInvalidText {
  889. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeInvalidText)
  890. }
  891. ready := messages[len(messages)-1]
  892. if ready.Type != MsgReadyForQuery || ready.Data[0] != TxStatusFailed {
  893. t.Fatalf("transaction status = %c, want failed", ready.Data[0])
  894. }
  895. // The connection recovers after ROLLBACK and a new cycle.
  896. conn.reset()
  897. if err := c.handleMessage(&Message{Type: MsgQuery, Data: []byte("ROLLBACK\x00")}); err != nil {
  898. t.Fatal(err)
  899. }
  900. conn.reset()
  901. sendExtended(t, c,
  902. parseMessage("s2", `SELECT x FROM t WHERE id = 1`),
  903. bindResultMessage("p2", "s2", nil, []int16{1}),
  904. executeMessage("p2"),
  905. &Message{Type: MsgSync},
  906. )
  907. messages = readAllMessages(t, conn.bytes())
  908. if msg := findError(messages); msg != nil {
  909. t.Fatalf("query after the encode failure failed: %s", errorCode(msg))
  910. }
  911. if rows := dataRowsRaw(t, messages); len(rows) != 1 || !bytes.Equal(rows[0][0], []byte{0, 0, 0, 5}) {
  912. t.Fatalf("rows after recovery = %v", rows)
  913. }
  914. }
  915. // A value that does not fit int4 (2^40 in an INTEGER column) is an encoding
  916. // failure even when the value converted cleanly at insert time.
  917. func TestBinaryResultsEncodeFailureOutOfRange(t *testing.T) {
  918. c, conn := extendedResultConnection(t)
  919. for _, sql := range []string{
  920. `CREATE TABLE big (id INTEGER PRIMARY KEY, x INTEGER)`,
  921. `INSERT INTO big VALUES (1, 1099511627776)`,
  922. } {
  923. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  924. t.Fatalf("%s: %v", sql, err)
  925. }
  926. }
  927. sendExtended(t, c,
  928. parseMessage("s", `SELECT x FROM big`),
  929. bindResultMessage("p", "s", nil, []int16{1}),
  930. executeMessage("p"),
  931. &Message{Type: MsgSync},
  932. )
  933. messages := readAllMessages(t, conn.bytes())
  934. if got := messageTypes(messages); got != "12TEZ" {
  935. t.Fatalf("messages = %q, want no DataRow for the failing value", got)
  936. }
  937. if code := errorCode(findError(messages)); code != ErrCodeInvalidText {
  938. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeInvalidText)
  939. }
  940. }
  941. // Close('P') drops the portal and therefore its result formats; a later Bind
  942. // under the same name starts from the new list.
  943. func TestBinaryResultsCloseDropsPortalFormats(t *testing.T) {
  944. c, conn := extendedResultConnection(t)
  945. prepareBinaryResultTable(t, c)
  946. sendExtended(t, c,
  947. parseMessage("s", `SELECT id FROM t ORDER BY id`),
  948. bindResultMessage("p", "s", nil, []int16{1}),
  949. executeMessage("p"),
  950. &Message{Type: MsgClose, Data: []byte{'P', 'p', 0}},
  951. bindResultMessage("p", "s", nil, nil),
  952. executeMessage("p"),
  953. &Message{Type: MsgSync},
  954. )
  955. messages := readAllMessages(t, conn.bytes())
  956. if msg := findError(messages); msg != nil {
  957. t.Fatalf("close/re-bind failed: %s", errorCode(msg))
  958. }
  959. var formats [][]int16
  960. var rows [][][]byte
  961. for _, message := range messages {
  962. switch message.Type {
  963. case MsgRowDescription:
  964. formats = append(formats, rowDescriptionFormats(t, message.Data))
  965. case MsgDataRow:
  966. rows = append(rows, dataRowRawValues(t, message))
  967. }
  968. }
  969. if len(formats) != 2 || !equalInt16(formats[0], []int16{1}) {
  970. t.Fatalf("formats = %v, want the first portal binary", formats)
  971. }
  972. if !equalInt16(formats[1], []int16{0}) {
  973. t.Fatalf("formats after Close = %v, want text", formats[1])
  974. }
  975. if len(rows) != 4 {
  976. t.Fatalf("got %d rows, want 4", len(rows))
  977. }
  978. assertBytes(t, "binary portal id", rows[0][0], []byte{0, 0, 0, 1})
  979. if string(rows[2][0]) != "1" {
  980. t.Fatalf("re-bound text portal id = %q", rows[2][0])
  981. }
  982. }
  983. // Float results keep their exact bit patterns, including -0 and NaN; float4
  984. // rejects values whose conversion would become an infinity.
  985. func TestBinaryFloatBitPatterns(t *testing.T) {
  986. negativeZero := math.Copysign(0, -1)
  987. nan := math.NaN()
  988. positiveInfinity := math.Inf(1)
  989. negativeInfinity := math.Inf(-1)
  990. cases := []struct {
  991. name string
  992. oid int32
  993. value interface{}
  994. want []byte
  995. }{
  996. {"float8 -0", 701, negativeZero, binary.BigEndian.AppendUint64(nil, 0x8000000000000000)},
  997. {"float8 NaN", 701, nan, binary.BigEndian.AppendUint64(nil, math.Float64bits(nan))},
  998. {"float8 +Inf", 701, positiveInfinity, binary.BigEndian.AppendUint64(nil, 0x7ff0000000000000)},
  999. {"float8 -Inf", 701, negativeInfinity, binary.BigEndian.AppendUint64(nil, 0xfff0000000000000)},
  1000. {"float8 Infinity text", 701, "Infinity", binary.BigEndian.AppendUint64(nil, 0x7ff0000000000000)},
  1001. {"float4 -0", 700, negativeZero, []byte{0x80, 0, 0, 0}},
  1002. {"float4 NaN", 700, nan, []byte{0x7f, 0xc0, 0, 0}},
  1003. {"int8 -2^63", 20, float64(math.MinInt64), []byte{0x80, 0, 0, 0, 0, 0, 0, 0}},
  1004. }
  1005. for _, tc := range cases {
  1006. t.Run(tc.name, func(t *testing.T) {
  1007. got, err := appendBinaryResultValue(nil, tc.oid, tc.value)
  1008. if err != nil {
  1009. t.Fatalf("encode %s: %v", tc.name, err)
  1010. }
  1011. assertBytes(t, tc.name, got, tc.want)
  1012. })
  1013. }
  1014. // float4 cannot represent an infinity, and 2^63 does not fit int8.
  1015. for _, tc := range []struct {
  1016. name string
  1017. oid int32
  1018. value interface{}
  1019. }{
  1020. {"float4 +Inf", 700, positiveInfinity},
  1021. {"float4 -Inf", 700, negativeInfinity},
  1022. {"float4 Infinity text", 700, "-Infinity"},
  1023. {"int8 2^63", 20, math.Pow(2, 63)},
  1024. } {
  1025. t.Run(tc.name, func(t *testing.T) {
  1026. if _, err := appendBinaryResultValue(nil, tc.oid, tc.value); err == nil {
  1027. t.Fatalf("%s: encoding succeeded, want an error", tc.name)
  1028. }
  1029. })
  1030. }
  1031. }