You can also find a row by a specific unique constraint using the C<key>
attribute. For example:
- my $cd = $schema->resultset('CD')->find('Massive Attack', 'Mezzanine', { key => 'artist_title' });
+ my $cd = $schema->resultset('CD')->find('Massive Attack', 'Mezzanine', { key => 'cd_artist_title' });
Additionally, you can specify the columns explicitly by name:
artist => 'Massive Attack',
title => 'Mezzanine',
},
- { key => 'artist_title' }
+ { key => 'cd_artist_title' }
);
If the C<key> is specified as C<primary>, it searches only on the primary key.
) unless @cols;
# Parse out a hashref from input
- my $cond;
+ my $input_query;
if (ref $_[0] eq 'HASH') {
- $cond = { %{$_[0]} };
+ $input_query = { %{$_[0]} };
}
elsif (@_ == @cols) {
- $cond = {};
- @{$cond}{@cols} = @_;
+ $input_query = {};
+ @{$input_query}{@cols} = @_;
}
else {
# Compatibility: Allow e.g. find(id => $value)
- carp "find by key => value deprecated; please use a hashref instead";
- $cond = {@_};
+ carp "Find by key => value deprecated; please use a hashref instead";
+ $input_query = {@_};
}
- return $self->_find($cond, $attrs);
+ my @unique_queries = $self->_unique_queries($input_query, $attrs);
+# use Data::Dumper; warn Dumper $self->result_source->name, $input_query, \@unique_queries, $self->{attrs}->{where};
+
+ # Handle cases where the ResultSet defines the query, or where the user is
+ # abusing find
+ my $query = @unique_queries ? \@unique_queries : $input_query;
+
+ # Run the query
+ if (keys %$attrs) {
+ my $rs = $self->search($query, $attrs);
+ $rs->_resolve;
+ return keys %{$rs->{_attrs}->{collapse}} ? $rs->next : $rs->single;
+ }
+ else {
+ $self->_resolve;
+ return (keys %{$self->{_attrs}->{collapse}})
+ ? $self->search($query)->next
+ : $self->single($query);
+ }
}
-# _find
+# _unique_queries
#
-# Helper method: search against the unique constraints.
+# Build a list of queries which satisfy unique constraints.
-sub _find {
- my ($self, $cond, $attrs) = @_;
+sub _unique_queries {
+ my ($self, $query, $attrs) = @_;
- # Check the condition against our source's unique constraints
my @constraint_names = exists $attrs->{key}
? ($attrs->{key})
: $self->result_source->unique_constraint_names;
- my @unique_conds;
+ my @unique_queries;
foreach my $name (@constraint_names) {
my @unique_cols = $self->result_source->unique_constraint_columns($name);
- my $unique_cond = $self->_build_unique_query($cond, \@unique_cols);
+ my $unique_query = $self->_build_unique_query($query, \@unique_cols);
- next unless scalar keys %$unique_cond == scalar @unique_cols;
+ next unless scalar keys %$unique_query;
# Add the ResultSet's alias
- foreach my $key (grep { ! m/\./ } keys %$unique_cond) {
- $unique_cond->{"$self->{attrs}{alias}.$key"} = delete $unique_cond->{$key};
+ foreach my $key (grep { ! m/\./ } keys %$unique_query) {
+ $unique_query->{"$self->{attrs}->{alias}.$key"} = delete $unique_query->{$key};
}
- push @unique_conds, $unique_cond;
+ push @unique_queries, $unique_query;
}
-# use Data::Dumper; warn Dumper $self->result_source->name, $cond, \@unique_conds;
- # Verify the query
- my $query = \@unique_conds;
- if (scalar @unique_conds == 0) {
- if (exists $attrs->{key}) {
- $self->throw_exception("required values for the $attrs->{key} key not provided");
- }
- else {
- # Compatibility: Allow broken find usage for now
- carp "find requires values for the primary key or a unique constraint"
- . "; please use search instead";
- $query = $cond;
- }
- }
-
- # Run the query
- if (keys %$attrs) {
- my $rs = $self->search($query, $attrs);
- $rs->_resolve;
- return keys %{$rs->{_attrs}->{collapse}} ? $rs->next : $rs->single;
- }
- else {
- $self->_resolve;
- return (keys %{$self->{_attrs}->{collapse}})
- ? $self->search($query)->next
- : $self->single($query);
- }
+ return @unique_queries;
}
# _build_unique_query
}
}
+ unless ($self->_is_unique_query($attrs->{where})) {
+ carp "Query not guarnteed to return a single row"
+ . "; please declare your unique constraints or use search instead";
+ }
+
my @data = $self->result_source->storage->select_single(
$attrs->{from}, $attrs->{select},
$attrs->{where},$attrs);
return (@data ? $self->_construct_object(@data) : ());
}
+# _is_unique_query
+#
+# Try to determine if the specified query is guaranteed to be unique, based on
+# the declared unique constraints.
+
+sub _is_unique_query {
+ my ($self, $query) = @_;
+
+ my $collapsed = $self->_collapse_query($query);
+# use Data::Dumper; warn Dumper $query, $collapsed;
+
+ foreach my $name ($self->result_source->unique_constraint_names) {
+ my @unique_cols = map { "$self->{attrs}->{alias}.$_" }
+ $self->result_source->unique_constraint_columns($name);
+
+ # Count the values for each unique column
+ my %seen = map { $_ => 0 } @unique_cols;
+
+ foreach my $key (keys %$collapsed) {
+ my $aliased = $key;
+ $aliased = "$self->{attrs}->{alias}.$key" unless $key =~ /\./;
+
+ next unless exists $seen{$aliased}; # Additional constraints are okay
+ $seen{$aliased} = scalar @{ $collapsed->{$key} };
+ }
+
+ # If we get 0 or more than 1 value for a column, it's not necessarily unique
+ return 1 unless grep { $_ != 1 } values %seen;
+ }
+
+ return 0;
+}
+
+# _collapse_query
+#
+# Recursively collapse the query, accumulating values for each column.
+
+sub _collapse_query {
+ my ($self, $query, $collapsed) = @_;
+
+ $collapsed ||= {};
+
+ if (ref $query eq 'ARRAY') {
+ foreach my $subquery (@$query) {
+ next unless ref $subquery; # -or
+# warn "ARRAY: " . Dumper $subquery;
+ $collapsed = $self->_collapse_query($subquery, $collapsed);
+ }
+ }
+ elsif (ref $query eq 'HASH') {
+ if (keys %$query and (keys %$query)[0] eq '-and') {
+ foreach my $subquery (@{$query->{-and}}) {
+# warn "HASH: " . Dumper $subquery;
+ $collapsed = $self->_collapse_query($subquery, $collapsed);
+ }
+ }
+ else {
+# warn "LEAF: " . Dumper $query;
+ foreach my $key (keys %$query) {
+ push @{$collapsed->{$key}}, $query->{$key};
+ }
+ }
+ }
+
+ return $collapsed;
+}
+
=head2 get_column
=over 4
artist => 'Massive Attack',
title => 'Mezzanine',
},
- { key => 'artist_title' }
+ { key => 'cd_artist_title' }
);
See also L</find> and L</update_or_create>. For information on how to declare
title => 'Mezzanine',
year => 1998,
},
- { key => 'artist_title' }
+ { key => 'cd_artist_title' }
);
If no C<key> is specified, it searches on all unique constraints defined on the