mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-24 13:25:01 +00:00
Update modules in pt-query-advisor, replacing MySQLDump with TableParser.
This commit is contained in:
@@ -141,12 +141,14 @@ sub parse_options {
|
|||||||
sub as_string {
|
sub as_string {
|
||||||
my ( $self, $dsn, $props ) = @_;
|
my ( $self, $dsn, $props ) = @_;
|
||||||
return $dsn unless ref $dsn;
|
return $dsn unless ref $dsn;
|
||||||
my %allowed = $props ? map { $_=>1 } @$props : ();
|
my @keys = $props ? @$props : sort keys %$dsn;
|
||||||
return join(',',
|
return join(',',
|
||||||
map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
|
map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
|
||||||
grep { defined $dsn->{$_} && $self->{opts}->{$_} }
|
grep {
|
||||||
grep { !$props || $allowed{$_} }
|
exists $self->{opts}->{$_}
|
||||||
sort keys %$dsn );
|
&& exists $dsn->{$_}
|
||||||
|
&& defined $dsn->{$_}
|
||||||
|
} @keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
@@ -1315,7 +1317,7 @@ sub _parse_size {
|
|||||||
$opt->{value} = ($pre || '') . $num;
|
$opt->{value} = ($pre || '') . $num;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self->save_error("Invalid size for --$opt->{long}");
|
$self->save_error("Invalid size for --$opt->{long}: $val");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1460,6 +1462,48 @@ sub join_quote {
|
|||||||
return $db ? "$db.$tbl" : $tbl;
|
return $db ? "$db.$tbl" : $tbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub serialize_list {
|
||||||
|
my ( $self, @args ) = @_;
|
||||||
|
return unless @args;
|
||||||
|
|
||||||
|
return $args[0] if @args == 1 && !defined $args[0];
|
||||||
|
|
||||||
|
die "Cannot serialize multiple values with undef/NULL"
|
||||||
|
if grep { !defined $_ } @args;
|
||||||
|
|
||||||
|
return join ',', map { quotemeta } @args;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub deserialize_list {
|
||||||
|
my ( $self, $string ) = @_;
|
||||||
|
return $string unless defined $string;
|
||||||
|
my @escaped_parts = $string =~ /
|
||||||
|
\G # Start of string, or end of previous match.
|
||||||
|
( # Each of these is an element in the original list.
|
||||||
|
[^\\,]* # Anything not a backslash or a comma
|
||||||
|
(?: # When we get here, we found one of the above.
|
||||||
|
\\. # A backslash followed by something so we can continue
|
||||||
|
[^\\,]* # Same as above.
|
||||||
|
)* # Repeat zero of more times.
|
||||||
|
)
|
||||||
|
, # Comma dividing elements
|
||||||
|
/sxgc;
|
||||||
|
|
||||||
|
push @escaped_parts, pos($string) ? substr( $string, pos($string) ) : $string;
|
||||||
|
|
||||||
|
my @unescaped_parts = map {
|
||||||
|
my $part = $_;
|
||||||
|
|
||||||
|
my $char_class = utf8::is_utf8($part) # If it's a UTF-8 string,
|
||||||
|
? qr/(?=\p{ASCII})\W/ # We only care about non-word
|
||||||
|
: qr/(?=\p{ASCII})\W|[\x{80}-\x{FF}]/; # Otherwise,
|
||||||
|
$part =~ s/\\($char_class)/$1/g;
|
||||||
|
$part;
|
||||||
|
} @escaped_parts;
|
||||||
|
|
||||||
|
return @unescaped_parts;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
# ###########################################################################
|
# ###########################################################################
|
||||||
@@ -2354,8 +2398,25 @@ sub fingerprint {
|
|||||||
$query =~ s/\\["']//g; # quoted strings
|
$query =~ s/\\["']//g; # quoted strings
|
||||||
$query =~ s/".*?"/?/sg; # quoted strings
|
$query =~ s/".*?"/?/sg; # quoted strings
|
||||||
$query =~ s/'.*?'/?/sg; # quoted strings
|
$query =~ s/'.*?'/?/sg; # quoted strings
|
||||||
$query =~ s/[0-9+-][0-9a-f.xb+-]*/?/g;# Anything vaguely resembling numbers
|
|
||||||
$query =~ s/[xb.+-]\?/?/g; # Clean up leftovers
|
if ( $self->{match_md5_checksums} ) {
|
||||||
|
$query =~ s/([._-])[a-f0-9]{32}/$1?/g;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !$self->{match_embedded_numbers} ) {
|
||||||
|
$query =~ s/[0-9+-][0-9a-f.xb+-]*/?/g;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$query =~ s/\b[0-9+-][0-9a-f.xb+-]*/?/g;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $self->{match_md5_checksums} ) {
|
||||||
|
$query =~ s/[xb+-]\?/?/g;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$query =~ s/[xb.+-]\?/?/g;
|
||||||
|
}
|
||||||
|
|
||||||
$query =~ s/\A\s+//; # Chop off leading whitespace
|
$query =~ s/\A\s+//; # Chop off leading whitespace
|
||||||
chomp $query; # Kill trailing whitespace
|
chomp $query; # Kill trailing whitespace
|
||||||
$query =~ tr[ \n\t\r\f][ ]s; # Collapse whitespace
|
$query =~ tr[ \n\t\r\f][ ]s; # Collapse whitespace
|
||||||
@@ -3435,7 +3496,7 @@ sub get_rules {
|
|||||||
foreach my $pred ( @$where ) {
|
foreach my $pred ( @$where ) {
|
||||||
my $val = $pred->{right_arg};
|
my $val = $pred->{right_arg};
|
||||||
next unless $val;
|
next unless $val;
|
||||||
return 0 if $val =~ m/^\d+$/ && $orderby_col{lc $pred->{left_arg}};
|
return 0 if $val =~ m/^\d+$/ && $orderby_col{lc($pred->{left_arg} || '')};
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
@@ -3494,7 +3555,7 @@ sub get_rules {
|
|||||||
my $cols = $event->{query_struct}->{columns};
|
my $cols = $event->{query_struct}->{columns};
|
||||||
return unless $cols;
|
return unless $cols;
|
||||||
foreach my $col ( @$cols ) {
|
foreach my $col ( @$cols ) {
|
||||||
return 0 if $col->{col} eq '*';
|
return 0 if $col->{col} eq '*' && !$col->{func};
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
@@ -4067,6 +4128,21 @@ my $column_ident = qr/(?:
|
|||||||
(?:$ident_alias)? # optional alias
|
(?:$ident_alias)? # optional alias
|
||||||
)/xo;
|
)/xo;
|
||||||
|
|
||||||
|
my $function_ident = qr/
|
||||||
|
\b
|
||||||
|
(
|
||||||
|
\w+ # function name
|
||||||
|
\( # opening parenthesis
|
||||||
|
[^\)]+ # function args, if any
|
||||||
|
\) # closing parenthesis
|
||||||
|
)
|
||||||
|
/x;
|
||||||
|
|
||||||
|
my %ignore_function = (
|
||||||
|
INDEX => 1,
|
||||||
|
KEY => 1,
|
||||||
|
);
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my ( $class, %args ) = @_;
|
my ( $class, %args ) = @_;
|
||||||
my $self = {
|
my $self = {
|
||||||
@@ -4085,6 +4161,7 @@ sub parse {
|
|||||||
|REPLACE
|
|REPLACE
|
||||||
|SELECT
|
|SELECT
|
||||||
|UPDATE
|
|UPDATE
|
||||||
|
|CREATE
|
||||||
)/xi;
|
)/xi;
|
||||||
|
|
||||||
$query = $self->clean_query($query);
|
$query = $self->clean_query($query);
|
||||||
@@ -4108,6 +4185,11 @@ sub parse {
|
|||||||
@subqueries = $self->remove_subqueries($query);
|
@subqueries = $self->remove_subqueries($query);
|
||||||
$query = shift @subqueries;
|
$query = shift @subqueries;
|
||||||
}
|
}
|
||||||
|
elsif ( $type eq 'create' && $query =~ m/\s+SELECT/ ) {
|
||||||
|
PTDEBUG && _d('CREATE..SELECT');
|
||||||
|
($subqueries[0]->{query}) = $query =~ m/\s+(SELECT .+)/;
|
||||||
|
$query =~ s/\s+SELECT.+//;
|
||||||
|
}
|
||||||
|
|
||||||
my $parse_func = "parse_$type";
|
my $parse_func = "parse_$type";
|
||||||
my $struct = $self->$parse_func($query);
|
my $struct = $self->$parse_func($query);
|
||||||
@@ -4316,11 +4398,31 @@ sub parse_update {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub parse_create {
|
||||||
|
my ($self, $query) = @_;
|
||||||
|
my ($obj, $name) = $query =~ m/
|
||||||
|
(\S+)\s+
|
||||||
|
(?:IF NOT EXISTS\s+)?
|
||||||
|
(\S+)
|
||||||
|
/xi;
|
||||||
|
return {
|
||||||
|
object => lc $obj,
|
||||||
|
name => $name,
|
||||||
|
unknown => undef,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
sub parse_from {
|
sub parse_from {
|
||||||
my ( $self, $from ) = @_;
|
my ( $self, $from ) = @_;
|
||||||
return unless $from;
|
return unless $from;
|
||||||
PTDEBUG && _d('Parsing FROM', $from);
|
PTDEBUG && _d('Parsing FROM', $from);
|
||||||
|
|
||||||
|
my $using_cols;
|
||||||
|
($from, $using_cols) = $self->remove_using_columns($from);
|
||||||
|
|
||||||
|
my $funcs;
|
||||||
|
($from, $funcs) = $self->remove_functions($from);
|
||||||
|
|
||||||
my $comma_join = qr/(?>\s*,\s*)/;
|
my $comma_join = qr/(?>\s*,\s*)/;
|
||||||
my $ansi_join = qr/(?>
|
my $ansi_join = qr/(?>
|
||||||
\s+
|
\s+
|
||||||
@@ -4348,13 +4450,10 @@ sub parse_from {
|
|||||||
|
|
||||||
$join->{condition} = lc $join_condition_verb;
|
$join->{condition} = lc $join_condition_verb;
|
||||||
if ( $join->{condition} eq 'on' ) {
|
if ( $join->{condition} eq 'on' ) {
|
||||||
my $where = $self->parse_where($join_condition_value);
|
$join->{where} = $self->parse_where($join_condition_value, $funcs);
|
||||||
$join->{where} = $where;
|
|
||||||
}
|
}
|
||||||
else { # USING
|
else { # USING
|
||||||
$join_condition_value =~ s/^\s*\(//;
|
$join->{columns} = $self->_parse_csv(shift @$using_cols);
|
||||||
$join_condition_value =~ s/\)\s*$//;
|
|
||||||
$join->{columns} = $self->_parse_csv($join_condition_value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ( $thing =~ m/(?:,|JOIN)/i ) {
|
elsif ( $thing =~ m/(?:,|JOIN)/i ) {
|
||||||
@@ -4433,13 +4532,13 @@ sub parse_table_reference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub parse_where {
|
sub parse_where {
|
||||||
my ( $self, $where ) = @_;
|
my ( $self, $where, $functions ) = @_;
|
||||||
return unless $where;
|
return unless $where;
|
||||||
PTDEBUG && _d("Parsing WHERE", $where);
|
PTDEBUG && _d("Parsing WHERE", $where);
|
||||||
|
|
||||||
my $op_symbol = qr/
|
my $op_symbol = qr/
|
||||||
(?:
|
(?:
|
||||||
<=
|
<=(?:>)?
|
||||||
|>=
|
|>=
|
||||||
|<>
|
|<>
|
||||||
|!=
|
|!=
|
||||||
@@ -4546,6 +4645,11 @@ sub parse_where {
|
|||||||
$val = lc $val;
|
$val = lc $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $functions ) {
|
||||||
|
$col = shift @$functions if $col =~ m/__FUNC\d+__/;
|
||||||
|
$val = shift @$functions if $val =~ m/__FUNC\d+__/;
|
||||||
|
}
|
||||||
|
|
||||||
push @predicates, {
|
push @predicates, {
|
||||||
predicate => $conj,
|
predicate => $conj,
|
||||||
left_arg => $col,
|
left_arg => $col,
|
||||||
@@ -4813,6 +4917,44 @@ sub remove_subqueries {
|
|||||||
return $query, @subqueries;
|
return $query, @subqueries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub remove_using_columns {
|
||||||
|
my ($self, $from) = @_;
|
||||||
|
return unless $from;
|
||||||
|
PTDEBUG && _d('Removing cols from USING clauses');
|
||||||
|
my $using = qr/
|
||||||
|
\bUSING
|
||||||
|
\s*
|
||||||
|
\(
|
||||||
|
([^\)]+)
|
||||||
|
\)
|
||||||
|
/xi;
|
||||||
|
my @cols;
|
||||||
|
$from =~ s/$using/push @cols, $1; "USING ($#cols)"/eg;
|
||||||
|
PTDEBUG && _d('FROM:', $from, Dumper(\@cols));
|
||||||
|
return $from, \@cols;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub replace_function {
|
||||||
|
my ($func, $funcs) = @_;
|
||||||
|
my ($func_name) = $func =~ m/^(\w+)/;
|
||||||
|
if ( !$ignore_function{uc $func_name} ) {
|
||||||
|
my $n = scalar @$funcs;
|
||||||
|
push @$funcs, $func;
|
||||||
|
return "__FUNC${n}__";
|
||||||
|
}
|
||||||
|
return $func;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub remove_functions {
|
||||||
|
my ($self, $clause) = @_;
|
||||||
|
return unless $clause;
|
||||||
|
PTDEBUG && _d('Removing functions from clause:', $clause);
|
||||||
|
my @funcs;
|
||||||
|
$clause =~ s/$function_ident/replace_function($1, \@funcs)/eg;
|
||||||
|
PTDEBUG && _d('Function-stripped clause:', $clause, Dumper(\@funcs));
|
||||||
|
return $clause, \@funcs;
|
||||||
|
}
|
||||||
|
|
||||||
sub parse_identifiers {
|
sub parse_identifiers {
|
||||||
my ( $self, $idents ) = @_;
|
my ( $self, $idents ) = @_;
|
||||||
return unless $idents;
|
return unless $idents;
|
||||||
@@ -4854,6 +4996,14 @@ sub parse_identifier {
|
|||||||
return unless $type && $ident;
|
return unless $type && $ident;
|
||||||
PTDEBUG && _d("Parsing", $type, "identifier:", $ident);
|
PTDEBUG && _d("Parsing", $type, "identifier:", $ident);
|
||||||
|
|
||||||
|
my ($func, $expr);
|
||||||
|
if ( $ident =~ m/^\w+\(/ ) { # Function like MIN(col)
|
||||||
|
($func, $expr) = $ident =~ m/^(\w+)\(([^\)]*)\)/;
|
||||||
|
PTDEBUG && _d('Function', $func, 'arg', $expr);
|
||||||
|
return { col => $ident } unless $expr; # NOW()
|
||||||
|
$ident = $expr; # col from MAX(col)
|
||||||
|
}
|
||||||
|
|
||||||
my %ident_struct;
|
my %ident_struct;
|
||||||
my @ident_parts = map { s/`//g; $_; } split /[.]/, $ident;
|
my @ident_parts = map { s/`//g; $_; } split /[.]/, $ident;
|
||||||
if ( @ident_parts == 3 ) {
|
if ( @ident_parts == 3 ) {
|
||||||
@@ -4888,6 +5038,10 @@ sub parse_identifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $func ) {
|
||||||
|
$ident_struct{func} = uc $func;
|
||||||
|
}
|
||||||
|
|
||||||
PTDEBUG && _d($type, "identifier struct:", Dumper(\%ident_struct));
|
PTDEBUG && _d($type, "identifier struct:", Dumper(\%ident_struct));
|
||||||
return \%ident_struct;
|
return \%ident_struct;
|
||||||
}
|
}
|
||||||
@@ -4942,311 +5096,6 @@ sub _d {
|
|||||||
# End SQLParser package
|
# End SQLParser package
|
||||||
# ###########################################################################
|
# ###########################################################################
|
||||||
|
|
||||||
# ###########################################################################
|
|
||||||
# MySQLDump package
|
|
||||||
# This package is a copy without comments from the original. The original
|
|
||||||
# with comments and its test file can be found in the Bazaar repository at,
|
|
||||||
# lib/MySQLDump.pm
|
|
||||||
# t/lib/MySQLDump.t
|
|
||||||
# See https://launchpad.net/percona-toolkit for more information.
|
|
||||||
# ###########################################################################
|
|
||||||
{
|
|
||||||
package MySQLDump;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings FATAL => 'all';
|
|
||||||
use English qw(-no_match_vars);
|
|
||||||
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
|
||||||
|
|
||||||
( our $before = <<'EOF') =~ s/^ //gm;
|
|
||||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
|
||||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
|
||||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
|
||||||
/*!40101 SET NAMES utf8 */;
|
|
||||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
|
||||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
|
||||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
|
||||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
|
||||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
|
||||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
( our $after = <<'EOF') =~ s/^ //gm;
|
|
||||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
|
||||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
|
||||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
|
||||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
|
||||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
|
||||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
|
||||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
|
||||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
sub new {
|
|
||||||
my ( $class, %args ) = @_;
|
|
||||||
my $self = {
|
|
||||||
cache => 0, # Afaik no script uses this cache any longer because
|
|
||||||
};
|
|
||||||
return bless $self, $class;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub dump {
|
|
||||||
my ( $self, $dbh, $quoter, $db, $tbl, $what ) = @_;
|
|
||||||
|
|
||||||
if ( $what eq 'table' ) {
|
|
||||||
my $ddl = $self->get_create_table($dbh, $quoter, $db, $tbl);
|
|
||||||
return unless $ddl;
|
|
||||||
if ( $ddl->[0] eq 'table' ) {
|
|
||||||
return $before
|
|
||||||
. 'DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . ";\n"
|
|
||||||
. $ddl->[1] . ";\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 'DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . ";\n"
|
|
||||||
. '/*!50001 DROP VIEW IF EXISTS '
|
|
||||||
. $quoter->quote($tbl) . "*/;\n/*!50001 "
|
|
||||||
. $self->get_tmp_table($dbh, $quoter, $db, $tbl) . "*/;\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif ( $what eq 'triggers' ) {
|
|
||||||
my $trgs = $self->get_triggers($dbh, $quoter, $db, $tbl);
|
|
||||||
if ( $trgs && @$trgs ) {
|
|
||||||
my $result = $before . "\nDELIMITER ;;\n";
|
|
||||||
foreach my $trg ( @$trgs ) {
|
|
||||||
if ( $trg->{sql_mode} ) {
|
|
||||||
$result .= qq{/*!50003 SET SESSION SQL_MODE='$trg->{sql_mode}' */;;\n};
|
|
||||||
}
|
|
||||||
$result .= "/*!50003 CREATE */ ";
|
|
||||||
if ( $trg->{definer} ) {
|
|
||||||
my ( $user, $host )
|
|
||||||
= map { s/'/''/g; "'$_'"; }
|
|
||||||
split('@', $trg->{definer}, 2);
|
|
||||||
$result .= "/*!50017 DEFINER=$user\@$host */ ";
|
|
||||||
}
|
|
||||||
$result .= sprintf("/*!50003 TRIGGER %s %s %s ON %s\nFOR EACH ROW %s */;;\n\n",
|
|
||||||
$quoter->quote($trg->{trigger}),
|
|
||||||
@{$trg}{qw(timing event)},
|
|
||||||
$quoter->quote($trg->{table}),
|
|
||||||
$trg->{statement});
|
|
||||||
}
|
|
||||||
$result .= "DELIMITER ;\n\n/*!50003 SET SESSION SQL_MODE=\@OLD_SQL_MODE */;\n\n";
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif ( $what eq 'view' ) {
|
|
||||||
my $ddl = $self->get_create_table($dbh, $quoter, $db, $tbl);
|
|
||||||
return '/*!50001 DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . "*/;\n"
|
|
||||||
. '/*!50001 DROP VIEW IF EXISTS ' . $quoter->quote($tbl) . "*/;\n"
|
|
||||||
. '/*!50001 ' . $ddl->[1] . "*/;\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
die "You didn't say what to dump.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _use_db {
|
|
||||||
my ( $self, $dbh, $quoter, $new ) = @_;
|
|
||||||
if ( !$new ) {
|
|
||||||
PTDEBUG && _d('No new DB to use');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
my $sql = 'USE ' . $quoter->quote($new);
|
|
||||||
PTDEBUG && _d($dbh, $sql);
|
|
||||||
$dbh->do($sql);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_create_table {
|
|
||||||
my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
|
|
||||||
if ( !$self->{cache} || !$self->{tables}->{$db}->{$tbl} ) {
|
|
||||||
my $sql = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
|
|
||||||
. q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
|
|
||||||
. '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
|
|
||||||
. '@@SQL_QUOTE_SHOW_CREATE := 1 */';
|
|
||||||
PTDEBUG && _d($sql);
|
|
||||||
eval { $dbh->do($sql); };
|
|
||||||
PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
|
|
||||||
$self->_use_db($dbh, $quoter, $db);
|
|
||||||
$sql = "SHOW CREATE TABLE " . $quoter->quote($db, $tbl);
|
|
||||||
PTDEBUG && _d($sql);
|
|
||||||
my $href;
|
|
||||||
eval { $href = $dbh->selectrow_hashref($sql); };
|
|
||||||
if ( $EVAL_ERROR ) {
|
|
||||||
warn "Failed to $sql. The table may be damaged.\nError: $EVAL_ERROR";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
|
|
||||||
. '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
|
|
||||||
PTDEBUG && _d($sql);
|
|
||||||
$dbh->do($sql);
|
|
||||||
my ($key) = grep { m/create table/i } keys %$href;
|
|
||||||
if ( $key ) {
|
|
||||||
PTDEBUG && _d('This table is a base table');
|
|
||||||
$self->{tables}->{$db}->{$tbl} = [ 'table', $href->{$key} ];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PTDEBUG && _d('This table is a view');
|
|
||||||
($key) = grep { m/create view/i } keys %$href;
|
|
||||||
$self->{tables}->{$db}->{$tbl} = [ 'view', $href->{$key} ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $self->{tables}->{$db}->{$tbl};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_columns {
|
|
||||||
my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
|
|
||||||
PTDEBUG && _d('Get columns for', $db, $tbl);
|
|
||||||
if ( !$self->{cache} || !$self->{columns}->{$db}->{$tbl} ) {
|
|
||||||
$self->_use_db($dbh, $quoter, $db);
|
|
||||||
my $sql = "SHOW COLUMNS FROM " . $quoter->quote($db, $tbl);
|
|
||||||
PTDEBUG && _d($sql);
|
|
||||||
my $cols = $dbh->selectall_arrayref($sql, { Slice => {} });
|
|
||||||
|
|
||||||
$self->{columns}->{$db}->{$tbl} = [
|
|
||||||
map {
|
|
||||||
my %row;
|
|
||||||
@row{ map { lc $_ } keys %$_ } = values %$_;
|
|
||||||
\%row;
|
|
||||||
} @$cols
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return $self->{columns}->{$db}->{$tbl};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_tmp_table {
|
|
||||||
my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
|
|
||||||
my $result = 'CREATE TABLE ' . $quoter->quote($tbl) . " (\n";
|
|
||||||
$result .= join(",\n",
|
|
||||||
map { ' ' . $quoter->quote($_->{field}) . ' ' . $_->{type} }
|
|
||||||
@{$self->get_columns($dbh, $quoter, $db, $tbl)});
|
|
||||||
$result .= "\n)";
|
|
||||||
PTDEBUG && _d($result);
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_triggers {
|
|
||||||
my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
|
|
||||||
if ( !$self->{cache} || !$self->{triggers}->{$db} ) {
|
|
||||||
$self->{triggers}->{$db} = {};
|
|
||||||
my $sql = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
|
|
||||||
. q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
|
|
||||||
. '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
|
|
||||||
. '@@SQL_QUOTE_SHOW_CREATE := 1 */';
|
|
||||||
PTDEBUG && _d($sql);
|
|
||||||
eval { $dbh->do($sql); };
|
|
||||||
PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
|
|
||||||
$sql = "SHOW TRIGGERS FROM " . $quoter->quote($db);
|
|
||||||
PTDEBUG && _d($sql);
|
|
||||||
my $sth = $dbh->prepare($sql);
|
|
||||||
$sth->execute();
|
|
||||||
if ( $sth->rows ) {
|
|
||||||
my $trgs = $sth->fetchall_arrayref({});
|
|
||||||
foreach my $trg (@$trgs) {
|
|
||||||
my %trg;
|
|
||||||
@trg{ map { lc $_ } keys %$trg } = values %$trg;
|
|
||||||
push @{ $self->{triggers}->{$db}->{ $trg{table} } }, \%trg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sql = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
|
|
||||||
. '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
|
|
||||||
PTDEBUG && _d($sql);
|
|
||||||
$dbh->do($sql);
|
|
||||||
}
|
|
||||||
if ( $tbl ) {
|
|
||||||
return $self->{triggers}->{$db}->{$tbl};
|
|
||||||
}
|
|
||||||
return values %{$self->{triggers}->{$db}};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_databases {
|
|
||||||
my ( $self, $dbh, $quoter, $like ) = @_;
|
|
||||||
if ( !$self->{cache} || !$self->{databases} || $like ) {
|
|
||||||
my $sql = 'SHOW DATABASES';
|
|
||||||
my @params;
|
|
||||||
if ( $like ) {
|
|
||||||
$sql .= ' LIKE ?';
|
|
||||||
push @params, $like;
|
|
||||||
}
|
|
||||||
my $sth = $dbh->prepare($sql);
|
|
||||||
PTDEBUG && _d($sql, @params);
|
|
||||||
$sth->execute( @params );
|
|
||||||
my @dbs = map { $_->[0] } @{$sth->fetchall_arrayref()};
|
|
||||||
$self->{databases} = \@dbs unless $like;
|
|
||||||
return @dbs;
|
|
||||||
}
|
|
||||||
return @{$self->{databases}};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_table_status {
|
|
||||||
my ( $self, $dbh, $quoter, $db, $like ) = @_;
|
|
||||||
if ( !$self->{cache} || !$self->{table_status}->{$db} || $like ) {
|
|
||||||
my $sql = "SHOW TABLE STATUS FROM " . $quoter->quote($db);
|
|
||||||
my @params;
|
|
||||||
if ( $like ) {
|
|
||||||
$sql .= ' LIKE ?';
|
|
||||||
push @params, $like;
|
|
||||||
}
|
|
||||||
PTDEBUG && _d($sql, @params);
|
|
||||||
my $sth = $dbh->prepare($sql);
|
|
||||||
$sth->execute(@params);
|
|
||||||
my @tables = @{$sth->fetchall_arrayref({})};
|
|
||||||
@tables = map {
|
|
||||||
my %tbl; # Make a copy with lowercased keys
|
|
||||||
@tbl{ map { lc $_ } keys %$_ } = values %$_;
|
|
||||||
$tbl{engine} ||= $tbl{type} || $tbl{comment};
|
|
||||||
delete $tbl{type};
|
|
||||||
\%tbl;
|
|
||||||
} @tables;
|
|
||||||
$self->{table_status}->{$db} = \@tables unless $like;
|
|
||||||
return @tables;
|
|
||||||
}
|
|
||||||
return @{$self->{table_status}->{$db}};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_table_list {
|
|
||||||
my ( $self, $dbh, $quoter, $db, $like ) = @_;
|
|
||||||
if ( !$self->{cache} || !$self->{table_list}->{$db} || $like ) {
|
|
||||||
my $sql = "SHOW /*!50002 FULL*/ TABLES FROM " . $quoter->quote($db);
|
|
||||||
my @params;
|
|
||||||
if ( $like ) {
|
|
||||||
$sql .= ' LIKE ?';
|
|
||||||
push @params, $like;
|
|
||||||
}
|
|
||||||
PTDEBUG && _d($sql, @params);
|
|
||||||
my $sth = $dbh->prepare($sql);
|
|
||||||
$sth->execute(@params);
|
|
||||||
my @tables = @{$sth->fetchall_arrayref()};
|
|
||||||
@tables = map {
|
|
||||||
my %tbl = (
|
|
||||||
name => $_->[0],
|
|
||||||
engine => ($_->[1] || '') eq 'VIEW' ? 'VIEW' : '',
|
|
||||||
);
|
|
||||||
\%tbl;
|
|
||||||
} @tables;
|
|
||||||
$self->{table_list}->{$db} = \@tables unless $like;
|
|
||||||
return @tables;
|
|
||||||
}
|
|
||||||
return @{$self->{table_list}->{$db}};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _d {
|
|
||||||
my ($package, undef, $line) = caller 0;
|
|
||||||
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
|
||||||
map { defined $_ ? $_ : 'undef' }
|
|
||||||
@_;
|
|
||||||
print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
||||||
}
|
|
||||||
# ###########################################################################
|
|
||||||
# End MySQLDump package
|
|
||||||
# ###########################################################################
|
|
||||||
|
|
||||||
# ###########################################################################
|
# ###########################################################################
|
||||||
# TableParser package
|
# TableParser package
|
||||||
# This package is a copy without comments from the original. The original
|
# This package is a copy without comments from the original. The original
|
||||||
@@ -5278,19 +5127,58 @@ sub new {
|
|||||||
return bless $self, $class;
|
return bless $self, $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_create_table {
|
||||||
|
my ( $self, $dbh, $db, $tbl ) = @_;
|
||||||
|
die "I need a dbh parameter" unless $dbh;
|
||||||
|
die "I need a db parameter" unless $db;
|
||||||
|
die "I need a tbl parameter" unless $tbl;
|
||||||
|
my $q = $self->{Quoter};
|
||||||
|
|
||||||
|
my $new_sql_mode
|
||||||
|
= '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
|
||||||
|
. q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
|
||||||
|
. '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
|
||||||
|
. '@@SQL_QUOTE_SHOW_CREATE := 1 */';
|
||||||
|
|
||||||
|
my $old_sql_mode = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
|
||||||
|
. '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
|
||||||
|
|
||||||
|
PTDEBUG && _d($new_sql_mode);
|
||||||
|
eval { $dbh->do($new_sql_mode); };
|
||||||
|
PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
|
||||||
|
|
||||||
|
my $use_sql = 'USE ' . $q->quote($db);
|
||||||
|
PTDEBUG && _d($dbh, $use_sql);
|
||||||
|
$dbh->do($use_sql);
|
||||||
|
|
||||||
|
my $show_sql = "SHOW CREATE TABLE " . $q->quote($db, $tbl);
|
||||||
|
PTDEBUG && _d($show_sql);
|
||||||
|
my $href;
|
||||||
|
eval { $href = $dbh->selectrow_hashref($show_sql); };
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
PTDEBUG && _d($EVAL_ERROR);
|
||||||
|
|
||||||
|
PTDEBUG && _d($old_sql_mode);
|
||||||
|
$dbh->do($old_sql_mode);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PTDEBUG && _d($old_sql_mode);
|
||||||
|
$dbh->do($old_sql_mode);
|
||||||
|
|
||||||
|
my ($key) = grep { m/create (?:table|view)/i } keys %$href;
|
||||||
|
if ( !$key ) {
|
||||||
|
die "Error: no 'Create Table' or 'Create View' in result set from "
|
||||||
|
. "$show_sql: " . Dumper($href);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $href->{$key};
|
||||||
|
}
|
||||||
|
|
||||||
sub parse {
|
sub parse {
|
||||||
my ( $self, $ddl, $opts ) = @_;
|
my ( $self, $ddl, $opts ) = @_;
|
||||||
return unless $ddl;
|
return unless $ddl;
|
||||||
if ( ref $ddl eq 'ARRAY' ) {
|
|
||||||
if ( lc $ddl->[0] eq 'table' ) {
|
|
||||||
$ddl = $ddl->[1];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return {
|
|
||||||
engine => 'VIEW',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
|
if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
|
||||||
die "Cannot parse table definition; is ANSI quoting "
|
die "Cannot parse table definition; is ANSI quoting "
|
||||||
@@ -5597,41 +5485,31 @@ sub remove_auto_increment {
|
|||||||
return $ddl;
|
return $ddl;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remove_secondary_indexes {
|
sub get_table_status {
|
||||||
my ( $self, $ddl ) = @_;
|
my ( $self, $dbh, $db, $like ) = @_;
|
||||||
my $sec_indexes_ddl;
|
my $q = $self->{Quoter};
|
||||||
my $tbl_struct = $self->parse($ddl);
|
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($db);
|
||||||
|
my @params;
|
||||||
if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) {
|
if ( $like ) {
|
||||||
my $clustered_key = $tbl_struct->{clustered_key};
|
$sql .= ' LIKE ?';
|
||||||
$clustered_key ||= '';
|
push @params, $like;
|
||||||
|
|
||||||
my @sec_indexes = map {
|
|
||||||
my $key_def = $_->{ddl};
|
|
||||||
$key_def =~ s/([\(\)])/\\$1/g;
|
|
||||||
$ddl =~ s/\s+$key_def//i;
|
|
||||||
|
|
||||||
my $key_ddl = "ADD $_->{ddl}";
|
|
||||||
$key_ddl .= ',' unless $key_ddl =~ m/,$/;
|
|
||||||
$key_ddl;
|
|
||||||
}
|
|
||||||
grep { $_->{name} ne $clustered_key }
|
|
||||||
values %{$tbl_struct->{keys}};
|
|
||||||
PTDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes));
|
|
||||||
|
|
||||||
if ( @sec_indexes ) {
|
|
||||||
$sec_indexes_ddl = join(' ', @sec_indexes);
|
|
||||||
$sec_indexes_ddl =~ s/,$//;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ddl =~ s/,(\n\) )/$1/s;
|
|
||||||
}
|
}
|
||||||
else {
|
PTDEBUG && _d($sql, @params);
|
||||||
PTDEBUG && _d('Not removing secondary indexes from',
|
my $sth = $dbh->prepare($sql);
|
||||||
$tbl_struct->{engine}, 'table');
|
eval { $sth->execute(@params); };
|
||||||
|
if ($EVAL_ERROR) {
|
||||||
|
PTDEBUG && _d($EVAL_ERROR);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
my @tables = @{$sth->fetchall_arrayref({})};
|
||||||
return $ddl, $sec_indexes_ddl, $tbl_struct;
|
@tables = map {
|
||||||
|
my %tbl; # Make a copy with lowercased keys
|
||||||
|
@tbl{ map { lc $_ } keys %$_ } = values %$_;
|
||||||
|
$tbl{engine} ||= $tbl{type} || $tbl{comment};
|
||||||
|
delete $tbl{type};
|
||||||
|
\%tbl;
|
||||||
|
} @tables;
|
||||||
|
return @tables;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _d {
|
sub _d {
|
||||||
@@ -6091,7 +5969,6 @@ sub main {
|
|||||||
my $qr = new QueryRewriter( QueryParser => $qp );
|
my $qr = new QueryRewriter( QueryParser => $qp );
|
||||||
my $sp = new SQLParser();
|
my $sp = new SQLParser();
|
||||||
my $tp = new TableParser(Quoter => $q);
|
my $tp = new TableParser(Quoter => $q);
|
||||||
my $du = new MySQLDump();
|
|
||||||
my %common_modules = (
|
my %common_modules = (
|
||||||
DSNParser => $dp,
|
DSNParser => $dp,
|
||||||
Quoter => $q,
|
Quoter => $q,
|
||||||
@@ -6100,7 +5977,6 @@ sub main {
|
|||||||
QueryRewriter => $qr,
|
QueryRewriter => $qr,
|
||||||
SQLParser => $sp,
|
SQLParser => $sp,
|
||||||
TableParser => $tp,
|
TableParser => $tp,
|
||||||
MySQLDump => $du,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
# #########################################################################
|
# #########################################################################
|
||||||
@@ -6286,8 +6162,8 @@ sub main {
|
|||||||
if ( !$tbl_structs->{$db}->{$tbl} ) {
|
if ( !$tbl_structs->{$db}->{$tbl} ) {
|
||||||
my $tbl_struct;
|
my $tbl_struct;
|
||||||
eval {
|
eval {
|
||||||
$tbl_struct
|
$tbl_struct = $tp->parse(
|
||||||
= $tp->parse($du->get_create_table($dbh, $q, $db, $tbl));
|
$tp->get_create_table($dbh, $db, $tbl));
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
if ( $EVAL_ERROR ) {
|
||||||
warn "Failed to get SHOW CREATE TABLE for $db.$tbl: "
|
warn "Failed to get SHOW CREATE TABLE for $db.$tbl: "
|
||||||
|
Reference in New Issue
Block a user