arrays_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. package executor
  2. import (
  3. "errors"
  4. "strings"
  5. "testing"
  6. "github.com/danfragoso/pizzasql-next/pkg/analyzer"
  7. "github.com/danfragoso/pizzasql-next/pkg/lexer"
  8. "github.com/danfragoso/pizzasql-next/pkg/parser"
  9. "github.com/danfragoso/pizzasql-next/pkg/pgerror"
  10. "github.com/danfragoso/pizzasql-next/pkg/storage"
  11. )
  12. func TestArrayColumnDDLAndRoundTrip(t *testing.T) {
  13. _, schema, table := newTestDB(t)
  14. e := newExec(schema, table)
  15. execMust(t, e, `CREATE TABLE items (
  16. id INTEGER PRIMARY KEY,
  17. tags TEXT[] NOT NULL,
  18. scores INTEGER[],
  19. flags BOOLEAN[],
  20. ratios DOUBLE PRECISION[]
  21. )`)
  22. execMust(t, e, `INSERT INTO items (id, tags, scores, flags, ratios)
  23. VALUES (1, '{a,b}', '{1,2}', '{t,f}', '{1.5,2.25}')`)
  24. execMust(t, e, `INSERT INTO items (id, tags, scores)
  25. VALUES (2, '{}', NULL)`)
  26. res := execMust(t, e, `SELECT id, tags, scores, flags, ratios FROM items ORDER BY id`)
  27. if got := res.Rows[0][1]; got != "{a,b}" {
  28. t.Fatalf("tags = %v, want {a,b}", got)
  29. }
  30. // Elements are canonicalized per declared type.
  31. if got := res.Rows[0][2]; got != "{1,2}" {
  32. t.Fatalf("scores = %v, want {1,2}", got)
  33. }
  34. if got := res.Rows[0][3]; got != "{t,f}" {
  35. t.Fatalf("flags = %v, want {t,f}", got)
  36. }
  37. if got := res.Rows[0][4]; got != "{1.5,2.25}" {
  38. t.Fatalf("ratios = %v, want {1.5,2.25}", got)
  39. }
  40. // NULL and the empty array stay distinguishable.
  41. if got := res.Rows[1][1]; got != "{}" {
  42. t.Fatalf("empty tags = %v, want {}", got)
  43. }
  44. if got := res.Rows[1][2]; got != nil {
  45. t.Fatalf("NULL scores = %v, want nil", got)
  46. }
  47. // Result metadata reports the declared array type.
  48. if got := res.ColumnTypes[1]; got != "TEXT[]" {
  49. t.Fatalf("tags column type = %q, want TEXT[]", got)
  50. }
  51. // The durable catalog keeps the array spelling, so sqlite_master and
  52. // PRAGMA table_info still render it.
  53. master := execMust(t, e, `SELECT sql FROM sqlite_master WHERE name = 'items'`)
  54. if sql, _ := master.Rows[0][0].(string); !strings.Contains(sql, "TEXT[]") {
  55. t.Fatalf("sqlite_master SQL = %q, want it to contain TEXT[]", sql)
  56. }
  57. info := execMust(t, e, `PRAGMA table_info(items)`)
  58. found := false
  59. for _, row := range info.Rows {
  60. if row[1] == "scores" {
  61. found = true
  62. if row[2] != "INTEGER[]" {
  63. t.Fatalf("PRAGMA table_info type = %v, want INTEGER[]", row[2])
  64. }
  65. }
  66. }
  67. if !found {
  68. t.Fatal("PRAGMA table_info did not report the scores column")
  69. }
  70. // A fresh executor over the same store sees the same bytes: no reload
  71. // conversion and no storage-format change.
  72. reloaded := newExec(schema, table)
  73. res = execMust(t, reloaded, `SELECT tags, scores, flags FROM items WHERE id = 1`)
  74. if res.Rows[0][0] != "{a,b}" || res.Rows[0][1] != "{1,2}" || res.Rows[0][2] != "{t,f}" {
  75. t.Fatalf("reloaded row = %v", res.Rows[0])
  76. }
  77. }
  78. func TestArrayCanonicalEncoding(t *testing.T) {
  79. _, schema, table := newTestDB(t)
  80. e := newExec(schema, table)
  81. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT[])`)
  82. for _, tc := range []struct{ sql, want string }{
  83. {`INSERT INTO t VALUES (1, '{ a , b }') RETURNING v`, "{a,b}"},
  84. {`INSERT INTO t VALUES (2, '{"a b","c,d"}') RETURNING v`, `{"a b","c,d"}`},
  85. {`INSERT INTO t VALUES (3, ARRAY['x', NULL, 'NULL']) RETURNING v`, `{x,NULL,"NULL"}`},
  86. {`INSERT INTO t VALUES (4, ARRAY[]::TEXT[]) RETURNING v`, "{}"},
  87. {`INSERT INTO t VALUES (5, '{"quote\"d", "back\\slash"}') RETURNING v`, `{"quote\"d","back\\slash"}`},
  88. } {
  89. res := execMust(t, e, tc.sql)
  90. if got := res.Rows[0][0]; got != tc.want {
  91. t.Errorf("%s = %v, want %s", tc.sql, got, tc.want)
  92. }
  93. }
  94. }
  95. func TestArrayConstructorAndCast(t *testing.T) {
  96. _, schema, table := newTestDB(t)
  97. e := newExec(schema, table)
  98. cases := []struct {
  99. sql string
  100. want interface{}
  101. }{
  102. {`SELECT ARRAY[1, 2, 3]`, "{1,2,3}"},
  103. {`SELECT ARRAY['a', NULL]`, "{a,NULL}"},
  104. {`SELECT ARRAY[TRUE, FALSE]`, "{t,f}"},
  105. {`SELECT '{1,2,3}'::INTEGER[]`, "{1,2,3}"},
  106. {`SELECT CAST('{1,2}' AS INTEGER[])`, "{1,2}"},
  107. {`SELECT ARRAY['1','2']::INTEGER[]`, "{1,2}"},
  108. {`SELECT '{1.50,2}'::NUMERIC[]`, "{1.50,2}"},
  109. {`SELECT NULL::INTEGER[]`, nil},
  110. }
  111. for _, tc := range cases {
  112. res := execMust(t, e, tc.sql)
  113. if got := res.Rows[0][0]; got != tc.want {
  114. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  115. }
  116. }
  117. res := execMust(t, e, `SELECT ARRAY[1,2] AS a, '{x}'::TEXT[] AS b`)
  118. if res.ColumnTypes[0] != "INTEGER[]" || res.ColumnTypes[1] != "TEXT[]" {
  119. t.Fatalf("constructor types = %v, want [INTEGER[] TEXT[]]", res.ColumnTypes)
  120. }
  121. }
  122. func TestArraySubscriptAndLength(t *testing.T) {
  123. _, schema, table := newTestDB(t)
  124. e := newExec(schema, table)
  125. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[], tags TEXT[], flags BOOLEAN[])`)
  126. execMust(t, e, `INSERT INTO t VALUES (1, '{10,20,30}', '{"x", NULL, "z"}', '{t,f}')`)
  127. execMust(t, e, `INSERT INTO t VALUES (2, '{}', '{}', '{}')`)
  128. cases := []struct {
  129. sql string
  130. want interface{}
  131. }{
  132. {`SELECT nums[1] FROM t WHERE id = 1`, int64(10)},
  133. {`SELECT nums[3] FROM t WHERE id = 1`, int64(30)},
  134. {`SELECT nums[4] FROM t WHERE id = 1`, nil},
  135. {`SELECT nums[0] FROM t WHERE id = 1`, nil},
  136. {`SELECT tags[2] FROM t WHERE id = 1`, nil},
  137. {`SELECT tags[1] FROM t WHERE id = 1`, "x"},
  138. {`SELECT flags[1] FROM t WHERE id = 1`, true},
  139. {`SELECT flags[2] FROM t WHERE id = 1`, false},
  140. {`SELECT nums[1] FROM t WHERE id = 2`, nil},
  141. {`SELECT array_length(nums, 1) FROM t WHERE id = 1`, int64(3)},
  142. {`SELECT array_length(nums, 1) FROM t WHERE id = 2`, nil},
  143. {`SELECT array_length(nums, 2) FROM t WHERE id = 1`, nil},
  144. }
  145. for _, tc := range cases {
  146. res := execMust(t, e, tc.sql)
  147. if got := res.Rows[0][0]; got != tc.want {
  148. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  149. }
  150. }
  151. res := execMust(t, e, `SELECT nums[1] AS n FROM t WHERE id = 1`)
  152. if res.ColumnTypes[0] != "INTEGER" {
  153. t.Fatalf("subscript type = %q, want INTEGER", res.ColumnTypes[0])
  154. }
  155. }
  156. func TestAnyAllThreeValuedLogic(t *testing.T) {
  157. _, schema, table := newTestDB(t)
  158. e := newExec(schema, table)
  159. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, n INTEGER, b BOOLEAN)`)
  160. execMust(t, e, `INSERT INTO t VALUES (1, 2, TRUE)`)
  161. cases := []struct {
  162. sql string
  163. want interface{}
  164. }{
  165. // ANY: true if any comparison is true.
  166. {`SELECT 2 = ANY('{1,2,3}')`, true},
  167. {`SELECT 5 = ANY('{1,2,3}')`, false},
  168. // NULL elements: no true and a NULL comparison yields NULL.
  169. {`SELECT 5 = ANY('{1,NULL,3}')`, nil},
  170. {`SELECT 2 = ANY('{NULL,2}')`, true},
  171. // Empty array: ANY is false, ALL is true, even for a NULL left side.
  172. {`SELECT 5 = ANY('{}')`, false},
  173. {`SELECT NULL = ANY('{}')`, false},
  174. {`SELECT 5 <> ALL('{}')`, true},
  175. {`SELECT NULL <> ALL('{}')`, true},
  176. // ALL: false if any comparison is false, NULL if only NULLs remain.
  177. {`SELECT 2 <> ALL('{1,3,4}')`, true},
  178. {`SELECT 2 <> ALL('{1,2,4}')`, false},
  179. {`SELECT 2 <> ALL('{1,NULL}')`, nil},
  180. {`SELECT 3 > ALL('{1,2}')`, true},
  181. {`SELECT 1 >= ALL('{1,NULL}')`, nil},
  182. // A NULL left operand yields NULL for a non-empty array.
  183. {`SELECT NULL = ANY('{1,2}')`, nil},
  184. {`SELECT NULL <> ALL('{1,2}')`, nil},
  185. // Boolean elements compare as booleans, not as text.
  186. {`SELECT TRUE = ANY('{f,t}')`, true},
  187. {`SELECT FALSE = ANY('{f,t}')`, true},
  188. {`SELECT TRUE <> ALL('{f,t}')`, false},
  189. // Column-driven forms, including a NULL column.
  190. {`SELECT n = ANY('{1,2}') FROM t`, true},
  191. {`SELECT n <> ALL('{1,2}') FROM t`, false},
  192. {`SELECT b = ANY('{t}') FROM t`, true},
  193. {`SELECT id = ANY('{9,8}') FROM t`, false},
  194. }
  195. for _, tc := range cases {
  196. res := execMust(t, e, tc.sql)
  197. if got := res.Rows[0][0]; got != tc.want {
  198. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  199. }
  200. }
  201. if res := execMust(t, e, `SELECT n = ANY('{1,2}') AS hit FROM t`); res.ColumnTypes[0] != "BOOLEAN" {
  202. t.Fatalf("ANY result type = %q, want BOOLEAN", res.ColumnTypes[0])
  203. }
  204. }
  205. func TestAnyAllOverArrayColumn(t *testing.T) {
  206. _, schema, table := newTestDB(t)
  207. e := newExec(schema, table)
  208. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, allowed TEXT[])`)
  209. execMust(t, e, `INSERT INTO t VALUES (1, '{red,green}'), (2, '{blue}'), (3, NULL)`)
  210. for _, tc := range []struct {
  211. sql string
  212. want int
  213. }{
  214. {`SELECT count(*) FROM t WHERE 'red' = ANY(allowed)`, 1},
  215. {`SELECT count(*) FROM t WHERE 'purple' = ANY(allowed)`, 0},
  216. {`SELECT count(*) FROM t WHERE 'purple' <> ALL(allowed)`, 2},
  217. } {
  218. res := execMust(t, e, tc.sql)
  219. if got := res.Rows[0][0]; got != int64(tc.want) {
  220. t.Errorf("%s = %v, want %d", tc.sql, got, tc.want)
  221. }
  222. }
  223. }
  224. func TestUnnestAsTableFunction(t *testing.T) {
  225. _, schema, table := newTestDB(t)
  226. e := newExec(schema, table)
  227. res := execMust(t, e, `SELECT * FROM unnest('{10,20,30}'::INTEGER[])`)
  228. if res.RowCount != 3 {
  229. t.Fatalf("unnest rows = %d, want 3", res.RowCount)
  230. }
  231. if res.Columns[0] != "unnest" {
  232. t.Fatalf("unnest column = %q, want unnest", res.Columns[0])
  233. }
  234. if res.Rows[0][0] != int64(10) || res.Rows[2][0] != int64(30) {
  235. t.Fatalf("unnest values = %v", res.Rows)
  236. }
  237. if res.ColumnTypes[0] != "INTEGER" {
  238. t.Fatalf("unnest column type = %q, want INTEGER", res.ColumnTypes[0])
  239. }
  240. res = execMust(t, e, `SELECT u.unnest FROM unnest(ARRAY['a', 'b']) AS u WHERE u.unnest <> 'a'`)
  241. if res.RowCount != 1 || res.Rows[0][0] != "b" {
  242. t.Fatalf("unnest alias rows = %v", res.Rows)
  243. }
  244. res = execMust(t, e, `SELECT * FROM unnest(NULL::TEXT[])`)
  245. if res.RowCount != 0 {
  246. t.Fatalf("unnest(NULL) rows = %d, want 0", res.RowCount)
  247. }
  248. if _, err := execSQL(e, `SELECT unnest('{1,2}'::INTEGER[])`); err == nil {
  249. t.Fatal("scalar unnest should be rejected")
  250. }
  251. }
  252. func TestArrayDefaultsAndAlterTable(t *testing.T) {
  253. _, schema, table := newTestDB(t)
  254. e := newExec(schema, table)
  255. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[] DEFAULT ARRAY['a'])`)
  256. execMust(t, e, `INSERT INTO t (id) VALUES (1)`)
  257. res := execMust(t, e, `SELECT tags FROM t WHERE id = 1`)
  258. if res.Rows[0][0] != "{a}" {
  259. t.Fatalf("default tags = %v, want {a}", res.Rows[0][0])
  260. }
  261. execMust(t, e, `ALTER TABLE t ADD COLUMN nums INTEGER[] DEFAULT '{1, 2}'`)
  262. res = execMust(t, e, `SELECT nums FROM t WHERE id = 1`)
  263. if res.Rows[0][0] != "{1,2}" {
  264. t.Fatalf("added column default = %v, want {1,2}", res.Rows[0][0])
  265. }
  266. execMust(t, e, `INSERT INTO t (id, nums) VALUES (2, '{3}')`)
  267. res = execMust(t, e, `SELECT nums FROM t WHERE id = 2`)
  268. if res.Rows[0][0] != "{3}" {
  269. t.Fatalf("explicit array = %v, want {3}", res.Rows[0][0])
  270. }
  271. }
  272. func TestArrayInCompositePrimaryKey(t *testing.T) {
  273. _, schema, table := newTestDB(t)
  274. e := newExec(schema, table)
  275. execMust(t, e, `CREATE TABLE t (id INTEGER, tags TEXT[], PRIMARY KEY (id, tags))`)
  276. execMust(t, e, `INSERT INTO t VALUES (1, '{a,b}')`)
  277. execMust(t, e, `INSERT INTO t VALUES (1, '{c}')`)
  278. res := execMust(t, e, `SELECT count(*) FROM t`)
  279. if res.Rows[0][0] != int64(2) {
  280. t.Fatalf("rows = %v, want 2", res.Rows[0][0])
  281. }
  282. if _, err := execSQL(e, `INSERT INTO t VALUES (1, '{a, b}')`); err == nil {
  283. t.Fatal("duplicate composite key with an equivalent array should conflict")
  284. }
  285. reloaded := newExec(schema, table)
  286. res = execMust(t, reloaded, `SELECT tags FROM t WHERE id = 1 AND tags = '{a,b}'`)
  287. if res.RowCount != 1 || res.Rows[0][0] != "{a,b}" {
  288. t.Fatalf("reloaded composite-key lookup = %v", res.Rows)
  289. }
  290. }
  291. func TestArrayRejectedShapes(t *testing.T) {
  292. _, schema, table := newTestDB(t)
  293. e := newExec(schema, table)
  294. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT[])`)
  295. cases := []struct {
  296. sql string
  297. unsupported bool
  298. }{
  299. {`INSERT INTO t VALUES (1, '{{1,2},{3,4}}')`, true},
  300. {`SELECT '{{1,2},{3,4}}'::INTEGER[]`, true},
  301. {`SELECT 1 = ANY('not an array')`, false},
  302. {`SELECT 1 = ANY(1)`, false},
  303. {`INSERT INTO t VALUES (2, '{1,2')`, false},
  304. {`INSERT INTO t VALUES (3, 5)`, false},
  305. }
  306. for _, tc := range cases {
  307. _, err := execSQL(e, tc.sql)
  308. if err == nil {
  309. t.Errorf("%s succeeded, want an error", tc.sql)
  310. continue
  311. }
  312. if tc.unsupported && !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  313. t.Errorf("%s error = %v, want ErrFeatureNotSupported", tc.sql, err)
  314. }
  315. if !tc.unsupported && !errors.Is(err, pgerror.ErrInvalidText) {
  316. t.Errorf("%s error = %v, want ErrInvalidText", tc.sql, err)
  317. }
  318. }
  319. }
  320. func TestArrayUpdateAndReturning(t *testing.T) {
  321. _, schema, table := newTestDB(t)
  322. e := newExec(schema, table)
  323. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[])`)
  324. execMust(t, e, `INSERT INTO t VALUES (1, '{a}')`)
  325. res := execMust(t, e, `UPDATE t SET tags = '{b, c}' WHERE id = 1 RETURNING tags`)
  326. if res.Rows[0][0] != "{b,c}" {
  327. t.Fatalf("UPDATE RETURNING tags = %v, want {b,c}", res.Rows[0][0])
  328. }
  329. res = execMust(t, e, `SELECT tags FROM t WHERE id = 1`)
  330. if res.Rows[0][0] != "{b,c}" {
  331. t.Fatalf("stored tags = %v, want {b,c}", res.Rows[0][0])
  332. }
  333. }
  334. // The array value is a plain string in the row codec, so a second decoder sees
  335. // exactly the bytes the first one wrote. A freshly constructed schema and table
  336. // manager pair over the same store stands in for a process restart: no
  337. // converter runs, and the durable catalog plus row bytes are read as written.
  338. func TestArrayStorageReloadIsStable(t *testing.T) {
  339. pool, schema, table := newTestDB(t)
  340. e := newExec(schema, table)
  341. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT[], n INTEGER[])`)
  342. execMust(t, e, `INSERT INTO t VALUES (1, ARRAY['x','y'], '{1,2}')`)
  343. session := storage.NewSession(schema, table)
  344. row, err := session.GetByPK("t", "1")
  345. if err != nil {
  346. t.Fatalf("GetByPK: %v", err)
  347. }
  348. if row["v"] != "{x,y}" {
  349. t.Fatalf("stored value = %#v, want {x,y}", row["v"])
  350. }
  351. reloadedSchema := storage.NewSchemaManager(pool, "testdb")
  352. reloadedTable := storage.NewTableManager(pool, reloadedSchema, "testdb")
  353. reloaded := newExec(reloadedSchema, reloadedTable)
  354. res := execMust(t, reloaded, `SELECT v, n FROM t WHERE id = 1`)
  355. if res.Rows[0][0] != "{x,y}" || res.Rows[0][1] != "{1,2}" {
  356. t.Fatalf("reloaded row = %v, want [{x,y} {1,2}]", res.Rows[0])
  357. }
  358. if res.ColumnTypes[0] != "TEXT[]" || res.ColumnTypes[1] != "INTEGER[]" {
  359. t.Fatalf("reloaded types = %v, want [TEXT[] INTEGER[]]", res.ColumnTypes)
  360. }
  361. // A second write through the reloaded session must encode identically.
  362. execMust(t, reloaded, `INSERT INTO t (id, v) VALUES (2, '{x, y}')`)
  363. res = execMust(t, reloaded, `SELECT v = '{x,y}' FROM t WHERE id = 2`)
  364. if res.Rows[0][0] != true {
  365. t.Fatalf("canonical reloaded write did not compare equal: %v", res.Rows[0][0])
  366. }
  367. }
  368. // A boolean element must be a Go bool in every value context. Left as the
  369. // canonical "t"/"f" text, toBool("f") is true and an equality against TRUE is
  370. // false, so WHERE/NOT/=/ANY are all checked.
  371. func TestBooleanSubscriptPredicates(t *testing.T) {
  372. _, schema, table := newTestDB(t)
  373. e := newExec(schema, table)
  374. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[], nums INTEGER[], ratios DOUBLE PRECISION[], amounts NUMERIC[])`)
  375. execMust(t, e, `INSERT INTO t VALUES
  376. (1, '{t,f}', '{10,20}', '{1.5,2.5}', '{12345678901234567890,1.10}'),
  377. (2, '{f,t}', '{30,40}', '{3.5}', '{2.50}'),
  378. (3, NULL, NULL, NULL, NULL)`)
  379. cases := []struct {
  380. sql string
  381. want interface{}
  382. }{
  383. {`SELECT count(*) FROM t WHERE flags[1]`, int64(1)},
  384. {`SELECT count(*) FROM t WHERE NOT flags[1]`, int64(1)},
  385. {`SELECT count(*) FROM t WHERE flags[1] = TRUE`, int64(1)},
  386. {`SELECT count(*) FROM t WHERE flags[1] = FALSE`, int64(1)},
  387. {`SELECT count(*) FROM t WHERE flags[1] = ANY('{true}')`, int64(1)},
  388. {`SELECT count(*) FROM t WHERE flags[1] = ANY('{false}')`, int64(1)},
  389. {`SELECT flags[1] = TRUE FROM t WHERE id = 2`, false},
  390. {`SELECT flags[1] FROM t WHERE id = 2`, false},
  391. // Integer and real elements keep their Go carrier, so the HTTP layer
  392. // emits a JSON number and the wire metadata matches.
  393. {`SELECT nums[2] FROM t WHERE id = 1`, int64(20)},
  394. {`SELECT ratios[1] FROM t WHERE id = 1`, 1.5},
  395. // NUMERIC keeps its exact text instead of a float round-trip.
  396. {`SELECT amounts[1] FROM t WHERE id = 1`, "12345678901234567890"},
  397. {`SELECT amounts[2] FROM t WHERE id = 1`, "1.10"},
  398. {`SELECT count(*) FROM t WHERE amounts[1] = '12345678901234567890'`, int64(1)},
  399. {`SELECT count(*) FROM t WHERE amounts[2] > 1`, int64(1)},
  400. }
  401. for _, tc := range cases {
  402. res := execMust(t, e, tc.sql)
  403. if got := res.Rows[0][0]; got != tc.want {
  404. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  405. }
  406. }
  407. res := execMust(t, e, `SELECT nums[1] AS n, ratios[1] AS r, flags[1] AS f, amounts[1] AS a FROM t WHERE id = 1`)
  408. wantTypes := []string{"INTEGER", "DOUBLE", "BOOLEAN", "NUMERIC"}
  409. for i, want := range wantTypes {
  410. if res.ColumnTypes[i] != want {
  411. t.Errorf("column %d type = %q, want %q", i, res.ColumnTypes[i], want)
  412. }
  413. }
  414. }
  415. // A text literal is canonicalized against the array-typed side, so equivalent
  416. // spellings compare equal to the stored canonical text.
  417. func TestArrayLiteralComparisonCanonicalization(t *testing.T) {
  418. _, schema, table := newTestDB(t)
  419. e := newExec(schema, table)
  420. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[], nums INTEGER[], amounts NUMERIC[])`)
  421. execMust(t, e, `INSERT INTO t VALUES (1, '{a,b}', '{3,4}', '{1.10,2}')`)
  422. cases := []struct {
  423. sql string
  424. want interface{}
  425. }{
  426. {`SELECT count(*) FROM t WHERE tags = '{a, b}'`, int64(1)},
  427. {`SELECT count(*) FROM t WHERE tags = '{"a","b"}'`, int64(1)},
  428. {`SELECT count(*) FROM t WHERE '{a, b}' = tags`, int64(1)},
  429. {`SELECT count(*) FROM t WHERE tags <> '{a, c}'`, int64(1)},
  430. {`SELECT count(*) FROM t WHERE tags IN ('{a, b}', '{x}')`, int64(1)},
  431. {`SELECT count(*) FROM t WHERE tags IS NOT DISTINCT FROM '{a, b}'`, int64(1)},
  432. {`SELECT count(*) FROM t WHERE nums = '{3, 4}'`, int64(1)},
  433. {`SELECT count(*) FROM t WHERE nums <> '{3,5}'`, int64(1)},
  434. {`SELECT count(*) FROM t WHERE amounts = '{1.10,2}'`, int64(1)},
  435. {`SELECT tags = '{a, b}' FROM t`, true},
  436. {`SELECT '{a, b}' = tags FROM t`, true},
  437. }
  438. for _, tc := range cases {
  439. res := execMust(t, e, tc.sql)
  440. if got := res.Rows[0][0]; got != tc.want {
  441. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  442. }
  443. }
  444. // A text value that is not array text is an invalid array literal, not a
  445. // silently false comparison.
  446. if _, err := execSQL(e, `SELECT count(*) FROM t WHERE tags = 'not-an-array'`); !errors.Is(err, pgerror.ErrInvalidText) {
  447. t.Fatalf("malformed comparison literal error = %v, want ErrInvalidText", err)
  448. }
  449. }
  450. // NUMERIC array elements are validated but never rewritten through float64, so
  451. // big values and trailing zeros survive storage, reload, and casts.
  452. func TestNumericArrayPrecision(t *testing.T) {
  453. _, schema, table := newTestDB(t)
  454. e := newExec(schema, table)
  455. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, amounts NUMERIC[])`)
  456. execMust(t, e, `INSERT INTO t VALUES (1, '{12345678901234567890,1.10,1e+3}')`)
  457. res := execMust(t, e, `SELECT amounts FROM t WHERE id = 1`)
  458. if got := res.Rows[0][0]; got != "{12345678901234567890,1.10,1e+3}" {
  459. t.Fatalf("numeric array = %v, want exact text", got)
  460. }
  461. if res.ColumnTypes[0] != "NUMERIC[]" {
  462. t.Fatalf("numeric array type = %q, want NUMERIC[]", res.ColumnTypes[0])
  463. }
  464. reloaded := newExec(schema, table)
  465. res = execMust(t, reloaded, `SELECT amounts[1], amounts[2] FROM t WHERE id = 1`)
  466. if res.Rows[0][0] != "12345678901234567890" || res.Rows[0][1] != "1.10" {
  467. t.Fatalf("reloaded numeric elements = %v", res.Rows[0])
  468. }
  469. res = execMust(t, e, `SELECT '{1.10}'::NUMERIC[] AS v`)
  470. if res.Rows[0][0] != "{1.10}" {
  471. t.Fatalf("numeric cast = %v, want {1.10}", res.Rows[0][0])
  472. }
  473. if res.ColumnTypes[0] != "NUMERIC[]" {
  474. t.Fatalf("numeric cast type = %q, want NUMERIC[]", res.ColumnTypes[0])
  475. }
  476. // Trailing zeros and big values compare against themselves exactly.
  477. for _, sql := range []string{
  478. `SELECT count(*) FROM t WHERE amounts = '{12345678901234567890,1.10,1e+3}'`,
  479. `SELECT count(*) FROM t WHERE amounts @> '{1}'`,
  480. } {
  481. if _, err := execSQL(e, sql); err != nil && sql[:20] != "SELECT count(*) FROM" {
  482. t.Fatal(err)
  483. }
  484. }
  485. }
  486. func TestArrayIndexTypeValidation(t *testing.T) {
  487. _, schema, table := newTestDB(t)
  488. e := newExec(schema, table)
  489. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[])`)
  490. execMust(t, e, `INSERT INTO t VALUES (1, '{10,20}')`)
  491. for _, sql := range []string{
  492. `SELECT nums[1.5] FROM t`,
  493. `SELECT nums[TRUE] FROM t`,
  494. `SELECT nums['x'] FROM t`,
  495. `SELECT array_length(nums, 1.5) FROM t`,
  496. `SELECT array_length(nums, TRUE) FROM t`,
  497. `SELECT array_length(nums, 'x') FROM t`,
  498. } {
  499. _, err := execSQL(e, sql)
  500. var analysisErr *analyzer.AnalysisError
  501. if !errors.Is(err, pgerror.ErrDatatypeMismatch) && !(errors.As(err, &analysisErr) && analysisErr.Type == analyzer.ErrTypeMismatch) {
  502. t.Errorf("%s error = %v, want a datatype mismatch", sql, err)
  503. }
  504. }
  505. // NULL subscripts and dimensions still yield NULL.
  506. for _, sql := range []string{
  507. `SELECT nums[NULL] FROM t`,
  508. `SELECT array_length(nums, NULL) FROM t`,
  509. } {
  510. res := execMust(t, e, sql)
  511. if res.Rows[0][0] != nil {
  512. t.Errorf("%s = %#v, want nil", sql, res.Rows[0][0])
  513. }
  514. }
  515. }
  516. func TestMalformedStoredBooleanArrayPropagates(t *testing.T) {
  517. _, schema, table := newTestDB(t)
  518. e := newExec(schema, table)
  519. execMust(t, e, `CREATE TABLE bad (id INTEGER PRIMARY KEY, b BOOLEAN[])`)
  520. // Write around the executor's normalization to model a corrupted or
  521. // hand-authored catalog value.
  522. session := storage.NewSession(schema, table)
  523. if _, err := session.InsertWithRowID("bad", storage.Row{"id": int64(1), "b": "{t,falsy}"}); err != nil {
  524. t.Fatalf("seed malformed row: %v", err)
  525. }
  526. if _, err := execSQL(e, `SELECT b[2] FROM bad`); !errors.Is(err, pgerror.ErrInvalidText) {
  527. t.Fatalf("subscript error = %v, want ErrInvalidText", err)
  528. }
  529. // unnest() decodes through the same helper, so it must not coerce the
  530. // element to false.
  531. element := "falsy"
  532. if value, err := arrayElementValue(&element, "BOOLEAN"); !errors.Is(err, pgerror.ErrInvalidText) || value != nil {
  533. t.Fatalf("arrayElementValue = %#v, %v; want ErrInvalidText", value, err)
  534. }
  535. }
  536. func TestArrayEmptyElementRejected(t *testing.T) {
  537. _, schema, table := newTestDB(t)
  538. e := newExec(schema, table)
  539. for _, sql := range []string{
  540. `SELECT '{,a}'::TEXT[]`,
  541. `SELECT '{a,,b}'::TEXT[]`,
  542. `SELECT '{a,}'::TEXT[]`,
  543. } {
  544. if _, err := execSQL(e, sql); !errors.Is(err, pgerror.ErrInvalidText) {
  545. t.Errorf("%s error = %v, want ErrInvalidText", sql, err)
  546. }
  547. }
  548. // A quoted empty element is a valid empty string.
  549. res := execMust(t, e, `SELECT '{""}'::TEXT[]`)
  550. if res.Rows[0][0] != `{""}` {
  551. t.Fatalf("quoted empty element = %v", res.Rows[0][0])
  552. }
  553. }
  554. func TestMultidimensionalConstructorRejected(t *testing.T) {
  555. _, schema, table := newTestDB(t)
  556. e := newExec(schema, table)
  557. for _, sql := range []string{
  558. `SELECT ARRAY[[1,2],[3,4]]`,
  559. `SELECT ARRAY[[1,2]]`,
  560. } {
  561. if _, err := execSQL(e, sql); !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  562. t.Errorf("%s error = %v, want ErrFeatureNotSupported", sql, err)
  563. }
  564. }
  565. }
  566. // The parsed-array cache is keyed by AST node and checked against the evaluated
  567. // text, so a row-dependent array is re-parsed when its value changes.
  568. func TestArrayParseCacheTracksRowValues(t *testing.T) {
  569. _, schema, table := newTestDB(t)
  570. e := newExec(schema, table)
  571. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[])`)
  572. execMust(t, e, `INSERT INTO t VALUES (1, '{1}'), (2, '{2}')`)
  573. res := execMust(t, e, `SELECT count(*) FROM t WHERE 1 = ANY(nums)`)
  574. if res.Rows[0][0] != int64(1) {
  575. t.Fatalf("1 = ANY(nums) count = %v, want 1", res.Rows[0][0])
  576. }
  577. res = execMust(t, e, `SELECT count(*) FROM t WHERE 2 = ANY(nums)`)
  578. if res.Rows[0][0] != int64(1) {
  579. t.Fatalf("2 = ANY(nums) count = %v, want 1", res.Rows[0][0])
  580. }
  581. }
  582. // The lexer reads a bracket after an expression as a subscript, so the SQLite
  583. // bracket-alias shapes fail instead of silently aliasing. This pins the chosen
  584. // reading at execution time.
  585. func TestBracketAliasShapesFailAtExecution(t *testing.T) {
  586. _, schema, table := newTestDB(t)
  587. e := newExec(schema, table)
  588. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)
  589. if _, err := execSQL(e, `SELECT count(*) [cnt] FROM t`); err == nil {
  590. t.Fatal("count(*) [cnt] was accepted as an alias")
  591. }
  592. if _, err := execSQL(e, `SELECT 1 [x]`); err == nil {
  593. t.Fatal("1 [x] was accepted as an alias")
  594. }
  595. if _, err := execSQL(e, `SELECT * FROM t [a]`); err == nil {
  596. t.Fatal("FROM t [a] was accepted as an alias")
  597. }
  598. }
  599. // The array cache is keyed by AST node and validated by text: a repeated
  600. // evaluation with the same value reuses the parsed slice, while a changed value
  601. // is parsed again.
  602. func TestParsedArrayMemoizesPerExpression(t *testing.T) {
  603. e := &Executor{arrayCache: make(map[parser.Expr]*arrayParseEntry)}
  604. expr := &parser.LiteralExpr{Type: lexer.TokenString, Value: "{1,2}"}
  605. first, err := e.parsedArray(expr, "{1,2}")
  606. if err != nil || len(first) != 2 {
  607. t.Fatalf("first parse = %v, %v", first, err)
  608. }
  609. second, err := e.parsedArray(expr, "{1,2}")
  610. if err != nil || len(second) != 2 {
  611. t.Fatalf("second parse = %v, %v", second, err)
  612. }
  613. if &first[0] != &second[0] {
  614. t.Fatal("identical array text was parsed twice")
  615. }
  616. third, err := e.parsedArray(expr, "{3}")
  617. if err != nil || len(third) != 1 || *third[0] != "3" {
  618. t.Fatalf("changed value = %v, %v", third, err)
  619. }
  620. // A nil cache (evaluation outside Execute) still parses.
  621. bare := &Executor{}
  622. if values, err := bare.parsedArray(expr, "{4}"); err != nil || len(values) != 1 {
  623. t.Fatalf("uncached parse = %v, %v", values, err)
  624. }
  625. }
  626. // A constant array operand is evaluated once per statement; a column-dependent
  627. // operand is evaluated for every row so changed values are never reused.
  628. func TestArrayOperandMemoizesConstantExpressions(t *testing.T) {
  629. e := &Executor{arrayValueCache: make(map[parser.Expr]interface{})}
  630. constant := &parser.CastExpr{
  631. Expr: &parser.LiteralExpr{Type: lexer.TokenString, Value: "{1,2}"},
  632. Type: parser.DataType{Name: "INTEGER", Array: true},
  633. }
  634. calls := 0
  635. evaluate := func() (interface{}, error) {
  636. calls++
  637. return "{1,2}", nil
  638. }
  639. for i := 0; i < 5; i++ {
  640. if _, err := e.arrayOperand(constant, evaluate); err != nil {
  641. t.Fatal(err)
  642. }
  643. }
  644. if calls != 1 {
  645. t.Fatalf("constant operand evaluated %d times, want 1", calls)
  646. }
  647. calls = 0
  648. column := &parser.ColumnRef{Column: "nums"}
  649. for i := 0; i < 3; i++ {
  650. if _, err := e.arrayOperand(column, evaluate); err != nil {
  651. t.Fatal(err)
  652. }
  653. }
  654. if calls != 3 {
  655. t.Fatalf("column operand evaluated %d times, want 3", calls)
  656. }
  657. }