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 }