Subversion Repositories DevTools

Rev

Rev 6776 | Go to most recent revision | 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
        #
265
        #   Kill the server task.
266
        #   It will hang around forever if we don't do this
267
        #
268
#        kill 9, $self->{PID};
269
        $self->{PID} = 0;
270
    }
271
 
272
    return 1;
273
}
274
 
275
#-------------------------------------------------------------------------------
276
# Function        : DESTROY
277
#
278
# Description     : Called when the object is destroyed
279
#
280
# Inputs          :
281
#
282
# Returns         :
283
#
284
sub DESTROY
285
{
286
    my $self = shift;
287
    $self->disconnect();
288
    $self->dumpSelf() if $verbose > 1;
289
    print "[DBI] Connection destroyed\n" if $verbose;
290
}
291
 
292
#==============================================================================
293
#   dumpSelf, debugging member to dump selfs hash
294
#==============================================================================
295
sub dumpSelf
296
{
297
    use Data::Dumper;
298
 
299
    my $self = shift;
300
 
301
    print Data::Dumper->Dump([$self], [ref($self)]);
302
}   # dumpSelf
303
 
304
 
305
 
306
#-------------------------------------------------------------------------------
307
#
308
#   A new package to encapulate the actual SQL operations
309
#
310
package DBI_Prepare;
311
 
312
#-------------------------------------------------------------------------------
313
# Function        : execute
314
#
315
# Description     : Execute the SQL statement
316
#
317
# Inputs          : A list of substitution arguments
318
#                   These will be repalced within the SELECT statement;
319
#
320
# Returns         : True: Execution was good
321
#
322
sub execute
323
{
324
    my $self = shift;
325
    my @args = @_;
326
    my @rows;
327
 
328
    my $statement = $self->{STATEMENT};
329
 
330
 
331
    #
332
    #   The users query may contain '?' characters
333
    #   These are replaced with arguments passed in to the 'execute'
334
    #
335
    if ( @args )
336
    {
337
        foreach my $arg ( @args )
338
        {
339
            $statement =~ s~\?~'$arg'~;
340
        }
341
    }
342
 
343
    #
344
    #   Write the select statement to the helper server task on the writer pipe
345
    #   The server will execute the task on our behalf and return the results
346
    #   on our reader handle
347
    #
348
    $self->{ERRSTR} = 'None';
349
    my $fhw = $self->{CONNECTION}{FH_WRITE};
350
    print $fhw "$statement\n";
351
 
352
    #
353
    #   Extract ALL the data from the link
354
    #   This will allow for nested calls
355
    #
356
    #   Assume that we have an error, until we see the start of data marker
357
    #
358
    $self->{ERROR} = 1;
359
    my $fhr = $self->{CONNECTION}{FH_READ};
360
    while ( <$fhr> )
361
    {
362
        chomp;
363
 
364
        if ( m/Data:(.*)/ )
365
        {
366
            push @rows, $1;
367
            print "[DBI] Execute: $_\n" if $verbose > 2;
368
        }
369
        else
370
        {
371
            print "[DBI] Execute: $_\n" if $verbose;
372
        }
373
 
374
 
375
        if ( m/^DataStart:/ )
376
        {
377
            $self->{ERROR} = 0;
378
        }
379
 
380
        if ( m/^DataEnd:/ )
381
        {
382
            last;
383
        }
384
 
385
        if ( m/^Warning:(.*?)\s*$/ )
386
        {
387
            $self->{ERROR} = 1;
388
            $self->{ERRSTR} = $1;
389
        }
390
 
391
    }
392
 
393
    $self->{ROWS} = \@rows;
394
 
395
    return ! $self->{ERROR};
396
}
397
 
398
#-------------------------------------------------------------------------------
399
# Function        : errstr
400
#
401
# Description     : Return the last execute error
402
#
403
# Inputs          : None
404
#
405
# Returns         : String
406
#
407
sub errstr
408
{
409
    my $self = shift;
410
    return $self->{ERRSTR};
411
}
412
 
413
#-------------------------------------------------------------------------------
414
# Function        : rows
415
#
416
# Description     : Return the number of rows extracted in the query
417
#
418
# Inputs          : None
419
#
420
# Returns         : -1, because we don't know
421
#
422
sub rows
423
{
424
    my $self = shift;
425
    my $row_count = 1 + $#{$self->{ROWS}};
426
    print "[DBI] Rows: $row_count\n" if $verbose > 1;
427
    return $row_count;
428
}
429
 
430
#-------------------------------------------------------------------------------
431
# Function        : fetchrow_array
432
#
433
# Description     : Return the next row of data
434
#
435
# Inputs          : None
436
#
437
# Returns         : The next row of data
438
#                   undef on end of data
439
#
440
sub fetchrow_array
441
{
442
    my $self = shift;
443
    my $rowref = $self->{ROWS};
7387 dpurdie 444
    my $data = shift @$rowref;
1038 dpurdie 445
 
446
    return () unless ( $data );
447
    $data =~ s~\s+$~~;
448
 
449
    my @row;
450
    foreach my $item ( split (',', $data ) )
451
    {
452
        push @row, pack( 'H*', $item);
453
    }
454
 
455
    print "[DBI] RawData: ". join(',', @row)."\n" if $verbose > 1;
456
#   print "[DBI] RawData: ". join(',', @row)."\n";
457
    $self->{ROWS_READ}++;
458
    return @row;
459
}
460
 
461
#-------------------------------------------------------------------------------
462
# Function        : finish
463
#
464
# Description     : Finish the 'execute'
465
#
466
# Inputs          : None
467
#
468
# Returns         : Nothing
469
#
470
sub finish
471
{
472
    my $self = shift;
473
}
474
 
475
#-------------------------------------------------------------------------------
476
# Function        : DESTROY
477
#
478
# Description     : Called when the object is destroyed
479
#
480
# Inputs          :
481
#
482
# Returns         :
483
#
484
sub DESTROY
485
{
486
    my $self = shift;
487
    $self->dumpSelf() if $verbose > 1;
488
    print "[DBI] Query destroyed\n" if $verbose;
489
}
490
 
491
#==============================================================================
492
#   dumpSelf, debugging member to dump selfs hash
493
#==============================================================================
494
sub dumpSelf
495
{
496
    use Data::Dumper;
497
 
498
    my $self = shift;
499
 
500
    print Data::Dumper->Dump([$self], [ref($self)]);
501
}   # dumpSelf
502
 
503
 
504
1;
505