File Coverage

blib/lib/Jifty/DBI/Handle/Pg.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             package Jifty::DBI::Handle::Pg;
2 2     2   51689 use strict;
  2         4  
  2         57  
3              
4 2     2   6 use vars qw($VERSION @ISA $DBIHandle $DEBUG);
  2         2  
  2         112  
5 2     2   8 use base qw(Jifty::DBI::Handle);
  2         2  
  2         506  
6              
7             use strict;
8              
9             =head1 NAME
10              
11             Jifty::DBI::Handle::Pg - A Postgres specific Handle object
12              
13             =head1 SYNOPSIS
14              
15              
16             =head1 DESCRIPTION
17              
18             This module provides a subclass of L<Jifty::DBI::Handle> that
19             compensates for some of the idiosyncrasies of Postgres.
20              
21             =head1 METHODS
22              
23             =cut
24              
25             =head2 connect
26              
27             connect takes a hashref and passes it off to SUPER::connect; Forces
28             the timezone to GMT, returns a database handle.
29              
30             =cut
31              
32             sub connect {
33             my $self = shift;
34              
35             $self->SUPER::connect(@_);
36             $self->simple_query("SET TIME ZONE 'GMT'");
37             $self->simple_query("SET DATESTYLE TO 'ISO'");
38             $self->auto_commit(1);
39             return ($DBIHandle);
40             }
41              
42             =head2 insert
43              
44             Takes a table name as the first argument and assumes that the rest of
45             the arguments are an array of key-value pairs to be inserted.
46              
47             In case of insert failure, returns a L<Class::ReturnValue> object
48             preloaded with error info
49              
50             =cut
51              
52             sub insert {
53             my $self = shift;
54             my $table = shift;
55             my %args = (@_);
56             my $sth = $self->SUPER::insert( $table, %args );
57              
58             unless ($sth) {
59             return ($sth);
60             }
61              
62             if ( $args{'id'} || $args{'Id'} ) {
63             $self->{'id'} = $args{'id'} || $args{'Id'};
64             return ( $self->{'id'} );
65             }
66              
67             my $sequence_name = $self->id_sequence_name($table);
68             unless ($sequence_name) { return ($sequence_name) } # Class::ReturnValue
69             my $seqsth = $self->dbh->prepare(
70             qq{SELECT CURRVAL('} . $sequence_name . qq{')} );
71             $seqsth->execute;
72             $self->{'id'} = $seqsth->fetchrow_array();
73              
74             return ( $self->{'id'} );
75             }
76              
77             =head2 id_sequence_name TABLE
78              
79             Takes a TABLE name and returns the name of the sequence of the primary key for that table.
80              
81             =cut
82              
83             sub id_sequence_name {
84             my $self = shift;
85             my $table = shift;
86              
87             return $self->{'_sequences'}{$table}
88             if ( exists $self->{'_sequences'}{$table} );
89              
90             #Lets get the id of that row we just inserted
91             my $seq;
92             my $colinfosth = $self->dbh->column_info( undef, undef, lc($table), '%' );
93             while ( my $foo = $colinfosth->fetchrow_hashref ) {
94              
95             # Regexp from DBIx::Class's Pg handle. Thanks to Marcus Ramberg
96             if ( defined $foo->{'COLUMN_DEF'}
97             && $foo->{'COLUMN_DEF'}
98             =~ m!^nextval\(+'"?([^"']+)"?'(::(?:text|regclass)\))+!i )
99             {
100             return $self->{'_sequences'}{$table} = $1;
101             }
102              
103             }
104             my $ret = Class::ReturnValue->new();
105             $ret->as_error(
106             errno => '-1',
107             message => "Found no sequence for $table",
108             do_backtrace => undef
109             );
110             return ( $ret->return_value );
111              
112             }
113              
114             =head2 blob_params column_NAME column_type
115              
116             Returns a hash ref for the bind_param call to identify BLOB types used
117             by the current database for a particular column type. The current
118             Postgres implementation only supports BYTEA types.
119              
120             =cut
121              
122             sub blob_params {
123             my $self = shift;
124             my $name = shift;
125             my $type = shift;
126              
127             # Don't assign to key 'value' as it is defined later.
128             return ( { pg_type => DBD::Pg::PG_BYTEA() } ) if $type eq "blob";
129             return ( {} );
130             }
131              
132             =head2 apply_limits STATEMENTREF ROWS_PER_PAGE FIRST_ROW
133              
134             takes an SQL SELECT statement and massages it to return ROWS_PER_PAGE
135             starting with FIRST_ROW;
136              
137             =cut
138              
139             sub apply_limits {
140             my $self = shift;
141             my $statementref = shift;
142             my $per_page = shift;
143             my $first = shift;
144              
145             my $limit_clause = '';
146              
147             if ($per_page) {
148             $limit_clause = " LIMIT ";
149             $limit_clause .= $per_page;
150             if ( $first && $first != 0 ) {
151             $limit_clause .= " OFFSET $first";
152             }
153             }
154              
155             $$statementref .= $limit_clause;
156              
157             }
158              
159             =head2 _make_clause_case_insensitive column operator VALUE
160              
161             Takes a column, operator and value. performs the magic necessary to make
162             your database treat this clause as case insensitive.
163              
164             Returns a column operator value triple.
165              
166             =cut
167              
168             sub _make_clause_case_insensitive {
169             my $self = shift;
170             my $column = shift;
171             my $operator = shift;
172             my $value = shift;
173              
174             if ($self->_case_insensitivity_valid($column, $operator, $value)) {
175             if ( $operator =~ /(?:LIKE|=)/i ) {
176             $column = "LOWER($column)";
177             $value = "LOWER($value)";
178             }
179             }
180             return ( $column, $operator, $value );
181             }
182              
183             =head2 distinct_query STATEMENTREF
184              
185             takes an incomplete SQL SELECT statement and massages it to return a DISTINCT result set.
186              
187             =cut
188              
189             sub distinct_query {
190             my $self = shift;
191             my $statementref = shift;
192             my $sb = shift;
193             my $table = $sb->table;
194              
195             if (
196             grep {
197             ( defined $_->{'alias'} and $_->{'alias'} ne 'main' )
198             || defined $_->{'function'}
199             } @{ $sb->order_by }
200             )
201             {
202              
203             # If we are ordering by something not in 'main', we need to GROUP
204             # BY and adjust the ORDER_BY accordingly
205             local $sb->{group_by}
206             = [ @{ $sb->{group_by} || [] }, { column => 'id' } ];
207             local $sb->{order_by} = [
208             map {
209             my $alias = $_->{alias} || '';
210             my $column = $_->{column};
211             $alias .= '.' if $alias;
212             #warn "alias $alias => column $column\n";
213             ((!$alias or $alias eq 'main.') and $column eq 'id')
214             ? $_
215             : { %{$_}, alias => '', column => "min($alias$column)" }
216             } @{ $sb->{order_by} }
217             ];
218             my $group = $sb->_group_clause;
219             my $order = $sb->_order_clause;
220             $$statementref
221             = "SELECT ".$sb->_preload_columns." FROM ( SELECT main.id FROM $$statementref $group $order ) distinctquery, $table main WHERE (main.id = distinctquery.id)";
222             }
223             else {
224             $$statementref = "SELECT DISTINCT ".$sb->_preload_columns." FROM $$statementref";
225             $$statementref .= $sb->_group_clause;
226             $$statementref .= $sb->_order_clause;
227             }
228             }
229              
230             1;
231              
232             __END__
233              
234             =head1 SEE ALSO
235              
236             L<Jifty::DBI>, L<Jifty::DBI::Handle>, L<DBD::Pg>
237              
238             =cut
239