diff --git a/bin/pt-show-grants b/bin/pt-show-grants index 4876b5b7..99f90d53 100755 --- a/bin/pt-show-grants +++ b/bin/pt-show-grants @@ -1766,11 +1766,14 @@ sub main { PTDEBUG && _d('Grants:', Dumper(\@grants)); next unless @grants; - if ( $o->get('separate') ) { # List each grant separately. + if ( $o->get('separate') ) { + # List each grant separately. @grants = map { my ( $grants, $on_what ) = $_ =~ m/GRANT (.*?) ON (.*)$/; - map { "GRANT $_ ON $on_what" } split(', ', $grants); + map { "GRANT $_ ON $on_what" } split_grants($grants); } @grants; + PTDEBUG && _d('Grants separated:', Dumper(\@grants)); + my $count; # If the row with IDENTIFIED BY has multiple grants, this will # create many such rows; strip it from all but the first. @@ -1782,12 +1785,15 @@ sub main { } $_; } @grants; + PTDEBUG && _d('Grants separated:', Dumper(\@grants)); } - else { # Sort the actual grants lexically within each row for consistency. + else { + # Sort the actual grants lexically within each row for consistency. @grants = map { - $_ =~ s/GRANT (.*?) ON (`|\*)/"GRANT " . join(', ', sort(split(', ', $1))) . " ON $2"/e; + $_ =~ s/GRANT (.*?) ON (`|\*)/"GRANT " . join(', ', sort(split_grants($1))) . " ON $2"/e; $_; } @grants; + PTDEBUG && _d('Grants grouped:', Dumper(\@grants)); } # Sort the grant rows for consistency too, but the one with the password @@ -1795,6 +1801,7 @@ sub main { @grants = sort { $b =~ m/IDENTIFIED BY/ <=> $a =~ m/IDENTIFIED BY/ || $a cmp $b } @grants; + PTDEBUG && _d('Grants sorted:', Dumper(\@grants)); # Print REVOKE statements. if ( $o->get('revoke') ) { @@ -1809,7 +1816,7 @@ sub main { my @result; if ( $o->get('separate') ) { @result = map { "REVOKE $_ ON $on_what FROM $user" } - split(', ', $grants); + split_grants($grants); } else { @result = "REVOKE $grants ON $on_what FROM $user"; @@ -1874,6 +1881,35 @@ sub parse_user { return ( $user, $host ); } +sub split_grants { + my ($grants) = @_; + return unless $grants; + my @grants; + if ( $grants =~ m/(?:INSERT|SELECT|UPDATE) \(/ ) { + PTDEBUG && _d('Splitting grants on keywords:', $grants); + @grants = map { + my $grant = $_; + $grant =~ s/^\s+//; + $grant =~ s/,\s*$//; + $grant; + } $grants =~ m/ + \G # Start matching after the previous match + \s? # Space after previous match's separating comma + (?: # Either match... + (?: (?:INSERT|SELECT|UPDATE)\s\(.+?\) ) # a column grant + | (?: [A-Z\s]+ ) # or a table grant + ) + ,? # Separted from the next grant, if any, by a comma + /xg; + } + else { + PTDEBUG && _d('Splitting grants on comma:', $grants); + @grants = split(', ', $grants); + } + PTDEBUG && _d('Grants split:', Dumper(\@grants)); + return @grants; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/t/pt-show-grants/basics.t b/t/pt-show-grants/basics.t index 86ab4281..5c739c7e 100644 --- a/t/pt-show-grants/basics.t +++ b/t/pt-show-grants/basics.t @@ -101,13 +101,33 @@ diag(`/tmp/12345/use -u root -e "GRANT SELECT(city_id), INSERT(city) ON sakila.c ok( no_diff( - sub { pt_show_grants::main('-F', $cnf, qw(--only sally)) }, + sub { pt_show_grants::main('-F', $cnf, qw(--only sally --no-header)) }, "t/pt-show-grants/samples/column-grants.txt", stderr => 1, ), "Column-level grants (bug 866075)" ); +ok( + no_diff( + sub { pt_show_grants::main('-F', $cnf, qw(--only sally --no-header), + qw(--separate)) }, + "t/pt-show-grants/samples/column-grants-separate.txt", + stderr => 1, + ), + "Column-level grants --separate (bug 866075)" +); + +ok( + no_diff( + sub { pt_show_grants::main('-F', $cnf, qw(--only sally --no-header), + qw(--separate --revoke)) }, + "t/pt-show-grants/samples/column-grants-separate-revoke.txt", + stderr => 1, + ), + "Column-level grants --separate --revoke (bug 866075)" +); + diag(`/tmp/12345/use -u root -e "DROP USER 'sally'\@'%'"`); # ############################################################################# diff --git a/t/pt-show-grants/samples/column-grants-separate-revoke.txt b/t/pt-show-grants/samples/column-grants-separate-revoke.txt new file mode 100644 index 00000000..157350be --- /dev/null +++ b/t/pt-show-grants/samples/column-grants-separate-revoke.txt @@ -0,0 +1,10 @@ +-- Revoke statements for 'sally'@'%' +REVOKE INSERT (city) ON `sakila`.`city` FROM 'sally'@'%'; +REVOKE SELECT (SANumber, DateCreated, PaymentStat, PckPrice) ON `test`.`t` FROM 'sally'@'%'; +REVOKE SELECT (city_id) ON `sakila`.`city` FROM 'sally'@'%'; +REVOKE USAGE ON *.* FROM 'sally'@'%'; +-- Grants for 'sally'@'%' +GRANT INSERT (city) ON `sakila`.`city` TO 'sally'@'%'; +GRANT SELECT (SANumber, DateCreated, PaymentStat, PckPrice) ON `test`.`t` TO 'sally'@'%'; +GRANT SELECT (city_id) ON `sakila`.`city` TO 'sally'@'%'; +GRANT USAGE ON *.* TO 'sally'@'%'; diff --git a/t/pt-show-grants/samples/column-grants-separate.txt b/t/pt-show-grants/samples/column-grants-separate.txt new file mode 100644 index 00000000..9b6361d9 --- /dev/null +++ b/t/pt-show-grants/samples/column-grants-separate.txt @@ -0,0 +1,5 @@ +-- Grants for 'sally'@'%' +GRANT INSERT (city) ON `sakila`.`city` TO 'sally'@'%'; +GRANT SELECT (SANumber, DateCreated, PaymentStat, PckPrice) ON `test`.`t` TO 'sally'@'%'; +GRANT SELECT (city_id) ON `sakila`.`city` TO 'sally'@'%'; +GRANT USAGE ON *.* TO 'sally'@'%'; diff --git a/t/pt-show-grants/samples/column-grants.txt b/t/pt-show-grants/samples/column-grants.txt index 4ba28051..54feb4a5 100644 --- a/t/pt-show-grants/samples/column-grants.txt +++ b/t/pt-show-grants/samples/column-grants.txt @@ -1 +1,4 @@ --- +-- Grants for 'sally'@'%' +GRANT INSERT (city), SELECT (city_id) ON `sakila`.`city` TO 'sally'@'%'; +GRANT SELECT (SANumber, DateCreated, PaymentStat, PckPrice) ON `test`.`t` TO 'sally'@'%'; +GRANT USAGE ON *.* TO 'sally'@'%';