Subversion Repositories DevTools

Rev

Rev 7387 | Details | Compare with Previous | Last modification | View Log | RSS feed

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