result_binary_test.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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 portal
  310. // replaces its 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. bindResultMessage("text_portal", "s", nil, []int16{0}),
  322. executeMessage("text_portal"),
  323. &Message{Type: MsgSync},
  324. )
  325. messages := readAllMessages(t, conn.bytes())
  326. if msg := findError(messages); msg != nil {
  327. t.Fatalf("per-portal result failed: %s", errorCode(msg))
  328. }
  329. var descriptions [][]int16
  330. var rows [][][]byte
  331. for _, message := range messages {
  332. switch message.Type {
  333. case MsgRowDescription:
  334. descriptions = append(descriptions, rowDescriptionFormats(t, message.Data))
  335. case MsgDataRow:
  336. rows = append(rows, dataRowRawValues(t, message))
  337. }
  338. }
  339. if len(descriptions) != 3 {
  340. t.Fatalf("got %d RowDescriptions, want 3", len(descriptions))
  341. }
  342. if !equalInt16(descriptions[0], []int16{0, 0}) {
  343. t.Fatalf("text portal formats = %v", descriptions[0])
  344. }
  345. if !equalInt16(descriptions[1], []int16{1, 1}) {
  346. t.Fatalf("binary portal formats = %v", descriptions[1])
  347. }
  348. if !equalInt16(descriptions[2], []int16{0, 0}) {
  349. t.Fatalf("re-bound text portal formats = %v", descriptions[2])
  350. }
  351. // The two text portals put "1" and "1" in the first row; the binary portal
  352. // puts four bytes.
  353. if len(rows) != 6 {
  354. t.Fatalf("got %d rows, want 6", len(rows))
  355. }
  356. if string(rows[0][0]) != "1" || string(rows[1][0]) != "2" {
  357. t.Fatalf("text portal rows = %q %q", rows[0][0], rows[1][0])
  358. }
  359. assertBytes(t, "binary portal id", rows[2][0], []byte{0, 0, 0, 1})
  360. if string(rows[4][0]) != "1" || string(rows[5][0]) != "2" {
  361. t.Fatalf("re-bound text portal rows = %q %q", rows[4][0], rows[5][0])
  362. }
  363. }
  364. // A format list whose length is neither one nor the column count is a protocol
  365. // violation, and no RowDescription or DataRow is sent before the error.
  366. func TestBinaryResultsFormatCountMismatch(t *testing.T) {
  367. c, conn := extendedResultConnection(t)
  368. prepareBinaryResultTable(t, c)
  369. sendExtended(t, c,
  370. parseMessage("s", `SELECT id, flag FROM t`),
  371. bindResultMessage("p", "s", nil, []int16{0, 1, 0}),
  372. executeMessage("p"),
  373. &Message{Type: MsgSync},
  374. )
  375. messages := readAllMessages(t, conn.bytes())
  376. if got := messageTypes(messages); got != "12"+string(MsgErrorResponse)+string(MsgReadyForQuery) {
  377. t.Fatalf("messages = %q", got)
  378. }
  379. if code := errorCode(findError(messages)); code != ErrCodeProtocolViolation {
  380. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeProtocolViolation)
  381. }
  382. if c.extendedFailed {
  383. t.Fatal("Sync did not clear the extended-failure state")
  384. }
  385. // The connection is reusable after the rejected cycle.
  386. conn.reset()
  387. sendExtended(t, c,
  388. parseMessage("s2", `SELECT id FROM t ORDER BY id`),
  389. bindResultMessage("p2", "s2", nil, []int16{1}),
  390. executeMessage("p2"),
  391. &Message{Type: MsgSync},
  392. )
  393. messages = readAllMessages(t, conn.bytes())
  394. if msg := findError(messages); msg != nil {
  395. t.Fatalf("query after format error failed: %s", errorCode(msg))
  396. }
  397. rows := dataRowsRaw(t, messages)
  398. if len(rows) != 2 || !bytes.Equal(rows[0][0], []byte{0, 0, 0, 1}) {
  399. t.Fatalf("rows after format error = %v", rows)
  400. }
  401. }
  402. // A binary request for a type the wire layer cannot encode fails with 0A000
  403. // before any result frame, including when the statement has rows to return.
  404. func TestBinaryResultsUnsupportedOID(t *testing.T) {
  405. c, conn := extendedResultConnection(t)
  406. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE stamps (id INTEGER PRIMARY KEY, created_at TIMESTAMP)`)); err != nil {
  407. t.Fatal(err)
  408. }
  409. if _, err := c.executor.Execute(parseStmt(t, `INSERT INTO stamps VALUES (1, '2024-01-02 03:04:05')`)); err != nil {
  410. t.Fatal(err)
  411. }
  412. sendExtended(t, c,
  413. parseMessage("s", `SELECT id, created_at FROM stamps`),
  414. bindResultMessage("p", "s", nil, []int16{1}),
  415. executeMessage("p"),
  416. &Message{Type: MsgSync},
  417. )
  418. messages := readAllMessages(t, conn.bytes())
  419. if got := messageTypes(messages); got != "12"+string(MsgErrorResponse)+string(MsgReadyForQuery) {
  420. t.Fatalf("messages = %q", got)
  421. }
  422. if code := errorCode(findError(messages)); code != ErrCodeFeatureNotSupported {
  423. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeFeatureNotSupported)
  424. }
  425. // Text format for the same statement still works.
  426. conn.reset()
  427. sendExtended(t, c,
  428. parseMessage("s2", `SELECT created_at FROM stamps`),
  429. bindResultMessage("p2", "s2", nil, nil),
  430. executeMessage("p2"),
  431. &Message{Type: MsgSync},
  432. )
  433. messages = readAllMessages(t, conn.bytes())
  434. if msg := findError(messages); msg != nil {
  435. t.Fatalf("text query after unsupported binary failed: %s", errorCode(msg))
  436. }
  437. if rows := dataRowsRaw(t, messages); len(rows) != 1 || string(rows[0][0]) != "2024-01-02 03:04:05" {
  438. t.Fatalf("text rows = %q", rows)
  439. }
  440. }
  441. // A rejected format inside an explicit transaction block marks the transaction
  442. // failed, as any statement error does, and no rows are emitted.
  443. func TestBinaryResultsErrorFailsTransaction(t *testing.T) {
  444. c, conn := extendedResultConnection(t)
  445. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)); err != nil {
  446. t.Fatal(err)
  447. }
  448. sendExtended(t, c,
  449. &Message{Type: MsgQuery, Data: []byte("BEGIN\x00")},
  450. parseMessage("s", `SELECT id FROM t`),
  451. bindResultMessage("p", "s", nil, []int16{0, 1}),
  452. executeMessage("p"),
  453. &Message{Type: MsgSync},
  454. )
  455. messages := readAllMessages(t, conn.bytes())
  456. ready := messages[len(messages)-1]
  457. if ready.Type != MsgReadyForQuery || ready.Data[0] != TxStatusFailed {
  458. t.Fatalf("transaction status = %q, want failed", messageTypes(messages))
  459. }
  460. if code := errorCode(findError(messages)); code != ErrCodeProtocolViolation {
  461. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeProtocolViolation)
  462. }
  463. // While failed, the next statement is refused with 25P02.
  464. conn.reset()
  465. sendExtended(t, c,
  466. parseMessage("s2", `SELECT id FROM t`),
  467. bindResultMessage("p2", "s2", nil, nil),
  468. executeMessage("p2"),
  469. &Message{Type: MsgSync},
  470. )
  471. messages = readAllMessages(t, conn.bytes())
  472. if code := errorCode(findError(messages)); code != ErrCodeTransactionAborted {
  473. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeTransactionAborted)
  474. }
  475. }
  476. // appendNumericText encodes exact decimal text into base-10000 groups, keeping
  477. // the display scale and rejecting values that are not decimal numbers.
  478. func TestAppendNumericTextEncoding(t *testing.T) {
  479. cases := []struct {
  480. text string
  481. ndigits uint16
  482. weight int16
  483. sign uint16
  484. dscale uint16
  485. digits []uint16
  486. }{
  487. {"0", 0, 0, 0, 0, nil},
  488. {"1.10", 2, 0, 0, 2, []uint16{1, 1000}},
  489. {"-2.5", 2, 0, 0x4000, 1, []uint16{2, 5000}},
  490. {"12345678901234567890", 5, 4, 0, 0, []uint16{1234, 5678, 9012, 3456, 7890}},
  491. {"1e5", 1, 1, 0, 0, []uint16{10}},
  492. {"0.00001", 1, -2, 0, 5, []uint16{1000}},
  493. {"-0.000", 0, 0, 0, 3, nil},
  494. }
  495. for _, tc := range cases {
  496. t.Run(tc.text, func(t *testing.T) {
  497. encoded, err := appendNumericText(nil, tc.text)
  498. if err != nil {
  499. t.Fatalf("appendNumericText(%q): %v", tc.text, err)
  500. }
  501. assertNumericBinary(t, tc.text, encoded, tc.ndigits, tc.weight, tc.dscale, tc.digits, tc.sign)
  502. })
  503. }
  504. for _, text := range []string{"", "abc", "1.2.3", "NaN", "1e999999"} {
  505. if _, err := appendNumericText(nil, text); err == nil {
  506. t.Errorf("appendNumericText(%q) succeeded, want an error", text)
  507. }
  508. }
  509. }
  510. // assertNumericBinary checks a binary numeric payload.
  511. func assertNumericBinary(t *testing.T, label string, data []byte, ndigits uint16, weight int16, dscale uint16, digits []uint16, sign ...uint16) {
  512. t.Helper()
  513. wantSign := uint16(0)
  514. if len(sign) > 0 {
  515. wantSign = sign[0]
  516. }
  517. if len(data) != 8+2*len(digits) {
  518. t.Fatalf("%s numeric length = %d, want %d (%v)", label, len(data), 8+2*len(digits), data)
  519. }
  520. if got := binary.BigEndian.Uint16(data[0:2]); got != ndigits {
  521. t.Errorf("%s ndigits = %d, want %d", label, got, ndigits)
  522. }
  523. if got := int16(binary.BigEndian.Uint16(data[2:4])); got != weight {
  524. t.Errorf("%s weight = %d, want %d", label, got, weight)
  525. }
  526. if got := binary.BigEndian.Uint16(data[4:6]); got != wantSign {
  527. t.Errorf("%s sign = %#x, want %#x", label, got, wantSign)
  528. }
  529. if got := binary.BigEndian.Uint16(data[6:8]); got != dscale {
  530. t.Errorf("%s dscale = %d, want %d", label, got, dscale)
  531. }
  532. for i, want := range digits {
  533. if got := binary.BigEndian.Uint16(data[8+2*i : 10+2*i]); got != want {
  534. t.Errorf("%s digit %d = %d, want %d", label, i, got, want)
  535. }
  536. }
  537. }
  538. func assertArrayHeader(t *testing.T, label string, data []byte, elementOID int32, count int, hasNull bool) {
  539. t.Helper()
  540. if len(data) < 12 {
  541. t.Fatalf("%s array is %d bytes, want a header", label, len(data))
  542. }
  543. if ndim := int32(binary.BigEndian.Uint32(data[0:4])); ndim != 1 {
  544. t.Fatalf("%s ndim = %d, want 1", label, ndim)
  545. }
  546. wantNull := uint32(0)
  547. if hasNull {
  548. wantNull = 1
  549. }
  550. if got := binary.BigEndian.Uint32(data[4:8]); got != wantNull {
  551. t.Errorf("%s hasnull = %d, want %d", label, got, wantNull)
  552. }
  553. if got := int32(binary.BigEndian.Uint32(data[8:12])); got != elementOID {
  554. t.Errorf("%s element OID = %d, want %d", label, got, elementOID)
  555. }
  556. if len(data) < 20 {
  557. t.Fatalf("%s array is %d bytes, want a dimension header", label, len(data))
  558. }
  559. if got := int32(binary.BigEndian.Uint32(data[12:16])); got != int32(count) {
  560. t.Errorf("%s dimension length = %d, want %d", label, got, count)
  561. }
  562. if got := int32(binary.BigEndian.Uint32(data[16:20])); got != 1 {
  563. t.Errorf("%s lower bound = %d, want 1", label, got)
  564. }
  565. }
  566. // arrayElements splits the element payloads of a one-dimensional binary array.
  567. func arrayElements(t *testing.T, data []byte) [][]byte {
  568. t.Helper()
  569. if len(data) < 20 {
  570. t.Fatalf("array is %d bytes, want a dimension header", len(data))
  571. }
  572. count := int(binary.BigEndian.Uint32(data[12:16]))
  573. pos := 20
  574. elements := make([][]byte, 0, count)
  575. for i := 0; i < count; i++ {
  576. if pos+4 > len(data) {
  577. t.Fatalf("array element %d is truncated", i)
  578. }
  579. length := int32(binary.BigEndian.Uint32(data[pos : pos+4]))
  580. pos += 4
  581. if length == -1 {
  582. elements = append(elements, nil)
  583. continue
  584. }
  585. if length < 0 || pos+int(length) > len(data) {
  586. t.Fatalf("array element %d length %d out of range", i, length)
  587. }
  588. elements = append(elements, data[pos:pos+int(length)])
  589. pos += int(length)
  590. }
  591. if pos != len(data) {
  592. t.Fatalf("array has %d trailing bytes", len(data)-pos)
  593. }
  594. return elements
  595. }
  596. func assertBytes(t *testing.T, label string, got, want []byte) {
  597. t.Helper()
  598. if !bytes.Equal(got, want) {
  599. t.Fatalf("%s = %q (%v), want %q (%v)", label, got, got, want, want)
  600. }
  601. }
  602. func equalInt16(left, right []int16) bool {
  603. if len(left) != len(right) {
  604. return false
  605. }
  606. for i := range left {
  607. if left[i] != right[i] {
  608. return false
  609. }
  610. }
  611. return true
  612. }
  613. // Describe never exposes the portal's bound formats: statement and portal
  614. // Describe both return NoData, and the formats travel with the RowDescription
  615. // emitted at Execute.
  616. func TestBinaryResultsDescribeStaysNoData(t *testing.T) {
  617. c, conn := extendedResultConnection(t)
  618. prepareBinaryResultTable(t, c)
  619. sendExtended(t, c,
  620. parseMessage("s", `SELECT id, flag FROM t`),
  621. bindResultMessage("p", "s", nil, []int16{1}),
  622. &Message{Type: MsgDescribe, Data: []byte{'S', 's', 0}},
  623. &Message{Type: MsgDescribe, Data: []byte{'P', 'p', 0}},
  624. &Message{Type: MsgSync},
  625. )
  626. messages := readAllMessages(t, conn.bytes())
  627. if got := messageTypes(messages); got != "12tnnZ" {
  628. t.Fatalf("messages = %q, want ParseComplete/BindComplete/ParameterDescription/NoData/NoData/ReadyForQuery", got)
  629. }
  630. }
  631. // Simple-query results keep text format; only a Bind result format list can
  632. // request binary columns.
  633. func TestSimpleQueryResultsStayText(t *testing.T) {
  634. c, conn := extendedResultConnection(t)
  635. prepareBinaryResultTable(t, c)
  636. messages := runSimple(t, c, conn, `SELECT id, flag, tags FROM t ORDER BY id`)
  637. if msg := findError(messages); msg != nil {
  638. t.Fatalf("simple query failed: %s", errorCode(msg))
  639. }
  640. rd := findRowDescription(t, messages)
  641. for i, format := range rowDescriptionFormats(t, rd.Data) {
  642. if format != 0 {
  643. t.Fatalf("column %d format = %d, want text", i, format)
  644. }
  645. }
  646. rows := dataRowsRaw(t, messages)
  647. if len(rows) != 2 || string(rows[0][2]) != `{a,"b c"}` {
  648. t.Fatalf("simple rows = %q", rows)
  649. }
  650. }
  651. // VARCHAR arrays report OID 1015 and carry element OID 1043.
  652. func TestBinaryResultsVarcharArray(t *testing.T) {
  653. c, conn := extendedResultConnection(t)
  654. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE v (id INTEGER PRIMARY KEY, names VARCHAR(10)[])`)); err != nil {
  655. t.Fatal(err)
  656. }
  657. if _, err := c.executor.Execute(parseStmt(t, `INSERT INTO v VALUES (1, '{a,"b c"}')`)); err != nil {
  658. t.Fatal(err)
  659. }
  660. sendExtended(t, c,
  661. parseMessage("s", `SELECT names FROM v`),
  662. bindResultMessage("p", "s", nil, []int16{1}),
  663. executeMessage("p"),
  664. &Message{Type: MsgSync},
  665. )
  666. messages := readAllMessages(t, conn.bytes())
  667. if msg := findError(messages); msg != nil {
  668. t.Fatalf("varchar[] result failed: %s", errorCode(msg))
  669. }
  670. rd := findRowDescription(t, messages)
  671. if oid := rowDescriptionOIDs(t, rd.Data)[0]; oid != 1015 {
  672. t.Fatalf("varchar[] OID = %d, want 1015", oid)
  673. }
  674. rows := dataRowsRaw(t, messages)
  675. assertArrayHeader(t, "varchar[]", rows[0][0], 1043, 2, false)
  676. elements := arrayElements(t, rows[0][0])
  677. if len(elements) != 2 || string(elements[0]) != "a" || string(elements[1]) != "b c" {
  678. t.Fatalf("varchar[] elements = %q", elements)
  679. }
  680. }
  681. // TestAppendBinaryResultValue covers the encoders that no SQL type reaches
  682. // directly, such as int2 results and int2 arrays.
  683. func TestAppendBinaryResultValue(t *testing.T) {
  684. cases := []struct {
  685. name string
  686. oid int32
  687. value interface{}
  688. want []byte
  689. }{
  690. {"bool true", 16, bool(true), []byte{1}},
  691. {"bool from int64", 16, int64(0), []byte{0}},
  692. {"bool from text", 16, "yes", []byte{1}},
  693. {"int2", 21, int64(-2), []byte{0xff, 0xfe}},
  694. {"int2 from text", 21, "12", []byte{0, 12}},
  695. {"int4", 23, int64(1), []byte{0, 0, 0, 1}},
  696. {"int8", 20, int64(-1), []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
  697. {"float4", 700, float64(1.5), binary.BigEndian.AppendUint32(nil, math.Float32bits(1.5))},
  698. {"float8", 701, int64(2), binary.BigEndian.AppendUint64(nil, math.Float64bits(2))},
  699. {"text", 25, "alpha", []byte("alpha")},
  700. {"varchar", 1043, "beta", []byte("beta")},
  701. {"bytea bytes", 17, []byte{0x00, 0xff}, []byte{0x00, 0xff}},
  702. {"bytea string", 17, "ab", []byte("ab")},
  703. {"numeric text", 1700, "1.10", []byte{0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0x03, 0xe8}},
  704. {"numeric int", 1700, int64(2), []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 2}},
  705. {"jsonb", 3802, `{"a": 1}`, []byte(`{"a": 1}`)},
  706. }
  707. for _, tc := range cases {
  708. t.Run(tc.name, func(t *testing.T) {
  709. got, err := appendBinaryResultValue(nil, tc.oid, tc.value)
  710. if err != nil {
  711. t.Fatalf("appendBinaryResultValue(%d, %v): %v", tc.oid, tc.value, err)
  712. }
  713. want := tc.want
  714. if tc.oid == 3802 {
  715. want = append([]byte{1}, tc.want...)
  716. }
  717. assertBytes(t, tc.name, got, want)
  718. })
  719. }
  720. for _, tc := range []struct {
  721. name string
  722. oid int32
  723. value interface{}
  724. }{
  725. {"int4 from text", 23, "abc"},
  726. {"int2 out of range", 21, int64(70000)},
  727. {"int4 out of range", 23, int64(1) << 40},
  728. {"bool from text", 16, "maybe"},
  729. {"float from text", 701, "abc"},
  730. {"unsupported oid", 1184, "2024-01-02 03:04:05"},
  731. {"numeric from bool", 1700, true},
  732. } {
  733. t.Run(tc.name, func(t *testing.T) {
  734. if _, err := appendBinaryResultValue(nil, tc.oid, tc.value); err == nil {
  735. t.Fatalf("%s: encoding succeeded, want an error", tc.name)
  736. }
  737. })
  738. }
  739. }
  740. // TestBinaryResultArrayOIDs covers every array OID in the encoding table,
  741. // including the int2/varchar forms the engine reports for declared arrays.
  742. func TestBinaryResultArrayOIDs(t *testing.T) {
  743. cases := []struct {
  744. name string
  745. arrayOID int32
  746. elementOID int32
  747. text string
  748. want [][]byte
  749. }{
  750. {"bool[]", 1000, 16, "{t,f}", [][]byte{{1}, {0}}},
  751. {"int2[]", 1005, 21, "{1,-2}", [][]byte{{0, 1}, {0xff, 0xfe}}},
  752. {"int4[]", 1007, 23, "{1,2}", [][]byte{{0, 0, 0, 1}, {0, 0, 0, 2}}},
  753. {"text[]", 1009, 25, `{a,"b c"}`, [][]byte{[]byte("a"), []byte("b c")}},
  754. {"varchar[]", 1015, 1043, "{a}", [][]byte{[]byte("a")}},
  755. {"int8[]", 1016, 20, "{1}", [][]byte{{0, 0, 0, 0, 0, 0, 0, 1}}},
  756. {"float4[]", 1021, 700, "{1.5}", [][]byte{binary.BigEndian.AppendUint32(nil, math.Float32bits(1.5))}},
  757. {"float8[]", 1022, 701, "{1.5}", [][]byte{binary.BigEndian.AppendUint64(nil, math.Float64bits(1.5))}},
  758. {"numeric[]", 1231, 1700, "{1.10}", [][]byte{{0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0x03, 0xe8}}},
  759. }
  760. for _, tc := range cases {
  761. t.Run(tc.name, func(t *testing.T) {
  762. got, err := appendBinaryResultValue(nil, tc.arrayOID, tc.text)
  763. if err != nil {
  764. t.Fatalf("encode %s: %v", tc.name, err)
  765. }
  766. assertArrayHeader(t, tc.name, got, tc.elementOID, len(tc.want), false)
  767. elements := arrayElements(t, got)
  768. if len(elements) != len(tc.want) {
  769. t.Fatalf("%s elements = %d, want %d", tc.name, len(elements), len(tc.want))
  770. }
  771. for i := range tc.want {
  772. assertBytes(t, tc.name+" element", elements[i], tc.want[i])
  773. }
  774. })
  775. }
  776. // Unsupported array OIDs are not encodable.
  777. for _, oid := range []int32{1001 /* bytea[] */, 1185 /* timestamptz[] */} {
  778. if canEncodeBinaryResult(oid) {
  779. t.Errorf("canEncodeBinaryResult(%d) = true", oid)
  780. }
  781. }
  782. for _, oid := range []int32{16, 17, 20, 21, 23, 25, 700, 701, 1043, 1700, 3802, 1000, 1005, 1007, 1009, 1015, 1016, 1021, 1022, 1231} {
  783. if !canEncodeBinaryResult(oid) {
  784. t.Errorf("canEncodeBinaryResult(%d) = false", oid)
  785. }
  786. }
  787. for _, oid := range []int32{1082, 1184} {
  788. if canEncodeBinaryResult(oid) {
  789. t.Errorf("canEncodeBinaryResult(%d) = true", oid)
  790. }
  791. }
  792. }
  793. // A statement that writes and returns rows executes before its Bind result
  794. // formats are resolved, because the row metadata only exists at Execute. The
  795. // rejected binary request therefore sends no rows but does not undo the write;
  796. // this ordering is documented in the capability document.
  797. func TestBinaryResultsRejectAfterDMLExecution(t *testing.T) {
  798. c, conn := extendedResultConnection(t)
  799. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE w (id INTEGER PRIMARY KEY, created_at TIMESTAMP)`)); err != nil {
  800. t.Fatal(err)
  801. }
  802. sendExtended(t, c,
  803. parseMessage("s", `INSERT INTO w VALUES (1, '2024-01-02 03:04:05') RETURNING created_at`),
  804. bindResultMessage("p", "s", nil, []int16{1}),
  805. executeMessage("p"),
  806. &Message{Type: MsgSync},
  807. )
  808. messages := readAllMessages(t, conn.bytes())
  809. if got := messageTypes(messages); got != "12"+string(MsgErrorResponse)+string(MsgReadyForQuery) {
  810. t.Fatalf("messages = %q, want no RowDescription or DataRow", got)
  811. }
  812. if code := errorCode(findError(messages)); code != ErrCodeFeatureNotSupported {
  813. t.Fatalf("SQLSTATE = %s, want %s", code, ErrCodeFeatureNotSupported)
  814. }
  815. result, err := c.executor.Execute(parseStmt(t, `SELECT COUNT(*) FROM w`))
  816. if err != nil {
  817. t.Fatal(err)
  818. }
  819. if result.Rows[0][0] != int64(1) {
  820. t.Fatalf("write was rolled back: %v", result.Rows)
  821. }
  822. }
  823. // Catalog emulation results use the same per-portal formats as executor
  824. // results: their text and boolean columns encode as binary.
  825. func TestBinaryResultsCatalogQuery(t *testing.T) {
  826. c, conn := extendedResultConnection(t)
  827. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)); err != nil {
  828. t.Fatal(err)
  829. }
  830. sendExtended(t, c,
  831. parseMessage("s", `SELECT tablename, hasindexes FROM pg_tables`),
  832. bindResultMessage("p", "s", nil, []int16{1}),
  833. executeMessage("p"),
  834. &Message{Type: MsgSync},
  835. )
  836. messages := readAllMessages(t, conn.bytes())
  837. if msg := findError(messages); msg != nil {
  838. t.Fatalf("binary catalog query failed: %s", errorCode(msg))
  839. }
  840. rd := findRowDescription(t, messages)
  841. if got := rowDescriptionFormats(t, rd.Data); !equalInt16(got, []int16{1, 1}) {
  842. t.Fatalf("catalog formats = %v", got)
  843. }
  844. rows := dataRowsRaw(t, messages)
  845. if len(rows) != 1 || string(rows[0][0]) != "t" {
  846. t.Fatalf("catalog rows = %q", rows)
  847. }
  848. // hasindexes is a binary boolean.
  849. if len(rows[0][1]) != 1 || rows[0][1][0] != 0 {
  850. t.Fatalf("hasindexes = %v, want false", rows[0][1])
  851. }
  852. }