Skip to content

Commit b788f52

Browse files
committed
0.01 Release
1 parent 65d2948 commit b788f52

File tree

7 files changed

+149
-75
lines changed

7 files changed

+149
-75
lines changed

Changes

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
Revision history for SQL-Inserter
22

3-
0.01 2023-06-20
3+
0.01 2023-06-26
44
First public release.
5-

README.pod

+40-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=head1 NAME
22

3-
SQL::Inserter - Fast insert SQL builder, buffered DBI inserting
3+
SQL::Inserter - Efficient buffered DBI inserter and fast INSERT SQL builder
44

55
=head1 VERSION
66

@@ -20,8 +20,12 @@ Version 0.01
2020
# Fastest method: pass single or multiple rows of data as an array
2121
$sql->insert($col1_row1, $col2_row1, $col1_row2...);
2222

23-
# Alternative, pass a single row as a hash, allows SQL code
24-
# instead of values (pass reference)
23+
# You can manually flush the buffer at any time with no argument on insert
24+
# (otherwise there is auto-flush on the object's destruction)
25+
$sql->insert();
26+
27+
# Alternative, pass a single row as a hash, allows SQL code passed as references
28+
# instead of values
2529
$sql->insert({
2630
column1 => $data1,
2731
column2 => \'NOW()',
@@ -113,17 +117,22 @@ db settings.
113117
=head2 insert
114118

115119
# Fastest array method. Only bind data is passed.
116-
$sql->insert(@column_data_array);
120+
my $ret = $sql->insert(@column_data_array);
117121

118122
# Alternative, allows SQL code as values in addition to bind variables
119-
$sql->insert(\%row_data);
123+
my $ret = $sql->insert(\%row_data);
120124

121125
# No parameters will force emtying of buffer (db write)
122-
$sql->insert();
126+
my $ret = $sql->insert();
123127

124-
The main insert method. It works in two modes, by passing an array or a hashref:
128+
The main insert method. Returns the return value of the last C<execute> statement
129+
if there was one called, 0 otherwise (buffer not full.
130+
131+
It works in two main modes, by passing an array or a hashref:
132+
133+
=over 4
125134

126-
=head3 Array mode
135+
=item Array mode
127136

128137
Pass the data for one or more rows in a flat array, buffering will work automatically
129138
based on your C<buffer> settings. Obviously your C<@column_data_array> has to contain
@@ -133,45 +142,48 @@ This is the fastest mode, but it only allows simple bind values. Any undefined v
133142
will be passed directly to DBI->execute, which may or may not be what you expect -
134143
there will not be any explicit conversion to SQL C<NULL>.
135144

136-
=head3 Hash mode
145+
=item Hash mode
137146

138147
Pass a reference to a hash containing the column names & values for a single row
139148
of data. If C<cols> was not defined on the constructor, the columns from the first
140149
data row will be used instead. For subsequent rows any extra columns will be disregarded
141150
and any missing columns will be considered to have an C<undef> (which can be
142151
automatically converted to C<NULL> if the C<null_undef> option was set).
143152

144-
=head3 Flushing the buffer
153+
=item Flushing the buffer
145154

146155
Calling C<insert> with no arguments forces a write to the db, flushing the buffer.
147-
You don't have to call this manually, the same will happen when the object is destroyed.
156+
You don't have to call this manually as the buffer will be flushed when the object
157+
is destroyed (e.g. your object falls out of scope).
148158

149-
=head3 Mixing modes
159+
=item Mixing modes
150160

151161
You can theoretically mix modes, but only when the buffer is empty e.g. you can start
152162
with the array mode, flush the buffer and continue with hash mode (C<cols> will be
153163
defined from the array mode). Or you can start with hash mode (so C<cols> will be defined
154164
from the very first hash), and after flushing the buffer you can switch to array mode.
155165

166+
=back
167+
156168
=head1 ATTRIBUTES
157169

158-
=head2 last_retval
170+
=head2 C<last_retval>
159171

160172
my $val = $sql->{last_retval}
161173

162174
The return value of the last DBI C<execute()> is stored in this attribute. On a successful
163175
insert it should contain the number of rows of that statement. Note that an C<insert>
164176
call, depending on the buffering, may call C<execute()> zero, one or more times.
165177

166-
=head2 row_total
178+
=head2 C<row_total>
167179

168180
my $total = $sql->{row_total}
169181

170182
Basically a running total of the return values, for successful inserts it shows you
171183
how many rows were inserted into the database. It will be undef if no C<execute()> has
172184
been called.
173185

174-
=head2 buffer_counter
186+
=head2 C<buffer_counter>
175187

176188
my $count = $sql->{buffer_counter}
177189

@@ -273,6 +285,18 @@ Note that as of MySQL 8.0.20 the C<VALUES> in C<UPDATE> is deprecated (row alias
273285
used instead), so this functionality might need to be updated some day if C<VALUES> is
274286
removed completely.
275287

288+
=head2 Output whitespace
289+
290+
No spaces are added to the output string beyond the minimum. However, there is a new
291+
line (C<\n>) added for each row of value placeholders - mainly to easily count the
292+
number of rows from the string.
293+
Also, the C<ON DUPLICATE KEY UPDATE> clause is on a new line.
294+
295+
=head2 Error handling
296+
297+
The module does not do any error handling on C<prepare>/C<execute> statements,
298+
you should use L<DBI>'s C<RaiseError> and C<HandleErrror>.
299+
276300
=head2 Performance
277301

278302
The OO interface has minimal overhead. The only consideration is that if your rows
@@ -320,7 +344,7 @@ L<https://metacpan.org/pod/SQL::Inserter>
320344

321345
=head1 LICENSE AND COPYRIGHT
322346

323-
Copyright (C) 2023, SpareRoom.com
347+
Copyright (C) 2023, SpareRoom
324348

325349
This is free software; you can redistribute it and/or modify it under
326350
the same terms as the Perl 5 programming language system itself.

lib/SQL/Inserter.pm

+46-16
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use Exporter 'import';
99

1010
=head1 NAME
1111
12-
SQL::Inserter - Fast insert SQL builder, buffered DBI inserting
12+
SQL::Inserter - Efficient buffered DBI inserter and fast INSERT SQL builder
1313
1414
=head1 VERSION
1515
@@ -35,8 +35,12 @@ our @EXPORT_OK = qw(simple_insert multi_insert_sql);
3535
# Fastest method: pass single or multiple rows of data as an array
3636
$sql->insert($col1_row1, $col2_row1, $col1_row2...);
3737
38-
# Alternative, pass a single row as a hash, allows SQL code
39-
# instead of values (pass reference)
38+
# You can manually flush the buffer at any time with no argument on insert
39+
# (otherwise there is auto-flush on the object's destruction)
40+
$sql->insert();
41+
42+
# Alternative, pass a single row as a hash, allows SQL code passed as references
43+
# instead of values
4044
$sql->insert({
4145
column1 => $data1,
4246
column2 => \'NOW()',
@@ -152,15 +156,18 @@ sub new {
152156
=head2 insert
153157
154158
# Fastest array method. Only bind data is passed.
155-
$sql->insert(@column_data_array);
159+
my $ret = $sql->insert(@column_data_array);
156160
157161
# Alternative, allows SQL code as values in addition to bind variables
158-
$sql->insert(\%row_data);
162+
my $ret = $sql->insert(\%row_data);
159163
160164
# No parameters will force emtying of buffer (db write)
161-
$sql->insert();
165+
my $ret = $sql->insert();
166+
167+
The main insert method. Returns the return value of the last C<execute> statement
168+
if there was one called, 0 otherwise (buffer not full.
162169
163-
The main insert method. It works in two modes, by passing an array or a hashref:
170+
It works in two main modes, by passing an array or a hashref:
164171
165172
=over 4
166173
@@ -185,7 +192,8 @@ automatically converted to C<NULL> if the C<null_undef> option was set).
185192
=item Flushing the buffer
186193
187194
Calling C<insert> with no arguments forces a write to the db, flushing the buffer.
188-
You don't have to call this manually, the same will happen when the object is destroyed.
195+
You don't have to call this manually as the buffer will be flushed when the object
196+
is destroyed (e.g. your object falls out of scope).
189197
190198
=item Mixing modes
191199
@@ -203,7 +211,7 @@ sub insert {
203211

204212
return $self->_hash_insert(@_) if $_[0] and ref($_[0]);
205213

206-
214+
my $ret = 0;
207215
if (@_) {
208216

209217
croak("Calling insert without a hash requires cols defined in constructor")
@@ -227,11 +235,12 @@ sub insert {
227235
push @{$self->{bind}}, splice(@_);
228236
$self->{buffer_counter} += $rows;
229237
}
230-
$self->_write_full_buffer() if $self->{buffer_counter} == $self->{buffer};
238+
$ret = $self->_write_full_buffer() if $self->{buffer_counter} == $self->{buffer};
231239
}
232240
} elsif ($self->{buffer_counter}) { # Empty the buffer
233-
$self->_empty_buffer();
241+
$ret = $self->_empty_buffer();
234242
}
243+
return $ret;
235244
}
236245

237246
=head1 ATTRIBUTES
@@ -365,7 +374,7 @@ sub multi_insert_sql {
365374
return unless $table && $columns && @$columns;
366375

367376
my $placeholders =
368-
join(',', ('(' . join(',', ('?') x @$columns) . ')') x $num_rows);
377+
join(",\n", ('(' . join(',', ('?') x @$columns) . ')') x $num_rows);
369378

370379
return _create_insert_sql($table, $columns, $placeholders, $dupe);
371380
}
@@ -375,6 +384,7 @@ sub multi_insert_sql {
375384
sub _hash_insert {
376385
my $self = shift;
377386
my $fields = shift;
387+
my $ret = 0;
378388

379389
croak("Insert was previously called with an array argument (still in buffer)")
380390
if $self->{buffer_counter} && !$self->{hash_buffer};
@@ -385,7 +395,9 @@ sub _hash_insert {
385395
push @{$self->{hash_buffer}}, $row;
386396
push @{$self->{bind}}, @bind;
387397

388-
$self->_write_hash_buffer() if $self->{buffer_counter} == $self->{buffer};
398+
$ret = $self->_write_hash_buffer() if $self->{buffer_counter} == $self->{buffer};
399+
400+
return $ret;
389401
}
390402

391403
sub _write_full_buffer {
@@ -396,6 +408,8 @@ sub _write_full_buffer {
396408

397409
$self->_execute($self->{full_buffer_insert});
398410
$self->_cleanup();
411+
412+
return $self->{last_retval};
399413
}
400414

401415
sub _prepare_full_buffer_insert {
@@ -411,7 +425,7 @@ sub _empty_buffer {
411425
return $self->_write_hash_buffer() if $self->{hash_buffer};
412426

413427
my $rows = scalar(@{$self->{bind}}) / scalar @{$self->{cols}};
414-
my $sth = $self->{dbh}->prepare(
428+
my $sth = $self->{dbh}->prepare(
415429
multi_insert_sql(
416430
$self->{table},
417431
$self->{cols},
@@ -421,6 +435,8 @@ sub _empty_buffer {
421435
);
422436
$self->_execute($sth);
423437
$self->_cleanup();
438+
439+
return $self->{last_retval};
424440
}
425441

426442
sub _write_hash_buffer {
@@ -434,6 +450,8 @@ sub _write_hash_buffer {
434450
);
435451
$self->_execute($sth);
436452
$self->_cleanup();
453+
454+
return $self->{last_retval};
437455
}
438456

439457
sub _execute {
@@ -472,7 +490,7 @@ sub _create_insert_sql {
472490

473491
$sql .= _on_duplicate_key_update($columns) if $dupe eq 'update';
474492

475-
return "$sql;";
493+
return $sql;
476494
}
477495

478496
sub _row_placeholders {
@@ -534,6 +552,18 @@ Note that as of MySQL 8.0.20 the C<VALUES> in C<UPDATE> is deprecated (row alias
534552
used instead), so this functionality might need to be updated some day if C<VALUES> is
535553
removed completely.
536554
555+
=head2 Output whitespace
556+
557+
No spaces are added to the output string beyond the minimum. However, there is a new
558+
line (C<\n>) added for each row of value placeholders - mainly to easily count the
559+
number of rows from the string.
560+
Also, the C<ON DUPLICATE KEY UPDATE> clause is on a new line.
561+
562+
=head2 Error handling
563+
564+
The module does not do any error handling on C<prepare>/C<execute> statements,
565+
you should use L<DBI>'s C<RaiseError> and C<HandleErrror>.
566+
537567
=head2 Performance
538568
539569
The OO interface has minimal overhead. The only consideration is that if your rows
@@ -581,7 +611,7 @@ L<https://metacpan.org/pod/SQL::Inserter>
581611
582612
=head1 LICENSE AND COPYRIGHT
583613
584-
Copyright (C) 2023, SpareRoom.com
614+
Copyright (C) 2023, SpareRoom
585615
586616
This is free software; you can redistribute it and/or modify it under
587617
the same terms as the Perl 5 programming language system itself.

t/error.t

+13-1
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,16 @@ subtest 'insert' => sub {
3333
like(dies {$sql->insert(1,2)}, qr/hash/, "Wrong mode again");
3434
};
3535

36-
done_testing;
36+
my $mock = Test2::Mock->new(
37+
class => 'SQL::Inserter',
38+
override => [
39+
croak => sub { return 0 },
40+
],
41+
);
42+
43+
subtest 'Cover croak fail' => sub {
44+
ok(lives {my $sql = SQL::Inserter->new()}, "Missing dbh");
45+
ok(lives {my $sql = SQL::Inserter->new(dbh=>1)}, "Missing table");
46+
};
47+
48+
done_testing;

t/private.t

+5-5
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ subtest '_on_duplicate_key_update' => sub {
3131

3232
subtest '_create_insert_sql' => sub {
3333
my @tests = (
34-
[['table', [qw/foo bar/], '(?,?)'], "INSERT INTO table (foo,bar)\nVALUES (?,?);"],
35-
[['table', [qw/foo bar/], '(?,?)', 'ignore'], "INSERT IGNORE INTO table (foo,bar)\nVALUES (?,?);"],
36-
[['table', [qw/foo bar/], '(?,?)', 'update'], "INSERT INTO table (foo,bar)\nVALUES (?,?)\nON DUPLICATE KEY UPDATE foo=VALUES(foo),bar=VALUES(bar);"],
37-
[['table', ["foo"], '(?)'], "INSERT INTO table (foo)\nVALUES (?);"],
34+
[['table', [qw/foo bar/], '(?,?)'], "INSERT INTO table (foo,bar)\nVALUES (?,?)"],
35+
[['table', [qw/foo bar/], '(?,?)', 'ignore'], "INSERT IGNORE INTO table (foo,bar)\nVALUES (?,?)"],
36+
[['table', [qw/foo bar/], '(?,?)', 'update'], "INSERT INTO table (foo,bar)\nVALUES (?,?)\nON DUPLICATE KEY UPDATE foo=VALUES(foo),bar=VALUES(bar)"],
37+
[['table', ["foo"], '(?)'], "INSERT INTO table (foo)\nVALUES (?)"],
3838
);
3939
foreach my $test (@tests) {
4040
is(SQL::Inserter::_create_insert_sql(@{$test->[0]}), $test->[1], "Statements match");
4141
}
4242
};
4343

44-
done_testing;
44+
done_testing;

0 commit comments

Comments
 (0)