File Coverage

blib/lib/DBD/MVS_FTPSQL.pm
Criterion Covered Total %
statement 20 262 7.6
branch 1 132 0.7
condition 0 24 0.0
subroutine 7 33 21.2
pod 0 1 0.0
total 28 452 6.1


line stmt bran cond sub pod time code
1             # DBD::MVS_FTPSQL - DBD driver to query IBM DB2 mainframe databases through an FTP server.
2             #
3             # Copyright (c) 2007 Clemente Biondo
4             #
5             # You may distribute under the terms of either the GNU General Public
6             # License or the Artistic License, as specified in the Perl README file.
7            
8 1     1   119610 use warnings;
  1         3  
  1         49  
9 1     1   6 use strict;
  1         1  
  1         42  
10             #require 5.004;
11             require DBI;
12 1     1   4 use Net::FTP;
  1         6  
  1         44  
13 1     1   5 use IO::File;
  1         1  
  1         318  
14 1     1   7 use Carp qw(croak);
  1         1  
  1         3888  
15            
16             package DBD::MVS_FTPSQL;
17             our $VERSION = '0.38.14';
18            
19             our $drh = undef; # Driver handle. Every thread has one (see CLONE method)
20            
21             # Driver handle constructor
22             sub driver {
23 1 50   1 0 2156 return $drh if $drh; # If already created, return it
24 1         3 my ($class, $attr) = @_;
25 1         2 $class .= "::dr";
26            
27 1         6 return DBI::_new_drh($class, {
28             'Name' => 'MVS_FTPSQL',
29             'Version' => $VERSION,
30             'Attribution' => 'DBD::MVS_FTPSQL by Clemente Biondo '.
31             ''
32             });
33             }
34            
35             #Ensure that two different ithreads don't' share the same driver object
36 0     0   0 sub CLONE {undef $drh;}
37            
38             #End of DBD::MVS_FTPSQL
39             package DBD::MVS_FTPSQL::dr;
40            
41             $DBD::MVS_FTPSQL::dr::imp_data_size = 0;
42            
43             # Database handle constructor.
44             # Some database specific verifications, default settings and the like can
45             # go here.
46             sub connect {
47 0     0   0 my ($drh, $dr_dsn, $username, $password, $attr) = @_;
48 0         0 my $driver_prefix = "mvs_ftpsql_";
49            
50             #The dr_dsn string is in "ODBC" format name1=value1;...;nameN=valueN
51 0         0 foreach my $var ( split /;/, $dr_dsn ) {
52 0         0 my ($attr_name, $attr_value) = split '=', $var, 2;
53 0 0       0 return $drh->set_err(1, "Can't parse DSN part '$var'")
54             unless defined $attr_value;
55            
56             # add driver prefix to attribute name if it doesn't have it already
57 0 0       0 $attr_name = $driver_prefix.$attr_name
58             unless $attr_name =~ /^$driver_prefix/o;
59            
60             # Store attribute into %$attr, replacing any existing value.
61             # The DBI will STORE() these into $dbh after we've connected
62 0         0 $attr->{$attr_name} = $attr_value;
63             }
64            
65            
66 0 0       0 return $drh->set_err(1, "Error in the dns string: you must specify the ".
67             "mainframe hostname.")
68             unless defined ($attr->{mvs_ftpsql_hostname});
69            
70             # Get the attributes we'll use to connect.
71             # We use delete here because these no need to STORE them
72 0         0 my $host = delete $attr->{mvs_ftpsql_hostname};
73 0   0     0 my $port = delete $attr->{mvs_ftpsql_port} || 21;
74 0   0     0 my $timeout = delete $attr->{mvs_ftpsql_timeout} || 120;
75 0   0     0 my $remote_directory = delete $attr->{mvs_ftpsql_remote_directory} || '';
76            
77             #Additional default attributes
78 0 0       0 $attr->{mvs_ftpsql_remote_prefix} = 'FSQL'
79             unless $attr->{mvs_ftpsql_remote_prefix};
80            
81 0 0       0 $attr->{mvs_ftpsql_ssid} = ''
82             unless $attr->{mvs_ftpsql_ssid};
83 0         0 my $debug = 0;
84 0 0       0 my $conn = Net::FTP->new( $host
85             ,Port => $port
86             ,Debug => $debug
87             ,Timeout => $timeout
88             ,Passive => 1 )
89             or return $drh->set_err(1,"Cannot establish an ftp connection to host ".
90             "$host at port $port. Error received: $!");
91            
92 0 0       0 return $drh->set_err(1,"Login failed. Error received: ". $conn->message)
93             unless ($conn->login($username,$password));
94            
95 0 0       0 unless ($remote_directory eq '') {
96 0         0 $remote_directory =~ s/^([^\/])/\/\/$1/;
97 0 0       0 return $drh->set_err(1,"Remote directory not accepted. Error received: ".
98             $conn->message) unless ($conn->cwd($remote_directory));
99             }
100            
101 0         0 my ($outer, $dbh) = DBI::_new_dbh($drh, { Name => $dr_dsn });
102             #$dbh->STORE('Active', 1 );
103            
104 0         0 $dbh->{mvs_ftpsql_connection} = $conn;
105 0         0 return $outer;
106             }
107            
108 0     0   0 sub data_sources {return undef;}
109            
110 1     1   1758 sub disconnect_all {}
111            
112             #End of DBD::MVS_FTPSQL::dr
113            
114             package DBD::MVS_FTPSQL::db;
115            
116             $DBD::MVS_FTPSQL::db::imp_data_size = 0;
117            
118             #Todo:
119             # primary_key
120             # foreign_key_info
121            
122             # The get_info function was automatically generated by
123             # DBI::DBD::Metadata::write_getinfo_pm v1.05.
124             sub get_info {
125 0     0     my($dbh, $info_type) = @_;
126 0           require DBD::MVS_FTPSQL::GetInfo;
127 0           my $v = $DBD::MVS_FTPSQL::GetInfo::info{int($info_type)};
128 0 0         $v = $v->($dbh) if ref $v eq 'CODE';
129 0           return $v;
130             }
131            
132             # The type_info_all function was automatically generated by
133             # DBI::DBD::Metadata::write_typeinfo_pm v1.05.
134             sub type_info_all {
135 0     0     my ($dbh) = @_;
136 0           require DBD::MVS_FTPSQL::TypeInfo;
137 0           return [ @$DBD::MVS_FTPSQL::TypeInfo::type_info_all ];
138             }
139            
140             #Note: blanks must become undef
141             sub column_info {
142 0     0     my $dbh = shift;
143 0           my $catalog = shift; #not applicable so not used at all
144 0           my $schema = shift;
145 0           my $table = shift;
146 0           my $column = shift;
147 0           my @where = ();
148            
149 0           foreach ( [\$schema,'TBCREATOR'], [\$table,'TBNAME'], [\$column,'NAME']) {
150 0 0 0       if (defined(${$_->[0]}) && ${$_->[0]} ne '') {
  0            
  0            
151 0 0         my $op = index(${$_->[0]},'%') < 0 ? '=' : 'LIKE';
  0            
152 0           push(@where,$_->[1]." $op '".${$_->[0]}."'");
  0            
153             }
154             }
155            
156 0 0         my $where = (($#where >= 0) ? 'WHERE ' : '') . join (' AND ',@where);
157 0   0       my $sth = $dbh->prepare(<
158             select
159             '' as TABLE_CAT
160             ,TBCREATOR as TABLE_SCHEM
161             ,TBNAME as TABLE_NAME
162             ,NAME as COLUMN_NAME
163             ,'' as DATA_TYPE
164             ,COLTYPE as TYPE_NAME
165             ,LENGTH as COLUMN_SIZE
166             ,'' as BUFFER_LENGTH
167             ,LENGTH - SCALE as DECIMAL_DIGITS
168             ,'' as NUM_PREC_RADIX
169             ,case NULLS when 'N' then
170             '0' else '1' end as NULLABLE
171             ,REMARKS as REMARKS
172             ,DEFAULTVALUE as COLUMN_DEF
173             ,'' as SQL_DATA_TYPE
174             ,'' as SQL_DATETIME_SUB
175             ,'' as CHAR_OCTET_LENGTH
176             ,COLNO as ORDINAL_POSITION
177             ,case NULLS when 'N' then
178             'NO' else 'YES' end as IS_NULLABLE
179             from sysibm.syscolumns
180             $where
181             order by TBCREATOR,TBNAME,NAME,COLNO
182             with ur
183             EOSQL
184 0 0         $sth->execute() || Carp::croak ("Execute operation failed:$!");
185 0           return $sth;
186             }
187            
188             #Note: blanks must become undef
189             sub table_info {
190 0     0     my $dbh = shift;
191 0           my $catalog = shift; #not applicable so not used at all
192 0           my $schema = shift;
193 0           my $table = shift;
194 0           my $type = shift;
195            
196 0           my %type2flag = (
197             'ALIAS' => 'A'
198             ,'GLOBAL TEMPORARY' => 'G'
199             ,'SYSTEM TABLE' => 'T'
200             ,'TABLE' => 'T'
201             ,'VIEW' => 'V'
202             ,'AUXILIARY TABLE' => 'X'
203             ,'MATERIALIZED QUERY TABLE' => 'M'
204             );
205            
206 0           my $flag_table = $type2flag{$type};
207 0 0         $flag_table = '' unless(defined($type2flag{$type}));
208            
209 0           my @where = ();
210            
211 0           foreach ( [\$schema,'CREATOR'], [\$table,'NAME'], [\$flag_table,'TYPE']) {
212 0 0 0       if (defined(${$_->[0]}) && ${$_->[0]} ne '') {
  0            
  0            
213 0 0         my $op = index(${$_->[0]},'%') < 0 ? '=' : 'LIKE';
  0            
214 0           push(@where,$_->[1]." $op '".${$_->[0]}."'");
  0            
215             }
216             }
217            
218 0 0         my $where = (($#where >= 0) ? 'WHERE ' : '') . join (' AND ',@where);
219            
220             #There is no need of escaping because only the first sql instruction can be
221             #executed and this driver alllows only selects.
222             # create a "blank" statement handle
223 0   0       my $sth = $dbh->prepare(<
224             SELECT
225             '' AS TABLE_CAT
226             ,NAME as TABLE_NAME
227             ,CREATOR as TABLE_SCHEM
228             ,case when type = 'A' then 'ALIAS'
229             when type = 'G' then 'GLOBAL TEMPORARY'
230             when type = 'T' and name like 'SYS' then 'SYSTEM TABLE'
231             when type = 'T' and name not like 'SYS' then 'TABLE'
232             when type = 'V' then 'VIEW'
233             when type = 'X' then 'AUXILIARY TABLE'
234             when type = 'M' then 'MATERIALIZED QUERY TABLE'
235             else 'UNKNOWN' END AS TABLE_TYPE
236             ,REMARKS
237             FROM SYSIBM.SYSTABLES
238             $where
239             WITH UR
240             EOSQL
241 0 0         $sth->execute() || Carp::croak ("Execute operation failed:$!");
242 0           return $sth;
243             }
244            
245             sub ping {
246 0     0     my $dbh = shift;
247 0 0         if ($dbh->FETCH('Active')) {
248 0           my $warnmsg = "";
249             {
250 0     0     local $SIG{__WARN__} = sub {$warnmsg=shift;};
  0            
  0            
251 0           $dbh->{mvs_ftpsql_connection}->quot('noop');
252             }
253 0 0         $dbh->disconnect() unless $warnmsg eq "";
254             #Todo: warnmsg needs to be returned to the user?
255             }
256 0           return $dbh->FETCH('Active');
257             }
258            
259             sub prepare {
260 0     0     my ($dbh, $statement, @attribs) = @_;
261 0 0         return $drh->set_err(1, 'Statement preparation failed: '.
262             'There is no active database connection.')
263             unless $dbh->FETCH('Active');
264 0 0         return $drh->set_err(1, 'Statement preparation failed: '.
265             'The sql statement is empty.') unless length($statement);
266            
267             # workaround for a peculiarity of the ftp server: if CR/LF is present
268             # the preceding character will be removed (the string will be chopped)
269 0           $statement =~ s/\r|\n/ /g;
270             # create a 'blank' sth
271 0           my ($outer, $sth) = DBI::_new_sth($dbh, {
272             Statement => $statement
273             });
274            
275             # Todo: improve the placeholder management
276 0           $sth->STORE('NUM_OF_PARAMS', ($statement =~ tr/?//));
277 0           $sth->{mvs_ftpsql_params} = [];
278            
279 0           return $outer;
280             }
281            
282             sub commit {
283 0     0     my ($dbh) = @_;
284 0 0         if ($dbh->FETCH('Warn')) {
285 0           warn("Commit ineffective while AutoCommit is on");
286             }
287 0           0;
288             }
289            
290             sub rollback {
291 0     0     my ($dbh) = @_;
292 0 0         if ($dbh->FETCH('Warn')) {
293 0           warn("Rollback ineffective while AutoCommit is on");
294             }
295 0           0;
296             }
297            
298             sub STORE {
299 0     0     my ($dbh, $attr, $val) = @_;
300 0 0         if ($attr eq 'AutoCommit') {
301 0 0         if (!$val) { die "Can't disable AutoCommit"; }
  0            
302 0           return 1;
303             }
304 0 0         if ($attr eq 'ChopBlanks') {
305 0 0         if (!$val) { die "Can't set ChopBlanks to false"; }
  0            
306 0           return 1;
307             }
308 0 0         if ($attr eq 'Active') {
309 0           die "Can't change the read-only connection status attribute 'Active'";
310 0           return 1;
311             }
312 0 0         if ($attr =~ m/^mvs_ftpsql_/) {
313 0           $dbh->{$attr} = $val;
314 0           return 1;
315             }
316 0           $dbh->SUPER::STORE($attr, $val);
317             }
318            
319             sub FETCH {
320 0     0     my ($dbh, $attr) = @_;
321 0 0         if ($attr eq 'AutoCommit') { return 1; }
  0            
322 0 0         if ($attr eq 'ChopBlanks') { return 1; }
  0            
323 0 0         if ($attr eq 'Active') {
324 0   0       return defined($dbh->{mvs_ftpsql_connection})
325             && defined($dbh->{mvs_ftpsql_connection}->connected());
326             }
327 0 0         if ($attr =~ m/^mvs_ftpsql_/) {
328 0           return $dbh->{$attr};
329             }
330            
331             # defined($conn->connected());
332            
333 0           $dbh->SUPER::FETCH($attr);
334             }
335            
336             sub disconnect () {
337 0     0     my $dbh = shift;
338 0 0         $dbh->{mvs_ftpsql_connection}->quit() if $dbh->FETCH('Active');
339             #$dbh->STORE('Active',0);
340 0           return 1;
341             }
342            
343             sub DESTROY ($) {
344 0     0     my $dbh = shift;
345             #Take care of DBI handle 0x....... cleared whilst still active error.
346 0           $dbh->disconnect();
347             }
348            
349             #End of DBD::MVS_FTPSQL::db
350            
351             package DBD::MVS_FTPSQL::st;
352            
353             $DBD::MVS_FTPSQL::st::imp_data_size = 0;
354            
355             #Attributes Implemented
356             #NUM_OF_FIELDS (integer, read-only)
357             #NAME (array-ref, read-only)
358             #NAME_lc (array-ref, read-only)
359             #NAME_uc (array-ref, read-only)
360             #NAME_hash (hash-ref, read-only)
361             #NAME_lc_hash (hash-ref, read-only)
362             #NAME_uc_hash (hash-ref, read-only)
363             #Statement (string, read-only)
364             #Database (dbh, read-only)
365             #Attributes not Implemented (todo)
366             #TYPE (array-ref, read-only)
367             #PRECISION (array-ref, read-only)
368             #SCALE (array-ref, read-only)
369             #NULLABLE (array-ref, read-only)
370             #CursorName (string, read-only)
371             #ParamValues (hash ref, read-only)
372             #ParamArrays (hash ref, read-only)
373             #ParamTypes (hash ref, read-only)
374             #RowsInCache (integer, read-only)
375            
376             sub STORE {
377 0     0     my ($sth, $attr, $val) = @_;
378 0 0         if ($attr =~ m/^mvs_ftpsql_/) {
379 0           $sth->{$attr} = $val;
380 0           return 1;
381             }
382 0           $sth->SUPER::STORE($attr, $val);
383             }
384            
385             sub FETCH {
386 0     0     my ($sth, $attr) = @_;
387 0 0         if ($attr =~ m/^mvs_ftpsql_/) {
388 0           return $sth->{$attr};
389             }
390 0           $sth->SUPER::FETCH($attr);
391             }
392            
393             #Taken (like other pieces of code) from DBI guide
394             sub bind_param {
395 0     0     my ($sth, $pNum, $val, $attr) = @_;
396 0 0         my $type = (ref $attr) ? $attr->{TYPE} : $attr;
397 0 0         if ($type) {
398 0           my $dbh = $sth->{Database};
399             #mhm seems a bug in the manual?
400             #$val = $dbh->quote($sth, $type);
401 0           $val = $dbh->quote($val, $type);
402             }
403 0           my $params = $sth->{mvs_ftpsql_params};
404 0           $params->[$pNum-1] = $val;
405 0           1;
406             }
407            
408             sub execute {
409 0     0     my ($sth, @bind_values) = @_;
410            
411             # start of by finishing any previous execution if still active
412 0 0         $sth->finish if $sth->FETCH('Active');
413 0 0         my $params = (@bind_values) ?
414             \@bind_values : $sth->{mvs_ftpsql_params};
415            
416 0           my $numParam = $sth->FETCH('NUM_OF_PARAMS');
417 0 0         return $sth->set_err(1, "Wrong number of parameters")
418             if @$params != $numParam;
419            
420 0           my $statement = $sth->{'Statement'};
421            
422             #Todo: the bind mechanism needs to be improved
423 0           for (my $i = 0; $i < $numParam; $i++) {
424 0           $statement =~ s/\?/$params->[$i]/;
425             }
426            
427             #very dirty error handling technique, but eval {} if(@$) seems to clutter
428             #(maybe my mistake) with $drh->set_err (todo: dig into the problem)
429 0           my ($error_code,$error_message,$error_state) = (1,"",0);
430 0           my $dbh = $sth->{Database};
431 0 0         my $fh = mvs_ftpsql_execute(
432             $dbh->{'mvs_ftpsql_connection'}
433             ,$dbh->{'mvs_ftpsql_ssid'}
434             ,$dbh->{'mvs_ftpsql_remote_prefix'}
435             ,$statement
436             ,\$error_message
437             ,\$error_state
438             ,\$error_code
439             ) or return $sth->set_err($error_code, $error_message,$error_state);
440            
441             # Notice that this driver processes only SELECT statement (a protocol
442             # limitation imposed by design), so $fh is ever a file handle to the
443             # output of a query.
444             #print while(<$fh>);exit;
445 0           my $header = <$fh>;
446            
447             #\x00 was placed as a workaround for a strange behaviour with some tables
448 0           $header =~ s/\x00| |\r|\n//g;
449 0           my @header = split(/\t/,$header);
450             #print $header[0];exit;
451 0 0         unless (exists($sth->{'NAME'})) {
452 0           $sth->STORE('NUM_OF_FIELDS' => $#header +1);
453 0           $sth->{'NAME'} = \@header;
454             }
455            
456 0           $sth->{'mvs_ftpsql_data'} = $fh;
457            
458             #Row counting
459 0           my $rowcount = 0;
460 0           my $pos = $fh->getpos();
461 0           $rowcount++ while(<$fh>);
462 0           $fh->setpos($pos);
463 0           $sth->{'mvs_ftpsql_rows'} = $rowcount;
464            
465 0           $sth->{Active} = 1;
466 0 0         return ($rowcount ? $rowcount : '0E0');
467             }
468            
469             sub fetchrow_arrayref {
470 0     0     my ($sth) = @_;
471 0           my $fh = $sth->{mvs_ftpsql_data};
472 0 0         unless ($fh) {
473 0           $sth->STORE(Active => 0);
474 0           return undef;
475             }
476 0           my $tmp = <$fh>;
477 0 0         unless ($tmp) {
478 0           $sth->STORE(Active => 0);
479 0           return undef;
480             }
481            
482             #Text fields are right padded, numbers are left padded.
483             #The field is at least long as his label.
484             #This is the reason we can't disable ChopBlanks
485 0           $tmp =~ s/\r|\n//g;
486 0           $tmp =~ s/ +\t/\t/g;
487 0           $tmp =~ s/ +$//g;
488            
489 0           my @fields = split(/\t/,$tmp,-1);
490 0 0         if (($sth->FETCH('NUM_OF_FIELDS')) < ($#fields+1) ) {
491 0           $fh->close();
492 0           $sth->SUPER::finish();
493             #Todo: give more info in the pod and propose as solution
494             #TRANSLATE (A, ' ', x'05') (lo horizontal tab (HT) \x09 in EBCDIC diventa \x05)
495 0           Carp::croak (
496             "Fetch failed: Horizontal tab found. One or more character columns in the resultset ".
497             "contain tabs characters ('\\x09').\nAlthough not an error, due to ".
498             "limitations imposed by the ftp/sql feature this driver can't ".
499             "manage those values.\nSee the documentation to learn how to work ".
500             "around this issue."
501             );
502             }
503 0           return $sth->_set_fbav(\@fields);
504             }
505            
506             *fetch = \&fetchrow_arrayref; # required alias for fetchrow_arrayref
507            
508 0     0     sub rows { shift->{mvs_ftpsql_rows}; }
509            
510             sub DESTROY {
511 0     0     my $sth = shift;
512 0 0         $sth->finish if $sth->FETCH('Active');
513             }
514            
515             sub finish {
516 0     0     my $sth = shift;
517 0           $sth->{mvs_ftpsql_data}->close();
518 0           $sth->SUPER::finish();
519             }
520            
521             sub mvs_ftpsql_execute {
522 0     0     my $ftp_conn = shift;
523 0           my $db2subsys = shift;
524 0           my $remote_sql_filename_prefix = shift;
525 0           my $sql = shift;
526 0           my $error_message = shift;
527 0           my $error_state = shift;
528 0           my $error_code = shift;
529 0           my $qlen = length($sql);
530            
531             #datasets allocated with RETPD > 0 can't be deleted
532 0           $ftp_conn->quot("site FILE=SEQ LR=$qlen BLOCKSI=$qlen REC=F RET=0");
533            
534             #Query upload
535 0 0         my $fh = IO::File->new_tmpfile()
536             or Carp::croak("Cannot create temporary storage for the sql statement:$!");
537 0 0         $fh->seek(0,0) || Carp::croak ("Seek operation failed:$!");
538 0           print $fh $sql;
539 0 0         $fh->flush() || Carp::croak ("Flush operation failed:$!");
540 0 0         $fh->seek(0,0) || Carp::croak ("Seek operation failed:$!");
541 0           $ftp_conn->put_unique($fh,$remote_sql_filename_prefix.'0001');
542            
543             #Workaround:the current implementation of Net::FTP::put_unique do not
544             #returns the filename. The error lie in the regexp at line 72 of
545             #Net/FTP/dataconn.pm
546 0 0 0       my $filename = $1
547             if $ftp_conn->message() =~
548             /($remote_sql_filename_prefix\d{4}) \(unique name\)/
549             or Carp::croak ("Cannot determine the remote sql filename.");
550 0           $ftp_conn->quot ('SITE NOTRAIL FILE=SQL DB2='.$db2subsys.' SPR LR=32000 REC=F '.
551             'SQLC=N BLOCKSI=32000');
552 0 0         $fh->truncate(0) || Carp::croak ("Truncate operation failed:$!");
553             #Error handling
554             #"551 Transfer aborted: SQL PREPARE/DESCRIBE failure" -> sql syntax error
555             #"551 Transfer aborted: SQL not available. Attempt to open plan EZAFTPMQ"
556             #"554 Transfer aborted: unsupported SQL statement" -> only selects
557             #"551 Transfer aborted: attempt to connect to DB2 failed" -> subsystem error
558             #MVS was unable to locate a DB2 subsystem with the specified name
559            
560 0           my $warnmsg = "";
561 0           my $transfer_msg="";
562             {
563 0     0     local $SIG{__WARN__} = sub {$warnmsg=shift;};
  0            
  0            
564 0           $ftp_conn->get ($filename,$fh);
565 0           $transfer_msg = $ftp_conn->message();
566             }
567 0           $ftp_conn->quot ('SITE FILETYPE=SEQ');
568 0           $ftp_conn->delete($filename);
569 0 0         if ($transfer_msg =~ /Transfer aborted: SQL PREPARE\/DESCRIBE failure/) {
    0          
    0          
570 0 0         $fh->flush() || Carp::croak ("Flush operation failed:$!");
571 0 0         $fh->seek(0,0) ||Carp::croak ("Seek operation failed:$!");
572            
573 0           $$error_message = "The SQL statement is invalid:\n". do {local $/; <$fh>} ."\n";
  0            
  0            
574            
575             #Workaround for a problem with filehandles and set_err
576             #Forces a copy of the content of the file.
577             #Without the following line the content of the error message is not reported.
578 0           $$error_message = sprintf ('%s',$$error_message);
579 0 0         $$error_state = $1 if ($$error_message =~ /SQLSTATE\s+=\s+(\d+)/);
580 0 0         $$error_code = $1 if ($$error_message =~ /SQLCODE\s+=\s+([\-0-9]+)/);
581            
582             #print "$$error_state";
583             #exit;
584            
585             } elsif($transfer_msg =~ /Transfer aborted: (.*)/) {
586 0           $$error_message = $1;
587 0           $$error_code = '-30080';
588 0           $$error_state = '08001'
589             } elsif ($warnmsg ne "") {
590 0           $$error_message = $warnmsg;
591 0           $$error_code = '-30080';
592 0           $$error_state = '08001'
593             } else {
594 0 0         $fh->seek(0,0) || Carp::croak ("Seek operation failed:$!");
595 0           return $fh;
596             }
597 0 0         $fh->flush() || Carp::croak ("Flush operation failed:$!");
598 0           $fh->close();
599 0           undef ($fh);
600 0           return undef;
601             }
602            
603             #End of DBD::MVS_FTPSQL::st
604            
605             1;
606            
607             __END__