From: Matt Phillips Date: Fri, 7 Feb 2014 23:14:46 +0000 (-0500) Subject: Rewritten void context populate fastpath X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=commitdiff_plain;h=60c4129599c5f79ce4455fa7ffe63904d244b2f6 Rewritten void context populate fastpath The code was written in a way that required a conversion of all input data to be in AoH format, despite insert_bulk needing AoA format. Now data is converted to AoA by default. --- diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 5fa0d04..454d00d 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -2227,40 +2227,51 @@ case there are obviously no benefits to using this method over L. sub populate { my $self = shift; - # cruft placed in standalone method - my $data = $self->_normalize_populate_args(@_); - return unless @$data; + if (defined wantarray) { + # cruft placed in standalone method + my $data = $self->_normalize_populate_to_hashref(@_); + + return unless @$data; - if(defined wantarray) { my @created = map { $self->create($_) } @$data; return wantarray ? @created : \@created; } else { - my $first = $data->[0]; + # cruft placed in standalone method + my $data = $self->_normalize_populate_to_arrayref(@_); + + return unless @$data; + + my $first = shift @$data; # if a column is a registered relationship, and is a non-blessed hash/array, consider # it relationship data my (@rels, @columns); my $rsrc = $self->result_source; my $rels = { map { $_ => $rsrc->relationship_info($_) } $rsrc->relationships }; - for (keys %$first) { - my $ref = ref $first->{$_}; - $rels->{$_} && ($ref eq 'ARRAY' or $ref eq 'HASH') - ? push @rels, $_ - : push @columns, $_ + for my $index (0..$#$first) { + my $col = $first->[$index]; + my $val = $data->[0][$index]; + my $ref = ref $val; + $rels->{$col} && ($ref eq 'ARRAY' or $ref eq 'HASH') + ? push @rels, $col + : push @columns, $col ; } my @pks = $rsrc->primary_columns; + my %colmap = map { $first->[$_] => $_ } (0..$#$first); ## do the belongs_to relationships foreach my $index (0..$#$data) { - # delegate to create() for any dataset without primary keys with specified relationships - if (grep { !defined $data->[$index]->{$_} } @pks ) { + # delegate to list context populate()/create() for any dataset without + # primary keys with specified relationships + + if (grep { defined $colmap{$_} && !defined $data->[$index][$colmap{$_}] } @pks) { for my $r (@rels) { - if (grep { ref $data->[$index]{$r} eq $_ } qw/HASH ARRAY/) { # a related set must be a HASH or AoH + if (grep { ref $data->[$index][$colmap{$_}] eq $_ } qw/HASH ARRAY/) { # a related set must be a HASH or AoH my @ret = $self->populate($data); return; } @@ -2268,8 +2279,8 @@ sub populate { } foreach my $rel (@rels) { - next unless ref $data->[$index]->{$rel} eq "HASH"; - my $result = $self->related_resultset($rel)->create($data->[$index]->{$rel}); + next unless ref $data->[$index][$colmap{$rel}] eq "HASH"; + my $result = $self->related_resultset($rel)->create($data->[$index][$colmap{$rel}]); my ($reverse_relname, $reverse_relinfo) = %{$rsrc->reverse_relationship_info($rel)}; my $related = $result->result_source->_resolve_condition( $reverse_relinfo->{cond}, @@ -2278,22 +2289,24 @@ sub populate { $rel, ); - delete $data->[$index]->{$rel}; - $data->[$index] = {%{$data->[$index]}, %$related}; - - push @columns, keys %$related if $index == 0; + $data->[$index][$colmap{$rel}] = $related->{$rel}; + if ($index == 0) { + for my $col (keys %$related) { + $colmap{$col} = $colmap{$rel}; + push @columns, $col; + } + } } } ## inherit the data locked in the conditions of the resultset my ($rs_data) = $self->_merge_with_rscond({}); - delete @{$rs_data}{@columns}; ## do bulk insert on current row $rsrc->storage->insert_bulk( $rsrc, [@columns, keys %$rs_data], - [ map { [ @$_{@columns}, values %$rs_data ] } @$data ], + [ map { [ @$_[@colmap{@columns}], values %$rs_data ] } @$data ], ); ## do the has_many relationships @@ -2302,9 +2315,9 @@ sub populate { my $main_row; foreach my $rel (@rels) { - next unless ref $item->{$rel} eq "ARRAY" && @{ $item->{$rel} }; + next unless ref $item->[$colmap{$rel}] eq "ARRAY" && @{ $item->[$colmap{$rel}] }; - $main_row ||= $self->new_result({map { $_ => $item->{$_} } @pks}); + $main_row ||= $self->new_result({map { $_ => $item->[$colmap{$_}] } @pks}); my $child = $main_row->$rel; @@ -2315,35 +2328,105 @@ sub populate { $rel, ); - my @rows_to_add = ref $item->{$rel} eq 'ARRAY' ? @{$item->{$rel}} : ($item->{$rel}); +=begin + if ( ref $item->[$colmap{$rel}] eq 'ARRAY') { + for my $subitem (@{ $item->[$colmap{$rel}] }) { + $subitem-> + + } + } + + my @rows_to_add = ref eq 'ARRAY' ? @{$item->[$colmap{$rel}]} : ($item->[$colmap{$rel}]); my @populate = map { {%$_, %$related} } @rows_to_add; - $child->populate( \@populate ); +=cut + $child->populate( $item->[$colmap{$rel}] ); } } } } - # populate() arguments went over several incarnations # can be any mixture of : # 1. AoA # 2. AoH # 3. AoS(calar) followed buy Arrayrefref (\@cols, $rs->as_query) # 4. coderef (tuple generator) -sub _normalize_populate_args { +# What we ultimately support is AoH +sub _normalize_populate_to_hashref { + my ($self, $arg) = @_; + + if (ref $arg eq 'ARRAY') { + if (!@$arg) { + return []; + } + elsif (ref $arg->[0] eq 'HASH') { + return $arg; + } + elsif (ref $arg->[0] eq 'ARRAY') { + my @ret; + my @colnames = @{$arg->[0]}; + foreach my $values (@{$arg}[1 .. $#$arg]) { + if (ref $values eq 'ARRAY') { + push @ret, { map { $colnames[$_] => $values->[$_] } (0 .. $#colnames) }; + } + elsif (ref $values eq 'CODE' || (ref $values eq 'REF' && ref $$values eq 'ARRAY')) { + push @ret, $values; + } + else { + $self->throw_exception('Populate expects an arrayref of either hashrefs, arrayrefs, coderefs or arrayrefrefs'); + } + } + return \@ret; + } + } + + $self->throw_exception('Populate expects an arrayref of either hashrefs, arrayrefs, coderefs or arrayrefrefs'); +} + +sub _normalize_populate_to_arrayref { my ($self, $args) = @_; - use DDP; my @normalized; + my @cols; -ARG: for (my $idx = 0; $idx < $#$args; $idx++) { +ARG: for (my $idx = 0; $idx <= $#$args; $idx++) { my $arg = $args->[$idx]; - if (ref $arg eq 'ARRAY') { + if ($idx == 0) { + if (ref $arg eq 'ARRAY') { + @cols = @$arg; + } + elsif (ref $arg eq 'HASH') { + @cols = keys %$arg; + push @normalized, [@{$arg}{@cols}]; + } + else { + $self->throw_exception('Populate expects first record to either be a hashref or arrayref of cols'); + } + next ARG; + } + + if (ref $arg eq 'ARRAY' || ref $arg eq 'CODE') { + push @normalized, $arg; + } + elsif (ref $arg eq 'HASH') { + push @normalized, [@{$arg}{@cols}]; + } + else { + $self->throw_exception('Populate expects either arrayref, coderef, or hashref'); + } + } + + return [\@cols, @normalized]; +} + +=begin + # AoH if (ref $arg eq 'ARRAY' && @$arg > 0 && ref $arg->[0] eq 'HASH') { - push @normalized, $arg; + @cols = sort keys %{$arg->[0]} if $idx == 1; + push @normalized, [ @{$args->[0]}{@cols} ]; next ARG; } # AoA @@ -2365,16 +2448,7 @@ ARG: for (my $idx = 0; $idx < $#$args; $idx++) { elsif (ref $arg eq 'CODE') { push @normalized, $arg; } - } - } - - if (scalar @normalized == 0) { - $self->throw_exception('Populate expects some combination of arrayref of hashrefs, arrayref of arrayrefs, array of cols followed by arrayrefref or coderef'); - } - - p @normalized; - return \@normalized; -} +=cut =head2 pager