File Coverage

blib/lib/Fey/SQL/Insert.pm
Criterion Covered Total %
statement 82 82 100.0
branch 14 14 100.0
condition 8 9 88.8
subroutine 17 17 100.0
pod 7 7 100.0
total 128 129 99.2


line stmt bran cond sub pod time code
1             package Fey::SQL::Insert;
2              
3 27     27   133 use strict;
  27         50  
  27         1186  
4 27     27   140 use warnings;
  27         46  
  27         878  
5 27     27   124 use namespace::autoclean;
  27         43  
  27         238  
6              
7             our $VERSION = '0.43';
8              
9             use Fey::Types
10 27         227 qw( ArrayRef HashRef CanQuote IntoElement NonNullableInsertValue
11 27     27   2536 NullableInsertValue );
  27         43  
12 27     27   307584 use overload ();
  27         57  
  27         677  
13 27     27   130 use Scalar::Util qw( blessed );
  27         33  
  27         2011  
14              
15 27     27   136 use Moose 2.1200;
  27         778  
  27         216  
16 27     27   152896 use MooseX::Params::Validate 0.21 qw( validated_hash pos_validated_list );
  27         790  
  27         194  
17 27     27   7227 use MooseX::SemiAffordanceAccessor 0.03;
  27         469  
  27         202  
18 27     27   87164 use MooseX::StrictConstructor 0.13;
  27         638  
  27         172  
19              
20             with 'Fey::Role::SQL::HasBindParams';
21              
22             has '_into' => (
23             is => 'rw',
24             isa => ArrayRef,
25             init_arg => undef,
26             );
27              
28             has '_values_spec' => (
29             is => 'rw',
30             isa => HashRef,
31             init_arg => undef,
32             );
33              
34             has '_values' => (
35             traits => ['Array'],
36             is => 'bare',
37             isa => ArrayRef [HashRef],
38             default => sub { [] },
39             handles => {
40             _add_values => 'push',
41             _values => 'elements',
42             },
43             init_arg => undef,
44             );
45              
46             with 'Fey::Role::SQL::Cloneable';
47              
48 26     26 1 69 sub insert { return $_[0] }
49              
50             sub into {
51 27     27 1 1846 my $self = shift;
52              
53 27 100       71 my $count = @_ ? scalar @_ : 1;
54 27         120 my @into = pos_validated_list(
55             \@_,
56             ( ( { isa => IntoElement } ) x $count ),
57             MX_PARAMS_VALIDATE_NO_CACHE => 1,
58             );
59              
60 26         594 my @cols;
61 26         57 for my $elt (@into) {
62 43 100       183 push @cols, $elt->isa('Fey::Table')
63             ? $elt->columns
64             : $elt;
65             }
66              
67 26         851 $self->_set_into( \@cols );
68              
69 26         33 my %spec;
70 26         29 for my $col ( @{ $self->_into() } ) {
  26         553  
71 46 100       924 $spec{ $col->name() }
72             = $col->is_nullable()
73             ? { isa => NullableInsertValue }
74             : { isa => NonNullableInsertValue };
75             }
76              
77 26         715 $self->_set_values_spec( \%spec );
78              
79 26         78 return $self;
80             }
81              
82             sub values {
83 26     26 1 100 my $self = shift;
84              
85 26         596 my %vals = validated_hash(
86             \@_,
87 26         58 %{ $self->_values_spec() },
88             MX_PARAMS_VALIDATE_NO_CACHE => 1
89             );
90              
91 23         1738 for my $col_name (
  37         79  
92 37         716 grep { exists $vals{$_} }
93 23         573 map { $_->name() } @{ $self->_into() }
94             ) {
95 37         51 my $val = $vals{$col_name};
96              
97 37 100 100     143 $val .= ''
98             if blessed $val && overload::Overloaded($val);
99              
100 37 100       772 if ( !blessed $val ) {
101 26 100 100     671 if ( defined $val && $self->auto_placeholders() ) {
102 16         511 $self->_add_bind_param($val);
103              
104 16         371 $val = Fey::Placeholder->new();
105             }
106             else {
107 10         49 $val = Fey::Literal->new_from_scalar($val);
108             }
109             }
110              
111 37         84 $vals{$col_name} = $val;
112             }
113              
114 23         775 $self->_add_values( \%vals );
115              
116 23         55 return $self;
117             }
118              
119             sub sql {
120 7     7 1 18 my $self = shift;
121 7         29 my ($dbh) = pos_validated_list( \@_, { isa => CanQuote } );
122              
123             return (
124 7         287 join ' ',
125             $self->insert_clause($dbh),
126             $self->columns_clause($dbh),
127             $self->values_clause($dbh),
128             );
129             }
130              
131             sub insert_clause {
132 10     10 1 259 return ( 'INSERT INTO '
133             . $_[1]->quote_identifier( $_[0]->_into()->[0]->table()->name() )
134             );
135             }
136              
137             sub columns_clause {
138             return (
139 25         1062 '('
140             . (
141             join ', ',
142 12     12 1 2740 map { $_[1]->quote_identifier( $_->name() ) } @{ $_[0]->_into() }
  12         319  
143             )
144             . ')'
145             );
146             }
147              
148             sub values_clause {
149 20     20 1 962 my $self = shift;
150 20         29 my $dbh = shift;
151              
152 20         30 my @cols = @{ $self->_into() };
  20         473  
153              
154 20         26 my @v;
155 20         581 for my $vals ( $self->_values() ) {
156 22         31 my $v = '(';
157              
158 35         1299 $v .= (
159             join ', ',
160             map {
161 22         35 my $val = $vals->{ $_->name() };
162 35         117 my $sql = $val->sql($dbh);
163 35 100 66     492 blessed $val
164             && $val->can('does')
165             && $val->does('Fey::Role::SQL::ReturnsData')
166             ? "($sql)"
167             : $sql
168             } @cols
169             );
170              
171 22         1175 $v .= ')';
172              
173 22         60 push @v, $v;
174             }
175              
176 20         142 return 'VALUES ' . join ',', @v;
177             }
178              
179             __PACKAGE__->meta()->make_immutable();
180              
181             1;
182              
183             # ABSTRACT: Represents a INSERT query
184              
185             __END__
186              
187             =pod
188              
189             =head1 NAME
190              
191             Fey::SQL::Insert - Represents a INSERT query
192              
193             =head1 VERSION
194              
195             version 0.43
196              
197             =head1 SYNOPSIS
198              
199             my $sql = Fey::SQL->new_insert();
200              
201             # INSERT INTO Part
202             # (part_id, name, quantity)
203             # VALUES
204             # (?, ?, ?)
205             $sql->insert()->into($Part);
206              
207             my $ph = Fey::Placeholder->new();
208              
209             $sql->values( part_id => $ph,
210             name => $ph,
211             quantity => $ph,
212             );
213              
214             print $sql->sql($dbh);
215              
216             =head1 DESCRIPTION
217              
218             This class represents a C<INSERT> query.
219              
220             =head1 METHODS
221              
222             This class provides the following methods:
223              
224             =head2 Constructor
225              
226             To construct an object of this class, call C<< $query->insert() >> on
227             a C<Fey::SQL> object.
228              
229             =head2 $insert->insert()
230              
231             This method is basically a no-op that exists to so that L<Fey::SQL>
232             has something to call after it constructs an object in this class.
233              
234             =head2 $insert->into()
235              
236             This method specifies the C<INTO> clause of the query. It expects a
237             list of L<Fey::Column> and/or L<Fey::Table> objects, but not aliases.
238              
239             If you pass a table object, then the C<INTO> will include all of that
240             table's columns, in the order returned by the C<< $table->columns() >>
241             method.
242              
243             Most RDBMS implementations only allow for a single table here, but
244             some (like MySQL) do allow for multi-table inserts.
245              
246             =head2 $insert->values(...)
247              
248             This method takes a hash where the keys are column names, and values are the
249             value to be inserted for that column. Each value can be of the following:
250              
251             =over 4
252              
253             =item * a plain scalar, including undef
254              
255             This will be passed to C<< Fey::Literal->new_from_scalar() >>.
256              
257             =item * C<Fey::Literal> object
258              
259             =item * C<Fey::Placeholder> object
260              
261             =back
262              
263             You can call this method multiple times in order to do a multi-row
264             insert.
265              
266             =head2 $insert->sql()
267              
268             Returns the full SQL statement which this object represents. A DBI
269             handle must be passed so that identifiers can be properly quoted.
270              
271             =head2 $insert->bind_params()
272              
273             See the L<Fey::SQL section on Bind Parameters|Fey::SQL/Bind
274             Parameters> for more details.
275              
276             =head2 $insert->insert_clause()
277              
278             Returns the C<INSERT INTO> clause portion of the SQL statement as a
279             string (just the tables).
280              
281             =head2 $insert->columns_clause()
282              
283             Returns the portion of the SQL statement containing the columns for
284             which values are being inserted as a string.
285              
286             =head2 $insert->values_clause()
287              
288             Returns the C<VALUES> clause portion of the SQL statement as a string.
289              
290             =head1 ROLES
291              
292             =over 4
293              
294             =item * L<Fey::Role::SQL::HasBindParams>
295              
296             =item * L<Fey::Role::SQL::Cloneable>
297              
298             =back
299              
300             =head1 BUGS
301              
302             See L<Fey> for details on how to report bugs.
303              
304             =head1 AUTHOR
305              
306             Dave Rolsky <autarch@urth.org>
307              
308             =head1 COPYRIGHT AND LICENSE
309              
310             This software is Copyright (c) 2011 - 2015 by Dave Rolsky.
311              
312             This is free software, licensed under:
313              
314             The Artistic License 2.0 (GPL Compatible)
315              
316             =cut