return $rs->all(); # all records for page 1
+ return $rs->page(2); # records for page 2
+
You can get a L<Data::Page> object for the resultset (suitable for use
in e.g. a template) using the C<pager> method:
This results in something like the following C<WHERE> clause:
- WHERE artist LIKE '%Lamb%' AND title LIKE '%Fear of Fours%'
+ WHERE artist LIKE ? AND title LIKE ?
+
+And the following bind values for the placeholders: C<'%Lamb%'>, C<'%Fear of
+Fours%'>.
Other queries might require slightly more complex logic:
=head2 Retrieve one and only one row from a resultset
-Sometimes you need only the first "top" row of a resultset. While this can be
-easily done with L<< $rs->first|DBIx::Class::ResultSet/first >>, it is suboptimal,
-as a full blown cursor for the resultset will be created and then immediately
-destroyed after fetching the first row object.
-L<< $rs->single|DBIx::Class::ResultSet/single >> is
-designed specifically for this case - it will grab the first returned result
+Sometimes you need only the first "top" row of a resultset. While this
+can be easily done with L<< $rs->first|DBIx::Class::ResultSet/first
+>>, it is suboptimal, as a full blown cursor for the resultset will be
+created and then immediately destroyed after fetching the first row
+object. L<< $rs->single|DBIx::Class::ResultSet/single >> is designed
+specifically for this case - it will grab the first returned result
without even instantiating a cursor.
Before replacing all your calls to C<first()> with C<single()> please observe the
=over
=item *
+
While single() takes a search condition just like search() does, it does
_not_ accept search attributes. However one can always chain a single() to
a search():
- my $top_cd = $cd_rs -> search({}, { order_by => 'rating' }) -> single;
+ my $top_cd = $cd_rs->search({}, { order_by => 'rating' })->single;
=item *
+
Since single() is the engine behind find(), it is designed to fetch a
single row per database query. Thus a warning will be issued when the
underlying SELECT returns more than one row. Sometimes however this usage
at the top of the charts at any given time. If you know what you are doing,
you can silence the warning by explicitly limiting the resultset size:
- my $top_cd = $cd_rs -> search ({}, { order_by => 'rating', rows => 1 }) -> single;
+ my $top_cd = $cd_rs->search ({}, { order_by => 'rating', rows => 1 })->single;
=back
(e.g. it contains Unions, Sub-Selects, Stored Procedures, etc.) or has to
be optimized for your database in a special way, but you still want to
get the results as a L<DBIx::Class::ResultSet>.
-The recommended way to accomplish this is by defining a separate ResultSource
-for your query. You can then inject complete SQL statements using a scalar
-reference (this is a feature of L<SQL::Abstract>).
-Say you want to run a complex custom query on your user data, here's what
-you have to add to your User class:
+This is accomplished by defining a
+L<ResultSource::View|DBIx::Class::ResultSource::View> for your query,
+almost like you would define a regular ResultSource.
- package My::Schema::Result::User;
-
- use base qw/DBIx::Class/;
+ package My::Schema::Result::UserFriendsComplex;
+ use strict;
+ use warnings;
+ use base qw/DBIx::Class::Core/;
- # ->load_components, ->table, ->add_columns, etc.
+ __PACKAGE__->table_class('DBIx::Class::ResultSource::View');
- # Make a new ResultSource based on the User class
- my $source = __PACKAGE__->result_source_instance();
- my $new_source = $source->new( $source );
- $new_source->source_name( 'UserFriendsComplex' );
+ # ->table, ->add_columns, etc.
- # Hand in your query as a scalar reference
- # It will be added as a sub-select after FROM,
- # so pay attention to the surrounding brackets!
- $new_source->name( \<<SQL );
- ( SELECT u.* FROM user u
- INNER JOIN user_friends f ON u.id = f.user_id
- WHERE f.friend_user_id = ?
- UNION
- SELECT u.* FROM user u
- INNER JOIN user_friends f ON u.id = f.friend_user_id
- WHERE f.user_id = ? )
- SQL
+ # do not attempt to deploy() this view
+ __PACKAGE__->result_source_instance->is_virtual(1);
- # Finally, register your new ResultSource with your Schema
- My::Schema->register_extra_source( 'UserFriendsComplex' => $new_source );
+ __PACKAGE__->result_source_instance->view_definition(q[
+ SELECT u.* FROM user u
+ INNER JOIN user_friends f ON u.id = f.user_id
+ WHERE f.friend_user_id = ?
+ UNION
+ SELECT u.* FROM user u
+ INNER JOIN user_friends f ON u.id = f.friend_user_id
+ WHERE f.user_id = ?
+ ]);
Next, you can execute your complex query using bind parameters like this:
- my $friends = [ $schema->resultset( 'UserFriendsComplex' )->search( {},
+ my $friends = $schema->resultset( 'UserFriendsComplex' )->search( {},
{
bind => [ 12345, 12345 ]
}
- ) ];
+ );
... and you'll get back a perfect L<DBIx::Class::ResultSet> (except, of course,
that you cannot modify the rows it contains, ie. cannot call L</update>,
L</delete>, ... on it).
-If you prefer to have the definitions of these custom ResultSources in separate
-files (instead of stuffing all of them into the same ResultSource class), you
-can achieve the same with subclassing the ResultSource class and defining the
-new ResultSource there:
-
- package My::Schema::Result::UserFriendsComplex;
+Note that you cannot have bind parameters unless is_virtual is set to true.
- use base qw/My::Schema::Result::User/;
+=over
- __PACKAGE__->table('dummy'); # currently must be called before anything else
+=item * NOTE
- # Hand in your query as a scalar reference
- # It will be added as a sub-select after FROM,
- # so pay attention to the surrounding brackets!
- __PACKAGE__->result_source_instance->name( \<<SQL );
- ( SELECT u.* FROM user u
- INNER JOIN user_friends f ON u.id = f.user_id
- WHERE f.friend_user_id = ?
- UNION
- SELECT u.* FROM user u
- INNER JOIN user_friends f ON u.id = f.friend_user_id
- WHERE f.user_id = ? )
- SQL
+If you're using the old deprecated C<< $rsrc_instance->name(\'( SELECT ...') >>
+method for custom SQL execution, you are highly encouraged to update your code
+to use a virtual view as above. If you do not want to change your code, and just
+want to suppress the deprecation warning when you call
+L<DBIx::Class::Schema/deploy>, add this line to your source definition, so that
+C<deploy> will exclude this "table":
- 1;
+ sub sqlt_deploy_hook { $_[1]->schema->drop_table ($_[1]) }
-TIMTOWDI.
+=back
=head2 Using specific columns
# SELECT name name, LENGTH( name )
# FROM artist
-Note that the C< as > attribute has absolutely nothing to with the sql
-syntax C< SELECT foo AS bar > (see the documentation in
-L<DBIx::Class::ResultSet/ATTRIBUTES>). If your alias exists as a
-column in your base class (i.e. it was added with C<add_columns>), you
-just access it as normal. Our C<Artist> class has a C<name> column, so
-we just use the C<name> accessor:
+Note that the C<as> attribute B<has absolutely nothing to do> with the sql
+syntax C< SELECT foo AS bar > (see the documentation in
+L<DBIx::Class::ResultSet/ATTRIBUTES>). You can control the C<AS> part of the
+generated SQL via the C<-as> field attribute as follows:
+
+ my $rs = $schema->resultset('Artist')->search(
+ {},
+ {
+ join => 'cds',
+ distinct => 1,
+ '+select' => [ { count => 'cds.cdid', -as => 'amount_of_cds' } ],
+ '+as' => [qw/num_cds/],
+ order_by => { -desc => 'amount_of_cds' },
+ }
+ );
+
+ # Equivalent SQL
+ # SELECT me.artistid, me.name, me.rank, me.charfield, COUNT( cds.cdid ) AS amount_of_cds
+ # FROM artist me LEFT JOIN cd cds ON cds.artist = me.artistid
+ # GROUP BY me.artistid, me.name, me.rank, me.charfield
+ # ORDER BY amount_of_cds DESC
+
+
+If your alias exists as a column in your base class (i.e. it was added with
+L<add_columns|DBIx::Class::ResultSource/add_columns>), you just access it as
+normal. Our C<Artist> class has a C<name> column, so we just use the C<name>
+accessor:
my $artist = $rs->first();
my $name = $artist->name();
# Or use DBIx::Class::AccessorGroup:
__PACKAGE__->mk_group_accessors('column' => 'name_length');
+See also L</Using SQL functions on the left hand side of a comparison>.
+
=head2 SELECT DISTINCT with multiple columns
my $rs = $schema->resultset('Artist')->search(
The following will B<not> work:
my $rs = $schema->resultset('CD')->search({
- artist_id => $inside_rs->get_column('id')->as_query,
+ artist_id => $inside_rs->get_column('id')->as_query, # does NOT work
});
=head3 Support
for you, in your My::DBIC::Schema::CD class:
# class definition as normal
- __PACKAGE__->load_components(qw/ Core /);
+ use base 'DBIx::Class::Core';
__PACKAGE__->table('cd');
# tell DBIC to use the custom ResultSet class
=head2 Using SQL functions on the left hand side of a comparison
-Using SQL functions on the left hand side of a comparison is generally
-not a good idea since it requires a scan of the entire table. However,
+Using SQL functions on the left hand side of a comparison is generally not a
+good idea since it requires a scan of the entire table. (Unless your RDBMS
+supports indexes on expressions - including return values of functions -, and
+you create an index on the return value of the function in question.) However,
it can be accomplished with C<DBIx::Class> when necessary.
If you do not have quoting on, simply include the function in your search
$rs->search({ 'YEAR(date_of_birth)' => 1979 });
-With quoting on, or for a more portable solution, use the C<where>
-attribute:
+With quoting on, or for a more portable solution, use literal SQL values with
+placeholders:
- $rs->search({}, { where => \'YEAR(date_of_birth) = 1979' });
+ $rs->search(\[ 'YEAR(date_of_birth) = ?', [ plain_value => 1979 ] ]);
-=begin hidden
+ # Equivalent SQL:
+ # SELECT * FROM employee WHERE YEAR(date_of_birth) = ?
-(When the bind args ordering bug is fixed, this technique will be better
-and can replace the one above.)
+ $rs->search({
+ name => 'Bob',
+ -nest => \[ 'YEAR(date_of_birth) = ?', [ plain_value => 1979 ] ],
+ });
-With quoting on, or for a more portable solution, use the C<where> and
-C<bind> attributes:
+ # Equivalent SQL:
+ # SELECT * FROM employee WHERE name = ? AND YEAR(date_of_birth) = ?
- $rs->search({}, {
- where => \'YEAR(date_of_birth) = ?',
- bind => [ 1979 ]
- });
+Note: the C<plain_value> string in the C<< [ plain_value => 1979 ] >> part
+should be either the same as the name of the column (do this if the type of the
+return value of the function is the same as the type of the column) or
+otherwise it's essentially a dummy string currently (use C<plain_value> as a
+habit). It is used by L<DBIx::Class> to handle special column types.
-=end hidden
+See also L<SQL::Abstract/Literal SQL with placeholders and bind values
+(subqueries)>.
=head1 JOINS AND PREFETCHING
use strict;
use warnings;
- use base qw/DBIx::Class/;
+ use base qw/DBIx::Class::Core/;
### Define what our admin class is, for ensure_class_loaded()
my $admin_class = __PACKAGE__ . '::Admin';
- __PACKAGE__->load_components(qw/Core/);
-
__PACKAGE__->table('users');
__PACKAGE__->add_columns(qw/user_id email password
### The statement below will print
print "I can do admin stuff\n" if $admin->can('do_admin_stuff');
+Alternatively you can use L<DBIx::Class::DynamicSubclass> that implements
+exactly the above functionality.
+
=head2 Skip row object creation for faster results
DBIx::Class is not built for speed, it's built for convenience and
To order C<< $book->pages >> by descending page_number, create the relation
as follows:
- __PACKAGE__->has_many('pages' => 'Page', 'book', { order_by => \'page_number DESC'} );
+ __PACKAGE__->has_many('pages' => 'Page', 'book', { order_by => { -desc => 'page_number'} } );
=head2 Filtering a relationship result set
This is straightforward using L<ManyToMany|DBIx::Class::Relationship/many_to_many>:
package My::User;
- use base 'DBIx::Class';
- __PACKAGE__->load_components('Core');
+ use base 'DBIx::Class::Core';
__PACKAGE__->table('user');
__PACKAGE__->add_columns(qw/id name/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->many_to_many('addresses' => 'user_address', 'address');
package My::UserAddress;
- use base 'DBIx::Class';
- __PACKAGE__->load_components('Core');
+ use base 'DBIx::Class::Core';
__PACKAGE__->table('user_address');
__PACKAGE__->add_columns(qw/user address/);
__PACKAGE__->set_primary_key(qw/user address/);
__PACKAGE__->belongs_to('address' => 'My::Address');
package My::Address;
- use base 'DBIx::Class';
- __PACKAGE__->load_components('Core');
+ use base 'DBIx::Class::Core';
__PACKAGE__->table('address');
__PACKAGE__->add_columns(qw/id street town area_code country/);
__PACKAGE__->set_primary_key('id');
$rs = $user->addresses(); # get all addresses for a user
$rs = $address->users(); # get all users for an address
+ my $address = $user->add_to_addresses( # returns a My::Address instance,
+ # NOT a My::UserAddress instance!
+ {
+ country => 'United Kingdom',
+ area_code => 'XYZ',
+ town => 'London',
+ street => 'Sesame',
+ }
+ );
+
=head2 Relationships across DB schemas
Mapping relationships across L<DB schemas|DBIx::Class::Manual::Glossary/DB schema>
declaration, like so...
package MyDatabase::Main::Artist;
- use base qw/DBIx::Class/;
- __PACKAGE__->load_components(qw/PK::Auto Core/);
+ use base qw/DBIx::Class::Core/;
__PACKAGE__->table('database1.artist'); # will use "database1.artist" in FROM clause
Nested transactions will work as expected. That is, only the outermost
transaction will actually issue a commit to the $dbh, and a rollback
at any level of any transaction will cause the entire nested
-transaction to fail. Support for savepoints and for true nested
-transactions (for databases that support them) will hopefully be added
-in the future.
+transaction to fail.
+
+=head2 Nested transactions and auto-savepoints
+
+If savepoints are supported by your RDBMS, it is possible to achieve true
+nested transactions with minimal effort. To enable auto-savepoints via nested
+transactions, supply the C<< auto_savepoint = 1 >> connection attribute.
+
+Here is an example of true nested transactions. In the example, we start a big
+task which will create several rows. Generation of data for each row is a
+fragile operation and might fail. If we fail creating something, depending on
+the type of failure, we want to abort the whole task, or only skip the failed
+row.
+
+ my $schema = MySchema->connect("dbi:Pg:dbname=my_db");
+
+ # Start a transaction. Every database change from here on will only be
+ # commited into the database if the eval block succeeds.
+ eval {
+ $schema->txn_do(sub {
+ # SQL: BEGIN WORK;
+
+ my $job = $schema->resultset('Job')->create({ name=> 'big job' });
+ # SQL: INSERT INTO job ( name) VALUES ( 'big job' );
+
+ for (1..10) {
+
+ # Start a nested transaction, which in fact sets a savepoint.
+ eval {
+ $schema->txn_do(sub {
+ # SQL: SAVEPOINT savepoint_0;
+
+ my $thing = $schema->resultset('Thing')->create({ job=>$job->id });
+ # SQL: INSERT INTO thing ( job) VALUES ( 1 );
+
+ if (rand > 0.8) {
+ # This will generate an error, thus setting $@
+
+ $thing->update({force_fail=>'foo'});
+ # SQL: UPDATE thing SET force_fail = 'foo'
+ # WHERE ( id = 42 );
+ }
+ });
+ };
+ if ($@) {
+ # SQL: ROLLBACK TO SAVEPOINT savepoint_0;
+
+ # There was an error while creating a $thing. Depending on the error
+ # we want to abort the whole transaction, or only rollback the
+ # changes related to the creation of this $thing
+
+ # Abort the whole job
+ if ($@ =~ /horrible_problem/) {
+ print "something horrible happend, aborting job!";
+ die $@; # rethrow error
+ }
+
+ # Ignore this $thing, report the error, and continue with the
+ # next $thing
+ print "Cannot create thing: $@";
+ }
+ # There was no error, so save all changes since the last
+ # savepoint.
+
+ # SQL: RELEASE SAVEPOINT savepoint_0;
+ }
+ });
+ };
+ if ($@) {
+ # There was an error while handling the $job. Rollback all changes
+ # since the transaction started, including the already commited
+ # ('released') savepoints. There will be neither a new $job nor any
+ # $thing entry in the database.
+
+ # SQL: ROLLBACK;
+
+ print "ERROR: $@\n";
+ }
+ else {
+ # There was no error while handling the $job. Commit all changes.
+ # Only now other connections can see the newly created $job and
+ # @things.
+
+ # SQL: COMMIT;
+
+ print "Ok\n";
+ }
+
+In this example it might be hard to see where the rollbacks, releases and
+commits are happening, but it works just the same as for plain L<<txn_do>>: If
+the C<eval>-block around C<txn_do> fails, a rollback is issued. If the C<eval>
+succeeds, the transaction is committed (or the savepoint released).
+
+While you can get more fine-grained controll using C<svp_begin>, C<svp_release>
+and C<svp_rollback>, it is strongly recommended to use C<txn_do> with coderefs.
=head1 SQL
package MyAppDB::Dual;
use strict;
use warnings;
- use base 'DBIx::Class';
- __PACKAGE__->load_components("Core");
+ use base 'DBIx::Class::Core';
__PACKAGE__->table("Dual");
__PACKAGE__->add_columns(
"dummy",
Alternatively, you can send the conversion sql scripts to your
customers as above.
-=head2 Setting quoting for the generated SQL.
+=head2 Setting quoting for the generated SQL
If the database contains column names with spaces and/or reserved words, they
need to be quoted in the SQL queries. This is done using:
- __PACKAGE__->storage->sql_maker->quote_char([ qw/[ ]/] );
- __PACKAGE__->storage->sql_maker->name_sep('.');
+ $schema->storage->sql_maker->quote_char([ qw/[ ]/] );
+ $schema->storage->sql_maker->name_sep('.');
The first sets the quote characters. Either a pair of matching
brackets, or a C<"> or C<'>:
- __PACKAGE__->storage->sql_maker->quote_char('"');
+ $schema->storage->sql_maker->quote_char('"');
Check the documentation of your database for the correct quote
characters to use. C<name_sep> needs to be set to allow the SQL
}
)
+In some cases, quoting will be required for all users of a schema. To enforce
+this, you can also overload the C<connection> method for your schema class:
+
+ sub connection {
+ my $self = shift;
+ my $rv = $self->next::method( @_ );
+ $rv->storage->sql_maker->quote_char([ qw/[ ]/ ]);
+ $rv->storage->sql_maker->name_sep('.');
+ return $rv;
+ }
+
=head2 Setting limit dialect for SQL::Abstract::Limit
In some cases, SQL::Abstract::Limit cannot determine the dialect of
Typically L<DBIx::Class> result classes start off with
- use base qw/DBIx::Class/;
- __PACKAGE__->load_components(qw/InflateColumn::DateTime Core/);
+ use base qw/DBIx::Class::Core/;
+ __PACKAGE__->load_components(qw/InflateColumn::DateTime/);
If this preamble is moved into a common base class:-
package MyDBICbase;
- use base qw/DBIx::Class/;
- __PACKAGE__->load_components(qw/InflateColumn::DateTime Core/);
+ use base qw/DBIx::Class::Core/;
+ __PACKAGE__->load_components(qw/InflateColumn::DateTime/);
1;
and each result class then uses this as a base:-