@@ -2234,127 +2234,266 @@ case there are obviously no benefits to using this method over L</create>.
2234
2234
sub populate {
2235
2235
my $self = shift ;
2236
2236
2237
- # cruft placed in standalone method
2238
- my $data = $self -> _normalize_populate_args(@_ );
2237
+ my ($data , $guard );
2239
2238
2240
- return unless @$data ;
2239
+ # this is naive and just a quick check
2240
+ # the types will need to be checked more thoroughly when the
2241
+ # multi-source populate gets added
2242
+ if (ref $_ [0] eq ' ARRAY' ) {
2243
+ return unless @{$_ [0]};
2241
2244
2242
- if (defined wantarray ) {
2243
- my @created = map { $self -> new_result($_ )-> insert } @$data ;
2244
- return wantarray ? @created : \@created ;
2245
+ $data = $_ [0] if (ref $_ [0][0] eq ' HASH' or ref $_ [0][0] eq ' ARRAY' );
2245
2246
}
2246
- else {
2247
- my $first = $data -> [0];
2248
2247
2249
- # if a column is a registered relationship, and is a non-blessed hash/array, consider
2250
- # it relationship data
2251
- my (@rels , @columns );
2252
- my $rsrc = $self -> result_source;
2253
- my $rels = { map { $_ => $rsrc -> relationship_info($_ ) } $rsrc -> relationships };
2254
- for (keys %$first ) {
2255
- my $ref = ref $first -> {$_ };
2256
- $rels -> {$_ } && ($ref eq ' ARRAY' or $ref eq ' HASH' )
2257
- ? push @rels , $_
2258
- : push @columns , $_
2248
+ $self -> throw_exception(' Populate expects an arrayref of hashrefs or arrayref of arrayrefs' )
2249
+ unless $data ;
2250
+
2251
+ # FIXME - no cref handling
2252
+ # At this point assume either hashes or arrays
2253
+
2254
+ if (defined wantarray ) {
2255
+ my @results ;
2256
+
2257
+ $guard = $self -> result_source-> schema-> storage-> txn_scope_guard
2258
+ if ( @$data > 2 or ( @$data == 2 and ref $data -> [0] eq ' ARRAY' ) );
2259
+
2260
+ if (ref $data -> [0] eq ' ARRAY' ) {
2261
+ @results = map
2262
+ { my $vals = $_ ; $self -> new_result({ map { $data -> [0][$_ ] => $vals -> [$_ ] } 0..$# {$data -> [0]} })-> insert }
2263
+ @{$data }[1 .. $# $data ]
2259
2264
;
2260
2265
}
2266
+ else {
2267
+ @results = map { $self -> new_result($_ )-> insert } @$data ;
2268
+ }
2269
+
2270
+ $guard -> commit if $guard ;
2271
+ return wantarray ? @results : \@results ;
2272
+ }
2273
+
2274
+ # we have to deal with *possibly incomplete* related data
2275
+ # this means we have to walk the data structure twice
2276
+ # whether we want this or not
2277
+ # jnap, I hate you ;)
2278
+ my $rsrc = $self -> result_source;
2279
+ my $rel_info = { map { $_ => $rsrc -> relationship_info($_ ) } $rsrc -> relationships };
2280
+
2281
+ my ($colinfo , $colnames , $slices_with_rels );
2282
+ my $data_start = 0;
2283
+
2284
+ DATA_SLICE:
2285
+ for my $i (0 .. $# $data ) {
2286
+
2287
+ my $current_slice_seen_rel_infos ;
2261
2288
2262
- my @pks = $rsrc -> primary_columns;
2289
+ # ## Determine/Supplement collists
2290
+ # ## BEWARE - This is a hot piece of code, a lot of weird idioms were used
2291
+ if ( ref $data -> [$i ] eq ' ARRAY' ) {
2263
2292
2264
- # # do the belongs_to relationships
2265
- foreach my $index (0.. $# $data ) {
2293
+ # positional(!) explicit column list
2294
+ if ( $i == 0 ) {
2266
2295
2267
- # delegate to create() for any dataset without primary keys with specified relationships
2268
- if (grep { !defined $data -> [$index ]-> {$_ } } @pks ) {
2269
- for my $r (@rels ) {
2270
- if (grep { ref $data -> [$index ]{$r } eq $_ } qw/ HASH ARRAY/ ) { # a related set must be a HASH or AoH
2271
- my @ret = $self -> populate($data );
2272
- return ;
2296
+ $colinfo -> {$data -> [0][$_ ]} = { pos => $_ , name => $data -> [0][$_ ] } and push @$colnames , $data -> [0][$_ ]
2297
+ for 0 .. $# {$data -> [0]};
2298
+
2299
+ $data_start = 1;
2300
+
2301
+ next DATA_SLICE;
2302
+ }
2303
+ else {
2304
+ for (values %$colinfo ) {
2305
+ if ($_ -> {is_rel } ||= (
2306
+ $rel_info -> {$_ -> {name }}
2307
+ and
2308
+ (
2309
+ ref $data -> [$i ][$_ -> {pos }] eq ' ARRAY'
2310
+ or
2311
+ ref $data -> [$i ][$_ -> {pos }] eq ' HASH'
2312
+ or
2313
+ ( defined blessed $data -> [$i ][$_ -> {pos }] and $data -> [$i ][$_ -> {pos }]-> isa(' DBIx::Class::Row' ) )
2314
+ )
2315
+ and
2316
+ 1
2317
+ )) {
2318
+
2319
+ # moar sanity check... sigh
2320
+ for ( ref $data -> [$i ][$_ -> {pos }] eq ' ARRAY' ? @{$data -> [$i ][$_ -> {pos }]} : $data -> [$i ][$_ -> {pos }] ) {
2321
+ if ( defined blessed $_ and $_ -> isa(' DBIx::Class::Row' ) ) {
2322
+ carp_unique(" Fast-path populate() with supplied related objects is not possible - falling back to regular create()" );
2323
+ return my $throwaway = $self -> populate(@_ );
2324
+ }
2325
+ }
2326
+
2327
+ push @$current_slice_seen_rel_infos , $rel_info -> {$_ -> {name }};
2273
2328
}
2274
2329
}
2275
2330
}
2276
2331
2277
- foreach my $rel (@rels ) {
2278
- next unless ref $data -> [$index ]-> {$rel } eq " HASH" ;
2279
- my $result = $self -> related_resultset($rel )-> new_result($data -> [$index ]-> {$rel })-> insert;
2280
- my (undef , $reverse_relinfo ) = %{$rsrc -> reverse_relationship_info($rel )};
2281
- my $related = $result -> result_source-> _resolve_condition(
2282
- $reverse_relinfo -> {cond },
2283
- $self ,
2284
- $result ,
2285
- $rel ,
2286
- );
2287
-
2288
- delete $data -> [$index ]-> {$rel };
2289
- $data -> [$index ] = {%{$data -> [$index ]}, %$related };
2290
-
2291
- push @columns , keys %$related if $index == 0;
2332
+ if ($current_slice_seen_rel_infos ) {
2333
+ push @$slices_with_rels , { map { $colnames -> [$_ ] => $data -> [$i ][$_ ] } 0 .. $# $colnames };
2334
+
2335
+ # this is needed further down to decide whether or not to fallback to create()
2336
+ $colinfo -> {$colnames -> [$_ ]}{seen_null } ||= ! defined $data -> [$i ][$_ ]
2337
+ for 0 .. $# $colnames ;
2292
2338
}
2293
2339
}
2340
+ elsif ( ref $data -> [$i ] eq ' HASH' ) {
2294
2341
2295
- # # inherit the data locked in the conditions of the resultset
2296
- my ($rs_data ) = $self -> _merge_with_rscond({});
2297
- delete @{$rs_data }{@columns };
2298
-
2299
- # # do bulk insert on current row
2300
- $rsrc -> storage-> insert_bulk(
2301
- $rsrc ,
2302
- [@columns , keys %$rs_data ],
2303
- [ map { [ @$_ {@columns }, values %$rs_data ] } @$data ],
2304
- );
2342
+ for ( sort keys %{$data -> [$i ]} ) {
2305
2343
2306
- # # do the has_many relationships
2307
- foreach my $item (@$data ) {
2344
+ $colinfo -> {$_ } ||= do {
2308
2345
2309
- my $main_row ;
2346
+ $self -> throw_exception(" Column '$_ ' must be present in supplied explicit column list" )
2347
+ if $data_start ; # it will be 0 on AoH, 1 on AoA
2310
2348
2311
- foreach my $rel (@rels ) {
2312
- next unless ref $item -> {$rel } eq " ARRAY" && @{ $item -> {$rel } };
2349
+ push @$colnames , $_ ;
2313
2350
2314
- $main_row ||= $self -> new_result({map { $_ => $item -> {$_ } } @pks });
2351
+ # RV
2352
+ { pos => $# $colnames , name => $_ }
2353
+ };
2315
2354
2316
- my $child = $main_row -> $rel ;
2355
+ if ($colinfo -> {$_ }{is_rel } ||= (
2356
+ $rel_info -> {$_ }
2357
+ and
2358
+ (
2359
+ ref $data -> [$i ]{$_ } eq ' ARRAY'
2360
+ or
2361
+ ref $data -> [$i ]{$_ } eq ' HASH'
2362
+ or
2363
+ ( defined blessed $data -> [$i ]{$_ } and $data -> [$i ]{$_ }-> isa(' DBIx::Class::Row' ) )
2364
+ )
2365
+ and
2366
+ 1
2367
+ )) {
2368
+
2369
+ # moar sanity check... sigh
2370
+ for ( ref $data -> [$i ]{$_ } eq ' ARRAY' ? @{$data -> [$i ]{$_ }} : $data -> [$i ]{$_ } ) {
2371
+ if ( defined blessed $_ and $_ -> isa(' DBIx::Class::Row' ) ) {
2372
+ carp_unique(" Fast-path populate() with supplied related objects is not possible - falling back to regular create()" );
2373
+ return my $throwaway = $self -> populate(@_ );
2374
+ }
2375
+ }
2317
2376
2318
- my $related = $child -> result_source-> _resolve_condition(
2319
- $rels -> {$rel }{cond },
2320
- $child ,
2321
- $main_row ,
2322
- $rel ,
2323
- );
2377
+ push @$current_slice_seen_rel_infos , $rel_info -> {$_ };
2378
+ }
2379
+ }
2324
2380
2325
- my @rows_to_add = ref $item -> { $rel } eq ' ARRAY ' ? @{ $item -> { $rel }} : ( $item -> { $rel });
2326
- my @populate = map { { %$_ , %$related } } @rows_to_add ;
2381
+ if ( $current_slice_seen_rel_infos ) {
2382
+ push @$slices_with_rels , $data -> [ $i ] ;
2327
2383
2328
- $child -> populate( \@populate );
2384
+ # this is needed further down to decide whether or not to fallback to create()
2385
+ $colinfo -> {$_ }{seen_null } ||= ! defined $data -> [$i ]{$_ }
2386
+ for keys %{$data -> [$i ]};
2329
2387
}
2330
2388
}
2389
+ else {
2390
+ $self -> throw_exception(' Unexpected populate() data structure member type: ' . ref $data -> [$i ] );
2391
+ }
2392
+
2393
+ if ( grep
2394
+ { $_ -> {attrs }{is_depends_on } }
2395
+ @{ $current_slice_seen_rel_infos || [] }
2396
+ ) {
2397
+ carp_unique(" Fast-path populate() of belongs_to relationship data is not possible - falling back to regular create()" );
2398
+ return my $throwaway = $self -> populate(@_ );
2399
+ }
2331
2400
}
2332
- }
2333
2401
2402
+ if ( $slices_with_rels ) {
2334
2403
2335
- # populate() arguments went over several incarnations
2336
- # What we ultimately support is AoH
2337
- sub _normalize_populate_args {
2338
- my ($self , $arg ) = @_ ;
2404
+ # need to exclude the rel "columns"
2405
+ $colnames = [ grep { ! $colinfo -> {$_ }{is_rel } } @$colnames ];
2339
2406
2340
- if (ref $arg eq ' ARRAY' ) {
2341
- if (!@$arg ) {
2342
- return [];
2343
- }
2344
- elsif (ref $arg -> [0] eq ' HASH' ) {
2345
- return $arg ;
2407
+ # extra sanity check - ensure the main source is in fact identifiable
2408
+ # the localizing of nullability is insane, but oh well... the use-case is legit
2409
+ my $ci = $rsrc -> columns_info($colnames );
2410
+
2411
+ $ci -> {$_ } = { %{$ci -> {$_ }}, is_nullable => 0 }
2412
+ for grep { ! $colinfo -> {$_ }{seen_null } } keys %$ci ;
2413
+
2414
+ unless ( $rsrc -> _identifying_column_set($ci ) ) {
2415
+ carp_unique(" Fast-path populate() of non-uniquely identifiable rows with related data is not possible - falling back to regular create()" );
2416
+ return my $throwaway = $self -> populate(@_ );
2346
2417
}
2347
- elsif (ref $arg -> [0] eq ' ARRAY' ) {
2348
- my @ret ;
2349
- my @colnames = @{$arg -> [0]};
2350
- foreach my $values (@{$arg }[1 .. $# $arg ]) {
2351
- push @ret , { map { $colnames [$_ ] => $values -> [$_ ] } (0 .. $#colnames ) };
2418
+ }
2419
+
2420
+ # ## inherit the data locked in the conditions of the resultset
2421
+ my ($rs_data ) = $self -> _merge_with_rscond({});
2422
+ delete @{$rs_data }{@$colnames }; # passed-in stuff takes precedence
2423
+
2424
+ # if anything left - decompose rs_data
2425
+ my $rs_data_vals ;
2426
+ if (keys %$rs_data ) {
2427
+ push @$rs_data_vals , $rs_data -> {$_ }
2428
+ for sort keys %$rs_data ;
2429
+ }
2430
+
2431
+ # ## start work
2432
+ $guard = $rsrc -> schema-> storage-> txn_scope_guard
2433
+ if $slices_with_rels ;
2434
+
2435
+ # ## main source data
2436
+ # FIXME - need to switch entirely to a coderef-based thing,
2437
+ # so that large sets aren't copied several times... I think
2438
+ $rsrc -> storage-> insert_bulk(
2439
+ $rsrc ,
2440
+ [ @$colnames , sort keys %$rs_data ],
2441
+ [ map {
2442
+ ref $data -> [$_ ] eq ' ARRAY'
2443
+ ? (
2444
+ $slices_with_rels ? [ @{$data -> [$_ ]}[0..$# $colnames ], @{$rs_data_vals ||[]} ] # the collist changed
2445
+ : $rs_data_vals ? [ @{$data -> [$_ ]}, @$rs_data_vals ]
2446
+ : $data -> [$_ ]
2447
+ )
2448
+ : [ @{$data -> [$_ ]}{@$colnames }, @{$rs_data_vals ||[]} ]
2449
+ } $data_start .. $# $data ],
2450
+ );
2451
+
2452
+ # ## do the children relationships
2453
+ if ( $slices_with_rels ) {
2454
+ my @rels = grep { $colinfo -> {$_ }{is_rel } } keys %$colinfo
2455
+ or die ' wtf... please report a bug with DBIC_TRACE=1 output (stacktrace)' ;
2456
+
2457
+ for my $sl (@$slices_with_rels ) {
2458
+
2459
+ my ($main_proto , $main_proto_rs );
2460
+ for my $rel (@rels ) {
2461
+ next unless defined $sl -> {$rel };
2462
+
2463
+ $main_proto ||= {
2464
+ %$rs_data ,
2465
+ (map { $_ => $sl -> {$_ } } @$colnames ),
2466
+ };
2467
+
2468
+ unless (defined $colinfo -> {$rel }{rs }) {
2469
+
2470
+ $colinfo -> {$rel }{rs } = $rsrc -> related_source($rel )-> resultset;
2471
+
2472
+ $colinfo -> {$rel }{fk_map } = { reverse %{ $rsrc -> _resolve_relationship_condition(
2473
+ rel_name => $rel ,
2474
+ self_alias => " \xFE " , # irrelevant
2475
+ foreign_alias => " \xFF " , # irrelevant
2476
+ )-> {identity_map } || {} } };
2477
+
2478
+ }
2479
+
2480
+ $colinfo -> {$rel }{rs }-> search({ map # only so that we inherit them values properly, no actual search
2481
+ {
2482
+ $_ => { ' =' =>
2483
+ ( $main_proto_rs ||= $rsrc -> resultset-> search($main_proto ) )
2484
+ -> get_column( $colinfo -> {$rel }{fk_map }{$_ } )
2485
+ -> as_query
2486
+ }
2487
+ }
2488
+ keys %{$colinfo -> {$rel }{fk_map }}
2489
+ })-> populate( ref $sl -> {$rel } eq ' ARRAY' ? $sl -> {$rel } : [ $sl -> {$rel } ] );
2490
+
2491
+ 1;
2352
2492
}
2353
- return \@ret ;
2354
2493
}
2355
2494
}
2356
2495
2357
- $self -> throw_exception( ' Populate expects an arrayref of hashrefs or arrayref of arrayrefs ' ) ;
2496
+ $guard -> commit if $guard ;
2358
2497
}
2359
2498
2360
2499
=head2 pager
0 commit comments