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

* Remove trailing spaces * PR-665 - Remove trailing spaces - Updated not stable test t/pt-online-schema-change/preserve_triggers.t - Updated utilities in bin directory * PR-665 - Remove trailing spaces - Fixed typos * PR-665 - Remove trailing spaces - Fixed typos --------- Co-authored-by: Sveta Smirnova <sveta.smirnova@percona.com>
250 lines
6.9 KiB
Perl
250 lines
6.9 KiB
Perl
# This program is copyright 2008-2011 Percona Ireland Ltd.
|
|
# Feedback and improvements are welcome.
|
|
#
|
|
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
|
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
|
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
|
|
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
|
|
# licenses.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
# Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
# ###########################################################################
|
|
# TextResultSetParser package
|
|
# ###########################################################################
|
|
{
|
|
# Package: TextResultSetParser
|
|
# TextResultSetParser converts a text result set to a data struct like
|
|
# DBI::selectall_arrayref(). Text result sets are like what SHOW PROCESSLIST
|
|
# and EXPLAIN print, like:
|
|
#
|
|
# +----+------+
|
|
# | Id | User |
|
|
# +----+------+
|
|
# | 1 | bob |
|
|
# +----+------+
|
|
#
|
|
# That converts to:
|
|
# (start code)
|
|
# [
|
|
# {
|
|
# Id => '1',
|
|
# User => 'bob',
|
|
# },
|
|
# ]
|
|
# (end code)
|
|
# Both horizontal and vertical (\G) text outputs are supported.
|
|
package TextResultSetParser;
|
|
|
|
use strict;
|
|
use warnings FATAL => 'all';
|
|
use English qw(-no_match_vars);
|
|
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
|
|
|
# Sub: new
|
|
#
|
|
# Parameters:
|
|
# %args - Arguments
|
|
#
|
|
# Optional Arguments:
|
|
# value_for - Hashref of original_val => new_val, used to alter values
|
|
# NAME_lc - Lowercase key names, like $dbh->{FetchHashKeyName} = 'NAME_lc'
|
|
#
|
|
# Returns:
|
|
# TextResultSetParser object
|
|
sub new {
|
|
my ( $class, %args ) = @_;
|
|
my %value_for = (
|
|
'NULL' => undef, # DBI::selectall_arrayref() does this
|
|
($args{value_for} ? %{$args{value_for}} : ()),
|
|
);
|
|
my $self = {
|
|
%args,
|
|
value_for => \%value_for,
|
|
};
|
|
return bless $self, $class;
|
|
}
|
|
|
|
# Sub: _parse_tabular
|
|
# Parse a line from tabular horizontal output.
|
|
#
|
|
# Parameters:
|
|
# $text - Text row from horizontal output, split in <parse_horizontal_rows()>
|
|
# @cols - Column names that text rows are organized by
|
|
#
|
|
# Returns:
|
|
# A record hashref
|
|
sub _parse_tabular {
|
|
my ( $text, @cols ) = @_;
|
|
my %row;
|
|
my @vals = $text =~ m/\| +([^\|]*?)(?= +\|)/msg;
|
|
return (undef, \@vals) unless @cols;
|
|
@row{@cols} = @vals;
|
|
return (\%row, undef);
|
|
}
|
|
|
|
# Sub: _parse_tabular
|
|
# Parse a line from tab-separated horizontal output.
|
|
#
|
|
# Parameters:
|
|
# $text - Text row from horizontal output, split in <parse_horizontal_rows()>
|
|
# @cols - Column names that text rows are organized by
|
|
#
|
|
# Returns:
|
|
# A record hashref
|
|
sub _parse_tab_sep {
|
|
my ( $text, @cols ) = @_;
|
|
my %row;
|
|
my @vals = split(/\t/, $text);
|
|
return (undef, \@vals) unless @cols;
|
|
@row{@cols} = @vals;
|
|
return (\%row, undef);
|
|
}
|
|
|
|
# Sub: parse_vertical_row
|
|
# Parse records split from vertical output by <split_vertical_rows()>.
|
|
#
|
|
# Parameters:
|
|
# $text - Text record
|
|
#
|
|
# Returns:
|
|
# A record hashref
|
|
sub parse_vertical_row {
|
|
my ( $self, $text ) = @_;
|
|
my %row = $text =~ m/^\s*(\w+):(?: ([^\n]*))?/msg;
|
|
if ( $self->{NAME_lc} ) {
|
|
my %lc_row = map {
|
|
my $key = lc $_;
|
|
$key => $row{$_};
|
|
} keys %row;
|
|
return \%lc_row;
|
|
}
|
|
else {
|
|
return \%row;
|
|
}
|
|
}
|
|
|
|
# Sub: parse
|
|
# Parse a text result set.
|
|
#
|
|
# Parameters:
|
|
# $text - Text result set
|
|
#
|
|
# Returns:
|
|
# Arrayref like:
|
|
# (start code)
|
|
# [
|
|
# {
|
|
# Time => '5',
|
|
# Command => 'Query',
|
|
# db => 'foo',
|
|
# },
|
|
# ]
|
|
# (end code)
|
|
sub parse {
|
|
my ( $self, $text ) = @_;
|
|
my $result_set;
|
|
|
|
# Detect text type: tabular, tab-separated, or vertical
|
|
if ( $text =~ m/^\+---/m ) { # standard "tabular" output
|
|
PTDEBUG && _d('Result set text is standard tabular');
|
|
my $line_pattern = qr/^(\| .*)[\r\n]+/m;
|
|
$result_set
|
|
= $self->parse_horizontal_row($text, $line_pattern, \&_parse_tabular);
|
|
}
|
|
elsif ( $text =~ m/^\w+\t\w+/m ) { # tab-separated
|
|
PTDEBUG && _d('Result set text is tab-separated');
|
|
my $line_pattern = qr/^(.*?\t.*)[\r\n]+/m;
|
|
$result_set
|
|
= $self->parse_horizontal_row($text, $line_pattern, \&_parse_tab_sep);
|
|
}
|
|
elsif ( $text =~ m/\*\*\* \d+\. row/ ) { # "vertical" output
|
|
PTDEBUG && _d('Result set text is vertical (\G)');
|
|
foreach my $row ( split_vertical_rows($text) ) {
|
|
push @$result_set, $self->parse_vertical_row($row);
|
|
}
|
|
}
|
|
else {
|
|
my $text_sample = substr $text, 0, 300;
|
|
my $remaining = length $text > 300 ? (length $text) - 300 : 0;
|
|
chomp $text_sample;
|
|
die "Cannot determine if text is tabular, tab-separated or vertical:\n"
|
|
. "$text_sample\n"
|
|
. ($remaining ? "(not showing last $remaining bytes of text)\n" : "");
|
|
}
|
|
|
|
# Convert values.
|
|
if ( $self->{value_for} ) {
|
|
foreach my $result_set ( @$result_set ) {
|
|
foreach my $key ( keys %$result_set ) {
|
|
next unless defined $result_set->{$key};
|
|
$result_set->{$key} = $self->{value_for}->{ $result_set->{$key} }
|
|
if exists $self->{value_for}->{ $result_set->{$key} };
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result_set;
|
|
}
|
|
|
|
|
|
# Sub: parse_horizontal_row
|
|
# Parse rows from horizontal output (regular MySQL style output).
|
|
#
|
|
# Parameters:
|
|
# $text - Text result set
|
|
# $line_pattern - Compiled regex pattern that matches one line
|
|
# $sub - Coderef to parse a line (tabular or tab-separated lines)
|
|
#
|
|
# Returns:
|
|
# Arrayref of records as hashrefs
|
|
sub parse_horizontal_row {
|
|
my ( $self, $text, $line_pattern, $sub ) = @_;
|
|
my @result_sets = ();
|
|
my @cols = ();
|
|
foreach my $line ( $text =~ m/$line_pattern/g ) {
|
|
my ( $row, $cols ) = $sub->($line, @cols);
|
|
if ( $row ) {
|
|
push @result_sets, $row;
|
|
}
|
|
else {
|
|
@cols = map { $self->{NAME_lc} ? lc $_ : $_ } @$cols;
|
|
}
|
|
}
|
|
return \@result_sets;
|
|
}
|
|
|
|
# Sub: parse_horizontal_row
|
|
# Split records in vertical output (\G style output).
|
|
#
|
|
# Parameters:
|
|
# $text - Text result set
|
|
#
|
|
# Returns:
|
|
# Array of text records, parsed by <parse_vertical_row()>.
|
|
sub split_vertical_rows {
|
|
my ( $text ) = @_;
|
|
my $ROW_HEADER = '\*{3,} \d+\. row \*{3,}';
|
|
my @rows = $text =~ m/($ROW_HEADER.*?)(?=$ROW_HEADER|\z)/omgs;
|
|
return @rows;
|
|
}
|
|
|
|
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 TextResultSetParser package
|
|
# ###########################################################################
|