File Coverage

blib/lib/DBIx/Class/Storage/DBI/Pg.pm
Criterion Covered Total %
statement 25 90 27.7
branch 3 38 7.8
condition 1 19 5.2
subroutine 9 25 36.0
pod 5 5 100.0
total 43 177 24.2


line stmt bran cond sub pod time code
1             package DBIx::Class::Storage::DBI::Pg;
2              
3 4     4   2370 use strict;
  4         10  
  4         114  
4 4     4   19 use warnings;
  4         8  
  4         111  
5              
6 4     4   19 use base qw/DBIx::Class::Storage::DBI/;
  4         7  
  4         1156  
7              
8 4     4   392 use Scope::Guard ();
  4         395  
  4         79  
9 4     4   24 use Context::Preserve 'preserve_context';
  4         8  
  4         180  
10 4     4   26 use DBIx::Class::Carp;
  4         8  
  4         23  
11 4     4   22 use Try::Tiny;
  4         10  
  4         207  
12 4     4   25 use namespace::clean;
  4         9  
  4         33  
13              
14             __PACKAGE__->sql_limit_dialect ('LimitOffset');
15             __PACKAGE__->sql_quote_char ('"');
16             __PACKAGE__->datetime_parser_type ('DateTime::Format::Pg');
17             __PACKAGE__->_use_multicolumn_in (1);
18              
19             sub _determine_supports_insert_returning {
20 0 0   0   0 return shift->_server_info->{normalized_dbms_version} >= 8.002
21             ? 1
22             : 0
23             ;
24             }
25              
26             sub with_deferred_fk_checks {
27 0     0 1 0 my ($self, $sub) = @_;
28              
29 0         0 my $txn_scope_guard = $self->txn_scope_guard;
30              
31 0         0 $self->_do_query('SET CONSTRAINTS ALL DEFERRED');
32              
33             my $sg = Scope::Guard->new(sub {
34 0     0   0 $self->_do_query('SET CONSTRAINTS ALL IMMEDIATE');
35 0         0 });
36              
37 0     0   0 return preserve_context { $sub->() } after => sub { $txn_scope_guard->commit };
  0         0  
  0         0  
38             }
39              
40             # only used when INSERT ... RETURNING is disabled
41             sub last_insert_id {
42 0     0 1 0 my ($self,$source,@cols) = @_;
43              
44 0         0 my @values;
45              
46 0         0 my $col_info = $source->columns_info(\@cols);
47              
48 0         0 for my $col (@cols) {
49 0 0 0     0 my $seq = ( $col_info->{$col}{sequence} ||= $self->dbh_do('_dbh_get_autoinc_seq', $source, $col) )
50             or $self->throw_exception( sprintf(
51             "Could not determine sequence for column '%s.%s', please consider adding a schema-qualified sequence to its column info",
52             $source->name,
53             $col,
54             ));
55              
56 0         0 push @values, $self->_dbh->last_insert_id(undef, undef, undef, undef, {sequence => $seq});
57             }
58              
59 0         0 return @values;
60             }
61              
62             sub _sequence_fetch {
63 0     0   0 my ($self, $function, $sequence) = @_;
64              
65 0 0       0 $self->throw_exception('No sequence to fetch') unless $sequence;
66              
67 0 0       0 my ($val) = $self->_get_dbh->selectrow_array(
68             sprintf ("select %s('%s')", $function, (ref $sequence eq 'SCALAR') ? $$sequence : $sequence)
69             );
70              
71 0         0 return $val;
72             }
73              
74             sub _dbh_get_autoinc_seq {
75 0     0   0 my ($self, $dbh, $source, $col) = @_;
76              
77 0         0 my $schema;
78 0         0 my $table = $source->name;
79              
80             # deref table name if it needs it
81 0 0       0 $table = $$table
82             if ref $table eq 'SCALAR';
83              
84             # parse out schema name if present
85 0 0       0 if( $table =~ /^(.+)\.(.+)$/ ) {
86 0         0 ( $schema, $table ) = ( $1, $2 );
87             }
88              
89             # get the column default using a Postgres-specific pg_catalog query
90 0         0 my $seq_expr = $self->_dbh_get_column_default( $dbh, $schema, $table, $col );
91              
92             # if no default value is set on the column, or if we can't parse the
93             # default value as a sequence, throw.
94 0 0 0     0 unless ( defined $seq_expr and $seq_expr =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/i ) {
95 0 0       0 $seq_expr = '' unless defined $seq_expr;
96 0 0 0     0 $schema = "$schema." if defined $schema && length $schema;
97 0 0       0 $self->throw_exception( sprintf (
98             "No sequence found for '%s%s.%s', check the RDBMS table definition or explicitly set the ".
99             "'sequence' for this column in %s",
100             $schema ? "$schema." : '',
101             $table,
102             $col,
103             $source->source_name,
104             ));
105             }
106              
107 0         0 return $1; # exception thrown unless match is made above
108             }
109              
110             # custom method for fetching column default, since column_info has a
111             # bug with older versions of DBD::Pg
112             sub _dbh_get_column_default {
113 0     0   0 my ( $self, $dbh, $schema, $table, $col ) = @_;
114              
115             # Build and execute a query into the pg_catalog to find the Pg
116             # expression for the default value for this column in this table.
117             # If the table name is schema-qualified, query using that specific
118             # schema name.
119              
120             # Otherwise, find the table in the standard Postgres way, using the
121             # search path. This is done with the pg_catalog.pg_table_is_visible
122             # function, which returns true if a given table is 'visible',
123             # meaning the first table of that name to be found in the search
124             # path.
125              
126             # I *think* we can be assured that this query will always find the
127             # correct column according to standard Postgres semantics.
128             #
129             # -- rbuels
130              
131 0         0 my $sqlmaker = $self->sql_maker;
132 0         0 local $sqlmaker->{bindtype} = 'normal';
133              
134 0 0 0     0 my ($where, @bind) = $sqlmaker->where ({
135             'a.attnum' => {'>', 0},
136             'c.relname' => $table,
137             'a.attname' => $col,
138             -not_bool => 'a.attisdropped',
139             (defined $schema && length $schema)
140             ? ( 'n.nspname' => $schema )
141             : ( -bool => \'pg_catalog.pg_table_is_visible(c.oid)' )
142             });
143              
144 0         0 my ($seq_expr) = $dbh->selectrow_array(<<EOS,undef,@bind);
145              
146             SELECT
147             (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid)
148             FROM pg_catalog.pg_attrdef d
149             WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
150             FROM pg_catalog.pg_class c
151             LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
152             JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
153             $where
154              
155             EOS
156              
157 0         0 return $seq_expr;
158             }
159              
160              
161             sub sqlt_type {
162 0     0 1 0 return 'PostgreSQL';
163             }
164              
165             # Pg is not able to MAX(boolean_column), sigh...
166             #
167             # Generally it would make more sense to have this in the SQLMaker hierarchy,
168             # so that eventually { -max => ... } DTRT, but plans going forward are
169             # murky at best
170             # --ribasushi
171             #
172             sub _minmax_operator_for_datatype {
173             #my ($self, $datatype, $want_max) = @_;
174              
175 2 100 50 2   26 return ($_[2] ? 'BOOL_OR' : 'BOOL_AND')
    50          
176             if ($_[1] || '') =~ /\Abool(?:ean)?\z/i;
177              
178 0           shift->next::method(@_);
179             }
180              
181             sub bind_attribute_by_data_type {
182 0     0 1   my ($self,$data_type) = @_;
183              
184 0 0         if ($self->_is_binary_lob_type($data_type)) {
185             # this is a hot-ish codepath, use an escape flag to minimize
186             # amount of function/method calls
187             # additionally version.pm is cock, and memleaks on multiple
188             # ->VERSION calls
189             # the flag is stored in the DBD namespace, so that Class::Unload
190             # will work (unlikely, but still)
191 0 0         unless ($DBD::Pg::__DBIC_DBD_VERSION_CHECK_DONE__) {
192 0 0         if ($self->_server_info->{normalized_dbms_version} >= 9.0) {
    0          
193 0 0   0     try { DBD::Pg->VERSION('2.17.2'); 1 } or carp (
  0            
  0            
194             __PACKAGE__.': BYTEA columns are known to not work on Pg >= 9.0 with DBD::Pg < 2.17.2'
195             );
196             }
197 0     0     elsif (not try { DBD::Pg->VERSION('2.9.2'); 1 } ) { carp (
  0            
  0            
198             __PACKAGE__.': DBD::Pg 2.9.2 or greater is strongly recommended for BYTEA column support'
199             )}
200              
201 0           $DBD::Pg::__DBIC_DBD_VERSION_CHECK_DONE__ = 1;
202             }
203              
204 0           return { pg_type => DBD::Pg::PG_BYTEA() };
205             }
206             else {
207 0           return undef;
208             }
209             }
210              
211             sub _exec_svp_begin {
212 0     0     my ($self, $name) = @_;
213              
214 0           $self->_dbh->pg_savepoint($name);
215             }
216              
217             sub _exec_svp_release {
218 0     0     my ($self, $name) = @_;
219              
220 0           $self->_dbh->pg_release($name);
221             }
222              
223             sub _exec_svp_rollback {
224 0     0     my ($self, $name) = @_;
225              
226 0           $self->_dbh->pg_rollback_to($name);
227             }
228              
229             sub deployment_statements {
230 0     0 1   my $self = shift;;
231 0           my ($schema, $type, $version, $dir, $sqltargs, @rest) = @_;
232              
233 0   0       $sqltargs ||= {};
234              
235 0 0 0       if (
236             ! exists $sqltargs->{producer_args}{postgres_version}
237             and
238             my $dver = $self->_server_info->{normalized_dbms_version}
239             ) {
240 0           $sqltargs->{producer_args}{postgres_version} = $dver;
241             }
242              
243 0           $self->next::method($schema, $type, $version, $dir, $sqltargs, @rest);
244             }
245              
246             1;
247              
248             __END__
249              
250             =head1 NAME
251              
252             DBIx::Class::Storage::DBI::Pg - Automatic primary key class for PostgreSQL
253              
254             =head1 SYNOPSIS
255              
256             # In your result (table) classes
257             use base 'DBIx::Class::Core';
258             __PACKAGE__->set_primary_key('id');
259              
260             =head1 DESCRIPTION
261              
262             This class implements autoincrements for PostgreSQL.
263              
264             =head1 POSTGRESQL SCHEMA SUPPORT
265              
266             This driver supports multiple PostgreSQL schemas, with one caveat: for
267             performance reasons, data about the search path, sequence names, and
268             so forth is queried as needed and CACHED for subsequent uses.
269              
270             For this reason, once your schema is instantiated, you should not
271             change the PostgreSQL schema search path for that schema's database
272             connection. If you do, Bad Things may happen.
273              
274             You should do any necessary manipulation of the search path BEFORE
275             instantiating your schema object, or as part of the on_connect_do
276             option to connect(), for example:
277              
278             my $schema = My::Schema->connect
279             ( $dsn,$user,$pass,
280             { on_connect_do =>
281             [ 'SET search_path TO myschema, foo, public' ],
282             },
283             );
284              
285             =head1 FURTHER QUESTIONS?
286              
287             Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
288              
289             =head1 COPYRIGHT AND LICENSE
290              
291             This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
292             by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
293             redistribute it and/or modify it under the same terms as the
294             L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.