arrays_test.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. func parseOne(t *testing.T, sql string) Statement {
  9. t.Helper()
  10. stmt, err := New(lexer.New(sql)).Parse()
  11. if err != nil {
  12. t.Fatalf("parse %q: %v", sql, err)
  13. }
  14. return stmt
  15. }
  16. func TestArrayColumnTypesParse(t *testing.T) {
  17. stmt := parseOne(t, `CREATE TABLE t (
  18. a TEXT[],
  19. b VARCHAR(20)[],
  20. c INTEGER[],
  21. d BIGINT[],
  22. e SMALLINT[],
  23. f BOOLEAN[],
  24. g REAL[],
  25. h DOUBLE PRECISION[],
  26. i NUMERIC(10,2)[]
  27. )`).(*CreateTableStmt)
  28. want := []string{
  29. "TEXT[]", "VARCHAR[]", "INTEGER[]", "BIGINT[]", "SMALLINT[]",
  30. "BOOLEAN[]", "REAL[]", "DOUBLE[]", "NUMERIC[]",
  31. }
  32. if len(stmt.Columns) != len(want) {
  33. t.Fatalf("got %d columns, want %d", len(stmt.Columns), len(want))
  34. }
  35. for i, column := range stmt.Columns {
  36. if got := column.Type.SQLTypeName(); got != want[i] {
  37. t.Errorf("column %s type = %q, want %q", column.Name, got, want[i])
  38. }
  39. if !column.Type.Array {
  40. t.Errorf("column %s is not marked as an array", column.Name)
  41. }
  42. }
  43. }
  44. func TestAlterTableAddArrayColumnParses(t *testing.T) {
  45. stmt := parseOne(t, `ALTER TABLE t ADD COLUMN tags TEXT[]`).(*AlterTableStmt)
  46. action, ok := stmt.Action.(*AddColumnAction)
  47. if !ok {
  48. t.Fatalf("action = %T, want *AddColumnAction", stmt.Action)
  49. }
  50. if got := action.Column.Type.SQLTypeName(); got != "TEXT[]" {
  51. t.Fatalf("added column type = %q, want TEXT[]", got)
  52. }
  53. }
  54. func TestArrayConstructorAndSubscriptParse(t *testing.T) {
  55. stmt := parseOne(t, `SELECT ARRAY[1, 'a', NULL], arr[2], (ARRAY[1,2])[1] FROM t`).(*SelectStmt)
  56. if len(stmt.Columns) != 3 {
  57. t.Fatalf("got %d projections, want 3", len(stmt.Columns))
  58. }
  59. constructor, ok := stmt.Columns[0].Expr.(*ArrayExpr)
  60. if !ok {
  61. t.Fatalf("projection 0 = %T, want *ArrayExpr", stmt.Columns[0].Expr)
  62. }
  63. if len(constructor.Elements) != 3 {
  64. t.Fatalf("constructor elements = %d, want 3", len(constructor.Elements))
  65. }
  66. if _, ok := stmt.Columns[1].Expr.(*SubscriptExpr); !ok {
  67. t.Fatalf("projection 1 = %T, want *SubscriptExpr", stmt.Columns[1].Expr)
  68. }
  69. paren, ok := stmt.Columns[2].Expr.(*SubscriptExpr)
  70. if !ok {
  71. t.Fatalf("projection 2 = %T, want *SubscriptExpr", stmt.Columns[2].Expr)
  72. }
  73. if _, ok := paren.Expr.(*ParenExpr); !ok {
  74. t.Fatalf("subscript base = %T, want *ParenExpr", paren.Expr)
  75. }
  76. }
  77. func TestArrayCastForms(t *testing.T) {
  78. for _, sql := range []string{
  79. `SELECT '{1,2}'::INTEGER[]`,
  80. `SELECT CAST('{1,2}' AS INTEGER[])`,
  81. `SELECT '{1,2}'::DOUBLE PRECISION[]`,
  82. `SELECT '{a,b}'::VARCHAR(5)[]`,
  83. } {
  84. stmt := parseOne(t, sql).(*SelectStmt)
  85. cast, ok := stmt.Columns[0].Expr.(*CastExpr)
  86. if !ok {
  87. t.Fatalf("%q expression = %T, want *CastExpr", sql, stmt.Columns[0].Expr)
  88. }
  89. if !cast.Type.Array {
  90. t.Fatalf("%q cast type is not an array", sql)
  91. }
  92. }
  93. }
  94. func TestAnyAllPredicatesParse(t *testing.T) {
  95. for _, sql := range []string{
  96. `SELECT * FROM t WHERE id = ANY('{1,2,3}')`,
  97. `SELECT * FROM t WHERE id <> ALL('{1,2,3}')`,
  98. `SELECT * FROM t WHERE id = ANY(ARRAY[1,2])`,
  99. `SELECT * FROM t WHERE id >= ALL(ARRAY[1,2])`,
  100. `SELECT * FROM t WHERE NOT (id = ANY('{1,2}'))`,
  101. } {
  102. stmt := parseOne(t, sql).(*SelectStmt)
  103. if stmt.Where == nil {
  104. t.Fatalf("%q has no WHERE", sql)
  105. }
  106. }
  107. }
  108. func TestAnyAllRejections(t *testing.T) {
  109. for _, sql := range []string{
  110. `SELECT * FROM t WHERE id = ANY(SELECT id FROM t)`,
  111. `SELECT any(id) FROM t`,
  112. `SELECT * FROM t WHERE id = ANY()`,
  113. } {
  114. _, err := New(lexer.New(sql)).Parse()
  115. if !errors.Is(err, pgerror.ErrFeatureNotSupported) && !errors.Is(err, pgerror.ErrInvalidText) {
  116. // A malformed operand is a parse error; the unsupported forms must
  117. // report feature-not-supported.
  118. if err == nil {
  119. t.Errorf("%q parsed, want an error", sql)
  120. }
  121. }
  122. }
  123. }
  124. func TestMultidimensionalAndUnsupportedElementArraysRejected(t *testing.T) {
  125. for _, sql := range []string{
  126. `CREATE TABLE t (a INTEGER[][])`,
  127. `CREATE TABLE t (a BLOB[])`,
  128. `CREATE TABLE t (a DATE[])`,
  129. `SELECT ARRAY[ARRAY[1,2]]`,
  130. } {
  131. t.Run(sql, func(t *testing.T) {
  132. _, err := New(lexer.New(sql)).Parse()
  133. if !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  134. t.Fatalf("error = %v, want ErrFeatureNotSupported", err)
  135. }
  136. })
  137. }
  138. }
  139. func TestUnnestInFromParses(t *testing.T) {
  140. stmt := parseOne(t, `SELECT * FROM unnest('{1,2}'::INTEGER[]) AS u`).(*SelectStmt)
  141. if len(stmt.From) != 1 {
  142. t.Fatalf("FROM entries = %d, want 1", len(stmt.From))
  143. }
  144. if stmt.From[0].Function == nil || stmt.From[0].Function.Name != "UNNEST" {
  145. t.Fatalf("FROM[0].Function = %+v, want UNNEST", stmt.From[0].Function)
  146. }
  147. if stmt.From[0].Alias != "u" {
  148. t.Fatalf("alias = %q, want u", stmt.From[0].Alias)
  149. }
  150. }
  151. func TestUnnestInJoinRejected(t *testing.T) {
  152. _, err := New(lexer.New(`SELECT * FROM t, unnest('{1,2}'::INTEGER[]) AS u`)).Parse()
  153. if !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  154. t.Fatalf("error = %v, want ErrFeatureNotSupported", err)
  155. }
  156. }
  157. func TestAnyAsOrdinaryColumnStillParses(t *testing.T) {
  158. // ANY is only a predicate keyword when it follows a comparison operator. A
  159. // column literally named any must keep working.
  160. if _, err := New(lexer.New("SELECT any FROM t")).Parse(); err != nil {
  161. t.Fatalf("parse: %v", err)
  162. }
  163. }
  164. func TestBracketIdentifierStillParses(t *testing.T) {
  165. stmt := parseOne(t, "SELECT [bracket] FROM [table]").(*SelectStmt)
  166. ref, ok := stmt.Columns[0].Expr.(*ColumnRef)
  167. if !ok || ref.Column != "bracket" {
  168. t.Fatalf("projection = %#v, want bracket column", stmt.Columns[0].Expr)
  169. }
  170. if stmt.From[0].Name != "table" {
  171. t.Fatalf("FROM = %q, want table", stmt.From[0].Name)
  172. }
  173. }