#!/usr/bin/perl

use strict;
use warnings;

=head1 DESCRIPTION

=encoding utf8

    Скрипт 

      
=head1 OPTIONS

    -H, --host <hostname>
        хост

    -P, --port <port>
        порт

    -u, --user <username>
        пользователь для коннекта к mysql

    -p, --pass <password>
        пароль для коннекта к mysql

=head1 EXAMPLES


  ... TODO ...


=head1 TODO


=cut


use Data::Dumper;
use Getopt::Long qw(:config no_ignore_case);
use Text::Diff;
use YAML;
#use Carp::Always;

use Yandex::DBTools;
use Yandex::DBShards;

my @fields = qw/id select_type table partitions type possible_keys key key_len ref rows filtered Extra/;
my %width = (
    possible_keys => 15,
    select_type => 11,
    partitions =>  10,
    ref => 20,
    rows => 8,
    key => 12,
    key_len => 7,
    filtered => 8,
    Extra => 20,
    id => 2,
);

run() unless caller();


sub run
{
    my %O = %{parse_options()};

    $Yandex::DBTools::DONT_SEND_LETTERS = 1;
    %Yandex::DBTools::DB_CONFIG = (
        CHILDS => {
            my_db => {
                'AutoCommit' => '1',
                'CHILDS' => {
                    '_' => {},
                },
                'connect_timeout' => '4',
                'host' => $O{mysql_host},
                'pass' => $O{mysql_password},
                'port' => $O{mysql_port},
                'user' => $O{mysql_user},
                'utf8' => '1',
                'db'   => $O{database},
            },
        },
    );

    my $pl = get_all_sql("my_db", 'select * from information_schema.processlist where command = "Query" and time >= 60'); 

    for my $p ( @$pl ){
        my $query = $p->{INFO};

        unless ( $query =~ /^ *(SELECT|UPDATE|INSERT|DELETE|REPLACE)\b/i ){
            print "WARNING: cant't explain $query\n, next\n";
            next;
        }

        if (length($p->{INFO}) >= 65535){
            print "WARNING: query in connection $p->{ID} too long, next\n";
            next;
        }

        my $plan_connection = get_all_sql("my_db", "explain for connection $p->{ID}"); 
        #push @$plan_connection, {};

        if($p->{INFO} =~ /;/){
            print "WARNING: skipping $p->{INFO}\n";
            next;
        }
        my $plan_query = eval { get_all_sql("my_db", "explain $p->{INFO}") }; 
        if($@){
            print "WARNING: can't explain query from connection $p->{ID} (?too long? length = ".(length($p->{INFO})).")\n";
            next;
        }

        my $plan_connection_text = plan2text($plan_connection);
        my $plan_query_text = plan2text($plan_query);

        if ( $plan_connection_text ne $plan_query_text ){
            my $d = my_date();
            print "### $d / $O{mysql_host}:$O{mysql_port} / $O{database}\n";
            print process2text($p);
            my $diff = diff \$plan_query_text, \$plan_connection_text, {};
            print "\n#### Explain for query\n$plan_query_text\n#### Explain for connection\n$plan_connection_text\n#### diff\n$diff\n\n";
        }

        
    }


    exit 0;
}

sub process2text
{
    my ($p) = @_;
    my @lines;
    for my $f( qw/ID DB HOST USER TIME STATE ROWS_EXAMINED ROWS_SENT/ ){
        push @lines, "$f: $p->{$f}";
    }
    push @lines, "QUERY:\n".$p->{INFO};

    return join("\n", @lines)."\n";
}

sub plan2text
{
    my ($plan) = @_;
    my @lines;

    my @parts;
    for my $f ( @fields ){
        my $w = $width{$f} || 6;
        push @parts, sprintf("%${w}s", $f );
    }
    push @lines, join " ", @parts;

    for my $s ( @$plan ){
        local $s->{rows} = $s->{rows} || '';

        # точный прогноз количества строк неважен, творчески округляем
        if ( $s->{rows} ne '' && $s->{rows} <= 50_000 ){
            $s->{rows} = "<50K";
        } else {
            $s->{rows} =~ s/(?<=.)./0/g;
        }

        my @parts;
        for my $f ( @fields ){
            my $w = $width{$f} || 6;
            push @parts, sprintf("%${w}s", $s->{$f} || '-' );
        }

        push @lines, join " ", @parts;
    }
    return join("\n", @lines)."\n";
}


sub p
{
    print localtime(time)." $_\n" for @_;
}


sub parse_options
{
    my %O = (
    );

    GetOptions(
        "help" => sub {
            system("podselect -section NAME -section DESCRIPTION -section OPTIONS -section EXAMPLES $0 | pod2text"); 
            exit 0;
        },
        "H|host=s" => \$O{mysql_host},
        "P|port=s" => \$O{mysql_port},
        "u|user=s" => \$O{mysql_user},
        "d|db|database=s" => \$O{database},
        "p|password=s" => \$O{mysql_password},
    ) || die "can't parse options, stop";

    return \%O;
}

sub my_date
{
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    $year += 1900;
    $mon++;
    return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
}
