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  }