use namespace::clean w/ Try::Tiny
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / InterBase.pm
CommitLineData
88a8d0fa 1package DBIx::Class::Storage::DBI::InterBase;
2
88a8d0fa 3use strict;
4use warnings;
bab40dee 5use base qw/DBIx::Class::Storage::DBI/;
88a8d0fa 6use mro 'c3';
88a8d0fa 7use List::Util();
ed7ab0f4 8use Try::Tiny;
fd323bf1 9use namespace::clean;
88a8d0fa 10
90489c23 11=head1 NAME
12
13DBIx::Class::Storage::DBI::InterBase - Driver for the Firebird RDBMS
14
15=head1 DESCRIPTION
16
95570280 17This class implements autoincrements for Firebird using C<RETURNING> as well as
18L<auto_nextval|DBIx::Class::ResultSource/auto_nextval> sets the limit dialect to
19C<FIRST X SKIP X> and provides L<DBIx::Class::InflateColumn::DateTime> support.
90489c23 20
dd2109ee 21You need to use either the
22L<disable_sth_caching|DBIx::Class::Storage::DBI/disable_sth_caching> option or
23L</connect_call_use_softcommit> (see L</CAVEATS>) for your code to function
47ec67c3 24correctly with this driver. Otherwise you will likely get bizarre error messages
d1fc96c7 25such as C<no statement executing>. The alternative is to use the
26L<ODBC|DBIx::Class::Storage::DBI::ODBC::Firebird> driver, which is more suitable
27for long running processes such as under L<Catalyst>.
90489c23 28
c5827074 29To turn on L<DBIx::Class::InflateColumn::DateTime> support, see
30L</connect_call_datetime_setup>.
32323fc2 31
90489c23 32=cut
33
227d8366 34sub _supports_insert_returning { 1 }
bab40dee 35
e1958268 36sub _sequence_fetch {
37 my ($self, $nextval, $sequence) = @_;
38
39 if ($nextval ne 'nextval') {
40 $self->throw_exception("Can only fetch 'nextval' for a sequence");
41 }
42
43 $self->throw_exception('No sequence to fetch') unless $sequence;
fd323bf1 44
e1958268 45 my ($val) = $self->_get_dbh->selectrow_array(
46'SELECT GEN_ID(' . $self->sql_maker->_quote($sequence) .
47', 1) FROM rdb$database');
48
49 return $val;
fd323bf1 50}
e1958268 51
52sub _dbh_get_autoinc_seq {
53 my ($self, $dbh, $source, $col) = @_;
54
55 my $table_name = $source->from;
56 $table_name = $$table_name if ref $table_name;
57 $table_name = $self->sql_maker->quote_char ? $table_name : uc($table_name);
58
59 local $dbh->{LongReadLen} = 100000;
60 local $dbh->{LongTruncOk} = 1;
61
62 my $sth = $dbh->prepare(<<'EOF');
63SELECT t.rdb$trigger_source
64FROM rdb$triggers t
65WHERE t.rdb$relation_name = ?
66AND t.rdb$system_flag = 0 -- user defined
67AND t.rdb$trigger_type = 1 -- BEFORE INSERT
68EOF
69 $sth->execute($table_name);
70
71 while (my ($trigger) = $sth->fetchrow_array) {
72 my @trig_cols = map {
73 /^"([^"]+)/ ? $1 : uc($1)
74 } $trigger =~ /new\.("?\w+"?)/ig;
75
76 my ($quoted, $generator) = $trigger =~
77/(?:gen_id\s* \( \s* |next \s* value \s* for \s*)(")?(\w+)/ix;
78
79 if ($generator) {
80 $generator = uc $generator unless $quoted;
81
82 return $generator
83 if List::Util::first {
84 $self->sql_maker->quote_char ? ($_ eq $col) : (uc($_) eq uc($col))
85 } @trig_cols;
86 }
87 }
88
89 return undef;
90}
91
145b2a3d 92# this sub stolen from DB2
88a8d0fa 93
145b2a3d 94sub _sql_maker_opts {
95 my ( $self, $opts ) = @_;
96
97 if ( $opts ) {
98 $self->{_sql_maker_opts} = { %$opts };
99 }
100
101 return { limit_dialect => 'FirstSkip', %{$self->{_sql_maker_opts}||{}} };
102}
103
32323fc2 104sub _svp_begin {
105 my ($self, $name) = @_;
106
107 $self->_get_dbh->do("SAVEPOINT $name");
108}
109
110sub _svp_release {
111 my ($self, $name) = @_;
112
113 $self->_get_dbh->do("RELEASE SAVEPOINT $name");
114}
115
116sub _svp_rollback {
117 my ($self, $name) = @_;
118
119 $self->_get_dbh->do("ROLLBACK TO SAVEPOINT $name")
120}
121
122sub _ping {
123 my $self = shift;
124
125 my $dbh = $self->_dbh or return 0;
126
127 local $dbh->{RaiseError} = 1;
ecdf1ac8 128 local $dbh->{PrintError} = 0;
32323fc2 129
52b420dd 130 return try {
32323fc2 131 $dbh->do('select 1 from rdb$database');
52b420dd 132 1;
ed7ab0f4 133 } catch {
52b420dd 134 0;
32323fc2 135 };
32323fc2 136}
137
9633951d 138# We want dialect 3 for new features and quoting to work, DBD::InterBase uses
139# dialect 1 (interbase compat) by default.
140sub _init {
141 my $self = shift;
142 $self->_set_sql_dialect(3);
143}
144
145sub _set_sql_dialect {
146 my $self = shift;
147 my $val = shift || 3;
148
149 my $dsn = $self->_dbi_connect_info->[0];
150
151 return if ref($dsn) eq 'CODE';
152
153 if ($dsn !~ /ib_dialect=/) {
154 $self->_dbi_connect_info->[0] = "$dsn;ib_dialect=$val";
155 my $connected = defined $self->_dbh;
156 $self->disconnect;
157 $self->ensure_connected if $connected;
158 }
159}
160
6d766626 161sub _get_server_version {
8f0a1e07 162 my $self = shift;
163
b22700c4 164 return $self->next::method(@_) if ref $self ne __PACKAGE__;
165
8f0a1e07 166 local $SIG{__WARN__} = sub {}; # silence warning due to bug in DBD::InterBase
167
168 return $self->next::method(@_);
169}
170
dd2109ee 171=head2 connect_call_use_softcommit
172
173Used as:
174
175 on_connect_call => 'use_softcommit'
176
177In L<connect_info|DBIx::Class::Storage::DBI/connect_info> to set the
178L<DBD::InterBase> C<ib_softcommit> option.
179
180You need either this option or C<< disable_sth_caching => 1 >> for
47ec67c3 181L<DBIx::Class> code to function correctly (otherwise you may get C<no statement
d1fc96c7 182executing> errors.) Or use the L<ODBC|DBIx::Class::Storage::DBI::ODBC::Firebird>
183driver.
dd2109ee 184
185The downside of using this option is that your process will B<NOT> see UPDATEs,
186INSERTs and DELETEs from other processes for already open statements.
187
188=cut
189
190sub connect_call_use_softcommit {
a499b173 191 my $self = shift;
192
193 $self->_dbh->{ib_softcommit} = 1;
a499b173 194}
195
32323fc2 196=head2 connect_call_datetime_setup
9cd0b325 197
32323fc2 198Used as:
9cd0b325 199
32323fc2 200 on_connect_call => 'datetime_setup'
201
f0f8ac86 202In L<connect_info|DBIx::Class::Storage::DBI/connect_info> to set the date and
203timestamp formats using:
32323fc2 204
205 $dbh->{ib_time_all} = 'ISO';
206
207See L<DBD::InterBase> for more details.
208
209The C<TIMESTAMP> data type supports up to 4 digits after the decimal point for
210second precision. The full precision is used.
211
c5827074 212The C<DATE> data type stores the date portion only, and it B<MUST> be declared
213with:
214
215 data_type => 'date'
216
217in your Result class.
218
219Timestamp columns can be declared with either C<datetime> or C<timestamp>.
220
32323fc2 221You will need the L<DateTime::Format::Strptime> module for inflation to work.
222
223For L<DBIx::Class::Storage::DBI::ODBC::Firebird>, this is a noop and sub-second
224precision is not currently available.
225
226=cut
227
228sub connect_call_datetime_setup {
229 my $self = shift;
230
231 $self->_get_dbh->{ib_time_all} = 'ISO';
9cd0b325 232}
233
c5827074 234sub datetime_parser_type {
235 'DBIx::Class::Storage::DBI::InterBase::DateTime::Format'
236}
32323fc2 237
c5827074 238package # hide from PAUSE
239 DBIx::Class::Storage::DBI::InterBase::DateTime::Format;
32323fc2 240
c5827074 241my $timestamp_format = '%Y-%m-%d %H:%M:%S.%4N'; # %F %T
242my $date_format = '%Y-%m-%d';
243
244my ($timestamp_parser, $date_parser);
245
246sub parse_datetime {
247 shift;
248 require DateTime::Format::Strptime;
249 $timestamp_parser ||= DateTime::Format::Strptime->new(
250 pattern => $timestamp_format,
251 on_error => 'croak',
252 );
253 return $timestamp_parser->parse_datetime(shift);
254}
255
256sub format_datetime {
257 shift;
258 require DateTime::Format::Strptime;
259 $timestamp_parser ||= DateTime::Format::Strptime->new(
260 pattern => $timestamp_format,
261 on_error => 'croak',
262 );
263 return $timestamp_parser->format_datetime(shift);
264}
265
266sub parse_date {
267 shift;
268 require DateTime::Format::Strptime;
269 $date_parser ||= DateTime::Format::Strptime->new(
270 pattern => $date_format,
271 on_error => 'croak',
272 );
273 return $date_parser->parse_datetime(shift);
274}
275
276sub format_date {
277 shift;
278 require DateTime::Format::Strptime;
279 $date_parser ||= DateTime::Format::Strptime->new(
280 pattern => $date_format,
32323fc2 281 on_error => 'croak',
282 );
c5827074 283 return $date_parser->format_datetime(shift);
9cd0b325 284}
285
145b2a3d 2861;
90489c23 287
288=head1 CAVEATS
289
290=over 4
291
292=item *
293
dd2109ee 294with L</connect_call_use_softcommit>, you will not be able to see changes made
295to data in other processes. If this is an issue, use
47ec67c3 296L<disable_sth_caching|DBIx::Class::Storage::DBI/disable_sth_caching> as a
297workaround for the C<no statement executing> errors, this of course adversely
298affects performance.
dd2109ee 299
d1fc96c7 300Alternately, use the L<ODBC|DBIx::Class::Storage::DBI::ODBC::Firebird> driver.
301
dd2109ee 302=item *
303
e1958268 304C<last_insert_id> support by default only works for Firebird versions 2 or
305greater, L<auto_nextval|DBIx::Class::ResultSource/auto_nextval> however should
306work with earlier versions.
90489c23 307
47ec67c3 308=item *
309
d1fc96c7 310Sub-second precision for TIMESTAMPs is not currently available when using the
311L<ODBC|DBIx::Class::Storage::DBI::ODBC::Firebird> driver.
47ec67c3 312
90489c23 313=back
314
315=head1 AUTHOR
316
317See L<DBIx::Class/AUTHOR> and L<DBIx::Class/CONTRIBUTORS>.
318
319=head1 LICENSE
320
321You may distribute this code under the same terms as Perl itself.
322
323=cut