Handle resume in NibbleIterator.

This commit is contained in:
Daniel Nichter
2011-11-15 11:55:07 -07:00
parent 6dd46590e2
commit 619cda713f
2 changed files with 76 additions and 3 deletions

View File

@@ -64,6 +64,14 @@ sub new {
: 0;
MKDEBUG && _d('One nibble:', $one_nibble ? 'yes' : 'no');
if ( my $nibble = $args{resume} ) {
if ( !defined $nibble->{lower_boundary}
&& !defined $nibble->{upper_boundary} ) {
MKDEBUG && _d('Resuming from one nibble table');
$one_nibble = 1;
}
}
# Get an index to nibble by. We'll order rows by the index's columns.
my $index = _find_best_index(%args, mysql_index => $mysql_index);
if ( !$index && !$one_nibble ) {
@@ -103,6 +111,7 @@ sub new {
limit => 0,
nibble_sql => $nibble_sql,
explain_nibble_sql => $explain_nibble_sql,
no_more_boundaries => $args{resume} ? 1 : 0,
};
}
else {
@@ -125,11 +134,15 @@ sub new {
my $order_by = join(', ', map {$q->quote($_)} @{$index_cols});
# These statements are only executed once, so they don't use sths.
my $first_lb_where = $where ? "($where)" : '';
if ( $args{resume} ) {
$first_lb_where .= ($where ? " AND " : '') . $asc->{boundaries}->{'>'};
}
my $first_lb_sql
= "SELECT /*!40001 SQL_NO_CACHE */ "
. join(', ', map { $q->quote($_) } @{$asc->{scols}})
. " FROM $from"
. ($where ? " WHERE $where" : '')
. ($first_lb_where ? " WHERE $first_lb_where" : '')
. " ORDER BY $order_by"
. " LIMIT 1"
. " /*first lower boundary*/";
@@ -204,6 +217,7 @@ sub new {
nibble_sql => $nibble_sql,
explain_ub_sql => "EXPLAIN $ub_sql",
explain_nibble_sql => $explain_nibble_sql,
resume => $args{resume},
sql => {
columns => $asc->{scols},
from => $from,
@@ -520,7 +534,17 @@ sub _get_bounds {
my $dbh = $self->{Cxn}->dbh();
$self->{first_lower} = $dbh->selectrow_arrayref($self->{first_lb_sql});
if ( my $nibble = $self->{resume} ) {
my $sth = $dbh->prepare($self->{first_lb_sql});
my @ub = split ',', $nibble->{upper_boundary};
MKDEBUG && _d($sth->{Statement}, 'params:', @ub);
$sth->execute(@ub);
$self->{first_lower} = $sth->fetchrow_arrayref();
$sth->finish();
}
else {
$self->{first_lower} = $dbh->selectrow_arrayref($self->{first_lb_sql});
}
$self->{next_lower} = $self->{first_lower};
MKDEBUG && _d('First lower boundary:', Dumper($self->{next_lower}));
@@ -544,6 +568,13 @@ sub _next_boundaries {
return 1; # continue nibbling
}
if ( !$self->{next_lower} ) {
# This happens if we resume from the end of the table.
MKDEBUG && _d('At end of table');
$self->{no_more_boundaries} = 1; # for next call
return; # stop nibbling
}
# Detect infinite loops. If the lower boundary we just nibbled from
# is identical to the next lower boundary, then this next nibble won't
# go anywhere, so to speak, unless perhaps the chunk size has changed

View File

@@ -39,7 +39,7 @@ if ( !$dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}
else {
plan tests => 42;
plan tests => 44;
}
my $q = new Quoter();
@@ -92,6 +92,7 @@ sub make_nibble_iter {
callbacks => $args{callbacks},
select => $args{select},
one_nibble => $args{one_nibble},
resume => $args{resume},
%common_modules,
);
@@ -720,6 +721,47 @@ is_deeply(
"--chunk-size-limit 0 on empty table"
);
# ############################################################################
# Resume.
# ############################################################################
$ni = make_nibble_iter(
sql_file => "a-z.sql",
db => 'test',
tbl => 't',
argv => [qw(--databases test --chunk-size 5)],
resume => { upper_boundary => 'j' },
);
@rows = ();
while (my $row = $ni->next()) {
push @rows, @$row;
}
is_deeply(
\@rows,
[ ('k'..'z') ],
"Resume from middle"
);
$ni = make_nibble_iter(
sql_file => "a-z.sql",
db => 'test',
tbl => 't',
argv => [qw(--databases test --chunk-size 5)],
resume => { upper_boundary => 'z' },
);
@rows = ();
while (my $row = $ni->next()) {
push @rows, @$row;
}
is_deeply(
\@rows,
[ ],
"Resume from end"
);
# #############################################################################
# Done.
# #############################################################################