arrays_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. `SELECT ARRAY[[1,2],[3,4]]`,
  131. } {
  132. t.Run(sql, func(t *testing.T) {
  133. _, err := New(lexer.New(sql)).Parse()
  134. if !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  135. t.Fatalf("error = %v, want ErrFeatureNotSupported", err)
  136. }
  137. })
  138. }
  139. }
  140. func TestUnnestInFromParses(t *testing.T) {
  141. stmt := parseOne(t, `SELECT * FROM unnest('{1,2}'::INTEGER[]) AS u`).(*SelectStmt)
  142. if len(stmt.From) != 1 {
  143. t.Fatalf("FROM entries = %d, want 1", len(stmt.From))
  144. }
  145. if stmt.From[0].Function == nil || stmt.From[0].Function.Name != "UNNEST" {
  146. t.Fatalf("FROM[0].Function = %+v, want UNNEST", stmt.From[0].Function)
  147. }
  148. if stmt.From[0].Alias != "u" {
  149. t.Fatalf("alias = %q, want u", stmt.From[0].Alias)
  150. }
  151. }
  152. func TestUnnestInJoinRejected(t *testing.T) {
  153. _, err := New(lexer.New(`SELECT * FROM t, unnest('{1,2}'::INTEGER[]) AS u`)).Parse()
  154. if !errors.Is(err, pgerror.ErrFeatureNotSupported) {
  155. t.Fatalf("error = %v, want ErrFeatureNotSupported", err)
  156. }
  157. }
  158. func TestAnyAsOrdinaryColumnStillParses(t *testing.T) {
  159. // ANY is only a predicate keyword when it follows a comparison operator. A
  160. // column literally named any must keep working.
  161. if _, err := New(lexer.New("SELECT any FROM t")).Parse(); err != nil {
  162. t.Fatalf("parse: %v", err)
  163. }
  164. }
  165. func TestBracketIdentifierStillParses(t *testing.T) {
  166. stmt := parseOne(t, "SELECT [bracket] FROM [table]").(*SelectStmt)
  167. ref, ok := stmt.Columns[0].Expr.(*ColumnRef)
  168. if !ok || ref.Column != "bracket" {
  169. t.Fatalf("projection = %#v, want bracket column", stmt.Columns[0].Expr)
  170. }
  171. if stmt.From[0].Name != "table" {
  172. t.Fatalf("FROM = %q, want table", stmt.From[0].Name)
  173. }
  174. }
  175. // A bracket directly after an expression starts an array subscript, so the
  176. // SQLite bracket-quoted alias in that position is read as a subscript. The
  177. // projection shapes below therefore parse as subscripts and fail later (the
  178. // base is not an array); a bracket alias on a FROM entry has no subscript
  179. // position and is a syntax error. Bracket identifiers keep working wherever a
  180. // name is expected (after a keyword or a comma).
  181. func TestBracketAliasShapesAfterExpression(t *testing.T) {
  182. for _, sql := range []string{
  183. `SELECT count(*) [cnt] FROM t`,
  184. `SELECT 1 [x]`,
  185. } {
  186. t.Run(sql, func(t *testing.T) {
  187. stmt := parseOne(t, sql).(*SelectStmt)
  188. if _, ok := stmt.Columns[0].Expr.(*SubscriptExpr); !ok {
  189. t.Fatalf("%q projection = %T, want *SubscriptExpr", sql, stmt.Columns[0].Expr)
  190. }
  191. })
  192. }
  193. if _, err := New(lexer.New(`SELECT * FROM t [a]`)).Parse(); err == nil {
  194. t.Fatal("bracket alias on a FROM entry parsed, want a syntax error")
  195. }
  196. // `[x]` after an expression is a subscript, not an alias.
  197. stmt := parseOne(t, `SELECT arr[1] FROM t`).(*SelectStmt)
  198. if _, ok := stmt.Columns[0].Expr.(*SubscriptExpr); !ok {
  199. t.Fatalf("projection = %T, want *SubscriptExpr", stmt.Columns[0].Expr)
  200. }
  201. // Name positions after a keyword or comma still quote with brackets.
  202. for _, sql := range []string{
  203. `SELECT [col], [col2] FROM [tbl]`,
  204. `INSERT INTO [tbl] ([col]) VALUES (1)`,
  205. } {
  206. if _, err := New(lexer.New(sql)).Parse(); err != nil {
  207. t.Errorf("%q should keep bracket identifiers: %v", sql, err)
  208. }
  209. }
  210. }