X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=70ff1fb18aeac733610c860c8f4ac042f2637ed1;hb=8893ffd05dee05ba32bb64b4ce16ca805145aeb3;hp=71595e3ac386f7604a63147c2b54b1e313ae821d;hpb=2268474f6427676f69515a0ea47d638eaa6b497c;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 71595e3..70ff1fb 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -207,11 +207,23 @@ automatically get one from e.g. a L called in scalar context: my $rs = $schema->resultset('CD')->search({ title => '100th Window' }); -IMPORTANT: If called on an object, proxies to new_result instead so +=over + +=item WARNING + +If called on an object, proxies to L instead, so my $cd = $schema->resultset('CD')->new({ title => 'Spoon' }); -will return a CD object, not a ResultSet. +will return a CD object, not a ResultSet, and is equivalent to: + + my $cd = $schema->resultset('CD')->new_result({ title => 'Spoon' }); + +Please also keep in mind that many internals call C directly, +so overloading this method with the idea of intercepting new result object +creation B. See also warning pertaining to L. + +=back =cut @@ -617,9 +629,18 @@ sub _stack_cond { =head2 search_literal +B: C is provided for Class::DBI compatibility and +should only be used in that context. C is a convenience +method. It is equivalent to calling C<< $schema->search(\[]) >>, but if you +want to ensure columns are bound correctly, use L. + +See L and +L for searching techniques that do not +require C. + =over 4 -=item Arguments: $sql_fragment, @bind_values +=item Arguments: $sql_fragment, @standalone_bind_values =item Return Value: $resultset (scalar context) || @row_objs (list context) @@ -631,21 +652,11 @@ sub _stack_cond { Pass a literal chunk of SQL to be added to the conditional part of the resultset query. -CAVEAT: C is provided for Class::DBI compatibility and should -only be used in that context. C is a convenience method. -It is equivalent to calling $schema->search(\[]), but if you want to ensure -columns are bound correctly, use C. - Example of how to use C instead of C my @cds = $cd_rs->search_literal('cdid = ? AND (artist = ? OR artist = ?)', (2, 1, 2)); my @cds = $cd_rs->search(\[ 'cdid = ? AND (artist = ? OR artist = ?)', [ 'cdid', 2 ], [ 'artist', 1 ], [ 'artist', 2 ] ]); - -See L and -L for searching techniques that do not -require C. - =cut sub search_literal { @@ -654,7 +665,7 @@ sub search_literal { if ( @bind && ref($bind[-1]) eq 'HASH' ) { $attr = pop @bind; } - return $self->search(\[ $sql, map [ __DUMMY__ => $_ ], @bind ], ($attr || () )); + return $self->search(\[ $sql, map [ {} => $_ ], @bind ], ($attr || () )); } =head2 find @@ -1628,9 +1639,12 @@ sub _bool { =head2 count_literal +B: C is provided for Class::DBI compatibility and +should only be used in that context. See L for further info. + =over 4 -=item Arguments: $sql_fragment, @bind_values +=item Arguments: $sql_fragment, @standalone_bind_values =item Return Value: $count @@ -1748,54 +1762,49 @@ sub _rs_update_delete { my $attrs = { %{$self->_resolved_attrs} }; - # "needs" is a strong word here - if the subquery is part of an IN clause - no point of - # even adding the group_by. It will really be used only when composing a poor-man's - # multicolumn-IN equivalent OR set - my $needs_group_by_subq = defined $attrs->{group_by}; + my $existing_group_by = delete $attrs->{group_by}; + my $needs_subq = defined $existing_group_by; + + # simplify the joinmap and maybe decide if a subquery is necessary + my $relation_classifications = {}; - # simplify the joinmap and maybe decide if a grouping (and thus subquery) is necessary - my $relation_classifications; if (ref($attrs->{from}) eq 'ARRAY') { - $attrs->{from} = $storage->_prune_unused_joins ($attrs->{from}, $attrs->{select}, $cond, $attrs); + # if we already know we need a subq, no point of classifying relations + if (!$needs_subq and @{$attrs->{from}} > 1) { + $attrs->{from} = $storage->_prune_unused_joins ($attrs->{from}, $attrs->{select}, $cond, $attrs); - $relation_classifications = $storage->_resolve_aliastypes_from_select_args ( - [ @{$attrs->{from}}[1 .. $#{$attrs->{from}}] ], - $attrs->{select}, - $cond, - $attrs - ) unless $needs_group_by_subq; # we already know we need a group, no point of resolving them + $relation_classifications = $storage->_resolve_aliastypes_from_select_args ( + [ @{$attrs->{from}}[1 .. $#{$attrs->{from}}] ], + $attrs->{select}, + $cond, + $attrs + ); + } } else { - $needs_group_by_subq ||= 1; # if {from} is unparseable assume the worst + $needs_subq ||= 1; # if {from} is unparseable assume the worst } - $needs_group_by_subq ||= exists $relation_classifications->{multiplying}; - - # if no subquery - life is easy-ish - unless ( - $needs_group_by_subq - or - keys %$relation_classifications # if any joins at all - need to wrap a subq - or - $self->_has_resolved_attr(qw/rows offset/) # limits call for a subq + # do we need anything like a subquery? + if ( + ! $needs_subq + and + ! keys %{ $relation_classifications->{restricting} || {} } + and + ! $self->_has_resolved_attr(qw/rows offset/) # limits call for a subq ) { # Most databases do not allow aliasing of tables in UPDATE/DELETE. Thus # a condition containing 'me' or other table prefixes will not work - # at all. What this code tries to do (badly) is to generate a condition - # with the qualifiers removed, by exploiting the quote mechanism of sqla - # - # this is atrocious and should be replaced by normal sqla introspection - # one sunny day - my ($sql, @bind) = do { + # at all. Tell SQLMaker to dequalify idents via a gross hack. + my $cond = do { my $sqla = $rsrc->storage->sql_maker; local $sqla->{_dequalify_idents} = 1; - $sqla->_recurse_where($self->{cond}); - } if $self->{cond}; - + \[ $sqla->_recurse_where($self->{cond}) ]; + }; return $rsrc->storage->$op( $rsrc, $op eq 'update' ? $values : (), - $self->{cond} ? \[$sql, @bind] : (), + $cond, ); } @@ -1807,7 +1816,6 @@ sub _rs_update_delete { $rsrc->source_name, ) ); - my $existing_group_by = delete $attrs->{group_by}; # make a new $rs selecting only the PKs (that's all we really need for the subq) delete $attrs->{$_} for qw/collapse _collapse_order_by select _prefetch_selector_range as/; @@ -1838,13 +1846,15 @@ sub _rs_update_delete { ); } else { + # if all else fails - get all primary keys and operate over a ORed set # wrap in a transaction for consistency # this is where the group_by starts to matter - my $subq_group_by; - if ($needs_group_by_subq) { - $subq_group_by = $attrs->{columns}; - + if ( + $existing_group_by + or + keys %{ $relation_classifications->{multiplying} || {} } + ) { # make sure if there is a supplied group_by it matches the columns compiled above # perfectly. Anything else can not be sanely executed on most databases so croak # right then and there @@ -1857,7 +1867,7 @@ sub _rs_update_delete { if ( join ("\x00", sort @current_group_by) ne - join ("\x00", sort @$subq_group_by ) + join ("\x00", sort @{$attrs->{columns}} ) ) { $self->throw_exception ( "You have just attempted a $op operation on a resultset which does group_by" @@ -1868,12 +1878,14 @@ sub _rs_update_delete { ); } } + + $subrs = $subrs->search({}, { group_by => $attrs->{columns} }); } my $guard = $storage->txn_scope_guard; my @op_condition; - for my $row ($subrs->search({}, { group_by => $subq_group_by })->cursor->all) { + for my $row ($subrs->cursor->all) { push @op_condition, { map { $idcols->[$_] => $row->[$_] } (0 .. $#$idcols) @@ -2311,7 +2323,11 @@ Passes the hashref of input on to L. sub new_result { my ($self, $values) = @_; - $self->throw_exception( "new_result needs a hash" ) + + $self->throw_exception( "new_result takes only one argument - a hashref of values" ) + if @_ > 2; + + $self->throw_exception( "new_result expects a hashref" ) unless (ref $values eq 'HASH'); my ($merged_cond, $cols_from_relations) = $self->_merge_with_rscond($values); @@ -2497,7 +2513,7 @@ sub _remove_alias { =item Arguments: none -=item Return Value: \[ $sql, @bind ] +=item Return Value: \[ $sql, L<@bind_values|/DBIC BIND VALUES> ] =back @@ -2601,7 +2617,6 @@ This can be applied recursively, and will work correctly for a structure with an arbitrary depth and width, as long as the relationships actually exists and the correct column data has been supplied. - Instead of hashrefs of plain related data (key/value pairs), you may also pass new or inserted objects. New objects (not inserted yet, see L), will be inserted into their appropriate tables. @@ -2646,7 +2661,8 @@ it is a simple shortcut for C<< $self->new_result($attrs)->insert >>, a lot of the internals simply never call it, so your override will be bypassed more often than not. Override either L or L depending on how early in the -L process you need to intervene. +L process you need to intervene. See also warning pertaining to +L. =back @@ -2724,7 +2740,7 @@ database! year => 2005, }); - if( $cd->in_storage ) { + if( !$cd->in_storage ) { # do some stuff $cd->insert; } @@ -2795,20 +2811,6 @@ L and L instead. Don't forget to call L to save the newly created row to the database! - my $cd = $schema->resultset('CD')->update_or_new( - { - artist => 'Massive Attack', - title => 'Mezzanine', - year => 1998, - }, - { key => 'cd_artist_title' } - ); - - if( $cd->in_storage ) { - # do some stuff - $cd->insert; - } - =cut sub update_or_create { @@ -3482,7 +3484,6 @@ sub _resolved_attrs { $attrs->{_collapse_order_by} = \@$prefetch_ordering; } - # if both page and offset are specified, produce a combined offset # even though it doesn't make much sense, this is what pre 081xx has # been doing @@ -4003,6 +4004,12 @@ to Earth' and a cd with title 'Popular'. If you want to fetch related objects from other tables as well, see C below. + NOTE: An internal join-chain pruner will discard certain joins while + constructing the actual SQL query, as long as the joins in question do not + affect the retrieved result. This for example includes 1:1 left joins + that are not part of the restriction specification (WHERE/HAVING) nor are + a part of the query selection. + For more help on using joins with search, see L. =head2 prefetch @@ -4191,6 +4198,37 @@ behavior may or may not survive the 0.09 transition. =back +=head2 alias + +=over 4 + +=item Value: $source_alias + +=back + +Sets the source alias for the query. Normally, this defaults to C, but +nested search queries (sub-SELECTs) might need specific aliases set to +reference inner queries. For example: + + my $q = $rs + ->related_resultset('CDs') + ->related_resultset('Tracks') + ->search({ + 'track.id' => { -ident => 'none_search.id' }, + }) + ->as_query; + + my $ids = $self->search({ + -not_exists => $q, + }, { + alias => 'none_search', + group_by => 'none_search.id', + })->get_column('id')->as_query; + + $self->search({ id => { -in => $ids } }) + +This attribute is directly tied to L. + =head2 page =over 4 @@ -4231,6 +4269,24 @@ rows per page if the page attribute or method is used. Specifies the (zero-based) row number for the first row to be returned, or the of the first row of the first page if paging is used. +=head2 software_limit + +=over 4 + +=item Value: (0 | 1) + +=back + +When combined with L and/or L the generated SQL will not +include any limit dialect stanzas. Instead the entire result will be selected +as if no limits were specified, and DBIC will perform the limit locally, by +artificially advancing and finishing the resulting L. + +This is the recommended way of performing resultset limiting when no sane RDBMS +implementation is available (e.g. +L using the +L hack) + =head2 group_by =over 4 @@ -4279,7 +4335,7 @@ attribute, this setting is ignored and an appropriate warning is issued. Adds to the WHERE clause. # only return rows WHERE deleted IS NULL for all searches - __PACKAGE__->resultset_attributes({ where => { deleted => undef } }); ) + __PACKAGE__->resultset_attributes({ where => { deleted => undef } }); Can be overridden by passing C<< { where => undef } >> as an attribute to a resultset. @@ -4310,12 +4366,69 @@ L. =over 4 -=item Value: ( 'update' | 'shared' ) +=item Value: ( 'update' | 'shared' | \$scalar ) =back Set to 'update' for a SELECT ... FOR UPDATE or 'shared' for a SELECT -... FOR SHARED. +... FOR SHARED. If \$scalar is passed, this is taken directly and embedded in the +query. + +=head1 DBIC BIND VALUES + +Because DBIC may need more information to bind values than just the column name +and value itself, it uses a special format for both passing and receiving bind +values. Each bind value should be composed of an arrayref of +C<< [ \%args => $val ] >>. The format of C<< \%args >> is currently: + +=over 4 + +=item dbd_attrs + +If present (in any form), this is what is being passed directly to bind_param. +Note that different DBD's expect different bind args. (e.g. DBD::SQLite takes +a single numerical type, while DBD::Pg takes a hashref if bind options.) + +If this is specified, all other bind options described below are ignored. + +=item sqlt_datatype + +If present, this is used to infer the actual bind attribute by passing to +C<< $resolved_storage->bind_attribute_by_data_type() >>. Defaults to the +"data_type" from the L. + +Note that the data type is somewhat freeform (hence the sqlt_ prefix); +currently drivers are expected to "Do the Right Thing" when given a common +datatype name. (Not ideal, but that's what we got at this point.) + +=item sqlt_size + +Currently used to correctly allocate buffers for bind_param_inout(). +Defaults to "size" from the L, +or to a sensible value based on the "data_type". + +=item dbic_colname + +Used to fill in missing sqlt_datatype and sqlt_size attributes (if they are +explicitly specified they are never overriden). Also used by some weird DBDs, +where the column name should be available at bind_param time (e.g. Oracle). + +=back + +For backwards compatibility and convenience, the following shortcuts are +supported: + + [ $name => $val ] === [ { dbic_colname => $name }, $val ] + [ \$dt => $val ] === [ { sqlt_datatype => $dt }, $val ] + [ undef, $val ] === [ {}, $val ] + +=head1 AUTHOR AND CONTRIBUTORS + +See L and L in DBIx::Class + +=head1 LICENSE + +You may distribute this code under the same terms as Perl itself. =cut