The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 013
MANIFEST 30
META.json 55
META.yml 22
MYMETA.json 55
MYMETA.yml 22
lib/DBIx/Custom/Where.pm 213
lib/DBIx/Custom.pm 2330
t/common.t 1156
t/mysql-async-opt-insert.t 610
t/mysql-async-opt.t 3896
11 files changed (This is a version diff) 152222
@@ -1,3 +1,16 @@
+0.36
+  - Fix the bug that the following feature which is add to 0.35 don't work.
+    add join attribute to DBIx::Custom::Where
+    to add join infomartion to select method
+0.35
+  - remove EXPERIMENTAL status the following.
+    add select method column option syntax
+    {__MY__ => '*'}
+  - remove EXPERIMENTAL last_sql attribute
+  - add join attribute to DBIx::Custom::Where
+    to add join infomartion to select method
+0.34
+  - (EXPERIMENTAL) fix async select not getting err bug.
 0.33
   - (EXPERIMENTAL) fix MySQL async insert result.
 0.32
@@ -1,6 +1,4 @@
 Changes
-DBIx-Custom-0.31/META.json
-DBIx-Custom-0.31/META.yml
 lib/DBIx/Custom.pm
 lib/DBIx/Custom/Mapper.pm
 lib/DBIx/Custom/Model.pm
@@ -108,7 +106,6 @@ t/common_uc/MyModel8/TABLE1.pm
 t/common_uc/MyModel8/TABLE2.pm
 t/create_fullqualified_module.pl
 t/create_uppercase_module.pl
-t/mysql-async-opt-insert.t
 t/mysql-async-opt.t
 t/mysql-async.t
 t/mysql.t
@@ -4,7 +4,7 @@
       "Yuki Kimoto <kimoto.yuki@gmail.com>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150",
+   "generated_by" : "ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.131560",
    "license" : [
       "perl_5"
    ],
@@ -22,12 +22,12 @@
    "prereqs" : {
       "build" : {
          "requires" : {
-            "ExtUtils::MakeMaker" : 0
+            "ExtUtils::MakeMaker" : "0"
          }
       },
       "configure" : {
          "requires" : {
-            "ExtUtils::MakeMaker" : 0
+            "ExtUtils::MakeMaker" : "0"
          }
       },
       "runtime" : {
@@ -35,10 +35,10 @@
             "DBD::SQLite" : "1.25",
             "DBI" : "1.605",
             "Object::Simple" : "3.1",
-            "Test::More" : 0
+            "Test::More" : "0"
          }
       }
    },
    "release_status" : "stable",
-   "version" : "0.33"
+   "version" : "0.36"
 }
@@ -7,7 +7,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: 0
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150'
+generated_by: 'ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.131560'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -22,4 +22,4 @@ requires:
   DBI: 1.605
   Object::Simple: 3.1
   Test::More: 0
-version: 0.33
+version: 0.36
@@ -4,7 +4,7 @@
       "Yuki Kimoto <kimoto.yuki@gmail.com>"
    ],
    "dynamic_config" : 0,
-   "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150",
+   "generated_by" : "ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.131560",
    "license" : [
       "perl_5"
    ],
@@ -22,12 +22,12 @@
    "prereqs" : {
       "build" : {
          "requires" : {
-            "ExtUtils::MakeMaker" : 0
+            "ExtUtils::MakeMaker" : "0"
          }
       },
       "configure" : {
          "requires" : {
-            "ExtUtils::MakeMaker" : 0
+            "ExtUtils::MakeMaker" : "0"
          }
       },
       "runtime" : {
@@ -35,10 +35,10 @@
             "DBD::SQLite" : "1.25",
             "DBI" : "1.605",
             "Object::Simple" : "3.1",
-            "Test::More" : 0
+            "Test::More" : "0"
          }
       }
    },
    "release_status" : "stable",
-   "version" : "0.33"
+   "version" : "0.36"
 }
@@ -7,7 +7,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: 0
 dynamic_config: 0
-generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150'
+generated_by: 'ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.131560'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -22,4 +22,4 @@ requires:
   DBI: 1.605
   Object::Simple: 3.1
   Test::More: 0
-version: 0.33
+version: 0.36
@@ -9,8 +9,10 @@ use overload '""' => sub { shift->to_string }, fallback => 1;
 # Carp trust relationship
 push @DBIx::Custom::CARP_NOT, __PACKAGE__;
 
-has [qw/dbi param/],
-  clause => sub { [] };
+has 'dbi';
+has 'param';
+has clause => sub { [] };
+has 'join';
 
 sub new {
   my $self = shift->SUPER::new(@_);
@@ -239,6 +241,15 @@ If all parameter names is exists.
 
 L<DBIx::Custom> object.
 
+=head2 join
+
+  my $join = $where->join;
+  $join = $where->join($join);
+
+join information. This values is addd to select method C<join> option values.
+
+  $where->join(['left join author on book.author = authro.id']);
+  
 =head1 METHODS
 
 L<DBIx::Custom::Where> inherits all methods from L<Object::Simple>
@@ -2,7 +2,7 @@ use 5.008007;
 package DBIx::Custom;
 use Object::Simple -base;
 
-our $VERSION = '0.33';
+our $VERSION = '0.36';
 
 use Carp 'croak';
 use DBI;
@@ -19,7 +19,6 @@ use DBIx::Custom::NotExists;
 use Encode qw/encode encode_utf8 decode_utf8/;
 use Scalar::Util qw/weaken/;
 
-
 has [qw/connector dsn default_schema password quote user exclude_table user_table_info
      user_column_info safety_character/],
   async_conf => sub { {} },
@@ -69,7 +68,6 @@ has [qw/connector dsn default_schema password quote user exclude_table user_tabl
   stash => sub { {} };
 
 has mytable_symbol => '__MY__';
-has 'last_sth';
 
 sub available_datatype {
   my $self = shift;
@@ -371,7 +369,7 @@ sub execute {
     my $new_dbi = bless {%$self}, ref $self;
     $new_dbi->connector(undef);
     $new_dbi->{dbh} = DBI->connect($dsn, $user, $password,
-      {%{$new_dbi->default_option}, %$option});
+      {%{$new_dbi->default_option}, %$option, PrintError => 0, RaiseError => 0});
     
     $new_dbi->{_new_connection} = 1;
     return $new_dbi->execute($sql, defined $params ? ($params) : (), %opt);
@@ -593,10 +591,7 @@ sub execute {
   }
 
   # Affected of insert, update, or delete
-  $self->last_sth($sth);
   if (!$sth->{NUM_OF_FIELDS} && $opt{statement} ne 'select') {
-    my $driver = $self->_driver;
-    
     # Non-Blocking
     if (my $cb = $opt{async}) {
       require AnyEvent;
@@ -606,6 +601,7 @@ sub execute {
         poll => 'w',
         cb => sub {
           my $affected;
+          my $driver = $self->_driver;
           if ($driver eq 'mysql') {
             $affected = $sth->mysql_async_result;
           }
@@ -656,6 +652,12 @@ sub execute {
         fh => $self->async_conf->{fh}->($self),
         poll => 'r',
         cb   => sub {
+          my $error;
+          my $driver = $self->_driver;
+          if ($driver eq 'mysql') {
+            $sth->mysql_async_result;
+          }
+          
           $cb->($self, $result);
           undef $watcher;
           undef $result;
@@ -1154,7 +1156,22 @@ sub select {
   unshift @$tables, @{$self->_search_tables($w->{clause})};
   
   # Join statement
-  $self->_push_join(\$sql, $opt{join}, $tables) if defined $opt{join};
+  my $join = [];
+  if (defined $opt{join}) {
+    my $opt_join = $opt{join};
+    if (ref $opt_join eq 'ARRAY') {
+      push @$join, @$opt_join;
+    }
+    else { push @$join, $opt_join }
+  }
+  if (defined $w->{join}) {
+    my $where_join = $w->{join};
+    if (ref $where_join eq 'ARRAY') {
+      push @$join, @$where_join;
+    }
+    else { push @$join, $where_join }
+  }
+  $self->_push_join(\$sql, $join, $tables) if @$join;
   
   # Add where clause
   $sql .= "$w->{clause} ";
@@ -1690,11 +1707,6 @@ sub _option {
 sub _push_join {
   my ($self, $sql, $join, $join_tables) = @_;
   
-  $join = [$join] unless ref $join eq 'ARRAY';
-  
-  # No join
-  return unless @$join;
-  
   # Push join clause
   my $tree = {};
   for (my $i = 0; $i < @$join; $i++) {
@@ -1853,6 +1865,7 @@ sub _where_clause_and_param {
     $w->{param} = keys %$where_param
       ? $self->merge_param($where_param, $obj->param)
       : $obj->param;
+    $w->{join} = $obj->{join};
   }
   elsif ($where) {
     $w->{clause} = "where $where";
@@ -2423,12 +2436,6 @@ Filters, registered by C<register_filter> method.
 
 Get last succeeded SQL executed by C<execute> method.
 
-=head2 (EXPERIMENTAL) last_sth
-
-  my $last_sth = $dbi->last_sth;
-
-Get last executed statement handle.
-
 =head2 now
 
   my $now = $dbi->now;
@@ -3768,10 +3775,10 @@ You can use this in insert statement.
 
 =head2 where
 
-  my $where = $dbi->where(
-    clause => ['and', 'title = :title', 'author = :author'],
-    param => {title => 'Perl', author => 'Ken'}
-  );
+  my $where = $dbi->where;
+  $where->clause(['and', 'title = :title', 'author = :author']);
+  $where->param({title => 'Perl', author => 'Ken'});
+  $where->join(['left join author on book.author = author.id]);
 
 Create a new L<DBIx::Custom::Where> object.
 See L<DBIx::Custom::Where> to know how to create where clause.
@@ -3523,16 +3523,6 @@ test 'DBIX_CUSTOM_TAG_PARSE environment variable';
   delete$ENV{DBIX_CUSTOM_TAG_PARSE};
 }
 
-test 'last_sql';
-$dbi = DBIx::Custom->connect;
-eval { $dbi->execute("drop table $table1") };
-$dbi->execute($create_table1);
-$dbi->execute("select * from $table1");
-is($dbi->last_sql, " select * from $table1");
-
-eval{$dbi->execute("aaa")};
-is($dbi->last_sql, ' aaa');
-
 test 'DBIx::Custom header';
 $dbi = DBIx::Custom->connect;
 eval { $dbi->execute("drop table $table1") };
@@ -5077,4 +5067,59 @@ $result = $dbi->execute(
 );
 like($result->one->{$key1}, qr/^2010-01-01/);
 
-1;
+# DBIx::Custom::Where join
+{
+  my $dbi = DBIx::Custom->connect;
+  eval { $dbi->execute("drop table $table1") };
+  $dbi->execute($create_table1);
+  $dbi->insert({$key1 => 1, $key2 => 2}, table => $table1);
+  $dbi->insert({$key1 => 3, $key2 => 4}, table => $table1);
+  eval { $dbi->execute("drop table $table2") };
+  $dbi->execute($create_table2);
+  $dbi->insert({$key1 => 1, $key3 => 5}, table => $table2);
+  eval { $dbi->execute("drop table $table3") };
+  $dbi->execute("create table $table3 ($key3 int, $key4 int)");
+  $dbi->insert({$key3 => 5, $key4 => 4}, table => $table3);
+  
+  {
+    my $where = $dbi->where;
+    $where->param({$key1 => 1});
+    $where->clause(":${key1}{=}");
+    $where->join(["left outer join $table3 on $table2.$key3 = $table3.$key3"]);
+
+    my $rows = $dbi->select(
+      table => $table1,
+      where   => $where,
+      join  => ["left outer join $table2 on $table1.$key1 = $table2.$key1"]
+    )->all;
+    is_deeply($rows, [{$key1 => 1, $key2 => 2}]);
+  }
+  {
+    my $where = $dbi->where;
+    $where->param({"$table1.$key1" => 1});
+    $where->clause(":$table1.${key1}{=}");
+    $where->join(["left outer join $table3 on $table2.$key3 = $table3.$key3"]);
+
+    my $rows = $dbi->select(
+      column => "$table3.$key4 as " . u2("${table3}__$key4"),
+      table => $table1,
+      where   => $where,
+      join  => ["left outer join $table2 on $table1.$key1 = $table2.$key1"]
+    )->all;
+    is_deeply($rows, [{u2"${table3}__$key4" => 4}]);
+  }
+  {
+    my $where = $dbi->where;
+    $where->param({"$table3.$key4" => 4});
+    $where->clause(":$table3.${key4}{=}");
+    $where->join(["left outer join $table3 on $table2.$key3 = $table3.$key3"]);
+
+    my $rows = $dbi->select(
+      column => "$table1.$key1 as " . u2("${table1}__$key1"),
+      table => $table1,
+      where   => $where,
+      join  => ["left outer join $table2 on $table1.$key1 = $table2.$key1"]
+    )->all;
+    is_deeply($rows, [{u2"${table1}__$key1" => 1}]);
+  }
+}
@@ -1,61 +0,0 @@
-use Test::More;
-use strict;
-use warnings;
-use utf8;
-
-use FindBin;
-use DBIx::Custom;
-
-my $dbi;
-my $dsn;
-my $args;
-my $user = 'dbix_custom';
-my $password = 'dbix_custom';
-my $database = 'dbix_custom';
-
-$dsn = "dbi:mysql:database=$database";
-$args = {dsn => $dsn, user => $user, password => $password,};
-
-plan skip_all => 'mysql private test' unless -f "$FindBin::Bin/run/mysql-async-opt-insert.run"
-  && eval { $dbi = DBIx::Custom->connect($args); 1 };
-plan 'no_plan';
-
-$SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /DEPRECATED/};
-
-# Function for test name
-sub test { print "# $_[0]\n" }
-
-$dbi = DBIx::Custom->connect(
-  dsn => "dbi:mysql:database=$database;",
-  user => $user,
-  password => $password
-);
-
-eval { $dbi->do('drop table table1') };
-$dbi->do('create table table1 (key1 varchar(255), key2 varchar(255)) engine=InnoDB');
-
-test 'async test';
-
-require AnyEvent;
-
-my $cond = AnyEvent->condvar;
-
-$dbi->async_conf({
-  prepare_attr => {async => 1},
-  fh => sub { shift->dbh->mysql_fd }
-});
-
-$dbi->insert(
-  {key1 => 1, key2 => 2},
-  table => 'table1',
-  async => sub {
-    my ($dbi, $affected) = @_;
-    is($affected, 1);
-    $cond->send;
-  }
-);
-
-$cond->recv;
-
-my $rows = $dbi->select(table => 'table1')->all;
-is_deeply($rows, [{key1 => 1, key2 => 2}]);
@@ -56,46 +56,104 @@ test 'async test';
 
 require AnyEvent;
 
-my $cond = AnyEvent->condvar;
-
-my $timer = AnyEvent->timer(
-  interval => 1,
-  cb => sub {
-    1;
-  }
-);
-
-my $count = 0;
-
 $dbi->async_conf({
   prepare_attr => {async => 1},
   fh => sub { shift->dbh->mysql_fd }
 });
 
-$dbi->execute('SELECT SLEEP(1), 3', undef,
-  select => 1,
-  async => sub {
-    my ($dbi, $result) = @_;
-    my $row = $result->fetch_one;
-    is($row->[1], 3, 'before');
-    $cond->send if ++$count == 2;
-  }
-);
-
-$dbi->select('key1', table => 'table1',
-  async => sub {
-    my ($dbi, $result) = @_;
-    my $row = $result->fetch_one;
-    is($row->[0], 1, 'after1');
-    $dbi->select('key1', table => 'table1',
-      async => sub {
-        my ($dbi, $result) = @_;
-        my $row = $result->fetch_one;
-        is($row->[0], 1, 'after2');
-        $cond->send if ++$count == 2;
-      }
-    )
-  }
-);
-
-$cond->recv;
+# Select
+{
+  my $cond = AnyEvent->condvar;
+
+  my $timer = AnyEvent->timer(
+    interval => 1,
+    cb => sub {
+      1;
+    }
+  );
+
+  my $count = 0;
+
+  $dbi->execute('SELECT SLEEP(1), 3', undef,
+    select => 1,
+    async => sub {
+      my ($dbi, $result) = @_;
+      my $row = $result->fetch_one;
+      is($row->[1], 3, 'before');
+      ok(!$dbi->errstr);
+      $cond->send if ++$count == 2;
+    }
+  );
+
+  $dbi->select('key1', table => 'table1',
+    async => sub {
+      my ($dbi, $result) = @_;
+      my $row = $result->fetch_one;
+      is($row->[0], 1, 'after1');
+      $dbi->select('key1', table => 'table1',
+        async => sub {
+          my ($dbi, $result) = @_;
+          my $row = $result->fetch_one;
+          is($row->[0], 1, 'after2');
+          $cond->send if ++$count == 2;
+        }
+      )
+    }
+  );
+
+  $cond->recv;
+}
+
+# Select error
+{
+  my $cond = AnyEvent->condvar;
+  $dbi->select('key1', table => 'table_not_exists',
+    async => sub {
+      my ($dbi, $result) = @_;
+      ok($dbi->errstr);
+      $cond->send;
+    }
+  );
+  
+  $cond->recv;
+}
+
+# insert
+{
+  $dbi->do('delete from table1');
+  my $cond = AnyEvent->condvar;
+
+  $dbi->insert(
+    {key1 => 1, key2 => 2},
+    table => 'table1',
+    async => sub {
+      my ($dbi, $affected) = @_;
+      is($affected, 1);
+      ok(!$dbi->errstr);
+      $cond->send;
+    }
+  );
+
+  $cond->recv;
+
+  my $rows = $dbi->select(table => 'table1')->all;
+  is_deeply($rows, [{key1 => 1, key2 => 2}]);
+}
+
+# insert error
+{
+  $dbi->do('delete from table1');
+  my $cond = AnyEvent->condvar;
+
+  $dbi->insert(
+    {key1 => 1, key2 => 2},
+    table => 'table_not_exists',
+    async => sub {
+      my ($dbi, $affected) = @_;
+      ok($dbi->errstr);
+      $cond->send;
+    }
+  );
+
+  $cond->recv;
+}