Subversion Repositories DevTools

Rev

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

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