1
# Copyright (C) 2008-2009 Sun Microsystems, Inc. All rights reserved.
2
# Use is subject to license terms.
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; version 2 of the License.
8
# This program is distributed in the hope that it will be useful, but
9
# WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
# General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
18
#####################################################################
20
# Author: Jorgen Loland
23
# Purpose: Implementation of WL#4218: Test that transactions executed
24
# concurrently with backup are either completely restored or not
25
# restored at all. No transaction should be partially represented
28
# See further documentation in invariant.yy
31
# mysql-test/gentest/conf/backup/invariant.yy
32
# mysql-test/gentest/conf/backup/invariant.zz
33
# mysql-test/gentest/lib/GenTest/Validator/Invariant.pm
34
# mysql-test/gentest/lib/GenTest/Reporter/BackupAndRestoreInvariant.pm
36
#####################################################################
37
package GenTest::Reporter::BackupAndRestoreInvariant;
40
@ISA = qw(GenTest::Reporter);
44
use GenTest::Reporter;
45
use GenTest::Constants;
48
my $file = 'rqg_'.time().'.bup';
49
my $expected_total=100000;
53
my $dsn = $reporter->dsn();
55
# Return if BACKUP has already been executed
56
return STATUS_OK if $count > 0;
58
# Execute BACKUP when 20 seconds remains
59
if (time() > $reporter->testEnd() - 20) {
60
my $dbh = DBI->connect($dsn);
63
say("Executing BACKUP DATABASE.");
64
$dbh->do("BACKUP DATABASE test TO '".$file."'");
65
say("Executing BACKUP DATABASE done.");
67
if (defined $dbh->err()) {
68
return STATUS_DATABASE_CORRUPTION;
77
my $dsn = $reporter->dsn();
78
my $dbh = DBI->connect($dsn);
80
###########################
81
# Get data before restore #
82
###########################
83
$dbh = DBI->connect($dsn) if not defined $dbh;
84
$tables = $dbh->selectcol_arrayref("SHOW TABLES") if not defined $tables;
87
my $query="SELECT SUM(alltbls.int_not_null) as sum,
88
COUNT(alltbls.int_not_null) as count
92
foreach my $table (@$tables) {
94
$query = $query . " UNION ALL ";
96
$query = $query . "SELECT int_not_null FROM " . $table;
99
$query = $query . ") AS alltbls";
102
my ($sum_before, $count_before) = $dbh->selectrow_array($query);
104
if (($sum_before eq '') || ($count_before eq '')) {
105
# Server probably crashed, the SELECT returned no data
106
say ("Failed to query status before RESTORE");
107
return STATUS_UNKNOWN_ERROR;
110
if (($sum_before ne $expected_total)) {
111
say("Bad sum before RESTORE: sum: $sum_before; count: $count_before");
112
return STATUS_DATABASE_CORRUPTION;
115
##################################
116
# RESTORE the backed up database #
117
##################################
119
say("Executing RESTORE FROM.");
120
$dbh->do("RESTORE FROM '".$file."' OVERWRITE");
122
if (defined $dbh->err()) {
123
return STATUS_DATABASE_CORRUPTION;
126
##########################
127
# Get data after restore #
128
##########################
130
######################
131
# Consistency test 1 #
132
######################
134
# Execute the same query that was executed before RESTORE
135
my ($sum_after, $count_after) = $dbh->selectrow_array($query);
137
if (($sum_after eq '') && ($count_after eq '')) {
138
# Server probably crashed, the SELECT returned no data
139
say ("Failed to query status after RESTORE");
140
return STATUS_UNKNOWN_ERROR;
143
# The total amount of money across all bank account should not have changed
144
if (($sum_after ne $expected_total)) {
145
say("Bad sum for tables: sum: $sum_after; count: $count_after");
146
return STATUS_DATABASE_CORRUPTION;
149
say("*** STARTING DATA CONSISTENCY CHECKING ***");
150
say("Invariant (total amount of money is ".$expected_total.") is still still OK after RESTORE");
152
######################
153
# Consistency test 2 #
154
######################
156
my @predicates = ("/* No predicate */");
159
" FORCE INDEX(PRIMARY) WHERE `pk` >= -922337",
160
" FORCE INDEX(PRIMARY) ORDER BY `pk` LIMIT 4294836225",
163
# Use other access methods than in test 1; access the two tables
164
# independantly and with different predicates.
167
foreach my $predicate (@predicates) {
168
foreach my $table (@$tables) {
169
$query="SELECT SUM(int_not_null) as sum,
170
COUNT(int_not_null) as count
171
FROM ".$table.$predicate;
172
my ($res1, $res2) = $dbh->selectrow_array($query);
177
if ($count ne $count_after) {
178
say("Bad count when executing query with predicate: \n\t\"$predicate\"\n\tCount: $count; Count for union: $count_after");
179
return STATUS_DATABASE_CORRUPTION;
182
if ($sum ne $expected_total) {
183
say("Bad sum when executing query with predicate: \n\t\"$predicate\"\n\tSum: $sum; Sum for union: $sum_after");
184
return STATUS_DATABASE_CORRUPTION;
187
say("Query OK with predicate: \"$predicate\"");
193
######################
194
# Consistency test 3 #
195
######################
197
# Use MySQL consistency testing functions to further exercise the
199
my $engine = $reporter->serverVariable('storage_engine');
200
foreach my $table (@$tables) {
202
"CHECK TABLE `$table` EXTENDED",
203
"ANALYZE TABLE `$table`",
204
"OPTIMIZE TABLE `$table`",
205
"REPAIR TABLE `$table` EXTENDED",
206
"ALTER TABLE `$table` ENGINE = $engine"
208
say("Executing $sql.");
209
my $sth = $dbh->prepare($sql);
210
$sth->execute() if defined $sth;
211
print Dumper $sth->fetchall_arrayref() if defined $sth && $sth->{NUM_OF_FIELDS} > 0;
213
return STATUS_DATABASE_CORRUPTION if $dbh->err() == 2013;
217
say("*** CONSISTENCY CHECKING DONE: ALL CHECKS PASSED ***");
220
$dbh->do("DROP DATABASE test");
227
return REPORTER_TYPE_PERIODIC | REPORTER_TYPE_SUCCESS;