File Coverage

blib/lib/DBD/Wire10.pm
Criterion Covered Total %
statement 36 370 9.7
branch 0 158 0.0
condition 0 29 0.0
subroutine 12 43 27.9
pod 1 1 100.0
total 49 601 8.1


line stmt bran cond sub pod time code
1             # TODO: Find out why DBI croaks under PerlSvc when DBD::Wire10 and Net::Wire10
2             # files are in UTF-8 (with BOM), this prevents using Unicode for the POD docs.
3              
4             # TODO: Put in a place where people expect to find this driver, maybe alias
5             # under package names matching the supported database systems?
6             # (DBD::MySQL::Wire10, DBD::Sphinx::Wire10, DBD::Drizzle::Wire10 etc)
7              
8             package DBD::Wire10;
9              
10 1     1   61389 use strict;
  1         2  
  1         40  
11 1     1   5 use warnings;
  1         2  
  1         28  
12 1     1   5 use DBI;
  1         5  
  1         39  
13 1     1   4 use vars qw($VERSION $err $errstr $state $drh);
  1         1  
  1         699  
14              
15             $VERSION = '1.08';
16             $err = 0;
17             $errstr = '';
18             $state = undef;
19             $drh = undef;
20              
21             our $methods_already_installed = 0;
22              
23             sub driver {
24 0 0   0 1   return $drh if $drh;
25              
26 0           my $class = shift;
27 0           my $attr = shift;
28 0           $class .= '::dr';
29              
30             # TODO: core->ping() + core->connect() useful as a generic mechanism
31 0 0         unless ($methods_already_installed++) {
32 0           eval {
33 0           my $method = "DBI::db::reconnect";
34 0           my $file = "__FILENAME__";
35 0           my $info = {};
36 0           DBI->_install_method($method, $file, $info);
37             };
38 0 0         warn "Failed to register reconnect method: $@" if $@;
39             }
40              
41 0           $drh = DBI::_new_drh($class, {
42             Name => 'Wire10',
43             Version => $VERSION,
44             }, {});
45 0           return $drh;
46             }
47              
48             sub CLONE {
49 0     0     undef $drh;
50             }
51              
52             # TODO: Is there a shrinkwrapped function to do this
53             sub _parse_dsn {
54 0     0     my $class = shift;
55 0           my ($dsn, $args) = @_;
56 0           my ($hash, $var, $val);
57 0 0         return undef if ! defined $dsn;
58              
59 0           while (length $dsn) {
60 0 0         if ($dsn =~ /([^:;]*)[:;](.*)/) {
61 0           $val = $1;
62 0           $dsn = $2;
63             } else {
64 0           $val = $dsn;
65 0           $dsn = '';
66             }
67 0 0         if ($val =~ /([^=]*)=(.*)/) {
68 0           $var = $1;
69 0           $val = $2;
70 0 0 0       if ($var eq 'hostname' || $var eq 'host') {
    0 0        
71 0           $hash->{host} = $val;
72             } elsif ($var eq 'db' || $var eq 'dbname') {
73 0           $hash->{database} = $val;
74             } else {
75 0           $hash->{$var} = $val;
76             }
77             } else {
78 0           for $var (@$args) {
79 0 0         if (! defined($hash->{$var})) {
80 0           $hash->{$var} = $val;
81 0           last;
82             }
83             }
84             }
85             }
86 0           return $hash;
87             }
88              
89             sub _parse_dsn_host {
90 0     0     my ($class, $dsn) = @_;
91 0           my $hash = $class->_parse_dsn($dsn, ['host', 'port']);
92 0           return ($hash->{host}, $hash->{port});
93             }
94              
95              
96              
97             package DBD::Wire10::dr;
98              
99 1     1   7 use strict;
  1         3  
  1         43  
100 1     1   6 use warnings;
  1         1  
  1         36  
101 1     1   1671 use Net::Wire10;
  1         156884  
  1         2549  
102              
103             # Note: rather undocumented, for now blindly hoping that 0 means auto-detect.
104             $DBD::Wire10::dr::imp_data_size = 0;
105              
106             sub connect {
107 0     0     my $drh = shift;
108 0           my ($dsn, $user, $password, $attrhash) = @_;
109              
110 0           my $data_source_info = DBD::Wire10->_parse_dsn(
111             $dsn, ['database', 'host', 'port'],
112             );
113 0   0       $user ||= '';
114 0   0       $password ||= '';
115              
116 0           my $dbh = DBI::_new_dbh($drh, { Name => $dsn });
117 0           eval {
118             # See note in take_imp_data().
119 0           my $wire = delete $attrhash->{dbi_imp_data};
120 0 0         unless (defined $wire) {
121 0   0       $wire = Net::Wire10->new(
      0        
      0        
122             host => $data_source_info->{host},
123             port => $data_source_info->{port},
124             database => $data_source_info->{database},
125             user => $user,
126             password => $password,
127             debug => $attrhash->{wire10_debug} || undef,
128             connect_timeout => $attrhash->{wire10_connect_timeout} || undef,
129             query_timeout => $attrhash->{wire10_query_timeout} || undef,
130             );
131 0           $wire->connect;
132             };
133 0           $dbh->STORE('wire10_driver_dbh', $wire);
134 0           $dbh->STORE('wire10_thread_id', $wire->{server_thread_id});
135 0           $dbh->STORE('wire10_server_version', $wire->{server_version});
136             };
137 0 0         if ($@) {
138 0           $dbh->DBI::set_err(-1, $@);
139 0           return undef;
140             }
141 0           $dbh->STORE('Active', 1);
142 0           return $dbh;
143             }
144              
145             sub data_sources {
146 0     0     return ("DBI:Wire10:");
147             }
148              
149              
150              
151             package DBD::Wire10::db;
152              
153 1     1   17 use strict;
  1         2  
  1         44  
154 1     1   7 use warnings;
  1         2  
  1         1662  
155              
156             $DBD::Wire10::db::imp_data_size = 0;
157              
158             sub quote {
159 0     0     my $dbh = shift;
160 0           my ($statement, $type) = @_;
161 0           return Net::Wire10::Util::quote($statement);
162             }
163              
164             sub quote_identifier {
165 0     0     my $dbh = shift;
166 0           my $name = shift;
167 0           return Net::Wire10::Util::quote_identifier($name);
168             }
169              
170             sub prepare {
171 0     0     my $dbh = shift;
172 0           my ($statement, @attribs) = @_;
173 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
174              
175 0           my $sth;
176 0           eval {
177 0           $sth = DBI::_new_sth($dbh, {Statement => $statement});
178 0           DBD::Wire10::st::_constructor($sth, $wire, $statement);
179             };
180 0 0         if ($@) {
181 0           $dbh->DBI::set_err(-1, $@);
182 0           return undef;
183             }
184 0           return $sth;
185             }
186              
187             sub STORE {
188 0     0     my $dbh = shift;
189 0           my ($key, $value) = @_;
190              
191 0 0         if ($key =~ /^AutoCommit$/) {
192 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
193 0           eval {
194 0           $wire->query("SET AUTOCOMMIT=".$value);
195             };
196 0 0         if ($@) {
197 0 0         die $@ unless $wire->is_connected;
198 0 0         if ($dbh->FETCH('Warn')) {
199 0           warn "Server does not allow setting AUTOCOMMIT: $@";
200             }
201             }
202             # Can't store as AutoCommit via SUPER::STORE, not sure why.
203 0           $dbh->STORE('wire10_autocommit', $value);
204 0           return 1;
205             }
206              
207 0 0         if ($key =~ /^(?:wire10_connect_timeout)$/) {
208 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
209 0           $wire->{connect_timeout} = $value;
210 0           return 1;
211             }
212              
213 0 0         if ($key =~ /^(?:wire10_query_timeout)$/) {
214 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
215 0           $wire->{query_timeout} = $value;
216 0           return 1;
217             }
218              
219 0 0         if ($key =~ /^(?:wire10_debug)$/) {
220 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
221 0           $wire->{debug} = $value;
222 0           return 1;
223             }
224              
225 0 0         if ($key =~ /^(?:wire10_.*)$/) {
226 0           $dbh->{$key} = $value;
227 0           return 1;
228             }
229              
230 0           return $dbh->SUPER::STORE($key, $value);
231             }
232              
233             sub FETCH {
234 0     0     my $dbh = shift;
235 0           my $key = shift;
236              
237 0 0         if ($key =~ /^(?:wire10_connect_timeout)$/) {
238 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
239 0           return $wire->{connect_timeout};
240             }
241 0 0         if ($key =~ /^(?:wire10_query_timeout)$/) {
242 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
243 0           return $wire->{query_timeout};
244             }
245 0 0         if ($key =~ /^(?:wire10_debug)$/) {
246 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
247 0           return $wire->{debug};
248             }
249              
250 0 0         if ($key =~ /^AutoCommit$/) {
251             # See comment in STORE.
252 0           return $dbh->FETCH('wire10_autocommit');
253             }
254              
255 0 0         return $dbh->{$key} if $key =~ /^(?:wire10_.*)$/;
256 0           return $dbh->SUPER::FETCH($key);
257             }
258              
259             sub commit {
260 0     0     my $dbh = shift;
261              
262 0 0         if ($dbh->FETCH('AutoCommit')) {
263 0 0         if ($dbh->FETCH('Warn')) {
264 0           warn 'Commit ineffective while AutoCommit is on';
265             }
266             }
267              
268 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
269 0           $wire->query("COMMIT");
270              
271 0           return 1;
272             }
273              
274             sub rollback {
275 0     0     my $dbh = shift;
276              
277 0 0         if ($dbh->FETCH('AutoCommit')) {
278 0 0         if ($dbh->FETCH('Warn')) {
279 0           warn 'Rollback ineffective while AutoCommit is on';
280             }
281             }
282              
283 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
284 0           $wire->query("ROLLBACK");
285              
286 0           return 1;
287             }
288              
289             sub ping {
290 0     0     my $dbh = shift;
291 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
292              
293 0           eval {
294 0           $wire->ping;
295             };
296              
297 0           my $error = $wire->get_error_info;
298 0 0         if ($error) {
    0          
299 0   0       $dbh->DBI::set_err($error->get_error_code || -1, $error->get_error_message, $error->get_error_state);
300             } elsif ($@) {
301 0           $dbh->DBI::set_err(-1, $@);
302             }
303              
304 0           return $wire->is_connected;
305             }
306              
307             sub reconnect {
308 0     0     my $dbh = shift;
309 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
310              
311 0 0         if ($wire->is_connected) {
312 0           eval {
313 0           $wire->ping;
314             };
315             }
316             # ping() also sets is_connected, unnecessary to check ping return value.
317 0 0         if (not $wire->is_connected) {
318 0           eval {
319 0           $wire->connect;
320             # The below is copy/paste from the drh connect() call.
321 0           $dbh->STORE('wire10_thread_id', $wire->{server_thread_id});
322 0           $dbh->STORE('wire10_server_version', $wire->{server_version});
323 0           $dbh->STORE('Active', 1);
324 0           $dbh->STORE('AutoCommit', $dbh->FETCH('AutoCommit'));
325             };
326             # Return 0 on failure.
327 0           my $error = $wire->get_error_info;
328 0 0         if ($error) {
    0          
329 0   0       $dbh->DBI::set_err($error->get_error_code || -1, $error->get_error_message, $error->get_error_state);
330 0           return 0;
331             } elsif ($@) {
332 0           $dbh->DBI::set_err(-1, $@);
333 0           return 0;
334             }
335             # Return 1 if connection was reestablished.
336 0           return 1;
337             }
338             # Return -1 if nothing besides a protocol ping was done.
339 0           return -1;
340             }
341              
342             sub disconnect {
343 0     0     my $dbh = shift;
344 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
345 0 0         $wire->disconnect if defined $wire;
346 0           $dbh->STORE('wire10_thread_id', undef);
347 0           $dbh->STORE('Active', 0);
348 0           return 1;
349             }
350              
351             sub DESTROY {
352 0     0     my $dbh = shift;
353 0 0         $dbh->disconnect if $dbh->FETCH('Active');
354 0           $dbh->SUPER::DESTROY;
355             }
356              
357             sub last_insert_id {
358 0     0     my $dbh = shift;
359 0           return $dbh->FETCH('wire10_insertid')
360             }
361              
362             # TODO: Support more get_info properties as needed.
363             sub get_info {
364 0     0     my $dbh = shift;
365 0           my $type = shift;
366             # 17: SQL_DBMS_NAME
367             # Difficult to return something intelligent here, the server
368             # only reports a version, not a daemon name in the handshake.
369 0 0         return 'Wire10' if $type == 17;
370             # 18: SQL_DBMS_VER
371 0 0         return $dbh->FETCH('wire10_server_version') if $type == 18;
372             # 29: SQL_IDENTIFIER_QUOTE_CHAR
373 0 0         return '`' if $type == 29;
374             # 41: SQL_CATALOG_NAME_SEPARATOR
375 0 0         return '.' if $type == 41;
376             # 114: SQL_CATALOG_LOCATION
377             # According to MSDN, 0 means "catalog not supported" which is accurate.
378             # (The server happily accepts, discards and prints a catalog named
379             # 'def', though.)
380 0 0         return 0 if $type == 114;
381             # Return undef for unknown and unsupported attributes.
382 0           return undef;
383             }
384              
385             sub take_imp_data {
386 0     0     my $dbh = shift;
387              
388             # Finish any active statements (important if streaming enabled).
389 0 0         for my $sth (@{$dbh->{ChildHandles} || []}) {
  0            
390 0 0         next unless $sth;
391 0 0         $sth->finish if $sth->{Active};
392             }
393              
394             # Take out core driver and remove reference to it.
395 0           my $wire = $dbh->FETCH('wire10_driver_dbh');
396 0           $dbh->STORE('wire10_driver_dbh', undef);
397              
398             # Remove reference to dbh from drh, probably also destroys dbh.
399 0           $dbh->SUPER::take_imp_data;
400              
401             # Note: It would be nice to serialize or tie the core such that
402             # it can be be shared among interpreters running in different
403             # processes or threads. Unfortunately, neither of the available
404             # modules, Storable and threads::shared, seem to be able to
405             # cope with socket handles:
406             #
407             # Storable error: "Can't store GLOB items"
408             # threads::shared error: "Invalid value for shared scalar"
409             #
410             # For now, we just return the core driver and expect that the
411             # caller serialize and deserialize the object if the caller needs
412             # to use it from a different context.
413              
414             # Return the core driver.
415 0           return $wire;
416             }
417              
418              
419              
420             package DBD::Wire10::st;
421              
422 1     1   7 use strict;
  1         3  
  1         37  
423 1     1   5 use warnings;
  1         3  
  1         26  
424 1     1   6 use DBI qw(:sql_types);
  1         2  
  1         2455  
425              
426             $DBD::Wire10::st::imp_data_size = 0;
427              
428             # TODO: Find out if DBI already calls a DBD st constructor somewhere
429             sub _constructor {
430 0     0     my $sth = shift;
431 0           my $wire = shift;
432 0           my $sql = shift;
433              
434 0           my $ps = $wire->prepare($sql);
435              
436             # Store driver handle and prepared statement for later.
437 0           $sth->STORE('wire10_driver_sth', $wire);
438 0           $sth->STORE('wire10_prepared', $ps);
439 0           $sth->STORE('NUM_OF_PARAMS', $ps->get_marker_count);
440             }
441              
442             sub bind_param {
443 0     0     my $sth = shift;
444 0           my ($index, $value, $attr) = @_;
445 0           my $binary = _test_for_binary_flag($sth, $attr);
446 0           my $ps = $sth->FETCH('wire10_prepared');
447 0           $ps->set_parameter($index, $value, $binary);
448 0           return 1;
449             }
450              
451             sub _test_for_binary_flag {
452 0     0     my $sth = shift;
453 0           my $attr = shift;
454 0 0         return 0 unless defined $attr;
455 0           my $binary = Net::Wire10::DATA_BINARY;
456 0           my $text = Net::Wire10::DATA_TEXT;
457 0           my $sqltype;
458             # May be undefined.
459 0 0         $sqltype = $attr if ref($attr) eq '';
460 0 0         $sqltype = $attr->{TYPE} if ref($attr) eq 'HASH';
461 0 0         if (defined $sqltype) {
462 0 0         return $binary if $sqltype == SQL_BINARY;
463 0 0         return $binary if $sqltype == SQL_VARBINARY;
464 0 0         return $binary if $sqltype == SQL_LONGVARBINARY;
465 0 0         return $binary if $sqltype == SQL_BLOB;
466             }
467             # For testing Oracle-based code with MySQL.
468             # ORA_BLOB is 113, defined in Oracle.h.
469 0           my $oratype;
470 0 0         $oratype = $attr->{ora_type} if ref($attr) eq 'HASH';
471 0 0 0       return $binary if defined $oratype and $oratype == 113;
472 0           return $text;
473             }
474              
475             sub execute {
476 0     0     my $sth = shift;
477 0           my @new_params = @_;
478 0           my $dbh = $sth->{Database};
479 0           my $wire = $sth->FETCH('wire10_driver_sth');
480 0           my $ps = $sth->FETCH('wire10_prepared');
481              
482 0 0         unless (defined($ps)) {
483 0           $sth->DBI::set_err(-1, "execute without prepare");
484 0           return undef;
485             }
486              
487 0 0         if (scalar(@new_params) > 0) {
488 0           $ps->clear_parameter;
489 0           my $i = 1;
490 0           foreach my $p (@new_params) {
491 0           $ps->set_parameter($i++, $p, 0);
492             }
493             }
494              
495 0           my $rowcount = eval {
496 0           $sth->finish;
497 0   0       my $stream_results = $sth->FETCH('wire10_streaming') || 0;
498 0 0         my $res = $stream_results ? $ps->stream : $ps->query;
499              
500 0 0         die if $wire->get_error_info;
501              
502 0           $sth->STORE('wire10_warning_count', $res->get_warning_count);
503             # For backward compatibility and/or do(), store in dbh too.
504 0           my $dbh = $sth->{Database};
505 0           $dbh->STORE('wire10_warning_count', $res->get_warning_count);
506              
507 0 0         if ($res->has_results) {
508 0           $sth->{wire10_iterator} = $res;
509 0           my @names = $res->get_column_info("name");
510 0           $sth->STORE('NUM_OF_FIELDS', scalar @names);
511 0           $sth->STORE('NAME', [@names]);
512 0           my @flags = $res->get_column_info("flags");
513 0           my @nullable = map { ! $_ & Net::Wire10::COLUMN_NOT_NULL } @flags;
  0            
514 0           $sth->STORE('NULLABLE', [@nullable]);
515             # DBI docs says this is important for bind_columns and bind_cols.
516 0           $sth->STORE('Active', 1);
517             # Note: Emulate DBD-MySQL by not resetting insertid in dbh (only sth).
518 0           $sth->STORE('wire10_insertid', undef);
519 0           $sth->{wire10_rows} = $res->get_no_of_selected_rows;
520 0           return $res->get_no_of_selected_rows;
521             } else {
522 0           $sth->{wire10_iterator} = undef;
523 0           $sth->STORE('NUM_OF_FIELDS', undef);
524 0           $sth->STORE('NAME', undef);
525 0           $sth->STORE('NULLABLE', undef);
526 0           my $insertid;
527 0           eval {
528 0           $insertid = $res->get_insert_id;
529             };
530 0 0         if ($@) {
531             # If the insert_id is too big for this Perl to handle,
532             # extract it using an alternate method.
533 0           my $res = $wire->query('SELECT LAST_INSERT_ID()');
534 0   0       $insertid = $res->next_array()->[0] || 0;
535             }
536 0           $sth->STORE('wire10_insertid', $insertid);
537             # For backward compatibility and/or do(), store in dbh too.
538 0           $dbh->STORE('wire10_insertid', $insertid);
539 0           $sth->{wire10_rows} = $res->get_no_of_affected_rows;
540 0           return $res->get_no_of_affected_rows;
541             }
542             };
543              
544 0           my $error = $wire->get_error_info;
545 0 0         if ($error) {
    0          
546 0   0       $sth->DBI::set_err($error->get_error_code || -1, $error->get_error_message, $error->get_error_state);
547 0           return undef;
548             } elsif ($@) {
549 0           $sth->DBI::set_err(-1, $@);
550 0           return undef;
551             }
552              
553 0 0         return $rowcount ? $rowcount : '0E0';
554             }
555              
556             sub cancel {
557 0     0     my $sth = shift;
558 0           my $wire = $sth->FETCH('wire10_driver_sth');
559              
560 0           eval {
561 0           $wire->cancel;
562             };
563              
564 0 0         if ($@) {
565 0           $sth->DBI::set_err(-1, $@);
566 0           return undef;
567             }
568              
569 0           return 1;
570             }
571              
572             sub finish {
573 0     0     my $sth = shift;
574 0           my $dbh = $sth->{Database};
575             # If in streaming mode, flush remaining results.
576 0           my $iterator = $sth->{wire10_iterator};
577 0 0         $iterator->spool if defined $iterator;
578 0           $sth->{wire10_iterator} = undef;
579 0           $sth->STORE('Active', 0);
580 0           $sth->SUPER::finish;
581             }
582              
583             sub fetchrow_arrayref {
584 0     0     my $sth = shift;
585              
586 0           my $iterator = $sth->FETCH('wire10_iterator');
587 0 0         unless ($iterator) {
588 0 0         if ($sth->FETCH('Warn')) {
589 0           warn 'fetch() without execute(), previous execute() failed, executed query does not have results, or last row was already fetched';
590             }
591 0           return undef;
592             }
593              
594 0           my $row = undef;
595 0           eval {
596 0           $row = $iterator->next_array;
597             };
598 0 0         if ($@) {
599 0           $sth->DBI::set_err(-1, $@);
600 0           return undef;
601             }
602 0 0         if (! $row) {
603 0           $sth->finish;
604 0           return undef;
605             }
606              
607 0 0         if ($sth->FETCH('ChopBlanks')) {
608 0           map {s/\s+$//} @$row;
  0            
609             }
610              
611 0           return $sth->_set_fbav($row);
612             }
613              
614             # required alias for fetchrow_arrayref
615             *fetch = \&fetchrow_arrayref;
616              
617             sub rows {
618 0     0     my $sth = shift;
619 0           my $rows = $sth->FETCH('wire10_rows');
620 0 0         return $rows unless $rows == -1;
621 0           return $sth->SUPER::rows;
622             }
623              
624             sub FETCH {
625 0     0     my $sth = shift;
626 0           my $key = shift;
627              
628 0 0         return $sth->{NAME} if $key eq 'NAME';
629 0 0         return $sth->{NULLABLE} if $key eq 'NULLABLE';
630 0 0         return $sth->{$key} if $key =~ /^wire10_/;
631 0           return $sth->SUPER::FETCH($key);
632             }
633              
634             sub STORE {
635 0     0     my $sth = shift;
636 0           my ($key, $value) = @_;
637              
638 0 0         if ($key eq 'NAME') {
639 0           $sth->{NAME} = $value;
640 0           return 1;
641             }
642 0 0         if ($key eq 'NULLABLE') {
643 0           $sth->{NULLABLE} = $value;
644 0           return 1;
645             }
646 0 0         if ($key =~ /^wire10_/) {
647 0           $sth->{$key} = $value;
648 0           return 1;
649             }
650              
651 0           return $sth->SUPER::STORE($key, $value);
652             }
653              
654             sub DESTROY {
655 0     0     my $sth = shift;
656 0 0         $sth->finish if $sth->FETCH('Active');
657 0           $sth->SUPER::DESTROY;
658             }
659              
660              
661              
662             1;
663             __END__