Subversion Repositories DevTools

Rev

Rev 6798 | Rev 6910 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
########################################################################
6177 dpurdie 2
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
227 dpurdie 3
#
6798 dpurdie 4
# Module name   : DBI.pm
227 dpurdie 5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : This package has been created to simplify the migration
10
#                 of the deployments scripts that use an DBI:ODBC interface
11
#                 to Release Manager, to a JDBC interface.
12
#
13
#                 The package provides suffient methods to trick the existing
14
#                 code into working. No More. No less.
15
#
16
#......................................................................#
17
 
233 dpurdie 18
require 5.006_001;
227 dpurdie 19
use strict;
20
use warnings;
21
#use Data::Dumper;
22
use Cwd;
23
use IPC::Open2;
24
 
25
package DBI;
26
our $VERSION = '2.0.1';
27
 
28
 
29
use Exporter;
30
our @EXPORT = qw();
31
our @EXPORT_OK = (@EXPORT, qw());
32
our %EXPORT_TAGS = qw( );
33
our $verbose = $ENV{'GBE_DBI_VERBOSE'} || 0;
34
our $errstr = "No Error";
4545 dpurdie 35
my  $appname = 'ReleaseManagerSelect.jar';
36
my $full_app;
227 dpurdie 37
 
38
#
39
#   The following hash simplifies existing code
40
#   in that it will convert known DBI:ODBC called to a more raw form
41
#
5637 dpurdie 42
my %url_convert = ( 'dbi:ODBC:RM3' => 'GBE_RM_LOCATION' );
227 dpurdie 43
 
44
 
45
#-------------------------------------------------------------------------------
46
# Function        : connect
47
#
48
# Description     : Connect to the SQL server
49
#
50
# Inputs          : $db                     - Database Name
51
#                   $user                   - User Name
52
#                   $passwd                 - PassWord
53
#
54
# Returns         : New connection
55
#                   undef if the required Java utility cannot be found
56
#
57
sub connect
58
{
59
    my $obclass = shift;
60
    my $class = ref($obclass) || $obclass;
61
 
62
    bless my $self = {
63
        DB          => $_[0],
64
        USER        => $_[1],
65
        PASSWORD    => $_[2],
66
        ATTRREF     => $_[3],
67
        ERROR       => 0,
68
        ECOUNT      => 0,
69
        FH_READ     => undef,
70
        FH_WRITE    => undef,
71
        PID         => 0,
72
    } => ( $class );
73
 
74
    if ( $self->{ATTRREF}->{verbose} )
75
    {
76
        $verbose = $self->{ATTRREF}->{verbose};
77
    }
78
 
79
    #
80
    #   Convert legacy ODBC refs
81
    #   Should not needed any more
82
    #
83
    if ( $url_convert{$self->{DB}} )
84
    {
85
        my $var = $url_convert{$self->{DB}};
86
        my $value = $ENV{$var};
5637 dpurdie 87
        die ("Cannot convert old DB value to new form\n",
88
               "User requested: " . $self->{DB} . "\n",
227 dpurdie 89
               "Need Environment Variable: $var" ) unless ($value);
90
 
91
        print "[DBI] Converting DB to URL: " . $self->{DB} ." to $value\n"
92
            if $verbose > 1;
93
        $self->{DB} = $value;
94
    }
95
 
96
    #
97
    #   Need to locate the path to the Java applet
98
    #   This can be in th "CLASSPATH", but that simply forces the problem of
99
    #   locating the utility into the configuration world
100
    #
101
    #   Look in Perls @INC list, as we expect to find the program within one of
102
    #   thePerl Libraries.
103
    #
104
    unless ( $full_app )
105
    {
106
        foreach my $dir ( @INC )
107
        {
4545 dpurdie 108
            my $apppath = "$dir/$appname";
227 dpurdie 109
            next unless ( -f $apppath );
110
 
111
            $full_app = $apppath;
112
            last;
113
        }
114
 
115
        if ( ! $full_app )
116
        {
4545 dpurdie 117
            $errstr = "Cannot find $appname in @INC path";
227 dpurdie 118
            return;
119
        }
120
    }
121
 
122
    $errstr = "OK";
123
 
124
    #
125
    #   Initiate the external application server
126
    #   This is slow and will only done once
127
    #
128
    #   Start up the Java based Oracle interface script
129
    #   Create it with both a read and a write handle as we will be pushing
130
    #   requests at it and pulling data from it.
131
    #
132
    #   Monitor the status of the server waiting for an indication that the
133
    #   connection is open, or that the connection link has terminated.
134
    #
135
    #
136
    my @cmd;
137
    push @cmd, "java";                      # Use form that will not invoke a shell
138
    push @cmd, "-jar";
139
    push @cmd, $full_app;
140
    push @cmd, $self->{DB};
141
    push @cmd, $self->{USER};
142
    push @cmd, $self->{PASSWORD};
143
 
144
    $self->{PID} = IPC::Open2::open2($self->{FH_READ}, $self->{FH_WRITE}, @cmd );
145
 
146
    $self->{ERROR} = 1;
147
    $errstr = "Failed to start server app";
148
    if ( $self->{PID} )
149
    {
150
        #
151
        #   Extract status and any error information
152
        #
153
        my $fh = $self->{FH_READ};
154
        while ( <$fh> )
155
        {
399 dpurdie 156
            $_ =~ s~\s+$~~;
227 dpurdie 157
            print "[DBI] Connect: $_\n" if $verbose;
158
 
159
            if ( m/^ConnectionOpened:/ )
160
            {
161
                $self->{ERROR} = 0;
162
                $errstr = "OK";
163
                last;
164
            }
165
 
166
            if ( m/^Error:(.*?)\s*$/ )
167
            {
168
                $errstr = $1;
169
            }
170
 
399 dpurdie 171
            if ( m/^Status:ConnectionClosed$/ )
172
            {
173
                $self->{PID} = 0;
174
            }
227 dpurdie 175
        }
176
    }
177
 
178
    #
179
    #   Return class iff the connection has been established
180
    #
181
 
182
    return $self->{ERROR} ? undef : ($self);
183
}
184
 
185
 
186
#-------------------------------------------------------------------------------
187
# Function        : errstr
188
#
189
# Description     : Return the last open error
190
#
191
# Inputs          : None
192
#
193
# Returns         : String
194
#
195
sub errstr
196
{
197
    return $errstr;
198
}
199
 
200
#-------------------------------------------------------------------------------
201
# Function        : prepare
202
#
203
# Description     : Prepare an SQL statement for processing
204
#
205
# Inputs          : $self
206
#                   $statement
207
#
208
# Returns         : Reference to a class
209
#                   undefined on error
210
#
211
sub prepare
212
{
213
    my $self = shift;
214
    my $statement = shift;
215
 
216
    #
217
    #   Remove new-lines from the statement
218
    #   Must not send new-lines to the application - they don't make it
219
    #
220
    $statement =~ s~\n~ ~g;
221
 
222
    #
223
    #   Remove leading and trailing whitespace
224
    #
225
    $statement =~ s~^\s*~~;
226
    $statement =~ s~\s*$~~;
227
 
228
    print "[DBI] Prepare: $statement\n" if $verbose;
229
 
230
    #
231
    #   Create a new object to represent the SQL statement being prepared
232
    #
233
    my $class = "DBI_Prepare";
234
    bless my $new_self = {
235
        CONNECTION => $self,
236
        STATEMENT => $statement,
237
        ROWS_READ   => 0,
238
        ROWS        => 0,
239
        FH          => undef,
240
        ERRSTR      => 'OK',
241
    } => ( $class );
242
 
243
    $self->{ECOUNT}++;
244
    return $new_self;
245
}
246
 
247
#-------------------------------------------------------------------------------
248
# Function        : disconnect
249
#
250
# Description     : Close the connection
251
#
252
# Inputs          : None
253
#
254
# Returns         : Nothing
255
#
256
sub disconnect
257
{
258
    my $self = shift;
259
    print "[DBI] Disconnect\n" if $verbose;
260
 
261
    if ( $self->{PID} )
262
    {
263
        #
264
        #   Send out a zero-length query
265
        #   This will cause the helper application to close the connection
266
        #
267
 
268
        my $fhw = $self->{FH_WRITE};
269
        print $fhw "\n\n\n";
270
 
271
        #
272
        #   Now read in data until the pipe breaks
273
        #
274
        my $fhr = $self->{FH_READ};
275
        while ( <$fhr> )
276
        {
277
            chomp;
278
            print "[DBI] Disconnect: $_\n" if $verbose;
279
        }
280
 
281
        close( $self->{FH_READ} );
282
        close( $self->{FH_WRITE} );
283
 
284
        $self->{FH_READ} = undef;
285
        $self->{FH_WRITE} = undef;
286
 
287
        #
288
        #   Kill the server task.
289
        #   It will hang around forever if we don't do this
290
        #
291
#        kill 9, $self->{PID};
292
        $self->{PID} = 0;
293
    }
294
 
295
    return 1;
296
}
297
 
298
#-------------------------------------------------------------------------------
299
# Function        : DESTROY
300
#
301
# Description     : Called when the object is destroyed
302
#
303
# Inputs          :
304
#
305
# Returns         :
306
#
307
sub DESTROY
308
{
309
    my $self = shift;
310
    $self->disconnect();
311
    $self->dumpSelf() if $verbose > 1;
312
    print "[DBI] Connection destroyed\n" if $verbose;
313
}
314
 
315
#==============================================================================
316
#   dumpSelf, debugging member to dump selfs hash
317
#==============================================================================
318
sub dumpSelf
319
{
320
    use Data::Dumper;
321
 
322
    my $self = shift;
323
 
324
    print Data::Dumper->Dump([$self], [ref($self)]);
325
}   # dumpSelf
326
 
327
 
328
 
329
#-------------------------------------------------------------------------------
330
#
331
#   A new package to encapulate the actual SQL operations
332
#
333
package DBI_Prepare;
334
 
335
#-------------------------------------------------------------------------------
336
# Function        : execute
337
#
338
# Description     : Execute the SQL statement
339
#
340
# Inputs          : A list of substitution arguments
341
#                   These will be repalced within the SELECT statement;
342
#
343
# Returns         : True: Execution was good
344
#
345
sub execute
346
{
347
    my $self = shift;
348
    my @args = @_;
349
    my @rows;
350
 
351
    my $statement = $self->{STATEMENT};
352
 
353
 
354
    #
355
    #   The users query may contain '?' characters
356
    #   These are replaced with arguments passed in to the 'execute'
357
    #
358
    if ( @args )
359
    {
360
        foreach my $arg ( @args )
361
        {
362
            $statement =~ s~\?~'$arg'~;
363
        }
364
    }
365
 
366
    #
367
    #   Write the select statement to the helper server task on the writer pipe
368
    #   The server will execute the task on our behalf and return the results
369
    #   on our reader handle
370
    #
371
    $self->{ERRSTR} = 'None';
372
    my $fhw = $self->{CONNECTION}{FH_WRITE};
373
    print $fhw "$statement\n";
374
 
375
    #
376
    #   Extract ALL the data from the link
377
    #   This will allow for nested calls
378
    #
379
    #   Assume that we have an error, until we see the start of data marker
380
    #
381
    $self->{ERROR} = 1;
382
    my $fhr = $self->{CONNECTION}{FH_READ};
383
    while ( <$fhr> )
384
    {
385
        chomp;
386
 
387
        if ( m/Data:(.*)/ )
388
        {
389
            push @rows, $1;
390
            print "[DBI] Execute: $_\n" if $verbose > 2;
391
        }
392
        else
393
        {
394
            print "[DBI] Execute: $_\n" if $verbose;
395
        }
396
 
397
 
398
        if ( m/^DataStart:/ )
399
        {
400
            $self->{ERROR} = 0;
401
        }
402
 
403
        if ( m/^DataEnd:/ )
404
        {
405
            last;
406
        }
407
 
408
        if ( m/^Warning:(.*?)\s*$/ )
409
        {
410
            $self->{ERROR} = 1;
411
            $self->{ERRSTR} = $1;
412
        }
413
 
414
    }
415
 
416
    $self->{ROWS} = \@rows;
417
 
418
    return ! $self->{ERROR};
419
}
420
 
421
#-------------------------------------------------------------------------------
422
# Function        : errstr
423
#
424
# Description     : Return the last execute error
425
#
426
# Inputs          : None
427
#
428
# Returns         : String
429
#
430
sub errstr
431
{
432
    my $self = shift;
433
    return $self->{ERRSTR};
434
}
435
 
436
#-------------------------------------------------------------------------------
437
# Function        : rows
438
#
439
# Description     : Return the number of rows extracted in the query
440
#
441
# Inputs          : None
442
#
443
# Returns         : -1, because we don't know
444
#
445
sub rows
446
{
447
    my $self = shift;
448
    my $row_count = 1 + $#{$self->{ROWS}};
449
    print "[DBI] Rows: $row_count\n" if $verbose > 1;
450
    return $row_count;
451
}
452
 
453
#-------------------------------------------------------------------------------
454
# Function        : fetchrow_array
455
#
456
# Description     : Return the next row of data
457
#
458
# Inputs          : None
459
#
460
# Returns         : The next row of data
461
#                   undef on end of data
462
#
463
sub fetchrow_array
464
{
465
    my $self = shift;
466
    my $rowref = $self->{ROWS};
6887 dpurdie 467
    my $data = shift @$rowref;
227 dpurdie 468
 
469
    return () unless ( $data );
470
    $data =~ s~\s+$~~;
471
 
472
    my @row;
473
    foreach my $item ( split (',', $data ) )
474
    {
475
        push @row, pack( 'H*', $item);
476
    }
477
 
478
    print "[DBI] RawData: ". join(',', @row)."\n" if $verbose > 1;
479
#   print "[DBI] RawData: ". join(',', @row)."\n";
480
    $self->{ROWS_READ}++;
481
    return @row;
482
}
483
 
484
#-------------------------------------------------------------------------------
485
# Function        : finish
486
#
487
# Description     : Finish the 'execute'
488
#
489
# Inputs          : None
490
#
491
# Returns         : Nothing
492
#
493
sub finish
494
{
495
    my $self = shift;
496
}
497
 
498
#-------------------------------------------------------------------------------
499
# Function        : DESTROY
500
#
501
# Description     : Called when the object is destroyed
502
#
503
# Inputs          :
504
#
505
# Returns         :
506
#
507
sub DESTROY
508
{
509
    my $self = shift;
510
    $self->dumpSelf() if $verbose > 1;
511
    print "[DBI] Query destroyed\n" if $verbose;
512
}
513
 
514
#==============================================================================
515
#   dumpSelf, debugging member to dump selfs hash
516
#==============================================================================
517
sub dumpSelf
518
{
519
    use Data::Dumper;
520
 
521
    my $self = shift;
522
 
523
    print Data::Dumper->Dump([$self], [ref($self)]);
524
}   # dumpSelf
525
 
526
 
527
1;
528