__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks');
+# Each of these methods need _determine_driver called before itself
+# in order to function reliably. This is a purely DRY optimization
+my @rdbms_specific_methods = qw/
+ sqlt_type
+ build_datetime_parser
+ datetime_parser_type
+
+ insert
+ insert_bulk
+ update
+ delete
+ select
+ select_single
+/;
+
+for my $meth (@rdbms_specific_methods) {
+
+ my $orig = __PACKAGE__->can ($meth)
+ or next;
+
+ no strict qw/refs/;
+ no warnings qw/redefine/;
+ *{__PACKAGE__ ."::$meth"} = sub {
+ if (not $_[0]->_driver_determined) {
+ $_[0]->_determine_driver;
+ goto $_[0]->can($meth);
+ }
+ $orig->(@_);
+ };
+}
+
+
=head1 NAME
DBIx::Class::Storage::DBI - DBI storage handler
# Storage subclasses should override this
sub with_deferred_fk_checks {
my ($self, $sub) = @_;
-
$sub->();
}
sub txn_commit {
my $self = shift;
if ($self->{transaction_depth} == 1) {
- my $dbh = $self->_dbh;
$self->debugobj->txn_commit()
if ($self->debug);
$self->_dbh_commit;
sub _dbh_commit {
my $self = shift;
- $self->_dbh->commit;
+ my $dbh = $self->_dbh
+ or $self->throw_exception('cannot COMMIT on a disconnected handle');
+ $dbh->commit;
}
sub txn_rollback {
sub _dbh_rollback {
my $self = shift;
- $self->_dbh->rollback;
+ my $dbh = $self->_dbh
+ or $self->throw_exception('cannot ROLLBACK on a disconnected handle');
+ $dbh->rollback;
}
# This used to be the top-half of _execute. It was split out to make it
sub insert {
my ($self, $source, $to_insert) = @_;
-# redispatch to insert method of storage we reblessed into, if necessary
- if (not $self->_driver_determined) {
- $self->_determine_driver;
- goto $self->can('insert');
- }
-
my $ident = $source->from;
my $bind_attributes = $self->source_bind_attributes($source);
sub insert_bulk {
my ($self, $source, $cols, $data) = @_;
-# redispatch to insert_bulk method of storage we reblessed into, if necessary
- if (not $self->_driver_determined) {
- $self->_determine_driver;
- goto $self->can('insert_bulk');
- }
-
my %colvalues;
@colvalues{@$cols} = (0..$#$cols);
next unless ref $first_val eq 'SCALAR';
$colvalues{ $cols->[$i] } = $first_val;
-## This is probably unnecessary since $rs->populate only looks at the first
-## slice anyway.
-# if (grep {
-# ref $_ eq 'SCALAR' && $$_ eq $$first_val
-# } map $data->[$_][$i], (1..$#$data)) == (@$data - 1);
}
- # check for bad data
+ # check for bad data and stringify stringifiable objects
my $bad_slice = sub {
my ($msg, $col_idx, $slice_idx) = @_;
$self->throw_exception(sprintf "%s for column '%s' in populate slice:\n%s",
$msg,
$cols->[$col_idx],
- Data::Dumper::Concise::Dumper({
- map { $cols->[$_] => $data->[$slice_idx][$_] } (0 .. $#$cols)
- }),
+ do {
+ local $Data::Dumper::Maxdepth = 1; # don't dump objects, if any
+ Data::Dumper::Concise::Dumper({
+ map { $cols->[$_] => $data->[$slice_idx][$_] } (0 .. $#$cols)
+ }),
+ }
);
};
}
}
elsif (my $reftype = ref $val) {
- $bad_slice->("$reftype reference found where bind expected",
- $col_idx, $datum_idx);
+ require overload;
+ if (overload::Method($val, '""')) {
+ $datum->[$col_idx] = "".$val;
+ }
+ else {
+ $bad_slice->("$reftype reference found where bind expected",
+ $col_idx, $datum_idx);
+ }
}
}
}
}
sub update {
- my ($self, $source, @args) = @_;
-
-# redispatch to update method of storage we reblessed into, if necessary
- if (not $self->_driver_determined) {
- $self->_determine_driver;
- goto $self->can('update');
- }
+ my ($self, $source, $data, $where, @args) = @_;
- my $bind_attributes = $self->source_bind_attributes($source);
+ my $bind_attrs = $self->source_bind_attributes($source);
+ $where = $self->_strip_cond_qualifiers ($where);
- return $self->_execute('update' => [], $source, $bind_attributes, @args);
+ return $self->_execute('update' => [], $source, $bind_attrs, $data, $where, @args);
}
sub delete {
- my $self = shift @_;
- my $source = shift @_;
- $self->_determine_driver;
+ my ($self, $source, $where, @args) = @_;
+
my $bind_attrs = $self->source_bind_attributes($source);
+ $where = $self->_strip_cond_qualifiers ($where);
+
+ return $self->_execute('delete' => [], $source, $bind_attrs, $where, @args);
+}
+
+sub _strip_cond_qualifiers {
+ my ($self, $where) = @_;
- return $self->_execute('delete' => [], $source, $bind_attrs, @_);
+ my $sqlmaker = $self->sql_maker;
+ my ($sql, @bind) = $sqlmaker->_recurse_where($where);
+ return undef unless $sql;
+
+ my ($qquot, $qsep) = map { quotemeta $_ } ( ($sqlmaker->quote_char||''), ($sqlmaker->name_sep||'.') );
+ $sql =~ s/ (?: $qquot [\w\-]+ $qquot | [\w\-]+ ) $qsep //gx;
+
+ return \[$sql, @bind];
}
# We were sent here because the $rs contains a complex search
=cut
sub sqlt_type {
- my ($self) = @_;
-
- if (not $self->_driver_determined) {
- $self->_determine_driver;
- goto $self->can ('sqlt_type');
- }
-
- $self->_get_dbh->{Driver}->{Name};
+ shift->_get_dbh->{Driver}->{Name};
}
=head2 bind_attribute_by_data_type
parser => 'SQL::Translator::Parser::DBIx::Class',
data => $schema,
);
- return $tr->translate;
+
+ my $ret = $tr->translate
+ or $self->throw_exception( 'Unable to produce deployment statements: ' . $tr->error);
+
+ return $ret;
}
sub deploy {
=cut
sub build_datetime_parser {
- if (not $_[0]->_driver_determined) {
- $_[0]->_determine_driver;
- goto $_[0]->can('build_datetime_parser');
- }
-
my $self = shift;
my $type = $self->datetime_parser_type(@_);
$self->ensure_class_loaded ($type);