mojolicious/mojo-pg

Bulk Insert support?

KnowZero opened this issue · 6 comments

It would be nice to support multiple inserts in a single statement, especially non-blocking.

Maybe something like this?

my @values = [ [ 'cat', 1 ], [ 'dog', 2 ] ];
 $abstract->insert( 'pets' => {  -values=>\@values }  );

my @keys = [ 'name', 'rank' ];
 $abstract->insert( 'pets' => { -keys=> \@keys,  -values=>\@values }  );

my @keyvalues = [ { name=>'cat', rank=>1 }, { name=>'dog', rank=>2 }  ];
 $abstract->insert( 'pets' => {  -keyvalues=>\@keyvalues }  );

Then backend something like this?

sub _insert_HASHREF { 
  my ($self, $data) = @_;

if ( ref $$data{'-keyvalues'} eq 'ARRAY' ) {
$$data{'-keys'}=[ sort keys %{ $$data{'-keyvalues'}[0] } ];
$$data{'-values'} = [ map {   
    my $keyvalues=$_;
    [ map {  $$keyvalues{ $_ }  } @{ $$data{'-keys'} }  ]   
    } @{ $$data{'-keyvalues'} } ];
}
   
   return $self->SUPER::_insert_HASHREF($data) if ref $$data{'-values'} ne 'ARRAY';
 


my @fields = @{$$data{'-keys'}};
 

   my ( @all_bind, @all_values);
  foreach my $rec (@{ $$data{'-values'} }) { 
      my @values;
    foreach my $value (@$rec) {

      my ($values, @bind) = $self->_insert_value(undef, $value);
      push @values, $values;
      push @all_bind, @bind;
    }

    push @all_values, \@values;
  }
  my $sql = ( exists $$data{'-keys'} ? "( ".join(", ", @fields )." ) ":'' ).
              $self->_sqlcase('values').
              join(',', map{ " ( ".join(", ", @$_)." )" } @all_values  );

  return ($sql, @all_bind);

}

kraih commented

Yes, i would like to see bulk inserts too. But getting the code right is not so easy.

Is there a problem with my approach of overwriting _insert_HASHREF?

or is it better to make a seperate bulk_insert function maybe?

I added bulk insert support to DBIx::Array you are more than welcome to see how that was implemented. https://metacpan.org/pod/DBIx::Array#METHODS-(Write)-Bulk-SQL

This would be a nice thing to have.

kraih commented

Note that SQL::Abstract::Pg is now a separate project. https://github.com/mojolicious/sql-abstract-pg

I found a workaround! Since SQL::Abstract::Pg is a subclass of SQL::Abstract, all the plugins for SQL::Abstract will work directly. I stumbled upon SQL::Abstract::Plugin::InsertMulti and made a simple test:

package My::Model {
    use Mojo::Base -base, -signatures;

    use SQL::Abstract::Plugin::InsertMulti;

    has 'pg';

    sub insert_multi_p ( $self, $table, $data, $opts = {} ) {
        my ( $stmt, @bind ) =
          $self->pg()->abstract()->insert_multi( $table, $data, $opts );

        return $self->pg()->db()->query_p( $stmt, @bind );
    }
};

1;

So far, it works well. I hope it solves the problem for everyone else.