github.com/supabase/cli@v1.168.1/internal/utils/parser/state_test.go (about)

     1  package parser
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  )
    10  
    11  func checkSplit(t *testing.T, sql []string) {
    12  	input := strings.Join(sql, "")
    13  	output, err := Split(strings.NewReader(input))
    14  	require.NoError(t, err)
    15  	assert.ElementsMatch(t, sql, output)
    16  }
    17  
    18  func TestLineComment(t *testing.T) {
    19  	t.Run("after separator", func(t *testing.T) {
    20  		sql := []string{"END;", "-- comment"}
    21  		checkSplit(t, sql)
    22  	})
    23  
    24  	t.Run("before separator", func(t *testing.T) {
    25  		sql := []string{"SELECT --; 1"}
    26  		checkSplit(t, sql)
    27  	})
    28  
    29  	t.Run("not started", func(t *testing.T) {
    30  		sql := []string{"- ;", "END"}
    31  		checkSplit(t, sql)
    32  	})
    33  
    34  	t.Run("between lines", func(t *testing.T) {
    35  		sql := []string{"-- /* \n;", " */ END"}
    36  		checkSplit(t, sql)
    37  	})
    38  }
    39  
    40  func TestBlockComment(t *testing.T) {
    41  	t.Run("contains separator", func(t *testing.T) {
    42  		sql := []string{"SELECT /* ; */ 1;"}
    43  		checkSplit(t, sql)
    44  	})
    45  
    46  	t.Run("nested block", func(t *testing.T) {
    47  		sql := []string{"SELECT /*; /*;*/ ;*/ 1"}
    48  		checkSplit(t, sql)
    49  	})
    50  
    51  	t.Run("not started", func(t *testing.T) {
    52  		sql := []string{"/ * ;", " */ END"}
    53  		checkSplit(t, sql)
    54  	})
    55  }
    56  
    57  func TestSeparator(t *testing.T) {
    58  	t.Run("no spaces", func(t *testing.T) {
    59  		sql := []string{";", "END;", ";"}
    60  		checkSplit(t, sql)
    61  	})
    62  
    63  	t.Run("between spaces", func(t *testing.T) {
    64  		sql := []string{"BEGIN   ;", "  END"}
    65  		checkSplit(t, sql)
    66  	})
    67  
    68  	t.Run("backslash escaped", func(t *testing.T) {
    69  		sql := []string{"\\;;", "\\;"}
    70  		checkSplit(t, sql)
    71  	})
    72  }
    73  
    74  func TestDollarQuote(t *testing.T) {
    75  	t.Run("named tag", func(t *testing.T) {
    76  		sql := []string{"$tag$ any ; string$tag$"}
    77  		checkSplit(t, sql)
    78  	})
    79  
    80  	t.Run("anonymous tag", func(t *testing.T) {
    81  		sql := []string{"$$\"Dane's horse\"$$"}
    82  		checkSplit(t, sql)
    83  	})
    84  
    85  	t.Run("not started", func(t *testing.T) {
    86  		sql := []string{"SELECT \"$\";", " $$"}
    87  		checkSplit(t, sql)
    88  	})
    89  }
    90  
    91  func TestSingleQuote(t *testing.T) {
    92  	t.Run("escapes separator", func(t *testing.T) {
    93  		sql := []string{"SELECT ';' 1"}
    94  		checkSplit(t, sql)
    95  	})
    96  
    97  	t.Run("preserves single quote", func(t *testing.T) {
    98  		sql := []string{"SELECT ';'';' 1"}
    99  		checkSplit(t, sql)
   100  	})
   101  
   102  	t.Run("literal backslash", func(t *testing.T) {
   103  		sql := []string{"SELECT '\\';", " 1'"}
   104  		checkSplit(t, sql)
   105  	})
   106  }
   107  
   108  func TestDoubleQuote(t *testing.T) {
   109  	t.Run("escapes separator", func(t *testing.T) {
   110  		sql := []string{`CREATE POLICY "cats;dogs" on cats_dogs;`, " END"}
   111  		checkSplit(t, sql)
   112  	})
   113  
   114  	t.Run("preserves single quote", func(t *testing.T) {
   115  		sql := []string{`CREATE POLICY "cat's and dog's" on cats_dogs;`, " END"}
   116  		checkSplit(t, sql)
   117  	})
   118  
   119  	t.Run("preserves double quote", func(t *testing.T) {
   120  		sql := []string{`CREATE POLICY "pet""name" on pets;`, " END"}
   121  		checkSplit(t, sql)
   122  	})
   123  }
   124  
   125  func TestParentheses(t *testing.T) {
   126  	t.Run("preserves parentheses", func(t *testing.T) {
   127  		sql := []string{
   128  			`CREATE RULE notify_me AS ON UPDATE TO mytable DO (NOTIFY mytable; SELECT "1");`,
   129  			`SELECT 1`,
   130  		}
   131  		checkSplit(t, sql)
   132  	})
   133  
   134  	t.Run("ignores literals", func(t *testing.T) {
   135  		sql := []string{`CREATE RULE notify_me AS ON UPDATE TO mytable DO (-- )
   136  SELECT ')';
   137  -- )
   138  )
   139  `}
   140  		checkSplit(t, sql)
   141  	})
   142  }
   143  
   144  func TestBeginAtomic(t *testing.T) {
   145  	t.Run("inline body", func(t *testing.T) {
   146  		sql := []string{`CREATE FUNCTION add(a integer, b integer) RETURNS integer
   147  LANGUAGE SQL
   148  IMMUTABLE STRICT PARALLEL SAFE
   149  RETURNS NULL ON NULL INPUT
   150  BEGIN ATOMIC
   151  	SELECT 'add';
   152  	SELECT a + b;
   153  END;`}
   154  		checkSplit(t, sql)
   155  	})
   156  
   157  	t.Run("case insenstive", func(t *testing.T) {
   158  		sql := []string{"begin atomic; select 'end'; end"}
   159  		checkSplit(t, sql)
   160  	})
   161  
   162  	t.Run("ignores literals", func(t *testing.T) {
   163  		sql := []string{`CREATE FUNCTION test() BEGIN
   164  ATOMIC-- END;
   165  -- END;
   166  END
   167  ;`}
   168  		checkSplit(t, sql)
   169  	})
   170  }