result_binary_test.go 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  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. // A table without a primary key or explicit index has no indexes; the
  829. // synthetic primary-key index is covered by the catalog tests.
  830. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER)`)); err != nil {
  831. t.Fatal(err)
  832. }
  833. sendExtended(t, c,
  834. parseMessage("s", `SELECT tablename, hasindexes FROM pg_tables`),
  835. bindResultMessage("p", "s", nil, []int16{1}),
  836. executeMessage("p"),
  837. &Message{Type: MsgSync},
  838. )
  839. messages := readAllMessages(t, conn.bytes())
  840. if msg := findError(messages); msg != nil {
  841. t.Fatalf("binary catalog query failed: %s", errorCode(msg))
  842. }
  843. rd := findRowDescription(t, messages)
  844. if got := rowDescriptionFormats(t, rd.Data); !equalInt16(got, []int16{1, 1}) {
  845. t.Fatalf("catalog formats = %v", got)
  846. }
  847. rows := dataRowsRaw(t, messages)
  848. if len(rows) != 1 || string(rows[0][0]) != "t" {
  849. t.Fatalf("catalog rows = %q", rows)
  850. }
  851. // hasindexes is a binary boolean.
  852. if len(rows[0][1]) != 1 || rows[0][1][0] != 0 {
  853. t.Fatalf("hasindexes = %v, want false", rows[0][1])
  854. }
  855. }
  856. // A stored value the requested binary type cannot represent fails mid-result:
  857. // the RowDescription and rows already written stay written, the failing DataRow
  858. // is never emitted, the transaction is marked failed, and the connection stays
  859. // usable after ROLLBACK.
  860. func TestBinaryResultsEncodeFailureMidResult(t *testing.T) {
  861. c, conn := extendedResultConnection(t)
  862. for _, sql := range []string{
  863. `CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER)`,
  864. `INSERT INTO t VALUES (1, 5), (2, 'abc'), (3, 7)`,
  865. } {
  866. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  867. t.Fatalf("%s: %v", sql, err)
  868. }
  869. }
  870. if msg := findError(runSimple(t, c, conn, "BEGIN")); msg != nil {
  871. t.Fatalf("BEGIN failed: %s", errorCode(msg))
  872. }
  873. conn.reset()
  874. sendExtended(t, c,
  875. parseMessage("s", `SELECT id, x FROM t ORDER BY id`),
  876. bindResultMessage("p", "s", nil, []int16{1, 1}),
  877. executeMessage("p"),
  878. &Message{Type: MsgSync},
  879. )
  880. messages := readAllMessages(t, conn.bytes())
  881. if got := messageTypes(messages); got != "12TDEZ" {
  882. t.Fatalf("messages = %q, want Parse/Bind/RowDescription/DataRow/Error/ReadyForQuery", got)
  883. }
  884. rows := dataRowsRaw(t, messages)
  885. if len(rows) != 1 {
  886. t.Fatalf("got %d DataRows, want only the rows before the failure", len(rows))
  887. }
  888. assertBytes(t, "row before the failure", rows[0][0], []byte{0, 0, 0, 1})
  889. assertBytes(t, "int4 before the failure", rows[0][1], []byte{0, 0, 0, 5})
  890. if code := errorCode(findError(messages)); code != ErrCodeInvalidText {
  891. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeInvalidText)
  892. }
  893. ready := messages[len(messages)-1]
  894. if ready.Type != MsgReadyForQuery || ready.Data[0] != TxStatusFailed {
  895. t.Fatalf("transaction status = %c, want failed", ready.Data[0])
  896. }
  897. // The connection recovers after ROLLBACK and a new cycle.
  898. conn.reset()
  899. if err := c.handleMessage(&Message{Type: MsgQuery, Data: []byte("ROLLBACK\x00")}); err != nil {
  900. t.Fatal(err)
  901. }
  902. conn.reset()
  903. sendExtended(t, c,
  904. parseMessage("s2", `SELECT x FROM t WHERE id = 1`),
  905. bindResultMessage("p2", "s2", nil, []int16{1}),
  906. executeMessage("p2"),
  907. &Message{Type: MsgSync},
  908. )
  909. messages = readAllMessages(t, conn.bytes())
  910. if msg := findError(messages); msg != nil {
  911. t.Fatalf("query after the encode failure failed: %s", errorCode(msg))
  912. }
  913. if rows := dataRowsRaw(t, messages); len(rows) != 1 || !bytes.Equal(rows[0][0], []byte{0, 0, 0, 5}) {
  914. t.Fatalf("rows after recovery = %v", rows)
  915. }
  916. }
  917. // A value that does not fit int4 (2^40 in an INTEGER column) is an encoding
  918. // failure even when the value converted cleanly at insert time.
  919. func TestBinaryResultsEncodeFailureOutOfRange(t *testing.T) {
  920. c, conn := extendedResultConnection(t)
  921. for _, sql := range []string{
  922. `CREATE TABLE big (id INTEGER PRIMARY KEY, x INTEGER)`,
  923. `INSERT INTO big VALUES (1, 1099511627776)`,
  924. } {
  925. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  926. t.Fatalf("%s: %v", sql, err)
  927. }
  928. }
  929. sendExtended(t, c,
  930. parseMessage("s", `SELECT x FROM big`),
  931. bindResultMessage("p", "s", nil, []int16{1}),
  932. executeMessage("p"),
  933. &Message{Type: MsgSync},
  934. )
  935. messages := readAllMessages(t, conn.bytes())
  936. if got := messageTypes(messages); got != "12TEZ" {
  937. t.Fatalf("messages = %q, want no DataRow for the failing value", got)
  938. }
  939. if code := errorCode(findError(messages)); code != ErrCodeInvalidText {
  940. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeInvalidText)
  941. }
  942. }
  943. // Close('P') drops the portal and therefore its result formats; a later Bind
  944. // under the same name starts from the new list.
  945. func TestBinaryResultsCloseDropsPortalFormats(t *testing.T) {
  946. c, conn := extendedResultConnection(t)
  947. prepareBinaryResultTable(t, c)
  948. sendExtended(t, c,
  949. parseMessage("s", `SELECT id FROM t ORDER BY id`),
  950. bindResultMessage("p", "s", nil, []int16{1}),
  951. executeMessage("p"),
  952. &Message{Type: MsgClose, Data: []byte{'P', 'p', 0}},
  953. bindResultMessage("p", "s", nil, nil),
  954. executeMessage("p"),
  955. &Message{Type: MsgSync},
  956. )
  957. messages := readAllMessages(t, conn.bytes())
  958. if msg := findError(messages); msg != nil {
  959. t.Fatalf("close/re-bind failed: %s", errorCode(msg))
  960. }
  961. var formats [][]int16
  962. var rows [][][]byte
  963. for _, message := range messages {
  964. switch message.Type {
  965. case MsgRowDescription:
  966. formats = append(formats, rowDescriptionFormats(t, message.Data))
  967. case MsgDataRow:
  968. rows = append(rows, dataRowRawValues(t, message))
  969. }
  970. }
  971. if len(formats) != 2 || !equalInt16(formats[0], []int16{1}) {
  972. t.Fatalf("formats = %v, want the first portal binary", formats)
  973. }
  974. if !equalInt16(formats[1], []int16{0}) {
  975. t.Fatalf("formats after Close = %v, want text", formats[1])
  976. }
  977. if len(rows) != 4 {
  978. t.Fatalf("got %d rows, want 4", len(rows))
  979. }
  980. assertBytes(t, "binary portal id", rows[0][0], []byte{0, 0, 0, 1})
  981. if string(rows[2][0]) != "1" {
  982. t.Fatalf("re-bound text portal id = %q", rows[2][0])
  983. }
  984. }
  985. // Float results keep their exact bit patterns, including -0 and NaN; float4
  986. // rejects values whose conversion would become an infinity.
  987. func TestBinaryFloatBitPatterns(t *testing.T) {
  988. negativeZero := math.Copysign(0, -1)
  989. nan := math.NaN()
  990. positiveInfinity := math.Inf(1)
  991. negativeInfinity := math.Inf(-1)
  992. cases := []struct {
  993. name string
  994. oid int32
  995. value interface{}
  996. want []byte
  997. }{
  998. {"float8 -0", 701, negativeZero, binary.BigEndian.AppendUint64(nil, 0x8000000000000000)},
  999. {"float8 NaN", 701, nan, binary.BigEndian.AppendUint64(nil, math.Float64bits(nan))},
  1000. {"float8 +Inf", 701, positiveInfinity, binary.BigEndian.AppendUint64(nil, 0x7ff0000000000000)},
  1001. {"float8 -Inf", 701, negativeInfinity, binary.BigEndian.AppendUint64(nil, 0xfff0000000000000)},
  1002. {"float8 Infinity text", 701, "Infinity", binary.BigEndian.AppendUint64(nil, 0x7ff0000000000000)},
  1003. {"float4 -0", 700, negativeZero, []byte{0x80, 0, 0, 0}},
  1004. {"float4 NaN", 700, nan, []byte{0x7f, 0xc0, 0, 0}},
  1005. {"int8 -2^63", 20, float64(math.MinInt64), []byte{0x80, 0, 0, 0, 0, 0, 0, 0}},
  1006. }
  1007. for _, tc := range cases {
  1008. t.Run(tc.name, func(t *testing.T) {
  1009. got, err := appendBinaryResultValue(nil, tc.oid, tc.value)
  1010. if err != nil {
  1011. t.Fatalf("encode %s: %v", tc.name, err)
  1012. }
  1013. assertBytes(t, tc.name, got, tc.want)
  1014. })
  1015. }
  1016. // float4 cannot represent an infinity, and 2^63 does not fit int8.
  1017. for _, tc := range []struct {
  1018. name string
  1019. oid int32
  1020. value interface{}
  1021. }{
  1022. {"float4 +Inf", 700, positiveInfinity},
  1023. {"float4 -Inf", 700, negativeInfinity},
  1024. {"float4 Infinity text", 700, "-Infinity"},
  1025. {"int8 2^63", 20, math.Pow(2, 63)},
  1026. } {
  1027. t.Run(tc.name, func(t *testing.T) {
  1028. if _, err := appendBinaryResultValue(nil, tc.oid, tc.value); err == nil {
  1029. t.Fatalf("%s: encoding succeeded, want an error", tc.name)
  1030. }
  1031. })
  1032. }
  1033. }