jsonb_test.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package parser
  2. import (
  3. "errors"
  4. "testing"
  5. "github.com/danfragoso/pizzasql-next/pkg/lexer"
  6. "github.com/danfragoso/pizzasql-next/pkg/pgerror"
  7. )
  8. // The jsonb path/containment/existence operators parse at their intended
  9. // precedence and keep the JSON1 arrows working.
  10. func TestParseJSONBOperators(t *testing.T) {
  11. cases := map[string]lexer.TokenType{
  12. `SELECT doc -> 'a' FROM docs`: lexer.TokenArrow,
  13. `SELECT doc ->> 'a' FROM docs`: lexer.TokenArrowText,
  14. `SELECT doc #> '{a}' FROM docs`: lexer.TokenHashArrow,
  15. `SELECT doc #>> '{a}' FROM docs`: lexer.TokenHashArrowText,
  16. `SELECT doc #- '{a}' FROM docs`: lexer.TokenHashMinus,
  17. `SELECT doc @> '{}' FROM docs`: lexer.TokenAtArrow,
  18. `SELECT doc <@ '{}' FROM docs`: lexer.TokenLtAt,
  19. `SELECT doc ? 'a' FROM docs`: lexer.TokenQuestion,
  20. `SELECT doc ?| '{a}' FROM docs`: lexer.TokenQuestionPipe,
  21. `SELECT doc ?& '{a}' FROM docs`: lexer.TokenQuestionAmp,
  22. }
  23. for sql, wantOp := range cases {
  24. stmt := parse(t, sql)
  25. sel := stmt.(*SelectStmt)
  26. binary, ok := sel.Columns[0].Expr.(*BinaryExpr)
  27. if !ok || binary.Op != wantOp {
  28. t.Fatalf("%s: expression = %#v, want operator %v", sql, sel.Columns[0].Expr, wantOp)
  29. }
  30. }
  31. // Containment binds looser than extraction: doc -> 'a' @> '1'.
  32. sel := parse(t, `SELECT doc -> 'a' @> '1' FROM docs`).(*SelectStmt)
  33. binary := sel.Columns[0].Expr.(*BinaryExpr)
  34. if binary.Op != lexer.TokenAtArrow {
  35. t.Fatalf("containment operator = %v", binary.Op)
  36. }
  37. if left, ok := binary.Left.(*BinaryExpr); !ok || left.Op != lexer.TokenArrow {
  38. t.Fatalf("left operand = %#v, want ->", binary.Left)
  39. }
  40. // A jsonb cast is a normal cast target and keeps the extract/compare chain.
  41. sel = parse(t, `SELECT CAST(doc AS JSONB) -> 'a' = '1'::jsonb FROM docs`).(*SelectStmt)
  42. if eq, ok := sel.Columns[0].Expr.(*BinaryExpr); !ok || eq.Op != lexer.TokenEq {
  43. t.Fatalf("cast chain = %#v, want =", sel.Columns[0].Expr)
  44. }
  45. // jsonpath operators are recognized and rejected as unsupported.
  46. for _, sql := range []string{
  47. `SELECT doc @? '$.a' FROM docs`,
  48. `SELECT doc @@ '$.a' FROM docs`,
  49. } {
  50. l := lexer.New(sql)
  51. if _, err := New(l).Parse(); !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  52. t.Errorf("%s error = %v, want 0A000", sql, err)
  53. }
  54. }
  55. }
  56. // Non-btree index methods, including the GIN method used for jsonb in
  57. // PostgreSQL, are rejected instead of creating a btree index with different
  58. // behavior.
  59. func TestParseCreateIndexMethod(t *testing.T) {
  60. if _, err := parseAllowError(t, `CREATE INDEX i ON docs USING BTREE (doc)`); err != nil {
  61. t.Fatalf("BTREE index: %v", err)
  62. }
  63. for _, sql := range []string{
  64. `CREATE INDEX i ON docs USING GIN (doc)`,
  65. `CREATE INDEX i ON docs USING GIST (doc)`,
  66. } {
  67. if _, err := parseAllowError(t, sql); !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  68. t.Errorf("%s error = %v, want 0A000", sql, err)
  69. }
  70. }
  71. }
  72. // The jsonb set-returning functions are recognized as FROM table functions,
  73. // while unknown function-shaped table references stay unsupported.
  74. func TestParseJSONBTableFunctions(t *testing.T) {
  75. for _, sql := range []string{
  76. `SELECT * FROM jsonb_each('{"a":1}')`,
  77. `SELECT * FROM jsonb_each_text('{"a":1}') AS e`,
  78. `SELECT * FROM jsonb_array_elements('[1]')`,
  79. `SELECT * FROM jsonb_array_elements_text('[1]')`,
  80. `SELECT * FROM jsonb_object_keys('{"a":1}')`,
  81. } {
  82. stmt := parse(t, sql)
  83. sel := stmt.(*SelectStmt)
  84. if len(sel.From) != 1 || sel.From[0].Function == nil {
  85. t.Fatalf("%s: FROM = %#v, want a table function", sql, sel.From)
  86. }
  87. }
  88. l := lexer.New(`SELECT * FROM mystery('x')`)
  89. if _, err := New(l).Parse(); !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  90. t.Fatalf("unknown table function error = %v, want 0A000", err)
  91. }
  92. }
  93. // parseAllowError parses without failing the test, so a rejected statement can
  94. // be inspected.
  95. func parseAllowError(t *testing.T, sql string) (Statement, error) {
  96. t.Helper()
  97. l := lexer.New(sql)
  98. return New(l).Parse()
  99. }