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
 
23
require 5.6.1;
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";
40
my  $appname = 'ReleaseManagerSelect.jar';
41
my $full_app;
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
        {
113
            my $apppath = "$dir/$appname";
114
            next unless ( -f $apppath );
115
 
116
            $full_app = $apppath;
117
            last;
118
        }
119
 
120
        if ( ! $full_app )
121
        {
122
            $errstr = "Cannot find $appname in @INC path";
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
        {
161
            chomp;
162
            print "[DBI] Connect: $_\n" if $verbose;
163
 
164
            if ( m/^ConnectionOpened:/ )
165
            {
166
                $self->{ERROR} = 0;
167
                $errstr = "OK";
168
                last;
169
            }
170
 
171
            if ( m/^Error:(.*)/ )
172
            {
173
                $errstr = $1;
174
            }
175
 
176
        }
177
    }
178
 
179
    #
180
    #   Return class iff the connection has been established
181
    #
182
 
183
    return $self->{ERROR} ? undef : ($self);
184
}
185
 
186
 
187
#-------------------------------------------------------------------------------
188
# Function        : errstr
189
#
190
# Description     : Return the last open error
191
#
192
# Inputs          : None
193
#
194
# Returns         : String
195
#
196
sub errstr
197
{
198
    return $errstr;
199
}
200
 
201
#-------------------------------------------------------------------------------
202
# Function        : prepare
203
#
204
# Description     : Prepare an SQL statement for processing
205
#
206
# Inputs          : $self
207
#                   $statement
208
#
209
# Returns         : Reference to a class
210
#                   undefined on error
211
#
212
sub prepare
213
{
214
    my $self = shift;
215
    my $statement = shift;
216
 
217
    #
218
    #   Remove new-lines from the statement
219
    #   Must not send new-lines to the application - they don't make it
220
    #
221
    $statement =~ s~\n~ ~g;
222
 
223
    #
224
    #   Remove leading and trailing whitespace
225
    #
226
    $statement =~ s~^\s*~~;
227
    $statement =~ s~\s*$~~;
228
 
229
    print "[DBI] Prepare: $statement\n" if $verbose;
230
 
231
    #
232
    #   Create a new object to represent the SQL statement being prepared
233
    #
234
    my $class = "DBI_Prepare";
235
    bless my $new_self = {
236
        CONNECTION => $self,
237
        STATEMENT => $statement,
238
        ROWS_READ   => 0,
239
        ROWS        => 0,
240
        FH          => undef,
241
        ERRSTR      => 'OK',
242
    } => ( $class );
243
 
244
    $self->{ECOUNT}++;
245
    return $new_self;
246
}
247
 
248
#-------------------------------------------------------------------------------
249
# Function        : disconnect
250
#
251
# Description     : Close the connection
252
#
253
# Inputs          : None
254
#
255
# Returns         : Nothing
256
#
257
sub disconnect
258
{
259
    my $self = shift;
260
    print "[DBI] Disconnect\n" if $verbose;
261
 
262
    if ( $self->{PID} )
263
    {
264
        #
265
        #   Send out a zero-length query
266
        #   This will cause the helper application to close the connection
267
        #
268
 
269
        my $fhw = $self->{FH_WRITE};
270
        print $fhw "\n\n\n";
271
 
272
        #
273
        #   Now read in data until the pipe breaks
274
        #
275
        my $fhr = $self->{FH_READ};
276
        while ( <$fhr> )
277
        {
278
            chomp;
279
            print "[DBI] Disconnect: $_\n" if $verbose;
280
        }
281
 
282
        close( $self->{FH_READ} );
283
        close( $self->{FH_WRITE} );
284
 
285
        $self->{FH_READ} = undef;
286
        $self->{FH_WRITE} = undef;
287
 
288
        #
289
        #   Kill the server task.
290
        #   It will hang around forever if we don't do this
291
        #
292
#        kill 9, $self->{PID};
293
        $self->{PID} = 0;
294
    }
295
 
296
    return 1;
297
}
298
 
299
#-------------------------------------------------------------------------------
300
# Function        : DESTROY
301
#
302
# Description     : Called when the object is destroyed
303
#
304
# Inputs          :
305
#
306
# Returns         :
307
#
308
sub DESTROY
309
{
310
    my $self = shift;
311
    $self->disconnect();
312
    $self->dumpSelf() if $verbose > 1;
313
    print "[DBI] Connection destroyed\n" if $verbose;
314
}
315
 
316
#==============================================================================
317
#   dumpSelf, debugging member to dump selfs hash
318
#==============================================================================
319
sub dumpSelf
320
{
321
    use Data::Dumper;
322
 
323
    my $self = shift;
324
 
325
    print Data::Dumper->Dump([$self], [ref($self)]);
326
}   # dumpSelf
327
 
328
 
329
 
330
#-------------------------------------------------------------------------------
331
#
332
#   A new package to encapulate the actual SQL operations
333
#
334
package DBI_Prepare;
335
 
336
#-------------------------------------------------------------------------------
337
# Function        : execute
338
#
339
# Description     : Execute the SQL statement
340
#
341
# Inputs          : A list of substitution arguments
342
#                   These will be repalced within the SELECT statement;
343
#
344
# Returns         : True: Execution was good
345
#
346
sub execute
347
{
348
    my $self = shift;
349
    my @args = @_;
350
    my @rows;
351
 
352
    my $statement = $self->{STATEMENT};
353
 
354
 
355
    #
356
    #   The users query may contain '?' characters
357
    #   These are replaced with arguments passed in to the 'execute'
358
    #
359
    if ( @args )
360
    {
361
        foreach my $arg ( @args )
362
        {
363
            $statement =~ s~\?~'$arg'~;
364
        }
365
    }
366
 
367
    #
368
    #   Write the select statement to the helper server task on the writer pipe
369
    #   The server will execute the task on our behalf and return the results
370
    #   on our reader handle
371
    #
372
    $self->{ERRSTR} = 'None';
373
    my $fhw = $self->{CONNECTION}{FH_WRITE};
374
    print $fhw "$statement\n";
375
 
376
    #
377
    #   Extract ALL the data from the link
378
    #   This will allow for nested calls
379
    #
380
    #   Assume that we have an error, until we see the start of data marker
381
    #
382
    $self->{ERROR} = 1;
383
    my $fhr = $self->{CONNECTION}{FH_READ};
384
    while ( <$fhr> )
385
    {
386
        chomp;
387
 
388
        if ( m/Data:(.*)/ )
389
        {
390
            push @rows, $1;
391
            print "[DBI] Execute: $_\n" if $verbose > 2;
392
        }
393
        else
394
        {
395
            print "[DBI] Execute: $_\n" if $verbose;
396
        }
397
 
398
 
399
        if ( m/^DataStart:/ )
400
        {
401
            $self->{ERROR} = 0;
402
        }
403
 
404
        if ( m/^DataEnd:/ )
405
        {
406
            last;
407
        }
408
 
409
        if ( m/^Warning:(.*)/ )
410
        {
411
            $self->{ERROR} = 1;
412
            $self->{ERRSTR} = $1;
413
        }
414
 
415
    }
416
 
417
    $self->{ROWS} = \@rows;
418
 
419
    return ! $self->{ERROR};
420
}
421
 
422
#-------------------------------------------------------------------------------
423
# Function        : errstr
424
#
425
# Description     : Return the last execute error
426
#
427
# Inputs          : None
428
#
429
# Returns         : String
430
#
431
sub errstr
432
{
433
    my $self = shift;
434
    return $self->{ERRSTR};
435
}
436
 
437
 
438
 
439
#-------------------------------------------------------------------------------
440
# Function        : rows
441
#
442
# Description     : Return the number of rows extracted in the query
443
#
444
# Inputs          : None
445
#
446
# Returns         : -1, because we don't know
447
#
448
sub rows
449
{
450
    my $self = shift;
451
    my $row_count = 1 + $#{$self->{ROWS}};
452
    print "[DBI] Rows: $row_count\n" if $verbose > 1;
453
    return $row_count;
454
}
455
 
456
#-------------------------------------------------------------------------------
457
# Function        : fetchrow_array
458
#
459
# Description     : Return the next row of data
460
#
461
# Inputs          : None
462
#
463
# Returns         : The next row of data
464
#                   undef on end of data
465
#
466
sub fetchrow_array
467
{
468
    my $self = shift;
469
    my $rowref = $self->{ROWS};
470
    my $data = pop @$rowref;
471
 
472
    return () unless ( $data );
473
    $data =~ s~\s+$~~;
474
 
475
    my @row;
476
    foreach my $item ( split (',', $data ) )
477
    {
478
        push @row, pack( 'H*', $item);
479
    }
480
 
481
    print "[DBI] RawData: ". join(',', @row)."\n" if $verbose > 1;
482
#   print "[DBI] RawData: ". join(',', @row)."\n";
483
    $self->{ROWS_READ}++;
484
    return @row;
485
}
486
 
487
#-------------------------------------------------------------------------------
488
# Function        : finish
489
#
490
# Description     : Finish the 'execute'
491
#
492
# Inputs          : None
493
#
494
# Returns         : Nothing
495
#
496
sub finish
497
{
498
    my $self = shift;
499
}
500
 
501
#-------------------------------------------------------------------------------
502
# Function        : DESTROY
503
#
504
# Description     : Called when the object is destroyed
505
#
506
# Inputs          :
507
#
508
# Returns         :
509
#
510
sub DESTROY
511
{
512
    my $self = shift;
513
    $self->dumpSelf() if $verbose > 1;
514
    print "[DBI] Query destroyed\n" if $verbose;
515
}
516
 
517
#==============================================================================
518
#   dumpSelf, debugging member to dump selfs hash
519
#==============================================================================
520
sub dumpSelf
521
{
522
    use Data::Dumper;
523
 
524
    my $self = shift;
525
 
526
    print Data::Dumper->Dump([$self], [ref($self)]);
527
}   # dumpSelf
528
 
529
 
530
1;
531