use DBIx::Class::ResultSetColumn;
use DBIx::Class::ResultSourceHandle;
use List::Util ();
+use Scalar::Util ();
use base qw/DBIx::Class/;
__PACKAGE__->mk_group_accessors('simple' => qw/result_class _source_handle/);
=head1 SYNOPSIS
- my $rs = $schema->resultset('User')->search(registered => 1);
- my @rows = $schema->resultset('CD')->search(year => 2005);
+ my $rs = $schema->resultset('User')->search({ registered => 1 });
+ my @rows = $schema->resultset('CD')->search({ year => 2005 })->all();
=head1 DESCRIPTION
=head1 OVERLOADING
-If a resultset is used as a number it returns the C<count()>. However, if it is used as a boolean it is always true. So if you want to check if a result set has any results use C<if $rs != 0>. C<if $rs> will always be true.
+If a resultset is used in a numeric context it returns the L</count>.
+However, if it is used in a booleand context it is always true. So if
+you want to check if a resultset has any results use C<if $rs != 0>.
+C<if $rs> will always be true.
=head1 METHODS
If the C<key> is specified as C<primary>, it searches only on the primary key.
If no C<key> is specified, it searches on all unique constraints defined on the
-source, including the primary key.
+source for which column data is provided, including the primary key.
If your table does not have a primary key, you B<must> provide a value for the
C<key> attribute matching one of the unique constraints on the source.
+Note: If your query does not return only one row, a warning is generated:
+
+ Query returned more than one row
+
See also L</find_or_create> and L</update_or_create>. For information on how to
declare unique constraints, see
L<DBIx::Class::ResultSource/add_unique_constraint>.
@{$input_query}{@keys} = values %related;
}
- my @unique_queries = $self->_unique_queries($input_query, $attrs);
# Build the final query: Default to the disjunction of the unique queries,
# but allow the input query in case the ResultSet defines the query or the
# user is abusing find
my $alias = exists $attrs->{alias} ? $attrs->{alias} : $self->{attrs}{alias};
- my $query = @unique_queries
- ? [ map { $self->_add_alias($_, $alias) } @unique_queries ]
- : $self->_add_alias($input_query, $alias);
+ my $query;
+ if (exists $attrs->{key}) {
+ my @unique_cols = $self->result_source->unique_constraint_columns($attrs->{key});
+ my $unique_query = $self->_build_unique_query($input_query, \@unique_cols);
+ $query = $self->_add_alias($unique_query, $alias);
+ }
+ else {
+ my @unique_queries = $self->_unique_queries($input_query, $attrs);
+ $query = @unique_queries
+ ? [ map { $self->_add_alias($_, $alias) } @unique_queries ]
+ : $self->_add_alias($input_query, $alias);
+ }
# Run the query
if (keys %$attrs) {
my $rs = $self->search($query, $attrs);
- return keys %{$rs->_resolved_attrs->{collapse}} ? $rs->next : $rs->single;
+ if (keys %{$rs->_resolved_attrs->{collapse}}) {
+ my $row = $rs->next;
+ carp "Query returned more than one row" if $rs->next;
+ return $row;
+ }
+ else {
+ return $rs->single;
+ }
}
else {
- return keys %{$self->_resolved_attrs->{collapse}}
- ? $self->search($query)->next
- : $self->single($query);
+ if (keys %{$self->_resolved_attrs->{collapse}}) {
+ my $rs = $self->search($query);
+ my $row = $rs->next;
+ carp "Query returned more than one row" if $rs->next;
+ return $row;
+ }
+ else {
+ return $self->single($query);
+ }
}
}
return shift->related_resultset(shift)->search(@_);
}
+=head2 search_related_rs
+
+This method works exactly the same as search_related, except that
+it guarantees a restultset, even in list context.
+
+=cut
+
+sub search_related_rs {
+ return shift->related_resultset(shift)->search_rs(@_);
+}
+
=head2 cursor
=over 4
Inflates the first result without creating a cursor if the resultset has
any records in it; if not returns nothing. Used by L</find> as an optimisation.
-Can optionally take an additional condition *only* - this is a fast-code-path
-method; if you need to add extra joins or similar call ->search and then
-->single without a condition on the $rs returned from that.
+Can optionally take an additional condition B<only> - this is a fast-code-path
+method; if you need to add extra joins or similar call L</search> and then
+L</single> without a condition on the L<DBIx::Class::ResultSet> returned from
+that.
+
+B<Note>: As of 0.08100, this method assumes that the query returns only one
+row. If more than one row is returned, you will receive a warning:
+
+ Query returned more than one row
+
+In this case, you should be using L</first> or L</find> instead.
=cut
# precendence must be given to passed values over values inherited from the cond,
# so the order here is important.
- my %new = (
- %{ $self->_remove_alias($collapsed_cond, $alias) },
+ my %new;
+ my %implied = %{$self->_remove_alias($collapsed_cond, $alias)};
+ while( my($col,$value) = each %implied ){
+ if(ref($value) eq 'HASH' && keys(%$value) && (keys %$value)[0] eq '='){
+ $new{$col} = $value->{'='};
+ next;
+ }
+ $new{$col} = $value if $self->_is_deterministic_value($value);
+ }
+
+ %new = (
+ %new,
%{ $self->_remove_alias($values, $alias) },
-source_handle => $self->_source_handle,
-result_source => $self->result_source, # DO NOT REMOVE THIS, REQUIRED
return $self->result_class->new(\%new);
}
+# _is_deterministic_value
+#
+# Make an effor to strip non-deterministic values from the condition,
+# to make sure new_result chokes less
+
+sub _is_deterministic_value {
+ my $self = shift;
+ my $value = shift;
+ my $ref_type = ref $value;
+ return 1 if $ref_type eq '' || $ref_type eq 'SCALAR';
+ return 1 if Scalar::Util::blessed($value);
+ return 0;
+}
+
# _collapse_cond
#
# Recursively collapse the condition.
{ key => 'cd_artist_title' }
);
+Note: Because find_or_create() reads from the database and then
+possibly inserts based on the result, this method is subject to a race
+condition. Another process could create a record in the table after
+the find has completed and before the create has started. To avoid
+this problem, use find_or_create() inside a transaction.
+
See also L</find> and L</update_or_create>. For information on how to declare
unique constraints, see L<DBIx::Class::ResultSource/add_unique_constraint>.
Gets the contents of the cache for the resultset, if the cache is set.
+The cache is populated either by using the L</prefetch> attribute to
+L</search> or by calling L</set_cache>.
+
=cut
sub get_cache {
if the cache is set the resultset will return the cached objects rather
than re-querying the database even if the cache attr is not set.
+The contents of the cache can also be populated by using the
+L</prefetch> attribute to L</search>.
+
=cut
sub set_cache {
case.
Simple prefetches will be joined automatically, so there is no need
-for a C<join> attribute in the above search. If you're prefetching to
-depth (e.g. { cd => { artist => 'label' } or similar), you'll need to
-specify the join as well.
+for a C<join> attribute in the above search.
C<prefetch> can be used with the following relationship types: C<belongs_to>,
C<has_one> (or if you're using C<add_relationship>, any relationship declared
-with an accessor type of 'single' or 'filter').
+with an accessor type of 'single' or 'filter'). A more complex example that
+prefetches an artists cds, the tracks on those cds, and the tags associted
+with that artist is given below (assuming many-to-many from artists to tags):
+
+ my $rs = $schema->resultset('Artist')->search(
+ undef,
+ {
+ prefetch => [
+ { cds => 'tracks' },
+ { artist_tags => 'tags' }
+ ]
+ }
+ );
+
+
+B<NOTE:> If you specify a C<prefetch> attribute, the C<join> and C<select>
+attributes will be ignored.
=head2 page
# SELECT child.* FROM person child
# INNER JOIN person father ON child.father_id = father.id
+=head2 for
+
+=over 4
+
+=item Value: ( 'update' | 'shared' )
+
+=back
+
+Set to 'update' for a SELECT ... FOR UPDATE or 'shared' for a SELECT
+... FOR SHARED.
+
=cut
1;