File Coverage

blib/lib/DBD/PgPP.pm
Criterion Covered Total %
statement 181 772 23.4
branch 25 276 9.0
condition 9 74 12.1
subroutine 56 167 33.5
pod 3 4 75.0
total 274 1293 21.1


line stmt bran cond sub pod time code
1             package DBD::PgPP;
2 3     3   153903 use strict;
  3         7  
  3         248  
3              
4 3     3   3149 use DBI;
  3         29822  
  3         251  
5 3     3   22 use Carp ();
  3         10  
  3         44  
6 3     3   3153 use IO::Socket ();
  3         130358  
  3         103  
7 3     3   29 use Digest::MD5 ();
  3         6  
  3         9786  
8              
9             =head1 NAME
10              
11             DBD::PgPP - Pure Perl PostgreSQL driver for the DBI
12              
13             =head1 SYNOPSIS
14              
15             use DBI;
16              
17             my $dbh = DBI->connect('dbi:PgPP:dbname=$dbname', '', '');
18              
19             # See the DBI module documentation for full details
20              
21             =cut
22              
23             our $VERSION = '0.08';
24             my $BUFFER_LEN = 1500;
25             my $DEBUG;
26              
27             my %BYTEA_DEMANGLE = (
28             '\\' => '\\',
29             map { sprintf('%03o', $_) => chr $_ } 0 .. 255,
30             );
31              
32             {
33             my $drh;
34             sub driver {
35 0     0 0 0 my ($class, $attr) = @_;
36 0   0     0 return $drh ||= DBI::_new_drh("$class\::dr", {
37             Name => 'PgPP',
38             Version => $VERSION,
39             Err => \(my $err = 0),
40             Errstr => \(my $errstr = ''),
41             State => \(my $state = undef),
42             Attribution => 'DBD::PgPP by Hiroyuki OYAMA',
43             }, {});
44             }
45             }
46              
47 0     0 1 0 sub pgpp_server_identification { $_[0]->FETCH('pgpp_connection')->{server_identification} }
48 0     0 1 0 sub pgpp_server_version_num { $_[0]->FETCH('pgpp_connection')->{server_version_num} }
49 0     0 1 0 sub pgpp_server_version { $_[0]->FETCH('pgpp_connection')->{server_version} }
50              
51             sub _parse_dsn {
52 0     0   0 my ($class, $dsn, $args) = @_;
53              
54 0 0       0 return if !defined $dsn;
55              
56 0         0 my ($hash, $var, $val);
57 0         0 while (length $dsn) {
58 0 0       0 if ($dsn =~ /([^:;]*)[:;](.*)/) {
59 0         0 $val = $1;
60 0         0 $dsn = $2;
61             }
62             else {
63 0         0 $val = $dsn;
64 0         0 $dsn = '';
65             }
66 0 0       0 if ($val =~ /([^=]*)=(.*)/) {
67 0         0 $var = $1;
68 0         0 $val = $2;
69 0 0 0     0 if ($var eq 'hostname' || $var eq 'host') {
    0 0        
70 0         0 $hash->{'host'} = $val;
71             }
72             elsif ($var eq 'db' || $var eq 'dbname') {
73 0         0 $hash->{'database'} = $val;
74             }
75             else {
76 0         0 $hash->{$var} = $val;
77             }
78             }
79             else {
80 0         0 for $var (@$args) {
81 0 0       0 if (!defined($hash->{$var})) {
82 0         0 $hash->{$var} = $val;
83 0         0 last;
84             }
85             }
86             }
87             }
88 0         0 return $hash;
89             }
90              
91             sub _parse_dsn_host {
92 0     0   0 my ($class, $dsn) = @_;
93 0         0 my $hash = $class->_parse_dsn($dsn, ['host', 'port']);
94 0         0 return @$hash{qw};
95             }
96              
97              
98             package DBD::PgPP::dr;
99              
100             $DBD::PgPP::dr::imp_data_size = 0;
101              
102             sub connect {
103 0     0   0 my ($drh, $dsn, $user, $password, $attrhash) = @_;
104              
105 0         0 my $data_source_info
106             = DBD::PgPP->_parse_dsn($dsn, ['database', 'host', 'port']);
107 0   0     0 $user ||= '';
108 0   0     0 $password ||= '';
109              
110 0         0 my $dbh = DBI::_new_dbh($drh, { Name => $dsn, USER => $user }, {});
111 0         0 eval {
112 0         0 my $pgsql = DBD::PgPP::Protocol->new(
113             hostname => $data_source_info->{host},
114             port => $data_source_info->{port},
115             database => $data_source_info->{database},
116             user => $user,
117             password => $password,
118             debug => $data_source_info->{debug},
119             path => $data_source_info->{path},
120             );
121 0         0 $dbh->STORE(pgpp_connection => $pgsql);
122             };
123 0 0       0 if ($@) {
124 0         0 $dbh->DBI::set_err(1, $@);
125 0         0 return undef;
126             }
127 0         0 return $dbh;
128             }
129              
130 0     0   0 sub data_sources { 'dbi:PgPP:' }
131              
132 0     0   0 sub disconnect_all {}
133              
134              
135             package DBD::PgPP::db;
136              
137             $DBD::PgPP::db::imp_data_size = 0;
138              
139             # We need to implement ->quote, because otherwise we get the default DBI
140             # one, which ignores backslashes. The DBD::Pg implementation doubles all
141             # backslashes and apostrophes; this version backslash-protects all of them.
142             # XXX: What about byte sequences that don't form valid characters in the
143             # relevant encoding?
144             # XXX: What about type-specific quoting?
145             sub quote {
146 0     0   0 my ($dbh, $s) = @_;
147              
148 0 0       0 if (!defined $s) {
149 0         0 return 'NULL';
150             }
151             else {
152             # In PostgreSQL versions before 8.1, plain old string literals are
153             # assumed to use backslash escaping. But that's incompatible with
154             # the SQL standard, which admits no special meaning for \ in a
155             # string literal, and requires the single-quote character to be
156             # doubled for inclusion in a literal. So PostgreSQL 8.1 introduces
157             # a new extension: an "escaped string" syntax E'...' which is
158             # unambiguously defined to support backslash sequences. The plan is
159             # apparently that some future version of PostgreSQL will change
160             # plain old literals to use the SQL-standard interpretation. So the
161             # only way I can quote reliably on both current versions and that
162             # hypothetical future version is to (a) always put backslashes in
163             # front of both single-quote and backslash, and (b) use the E'...'
164             # syntax if we know we're speaking to a version recent enough to
165             # support it.
166             #
167             # Also, it's best to always quote the value, even if it looks like a
168             # simple integer. Otherwise you can't compare the result of quoting
169             # Perl numeric zero to a boolean column. (You can't _reliably_
170             # compare a Perl scalar to a boolean column anyway, because there
171             # are six Postgres syntaxes for TRUE, and six for FALSE, and
172             # everything else is an error -- but that's another story, and at
173             # least if you quote '0' it looks false to Postgres. Sigh. I have
174             # some plans for a pure-Perl DBD which understands the 7.4 protocol,
175             # and can therefore fix up bools in _both_ directions.)
176              
177 0         0 my $version = $dbh->FETCH('pgpp_connection')->{server_version_num};
178 0         0 $s =~ s/(?=[\\\'])/\\/g;
179 0         0 $s =~ s/\0/\\0/g;
180 0 0       0 return $version >= 80100 ? "E'$s'" : "'$s'";
181             }
182             }
183              
184             sub prepare {
185 0     0   0 my ($dbh, $statement, @attribs) = @_;
186              
187 0 0       0 die 'PostgreSQL does not accept queries containing \0 bytes'
188             if $statement =~ /\0/;
189              
190 0         0 my $pgsql = $dbh->FETCH('pgpp_connection');
191 0         0 my $parsed = $pgsql->parse_statement($statement);
192              
193 0         0 my $sth = DBI::_new_sth($dbh, { Statement => $statement });
194 0         0 $sth->STORE(pgpp_parsed_stmt => $parsed);
195 0         0 $sth->STORE(pgpp_handle => $pgsql);
196 0         0 $sth->STORE(pgpp_params => []);
197 0         0 $sth->STORE(NUM_OF_PARAMS => scalar grep { ref } @$parsed);
  0         0  
198 0         0 $sth;
199             }
200              
201             sub commit {
202 0     0   0 my ($dbh) = @_;
203              
204 0         0 my $pgsql = $dbh->FETCH('pgpp_connection');
205 0         0 eval {
206 0         0 my $pgsth = $pgsql->prepare('COMMIT');
207 0         0 $pgsth->execute;
208             };
209 0 0       0 if ($@) {
210 0         0 $dbh->DBI::set_err(1, $@); # $pgsql->get_error_message ???
211 0         0 return undef;
212             }
213 0         0 return 1;
214             }
215              
216             sub rollback {
217 0     0   0 my ($dbh) = @_;
218 0         0 my $pgsql = $dbh->FETCH('pgpp_connection');
219 0         0 eval {
220 0         0 my $pgsth = $pgsql->prepare('ROLLBACK');
221 0         0 $pgsth->execute;
222             };
223 0 0       0 if ($@) {
224 0         0 $dbh->DBI::set_err(1, $@); # $pgsql->get_error_message ???
225 0         0 return undef;
226             }
227 0         0 return 1;
228             }
229              
230             sub disconnect {
231 0     0   0 my ($dbh) = @_;
232              
233 0 0       0 if (my $conn = $dbh->FETCH('pgpp_connection')) {
234 0         0 $conn->close;
235 0         0 $dbh->STORE('pgpp_connection', undef);
236             }
237              
238 0         0 return 1;
239             }
240              
241             sub FETCH {
242 0     0   0 my ($dbh, $key) = @_;
243              
244 0 0       0 return $dbh->{$key} if $key =~ /^pgpp_/;
245 0 0       0 return $dbh->{AutoCommit} if $key eq 'AutoCommit';
246 0         0 return $dbh->SUPER::FETCH($key);
247             }
248              
249             sub STORE {
250 0     0   0 my ($dbh, $key, $new) = @_;
251              
252 0 0       0 if ($key eq 'AutoCommit') {
253 0         0 my $old = $dbh->{$key};
254 0         0 my $never_set = !$dbh->{pgpp_ever_set_autocommit};
255              
256             # This logic is stolen from DBD::Pg
257 0 0 0     0 if (!$old && $new && $never_set) {
    0 0        
    0 0        
      0        
      0        
      0        
      0        
258             # Do nothing; fall through
259             }
260             elsif (!$old && $new) {
261             # Turning it on: commit
262             # XXX: Avoid this if no uncommitted changes.
263             # XXX: Desirable? See dbi-dev archives.
264             # XXX: Handle errors.
265 0         0 my $st = $dbh->{pgpp_connection}->prepare('COMMIT');
266 0         0 $st->execute;
267             }
268             elsif ($old && !$new || !$old && !$new && $never_set) {
269             # Turning it off, or initializing it to off at
270             # connection time: begin a new transaction
271             # XXX: Handle errors.
272 0         0 my $st = $dbh->{pgpp_connection}->prepare('BEGIN');
273 0         0 $st->execute;
274             }
275              
276 0         0 $dbh->{pgpp_ever_set_autocommit} = 1;
277 0         0 $dbh->{$key} = $new;
278              
279 0         0 return 1;
280             }
281              
282 0 0       0 if ($key =~ /^pgpp_/) {
283 0         0 $dbh->{$key} = $new;
284 0         0 return 1;
285             }
286              
287 0         0 return $dbh->SUPER::STORE($key, $new);
288             }
289              
290             sub last_insert_id {
291 0     0   0 my ($db, undef, $schema, $table, undef, $attr) = @_;
292             # DBI uses (catalog,schema,table,column), but we don't make use of
293             # catalog or column, so don't bother storing them.
294              
295 0         0 my $pgsql = $db->FETCH('pgpp_connection');
296              
297 0 0 0     0 if (!defined $attr) {
    0          
    0          
298 0         0 $attr = {};
299             }
300             elsif (!ref $attr && $attr ne '') {
301             # If not a hash, assume it is a sequence name
302 0         0 $attr = { sequence => $attr };
303             }
304             elsif (ref $attr ne 'HASH') {
305 0         0 return $db->set_err(1, "last_insert_id attrs must be a hashref");
306             }
307              
308             # Catalog and col are not used
309 0 0       0 $schema = '' if !defined $schema;
310 0 0       0 $table = '' if !defined $table;
311              
312             # Cache all of our table lookups? Default is yes
313 0 0       0 my $use_cache = exists $attr->{pgpp_cache} ? $attr->{pgpp_cache} : 1;
314              
315             # Cache key. Note we must distinguish ("a.b", "c") from ("a", "b.c")
316             # (and XXX: we ought really to have tests for that)
317 0         0 my $cache_key = join '.', map { quotemeta } $schema, $table;
  0         0  
318              
319 0         0 my $sequence;
320 0 0 0     0 if (defined $attr->{sequence}) {
    0          
321             # Named sequence overrides any table or schema settings
322 0         0 $sequence = $attr->{sequence};
323             }
324             elsif ($use_cache && exists $db->{pgpp_liicache}{$cache_key}) {
325 0         0 $sequence = $db->{pgpp_liicache}{$cache_key};
326             }
327             else {
328             # At this point, we must have a valid table name
329 0 0       0 return $db->set_err(1, "last_insert_id needs a sequence or table name")
330             if $table eq '';
331              
332 0         0 my @args = $table;
333              
334             # Only 7.3 and up can use schemas
335 0         0 my $pg_catalog;
336 0 0       0 if ($pgsql->{server_version_num} < 70300) {
337 0         0 $schema = '';
338 0         0 $pg_catalog = '';
339             }
340             else {
341 0         0 $pg_catalog = 'pg_catalog.';
342             }
343              
344             # Make sure the table in question exists and grab its oid
345 0         0 my ($schemajoin, $schemawhere) = ('','');
346 0 0       0 if (length $schema) {
347 0         0 $schemajoin =
348             ' JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace';
349 0         0 $schemawhere = ' AND n.nspname = ?';
350 0         0 push @args, $schema;
351             }
352              
353 0         0 my $st = $db->prepare(qq[
354             SELECT c.oid FROM ${pg_catalog}pg_class c $schemajoin
355             WHERE relname = ? $schemawhere
356             ]);
357 0         0 my $count = $st->execute(@args);
358 0 0       0 if (!defined $count) {
359 0         0 $st->finish;
360 0         0 my $message = qq{Could not find the table "$table"};
361 0 0       0 $message .= qq{ in the schema "$schema"} if $schema ne '';
362 0         0 return $db->set_err(1, $message);
363             }
364 0         0 my $oid = $st->fetchall_arrayref->[0][0];
365             # This table has a primary key. Is there a sequence associated with
366             # it via a unique, indexed column?
367 0         0 $st = $db->prepare(qq[
368             SELECT a.attname, i.indisprimary, substring(d.adsrc for 128) AS def
369             FROM ${pg_catalog}pg_index i
370             JOIN ${pg_catalog}pg_attribute a ON a.attrelid = i.indrelid
371             AND a.attnum = i.indkey[0]
372             JOIN ${pg_catalog}pg_attrdef d ON d.adrelid = a.attrelid
373             AND d.adnum = a.attnum
374             WHERE i.indrelid = $oid
375             AND a.attrelid = $oid
376             AND i.indisunique IS TRUE
377             AND a.atthasdef IS TRUE
378             AND d.adsrc ~ '^nextval'
379             ]);
380 0         0 $count = $st->execute;
381 0 0       0 if (!defined $count) {
382 0         0 $st->finish;
383 0         0 return $db->set_err(1, qq{No suitable column found for last_insert_id of table "$table"});
384             }
385 0         0 my $info = $st->fetchall_arrayref;
386              
387             # We have at least one with a default value. See if we can determine
388             # sequences
389 0         0 my @def;
390 0         0 for (@$info) {
391 0 0       0 my ($seq) = $_->[2] =~ /^nextval\('([^']+)'::/ or next;
392 0         0 push @def, [@$_, $seq];
393             }
394              
395 0 0       0 return $db->set_err(1, qq{No suitable column found for last_insert_id of table "$table"\n})
396             if !@def;
397              
398             # Tiebreaker goes to the primary keys
399 0 0       0 if (@def > 1) {
400 0         0 my @pri = grep { $_->[1] } @def;
  0         0  
401 0 0       0 return $db->set_err(1, qq{No suitable column found for last_insert_id of table "$table"\n})
402             if @pri != 1;
403 0         0 @def = @pri;
404             }
405              
406 0         0 $sequence = $def[0][3];
407              
408             # Cache this information for subsequent calls
409 0         0 $db->{pgpp_liicache}{$cache_key} = $sequence;
410             }
411              
412 0         0 my $st = $db->prepare("SELECT currval(?)");
413 0         0 $st->execute($sequence);
414 0         0 return $st->fetchall_arrayref->[0][0];
415             }
416              
417             sub DESTROY {
418 0     0   0 my ($dbh) = @_;
419 0         0 $dbh->disconnect;
420             }
421              
422             package DBD::PgPP::st;
423              
424             $DBD::PgPP::st::imp_data_size = 0;
425              
426             sub bind_param {
427 0     0   0 my ($sth, $index, $value, $attr) = @_;
428 0 0       0 my $type = ref($attr) ? $attr->{TYPE} : $attr;
429 0         0 my $dbh = $sth->{Database};
430 0         0 my $params = $sth->FETCH('pgpp_params');
431 0         0 $params->[$index - 1] = $dbh->quote($value, $type);
432             }
433              
434             sub execute {
435 0     0   0 my ($sth, @args) = @_;
436              
437 0         0 my $pgsql = $sth->FETCH('pgpp_handle');
438 0 0       0 die "execute on disconnected database" if $pgsql->{closed};
439              
440 0         0 my $num_params = $sth->FETCH('NUM_OF_PARAMS');
441              
442 0 0       0 if (@args) {
443 0 0       0 return $sth->set_err(1, "Wrong number of arguments")
444             if @args != $num_params;
445 0         0 my $dbh = $sth->{Database};
446 0         0 $_ = $dbh->quote($_) for @args;
447             }
448             else {
449 0         0 my $bind_params = $sth->FETCH('pgpp_params');
450 0 0       0 return $sth->set_err(1, "Wrong number of bound parameters")
451             if @$bind_params != $num_params;
452              
453             # They've already been quoted by ->bind_param
454 0         0 @args = @$bind_params;
455             }
456              
457 0         0 my $parsed_statement = $sth->FETCH('pgpp_parsed_stmt');
458 0 0       0 my $statement = join '', map { ref() ? $args[$$_] : $_ } @$parsed_statement;
  0         0  
459              
460 0         0 my $result;
461 0         0 eval {
462 0         0 $sth->{pgpp_record_iterator} = undef;
463 0         0 my $pgsql_sth = $pgsql->prepare($statement);
464 0         0 $pgsql_sth->execute;
465 0         0 $sth->{pgpp_record_iterator} = $pgsql_sth;
466 0         0 my $dbh = $sth->{Database};
467              
468 0 0       0 if (defined $pgsql_sth->{affected_rows}) {
469 0         0 $sth->{pgpp_rows} = $pgsql_sth->{affected_rows};
470 0         0 $result = $pgsql_sth->{affected_rows};
471             }
472             else {
473 0         0 $sth->{pgpp_rows} = 0;
474 0         0 $result = $pgsql_sth->{affected_rows};
475             }
476 0 0       0 if (!$pgsql_sth->{row_description}) {
477 0         0 $sth->STORE(NUM_OF_FIELDS => 0);
478 0         0 $sth->STORE(NAME => []);
479             }
480             else {
481 0         0 $sth->STORE(NUM_OF_FIELDS => scalar @{$pgsql_sth->{row_description}});
  0         0  
482 0         0 $sth->STORE(NAME => [ map {$_->{name}} @{$pgsql_sth->{row_description}} ]);
  0         0  
  0         0  
483             }
484             };
485 0 0       0 if ($@) {
486 0         0 $sth->DBI::set_err(1, $@);
487 0         0 return undef;
488             }
489              
490 0 0       0 return $pgsql->has_error ? undef
    0          
491             : $result ? $result
492             : '0E0';
493             }
494              
495             sub fetch {
496 0     0   0 my ($sth) = @_;
497              
498 0         0 my $iterator = $sth->FETCH('pgpp_record_iterator');
499 0 0       0 return undef if $iterator->{finished};
500              
501 0 0       0 if (my $row = $iterator->fetch) {
502 0 0       0 if ($sth->FETCH('ChopBlanks')) {
503 0         0 s/\s+\z// for @$row;
504             }
505 0         0 return $sth->_set_fbav($row);
506             }
507              
508 0         0 $iterator->{finished} = 1;
509 0         0 return undef;
510             }
511             *fetchrow_arrayref = \&fetch;
512              
513             sub rows {
514 0     0   0 my ($sth) = @_;
515 0 0       0 return defined $sth->{pgpp_rows} ? $sth->{pgpp_rows} : 0;
516             }
517              
518             sub FETCH {
519 0     0   0 my ($dbh, $key) = @_;
520              
521             # return $dbh->{AutoCommit} if $key eq 'AutoCommit';
522 0 0       0 return $dbh->{NAME} if $key eq 'NAME';
523 0 0       0 return $dbh->{$key} if $key =~ /^pgpp_/;
524 0         0 return $dbh->SUPER::FETCH($key);
525             }
526              
527             sub STORE {
528 0     0   0 my ($sth, $key, $value) = @_;
529              
530 0 0       0 if ($key eq 'NAME') {
    0          
    0          
531 0         0 $sth->{NAME} = $value;
532 0         0 return 1;
533             }
534             elsif ($key =~ /^pgpp_/) {
535 0         0 $sth->{$key} = $value;
536 0         0 return 1;
537             }
538             elsif ($key eq 'NUM_OF_FIELDS') {
539             # Don't set this twice; DBI doesn't seem to like it.
540             # XXX: why not? Perhaps this conceals a PgPP bug.
541 0         0 my $curr = $sth->FETCH($key);
542 0 0 0     0 return 1 if $curr && $curr == $value;
543             }
544 0         0 return $sth->SUPER::STORE($key, $value);
545             }
546              
547 0     0   0 sub DESTROY { return }
548              
549              
550             package DBD::PgPP::Protocol;
551              
552 3     3   28 use constant DEFAULT_UNIX_SOCKET => '/tmp';
  3         6  
  3         274  
553 3     3   18 use constant DEFAULT_PORT_NUMBER => 5432;
  3         5  
  3         137  
554 3     3   17 use constant DEFAULT_TIMEOUT => 60;
  3         5  
  3         220  
555              
556 3     3   23 use constant AUTH_OK => 0;
  3         6  
  3         129  
557 3     3   25 use constant AUTH_KERBEROS_V4 => 1;
  3         5  
  3         125  
558 3     3   20 use constant AUTH_KERBEROS_V5 => 2;
  3         11  
  3         121  
559 3     3   13 use constant AUTH_CLEARTEXT_PASSWORD => 3;
  3         5  
  3         127  
560 3     3   12 use constant AUTH_CRYPT_PASSWORD => 4;
  3         18  
  3         132  
561 3     3   19 use constant AUTH_MD5_PASSWORD => 5;
  3         6  
  3         107  
562 3     3   13 use constant AUTH_SCM_CREDENTIAL => 6;
  3         4  
  3         7601  
563              
564             sub new {
565 0     0   0 my ($class, %args) = @_;
566              
567 0   0     0 my $self = bless {
      0        
      0        
      0        
      0        
      0        
      0        
      0        
568             hostname => $args{hostname},
569             path => $args{path} || DEFAULT_UNIX_SOCKET,
570             port => $args{port} || DEFAULT_PORT_NUMBER,
571             database => $args{database} || $ENV{USER} || '',
572             user => $args{user} || $ENV{USER} || '',
573             password => $args{password} || '',
574             args => $args{args} || '',
575             tty => $args{tty} || '',
576             timeout => $args{timeout} || DEFAULT_TIMEOUT,
577             'socket' => undef,
578             backend_pid => '',
579             secret_key => '',
580             selected_record => undef,
581             error_message => '',
582             last_oid => undef,
583             server_identification => '',
584             server_version => '0.0.0',
585             server_version_num => 0,
586             }, $class;
587 0 0       0 $DEBUG = 1 if $args{debug};
588 0         0 $self->_initialize;
589 0         0 return $self;
590             }
591              
592             sub close {
593 0     0   0 my ($self) = @_;
594 0 0       0 my $socket = $self->{'socket'} or return;
595 0 0       0 return if !fileno $socket;
596              
597 0         0 my $terminate_packet = 'X' . pack 'N', 5;
598 0 0       0 print " ==> Terminate\n" if $DEBUG;
599 0         0 _dump_packet($terminate_packet);
600 0         0 $socket->send($terminate_packet, 0);
601 0         0 $socket->close;
602 0         0 $self->{closed} = 1;
603             }
604              
605             sub DESTROY {
606 0     0   0 my ($self) = @_;
607 0 0       0 $self->close if $self;
608             }
609              
610             sub _initialize {
611 0     0   0 my ($self) = @_;
612 0         0 $self->_connect;
613 0         0 $self->_do_startup;
614 0         0 $self->_find_server_version;
615             }
616              
617             sub _connect {
618 0     0   0 my ($self) = @_;
619              
620 0         0 my $sock;
621 0 0       0 if ($self->{hostname}) {
622 0 0       0 $sock = IO::Socket::INET->new(
623             PeerAddr => $self->{hostname},
624             PeerPort => $self->{port},
625             Proto => 'tcp',
626             Timeout => $self->{timeout},
627             ) or Carp::croak("Couldn't connect to $self->{hostname}:$self->{port}/tcp: $!");
628             }
629             else {
630 0         0 (my $path = $self->{path}) =~ s{/*\z}{/.s.PGSQL.$self->{port}};
631 0 0       0 $sock = IO::Socket::UNIX->new(
632             Type => IO::Socket::SOCK_STREAM,
633             Peer => $path,
634             ) or Carp::croak("Couldn't connect to $path: $!");
635             }
636 0         0 $sock->autoflush(1);
637 0         0 $self->{socket} = $sock;
638             }
639              
640 0     0   0 sub get_handle { $_[0]{socket} }
641              
642             sub _do_startup {
643 0     0   0 my ($self) = @_;
644              
645             # create message body
646 0         0 my $packet = pack 'n n a64 a32 a64 a64 a64', (
647             2, # Protocol major version - Int16bit
648             0, # Protocol minor version - Int16bit
649             $self->{database}, # Database name - LimString64
650             $self->{user}, # User name - LimString32
651             $self->{args}, # Command line args - LimString64
652             '', # Unused - LimString64
653             $self->{tty} # Debugging msg tty - LimString64
654             );
655              
656             # add packet length
657 0         0 $packet = pack('N', length($packet) + 4). $packet;
658              
659 0 0       0 print " ==> StartupPacket\n" if $DEBUG;
660 0         0 _dump_packet($packet);
661 0         0 $self->{socket}->send($packet, 0);
662 0         0 $self->_do_authentication;
663             }
664              
665             sub _find_server_version {
666 0     0   0 my ($self) = @_;
667 0         0 eval {
668             # If this function doesn't exist (as was the case in PostgreSQL 7.1
669             # and earlier), we'll end up leaving the version as 0.0.0. I can
670             # live with that.
671 0         0 my $st = $self->prepare(q[SELECT version()]);
672 0         0 $st->execute;
673 0         0 my $data = $st->fetch;
674 0         0 1 while $st->fetch;
675 0         0 my $id = $data->[0];
676 0         0 $self->{server_identification} = $id;
677 0 0       0 if (my ($ver) = $id =~ /\A PostgreSQL \s+ ([0-9._]+) (?:\s|\z)/x) {
678 0         0 $self->{server_version} = $ver;
679 0 0       0 if (my ($maj, $min, $sub)
680             = $ver =~ /\A ([0-9]+)\.([0-9]{1,2})\.([0-9]{1,2}) \z/x) {
681 0         0 $self->{server_version_num} = ($maj * 100 + $min) * 100 + $sub;
682             }
683             }
684             };
685             }
686              
687             sub _dump_packet {
688 0 0   0   0 return unless $DBD::PgPP::Protocol::DEBUG;
689              
690 0         0 my ($packet) = @_;
691              
692 0         0 printf "%s()\n", (caller 1)[3];
693 0         0 while ($packet =~ m/(.{1,16})/g) {
694 0         0 my $chunk = $1;
695 0         0 print join ' ', map { sprintf '%02X', ord $_ } split //, $chunk;
  0         0  
696 0         0 print ' ' x (16 - length $chunk);
697 0         0 print ' ';
698 0 0       0 print join '',
699 0         0 map { sprintf '%s', (/[[:graph:] ]/) ? $_ : '.' } split //, $chunk;
700 0         0 print "\n";
701             }
702             }
703              
704             sub get_stream {
705 0     0   0 my ($self) = @_;
706 0 0       0 $self->{stream} = DBD::PgPP::PacketStream->new($self->{'socket'})
707             if !defined $self->{stream};
708 0         0 return $self->{stream};
709             }
710              
711             sub _do_authentication {
712 0     0   0 my ($self) = @_;
713 0         0 my $stream = $self->get_stream;
714 0         0 while (1) {
715 0         0 my $packet = $stream->each;
716 0 0       0 last if $packet->is_end_of_response;
717 0 0       0 Carp::croak($packet->get_message) if $packet->is_error;
718 0         0 $packet->compute($self);
719             }
720             }
721              
722             sub prepare {
723 0     0   0 my ($self, $sql) = @_;
724              
725 0         0 $self->{error_message} = '';
726 0         0 return DBD::PgPP::ProtocolStatement->new($self, $sql);
727             }
728              
729             sub has_error {
730 0     0   0 my ($self) = @_;
731 0 0       0 return 1 if $self->{error_message};
732             }
733              
734             sub get_error_message {
735 0     0   0 my ($self) = @_;
736 0         0 return $self->{error_message};
737             }
738              
739             sub parse_statement {
740 19     19   44403 my ($invocant, $statement) = @_;
741              
742 19         34 my $param_num = 0;
743 19         26 my $comment_depth = 0;
744 19         50 my @tokens = ('');
745 19         44 Parse: for ($statement) {
746             # Observe the default action at the end
747 160 100 100     1589 if (m{\G \z}xmsgc) { last Parse }
  19 100 100     40  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
748 7         10 elsif (m{\G( /\* .*? ) (?= /\* | \*/) }xmsgc) { $comment_depth++ }
749             elsif ($comment_depth && m{\G( .*? ) (?= /\* | \*/)}xmsgc) { }
750 3         5 elsif ($comment_depth && m{\G( \*/ )}xmsgc) { $comment_depth-- }
751             elsif (m{\G \?}xmsgc) {
752 16 100       38 pop @tokens if $tokens[-1] eq '';
753 16         41 push @tokens, \(my $tmp = $param_num++), '';
754 16         24 redo Parse;
755             }
756             elsif (m{\G( -- [^\n]* )}xmsgc) { }
757             elsif (m{\G( \' (?> [^\\\']* (?> \\. [^\\\']*)* ) \' )}xmsgc) { }
758             elsif (m{\G( \" [^\"]* \" )}xmsgc) { }
759             elsif (m{\G( \s+ | \w+ | ::? | \$[0-9]+ | [-/*\$]
760             | [^[:ascii:]]+ | [\0-\037\177]+)}xmsgc) { }
761             elsif (m{\G( [+<>=~!\@\#%^&|`,;.()\[\]{}]+ )}xmsgc) { }
762             elsif (m{\G( [\'\"\\] )}xmsgc) { } # unmatched: a bug in your query
763             else {
764 0         0 my $pos = pos;
765 0         0 die "BUG: can't parse statement at $pos\n$statement\n";
766             }
767              
768 125         304 $tokens[-1] .= $1;
769 125         143 redo Parse;
770             }
771              
772 19 100 100     75 pop @tokens if @tokens > 1 && $tokens[-1] eq '';
773              
774 19         74 return \@tokens;
775             }
776              
777              
778             package DBD::PgPP::ProtocolStatement;
779              
780             sub new {
781 0     0     my ($class, $pgsql, $statement) = @_;
782 0           bless {
783             postgres => $pgsql,
784             statement => $statement,
785             rows => [],
786             }, $class;
787             }
788              
789             sub execute {
790 0     0     my ($self) = @_;
791              
792 0           my $pgsql = $self->{postgres};
793 0           my $handle = $pgsql->get_handle;
794              
795 0           my $query_packet = "Q$self->{statement}\0";
796 0 0         print " ==> Query\n" if $DEBUG;
797 0           DBD::PgPP::Protocol::_dump_packet($query_packet);
798 0           $handle->send($query_packet, 0);
799 0           $self->{affected_rows} = 0;
800 0           $self->{last_oid} = undef;
801 0           $self->{rows} = [];
802              
803 0           my $stream = $pgsql->get_stream;
804 0           my $packet = $stream->each;
805 0 0         if ($packet->is_error) {
    0          
    0          
806 0           $self->_to_end_of_response($stream);
807 0           die $packet->get_message;
808             }
809             elsif ($packet->is_end_of_response) {
810 0           return;
811             }
812             elsif ($packet->is_empty) {
813 0           $self->_to_end_of_response($stream);
814 0           return;
815             }
816 0           while ($packet->is_notice_response) {
817             # XXX: discard it for now
818 0           $packet = $stream->each;
819             }
820 0 0         if ($packet->is_cursor_response) {
821 0           $packet->compute($pgsql);
822 0           my $row_info = $stream->each; # fetch RowDescription
823 0 0         if ($row_info->is_error) {
824 0           $self->_to_end_of_response($stream);
825 0           Carp::croak($row_info->get_message);
826             }
827 0           $row_info->compute($self);
828 0           while (1) {
829 0           my $row_packet = $stream->each;
830 0 0         if ($row_packet->is_error) {
831 0           $self->_to_end_of_response($stream);
832 0           Carp::croak($row_packet->get_message);
833             }
834 0           $row_packet->compute($self);
835 0           push @{ $self->{rows} }, $row_packet->get_result;
  0            
836 0 0         last if $row_packet->is_end_of_response;
837             }
838 0           return;
839             }
840             else { # CompletedResponse
841 0           $packet->compute($self);
842 0           while (1) {
843 0           my $end = $stream->each;
844 0 0         if ($end->is_error) {
845 0           $self->_to_end_of_response($stream);
846 0           Carp::croak($end->get_message);
847             }
848 0 0         last if $end->is_end_of_response;
849             }
850 0           return;
851             }
852             }
853              
854             sub _to_end_of_response {
855 0     0     my ($self, $stream) = @_;
856              
857 0           while (1) {
858 0           my $packet = $stream->each;
859 0           $packet->compute($self);
860 0 0         last if $packet->is_end_of_response;
861             }
862             }
863              
864             sub fetch {
865 0     0     my ($self) = @_;
866 0           return shift @{ $self->{rows} }; # shift returns undef if empty
  0            
867             }
868              
869              
870             package DBD::PgPP::PacketStream;
871              
872             # Message Identifiers
873 3     3   27 use constant ASCII_ROW => 'D';
  3         6  
  3         169  
874 3     3   21 use constant AUTHENTICATION => 'R';
  3         5  
  3         132  
875 3     3   14 use constant BACKEND_KEY_DATA => 'K';
  3         5  
  3         117  
876 3     3   15 use constant BINARY_ROW => 'B';
  3         5  
  3         129  
877 3     3   13 use constant COMPLETED_RESPONSE => 'C';
  3         33  
  3         114  
878 3     3   13 use constant COPY_IN_RESPONSE => 'G';
  3         5  
  3         122  
879 3     3   17 use constant COPY_OUT_RESPONSE => 'H';
  3         6  
  3         149  
880 3     3   14 use constant CURSOR_RESPONSE => 'P';
  3         5  
  3         122  
881 3     3   22 use constant EMPTY_QUERY_RESPONSE => 'I';
  3         4  
  3         135  
882 3     3   14 use constant ERROR_RESPONSE => 'E';
  3         5  
  3         148  
883 3     3   13 use constant FUNCTION_RESPONSE => 'V';
  3         5  
  3         135  
884 3     3   14 use constant NOTICE_RESPONSE => 'N';
  3         9  
  3         133  
885 3     3   16 use constant NOTIFICATION_RESPONSE => 'A';
  3         3  
  3         217  
886 3     3   5189 use constant READY_FOR_QUERY => 'Z';
  3         10  
  3         223  
887 3     3   15 use constant ROW_DESCRIPTION => 'T';
  3         7  
  3         128  
888              
889             # Authentication Message specifiers
890 3     3   13 use constant AUTHENTICATION_OK => 0;
  3         27  
  3         130  
891 3     3   14 use constant AUTHENTICATION_KERBEROS_V4 => 1;
  3         5  
  3         142  
892 3     3   13 use constant AUTHENTICATION_KERBEROS_V5 => 2;
  3         5  
  3         135  
893 3     3   14 use constant AUTHENTICATION_CLEARTEXT_PASSWORD => 3;
  3         5  
  3         177  
894 3     3   14 use constant AUTHENTICATION_CRYPT_PASSWORD => 4;
  3         17  
  3         112  
895 3     3   14 use constant AUTHENTICATION_MD5_PASSWORD => 5;
  3         4  
  3         116  
896 3     3   13 use constant AUTHENTICATION_SCM_CREDENTIAL => 6;
  3         4  
  3         9535  
897              
898             sub new {
899 0     0     my ($class, $handle) = @_;
900 0           bless {
901             handle => $handle,
902             buffer => '',
903             }, $class;
904             }
905              
906             sub set_buffer {
907 0     0     my ($self, $buffer) = @_;
908 0           $self->{buffer} = $buffer;
909             }
910              
911 0     0     sub get_buffer { $_[0]{buffer} }
912              
913             sub each {
914 0     0     my ($self) = @_;
915 0           my $type = $self->_get_byte;
916             # XXX: This would perhaps be better as a dispatch table
917 0 0         my $p = $type eq ASCII_ROW ? $self->_each_ascii_row
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
918             : $type eq AUTHENTICATION ? $self->_each_authentication
919             : $type eq BACKEND_KEY_DATA ? $self->_each_backend_key_data
920             : $type eq BINARY_ROW ? $self->_each_binary_row
921             : $type eq COMPLETED_RESPONSE ? $self->_each_completed_response
922             : $type eq COPY_IN_RESPONSE ? $self->_each_copy_in_response
923             : $type eq COPY_OUT_RESPONSE ? $self->_each_copy_out_response
924             : $type eq CURSOR_RESPONSE ? $self->_each_cursor_response
925             : $type eq EMPTY_QUERY_RESPONSE ? $self->_each_empty_query_response
926             : $type eq ERROR_RESPONSE ? $self->_each_error_response
927             : $type eq FUNCTION_RESPONSE ? $self->_each_function_response
928             : $type eq NOTICE_RESPONSE ? $self->_each_notice_response
929             : $type eq NOTIFICATION_RESPONSE ? $self->_each_notification_response
930             : $type eq READY_FOR_QUERY ? $self->_each_ready_for_query
931             : $type eq ROW_DESCRIPTION ? $self->_each_row_description
932             : Carp::croak("Unknown message type: '$type'");
933 0 0         if ($DEBUG) {
934 0           (my $type = ref $p) =~ s/.*:://;
935 0           print "<== $type\n";
936             }
937 0           return $p;
938             }
939              
940             sub _each_authentication {
941 0     0     my ($self) = @_;
942              
943 0           my $code = $self->_get_int32;
944 0 0         if ($code == AUTHENTICATION_OK) {
    0          
    0          
    0          
    0          
    0          
    0          
945 0           return DBD::PgPP::AuthenticationOk->new;
946             }
947             elsif ($code == AUTHENTICATION_KERBEROS_V4) {
948 0           return DBD::PgPP::AuthenticationKerberosV4->new;
949             }
950             elsif ($code == AUTHENTICATION_KERBEROS_V5) {
951 0           return DBD::PgPP::AuthenticationKerberosV5->new;
952             }
953             elsif ($code == AUTHENTICATION_CLEARTEXT_PASSWORD) {
954 0           return DBD::PgPP::AuthenticationCleartextPassword->new;
955             }
956             elsif ($code == AUTHENTICATION_CRYPT_PASSWORD) {
957 0           my $salt = $self->_get_byte(2);
958 0           return DBD::PgPP::AuthenticationCryptPassword->new($salt);
959             }
960             elsif ($code == AUTHENTICATION_MD5_PASSWORD) {
961 0           my $salt = $self->_get_byte(4);
962 0           return DBD::PgPP::AuthenticationMD5Password->new($salt);
963             }
964             elsif ($code == AUTHENTICATION_SCM_CREDENTIAL) {
965 0           return DBD::PgPP::AuthenticationSCMCredential->new;
966             }
967             else {
968 0           Carp::croak("Unknown authentication type: $code");
969             }
970             }
971              
972             sub _each_backend_key_data {
973 0     0     my ($self) = @_;
974 0           my $process_id = $self->_get_int32;
975 0           my $secret_key = $self->_get_int32;
976 0           return DBD::PgPP::BackendKeyData->new($process_id, $secret_key);
977             }
978              
979             sub _each_error_response {
980 0     0     my ($self) = @_;
981 0           my $error_message = $self->_get_c_string;
982 0           return DBD::PgPP::ErrorResponse->new($error_message);
983             }
984              
985             sub _each_notice_response {
986 0     0     my ($self) = @_;
987 0           my $notice_message = $self->_get_c_string;
988 0           return DBD::PgPP::NoticeResponse->new($notice_message);
989             }
990              
991             sub _each_notification_response {
992 0     0     my ($self) = @_;
993 0           my $process_id = $self->_get_int32;
994 0           my $condition = $self->_get_c_string;
995 0           return DBD::PgPP::NotificationResponse->new($process_id, $condition);
996             }
997              
998             sub _each_ready_for_query {
999 0     0     my ($self) = @_;
1000 0           return DBD::PgPP::ReadyForQuery->new;
1001             }
1002              
1003             sub _each_cursor_response {
1004 0     0     my ($self) = @_;
1005 0           my $name = $self->_get_c_string;
1006 0           return DBD::PgPP::CursorResponse->new($name);
1007             }
1008              
1009             sub _each_row_description {
1010 0     0     my ($self) = @_;
1011 0           my $row_number = $self->_get_int16;
1012 0           my @description;
1013 0           for my $i (1 .. $row_number) {
1014 0           push @description, {
1015             name => $self->_get_c_string,
1016             type => $self->_get_int32,
1017             size => $self->_get_int16,
1018             modifier => $self->_get_int32,
1019             };
1020             }
1021 0           return DBD::PgPP::RowDescription->new(\@description);
1022             }
1023              
1024             sub _each_ascii_row {
1025 0     0     my ($self) = @_;
1026 0           return DBD::PgPP::AsciiRow->new($self);
1027             }
1028              
1029             sub _each_completed_response {
1030 0     0     my ($self) = @_;
1031 0           my $tag = $self->_get_c_string;
1032 0           return DBD::PgPP::CompletedResponse->new($tag);
1033             }
1034              
1035             sub _each_empty_query_response {
1036 0     0     my ($self) = @_;
1037 0           my $unused = $self->_get_c_string;
1038 0           return DBD::PgPP::EmptyQueryResponse->new($unused);
1039             }
1040              
1041             sub _get_byte {
1042 0     0     my ($self, $length) = @_;
1043 0 0         $length = 1 if !defined $length;
1044              
1045 0           $self->_if_short_then_add_buffer($length);
1046 0           return substr $self->{buffer}, 0, $length, '';
1047             }
1048              
1049             sub _get_int32 {
1050 0     0     my ($self) = @_;
1051 0           $self->_if_short_then_add_buffer(4);
1052 0           return unpack 'N', substr $self->{buffer}, 0, 4, '';
1053             }
1054              
1055             sub _get_int16 {
1056 0     0     my ($self) = @_;
1057 0           $self->_if_short_then_add_buffer(2);
1058 0           return unpack 'n', substr $self->{buffer}, 0, 2, '';
1059             }
1060              
1061             sub _get_c_string {
1062 0     0     my ($self) = @_;
1063              
1064 0           my $null_pos;
1065 0           while (1) {
1066 0           $null_pos = index $self->{buffer}, "\0";
1067 0 0         last if $null_pos >= 0;
1068 0           $self->_if_short_then_add_buffer(1 + length $self->{buffer});
1069             }
1070 0           my $result = substr $self->{buffer}, 0, $null_pos, '';
1071 0           substr $self->{buffer}, 0, 1, ''; # remove trailing \0
1072 0           return $result;
1073             }
1074              
1075             # This method means "I'm about to read *this* many bytes from the buffer, so
1076             # make sure there are enough bytes available". That is, on exit, you are
1077             # guaranteed that $length bytes are available.
1078             sub _if_short_then_add_buffer {
1079 0     0     my ($self, $length) = @_;
1080 0   0       $length ||= 0;
1081              
1082 0           my $handle = $self->{handle};
1083 0           while (length($self->{buffer}) < $length) {
1084 0           my $packet = '';
1085 0           $handle->recv($packet, $BUFFER_LEN, 0);
1086 0           DBD::PgPP::Protocol::_dump_packet($packet);
1087 0           $self->{buffer} .= $packet;
1088             }
1089             }
1090              
1091              
1092             package DBD::PgPP::Response;
1093              
1094             sub new {
1095 0     0     my ($class) = @_;
1096 0           bless {}, $class;
1097             }
1098              
1099 0     0     sub compute { return }
1100 0     0     sub is_empty { undef }
1101 0     0     sub is_error { undef }
1102 0     0     sub is_end_of_response { undef }
1103 0     0     sub get_result { undef }
1104 0     0     sub is_cursor_response { undef }
1105 0     0     sub is_notice_response { undef }
1106              
1107              
1108             package DBD::PgPP::AuthenticationOk;
1109 3     3   27 use base qw;
  3         6  
  3         2136  
1110              
1111              
1112             package DBD::PgPP::AuthenticationKerberosV4;
1113 3     3   18 use base qw;
  3         8  
  3         1556  
1114              
1115 0     0     sub compute { Carp::croak("authentication type 'Kerberos V4' not supported.\n") }
1116              
1117              
1118             package DBD::PgPP::AuthenticationKerberosV5;
1119 3     3   17 use base qw;
  3         6  
  3         1525  
1120              
1121 0     0     sub compute { Carp::croak("authentication type 'Kerberos V5' not supported.\n") }
1122              
1123              
1124             package DBD::PgPP::AuthenticationCleartextPassword;
1125 3     3   16 use base qw;
  3         5  
  3         1750  
1126              
1127             sub compute {
1128 0     0     my ($self, $pgsql) = @_;
1129 0           my $handle = $pgsql->get_handle;
1130 0           my $password = $pgsql->{password};
1131              
1132 0           my $packet = pack('N', length($password) + 4 + 1). $password. "\0";
1133 0 0         print " ==> PasswordPacket (cleartext)\n" if $DEBUG;
1134 0           DBD::PgPP::Protocol::_dump_packet($packet);
1135 0           $handle->send($packet, 0);
1136             }
1137              
1138              
1139             package DBD::PgPP::AuthenticationCryptPassword;
1140 3     3   17 use base qw;
  3         6  
  3         2513  
1141              
1142             sub new {
1143 0     0     my ($class, $salt) = @_;
1144 0           my $self = $class->SUPER::new;
1145 0           $self->{salt} = $salt;
1146 0           $self;
1147             }
1148              
1149 0     0     sub get_salt { $_[0]{salt} }
1150              
1151             sub compute {
1152 0     0     my ($self, $pgsql) = @_;
1153 0           my $handle = $pgsql->get_handle;
1154 0   0       my $password = $pgsql->{password} || '';
1155              
1156 0           $password = _encode_crypt($password, $self->{salt});
1157 0           my $packet = pack('N', length($password) + 4 + 1). $password. "\0";
1158 0 0         print " ==> PasswordPacket (crypt)\n" if $DEBUG;
1159 0           DBD::PgPP::Protocol::_dump_packet($packet);
1160 0           $handle->send($packet, 0);
1161             }
1162              
1163             sub _encode_crypt {
1164 0     0     my ($password, $salt) = @_;
1165              
1166 0           my $crypted = '';
1167 0           eval {
1168 0           $crypted = crypt($password, $salt);
1169 0 0         die "is MD5 crypt()" if _is_md5_crypt($crypted, $salt);
1170             };
1171 0 0         Carp::croak("authentication type 'crypt' not supported on your platform. please use 'trust' or 'md5' or 'ident' authentication")
1172             if $@;
1173 0           return $crypted;
1174             }
1175              
1176             sub _is_md5_crypt {
1177 0     0     my ($crypted, $salt) = @_;
1178 0           $crypted =~ /^\$1\$\Q$salt\E\$/;
1179             }
1180              
1181              
1182             package DBD::PgPP::AuthenticationMD5Password;
1183 3     3   19 use base qw;
  3         5  
  3         2390  
1184              
1185             sub new {
1186 0     0     my ($class, $salt) = @_;
1187 0           my $self = $class->SUPER::new;
1188 0           $self->{salt} = $salt;
1189 0           return $self;
1190             }
1191              
1192             sub compute {
1193 0     0     my ($self, $pgsql) = @_;
1194 0           my $handle = $pgsql->get_handle;
1195 0   0       my $password = $pgsql->{password} || '';
1196              
1197 0           my $md5ed_password = _encode_md5($pgsql->{user}, $password, $self->{salt});
1198 0           my $packet = pack('N', 1 + 4 + length $md5ed_password). "$md5ed_password\0";
1199 0 0         print " ==> PasswordPacket (md5)\n" if $DEBUG;
1200 0           DBD::PgPP::Protocol::_dump_packet($packet);
1201 0           $handle->send($packet, 0);
1202             }
1203              
1204             sub _encode_md5 {
1205 0     0     my ($user, $password, $salt) = @_;
1206              
1207 0           my $md5 = Digest::MD5->new;
1208 0           $md5->add($password);
1209 0           $md5->add($user);
1210              
1211 0           my $tmp_digest = $md5->hexdigest;
1212 0           $md5->add($tmp_digest);
1213 0           $md5->add($salt);
1214              
1215 0           return 'md5' . $md5->hexdigest;
1216             }
1217              
1218              
1219             package DBD::PgPP::AuthenticationSCMCredential;
1220 3     3   21 use base qw;
  3         6  
  3         1746  
1221              
1222 0     0     sub compute { Carp::croak("authentication type 'SCM Credential' not supported.\n") }
1223              
1224              
1225             package DBD::PgPP::BackendKeyData;
1226 3     3   20 use base qw;
  3         4  
  3         6849  
1227              
1228             sub new {
1229 0     0     my ($class, $process_id, $secret_key) = @_;
1230 0           my $self = $class->SUPER::new;
1231 0           $self->{process_id} = $process_id;
1232 0           $self->{secret_key} = $secret_key;
1233 0           return $self;
1234             }
1235              
1236 0     0     sub get_process_id { $_[0]{process_id} }
1237 0     0     sub get_secret_key { $_[0]{secret_key} }
1238              
1239             sub compute {
1240 0     0     my ($self, $postgres) = @_;;
1241              
1242 0           $postgres->{process_id} = $self->get_process_id;
1243 0           $postgres->{secret_key} = $self->get_secret_key;
1244             }
1245              
1246              
1247             package DBD::PgPP::ErrorResponse;
1248 3     3   30 use base qw;
  3         5  
  3         1713  
1249              
1250             sub new {
1251 0     0     my ($class, $message) = @_;
1252 0           my $self = $class->SUPER::new;
1253 0           $self->{message} = $message;
1254 0           return $self;
1255             }
1256              
1257 0     0     sub get_message { $_[0]{message} }
1258 0     0     sub is_error { 1 }
1259              
1260              
1261             package DBD::PgPP::NoticeResponse;
1262 3     3   18 use base qw;
  3         11  
  3         1568  
1263              
1264 0     0     sub is_error { undef }
1265 0     0     sub is_notice_response { 1 }
1266              
1267              
1268             package DBD::PgPP::NotificationResponse;
1269 3     3   17 use base qw;
  3         9  
  3         2405  
1270              
1271             sub new {
1272 0     0     my ($class, $process_id, $condition) = @_;
1273 0           my $self = $class->SUPER::new;
1274 0           $self->{process_id} = $process_id;
1275 0           $self->{condition} = $condition;
1276 0           return $self;
1277             }
1278              
1279 0     0     sub get_process_id { $_[0]{process_id} }
1280 0     0     sub get_condition { $_[0]{condition} }
1281              
1282              
1283             package DBD::PgPP::ReadyForQuery;
1284 3     3   18 use base qw;
  3         7  
  3         1507  
1285              
1286 0     0     sub is_end_of_response { 1 }
1287              
1288              
1289             package DBD::PgPP::CursorResponse;
1290 3     3   19 use base qw;
  3         15  
  3         1832  
1291              
1292             sub new {
1293 0     0     my ($class, $name) = @_;
1294 0           my $self = $class->SUPER::new;
1295 0           $self->{name} = $name;
1296 0           return $self;
1297             }
1298              
1299 0     0     sub get_name { $_[0]{name} }
1300 0     0     sub is_cursor_response { 1 }
1301              
1302             sub compute {
1303 0     0     my ($self, $pgsql) = @_;
1304 0           $pgsql->{cursor_name} = $self->get_name;
1305             }
1306              
1307              
1308             package DBD::PgPP::RowDescription;
1309 3     3   30 use base qw;
  3         5  
  3         1871  
1310              
1311             sub new {
1312 0     0     my ($class, $row_description) = @_;
1313 0           my $self = $class->SUPER::new;
1314 0           $self->{row_description} = $row_description;
1315 0           return $self;
1316             }
1317              
1318             sub compute {
1319 0     0     my ($self, $pgsql_sth) = @_;
1320 0           $pgsql_sth->{row_description} = $self->{row_description};
1321             }
1322              
1323              
1324             package DBD::PgPP::AsciiRow;
1325 3     3   18 use base qw;
  3         12  
  3         2497  
1326              
1327             sub new {
1328 0     0     my ($class, $stream) = @_;
1329 0           my $self = $class->SUPER::new;
1330 0           $self->{stream} = $stream;
1331 0           return $self;
1332             }
1333              
1334             sub compute {
1335 0     0     my ($self, $pgsql_sth) = @_;
1336              
1337 0           my $stream = $self->{stream};
1338 0           my $fields_length = @{ $pgsql_sth->{row_description} };
  0            
1339 0           my $bitmap_length = $self->_get_length_of_null_bitmap($fields_length);
1340 0           my $non_null = unpack 'B*', $stream->_get_byte($bitmap_length);
1341              
1342 0           my @result;
1343 0           for my $i (0 .. $fields_length - 1) {
1344 0           my $value;
1345 0 0         if (substr $non_null, $i, 1) {
1346 0           my $length = $stream->_get_int32;
1347 0           $value = $stream->_get_byte($length - 4);
1348 0           my $type_oid = $pgsql_sth->{row_description}[$i]{type};
1349 0 0         if ($type_oid == 16) { # bool
    0          
1350 0 0         $value = ($value eq 'f') ? 0 : 1;
1351             }
1352             elsif ($type_oid == 17) { # bytea
1353 0           $value =~ s{\\(\\|[0-7]{3})}{$BYTEA_DEMANGLE{$1}}g;
1354             }
1355             }
1356 0           push @result, $value;
1357             }
1358              
1359 0           $self->{result} = \@result;
1360             }
1361              
1362             sub _get_length_of_null_bitmap {
1363 0     0     my ($self, $number) = @_;
1364 3     3   3359 use integer;
  3         36  
  3         18  
1365 0           my $length = $number / 8;
1366 0 0         ++$length if $number % 8;
1367 0           return $length;
1368             }
1369              
1370 0     0     sub get_result { $_[0]{result} }
1371 0     0     sub is_cursor_response { 1 }
1372              
1373              
1374             package DBD::PgPP::CompletedResponse;
1375 3     3   303 use base qw;
  3         6  
  3         10723  
1376              
1377             sub new {
1378 0     0     my ($class, $tag) = @_;
1379 0           my $self = $class->SUPER::new;
1380 0           $self->{tag} = $tag;
1381 0           return $self;
1382             }
1383              
1384 0     0     sub get_tag { $_[0]{tag} }
1385              
1386             sub compute {
1387 0     0     my ($self, $pgsql_sth) = @_;
1388 0           my $tag = $self->{tag};
1389              
1390 0 0         if ($tag =~ /^INSERT (\d+) (\d+)/) {
    0          
    0          
1391 0           $pgsql_sth->{affected_oid} = $1;
1392 0           $pgsql_sth->{affected_rows} = $2;
1393             }
1394             elsif ($tag =~ /^DELETE (\d+)/) {
1395 0           $pgsql_sth->{affected_rows} = $1;
1396             }
1397             elsif ($tag =~ /^UPDATE (\d+)/) {
1398 0           $pgsql_sth->{affected_rows} = $1;
1399             }
1400             }
1401              
1402              
1403             package DBD::PgPP::EmptyQueryResponse;
1404 3     3   28 use base qw;
  3         7  
  3         1567  
1405              
1406 0     0     sub is_empty { 1 }
1407              
1408              
1409             1;
1410             __END__