github.com/XiaoMi/Gaea@v1.2.5/mysql/sql_fingerprint_test.go (about) 1 // Copyright (c) 2014-2015, Percona LLC and/or its affiliates. All rights reserved. 2 // This program is free software: you can redistribute it and/or modify 3 // it under the terms of the GNU Affero General Public License as published by 4 // the Free Software Foundation, either version 3 of the License, or 5 // (at your option) any later version. 6 // This program is distributed in the hope that it will be useful, 7 // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 // GNU Affero General Public License for more details. 10 // You should have received a copy of the GNU Affero General Public License 11 // along with this program. If not, see <http://www.gnu.org/licenses/> 12 13 // Copyright 2016 The kingshard Authors. All rights reserved. 14 // 15 // Licensed under the Apache License, Version 2.0 (the "License"): you may 16 // not use this file except in compliance with the License. You may obtain 17 // a copy of the License at 18 // 19 // http://www.apache.org/licenses/LICENSE-2.0 20 // 21 // Unless required by applicable law or agreed to in writing, software 22 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 23 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 24 // License for the specific language governing permissions and limitations 25 // under the License. 26 27 package mysql 28 29 import ( 30 "testing" 31 ) 32 33 func TestFingerprintBasic(t *testing.T) { 34 var q, fp string 35 36 // A most basic case. 37 q = "SELECT c FROM t WHERE id=1" 38 fp = "select c from t where id=?" 39 if GetFingerprint(q) != fp { 40 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 41 } 42 43 // The values looks like one line -- comments, but they're not. 44 q = `UPDATE groups_search SET charter = ' -------3\'\' XXXXXXXXX.\n \n -----------------------------------------------------', show_in_list = 'Y' WHERE group_id='aaaaaaaa'` 45 fp = "update groups_search set charter = ?, show_in_list = ? where group_id=?" 46 if GetFingerprint(q) != fp { 47 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 48 } 49 50 // PT treats this as "mysqldump", but we don't do any special fingerprints. 51 q = "SELECT /*!40001 SQL_NO_CACHE */ * FROM `film`" 52 fp = "select /*!? sql_no_cache */ * from `film`" 53 if GetFingerprint(q) != fp { 54 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 55 } 56 57 // Fingerprints stored procedure calls specially 58 q = "CALL foo(1, 2, 3)" 59 fp = "call foo" 60 if GetFingerprint(q) != fp { 61 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 62 } 63 64 // Fingerprints admin commands as themselves 65 q = "administrator command: Init DB" 66 fp = "administrator command: Init DB" 67 if GetFingerprint(q) != fp { 68 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 69 } 70 71 // Removes identifier from USE 72 q = "use `foo`" 73 fp = "use ?" 74 if GetFingerprint(q) != fp { 75 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 76 } 77 78 // Handles bug from perlmonks thread 728718 79 q = "select null, 5.001, 5001. from foo" 80 fp = "select ?, ?, ? from foo" 81 if GetFingerprint(q) != fp { 82 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 83 } 84 85 // Handles quoted strings 86 q = "select 'hello', '\nhello\n', \"hello\", '\\'' from foo" 87 fp = "select ?, ?, ?, ? from foo" 88 if GetFingerprint(q) != fp { 89 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 90 } 91 92 // Handles trailing newline 93 q = "select 'hello'\n" 94 fp = "select ?" 95 if GetFingerprint(q) != fp { 96 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 97 } 98 99 q = "select '\\\\' from foo" 100 fp = "select ? from foo" 101 if GetFingerprint(q) != fp { 102 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 103 } 104 105 // Collapses whitespace 106 q = "select foo" 107 fp = "select foo" 108 if GetFingerprint(q) != fp { 109 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 110 } 111 112 // Lowercases, replaces integer 113 q = "SELECT * from foo where a = 5" 114 fp = "select * from foo where a = ?" 115 if GetFingerprint(q) != fp { 116 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 117 } 118 119 // Floats 120 q = "select 0e0, +6e-30, -6.00 from foo where a = 5.5 or b=0.5 or c=.5" 121 fp = "select ?, ?, ? from foo where a = ? or b=? or c=?" 122 if GetFingerprint(q) != fp { 123 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 124 } 125 126 // Hex/bit 127 q = "select 0x0, x'123', 0b1010, b'10101' from foo" 128 fp = "select ?, ?, ?, ? from foo" 129 if GetFingerprint(q) != fp { 130 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 131 } 132 133 // Collapses whitespace 134 q = " select * from\nfoo where a = 5" 135 fp = "select * from foo where a = ?" 136 if GetFingerprint(q) != fp { 137 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 138 } 139 140 // IN lists 141 q = "select * from foo where a in (5) and b in (5, 8,9 ,9 , 10)" 142 fp = "select * from foo where a in(?+) and b in(?+)" 143 if GetFingerprint(q) != fp { 144 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 145 } 146 // Numeric table names. By default, PT will return foo_?, etc. because 147 // match_embedded_numbers is false by default for speed. 148 q = "select foo_1 from foo_2_3" 149 fp = "select foo_1 from foo_2_3" 150 if GetFingerprint(q) != fp { 151 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 152 } 153 154 // Numeric table name prefixes 155 q = "select 123foo from 123foo" 156 fp = "select 123foo from 123foo" 157 if GetFingerprint(q) != fp { 158 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 159 } 160 161 // Numeric table name prefixes with underscores 162 q = "select 123_foo from 123_foo" 163 fp = "select 123_foo from 123_foo" 164 if GetFingerprint(q) != fp { 165 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 166 } 167 168 // A string that needs no changes 169 q = "insert into abtemp.coxed select foo.bar from foo" 170 fp = "insert into abtemp.coxed select foo.bar from foo" 171 if GetFingerprint(q) != fp { 172 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 173 } 174 175 // limit alone 176 q = "select * from foo limit 5" 177 fp = "select * from foo limit ?" 178 if GetFingerprint(q) != fp { 179 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 180 } 181 182 // limit with comma-offset 183 q = "select * from foo limit 5, 10" 184 fp = "select * from foo limit ?, ?" 185 if GetFingerprint(q) != fp { 186 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 187 } 188 189 // limit with offset 190 q = "select * from foo limit 5 offset 10" 191 fp = "select * from foo limit ? offset ?" 192 if GetFingerprint(q) != fp { 193 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 194 } 195 196 // Fingerprint LOAD DATA INFILE 197 q = "LOAD DATA INFILE '/tmp/foo.txt' INTO db.tbl" 198 fp = "load data infile ? into db.tbl" 199 if GetFingerprint(q) != fp { 200 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 201 } 202 203 // Fingerprint db.tbl<number>name (preserve number) 204 q = "SELECT * FROM prices.rt_5min where id=1" 205 fp = "select * from prices.rt_5min where id=?" 206 if GetFingerprint(q) != fp { 207 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 208 } 209 210 // Fingerprint /* -- comment */ SELECT (bug 1174956) 211 q = "/* -- S++ SU ABORTABLE -- spd_user: rspadim */SELECT SQL_SMALL_RESULT SQL_CACHE DISTINCT centro_atividade FROM est_dia WHERE unidade_id=1001 AND item_id=67 AND item_id_red=573" 212 fp = "select sql_small_result sql_cache distinct centro_atividade from est_dia where unidade_id=? and item_id=? and item_id_red=?" 213 if GetFingerprint(q) != fp { 214 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 215 } 216 217 q = "INSERT INTO t (ts) VALUES (NOW())" 218 fp = "insert into t (ts) values(?+)" 219 if GetFingerprint(q) != fp { 220 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 221 } 222 223 q = "INSERT INTO t (ts) VALUES ('()', '\\(', '\\)')" 224 fp = "insert into t (ts) values(?+)" 225 if GetFingerprint(q) != fp { 226 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 227 } 228 229 } 230 231 func TestFingerprintValueList(t *testing.T) { 232 var q, fp string 233 234 // VALUES lists 235 q = "insert into foo(a, b, c) values(2, 4, 5)" 236 fp = "insert into foo(a, b, c) values(?+)" 237 if GetFingerprint(q) != fp { 238 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 239 } 240 241 // VALUES lists with multiple () 242 q = "insert into foo(a, b, c) values(2, 4, 5) , (2,4,5)" 243 fp = "insert into foo(a, b, c) values(?+)" 244 if GetFingerprint(q) != fp { 245 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 246 } 247 248 // VALUES lists with VALUE() 249 q = "insert into foo(a, b, c) value(2, 4, 5)" 250 fp = "insert into foo(a, b, c) value(?+)" 251 if GetFingerprint(q) != fp { 252 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 253 } 254 255 q = "insert into foo values (1, '(2)', 'This is a trick: ). More values.', 4)" 256 fp = "insert into foo values(?+)" 257 if GetFingerprint(q) != fp { 258 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 259 } 260 261 } 262 263 func TestFingerprintInList(t *testing.T) { 264 var q, fp string 265 266 q = "select * from t where (base.nid IN ('1412', '1410', '1411'))" 267 fp = "select * from t where (base.nid in(?+))" 268 if GetFingerprint(q) != fp { 269 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 270 } 271 272 q = "SELECT ID, name, parent, type FROM posts WHERE _name IN ('perf','caching') AND (type = 'page' OR type = 'attachment')" 273 fp = "select id, name, parent, type from posts where _name in(?+) and (type = ? or type = ?)" 274 if GetFingerprint(q) != fp { 275 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 276 } 277 278 q = "SELECT t FROM field WHERE (entity_type = 'node') AND (entity_id IN ('609')) AND (language IN ('und')) AND (deleted = '0') ORDER BY delta ASC" 279 fp = "select t from field where (entity_type = ?) and (entity_id in(?+)) and (language in(?+)) and (deleted = ?) order by delta" 280 if GetFingerprint(q) != fp { 281 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 282 } 283 284 } 285 286 func TestFingerprintOrderBy(t *testing.T) { 287 var q, fp string 288 289 // Remove ASC from ORDER BY 290 // Issue 1030: Fingerprint can remove ORDER BY ASC 291 q = "select c from t where i=1 order by c asc" 292 fp = "select c from t where i=? order by c" 293 if GetFingerprint(q) != fp { 294 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 295 } 296 297 // Remove only ASC from ORDER BY 298 q = "select * from t where i=1 order by a, b ASC, d DESC, e asc" 299 fp = "select * from t where i=? order by a, b, d desc, e" 300 if GetFingerprint(q) != fp { 301 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 302 } 303 304 // Remove ASC from spacey ORDER BY 305 q = `select * from t where i=1 order by 306 a, b ASC, d DESC, 307 308 e asc` 309 fp = "select * from t where i=? order by a, b, d desc, e" 310 if GetFingerprint(q) != fp { 311 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 312 } 313 314 } 315 316 func TestFingerprintOneLineComments(t *testing.T) { 317 var q, fp string 318 319 // Removes one-line comments in fingerprints 320 q = "select \n-- bar\n foo" 321 fp = "select foo" 322 if GetFingerprint(q) != fp { 323 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 324 } 325 326 // Removes one-line comments in fingerprint without mushing things together 327 q = "select foo-- bar\nfoo" 328 fp = "select foo foo" 329 if GetFingerprint(q) != fp { 330 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 331 } 332 333 // Removes one-line EOL comments in fingerprints 334 q = "select foo -- bar\n" 335 fp = "select foo" 336 if GetFingerprint(q) != fp { 337 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 338 } 339 340 } 341 342 func TestFingerprintTricky(t *testing.T) { 343 var q, fp string 344 345 // Full hex can look like an ident if not for the leading 0x. 346 q = "SELECT c FROM t WHERE id=0xdeadbeaf" 347 fp = "select c from t where id=?" 348 if GetFingerprint(q) != fp { 349 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 350 } 351 352 // Caused a crash. 353 q = "SELECT * FROM t WHERE 1=1 AND id=1" 354 fp = "select * from t where ?=? and id=?" 355 if GetFingerprint(q) != fp { 356 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 357 } 358 359 // Caused a crash. 360 q = "SELECT `db`.*, (CASE WHEN (`date_start` <= '2014-09-10 09:17:59' AND `date_end` >= '2014-09-10 09:17:59') THEN 'open' WHEN (`date_start` > '2014-09-10 09:17:59' AND `date_end` > '2014-09-10 09:17:59') THEN 'tbd' ELSE 'none' END) AS `status` FROM `foo` AS `db` WHERE (a_b in ('1', '10101'))" 361 fp = "select `db`.*, (case when (`date_start` <= ? and `date_end` >= ?) then ? when (`date_start` > ? and `date_end` > ?) then ? else ? end) as `status` from `foo` as `db` where (a_b in(?+))" 362 if GetFingerprint(q) != fp { 363 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 364 } 365 366 // VALUES() after ON DUPE KEY is not the same as VALUES() for INSERT. 367 q = "insert into t values (1) on duplicate key update query_count=COALESCE(query_count, 0) + VALUES(query_count)" 368 fp = "insert into t values(?+) on duplicate key update query_count=coalesce(query_count, ?) + values(query_count)" 369 if GetFingerprint(q) != fp { 370 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 371 } 372 373 q = "insert into t values (1), (2), (3)\n\n\ton duplicate key update query_count=1" 374 fp = "insert into t values(?+) on duplicate key update query_count=?" 375 if GetFingerprint(q) != fp { 376 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 377 } 378 379 q = "select t.table_schema,t.table_name,engine from information_schema.tables t inner join information_schema.columns c on t.table_schema=c.table_schema and t.table_name=c.table_name group by t.table_schema,t.table_name having sum(if(column_key in ('PRI','UNI'),1,0))=0" 380 fp = "select t.table_schema,t.table_name,engine from information_schema.tables t inner join information_schema.columns c on t.table_schema=c.table_schema and t.table_name=c.table_name group by t.table_schema,t.table_name having sum(if(column_key in(?+),?,?))=?" 381 if GetFingerprint(q) != fp { 382 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 383 } 384 385 // Empty value list is valid SQL. 386 q = "INSERT INTO t () VALUES ()" 387 fp = "insert into t () values()" 388 if GetFingerprint(q) != fp { 389 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 390 } 391 392 } 393 394 func TestNumbersInFunctions(t *testing.T) { 395 var q, fp string 396 397 // Full hex can look like an ident if not for the leading 0x. 398 q = "select sleep(2) from test.n" 399 fp = "select sleep(?) from test.n" 400 if GetFingerprint(q) != fp { 401 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 402 } 403 404 } 405 406 func TestFingerprintPanicChallenge1(t *testing.T) { 407 q := "SELECT '' '' ''" 408 fp := "select ? ? ?" 409 if GetFingerprint(q) != fp { 410 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 411 } 412 413 q = "SELECT '' '' '' FROM kamil" 414 fp = "select ? ? ? from kamil" 415 if GetFingerprint(q) != fp { 416 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 417 } 418 419 } 420 421 func TestFingerprintPanicChallenge2(t *testing.T) { 422 q := "SELECT 'a' 'b' 'c' 'd'" 423 fp := "select ? ? ? ?" 424 if GetFingerprint(q) != fp { 425 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 426 } 427 428 q = "SELECT 'a' 'b' 'c' 'd' FROM kamil" 429 fp = "select ? ? ? ? from kamil" 430 if GetFingerprint(q) != fp { 431 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 432 } 433 434 } 435 436 func TestFingerprintKeywords(t *testing.T) { 437 var q, fp string 438 439 // values is a keyword but value is not. 440 q = "SELECT name, value FROM variable" 441 fp = "select name, value from variable" 442 if GetFingerprint(q) != fp { 443 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 444 } 445 446 } 447 448 func TestFingerprintUseIndex(t *testing.T) { 449 var q, fp string 450 451 q = `SELECT 1 AS one FROM calls USE INDEX(index_name)` 452 fp = "select ? as one from calls use index(index_name)" 453 if GetFingerprint(q) != fp { 454 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 455 } 456 457 } 458 459 func TestFingerprintWithNumberInDbName(t *testing.T) { 460 var q, fp string 461 defaultReplaceNumbersInWords := ReplaceNumbersInWords 462 ReplaceNumbersInWords = true 463 defer func() { 464 // Restore default value for other tests 465 ReplaceNumbersInWords = defaultReplaceNumbersInWords 466 }() 467 468 q = "SELECT c FROM org235.t WHERE id=0xdeadbeaf" 469 fp = "select c from org?.t where id=?" 470 if GetFingerprint(q) != fp { 471 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 472 } 473 474 q = "CREATE DATABASE org235_percona345 COLLATE 'utf8_general_ci'" 475 fp = "create database org?_percona? collate ?" 476 if GetFingerprint(q) != fp { 477 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 478 } 479 480 q = "select foo_1 from foo_2_3" 481 fp = "select foo_? from foo_?_?" 482 if GetFingerprint(q) != fp { 483 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 484 } 485 486 q = "SELECT * FROM prices.rt_5min where id=1" 487 fp = "select * from prices.rt_?min where id=?" 488 if GetFingerprint(q) != fp { 489 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 490 } 491 492 // @todo prefixes are not supported, requires more hacks 493 q = "select 123foo from 123foo" 494 fp = "select 123foo from 123foo" 495 if GetFingerprint(q) != fp { 496 t.Fatalf("query=%s,and fingerPrint=%s\n", q, fp) 497 } 498 } 499 500 func Test_Insert(t *testing.T) { 501 q := "t2 values(2)" 502 t.Logf(GetFingerprint(q)) 503 }