github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/reserved_words_test.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  //go:build reserved_words_test
    15  // +build reserved_words_test
    16  
    17  // This file ensures that the set of reserved keywords is the same as that of
    18  // MySQL. To run:
    19  //
    20  //  1. Set up a MySQL server listening at 127.0.0.1:3306 using root and no password
    21  //  2. Run this test with:
    22  //
    23  //		go test -tags reserved_words_test -run '^TestCompareReservedWordsWithMySQL$'
    24  
    25  package parser
    26  
    27  import (
    28  	// needed to connect to MySQL
    29  
    30  	dbsql "database/sql"
    31  	gio "io"
    32  	"os"
    33  	"testing"
    34  
    35  	_ "github.com/go-sql-driver/mysql"
    36  	"github.com/pingcap/tidb/parser/ast"
    37  	requires "github.com/stretchr/testify/require"
    38  )
    39  
    40  func TestCompareReservedWordsWithMySQL(t *testing.T) {
    41  	parserFilename := "parser.y"
    42  	parserFile, err := os.Open(parserFilename)
    43  	requires.NoError(t, err)
    44  	data, err := gio.ReadAll(parserFile)
    45  	requires.NoError(t, err)
    46  	content := string(data)
    47  
    48  	reservedKeywordStartMarker := "\t/* The following tokens belong to ReservedKeyword. Notice: make sure these tokens are contained in ReservedKeyword. */"
    49  	unreservedKeywordStartMarker := "\t/* The following tokens belong to UnReservedKeyword. Notice: make sure these tokens are contained in UnReservedKeyword. */"
    50  	notKeywordTokenStartMarker := "\t/* The following tokens belong to NotKeywordToken. Notice: make sure these tokens are contained in NotKeywordToken. */"
    51  	tidbKeywordStartMarker := "\t/* The following tokens belong to TiDBKeyword. Notice: make sure these tokens are contained in TiDBKeyword. */"
    52  	identTokenEndMarker := "%token\t<item>"
    53  
    54  	reservedKeywords := extractKeywords(content, reservedKeywordStartMarker, unreservedKeywordStartMarker)
    55  	unreservedKeywords := extractKeywords(content, unreservedKeywordStartMarker, notKeywordTokenStartMarker)
    56  	notKeywordTokens := extractKeywords(content, notKeywordTokenStartMarker, tidbKeywordStartMarker)
    57  	tidbKeywords := extractKeywords(content, tidbKeywordStartMarker, identTokenEndMarker)
    58  
    59  	p := New()
    60  	db, err := dbsql.Open("mysql", "root@tcp(127.0.0.1:3306)/")
    61  	requires.NoError(t, err)
    62  	defer func() {
    63  		requires.NoError(t, db.Close())
    64  	}()
    65  
    66  	for _, kw := range reservedKeywords {
    67  		switch kw {
    68  		case "CURRENT_ROLE", "INTERSECT", "STATS_EXTENDED", "TABLESAMPLE":
    69  			// special case: we do reserve these words but MySQL didn't,
    70  			// and unreservering it causes legit parser conflict.
    71  			continue
    72  		}
    73  
    74  		query := "do (select 1 as " + kw + ")"
    75  		errRegexp := ".*" + kw + ".*"
    76  
    77  		var err error
    78  
    79  		if _, ok := windowFuncTokenMap[kw]; !ok {
    80  			// for some reason the query does parse even then the keyword is reserved in TiDB.
    81  			_, _, err = p.Parse(query, "", "")
    82  			requires.Error(t, err)
    83  			requires.Regexp(t, errRegexp, err.Error())
    84  		}
    85  		_, err = db.Exec(query)
    86  		requires.Error(t, err)
    87  		requires.Regexp(t, errRegexp, err.Error(), "MySQL suggests that '%s' should *not* be reserved!", kw)
    88  	}
    89  
    90  	for _, kws := range [][]string{unreservedKeywords, notKeywordTokens, tidbKeywords} {
    91  		for _, kw := range kws {
    92  			switch kw {
    93  			case "FUNCTION", // reserved in 8.0.1
    94  				"PURGE", "SYSTEM", "SEPARATOR": // ?
    95  				continue
    96  			}
    97  
    98  			query := "do (select 1 as " + kw + ")"
    99  
   100  			stmts, _, err := p.Parse(query, "", "")
   101  			requires.NoError(t, err)
   102  			requires.Len(t, stmts, 1)
   103  			requires.IsType(t, &ast.DoStmt{}, stmts[0])
   104  
   105  			_, err = db.Exec(query)
   106  			println(query)
   107  			requires.NoErrorf(t, err, "MySQL suggests that '%s' should be reserved!", kw)
   108  		}
   109  	}
   110  }