mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-01 18:25:59 +00:00

* PT-1900 WIP * Fixed quoted strings regexes * PT-1900 Fixed query rewriter to properly handle quoted text * Fixed merge conflicts with 3.x branch
1498 lines
43 KiB
Perl
1498 lines
43 KiB
Perl
#!/usr/bin/perl
|
|
|
|
|
|
BEGIN {
|
|
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
|
|
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
|
|
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
|
|
};
|
|
|
|
use strict;
|
|
use warnings FATAL => 'all';
|
|
use English qw(-no_match_vars);
|
|
use Test::More;
|
|
|
|
use QueryRewriter;
|
|
use QueryParser;
|
|
use PerconaTest;
|
|
|
|
my $qp = new QueryParser();
|
|
my $qr = new QueryRewriter(QueryParser=>$qp);
|
|
|
|
# #############################################################################
|
|
# strip_comments()
|
|
# #############################################################################
|
|
|
|
is(
|
|
$qr->strip_comments("select \n--bar\n foo"),
|
|
"select \n\n foo",
|
|
'Removes one-line comments',
|
|
);
|
|
|
|
is(
|
|
$qr->strip_comments("select foo--bar\nfoo"),
|
|
"select foo\nfoo",
|
|
'Removes one-line comments without running them together',
|
|
);
|
|
|
|
is(
|
|
$qr->strip_comments("select foo -- bar"),
|
|
"select foo ",
|
|
'Removes one-line comments at end of line',
|
|
);
|
|
|
|
is(
|
|
$qr->strip_comments("select /*\nhello!*/ 1"),
|
|
'select 1',
|
|
'Stripped star comment',
|
|
);
|
|
|
|
is(
|
|
$qr->strip_comments('select /*!40101 hello*/ 1'),
|
|
'select /*!40101 hello*/ 1',
|
|
'Left version star comment',
|
|
);
|
|
|
|
# #############################################################################
|
|
# fingerprint()
|
|
# #############################################################################
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
q{UPDATE groups_search SET charter = ' -------3\'\' XXXXXXXXX.\n \n -----------------------------------------------------', show_in_list = 'Y' WHERE group_id='aaaaaaaa'}),
|
|
'update groups_search set charter = ?, show_in_list = ? where group_id=?',
|
|
'complex comments',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("SELECT /*!40001 SQL_NO_CACHE */ * FROM `film`"),
|
|
"mysqldump",
|
|
'Fingerprints all mysqldump SELECTs together',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("CALL foo(1, 2, 3)"),
|
|
"call foo",
|
|
'Fingerprints stored procedure calls specially',
|
|
);
|
|
|
|
|
|
is(
|
|
$qr->fingerprint('administrator command: Init DB'),
|
|
'administrator command: Init DB',
|
|
'Fingerprints admin commands as themselves',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
q{REPLACE /*foo.bar:3/3*/ INTO checksum.checksum (db, tbl, }
|
|
.q{chunk, boundaries, this_cnt, this_crc) SELECT 'foo', 'bar', }
|
|
.q{2 AS chunk_num, '`id` >= 2166633', COUNT(*) AS cnt, }
|
|
.q{LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `created_by`, }
|
|
.q{`created_date`, `updated_by`, `updated_date`, `ppc_provider`, }
|
|
.q{`account_name`, `provider_account_id`, `campaign_name`, }
|
|
.q{`provider_campaign_id`, `adgroup_name`, `provider_adgroup_id`, }
|
|
.q{`provider_keyword_id`, `provider_ad_id`, `foo`, `reason`, }
|
|
.q{`foo_bar_bazz_id`, `foo_bar_baz`, CONCAT(ISNULL(`created_by`), }
|
|
.q{ISNULL(`created_date`), ISNULL(`updated_by`), ISNULL(`updated_date`), }
|
|
.q{ISNULL(`ppc_provider`), ISNULL(`account_name`), }
|
|
.q{ISNULL(`provider_account_id`), ISNULL(`campaign_name`), }
|
|
.q{ISNULL(`provider_campaign_id`), ISNULL(`adgroup_name`), }
|
|
.q{ISNULL(`provider_adgroup_id`), ISNULL(`provider_keyword_id`), }
|
|
.q{ISNULL(`provider_ad_id`), ISNULL(`foo`), ISNULL(`reason`), }
|
|
.q{ISNULL(`foo_base_foo_id`), ISNULL(`fooe_foo_id`)))) AS UNSIGNED)), 10, }
|
|
.q{16)) AS crc FROM `foo`.`bar` USE INDEX (`PRIMARY`) WHERE }
|
|
.q{(`id` >= 2166633); }),
|
|
'percona-toolkit',
|
|
'Fingerprints mk-table-checksum queries together',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("use `foo`"),
|
|
"use ?",
|
|
'Removes identifier from USE',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select \n--bar\n foo"),
|
|
"select foo",
|
|
'Removes one-line comments in fingerprints',
|
|
);
|
|
|
|
|
|
is(
|
|
$qr->fingerprint("select foo--bar\nfoo"),
|
|
"select foo foo",
|
|
'Removes one-line comments in fingerprint without mushing things together',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select foo -- bar\n"),
|
|
"select foo ",
|
|
'Removes one-line EOL comments in fingerprints',
|
|
);
|
|
|
|
# This one is too expensive!
|
|
#is(
|
|
# $qr->fingerprint(
|
|
# "select a,b ,c , d from tbl where a=5 or a = 5 or a=5 or a =5"),
|
|
# "select a, b, c, d from tbl where a=? or a=? or a=? or a=?",
|
|
# "Normalizes commas and equals",
|
|
#);
|
|
|
|
is(
|
|
$qr->fingerprint("select null, 5.001, 5001. from foo"),
|
|
"select ?, ?, ? from foo",
|
|
"Handles bug from perlmonks thread 728718",
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select 'hello', '\nhello\n', \"hello\", '\\'' from foo"),
|
|
"select ?, ?, ?, ? from foo",
|
|
"Handles quoted strings",
|
|
);
|
|
|
|
|
|
is(
|
|
$qr->fingerprint("select 'hello'\n"),
|
|
"select ?",
|
|
"Handles trailing newline",
|
|
);
|
|
|
|
# This is a known deficiency, fixes seem to be expensive though.
|
|
is(
|
|
$qr->fingerprint("select '\\\\' from foo"),
|
|
"select ? from foo",
|
|
"Does not handle all quoted strings",
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select foo"),
|
|
"select foo",
|
|
'Collapses whitespace',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('SELECT * from foo where a = 5'),
|
|
'select * from foo where a = ?',
|
|
'Lowercases, replaces integer',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('select 0e0, +6e-30, -6.00 from foo where a = 5.5 or b=0.5 or c=.5'),
|
|
'select ?, ?, ? from foo where a = ? or b=? or c=?',
|
|
'Floats',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select 0x0, x'123', 0b1010, b'10101' from foo"),
|
|
'select ?, ?, ?, ? from foo',
|
|
'Hex/bit',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(" select * from\nfoo where a = 5"),
|
|
'select * from foo where a = ?',
|
|
'Collapses whitespace',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select * from foo where a in (5) and b in (5, 8,9 ,9 , 10)"),
|
|
'select * from foo where a in(?+) and b in(?+)',
|
|
'IN lists',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select foo_1 from foo_2_3"),
|
|
'select foo_? from foo_?_?',
|
|
'Numeric table names',
|
|
);
|
|
|
|
# 123f00 => ?oo because f "looks like it could be a number".
|
|
is(
|
|
$qr->fingerprint("select 123foo from 123foo", { prefixes => 1 }),
|
|
'select ?oo from ?oo',
|
|
'Numeric table name prefixes',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("select 123_foo from 123_foo", { prefixes => 1 }),
|
|
'select ?_foo from ?_foo',
|
|
'Numeric table name prefixes with underscores',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("insert into abtemp.coxed select foo.bar from foo"),
|
|
'insert into abtemp.coxed select foo.bar from foo',
|
|
'A string that needs no changes',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('insert into foo(a, b, c) values(2, 4, 5)'),
|
|
'insert into foo(a, b, c) values(?+)',
|
|
'VALUES lists',
|
|
);
|
|
|
|
|
|
is(
|
|
$qr->fingerprint('insert into foo(a, b, c) values(2, 4, 5) , (2,4,5)'),
|
|
'insert into foo(a, b, c) values(?+)',
|
|
'VALUES lists with multiple ()',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('insert into foo(a, b, c) value(2, 4, 5)'),
|
|
'insert into foo(a, b, c) value(?+)',
|
|
'VALUES lists with VALUE()',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('select * from foo limit 5'),
|
|
'select * from foo limit ?',
|
|
'limit alone',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('select * from foo limit 5, 10'),
|
|
'select * from foo limit ?',
|
|
'limit with comma-offset',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('select * from foo limit 5 offset 10'),
|
|
'select * from foo limit ?',
|
|
'limit with offset',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('select 1 union select 2 union select 4'),
|
|
'select ? /*repeat union*/',
|
|
'union fingerprints together',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint('select 1 union all select 2 union all select 4'),
|
|
'select ? /*repeat union all*/',
|
|
'union all fingerprints together',
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
q{select * from (select 1 union all select 2 union all select 4) as x }
|
|
. q{join (select 2 union select 2 union select 3) as y}),
|
|
q{select * from (select ? /*repeat union all*/) as x }
|
|
. q{join (select ? /*repeat union*/) as y},
|
|
'union all fingerprints together',
|
|
);
|
|
|
|
# Issue 322: mk-query-digest segfault before report
|
|
is(
|
|
$qr->fingerprint( load_file('t/lib/samples/huge_replace_into_values.txt') ),
|
|
q{replace into `film_actor` values(?+)},
|
|
'huge replace into values() (issue 322)',
|
|
);
|
|
is(
|
|
$qr->fingerprint( load_file('t/lib/samples/huge_insert_ignore_into_values.txt') ),
|
|
q{insert ignore into `film_actor` values(?+)},
|
|
'huge insert ignore into values() (issue 322)',
|
|
);
|
|
is(
|
|
$qr->fingerprint( load_file('t/lib/samples/huge_explicit_cols_values.txt') ),
|
|
q{insert into foo (a,b,c,d,e,f,g,h) values(?+)},
|
|
'huge insert with explicit columns before values() (issue 322)',
|
|
);
|
|
|
|
# Those ^ aren't huge enough. This one is 1.2M large.
|
|
my $zcat = `uname` =~ m/Darwin/ ? 'gzcat' : 'zcat';
|
|
my $huge_insert = `$zcat $trunk/t/lib/samples/slowlogs/slow039.txt.gz | tail -n 1`;
|
|
is(
|
|
$qr->fingerprint($huge_insert),
|
|
q{insert into the_universe values(?+)},
|
|
'truly huge insert 1/2 (issue 687)'
|
|
);
|
|
$huge_insert = `$zcat $trunk/t/lib/samples/slowlogs/slow040.txt.gz | tail -n 2`;
|
|
is(
|
|
$qr->fingerprint($huge_insert),
|
|
q{insert into the_universe values(?+)},
|
|
'truly huge insert 2/2 (issue 687)'
|
|
);
|
|
|
|
# Issue 1030: Fingerprint can remove ORDER BY ASC
|
|
is(
|
|
$qr->fingerprint(
|
|
"select c from t where i=1 order by c asc",
|
|
),
|
|
"select c from t where i=? order by c",
|
|
"Remove ASC from ORDER BY"
|
|
);
|
|
is(
|
|
$qr->fingerprint(
|
|
"select * from t where i=1 order by a, b ASC, d DESC, e asc",
|
|
),
|
|
"select * from t where i=? order by a, b, d desc, e",
|
|
"Remove only ASC from ORDER BY"
|
|
);
|
|
is(
|
|
$qr->fingerprint(
|
|
"select * from t where i=1 order by
|
|
a, b ASC, d DESC,
|
|
|
|
e asc",
|
|
),
|
|
"select * from t where i=? order by a, b, d desc, e",
|
|
"Remove ASC from spacey ORDER BY"
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("LOAD DATA INFILE '/tmp/foo.txt' INTO db.tbl"),
|
|
"load data infile ? into db.tbl",
|
|
"Fingerprint LOAD DATA INFILE"
|
|
);
|
|
|
|
# fingerprint MD5 checksums, 32 char hex strings. This is a
|
|
# special feature used by pt-fingerprint.
|
|
$qr = new QueryRewriter(
|
|
QueryParser => $qp,
|
|
match_md5_checksums => 1,
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"SELECT * FROM db.fbc5e685a5d3d45aa1d0347fdb7c4d35_temp where id=1"
|
|
),
|
|
"select * from db.?_temp where id=?",
|
|
"Fingerprint db.MD5_tbl"
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"SELECT * FROM db.temp_fbc5e685a5d3d45aa1d0347fdb7c4d35 where id=1"
|
|
),
|
|
"select * from db.temp_? where id=?",
|
|
"Fingerprint db.tbl_MD5"
|
|
);
|
|
|
|
$qr = new QueryRewriter(
|
|
QueryParser => $qp,
|
|
match_md5_checksums => 1,
|
|
match_embedded_numbers => 1,
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"SELECT * FROM db.fbc5e685a5d3d45aa1d0347fdb7c4d35_temp where id=1"
|
|
),
|
|
"select * from db.?_temp where id=?",
|
|
"Fingerprint db.MD5_tbl (with match_embedded_numbers)"
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"SELECT * FROM db.temp_fbc5e685a5d3d45aa1d0347fdb7c4d35 where id=1"
|
|
),
|
|
"select * from db.temp_? where id=?",
|
|
"Fingerprint db.tbl_MD5 (with match_embedded_numbers)"
|
|
);
|
|
|
|
$qr = new QueryRewriter(
|
|
QueryParser => $qp,
|
|
match_embedded_numbers => 1,
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"SELECT * FROM prices.rt_5min where id=1"
|
|
),
|
|
"select * from prices.rt_5min where id=?",
|
|
"Fingerprint db.tbl<number>name (preserve number)"
|
|
);
|
|
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"/* -- 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"
|
|
),
|
|
"select sql_small_result sql_cache distinct centro_atividade from est_dia where unidade_id=? and item_id=? and item_id_red=?",
|
|
"Fingerprint /* -- comment */ SELECT (bug 1174956)"
|
|
);
|
|
|
|
|
|
# issue 965553
|
|
|
|
is(
|
|
$qr->fingerprint('SELECT * FROM tbl WHERE id=1 AND flag=true AND trueflag=FALSE'),
|
|
'select * from tbl where id=? and flag=? and trueflag=?',
|
|
'boolean values abstracted correctly',
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# convert_to_select()
|
|
# #############################################################################
|
|
|
|
is($qr->convert_to_select(), undef, 'No query');
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'select * from tbl where id = 1'
|
|
),
|
|
'select * from tbl where id = 1',
|
|
'Does not convert select to select',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(q{INSERT INTO foo.bar (col1, col2, col3)
|
|
VALUES ('unbalanced(', 'val2', 3)}),
|
|
q{select * from foo.bar where col1='unbalanced(' and }
|
|
. q{col2= 'val2' and col3= 3},
|
|
'unbalanced paren inside a string in VALUES',
|
|
);
|
|
|
|
# convert REPLACE #############################################################
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'replace into foo select * from bar',
|
|
),
|
|
'select * from bar',
|
|
'convert REPLACE SELECT',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'replace into foo select`faz` from bar',
|
|
),
|
|
'select`faz` from bar',
|
|
'convert REPLACE SELECT`col`',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'replace into foo(a, b, c) values(1, 3, 5) on duplicate key update foo=bar',
|
|
),
|
|
'select * from foo where a=1 and b= 3 and c= 5',
|
|
'convert REPLACE (cols) VALUES ON DUPE KEY',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'replace into foo(a, b, c) values(now(), "3", 5)',
|
|
),
|
|
'select * from foo where a=now() and b= "3" and c= 5',
|
|
'convert REPLACE (cols) VALUES (now())',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'replace into foo(a, b, c) values(current_date - interval 1 day, "3", 5)',
|
|
),
|
|
'select * from foo where a=current_date - interval 1 day and b= "3" and c= 5',
|
|
'convert REPLACE (cols) VALUES (complex expression)',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(q{
|
|
REPLACE DELAYED INTO
|
|
`db1`.`tbl2`(`col1`,col2)
|
|
VALUES ('617653','2007-09-11')}),
|
|
qq{select * from \n`db1`.`tbl2` where `col1`='617653' and col2='2007-09-11'},
|
|
'convert REPLACE DELAYED (cols) VALUES',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'replace into tbl set col1="a val", col2=123, col3=null',
|
|
),
|
|
'select * from tbl where col1="a val" and col2=123 and col3=null ',
|
|
'convert REPLACE SET'
|
|
);
|
|
|
|
# convert INSERT ##############################################################
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'insert into foo(a, b, c) values(1, 3, 5)',
|
|
),
|
|
'select * from foo where a=1 and b= 3 and c= 5',
|
|
'convert INSERT (cols) VALUES',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'insert into foo(a, b, c) value(1, 3, 5)',
|
|
),
|
|
'select * from foo where a=1 and b= 3 and c= 5',
|
|
'convert INSERT (cols) VALUE',
|
|
);
|
|
|
|
# Issue 599: mk-slave-prefetch doesn't parse INSERT IGNORE
|
|
is(
|
|
$qr->convert_to_select(
|
|
'insert ignore into foo(a, b, c) values(1, 3, 5)',
|
|
),
|
|
'select * from foo where a=1 and b= 3 and c= 5',
|
|
'convert INSERT IGNORE (cols) VALUES',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'INSERT IGNORE INTO Foo (clm1, clm2) VALUE (1,2)',
|
|
),
|
|
'select * from Foo where clm1=1 and clm2=2',
|
|
'convert INSERT IGNORE (cols) VALUE',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'insert into foo select * from bar join baz using (bat)',
|
|
),
|
|
'select * from bar join baz using (bat)',
|
|
'convert INSERT SELECT',
|
|
);
|
|
|
|
# Issue 600: mk-slave-prefetch doesn't parse INSERT INTO Table SET c1 = v1,
|
|
# c2 = v2 ...
|
|
is(
|
|
$qr->convert_to_select(
|
|
"INSERT INTO Table SET c1 = 'v1', c2 = 'v2', c3 = 'v3'",
|
|
),
|
|
"select * from Table where c1 = 'v1' and c2 = 'v2' and c3 = 'v3' ",
|
|
'convert INSERT SET char cols',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
"INSERT INTO db.tbl SET c1=NULL,c2=42,c3='some value with spaces'",
|
|
),
|
|
"select * from db.tbl where c1=NULL and c2=42 and c3='some value with spaces' ",
|
|
'convert INSERT SET NULL col, int col, char col with space',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'insert into foo (col1) values (1) on duplicate key update',
|
|
),
|
|
'select * from foo where col1=1',
|
|
'convert INSERT (cols) VALUES ON DUPE KEY UPDATE'
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'insert into foo (col1) value (1) on duplicate key update',
|
|
),
|
|
'select * from foo where col1=1',
|
|
'convert INSERT (cols) VALUE ON DUPE KEY UPDATE'
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
"insert into tbl set col='foo', col2='some val' on duplicate key update",
|
|
),
|
|
"select * from tbl where col='foo' and col2='some val' ",
|
|
'convert INSERT SET ON DUPE KEY UPDATE',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'insert into foo select * from bar where baz=bat on duplicate key update',
|
|
),
|
|
'select * from bar where baz=bat',
|
|
'convert INSERT SELECT ON DUPE KEY UPDATE',
|
|
);
|
|
|
|
# convert UPDATE ##############################################################
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'update foo set bar=baz where bat=fiz',
|
|
),
|
|
'select bar=baz from foo where bat=fiz',
|
|
'update set',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'update foo inner join bar using(baz) set big=little',
|
|
),
|
|
'select big=little from foo inner join bar using(baz) ',
|
|
'delete inner join',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'update foo set bar=baz limit 50',
|
|
),
|
|
'select bar=baz from foo limit 50 ',
|
|
'update with limit',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
q{UPDATE foo.bar
|
|
SET whereproblem= '3364', apple = 'fish'
|
|
WHERE gizmo='5091'}
|
|
),
|
|
q{select whereproblem= '3364', apple = 'fish' from foo.bar where gizmo='5091'},
|
|
'unknown issue',
|
|
);
|
|
|
|
# Insanity...
|
|
is(
|
|
$qr->convert_to_select('
|
|
update db2.tbl1 as p
|
|
inner join (
|
|
select p2.col1, p2.col2
|
|
from db2.tbl1 as p2
|
|
inner join db2.tbl3 as ba
|
|
on p2.col1 = ba.tbl3
|
|
where col4 = 0
|
|
order by priority desc, col1, col2
|
|
limit 10
|
|
) as chosen on chosen.col1 = p.col1
|
|
and chosen.col2 = p.col2
|
|
set p.col4 = 149945'),
|
|
'select p.col4 = 149945 from db2.tbl1 as p
|
|
inner join (
|
|
select p2.col1, p2.col2
|
|
from db2.tbl1 as p2
|
|
inner join db2.tbl3 as ba
|
|
on p2.col1 = ba.tbl3
|
|
where col4 = 0
|
|
order by priority desc, col1, col2
|
|
limit 10
|
|
) as chosen on chosen.col1 = p.col1
|
|
and chosen.col2 = p.col2 ',
|
|
'SELECT in the FROM clause',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select("UPDATE tbl SET col='wherex'WHERE crazy=1"),
|
|
"select col='wherex' from tbl where crazy=1",
|
|
"update with SET col='wherex'WHERE"
|
|
);
|
|
|
|
is($qr->convert_to_select(
|
|
q{UPDATE GARDEN_CLUPL PL, GARDENJOB GC, APLTRACT_GARDENPLANT ABU SET }
|
|
. q{GC.MATCHING_POT = 5, GC.LAST_GARDENPOT = 5, GC.LAST_NAME=}
|
|
. q{'Rotary', GC.LAST_BUCKET='Pail', GC.LAST_UPDATE='2008-11-27 04:00:59'WHERE}
|
|
. q{ PL.APLTRACT_GARDENPLANT_ID = GC.APLTRACT_GARDENPLANT_ID AND PL.}
|
|
. q{APLTRACT_GARDENPLANT_ID = ABU.ID AND GC.MATCHING_POT = 0 AND GC.PERFORM_DIG=1}
|
|
. q{ AND ABU.DIG = 6 AND ( ((SOIL-COST) > -80.0}
|
|
. q{ AND BUGS < 60.0 AND (SOIL-COST) < 200.0) AND POTS < 10.0 )}),
|
|
"select GC.MATCHING_POT = 5, GC.LAST_GARDENPOT = 5, GC.LAST_NAME='Rotary', GC.LAST_BUCKET='Pail', GC.LAST_UPDATE='2008-11-27 04:00:59' from GARDEN_CLUPL PL, GARDENJOB GC, APLTRACT_GARDENPLANT ABU where PL.APLTRACT_GARDENPLANT_ID = GC.APLTRACT_GARDENPLANT_ID AND PL.APLTRACT_GARDENPLANT_ID = ABU.ID AND GC.MATCHING_POT = 0 AND GC.PERFORM_DIG=1 AND ABU.DIG = 6 AND ( ((SOIL-COST) > -80.0 AND BUGS < 60.0 AND (SOIL-COST) < 200.0) AND POTS < 10.0 )",
|
|
'update with no space between quoted string and where (issue 168)'
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select("UPDATE LOW_PRIORITY db.tbl SET field='new' WHERE id=1"),
|
|
"select field='new' from db.tbl where id=1",
|
|
"update with LOW_PRIORITY"
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select("UPDATE ignore db.tbl SET field='new' WHERE id=1"),
|
|
"select field='new' from db.tbl where id=1",
|
|
"update with IGNORE"
|
|
);
|
|
|
|
# convert DELETE ##############################################################
|
|
|
|
is(
|
|
$qr->convert_to_select(
|
|
'delete from foo where bar = baz',
|
|
),
|
|
'select * from foo where bar = baz',
|
|
'delete',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select(q{delete foo.bar b from foo.bar b left join baz.bat c on a=b where nine>eight}),
|
|
'select 1 from foo.bar b left join baz.bat c on a=b where nine>eight',
|
|
'Do not select * from a join',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select("DELETE LOW_PRIORITY FROM tbl WHERE id=1"),
|
|
"select * from tbl WHERE id=1",
|
|
"delete with LOW_PRIORITY"
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select("delete ignore from tbl WHERE id=1"),
|
|
"select * from tbl WHERE id=1",
|
|
"delete with IGNORE"
|
|
);
|
|
|
|
is(
|
|
$qr->convert_to_select("delete from file where id='ima-long-uuid-string'"),
|
|
"select * from file where id='ima-long-uuid-string'",
|
|
"Peter's DELTE"
|
|
);
|
|
|
|
# do not convert subqueries ###################################################
|
|
|
|
is(
|
|
$qr->convert_to_select("UPDATE mybbl_MBMessage SET groupId = (select groupId from Group_ where name = 'Guest')"),
|
|
undef,
|
|
'Do not convert subquery'
|
|
);
|
|
|
|
# #############################################################################
|
|
# wrap_in_derived()
|
|
# #############################################################################
|
|
|
|
is($qr->wrap_in_derived(), undef, 'Cannot wrap undef');
|
|
|
|
is(
|
|
$qr->wrap_in_derived(
|
|
'select * from foo',
|
|
),
|
|
'select 1 from (select * from foo) as x limit 1',
|
|
'wrap in derived table',
|
|
);
|
|
|
|
is(
|
|
$qr->wrap_in_derived('set timestamp=134'),
|
|
'set timestamp=134',
|
|
'Do not wrap non-SELECT queries',
|
|
);
|
|
|
|
# #############################################################################
|
|
# convert_select_list()
|
|
# #############################################################################
|
|
|
|
is(
|
|
$qr->convert_select_list('select * from tbl'),
|
|
'select 1 from tbl',
|
|
'Star to one',
|
|
);
|
|
|
|
is(
|
|
$qr->convert_select_list('select a, b, c from tbl'),
|
|
'select isnull(coalesce( a, b, c )) from tbl',
|
|
'column list to isnull/coalesce'
|
|
);
|
|
|
|
# #############################################################################
|
|
# shorten()
|
|
# #############################################################################
|
|
|
|
is(
|
|
$qr->shorten("insert into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"insert into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten simple insert",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("insert low_priority into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"insert low_priority into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten low_priority simple insert",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("insert delayed into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"insert delayed into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten delayed simple insert",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("insert high_priority into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"insert high_priority into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten high_priority simple insert",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("insert ignore into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"insert ignore into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten ignore simple insert",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("insert high_priority ignore into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"insert high_priority ignore into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten high_priority ignore simple insert",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("replace low_priority into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"replace low_priority into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten replace low_priority",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("replace delayed into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
|
|
"replace delayed into t(a,b,c) values(a,b,c) /*... omitted ...*/",
|
|
"shorten replace delayed",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("insert into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i) on duplicate key update a = b"),
|
|
"insert into t(a,b,c) values(a,b,c) /*... omitted ...*/on duplicate key update a = b",
|
|
"shorten insert ... odku",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten(
|
|
"select * from a where b in(" . join(',', 1..60) . ") and "
|
|
. "a in(" . join(',', 1..5000) . ")", 1),
|
|
"select * from a where b in(" . join(',', 1..20) . "/*... omitted 40 items ...*/)"
|
|
. " and a in(" . join(',', 1..20) . "/*... omitted 4980 items ...*/)",
|
|
"shorten two IN() lists of numbers",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("select * from a", 1),
|
|
"select * from a",
|
|
"Does not shorten strings it does not match",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("select * from a where b in(". join(',', 1..100) . ")", 1024),
|
|
"select * from a where b in(". join(',', 1..100) . ")",
|
|
"shorten IN() list numbers but not those that are already short enough",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("select * from a where b in(" . join(',', 1..100) . "'a,b')", 1),
|
|
"select * from a where b in(" . join(',', 1..20) . "/*... omitted 81 items ...*/)",
|
|
"Test case to document that commas are expected to mess up omitted count",
|
|
);
|
|
|
|
is(
|
|
$qr->shorten("select * from a where b in(1, 'a)b', " . join(',', 1..100) . ")", 1),
|
|
"select * from a where b in(1, 'a)b', " . join(',', 1..100) . ")",
|
|
"Test case to document that parens are expected to prevent shortening",
|
|
);
|
|
|
|
# #############################################################################
|
|
# distill()
|
|
# All tests below here are distill() tests. There's a lot of them.
|
|
# #############################################################################
|
|
|
|
is(
|
|
$qr->distill("SELECT /*!40001 SQL_NO_CACHE */ * FROM `film`"),
|
|
"SELECT film",
|
|
'Distills mysqldump SELECTs to selects',
|
|
);
|
|
|
|
is(
|
|
$qr->distill("CALL foo(1, 2, 3)"),
|
|
"CALL foo",
|
|
'Distills stored procedure calls specially',
|
|
);
|
|
|
|
is(
|
|
$qr->distill(
|
|
q{REPLACE /*foo.bar:3/3*/ INTO checksum.checksum (db, tbl, }
|
|
.q{chunk, boundaries, this_cnt, this_crc) SELECT 'foo', 'bar', }
|
|
.q{2 AS chunk_num, '`id` >= 2166633', COUNT(*) AS cnt, }
|
|
.q{LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `created_by`, }
|
|
.q{`created_date`, `updated_by`, `updated_date`, `ppc_provider`, }
|
|
.q{`account_name`, `provider_account_id`, `campaign_name`, }
|
|
.q{`provider_campaign_id`, `adgroup_name`, `provider_adgroup_id`, }
|
|
.q{`provider_keyword_id`, `provider_ad_id`, `foo`, `reason`, }
|
|
.q{`foo_bar_bazz_id`, `foo_bar_baz`, CONCAT(ISNULL(`created_by`), }
|
|
.q{ISNULL(`created_date`), ISNULL(`updated_by`), ISNULL(`updated_date`), }
|
|
.q{ISNULL(`ppc_provider`), ISNULL(`account_name`), }
|
|
.q{ISNULL(`provider_account_id`), ISNULL(`campaign_name`), }
|
|
.q{ISNULL(`provider_campaign_id`), ISNULL(`adgroup_name`), }
|
|
.q{ISNULL(`provider_adgroup_id`), ISNULL(`provider_keyword_id`), }
|
|
.q{ISNULL(`provider_ad_id`), ISNULL(`foo`), ISNULL(`reason`), }
|
|
.q{ISNULL(`foo_base_foo_id`), ISNULL(`fooe_foo_id`)))) AS UNSIGNED)), 10, }
|
|
.q{16)) AS crc FROM `foo`.`bar` USE INDEX (`PRIMARY`) WHERE }
|
|
.q{(`id` >= 2166633); }),
|
|
'REPLACE SELECT checksum.checksum foo.bar',
|
|
'Distills mk-table-checksum query',
|
|
);
|
|
|
|
is(
|
|
$qr->distill("use `foo`"),
|
|
"USE",
|
|
'distills USE',
|
|
);
|
|
|
|
is(
|
|
$qr->distill(q{delete foo.bar b from foo.bar b left join baz.bat c on a=b where nine>eight}),
|
|
'DELETE foo.bar baz.bat',
|
|
'distills and then collapses same tables',
|
|
);
|
|
|
|
is(
|
|
$qr->distill("select \n--bar\n foo"),
|
|
"SELECT",
|
|
'distills queries from DUAL',
|
|
);
|
|
|
|
is(
|
|
$qr->distill("select null, 5.001, 5001. from foo"),
|
|
"SELECT foo",
|
|
"distills simple select",
|
|
);
|
|
|
|
is(
|
|
$qr->distill("select 'hello', '\nhello\n', \"hello\", '\\'' from foo"),
|
|
"SELECT foo",
|
|
"distills with quoted strings",
|
|
);
|
|
|
|
is(
|
|
$qr->distill("select foo_1 from foo_2_3"),
|
|
'SELECT foo_?_?',
|
|
'distills numeric table names',
|
|
);
|
|
|
|
is(
|
|
$qr->distill("insert into abtemp.coxed select foo.bar from foo"),
|
|
'INSERT SELECT abtemp.coxed foo',
|
|
'distills insert/select',
|
|
);
|
|
|
|
is(
|
|
$qr->distill('insert into foo(a, b, c) values(2, 4, 5)'),
|
|
'INSERT foo',
|
|
'distills value lists',
|
|
);
|
|
|
|
is(
|
|
$qr->distill('select 1 union select 2 union select 4'),
|
|
'SELECT UNION',
|
|
'distill unions together',
|
|
);
|
|
|
|
is(
|
|
$qr->distill(
|
|
'delete from foo where bar = baz',
|
|
),
|
|
'DELETE foo',
|
|
'distills delete',
|
|
);
|
|
|
|
is(
|
|
$qr->distill('set timestamp=134'),
|
|
'SET',
|
|
'distills set',
|
|
);
|
|
|
|
is(
|
|
$qr->distill(
|
|
'replace into foo(a, b, c) values(1, 3, 5) on duplicate key update foo=bar',
|
|
),
|
|
'REPLACE UPDATE foo',
|
|
'distills ODKU',
|
|
);
|
|
|
|
is($qr->distill(
|
|
q{UPDATE GARDEN_CLUPL PL, GARDENJOB GC, APLTRACT_GARDENPLANT ABU SET }
|
|
. q{GC.MATCHING_POT = 5, GC.LAST_GARDENPOT = 5, GC.LAST_NAME=}
|
|
. q{'Rotary', GC.LAST_BUCKET='Pail', GC.LAST_UPDATE='2008-11-27 04:00:59'WHERE}
|
|
. q{ PL.APLTRACT_GARDENPLANT_ID = GC.APLTRACT_GARDENPLANT_ID AND PL.}
|
|
. q{APLTRACT_GARDENPLANT_ID = ABU.ID AND GC.MATCHING_POT = 0 AND GC.PERFORM_DIG=1}
|
|
. q{ AND ABU.DIG = 6 AND ( ((SOIL-COST) > -80.0}
|
|
. q{ AND BUGS < 60.0 AND (SOIL-COST) < 200.0) AND POTS < 10.0 )}),
|
|
'UPDATE GARDEN_CLUPL GARDENJOB APLTRACT_GARDENPLANT',
|
|
'distills where there is alias and comma-join',
|
|
);
|
|
|
|
is(
|
|
$qr->distill(q{SELECT STRAIGHT_JOIN distinct foo, bar FROM A, B, C}),
|
|
'SELECT A B C',
|
|
'distill with STRAIGHT_JOIN',
|
|
);
|
|
|
|
is (
|
|
$qr->distill(q{
|
|
REPLACE DELAYED INTO
|
|
`db1`.`tbl2`(`col1`,col2)
|
|
VALUES ('617653','2007-09-11')}),
|
|
'REPLACE db?.tbl?',
|
|
'distills replace-delayed',
|
|
);
|
|
|
|
is(
|
|
$qr->distill(
|
|
'update foo inner join bar using(baz) set big=little',
|
|
),
|
|
'UPDATE foo bar',
|
|
'distills update-multi',
|
|
);
|
|
|
|
is(
|
|
$qr->distill('
|
|
update db2.tbl1 as p
|
|
inner join (
|
|
select p2.col1, p2.col2
|
|
from db2.tbl1 as p2
|
|
inner join db2.tbl3 as ba
|
|
on p2.col1 = ba.tbl3
|
|
where col4 = 0
|
|
order by priority desc, col1, col2
|
|
limit 10
|
|
) as chosen on chosen.col1 = p.col1
|
|
and chosen.col2 = p.col2
|
|
set p.col4 = 149945'),
|
|
'UPDATE SELECT db?.tbl?',
|
|
'distills complex subquery',
|
|
);
|
|
|
|
is(
|
|
$qr->distill(
|
|
'replace into checksum.checksum select `last_update`, `foo` from foo.foo'),
|
|
'REPLACE SELECT checksum.checksum foo.foo',
|
|
'distill with reserved words');
|
|
|
|
is($qr->distill('SHOW STATUS'), 'SHOW STATUS', 'distill SHOW STATUS');
|
|
|
|
is($qr->distill('commit'), 'COMMIT', 'distill COMMIT');
|
|
|
|
is($qr->distill('FLUSH TABLES WITH READ LOCK'), 'FLUSH', 'distill FLUSH');
|
|
|
|
is($qr->distill('BEGIN'), 'BEGIN', 'distill BEGIN');
|
|
|
|
is($qr->distill('start'), 'START', 'distill START');
|
|
|
|
is($qr->distill('ROLLBACK'), 'ROLLBACK', 'distill ROLLBACK');
|
|
|
|
is(
|
|
$qr->distill(
|
|
'insert into foo select * from bar join baz using (bat)',
|
|
),
|
|
'INSERT SELECT foo bar baz',
|
|
'distills insert select',
|
|
);
|
|
|
|
is(
|
|
$qr->distill('create database foo'),
|
|
'CREATE DATABASE foo',
|
|
'distills create database'
|
|
);
|
|
is(
|
|
$qr->distill('create table foo'),
|
|
'CREATE TABLE foo',
|
|
'distills create table'
|
|
);
|
|
is(
|
|
$qr->distill('alter database foo'),
|
|
'ALTER DATABASE foo',
|
|
'distills alter database'
|
|
);
|
|
is(
|
|
$qr->distill('alter table foo'),
|
|
'ALTER TABLE foo',
|
|
'distills alter table'
|
|
);
|
|
is(
|
|
$qr->distill('drop database foo'),
|
|
'DROP DATABASE foo',
|
|
'distills drop database'
|
|
);
|
|
is(
|
|
$qr->distill('drop table foo'),
|
|
'DROP TABLE foo',
|
|
'distills drop table'
|
|
);
|
|
is(
|
|
$qr->distill('rename database foo'),
|
|
'RENAME DATABASE foo',
|
|
'distills rename database'
|
|
);
|
|
is(
|
|
$qr->distill('rename table foo'),
|
|
'RENAME TABLE foo',
|
|
'distills rename table'
|
|
);
|
|
is(
|
|
$qr->distill('truncate table foo'),
|
|
'TRUNCATE TABLE foo',
|
|
'distills truncate table'
|
|
);
|
|
|
|
# Test generic distillation for memcached, http, etc.
|
|
my $trf = sub {
|
|
my ( $query ) = @_;
|
|
$query =~ s/(\S+ \S+?)(?:[?;].+)/$1/;
|
|
return $query;
|
|
};
|
|
|
|
is(
|
|
$qr->distill('get percona.com/', generic => 1, trf => $trf),
|
|
'GET percona.com/',
|
|
'generic distill HTTP get'
|
|
);
|
|
|
|
is(
|
|
$qr->distill('get percona.com/page.html?some=thing', generic => 1, trf => $trf),
|
|
'GET percona.com/page.html',
|
|
'generic distill HTTP get with args'
|
|
);
|
|
|
|
is(
|
|
$qr->distill('put percona.com/contacts.html', generic => 1, trf => $trf),
|
|
'PUT percona.com/contacts.html',
|
|
'generic distill HTTP put'
|
|
);
|
|
|
|
is(
|
|
$qr->distill(
|
|
'update foo set bar=baz where bat=fiz',
|
|
),
|
|
'UPDATE foo',
|
|
'distills update',
|
|
);
|
|
|
|
# Issue 563: Lock tables is not distilled
|
|
is(
|
|
$qr->distill('LOCK TABLES foo WRITE'),
|
|
'LOCK foo',
|
|
'distills lock tables'
|
|
);
|
|
is(
|
|
$qr->distill('LOCK TABLES foo READ, bar WRITE'),
|
|
'LOCK foo bar',
|
|
'distills lock tables (2 tables)'
|
|
);
|
|
is(
|
|
$qr->distill('UNLOCK TABLES'),
|
|
'UNLOCK',
|
|
'distills unlock tables'
|
|
);
|
|
|
|
# Issue 712: Queries not handled by "distill"
|
|
is(
|
|
$qr->distill('XA START 0x123'),
|
|
'XA_START',
|
|
'distills xa start'
|
|
);
|
|
is(
|
|
$qr->distill('XA PREPARE 0x123'),
|
|
'XA_PREPARE',
|
|
'distills xa prepare'
|
|
);
|
|
is(
|
|
$qr->distill('XA COMMIT 0x123'),
|
|
'XA_COMMIT',
|
|
'distills xa commit'
|
|
);
|
|
is(
|
|
$qr->distill('XA END 0x123'),
|
|
'XA_END',
|
|
'distills xa end'
|
|
);
|
|
|
|
is(
|
|
$qr->distill("/* mysql-connector-java-5.1-nightly-20090730 ( Revision: \${svn.Revision} ) */SHOW VARIABLES WHERE Variable_name ='language' OR Variable_name =
|
|
'net_write_timeout' OR Variable_name = 'interactive_timeout' OR
|
|
Variable_name = 'wait_timeout' OR Variable_name = 'character_set_client' OR
|
|
Variable_name = 'character_set_connection' OR Variable_name =
|
|
'character_set' OR Variable_name = 'character_set_server' OR Variable_name
|
|
= 'tx_isolation' OR Variable_name = 'transaction_isolation' OR
|
|
Variable_name = 'character_set_results' OR Variable_name = 'timezone' OR
|
|
Variable_name = 'time_zone' OR Variable_name = 'system_time_zone' OR
|
|
Variable_name = 'lower_case_table_names' OR Variable_name =
|
|
'max_allowed_packet' OR Variable_name = 'net_buffer_length' OR
|
|
Variable_name = 'sql_mode' OR Variable_name = 'query_cache_type' OR
|
|
Variable_name = 'query_cache_size' OR Variable_name = 'init_connect'"),
|
|
'SHOW VARIABLES',
|
|
'distills /* comment */SHOW VARIABLES'
|
|
);
|
|
|
|
# This is a list of all the types of syntax for SHOW on
|
|
# http://dev.mysql.com/doc/refman/5.0/en/show.html
|
|
my %status_tests = (
|
|
'SHOW BINARY LOGS' => 'SHOW BINARY LOGS',
|
|
'SHOW BINLOG EVENTS in "log_name"' => 'SHOW BINLOG EVENTS',
|
|
'SHOW CHARACTER SET LIKE "pattern"' => 'SHOW CHARACTER SET',
|
|
'SHOW COLLATION WHERE "something"' => 'SHOW COLLATION',
|
|
'SHOW COLUMNS FROM tbl' => 'SHOW COLUMNS',
|
|
'SHOW FULL COLUMNS FROM tbl' => 'SHOW COLUMNS',
|
|
'SHOW COLUMNS FROM tbl in db' => 'SHOW COLUMNS',
|
|
'SHOW COLUMNS FROM tbl IN db LIKE "pattern"' => 'SHOW COLUMNS',
|
|
'SHOW CREATE DATABASE db_name' => 'SHOW CREATE DATABASE',
|
|
'SHOW CREATE SCHEMA db_name' => 'SHOW CREATE DATABASE',
|
|
'SHOW CREATE FUNCTION func' => 'SHOW CREATE FUNCTION',
|
|
'SHOW CREATE PROCEDURE proc' => 'SHOW CREATE PROCEDURE',
|
|
'SHOW CREATE TABLE tbl_name' => 'SHOW CREATE TABLE',
|
|
'SHOW CREATE VIEW vw_name' => 'SHOW CREATE VIEW',
|
|
'SHOW DATABASES' => 'SHOW DATABASES',
|
|
'SHOW SCHEMAS' => 'SHOW DATABASES',
|
|
'SHOW DATABASES LIKE "pattern"' => 'SHOW DATABASES',
|
|
'SHOW DATABASES WHERE foo=bar' => 'SHOW DATABASES',
|
|
'SHOW ENGINE ndb status' => 'SHOW NDB STATUS',
|
|
'SHOW ENGINE innodb status' => 'SHOW INNODB STATUS',
|
|
'SHOW ENGINES' => 'SHOW ENGINES',
|
|
'SHOW STORAGE ENGINES' => 'SHOW ENGINES',
|
|
'SHOW ERRORS' => 'SHOW ERRORS',
|
|
'SHOW ERRORS limit 5' => 'SHOW ERRORS',
|
|
'SHOW COUNT(*) ERRORS' => 'SHOW ERRORS',
|
|
'SHOW FUNCTION CODE func' => 'SHOW FUNCTION CODE',
|
|
'SHOW FUNCTION STATUS' => 'SHOW FUNCTION STATUS',
|
|
'SHOW FUNCTION STATUS LIKE "pattern"' => 'SHOW FUNCTION STATUS',
|
|
'SHOW FUNCTION STATUS WHERE foo=bar' => 'SHOW FUNCTION STATUS',
|
|
'SHOW GRANTS' => 'SHOW GRANTS',
|
|
'SHOW GRANTS FOR user@localhost' => 'SHOW GRANTS',
|
|
'SHOW INDEX' => 'SHOW INDEX',
|
|
'SHOW INDEXES' => 'SHOW INDEX',
|
|
'SHOW KEYS' => 'SHOW INDEX',
|
|
'SHOW INDEX FROM tbl' => 'SHOW INDEX',
|
|
'SHOW INDEX FROM tbl IN db' => 'SHOW INDEX',
|
|
'SHOW INDEX IN tbl FROM db' => 'SHOW INDEX',
|
|
'SHOW INNODB STATUS' => 'SHOW INNODB STATUS',
|
|
'SHOW LOGS' => 'SHOW LOGS',
|
|
'SHOW MASTER STATUS' => 'SHOW MASTER STATUS',
|
|
'SHOW MUTEX STATUS' => 'SHOW MUTEX STATUS',
|
|
'SHOW OPEN TABLES' => 'SHOW OPEN TABLES',
|
|
'SHOW OPEN TABLES FROM db' => 'SHOW OPEN TABLES',
|
|
'SHOW OPEN TABLES IN db' => 'SHOW OPEN TABLES',
|
|
'SHOW OPEN TABLES IN db LIKE "pattern"' => 'SHOW OPEN TABLES',
|
|
'SHOW OPEN TABLES IN db WHERE foo=bar' => 'SHOW OPEN TABLES',
|
|
'SHOW OPEN TABLES WHERE foo=bar' => 'SHOW OPEN TABLES',
|
|
'SHOW PRIVILEGES' => 'SHOW PRIVILEGES',
|
|
'SHOW PROCEDURE CODE proc' => 'SHOW PROCEDURE CODE',
|
|
'SHOW PROCEDURE STATUS' => 'SHOW PROCEDURE STATUS',
|
|
'SHOW PROCEDURE STATUS LIKE "pattern"' => 'SHOW PROCEDURE STATUS',
|
|
'SHOW PROCEDURE STATUS WHERE foo=bar' => 'SHOW PROCEDURE STATUS',
|
|
'SHOW PROCESSLIST' => 'SHOW PROCESSLIST',
|
|
'SHOW FULL PROCESSLIST' => 'SHOW PROCESSLIST',
|
|
'SHOW PROFILE' => 'SHOW PROFILE',
|
|
'SHOW PROFILES' => 'SHOW PROFILES',
|
|
'SHOW PROFILES CPU FOR QUERY 1' => 'SHOW PROFILES CPU',
|
|
'SHOW SLAVE HOSTS' => 'SHOW SLAVE HOSTS',
|
|
'SHOW SLAVE STATUS' => 'SHOW SLAVE STATUS',
|
|
'SHOW STATUS' => 'SHOW STATUS',
|
|
'SHOW GLOBAL STATUS' => 'SHOW GLOBAL STATUS',
|
|
'SHOW SESSION STATUS' => 'SHOW STATUS',
|
|
'SHOW STATUS LIKE "pattern"' => 'SHOW STATUS',
|
|
'SHOW STATUS WHERE foo=bar' => 'SHOW STATUS',
|
|
'SHOW TABLE STATUS' => 'SHOW TABLE STATUS',
|
|
'SHOW TABLE STATUS FROM db_name' => 'SHOW TABLE STATUS',
|
|
'SHOW TABLE STATUS IN db_name' => 'SHOW TABLE STATUS',
|
|
'SHOW TABLE STATUS LIKE "pattern"' => 'SHOW TABLE STATUS',
|
|
'SHOW TABLE STATUS WHERE foo=bar' => 'SHOW TABLE STATUS',
|
|
'SHOW TABLES' => 'SHOW TABLES',
|
|
'SHOW FULL TABLES' => 'SHOW TABLES',
|
|
'SHOW TABLES FROM db' => 'SHOW TABLES',
|
|
'SHOW TABLES IN db' => 'SHOW TABLES',
|
|
'SHOW TABLES LIKE "pattern"' => 'SHOW TABLES',
|
|
'SHOW TABLES FROM db LIKE "pattern"' => 'SHOW TABLES',
|
|
'SHOW TABLES WHERE foo=bar' => 'SHOW TABLES',
|
|
'SHOW TRIGGERS' => 'SHOW TRIGGERS',
|
|
'SHOW TRIGGERS IN db' => 'SHOW TRIGGERS',
|
|
'SHOW TRIGGERS FROM db' => 'SHOW TRIGGERS',
|
|
'SHOW TRIGGERS LIKE "pattern"' => 'SHOW TRIGGERS',
|
|
'SHOW TRIGGERS WHERE foo=bar' => 'SHOW TRIGGERS',
|
|
'SHOW VARIABLES' => 'SHOW VARIABLES',
|
|
'SHOW GLOBAL VARIABLES' => 'SHOW GLOBAL VARIABLES',
|
|
'SHOW SESSION VARIABLES' => 'SHOW VARIABLES',
|
|
'SHOW VARIABLES LIKE "pattern"' => 'SHOW VARIABLES',
|
|
'SHOW VARIABLES WHERE foo=bar' => 'SHOW VARIABLES',
|
|
'SHOW WARNINGS' => 'SHOW WARNINGS',
|
|
'SHOW WARNINGS LIMIT 5' => 'SHOW WARNINGS',
|
|
'SHOW COUNT(*) WARNINGS' => 'SHOW WARNINGS',
|
|
'SHOW COUNT ( *) WARNINGS' => 'SHOW WARNINGS',
|
|
);
|
|
|
|
foreach my $key ( keys %status_tests ) {
|
|
is($qr->distill($key), $status_tests{$key}, "distills $key");
|
|
}
|
|
|
|
is(
|
|
$qr->distill('SHOW SLAVE STATUS'),
|
|
'SHOW SLAVE STATUS',
|
|
'distills SHOW SLAVE STATUS'
|
|
);
|
|
is(
|
|
$qr->distill('SHOW INNODB STATUS'),
|
|
'SHOW INNODB STATUS',
|
|
'distills SHOW INNODB STATUS'
|
|
);
|
|
is(
|
|
$qr->distill('SHOW CREATE TABLE'),
|
|
'SHOW CREATE TABLE',
|
|
'distills SHOW CREATE TABLE'
|
|
);
|
|
|
|
my @show = qw(COLUMNS GRANTS INDEX STATUS TABLES TRIGGERS WARNINGS);
|
|
foreach my $show ( @show ) {
|
|
is(
|
|
$qr->distill("SHOW $show"),
|
|
"SHOW $show",
|
|
"distills SHOW $show"
|
|
);
|
|
}
|
|
|
|
# Issue 735: mk-query-digest doesn't distill query correctly
|
|
is(
|
|
$qr->distill('SHOW /*!50002 GLOBAL */ STATUS'),
|
|
'SHOW GLOBAL STATUS',
|
|
"distills SHOW /*!50002 GLOBAL */ STATUS"
|
|
);
|
|
|
|
is(
|
|
$qr->distill('SHOW /*!50002 ENGINE */ INNODB STATUS'),
|
|
'SHOW INNODB STATUS',
|
|
"distills SHOW INNODB STATUS"
|
|
);
|
|
|
|
is(
|
|
$qr->distill('SHOW MASTER LOGS'),
|
|
'SHOW MASTER LOGS',
|
|
"distills SHOW MASTER LOGS"
|
|
);
|
|
|
|
is(
|
|
$qr->distill('SHOW GLOBAL STATUS'),
|
|
'SHOW GLOBAL STATUS',
|
|
"distills SHOW GLOBAL STATUS"
|
|
);
|
|
|
|
is(
|
|
$qr->distill('SHOW GLOBAL VARIABLES'),
|
|
'SHOW GLOBAL VARIABLES',
|
|
"distills SHOW GLOBAL VARIABLES"
|
|
);
|
|
|
|
is(
|
|
$qr->distill('administrator command: Statistics'),
|
|
'ADMIN STATISTICS',
|
|
"distills ADMIN STATISTICS"
|
|
);
|
|
|
|
# Issue 781: mk-query-digest doesn't distill or extract tables properly
|
|
is(
|
|
$qr->distill("SELECT `id` FROM (`field`) WHERE `id` = '10000016228434112371782015185031'"),
|
|
'SELECT field',
|
|
'distills SELECT clm from (`tbl`)'
|
|
);
|
|
|
|
is(
|
|
$qr->distill("INSERT INTO (`jedi_forces`) (name, side, email) values ('Anakin Skywalker', 'jedi', 'anakin_skywalker_at_jedi.sw')"),
|
|
'INSERT jedi_forces',
|
|
'distills INSERT INTO (`tbl`)'
|
|
);
|
|
|
|
is(
|
|
$qr->distill("UPDATE (`jedi_forces`) set side = 'dark' and name = 'Lord Vader' where name = 'Anakin Skywalker'"),
|
|
'UPDATE jedi_forces',
|
|
'distills UPDATE (`tbl`)'
|
|
);
|
|
|
|
is(
|
|
$qr->distill("select c from (tbl1 JOIN tbl2 on (id)) where x=y"),
|
|
'SELECT tbl?',
|
|
'distills SELECT (t1 JOIN t2)'
|
|
);
|
|
|
|
is(
|
|
$qr->distill("insert into (t1) value('a')"),
|
|
'INSERT t?',
|
|
'distills INSERT (tbl)'
|
|
);
|
|
|
|
# Something that will (should) never distill.
|
|
is(
|
|
$qr->distill("-- how /*did*/ `THIS` #happen?"),
|
|
'',
|
|
'distills nonsense'
|
|
);
|
|
|
|
is(
|
|
$qr->distill("peek tbl poke db"),
|
|
'',
|
|
'distills non-SQL'
|
|
);
|
|
|
|
# Issue 1176: mk-query-digest incorrectly distills queries with certain keywords
|
|
|
|
# I want to see first how this is handled. It's correct because the query
|
|
# really does read from tables a and c; table b is just an alias.
|
|
is(
|
|
$qr->distill("select c from (select * from a) as b where exists (select * from c where id is null)"),
|
|
"SELECT a c",
|
|
"distills SELECT with subquery in FROM and WHERE"
|
|
);
|
|
|
|
is(
|
|
$qr->distill("select c from t where col='delete'"),
|
|
'SELECT t',
|
|
'distills SELECT with keyword as value (issue 1176)'
|
|
);
|
|
|
|
is(
|
|
$qr->distill('SELECT c, replace(foo, bar) FROM t WHERE col <> "insert"'),
|
|
'SELECT t',
|
|
'distills SELECT with REPLACE function (issue 1176)'
|
|
);
|
|
|
|
# LOAD DATA
|
|
# https://bugs.launchpad.net/percona-toolkit/+bug/821692
|
|
# INSERT and REPLACE without INTO
|
|
# https://bugs.launchpad.net/percona-toolkit/+bug/984053
|
|
is(
|
|
$qr->distill("LOAD DATA LOW_PRIORITY LOCAL INFILE 'file' INTO TABLE tbl"),
|
|
"LOAD DATA tbl",
|
|
"distill LOAD DATA (bug 821692)"
|
|
);
|
|
|
|
is(
|
|
$qr->distill("LOAD DATA LOW_PRIORITY LOCAL INFILE 'file' INTO TABLE `tbl`"),
|
|
"LOAD DATA tbl",
|
|
"distill LOAD DATA (bug 821692)"
|
|
);
|
|
|
|
is(
|
|
$qr->distill("insert ignore_bar (id) values (4029731)"),
|
|
"INSERT ignore_bar",
|
|
"distill INSERT without INTO (bug 984053)"
|
|
);
|
|
|
|
is(
|
|
$qr->distill("replace ignore_bar (id) values (4029731)"),
|
|
"REPLACE ignore_bar",
|
|
"distill REPLACE without INTO (bug 984053)"
|
|
);
|
|
|
|
# IF EXISTS
|
|
# https://bugs.launchpad.net/percona-toolkit/+bug/821690
|
|
is(
|
|
$qr->distill("DROP TABLE IF EXISTS foo"),
|
|
"DROP TABLE foo",
|
|
"distill DROP TABLE IF EXISTS foo (bug 821690)"
|
|
);
|
|
|
|
is(
|
|
$qr->distill("CREATE TABLE IF NOT EXISTS foo"),
|
|
"CREATE TABLE foo",
|
|
"distill CREATE TABLE IF NOT EXISTS foo",
|
|
);
|
|
|
|
|
|
$qr = new QueryRewriter(
|
|
QueryParser => $qp,
|
|
match_embedded_numbers => 1,
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"SELECT * FROM prices2.t1 where id=1"
|
|
),
|
|
"select * from prices2.t1 where id=?",
|
|
"Fingerprint db.tbl<number>name (preserve number)"
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint(
|
|
"SELECT i FROM d.t WHERE i=\"3\""
|
|
),
|
|
"select i from d.t where i=?",
|
|
"Fingerprint db.tbl<number>name (preserve number)"
|
|
);
|
|
|
|
is(
|
|
$qr->fingerprint("CALL foo(1, 2, 3)"),
|
|
"call foo",
|
|
'Fingerprints stored procedure calls specially',
|
|
);
|
|
# #############################################################################
|
|
# Done.
|
|
# #############################################################################
|
|
done_testing;
|