sub _dbh_begin_work {
my $self = shift;
- # being here implies we have AutoCommit => 1
- # if the user is utilizing txn_do - good for
- # him, otherwise we need to ensure that the
- # $dbh is healthy on BEGIN
- my $dbh_method = $self->{_in_dbh_do} ? '_dbh' : 'dbh';
- $self->$dbh_method->begin_work;
+ if ($self->{_in_dbh_do}) {
+ $self->_dbh->begin_work;
+ } else {
+ $self->dbh_do(sub { $_[1]->begin_work });
+ }
}
sub txn_commit {
foreach my $data (@$bound) {
$data = ''.$data if ref $data;
- $data = $self->_prep_bind_value($datatype, $data)
+ $data = $self->_prep_interpolated_value($datatype, $data)
if $datatype;
$data = $self->_dbh->quote($data)
return 1;
}
-=head2 _prep_bind_value
+=head2 _prep_interpolated_value
Given a datatype and the value to be inserted directly into a SQL query, returns
the necessary string to represent that value (by e.g. adding a '$' sign)
=cut
-sub _prep_bind_value {
+sub _prep_interpolated_value {
#my ($self, $datatype, $value) = @_;
return $_[2];
}
use List::Util ();
__PACKAGE__->mk_group_accessors('simple' =>
- qw/_identity _blob_log_on_update insert_txn/
+ qw/_identity _blob_log_on_update insert_txn _extra_dbh/
);
=head1 NAME
With this driver there is unfortunately no way to get the C<last_insert_id>
without doing a C<SELECT MAX(col)>. This is done safely in a transaction
(locking the table.) The transaction can be turned off if concurrency is not an
-issue, see L<DBIx::Class::Storage::DBI::Sybase/connect_call_unsafe_insert>.
+issue, or you don't need the C<IDENTITY> value, see
+L<DBIx::Class::Storage::DBI::Sybase/connect_call_unsafe_insert>.
But your queries will be cached.
$self->_dbh->do('SET CHAINED ON');
}
}
+
+# for insert transactions
+ $self->_extra_dbh($self->_connect(@{ $self->_dbi_connect_info }));
+ $self->_extra_dbh->{AutoCommit} = 1;
}
=head2 connect_call_blob_setup
my $updated_cols = do {
if ($need_last_insert_id && $self->insert_txn &&
(not $self->{transaction_depth})) {
+ local $self->{_dbh} = $self->_extra_dbh;
my $guard = $self->txn_scope_guard;
my $upd_cols = $self->next::method (@_);
$guard->commit;
Inserts or updates of TEXT/IMAGE columns will B<NOT> work with FreeTDS.
+=head1 TRANSACTIONS
+
+Due to limitations of the TDS protocol, L<DBD::Sybase>, or both; you cannot
+begin a transaction while there are active cursors. An active cursor is, for
+example, a L<ResultSet|DBIx::Class::ResultSet> that has been executed using
+C<next> or C<first> but has not been exhausted or
+L<DBIx::Class::ResultSet/reset>.
+
+To get around this problem, use L<DBIx::Class::ResultSet/all> for smaller
+ResultSets, and/or put the active cursors you will need in the scope of the
+transaction.
+
+Transactions done for inserts in C<AutoCommit> mode when placeholders are in use
+are not affected, as they are executed on a separate connection.
+
=head1 MAXIMUM CONNECTIONS
The TDS protocol makes separate connections to the server for active statements
return $self->next::method(@_);
}
-sub _prep_bind_value {
+sub _prep_interpolated_value {
my ($self, $type, $value) = @_;
if ($type =~ /money/i && defined $value) {
use Test::Exception;
use lib qw(t/lib);
use DBICTest;
+use DBIx::Class::Storage::DBI::Sybase;
+use DBIx::Class::Storage::DBI::Sybase::NoBindVars;
my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_SYBASE_${_}" } qw/DSN USER PASS/};
-my $TESTS = 37 + 2;
+my $TESTS = 38 + 2;
if (not ($dsn && $user)) {
plan skip_all =>
});
}
+my $ping_count = 0;
+{
+ my $ping = DBIx::Class::Storage::DBI::Sybase->can('_ping');
+ *DBIx::Class::Storage::DBI::Sybase::_ping = sub {
+ $ping_count++;
+ goto $ping;
+ };
+}
+
for my $storage_type (@storage_types) {
$storage_idx++;
SQL
});
+# First, we'll open a cursor to test insert transactions when there's an active
+# cursor.
+ SKIP: {
+ skip 'not testing insert with active cursor unless using insert_txn', 1
+ unless $schema->storage->insert_txn;
+
+ my $artist_rs = $schema->resultset('Artist');
+ $artist_rs->first;
+ lives_ok {
+ my $row = $schema->resultset('Money')->create({ amount => 100 });
+ $row->delete;
+ } 'inserted a row with an active cursor';
+ }
+
+# Now test money values.
my $rs = $schema->resultset('Money');
my $row;
eval { $dbh->do("DROP TABLE $_") }
for qw/artist bindtype_test money_test/;
}
+ diag "ping count was $ping_count" unless $ping_count == 0;
}