Merge 'trunk' into 'subquery'
Rob Kinyon [Mon, 16 Feb 2009 22:03:55 +0000 (22:03 +0000)]
r5380@rkinyon-lt-osx (orig r5379):  jmmills | 2009-01-29 20:37:01 -0500
Added a more verbose non column accessor example.

r5381@rkinyon-lt-osx (orig r5380):  jmmills | 2009-01-29 20:42:02 -0500
editorial typo in my previous commit.

r5383@rkinyon-lt-osx (orig r5382):  semifor | 2009-01-30 08:54:42 -0500
Bring test current with DBICTest schema.
r5384@rkinyon-lt-osx (orig r5383):  castaway | 2009-01-30 09:27:38 -0500
DB2 compatibility fix, update test to add new artist column (ick)
remove code that calls now removed sub _RowNumberOver

r5385@rkinyon-lt-osx (orig r5384):  ribasushi | 2009-01-30 09:32:42 -0500
Add the _find_syntax caching back
r5386@rkinyon-lt-osx (orig r5385):  castaway | 2009-01-30 09:33:24 -0500
DB2 compat: add new artist rank field to col_info_for test

r5393@rkinyon-lt-osx (orig r5392):  ribasushi | 2009-01-31 15:40:50 -0500
pod align by dandv
r5394@rkinyon-lt-osx (orig r5393):  ribasushi | 2009-02-01 16:33:32 -0500
Fix the dbicadmin test for good
r5395@rkinyon-lt-osx (orig r5394):  ribasushi | 2009-02-01 17:44:39 -0500
Fix the dbicadmin test for good (take 2)
r5400@rkinyon-lt-osx (orig r5399):  ribasushi | 2009-02-02 12:38:16 -0500
Bring back _RowNumberOver deleted in the sqla commotion (revs: 5096,5322,5383)
r5401@rkinyon-lt-osx (orig r5400):  ribasushi | 2009-02-02 12:52:20 -0500
part 2
r5414@rkinyon-lt-osx (orig r5413):  ribasushi | 2009-02-05 04:09:39 -0500
Commit some debugging code
r5420@rkinyon-lt-osx (orig r5419):  plu | 2009-02-07 08:40:41 -0500
Possible to set locale in IC::DateTime extra => {} config
r5427@rkinyon-lt-osx (orig r5426):  ribasushi | 2009-02-07 12:57:53 -0500
Up trunk dependency versions
r5428@rkinyon-lt-osx (orig r5427):  ribasushi | 2009-02-07 12:59:04 -0500
Up dependency on SQLT (releasing now)
r5432@rkinyon-lt-osx (orig r5431):  groditi | 2009-02-09 15:27:27 -0500
backporting the set_column/store_column fix
r5433@rkinyon-lt-osx (orig r5432):  groditi | 2009-02-09 15:29:03 -0500
ahhhh my bad. svk patch ne patch. oops adding missing file
r5439@rkinyon-lt-osx (orig r5438):  ribasushi | 2009-02-10 05:18:09 -0500
Finally make the indexer happy wrt SQL::Translator::Parser::DBIx::Class
Hide DBIx::Class::CDBICompat::ColumnGroups::GrouperShim (was never indexed to begin with)

r5441@rkinyon-lt-osx (orig r5440):  ribasushi | 2009-02-10 06:19:20 -0500
More rh-bug related stuff:
- Improve message emmitted when confronted with a buggy system
- Port forgotten pieces from trunk to stopgap
r5442@rkinyon-lt-osx (orig r5441):  castaway | 2009-02-10 08:57:34 -0500
More docs, pointing at using deploy/create_ddl_dir

r5445@rkinyon-lt-osx (orig r5444):  castaway | 2009-02-10 16:41:58 -0500
1) Add docs to ATTRIBUTES to better explain where to use them
2) Make all() die if passed arguments.

r5447@rkinyon-lt-osx (orig r5446):  ribasushi | 2009-02-11 03:24:58 -0500
Test for resultset corruption by search()
r5448@rkinyon-lt-osx (orig r5447):  ribasushi | 2009-02-11 04:02:42 -0500
Add a weird extra test, that should work nevertheless
r5449@rkinyon-lt-osx (orig r5448):  ribasushi | 2009-02-11 04:17:55 -0500
Add missing shallow copy of seen_join
r5450@rkinyon-lt-osx (orig r5449):  ribasushi | 2009-02-11 05:37:36 -0500
make single() die if passed attributes
r5455@rkinyon-lt-osx (orig r5454):  nigel | 2009-02-11 10:07:28 -0500
 r10743@hex:  nigel | 2009-02-11 15:07:05 +0000
 Documentation update on startup speed

r5459@rkinyon-lt-osx (orig r5458):  groditi | 2009-02-11 20:49:26 -0500
fixed bug for undef_on_null_fk edge case
r5464@rkinyon-lt-osx (orig r5463):  nigel | 2009-02-12 09:27:38 -0500
 r10772@hex:  nigel | 2009-02-12 14:27:22 +0000
 Made update() on a rs that includes joins complain in the same way that delete() does.  Added tests for both update and delete checks.

r5465@rkinyon-lt-osx (orig r5464):  plu | 2009-02-13 05:27:36 -0500
Refactored and re-added r5041: Split sql statements for deploy only if SQLT::Producer returned a scalar containing all statements to be executed
r5466@rkinyon-lt-osx (orig r5465):  caelum | 2009-02-13 05:40:55 -0500
Some fixes for Oracle edge cases

r5467@rkinyon-lt-osx (orig r5466):  ribasushi | 2009-02-13 05:45:46 -0500
Some forgotten tests
r5468@rkinyon-lt-osx (orig r5467):  caelum | 2009-02-13 06:29:37 -0500
Throw exception on invalidated session if in a transaction.

r5472@rkinyon-lt-osx (orig r5471):  ribasushi | 2009-02-14 19:44:02 -0500
Make 81transactions.t DBIC_TRACE friendly
r5473@rkinyon-lt-osx (orig r5472):  ribasushi | 2009-02-14 19:45:44 -0500
A dbh_do statement executed with bind values will confuse the hell out of DBIC running in DBIC_TRACE=1 mode - stop sending TMI to _query_[start|end] from within dbh_do/_do_query
r5475@rkinyon-lt-osx (orig r5474):  plu | 2009-02-15 09:20:25 -0500
RT#38251: DBIx::Class::Storage::DBI::Oracle::Generic does not handle fully-qualified table names
r5480@rkinyon-lt-osx (orig r5479):  caelum | 2009-02-15 18:30:00 -0500
Improved connected logic for Oracle storage

r5481@rkinyon-lt-osx (orig r5480):  caelum | 2009-02-15 19:20:27 -0500
sorry, I fucked up the indentation...
r5482@rkinyon-lt-osx (orig r5481):  ribasushi | 2009-02-16 03:48:49 -0500
Some cleanups of oracle patch
r5490@rkinyon-lt-osx (orig r5489):  robkinyon | 2009-02-16 16:27:49 -0500
Added a bit of explanation to resultsets to cut down on the repeated questions in #dbix-class

lib/DBIx/Class/Manual/Cookbook.pod
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/ResultSetColumn.pm
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Storage/DBI/Cursor.pm
t/resultset/as_query.t [new file with mode: 0644]

index 110091f..b7fc30a 100644 (file)
@@ -295,6 +295,27 @@ Please see L<DBIx::Class::ResultSet/ATTRIBUTES> documentation if you
 are in any way unsure about the use of the attributes above (C< join
 >, C< select >, C< as > and C< group_by >).
 
+=head2 Subqueries
+
+You can write subqueries relatively easily in DBIC.
+
+  my $inside_rs = $schema->resultset('Artist')->search({
+    name => [ 'Billy Joel', 'Brittany Spears' ],
+  });
+
+  my $rs = $schema->resulset('CD')->search({
+    artist_id => { 'IN' => $inside_rs->get_column('id')->as_query },
+  });
+
+The usual operators ( =, !=, IN, NOT IN, etc) are supported.
+
+B<NOTE>: You have to explicitly use '=' when doing an equality comparison.
+The following will B<not> work:
+
+  my $rs = $schema->resulset('CD')->search({
+    artist_id => $inside_rs->get_column('id')->as_query,
+  });
+
 =head2 Predefined searches
 
 You can write your own L<DBIx::Class::ResultSet> class by inheriting from it
index 1d0131a..29810f5 100644 (file)
@@ -1802,6 +1802,24 @@ sub _remove_alias {
   return \%unaliased;
 }
 
+=head2 as_query
+
+=over 4
+
+=item Arguments: none
+
+=item Return Value: \[ $sql, @bind ]
+
+=back
+
+Returns the SQL query and bind vars associated with the invocant.
+
+This is generally used as the RHS for a subquery.
+
+=cut
+
+sub as_query { return shift->cursor->as_query(@_) }
+
 =head2 find_or_new
 
 =over 4
index 7b0fee6..2a7ce97 100644 (file)
@@ -54,6 +54,24 @@ sub new {
   return $new;
 }
 
+=head2 as_query
+
+=over 4
+
+=item Arguments: none
+
+=item Return Value: \[ $sql, @bind ]
+
+=back
+
+Returns the SQL query and bind vars associated with the invocant.
+
+This is generally used as the RHS for a subquery.
+
+=cut
+
+sub as_query { return shift->_resultset->as_query }
+
 =head2 next
 
 =over 4
@@ -278,7 +296,6 @@ sub _resultset {
   );
 }
 
-
 1;
 
 =head1 AUTHORS
index a337f51..5865916 100644 (file)
@@ -1173,11 +1173,15 @@ sub txn_rollback {
 sub _prep_for_execute {
   my ($self, $op, $extra_bind, $ident, $args) = @_;
 
+  if( blessed($ident) && $ident->isa("DBIx::Class::ResultSource") ) {
+    $ident = $ident->from();
+  }
+
   my ($sql, @bind) = $self->sql_maker->$op($ident, @$args);
+
   unshift(@bind,
     map { ref $_ eq 'ARRAY' ? $_ : [ '!!dummy', $_ ] } @$extra_bind)
       if $extra_bind;
-
   return ($sql, \@bind);
 }
 
@@ -1219,10 +1223,6 @@ sub _query_end {
 sub _dbh_execute {
   my ($self, $dbh, $op, $extra_bind, $ident, $bind_attributes, @args) = @_;
   
-  if( blessed($ident) && $ident->isa("DBIx::Class::ResultSource") ) {
-    $ident = $ident->from();
-  }
-
   my ($sql, $bind) = $self->_prep_for_execute($op, $extra_bind, $ident, \@args);
 
   $self->_query_start( $sql, @$bind );
@@ -1357,6 +1357,13 @@ sub delete {
 }
 
 sub _select {
+  my $self = shift;
+  my $sql_maker = $self->sql_maker;
+  local $sql_maker->{for};
+  return $self->_execute($self->_select_args(@_));
+}
+
+sub _select_args {
   my ($self, $ident, $select, $condition, $attrs) = @_;
   my $order = $attrs->{order_by};
 
@@ -1393,7 +1400,7 @@ sub _select {
     push @args, $attrs->{rows}, $attrs->{offset};
   }
 
-  return $self->_execute(@args);
+  return @args;
 }
 
 sub source_bind_attributes {
index 426f72e..6c90191 100644 (file)
@@ -49,6 +49,32 @@ sub new {
   return bless ($new, $class);
 }
 
+=head2 as_query
+
+=over 4
+
+=item Arguments: none
+
+=item Return Value: \[ $sql, @bind ]
+
+=back
+
+Returns the SQL statement and bind vars associated with the invocant.
+
+=cut
+
+sub as_query {
+  my $self = shift;
+
+  my $storage = $self->{storage};
+  my $sql_maker = $storage->sql_maker;
+  local $sql_maker->{for};
+
+  my @args = $storage->_select_args(@{$self->{args}});
+  my ($sql, $bind)  = $storage->_prep_for_execute(@args[0 .. 2], [@args[4 .. $#args]]);
+  return \[ $sql, @$bind ];
+}
+
 =head2 next
 
 =over 4
diff --git a/t/resultset/as_query.t b/t/resultset/as_query.t
new file mode 100644 (file)
index 0000000..8df7e7d
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings FATAL => 'all';
+
+use Data::Dumper;
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+use DBIC::SqlMakerTest;
+
+plan tests => 5;
+
+my $schema = DBICTest->init_schema();
+my $art_rs = $schema->resultset('Artist');
+my $cdrs = $schema->resultset('CD');
+
+{
+  my $arr = $art_rs->as_query;
+  my ($query, @bind) = @{$$arr};
+
+  is_same_sql_bind(
+    $query, \@bind,
+    "SELECT me.artistid, me.name, me.rank, me.charfield FROM artist me", [],
+  );
+}
+
+$art_rs = $art_rs->search({ name => 'Billy Joel' });
+
+{
+  my $arr = $art_rs->as_query;
+  my ($query, @bind) = @{$$arr};
+
+  is_same_sql_bind(
+    $query, \@bind,
+    "SELECT me.artistid, me.name, me.rank, me.charfield FROM artist me WHERE ( name = ? )",
+    [ [ name => 'Billy Joel' ] ],
+  );
+}
+
+$art_rs = $art_rs->search({ rank => 2 });
+
+{
+  my $arr = $art_rs->as_query;
+  my ($query, @bind) = @{$$arr};
+
+  is_same_sql_bind(
+    $query, \@bind,
+    "SELECT me.artistid, me.name, me.rank, me.charfield FROM artist me WHERE ( ( rank = ? ) AND ( name = ? ) )",
+    [ [ rank => 2 ], [ name => 'Billy Joel' ] ],
+  );
+}
+
+my $rscol = $art_rs->get_column( 'charfield' );
+
+{
+  my $arr = $rscol->as_query;
+  my ($query, @bind) = @{$$arr};
+
+  is_same_sql_bind(
+    $query, \@bind,
+    "SELECT me.charfield FROM artist me WHERE ( ( ( rank = ? ) AND ( name = ? ) ) )",
+    [ [ rank => 2 ], [ name => 'Billy Joel' ] ],
+  );
+}
+
+# This is an actual subquery.
+{
+  my $cdrs2 = $cdrs->search({
+    artist_id => { 'in' => $art_rs->search({}, { rows => 1 })->get_column( 'id' )->as_query },
+  });
+
+  my $arr = $cdrs2->as_query;
+  my ($query, @bind) = @{$$arr};
+  is_same_sql_bind(
+    $query, \@bind,
+    "SELECT me.cdid,me.artist,me.title,me.year,me.genreid,me.single_track FROM cd me WHERE artist_id IN ( SELECT id FROM artist me WHERE ( rank = ? ) AND ( name = ? ) LIMIT 1 )",
+    [ [ rank => 2 ], [ name => 'Billy Joel' ] ],
+  );
+}
+
+__END__