array_param_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. package pgserver
  2. import (
  3. "bufio"
  4. "testing"
  5. )
  6. // arrayConnection returns a connection whose output is captured in memory, so
  7. // both simple and extended protocol responses can be decoded.
  8. func arrayConnection(t *testing.T) (*Connection, *countingConn) {
  9. t.Helper()
  10. c, _ := newDataConnection(t)
  11. conn := &countingConn{}
  12. c.conn = conn
  13. c.writer = bufio.NewWriter(conn)
  14. return c, conn
  15. }
  16. // runSimple sends a simple Query and returns the decoded response messages.
  17. func runSimple(t *testing.T, c *Connection, conn *countingConn, sql string) []*Message {
  18. t.Helper()
  19. if err := c.handleMessage(&Message{Type: MsgQuery, Data: append([]byte(sql), 0)}); err != nil {
  20. t.Fatalf("handleQuery %q: %v", sql, err)
  21. }
  22. return readAllMessages(t, conn.bytes())
  23. }
  24. // runExtendedParseBindExecute drives one extended-protocol cycle with a single
  25. // text-format parameter of the given OID.
  26. func runExtendedParseBindExecute(t *testing.T, c *Connection, conn *countingConn, sql string, oid int32, value string) []*Message {
  27. t.Helper()
  28. parse := NewMessageBuilder()
  29. parse.WriteString("s1")
  30. parse.WriteString(sql)
  31. parse.WriteInt16(1)
  32. parse.WriteInt32(oid)
  33. bind := NewMessageBuilder()
  34. bind.WriteString("p1")
  35. bind.WriteString("s1")
  36. bind.WriteInt16(0) // all-text parameter formats
  37. bind.WriteInt16(1)
  38. bind.WriteInt32(int32(len(value)))
  39. bind.WriteBytes([]byte(value))
  40. bind.WriteInt16(0) // all-text result formats
  41. execute := NewMessageBuilder()
  42. execute.WriteString("p1")
  43. execute.WriteInt32(0)
  44. for _, msg := range []*Message{
  45. {Type: MsgParse, Data: parse.Bytes()},
  46. {Type: MsgBind, Data: bind.Bytes()},
  47. {Type: MsgExecute, Data: execute.Bytes()},
  48. {Type: MsgSync},
  49. } {
  50. if err := c.handleMessage(msg); err != nil {
  51. t.Fatalf("handle %c: %v", msg.Type, err)
  52. }
  53. }
  54. return readAllMessages(t, conn.bytes())
  55. }
  56. func firstDataRow(t *testing.T, messages []*Message) []string {
  57. t.Helper()
  58. for _, msg := range messages {
  59. if msg.Type == MsgDataRow {
  60. return dataRowValues(t, msg)
  61. }
  62. }
  63. return nil
  64. }
  65. func findError(messages []*Message) *Message {
  66. for _, msg := range messages {
  67. if msg.Type == MsgErrorResponse {
  68. return msg
  69. }
  70. }
  71. return nil
  72. }
  73. // A pg client that binds text[] for "id = ANY($1)" gets the rows the array
  74. // describes instead of a rewritten query.
  75. func TestArrayParameterOverExtendedQuery(t *testing.T) {
  76. c, conn := arrayConnection(t)
  77. for _, sql := range []string{
  78. `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[])`,
  79. `INSERT INTO t VALUES (1, '{a}'), (2, '{b}'), (3, '{c}')`,
  80. } {
  81. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  82. t.Fatalf("%s: %v", sql, err)
  83. }
  84. }
  85. messages := runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1) ORDER BY id`, 1007, "{1,3}")
  86. if errMsg := findError(messages); errMsg != nil {
  87. t.Fatalf("extended ANY($1) failed: %s", errorCode(errMsg))
  88. }
  89. rows := [][]string{}
  90. for _, msg := range messages {
  91. if msg.Type == MsgDataRow {
  92. rows = append(rows, dataRowValues(t, msg))
  93. }
  94. }
  95. if len(rows) != 2 || rows[0][0] != "1" || rows[1][0] != "3" {
  96. t.Fatalf("ANY($1) rows = %v, want [[1] [3]]", rows)
  97. }
  98. // text[] uses the same text binding path.
  99. conn.reset()
  100. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1)`, 1009, "{1,2}")
  101. if errMsg := findError(messages); errMsg != nil {
  102. t.Fatalf("extended text[] ANY($1) failed: %s", errorCode(errMsg))
  103. }
  104. if row := firstDataRow(t, messages); len(row) != 1 {
  105. t.Fatalf("text[] ANY($1) returned %v", row)
  106. }
  107. // With no declared OID the server infers int4[] from id = ANY($1). The
  108. // text-format value still binds as array text, which is what drivers that
  109. // send text parameters rely on.
  110. conn.reset()
  111. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1) ORDER BY id`, 0, "{2,3}")
  112. if errMsg := findError(messages); errMsg != nil {
  113. t.Fatalf("extended untyped ANY($1) failed: %s", errorCode(errMsg))
  114. }
  115. rows = nil
  116. for _, msg := range messages {
  117. if msg.Type == MsgDataRow {
  118. rows = append(rows, dataRowValues(t, msg))
  119. }
  120. }
  121. if len(rows) != 2 || rows[0][0] != "2" || rows[1][0] != "3" {
  122. t.Fatalf("untyped ANY($1) rows = %v, want [[2] [3]]", rows)
  123. }
  124. // NULL elements follow three-valued logic through the parameter path: rows
  125. // whose comparison is NULL are not returned, rows that match are.
  126. conn.reset()
  127. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1) ORDER BY id`, 1007, "{NULL,2}")
  128. if errMsg := findError(messages); errMsg != nil {
  129. t.Fatalf("extended NULL-element ANY($1) failed: %s", errorCode(errMsg))
  130. }
  131. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "2" {
  132. t.Fatalf("NULL-element ANY($1) row = %v, want [2]", row)
  133. }
  134. // An empty array parameter matches nothing and is not an error.
  135. conn.reset()
  136. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1)`, 1007, "{}")
  137. if errMsg := findError(messages); errMsg != nil {
  138. t.Fatalf("extended empty-array ANY($1) failed: %s", errorCode(errMsg))
  139. }
  140. if row := firstDataRow(t, messages); len(row) != 0 {
  141. t.Fatalf("empty-array ANY($1) returned %v, want no rows", row)
  142. }
  143. // ALL keeps its dual semantics through the parameter path.
  144. conn.reset()
  145. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id <> ALL($1)`, 1007, "{1,2}")
  146. if errMsg := findError(messages); errMsg != nil {
  147. t.Fatalf("extended ALL($1) failed: %s", errorCode(errMsg))
  148. }
  149. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "3" {
  150. t.Fatalf("ALL($1) row = %v, want [3]", row)
  151. }
  152. }
  153. func TestArrayParameterOverSimpleQuery(t *testing.T) {
  154. c, conn := arrayConnection(t)
  155. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)); err != nil {
  156. t.Fatal(err)
  157. }
  158. for _, sql := range []string{
  159. `INSERT INTO t VALUES (1), (2), (3)`,
  160. } {
  161. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  162. t.Fatalf("%s: %v", sql, err)
  163. }
  164. }
  165. messages := runSimple(t, c, conn, `SELECT id FROM t WHERE id = ANY('{1,3}') ORDER BY id`)
  166. if errMsg := findError(messages); errMsg != nil {
  167. t.Fatalf("simple ANY failed: %s", errorCode(errMsg))
  168. }
  169. rows := [][]string{}
  170. for _, msg := range messages {
  171. if msg.Type == MsgDataRow {
  172. rows = append(rows, dataRowValues(t, msg))
  173. }
  174. }
  175. if len(rows) != 2 || rows[0][0] != "1" || rows[1][0] != "3" {
  176. t.Fatalf("simple ANY rows = %v", rows)
  177. }
  178. }
  179. func TestArrayResultMetadataOIDs(t *testing.T) {
  180. c, conn := arrayConnection(t)
  181. for _, sql := range []string{
  182. `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[], nums INTEGER[], big BIGINT[], small SMALLINT[], flags BOOLEAN[], reals REAL[], doubles DOUBLE PRECISION[], nums2 NUMERIC[], names VARCHAR(10)[])`,
  183. `INSERT INTO t VALUES (1, '{a}', '{1}', '{1}', '{1}', '{t}', '{1.5}', '{1.5}', '{1.5}', '{x}')`,
  184. } {
  185. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  186. t.Fatalf("%s: %v", sql, err)
  187. }
  188. }
  189. messages := runSimple(t, c, conn, `SELECT tags, nums, big, small, flags, reals, doubles, nums2, names FROM t`)
  190. rd := findRowDescription(t, messages)
  191. oids := rowDescriptionOIDs(t, rd.Data)
  192. want := []int32{1009, 1007, 1016, 1016, 1000, 1021, 1022, 1231, 1015}
  193. if len(oids) != len(want) {
  194. t.Fatalf("array OIDs = %v, want %v", oids, want)
  195. }
  196. for i, oid := range want {
  197. if oids[i] != oid {
  198. t.Errorf("column %d OID = %d, want %d", i, oids[i], oid)
  199. }
  200. }
  201. conn.reset()
  202. messages = runSimple(t, c, conn, `SELECT '{1,2}'::INTEGER[] AS arr`)
  203. rd = findRowDescription(t, messages)
  204. if oid := rowDescriptionOIDs(t, rd.Data)[0]; oid != 1007 {
  205. t.Fatalf("cast array OID = %d, want 1007", oid)
  206. }
  207. if row := firstDataRow(t, messages); row[0] != "{1,2}" {
  208. t.Fatalf("cast array text = %v, want {1,2}", row)
  209. }
  210. }
  211. // A binary int4[] parameter is decoded from PostgreSQL's array envelope, so
  212. // drivers that always encode arrays in binary (pgx) can bind ANY($1).
  213. func TestArrayParameterBinaryFormatAccepted(t *testing.T) {
  214. c, conn := arrayConnection(t)
  215. for _, sql := range []string{
  216. `CREATE TABLE t (id INTEGER PRIMARY KEY)`,
  217. `INSERT INTO t VALUES (1), (2), (3)`,
  218. } {
  219. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  220. t.Fatalf("%s: %v", sql, err)
  221. }
  222. }
  223. parse := NewMessageBuilder()
  224. parse.WriteString("s1")
  225. parse.WriteString(`SELECT id FROM t WHERE id = ANY($1) ORDER BY id`)
  226. parse.WriteInt16(1)
  227. parse.WriteInt32(1007) // int4[]
  228. value := encodeBinaryArray(23, [][]byte{encodeInt4(1), encodeInt4(3)})
  229. bind := NewMessageBuilder()
  230. bind.WriteString("p1")
  231. bind.WriteString("s1")
  232. bind.WriteInt16(1) // one parameter format code
  233. bind.WriteInt16(1) // binary
  234. bind.WriteInt16(1) // one parameter
  235. bind.WriteInt32(int32(len(value)))
  236. bind.WriteBytes(value)
  237. bind.WriteInt16(0)
  238. execute := NewMessageBuilder()
  239. execute.WriteString("p1")
  240. execute.WriteInt32(0)
  241. for _, msg := range []*Message{
  242. {Type: MsgParse, Data: parse.Bytes()},
  243. {Type: MsgBind, Data: bind.Bytes()},
  244. {Type: MsgExecute, Data: execute.Bytes()},
  245. {Type: MsgSync},
  246. } {
  247. if err := c.handleMessage(msg); err != nil {
  248. t.Fatalf("handle %c: %v", msg.Type, err)
  249. }
  250. }
  251. messages := readAllMessages(t, conn.bytes())
  252. if errMsg := findError(messages); errMsg != nil {
  253. t.Fatalf("binary array parameter failed: %s", errorCode(errMsg))
  254. }
  255. rows := [][]string{}
  256. for _, message := range messages {
  257. if message.Type == MsgDataRow {
  258. rows = append(rows, dataRowValues(t, message))
  259. }
  260. }
  261. if len(rows) != 2 || rows[0][0] != "1" || rows[1][0] != "3" {
  262. t.Fatalf("binary ANY($1) rows = %v, want [[1] [3]]", rows)
  263. }
  264. }
  265. func TestMalformedArrayParameterReportsInvalidText(t *testing.T) {
  266. c, conn := arrayConnection(t)
  267. for _, sql := range []string{
  268. `CREATE TABLE t (id INTEGER PRIMARY KEY)`,
  269. `INSERT INTO t VALUES (1)`,
  270. } {
  271. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  272. t.Fatalf("%s: %v", sql, err)
  273. }
  274. }
  275. messages := runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1)`, 1007, "{1,2")
  276. errMsg := findError(messages)
  277. if errMsg == nil {
  278. t.Fatal("malformed array parameter was accepted")
  279. }
  280. if code := errorCode(errMsg); code != ErrCodeInvalidText {
  281. t.Fatalf("malformed array error code = %q, want %q", code, ErrCodeInvalidText)
  282. }
  283. conn.reset()
  284. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1)`, 1007, "{not-an-int}")
  285. errMsg = findError(messages)
  286. if errMsg == nil {
  287. t.Fatal("non-integer element was accepted for int4[]")
  288. }
  289. if code := errorCode(errMsg); code != ErrCodeInvalidText {
  290. t.Fatalf("invalid element error code = %q, want %q", code, ErrCodeInvalidText)
  291. }
  292. }
  293. // reset discards captured output so one test can inspect several responses.
  294. func (c *countingConn) reset() {
  295. c.mu.Lock()
  296. defer c.mu.Unlock()
  297. c.buf.Reset()
  298. }
  299. // A parameter inside a subscript must bind: brackets are ordinary syntax, not a
  300. // quoted region.
  301. func TestArraySubscriptParameterBinding(t *testing.T) {
  302. c, conn := arrayConnection(t)
  303. for _, sql := range []string{
  304. `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[])`,
  305. `INSERT INTO t VALUES (1, '{a,b,c}')`,
  306. } {
  307. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  308. t.Fatalf("%s: %v", sql, err)
  309. }
  310. }
  311. messages := runExtendedParseBindExecute(t, c, conn, `SELECT tags[$1] FROM t`, 23, "2")
  312. if errMsg := findError(messages); errMsg != nil {
  313. t.Fatalf("tags[$1] failed: %s", errorCode(errMsg))
  314. }
  315. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "b" {
  316. t.Fatalf("tags[$1] row = %v, want [b]", row)
  317. }
  318. // A parameter with no declared OID is inferred as INTEGER from the
  319. // subscript and binds the same way.
  320. conn.reset()
  321. messages = runExtendedParseBindExecute(t, c, conn, `SELECT tags[$1] FROM t`, 0, "3")
  322. if errMsg := findError(messages); errMsg != nil {
  323. t.Fatalf("untyped tags[$1] failed: %s", errorCode(errMsg))
  324. }
  325. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "c" {
  326. t.Fatalf("untyped tags[$1] row = %v, want [c]", row)
  327. }
  328. }
  329. // Quotes and backslashes inside a bound array element survive canonicalization,
  330. // SQL-literal interpolation, casting, and storage unchanged.
  331. func TestArrayParameterEscapedElements(t *testing.T) {
  332. c, conn := arrayConnection(t)
  333. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[])`)); err != nil {
  334. t.Fatal(err)
  335. }
  336. const arrayText = "{\"it's\",\"back\\\\slash\"}"
  337. const canonical = "{it's,\"back\\\\slash\"}"
  338. messages := runExtendedParseBindExecute(t, c, conn, `SELECT $1::TEXT[]`, 1009, arrayText)
  339. if errMsg := findError(messages); errMsg != nil {
  340. t.Fatalf("escaped array cast failed: %s", errorCode(errMsg))
  341. }
  342. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != canonical {
  343. t.Fatalf("escaped array text = %v, want %s", row, canonical)
  344. }
  345. conn.reset()
  346. messages = runExtendedParseBindExecute(t, c, conn, `INSERT INTO t VALUES (1, $1) RETURNING tags`, 1009, arrayText)
  347. if errMsg := findError(messages); errMsg != nil {
  348. t.Fatalf("escaped array insert failed: %s", errorCode(errMsg))
  349. }
  350. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != canonical {
  351. t.Fatalf("escaped array insert = %v, want %s", row, canonical)
  352. }
  353. }
  354. func TestNumericArrayCastOID(t *testing.T) {
  355. c, conn := arrayConnection(t)
  356. messages := runSimple(t, c, conn, `SELECT '{1.10,12345678901234567890}'::NUMERIC[] AS amounts`)
  357. if errMsg := findError(messages); errMsg != nil {
  358. t.Fatalf("numeric array cast failed: %s", errorCode(errMsg))
  359. }
  360. rd := findRowDescription(t, messages)
  361. if oid := rowDescriptionOIDs(t, rd.Data)[0]; oid != 1231 {
  362. t.Fatalf("numeric array OID = %d, want 1231", oid)
  363. }
  364. if row := firstDataRow(t, messages); row[0] != "{1.10,12345678901234567890}" {
  365. t.Fatalf("numeric array value = %v", row[0])
  366. }
  367. }
  368. func TestArrayIndexTypeSQLState(t *testing.T) {
  369. c, conn := arrayConnection(t)
  370. for _, sql := range []string{
  371. `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[])`,
  372. `INSERT INTO t VALUES (1, '{10,20}')`,
  373. } {
  374. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  375. t.Fatalf("%s: %v", sql, err)
  376. }
  377. }
  378. for _, sql := range []string{
  379. `SELECT nums[1.5] FROM t`,
  380. `SELECT nums[TRUE] FROM t`,
  381. `SELECT nums['x'] FROM t`,
  382. `SELECT array_length(nums, 1.5) FROM t`,
  383. } {
  384. conn.reset()
  385. messages := runSimple(t, c, conn, sql)
  386. errMsg := findError(messages)
  387. if errMsg == nil {
  388. t.Errorf("%s was accepted", sql)
  389. continue
  390. }
  391. if code := errorCode(errMsg); code != ErrCodeDatatypeMismatch {
  392. t.Errorf("%s SQLSTATE = %q, want %q", sql, code, ErrCodeDatatypeMismatch)
  393. }
  394. }
  395. }
  396. func TestBooleanSubscriptPredicateOverSimpleQuery(t *testing.T) {
  397. c, conn := arrayConnection(t)
  398. for _, sql := range []string{
  399. `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[])`,
  400. `INSERT INTO t VALUES (1, '{t,f}'), (2, '{f,t}')`,
  401. } {
  402. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  403. t.Fatalf("%s: %v", sql, err)
  404. }
  405. }
  406. for _, tc := range []struct{ sql, want string }{
  407. {`SELECT count(*) FROM t WHERE flags[1]`, "1"},
  408. {`SELECT count(*) FROM t WHERE NOT flags[1]`, "1"},
  409. {`SELECT count(*) FROM t WHERE flags[1] = TRUE`, "1"},
  410. } {
  411. conn.reset()
  412. messages := runSimple(t, c, conn, tc.sql)
  413. if errMsg := findError(messages); errMsg != nil {
  414. t.Fatalf("%s failed: %s", tc.sql, errorCode(errMsg))
  415. }
  416. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != tc.want {
  417. t.Errorf("%s = %v, want %s", tc.sql, row, tc.want)
  418. }
  419. }
  420. }