Updated according to ribasushi's review.
Jess Robinson [Fri, 6 Jan 2012 16:24:48 +0000 (16:24 +0000)]
Some minor parts still to finish

lib/DBIx/Class/Manual/SQLHackers/Introduction.pod
lib/DBIx/Class/Manual/SQLHackers/SELECT.pod
lib/DBIx/Class/Manual/SQLHackers/TOC.pod
lib/DBIx/Class/Manual/SQLHackers/Transactions.pod
lib/DBIx/Class/Manual/SQLHackers/UPDATE.pod

index 54e4ded..4edbdf7 100644 (file)
@@ -22,9 +22,7 @@ The part we can't do much about is the SQL in the code. We can move it around, p
 
 Why would you not want SQL in your Perl code? For a start, it's just a string to pass to the database interpreter, there is no syntax checking at the Perl compilation level. Thus it fails late, not early. Your editor will also not syntax check what it just sees as strings of text.
 
-Modern Perl should also leverage OO where it can. DBI is a low level library that gets things right, but returns plain hashes and arrays, not objects.
-
-Perl ORMs still use DBI underneath, extending it to catch coding errors early (names of columns, SQL clauses), and to return the results as objects containing the database values, which work just like any other Perl object.
+Modern Perl should also leverage code reuse and OO where it can. DBIx::Class promotes code reuse by allowing you to add methods for common queries, fetch related data in one query and cache data, also without refetching. DBIC uses the DBI library underneath, which gets things right, but returns plain hashes and arrays, not objects.
 
 DBIx::Class solves these issues, you write your SQL in perl instead of plain text. The syntax will be checked for you, existance of columns, catching typos and so on. It uses objects so that you can write re-usable queries, and string methods together to create complex queries. You define the database layout once, or you export it from your actual database (with ability to re-export on update).
 
index 58cbd25..0571795 100644 (file)
@@ -37,7 +37,7 @@ You can either fetch all the data at once, or iterate over the results:
 
         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
 
-=item 2. The B<resultset> method returns a ResultSet representing a query with no conditions on the given B<Source>:
+=item 2. The B<resultset> method returns a ResultSet representing a query with no conditions on the given B<ResultSource>:
 
         my $user_resultset = $schema->resultset('User');
 
@@ -58,7 +58,7 @@ The Row object represents the results from a single data source table in the que
 
     print $user->username;
 
-See the [DBIx::Class::Row]() documentation for more things you can do
+See the L<DBIx::Class::Row> documentation for more things you can do
 with Row objects.
     
 =head2 Simple SELECT, one row via the primary key
@@ -91,7 +91,7 @@ B<$fred_user> is a now Row object.
     FROM users
     WHERE username = 'fredbloggs';
 
-B<find> also works well on unique constraints, for example the username of our user. Unique constraints can be defined on Result classes using B<add_unique_constraint> (See L<CREATE|DBIx::Class::Manual::SQLHackers::CREATE>.
+B<find> also works well on unique constraints, for example the username of our user. Unique constraints can be defined on Result classes using B<add_unique_constraint> (See L<CREATE|DBIx::Class::Manual::SQLHackers::CREATE>).
 
 =over 
 
@@ -132,7 +132,7 @@ To select all users born on the date '1910-02-01', we can use the B<search> meth
 
 =back
 
-To run the query, use the B<all> or B<next> methods show at the beginning of this page.
+To run the query, use the B<all> or B<next> methods shown at the beginning of this page.
 
 =head2 SELECT with different WHERE conditions
 
@@ -206,6 +206,8 @@ To run the query, use the B<all> or B<next> methods show at the beginning of thi
 
 There's usually little reason to do this sort of query, as fetching all the data in a row doesn't cost any more time than fetching some of it. Unless of course your source is a View with calculations, or has huge blobs, or.. Okay, you might well want to do this occasionally.
 
+# this is completely false, is there a doc that states this that we need to fix?
+# find() takes all search() attributes, including things like prefetch
 B<find> will always pull all the columns for the found row, so use the *search* method for this.
 
 =over
@@ -223,7 +225,7 @@ B<find> will always pull all the columns for the found row, so use the *search*
 
 =back
 
-Note that accessors for other columns not fetched will return B<undef>. To discover whether a columns data has been loaded or not, use the B<has_column_loaded> method.
+Note that accessors for other columns not fetched will return B<undef>, which is also the perl equivalent of the SQL C<NULL> value. To discover whether a columns data has been loaded or not, use L<DBIx::Class::Row/has_column_loaded>.
     
 
 =head2 SELECT with aggregates
@@ -279,8 +281,7 @@ The alternate way uses the B<search> method and is easier to build further refin
 
         my $sum_prices_rs = $schema->resultset('Price')->search(
           { },
-          { select => [ { SUM => 'amount'} ],
-            as     => [ 'sum_amount' ] }
+          { columns => { sum_amount => { SUM => 'amount'} } },
         );
 
 =back
@@ -307,8 +308,7 @@ To select data from other tables, use the B<join> attribute to name the table re
 
         my $posts_count_per_user = $schema->resultset('User')->search(
           { },
-          { select => [ qw/id username posts.id posts.title/ ],
-            as     => [ qw/id username posts.id posts.title/ ],
+          { columns => [ qw/id username posts.id posts.title/ ],
             join => 'posts',
           }
         );
@@ -333,7 +333,7 @@ To retrieve the extra data, call the usual relationship accessor:
     JOIN posts posts ON posts.used_id = users.id
     GROUP BY users.id, username;
 
-To group your results, use the B<group_by> attribute on a B<search> method. We also use the B<select> and B<as> attributes to select and name a subset of columns.
+To group your results, use the B<group_by> attribute on a B<search> method. We also use the B<columns>  attribute to select and name a subset of columns.
 
 =over
 
@@ -345,8 +345,7 @@ To group your results, use the B<group_by> attribute on a B<search> method. We a
 
         my $posts_count_per_user = $schema->resultset('User')->search(
           { },
-          { select => [ qw/id username/, { count => 'posts.id' } ],
-            as     => [ qw/id username post_count/ ],
+          { columns => [ qw/id username/, { post_count => { count => 'posts.id' } } ],
             join => 'posts',
             group_by => [qw/id username/],
           }
@@ -381,10 +380,10 @@ To sort the results, use the B<order_by> attributes on a B<search> method. Conte
 
 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to sort data on:
 
+# this will not collapse results (you will get users * (amount of posts || 1) as a result, sure you want to showcase this?
         my $sorted_users = $schema->resultset('User')->search(
           { },
-          { '+select' => [ qw/posts.id posts.title/ ],
-            '+as'     => [ qw/posts.id posts.title/ ],
+          { '+columns' => [ qw/posts.id posts.title/ ],
             join => 'posts',
             order_by => [qw/username posts.title/],
           }
@@ -396,7 +395,7 @@ Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship>
 
 The results will be ordered by username, then post title, ready for outputting.
 
-Note how we have added the title of each post, this prevents us having to fire off a second query to fetch the post data to output it. The B<+select> attribute specifies an extended set of columns to fetch, in addition to the columns of the main query table.
+Note how we have added the title of each post, this prevents us having to fire off a second query to fetch the post data to output it. The B<+columns> attribute specifies an extended set of columns to fetch, in addition to the columns of the main query table.
 
 To retrieve the extra data, call the usual relationship accessor:
 
@@ -411,7 +410,7 @@ To retrieve the extra data, call the usual relationship accessor:
     SELECT users.id, username, dob
     FROM users
     JOIN posts posts ON posts.used_id = users.id
-    GROUP BY users.id, username;
+    GROUP BY users.id, username, dob
     HAVING count(posts.id) = 1
 
 To add a B<having> clause to your query, use the corresponding B<having> attribute.
@@ -426,10 +425,9 @@ To add a B<having> clause to your query, use the corresponding B<having> attribu
 
         my $filtered_users = $schema->resultset('User')->search(
           { },
-          { '+select' => [ qw/me.id me.username me.dob/ ],
-            '+as'     => [ qw/me.id me.username me.dob/ ],
+          { 'columns' => [ qw/me.id me.username me.dob/ ],
             join => 'posts',
-            group_by => [qw/me.id me.username/],
+            group_by => [qw/me.id me.username me.dob/],
             having   => [{ 'posts.id' => 1 }],
           }
         );
@@ -445,7 +443,7 @@ The results will be filtered by the HAVING clause.
     SELECT DISTINCT(posts.title)
     FROM posts
 
-DBIx::Class doesn't currently produce a B<DISTINCT> keyword, but will output a B<GROUP BY> to simulate the same effect.
+To produce DISTINCT clauses, we need to use a hashref argument to the list of items passed to the B<columns> attribute.
 
 =over
 
@@ -453,22 +451,37 @@ DBIx::Class doesn't currently produce a B<DISTINCT> keyword, but will output a B
 
         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
 
-=item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to group data on:
+=item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to find distinct rows on:
 
         my $distinct_posts = $schema->resultset('Post')->search(
           { },
-          { columns => ['me.title'],
-            distinct => 1,
+          { columns => [{ 'd_title' => { distinct => 'me.title' } }],
           }
         );
 
 =back
 
-This actually outputs:
+This can also be achieved by using the ResultSet method B<get_column>. The method returns a ResultSetColumn object based on the given column name argument, which can call SQL aggregate functions based upon the column of that data.
 
-    SELECT posts.title
-    FROM posts
-    GROUP BY posts.title
+So we can also do this, for single column DISTINCT clauses:
+
+=over
+
+=item 1. Create a Schema object representing the database you are working with:
+
+        my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
+
+=item 2. Call the B<get_column> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to find distinct rows on:
+
+        my $rs_column = $schema->resultset('Post')->get_column('title');
+
+=item 3. Call the B<func> method on the resultset column object and pass it the name of the function to apply:
+
+        my $titles = $rs_column->func('distinct');
+
+=back
+
+The result will be an arrayref of the actual values. If a ResultSet object is needed for further refinement, use B<func_rs> instead.
 
 =head2 SELECT ... FOR UPDATE
 
@@ -497,14 +510,14 @@ To fetch data and lock it for updating from other transactions, use the B<for> a
 
 The resultset and rows will be returned as normal, and can be used to update the rows without worrying about other 
 
-=head2 SELECT with LIMIT
+=head2 SELECT with LIMIT and OFFSET
 
     SELECT users.id, users.username
     FROM users
     ORDER BY user.dob DESC
-    LIMIT 10;
+    LIMIT 10 OFFSET 11;
 
-To reduce the set or rows fetched, use the B<rows> attribute.
+To reduce the set of rows fetched, use the B<rows> and B<page> attributes. The value of B<page> will default to 1, which means no OFFSET will be applied.
 
 =over
 
@@ -519,10 +532,11 @@ To reduce the set or rows fetched, use the B<rows> attribute.
           { columns  => [qw/me.id me.username/],
             order_by => { '-desc' => ['user.dob'] },
             rows     => 10,
+            page     => 2,
           }
         );
 
-This will return exactly 10 row objects, sorted by descending date of birth of the users.
+This will return exactly 10 row objects, sorted by descending date of birth of the users, starting at the 11th row of the sorted result.
 
 =back
 
index 9013f3e..d54c492 100644 (file)
@@ -20,7 +20,7 @@ DBIx::Class::Manual::SQLHackers::TOC - DBIx::Class for SQL Hackers - TOC
 
 =back
 
-=head AUTHOR
+=head1 AUTHOR
 
 Jess Robinson <castaway@desert-island.me.uk>, L<http://metacpan.org/author/JROBINSON>
 
index 47aa800..40accb3 100644 (file)
@@ -27,7 +27,7 @@ DBIx::Class::Manual::SQLHackers::DELETE - DBIx::Class for SQL Hackers - DELETE
     UPDATE users SET username = 'fred' WHERE id = 1;
     COMMIT;
 
-To create a transaction, put all your changes inside a coderef, and pass it to the B<txn_do> method on the Schema object.
+To create a transaction, put all your changes inside a coderef, and pass it to the B<txn_do> method on the Schema object. Transactions can also be safely nested
 
 =over
 
index 0cb3cb8..4e88e24 100644 (file)
@@ -22,13 +22,13 @@ DBIx::Class::Manual::SQLHackers::UPDATE - DBIx::Class for SQL Hackers - UPDATE
 
 =head1 UPDATEing data
 
-=head2 Delayed update versus direct update
+=head2 Single row delayed update versus direct update
 
-Rows may be updated in one of two ways. You can create an object
-representing an existing database table row, and hold it in your
-programmes memory, passing it around from function to function,
-changing its values, before actually updating the contents into the
-database. This is a delayed update.
+Individual rows may be updated via their Result object in one of two
+ways. You can create an object representing an existing database table
+row and hold it in your programmes memory, passing it around from
+function to function changing its values, before actually updating
+the contents into the database. This is a delayed update.
 
 A direct update still involves fetching the existing row from the
 database, but instead of storing new column values in the Row object,
@@ -54,7 +54,7 @@ To create a Row object for delayed update (or other manipulations), first fetch
 
         my $fred_user = $schema->resultset('User')->find({ id => 1 });
 
-    B<$fred_user>'s contents can now be changed using the accessor
+B<$fred_user>'s contents can now be changed using the accessor
 methods created by B<add_columns>, back in
 L<CREATE|DBIx::Class::Manual::SQLHackers::CREATE>. These are generally named
 after the columns in the database, so to change fred's real name, use
@@ -64,8 +64,9 @@ the B<realname> method.
 
         $fred_user->realname("John Bloggs");
         
-    This value has not yet changed in the database, we can make the actual
-update by calling *update:
+
+This value has not yet changed in the database, we can make the actual
+update by calling *update*:
 
 =item 4. Update the set value(s) into the database:
 
@@ -73,6 +74,15 @@ update by calling *update:
 
 =back
 
+The update method will only actually send an UPDATE statement to the
+database if one or more of the columns have changed. The internal
+tracking of which columns have been changed can be queried using
+several methods. B<is_changed> returns true (or a list of changed
+column names), if any column values have changed. B<is_column_changed>
+will return true or false for the given column name argument. The
+previous values of the columns are not stored.
+
+
 =head2 Update a single row with simple values
 
     UPDATE users
@@ -151,11 +161,16 @@ the object, and send an UPDATE query to the database.
 
 =item 3. Call the B<update> method, passing it a hashref of new data:
 
+# this won't yet work, DBIC for now mandates the [ {} => $value ] format, the simple \[ $sql, $value1, $value2 ] will start being recognized later on 
+# the only documentation we currently have is this, if you can turn it into a DBIC pod-patch it will be freaking awesome
+# https://github.com/dbsrgits/dbix-class/commit/0e773352
         $fred_user->update({ username => \['username || ?', '.uk'] }); 
 
+# the DBIC syntax is a tad different from te thing above (i.e. we no longer encourage 'dummy' crap)
 The \[ .. ] syntax here is described in L<SQL::Abstract>
 documentation, used for passing bind parameters.
 
+
 =back
 
 =head2 Update a row based on data in other tables