File Coverage

blib/lib/Pg/Simple.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             # Copyright (C) 2000-2006 Smithsonian Astrophysical Observatory
2             # All rights reserved.
3             #
4             # This program is free software; you can redistribute it and/or
5             # modify it under the terms of the GNU General Public License
6             # as published by the Free Software Foundation; either version 2
7             # of the License, or (at your option) any later version.
8             #
9             # This program is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # GNU General Public License for more details.
13             #
14             # You should have received a copy of the GNU General Public License
15             # along with this program; if not, write to the Free Software
16             # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17              
18             =head1 NAME
19              
20             Pg::Simple - simple OO interface to PostgreSQL
21              
22             =head1 SYNOPSIS
23              
24             use Pg::Simple;
25              
26             my $db = new Pg::Simple;
27             $db->connect( { DB => 'spectral' } );
28              
29             my $db = new Pg::Simple( { DB => 'spectral' } );
30              
31             $db->execute( SQL command or query ) or die;
32              
33             while ( $db->fetch( \$field1, \$field2 ) )
34             {
35             print "$field1 $field2\n"
36             }
37              
38             # or
39              
40             while ( $hash = $db->fetchhash and keys %$hash )
41             {
42             print $hash->{some_key};
43             }
44              
45             $db->finish;
46              
47             =head1 DESCRIPTION
48              
49             B is a very simple interface to PostgreSQL. It is patterned after
50             the DBI interface. Why not use the DBI interface instead? The main
51             reason is that it does not yet support cursors. This module does. When
52             the DBI Postgres interface supports cursors, one should use that instead.
53              
54             This module is designed primarily to ease reading data from a database.
55             All statements are executed within a transaction. Normally the C
56             flag is set, meaning that after each execution of the B or B
57             method, the backend is sent a C directive. If C is
58             set, B will not perform a commit, as it would destroy the cursor.
59              
60             Usually one uses the B method for directives which do not return
61             any data. The B and B pair should be used when data
62             is to be returned. The main difference is that B will create
63             a cursor if C is set, while B won't. B is
64             required to close the cursor.
65              
66             =head1 Object action methods
67              
68             =over 4
69              
70             =cut
71              
72              
73             package Pg::Simple;
74 1     1   8842 use Pg;
  0            
  0            
75             use Carp;
76             use strict;
77             use vars qw( $VERSION );
78              
79             $VERSION = '1.20';
80              
81             =item new [ \%attr ]
82              
83             This method creates a new B object. It returns C upon error.
84             The optional hash of attributes may include any of the following:
85              
86             =over 4
87              
88             =item AutoCursor
89              
90             If set, the B method always creates a cursor before sending the
91             command to the backend. This defaults to on.
92              
93             =item AutoCommit
94              
95             If set, a C directive will be sent to the backend after each
96             B is performed. In order not to abort selections performed via
97             B, a C directive will I
98             be sent to the backend by B if C is set.
99             It will be sent by the B method.
100             If not set, use the B method to commit any changes. There is no
101             need to start a new transaction; that is done automatically.
102              
103             =item RaiseError
104              
105             If set, errors will result in an exception being thrown ( via
106             B), rather. If not set, errors will result in a message being
107             printed to C and an error value returned to the caller. It
108             defaults to on.
109              
110             =item Verbose
111              
112             If set, B will say a few things about what it is doing (to STDERR).
113              
114              
115             =item NFetch
116              
117             The number of tuples to fetch from the cursor at once. This defaults to 1000.
118              
119              
120             =item Name
121              
122             A symbolic name to assign to the connection. This is used to differentiate
123             output when C is set.
124              
125             =item Trace
126              
127             If set (to a stream glob, i.e. C<\*STDERR>), the underlying C interface
128             will send debugging information to that stream. Defaults to off.
129              
130             =item DB
131              
132             The name of the database to which to connect. If this is set, B
133             will attempt to make a connection. Alternatively, see the B method.
134              
135             =item Host
136              
137             The host to which to connect. This defaults to the value of the C
138             environmental variable, if that exists, else the undefined value.
139              
140             =item Port
141              
142             The port to which to connect. This defaults to the value of the C
143             environmental variable, if that exists, else C<5432>.
144              
145             =item User
146              
147             The database user id.
148              
149             =item Password
150              
151             The password to pass to the backend. Required only if the database
152             requires password authentication. If not specified, the value of the
153             C environmental variable is used.
154              
155             =back
156              
157              
158             =cut
159              
160             sub new
161             {
162             my $this = shift;
163             my $class = ref($this) || $this;
164            
165             my $self = {
166             attr => {
167             AutoCursor => 1,
168             AutoCommit => 1,
169             RaiseError => 1,
170             Verbose => 0,
171             NFetch => 1000,
172             Trace => undef,
173             Name => 'unknown'
174             },
175             db => {
176             DB => undef,
177             Host => $ENV{'PGHOST'} || undef,
178             Port => $ENV{'PGPORT'} || 5432,
179             User => $ENV{'USER'} || $ENV{'PGUSER'} || undef,
180             Password => $ENV{'PGPASSWORD'} || undef
181             },
182             cursor => undef,
183             transaction => undef,
184             conn => undef,
185             result => undef,
186             last_tuple => undef,
187             };
188            
189             bless $self, $class;
190            
191             while ( @_ )
192             {
193             my $arg = shift;
194              
195             # hash of attributes
196             if ( ref( $arg ) eq 'HASH' )
197             {
198             while ( my ( $key, $val ) = each ( %$arg ) )
199             {
200             if ( exists $self->{attr}->{$key} )
201             {
202             $self->{attr}->{$key} = $val;
203             }
204             elsif ( exists $self->{db}->{$key} )
205             {
206             $self->{db}->{$key} = $val;
207             }
208             else
209             {
210             $self->_error( "Pg::Simple::new unknown attribute: $key\n" );
211             return undef;
212             }
213             }
214             }
215             else
216             {
217             $self->_error( "unacceptable argument to Pg::Simple::new\n" );
218             return undef;
219             }
220             }
221            
222             if ( defined $self->{db}->{DB} )
223             {
224             $self->connect or return undef;
225             }
226              
227             return $self;
228             }
229              
230             sub DESTROY
231             {
232             my $self = shift;
233              
234             # close cursor
235             $self->finish;
236              
237             # close off old transaction
238             $self->_msg( "commit" );
239             $self->_exec( "commit", "couldn't commit transaction\n" );
240             }
241              
242             =item connect([\%attr])
243              
244             This method will connect to a database. It takes an optional hash
245             which may contain the following attributes:
246              
247             =over 4
248              
249             =item DB
250              
251             The name of the database to which to connect. If this is set, B
252             will attempt to make a connection. Alternatively, see the B method.
253              
254             =item Host
255              
256             The host to which to connect. This defaults to the value of the C
257             environmental variable, if that exists, else the undefined value.
258              
259             =item Port
260              
261             The port to which to connect. This defaults to the value of the C
262             environmental variable, if that exists, else C<5432>.
263              
264             =item User
265              
266             The database user id.
267              
268             =item Password
269              
270             The password to pass to the backend. Required only if the database
271             requires password authentication. If not specified, the value of the
272             C environmental variable is used.
273              
274             =back
275              
276             It returns C upon error, else something else.
277              
278             =cut
279              
280             sub connect
281             {
282             my $self = shift;
283             my $attr = shift;
284              
285             my %db = ( %{$self->{db}}, $attr ? %$attr : () );
286              
287             my $connstr = "dbname=$db{DB} host=$db{Host} port=$db{Port}";
288             $connstr .= " user=$db{User}"
289             if $db{User};
290              
291             $connstr .= " password=$db{Password}"
292             if $db{Password};
293              
294             $self->_msg( "opening connection `$connstr'" );
295             $self->{conn} = Pg::connectdb( $connstr );
296              
297             if ( PGRES_CONNECTION_OK ne $self->{conn}->status )
298             {
299             $self->_error( "error opening connection: $connstr\n",
300             $self->{conn}->errorMessage, "\n" );
301             $self->{conn} = undef;
302             return undef;
303             }
304             if ( defined $self->{attr}->{Trace} )
305             {
306             $self->{conn}->trace( $self->{attr}->{Trace} );
307             }
308              
309             # start transaction
310             $self->_msg( "begin" );
311             $self->_exec( "begin", "couldn't begin transaction\n" )
312             or return undef;
313              
314             return $self->{conn};
315             }
316              
317             =item execute( command [, \%attr] )
318              
319             This method will pass a command or query to the backend.
320             It may be passed a hash containing the following attributes:
321              
322             =over 4
323              
324             =item AutoCursor
325              
326             If set, the B method always creates a cursor before sending the
327             command to the backend. This defaults to on.
328              
329             =item RaiseError
330              
331             If set, errors will result in an exception being thrown ( via
332             B), rather. If not set, errors will result in a message being
333             printed to C and an error value returned to the caller. It
334             defaults to on.
335              
336             =back
337              
338             The attributes apply to this method call only.
339              
340             It returns C upon error.
341              
342             =cut
343              
344              
345             sub execute
346             {
347             my $self = shift;
348             my $exp = shift;
349             my $attr = shift;
350              
351             my %attr = ( %{$self->{attr}}, $attr ? %$attr : () );
352              
353             local $self->{attr} = \%attr;
354              
355             if ( $attr{AutoCursor} )
356             {
357             $self->_msg( "declare mycursor cursor for $exp" );
358             unless ( $self->_exec( "declare mycursor cursor for $exp",
359             "couldn't create cursor for $exp\n" ) )
360             {
361             $self->abort;
362             return undef;
363             }
364             $self->{cursor}++;
365             }
366             else
367             {
368             $self->_msg( "$exp" );
369             $self->{result} =
370             $self->_exec( $exp, "error performing `$exp'\n" ) or
371             ( $self->abort, return undef );
372             $self->{last_tuple} = -1;
373             $self->commit if $self->{attr}->{AutoCommit};
374             }
375              
376             1;
377             }
378              
379             =item do( command [, \%attr] )
380              
381             This method sends the command to the backend. It does not create
382             a cursor. It may be passed a hash containing the following attributes:
383              
384             =over 4
385              
386             =item RaiseError
387              
388             If set, errors will result in an exception being thrown ( via
389             B), rather. If not set, errors will result in a message being
390             printed to C and an error value returned to the caller. It
391             defaults to on.
392              
393             =back
394              
395             The attributes apply to this method call only.
396              
397             It returns C upon error.
398              
399             =cut
400              
401              
402             sub do
403             {
404             my $self = shift;
405             my $exp = shift;
406             my $attr = shift;
407              
408             my %attr = ( %{$self->{attr}}, $attr ? %$attr : () );
409             local $self->{attr} = \%attr;
410              
411             $self->_msg( "$exp" );
412             $self->{result} =
413             $self->_exec( $exp, "error performing `$exp'\n" ) or
414             ( $self->abort, return undef );
415             $self->{last_tuple} = -1;
416              
417             $self->commit if $self->{attr}->{AutoCommit};
418              
419             1;
420             }
421              
422             =item ntuples
423              
424             The number of tuples returned by the last query. It returns -1 if it
425             can't access that information.
426              
427             =cut
428              
429             sub ntuples
430             {
431             my $self = shift;
432              
433             return $self->{result} ? $self->{result}->ntuples : -1;
434             }
435              
436              
437             =item fetch( \$field1, \$field2, ... )
438              
439             Returns the next tuple of data generated by a previous call to
440             B. If the C attribute was set, it will
441             internally fetch C tuples at a time. It stores the returned
442             fields in the scalars referenced by the passed arguments. It is an
443             error if there are fewer passed references than fields requested by
444             the select.
445              
446             It returns 0 if there are no more tuples, C upon error.
447              
448             =cut
449              
450              
451             sub fetch
452             {
453             my $self = shift;
454              
455             my $result = $self->_fetch;
456              
457             return $result unless ref $result;
458              
459             if ( @_ != $result->nfields )
460             {
461             $self->_error("expected ", scalar @_, " got $result->{nfields}\n");
462             return undef;
463             }
464              
465             ${ shift @_ } = $_ foreach ( $result->fetchrow );
466              
467             1;
468             }
469              
470             =item fetch_hashref
471              
472             $hash = $db->fetch_hasherf
473              
474             Returns the next tuple of data generated by a previous call to
475             B. If the C attribute was set, it will
476             internally fetch C tuples at a time. It returns the row
477             as a hashref.
478              
479             It returns an empty hash if there are no more tuples, C upon error.
480              
481             =cut
482              
483             sub fetch_hashref
484             {
485             my $self = shift;
486              
487             my $result = $self->_fetch;
488              
489             if ( defined $result )
490             {
491             if ( ref $result )
492             {
493             @{$self->{hash}}{@{$self->{fname}}} = $result->fetchrow;
494             return $self->{hash};
495             }
496             else
497             {
498             return {};
499             }
500             }
501              
502             return $result;
503             }
504              
505             sub _fetch
506             {
507             my $self = shift;
508              
509             my $result = $self->{result};
510             my $tuple = ++$self->{last_tuple};
511              
512             # we're in a cursor
513             if ( $self->{cursor} )
514             {
515             # check if there are still any left from a previous fetch
516              
517             if ( ! defined $result || $tuple >= $result->ntuples )
518             {
519             # delete old results and reset tuple counter
520             $self->{result} = undef;
521             $self->{last_tuple} = -1;
522              
523             # get new results
524             my $fetch = "fetch $self->{attr}->{NFetch} in mycursor";
525             $self->_msg( $fetch );
526             $result = $self->_exec( $fetch, "couldn't fetch\n" )
527             or return undef;
528              
529             return 0
530             unless $result->ntuples;
531              
532             # succeeded, save result
533             $self->{result} = $result;
534              
535             unless ( defined $self->{fname} )
536             {
537             my @fnames;
538             push @fnames, $result->fname($_) for 0.. $result->nfields-1;
539             $self->{fname} = \@fnames;
540             $self->{hash} = {};
541             @{$self->{hash}}{@fnames} = (undef) x @fnames;
542             }
543              
544             $tuple = $self->{last_tuple} = 0;
545             }
546             }
547              
548             # not in a cursor
549             else
550             {
551             return 0
552             if $tuple >= $result->ntuples;
553             }
554              
555             $result;
556             }
557              
558              
559             sub _exec
560             {
561             my $self = shift;
562             my $exp = shift;
563             my $errmsg = shift;
564              
565             my $result = $self->{conn}->exec( $exp );
566             if (
567             PGRES_COMMAND_OK != $result->resultStatus and
568             PGRES_TUPLES_OK != $result->resultStatus
569             )
570             {
571             $self->_error( $errmsg );
572             return undef;
573             }
574             return $result;
575             }
576              
577             =item commit
578              
579             This should be called to commit any changes to the database.
580              
581             =cut
582              
583             sub commit
584             {
585             my $self = shift;
586              
587             # close off old transaction
588             $self->_msg( "commit" );
589             $self->_exec( "commit", "couldn't commit transaction\n" );
590              
591             # start a new one
592             $self->_msg( "begin" );
593             $self->_exec( "begin", "couldn't begin transaction\n" );
594             }
595              
596             =item abort
597              
598             This should be called to abort the current transaction
599              
600             =cut
601              
602             sub abort
603             {
604             my $self = shift;
605              
606             # close off old transaction
607             $self->_msg( "abort" );
608             $self->_exec( "abort", "couldn't abort transaction\n" );
609              
610             # start a new one
611             $self->_msg( "begin" );
612             $self->_exec( "begin", "couldn't begin transaction\n" );
613             }
614              
615              
616             =item finish
617              
618             This should be called after all fetchs have been completed for a
619             select statement. It closes the cursor (if C was specified).
620              
621             =cut
622              
623             sub finish
624             {
625             my $self = shift;
626              
627             if ( $self->{cursor} )
628             {
629             $self->_msg( "close mycursor" );
630             $self->_exec( "close mycursor", "couldn't close cursor\n" );
631             $self->{cursor} = 0;
632             }
633              
634             $self->commit if $self->{attr}->{AutoCommit};
635              
636             }
637              
638             sub _error
639             {
640             my $self = shift;
641              
642             if ( $self->{attr}{RaiseError} )
643             {
644             croak @_;
645             }
646             else
647             {
648             carp @_;
649             }
650             }
651              
652             sub _msg
653             {
654             my $self = shift;
655              
656             warn $self->{attr}{Name}, ': ', @_, "\n"
657             if $self->{attr}{Verbose};
658             }
659              
660              
661             1;
662              
663             =back
664              
665             =head1 LICENSE
666              
667             This software is released under the GNU General Public License. You
668             may find a copy at
669              
670             http://www.fsf.org/copyleft/gpl.html
671              
672             =head1 AUTHOR
673              
674             Diab Jerius ( djerius@cpan.org )
675              
676              
677             =cut