json_operator_test.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package executor
  2. import (
  3. "strings"
  4. "testing"
  5. )
  6. func TestJSONArrowObjectAndArrayAccess(t *testing.T) {
  7. _, schema, table := newTestDB(t)
  8. e := newExec(schema, table)
  9. tests := []struct {
  10. name string
  11. sql string
  12. want interface{}
  13. }{
  14. {"object member as JSON text", `SELECT '{"a":1,"s":"x"}' -> 'a'`, "1"},
  15. {"string member keeps JSON quotes", `SELECT '{"a":1,"s":"x"}' -> 's'`, `"x"`},
  16. {"string member as SQL text", `SELECT '{"a":1,"s":"x"}' ->> 's'`, "x"},
  17. {"object member as JSON text", `SELECT '{"b":[10,20]}' -> 'b'`, "[10,20]"},
  18. {"array element as JSON text", `SELECT '[10,20,30]' -> 1`, "20"},
  19. {"array element as SQL number", `SELECT '[10,20,30]' ->> 1`, int64(20)},
  20. {"array element as JSON text", `SELECT '["a","b"]' -> 0`, `"a"`},
  21. {"negative array index", `SELECT '[10,20,30]' ->> -1`, int64(30)},
  22. {"nested chaining", `SELECT '{"a":{"b":[{"c":7}]}}' -> 'a' -> 'b' -> 0 ->> 'c'`, int64(7)},
  23. {"numeric key selects an array index", `SELECT '[10,20]' -> 1`, "20"},
  24. {"string key names an object member", `SELECT '{"1":"one"}' -> '1'`, `"one"`},
  25. {"numeric key on an object is missing", `SELECT '{"1":"one"}' -> 1`, nil},
  26. {"json null as JSON text", `SELECT '{"a":null}' -> 'a'`, "null"},
  27. {"json null as SQL NULL", `SELECT '{"a":null}' ->> 'a'`, nil},
  28. {"json true as JSON text", `SELECT '{"a":true}' -> 'a'`, "true"},
  29. {"json true as SQL integer", `SELECT '{"a":true}' ->> 'a'`, int64(1)},
  30. {"json false as SQL integer", `SELECT '{"a":false}' ->> 'a'`, int64(0)},
  31. {"path-style key", `SELECT '{"a":{"b":2}}' -> '$.a' ->> 'b'`, int64(2)},
  32. {"whole document path", `SELECT '{"a":1}' ->> '$'`, `{"a":1}`},
  33. {"missing object key", `SELECT '{"a":1}' -> 'b'`, nil},
  34. {"missing object key as SQL", `SELECT '{"a":1}' ->> 'b'`, nil},
  35. {"array index past the end", `SELECT '[1,2]' -> 5`, nil},
  36. {"NULL document", `SELECT NULL -> 'a'`, nil},
  37. {"NULL document as SQL", `SELECT NULL ->> 'a'`, nil},
  38. {"NULL key", `SELECT '{"a":1}' -> NULL`, nil},
  39. }
  40. for _, tt := range tests {
  41. t.Run(tt.name, func(t *testing.T) {
  42. res := execMust(t, e, tt.sql)
  43. got := res.Rows[0][0]
  44. if got != tt.want {
  45. t.Fatalf("%s = %v (%T), want %v (%T)", tt.sql, got, got, tt.want, tt.want)
  46. }
  47. })
  48. }
  49. }
  50. func TestJSONArrowMalformedInputIsAnError(t *testing.T) {
  51. _, schema, table := newTestDB(t)
  52. e := newExec(schema, table)
  53. for _, sql := range []string{
  54. `SELECT 'not json' -> 'a'`,
  55. `SELECT 'not json' ->> 'a'`,
  56. `SELECT '{"a":1}' -> '$.['`,
  57. } {
  58. if _, err := execSQL(e, sql); err == nil {
  59. t.Fatalf("%s: expected an error", sql)
  60. } else if !strings.Contains(err.Error(), "JSON") {
  61. t.Fatalf("%s: unexpected error: %v", sql, err)
  62. }
  63. }
  64. }
  65. func TestJSONArrowInWhereOrderByAndUpdate(t *testing.T) {
  66. _, schema, table := newTestDB(t)
  67. e := newExec(schema, table)
  68. execMust(t, e, "CREATE TABLE docs (id INTEGER PRIMARY KEY, doc TEXT, v TEXT)")
  69. execMust(t, e, `INSERT INTO docs VALUES (1, '{"kind":"a","n":1}', NULL)`)
  70. execMust(t, e, `INSERT INTO docs VALUES (2, '{"kind":"b","n":2}', NULL)`)
  71. execMust(t, e, `INSERT INTO docs VALUES (3, '{"kind":"c","n":3}', NULL)`)
  72. res := execMust(t, e, `SELECT id FROM docs WHERE doc ->> 'kind' = 'b'`)
  73. if res.RowCount != 1 || res.Rows[0][0] != int64(2) {
  74. t.Fatalf("WHERE doc ->> 'kind' = 'b' rows = %v, want id 2", res.Rows)
  75. }
  76. res = execMust(t, e, `SELECT doc ->> 'kind' AS kind FROM docs ORDER BY doc -> 'n' DESC`)
  77. if res.RowCount != 3 {
  78. t.Fatalf("ORDER BY rows = %d, want 3", res.RowCount)
  79. }
  80. if res.Rows[0][0] != "c" || res.Rows[1][0] != "b" || res.Rows[2][0] != "a" {
  81. t.Fatalf("ORDER BY doc -> 'n' DESC = %v, want c, b, a", res.Rows)
  82. }
  83. execMust(t, e, `UPDATE docs SET v = doc ->> 'kind'`)
  84. res = execMust(t, e, `SELECT id, v FROM docs ORDER BY id`)
  85. for i, want := range []string{"a", "b", "c"} {
  86. if res.Rows[i][1] != want {
  87. t.Fatalf("row %d v = %v, want %q", i, res.Rows[i][1], want)
  88. }
  89. }
  90. }