Subversion Repositories DevTools

Rev

Rev 7326 | Blame | Compare with Previous | Last modification | View Log | RSS feed

########################################################################
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
#
# Module name   : jats.sh
# Module type   : Makefile system
# Compiler(s)   : n/a
# Environment(s): jats
#
# Description   : This package has been created to simplify the migration
#                 of the deployments scripts that use an DBI:ODBC interface
#                 to Release Manager, to a JDBC interface.
#
#                 The package provides suffient methods to trick the existing
#                 code into working. No More. No less.
#
# Usage:
#
# Version   Who      Date        Description
#
#......................................................................#

require 5.006_001;
use strict;
use warnings;
#use Data::Dumper;
use Cwd;
use IPC::Open2;

package DBI;
our $VERSION = '2.0.1';


use Exporter;
our @EXPORT = qw();
our @EXPORT_OK = (@EXPORT, qw());
our %EXPORT_TAGS = qw( );
our $verbose = $ENV{'GBE_DBI_VERBOSE'} || 0;
our $errstr = "No Error";
my  $appname = 'ReleaseManagerSelect.jar';
my $full_app;

#
#   The following hash simplifies existing code
#   in that it will convert known DBI:ODBC called to a more raw form
#
my %url_convert = ( 'dbi:ODBC:RM3' => '$GBE_RM_LOCATION' );


#-------------------------------------------------------------------------------
# Function        : connect
#
# Description     : Connect to the SQL server
#
# Inputs          : $db                     - Database Name
#                   $user                   - User Name
#                   $passwd                 - PassWord
#
# Returns         : New connection
#                   undef if the required Java utility cannot be found
#
sub connect
{
    my $obclass = shift;
    my $class = ref($obclass) || $obclass;

    bless my $self = {
        DB          => $_[0],
        USER        => $_[1],
        PASSWORD    => $_[2],
        ATTRREF     => $_[3],
        ERROR       => 0,
        ECOUNT      => 0,
        FH_READ     => undef,
        FH_WRITE    => undef,
        PID         => 0,
    } => ( $class );

    if ( $self->{ATTRREF}->{verbose} )
    {
        $verbose = $self->{ATTRREF}->{verbose};
    }

    #
    #   Convert legacy ODBC refs
    #   Should not needed any more
    #
    if ( $url_convert{$self->{DB}} )
    {
        my $var = $url_convert{$self->{DB}};
        my $value = $ENV{$var};
        unless ($value) {
            print "[DBI] Cannot convert old DB value to new form",
                   "\nUser requested: " . $self->{DB},
                   "\nNeed Environment Variable: $var";
            exit 1;
        }

        print "[DBI] Converting DB to URL: " . $self->{DB} ." to $value\n"
            if $verbose > 1;
        $self->{DB} = $value;
    }

    #
    #   Need to locate the path to the Java applet
    #   This can be in th "CLASSPATH", but that simply forces the problem of
    #   locating the utility into the configuration world
    #
    #   Look in Perls @INC list, as we expect to find the program within one of
    #   thePerl Libraries.
    #
    unless ( $full_app )
    {
        foreach my $dir ( @INC )
        {
            my $apppath = "$dir/$appname";
            next unless ( -f $apppath );

            $full_app = $apppath;
            last;
        }

        if ( ! $full_app )
        {
            $errstr = "Cannot find $appname in @INC path";
            return;
        }
    }

    $errstr = "OK";

    #
    #   Initiate the external application server
    #   This is slow and will only done once
    #
    #   Start up the Java based Oracle interface script
    #   Create it with both a read and a write handle as we will be pushing
    #   requests at it and pulling data from it.
    #
    #   Monitor the status of the server waiting for an indication that the
    #   connection is open, or that the connection link has terminated.
    #
    #
    my @cmd;
    push @cmd, "java";                      # Use form that will not invoke a shell
    push @cmd, "-jar";
    push @cmd, $full_app;
    push @cmd, $self->{DB};
    push @cmd, $self->{USER};
    push @cmd, $self->{PASSWORD};

    print "[DBI] Command: @cmd"
        if $verbose > 1;

    $self->{PID} = IPC::Open2::open2($self->{FH_READ}, $self->{FH_WRITE}, @cmd );

    $self->{ERROR} = 1;
    $errstr = "Failed to start server app";
    if ( $self->{PID} )
    {
        #
        #   Extract status and any error information
        #
        my $fh = $self->{FH_READ};
        while ( <$fh> )
        {
            $_ =~ s~\s+$~~;
            print "[DBI] Connect: $_\n" if $verbose;

            if ( m/^ConnectionOpened:/ )
            {
                $self->{ERROR} = 0;
                $errstr = "OK";
                last;
            }

            if ( m/^Error:(.*?)\s*$/ )
            {
                $errstr = $1;
            }

            if ( m/^Status:ConnectionClosed$/ )
            {
                $self->{PID} = 0;
            }
        }
    }

    #
    #   Return class iff the connection has been established
    #

    return $self->{ERROR} ? undef : ($self);
}


#-------------------------------------------------------------------------------
# Function        : errstr
#
# Description     : Return the last open error
#
# Inputs          : None
#
# Returns         : String
#
sub errstr
{
    return $errstr;
}

#-------------------------------------------------------------------------------
# Function        : prepare
#
# Description     : Prepare an SQL statement for processing
#
# Inputs          : $self
#                   $statement
#
# Returns         : Reference to a class
#                   undefined on error
#
sub prepare
{
    my $self = shift;
    my $statement = shift;

    #
    #   Remove new-lines from the statement
    #   Must not send new-lines to the application - they don't make it
    #
    $statement =~ s~\n~ ~g;

    #
    #   Remove leading and trailing whitespace
    #
    $statement =~ s~^\s*~~;
    $statement =~ s~\s*$~~;

    print "[DBI] Prepare: $statement\n" if $verbose;

    #
    #   Create a new object to represent the SQL statement being prepared
    #
    my $class = "DBI_Prepare";
    bless my $new_self = {
        CONNECTION => $self,
        STATEMENT => $statement,
        ROWS_READ   => 0,
        ROWS        => 0,
        FH          => undef,
        ERRSTR      => 'OK',
    } => ( $class );

    $self->{ECOUNT}++;
    return $new_self;
}

#-------------------------------------------------------------------------------
# Function        : disconnect
#
# Description     : Close the connection
#
# Inputs          : None
#
# Returns         : Nothing
#
sub disconnect
{
    my $self = shift;
    print "[DBI] Disconnect\n" if $verbose;

    if ( $self->{PID} )
    {
        #
        #   Send out a zero-length query
        #   This will cause the helper application to close the connection
        #

        my $fhw = $self->{FH_WRITE};
        print $fhw "\n\n\n";

        #
        #   Now read in data until the pipe breaks
        #
        my $fhr = $self->{FH_READ};
        while ( <$fhr> )
        {
            chomp;
            print "[DBI] Disconnect: $_\n" if $verbose;
        }

        close( $self->{FH_READ} );
        close( $self->{FH_WRITE} );

        $self->{FH_READ} = undef;
        $self->{FH_WRITE} = undef;

        #
        #   Kill the server task.
        #   It will hang around forever if we don't do this
        #
#        kill 9, $self->{PID};
        $self->{PID} = 0;
    }

    return 1;
}

#-------------------------------------------------------------------------------
# Function        : DESTROY
#
# Description     : Called when the object is destroyed
#
# Inputs          :
#
# Returns         :
#
sub DESTROY
{
    my $self = shift;
    $self->disconnect();
    $self->dumpSelf() if $verbose > 1;
    print "[DBI] Connection destroyed\n" if $verbose;
}

#==============================================================================
#   dumpSelf, debugging member to dump selfs hash
#==============================================================================
sub dumpSelf
{
    use Data::Dumper;

    my $self = shift;

    print Data::Dumper->Dump([$self], [ref($self)]);
}   # dumpSelf



#-------------------------------------------------------------------------------
#
#   A new package to encapulate the actual SQL operations
#
package DBI_Prepare;

#-------------------------------------------------------------------------------
# Function        : execute
#
# Description     : Execute the SQL statement
#
# Inputs          : A list of substitution arguments
#                   These will be repalced within the SELECT statement;
#
# Returns         : True: Execution was good
#
sub execute
{
    my $self = shift;
    my @args = @_;
    my @rows;
    my @colData;

    my $statement = $self->{STATEMENT};


    #
    #   The users query may contain '?' characters
    #   These are replaced with arguments passed in to the 'execute'
    #
    if ( @args )
    {
        foreach my $arg ( @args )
        {
            $statement =~ s~\?~'$arg'~;
        }
    }

    #
    #   Write the select statement to the helper server task on the writer pipe
    #   The server will execute the task on our behalf and return the results
    #   on our reader handle
    #
    $self->{ERRSTR} = 'None';
    my $fhw = $self->{CONNECTION}{FH_WRITE};
    print $fhw "$statement\n";

    #
    #   Extract ALL the data from the link
    #   This will allow for nested calls
    #
    #   Assume that we have an error, until we see the start of data marker
    #
    $self->{ERROR} = 1;
    my $fhr = $self->{CONNECTION}{FH_READ};
    while ( <$fhr> )
    {
        chomp;
        if ( m/Data:(.*)/ )
        {
            push @rows, $1;
            print "[DBI] Execute: $_\n" if $verbose > 2;
        }
        else
        {
            print "[DBI] Execute: $_\n" if $verbose;
        }
        
        if (m~^Info:ColumnInfo:(\d+):(.*)~) {
            my $data = $2;
            $data =~ s~\s+$~~;
            push @colData, $data;
        }

        if ( m/^DataStart:/ )
        {
            $self->{ERROR} = 0;
        }

        if ( m/^DataEnd:/ )
        {
            last;
        }
        
        if ( m/^Warning:(.*?)\s*$/ )
        {
            $self->{ERROR} = 1;
            $self->{ERRSTR} = $1;
        }

    }

    $self->{ROWS} = \@rows;
    $self->{COLDATA} = \@colData;

    return ! $self->{ERROR};
}

#-------------------------------------------------------------------------------
# Function        : errstr
#
# Description     : Return the last execute error
#
# Inputs          : None
#
# Returns         : String
#
sub errstr
{
    my $self = shift;
    return $self->{ERRSTR};
}

#-------------------------------------------------------------------------------
# Function        : rows
#
# Description     : Return the number of rows extracted in the query
#
# Inputs          : None
#
# Returns         : -1, because we don't know
#
sub rows
{
    my $self = shift;
    my $row_count = 1 + $#{$self->{ROWS}};
    print "[DBI] Rows: $row_count\n" if $verbose > 1;
    return $row_count;
}

#-------------------------------------------------------------------------------
# Function        : fetchrow_array
#
# Description     : Return the next row of data
#
# Inputs          : None
#
# Returns         : The next row of data
#                   undef on end of data
#
sub fetchrow_array
{
    my $self = shift;
    my $rowref = $self->{ROWS};
    my $data = shift @$rowref;

    return () unless ( $data );
    $data =~ s~\s+$~~;

    my @row;
    foreach my $item ( split (',', $data ) )
    {
        push @row, pack( 'H*', $item);
    }

    print "[DBI] RawData: ". join(',', @row)."\n" if $verbose > 1;
#   print "[DBI] RawData: ". join(',', @row)."\n";
    $self->{ROWS_READ}++;
    return @row;
}

#-------------------------------------------------------------------------------
# Function        : fetch_columndata
#
# Description     : NonStandard function
#                   Fetch array of colum names
#
# Inputs          : None 
#
# Returns         : An array of column data
#                   :Sep data of Name:Size:Type
#
sub fetch_columndata
{
    my $self = shift;
    return $self->{COLDATA};
}

#-------------------------------------------------------------------------------
# Function        : finish
#
# Description     : Finish the 'execute'
#
# Inputs          : None
#
# Returns         : Nothing
#
sub finish
{
    my $self = shift;
}

#-------------------------------------------------------------------------------
# Function        : DESTROY
#
# Description     : Called when the object is destroyed
#
# Inputs          :
#
# Returns         :
#
sub DESTROY
{
    my $self = shift;
    $self->dumpSelf() if $verbose > 1;
    print "[DBI] Query destroyed\n" if $verbose;
}

#==============================================================================
#   dumpSelf, debugging member to dump selfs hash
#==============================================================================
sub dumpSelf
{
    use Data::Dumper;

    my $self = shift;

    print Data::Dumper->Dump([$self], [ref($self)]);
}   # dumpSelf


1;