arrays_test.go 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. package executor
  2. import (
  3. "errors"
  4. "strings"
  5. "testing"
  6. "time"
  7. "github.com/danfragoso/pizzasql-next/pkg/analyzer"
  8. "github.com/danfragoso/pizzasql-next/pkg/lexer"
  9. "github.com/danfragoso/pizzasql-next/pkg/parser"
  10. "github.com/danfragoso/pizzasql-next/pkg/pgerror"
  11. "github.com/danfragoso/pizzasql-next/pkg/storage"
  12. )
  13. func TestArrayColumnDDLAndRoundTrip(t *testing.T) {
  14. _, schema, table := newTestDB(t)
  15. e := newExec(schema, table)
  16. execMust(t, e, `CREATE TABLE items (
  17. id INTEGER PRIMARY KEY,
  18. tags TEXT[] NOT NULL,
  19. scores INTEGER[],
  20. flags BOOLEAN[],
  21. ratios DOUBLE PRECISION[]
  22. )`)
  23. execMust(t, e, `INSERT INTO items (id, tags, scores, flags, ratios)
  24. VALUES (1, '{a,b}', '{1,2}', '{t,f}', '{1.5,2.25}')`)
  25. execMust(t, e, `INSERT INTO items (id, tags, scores)
  26. VALUES (2, '{}', NULL)`)
  27. res := execMust(t, e, `SELECT id, tags, scores, flags, ratios FROM items ORDER BY id`)
  28. if got := res.Rows[0][1]; got != "{a,b}" {
  29. t.Fatalf("tags = %v, want {a,b}", got)
  30. }
  31. // Elements are canonicalized per declared type.
  32. if got := res.Rows[0][2]; got != "{1,2}" {
  33. t.Fatalf("scores = %v, want {1,2}", got)
  34. }
  35. if got := res.Rows[0][3]; got != "{t,f}" {
  36. t.Fatalf("flags = %v, want {t,f}", got)
  37. }
  38. if got := res.Rows[0][4]; got != "{1.5,2.25}" {
  39. t.Fatalf("ratios = %v, want {1.5,2.25}", got)
  40. }
  41. // NULL and the empty array stay distinguishable.
  42. if got := res.Rows[1][1]; got != "{}" {
  43. t.Fatalf("empty tags = %v, want {}", got)
  44. }
  45. if got := res.Rows[1][2]; got != nil {
  46. t.Fatalf("NULL scores = %v, want nil", got)
  47. }
  48. // Result metadata reports the declared array type.
  49. if got := res.ColumnTypes[1]; got != "TEXT[]" {
  50. t.Fatalf("tags column type = %q, want TEXT[]", got)
  51. }
  52. // The durable catalog keeps the array spelling, so sqlite_master and
  53. // PRAGMA table_info still render it.
  54. master := execMust(t, e, `SELECT sql FROM sqlite_master WHERE name = 'items'`)
  55. if sql, _ := master.Rows[0][0].(string); !strings.Contains(sql, "TEXT[]") {
  56. t.Fatalf("sqlite_master SQL = %q, want it to contain TEXT[]", sql)
  57. }
  58. info := execMust(t, e, `PRAGMA table_info(items)`)
  59. found := false
  60. for _, row := range info.Rows {
  61. if row[1] == "scores" {
  62. found = true
  63. if row[2] != "INTEGER[]" {
  64. t.Fatalf("PRAGMA table_info type = %v, want INTEGER[]", row[2])
  65. }
  66. }
  67. }
  68. if !found {
  69. t.Fatal("PRAGMA table_info did not report the scores column")
  70. }
  71. // A fresh executor over the same store sees the same bytes: no reload
  72. // conversion and no storage-format change.
  73. reloaded := newExec(schema, table)
  74. res = execMust(t, reloaded, `SELECT tags, scores, flags FROM items WHERE id = 1`)
  75. if res.Rows[0][0] != "{a,b}" || res.Rows[0][1] != "{1,2}" || res.Rows[0][2] != "{t,f}" {
  76. t.Fatalf("reloaded row = %v", res.Rows[0])
  77. }
  78. }
  79. func TestArrayCanonicalEncoding(t *testing.T) {
  80. _, schema, table := newTestDB(t)
  81. e := newExec(schema, table)
  82. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT[])`)
  83. for _, tc := range []struct{ sql, want string }{
  84. {`INSERT INTO t VALUES (1, '{ a , b }') RETURNING v`, "{a,b}"},
  85. {`INSERT INTO t VALUES (2, '{"a b","c,d"}') RETURNING v`, `{"a b","c,d"}`},
  86. {`INSERT INTO t VALUES (3, ARRAY['x', NULL, 'NULL']) RETURNING v`, `{x,NULL,"NULL"}`},
  87. {`INSERT INTO t VALUES (4, ARRAY[]::TEXT[]) RETURNING v`, "{}"},
  88. {`INSERT INTO t VALUES (5, '{"quote\"d", "back\\slash"}') RETURNING v`, `{"quote\"d","back\\slash"}`},
  89. } {
  90. res := execMust(t, e, tc.sql)
  91. if got := res.Rows[0][0]; got != tc.want {
  92. t.Errorf("%s = %v, want %s", tc.sql, got, tc.want)
  93. }
  94. }
  95. }
  96. func TestArrayConstructorAndCast(t *testing.T) {
  97. _, schema, table := newTestDB(t)
  98. e := newExec(schema, table)
  99. cases := []struct {
  100. sql string
  101. want interface{}
  102. }{
  103. {`SELECT ARRAY[1, 2, 3]`, "{1,2,3}"},
  104. {`SELECT ARRAY['a', NULL]`, "{a,NULL}"},
  105. {`SELECT ARRAY[TRUE, FALSE]`, "{t,f}"},
  106. {`SELECT '{1,2,3}'::INTEGER[]`, "{1,2,3}"},
  107. {`SELECT CAST('{1,2}' AS INTEGER[])`, "{1,2}"},
  108. {`SELECT ARRAY['1','2']::INTEGER[]`, "{1,2}"},
  109. {`SELECT '{1.50,2}'::NUMERIC[]`, "{1.50,2}"},
  110. {`SELECT NULL::INTEGER[]`, nil},
  111. }
  112. for _, tc := range cases {
  113. res := execMust(t, e, tc.sql)
  114. if got := res.Rows[0][0]; got != tc.want {
  115. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  116. }
  117. }
  118. res := execMust(t, e, `SELECT ARRAY[1,2] AS a, '{x}'::TEXT[] AS b`)
  119. if res.ColumnTypes[0] != "INTEGER[]" || res.ColumnTypes[1] != "TEXT[]" {
  120. t.Fatalf("constructor types = %v, want [INTEGER[] TEXT[]]", res.ColumnTypes)
  121. }
  122. }
  123. func TestArraySubscriptAndLength(t *testing.T) {
  124. _, schema, table := newTestDB(t)
  125. e := newExec(schema, table)
  126. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[], tags TEXT[], flags BOOLEAN[])`)
  127. execMust(t, e, `INSERT INTO t VALUES (1, '{10,20,30}', '{"x", NULL, "z"}', '{t,f}')`)
  128. execMust(t, e, `INSERT INTO t VALUES (2, '{}', '{}', '{}')`)
  129. cases := []struct {
  130. sql string
  131. want interface{}
  132. }{
  133. {`SELECT nums[1] FROM t WHERE id = 1`, int64(10)},
  134. {`SELECT nums[3] FROM t WHERE id = 1`, int64(30)},
  135. {`SELECT nums[4] FROM t WHERE id = 1`, nil},
  136. {`SELECT nums[0] FROM t WHERE id = 1`, nil},
  137. {`SELECT tags[2] FROM t WHERE id = 1`, nil},
  138. {`SELECT tags[1] FROM t WHERE id = 1`, "x"},
  139. {`SELECT flags[1] FROM t WHERE id = 1`, true},
  140. {`SELECT flags[2] FROM t WHERE id = 1`, false},
  141. {`SELECT nums[1] FROM t WHERE id = 2`, nil},
  142. {`SELECT array_length(nums, 1) FROM t WHERE id = 1`, int64(3)},
  143. {`SELECT array_length(nums, 1) FROM t WHERE id = 2`, nil},
  144. {`SELECT array_length(nums, 2) FROM t WHERE id = 1`, nil},
  145. }
  146. for _, tc := range cases {
  147. res := execMust(t, e, tc.sql)
  148. if got := res.Rows[0][0]; got != tc.want {
  149. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  150. }
  151. }
  152. res := execMust(t, e, `SELECT nums[1] AS n FROM t WHERE id = 1`)
  153. if res.ColumnTypes[0] != "INTEGER" {
  154. t.Fatalf("subscript type = %q, want INTEGER", res.ColumnTypes[0])
  155. }
  156. }
  157. func TestAnyAllThreeValuedLogic(t *testing.T) {
  158. _, schema, table := newTestDB(t)
  159. e := newExec(schema, table)
  160. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, n INTEGER, b BOOLEAN)`)
  161. execMust(t, e, `INSERT INTO t VALUES (1, 2, TRUE)`)
  162. cases := []struct {
  163. sql string
  164. want interface{}
  165. }{
  166. // ANY: true if any comparison is true.
  167. {`SELECT 2 = ANY('{1,2,3}')`, true},
  168. {`SELECT 5 = ANY('{1,2,3}')`, false},
  169. // NULL elements: no true and a NULL comparison yields NULL.
  170. {`SELECT 5 = ANY('{1,NULL,3}')`, nil},
  171. {`SELECT 2 = ANY('{NULL,2}')`, true},
  172. // Empty array: ANY is false, ALL is true, even for a NULL left side.
  173. {`SELECT 5 = ANY('{}')`, false},
  174. {`SELECT NULL = ANY('{}')`, false},
  175. {`SELECT 5 <> ALL('{}')`, true},
  176. {`SELECT NULL <> ALL('{}')`, true},
  177. // ALL: false if any comparison is false, NULL if only NULLs remain.
  178. {`SELECT 2 <> ALL('{1,3,4}')`, true},
  179. {`SELECT 2 <> ALL('{1,2,4}')`, false},
  180. {`SELECT 2 <> ALL('{1,NULL}')`, nil},
  181. {`SELECT 3 > ALL('{1,2}')`, true},
  182. {`SELECT 1 >= ALL('{1,NULL}')`, nil},
  183. // A NULL left operand yields NULL for a non-empty array.
  184. {`SELECT NULL = ANY('{1,2}')`, nil},
  185. {`SELECT NULL <> ALL('{1,2}')`, nil},
  186. // Boolean elements compare as booleans, not as text.
  187. {`SELECT TRUE = ANY('{f,t}')`, true},
  188. {`SELECT FALSE = ANY('{f,t}')`, true},
  189. {`SELECT TRUE <> ALL('{f,t}')`, false},
  190. // Column-driven forms, including a NULL column.
  191. {`SELECT n = ANY('{1,2}') FROM t`, true},
  192. {`SELECT n <> ALL('{1,2}') FROM t`, false},
  193. {`SELECT b = ANY('{t}') FROM t`, true},
  194. {`SELECT id = ANY('{9,8}') FROM t`, false},
  195. }
  196. for _, tc := range cases {
  197. res := execMust(t, e, tc.sql)
  198. if got := res.Rows[0][0]; got != tc.want {
  199. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  200. }
  201. }
  202. if res := execMust(t, e, `SELECT n = ANY('{1,2}') AS hit FROM t`); res.ColumnTypes[0] != "BOOLEAN" {
  203. t.Fatalf("ANY result type = %q, want BOOLEAN", res.ColumnTypes[0])
  204. }
  205. }
  206. func TestAnyAllOverArrayColumn(t *testing.T) {
  207. _, schema, table := newTestDB(t)
  208. e := newExec(schema, table)
  209. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, allowed TEXT[])`)
  210. execMust(t, e, `INSERT INTO t VALUES (1, '{red,green}'), (2, '{blue}'), (3, NULL)`)
  211. for _, tc := range []struct {
  212. sql string
  213. want int
  214. }{
  215. {`SELECT count(*) FROM t WHERE 'red' = ANY(allowed)`, 1},
  216. {`SELECT count(*) FROM t WHERE 'purple' = ANY(allowed)`, 0},
  217. {`SELECT count(*) FROM t WHERE 'purple' <> ALL(allowed)`, 2},
  218. } {
  219. res := execMust(t, e, tc.sql)
  220. if got := res.Rows[0][0]; got != int64(tc.want) {
  221. t.Errorf("%s = %v, want %d", tc.sql, got, tc.want)
  222. }
  223. }
  224. }
  225. func TestUnnestAsTableFunction(t *testing.T) {
  226. _, schema, table := newTestDB(t)
  227. e := newExec(schema, table)
  228. res := execMust(t, e, `SELECT * FROM unnest('{10,20,30}'::INTEGER[])`)
  229. if res.RowCount != 3 {
  230. t.Fatalf("unnest rows = %d, want 3", res.RowCount)
  231. }
  232. if res.Columns[0] != "unnest" {
  233. t.Fatalf("unnest column = %q, want unnest", res.Columns[0])
  234. }
  235. if res.Rows[0][0] != int64(10) || res.Rows[2][0] != int64(30) {
  236. t.Fatalf("unnest values = %v", res.Rows)
  237. }
  238. if res.ColumnTypes[0] != "INTEGER" {
  239. t.Fatalf("unnest column type = %q, want INTEGER", res.ColumnTypes[0])
  240. }
  241. res = execMust(t, e, `SELECT u.unnest FROM unnest(ARRAY['a', 'b']) AS u WHERE u.unnest <> 'a'`)
  242. if res.RowCount != 1 || res.Rows[0][0] != "b" {
  243. t.Fatalf("unnest alias rows = %v", res.Rows)
  244. }
  245. res = execMust(t, e, `SELECT * FROM unnest(NULL::TEXT[])`)
  246. if res.RowCount != 0 {
  247. t.Fatalf("unnest(NULL) rows = %d, want 0", res.RowCount)
  248. }
  249. if _, err := execSQL(e, `SELECT unnest('{1,2}'::INTEGER[])`); err == nil {
  250. t.Fatal("scalar unnest should be rejected")
  251. }
  252. }
  253. func TestArrayDefaultsAndAlterTable(t *testing.T) {
  254. _, schema, table := newTestDB(t)
  255. e := newExec(schema, table)
  256. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[] DEFAULT ARRAY['a'])`)
  257. execMust(t, e, `INSERT INTO t (id) VALUES (1)`)
  258. res := execMust(t, e, `SELECT tags FROM t WHERE id = 1`)
  259. if res.Rows[0][0] != "{a}" {
  260. t.Fatalf("default tags = %v, want {a}", res.Rows[0][0])
  261. }
  262. execMust(t, e, `ALTER TABLE t ADD COLUMN nums INTEGER[] DEFAULT '{1, 2}'`)
  263. res = execMust(t, e, `SELECT nums FROM t WHERE id = 1`)
  264. if res.Rows[0][0] != "{1,2}" {
  265. t.Fatalf("added column default = %v, want {1,2}", res.Rows[0][0])
  266. }
  267. execMust(t, e, `INSERT INTO t (id, nums) VALUES (2, '{3}')`)
  268. res = execMust(t, e, `SELECT nums FROM t WHERE id = 2`)
  269. if res.Rows[0][0] != "{3}" {
  270. t.Fatalf("explicit array = %v, want {3}", res.Rows[0][0])
  271. }
  272. }
  273. func TestArrayInCompositePrimaryKey(t *testing.T) {
  274. _, schema, table := newTestDB(t)
  275. e := newExec(schema, table)
  276. execMust(t, e, `CREATE TABLE t (id INTEGER, tags TEXT[], PRIMARY KEY (id, tags))`)
  277. execMust(t, e, `INSERT INTO t VALUES (1, '{a,b}')`)
  278. execMust(t, e, `INSERT INTO t VALUES (1, '{c}')`)
  279. res := execMust(t, e, `SELECT count(*) FROM t`)
  280. if res.Rows[0][0] != int64(2) {
  281. t.Fatalf("rows = %v, want 2", res.Rows[0][0])
  282. }
  283. if _, err := execSQL(e, `INSERT INTO t VALUES (1, '{a, b}')`); err == nil {
  284. t.Fatal("duplicate composite key with an equivalent array should conflict")
  285. }
  286. reloaded := newExec(schema, table)
  287. res = execMust(t, reloaded, `SELECT tags FROM t WHERE id = 1 AND tags = '{a,b}'`)
  288. if res.RowCount != 1 || res.Rows[0][0] != "{a,b}" {
  289. t.Fatalf("reloaded composite-key lookup = %v", res.Rows)
  290. }
  291. }
  292. func TestArrayRejectedShapes(t *testing.T) {
  293. _, schema, table := newTestDB(t)
  294. e := newExec(schema, table)
  295. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT[])`)
  296. cases := []struct {
  297. sql string
  298. unsupported bool
  299. }{
  300. {`INSERT INTO t VALUES (1, '{{1,2},{3,4}}')`, true},
  301. {`SELECT '{{1,2},{3,4}}'::INTEGER[]`, true},
  302. {`SELECT 1 = ANY('not an array')`, false},
  303. {`SELECT 1 = ANY(1)`, false},
  304. {`INSERT INTO t VALUES (2, '{1,2')`, false},
  305. {`INSERT INTO t VALUES (3, 5)`, false},
  306. }
  307. for _, tc := range cases {
  308. _, err := execSQL(e, tc.sql)
  309. if err == nil {
  310. t.Errorf("%s succeeded, want an error", tc.sql)
  311. continue
  312. }
  313. if tc.unsupported && !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  314. t.Errorf("%s error = %v, want ErrFeatureNotSupported", tc.sql, err)
  315. }
  316. if !tc.unsupported && !errors.Is(err, pgerror.ErrInvalidText) {
  317. t.Errorf("%s error = %v, want ErrInvalidText", tc.sql, err)
  318. }
  319. }
  320. }
  321. func TestArrayUpdateAndReturning(t *testing.T) {
  322. _, schema, table := newTestDB(t)
  323. e := newExec(schema, table)
  324. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[])`)
  325. execMust(t, e, `INSERT INTO t VALUES (1, '{a}')`)
  326. res := execMust(t, e, `UPDATE t SET tags = '{b, c}' WHERE id = 1 RETURNING tags`)
  327. if res.Rows[0][0] != "{b,c}" {
  328. t.Fatalf("UPDATE RETURNING tags = %v, want {b,c}", res.Rows[0][0])
  329. }
  330. res = execMust(t, e, `SELECT tags FROM t WHERE id = 1`)
  331. if res.Rows[0][0] != "{b,c}" {
  332. t.Fatalf("stored tags = %v, want {b,c}", res.Rows[0][0])
  333. }
  334. }
  335. // The array value is a plain string in the row codec, so a second decoder sees
  336. // exactly the bytes the first one wrote. A freshly constructed schema and table
  337. // manager pair over the same store stands in for a process restart: no
  338. // converter runs, and the durable catalog plus row bytes are read as written.
  339. func TestArrayStorageReloadIsStable(t *testing.T) {
  340. pool, schema, table := newTestDB(t)
  341. e := newExec(schema, table)
  342. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT[], n INTEGER[])`)
  343. execMust(t, e, `INSERT INTO t VALUES (1, ARRAY['x','y'], '{1,2}')`)
  344. session := storage.NewSession(schema, table)
  345. row, err := session.GetByPK("t", "1")
  346. if err != nil {
  347. t.Fatalf("GetByPK: %v", err)
  348. }
  349. if row["v"] != "{x,y}" {
  350. t.Fatalf("stored value = %#v, want {x,y}", row["v"])
  351. }
  352. reloadedSchema := storage.NewSchemaManager(pool, "testdb")
  353. reloadedTable := storage.NewTableManager(pool, reloadedSchema, "testdb")
  354. reloaded := newExec(reloadedSchema, reloadedTable)
  355. res := execMust(t, reloaded, `SELECT v, n FROM t WHERE id = 1`)
  356. if res.Rows[0][0] != "{x,y}" || res.Rows[0][1] != "{1,2}" {
  357. t.Fatalf("reloaded row = %v, want [{x,y} {1,2}]", res.Rows[0])
  358. }
  359. if res.ColumnTypes[0] != "TEXT[]" || res.ColumnTypes[1] != "INTEGER[]" {
  360. t.Fatalf("reloaded types = %v, want [TEXT[] INTEGER[]]", res.ColumnTypes)
  361. }
  362. // A second write through the reloaded session must encode identically.
  363. execMust(t, reloaded, `INSERT INTO t (id, v) VALUES (2, '{x, y}')`)
  364. res = execMust(t, reloaded, `SELECT v = '{x,y}' FROM t WHERE id = 2`)
  365. if res.Rows[0][0] != true {
  366. t.Fatalf("canonical reloaded write did not compare equal: %v", res.Rows[0][0])
  367. }
  368. }
  369. // A boolean element must be a Go bool in every value context. Left as the
  370. // canonical "t"/"f" text, toBool("f") is true and an equality against TRUE is
  371. // false, so WHERE/NOT/=/ANY are all checked.
  372. func TestBooleanSubscriptPredicates(t *testing.T) {
  373. _, schema, table := newTestDB(t)
  374. e := newExec(schema, table)
  375. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[], nums INTEGER[], ratios DOUBLE PRECISION[], amounts NUMERIC[])`)
  376. execMust(t, e, `INSERT INTO t VALUES
  377. (1, '{t,f}', '{10,20}', '{1.5,2.5}', '{12345678901234567890,1.10}'),
  378. (2, '{f,t}', '{30,40}', '{3.5}', '{2.50}'),
  379. (3, NULL, NULL, NULL, NULL)`)
  380. cases := []struct {
  381. sql string
  382. want interface{}
  383. }{
  384. {`SELECT count(*) FROM t WHERE flags[1]`, int64(1)},
  385. {`SELECT count(*) FROM t WHERE NOT flags[1]`, int64(1)},
  386. {`SELECT count(*) FROM t WHERE flags[1] = TRUE`, int64(1)},
  387. {`SELECT count(*) FROM t WHERE flags[1] = FALSE`, int64(1)},
  388. {`SELECT count(*) FROM t WHERE flags[1] = ANY('{true}')`, int64(1)},
  389. {`SELECT count(*) FROM t WHERE flags[1] = ANY('{false}')`, int64(1)},
  390. {`SELECT flags[1] = TRUE FROM t WHERE id = 2`, false},
  391. {`SELECT flags[1] FROM t WHERE id = 2`, false},
  392. // Integer and real elements keep their Go carrier, so the HTTP layer
  393. // emits a JSON number and the wire metadata matches.
  394. {`SELECT nums[2] FROM t WHERE id = 1`, int64(20)},
  395. {`SELECT ratios[1] FROM t WHERE id = 1`, 1.5},
  396. // NUMERIC keeps its exact text instead of a float round-trip.
  397. {`SELECT amounts[1] FROM t WHERE id = 1`, "12345678901234567890"},
  398. {`SELECT amounts[2] FROM t WHERE id = 1`, "1.10"},
  399. {`SELECT count(*) FROM t WHERE amounts[1] = '12345678901234567890'`, int64(1)},
  400. {`SELECT count(*) FROM t WHERE amounts[2] > 1`, int64(1)},
  401. }
  402. for _, tc := range cases {
  403. res := execMust(t, e, tc.sql)
  404. if got := res.Rows[0][0]; got != tc.want {
  405. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  406. }
  407. }
  408. 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`)
  409. wantTypes := []string{"INTEGER", "DOUBLE", "BOOLEAN", "NUMERIC"}
  410. for i, want := range wantTypes {
  411. if res.ColumnTypes[i] != want {
  412. t.Errorf("column %d type = %q, want %q", i, res.ColumnTypes[i], want)
  413. }
  414. }
  415. }
  416. // A text literal is canonicalized against the array-typed side, so equivalent
  417. // spellings compare equal to the stored canonical text.
  418. func TestArrayLiteralComparisonCanonicalization(t *testing.T) {
  419. _, schema, table := newTestDB(t)
  420. e := newExec(schema, table)
  421. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[], nums INTEGER[], amounts NUMERIC[])`)
  422. execMust(t, e, `INSERT INTO t VALUES (1, '{a,b}', '{3,4}', '{1.10,2}')`)
  423. cases := []struct {
  424. sql string
  425. want interface{}
  426. }{
  427. {`SELECT count(*) FROM t WHERE tags = '{a, b}'`, int64(1)},
  428. {`SELECT count(*) FROM t WHERE tags = '{"a","b"}'`, int64(1)},
  429. {`SELECT count(*) FROM t WHERE '{a, b}' = tags`, int64(1)},
  430. {`SELECT count(*) FROM t WHERE tags <> '{a, c}'`, int64(1)},
  431. {`SELECT count(*) FROM t WHERE tags IN ('{a, b}', '{x}')`, int64(1)},
  432. {`SELECT count(*) FROM t WHERE tags IS NOT DISTINCT FROM '{a, b}'`, int64(1)},
  433. {`SELECT count(*) FROM t WHERE nums = '{3, 4}'`, int64(1)},
  434. {`SELECT count(*) FROM t WHERE nums <> '{3,5}'`, int64(1)},
  435. {`SELECT count(*) FROM t WHERE amounts = '{1.10,2}'`, int64(1)},
  436. {`SELECT tags = '{a, b}' FROM t`, true},
  437. {`SELECT '{a, b}' = tags FROM t`, true},
  438. }
  439. for _, tc := range cases {
  440. res := execMust(t, e, tc.sql)
  441. if got := res.Rows[0][0]; got != tc.want {
  442. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  443. }
  444. }
  445. // A text value that is not array text is an invalid array literal, not a
  446. // silently false comparison.
  447. if _, err := execSQL(e, `SELECT count(*) FROM t WHERE tags = 'not-an-array'`); !errors.Is(err, pgerror.ErrInvalidText) {
  448. t.Fatalf("malformed comparison literal error = %v, want ErrInvalidText", err)
  449. }
  450. }
  451. // NUMERIC array elements are validated but never rewritten through float64, so
  452. // big values and trailing zeros survive storage, reload, and casts.
  453. func TestNumericArrayPrecision(t *testing.T) {
  454. _, schema, table := newTestDB(t)
  455. e := newExec(schema, table)
  456. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, amounts NUMERIC[])`)
  457. execMust(t, e, `INSERT INTO t VALUES (1, '{12345678901234567890,1.10,1e+3}')`)
  458. res := execMust(t, e, `SELECT amounts FROM t WHERE id = 1`)
  459. if got := res.Rows[0][0]; got != "{12345678901234567890,1.10,1e+3}" {
  460. t.Fatalf("numeric array = %v, want exact text", got)
  461. }
  462. if res.ColumnTypes[0] != "NUMERIC[]" {
  463. t.Fatalf("numeric array type = %q, want NUMERIC[]", res.ColumnTypes[0])
  464. }
  465. reloaded := newExec(schema, table)
  466. res = execMust(t, reloaded, `SELECT amounts[1], amounts[2] FROM t WHERE id = 1`)
  467. if res.Rows[0][0] != "12345678901234567890" || res.Rows[0][1] != "1.10" {
  468. t.Fatalf("reloaded numeric elements = %v", res.Rows[0])
  469. }
  470. res = execMust(t, e, `SELECT '{1.10}'::NUMERIC[] AS v`)
  471. if res.Rows[0][0] != "{1.10}" {
  472. t.Fatalf("numeric cast = %v, want {1.10}", res.Rows[0][0])
  473. }
  474. if res.ColumnTypes[0] != "NUMERIC[]" {
  475. t.Fatalf("numeric cast type = %q, want NUMERIC[]", res.ColumnTypes[0])
  476. }
  477. // Whole-array equality is numeric-aware for NUMERIC[]: spelling differences
  478. // (1.10 vs 1.1, 1e+3 vs 1000) compare equal, while a changed digit does not.
  479. cases := []struct {
  480. sql string
  481. want interface{}
  482. }{
  483. {`SELECT count(*) FROM t WHERE amounts = '{12345678901234567890,1.10,1e+3}'`, int64(1)},
  484. {`SELECT count(*) FROM t WHERE amounts = '{12345678901234567890,1.1,1000}'`, int64(1)},
  485. {`SELECT count(*) FROM t WHERE amounts = '{12345678901234567891,1.10,1e+3}'`, int64(0)},
  486. {`SELECT count(*) FROM t WHERE amounts <> '{12345678901234567890,1.1,1000}'`, int64(0)},
  487. {`SELECT amounts = '{12345678901234567890,1.1,1000}' FROM t`, true},
  488. {`SELECT amounts IS DISTINCT FROM '{12345678901234567890,1.1,1000}' FROM t`, false},
  489. {`SELECT '{1.10}'::NUMERIC[] = '{1.1}'::NUMERIC[]`, true},
  490. {`SELECT '{1.10}'::NUMERIC[] <> '{1.1}'::NUMERIC[]`, false},
  491. {`SELECT '{1.10,2.50}'::NUMERIC[] = '{1.1,2.5}'::NUMERIC[]`, true},
  492. // Element comparison is exact too, so a float64 rounding cannot make two
  493. // adjacent 20-digit values equal.
  494. {`SELECT count(*) FROM t WHERE amounts[1] = '12345678901234567890'`, int64(1)},
  495. {`SELECT count(*) FROM t WHERE amounts[1] = '12345678901234567891'`, int64(0)},
  496. {`SELECT count(*) FROM t WHERE amounts[2] = '1.1'`, int64(1)},
  497. {`SELECT count(*) FROM t WHERE amounts[2] = '1.10'`, int64(1)},
  498. {`SELECT count(*) FROM t WHERE amounts[2] = ANY('{1.1}')`, int64(1)},
  499. {`SELECT count(*) FROM t WHERE amounts[2] = ANY('{1.11}')`, int64(0)},
  500. {`SELECT count(*) FROM t WHERE amounts IN ('{12345678901234567890,1.1,1000}')`, int64(1)},
  501. }
  502. for _, tc := range cases {
  503. res := execMust(t, e, tc.sql)
  504. if got := res.Rows[0][0]; got != tc.want {
  505. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  506. }
  507. }
  508. }
  509. // NUMERIC element text is trimmed and a leading + is normalized; non-decimal
  510. // spellings ParseFloat would accept (hex, NaN, Inf) are rejected.
  511. func TestNumericArrayValidation(t *testing.T) {
  512. _, schema, table := newTestDB(t)
  513. e := newExec(schema, table)
  514. for _, tc := range []struct{ sql, want string }{
  515. {`SELECT '{" 1.10 "}'::NUMERIC[]`, "{1.10}"},
  516. {`SELECT '{+1.10}'::NUMERIC[]`, "{1.10}"},
  517. {`SELECT '{-1.10,+.5}'::NUMERIC[]`, "{-1.10,.5}"},
  518. {`SELECT '{1e+3,2E-2}'::NUMERIC[]`, "{1e+3,2E-2}"},
  519. } {
  520. res := execMust(t, e, tc.sql)
  521. if got := res.Rows[0][0]; got != tc.want {
  522. t.Errorf("%s = %v, want %s", tc.sql, got, tc.want)
  523. }
  524. }
  525. for _, sql := range []string{
  526. `SELECT '{0x1p4}'::NUMERIC[]`,
  527. `SELECT '{NaN}'::NUMERIC[]`,
  528. `SELECT '{Inf}'::NUMERIC[]`,
  529. `SELECT '{Infinity}'::NUMERIC[]`,
  530. `SELECT '{1.2.3}'::NUMERIC[]`,
  531. `SELECT '{1e}'::NUMERIC[]`,
  532. `SELECT '{+}'::NUMERIC[]`,
  533. } {
  534. if _, err := execSQL(e, sql); !errors.Is(err, pgerror.ErrInvalidText) {
  535. t.Errorf("%s error = %v, want ErrInvalidText", sql, err)
  536. }
  537. }
  538. }
  539. func TestArrayIndexTypeValidation(t *testing.T) {
  540. _, schema, table := newTestDB(t)
  541. e := newExec(schema, table)
  542. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[])`)
  543. execMust(t, e, `INSERT INTO t VALUES (1, '{10,20}')`)
  544. for _, sql := range []string{
  545. `SELECT nums[1.5] FROM t`,
  546. `SELECT nums[TRUE] FROM t`,
  547. `SELECT nums['x'] FROM t`,
  548. `SELECT array_length(nums, 1.5) FROM t`,
  549. `SELECT array_length(nums, TRUE) FROM t`,
  550. `SELECT array_length(nums, 'x') FROM t`,
  551. } {
  552. _, err := execSQL(e, sql)
  553. var analysisErr *analyzer.AnalysisError
  554. if !errors.Is(err, pgerror.ErrDatatypeMismatch) && !(errors.As(err, &analysisErr) && analysisErr.Type == analyzer.ErrTypeMismatch) {
  555. t.Errorf("%s error = %v, want a datatype mismatch", sql, err)
  556. }
  557. }
  558. // NULL subscripts and dimensions still yield NULL.
  559. for _, sql := range []string{
  560. `SELECT nums[NULL] FROM t`,
  561. `SELECT array_length(nums, NULL) FROM t`,
  562. } {
  563. res := execMust(t, e, sql)
  564. if res.Rows[0][0] != nil {
  565. t.Errorf("%s = %#v, want nil", sql, res.Rows[0][0])
  566. }
  567. }
  568. }
  569. func TestMalformedStoredBooleanArrayPropagates(t *testing.T) {
  570. _, schema, table := newTestDB(t)
  571. e := newExec(schema, table)
  572. execMust(t, e, `CREATE TABLE bad (id INTEGER PRIMARY KEY, b BOOLEAN[])`)
  573. // Write around the executor's normalization to model a corrupted or
  574. // hand-authored catalog value.
  575. session := storage.NewSession(schema, table)
  576. if _, err := session.InsertWithRowID("bad", storage.Row{"id": int64(1), "b": "{t,falsy}"}); err != nil {
  577. t.Fatalf("seed malformed row: %v", err)
  578. }
  579. if _, err := execSQL(e, `SELECT b[2] FROM bad`); !errors.Is(err, pgerror.ErrInvalidText) {
  580. t.Fatalf("subscript error = %v, want ErrInvalidText", err)
  581. }
  582. // unnest() decodes through the same helper, so it must not coerce the
  583. // element to false.
  584. element := "falsy"
  585. if value, err := arrayElementValue(&element, "BOOLEAN"); !errors.Is(err, pgerror.ErrInvalidText) || value != nil {
  586. t.Fatalf("arrayElementValue = %#v, %v; want ErrInvalidText", value, err)
  587. }
  588. }
  589. func TestArrayEmptyElementRejected(t *testing.T) {
  590. _, schema, table := newTestDB(t)
  591. e := newExec(schema, table)
  592. for _, sql := range []string{
  593. `SELECT '{,a}'::TEXT[]`,
  594. `SELECT '{a,,b}'::TEXT[]`,
  595. `SELECT '{a,}'::TEXT[]`,
  596. } {
  597. if _, err := execSQL(e, sql); !errors.Is(err, pgerror.ErrInvalidText) {
  598. t.Errorf("%s error = %v, want ErrInvalidText", sql, err)
  599. }
  600. }
  601. // A quoted empty element is a valid empty string.
  602. res := execMust(t, e, `SELECT '{""}'::TEXT[]`)
  603. if res.Rows[0][0] != `{""}` {
  604. t.Fatalf("quoted empty element = %v", res.Rows[0][0])
  605. }
  606. }
  607. func TestMultidimensionalConstructorRejected(t *testing.T) {
  608. _, schema, table := newTestDB(t)
  609. e := newExec(schema, table)
  610. for _, sql := range []string{
  611. `SELECT ARRAY[[1,2],[3,4]]`,
  612. `SELECT ARRAY[[1,2]]`,
  613. } {
  614. if _, err := execSQL(e, sql); !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  615. t.Errorf("%s error = %v, want ErrFeatureNotSupported", sql, err)
  616. }
  617. }
  618. }
  619. // The parsed-array cache is keyed by AST node and checked against the evaluated
  620. // text, so a row-dependent array is re-parsed when its value changes.
  621. func TestArrayParseCacheTracksRowValues(t *testing.T) {
  622. _, schema, table := newTestDB(t)
  623. e := newExec(schema, table)
  624. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, nums INTEGER[])`)
  625. execMust(t, e, `INSERT INTO t VALUES (1, '{1}'), (2, '{2}')`)
  626. res := execMust(t, e, `SELECT count(*) FROM t WHERE 1 = ANY(nums)`)
  627. if res.Rows[0][0] != int64(1) {
  628. t.Fatalf("1 = ANY(nums) count = %v, want 1", res.Rows[0][0])
  629. }
  630. res = execMust(t, e, `SELECT count(*) FROM t WHERE 2 = ANY(nums)`)
  631. if res.Rows[0][0] != int64(1) {
  632. t.Fatalf("2 = ANY(nums) count = %v, want 1", res.Rows[0][0])
  633. }
  634. }
  635. // The lexer reads a bracket after an expression as a subscript, so the SQLite
  636. // bracket-alias shapes fail instead of silently aliasing. This pins the chosen
  637. // reading at execution time.
  638. func TestBracketAliasShapesFailAtExecution(t *testing.T) {
  639. _, schema, table := newTestDB(t)
  640. e := newExec(schema, table)
  641. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY)`)
  642. if _, err := execSQL(e, `SELECT count(*) [cnt] FROM t`); err == nil {
  643. t.Fatal("count(*) [cnt] was accepted as an alias")
  644. }
  645. if _, err := execSQL(e, `SELECT 1 [x]`); err == nil {
  646. t.Fatal("1 [x] was accepted as an alias")
  647. }
  648. if _, err := execSQL(e, `SELECT * FROM t [a]`); err == nil {
  649. t.Fatal("FROM t [a] was accepted as an alias")
  650. }
  651. }
  652. // The array cache is keyed by AST node and validated by text: a repeated
  653. // evaluation with the same value reuses the parsed slice, while a changed value
  654. // is parsed again.
  655. func TestParsedArrayMemoizesPerExpression(t *testing.T) {
  656. e := &Executor{arrayCache: make(map[parser.Expr]*arrayParseEntry)}
  657. expr := &parser.LiteralExpr{Type: lexer.TokenString, Value: "{1,2}"}
  658. first, err := e.parsedArray(expr, "{1,2}")
  659. if err != nil || len(first) != 2 {
  660. t.Fatalf("first parse = %v, %v", first, err)
  661. }
  662. second, err := e.parsedArray(expr, "{1,2}")
  663. if err != nil || len(second) != 2 {
  664. t.Fatalf("second parse = %v, %v", second, err)
  665. }
  666. if &first[0] != &second[0] {
  667. t.Fatal("identical array text was parsed twice")
  668. }
  669. third, err := e.parsedArray(expr, "{3}")
  670. if err != nil || len(third) != 1 || *third[0] != "3" {
  671. t.Fatalf("changed value = %v, %v", third, err)
  672. }
  673. // A nil cache (evaluation outside Execute) still parses.
  674. bare := &Executor{}
  675. if values, err := bare.parsedArray(expr, "{4}"); err != nil || len(values) != 1 {
  676. t.Fatalf("uncached parse = %v, %v", values, err)
  677. }
  678. }
  679. // A constant array operand is evaluated once per statement; a column-dependent
  680. // operand is evaluated for every row so changed values are never reused.
  681. func TestArrayOperandMemoizesConstantExpressions(t *testing.T) {
  682. e := &Executor{arrayValueCache: make(map[parser.Expr]interface{})}
  683. constant := &parser.CastExpr{
  684. Expr: &parser.LiteralExpr{Type: lexer.TokenString, Value: "{1,2}"},
  685. Type: parser.DataType{Name: "INTEGER", Array: true},
  686. }
  687. calls := 0
  688. evaluate := func() (interface{}, error) {
  689. calls++
  690. return "{1,2}", nil
  691. }
  692. for i := 0; i < 5; i++ {
  693. if _, err := e.arrayOperand(constant, evaluate); err != nil {
  694. t.Fatal(err)
  695. }
  696. }
  697. if calls != 1 {
  698. t.Fatalf("constant operand evaluated %d times, want 1", calls)
  699. }
  700. calls = 0
  701. column := &parser.ColumnRef{Column: "nums"}
  702. for i := 0; i < 3; i++ {
  703. if _, err := e.arrayOperand(column, evaluate); err != nil {
  704. t.Fatal(err)
  705. }
  706. }
  707. if calls != 3 {
  708. t.Fatalf("column operand evaluated %d times, want 3", calls)
  709. }
  710. }
  711. // Column typing survives a derived table: the outer statement must decode
  712. // boolean subscripts instead of treating "t"/"f" as text.
  713. func TestDerivedTableArrayTyping(t *testing.T) {
  714. _, schema, table := newTestDB(t)
  715. e := newExec(schema, table)
  716. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[], tags TEXT[])`)
  717. execMust(t, e, `INSERT INTO t VALUES (1, '{t,f}', '{a,b}'), (2, '{f,t}', '{c}')`)
  718. cases := []struct {
  719. sql string
  720. want interface{}
  721. }{
  722. {`SELECT count(*) FROM (SELECT flags FROM t) x WHERE flags[1]`, int64(1)},
  723. {`SELECT count(*) FROM (SELECT flags FROM t) x WHERE NOT flags[1]`, int64(1)},
  724. {`SELECT count(*) FROM (SELECT flags FROM t) x WHERE flags[1] = TRUE`, int64(1)},
  725. {`SELECT count(*) FROM (SELECT flags FROM t) x WHERE flags[1] = ANY('{true}')`, int64(1)},
  726. {`SELECT count(*) FROM (SELECT tags FROM t) x WHERE tags = '{a, b}'`, int64(1)},
  727. {`SELECT count(*) FROM (SELECT tags FROM t) x WHERE tags = '{"a","b"}'`, int64(1)},
  728. }
  729. for _, tc := range cases {
  730. res := execMust(t, e, tc.sql)
  731. if got := res.Rows[0][0]; got != tc.want {
  732. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  733. }
  734. }
  735. }
  736. // Column typing survives a CTE, including NOT over a boolean element.
  737. func TestCTEArrayTyping(t *testing.T) {
  738. _, schema, table := newTestDB(t)
  739. e := newExec(schema, table)
  740. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[], nums INTEGER[])`)
  741. execMust(t, e, `INSERT INTO t VALUES (1, '{t,f}', '{10,20}'), (2, '{f,t}', '{30,40}')`)
  742. cases := []struct {
  743. sql string
  744. want interface{}
  745. }{
  746. {`WITH c AS (SELECT flags FROM t) SELECT count(*) FROM c WHERE flags[1]`, int64(1)},
  747. {`WITH c AS (SELECT flags FROM t) SELECT count(*) FROM c WHERE NOT flags[1]`, int64(1)},
  748. {`WITH c AS (SELECT flags FROM t) SELECT count(*) FROM c WHERE flags[1] = ANY('{true}')`, int64(1)},
  749. {`WITH c AS (SELECT nums FROM t) SELECT count(*) FROM c WHERE nums[1] = 10`, int64(1)},
  750. {`WITH c AS (SELECT nums FROM t) SELECT count(*) FROM c WHERE nums = '{10,20}'`, int64(1)},
  751. {`WITH c AS (SELECT nums FROM t) SELECT count(*) FROM c WHERE nums = '{10, 20}'`, int64(1)},
  752. }
  753. for _, tc := range cases {
  754. res := execMust(t, e, tc.sql)
  755. if got := res.Rows[0][0]; got != tc.want {
  756. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  757. }
  758. }
  759. }
  760. // A materialized CTE shadows a real table with the same name. Its own column
  761. // types must win; reading the shadowed table's schema would decode INTEGER[]
  762. // elements as text or report a spurious invalid-array error.
  763. func TestCTEShadowingRealTableUsesCTETypes(t *testing.T) {
  764. _, schema, table := newTestDB(t)
  765. e := newExec(schema, table)
  766. execMust(t, e, `CREATE TABLE shadow (tags INTEGER[], flags TEXT[])`)
  767. execMust(t, e, `INSERT INTO shadow VALUES ('{1}', '{"not a bool"}')`)
  768. execMust(t, e, `CREATE TABLE docs (id INTEGER PRIMARY KEY, tags TEXT[], flags BOOLEAN[])`)
  769. execMust(t, e, `INSERT INTO docs VALUES (1, '{a,b}', '{f}')`)
  770. // Boolean NOT only flips when the CTE's BOOLEAN[] type is used.
  771. res := execMust(t, e, `WITH shadow AS (SELECT flags FROM docs) SELECT count(*) FROM shadow WHERE NOT flags[1]`)
  772. if res.Rows[0][0] != int64(1) {
  773. t.Fatalf("CTE boolean shadow count = %v, want 1", res.Rows[0][0])
  774. }
  775. // TEXT[] element access accepts 'a'; the shadowed INTEGER[] type would fail.
  776. res = execMust(t, e, `WITH shadow AS (SELECT tags FROM docs) SELECT count(*) FROM shadow WHERE tags[1] = 'a'`)
  777. if res.Rows[0][0] != int64(1) {
  778. t.Fatalf("CTE text shadow count = %v, want 1", res.Rows[0][0])
  779. }
  780. // Comparison canonicalization also uses the CTE's type.
  781. res = execMust(t, e, `WITH shadow AS (SELECT tags FROM docs) SELECT count(*) FROM shadow WHERE tags = '{a, b}'`)
  782. if res.Rows[0][0] != int64(1) {
  783. t.Fatalf("CTE comparison shadow count = %v, want 1", res.Rows[0][0])
  784. }
  785. }
  786. // The deadlock fix must hold for a locked in-transaction scan that evaluates an
  787. // array subscript: a SELECT over the table followed by an UPDATE must not
  788. // re-enter the session lock.
  789. func TestArraySubscriptInTransactionScan(t *testing.T) {
  790. _, schema, table := newTestDB(t)
  791. e := newExec(schema, table)
  792. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[], n INTEGER)`)
  793. execMust(t, e, `INSERT INTO t VALUES (1, '{t,f}', 0), (2, '{f,t}', 0)`)
  794. execMust(t, e, `BEGIN`)
  795. runWithTimeout(t, 5*time.Second, func() {
  796. res := execMust(t, e, `SELECT count(*) FROM t WHERE flags[1]`)
  797. if res.Rows[0][0] != int64(1) {
  798. t.Errorf("in-transaction count = %v, want 1", res.Rows[0][0])
  799. }
  800. })
  801. runWithTimeout(t, 5*time.Second, func() {
  802. execMust(t, e, `UPDATE t SET n = n + 1 WHERE flags[1]`)
  803. })
  804. execMust(t, e, `COMMIT`)
  805. res := execMust(t, e, `SELECT n FROM t ORDER BY id`)
  806. if res.Rows[0][0] != int64(1) || res.Rows[1][0] != int64(0) {
  807. t.Fatalf("post-commit values = %v", res.Rows)
  808. }
  809. }
  810. // canonicalArrayValue memoizes a constant comparison literal per statement and
  811. // still re-canonicalizes changed text or a different target type.
  812. func TestCanonicalArrayValueMemoizes(t *testing.T) {
  813. e := &Executor{arrayCanonicalCache: make(map[parser.Expr]*arrayCanonicalEntry)}
  814. e.pushTypeScope(func(_, column string) (string, bool) {
  815. if strings.EqualFold(column, "tags") {
  816. return "TEXT[]", true
  817. }
  818. return "", false
  819. })
  820. expr := &parser.ColumnRef{Column: "tags"}
  821. first, err := e.canonicalArrayValue(expr, "{a, b}")
  822. if err != nil || first != "{a,b}" {
  823. t.Fatalf("canonical = %v, %v", first, err)
  824. }
  825. if _, ok := e.arrayCanonicalCache[expr]; !ok {
  826. t.Fatal("canonical literal was not cached")
  827. }
  828. second, err := e.canonicalArrayValue(expr, "{a, b}")
  829. if err != nil || second != "{a,b}" {
  830. t.Fatalf("cached canonical = %v, %v", second, err)
  831. }
  832. changed, err := e.canonicalArrayValue(expr, "{c}")
  833. if err != nil || changed != "{c}" {
  834. t.Fatalf("changed canonical = %v, %v", changed, err)
  835. }
  836. if _, err := e.canonicalArrayValue(expr, "not-an-array"); !errors.Is(err, pgerror.ErrInvalidText) {
  837. t.Fatalf("malformed literal error = %v, want ErrInvalidText", err)
  838. }
  839. if _, err := e.canonicalArrayValue(expr, "not-an-array"); !errors.Is(err, pgerror.ErrInvalidText) {
  840. t.Fatalf("cached malformed literal error = %v, want ErrInvalidText", err)
  841. }
  842. }
  843. // A derived table or CTE materialized on the right side of a JOIN also keeps
  844. // its column types for the outer WHERE.
  845. func TestJoinedDerivedAndCTEArrayTyping(t *testing.T) {
  846. _, schema, table := newTestDB(t)
  847. e := newExec(schema, table)
  848. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[])`)
  849. execMust(t, e, `INSERT INTO t VALUES (1, '{t,f}'), (2, '{f,t}')`)
  850. execMust(t, e, `CREATE TABLE u (id INTEGER PRIMARY KEY, flags BOOLEAN[], tags TEXT[])`)
  851. execMust(t, e, `INSERT INTO u VALUES (1, '{f,t}', '{a,b}'), (2, '{t,f}', '{c}')`)
  852. cases := []struct {
  853. sql string
  854. want interface{}
  855. }{
  856. {`SELECT count(*) FROM t JOIN (SELECT id, flags FROM u) x ON x.id = t.id WHERE NOT x.flags[1]`, int64(1)},
  857. {`SELECT count(*) FROM t JOIN (SELECT id, flags FROM u) x ON x.id = t.id WHERE x.flags[1] = ANY('{true}')`, int64(1)},
  858. {`SELECT count(*) FROM t JOIN (SELECT id, tags FROM u) x ON x.id = t.id WHERE x.tags = '{a, b}'`, int64(1)},
  859. {`WITH x AS (SELECT id, flags FROM u) SELECT count(*) FROM t JOIN x ON x.id = t.id WHERE NOT x.flags[1]`, int64(1)},
  860. {`WITH x AS (SELECT id, tags FROM u) SELECT count(*) FROM t JOIN x ON x.id = t.id WHERE x.tags = '{a, b}'`, int64(1)},
  861. }
  862. for _, tc := range cases {
  863. res := execMust(t, e, tc.sql)
  864. if got := res.Rows[0][0]; got != tc.want {
  865. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  866. }
  867. }
  868. }
  869. // A view expands to a derived table, so it keeps the same typing guarantees.
  870. func TestViewArrayTyping(t *testing.T) {
  871. _, schema, table := newTestDB(t)
  872. e := newExec(schema, table)
  873. execMust(t, e, `CREATE TABLE t (id INTEGER PRIMARY KEY, flags BOOLEAN[], tags TEXT[])`)
  874. execMust(t, e, `INSERT INTO t VALUES (1, '{t,f}', '{a,b}'), (2, '{f,t}', '{c}')`)
  875. execMust(t, e, `CREATE VIEW v AS SELECT flags, tags FROM t`)
  876. for _, tc := range []struct {
  877. sql string
  878. want interface{}
  879. }{
  880. {`SELECT count(*) FROM v WHERE NOT flags[1]`, int64(1)},
  881. {`SELECT count(*) FROM v WHERE flags[1] = ANY('{true}')`, int64(1)},
  882. {`SELECT count(*) FROM v WHERE tags = '{a, b}'`, int64(1)},
  883. } {
  884. res := execMust(t, e, tc.sql)
  885. if got := res.Rows[0][0]; got != tc.want {
  886. t.Errorf("%s = %#v, want %#v", tc.sql, got, tc.want)
  887. }
  888. }
  889. }
  890. func TestTableFunctionColumnAliasList(t *testing.T) {
  891. _, schema, table := newTestDB(t)
  892. e := newExec(schema, table)
  893. res := execMust(t, e, `SELECT unnest FROM unnest(ARRAY[1,2,3]) AS t(unnest)`)
  894. if len(res.Columns) != 1 || res.Columns[0] != "unnest" {
  895. t.Fatalf("aliased unnest columns = %v, want [unnest]", res.Columns)
  896. }
  897. if res.RowCount != 3 || res.Rows[2][0] != int64(3) {
  898. t.Fatalf("aliased unnest rows = %v", res.Rows)
  899. }
  900. res = execMust(t, e, `SELECT t.n FROM unnest(ARRAY[10,20,30]) AS t(n) WHERE n > 10 ORDER BY n`)
  901. if res.RowCount != 2 || res.Rows[0][0] != int64(20) || res.Rows[1][0] != int64(30) {
  902. t.Fatalf("aliased unnest WHERE rows = %v", res.Rows)
  903. }
  904. if len(res.ColumnTypes) != 1 || res.ColumnTypes[0] != "INTEGER" {
  905. t.Fatalf("aliased unnest column type = %v, want INTEGER", res.ColumnTypes)
  906. }
  907. res = execMust(t, e, `SELECT n FROM unnest('{a,b}'::TEXT[]) AS t(n) WHERE n = 'b'`)
  908. if res.RowCount != 1 || res.Rows[0][0] != "b" {
  909. t.Fatalf("aliased text unnest rows = %v", res.Rows)
  910. }
  911. _, err := execSQL(e, `SELECT * FROM unnest(ARRAY[1]) AS t(a, b)`)
  912. if !errors.Is(err, pgerror.ErrInvalidColumnReference) {
  913. t.Fatalf("alias arity error = %v, want ErrInvalidColumnReference", err)
  914. }
  915. }
  916. func TestArrayContainment(t *testing.T) {
  917. _, schema, table := newTestDB(t)
  918. e := newExec(schema, table)
  919. execMust(t, e, `CREATE TABLE t (
  920. id INTEGER PRIMARY KEY,
  921. nums INTEGER[],
  922. tags TEXT[],
  923. flags BOOLEAN[],
  924. exact NUMERIC[]
  925. )`)
  926. execMust(t, e, `INSERT INTO t VALUES
  927. (1, '{1,2,3}', '{a,b}', '{t,f}', '{1.10,3}'),
  928. (2, '{1,NULL}', '{NULL,"x"}', NULL, NULL),
  929. (3, '{}', '{}', '{}', '{}'),
  930. (4, NULL, NULL, NULL, NULL)`)
  931. cases := []struct {
  932. sql string
  933. want interface{}
  934. }{
  935. // Each contained element needs some containing element; duplicates are
  936. // irrelevant.
  937. {`SELECT nums @> ARRAY[1] FROM t WHERE id = 1`, true},
  938. {`SELECT nums @> ARRAY[3,1] FROM t WHERE id = 1`, true},
  939. {`SELECT nums @> ARRAY[2,2] FROM t WHERE id = 1`, true},
  940. {`SELECT nums @> ARRAY[4] FROM t WHERE id = 1`, false},
  941. {`SELECT nums <@ ARRAY[3,2,1,5] FROM t WHERE id = 1`, true},
  942. {`SELECT ARRAY[3,2,1,5] @> nums FROM t WHERE id = 1`, true},
  943. // Empty arrays: the empty array is contained by everything.
  944. {`SELECT nums @> ARRAY[]::INTEGER[] FROM t WHERE id = 1`, true},
  945. {`SELECT nums @> ARRAY[1] FROM t WHERE id = 3`, false},
  946. {`SELECT nums @> ARRAY[]::INTEGER[] FROM t WHERE id = 3`, true},
  947. {`SELECT nums <@ ARRAY[1] FROM t WHERE id = 3`, true},
  948. // NULL elements only match other NULL elements.
  949. {`SELECT nums @> ARRAY[NULL]::INTEGER[] FROM t WHERE id = 2`, true},
  950. {`SELECT nums @> ARRAY[1] FROM t WHERE id = 2`, true},
  951. {`SELECT nums @> ARRAY[2] FROM t WHERE id = 2`, false},
  952. {`SELECT nums @> ARRAY[1, NULL]::INTEGER[] FROM t WHERE id = 2`, true},
  953. {`SELECT nums @> ARRAY[1, NULL]::INTEGER[] FROM t WHERE id = 1`, false},
  954. // A NULL operand yields NULL.
  955. {`SELECT nums @> NULL FROM t WHERE id = 1`, nil},
  956. {`SELECT NULL::INTEGER[] @> nums FROM t WHERE id = 1`, nil},
  957. {`SELECT nums @> ARRAY[1] FROM t WHERE id = 4`, nil},
  958. // Other element types and a text array literal on the right.
  959. {`SELECT tags @> ARRAY['a'] FROM t WHERE id = 1`, true},
  960. {`SELECT tags @> ARRAY['c'] FROM t WHERE id = 1`, false},
  961. {`SELECT tags @> '{a,b}' FROM t WHERE id = 1`, true},
  962. {`SELECT tags @> '{a,b,c}' FROM t WHERE id = 1`, false},
  963. {`SELECT '{1,2}' <@ nums FROM t WHERE id = 1`, true},
  964. {`SELECT '{1,4}' <@ nums FROM t WHERE id = 1`, false},
  965. {`SELECT tags @> ARRAY[NULL]::TEXT[] FROM t WHERE id = 2`, true},
  966. {`SELECT flags @> ARRAY[TRUE] FROM t WHERE id = 1`, true},
  967. {`SELECT flags @> ARRAY[TRUE] FROM t WHERE id = 3`, false},
  968. // NUMERIC elements compare exactly, so 1.10 matches 1.1.
  969. {`SELECT exact @> ARRAY[1.1]::NUMERIC[] FROM t WHERE id = 1`, true},
  970. }
  971. for _, tc := range cases {
  972. res := execMust(t, e, tc.sql)
  973. if res.RowCount != 1 || res.Rows[0][0] != tc.want {
  974. t.Errorf("%s = %v, want %v", tc.sql, res.Rows, tc.want)
  975. }
  976. }
  977. if _, err := execSQL(e, `SELECT nums @> 5 FROM t`); jsonbErrorCode(t, err) != pgerror.CodeDatatypeMismatch {
  978. t.Fatalf("integer containment error = %v, want 42804", err)
  979. }
  980. }