File Coverage

blib/lib/DBIx/Class/ResultSet/SQLA2Support.pm
Criterion Covered Total %
statement 56 57 98.2
branch 7 10 70.0
condition 1 2 50.0
subroutine 11 11 100.0
pod 3 3 100.0
total 78 83 93.9


line stmt bran cond sub pod time code
1             package DBIx::Class::ResultSet::SQLA2Support;
2 5     5   73350 use strict;
  5         16  
  5         203  
3 5     5   24 use warnings;
  5         11  
  5         309  
4 5     5   2958 use experimental 'signatures';
  5         18407  
  5         53  
5 5     5   890 use parent 'DBIx::Class::ResultSet';
  5         10  
  5         52  
6 5     5   490 use List::Util 'pairmap';
  5         26  
  5         409  
7 5     5   29 use Carp 'croak';
  5         10  
  5         3838  
8              
9 3     3   7 my sub _create_upsert_clause ($self, $columns_ary, $overrides) {
  3         6  
  3         6  
  3         5  
  3         5  
10 3         26 my %pks = map +($_ => 1), $self->result_source->primary_columns;
11             return +{
12             -target => [ keys %pks ],
13             -set => {
14             # create a hash of non-pk cols w/ the value excluded.$_, which does the upsert
15 3         107 (map +($_ => { -ident => "excluded.$_" }), grep !$pks{$_}, $columns_ary->@*),
16             # and allow overrides from the caller
17             $overrides->%*
18             }
19             };
20             }
21              
22 2     2 1 11721 sub upsert ($self, $to_insert, $overrides = {}) {
  2         6  
  2         6  
  2         4  
  2         6  
23             # in case there are other passthroughs, you never know
24 2   50     17 my $sqla2_passthru = delete $to_insert->{-sqla2} || {};
25              
26             # evil handling for RETURNING, b/c DBIC doesn't give us a place to do it properly.
27             # Basically force each input value to be a ref, and update the column config to use
28             # RETURNING, thus ensuring we get RETURNING handling
29 2         11 for my $col (keys $overrides->%*) {
30 1 50       6 next if ref $to_insert->{$col};
31 1         5 $to_insert->{$col} = \[ '?' => $to_insert->{$col} ];
32             }
33 2         9 my $source = $self->result_source;
34             local $source->{_columns}
35 4 100   4   40 = { pairmap { $a => { $b->%*, exists $overrides->{$a} ? (retrieve_on_insert => 1) : () } }
36 2         152 $source->{_columns}->%* };
37              
38 2         19 $sqla2_passthru->{on_conflict} = _create_upsert_clause($self, [ keys $to_insert->%* ], $overrides);
39 2         20 $self->create({ $to_insert->%*, -sqla2 => $sqla2_passthru });
40             }
41              
42 22     22 1 2846742 sub populate ($self, $to_insert, $attrs = undef) {
  22         70  
  22         54  
  22         56  
  22         45  
43             # NOTE - hrm, relations is a hard problem here. A "DO NOTHING" should be global, which
44             # is why we don't stomp when empty
45 22 100       95 local $self->result_source->storage->sql_maker->{_sqla2_insert_attrs} = $attrs if $attrs;
46 22         325 $self->next::method($to_insert);
47             }
48              
49 1     1 1 15294 sub populate_upsert ($self, $to_insert, $overrides = {}) {
  1         2  
  1         2  
  1         2  
  1         2  
50 1 50       5 croak "populate_upsert must be called in void context" if defined wantarray;
51 1         2 my @inserted_cols;
52 1 50       4 if (ref $to_insert->[0] eq 'ARRAY') {
53 0         0 @inserted_cols = $to_insert->[0]->@*;
54             } else {
55 1         3 @inserted_cols = keys $to_insert->[0]->%*;
56             }
57 1         6 $self->populate($to_insert, { on_conflict => _create_upsert_clause($self, \@inserted_cols, $overrides) });
58             }
59              
60             1;
61              
62             =encoding utf8
63              
64             =head1 NAME
65              
66             DBIx::Class::SQLA2 - SQL::Abstract v2 support in DBIx::Class
67              
68             =head1 SYNOPSIS
69              
70             # resultset code
71             package MyApp::Schema::ResultSet;
72             __PACKAGE__->load_components('ResultSet::SQLA2Support');
73              
74              
75             # client code
76             my $rs = $schema->resultset('Album')->populate([{ name => 'thing' }, { name => 'stuff' } ], -sqla2 => { on_conflict => 0 }})
77              
78             =head1 DESCRIPTION
79              
80             This is a work in progress for simplifying using SQLA2 with DBIC. This is for using w/ the
81             most recent version of DBIC.
82              
83             B
84              
85             Allows you to passthru sqla2 options as an extra arg to populate, as in the SYNOPSIS. In
86             addition, provides some extra methods.
87              
88             =head2 METHODS
89              
90             =over 2
91              
92             =item upsert
93              
94             # on conflict, add this name to the existing name
95             $rs->upsert({ name => 'thingy', id => 9001 }, { name => \"name || ' ' || excluded.name" });
96              
97             The first argument is the same as you would pass to a call to C, except we generate
98             an ON CONFLICT clause which will upsert all non-primary-key values. You can pass a hashref
99             as the second argument to override the default on conflict value. You can pass in anything
100             (literal SQL is quite helpful here) and it will be retrieved by DBIC on the insert using
101             DBIC's return_on_insert functionality.
102              
103             =item populate_upsert
104              
105             # on conflict, add this name to the existing name
106             $rs->populate_upsert([{ name => 'thingy', id => 9001 }, { name => 'over 9000', id => 9002 }], { name => \"name || ' ' || excluded.name" });
107              
108             Same as C above, just for C. Dies a horrible death if called in non-void context.
109              
110             =back
111              
112             =cut