X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=60da1ca5a6c2a597b7123941d2206a984e5698d8;hb=763026c15bf2047020c261f430bd782bb5180850;hp=9bbeb73a8571f343ffc176218dac431b27c6ebe0;hpb=0e4fb877c0d647259a7b7f1f38f9dbe65b31cf6b;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 9bbeb73..60da1ca 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -7,6 +7,7 @@ use overload 'bool' => "_bool", fallback => 1; use Carp::Clan qw/^DBIx::Class/; +use DBIx::Class::Exception; use Data::Page; use Storable; use DBIx::Class::ResultSetColumn; @@ -570,12 +571,16 @@ sub _unique_queries { my $where = $self->_collapse_cond($self->{attrs}{where} || {}); my $num_where = scalar keys %$where; - my @unique_queries; + my (@unique_queries, %seen_column_combinations); foreach my $name (@constraint_names) { - my @unique_cols = $self->result_source->unique_constraint_columns($name); - my $unique_query = $self->_build_unique_query($query, \@unique_cols); + my @constraint_cols = $self->result_source->unique_constraint_columns($name); - my $num_cols = scalar @unique_cols; + my $constraint_sig = join "\x00", sort @constraint_cols; + next if $seen_column_combinations{$constraint_sig}++; + + my $unique_query = $self->_build_unique_query($query, \@constraint_cols); + + my $num_cols = scalar @constraint_cols; my $num_query = scalar keys %$unique_query; my $total = $num_query + $num_where; @@ -2192,13 +2197,14 @@ You most likely want this method when looking for existing rows using a unique constraint that is not the primary key, or looking for related rows. -If you want objects to be saved immediately, use L instead. +If you want objects to be saved immediately, use L +instead. -B: C is probably not what you want when creating a -new row in a table that uses primary keys supplied by the -database. Passing in a primary key column with a value of I -will cause L to attempt to search for a row with a value of -I. +B: Take care when using C with a table having +columns with default values that you intend to be automatically +supplied by the database (e.g. an auto_increment primary key column). +In normal usage, the value of such columns should NOT be included at +all in the call to C, even when set to C. =cut @@ -2340,11 +2346,11 @@ 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. -B: C is probably not what you want when creating -a new row in a table that uses primary keys supplied by the -database. Passing in a primary key column with a value of I -will cause L to attempt to search for a row with a value of -I. +B: Take care when using C with a table having +columns with default values that you intend to be automatically +supplied by the database (e.g. an auto_increment primary key column). +In normal usage, the value of such columns should NOT be included at +all in the call to C, even when set to C. See also L and L. For information on how to declare unique constraints, see L. @@ -2407,11 +2413,11 @@ If the C is specified as C, it searches only on the primary key. See also L and L. For information on how to declare unique constraints, see L. -B: C is probably not what you want when -looking for a row in a table that uses primary keys supplied by the -database, unless you actually have a key value. Passing in a primary -key column with a value of I will cause L to attempt to -search for a row with a value of I. +B: Take care when using C with a table having +columns with default values that you intend to be automatically +supplied by the database (e.g. an auto_increment primary key column). +In normal usage, the value of such columns should NOT be included at +all in the call to C, even when set to C. =cut @@ -2468,7 +2474,13 @@ For example: $cd->insert; } -See also L, L and L. +B: Take care when using C with a table having +columns with default values that you intend to be automatically +supplied by the database (e.g. an auto_increment primary key column). +In normal usage, the value of such columns should NOT be included at +all in the call to C, even when set to C. + +See also L, L and L. =cut @@ -2778,24 +2790,38 @@ sub _resolved_attrs { # build columns (as long as select isn't set) into a set of as/select hashes unless ( $attrs->{select} ) { - @colbits = map { - ( ref($_) eq 'HASH' ) - ? $_ - : { - ( - /^\Q${alias}.\E(.+)$/ - ? "$1" - : "$_" - ) - => - ( - /\./ - ? "$_" - : "${alias}.$_" - ) - } - } ( ref($attrs->{columns}) eq 'ARRAY' ) ? @{ delete $attrs->{columns}} : (delete $attrs->{columns} || $source->columns ); + + my @cols = ( ref($attrs->{columns}) eq 'ARRAY' ) + ? @{ delete $attrs->{columns}} + : ( + ( delete $attrs->{columns} ) + || + $source->storage->_order_select_columns( + $source, + [ $source->columns ], + ) + ) + ; + + @colbits = map { + ( ref($_) eq 'HASH' ) + ? $_ + : { + ( + /^\Q${alias}.\E(.+)$/ + ? "$1" + : "$_" + ) + => + ( + /\./ + ? "$_" + : "${alias}.$_" + ) + } + } @cols; } + # add the additional columns on foreach ( 'include_columns', '+columns' ) { push @colbits, map { @@ -2892,7 +2918,12 @@ sub _resolved_attrs { # generate the distinct induced group_by early, as prefetch will be carried via a # subquery (since a group_by is present) if (delete $attrs->{distinct}) { - $attrs->{group_by} ||= [ grep { !ref($_) || (ref($_) ne 'HASH') } @{$attrs->{select}} ]; + if ($attrs->{group_by}) { + carp ("Useless use of distinct on a grouped resultset ('distinct' is ignored when a 'group_by' is present)"); + } + else { + $attrs->{group_by} = [ grep { !ref($_) || (ref($_) ne 'HASH') } @{$attrs->{select}} ]; + } } $attrs->{collapse} ||= {}; @@ -3087,12 +3118,13 @@ See L for details. sub throw_exception { my $self=shift; + if (ref $self && $self->_source_handle->schema) { $self->_source_handle->schema->throw_exception(@_) - } else { - croak(@_); } - + else { + DBIx::Class::Exception->throw(@_); + } } # XXX: FIXME: Attributes docs need clearing up @@ -3511,7 +3543,8 @@ done. =back -Set to 1 to group by all columns. +Set to 1 to group by all columns. If the resultset already has a group_by +attribute, this setting is ignored and an appropriate warning is issued. =head2 where @@ -3545,177 +3578,6 @@ By default, searches are not cached. For more examples of using these attributes, see L. -=head2 from - -=over 4 - -=item Value: \@from_clause - -=back - -The C attribute gives you manual control over the C clause of SQL -statements generated by L, allowing you to express custom C -clauses. - -NOTE: Use this on your own risk. This allows you to shoot off your foot! - -C will usually do what you need and it is strongly recommended that you -avoid using C unless you cannot achieve the desired result using C. -And we really do mean "cannot", not just tried and failed. Attempting to use -this because you're having problems with C is like trying to use x86 -ASM because you've got a syntax error in your C. Trust us on this. - -Now, if you're still really, really sure you need to use this (and if you're -not 100% sure, ask the mailing list first), here's an explanation of how this -works. - -The syntax is as follows - - - [ - { => }, - [ - { => , -join_type => 'inner|left|right' }, - [], # nested JOIN (optional) - { => , ... (more conditions) }, - ], - # More of the above [ ] may follow for additional joins - ] - - - JOIN - - [JOIN ...] - ON = - - -An easy way to follow the examples below is to remember the following: - - Anything inside "[]" is a JOIN - Anything inside "{}" is a condition for the enclosing JOIN - -The following examples utilize a "person" table in a family tree application. -In order to express parent->child relationships, this table is self-joined: - - # Person->belongs_to('father' => 'Person'); - # Person->belongs_to('mother' => 'Person'); - -C can be used to nest joins. Here we return all children with a father, -then search against all mothers of those children: - - $rs = $schema->resultset('Person')->search( - undef, - { - alias => 'mother', # alias columns in accordance with "from" - from => [ - { mother => 'person' }, - [ - [ - { child => 'person' }, - [ - { father => 'person' }, - { 'father.person_id' => 'child.father_id' } - ] - ], - { 'mother.person_id' => 'child.mother_id' } - ], - ] - }, - ); - - # Equivalent SQL: - # SELECT mother.* FROM person mother - # JOIN ( - # person child - # JOIN person father - # ON ( father.person_id = child.father_id ) - # ) - # ON ( mother.person_id = child.mother_id ) - -The type of any join can be controlled manually. To search against only people -with a father in the person table, we could explicitly use C: - - $rs = $schema->resultset('Person')->search( - undef, - { - alias => 'child', # alias columns in accordance with "from" - from => [ - { child => 'person' }, - [ - { father => 'person', -join_type => 'inner' }, - { 'father.id' => 'child.father_id' } - ], - ] - }, - ); - - # Equivalent SQL: - # SELECT child.* FROM person child - # INNER JOIN person father ON child.father_id = father.id - -You can select from a subquery by passing a resultset to from as follows. - - $schema->resultset('Artist')->search( - undef, - { alias => 'artist2', - from => [ { artist2 => $artist_rs->as_query } ], - } ); - - # and you'll get sql like this.. - # SELECT artist2.artistid, artist2.name, artist2.rank, artist2.charfield FROM - # ( SELECT me.artistid, me.name, me.rank, me.charfield FROM artists me ) artist2 - -If you need to express really complex joins, you -can supply literal SQL to C via a scalar reference. In this case -the contents of the scalar will replace the table name associated with the -resultsource. - -WARNING: This technique might very well not work as expected on chained -searches - you have been warned. - - # Assuming the Event resultsource is defined as: - - MySchema::Event->add_columns ( - sequence => { - data_type => 'INT', - is_auto_increment => 1, - }, - location => { - data_type => 'INT', - }, - type => { - data_type => 'INT', - }, - ); - MySchema::Event->set_primary_key ('sequence'); - - # This will get back the latest event for every location. The column - # selector is still provided by DBIC, all we do is add a JOIN/WHERE - # combo to limit the resultset - - $rs = $schema->resultset('Event'); - $table = $rs->result_source->name; - $latest = $rs->search ( - undef, - { from => \ " - (SELECT e1.* FROM $table e1 - JOIN $table e2 - ON e1.location = e2.location - AND e1.sequence < e2.sequence - WHERE e2.sequence is NULL - ) me", - }, - ); - - # Equivalent SQL (with the DBIC chunks added): - - SELECT me.sequence, me.location, me.type FROM - (SELECT e1.* FROM events e1 - JOIN events e2 - ON e1.location = e2.location - AND e1.sequence < e2.sequence - WHERE e2.sequence is NULL - ) me; - =head2 for =over 4