Author: spadkins
Date: Fri Feb 22 12:48:06 2008
New Revision: 10819

Added:
   p5ee/trunk/App-Repository/lib/App/SharedDatastore/
   p5ee/trunk/App-Repository/lib/App/SharedDatastore/Repository.pm
   p5ee/trunk/App-Repository/t/DBI-getset-cache.t   (contents, props changed)
   p5ee/trunk/App-Repository/t/SharedDatastore.t   (contents, props changed)
Modified:
   p5ee/trunk/App-Repository/CHANGES
   p5ee/trunk/App-Repository/bin/dbget
   p5ee/trunk/App-Repository/lib/App/Repository.pm
   p5ee/trunk/App-Repository/lib/App/Repository/DBI.pm
   p5ee/trunk/App-Repository/t/DBI-metadata.t

Log:
add query caching and support for a Repository-based shared_datastore

Modified: p5ee/trunk/App-Repository/CHANGES
==============================================================================
--- p5ee/trunk/App-Repository/CHANGES   (original)
+++ p5ee/trunk/App-Repository/CHANGES   Fri Feb 22 12:48:06 2008
@@ -3,6 +3,7 @@
 #########################################
 
 0.966 (not yet released)
+ x App::Repository::get_rows()/get_row(): use query caching if turned on for 
the table ({cache_name} => "name_of_shared_datastore")
  x App::Repository::create_temporary_object_set(): can create a temporary 
object set with data, not bound to the database
  x App::Repository::create_temporary_object_domain(): can create a temporary 
object domain with data, not bound to the database
  x App::Repository::evaluate_expression(): can now supply defaults for null 
columns

Modified: p5ee/trunk/App-Repository/bin/dbget
==============================================================================
--- p5ee/trunk/App-Repository/bin/dbget (original)
+++ p5ee/trunk/App-Repository/bin/dbget Fri Feb 22 12:48:06 2008
@@ -3,7 +3,8 @@
 use Date::Format;
 
 use App::Options (
-    options => [ qw(dbhost dbname dbuser dbpass repository table params 
columns headings compact decimals subtotal_columns totals verbose) ],
+    options => [ qw(dbhost dbname dbuser dbpass repository table params 
columns headings compact decimals subtotal_columns totals
+                    cache_skip cache_refresh verbose) ],
     option => {
         repository => {
             default => "default",
@@ -36,6 +37,12 @@
         totals => {
             description => "Print totals at the end",
         },
+        cache_skip => {
+            description => "Skip any cached values for the table",
+        },
+        cache_refresh => {
+            description => "Skip any cached values for the table but save the 
results in the cache",
+        },
         verbose => {
             default => 1,
             description => "Verbose level",
@@ -62,7 +69,10 @@
     my $params   = { split(/[=>\|]+/, $App::options{params}) };
     my $headings = $App::options{headings} ? [ split(/,/, 
$App::options{headings}) ] : [];
     my $verbose  = $App::options{verbose};
-    my $rows     = $db->get_rows($table, $params, $columns, {extend_columns => 
1});
+    my $get_options = { extend_columns => 1 };
+    $get_options->{cache_skip} = 1 if ($App::options{cache_skip});
+    $get_options->{cache_refresh} = 1 if ($App::options{cache_refresh});
+    my $rows     = $db->get_rows($table, $params, $columns, $get_options);
     my ($subtotal_rows, $total_rows);
     if ($App::options{subtotal_columns}) {
         my $subtotal_columns = [ split(/,/, $App::options{subtotal_columns}) ];

Modified: p5ee/trunk/App-Repository/lib/App/Repository.pm
==============================================================================
--- p5ee/trunk/App-Repository/lib/App/Repository.pm     (original)
+++ p5ee/trunk/App-Repository/lib/App/Repository.pm     Fri Feb 22 12:48:06 2008
@@ -613,6 +613,7 @@
     }
     else {
         $self->_load_table_metadata($table) if (! defined 
$self->{table}{$table}{loaded});
+
         if (!defined $cols) {
             $cols = $self->_get_default_columns($table);
         }
@@ -624,33 +625,80 @@
             @$cols = @$columns;
         }
 
-        my ($col, $contains_expr);
-        my $column_defs = $self->{table}{$table}{column};
-        for (my $i = 0; $i <= $#$cols; $i++) {
-            $col = $cols->[$i];
-            $contains_expr = 1 if ($column_defs->{$col}{expr});
-            # TO BE IMPLEMENTED: Automatically follow relationships for column 
defs
-            # TO BE IMPLEMENTED: Delegated get_rows() and merge on another 
table
-            #for ($rel = 0; $rel <= $#rel_prefix; $rel++) {
-            #    $rel_prefix  = $rel_prefix[$rel];
-            #    $rel_cols    = $rel_cols[$rel];
-            #    $rel_col_idx = $rel_col_idx[$rel];
-            #    if ($col =~ /^${rel_prefix}_(.+)$/) {
-            #        $col2 = $1;
-            #        push(@$rel_cols, $col2);
-            #        $rel_col_idx->[$#$rel_cols] = $i;
-            #        last;
-            #    }
-            #}
-        }
-        if ($contains_expr) {
-            $cols = $self->extend_columns($table, $cols);
-        }
-
-        $row = $self->_get_row($table, $params, $cols, $options);
+        my $tabledef = $self->{table}{$table};
+        my ($sds, $hashkey, @cache_colidx_map);
+        if ($tabledef->{cache_name} && !$options->{cache_skip}) {
+            my $context = $self->{context};
+            my $cache_minimum_columns = $tabledef->{cache_minimum_columns};
+            if ($cache_minimum_columns) {
+                my (%colidx, $col);
+                my $cache_columns = [ @$cache_minimum_columns ];
+                for (my $i = 0; $i <= $#$cache_minimum_columns; $i++) {
+                    $col = $cache_minimum_columns->[$i];
+                    $colidx{$col} = $i;
+                }
+                foreach $col (sort @$cols) {
+                    if (! defined $colidx{$col}) {
+                        push(@$cache_columns, $col);
+                        $colidx{$col} = $#$cache_columns;
+                    }
+                }
+                for (my $i = 0; $i <= $#$cols; $i++) {
+                    $col = $cols->[$i];
+                    $cache_colidx_map[$i] = $colidx{$col};
+                }
+                $cols = $cache_columns;
+            }
+            $sds = $context->shared_datastore($tabledef->{cache_name});
+            my ($hash_options);
+            if (defined $options) {
+                $hash_options = { %$options };
+                delete $hash_options->{cache_skip};
+                delete $hash_options->{cache_refresh};
+                $hash_options = undef if (! %$hash_options);
+            }
+            $hashkey = $sds->hashkey([$table, $params, $cols, $hash_options, 
"row"]);
+            if (!$options->{cache_refresh}) {
+                $row = $sds->get_ref($hashkey);
+            }
+        }
+
+        if (! defined $row) {
+            my ($col, $contains_expr);
+            my $column_defs = $self->{table}{$table}{column};
+            for (my $i = 0; $i <= $#$cols; $i++) {
+                $col = $cols->[$i];
+                $contains_expr = 1 if ($column_defs->{$col}{expr});
+                # TO BE IMPLEMENTED: Automatically follow relationships for 
column defs
+                # TO BE IMPLEMENTED: Delegated get_rows() and merge on another 
table
+                #for ($rel = 0; $rel <= $#rel_prefix; $rel++) {
+                #    $rel_prefix  = $rel_prefix[$rel];
+                #    $rel_cols    = $rel_cols[$rel];
+                #    $rel_col_idx = $rel_col_idx[$rel];
+                #    if ($col =~ /^${rel_prefix}_(.+)$/) {
+                #        $col2 = $1;
+                #        push(@$rel_cols, $col2);
+                #        $rel_col_idx->[$#$rel_cols] = $i;
+                #        last;
+                #    }
+                #}
+            }
+            if ($contains_expr) {
+                $cols = $self->extend_columns($table, $cols);
+            }
+
+            $row = $self->_get_row($table, $params, $cols, $options);
+
+            if ($contains_expr) {
+                $self->evaluate_expressions($table, $params, $cols, [$row], 
$options);
+            }
 
-        if ($contains_expr) {
-            $self->evaluate_expressions($table, $params, $cols, [$row], 
$options);
+            if ($sds) {
+                $sds->set_ref($hashkey, $row);
+            }
+        }
+        if ($sds && $tabledef->{cache_minimum_columns} && $row) {
+            $row = [ @[EMAIL PROTECTED] ];
         }
     }
     &App::sub_exit($row) if ($App::trace);
@@ -849,6 +897,7 @@
     }
     else {
         $self->_load_table_metadata($table) if (! defined 
$self->{table}{$table}{loaded});
+
         if (!defined $cols) {
             $cols = $self->_get_default_columns($table);
         }
@@ -860,38 +909,88 @@
             @$cols = @$columns;
         }
 
-        my ($col, $contains_expr);
-        my $column_defs = $self->{table}{$table}{column};
-        for (my $i = 0; $i <= $#$cols; $i++) {
-            $col = $cols->[$i];
-            $contains_expr = 1 if ($column_defs->{$col}{expr});
-            # TO BE IMPLEMENTED: Automatically follow relationships for column 
defs
-            # TO BE IMPLEMENTED: Delegated get_rows() and merge on another 
table
-            #for ($rel = 0; $rel <= $#rel_prefix; $rel++) {
-            #    $rel_prefix  = $rel_prefix[$rel];
-            #    $rel_cols    = $rel_cols[$rel];
-            #    $rel_col_idx = $rel_col_idx[$rel];
-            #    if ($col =~ /^${rel_prefix}_(.+)$/) {
-            #        $col2 = $1;
-            #        push(@$rel_cols, $col2);
-            #        $rel_col_idx->[$#$rel_cols] = $i;
-            #        last;
-            #    }
-            #}
-        }
-        if ($contains_expr) {
-            my $new_cols = $self->extend_columns($table, $cols);
-            # the caller wanted his column list extended
-            if ($#$new_cols > $#$cols && $options->{extend_columns}) {
-                @$cols = @$new_cols;  # so copy the columns
+        my $tabledef = $self->{table}{$table};
+        my ($sds, $hashkey, @cache_colidx_map);
+        if ($tabledef->{cache_name} && !$options->{cache_skip}) {
+            my $context = $self->{context};
+            my $cache_minimum_columns = $tabledef->{cache_minimum_columns};
+            if ($cache_minimum_columns) {
+                my (%colidx, $col);
+                my $cache_columns = [ @$cache_minimum_columns ];
+                for (my $i = 0; $i <= $#$cache_minimum_columns; $i++) {
+                    $col = $cache_minimum_columns->[$i];
+                    $colidx{$col} = $i;
+                }
+                for (my $i = 0; $i <= $#$cols; $i++) {
+                    $col = $cols->[$i];
+                    if (! defined $colidx{$col}) {
+                        push(@$cache_columns, $col);
+                        $colidx{$col} = $#$cache_columns;
+                    }
+                    $cache_colidx_map[$i] = $colidx{$col};
+                }
+                $cols = $cache_columns;
+            }
+            $sds = $context->shared_datastore($tabledef->{cache_name});
+            my ($hash_options);
+            if (defined $options) {
+                $hash_options = { %$options };
+                delete $hash_options->{cache_skip};
+                delete $hash_options->{cache_refresh};
+                $hash_options = undef if (! %$hash_options);
+            }
+            $hashkey = $sds->hashkey([$table, $params, $cols, $hash_options, 
"row"]);
+            if (!$options->{cache_refresh}) {
+                $rows = $sds->get_ref($hashkey);
+            }
+        }
+
+        if (! defined $rows) {
+
+            my ($col, $contains_expr);
+            my $column_defs = $self->{table}{$table}{column};
+            for (my $i = 0; $i <= $#$cols; $i++) {
+                $col = $cols->[$i];
+                $contains_expr = 1 if ($column_defs->{$col}{expr});
+                # TO BE IMPLEMENTED: Automatically follow relationships for 
column defs
+                # TO BE IMPLEMENTED: Delegated get_rows() and merge on another 
table
+                #for ($rel = 0; $rel <= $#rel_prefix; $rel++) {
+                #    $rel_prefix  = $rel_prefix[$rel];
+                #    $rel_cols    = $rel_cols[$rel];
+                #    $rel_col_idx = $rel_col_idx[$rel];
+                #    if ($col =~ /^${rel_prefix}_(.+)$/) {
+                #        $col2 = $1;
+                #        push(@$rel_cols, $col2);
+                #        $rel_col_idx->[$#$rel_cols] = $i;
+                #        last;
+                #    }
+                #}
+            }
+            if ($contains_expr) {
+                my $new_cols = $self->extend_columns($table, $cols);
+                # the caller wanted his column list extended
+                if ($#$new_cols > $#$cols && $options->{extend_columns}) {
+                    @$cols = @$new_cols;  # so copy the columns
+                }
+                $cols = $new_cols;        # then point to the new columns 
regardless
+            }
+    
+            $rows = $self->_get_rows($table, $params, $cols, $options);
+    
+            if ($contains_expr) {
+                $self->evaluate_expressions($table, $params, $cols, $rows, 
$options);
             }
-            $cols = $new_cols;        # then point to the new columns 
regardless
-        }
-
-        $rows = $self->_get_rows($table, $params, $cols, $options);
 
-        if ($contains_expr) {
-            $self->evaluate_expressions($table, $params, $cols, $rows, 
$options);
+            if ($sds) {
+                $sds->set_ref($hashkey, $rows);
+            }
+        }
+        if ($sds && $tabledef->{cache_minimum_columns}) {
+            my $requested_rows = [];
+            foreach my $row (@$rows) {
+                push(@$requested_rows, [ @[EMAIL PROTECTED] ]);
+            }
+            $rows = $requested_rows;
         }
     }
     &App::sub_exit($rows) if ($App::trace);

Modified: p5ee/trunk/App-Repository/lib/App/Repository/DBI.pm
==============================================================================
--- p5ee/trunk/App-Repository/lib/App/Repository/DBI.pm (original)
+++ p5ee/trunk/App-Repository/lib/App/Repository/DBI.pm Fri Feb 22 12:48:06 2008
@@ -2818,7 +2818,8 @@
         # get a list of the physical tables from the database
         # in MySQL 4.0.13, the table names are surrounded by backticks (!?!)
         # so for safe measure, get rid of all quotes
-        @tables = grep(s/['"`]//g, $dbh->tables(undef, undef, undef, "TABLE"));
+        # Also, get rid of prepended schema names.
+        @tables = grep(s/^[^.]+\.//, grep(s/['"`]//g, $dbh->tables(undef, 
undef, undef, "TABLE")));
 
         # REMOVE ALL DEPENDENCE ON DBIx::Compat
         # if the DBI method doesn't work, try the DBIx method...

Added: p5ee/trunk/App-Repository/lib/App/SharedDatastore/Repository.pm
==============================================================================
--- (empty file)
+++ p5ee/trunk/App-Repository/lib/App/SharedDatastore/Repository.pm     Fri Feb 
22 12:48:06 2008
@@ -0,0 +1,257 @@
+
+#############################################################################
+## $Id: Repository.pm 6783 2006-08-11 17:43:28Z spadkins $
+#############################################################################
+
+package App::SharedDatastore::Repository;
+$VERSION = (q$Revision: 6783 $ =~ /(\d[\d\.]*)/)[0];  # VERSION numbers 
generated by svn
+
+use App;
+use App::SharedDatastore;
[EMAIL PROTECTED] = ( "App::SharedDatastore" );
+
+use strict;
+
+use Storable qw(nfreeze thaw);
+use Digest::SHA qw(sha1_hex);
+use Date::Format;
+
+$Storable::canonical = 1;  # this will cause hashes to be serialized the same 
way every time
+
+=head1 NAME
+
+App::SharedDatastore - Interface for sharing data between processes
+
+=head1 SYNOPSIS
+
+    use App;
+
+    $context = App->context();
+    $sds = $context->service("SharedDatastore");
+    $sds = $context->shared_datastore();
+
+=head1 DESCRIPTION
+
+A SharedDatastore service represents a single hash in which scalars or
+deep references may be stored (basically an MLDBM).
+
+=cut
+
+#############################################################################
+# CLASS GROUP
+#############################################################################
+
+=head1 Class Group: SharedDatastore
+
+The following classes might be a part of the SharedDatastore Class Group.
+
+=over
+
+=item * Class: App::SharedDatastore
+
+=item * Class: App::SharedDatastore::Repository
+
+=item * Class: App::SharedDatastore::IPCMM
+
+=item * Class: App::SharedDatastore::DBI
+
+=item * Class: App::SharedDatastore::MLDBM
+
+=item * Class: App::SharedDatastore::ApacheSession
+
+=item * Class: App::SharedDatastore::IPCShareLite
+
+=item * Class: App::SharedDatastore::IPCShareable
+
+=back
+
+=cut
+
+#############################################################################
+# CLASS
+#############################################################################
+
+=head1 Class: App::SharedDatastore::Repository
+
+A SharedDatastore service represents a single hash in which scalars or
+deep references may be stored.  (They are automatically serialized
+for storage.) 
+
+A sample configuration for an App::SharedDatastore::Repository is the 
following.
+
+        SharedDatastore => {
+            default => {
+                class                     => 
"App::SharedDatastore::Repository",
+                compress                  => 1,
+                repository                => "default",
+                table                     => "app_cache",
+                cache_type                => "dbquery",
+                cache_type_column         => "cache_type",
+                cache_key_column          => "cache_key",
+                data_column               => "data",
+                generate_dttm_column      => "generate_dttm",
+                serializer_column         => "serializer",
+                serialization_args_column => "serialization_args",
+            },
+
+=cut
+
+#############################################################################
+# CONSTRUCTOR METHODS
+#############################################################################
+
+=head1 Constructor Methods:
+
+=cut
+
+#############################################################################
+# new()
+#############################################################################
+
+=head2 new()
+
+The constructor is inherited from
+L<C<App::Service>|App::Service/"new()">.
+
+=cut
+
+#############################################################################
+# _init()
+#############################################################################
+
+=head2 _init()
+
+=cut
+
+sub _init {
+    &App::sub_entry if ($App::trace);
+    my ($self) = @_;
+    $self->SUPER::_init();
+    &App::sub_exit() if ($App::trace);
+}
+
+#############################################################################
+# PUBLIC METHODS
+#############################################################################
+
+=head1 Public Methods:
+
+=cut
+
+#############################################################################
+# set()
+#############################################################################
+
+=head2 set()
+
+    * Signature: $sds->set($key, $value);
+    * Signature: $sds->set($key, $value, $options);
+    * Param:     $key               scalar
+    * Param:     $value             scalar
+    * Param:     $options           HASH (optional)
+    * Return:    void
+
+    $sds->set($key,$value);
+    $options = {
+        info_columns => [ "col1", "col2" ],
+        info_values  => [ "value1", "value2" ],
+    };
+    $sds->set($key, $value, $options);
+
+=cut
+
+sub set {
+    &App::sub_entry if ($App::trace);
+    my ($self, $key, $value, $options) = @_;
+
+    my $context                     = $self->{context};
+    my $rep                         = 
$context->repository($self->{repository});
+    my $table                       = $self->{table}              || 
"app_cache";
+    my $cache_type                  = $self->{cache_type}         || "default";
+    my $cache_type_column           = $self->{cache_type_column}  || 
"cache_type";
+    my $cache_key_column            = $self->{cache_key_column}   || 
"cache_key";
+    my $data_column                 = $self->{data_column}        || "data";
+
+    my @columns = ($cache_type_column, $cache_key_column, $data_column);
+    my @row     = ($cache_type,        $key,              $value);
+    my %update_columns = ( $data_column => 1 );
+
+    my $generate_dttm_column        = $self->{generate_dttm_column};
+    if ($generate_dttm_column) {
+        push(@columns, $generate_dttm_column);
+        push(@row, time2str("%Y-%m-%d %H:%M:%S", time()));
+        $update_columns{$generate_dttm_column} = 1;
+    }
+
+    my $serializer_column           = $self->{serializer_column};
+    if ($serializer_column) {
+        push(@columns, $serializer_column);
+        push(@row, "internal");
+        $update_columns{$serializer_column} = 1;
+    }
+
+    my $serialization_args_column   = $self->{serialization_args_column};
+    if ($serialization_args_column) {
+        push(@columns, $serialization_args_column);
+        my $serialization_args = "";
+        $serialization_args = "compress" if ($self->{compress});
+        if ($self->{base64}) {
+            $serialization_args .= "," if ($serialization_args);
+            $serialization_args .= "base64";
+        }
+        push(@row, $serialization_args);
+        $update_columns{$serialization_args_column} = 1;
+    }
+
+    $rep->insert($table, [EMAIL PROTECTED], [EMAIL PROTECTED], { update => 
\%update_columns });
+
+    &App::sub_exit() if ($App::trace);
+}
+
+#############################################################################
+# get()
+#############################################################################
+
+=head2 get()
+
+    * Signature: $value = $sds->get($key);
+    * Param:     $key               scalar
+    * Return:    $value             scalar
+
+    $value = $sds->get($key);
+
+=cut
+
+sub get {
+    &App::sub_entry if ($App::trace);
+    my ($self, $key) = @_;
+
+    my $context                     = $self->{context};
+    my $rep                         = 
$context->repository($self->{repository});
+    my $table                       = $self->{table}              || 
"app_cache";
+    my $cache_type                  = $self->{cache_type}         || "default";
+    my $cache_type_column           = $self->{cache_type_column}  || 
"cache_type";
+    my $cache_key_column            = $self->{cache_key_column}   || 
"cache_key";
+    my $data_column                 = $self->{data_column}        || "data";
+
+    my $value = $rep->get($table, { $cache_type_column => $cache_type, 
$cache_key_column => $key }, $data_column);
+
+    &App::sub_exit("<binary>") if ($App::trace);
+    return($value);
+}
+
+=head1 ACKNOWLEDGEMENTS
+
+ * Author:  Stephen Adkins <[EMAIL PROTECTED]>
+ * License: This is free software. It is licensed under the same terms as Perl 
itself.
+
+=head1 SEE ALSO
+
+L<C<App::Context>|App::Context>,
+L<C<App::Service>|App::Service>
+L<C<App::SharedDatastore>|App::SharedDatastore>
+
+=cut
+
+1;
+

Added: p5ee/trunk/App-Repository/t/DBI-getset-cache.t
==============================================================================
--- (empty file)
+++ p5ee/trunk/App-Repository/t/DBI-getset-cache.t      Fri Feb 22 12:48:06 2008
@@ -0,0 +1,215 @@
+#!/usr/local/bin/perl -w
+
+use App::Options (
+    options => [qw(dbdriver dbclass dbhost dbname dbuser dbpass)],
+    option => {
+        dbclass  => { default => "App::Repository::MySQL", },
+        dbdriver => { default => "mysql", },
+        dbhost   => { default => "localhost", },
+        dbname   => { default => "test", },
+        dbuser   => { default => "", },
+        dbpass   => { default => "", },
+    },
+);
+
+use Test::More qw(no_plan);
+use lib "../App-Context/lib";
+use lib "../../App-Context/lib";
+use lib "lib";
+use lib "../lib";
+
+use App;
+use App::Repository;
+use strict;
+
+if (!$App::options{dbuser}) {
+    ok(1, "No dbuser given. Tests assumed OK. (add dbuser=xxx and dbpass=yyy 
to app.conf in 't' directory)");
+    exit(0);
+}
+
+my $context = App->context(
+    conf_file => "",
+    conf => {
+        Repository => {
+            default => {
+                class => $App::options{dbclass},
+                dbdriver => $App::options{dbdriver},
+                dbhost => $App::options{dbhost},
+                dbname => $App::options{dbname},
+                dbuser => $App::options{dbuser},
+                dbpass => $App::options{dbpass},
+                table => {
+                    test_person => {
+                        primary_key => ["person_id"],
+                        cache_name => "test_cache",
+                        cache_minimum_columns => [ "person_id", "age", 
"first_name", "gender", ],
+                    },
+                },
+            },
+        },
+        SharedDatastore => {
+            test_cache => {
+                class                     => 
"App::SharedDatastore::Repository",
+                compress                  => 1,
+                repository                => "default",
+                table                     => "app_cache",
+                cache_type                => "dbquery",
+                cache_type_column         => "cache_type",
+                cache_key_column          => "cache_key",
+                data_column               => "data",
+                generate_dttm_column      => "generate_dttm",
+                serializer_column         => "serializer",
+                serialization_args_column => "serialization_args",
+            },
+        },
+    },
+    debug_sql => $App::options{debug_sql},
+);
+
+my $rep = $context->repository();
+
+{
+    #cheating... I know its a DBI, but I have to set up the test somehow
+    my $dbh     = $rep->{dbh};
+    eval { $dbh->do("drop table test_person"); };
+
+    my $ddl     = <<EOF;
+create table test_person (
+    person_id          integer      not null auto_increment primary key,
+    first_name         varchar(99)  null,
+    last_name          varchar(99)  null,
+    address            varchar(99)  null,
+    city               varchar(99)  null,
+    state              varchar(99)  null,
+    zip                varchar(10)  null,
+    country            char(2)      null,
+    home_phone         varchar(99)  null,
+    work_phone         varchar(99)  null,
+    email_address      varchar(99)  null,
+    gender             char(1)      null,
+    birth_dt           date         null,
+    age                integer      null,
+    index person_ie1 (last_name, first_name)
+)
+EOF
+    $dbh->do($ddl);
+
+    eval { $dbh->do("drop table if exists app_cache"); };
+    $ddl = <<EOF;
+create table app_cache (
+    cache_type         varchar(16) not null,
+    cache_key          varchar(40) not null,
+    generate_dttm      datetime    default null,
+    serializer         varchar(12) default null,
+    serialization_args varchar(64) default null,
+    data               longblob,
+    modify_dttm        timestamp   not null default CURRENT_TIMESTAMP on 
update CURRENT_TIMESTAMP,
+    PRIMARY KEY        (cache_type,cache_key),
+    KEY app_cache_ie1  (modify_dttm)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+EOF
+    $dbh->do($ddl);
+
+    $dbh->do("insert into test_person (person_id,age,first_name,gender,state) 
values (1,39,'stephen',  'M','GA')");
+    $dbh->do("insert into test_person (person_id,age,first_name,gender,state) 
values (2,37,'susan',    'F','GA')");
+    $dbh->do("insert into test_person (person_id,age,first_name,gender,state) 
values (3, 6,'maryalice','F','GA')");
+    $dbh->do("insert into test_person (person_id,age,first_name,gender,state) 
values (4, 3,'paul',     'M','GA')");
+    $dbh->do("insert into test_person (person_id,age,first_name,gender,state) 
values (5, 1,'christine','F','GA')");
+    $dbh->do("insert into test_person (person_id,age,first_name,gender,state) 
values (6,45,'tim',      'M','FL')");
+    $dbh->do("insert into test_person (person_id,age,first_name,gender,state) 
values (7,39,'keith',    'M','GA')");
+}
+
+###########################################################################
+# DATA ACCESS TESTS
+###########################################################################
+my ($person_id, $first_name, $last_name, $address, $city, $state, $zip, 
$country);
+my ($home_phone, $work_phone, $email_address, $gender, $birth_dt, $age);
+
+my $columns = [ "person_id", "age", "first_name", "gender", "state" ];
+my $rows = [
+    [ 1, 39, "stephen",   "M", "GA", ],
+    [ 2, 37, "susan",     "F", "GA", ],
+    [ 3,  6, "maryalice", "F", "GA", ],
+    [ 4,  3, "paul",      "M", "GA", ],
+    [ 5,  1, "christine", "F", "GA", ],
+    [ 6, 45, "tim",       "M", "FL", ],
+    [ 7, 39, "keith",     "M", "GA", ],
+];
+
+my ($row, $data_rows, $data_rows2, $nrows);
+
+#####################################################################
+#  $value  = $rep->get ($table, $key,     $col,   \%options);
+#  $rep->set($table, $key,     $col,   $value,    \%options);
+#####################################################################
+$data_rows = $rep->get_rows("test_person", {}, ["state"], 
{order_by=>["person_id"]});
+$first_name = $rep->get("test_person", 1, "first_name");
+is($first_name, "stephen", "get() first_name [$first_name]");
+is($rep->set("test_person", 1, "first_name", "steve"),1,"set() first name 
[steve]");
+$first_name = $rep->get("test_person", 1, "first_name");
+is($first_name, "stephen", "get() modified first_name [$first_name] got cache 
instead");
+$first_name = $rep->get("test_person", {person_id => 1}, "first_name");
+is($first_name, "steve", "get() modified first_name [$first_name]");
+$age = $rep->get("test_person", 1, "age");
+is($age, 39, "get() age");
+
+ok($rep->set("test_person", 2, ["first_name","age"], ["sue",38]), "set() 2 
values");
+($first_name, $age) = $rep->get("test_person", 2, ["first_name","age"]);
+is($first_name, "sue", "get() 2 values (checking 1 of 2)");
+is($age, 38, "get() 2 values (checking 2 of 2)");
+
+ok($rep->set_row("test_person", 3, ["age", "state"], [7, "CA"]),"set_row() 2 
values");
+$row = $rep->get_row("test_person", 4, ["age", "gender"]);
+($age, $gender) = @$row;
+is($age, 3, "get_row() 2 values (checking 1 of 2)");
+is($gender, "M", "get_row() 2 values (checking 2 of 2)");
+
+ok($rep->set_row("test_person", {first_name=>'paul'}, ["age", "state"], [5, 
"CA"]),"set_row() 2 values w/ %crit");
+$row = $rep->get_row("test_person", {first_name=>'paul'}, ["age", 
"state","person_id"]);
+($age, $state, $person_id) = @$row;
+is($age,         5, "get_row() 3 values w/ %crit (checking 1 of 3) age=$age");
+is($state,    "CA", "get_row() 3 values w/ %crit (checking 2 of 3) 
state=$state");
+is($person_id,   4, "get_row() 3 values w/ %crit (checking 3 of 3) 
person_id=$person_id");
+
+ok($rep->set_row("test_person", {first_name=>'paul'}, ["age", "state"], 
{age=>6, state=>"GA", person_id=>99}),
+   "set_row() 2 values w/ %crit and values in hash");
+
+$row = $rep->get_row("test_person", {first_name=>'paul'}, ["age", 
"state","person_id"]);
+($age, $state, $person_id) = @$row;
+is($age,         5, "get_row() 3 values w/ %crit (checking 1 of 3) age=$age 
got cache instead");
+is($state,    "CA", "get_row() 3 values w/ %crit (checking 2 of 3) 
state=$state got cache instead");
+is($person_id,   4, "get_row() 3 values w/ %crit (checking 3 of 3) 
person_id=$person_id");
+
+$data_rows = $rep->get_rows("test_person", {first_name=>'paul', x=>1}, ["age", 
"state","person_id"]);
+$row = $data_rows->[0];
+($age, $state, $person_id) = @$row;
+is($age,         6, "get_row() 3 values w/ %crit (checking 1 of 3) age=$age");
+is($state,    "GA", "get_row() 3 values w/ %crit (checking 2 of 3) 
state=$state");
+is($person_id,   4, "get_row() 3 values w/ %crit (checking 3 of 3) 
person_id=$person_id");
+
+$data_rows = $rep->get_rows("test_person", {}, [ "person_id", "age", 
"first_name", "gender", "state" ], {order_by=>["person_id"]});
+is_deeply($data_rows, $rows, "get_rows() got original cached data thanks to 
cache_minimum_rows");
+
+$data_rows2 = $rep->{dbh}->selectall_arrayref("select person_id, age, 
first_name, gender, state from test_person order by person_id");
+$data_rows = $rep->get_rows("test_person", {}, [ "person_id", "age", 
"first_name", "gender", "state" ], {order_by=>["person_id"], cache_skip => 1});
+is_deeply($data_rows, $data_rows2, "get_rows() skipped cached data thanks to 
cache_skip");
+$first_name = $rep->get("test_person", 1, "first_name", { cache_skip => 1 });
+is($first_name, "steve", "get() modified first_name [$first_name] by skipping 
the cache");
+
+$data_rows = $rep->get_rows("test_person", {}, [ "person_id", "age", 
"first_name", "gender", "state" ], {order_by=>["person_id"], cache_refresh => 
1});
+is_deeply($data_rows, $data_rows2, "get_rows() refreshed cached data thanks to 
cache_refresh");
+$first_name = $rep->get("test_person", 1, "first_name", { cache_refresh => 1 
});
+is($first_name, "steve", "get() modified first_name [$first_name] by 
refreshing the cache");
+
+$data_rows = $rep->get_rows("test_person", {}, [ "person_id", "age", 
"first_name", "gender", "state" ], {order_by=>["person_id"]});
+is_deeply($data_rows, $data_rows2, "get_rows() confirmed that the cache was 
refreshed");
+$first_name = $rep->get("test_person", 1, "first_name");
+is($first_name, "steve", "get() modified first_name [$first_name] confirming 
that the cache was refreshed");
+
+{
+    my $dbh = $rep->{dbh};
+    $dbh->do("drop table test_person");
+}
+
+exit 0;
+

Modified: p5ee/trunk/App-Repository/t/DBI-metadata.t
==============================================================================
--- p5ee/trunk/App-Repository/t/DBI-metadata.t  (original)
+++ p5ee/trunk/App-Repository/t/DBI-metadata.t  Fri Feb 22 12:48:06 2008
@@ -59,6 +59,7 @@
 {
     #cheating... I know its a DBI, but I have to set up the test somehow
     my $dbh     = $db->{dbh};
+    eval { $dbh->do("drop table if exists app_cache"); };
     eval { $dbh->do("drop table test_person"); };
     my $ddl     = <<EOF;
 create table test_person (
@@ -87,7 +88,7 @@
 # METADATA TESTS
 ###########################################################################
 my $table_names = $db->get_table_names();
-#print "[EMAIL PROTECTED]";
+print "[EMAIL PROTECTED]";
 my %tables = ( map { $_ => 1 } @$table_names );
 ok(defined $tables{test_person}, "get_table_names()");
 $db->_load_rep_metadata();

Added: p5ee/trunk/App-Repository/t/SharedDatastore.t
==============================================================================
--- (empty file)
+++ p5ee/trunk/App-Repository/t/SharedDatastore.t       Fri Feb 22 12:48:06 2008
@@ -0,0 +1,154 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use App::Options (
+    options => [qw(dbdriver dbclass dbhost dbname dbuser dbpass)],
+    option => {
+        dbclass  => { default => "App::Repository::MySQL", },
+        dbdriver => { default => "mysql", },
+        dbhost   => { default => "localhost", },
+        dbname   => { default => "test", },
+        dbuser   => { default => "", },
+        dbpass   => { default => "", },
+    },
+);
+
+use Test::More qw(no_plan);
+use lib "../App-Context/lib";
+use lib "../../App-Context/lib";
+use lib "lib";
+use lib "../lib";
+
+if (!$App::options{dbuser}) {
+    ok(1, "No dbuser given. Tests assumed OK. (add dbuser=xxx and dbpass=yyy 
to app.conf in 't' directory)");
+    exit(0);
+}
+
+BEGIN {
+   use_ok("App");
+}
+
+my $context = App->context(
+    %App::options,
+    conf_file => "",
+    conf => {
+        Repository => {
+            default => {
+                class => $App::options{dbclass},
+                dbdriver => $App::options{dbdriver},
+                dbhost => $App::options{dbhost},
+                dbname => $App::options{dbname},
+                dbuser => $App::options{dbuser},
+                dbpass => $App::options{dbpass},
+                table => {
+                    test_person => {
+                        primary_key => ["person_id"],
+                        column => {
+                            data => {
+                                dbexpr => "ifnull(uncompress(data),data)",
+                                dbexpr_update => "compress(%s)",
+                            },
+                        },
+                    },
+                },
+            },
+        },
+        SharedDatastore => {
+            default => {
+                class                     => 
"App::SharedDatastore::Repository",
+                compress                  => 1,
+                repository                => "default",
+                table                     => "app_cache",
+                cache_type                => "dbquery",
+                cache_type_column         => "cache_type",
+                cache_key_column          => "cache_key",
+                data_column               => "data",
+                generate_dttm_column      => "generate_dttm",
+                serializer_column         => "serializer",
+                serialization_args_column => "serialization_args",
+            },
+        },
+    },
+);
+
+{
+    &initialize_tests();
+
+    my ($sds, $key, $value, $keyref, $valueref, $valueref2, $hashkey, 
$serialized_value);
+
+    foreach my $name ("default") {
+        $sds = $context->service("SharedDatastore", $name);
+        ok(defined $sds, "[$name] constructor ok");
+
+        isa_ok($sds, "App::SharedDatastore", "[$name] right class");
+        is($sds->service_type(), "SharedDatastore", "[$name] right service 
type");
+
+        my $dump = $sds->dump();
+        ok($dump =~ /^\$SharedDatastore__$name = /, "[$name] dump");
+
+        $sds->set("pi", 3.1416);
+        $value = $sds->get("pi");
+        is($value, 3.1416, "[$name] set()/get() works (for pi=$value)");
+
+        $keyref = [ "person",
+            { "age.ge" => 21, last_name => "Adkins" },
+            [ "person_id", "last_name", "first_name", "age", "eye_color" ],
+            { numrows => 20, cache => {}, },
+        ];
+        $valueref = [
+            [ 1, "Adkins", "Stephen",        40, "Blue",  ],
+            [ 2, "Adkins", "Susan (Little)", 40, "Brown", ],
+            [ 3, "Adkins", "Bill",           43, "Brown", ],
+            [ 4, "Adkins", "Susan",          44, "Brown", ],
+            [ 5, "Adkins", "Marybeth",       47, "Blue",  ],
+        ];
+
+        $sds->set_ref($keyref, $valueref);
+        $valueref2 = $sds->get_ref($keyref);
+        is_deeply($valueref, $valueref2, "[$name] set_ref()/get_ref() works");
+
+        $hashkey = $sds->hashkey($keyref);
+        $valueref2 = $sds->get_ref($hashkey);
+        is_deeply($valueref, $valueref2, "[$name] set_ref()/get_ref(hashkey) 
works (hashkey=$hashkey)");
+
+        $serialized_value = $sds->serialize($valueref);
+        $value = $sds->get($hashkey);
+        is($value, $serialized_value, "[$name] set_ref()/get(hashkey) works");
+
+        $valueref2 = $sds->deserialize($serialized_value);
+        is_deeply($valueref, $valueref2, "[$name] serialize()/deserialize() 
works");
+
+        $value = $sds->get("foo");
+        is($value, undef, "[$name] get(foo) is undef");
+
+        $valueref2 = $sds->get_ref("foo");
+        is($valueref2, undef, "[$name] get_ref(foo) is undef");
+
+        $sds->set_ref("foo", undef);
+        $value = $sds->get_ref("foo");
+        is($value, undef, "[$name] get_ref(foo) is undef after set to undef");
+    }
+}
+
+sub initialize_tests {
+    my $rep = $context->repository("default");
+    $rep->_do("drop table if exists app_cache");
+    my $ddl = <<EOF;
+CREATE TABLE app_cache (
+  cache_type varchar(16) NOT NULL,
+  cache_key varchar(40) NOT NULL,
+  generate_dttm datetime default NULL,
+  serializer varchar(12) default NULL,
+  serialization_args varchar(64) default NULL,
+  data longblob,
+  modify_dttm timestamp NOT NULL default CURRENT_TIMESTAMP on update 
CURRENT_TIMESTAMP,
+  PRIMARY KEY  (cache_type,cache_key),
+  KEY app_cache_ie1 (modify_dttm)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+EOF
+    $rep->_do($ddl);
+}
+
+exit 0;
+

Reply via email to