File Coverage

blib/lib/SQL/Maker/Plugin/InsertMulti.pm
Criterion Covered Total %
statement 62 63 98.4
branch 16 18 88.8
condition 16 21 76.1
subroutine 5 6 83.3
pod 0 1 0.0
total 99 109 90.8


line stmt bran cond sub pod time code
1             use strict;
2 1     1   534 use warnings;
  1         3  
  1         29  
3 1     1   8 use utf8;
  1         2  
  1         23  
4 1     1   8 use Scalar::Util ();
  1         2  
  1         12  
5 1     1   20  
  1         2  
  1         679  
6             our @EXPORT = qw/insert_multi/;
7              
8             # for mysql
9             # my ($self, $table, $cols, $binds, $opts) = @_;
10             # my ($self, $table, $colvals, $opts) = @_;
11             my ( $self, $table, @args ) = @_;
12             return unless @{$args[0]};
13 8     8 0 1981  
14 8 50       16 my (@cols, @bind, @values, $opts);
  8         21  
15             my $first_arg = $args[0]->[0];
16 8         16 my $is_colvals = ( ref $first_arg ) ? 1 : 0;
17 8         13  
18 8 100       19 if ( $is_colvals ) {
19             @cols = keys %{$first_arg};
20 8 100       23 @values = map { [ @$_{@cols} ] } @{$args[0]};
21 5         8 $opts = $args[1] || +{};
  5         25  
22 5         136 }
  10         131  
  5         40  
23 5   100     85 else {
24             @cols = @{$args[0]};
25             @values = @{$args[1]};
26 3         6 $opts = $args[2] || +{};
  3         8  
27 3         6 }
  3         4  
28 3   100     12  
29             my $prefix = $opts->{prefix} || 'INSERT INTO';
30             my $quoted_table = $self->_quote($table);
31 8   100     31 my @quoted_cols = map { $self->_quote($_) } @cols;
32 8         30  
33 8         18 my $sql = "$prefix $quoted_table" . $self->new_line;
  23         50  
34             $sql .= '(' . join(', ', @quoted_cols) . ')' . $self->new_line . "VALUES ";
35 8         35  
36 8         60 for my $value ( @values ) {
37             my @value_stmt;
38 8         58 for my $val (@$value) {
39 16         70 if (Scalar::Util::blessed($val)) {
40 16         23 if ($val->can('as_sql')) {
41 46 100       142 push @value_stmt, $val->as_sql(undef, sub { $self->_quote($_[0]) });
42 6 100       33 push @bind, $val->bind();
43 4     0   21 } else {
  0         0  
44 4         52 push @value_stmt, '?';
45             push @bind, $val;
46 2         5 }
47 2         12 } else {
48             Carp::croak("cannot pass in an unblessed ref as an argument in strict mode")
49             if ref($val) && $self->strict;
50 40 50 66     115 if (! $self->strict && ref $val eq 'SCALAR') {
51             # $val = \'NOW()'
52 40 100 66     112 push @value_stmt, $$val;
    100 66        
      66        
53             }
54 4         30 elsif (! $self->strict && ref $val eq 'REF' && ref $$val eq 'ARRAY') {
55             # $val = \['UNIX_TIMESTAMP(?)', '2011-04-20 00:30:00']
56             my ( $stmt, @sub_bind ) = @{$$val};
57             push @value_stmt, $stmt;
58 4         52 push @bind, @sub_bind;
  4         12  
59 4         10 }
60 4         9 else {
61             # normal values
62             push @value_stmt, '?';
63             push @bind, $val;
64 32         389 }
65 32         64 }
66             }
67             $sql .= '(' . join(', ', @value_stmt) . '),' . $self->new_line;
68             }
69 16         80  
70             $sql =~ s/,$self->{new_line}$/$self->{new_line}/;
71              
72 8         127 if ( $self->{driver} eq 'mysql' && exists $opts->{update} ) {
73             my ($update_cols, $update_vals) = $self->make_set_clause($opts->{update});
74 8 100 66     41 $sql .= "ON DUPLICATE KEY UPDATE " . join(', ', @$update_cols) . $self->{new_line};
75 3         15 push @bind, @$update_vals;
76 3         22 }
77 3         13  
78             $sql =~ s/$self->{new_line}+$//;
79              
80 8         64 return ($sql, @bind);
81             }
82 8         77  
83             1;
84              
85             =for test_synopsis
86             my ($table, @rows);
87              
88             =head1 NAME
89              
90             SQL::Maker::Plugin::InsertMulti - insert multiple rows at once on MySQL
91              
92             =head1 SYNOPSIS
93              
94             use SQL::Maker;
95              
96             SQL::Maker->load_plugin('InsertMulti');
97              
98             my $table = 'foo';
99             my @rows = ( +{ bar => 'baz', john => 'man' }, +{ bar => 'bee', john => 'row' } );
100             my $builder = SQL::Maker->new( driver => 'mysql' );
101             my ($sql, @binds);
102             ### INSERT INTO `foo` (`bar`, `john`) VALUES (?, ?), (?, ?)
103             ( $sql, @binds ) = $builder->insert_multi($table, \@rows);
104             ( $sql, @binds ) = $builder->insert_multi($table, [qw/bar john/], [ map { @$_{qw/bar john/} } @rows ]);
105             ### INSERT IGNORE `foo` (`bar`, `john`) VALUES (?, ?), (?, ?)
106             ( $sql, @binds ) = $builder->insert_multi($table, [qw/bar john/], [ map { @$_{qw/bar john/} } @rows ], +{ prefix => 'INSERT IGNORE' });
107             ### INSERT INTO `foo` (`bar`. `john`) VALUES (?, ?), (?, ?) ON DUPLICATE KEY UPDATE `bar` => ?
108             ( $sql, @binds ) = $builder->insert_multi($table, \@rows, +{ update => +{ bar => 'updated' } });
109             ( $sql, @binds ) = $builder->insert_multi($table, [qw/bar john/], [ map { @$_{qw/bar john/} } @rows ], +{ update => +{ bar => 'updated' } });
110              
111             =head1 DESCRIPTION
112              
113             This is a plugin to generate MySQL's INSERT-multi statement.