File Coverage

blib/lib/DBIx/Class/Storage/DBI/Pg.pm
Criterion Covered Total %
statement 25 88 28.4
branch 3 38 7.8
condition 1 28 3.5
subroutine 9 24 37.5
pod 5 5 100.0
total 43 183 23.5


line stmt bran cond sub pod time code
1             package DBIx::Class::Storage::DBI::Pg;
2              
3 4     4   2545 use strict;
  4         12  
  4         125  
4 4     4   25 use warnings;
  4         9  
  4         137  
5              
6 4     4   23 use base qw/DBIx::Class::Storage::DBI/;
  4         11  
  4         925  
7              
8 4     4   923 use Scope::Guard ();
  4         1083  
  4         93  
9 4     4   27 use Context::Preserve 'preserve_context';
  4         9  
  4         206  
10 4     4   26 use DBIx::Class::Carp;
  4         11  
  4         33  
11 4     4   25 use DBIx::Class::_Util 'modver_gt_or_eq';
  4         11  
  4         177  
12 4     4   25 use namespace::clean;
  4         9  
  4         203  
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             sub _dbh_execute_for_fetch {
161             #my ($self, $source, $sth, $tuple_status, @extra) = @_;
162              
163             # This is used for bulk insert, so make sure we use a server-side
164             # prepared statement from the start, unless it's disabled
165 0 0 0 0   0 local $_[2]->{pg_switch_prepared} = 1 if
166             modver_gt_or_eq( 'DBD::Pg', '3.0.0' )
167             and
168             $_[2]->FETCH('pg_switch_prepared') > 0
169             ;
170              
171 0         0 shift->next::method(@_);
172             }
173              
174             sub sqlt_type {
175 0     0 1 0 return 'PostgreSQL';
176             }
177              
178             # Pg is not able to MAX(boolean_column), sigh...
179             #
180             # Generally it would make more sense to have this in the SQLMaker hierarchy,
181             # so that eventually { -max => ... } DTRT, but plans going forward are
182             # murky at best
183             # --ribasushi
184             #
185             sub _minmax_operator_for_datatype {
186             #my ($self, $datatype, $want_max) = @_;
187              
188 2 100 50 2   25 return ($_[2] ? 'BOOL_OR' : 'BOOL_AND')
    50          
189             if ($_[1] || '') =~ /\Abool(?:ean)?\z/i;
190              
191 0           shift->next::method(@_);
192             }
193              
194             sub bind_attribute_by_data_type {
195 0     0 1   my ($self,$data_type) = @_;
196              
197 0 0         if ($self->_is_binary_lob_type($data_type)) {
198             # this is a hot-ish codepath, use an escape flag to minimize
199             # amount of function/method calls
200             # the flag is stored in the DBD namespace, so that Class::Unload
201             # will work (unlikely, but still)
202 0 0 0       unless (
203             modver_gt_or_eq( 'DBD::Pg', '2.17.2' )
204             or
205             $DBD::Pg::__DBIC_DBD_VERSION_CHECK_DONE__
206             ) {
207 0 0 0       if ( $self->_server_info->{normalized_dbms_version} >= 9.0 ) {
    0          
208 0           $self->throw_exception(
209             'BYTEA columns are known to not work on Pg >= 9.0 with DBD::Pg < 2.17.2'
210             );
211             }
212             elsif (
213             require DBIx::Class::Optional::Dependencies
214             and
215             my $missing = DBIx::Class::Optional::Dependencies->req_missing_for([qw( rdbms_pg binary_data )])
216             ) {
217             # FIXME - perhaps this needs to be an exception too...?
218             # too old to test sensibly...
219 0           carp (
220             __PACKAGE__ . ": BYTEA column support strongly recommends $missing"
221             )
222             }
223              
224 0           $DBD::Pg::__DBIC_DBD_VERSION_CHECK_DONE__ = 1;
225             }
226              
227 0           return { pg_type => DBD::Pg::PG_BYTEA() };
228             }
229             else {
230 0           return undef;
231             }
232             }
233              
234             sub _exec_svp_begin {
235 0     0     my ($self, $name) = @_;
236              
237 0           $self->_dbh->pg_savepoint($name);
238             }
239              
240             sub _exec_svp_release {
241 0     0     my ($self, $name) = @_;
242              
243 0           $self->_dbh->pg_release($name);
244             }
245              
246             sub _exec_svp_rollback {
247 0     0     my ($self, $name) = @_;
248              
249 0           $self->_dbh->pg_rollback_to($name);
250             }
251              
252             sub deployment_statements {
253 0     0 1   my $self = shift;;
254 0           my ($schema, $type, $version, $dir, $sqltargs, @rest) = @_;
255              
256 0   0       $sqltargs ||= {};
257              
258 0 0 0       if (
259             ! exists $sqltargs->{producer_args}{postgres_version}
260             and
261             my $dver = $self->_server_info->{normalized_dbms_version}
262             ) {
263 0           $sqltargs->{producer_args}{postgres_version} = $dver;
264             }
265              
266 0           $self->next::method($schema, $type, $version, $dir, $sqltargs, @rest);
267             }
268              
269             1;
270              
271             __END__