Merge 'views' into 'trunk'
Matt S Trout [Fri, 20 Feb 2009 07:56:49 +0000 (07:56 +0000)]
lib/DBIx/Class/ResultSource.pm
lib/DBIx/Class/ResultSource/View.pm [new file with mode: 0644]
lib/SQL/Translator/Parser/DBIx/Class.pm
t/104view.t [new file with mode: 0644]
t/lib/DBICTest/Schema.pm
t/lib/DBICTest/Schema/Year2000CDs.pm [new file with mode: 0644]
t/lib/sqlite.sql

index bc88091..0d49c00 100644 (file)
@@ -29,6 +29,8 @@ DBIx::Class::ResultSource - Result source object
 A ResultSource is a component of a schema from which results can be directly
 retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
 
+Basic view support also exists, see L<<DBIx::Class::ResultSource::View>.
+
 =head1 METHODS
 
 =pod
diff --git a/lib/DBIx/Class/ResultSource/View.pm b/lib/DBIx/Class/ResultSource/View.pm
new file mode 100644 (file)
index 0000000..f1ab5f1
--- /dev/null
@@ -0,0 +1,123 @@
+package DBIx::Class::ResultSource::View;
+
+use strict;
+use warnings;
+
+use DBIx::Class::ResultSet;
+
+use base qw/DBIx::Class/;
+__PACKAGE__->load_components(qw/ResultSource/);
+__PACKAGE__->mk_group_accessors(
+  'simple' => qw(is_virtual view_definition)
+);
+
+=head1 NAME
+
+DBIx::Class::ResultSource::View - ResultSource object representing a view
+
+=head1 SYNOPSIS
+
+  package MyDB::Schema::Year2000CDs;
+
+  use DBIx::Class::ResultSource::View;
+
+  __PACKAGE__->load_components('Core');
+  __PACKAGE__->table_class('DBIx::Class::ResultSource::View');
+
+  __PACKAGE__->table('year2000cds');
+  __PACKAGE__->result_source_instance->is_virtual(1);
+  __PACKAGE__->result_source_instance->view_definition(
+      "SELECT cdid, artist, title FROM cd WHERE year ='2000'"
+      );
+
+=head1 DESCRIPTION
+
+View object that inherits from L<DBIx::Class::ResultSource>
+
+This class extends ResultSource to add basic view support. 
+
+A view has a L</view_definition>, which contains an SQL query. The
+query cannot have parameters. It may contain JOINs, sub selects and
+any other SQL your database supports.
+
+View definition SQL is deployed to your database on
+L<DBIx::Class::Schema/deploy> unless you set L</is_virtual> to true.
+
+Deploying the view does B<not> translate it between different database
+syntaxes, so be careful what you write in your view SQL.
+
+Virtual views (L</is_virtual> unset or false), are assumed to not
+exist in your database as a real view. The L</view_definition> in this
+case replaces the view name in a FROM clause in a subselect.
+
+=head1 SQL EXAMPLES
+
+=over
+
+=item is_virtual set to true
+
+  $schema->resultset('Year2000CDs')->all();
+
+  SELECT cdid, artist, title FROM year2000cds me
+
+=item is_virtual set to false
+
+  $schema->resultset('Year2000CDs')->all();
+
+  SELECT cdid, artist, title FROM 
+    (SELECT cdid, artist, title FROM cd WHERE year ='2000') me
+
+=back
+
+=head1 METHODS
+
+=head2 is_virtual
+
+  __PACKAGE__->result_source_instance->is_virtual(1);
+
+Set to true for a virtual view, false or unset for a real
+database-based view.
+
+=head2 view_definition
+
+  __PACKAGE__->result_source_instance->view_definition(
+      "SELECT cdid, artist, title FROM cd WHERE year ='2000'"
+      );
+
+An SQL query for your view. Will not be translated across database
+syntaxes.
+
+
+=head1 OVERRIDDEN METHODS
+
+=head2 from
+
+Returns the FROM entry for the table (i.e. the view name)
+or the SQL as a subselect if this is a virtual view.
+
+=cut
+
+sub from {
+  my $self = shift;
+  return \"(${\$self->view_definition})" if $self->is_virtual;
+  return $self->name;
+}
+
+1;
+
+=head1 AUTHORS
+
+Matt S. Trout <mst@shadowcatsystems.co.uk>
+
+With Contributions from:
+
+Guillermo Roditi E<lt>groditi@cpan.orgE<gt>
+
+Jess Robinson <castaway@desert-island.me.uk>
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
+
index 2b408e2..bff7ddc 100644 (file)
@@ -66,7 +66,18 @@ sub parse {
     }
 
 
-    foreach my $moniker (sort @monikers)
+    my(@table_monikers, @view_monikers);
+    for my $moniker (@monikers){
+      my $source = $dbicschema->source($moniker);
+       if ( $source->isa('DBIx::Class::ResultSource::Table') ) {
+         push(@table_monikers, $moniker);
+      } elsif( $source->isa('DBIx::Class::ResultSource::View') ){
+          next if $source->is_virtual;
+         push(@view_monikers, $moniker);
+      }
+    }
+
+    foreach my $moniker (sort @table_monikers)
     {
         my $source = $dbicschema->source($moniker);
         
@@ -218,6 +229,25 @@ sub parse {
         $source->_invoke_sqlt_deploy_hook($table);
     }
 
+    foreach my $moniker (sort @view_monikers)
+    {
+        my $source = $dbicschema->source($moniker);
+        # Skip custom query sources
+        next if ref($source->name);
+
+        # Its possible to have multiple DBIC source using same table
+        next if $seen_tables{$source->name}++;
+
+        my $view = $schema->add_view(
+          name => $source->name,
+          fields => [ $source->columns ],
+          $source->view_definition ? ( 'sql' => $source->view_definition ) : ()
+        );
+        if ($source->result_class->can('sqlt_deploy_hook')) {
+          $source->result_class->sqlt_deploy_hook($view);
+        }
+    }
+
     if ($dbicschema->can('sqlt_deploy_hook')) {
       $dbicschema->sqlt_deploy_hook($schema);
     }
diff --git a/t/104view.t b/t/104view.t
new file mode 100644 (file)
index 0000000..0539d48
--- /dev/null
@@ -0,0 +1,28 @@
+use strict;
+use warnings;  
+
+use Test::More;
+use Test::Exception;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+plan tests => 2;
+
+## Real view
+my $cds_rs = $schema->resultset('CD')->search( { year => 2000 });
+my $year2kcds_rs = $schema->resultset('Year2000CDs');
+
+is($cds_rs->count, $year2kcds_rs->count, 'View Year2000CDs sees all CDs in year 2000');
+
+
+## Virtual view
+my $cds_rs = $schema->resultset('CD')->search( { year => 1999 });
+my $year1999cds_rs = $schema->resultset('Year1999CDs');
+
+is($cds_rs->count, $year1999cds_rs->count, 'View Year1999CDs sees all CDs in year 1999');
+
+
+
+
index 2ff55c6..b03d090 100644 (file)
@@ -18,6 +18,8 @@ __PACKAGE__->load_classes(qw/
   #dummy
   Track
   Tag
+  Year2000CDs
+  Year1999CDs
   /,
   { 'DBICTest::Schema' => [qw/
     LinerNotes
diff --git a/t/lib/DBICTest/Schema/Year2000CDs.pm b/t/lib/DBICTest/Schema/Year2000CDs.pm
new file mode 100644 (file)
index 0000000..5293c69
--- /dev/null
@@ -0,0 +1,31 @@
+package # hide from PAUSE 
+    DBICTest::Schema::Year2000CDs;
+## Used in 104view.t
+
+use base 'DBIx::Class::Core';
+use DBIx::Class::ResultSource::View;
+
+__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
+
+__PACKAGE__->table('year2000cds');
+__PACKAGE__->result_source_instance->view_definition(
+  "SELECT cdid, artist, title FROM cd WHERE year ='2000'"
+);
+__PACKAGE__->add_columns(
+  'cdid' => {
+    data_type => 'integer',
+    is_auto_increment => 1,
+  },
+  'artist' => {
+    data_type => 'integer',
+  },
+  'title' => {
+    data_type => 'varchar',
+    size      => 100,
+  },
+
+);
+__PACKAGE__->set_primary_key('cdid');
+__PACKAGE__->add_unique_constraint([ qw/artist title/ ]);
+
+1;
index b364cb4..e2dceb0 100644 (file)
@@ -1,10 +1,11 @@
 -- 
 -- Created by SQL::Translator::Producer::SQLite
--- Created on Sat Jan 24 19:42:15 2009
+-- Created on Thu Feb 19 22:09:32 2009
 -- 
-BEGIN TRANSACTION;
 
 
+BEGIN TRANSACTION;
+
 --
 -- Table: artist
 --
@@ -15,7 +16,6 @@ CREATE TABLE artist (
   charfield char(10)
 );
 
-
 --
 -- Table: artist_undirected_map
 --
@@ -26,6 +26,7 @@ CREATE TABLE artist_undirected_map (
 );
 
 CREATE INDEX artist_undirected_map_idx_id1_ ON artist_undirected_map (id1);
+
 CREATE INDEX artist_undirected_map_idx_id2_ ON artist_undirected_map (id2);
 
 --
@@ -59,7 +60,6 @@ CREATE TABLE bindtype_test (
   clob clob
 );
 
-
 --
 -- Table: bookmark
 --
@@ -81,7 +81,6 @@ CREATE TABLE books (
   price integer
 );
 
-
 --
 -- Table: cd
 --
@@ -95,8 +94,11 @@ CREATE TABLE cd (
 );
 
 CREATE INDEX cd_idx_artist_cd ON cd (artist);
+
 CREATE INDEX cd_idx_genreid_cd ON cd (genreid);
+
 CREATE INDEX cd_idx_single_track_cd ON cd (single_track);
+
 CREATE UNIQUE INDEX cd_artist_title_cd ON cd (artist, title);
 
 --
@@ -109,6 +111,7 @@ CREATE TABLE cd_to_producer (
 );
 
 CREATE INDEX cd_to_producer_idx_cd_cd_to_pr ON cd_to_producer (cd);
+
 CREATE INDEX cd_to_producer_idx_producer_cd ON cd_to_producer (producer);
 
 --
@@ -119,7 +122,6 @@ CREATE TABLE collection (
   name varchar(100) NOT NULL
 );
 
-
 --
 -- Table: collection_object
 --
@@ -130,6 +132,7 @@ CREATE TABLE collection_object (
 );
 
 CREATE INDEX collection_object_idx_collection_collection_obj ON collection_object (collection);
+
 CREATE INDEX collection_object_idx_object_c ON collection_object (object);
 
 --
@@ -143,6 +146,13 @@ CREATE TABLE employee (
   name varchar(100)
 );
 
+--
+-- Table: encoded
+--
+CREATE TABLE encoded (
+  id INTEGER PRIMARY KEY NOT NULL,
+  encoded varchar(100)
+);
 
 --
 -- Table: event
@@ -156,7 +166,6 @@ CREATE TABLE event (
   skip_inflation datetime
 );
 
-
 --
 -- Table: file_columns
 --
@@ -165,7 +174,6 @@ CREATE TABLE file_columns (
   file varchar(255) NOT NULL
 );
 
-
 --
 -- Table: forceforeign
 --
@@ -188,7 +196,6 @@ CREATE TABLE fourkeys (
   PRIMARY KEY (foo, bar, hello, goodbye)
 );
 
-
 --
 -- Table: fourkeys_to_twokeys
 --
@@ -204,6 +211,7 @@ CREATE TABLE fourkeys_to_twokeys (
 );
 
 CREATE INDEX fourkeys_to_twokeys_idx_f_foo_f_bar_f_hello_f_goodbye_ ON fourkeys_to_twokeys (f_foo, f_bar, f_hello, f_goodbye);
+
 CREATE INDEX fourkeys_to_twokeys_idx_t_artist_t_cd_fourkeys_to ON fourkeys_to_twokeys (t_artist, t_cd);
 
 --
@@ -247,7 +255,6 @@ CREATE TABLE link (
   title varchar(100)
 );
 
-
 --
 -- Table: lyric_versions
 --
@@ -289,7 +296,6 @@ CREATE TABLE onekey (
   cd integer NOT NULL
 );
 
-
 --
 -- Table: owners
 --
@@ -298,7 +304,6 @@ CREATE TABLE owners (
   name varchar(100) NOT NULL
 );
 
-
 --
 -- Table: producer
 --
@@ -317,7 +322,6 @@ CREATE TABLE self_ref (
   name varchar(100) NOT NULL
 );
 
-
 --
 -- Table: self_ref_alias
 --
@@ -328,6 +332,7 @@ CREATE TABLE self_ref_alias (
 );
 
 CREATE INDEX self_ref_alias_idx_alias_self_ ON self_ref_alias (alias);
+
 CREATE INDEX self_ref_alias_idx_self_ref_se ON self_ref_alias (self_ref);
 
 --
@@ -341,7 +346,6 @@ CREATE TABLE sequence_test (
   PRIMARY KEY (pkid1, pkid2)
 );
 
-
 --
 -- Table: serialized
 --
@@ -350,7 +354,6 @@ CREATE TABLE serialized (
   serialized text NOT NULL
 );
 
-
 --
 -- Table: tags
 --
@@ -374,7 +377,9 @@ CREATE TABLE track (
 );
 
 CREATE INDEX track_idx_cd_track ON track (cd);
+
 CREATE UNIQUE INDEX track_cd_position_track ON track (cd, position);
+
 CREATE UNIQUE INDEX track_cd_title_track ON track (cd, title);
 
 --
@@ -401,6 +406,7 @@ CREATE TABLE twokeytreelike (
 );
 
 CREATE INDEX twokeytreelike_idx_parent1_parent2_twokeytre ON twokeytreelike (parent1, parent2);
+
 CREATE UNIQUE INDEX tktlnameunique_twokeytreelike ON twokeytreelike (name);
 
 --
@@ -424,11 +430,9 @@ CREATE TABLE typed_object (
 );
 
 --
--- Table: encoded
+-- View: year2000cds
 --
-CREATE TABLE encoded (
-  id INTEGER PRIMARY KEY NOT NULL,
-  encoded varchar(100) NOT NULL
-);
+CREATE VIEW year2000cds AS
+    SELECT cdid, artist, title FROM cd WHERE year ='2000';
 
 COMMIT;