File Coverage

blib/lib/PgSQL.pm
Criterion Covered Total %
statement 33 263 12.5
branch 2 92 2.1
condition 4 43 9.3
subroutine 10 57 17.5
pod 1 50 2.0
total 50 505 9.9


line stmt bran cond sub pod time code
1             package PgSQL;
2              
3             # This perl module is Copyright (c) 1998 Göran Thyni, Sweden.
4             # All rights reserved.
5             # You may distribute under the terms of either
6             # the GNU General Public License version 2 (or later)
7             # or the Artistic License, as specified in the Perl README file.
8             #
9             # $Id: PgSQL.pm,v 1.5 1998/08/15 15:08:39 goran Exp $
10              
11 1     1   777 use strict;
  1         2  
  1         36  
12              
13 1     1   504 use PgSQL::Cursor;
  1         3  
  1         43  
14              
15 1     1   1719 use IO::Socket::UNIX;
  1         116319  
  1         9  
16 1     1   3890 use IO::Select;
  1         3596  
  1         72  
17              
18 1     1   11 use vars qw (@ISA $VERSION);
  1         2  
  1         3857  
19              
20             $VERSION = '0.51';
21              
22             @ISA = ('IO::Socket::UNIX');
23              
24 0     0 0 0 sub PG_PROTOCOL_LATEST { 0x00020000; }
25              
26             my $EnvironmentOptions =
27             {
28             "PGDATESTYLE" => "datestyle",
29             "PGTZ" => "timezone",
30             "PGCLIENTENCODING" => "client_encoding",
31             "PGCOSTHEAP" => "cost_heap",
32             "PGCOSTINDEX" => "cost_index",
33             "PGRPLANS" => "r_plans",
34             "PGGEQO" => "geqo",
35             };
36              
37 0     0 0 0 sub STATUS_OK {1}
38 0     0 0 0 sub STATUS_ERROR {0}
39              
40 0     0 0 0 sub PGRES_EMPTY_QUERY {0}
41 0     0 0 0 sub PGRES_COMMAND_OK {1}
42 0     0 0 0 sub PGRES_TUPLES_OK {2}
43 0     0 0 0 sub PGRES_COPY_OUT {'G'}
44 0     0 0 0 sub PGRES_COPY_IN {'H'}
45 0     0 0 0 sub PGRES_BAD_RESPONSE {5}
46 0     0 0 0 sub PGRES_NONFATAL_ERROR {6}
47 0     0 0 0 sub PGRES_FATAL_ERROR {7}
48              
49 2     2 0 18 sub DEF_PGPORT { 5432; }
50 0     0 0 0 sub DEF_PGTTY { ''; }
51 0     0 0 0 sub DEF_PGOPTIONS { ''; }
52              
53 0     0 0 0 sub AUTH_REQ_OK {0}
54 0     0 0 0 sub AUTH_REQ_PASSWORD {3}
55 0     0 0 0 sub AUTH_REQ_CRYPT {4}
56              
57             sub UNIXSOCK_PATH
58             {
59 1     1 0 3 my $href = shift;
60 1         19 '/tmp/.s.PGSQL.' . $href->{Port};
61             }
62              
63              
64             sub new
65             {
66 1     1 1 20700 my ($class, %args) = @_;
67 1   33     623 my $host = $args{Host} || $ENV{PGHOST};
68 1   33     254 my $port = $args{Port} || $ENV{PGPORT} || &DEF_PGPORT;
69 1   33     87 $args{Port} ||= DEF_PGPORT;
70 1 50       16 if ($host)
71             {
72 0         0 $args{PeerAddr} = "${host}:${port}";
73 0         0 @ISA = qw (IO::Socket::INET);
74             }
75             else
76             {
77 1   33     17 $args{Peer} ||= UNIXSOCK_PATH(\%args);
78             }
79 1         392 my $self = $class->SUPER::new(%args);
80 1 50       1754 if ($self)
81             {
82 0   0     0 ${*$self}{DBNAME} = $args{DBName} || $ENV{PGDATABASE} || '';
  0         0  
83 0         0 ${*$self}{PGHOST} = $host;
  0         0  
84 0         0 ${*$self}{PGPORT} = $port;
  0         0  
85 0   0     0 ${*$self}{PGPORT} = $args{Tty} || $ENV{PGTTY} || &DEF_PGTTY;
  0         0  
86 0   0     0 ${*$self}{PGOPTIONS} =
  0         0  
87             $args{Options} || $ENV{PGOPTIONS} || &DEF_PGOPTIONS;
88 0   0     0 ${*$self}{PGUSER} = $args{User} || $ENV{PGUSER} || ${*$self}{DBNAME};
  0         0  
89 0   0     0 ${*$self}{PGPASS} = $args{Password} || $ENV{PGPASS} || '';
  0         0  
90 0         0 $self->blocking(0);
91 0         0 eval { $self->handshake; };
  0         0  
92 0 0       0 if ($@)
93             {
94 0         0 $self->close;
95 0         0 warn $@;
96 0         0 return undef;
97             }
98 0         0 $self->setenv();
99             }
100 1         10 $self;
101             }
102              
103              
104             sub handshake
105             {
106 0     0 0   my $self = shift;
107 0 0         unless ($self->packetSend($self->startup_packet))
108             {
109 0           die "handshake: couldn't send startup packet:errno=$!\n";
110             }
111 0           while (1)
112             {
113 0 0         last if $self->wait(1,0);
114 0           my $resp = $self->getc;
115 0 0         die $self->getline() if ($resp eq 'E');
116 0 0         die "connectDB() -- expected authentication request\n" if $resp ne 'R';
117 0           my $areq = $self->getint(4);
118 0           my $salt = 0;
119 0 0         if ($areq == AUTH_REQ_CRYPT)
120             {
121 0 0         next if $self->pqGetnchar($salt, 4);
122             # FIXME, this does not work, I think!
123             }
124 0           $self->sendauth($areq);
125 0           $self->flush;
126 0 0         last if ($areq == AUTH_REQ_OK);
127             }
128             }
129              
130             sub setenv
131             {
132 0     0 0   my $self = shift;
133 0           for my $eo (keys %$EnvironmentOptions)
134             {
135 0           my $val = $ENV{$eo};
136 0 0         if ($val)
137             {
138 0           my $setQuery = sprintf("SET %s = '%.60s'", $eo->pgName, $val);
139 0 0         $setQuery = sprintf("SET %s = default", $eo->pgName)
140             if $val eq "default";
141 0           my $res = $self->do($setQuery);
142             }
143             }
144             }
145              
146              
147             sub close
148             {
149 0     0 0   my $self = shift;
150 0 0         if ($self->opened)
151             {
152 0           local $SIG{PIPE} = 'IGNORE';
153 0           $self->print('X');
154 0           $self->flush;
155 0           $self->flush_input;
156 0           $self->SUPER::close;
157             }
158             }
159              
160             sub packetSend
161             {
162 0     0 0   my ($self,$buf,$len) = @_;
163 0 0         return STATUS_ERROR unless $self->putint(4 + $len, 4); # size
164 0 0         return STATUS_ERROR unless $self->write($buf, $len);
165 0 0         return STATUS_ERROR unless $self->flush;
166 0           STATUS_OK;
167             }
168              
169             sub startup_packet
170             {
171 0     0 0   my $self = shift;
172 0           my $len = 4 + 64 + 32 + 64 + 64 + 64;
173 0           my $packet = pack('Na64a32a64a64a64',
174             $self->PG_PROTOCOL_LATEST,
175 0           ${*$self}{DBNAME},
176 0           ${*$self}{PGUSER},
177 0           ${*$self}{PGOPTIONS},
178             '',
179 0           ${*$self}{PGTTY});
180 0           ($packet,$len);
181             }
182              
183             sub getint
184             {
185 0     0 0   my ($self, $sz) = @_;
186 0           my $value;
187 0           $self->read($value,$sz);
188 0 0         my @a = unpack($sz == 4 ? 'N' : 'n', $value);
189 0           return $a[0];
190             }
191              
192             sub putint
193             {
194 0     0 0   my ($self, $value, $sz) = @_;
195 0 0         $self->write(pack($sz == 4 ? 'N' : 'n', $value), $sz);
196             }
197              
198             sub wait
199             {
200 0     0 0   my ($self,$forRead,$forWrite) = @_;
201             # loop in case select returns EINTR
202             # for (;;)
203             {
204 0           my $sel = IO::Select->new;
  0            
205 0 0 0       $sel->add($self) if ($forRead || $forWrite);
206 0 0 0       last if (($forRead && $sel->can_read) || ($forWrite && $sel->can_write));
      0        
      0        
207             # next if ($errno == EINTR);
208             # return EOF;
209             }
210 0           return 0;
211             }
212              
213              
214             sub sendauth
215             {
216 0     0 0   my ($self, $areq, $hostname, $password, $errmsg) = @_;
217 0 0         return STATUS_OK if $areq == AUTH_REQ_OK;
218 0 0         die "sendauth: no password supplied\n" if (!$password);
219             # if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
220             {
221 0           die "fe_sendauth: error sending password authentication\n";
  0            
222             }
223 0           return STATUS_OK;
224             }
225              
226              
227             sub do
228             {
229 0     0 0   my ($self, $query) = @_;
230 0           my $is_select = $query =~ /^\s*select\s/i;
231 0           my $sth = $self->prepare($query);
232 0           my $stat = $sth->exec;
233 0 0         undef $sth unless $stat;
234 0 0         return $sth if $is_select;
235 0           return $stat;
236             }
237              
238             sub prepare
239             {
240 0     0 0   my ($self, $query) = @_;
241 0           ${*$self}{CURSOR} = PgSQL::Cursor->new($self, undef, $query);
  0            
242 0           ${*$self}{CURSOR};
  0            
243             }
244              
245             sub sendQuery
246             {
247 0     0 0   my ($self, $query) = @_;
248 0           $self->flush_input;
249 0 0         return 0 unless $self->print('Q' . $query . "\0");
250 0           $self->flush;
251 0           return 1;
252             }
253              
254             sub flush_input
255             {
256 0     0 0   my $self = shift;
257 0 0         while (1) { last unless $self->parseInput; }
  0            
258             }
259              
260              
261             # parseInput: if appropriate, parse input data from backend
262             # until input is exhausted or a stopping state is reached.
263             # Note that this function will NOT attempt to read more data from the backend.
264              
265             sub parseInput
266             {
267 0     0 0   my ($self) = @_;
268             # Loop to parse successive complete messages available in the buffer.
269             # OK to try to read a message type code.
270 0           my $id = $self->getc;
271 0 0         return unless defined $id;
272 0 0         return if $id eq "\0";
273             # NOTIFY and NOTICE messages can happen in any state besides COPY OUT;
274             # always process them right away.
275 0 0         return ($id, $self->getNotify) if $id eq 'A';
276 0 0         return ($id, $self->getNotice) if $id eq 'N';
277 0 0         return ($id, $self->getCompleted) if $id eq 'C';
278 0 0         return ($id, $self->getError) if $id eq 'E';
279 0 0         return ($id) if $id eq 'Z';
280 0 0         return ($id, $self->getc) if $id eq 'I';
281 0 0         return ($id, $self->getint(4), $self->getint(4)) if $id eq 'K';
282 0 0         return ($id, $self->getCursor) if $id eq 'P'; # cursor
283 0 0         return ($id, $self->getRowDescr) if $id eq 'T';
284 0 0         return ($id, $self->getTuple(0)) if $id eq 'D';
285 0 0         return ($id, $self->getTuple(1)) if $id eq 'B';
286 0 0         return ($id, $self->getCopyIn) if $id eq 'G';
287 0 0         return ($id, $self->getCopyOut) if $id eq 'H';
288 0           die "unknown protocol character '$id' (" . $id .
289             ") read from backend. " .
290             "(The protocol character is the first character the " .
291             "backend sends in response to a query it receives).\n";
292             }
293              
294             sub getNotify
295             {
296 0     0 0   my $self = shift;
297 0           my $i = $self->getint(4);
298 0           my $note = $self->gets;
299 0           $self->trace("NOTIFY: $i / $note");
300 0           $note;
301             }
302              
303             sub getNotice
304             {
305 0     0 0   my $self = shift;
306 0           my $note = $self->gets;
307 0           $self->trace("$note");
308 0           $note;
309             }
310              
311             sub getCompleted
312             {
313             # local $/ = 0;
314 0     0 0   my $self = shift;
315 0           my $note = $self->xgets;
316 0           $self->trace("COMPELETED: $note");
317 0           $note;
318             }
319              
320             sub getError
321             {
322 0     0 0   my $self = shift;
323 0           my $note = $self->gets;
324 0           die $note;
325             }
326              
327             sub getCursor
328             {
329 0     0 0   my $self = shift;
330 0           my $note = $self->xgets;
331 0           $self->trace("CURSOR: $note");
332             # ${*$self}{CURSOR} = PgSQL::Cursor->new($self,$note,undef);
333             # ${*$self}{CURSOR};
334 0           $note;
335             }
336              
337             sub getCopyIn
338             {
339 0     0 0   my $self = shift;
340 0           $self->trace("COPY_IN");
341             }
342              
343             sub getCoyout
344             {
345 0     0 0   my $self = shift;
346 0           $self->trace("COPY_OUT");
347             }
348            
349             sub getRowDescr
350             {
351 0     0 0   my $self = shift;
352 0           my ($i,@s,@oids,@typs,@sz);
353 0           my $nfields = $self->getint(2);
354 0           $self->trace("ROWDESCR: $nfields fields");
355 0           for ($i = 0; $i < $nfields; $i++)
356             {
357 0           my $s = $self->xgets;
358 0           my $typ = $self->getint(4);
359 0           my $sz = $self->getint(2);
360 0           my $oid = $self->getint(4);
361 0           push @s, $s;
362 0           push @oids, $oid;
363 0           push @typs, $typ;
364 0           push @sz, unpack("s",pack("s", $sz));
365 0           $self->trace(sprintf("\t%s SIZE:%d TYPE:%d MODIFER:%d",
366             $s, $sz, $typ, $oid));
367             }
368 0   0       my $sth = ${*$self}{CURSOR} || PgSQL::Cursor->new($self,undef,undef);
369 0           $sth->{NAME} = \@s;
370 0           $sth->{TYPE} = \@typs;
371 0           $sth->{SIZE} = \@sz;
372 0           $sth->nfields($nfields);
373 0           $nfields;
374             }
375              
376             sub getTuple
377             {
378 0     0 0   my ($self, $binary) = @_;
379 0           my ($nullbits, $i, $mapbytes);
380 0           my $nfields = ${*$self}{CURSOR}->nfields;
  0            
381             {
382 1     1   1353 use integer;
  1         13  
  1         6  
  0            
383 0           $mapbytes = $nfields / 8;
384 0 0         $mapbytes++ if $nfields % 8;
385             }
386 0           $self->read($nullbits, $mapbytes);
387 0           $self->trace("ROW:");
388 0           my $a = [];
389 0           for ($i = 0; $i < $nfields; $i++)
390             {
391 1     1   81 use integer;
  1         2  
  1         4  
392 0           my ($sz,$value) = (0,'NULL');
393 0           my $bit = (ord(substr($nullbits, $i / 8, 1)) >> (7 - ($i % 8))) & 1;
394 0 0         if ($bit)
395             {
396 0           $sz = $self->getint(4) - 4;
397 0           $self->read($value, $sz);
398             }
399 0           $self->trace("\tTUPLE ($sz):\t$value");
400 0           push @$a, $value;
401             }
402 0           my $sth = ${*$self}{CURSOR};
  0            
403 0           $sth->add($a);
404             }
405              
406             sub xgets
407             {
408 0     0 0   my $self = shift;
409 0           my $s = '';
410 0           while (1)
411             {
412 0           $_ = $self->getc;
413 0 0 0       last if int($_) == 0 and $_ eq "\0";
414 0           $s .= $_;
415             }
416 0           return $s;
417             }
418              
419             sub begin
420             {
421 0     0 0   shift->do("BEGIN");
422             }
423              
424             sub commit
425             {
426 0     0 0   shift->do("COMMIT");
427             }
428              
429             sub rollback
430             {
431 0     0 0   shift->do("ROLLBACK");
432             }
433              
434             sub ping
435             {
436 0     0 0   my $self = shift;
437 0           $self->do(' ');
438             }
439              
440             sub errmsg
441             {
442 0     0 0   my $self = shift;
443 0           my $msg = shift;
444 0 0         if (defined $msg) { ${*$self}{ERRMSG} = $msg; }
  0            
  0            
445 0           ${*$self}{ERRMSG};
  0            
446             }
447              
448             sub trace
449             {
450 0     0 0   my ($self, @msgs) = @_;
451 0           my $lvl = ${*$self}{TRACE};
  0            
452 0 0         print STDERR @msgs,"\n" if $lvl;
453             }
454              
455             sub debug
456             {
457 0     0 0   my ($self, $lvl) = @_;
458 0           ${*$self}{TRACE} = $lvl;
  0            
459             }
460              
461             1;
462              
463