array_param_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. // An unspecified parameter OID keeps the array literal untyped; ANY still
  108. // reads it as array text, which is what drivers that omit OIDs rely on.
  109. conn.reset()
  110. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1) ORDER BY id`, 0, "{2,3}")
  111. if errMsg := findError(messages); errMsg != nil {
  112. t.Fatalf("extended untyped ANY($1) failed: %s", errorCode(errMsg))
  113. }
  114. rows = nil
  115. for _, msg := range messages {
  116. if msg.Type == MsgDataRow {
  117. rows = append(rows, dataRowValues(t, msg))
  118. }
  119. }
  120. if len(rows) != 2 || rows[0][0] != "2" || rows[1][0] != "3" {
  121. t.Fatalf("untyped ANY($1) rows = %v, want [[2] [3]]", rows)
  122. }
  123. // NULL elements follow three-valued logic through the parameter path: rows
  124. // whose comparison is NULL are not returned, rows that match are.
  125. conn.reset()
  126. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1) ORDER BY id`, 1007, "{NULL,2}")
  127. if errMsg := findError(messages); errMsg != nil {
  128. t.Fatalf("extended NULL-element ANY($1) failed: %s", errorCode(errMsg))
  129. }
  130. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "2" {
  131. t.Fatalf("NULL-element ANY($1) row = %v, want [2]", row)
  132. }
  133. // An empty array parameter matches nothing and is not an error.
  134. conn.reset()
  135. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1)`, 1007, "{}")
  136. if errMsg := findError(messages); errMsg != nil {
  137. t.Fatalf("extended empty-array ANY($1) failed: %s", errorCode(errMsg))
  138. }
  139. if row := firstDataRow(t, messages); len(row) != 0 {
  140. t.Fatalf("empty-array ANY($1) returned %v, want no rows", row)
  141. }
  142. // ALL keeps its dual semantics through the parameter path.
  143. conn.reset()
  144. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id <> ALL($1)`, 1007, "{1,2}")
  145. if errMsg := findError(messages); errMsg != nil {
  146. t.Fatalf("extended ALL($1) failed: %s", errorCode(errMsg))
  147. }
  148. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "3" {
  149. t.Fatalf("ALL($1) row = %v, want [3]", row)
  150. }
  151. }
  152. func TestArrayParameterOverSimpleQuery(t *testing.T) {
  153. c, conn := arrayConnection(t)
  154. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)); err != nil {
  155. t.Fatal(err)
  156. }
  157. for _, sql := range []string{
  158. `INSERT INTO t VALUES (1), (2), (3)`,
  159. } {
  160. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  161. t.Fatalf("%s: %v", sql, err)
  162. }
  163. }
  164. messages := runSimple(t, c, conn, `SELECT id FROM t WHERE id = ANY('{1,3}') ORDER BY id`)
  165. if errMsg := findError(messages); errMsg != nil {
  166. t.Fatalf("simple ANY failed: %s", errorCode(errMsg))
  167. }
  168. rows := [][]string{}
  169. for _, msg := range messages {
  170. if msg.Type == MsgDataRow {
  171. rows = append(rows, dataRowValues(t, msg))
  172. }
  173. }
  174. if len(rows) != 2 || rows[0][0] != "1" || rows[1][0] != "3" {
  175. t.Fatalf("simple ANY rows = %v", rows)
  176. }
  177. }
  178. func TestArrayResultMetadataOIDs(t *testing.T) {
  179. c, conn := arrayConnection(t)
  180. for _, sql := range []string{
  181. `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)[])`,
  182. `INSERT INTO t VALUES (1, '{a}', '{1}', '{1}', '{1}', '{t}', '{1.5}', '{1.5}', '{1.5}', '{x}')`,
  183. } {
  184. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  185. t.Fatalf("%s: %v", sql, err)
  186. }
  187. }
  188. messages := runSimple(t, c, conn, `SELECT tags, nums, big, small, flags, reals, doubles, nums2, names FROM t`)
  189. rd := findRowDescription(t, messages)
  190. oids := rowDescriptionOIDs(t, rd.Data)
  191. want := []int32{1009, 1007, 1016, 1016, 1000, 1021, 1022, 1231, 1015}
  192. if len(oids) != len(want) {
  193. t.Fatalf("array OIDs = %v, want %v", oids, want)
  194. }
  195. for i, oid := range want {
  196. if oids[i] != oid {
  197. t.Errorf("column %d OID = %d, want %d", i, oids[i], oid)
  198. }
  199. }
  200. conn.reset()
  201. messages = runSimple(t, c, conn, `SELECT '{1,2}'::INTEGER[] AS arr`)
  202. rd = findRowDescription(t, messages)
  203. if oid := rowDescriptionOIDs(t, rd.Data)[0]; oid != 1007 {
  204. t.Fatalf("cast array OID = %d, want 1007", oid)
  205. }
  206. if row := firstDataRow(t, messages); row[0] != "{1,2}" {
  207. t.Fatalf("cast array text = %v, want {1,2}", row)
  208. }
  209. }
  210. func TestArrayParameterBinaryFormatRejected(t *testing.T) {
  211. c, conn := arrayConnection(t)
  212. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)); err != nil {
  213. t.Fatal(err)
  214. }
  215. parse := NewMessageBuilder()
  216. parse.WriteString("s1")
  217. parse.WriteString(`SELECT id FROM t WHERE id = ANY($1)`)
  218. parse.WriteInt16(1)
  219. parse.WriteInt32(1007) // int4[]
  220. bind := NewMessageBuilder()
  221. bind.WriteString("p1")
  222. bind.WriteString("s1")
  223. bind.WriteInt16(1) // one parameter format code
  224. bind.WriteInt16(1) // binary
  225. bind.WriteInt16(1) // one parameter
  226. bind.WriteInt32(8)
  227. bind.WriteBytes([]byte{0, 0, 0, 1, 0, 0, 0, 2})
  228. bind.WriteInt16(0)
  229. for _, msg := range []*Message{
  230. {Type: MsgParse, Data: parse.Bytes()},
  231. {Type: MsgBind, Data: bind.Bytes()},
  232. {Type: MsgSync},
  233. } {
  234. if err := c.handleMessage(msg); err != nil {
  235. t.Fatalf("handle %c: %v", msg.Type, err)
  236. }
  237. }
  238. messages := readAllMessages(t, conn.bytes())
  239. errMsg := findError(messages)
  240. if errMsg == nil {
  241. t.Fatal("binary array parameter was accepted")
  242. }
  243. if code := errorCode(errMsg); code != ErrCodeFeatureNotSupported {
  244. t.Fatalf("binary array error code = %q, want %q", code, ErrCodeFeatureNotSupported)
  245. }
  246. }
  247. func TestMalformedArrayParameterReportsInvalidText(t *testing.T) {
  248. c, conn := arrayConnection(t)
  249. for _, sql := range []string{
  250. `CREATE TABLE t (id INTEGER PRIMARY KEY)`,
  251. `INSERT INTO t VALUES (1)`,
  252. } {
  253. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  254. t.Fatalf("%s: %v", sql, err)
  255. }
  256. }
  257. messages := runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1)`, 1007, "{1,2")
  258. errMsg := findError(messages)
  259. if errMsg == nil {
  260. t.Fatal("malformed array parameter was accepted")
  261. }
  262. if code := errorCode(errMsg); code != ErrCodeInvalidText {
  263. t.Fatalf("malformed array error code = %q, want %q", code, ErrCodeInvalidText)
  264. }
  265. conn.reset()
  266. messages = runExtendedParseBindExecute(t, c, conn, `SELECT id FROM t WHERE id = ANY($1)`, 1007, "{not-an-int}")
  267. errMsg = findError(messages)
  268. if errMsg == nil {
  269. t.Fatal("non-integer element was accepted for int4[]")
  270. }
  271. if code := errorCode(errMsg); code != ErrCodeInvalidText {
  272. t.Fatalf("invalid element error code = %q, want %q", code, ErrCodeInvalidText)
  273. }
  274. }
  275. // reset discards captured output so one test can inspect several responses.
  276. func (c *countingConn) reset() {
  277. c.mu.Lock()
  278. defer c.mu.Unlock()
  279. c.buf.Reset()
  280. }
  281. // A parameter inside a subscript must bind: brackets are ordinary syntax, not a
  282. // quoted region.
  283. func TestArraySubscriptParameterBinding(t *testing.T) {
  284. c, conn := arrayConnection(t)
  285. for _, sql := range []string{
  286. `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[])`,
  287. `INSERT INTO t VALUES (1, '{a,b,c}')`,
  288. } {
  289. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  290. t.Fatalf("%s: %v", sql, err)
  291. }
  292. }
  293. messages := runExtendedParseBindExecute(t, c, conn, `SELECT tags[$1] FROM t`, 23, "2")
  294. if errMsg := findError(messages); errMsg != nil {
  295. t.Fatalf("tags[$1] failed: %s", errorCode(errMsg))
  296. }
  297. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "b" {
  298. t.Fatalf("tags[$1] row = %v, want [b]", row)
  299. }
  300. // An untyped parameter binds the same way.
  301. conn.reset()
  302. messages = runExtendedParseBindExecute(t, c, conn, `SELECT tags[$1] FROM t`, 0, "3")
  303. if errMsg := findError(messages); errMsg != nil {
  304. t.Fatalf("untyped tags[$1] failed: %s", errorCode(errMsg))
  305. }
  306. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != "c" {
  307. t.Fatalf("untyped tags[$1] row = %v, want [c]", row)
  308. }
  309. }
  310. // Quotes and backslashes inside a bound array element survive canonicalization,
  311. // SQL-literal interpolation, casting, and storage unchanged.
  312. func TestArrayParameterEscapedElements(t *testing.T) {
  313. c, conn := arrayConnection(t)
  314. if _, err := c.executor.Execute(parseStmt(t, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[])`)); err != nil {
  315. t.Fatal(err)
  316. }
  317. const arrayText = "{\"it's\",\"back\\\\slash\"}"
  318. const canonical = "{it's,\"back\\\\slash\"}"
  319. messages := runExtendedParseBindExecute(t, c, conn, `SELECT $1::TEXT[]`, 1009, arrayText)
  320. if errMsg := findError(messages); errMsg != nil {
  321. t.Fatalf("escaped array cast failed: %s", errorCode(errMsg))
  322. }
  323. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != canonical {
  324. t.Fatalf("escaped array text = %v, want %s", row, canonical)
  325. }
  326. conn.reset()
  327. messages = runExtendedParseBindExecute(t, c, conn, `INSERT INTO t VALUES (1, $1) RETURNING tags`, 1009, arrayText)
  328. if errMsg := findError(messages); errMsg != nil {
  329. t.Fatalf("escaped array insert failed: %s", errorCode(errMsg))
  330. }
  331. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != canonical {
  332. t.Fatalf("escaped array insert = %v, want %s", row, canonical)
  333. }
  334. }
  335. func TestNumericArrayCastOID(t *testing.T) {
  336. c, conn := arrayConnection(t)
  337. messages := runSimple(t, c, conn, `SELECT '{1.10,12345678901234567890}'::NUMERIC[] AS amounts`)
  338. if errMsg := findError(messages); errMsg != nil {
  339. t.Fatalf("numeric array cast failed: %s", errorCode(errMsg))
  340. }
  341. rd := findRowDescription(t, messages)
  342. if oid := rowDescriptionOIDs(t, rd.Data)[0]; oid != 1231 {
  343. t.Fatalf("numeric array OID = %d, want 1231", oid)
  344. }
  345. if row := firstDataRow(t, messages); row[0] != "{1.10,12345678901234567890}" {
  346. t.Fatalf("numeric array value = %v", row[0])
  347. }
  348. }
  349. func TestArrayIndexTypeSQLState(t *testing.T) {
  350. c, conn := arrayConnection(t)
  351. for _, sql := range []string{
  352. `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[])`,
  353. `INSERT INTO t VALUES (1, '{10,20}')`,
  354. } {
  355. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  356. t.Fatalf("%s: %v", sql, err)
  357. }
  358. }
  359. for _, sql := range []string{
  360. `SELECT nums[1.5] FROM t`,
  361. `SELECT nums[TRUE] FROM t`,
  362. `SELECT nums['x'] FROM t`,
  363. `SELECT array_length(nums, 1.5) FROM t`,
  364. } {
  365. conn.reset()
  366. messages := runSimple(t, c, conn, sql)
  367. errMsg := findError(messages)
  368. if errMsg == nil {
  369. t.Errorf("%s was accepted", sql)
  370. continue
  371. }
  372. if code := errorCode(errMsg); code != ErrCodeDatatypeMismatch {
  373. t.Errorf("%s SQLSTATE = %q, want %q", sql, code, ErrCodeDatatypeMismatch)
  374. }
  375. }
  376. }
  377. func TestBooleanSubscriptPredicateOverSimpleQuery(t *testing.T) {
  378. c, conn := arrayConnection(t)
  379. for _, sql := range []string{
  380. `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[])`,
  381. `INSERT INTO t VALUES (1, '{t,f}'), (2, '{f,t}')`,
  382. } {
  383. if _, err := c.executor.Execute(parseStmt(t, sql)); err != nil {
  384. t.Fatalf("%s: %v", sql, err)
  385. }
  386. }
  387. for _, tc := range []struct{ sql, want string }{
  388. {`SELECT count(*) FROM t WHERE flags[1]`, "1"},
  389. {`SELECT count(*) FROM t WHERE NOT flags[1]`, "1"},
  390. {`SELECT count(*) FROM t WHERE flags[1] = TRUE`, "1"},
  391. } {
  392. conn.reset()
  393. messages := runSimple(t, c, conn, tc.sql)
  394. if errMsg := findError(messages); errMsg != nil {
  395. t.Fatalf("%s failed: %s", tc.sql, errorCode(errMsg))
  396. }
  397. if row := firstDataRow(t, messages); len(row) != 1 || row[0] != tc.want {
  398. t.Errorf("%s = %v, want %s", tc.sql, row, tc.want)
  399. }
  400. }
  401. }