added async asccess and statement option is renamed...
...to select option
| ... | ... |
@@ -1,3 +1,6 @@ |
| 1 |
+0.2108 |
|
| 2 |
+ - added async database access support using AnyEvent |
|
| 3 |
+ and added "async" option to execute method |
|
| 1 | 4 |
0.2107 |
| 2 | 5 |
- removed EXPERIMENTAL status from the following methods and functinalities. |
| 3 | 6 |
DBIx::Custom::Result::value method |
| ... | ... |
@@ -344,6 +344,27 @@ sub execute {
|
| 344 | 344 |
my $params; |
| 345 | 345 |
$params = shift if @_ % 2; |
| 346 | 346 |
my %opt = @_; |
| 347 |
+ |
|
| 348 |
+ # Async query |
|
| 349 |
+ if ($opt{async} && !$self->{_new_connection}) {
|
|
| 350 |
+ my $dsn = $self->dsn; |
|
| 351 |
+ croak qq/Data source must be specified when "async" option is used/ |
|
| 352 |
+ unless defined $dsn; |
|
| 353 |
+ |
|
| 354 |
+ my $user = $self->user; |
|
| 355 |
+ my $password = $self->password; |
|
| 356 |
+ my $option = $self->_option; |
|
| 357 |
+ |
|
| 358 |
+ my $new_dbi = bless {%$self}, ref $self;
|
|
| 359 |
+ $new_dbi->connector(undef); |
|
| 360 |
+ $new_dbi->{dbh} = DBI->connect($dsn, $user, $password,
|
|
| 361 |
+ {%{$new_dbi->default_option}, %$option});
|
|
| 362 |
+ |
|
| 363 |
+ $new_dbi->{_new_connection} = 1;
|
|
| 364 |
+ return $new_dbi->execute($sql, defined $params ? ($params) : (), %opt); |
|
| 365 |
+ } |
|
| 366 |
+ |
|
| 367 |
+ # Options |
|
| 347 | 368 |
warn "sqlfilter option is DEPRECATED" if $opt{sqlfilter};
|
| 348 | 369 |
$params ||= $opt{param} || {};
|
| 349 | 370 |
my $tables = $opt{table} || [];
|
| ... | ... |
@@ -355,6 +376,7 @@ sub execute {
|
| 355 | 376 |
my @cleanup; |
| 356 | 377 |
my $saved_param; |
| 357 | 378 |
$opt{statement} ||= '';
|
| 379 |
+ $opt{statement} = 'select' if $opt{select};
|
|
| 358 | 380 |
if (($opt{statement} || '') ne 'insert' && ref $params eq 'ARRAY') {
|
| 359 | 381 |
my $params2 = $params->[1]; |
| 360 | 382 |
$params = $params->[0]; |
| ... | ... |
@@ -573,7 +595,7 @@ sub execute {
|
| 573 | 595 |
} |
| 574 | 596 |
|
| 575 | 597 |
# Result |
| 576 |
- $self->result_class->new( |
|
| 598 |
+ my $result = $self->result_class->new( |
|
| 577 | 599 |
sth => $sth, |
| 578 | 600 |
dbi => $self, |
| 579 | 601 |
default_filter => $self->{default_in_filter},
|
| ... | ... |
@@ -584,6 +606,22 @@ sub execute {
|
| 584 | 606 |
from2 => $self->type_rule->{from2}
|
| 585 | 607 |
}, |
| 586 | 608 |
); |
| 609 |
+ |
|
| 610 |
+ if (my $cb = $opt{async}) {
|
|
| 611 |
+ require AnyEvent; |
|
| 612 |
+ my $watcher; |
|
| 613 |
+ weaken $self; |
|
| 614 |
+ $watcher = AnyEvent->io( |
|
| 615 |
+ fh => $self->{dbh}->mysql_fd,
|
|
| 616 |
+ poll => 'r', |
|
| 617 |
+ cb => sub {
|
|
| 618 |
+ $cb->($self, $result); |
|
| 619 |
+ $watcher = undef; |
|
| 620 |
+ $result =undef; |
|
| 621 |
+ }, |
|
| 622 |
+ ); |
|
| 623 |
+ } |
|
| 624 |
+ else { $result }
|
|
| 587 | 625 |
} |
| 588 | 626 |
|
| 589 | 627 |
sub get_table_info {
|
| ... | ... |
@@ -2390,6 +2428,56 @@ This is used to create update clause. |
| 2390 | 2428 |
|
| 2391 | 2429 |
"update book set " . $dbi->assign_clause({title => 'a', age => 2});
|
| 2392 | 2430 |
|
| 2431 |
+=head2 C<async> EXPERIMENTAL |
|
| 2432 |
+ |
|
| 2433 |
+ async => sub {
|
|
| 2434 |
+ my ($dbi, $result) = @_; |
|
| 2435 |
+ ... |
|
| 2436 |
+ }; |
|
| 2437 |
+ |
|
| 2438 |
+Database async access. L<AnyEvent> is required. |
|
| 2439 |
+ |
|
| 2440 |
+This is C<mysql> async access example. |
|
| 2441 |
+ |
|
| 2442 |
+ use AnyEvent; |
|
| 2443 |
+ |
|
| 2444 |
+ my $cond = AnyEvent->condvar; |
|
| 2445 |
+ |
|
| 2446 |
+ my $timer = AnyEvent->timer( |
|
| 2447 |
+ interval => 1, |
|
| 2448 |
+ cb => sub { 1 }
|
|
| 2449 |
+ ); |
|
| 2450 |
+ |
|
| 2451 |
+ my $count = 0; |
|
| 2452 |
+ |
|
| 2453 |
+ $dbi->execute('SELECT SLEEP(1), 3', undef,
|
|
| 2454 |
+ prepare_attr => {async => 1}, statement => 'select',
|
|
| 2455 |
+ async => sub {
|
|
| 2456 |
+ my ($dbi, $result) = @_; |
|
| 2457 |
+ my $row = $result->fetch_one; |
|
| 2458 |
+ is($row->[1], 3, 'before'); |
|
| 2459 |
+ $cond->send if ++$count == 2; |
|
| 2460 |
+ } |
|
| 2461 |
+ ); |
|
| 2462 |
+ |
|
| 2463 |
+ $dbi->select('key1', table => 'table1', prepare_attr => {async => 1},
|
|
| 2464 |
+ async => sub {
|
|
| 2465 |
+ my ($dbi, $result) = @_; |
|
| 2466 |
+ my $row = $result->fetch_one; |
|
| 2467 |
+ is($row->[0], 1, 'after1'); |
|
| 2468 |
+ $dbi->select('key1', table => 'table1', prepare_attr => {async => 1},
|
|
| 2469 |
+ async => sub {
|
|
| 2470 |
+ my ($dbi, $result) = @_; |
|
| 2471 |
+ my $row = $result->fetch_one; |
|
| 2472 |
+ is($row->[0], 1, 'after2'); |
|
| 2473 |
+ $cond->send if ++$count == 2; |
|
| 2474 |
+ } |
|
| 2475 |
+ ) |
|
| 2476 |
+ } |
|
| 2477 |
+ ); |
|
| 2478 |
+ |
|
| 2479 |
+ $cond->recv; |
|
| 2480 |
+ |
|
| 2393 | 2481 |
=head2 C<column> |
| 2394 | 2482 |
|
| 2395 | 2483 |
my $column = $dbi->column(book => ['author', 'title']); |
| ... | ... |
@@ -2705,11 +2793,12 @@ because generally creating query object is slow. |
| 2705 | 2793 |
|
| 2706 | 2794 |
Priamry key. This is used for C<id> option. |
| 2707 | 2795 |
|
| 2708 |
-=item C<statement> EXPERIMETAL |
|
| 2796 |
+=item C<select> EXPERIMETAL |
|
| 2709 | 2797 |
|
| 2710 |
- statement => 'select' |
|
| 2798 |
+ select => 1 |
|
| 2711 | 2799 |
|
| 2712 |
-If you set statement to C<select>, return value is always L<DBIx::Custom::Result> object. |
|
| 2800 |
+If you set C<select> to 1, this statement become select statement |
|
| 2801 |
+and return value is always L<DBIx::Custom::Result> object. |
|
| 2713 | 2802 |
|
| 2714 | 2803 |
=item C<table> |
| 2715 | 2804 |
|
| ... | ... |
@@ -0,0 +1,96 @@ |
| 1 |
+use Test::More; |
|
| 2 |
+use strict; |
|
| 3 |
+use warnings; |
|
| 4 |
+use utf8; |
|
| 5 |
+ |
|
| 6 |
+use FindBin; |
|
| 7 |
+use DBIx::Custom; |
|
| 8 |
+ |
|
| 9 |
+my $dbi; |
|
| 10 |
+my $dsn; |
|
| 11 |
+my $args; |
|
| 12 |
+my $user = 'dbix_custom'; |
|
| 13 |
+my $password = 'dbix_custom'; |
|
| 14 |
+my $database = 'dbix_custom'; |
|
| 15 |
+ |
|
| 16 |
+$dsn = "dbi:mysql:database=$database"; |
|
| 17 |
+$args = {dsn => $dsn, user => $user, password => $password,};
|
|
| 18 |
+ |
|
| 19 |
+plan skip_all => 'mysql private test' unless -f "$FindBin::Bin/run/mysql-async-opt.run" |
|
| 20 |
+ && eval { $dbi = DBIx::Custom->connect($args); 1 };
|
|
| 21 |
+plan 'no_plan'; |
|
| 22 |
+ |
|
| 23 |
+$SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /DEPRECATED/};
|
|
| 24 |
+ |
|
| 25 |
+# Function for test name |
|
| 26 |
+sub test { print "# $_[0]\n" }
|
|
| 27 |
+ |
|
| 28 |
+# Varialbes for tests |
|
| 29 |
+my $dbname; |
|
| 30 |
+my $row; |
|
| 31 |
+my $rows; |
|
| 32 |
+my $result; |
|
| 33 |
+my $result2; |
|
| 34 |
+my $model; |
|
| 35 |
+my $dbi1; |
|
| 36 |
+my $dbi2; |
|
| 37 |
+my $dbi3; |
|
| 38 |
+my @dbis; |
|
| 39 |
+my @results; |
|
| 40 |
+ |
|
| 41 |
+test 'connect'; |
|
| 42 |
+eval {
|
|
| 43 |
+ $dbi = DBIx::Custom->connect( |
|
| 44 |
+ dsn => "dbi:mysql:database=$database;", |
|
| 45 |
+ user => $user, |
|
| 46 |
+ password => $password |
|
| 47 |
+ ); |
|
| 48 |
+}; |
|
| 49 |
+ok(!$@); |
|
| 50 |
+ |
|
| 51 |
+eval { $dbi->do('drop table table1') };
|
|
| 52 |
+$dbi->do('create table table1 (key1 varchar(255), key2 varchar(255)) engine=InnoDB');
|
|
| 53 |
+$dbi->insert({key1 => 1, key2 => 2}, table => 'table1');
|
|
| 54 |
+ |
|
| 55 |
+test 'async test'; |
|
| 56 |
+ |
|
| 57 |
+require AnyEvent; |
|
| 58 |
+ |
|
| 59 |
+my $cond = AnyEvent->condvar; |
|
| 60 |
+ |
|
| 61 |
+my $timer = AnyEvent->timer( |
|
| 62 |
+ interval => 1, |
|
| 63 |
+ cb => sub {
|
|
| 64 |
+ 1; |
|
| 65 |
+ } |
|
| 66 |
+); |
|
| 67 |
+ |
|
| 68 |
+my $count = 0; |
|
| 69 |
+ |
|
| 70 |
+$dbi->execute('SELECT SLEEP(1), 3', undef,
|
|
| 71 |
+ prepare_attr => {async => 1}, select => 1,
|
|
| 72 |
+ async => sub {
|
|
| 73 |
+ my ($dbi, $result) = @_; |
|
| 74 |
+ my $row = $result->fetch_one; |
|
| 75 |
+ is($row->[1], 3, 'before'); |
|
| 76 |
+ $cond->send if ++$count == 2; |
|
| 77 |
+ } |
|
| 78 |
+); |
|
| 79 |
+ |
|
| 80 |
+$dbi->select('key1', table => 'table1', prepare_attr => {async => 1},
|
|
| 81 |
+ async => sub {
|
|
| 82 |
+ my ($dbi, $result) = @_; |
|
| 83 |
+ my $row = $result->fetch_one; |
|
| 84 |
+ is($row->[0], 1, 'after1'); |
|
| 85 |
+ $dbi->select('key1', table => 'table1', prepare_attr => {async => 1},
|
|
| 86 |
+ async => sub {
|
|
| 87 |
+ my ($dbi, $result) = @_; |
|
| 88 |
+ my $row = $result->fetch_one; |
|
| 89 |
+ is($row->[0], 1, 'after2'); |
|
| 90 |
+ $cond->send if ++$count == 2; |
|
| 91 |
+ } |
|
| 92 |
+ ) |
|
| 93 |
+ } |
|
| 94 |
+); |
|
| 95 |
+ |
|
| 96 |
+$cond->recv; |
| ... | ... |
@@ -75,7 +75,8 @@ my $timer = AnyEvent->timer( |
| 75 | 75 |
|
| 76 | 76 |
my $count = 0; |
| 77 | 77 |
|
| 78 |
-my $mysql_watcher = AnyEvent->io( |
|
| 78 |
+my $mysql_watcher; |
|
| 79 |
+$mysql_watcher = AnyEvent->io( |
|
| 79 | 80 |
fh => $dbi->dbh->mysql_fd, |
| 80 | 81 |
poll => 'r', |
| 81 | 82 |
cb => sub {
|
| ... | ... |
@@ -83,6 +84,7 @@ my $mysql_watcher = AnyEvent->io( |
| 83 | 84 |
is($row->[1], 3, 'before'); |
| 84 | 85 |
$cond->send if ++$count == 2; |
| 85 | 86 |
undef $result; |
| 87 |
+ undef $mysql_watcher; |
|
| 86 | 88 |
} |
| 87 | 89 |
); |
| 88 | 90 |
|