Skip to content

Commit

Permalink
feat(upsert): add override support, and RETURNING for those
Browse files Browse the repository at this point in the history
  • Loading branch information
rabbiveesh committed Jun 14, 2024
1 parent 5e4fc27 commit ccd7f8c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 46 deletions.
25 changes: 21 additions & 4 deletions lib/DBIx/Class/ResultSet/SQLA2Support.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,36 @@ package DBIx::Class::ResultSet::SQLA2Support;
use strict;
use warnings;
use parent 'DBIx::Class::ResultSet';
use List::Util 'pairmap';

sub upsert {
my ($self, $to_insert) = @_;
my ($self, $to_insert, %overrides) = @_;
my $sqla2_passthru = delete $to_insert->{-sqla2} || {};

# generate our on_conflict clause
my $to_upsert = {%$to_insert};
my @pks = $self->result_source->primary_columns;
my $source = $self->result_source;
my @pks = $source->primary_columns;
delete @$to_upsert{@pks};

# evil handling for RETURNING, b/c DBIC doesn't give us a place to do it properly.
# Basically force each input value to be a ref, and update the column config to use
# RETURNING, thus ensuring we get RETURNING handling
for my $col (keys %overrides) {
next if ref $to_insert->{$col};
$to_insert->{$col} = \[ '?' => $to_insert->{$col} ];
}
local $source->{_columns}
= { pairmap { $a => { %$b, $overrides{$a} ? (retrieve_on_insert => 1) : () } } $source->{_columns}->%* };

$sqla2_passthru->{on_conflict} = {
-target => \@pks,
# use excluded so we don't mess up inflated values
-set => { map +($_ => { -ident => "excluded.$_" }), keys %$to_upsert }
-set => {
# unroll all upserty columns
(map +($_ => { -ident => "excluded.$_" }), keys %$to_upsert),
# and allow overrides from the client
%overrides
}
};
$self->create({ $to_insert->%*, -sqla2 => $sqla2_passthru });
}
Expand Down
81 changes: 47 additions & 34 deletions t/case_expr.t
Original file line number Diff line number Diff line change
Expand Up @@ -24,47 +24,60 @@ $schema->resultset('Artist')->populate([
]);

subtest 'using a CASE expression' => sub {
my @oddity = $schema->resultset('Album')->search(undef, {
my @oddity = $schema->resultset('Album')->search(
undef,
{
'columns' => [
'rank',
'title',
{ oddity => \{ -case => [
{ if => { -mod => [ 'rank', 2 ] }, then => 'Quite Odd' },
'Even'
] ,
-as => 'oddity'}
}],
order_by => { -asc => 'rank'}
})->all;
'rank', 'title',
{
oddity => \{
-case => [ { if => { -mod => [ 'rank', 2 ] }, then => 'Quite Odd' }, 'Even' ],
-as => 'oddity'
}
}
],
order_by => { -asc => 'rank' }
}
)->all;

is_deeply \@oddity, [
{ title => 'Second Coming', rank => 1, oddity => 'Quite Odd'},
{ title => 'Portishead', rank => 2, oddity => 'Even'},
{ title => 'Dummy', rank => 3, oddity => 'Quite Odd'},
{ title => 'Third', rank => 4, oddity => 'Even'},
], 'got expected result';
is_deeply \@oddity,
[
{ title => 'Second Coming', rank => 1, oddity => 'Quite Odd' },
{ title => 'Portishead', rank => 2, oddity => 'Even' },
{ title => 'Dummy', rank => 3, oddity => 'Quite Odd' },
{ title => 'Third', rank => 4, oddity => 'Even' },
],
'got expected result';
};

subtest 'passes through the double arrayref syntax' => sub {
my @oddity = $schema->resultset('Album')->search(undef, {
my @oddity = $schema->resultset('Album')->search(
undef,
{
'columns' => [
'rank',
'title',
{ oddity => \{ -case => [
[{ -func => [ 'mod', 'rank', 2 ] } => { -bind => [ undef, 'Quite Odd' ] }],
{ -bind => [ undef, 'Even']}
] ,
-as => 'oddity'}
}],
order_by => { -asc => 'rank'}
})->all;
'rank', 'title',
{
oddity => \{
-case => [
[ { -func => [ 'mod', 'rank', 2 ] } => { -bind => [ undef, 'Quite Odd' ] } ],
{ -bind => [ undef, 'Even' ] }
],
-as => 'oddity'
}
}
],
order_by => { -asc => 'rank' }
}
)->all;

is_deeply \@oddity, [
{ title => 'Second Coming', rank => 1, oddity => 'Quite Odd'},
{ title => 'Portishead', rank => 2, oddity => 'Even'},
{ title => 'Dummy', rank => 3, oddity => 'Quite Odd'},
{ title => 'Third', rank => 4, oddity => 'Even'},
], 'got expected result';
is_deeply \@oddity,
[
{ title => 'Second Coming', rank => 1, oddity => 'Quite Odd' },
{ title => 'Portishead', rank => 2, oddity => 'Even' },
{ title => 'Dummy', rank => 3, oddity => 'Quite Odd' },
{ title => 'Third', rank => 4, oddity => 'Even' },
],
'got expected result';

};

Expand Down
12 changes: 4 additions & 8 deletions t/upsert.t
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,13 @@ subtest 'do nothing on populate' => sub {

subtest 'update' => sub {
my $updated = $schema->resultset('Artist')
->create({
artistid => 3,
name => 'LSD',
-sqla2 => {
on_conflict => { artistid => { name => \"name || ' ' || excluded.name" } },
}
});
->upsert({ artistid => 3, name => 'LSD' }, name => \"name || ' ' || excluded.name");
is $updated->name, 'LSG LSD', 'holycrud, our fancy RETURNING werkz';
is $schema->resultset('Artist')->find(3)->{name}, 'LSG LSD', 'a hash sets';

$schema->resultset('Artist')
my $returned = $schema->resultset('Artist')
->upsert({ artistid => 3, name => 'LSB' });
is $returned->name, 'LSB', 'returned row is properly updated';
is $schema->resultset('Artist')->find(3)->{name}, 'LSB', '-upsert is a shortcut!';
};

Expand Down

0 comments on commit ccd7f8c

Please sign in to comment.