vitess.io/vitess@v0.16.2/go/vt/sqlparser/parse_next_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sqlparser 18 19 import ( 20 "bytes" 21 "io" 22 "strings" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 // TestParseNextValid concatenates all the valid SQL test cases and check it can read 30 // them as one long string. 31 func TestParseNextValid(t *testing.T) { 32 var sql bytes.Buffer 33 for _, tcase := range validSQL { 34 sql.WriteString(strings.TrimSuffix(tcase.input, ";")) 35 sql.WriteRune(';') 36 } 37 38 tokens := NewStringTokenizer(sql.String()) 39 for i, tcase := range validSQL { 40 input := tcase.input + ";" 41 want := tcase.output 42 if want == "" { 43 want = tcase.input 44 } 45 46 tree, err := ParseNext(tokens) 47 require.NoError(t, err) 48 49 if got := String(tree); got != want { 50 t.Fatalf("[%d] ParseNext(%q) = %q, want %q", i, input, got, want) 51 } 52 } 53 54 // Read once more and it should be EOF. 55 if tree, err := ParseNext(tokens); err != io.EOF { 56 t.Errorf("ParseNext(tokens) = (%q, %v) want io.EOF", String(tree), err) 57 } 58 } 59 60 func TestIgnoreSpecialComments(t *testing.T) { 61 input := `SELECT 1;/*! ALTER TABLE foo DISABLE KEYS */;SELECT 2;` 62 63 tokenizer := NewStringTokenizer(input) 64 tokenizer.SkipSpecialComments = true 65 one, err := ParseNextStrictDDL(tokenizer) 66 require.NoError(t, err) 67 require.Equal(t, "select 1 from dual", String(one)) 68 two, err := ParseNextStrictDDL(tokenizer) 69 require.NoError(t, err) 70 require.Equal(t, "select 2 from dual", String(two)) 71 } 72 73 // TestParseNextErrors tests all the error cases, and ensures a valid 74 // SQL statement can be passed afterwards. 75 func TestParseNextErrors(t *testing.T) { 76 for _, tcase := range invalidSQL { 77 if tcase.excludeMulti { 78 // Skip tests which leave unclosed strings, or comments. 79 continue 80 } 81 t.Run(tcase.input, func(t *testing.T) { 82 sql := tcase.input + "; select 1 from t" 83 tokens := NewStringTokenizer(sql) 84 85 // The first statement should be an error 86 _, err := ParseNextStrictDDL(tokens) 87 require.EqualError(t, err, tcase.output) 88 89 // The second should be valid 90 tree, err := ParseNextStrictDDL(tokens) 91 require.NoError(t, err) 92 93 want := "select 1 from t" 94 assert.Equal(t, want, String(tree)) 95 96 // Read once more and it should be EOF. 97 _, err = ParseNextStrictDDL(tokens) 98 require.Same(t, io.EOF, err) 99 }) 100 } 101 } 102 103 // TestParseNextEdgeCases tests various ParseNext edge cases. 104 func TestParseNextEdgeCases(t *testing.T) { 105 tests := []struct { 106 name string 107 input string 108 want []string 109 }{{ 110 name: "Trailing ;", 111 input: "select 1 from a; update a set b = 2;", 112 want: []string{"select 1 from a", "update a set b = 2"}, 113 }, { 114 name: "No trailing ;", 115 input: "select 1 from a; update a set b = 2", 116 want: []string{"select 1 from a", "update a set b = 2"}, 117 }, { 118 name: "Trailing whitespace", 119 input: "select 1 from a; update a set b = 2 ", 120 want: []string{"select 1 from a", "update a set b = 2"}, 121 }, { 122 name: "Trailing whitespace and ;", 123 input: "select 1 from a; update a set b = 2 ; ", 124 want: []string{"select 1 from a", "update a set b = 2"}, 125 }, { 126 name: "Handle SkipToEnd statements", 127 input: "set character set utf8; select 1 from a", 128 want: []string{"set charset 'utf8'", "select 1 from a"}, 129 }, { 130 name: "Semicolin inside a string", 131 input: "set character set ';'; select 1 from a", 132 want: []string{"set charset ';'", "select 1 from a"}, 133 }, { 134 name: "Partial DDL", 135 input: "create table a; select 1 from a", 136 want: []string{"create table a", "select 1 from a"}, 137 }, { 138 name: "Partial DDL", 139 input: "create table a ignore me this is garbage; select 1 from a", 140 want: []string{"create table a", "select 1 from a"}, 141 }} 142 143 for _, test := range tests { 144 tokens := NewStringTokenizer(test.input) 145 146 for i, want := range test.want { 147 tree, err := ParseNext(tokens) 148 require.NoError(t, err) 149 150 if got := String(tree); got != want { 151 t.Fatalf("[%d] ParseNext(%q) = %q, want %q", i, test.input, got, want) 152 } 153 } 154 155 // Read once more and it should be EOF. 156 if tree, err := ParseNext(tokens); err != io.EOF { 157 t.Errorf("ParseNext(%q) = (%q, %v) want io.EOF", test.input, String(tree), err) 158 } 159 160 // And again, once more should be EOF. 161 if tree, err := ParseNext(tokens); err != io.EOF { 162 t.Errorf("ParseNext(%q) = (%q, %v) want io.EOF", test.input, String(tree), err) 163 } 164 } 165 } 166 167 // TestParseNextEdgeCases tests various ParseNext edge cases. 168 func TestParseNextStrictNonStrict(t *testing.T) { 169 // This is one of the edge cases above. 170 input := "create table a ignore me this is garbage; select 1 from a" 171 want := []string{"create table a", "select 1 from a"} 172 173 // First go through as expected with non-strict DDL parsing. 174 tokens := NewStringTokenizer(input) 175 for i, want := range want { 176 tree, err := ParseNext(tokens) 177 if err != nil { 178 t.Fatalf("[%d] ParseNext(%q) err = %q, want nil", i, input, err) 179 } 180 if got := String(tree); got != want { 181 t.Fatalf("[%d] ParseNext(%q) = %q, want %q", i, input, got, want) 182 } 183 } 184 185 // Now try again with strict parsing and observe the expected error. 186 tokens = NewStringTokenizer(input) 187 _, err := ParseNextStrictDDL(tokens) 188 if err == nil || !strings.Contains(err.Error(), "ignore") { 189 t.Fatalf("ParseNext(%q) err = %q, want ignore", input, err) 190 } 191 tree, err := ParseNextStrictDDL(tokens) 192 if err != nil { 193 t.Fatalf("ParseNext(%q) err = %q, want nil", input, err) 194 } 195 if got := String(tree); got != want[1] { 196 t.Fatalf("ParseNext(%q) = %q, want %q", input, got, want) 197 } 198 }