Subversion Repositories DevTools

Rev

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