File Coverage

lib/DBD/PgPPSjis.pm
Criterion Covered Total %
statement 175 825 21.2
branch 1 288 0.3
condition 0 83 0.0
subroutine 59 180 32.7
pod 3 4 75.0
total 238 1380 17.2


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