Data-ObjectDriver
view release on metacpan or search on metacpan
lib/Data/ObjectDriver/Driver/DBI.pm view on Meta::CPAN
## the new record; otherwise, we assume that the DB is using an
## auto-increment column of some sort, so we don't specify an ID
## at all.
my $pk = $obj->primary_key_tuple;
if(my $generated = $driver->generate_pk($obj)) {
## The ID is the only thing we *are* allowed to change on
## the original object, so copy it back.
$orig_obj->$_($obj->$_) for @$pk;
} else {
## Filter the undefined key fields out of the columns to include
## in the query, so that we don't specify them in the query.
my %pk = map { $_ => 1 } @$pk;
$cols = [ grep !$pk{$_} || defined $obj->$_(), @$cols ];
}
}
my $tbl = $driver->table_for($obj);
my $sql = "$INSERT_OR_REPLACE INTO $tbl\n";
my $dbd = $driver->dbd;
$sql .= '(' . join(', ',
map { $dbd->db_column_name($tbl, $_) }
@$cols) .
')' . "\n" .
'VALUES (' . join(', ', ('?') x @$cols) . ')' . "\n";
my $dbh = $driver->rw_handle($obj->properties->{db});
$driver->start_query($sql, $obj->{column_values});
my $sth = $driver->_prepare_cached($dbh, $sql);
my $i = 1;
my $col_defs = $obj->properties->{column_defs};
for my $col (@$cols) {
my $val = $obj->column($col);
my $type = $col_defs->{$col} || 'char';
my $attr = $dbd->bind_param_attributes($type, $obj, $col);
$sth->bind_param($i++, $val, $attr);
}
eval { $sth->execute };
die "Failed to execute $sql with ".join(", ",@$cols).": $@" if $@;
## Now, if we didn't have an object ID, we need to grab the
## newly-assigned ID.
if (!$obj->is_pkless && ! $obj->has_primary_key) {
my $pk = $obj->primary_key_tuple; ## but do that only for relation that aren't PK-less
my $id_col = $pk->[0]; # XXX are we sure we will always use '0' ?
my $id = $dbd->fetch_id(ref($obj), $dbh, $sth, $driver);
$obj->$id_col($id);
## The ID is the only thing we *are* allowed to change on
## the original object.
$orig_obj->$id_col($id);
}
_close_sth($sth);
$driver->end_query($sth);
$obj->call_trigger('post_save', $orig_obj);
$obj->call_trigger('post_insert', $orig_obj);
$orig_obj->{__is_stored} = 1;
$orig_obj->{changed_cols} = {};
1;
}
sub update {
my $driver = shift;
my($orig_obj, $terms) = @_;
if ($Data::ObjectDriver::RESTRICT_IO) {
use Data::Dumper;
die "Attempted DBI I/O while in restricted mode: _update() " . Dumper($terms);
}
## Use a duplicate so the pre_save trigger can modify it.
my $obj = $orig_obj->clone_all;
$obj->call_trigger('pre_save', $orig_obj);
$obj->call_trigger('pre_update', $orig_obj);
my $cols = $obj->column_names;
my @changed_cols = $obj->changed_cols;
## If there's no updated columns, update() is no-op
## but we should call post_* triggers
unless (@changed_cols) {
$obj->call_trigger('post_save', $orig_obj);
$obj->call_trigger('post_update', $orig_obj);
return 1;
}
my $tbl = $driver->table_for($obj);
my $sql = "UPDATE $tbl SET\n";
my $dbd = $driver->dbd;
$sql .= join(', ',
map { $dbd->db_column_name($tbl, $_) . " = ?" }
@changed_cols) . "\n";
my $stmt = $driver->prepare_statement(ref($obj), {
%{ $obj->primary_key_to_terms },
%{ $terms || {} }
});
$sql .= $stmt->as_sql_where;
my $dbh = $driver->rw_handle($obj->properties->{db});
$driver->start_query($sql, $obj->{column_values});
my $sth = $driver->_prepare_cached($dbh, $sql);
my $i = 1;
my $col_defs = $obj->properties->{column_defs};
for my $col (@changed_cols) {
my $val = $obj->column($col);
my $type = $col_defs->{$col} || 'char';
my $attr = $dbd->bind_param_attributes($type, $obj, $col);
$sth->bind_param($i++, $val, $attr);
}
## Bind the primary key value(s).
for my $val (@{ $stmt->{bind} }) {
$sth->bind_param($i++, $val);
}
my $rows = $sth->execute;
_close_sth($sth);
$driver->end_query($sth);
$obj->call_trigger('post_save', $orig_obj);
$obj->call_trigger('post_update', $orig_obj);
( run in 0.981 second using v1.01-cache-2.11-cpan-39bf76dae61 )