diff --git a/lib/DBIx/Class/ResultSet/SQLA2Support.pm b/lib/DBIx/Class/ResultSet/SQLA2Support.pm index c3978c3..c8c6635 100644 --- a/lib/DBIx/Class/ResultSet/SQLA2Support.pm +++ b/lib/DBIx/Class/ResultSet/SQLA2Support.pm @@ -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 }); } diff --git a/t/case_expr.t b/t/case_expr.t index 988b17d..9a3b5aa 100644 --- a/t/case_expr.t +++ b/t/case_expr.t @@ -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'; }; diff --git a/t/upsert.t b/t/upsert.t index 937ed05..0cfeaa5 100644 --- a/t/upsert.t +++ b/t/upsert.t @@ -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!'; };