DBIx-Class-Async
view release on metacpan or search on metacpan
lib/DBIx/Class/Async/Row.pm view on Meta::CPAN
} elsif (defined $old && !defined $value) {
$changed = 1;
} elsif (defined $old && defined $value) {
if ($old_ref ne 'SCALAR' || $new_ref ne 'SCALAR') {
$changed = 1;
} else {
# Safe to use string comparison
if ($old ne $value) {
$changed = 1;
}
}
}
if ($changed) {
$self->{_data}{$col} = $value;
$self->{_dirty}{$col} = 1;
# Clear the inflated cache so the next 'get_column' re-inflates the new data
delete $self->{_inflated}{$col};
# We delete the top-level key so it doesn't "shadow" the new data
delete $self->{$col};
}
return $value;
}
sub set_columns {
my ($self, $values) = @_;
croak("hashref of column-value pairs required")
unless defined $values && ref $values eq 'HASH';
while (my ($column, $value) = each %$values) {
$self->set_column($column, $value);
}
return $self;
}
sub _update_internal_state {
my ($self, $res) = @_;
# 1. Update the internal data hash directly
# This assumes $res contains the updated column data
if (ref $res eq 'HASH') {
foreach my $col (keys %$res) {
$self->{_data}{$col} = $res->{$col};
# Optionally update the shadow variable if necessary
$self->{$col} = $res->{$col} if exists $self->{$col};
}
}
# 2. Update state flags
$self->{_in_storage} = 1;
delete $self->{_dirty}; # Clear dirty flags
return $self;
}
sub update {
my ($self, $values) = @_;
if (!$self->{_schema_instance}) {
# Return a failed future to prevent further cascading failures
return Future->fail("ResultSet is not initialized with a schema instance.");
}
unless ($self->in_storage) {
return Future->fail("Cannot update row: not in storage. Did you mean to call insert or update_or_insert?");
}
if ($values) {
croak("Usage: update({ col => val })") unless ref $values eq 'HASH';
foreach my $col (keys %$values) {
$self->set_column($col, $values->{$col});
}
}
return $self->update_or_insert->on_done(sub {
# 1. Get the source and necessary metadata
my $source = $self->result_source;
my $schema = $source->schema;
my $source_name = $source->source_name;
# 2. Explicitly create the custom ResultSet object using your new() method
my $rs = DBIx::Class::Async::ResultSet->new(
schema_instance => $schema,
source_name => $source_name,
async_db => $self->{_async_db},
);
# 3. Construct the PK condition
my @pk_cols = $source->primary_columns;
my %pk_cond = map { $_ => $self->get_column($_) } @pk_cols;
# 4. Now we can safely call _generate_cache_key
my $cache_key = $rs->_generate_cache_key(0, \%pk_cond);
$rs->clear_cache($cache_key);
return $self->_update_internal_state($rs);
});
}
sub update_or_insert {
my ($self, $data) = @_;
my $async_db = $self->{_async_db};
my $source_name = $self->{_source_name};
my $source = $self->result_source;
my ($pk_col) = $source->primary_columns;
# 1. Apply changes to the object
if ($data && ref $data eq 'HASH') {
foreach my $col (keys %$data) {
$self->set_column($col, $data->{$col});
}
}
my $is_update = $self->in_storage;
# 2. Prepare Payload
my %raw_payload = $is_update ? $self->get_dirty_columns : %{ $self->{_data} // {} };
my %to_save;
foreach my $col (keys %raw_payload) {
# If it's not in _inflated, fall back to the raw value in %raw_payload.
my $val = exists $self->{_inflated}{$col} ? $self->{_inflated}{$col} : $raw_payload{$col};
my $info = $source->column_info($col);
# If a deflate handler exists and we have a reference, turn it into a string
if ($info && $info->{deflate} && defined $val && ref $val) {
$val = $info->{deflate}->($val, $self);
}
$to_save{$col} = $val;
}
# 3. Success handler
my $on_success = sub {
my ($res) = @_;
# We check if it's an object FIRST before asking what kind of object it is.
if (blessed($res) && $res->isa('DBIx::Class::Exception')) {
return Future->fail($res->msg, 'db_error');
}
# Also check for the HASH-style error envelope which we saw in your logs
if (ref $res eq 'HASH' && ($res->{error} || $res->{__error})) {
my $err = $res->{error} // $res->{__error};
return Future->fail($err, 'db_error');
}
# Normalise data source
my $final_data;
if (ref $res && ref $res eq 'HASH') {
$final_data = $res;
}
elsif (ref $res && eval { $res->can('get_columns') }) {
# Handle case where $res is another Row object
my %cols = $res->get_columns;
$final_data = \%cols;
}
else {
# Scalar result (ID) or fallback
( run in 0.502 second using v1.01-cache-2.11-cpan-39bf76dae61 )