github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/hintparser_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  package parser_test
    15  
    16  import (
    17  	"testing"
    18  
    19  	"github.com/pingcap/tidb/parser"
    20  	"github.com/pingcap/tidb/parser/ast"
    21  	"github.com/pingcap/tidb/parser/model"
    22  	"github.com/pingcap/tidb/parser/mysql"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func TestParseHint(t *testing.T) {
    27  	testCases := []struct {
    28  		input  string
    29  		mode   mysql.SQLMode
    30  		output []*ast.TableOptimizerHint
    31  		errs   []string
    32  	}{
    33  		{
    34  			input: "",
    35  			errs:  []string{`Optimizer hint syntax error at line 1 `},
    36  		},
    37  		{
    38  			input: "MEMORY_QUOTA(8 MB) MEMORY_QUOTA(6 GB)",
    39  			output: []*ast.TableOptimizerHint{
    40  				{
    41  					HintName: model.NewCIStr("MEMORY_QUOTA"),
    42  					HintData: int64(8 * 1024 * 1024),
    43  				},
    44  				{
    45  					HintName: model.NewCIStr("MEMORY_QUOTA"),
    46  					HintData: int64(6 * 1024 * 1024 * 1024),
    47  				},
    48  			},
    49  		},
    50  		{
    51  			input: "QB_NAME(qb1) QB_NAME(`qb2`), QB_NAME(TRUE) QB_NAME(\"ANSI quoted\") QB_NAME(_utf8), QB_NAME(0b10) QB_NAME(0x1a)",
    52  			mode:  mysql.ModeANSIQuotes,
    53  			output: []*ast.TableOptimizerHint{
    54  				{
    55  					HintName: model.NewCIStr("QB_NAME"),
    56  					QBName:   model.NewCIStr("qb1"),
    57  				},
    58  				{
    59  					HintName: model.NewCIStr("QB_NAME"),
    60  					QBName:   model.NewCIStr("qb2"),
    61  				},
    62  				{
    63  					HintName: model.NewCIStr("QB_NAME"),
    64  					QBName:   model.NewCIStr("TRUE"),
    65  				},
    66  				{
    67  					HintName: model.NewCIStr("QB_NAME"),
    68  					QBName:   model.NewCIStr("ANSI quoted"),
    69  				},
    70  				{
    71  					HintName: model.NewCIStr("QB_NAME"),
    72  					QBName:   model.NewCIStr("_utf8"),
    73  				},
    74  				{
    75  					HintName: model.NewCIStr("QB_NAME"),
    76  					QBName:   model.NewCIStr("0b10"),
    77  				},
    78  				{
    79  					HintName: model.NewCIStr("QB_NAME"),
    80  					QBName:   model.NewCIStr("0x1a"),
    81  				},
    82  			},
    83  		},
    84  		{
    85  			input: "QB_NAME(1)",
    86  			errs:  []string{`Optimizer hint syntax error at line 1 `},
    87  		},
    88  		{
    89  			input: "QB_NAME('string literal')",
    90  			errs:  []string{`Optimizer hint syntax error at line 1 `},
    91  		},
    92  		{
    93  			input: "QB_NAME(many identifiers)",
    94  			errs:  []string{`Optimizer hint syntax error at line 1 `},
    95  		},
    96  		{
    97  			input: "QB_NAME(@qb1)",
    98  			errs:  []string{`Optimizer hint syntax error at line 1 `},
    99  		},
   100  		{
   101  			input: "QB_NAME(b'10')",
   102  			errs: []string{
   103  				`Cannot use bit-value literal`,
   104  				`Optimizer hint syntax error at line 1 `,
   105  			},
   106  		},
   107  		{
   108  			input: "QB_NAME(x'1a')",
   109  			errs: []string{
   110  				`Cannot use hexadecimal literal`,
   111  				`Optimizer hint syntax error at line 1 `,
   112  			},
   113  		},
   114  		{
   115  			input: "JOIN_FIXED_ORDER() BKA()",
   116  			errs: []string{
   117  				`Optimizer hint JOIN_FIXED_ORDER is not supported`,
   118  				`Optimizer hint BKA is not supported`,
   119  			},
   120  		},
   121  		{
   122  			input: "HASH_JOIN() TIDB_HJ(@qb1) INL_JOIN(x, `y y`.z) MERGE_JOIN(w@`First QB`)",
   123  			output: []*ast.TableOptimizerHint{
   124  				{
   125  					HintName: model.NewCIStr("HASH_JOIN"),
   126  				},
   127  				{
   128  					HintName: model.NewCIStr("TIDB_HJ"),
   129  					QBName:   model.NewCIStr("qb1"),
   130  				},
   131  				{
   132  					HintName: model.NewCIStr("INL_JOIN"),
   133  					Tables: []ast.HintTable{
   134  						{TableName: model.NewCIStr("x")},
   135  						{DBName: model.NewCIStr("y y"), TableName: model.NewCIStr("z")},
   136  					},
   137  				},
   138  				{
   139  					HintName: model.NewCIStr("MERGE_JOIN"),
   140  					Tables: []ast.HintTable{
   141  						{TableName: model.NewCIStr("w"), QBName: model.NewCIStr("First QB")},
   142  					},
   143  				},
   144  			},
   145  		},
   146  		{
   147  			input: "USE_INDEX_MERGE(@qb1 tbl1 x, y, z) IGNORE_INDEX(tbl2@qb2) USE_INDEX(tbl3 PRIMARY) FORCE_INDEX(tbl4@qb3 c1)",
   148  			output: []*ast.TableOptimizerHint{
   149  				{
   150  					HintName: model.NewCIStr("USE_INDEX_MERGE"),
   151  					Tables:   []ast.HintTable{{TableName: model.NewCIStr("tbl1")}},
   152  					QBName:   model.NewCIStr("qb1"),
   153  					Indexes:  []model.CIStr{model.NewCIStr("x"), model.NewCIStr("y"), model.NewCIStr("z")},
   154  				},
   155  				{
   156  					HintName: model.NewCIStr("IGNORE_INDEX"),
   157  					Tables:   []ast.HintTable{{TableName: model.NewCIStr("tbl2"), QBName: model.NewCIStr("qb2")}},
   158  				},
   159  				{
   160  					HintName: model.NewCIStr("USE_INDEX"),
   161  					Tables:   []ast.HintTable{{TableName: model.NewCIStr("tbl3")}},
   162  					Indexes:  []model.CIStr{model.NewCIStr("PRIMARY")},
   163  				},
   164  				{
   165  					HintName: model.NewCIStr("FORCE_INDEX"),
   166  					Tables:   []ast.HintTable{{TableName: model.NewCIStr("tbl4"), QBName: model.NewCIStr("qb3")}},
   167  					Indexes:  []model.CIStr{model.NewCIStr("c1")},
   168  				},
   169  			},
   170  		},
   171  		{
   172  			input: "USE_INDEX(@qb1 tbl1 partition(p0) x) USE_INDEX_MERGE(@qb2 tbl2@qb2 partition(p0, p1) x, y, z)",
   173  			output: []*ast.TableOptimizerHint{
   174  				{
   175  					HintName: model.NewCIStr("USE_INDEX"),
   176  					Tables: []ast.HintTable{{
   177  						TableName:     model.NewCIStr("tbl1"),
   178  						PartitionList: []model.CIStr{model.NewCIStr("p0")},
   179  					}},
   180  					QBName:  model.NewCIStr("qb1"),
   181  					Indexes: []model.CIStr{model.NewCIStr("x")},
   182  				},
   183  				{
   184  					HintName: model.NewCIStr("USE_INDEX_MERGE"),
   185  					Tables: []ast.HintTable{{
   186  						TableName:     model.NewCIStr("tbl2"),
   187  						QBName:        model.NewCIStr("qb2"),
   188  						PartitionList: []model.CIStr{model.NewCIStr("p0"), model.NewCIStr("p1")},
   189  					}},
   190  					QBName:  model.NewCIStr("qb2"),
   191  					Indexes: []model.CIStr{model.NewCIStr("x"), model.NewCIStr("y"), model.NewCIStr("z")},
   192  				},
   193  			},
   194  		},
   195  		{
   196  			input: `SET_VAR(sbs = 16M) SET_VAR(fkc=OFF) SET_VAR(os="mcb=off") set_var(abc=1) set_var(os2='mcb2=off')`,
   197  			output: []*ast.TableOptimizerHint{
   198  				{
   199  					HintName: model.NewCIStr("SET_VAR"),
   200  					HintData: ast.HintSetVar{
   201  						VarName: "sbs",
   202  						Value:   "16M",
   203  					},
   204  				},
   205  				{
   206  					HintName: model.NewCIStr("SET_VAR"),
   207  					HintData: ast.HintSetVar{
   208  						VarName: "fkc",
   209  						Value:   "OFF",
   210  					},
   211  				},
   212  				{
   213  					HintName: model.NewCIStr("SET_VAR"),
   214  					HintData: ast.HintSetVar{
   215  						VarName: "os",
   216  						Value:   "mcb=off",
   217  					},
   218  				},
   219  				{
   220  					HintName: model.NewCIStr("set_var"),
   221  					HintData: ast.HintSetVar{
   222  						VarName: "abc",
   223  						Value:   "1",
   224  					},
   225  				},
   226  				{
   227  					HintName: model.NewCIStr("set_var"),
   228  					HintData: ast.HintSetVar{
   229  						VarName: "os2",
   230  						Value:   "mcb2=off",
   231  					},
   232  				},
   233  			},
   234  		},
   235  		{
   236  			input: "USE_TOJA(TRUE) IGNORE_PLAN_CACHE() USE_CASCADES(TRUE) QUERY_TYPE(@qb1 OLAP) QUERY_TYPE(OLTP) NO_INDEX_MERGE() RESOURCE_GROUP(rg1)",
   237  			output: []*ast.TableOptimizerHint{
   238  				{
   239  					HintName: model.NewCIStr("USE_TOJA"),
   240  					HintData: true,
   241  				},
   242  				{
   243  					HintName: model.NewCIStr("IGNORE_PLAN_CACHE"),
   244  				},
   245  				{
   246  					HintName: model.NewCIStr("USE_CASCADES"),
   247  					HintData: true,
   248  				},
   249  				{
   250  					HintName: model.NewCIStr("QUERY_TYPE"),
   251  					QBName:   model.NewCIStr("qb1"),
   252  					HintData: model.NewCIStr("OLAP"),
   253  				},
   254  				{
   255  					HintName: model.NewCIStr("QUERY_TYPE"),
   256  					HintData: model.NewCIStr("OLTP"),
   257  				},
   258  				{
   259  					HintName: model.NewCIStr("NO_INDEX_MERGE"),
   260  				},
   261  				{
   262  					HintName: model.NewCIStr("RESOURCE_GROUP"),
   263  					HintData: "rg1",
   264  				},
   265  			},
   266  		},
   267  		{
   268  			input: "READ_FROM_STORAGE(@foo TIKV[a, b], TIFLASH[c, d]) HASH_AGG() SEMI_JOIN_REWRITE() READ_FROM_STORAGE(TIKV[e])",
   269  			output: []*ast.TableOptimizerHint{
   270  				{
   271  					HintName: model.NewCIStr("READ_FROM_STORAGE"),
   272  					HintData: model.NewCIStr("TIKV"),
   273  					QBName:   model.NewCIStr("foo"),
   274  					Tables: []ast.HintTable{
   275  						{TableName: model.NewCIStr("a")},
   276  						{TableName: model.NewCIStr("b")},
   277  					},
   278  				},
   279  				{
   280  					HintName: model.NewCIStr("READ_FROM_STORAGE"),
   281  					HintData: model.NewCIStr("TIFLASH"),
   282  					QBName:   model.NewCIStr("foo"),
   283  					Tables: []ast.HintTable{
   284  						{TableName: model.NewCIStr("c")},
   285  						{TableName: model.NewCIStr("d")},
   286  					},
   287  				},
   288  				{
   289  					HintName: model.NewCIStr("HASH_AGG"),
   290  				},
   291  				{
   292  					HintName: model.NewCIStr("SEMI_JOIN_REWRITE"),
   293  				},
   294  				{
   295  					HintName: model.NewCIStr("READ_FROM_STORAGE"),
   296  					HintData: model.NewCIStr("TIKV"),
   297  					Tables: []ast.HintTable{
   298  						{TableName: model.NewCIStr("e")},
   299  					},
   300  				},
   301  			},
   302  		},
   303  		{
   304  			input: "unknown_hint()",
   305  			errs:  []string{`Optimizer hint syntax error at line 1 `},
   306  		},
   307  		{
   308  			input: "set_var(timestamp = 1.5)",
   309  			errs: []string{
   310  				`Cannot use decimal number`,
   311  				`Optimizer hint syntax error at line 1 `,
   312  			},
   313  		},
   314  		{
   315  			input: "set_var(timestamp = _utf8mb4'1234')", // Optimizer hint doesn't recognize _charset'strings'.
   316  			errs:  []string{`Optimizer hint syntax error at line 1 `},
   317  		},
   318  		{
   319  			input: "set_var(timestamp = 9999999999999999999999999999999999999)",
   320  			errs: []string{
   321  				`integer value is out of range`,
   322  				`Optimizer hint syntax error at line 1 `,
   323  			},
   324  		},
   325  		{
   326  			input: "time_range('2020-02-20 12:12:12',456)",
   327  			errs: []string{
   328  				`Optimizer hint syntax error at line 1 `,
   329  			},
   330  		},
   331  		{
   332  			input: "time_range(456,'2020-02-20 12:12:12')",
   333  			errs: []string{
   334  				`Optimizer hint syntax error at line 1 `,
   335  			},
   336  		},
   337  		{
   338  			input: "TIME_RANGE('2020-02-20 12:12:12','2020-02-20 13:12:12')",
   339  			output: []*ast.TableOptimizerHint{
   340  				{
   341  					HintName: model.NewCIStr("TIME_RANGE"),
   342  					HintData: ast.HintTimeRange{
   343  						From: "2020-02-20 12:12:12",
   344  						To:   "2020-02-20 13:12:12",
   345  					},
   346  				},
   347  			},
   348  		},
   349  	}
   350  
   351  	for _, tc := range testCases {
   352  		output, errs := parser.ParseHint("/*+"+tc.input+"*/", tc.mode, parser.Pos{Line: 1})
   353  		require.Lenf(t, errs, len(tc.errs), "input = %s,\n... errs = %q", tc.input, errs)
   354  		for i, err := range errs {
   355  			require.Errorf(t, err, "input = %s, i = %d", tc.input, i)
   356  			require.Containsf(t, err.Error(), tc.errs[i], "input = %s, i = %d", tc.input, i)
   357  		}
   358  		require.Equalf(t, tc.output, output, "input = %s,\n... output = %q", tc.input, output)
   359  	}
   360  }