Make DateTime objects work for all ops
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Pg.pm
CommitLineData
843f8ecd 1package DBIx::Class::Storage::DBI::Pg;
2
3use strict;
4use warnings;
5
be860760 6use base qw/
7 DBIx::Class::Storage::DBI::MultiColumnIn
be860760 8/;
ac45262b 9use mro 'c3';
843f8ecd 10
4d4dc518 11use Scope::Guard ();
6298a324 12use Context::Preserve 'preserve_context';
ee53ca0f 13use DBIx::Class::Carp;
9aec3ec6 14use Try::Tiny;
6298a324 15use namespace::clean;
843f8ecd 16
8aac0b48 17__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::Pg');
6a247f33 18__PACKAGE__->sql_limit_dialect ('LimitOffset');
2b8cc2f2 19__PACKAGE__->sql_quote_char ('"');
6f7a118e 20__PACKAGE__->datetime_parser_type ('DateTime::Format::Pg');
6a247f33 21
bbdda281 22sub _determine_supports_insert_returning {
23 return shift->_server_info->{normalized_dbms_version} >= 8.002
24 ? 1
25 : 0
26 ;
38d5ea9f 27}
28
e96a93df 29sub with_deferred_fk_checks {
30 my ($self, $sub) = @_;
31
4d4dc518 32 my $txn_scope_guard = $self->txn_scope_guard;
33
34 $self->_do_query('SET CONSTRAINTS ALL DEFERRED');
38d5ea9f 35
4d4dc518 36 my $sg = Scope::Guard->new(sub {
37 $self->_do_query('SET CONSTRAINTS ALL IMMEDIATE');
38 });
39
6298a324 40 return preserve_context { $sub->() } after => sub { $txn_scope_guard->commit };
e96a93df 41}
42
bab40dee 43# only used when INSERT ... RETURNING is disabled
38d5ea9f 44sub last_insert_id {
45 my ($self,$source,@cols) = @_;
46
47 my @values;
48
52416317 49 my $col_info = $source->columns_info(\@cols);
50
38d5ea9f 51 for my $col (@cols) {
52416317 52 my $seq = ( $col_info->{$col}{sequence} ||= $self->dbh_do('_dbh_get_autoinc_seq', $source, $col) )
38d5ea9f 53 or $self->throw_exception( sprintf(
54 'could not determine sequence for column %s.%s, please consider adding a schema-qualified sequence to its column info',
55 $source->name,
56 $col,
57 ));
58
59 push @values, $self->_dbh->last_insert_id(undef, undef, undef, undef, {sequence => $seq});
60 }
61
62 return @values;
63}
64
4d4dc518 65sub _sequence_fetch {
66 my ($self, $function, $sequence) = @_;
67
68 $self->throw_exception('No sequence to fetch') unless $sequence;
38d5ea9f 69
4d4dc518 70 my ($val) = $self->_get_dbh->selectrow_array(
07cda1c5 71 sprintf ("select %s('%s')", $function, (ref $sequence eq 'SCALAR') ? $$sequence : $sequence)
4d4dc518 72 );
73
74 return $val;
38d5ea9f 75}
9a0b7b26 76
2d424996 77sub _dbh_get_autoinc_seq {
78 my ($self, $dbh, $source, $col) = @_;
0063119f 79
2d424996 80 my $schema;
81 my $table = $source->name;
0063119f 82
2d424996 83 # deref table name if it needs it
84 $table = $$table
85 if ref $table eq 'SCALAR';
0063119f 86
2d424996 87 # parse out schema name if present
88 if( $table =~ /^(.+)\.(.+)$/ ) {
89 ( $schema, $table ) = ( $1, $2 );
90 }
0680ac39 91
2d424996 92 # get the column default using a Postgres-specific pg_catalog query
93 my $seq_expr = $self->_dbh_get_column_default( $dbh, $schema, $table, $col );
94
95 # if no default value is set on the column, or if we can't parse the
96 # default value as a sequence, throw.
5861223d 97 unless ( defined $seq_expr and $seq_expr =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/i ) {
2d424996 98 $seq_expr = '' unless defined $seq_expr;
99 $schema = "$schema." if defined $schema && length $schema;
d3fdc7b8 100 $self->throw_exception( sprintf (
101 'no sequence found for %s%s.%s, check the RDBMS table definition or explicitly set the '.
102 "'sequence' for this column in %s",
103 $schema ? "$schema." : '',
104 $table,
105 $col,
106 $source->source_name,
107 ));
777f7527 108 }
2d424996 109
110 return $1;
777f7527 111}
112
2d424996 113# custom method for fetching column default, since column_info has a
114# bug with older versions of DBD::Pg
115sub _dbh_get_column_default {
116 my ( $self, $dbh, $schema, $table, $col ) = @_;
117
118 # Build and execute a query into the pg_catalog to find the Pg
119 # expression for the default value for this column in this table.
120 # If the table name is schema-qualified, query using that specific
121 # schema name.
122
123 # Otherwise, find the table in the standard Postgres way, using the
124 # search path. This is done with the pg_catalog.pg_table_is_visible
125 # function, which returns true if a given table is 'visible',
126 # meaning the first table of that name to be found in the search
127 # path.
128
129 # I *think* we can be assured that this query will always find the
130 # correct column according to standard Postgres semantics.
131 #
132 # -- rbuels
133
134 my $sqlmaker = $self->sql_maker;
135 local $sqlmaker->{bindtype} = 'normal';
136
137 my ($where, @bind) = $sqlmaker->where ({
138 'a.attnum' => {'>', 0},
139 'c.relname' => $table,
140 'a.attname' => $col,
141 -not_bool => 'a.attisdropped',
142 (defined $schema && length $schema)
143 ? ( 'n.nspname' => $schema )
144 : ( -bool => \'pg_catalog.pg_table_is_visible(c.oid)' )
145 });
146
147 my ($seq_expr) = $dbh->selectrow_array(<<EOS,undef,@bind);
148
149SELECT
150 (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid)
151 FROM pg_catalog.pg_attrdef d
152 WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
153FROM pg_catalog.pg_class c
154 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
155 JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
156$where
157
158EOS
159
160 return $seq_expr;
161}
162
163
4f533e8c 164sub sqlt_type {
165 return 'PostgreSQL';
166}
167
ee53ca0f 168my $type_cache;
a71859b4 169sub bind_attribute_by_data_type {
170 my ($self,$data_type) = @_;
171
ee53ca0f 172 # Ask for a DBD::Pg with array support
173 # pg uses (used?) version::qv()
174 require DBD::Pg;
9aec3ec6 175
176 if ($self->_server_info->{normalized_dbms_version} >= 9.0) {
177 if (not try { DBD::Pg->VERSION('2.17.2') }) {
178 carp_once( __PACKAGE__.': BYTEA columns are known to not work on Pg >='
179 . " 9.0 with DBD::Pg < 2.17.2\n" );
180 }
181 }
182 elsif (not try { DBD::Pg->VERSION('2.9.2') }) {
183 carp_once( __PACKAGE__.': DBD::Pg 2.9.2 or greater is strongly recommended'
184 . "for BYTEA column support.\n" );
a71859b4 185 }
fbf2cd0a 186 my $bind_attributes = {
187 bytea => { pg_type => DBD::Pg::PG_BYTEA() },
188 blob => { pg_type => DBD::Pg::PG_BYTEA() },
189 timestamp => { pg_type => DBD::Pg::PG_TIMESTAMP() },
190 };
191
192 if( defined $bind_attributes->{$data_type} ) {
193 return $bind_attributes->{$data_type};
194 }
ee53ca0f 195
196 # cache the result of _is_binary_lob_type
197 if (!exists $type_cache->{$data_type}) {
198 $type_cache->{$data_type} = $self->_is_binary_lob_type($data_type)
199 ? +{ pg_type => DBD::Pg::PG_BYTEA() }
200 : undef
a71859b4 201 }
ee53ca0f 202
203 $type_cache->{$data_type};
a71859b4 204}
205
90d7422f 206sub _exec_svp_begin {
eeb8cfeb 207 my ($self, $name) = @_;
adb3554a 208
90d7422f 209 $self->_dbh->pg_savepoint($name);
adb3554a 210}
211
90d7422f 212sub _exec_svp_release {
eeb8cfeb 213 my ($self, $name) = @_;
adb3554a 214
90d7422f 215 $self->_dbh->pg_release($name);
adb3554a 216}
217
90d7422f 218sub _exec_svp_rollback {
eeb8cfeb 219 my ($self, $name) = @_;
adb3554a 220
90d7422f 221 $self->_dbh->pg_rollback_to($name);
adb3554a 222}
223
c6375b4d 224sub deployment_statements {
225 my $self = shift;;
226 my ($schema, $type, $version, $dir, $sqltargs, @rest) = @_;
227
228 $sqltargs ||= {};
229
96736321 230 if (
231 ! exists $sqltargs->{producer_args}{postgres_version}
232 and
233 my $dver = $self->_server_info->{normalized_dbms_version}
234 ) {
235 $sqltargs->{producer_args}{postgres_version} = $dver;
c6375b4d 236 }
237
238 $self->next::method($schema, $type, $version, $dir, $sqltargs, @rest);
239}
240
843f8ecd 2411;
242
fd159e2a 243__END__
244
75d07914 245=head1 NAME
843f8ecd 246
247DBIx::Class::Storage::DBI::Pg - Automatic primary key class for PostgreSQL
248
249=head1 SYNOPSIS
250
d88ecca6 251 # In your result (table) classes
252 use base 'DBIx::Class::Core';
843f8ecd 253 __PACKAGE__->set_primary_key('id');
843f8ecd 254
255=head1 DESCRIPTION
256
257This class implements autoincrements for PostgreSQL.
258
7c0176a1 259=head1 POSTGRESQL SCHEMA SUPPORT
260
4f609014 261This driver supports multiple PostgreSQL schemas, with one caveat: for
6ff1d58c 262performance reasons, data about the search path, sequence names, and
263so forth is queried as needed and CACHED for subsequent uses.
7c0176a1 264
4f609014 265For this reason, once your schema is instantiated, you should not
266change the PostgreSQL schema search path for that schema's database
267connection. If you do, Bad Things may happen.
268
269You should do any necessary manipulation of the search path BEFORE
270instantiating your schema object, or as part of the on_connect_do
271option to connect(), for example:
7c0176a1 272
273 my $schema = My::Schema->connect
274 ( $dsn,$user,$pass,
275 { on_connect_do =>
276 [ 'SET search_path TO myschema, foo, public' ],
277 },
278 );
279
7ff926e6 280=head1 AUTHORS
7c0176a1 281
7ff926e6 282See L<DBIx::Class/CONTRIBUTORS>
843f8ecd 283
284=head1 LICENSE
285
286You may distribute this code under the same terms as Perl itself.
287
288=cut