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   2596 use strict;
  4         6  
  4         120  
4 4     4   15 use warnings;
  4         4  
  4         122  
5              
6 4     4   17 use base qw/DBIx::Class::Storage::DBI/;
  4         6  
  4         1377  
7              
8 4     4   24 use Scope::Guard ();
  4         7  
  4         73  
9 4     4   16 use Context::Preserve 'preserve_context';
  4         7  
  4         193  
10 4     4   16 use DBIx::Class::Carp;
  4         7  
  4         25  
11 4     4   19 use Try::Tiny;
  4         5  
  4         205  
12 4     4   51 use namespace::clean;
  4         11  
  4         25  
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(<
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   22 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__