File Coverage

blib/lib/MySQL/Insert.pm
Criterion Covered Total %
statement 6 85 7.0
branch 0 28 0.0
condition 0 10 0.0
subroutine 2 12 16.6
pod 4 4 100.0
total 12 139 8.6


line stmt bran cond sub pod time code
1             package MySQL::Insert;
2              
3 1     1   23522 use warnings;
  1         2  
  1         28  
4 1     1   5 use strict;
  1         1  
  1         1544  
5              
6             our $MAX_ROWS_TO_QUERY = 1000;
7              
8             =head1 NAME
9              
10             MySQL::Insert - extended inserts for MySQL via DBI
11              
12             =cut
13              
14             our $VERSION = '0.06';
15              
16             =head1 SYNOPSIS
17              
18             # Insert two rows into sample_table using $dbh database handle
19              
20             use MySQL::Insert;
21              
22             $MySQL::Insert::MAX_ROWS_TO_QUERY = 1000;
23              
24             my $inserter = MySQL::Insert->new( $dbh, 'sample_table', [ @field_names ], %param );
25              
26             # Param can be:
27             # statement = 'INSERT' | 'REPLACE' | 'INSERT IGNORE' (by default)
28             # on_duplicate_update = { field_name => field_value, .. } (not used by default)
29              
30             # simple insertion
31             $inserter->insert_row( { fldname => 'fldvalue1' } );
32             $inserter->insert_row( { fldname => 'fldvalue2' } );
33              
34             # multirow insertion
35             $inserter->insert_row( { fldname => 'fldvalue3' }, { fldname => 'fldvalue4' } );
36             $inserter->insert_row( [ 'fldvalue5' ], [ 'fldvalue6' ] } );
37              
38             # Insert row into sample_table using $dbh database handle
39             # If fldvalue3 is passed as scalar ref then it is not quoted
40             # Used to insert MySQL built-in functions like NOW() and NULL values.
41             # @field_names must be predefined in case of arrayref row data usage
42              
43             $inserter->insert_row( { fldname => \'NOW()' } );
44              
45             undef $inserter;
46              
47             =head1 DESCRIPTION
48              
49             Use multiple-row INSERT syntax that include several VALUES lists.
50             (for example INSERT INTO test VALUES ('1',Some data',2234),('2','Some More Data',23444)).
51             EXTENDED INSERT syntax is more efficient of execution many insert queries.
52             It is not compatible with most RDBMSes.
53              
54             =head1 FUNCTIONS / METHODS
55              
56             The following methods are available:
57              
58             =head2 new
59              
60             Create new MySQL::Insert object
61              
62             =cut
63              
64             sub new {
65 0     0 1   my $type = shift;
66              
67 0           my $self = { };
68 0           $self = bless $self, $type;
69 0           $self->_init( @_ );
70 0           return $self;
71             }
72              
73             sub _init {
74 0     0     my ( $self, $dbh, $table, $fields, %ext_params ) = @_;
75              
76 0           $self->{_dbh} = $dbh;
77 0           $self->{_table} = $table;
78              
79 0           $self->set_fields( $fields );
80              
81 0           $self->{_total_rows} = 0;
82 0           $self->{_do_append_row_to_query} = 0;
83 0           $self->{_query_exists} = 0;
84 0   0       $self->{_statement} = $ext_params{statement} || 'INSERT IGNORE';
85 0           $self->{_on_duplicate_update} = $ext_params{on_duplicate_update};
86             }
87              
88             =head2 set_fields
89              
90             Set fields list (by plain list or list reference)
91              
92             =cut
93              
94             sub set_fields {
95 0     0 1   my $self = shift;
96              
97 0 0 0       return unless @_ && $_[0];
98              
99 0 0         my @fields = ref $_[0] ? @{$_[0]} : @_;
  0            
100              
101 0           $self->{_fields} = \@fields;
102 0           $self->{_name_fields} = "( " . ( join ", ", map "`$_`", @fields ) . " )";
103              
104 0           return 1;
105             }
106              
107             =head2 get_fields
108              
109             Get fields list (or its quantity in scalar context)
110              
111             =cut
112              
113             sub get_fields {
114 0     0 1   my ($self) = @_;
115              
116 0 0         return unless $self->{_fields};
117 0 0         return wantarray ? @{$self->{_fields}} : scalar @{$self->{_fields}};
  0            
  0            
118             }
119              
120             DESTROY {
121 0     0     my $self = shift;
122              
123 0           $self->_finish_current_row;
124              
125 0           $self->_execute_query();
126             }
127              
128             =head2 insert_row
129              
130             Schedule row for insertion
131              
132             =cut
133              
134             sub insert_row {
135 0     0 1   my ( $self, @new_rows ) = @_;
136              
137 0           my $query_executed = 0;
138              
139 0           foreach my $new_row ( @new_rows ) {
140 0 0         $query_executed = 1 if $self->_finish_current_row();
141              
142 0           $self->{_do_append_row_to_query} = 1;
143 0           $self->{_current_row} = $new_row;
144             }
145              
146 0           return $query_executed;
147             }
148              
149             # Private methods
150              
151             sub _finish_current_row {
152 0     0     my $self = shift;
153              
154 0           my $query_executed;
155              
156 0 0         if ( $self->{_do_append_row_to_query} ) {
157              
158 0 0         if ( $self->{_total_rows} >= $MAX_ROWS_TO_QUERY ) {
159 0           $query_executed = $self->_execute_query();
160             }
161              
162 0           $self->_append_row_to_query_rows;
163              
164 0           $self->{_do_append_row_to_query} = 0;
165             }
166              
167 0           return $query_executed;
168             }
169              
170             sub _execute_query {
171 0     0     my $self = shift;
172              
173 0 0         return if ! $self->{_query_exists};
174              
175 0           my $values = join ',', @{$self->{_query_rows}};
  0            
176              
177 0           my $query = "$self->{_statement} $self->{_table} $self->{_name_fields} VALUES $values";
178              
179 0 0 0       if ( $self->{_statement} =~ /^INSERT/i && $self->{_on_duplicate_update} ) {
180              
181 0           my $update_statement = join ', ',
182 0           map { "$_ = " . $self->_prepare_value( $self->{_on_duplicate_update}->{$_} ) }
183 0           keys %{ $self->{_on_duplicate_update} };
184              
185 0           $query .= ' ON DUPLICATE KEY UPDATE ' . $update_statement;
186             }
187              
188 0 0         my $result = $self->{_dbh}->do( $query ) or return;
189              
190             # clear everyting
191 0           $self->{_query_exists} = 0;
192 0           $self->{_total_rows} = 0;
193 0           $self->{_query_rows} = [];
194              
195 0           return $result;
196             }
197              
198             sub _append_row_to_query_rows {
199 0     0     my ( $self ) = @_;
200              
201 0 0         unless ( $self->get_fields() ) {
202 0 0         die 'Undefined field names!' unless ref $self->{_current_row} eq 'HASH';
203              
204 0           $self->set_fields( keys %{$self->{_current_row}} );
  0            
205             }
206              
207 0           my @data_row;
208              
209 0 0         if ( ref $self->{_current_row} eq 'HASH' ) {
210 0           for my $field ( $self->get_fields() ) {
211 0           push @data_row, $self->_prepare_value( $self->{_current_row}->{ $field } );
212             }
213             }
214             else {
215 0           push @data_row, map { $self->_prepare_value( $_ ) } @{ $self->{_current_row} };
  0            
  0            
216             }
217              
218 0           push @{$self->{_query_rows}}, "\n\t( ".join(', ', @data_row)." )";
  0            
219              
220 0           $self->{_query_exists} = 1;
221 0           $self->{_total_rows}++;
222             }
223              
224             sub _prepare_value {
225 0     0     my ( $self, $value ) = @_;
226              
227 0 0         if ( ref $value eq 'SCALAR' ) {
228 0   0       return ${ $value } || q{''};
229             }
230             else {
231 0           return $self->{_dbh}->quote( $value );
232             }
233             }
234              
235              
236             =head1 AUTHORS
237              
238             Gleb Tumanov C<< >> (original author)
239             Walery Studennikov C<< >> (CPAN distribution)
240              
241             =head1 BUGS
242              
243             Please report any bugs or feature requests to C, or through
244             the web interface at L. I will be notified, and then you'll
245             automatically be notified of progress on your bug as I make changes.
246              
247             =head1 LICENSE
248              
249             This program is free software; you can redistribute it and/or modify it
250             under the same terms as Perl itself.
251              
252              
253             =cut
254              
255             1; # End of MySQL::Insert