File Coverage

blib/lib/Fey/SQL/Update.pm
Criterion Covered Total %
statement 80 80 100.0
branch 16 16 100.0
condition 14 18 77.7
subroutine 21 21 100.0
pod 6 6 100.0
total 137 141 97.1


line stmt bran cond sub pod time code
1             package Fey::SQL::Update;
2              
3 27     27   126 use strict;
  27         41  
  27         1067  
4 27     27   148 use warnings;
  27         42  
  27         817  
5 27     27   129 use namespace::autoclean;
  27         44  
  27         309  
6              
7             our $VERSION = '0.42';
8              
9 27     27   2404 use Fey::Exceptions qw( param_error );
  27         46  
  27         1530  
10 27     27   146 use Fey::Literal;
  27         38  
  27         578  
11 27         203 use Fey::Types qw( ArrayRef CanQuote ColumnWithTable NonNullableUpdateValue
12 27     27   147 NullableUpdateValue Table );
  27         35  
13 27     27   297818 use overload ();
  27         57  
  27         623  
14 27     27   109 use Scalar::Util qw( blessed );
  27         36  
  27         1758  
15              
16 27     27   127 use Moose 0.90;
  27         672  
  27         203  
17 27     27   147110 use MooseX::Params::Validate 0.08 qw( pos_validated_list );
  27         678  
  27         177  
18 27     27   4952 use MooseX::SemiAffordanceAccessor 0.03;
  27         439  
  27         165  
19 27     27   80593 use MooseX::StrictConstructor 0.07;
  27         555  
  27         159  
20              
21             with 'Fey::Role::SQL::HasOrderByClause', 'Fey::Role::SQL::HasLimitClause';
22              
23             with 'Fey::Role::SQL::HasWhereClause' => {
24             -excludes => 'bind_params',
25             -alias => { bind_params => '_where_clause_bind_params' },
26             };
27              
28             with 'Fey::Role::SQL::HasBindParams' => {
29             -excludes => 'bind_params',
30             -alias => { bind_params => '_update_bind_params' },
31             };
32              
33             has '_update' => (
34             is => 'rw',
35             isa => ArrayRef,
36             default => sub { [] },
37             init_arg => undef,
38             );
39              
40             has '_set_pairs' => (
41             traits => ['Array'],
42             is => 'bare',
43             isa => ArrayRef [ArrayRef],
44             default => sub { [] },
45             handles => {
46             _add_set_pair => 'push',
47             _set_pairs => 'elements',
48             },
49             init_arg => undef,
50             );
51              
52             with 'Fey::Role::SQL::Cloneable';
53              
54             sub update {
55 26     26 1 4111 my $self = shift;
56              
57 26 100       95 my $count = @_ ? @_ : 1;
58 26         191 my (@tables) = pos_validated_list(
59             \@_,
60             ( ( { isa => Table } ) x $count ),
61             MX_PARAMS_VALIDATE_NO_CACHE => 1,
62             );
63              
64 25         17085 $self->_set_update( \@tables );
65              
66 25         81 return $self;
67             }
68              
69             sub set {
70 23     23 1 2009 my $self = shift;
71              
72 23 100 100     182 if ( !@_ || @_ % 2 ) {
73 2         4 my $count = @_;
74 2         14 param_error
75             "The set method expects a list of paired column objects and values but you passed $count parameters";
76             }
77              
78 21         38 my @spec;
79 21         98 for ( my $x = 0; $x < @_; $x += 2 ) {
80 25         658 push @spec, { isa => ColumnWithTable };
81 25 100 66     4069 push @spec,
82             blessed $_[$x] && $_[$x]->is_nullable()
83             ? { isa => NullableUpdateValue }
84             : { isa => NonNullableUpdateValue };
85             }
86              
87             my @set
88 21         2540 = pos_validated_list( \@_, @spec, MX_PARAMS_VALIDATE_NO_CACHE => 1 );
89              
90 21         3220 for ( my $x = 0; $x < @_; $x += 2 ) {
91 25         68 my $val = $_[ $x + 1 ];
92              
93 25 100 100     158 $val .= ''
94             if blessed $val && overload::Overloaded($val);
95              
96 25 100       826 if ( !blessed $val ) {
97 16 100 100     639 if ( defined $val && $self->auto_placeholders() ) {
98 8         370 $self->_add_bind_param($val);
99              
100 8         348 $val = Fey::Placeholder->new();
101             }
102             else {
103 8         57 $val = Fey::Literal->new_from_scalar($val);
104             }
105             }
106              
107 25         1149 $self->_add_set_pair( [ $_[$x], $val ] );
108             }
109              
110 21         134 return $self;
111             }
112              
113             sub sql {
114 4     4 1 25 my $self = shift;
115 4         22 my ($dbh) = pos_validated_list( \@_, { isa => CanQuote } );
116              
117             return (
118 4         191 join ' ',
119             $self->update_clause($dbh),
120             $self->set_clause($dbh),
121             $self->where_clause($dbh),
122             $self->order_by_clause($dbh),
123             $self->limit_clause($dbh),
124             );
125             }
126              
127             sub update_clause {
128 6     6 1 38 return 'UPDATE ' . $_[0]->_tables_subclause( $_[1] );
129             }
130              
131             sub _tables_subclause {
132             return (
133 7         260 join ', ',
134 6     6   20 map { $_[1]->quote_identifier( $_->name() ) } @{ $_[0]->_update() }
  6         196  
135             );
136             }
137              
138             sub set_clause {
139 21     21 1 451 my $self = shift;
140 21         36 my $dbh = shift;
141              
142             # SQLite objects when the table name is provided ("User"."email")
143             # on the LHS of the set. I'm hoping that a DBMS which allows a
144             # multi-table update also allows the table name in the LHS.
145 21 100       32 my $col_quote = @{ $self->_update() } > 1 ? '_name_and_table' : '_name';
  21         589  
146              
147             return (
148 24         299 'SET ' . (
149             join ', ',
150             map {
151 21         757 my $val = $_->[1];
152 24         124 my $val_sql = $val->sql($dbh);
153 24 100 33     1001 $val_sql = "($val_sql)"
      66        
154             if blessed $val
155             && $val->can('does')
156             && $val->does('Fey::Role::SQL::ReturnsData');
157 24         2718 $self->$col_quote( $_->[0], $dbh ) . ' = ' . $val_sql;
158             } $self->_set_pairs()
159             )
160             );
161             }
162              
163             sub _name_and_table {
164 1     1   5 return $_[1]->sql( $_[2] );
165             }
166              
167             sub _name {
168 23     23   752 return $_[2]->quote_identifier( $_[1]->name() );
169             }
170              
171             sub bind_params {
172 5     5 1 2925 my $self = shift;
173              
174             return (
175 5         37 $self->_update_bind_params(),
176             $self->_where_clause_bind_params(),
177             );
178             }
179              
180             __PACKAGE__->meta()->make_immutable();
181              
182             1;
183              
184             # ABSTRACT: Represents a UPDATE query
185              
186             __END__
187              
188             =pod
189              
190             =head1 NAME
191              
192             Fey::SQL::Update - Represents a UPDATE query
193              
194             =head1 VERSION
195              
196             version 0.42
197              
198             =head1 SYNOPSIS
199              
200             my $sql = Fey::SQL->new_update();
201              
202             # UPDATE Part
203             # SET quantity = 10
204             # WHERE part_id IN (1, 5)
205             $sql->update($Part);
206             $sql->set( $quantity, 10 );
207             $sql->where( $part_id, 'IN', 1, 5 );
208              
209             print $sql->sql($dbh);
210              
211             =head1 DESCRIPTION
212              
213             This class represents a C<UPDATE> query.
214              
215             =head1 METHODS
216              
217             This class provides the following methods:
218              
219             =head2 Constructor
220              
221             To construct an object of this class, call C<< $query->update() >> on
222             a C<Fey::SQL> object.
223              
224             =head2 $update->update()
225              
226             This method specifies the C<UPDATE> clause of the query. It expects
227             one or more L<Fey::Table> objects (not aliases). Most RDBMS
228             implementations only allow for a single table here, but some (like
229             MySQL) do allow for multi-table updates.
230              
231             =head2 $update->set(...)
232              
233             This method takes a list of key/value pairs. The keys should be column
234             objects, and the value can be one of the following:
235              
236             =over 4
237              
238             =item * a plain scalar, including undef
239              
240             This will be passed to C<< Fey::Literal->new_from_scalar() >>.
241              
242             =item * C<Fey::Literal> object
243              
244             =item * C<Fey::Column> object
245              
246             A column alias cannot be used.
247              
248             =item * C<Fey::Placeholder> object
249              
250             =back
251              
252             =head2 $update->where(...)
253              
254             See the L<Fey::SQL section on WHERE Clauses|Fey::SQL/WHERE Clauses>
255             for more details.
256              
257             =head2 $update->order_by(...)
258              
259             See the L<Fey::SQL section on ORDER BY Clauses|Fey::SQL/ORDER BY
260             Clauses> for more details.
261              
262             =head2 $update->limit(...)
263              
264             See the L<Fey::SQL section on LIMIT Clauses|Fey::SQL/LIMIT Clauses>
265             for more details.
266              
267             =head2 $update->sql($dbh)
268              
269             Returns the full SQL statement which this object represents. A DBI
270             handle must be passed so that identifiers can be properly quoted.
271              
272             =head2 $update->bind_params()
273              
274             See the L<Fey::SQL section on Bind Parameters|Fey::SQL/Bind
275             Parameters> for more details.
276              
277             =head2 $update->update_clause()
278              
279             Returns the C<UPDATE> clause portion of the SQL statement as a string.
280              
281             =head2 $update->set_clause()
282              
283             Returns the C<SET> clause portion of the SQL statement as a string.
284              
285             =head2 $update->where_clause()
286              
287             Returns the C<WHERE> clause portion of the SQL statement as a string.
288              
289             =head2 $update->order_by_clause()
290              
291             Returns the C<ORDER BY> clause portion of the SQL statement as a
292             string.
293              
294             =head2 $update->limit_clause()
295              
296             Returns the C<LIMIT> clause portion of the SQL statement as a string.
297              
298             =head1 ROLES
299              
300             =over 4
301              
302             =item * L<Fey::Role::SQL::HasBindParams>
303              
304             =item * L<Fey::Role::SQL::HasWhereClause>
305              
306             =item * L<Fey::Role::SQL::HasOrderByClause>
307              
308             =item * L<Fey::Role::SQL::HasLimitClause>
309              
310             =item * L<Fey::Role::SQL::Cloneable>
311              
312             =back
313              
314             =head1 BUGS
315              
316             See L<Fey> for details on how to report bugs.
317              
318             =head1 AUTHOR
319              
320             Dave Rolsky <autarch@urth.org>
321              
322             =head1 COPYRIGHT AND LICENSE
323              
324             This software is Copyright (c) 2011 - 2015 by Dave Rolsky.
325              
326             This is free software, licensed under:
327              
328             The Artistic License 2.0 (GPL Compatible)
329              
330             =cut