github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/parsers/dialect/mysql/scanner_test.go (about)

     1  // Copyright 2021 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mysql
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect"
    22  )
    23  
    24  func TestLiteralID(t *testing.T) {
    25  	testcases := []struct {
    26  		in  string
    27  		id  int
    28  		out string
    29  	}{{
    30  		in:  "`aa`",
    31  		id:  QUOTE_ID,
    32  		out: "aa",
    33  	}, {
    34  		in:  "```a```",
    35  		id:  QUOTE_ID,
    36  		out: "`a`",
    37  	}, {
    38  		in:  "`a``b`",
    39  		id:  QUOTE_ID,
    40  		out: "a`b",
    41  	}, {
    42  		in:  "`a``b`c",
    43  		id:  QUOTE_ID,
    44  		out: "a`b",
    45  	}, {
    46  		in:  "`a``b",
    47  		id:  LEX_ERROR,
    48  		out: "a`b",
    49  	}, {
    50  		in:  "`a``b``",
    51  		id:  LEX_ERROR,
    52  		out: "a`b`",
    53  	}, {
    54  		in:  "``",
    55  		id:  LEX_ERROR,
    56  		out: "",
    57  	}, {
    58  		in:  "```a``b``",
    59  		id:  LEX_ERROR,
    60  		out: "`a`b`",
    61  	}, {
    62  		in:  "```",
    63  		id:  LEX_ERROR,
    64  		out: "`",
    65  	}}
    66  
    67  	for _, tcase := range testcases {
    68  		s := NewScanner(dialect.MYSQL, tcase.in)
    69  		id, out := s.Scan()
    70  		if tcase.id != id || string(out) != tcase.out {
    71  			t.Errorf("Scan(%s): %d, %s, want %d, %s", tcase.in, id, out, tcase.id, tcase.out)
    72  		}
    73  	}
    74  }
    75  
    76  func tokenName(id int) string {
    77  	if id == STRING {
    78  		return "STRING"
    79  	} else if id == HEXNUM {
    80  		return "HEXNUM"
    81  	} else if id == BIT_LITERAL {
    82  		return "BIT_LITERAL"
    83  	} else if id == LEX_ERROR {
    84  		return "LEX_ERROR"
    85  	}
    86  	return fmt.Sprintf("%d", id)
    87  }
    88  
    89  func TestString(t *testing.T) {
    90  	testcases := []struct {
    91  		in   string
    92  		id   int
    93  		want string
    94  	}{{
    95  		in:   "''",
    96  		id:   STRING,
    97  		want: "",
    98  	}, {
    99  		in:   "''''",
   100  		id:   STRING,
   101  		want: "'",
   102  	}, {
   103  		in:   "'hello'",
   104  		id:   STRING,
   105  		want: "hello",
   106  	}, {
   107  		in:   "'\\n'",
   108  		id:   STRING,
   109  		want: "\n",
   110  	}, {
   111  		in:   "'\\nhello\\n'",
   112  		id:   STRING,
   113  		want: "\nhello\n",
   114  	}, {
   115  		in:   "'a''b'",
   116  		id:   STRING,
   117  		want: "a'b",
   118  	}, {
   119  		in:   "'a\\'b'",
   120  		id:   STRING,
   121  		want: "a'b",
   122  	}, {
   123  		in:   "'\\'",
   124  		id:   LEX_ERROR,
   125  		want: "'",
   126  	}, {
   127  		in:   "'",
   128  		id:   LEX_ERROR,
   129  		want: "",
   130  	}, {
   131  		in:   "'hello\\'",
   132  		id:   LEX_ERROR,
   133  		want: "hello'",
   134  	}, {
   135  		in:   "'hello",
   136  		id:   LEX_ERROR,
   137  		want: "hello",
   138  	}, {
   139  		in:   "'hello\\",
   140  		id:   LEX_ERROR,
   141  		want: "hello",
   142  	}, {
   143  		in:   "'C:\\Program Files(x86)'",
   144  		id:   STRING,
   145  		want: "C:Program Files(x86)",
   146  	}, {
   147  		in:   "'C:\\\\Program Files(x86)'",
   148  		id:   STRING,
   149  		want: "C:\\Program Files(x86)",
   150  	}}
   151  
   152  	for _, tcase := range testcases {
   153  		id, got := NewScanner(dialect.MYSQL, tcase.in).Scan()
   154  		if tcase.id != id || string(got) != tcase.want {
   155  			t.Errorf("Scan(%q) = (%s, %q), want (%s, %q)", tcase.in, tokenName(id), got, tokenName(tcase.id), tcase.want)
   156  		}
   157  	}
   158  }
   159  
   160  func TestBuffer(t *testing.T) {
   161  	testcases := []struct {
   162  		in   string
   163  		id   int
   164  		want string
   165  	}{{
   166  		in:   "'webapp'@'localhost'",
   167  		id:   STRING,
   168  		want: "webapp",
   169  	}}
   170  
   171  	for _, tcase := range testcases {
   172  		id, got := NewScanner(dialect.MYSQL, tcase.in).Scan()
   173  		if tcase.id != id || string(got) != tcase.want {
   174  			t.Errorf("Scan(%q) = (%s, %q), want (%s, %q)", tcase.in, tokenName(id), got, tokenName(tcase.id), tcase.want)
   175  		}
   176  	}
   177  }
   178  
   179  func TestComment(t *testing.T) {
   180  	testcases := []struct {
   181  		name  string
   182  		in    string
   183  		id    int
   184  		want  string
   185  		want2 string
   186  	}{
   187  		{
   188  			name: "1",
   189  			in:   "abc /* abc */ abc",
   190  			id:   COMMENT,
   191  			want: "/* abc */",
   192  		},
   193  		{
   194  			name: "1",
   195  			in:   "abc /** abc **/ abc",
   196  			id:   COMMENT,
   197  			want: "/** abc **/",
   198  		},
   199  		{
   200  			name: "*/ after comment",
   201  			in:   "abc /** abc **/*/ abc",
   202  			id:   COMMENT,
   203  			want: "/** abc **/",
   204  		},
   205  		{
   206  			name: "//comment",
   207  			in:   "abc //** abc **/*/ abc",
   208  			id:   COMMENT,
   209  			want: "//** abc **/*/ abc",
   210  		},
   211  		{
   212  			name: "// in block comment",
   213  			in:   "abc /** //abc **/*/ abc",
   214  			id:   COMMENT,
   215  			want: "/** //abc **/",
   216  		},
   217  		{
   218  			name: "embedded block comment",
   219  			in:   "abc /** /* /abc **/*/ abc",
   220  			id:   COMMENT,
   221  			want: "/** /* /abc **/",
   222  		},
   223  		{
   224  			name: "no comment",
   225  			in:   "abc /a/a abc",
   226  			id:   eofChar,
   227  			want: "",
   228  		},
   229  		{
   230  			name: "no comment",
   231  			in:   "abc /a/a abc/",
   232  			id:   eofChar,
   233  			want: "",
   234  		},
   235  		{
   236  			name: "nothing",
   237  			in:   "",
   238  			id:   eofChar,
   239  			want: "",
   240  		},
   241  		{
   242  			name: "newline",
   243  			in:   "\n",
   244  			id:   eofChar,
   245  			want: "",
   246  		},
   247  		{
   248  			name: "newline",
   249  			in:   "dfa fda \r\n",
   250  			id:   eofChar,
   251  			want: "",
   252  		},
   253  		{
   254  			name: "no newline",
   255  			in:   "dfa fda ",
   256  			id:   eofChar,
   257  			want: "",
   258  		},
   259  		{
   260  			name: "incomplete line comment",
   261  			in:   " / ",
   262  			id:   eofChar,
   263  			want: "",
   264  		},
   265  		{
   266  			name: "incomplete block comment",
   267  			in:   " /* ",
   268  			id:   LEX_ERROR,
   269  			want: "",
   270  		},
   271  		{
   272  			name: "incomplete block comment",
   273  			in:   " /* * ",
   274  			id:   LEX_ERROR,
   275  			want: "",
   276  		},
   277  		{
   278  			name: "incomplete block comment",
   279  			in:   " /* * /",
   280  			id:   LEX_ERROR,
   281  			want: "",
   282  		},
   283  		{
   284  			name: "incomplete block comment",
   285  			in:   " / * * /",
   286  			id:   eofChar,
   287  			want: "",
   288  		},
   289  		{
   290  			name: "block comment",
   291  			in:   " /* * /  /* */ ",
   292  			id:   COMMENT,
   293  			want: "/* * /  /* */",
   294  		},
   295  		{
   296  			name: "block comment",
   297  			in:   " /* * /  /* */ ",
   298  			id:   COMMENT,
   299  			want: "/* * /  /* */",
   300  		},
   301  		{
   302  			name:  "two block comment",
   303  			in:    " /* * /  /* */ /* abc */ ",
   304  			id:    COMMENT,
   305  			want:  "/* * /  /* */",
   306  			want2: "/* abc */",
   307  		},
   308  		{
   309  			name:  "two block comment",
   310  			in:    " /* * /  /* */ // ",
   311  			id:    COMMENT,
   312  			want:  "/* * /  /* */",
   313  			want2: "// ",
   314  		},
   315  	}
   316  
   317  	for _, tcase := range testcases {
   318  		scan := NewScanner(dialect.MYSQL, tcase.in)
   319  		id, got := scan.ScanComment()
   320  		if tcase.id != id || id != LEX_ERROR && string(got) != tcase.want {
   321  			t.Errorf("ScanComment(%q) = (%s, %q), want (%s, %q)", tcase.in, tokenName(id), got, tokenName(tcase.id), tcase.want)
   322  		}
   323  
   324  		if tcase.want2 != "" {
   325  			id, got = scan.ScanComment()
   326  			if tcase.id != id || id != LEX_ERROR && string(got) != tcase.want2 {
   327  				t.Errorf("ScanComment(%q) = (%s, %q), want (%s, %q)", tcase.in, tokenName(id), got, tokenName(tcase.id), tcase.want2)
   328  			}
   329  		}
   330  	}
   331  }
   332  
   333  func TestBitValueLiteral(t *testing.T) {
   334  	testcases := []struct {
   335  		in   string
   336  		id   int
   337  		want string
   338  	}{{
   339  		in:   "b'00011011'",
   340  		id:   BIT_LITERAL,
   341  		want: "0b00011011",
   342  	}, {
   343  		in:   "0b00011011",
   344  		id:   BIT_LITERAL,
   345  		want: "0b00011011",
   346  	}, {
   347  		in:   "0b",
   348  		id:   ID,
   349  		want: "0b",
   350  	}, {
   351  		in:   "0b0a1fg",
   352  		id:   ID,
   353  		want: "0b0a1fg",
   354  	}}
   355  
   356  	for _, tcase := range testcases {
   357  		id, got := NewScanner(dialect.MYSQL, tcase.in).Scan()
   358  		if tcase.id != id || got != tcase.want {
   359  			t.Errorf("Scan(%q) = (%s, %q), want (%s, %q)", tcase.in, tokenName(id), got, tokenName(tcase.id), tcase.want)
   360  		}
   361  	}
   362  }
   363  
   364  func TestHexadecimalLiteral(t *testing.T) {
   365  	testcases := []struct {
   366  		in   string
   367  		id   int
   368  		want string
   369  	}{{
   370  		in:   "x'616263'",
   371  		id:   HEXNUM,
   372  		want: "0x616263",
   373  	}, {
   374  		in:   "0x616263",
   375  		id:   HEXNUM,
   376  		want: "0x616263",
   377  	}, {
   378  		in:   "0x",
   379  		id:   ID,
   380  		want: "0x",
   381  	}, {
   382  		in:   "0X0a1fg",
   383  		id:   ID,
   384  		want: "0x0a1fg",
   385  	}}
   386  
   387  	for _, tcase := range testcases {
   388  		id, got := NewScanner(dialect.MYSQL, tcase.in).Scan()
   389  		if tcase.id != id || got != tcase.want {
   390  			t.Errorf("Scan(%q) = (%s, %q), want (%s, %q)", tcase.in, tokenName(id), got, tokenName(tcase.id), tcase.want)
   391  		}
   392  	}
   393  }