#!/usr/bin/perl -w

# Ausführungsplananalyse mehrerer SQL-Statements mithilfe von "EXPLAIN PLAN"
#
# Author: Uwe Schneider <us@webde-ag.de>
#
# Diese Software ist UNSUPPORTET.
#

use strict;
use FileHandle;
use DBI;

#######################################################################
my ($sth_plan);
my ($statement_id);
sub do_explain
{
    my ($dbh,$sql) = @_;
    
    # Statischer Block
    unless ($sth_plan)
    {
        $statement_id = "SID.$$";
        $sth_plan = $dbh->prepare(qq{
            SELECT 
                LPAD(' ',2*(LEVEL-1))||operation operation
            ,   options
            ,   object_name
            FROM 
                plan_table
            START WITH 
                id = 0 AND statement_id = '$statement_id'
            CONNECT BY PRIOR 
                id = parent_id AND statement_id = '$statement_id'         
        }) || die;
    }
    
    return if $sql =~ /EXPLAIN PLAN/i;
    
    $dbh->do(qq{
        EXPLAIN PLAN SET STATEMENT_ID = '$statement_id' INTO plan_table FOR
        $sql
    }) || return;
    
    my ($operation, $options, $object_name);
    $sth_plan->execute() || die;
    while (($operation, $options, $object_name) = $sth_plan->fetchrow_array()) 
    {
        printf ">> %s %s %s", $operation, $options || "", $object_name || "";
        print " (O:$object_name)" if ($object_name);
        print "\n";
    }
    $sth_plan->finish();
    print "\n";
    
    # Damit werden auch die plan_table-Einträge gelöscht.   
    $dbh->rollback();
            
}
###############################################################
sub usage_exit
{
    print STDERR <<EOUSAGE;
Aufruf: $0 <Loginstring>
Erzeugt zu jedem SQL-Statement den Ausführungsplan.
Input:  Tab-separierte Liste, erste Spalte: ausf. User, letzte Spalte: SQL-Text
Output: Das gleiche, um Query-Plans angereichert.
EOUSAGE
    die;
}
#######################################################################

# Feldseparator bei der Eingabe
my ($SEP) = "\t";

my ($login) = shift || usage_exit();
my ($dbh) = DBI->connect("DBI:Oracle:",$login,) || die;
$dbh->{'AutoCommit'} = 0;


my ($user) = ($login =~ m|(\w+)/\w+@\w+|)[0];

my ($sql);
# Schleife über die Eingabe
while (<>)
{
    print;
    
    # Wir setzen voraus, daß in der ersten Spalte der User und 
    # in der letzten das SQL steht
    # Wenn der parsende User im Login-String steht...
    chop;
    my @a = split(/$SEP/);
    if (@a >= 2  && uc($a[0]) eq uc($user))
    {
        do_explain($dbh,$a[$#a]);
    }
}

$dbh->disconnect();
