jsonb_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. package executor
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "strings"
  6. "testing"
  7. "github.com/danfragoso/pizzasql-next/pkg/pgerror"
  8. "github.com/danfragoso/pizzasql-next/pkg/storage"
  9. )
  10. // jsonbExec parses and executes one statement, returning the value at (0, 0).
  11. func jsonbValue(t *testing.T, e *Executor, sql string) interface{} {
  12. t.Helper()
  13. res := execMust(t, e, sql)
  14. if len(res.Rows) == 0 || len(res.Rows[0]) == 0 {
  15. t.Fatalf("%s: no value", sql)
  16. }
  17. return res.Rows[0][0]
  18. }
  19. func jsonbErrorCode(t *testing.T, err error) string {
  20. t.Helper()
  21. switch {
  22. case err == nil:
  23. return ""
  24. case errors.Is(err, pgerror.ErrInvalidText):
  25. return pgerror.CodeInvalidText
  26. case errors.Is(err, pgerror.ErrInvalidParameter):
  27. return pgerror.CodeInvalidParameter
  28. case errors.Is(err, pgerror.ErrFeatureNotSupported):
  29. return pgerror.CodeFeatureNotSupported
  30. case errors.Is(err, pgerror.ErrDatatypeMismatch):
  31. return pgerror.CodeDatatypeMismatch
  32. default:
  33. return "other:" + err.Error()
  34. }
  35. }
  36. func TestJSONBCanonicalization(t *testing.T) {
  37. pool, schema, table := newTestDB(t)
  38. e := newExec(schema, table)
  39. execMust(t, e, "CREATE TABLE docs (id INTEGER PRIMARY KEY, doc JSONB)")
  40. execMust(t, e, `INSERT INTO docs VALUES (1, '{"b": 1, "a": 2, "b": 3}')`)
  41. execMust(t, e, `INSERT INTO docs VALUES (2, '{"z": 1.50, "y": 1e2, "x": -0.0, "w": 1.0}')`)
  42. execMust(t, e, `INSERT INTO docs VALUES (3, ' [ 1 , { "b" : 2 , "a" : 1 } ] ')`)
  43. want := map[int64]string{
  44. 1: `{"a":2,"b":3}`,
  45. 2: `{"w":1,"x":0,"y":100,"z":1.5}`,
  46. 3: `[1,{"a":1,"b":2}]`,
  47. }
  48. res := execMust(t, e, "SELECT id, doc FROM docs ORDER BY id")
  49. for _, row := range res.Rows {
  50. id := row[0].(int64)
  51. if row[1] != want[id] {
  52. t.Fatalf("row %d doc = %v, want %s", id, row[1], want[id])
  53. }
  54. }
  55. // A JSONB column rejects malformed documents and stores nothing.
  56. if _, err := execSQL(e, `INSERT INTO docs VALUES (4, 'not json')`); jsonbErrorCode(t, err) != pgerror.CodeInvalidText {
  57. t.Fatalf("malformed JSONB insert error = %v", err)
  58. }
  59. if res := execMust(t, e, "SELECT COUNT(*) FROM docs WHERE id = 4"); res.Rows[0][0] != int64(0) {
  60. t.Fatalf("malformed JSONB insert stored a row")
  61. }
  62. // Non-text input is not silently stringified, matching array columns.
  63. if _, err := execSQL(e, `INSERT INTO docs VALUES (5, 5)`); jsonbErrorCode(t, err) != pgerror.CodeInvalidText {
  64. t.Fatalf("integer JSONB insert error = %v", err)
  65. }
  66. // Canonicalization is idempotent, and a fresh executor over the same store
  67. // reads back the same bytes (no hidden normalization at read time).
  68. reloadedSchema := storage.NewSchemaManager(pool, "testdb")
  69. reloadedTable := storage.NewTableManager(pool, reloadedSchema, "testdb")
  70. reloaded := newExec(reloadedSchema, reloadedTable)
  71. res = execMust(t, reloaded, "SELECT id, doc FROM docs ORDER BY id")
  72. for _, row := range res.Rows {
  73. id := row[0].(int64)
  74. if row[1] != want[id] {
  75. t.Fatalf("reloaded row %d doc = %v, want %s", id, row[1], want[id])
  76. }
  77. canonical, err := CanonicalizeJSONB(row[1].(string))
  78. if err != nil || canonical != row[1] {
  79. t.Fatalf("row %d is not a canonical fixed point: %q (%v)", id, row[1], err)
  80. }
  81. }
  82. }
  83. func TestJSONBCastsAndToJSONB(t *testing.T) {
  84. _, schema, table := newTestDB(t)
  85. e := newExec(schema, table)
  86. cases := []struct {
  87. sql string
  88. want interface{}
  89. }{
  90. {`SELECT '{"b":2,"a":1}'::jsonb`, `{"a":1,"b":2}`},
  91. {`SELECT CAST(' [1, 2] ' AS JSONB)`, `[1,2]`},
  92. {`SELECT ('{"a":1}'::jsonb)::jsonb`, `{"a":1}`},
  93. {`SELECT to_jsonb(5)`, `5`},
  94. {`SELECT to_jsonb('x')`, `"x"`},
  95. {`SELECT to_jsonb(TRUE)`, `true`},
  96. {`SELECT to_jsonb(NULL)`, nil},
  97. {`SELECT to_jsonb(ARRAY[1,2])`, `[1,2]`},
  98. {`SELECT to_jsonb(ARRAY['a','b'])`, `["a","b"]`},
  99. {`SELECT to_jsonb('{"a":1}'::jsonb)`, `{"a":1}`},
  100. {`SELECT CAST('{"a":1}'::jsonb AS TEXT)`, `{"a":1}`},
  101. }
  102. for _, tc := range cases {
  103. if got := jsonbValue(t, e, tc.sql); got != tc.want {
  104. t.Errorf("%s = %v (%T), want %v", tc.sql, got, got, tc.want)
  105. }
  106. }
  107. for _, sql := range []string{
  108. `SELECT 'not json'::jsonb`,
  109. `SELECT '{"a":}'::jsonb`,
  110. `SELECT '{"a":1} trailing'::jsonb`,
  111. `SELECT 5::jsonb`,
  112. `SELECT '{"a":1e999999}'::jsonb`,
  113. } {
  114. if _, err := execSQL(e, sql); jsonbErrorCode(t, err) != pgerror.CodeInvalidText {
  115. t.Errorf("%s error = %v, want 22P02", sql, err)
  116. }
  117. }
  118. // The JSON1 normalization function and its json() sibling keep working.
  119. if got := jsonbValue(t, e, `SELECT jsonb('{"a":1}')`); got != `{"a":1}` {
  120. t.Fatalf("jsonb() = %v", got)
  121. }
  122. if got := jsonbValue(t, e, `SELECT jsonb_valid('{"a":1}')`); got != int64(1) {
  123. t.Fatalf("jsonb_valid() = %v", got)
  124. }
  125. }
  126. func TestJSONBArrowOperators(t *testing.T) {
  127. _, schema, table := newTestDB(t)
  128. e := newExec(schema, table)
  129. execMust(t, e, "CREATE TABLE docs (id INTEGER PRIMARY KEY, doc JSONB)")
  130. execMust(t, e, `INSERT INTO docs VALUES (1, '{"a":1,"s":"x","n":null,"arr":[10,{"b":2}]}')`)
  131. cases := []struct {
  132. sql string
  133. want interface{}
  134. }{
  135. {`SELECT doc -> 'a' FROM docs`, `1`},
  136. {`SELECT doc -> 's' FROM docs`, `"x"`},
  137. {`SELECT doc ->> 's' FROM docs`, `x`},
  138. {`SELECT doc -> 'n' FROM docs`, `null`},
  139. {`SELECT doc ->> 'n' FROM docs`, nil},
  140. {`SELECT doc -> 'missing' FROM docs`, nil},
  141. {`SELECT doc ->> 'missing' FROM docs`, nil},
  142. {`SELECT doc -> 'arr' -> 0 FROM docs`, `10`},
  143. {`SELECT doc -> 'arr' -> -1 ->> 'b' FROM docs`, `2`},
  144. {`SELECT doc -> 'arr' -> 3 FROM docs`, nil},
  145. {`SELECT doc -> '$."arr"[1]' ->> 'b' FROM docs`, `2`},
  146. {`SELECT NULL::jsonb -> 'a'`, nil},
  147. {`SELECT doc -> NULL FROM docs`, nil},
  148. }
  149. for _, tc := range cases {
  150. if got := jsonbValue(t, e, tc.sql); got != tc.want {
  151. t.Errorf("%s = %v (%T), want %v", tc.sql, got, got, tc.want)
  152. }
  153. }
  154. // The JSON text surface is unchanged: a text-backed document still returns
  155. // JSON1 coercions for ->>.
  156. if got := jsonbValue(t, e, `SELECT '{"a":true}' ->> 'a'`); got != int64(1) {
  157. t.Fatalf("JSON1 ->> boolean = %v (%T), want int64 1", got, got)
  158. }
  159. }
  160. func TestJSONBPathOperators(t *testing.T) {
  161. _, schema, table := newTestDB(t)
  162. e := newExec(schema, table)
  163. cases := []struct {
  164. sql string
  165. want interface{}
  166. }{
  167. {`SELECT '{"a":{"b":2}}'::jsonb #> '{a,b}'`, `2`},
  168. {`SELECT '{"a":{"b":2}}'::jsonb #>> '{a,b}'`, `2`},
  169. {`SELECT '{"a":[1,2,3]}'::jsonb #> '{a,-1}'`, `3`},
  170. {`SELECT '{"a":{"b":2}}'::jsonb #> '$."a"."b"'`, `2`},
  171. {`SELECT '{"a":{"b":2}}'::jsonb #> ARRAY['a','b']`, `2`},
  172. {`SELECT '{"a":1}'::jsonb #> '{missing}'`, nil},
  173. {`SELECT '{"a":1}'::jsonb #- '{a}'`, `{}`},
  174. {`SELECT '{"a":{"b":1},"c":2}'::jsonb #- '{a,b}'`, `{"a":{},"c":2}`},
  175. {`SELECT '[1,2,3]'::jsonb #- '{1}'`, `[1,3]`},
  176. {`SELECT '{"a":1}'::jsonb #- '{missing}'`, `{"a":1}`},
  177. {`SELECT NULL::jsonb #> '{a}'`, nil},
  178. }
  179. for _, tc := range cases {
  180. if got := jsonbValue(t, e, tc.sql); got != tc.want {
  181. t.Errorf("%s = %v (%T), want %v", tc.sql, got, got, tc.want)
  182. }
  183. }
  184. // Wildcards, slices, and unbounded expansion are rejected, not approximated.
  185. for _, sql := range []string{
  186. `SELECT '{"a":1}'::jsonb #> '$[*]'`,
  187. `SELECT '{"a":1}'::jsonb #> '$.**'`,
  188. `SELECT '{"a":1}'::jsonb -> '$[*]'`,
  189. `SELECT '{"a":1}'::jsonb #- '$[0:1]'`,
  190. } {
  191. if _, err := execSQL(e, sql); jsonbErrorCode(t, err) != pgerror.CodeFeatureNotSupported {
  192. t.Errorf("%s error = %v, want 0A000", sql, err)
  193. }
  194. }
  195. }
  196. func TestJSONBContainmentAndExistence(t *testing.T) {
  197. _, schema, table := newTestDB(t)
  198. e := newExec(schema, table)
  199. cases := []struct {
  200. sql string
  201. want interface{}
  202. }{
  203. {`SELECT '{"a":1,"b":{"c":2}}'::jsonb @> '{"a":1}'::jsonb`, true},
  204. {`SELECT '{"a":1,"b":{"c":2}}'::jsonb @> '{"b":{"c":2}}'::jsonb`, true},
  205. {`SELECT '{"a":1,"b":{"c":2}}'::jsonb @> '{"b":{}}'::jsonb`, true},
  206. {`SELECT '{"a":1}'::jsonb @> '{"b":1}'::jsonb`, false},
  207. {`SELECT '{"a":1}'::jsonb @> '{"a":1,"b":2}'::jsonb`, false},
  208. {`SELECT '[1,2,3]'::jsonb @> '[3,1]'::jsonb`, true},
  209. {`SELECT '[1,2,3]'::jsonb @> '[4]'::jsonb`, false},
  210. {`SELECT '[1,2]'::jsonb @> '1'::jsonb`, true},
  211. {`SELECT '["a"]'::jsonb @> '"a"'::jsonb`, true},
  212. {`SELECT '[true]'::jsonb @> 'true'::jsonb`, true},
  213. {`SELECT '[null]'::jsonb @> 'null'::jsonb`, true},
  214. {`SELECT '[1]'::jsonb @> '2'::jsonb`, false},
  215. {`SELECT '{"a":[1,2]}'::jsonb @> '{"a":[2]}'::jsonb`, true},
  216. {`SELECT '{"a":null}'::jsonb @> '{"a":null}'::jsonb`, true},
  217. {`SELECT '{"a":null}'::jsonb @> '{"a":1}'::jsonb`, false},
  218. {`SELECT '{"a":1.0}'::jsonb @> '{"a":1}'::jsonb`, true},
  219. {`SELECT '{"a":1}'::jsonb <@ '{"a":1,"b":2}'::jsonb`, true},
  220. {`SELECT '{"a":1}'::jsonb @> '{"a":1}'`, true},
  221. {`SELECT NULL::jsonb @> '{}'::jsonb`, nil},
  222. {`SELECT '{"a":1}'::jsonb @> NULL::jsonb`, nil},
  223. {`SELECT '{"a":1}'::jsonb ? 'a'`, true},
  224. {`SELECT '{"a":1}'::jsonb ? 'b'`, false},
  225. {`SELECT '[1,"x"]'::jsonb ? 'x'`, true},
  226. {`SELECT '[1,"x"]'::jsonb ? '1'`, false},
  227. {`SELECT '"x"'::jsonb ? 'x'`, false},
  228. {`SELECT 'null'::jsonb ? 'x'`, false},
  229. {`SELECT '{"a":1,"b":2}'::jsonb ?| '{b,c}'`, true},
  230. {`SELECT '{"a":1,"b":2}'::jsonb ?| '{c,d}'`, false},
  231. {`SELECT '{"a":1,"b":2}'::jsonb ?& '{a,b}'`, true},
  232. {`SELECT '{"a":1,"b":2}'::jsonb ?& '{a,c}'`, false},
  233. {`SELECT '{"1":true}'::jsonb ? '1'`, true},
  234. {`SELECT '{"1":true}'::jsonb ?| '{1,2}'`, true},
  235. {`SELECT '[1,"x"]'::jsonb ?| '{x}'`, true},
  236. {`SELECT NULL::jsonb ? 'a'`, nil},
  237. }
  238. for _, tc := range cases {
  239. if got := jsonbValue(t, e, tc.sql); got != tc.want {
  240. t.Errorf("%s = %v (%T), want %v", tc.sql, got, got, tc.want)
  241. }
  242. }
  243. // Containment needs a jsonb operand on at least one side.
  244. if _, err := execSQL(e, `SELECT '{"a":1}' @> '{"a":1}'`); jsonbErrorCode(t, err) != pgerror.CodeDatatypeMismatch {
  245. t.Errorf("text @> text error = %v, want 42804", err)
  246. }
  247. }
  248. func TestJSONBConcatAndDelete(t *testing.T) {
  249. _, schema, table := newTestDB(t)
  250. e := newExec(schema, table)
  251. cases := []struct {
  252. sql string
  253. want interface{}
  254. }{
  255. {`SELECT '{"a":1}'::jsonb || '{"b":2,"a":3}'::jsonb`, `{"a":3,"b":2}`},
  256. {`SELECT '[1]'::jsonb || '[2,3]'::jsonb`, `[1,2,3]`},
  257. {`SELECT '[1]'::jsonb || '2'::jsonb`, `[1,2]`},
  258. {`SELECT '{"a":1}'::jsonb || '[1]'::jsonb`, `[{"a":1},1]`},
  259. {`SELECT '1'::jsonb || '2'::jsonb`, `[1,2]`},
  260. {`SELECT NULL::jsonb || '{}'::jsonb`, nil},
  261. {`SELECT '{"a":1,"b":2}'::jsonb - 'a'`, `{"b":2}`},
  262. {`SELECT '{"a":1,"b":2}'::jsonb - '{a,b}'::text[]`, `{}`},
  263. {`SELECT '[1,2,3]'::jsonb - 1`, `[1,3]`},
  264. {`SELECT '[1,2,3]'::jsonb - -1`, `[1,2]`},
  265. {`SELECT '[1,2,3]'::jsonb - 9`, `[1,2,3]`},
  266. {`SELECT '["a","b","a"]'::jsonb - 'a'`, `["b"]`},
  267. {`SELECT '"x"'::jsonb - 'a'`, `"x"`},
  268. {`SELECT NULL::jsonb - 'a'`, nil},
  269. }
  270. for _, tc := range cases {
  271. if got := jsonbValue(t, e, tc.sql); got != tc.want {
  272. t.Errorf("%s = %v (%T), want %v", tc.sql, got, got, tc.want)
  273. }
  274. }
  275. // Text concatenation is unchanged when neither side is jsonb.
  276. if got := jsonbValue(t, e, `SELECT 'a' || 'b'`); got != "ab" {
  277. t.Fatalf("text concatenation = %v", got)
  278. }
  279. if _, err := execSQL(e, `SELECT '{"a":1}'::jsonb - 1`); jsonbErrorCode(t, err) != pgerror.CodeDatatypeMismatch {
  280. t.Errorf("jsonb - integer on object error = %v, want 42804", err)
  281. }
  282. }
  283. func TestJSONBScalarFunctions(t *testing.T) {
  284. _, schema, table := newTestDB(t)
  285. e := newExec(schema, table)
  286. cases := []struct {
  287. sql string
  288. want interface{}
  289. }{
  290. {`SELECT jsonb_typeof('{"a":1}')`, "object"},
  291. {`SELECT jsonb_typeof('[1]')`, "array"},
  292. {`SELECT jsonb_typeof('"x"')`, "string"},
  293. {`SELECT jsonb_typeof('1.50')`, "number"},
  294. {`SELECT jsonb_typeof('true')`, "boolean"},
  295. {`SELECT jsonb_typeof('null')`, "null"},
  296. {`SELECT jsonb_typeof(NULL)`, nil},
  297. {`SELECT jsonb_array_length('[1,2,3]')`, int64(3)},
  298. {`SELECT jsonb_array_length('[]')`, int64(0)},
  299. {`SELECT jsonb_array_length(NULL)`, nil},
  300. {`SELECT jsonb_pretty('{"b":2,"a":[1]}')`, "{\n \"a\": [\n 1\n ],\n \"b\": 2\n}"},
  301. {`SELECT jsonb_strip_nulls('{"a":null,"b":{"c":null,"d":1},"e":[null,1]}')`, `{"b":{"d":1},"e":[null,1]}`},
  302. {`SELECT jsonb_build_object('b', 1, 'a', 'x')`, `{"a":"x","b":1}`},
  303. {`SELECT jsonb_build_object('a', NULL)`, `{"a":null}`},
  304. {`SELECT jsonb_build_object('a', '{"b":1}'::jsonb)`, `{"a":{"b":1}}`},
  305. {`SELECT jsonb_build_array(1, 'x', NULL, TRUE)`, `[1,"x",null,true]`},
  306. {`SELECT jsonb_build_array()`, `[]`},
  307. {`SELECT jsonb_build_object()`, `{}`},
  308. {`SELECT jsonb_set('{"a":1}', '{a}', '2')`, `{"a":2}`},
  309. {`SELECT jsonb_set('{"a":1}', '{b}', '2')`, `{"a":1,"b":2}`},
  310. {`SELECT jsonb_set('{"a":1}', '{b}', '2', false)`, `{"a":1}`},
  311. {`SELECT jsonb_set('{"a":{"b":1}}', '{a,b}', '2')`, `{"a":{"b":2}}`},
  312. {`SELECT jsonb_set('{"a":{"b":1}}', '{x,b}', '2')`, `{"a":{"b":1}}`},
  313. {`SELECT jsonb_set('[1,2]', '{2}', '3')`, `[1,2,3]`},
  314. {`SELECT jsonb_set('{"a":1}', NULL, '2')`, nil},
  315. {`SELECT jsonb_insert('{"a":1}', NULL, '2')`, nil},
  316. {`SELECT jsonb_insert('{"a":[0,1,2]}', '{a,1}', '"n"')`, `{"a":[0,"n",1,2]}`},
  317. {`SELECT jsonb_insert('{"a":[0,1,2]}', '{a,1}', '"n"', true)`, `{"a":[0,1,"n",2]}`},
  318. {`SELECT jsonb_insert('{"a":1}', '{b}', '2')`, `{"a":1,"b":2}`},
  319. {`SELECT jsonb_insert('{"a":1}', '{a}', '2')`, `{"a":1}`},
  320. {`SELECT jsonb_set('{"a":1}', '{a}', NULL)`, nil},
  321. {`SELECT jsonb_insert('[0,1]', '{-1}', '9')`, `[0,9,1]`},
  322. // The JSON1 $ path form keeps its previous set/insert behavior and now
  323. // returns canonical jsonb.
  324. {`SELECT jsonb_set('{"a":1}', '$.b', '2')`, `{"a":1,"b":2}`},
  325. {`SELECT jsonb_set('{"a":1}', '$.a', '2')`, `{"a":2}`},
  326. {`SELECT jsonb_insert('{"a":1}', '$.b', '2')`, `{"a":1,"b":2}`},
  327. {`SELECT jsonb_remove('{"a":1,"b":2}', '$.a')`, `{"b":2}`},
  328. }
  329. for _, tc := range cases {
  330. if got := jsonbValue(t, e, tc.sql); got != tc.want {
  331. t.Errorf("%s = %v (%T), want %v", tc.sql, got, got, tc.want)
  332. }
  333. }
  334. // JSON1 scalar functions keep their text surface.
  335. if got := jsonbValue(t, e, `SELECT json_type('{"a":1.5}', '$.a')`); got != "real" {
  336. t.Fatalf("json_type text surface = %v", got)
  337. }
  338. if got := jsonbValue(t, e, `SELECT json_set('{"a":1}', '$.b', '2')`); got != `{"a":1,"b":"2"}` {
  339. t.Fatalf("json_set text surface = %v", got)
  340. }
  341. for _, tc := range []struct {
  342. sql string
  343. code string
  344. }{
  345. {`SELECT jsonb_array_length('{"a":1}')`, pgerror.CodeInvalidParameter},
  346. {`SELECT jsonb_typeof('not json')`, pgerror.CodeInvalidText},
  347. {`SELECT jsonb_build_object('a')`, pgerror.CodeInvalidParameter},
  348. {`SELECT jsonb_build_object(NULL, 1)`, pgerror.CodeInvalidParameter},
  349. {`SELECT jsonb_set('{"a":1}', '{a,0}', '2')`, pgerror.CodeInvalidParameter},
  350. {`SELECT jsonb_insert('{"a":1}', '{a,0}', '2')`, pgerror.CodeInvalidParameter},
  351. } {
  352. if _, err := execSQL(e, tc.sql); jsonbErrorCode(t, err) != tc.code {
  353. t.Errorf("%s error = %v, want %s", tc.sql, err, tc.code)
  354. }
  355. }
  356. }
  357. func TestJSONBSetReturningFunctionsInFrom(t *testing.T) {
  358. _, schema, table := newTestDB(t)
  359. e := newExec(schema, table)
  360. res := execMust(t, e, `SELECT * FROM jsonb_each('{"b":2,"a":{"c":1}}')`)
  361. if len(res.Columns) != 2 || res.Columns[0] != "key" || res.Columns[1] != "value" {
  362. t.Fatalf("jsonb_each columns = %v", res.Columns)
  363. }
  364. if res.RowCount != 2 || res.Rows[0][0] != "a" || res.Rows[0][1] != `{"c":1}` || res.Rows[1][0] != "b" || res.Rows[1][1] != `2` {
  365. t.Fatalf("jsonb_each rows = %v", res.Rows)
  366. }
  367. if len(res.ColumnTypes) != 2 || res.ColumnTypes[1] != "JSONB" {
  368. t.Fatalf("jsonb_each column types = %v", res.ColumnTypes)
  369. }
  370. res = execMust(t, e, `SELECT e.key, e.value FROM jsonb_each('{"a":1}') AS e`)
  371. if res.RowCount != 1 || res.Rows[0][0] != "a" || res.Rows[0][1] != `1` {
  372. t.Fatalf("aliased jsonb_each rows = %v", res.Rows)
  373. }
  374. res = execMust(t, e, `SELECT key, value FROM jsonb_each_text('{"a":null,"b":true}')`)
  375. if res.RowCount != 2 || res.Rows[0][0] != "a" || res.Rows[0][1] != nil || res.Rows[1][1] != "true" {
  376. t.Fatalf("jsonb_each_text rows = %v", res.Rows)
  377. }
  378. res = execMust(t, e, `SELECT value FROM jsonb_array_elements('[1,{"a":2}]')`)
  379. if res.RowCount != 2 || res.Rows[0][0] != `1` || res.Rows[1][0] != `{"a":2}` {
  380. t.Fatalf("jsonb_array_elements rows = %v", res.Rows)
  381. }
  382. if res.ColumnTypes[0] != "JSONB" {
  383. t.Fatalf("jsonb_array_elements type = %v", res.ColumnTypes)
  384. }
  385. res = execMust(t, e, `SELECT value FROM jsonb_array_elements_text('[1,null,"x"]')`)
  386. if res.RowCount != 3 || res.Rows[0][0] != "1" || res.Rows[1][0] != nil || res.Rows[2][0] != "x" {
  387. t.Fatalf("jsonb_array_elements_text rows = %v", res.Rows)
  388. }
  389. res = execMust(t, e, `SELECT jsonb_object_keys FROM jsonb_object_keys('{"b":1,"a":2}')`)
  390. if res.RowCount != 2 || res.Rows[0][0] != "a" || res.Rows[1][0] != "b" {
  391. t.Fatalf("jsonb_object_keys rows = %v", res.Rows)
  392. }
  393. // A NULL document produces no rows.
  394. res = execMust(t, e, `SELECT * FROM jsonb_array_elements(NULL)`)
  395. if res.RowCount != 0 {
  396. t.Fatalf("jsonb_array_elements(NULL) rows = %v", res.Rows)
  397. }
  398. // Wrong document types and projections of set-returning functions are
  399. // rejected clearly instead of executing as scalar calls.
  400. for _, tc := range []struct {
  401. sql string
  402. code string
  403. }{
  404. {`SELECT * FROM jsonb_array_elements('{"a":1}')`, pgerror.CodeInvalidParameter},
  405. {`SELECT * FROM jsonb_each('[1]')`, pgerror.CodeInvalidParameter},
  406. {`SELECT * FROM jsonb_object_keys('[1]')`, pgerror.CodeInvalidParameter},
  407. {`SELECT * FROM jsonb_each('null')`, pgerror.CodeInvalidParameter},
  408. {`SELECT * FROM jsonb_array_elements('null')`, pgerror.CodeInvalidParameter},
  409. {`SELECT jsonb_each('{"a":1}')`, pgerror.CodeFeatureNotSupported},
  410. {`SELECT jsonb_array_elements('[1]')`, pgerror.CodeFeatureNotSupported},
  411. {`SELECT * FROM jsonb_each('{"a":1}') AS e JOIN docs d ON TRUE`, pgerror.CodeFeatureNotSupported},
  412. } {
  413. if _, err := execSQL(e, tc.sql); jsonbErrorCode(t, err) != tc.code {
  414. t.Errorf("%s error = %v, want %s", tc.sql, err, tc.code)
  415. }
  416. }
  417. }
  418. func TestJSONBEqualityOrderingAndDistinct(t *testing.T) {
  419. _, schema, table := newTestDB(t)
  420. e := newExec(schema, table)
  421. execMust(t, e, "CREATE TABLE docs (id INTEGER PRIMARY KEY, doc JSONB)")
  422. execMust(t, e, `INSERT INTO docs VALUES (1, '{"b":1,"a":2}'), (2, '{"a":2,"b":1}'), (3, '[1,2]')`)
  423. if got := jsonbValue(t, e, `SELECT '{"b":1,"a":2}'::jsonb = '{"a":2, "b": 1}'::jsonb`); got != true {
  424. t.Fatalf("jsonb equality = %v, want true", got)
  425. }
  426. if got := jsonbValue(t, e, `SELECT '{"a":1}'::jsonb = '[1]'::jsonb`); got != false {
  427. t.Fatalf("jsonb inequality = %v, want false", got)
  428. }
  429. if got := jsonbValue(t, e, `SELECT doc IS NOT DISTINCT FROM '{"a":2,"b":1}'::jsonb FROM docs WHERE id = 2`); got != true {
  430. t.Fatalf("IS NOT DISTINCT FROM = %v", got)
  431. }
  432. if got := jsonbValue(t, e, `SELECT '{"b":1,"a":2}'::jsonb IS DISTINCT FROM '{"a":2,"b":1.0}'::jsonb`); got != false {
  433. t.Fatalf("IS DISTINCT FROM key-order/number-spelling = %v, want false", got)
  434. }
  435. res := execMust(t, e, `SELECT COUNT(*) FROM docs WHERE doc = '{"a":2,"b":1}'::jsonb`)
  436. if res.Rows[0][0] != int64(2) {
  437. t.Fatalf("key-order-independent equality matched %v rows, want 2", res.Rows[0][0])
  438. }
  439. res = execMust(t, e, `SELECT DISTINCT doc FROM docs ORDER BY doc`)
  440. if res.RowCount != 2 {
  441. t.Fatalf("DISTINCT jsonb rows = %v, want 2", res.Rows)
  442. }
  443. // Canonical numeric scalars order numerically, and non-scalars order by
  444. // canonical text.
  445. execMust(t, e, "CREATE TABLE scalars (id INTEGER PRIMARY KEY, v JSONB)")
  446. execMust(t, e, `INSERT INTO scalars VALUES (1, '10'), (2, '2')`)
  447. res = execMust(t, e, `SELECT v FROM scalars ORDER BY v`)
  448. if res.RowCount != 2 || res.Rows[0][0] != `2` || res.Rows[1][0] != `10` {
  449. t.Fatalf("jsonb scalar ordering = %v", res.Rows)
  450. }
  451. res = execMust(t, e, `SELECT doc FROM docs WHERE id IN (1,3) ORDER BY doc`)
  452. if res.RowCount != 2 || res.Rows[0][0] != `[1,2]` || res.Rows[1][0] != `{"a":2,"b":1}` {
  453. t.Fatalf("jsonb text ordering = %v", res.Rows)
  454. }
  455. // A jsonb column cannot be compared with a number.
  456. if _, err := execSQL(e, `SELECT id FROM docs WHERE doc = 5`); err == nil {
  457. t.Fatal("jsonb = integer was accepted")
  458. }
  459. }
  460. func TestJSONBRejectedShapes(t *testing.T) {
  461. _, schema, table := newTestDB(t)
  462. e := newExec(schema, table)
  463. execMust(t, e, "CREATE TABLE docs (id INTEGER PRIMARY KEY, doc JSONB)")
  464. for _, sql := range []string{
  465. `SELECT jsonb_path_exists('{"a":1}', '$.a')`,
  466. `SELECT jsonb_path_query('{"a":1}', '$.a')`,
  467. `CREATE INDEX docs_gin ON docs USING GIN (doc)`,
  468. `CREATE INDEX docs_expr ON docs ((doc -> 'a'))`,
  469. `CREATE TABLE bad_arrays (doc JSONB[])`,
  470. } {
  471. if _, err := execSQL(e, sql); jsonbErrorCode(t, err) != pgerror.CodeFeatureNotSupported {
  472. t.Errorf("%s error = %v, want 0A000", sql, err)
  473. }
  474. }
  475. // A scalar extraction index over jsonb is a text expression and stays legal.
  476. execMust(t, e, `CREATE INDEX docs_kind ON docs ((doc ->> 'kind'))`)
  477. }
  478. func TestJSONBColumnMetadataAndDefaults(t *testing.T) {
  479. _, schema, table := newTestDB(t)
  480. e := newExec(schema, table)
  481. execMust(t, e, `CREATE TABLE docs (id INTEGER PRIMARY KEY, doc JSONB DEFAULT '{"b":2,"a":1}')`)
  482. execMust(t, e, `INSERT INTO docs (id) VALUES (1)`)
  483. res := execMust(t, e, `SELECT doc FROM docs`)
  484. if res.Rows[0][0] != `{"a":1,"b":2}` {
  485. t.Fatalf("canonical default = %v", res.Rows[0][0])
  486. }
  487. if len(res.ColumnTypes) != 1 || res.ColumnTypes[0] != "JSONB" {
  488. t.Fatalf("column types = %v, want [JSONB]", res.ColumnTypes)
  489. }
  490. // ALTER TABLE backfills the canonical default into existing rows.
  491. execMust(t, e, "CREATE TABLE raw (id INTEGER PRIMARY KEY)")
  492. execMust(t, e, "INSERT INTO raw VALUES (1)")
  493. execMust(t, e, `ALTER TABLE raw ADD COLUMN doc JSONB DEFAULT '{"b":2,"a":1}'`)
  494. res = execMust(t, e, `SELECT doc FROM raw`)
  495. if res.Rows[0][0] != `{"a":1,"b":2}` {
  496. t.Fatalf("backfilled default = %v", res.Rows[0][0])
  497. }
  498. // UPDATE canonicalizes too.
  499. execMust(t, e, `UPDATE raw SET doc = '{"z": 1.50}'`)
  500. res = execMust(t, e, `SELECT doc FROM raw`)
  501. if res.Rows[0][0] != `{"z":1.5}` {
  502. t.Fatalf("updated doc = %v", res.Rows[0][0])
  503. }
  504. }
  505. func TestCanonicalJSONBNumber(t *testing.T) {
  506. cases := map[string]string{
  507. "0": "0",
  508. "-0": "0",
  509. "-0.0": "0",
  510. "1": "1",
  511. "1.0": "1",
  512. "1.50": "1.5",
  513. "1e2": "100",
  514. "1E+2": "100",
  515. "1e-2": "0.01",
  516. "-12.340": "-12.34",
  517. "123456789012345678901234567890": "123456789012345678901234567890",
  518. }
  519. for input, want := range cases {
  520. got, err := canonicalJSONBNumber(json.Number(input))
  521. if err != nil || got != want {
  522. t.Errorf("canonicalJSONBNumber(%q) = %q, %v; want %q", input, got, err, want)
  523. }
  524. }
  525. for _, input := range []string{"1e999999", "1e-999999"} {
  526. if _, err := canonicalJSONBNumber(json.Number(input)); err == nil {
  527. t.Errorf("canonicalJSONBNumber(%q) accepted an unbounded expansion", input)
  528. }
  529. }
  530. }
  531. // JSON operations must keep working from a derived table and a CTE, where the
  532. // column type travels through Result column metadata.
  533. func TestJSONBTypePropagation(t *testing.T) {
  534. _, schema, table := newTestDB(t)
  535. e := newExec(schema, table)
  536. execMust(t, e, "CREATE TABLE docs (id INTEGER PRIMARY KEY, doc JSONB)")
  537. execMust(t, e, `INSERT INTO docs VALUES (1, '{"a":1}')`)
  538. if got := jsonbValue(t, e, `SELECT doc -> 'a' FROM (SELECT doc FROM docs) AS d`); got != `1` {
  539. t.Fatalf("derived jsonb -> = %v", got)
  540. }
  541. if got := jsonbValue(t, e, `SELECT doc ? 'a' FROM (SELECT doc FROM docs) AS d`); got != true {
  542. t.Fatalf("derived jsonb ? = %v", got)
  543. }
  544. if got := jsonbValue(t, e, `WITH c AS (SELECT doc FROM docs) SELECT doc @> '{"a":1}' FROM c`); got != true {
  545. t.Fatalf("CTE jsonb @> = %v", got)
  546. }
  547. if got := jsonbValue(t, e, `SELECT COALESCE(doc, '{}'::jsonb) ->> 'a' FROM docs`); got != "1" {
  548. t.Fatalf("COALESCE jsonb ->> = %v", got)
  549. }
  550. // A jsonb column keeps its type when it is the left side of a join and when
  551. // it is referenced through an alias.
  552. res := execMust(t, e, `SELECT a.id FROM docs AS a JOIN docs AS b ON a.id = b.id WHERE a.doc @> '{"a":1}'`)
  553. if res.RowCount != 1 || res.Rows[0][0] != int64(1) {
  554. t.Fatalf("joined jsonb containment rows = %v", res.Rows)
  555. }
  556. if got := jsonbValue(t, e, `SELECT a.doc -> 'a' FROM docs AS a`); got != `1` {
  557. t.Fatalf("aliased jsonb -> = %v", got)
  558. }
  559. if got := jsonbValue(t, e, `SELECT MAX(jsonb_build_object('a', doc)) FROM docs`); got != `{"a":{"a":1}}` {
  560. t.Fatalf("jsonb build function inside an aggregate = %v", got)
  561. }
  562. }
  563. func TestJSONBRejectsUnboundedCanonicalExpansion(t *testing.T) {
  564. _, schema, table := newTestDB(t)
  565. e := newExec(schema, table)
  566. if _, err := execSQL(e, `SELECT '{"a":1e999999}'::jsonb`); !strings.Contains(err.Error(), "invalid input syntax") {
  567. t.Fatalf("unbounded number error = %v", err)
  568. }
  569. }