added big block comment explaining Pg sequence discovery strategy
[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
4ce3b851 6use base qw/DBIx::Class::Storage::DBI::MultiColumnIn/;
ac45262b 7use mro 'c3';
843f8ecd 8
ac45262b 9use DBD::Pg qw(:pg_types);
843f8ecd 10
ac45262b 11# Ask for a DBD::Pg with array support
c70c716e 12warn "DBD::Pg 2.9.2 or greater is strongly recommended\n"
ac45262b 13 if ($DBD::Pg::VERSION < 2.009002); # pg uses (used?) version::qv()
17614944 14
e96a93df 15sub with_deferred_fk_checks {
16 my ($self, $sub) = @_;
17
9ae966b9 18 $self->_get_dbh->do('SET CONSTRAINTS ALL DEFERRED');
e96a93df 19 $sub->();
20}
21
843f8ecd 22sub last_insert_id {
ca48cd7d 23 my ($self,$source,$col) = @_;
0063119f 24 my $seq = ( $source->column_info($col)->{sequence} ||= $self->dbh_do('_dbh_get_autoinc_seq', $source, $col) )
5f70776f 25 or $self->throw_exception( "could not determine sequence for "
26 . $source->name
27 . ".$col, please consider adding a "
28 . "schema-qualified sequence to its column info"
29 );
9a0b7b26 30
31 $self->_dbh_last_insert_id ($self->_dbh, $seq);
0680ac39 32}
33
9a0b7b26 34# there seems to be absolutely no reason to have this as a separate method,
35# but leaving intact in case someone is already overriding it
36sub _dbh_last_insert_id {
37 my ($self, $dbh, $seq) = @_;
38 $dbh->last_insert_id(undef, undef, undef, undef, {sequence => $seq});
39}
40
41
d4f16b21 42sub _dbh_get_autoinc_seq {
0063119f 43 my ($self, $dbh, $source, $col) = @_;
44
45 my $schema;
46 my $table = $source->name;
47
48 # deref table name if it needs it
49 $table = $$table
50 if ref $table eq 'SCALAR';
51
52 # parse out schema name if present
53 if( $table =~ /^(.+)\.(.+)$/ ) {
54 ( $schema, $table ) = ( $1, $2 );
55 }
0680ac39 56
46ae368d 57 # Build and execute a query into the pg_catalog to find the Pg
58 # expression for the default value for this column in this table.
59 # If the table name is schema-qualified, query using that specific
60 # schema name.
61
62 # Otherwise, find the table in the standard Postgres way, using the
63 # search path. This is done with the pg_catalog.pg_table_is_visible
64 # function, which returns true if a given table is 'visible',
65 # meaning the first table of that name to be found in the search
66 # path.
67
68 # I *think* we can be assured that this query will always find the
69 # correct column according to standard Postgres semantics.
70 #
71 # -- rbuels
72
20f43d0c 73 my $sqlmaker = $self->sql_maker;
74 local $sqlmaker->{bindtype} = 'normal';
75
0063119f 76 my ($where, @bind) = $sqlmaker->where ({
20f43d0c 77 'a.attnum' => {'>', 0},
78 'c.relname' => $table,
79 'a.attname' => $col,
80 -not_bool => 'a.attisdropped',
81 (defined $schema && length $schema)
82 ? ( 'n.nspname' => $schema )
83 : ( -bool => \'pg_catalog.pg_table_is_visible(c.oid)' )
84 });
5e14a204 85
86 my ($seq_expr) = $dbh->selectrow_array(<<EOS,undef,@bind);
20f43d0c 87
5e14a204 88SELECT
89 (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid)
90 FROM pg_catalog.pg_attrdef d
91 WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
92FROM pg_catalog.pg_class c
0063119f 93 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5e14a204 94 JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
20f43d0c 95$where
96
5e14a204 97EOS
98
c20066e6 99 # if no default value is set on the column, or if we can't parse the
100 # default value as a sequence, throw.
101 unless ( defined $seq_expr and $seq_expr =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/i ){
20f43d0c 102 $seq_expr = '' unless defined $seq_expr;
c20066e6 103 $schema = "$schema." if defined $schema && length $schema;
104 $self->throw_exception( "no sequence found for $schema$table.$col, check table definition, "
105 . "or explicitly set the 'sequence' for this column in the "
106 . $source->source_name
107 . " class"
108 );
20f43d0c 109 }
5e14a204 110
111 return $1;
843f8ecd 112}
113
114sub get_autoinc_seq {
ca48cd7d 115 my ($self,$source,$col) = @_;
d4daee7b 116
843f8ecd 117}
118
4f533e8c 119sub sqlt_type {
120 return 'PostgreSQL';
121}
122
45fa8288 123sub datetime_parser_type { return "DateTime::Format::Pg"; }
124
a71859b4 125sub bind_attribute_by_data_type {
126 my ($self,$data_type) = @_;
127
128 my $bind_attributes = {
eda28767 129 bytea => { pg_type => DBD::Pg::PG_BYTEA },
5ba88f68 130 blob => { pg_type => DBD::Pg::PG_BYTEA },
a71859b4 131 };
d4daee7b 132
a71859b4 133 if( defined $bind_attributes->{$data_type} ) {
9fdf90df 134 return $bind_attributes->{$data_type};
a71859b4 135 }
136 else {
137 return;
138 }
139}
140
f04b2839 141sub _sequence_fetch {
142 my ( $self, $type, $seq ) = @_;
9ae966b9 143 my ($id) = $self->_get_dbh->selectrow_array("SELECT nextval('${seq}')");
f04b2839 144 return $id;
145}
146
adb3554a 147sub _svp_begin {
eeb8cfeb 148 my ($self, $name) = @_;
adb3554a 149
9ae966b9 150 $self->_get_dbh->pg_savepoint($name);
adb3554a 151}
152
153sub _svp_release {
eeb8cfeb 154 my ($self, $name) = @_;
adb3554a 155
9ae966b9 156 $self->_get_dbh->pg_release($name);
adb3554a 157}
158
159sub _svp_rollback {
eeb8cfeb 160 my ($self, $name) = @_;
adb3554a 161
9ae966b9 162 $self->_get_dbh->pg_rollback_to($name);
adb3554a 163}
164
843f8ecd 1651;
166
fd159e2a 167__END__
168
75d07914 169=head1 NAME
843f8ecd 170
171DBIx::Class::Storage::DBI::Pg - Automatic primary key class for PostgreSQL
172
173=head1 SYNOPSIS
174
175 # In your table classes
176 __PACKAGE__->load_components(qw/PK::Auto Core/);
177 __PACKAGE__->set_primary_key('id');
178 __PACKAGE__->sequence('mysequence');
179
180=head1 DESCRIPTION
181
182This class implements autoincrements for PostgreSQL.
183
7c0176a1 184=head1 POSTGRESQL SCHEMA SUPPORT
185
4f609014 186This driver supports multiple PostgreSQL schemas, with one caveat: for
6ff1d58c 187performance reasons, data about the search path, sequence names, and
188so forth is queried as needed and CACHED for subsequent uses.
7c0176a1 189
4f609014 190For this reason, once your schema is instantiated, you should not
191change the PostgreSQL schema search path for that schema's database
192connection. If you do, Bad Things may happen.
193
194You should do any necessary manipulation of the search path BEFORE
195instantiating your schema object, or as part of the on_connect_do
196option to connect(), for example:
7c0176a1 197
198 my $schema = My::Schema->connect
199 ( $dsn,$user,$pass,
200 { on_connect_do =>
201 [ 'SET search_path TO myschema, foo, public' ],
202 },
203 );
204
7ff926e6 205=head1 AUTHORS
7c0176a1 206
7ff926e6 207See L<DBIx::Class/CONTRIBUTORS>
843f8ecd 208
209=head1 LICENSE
210
211You may distribute this code under the same terms as Perl itself.
212
213=cut