~drizzle-trunk/drizzle/development

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Copyright (C) 2008-2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
# USA

#####################################################################
#
# Author: Jorgen Loland
# Date: April 2009
#
# Purpose: Implementation of WL#4218: Test that transactions executed
# concurrently with backup are either completely restored or not
# restored at all. No transaction should be partially represented
# after restore.
#
# See further documentation in invariant.yy
#
# Associated files: 
#   mysql-test/gentest/conf/backup/invariant.yy
#   mysql-test/gentest/conf/backup/invariant.zz
#   mysql-test/gentest/lib/GenTest/Validator/Invariant.pm
#   mysql-test/gentest/lib/GenTest/Reporter/BackupAndRestoreInvariant.pm
#
#####################################################################
package GenTest::Validator::Invariant;

require Exporter;
@ISA = qw(GenTest::Validator GenTest);

use strict;

use DBI;
use GenTest;
use GenTest::Constants;
use GenTest::Result;
use GenTest::Validator;

my $tables;
my $dbh;
my $inconsistent_state = 0;

my $total_sum;
my $total_count;

sub validate {
  my ($validator, $executors, $results) = @_;
  my $dsn = $executors->[0]->dsn();

  # RQG does not stop the script if an error occurs.
  # The following code preserves transactional consistency by
  # explicitly rolling back the rest of the statements in a
  # transaction that has failed.
  foreach my $i (0..$#$results) {

    # Additional rules where the transaction must rollback for the
    # invariant script to work
    #
    # "ERROR 1048 (23000): Column 'x' cannot be null" 
    #   '-> used to check that @val!=null (see the delete_update
    #       rule in invariant.yy)

    if ($results->[$i]->err() == 1048) {
      # say("Found transaction that failed due to insert of NULL value");
      $inconsistent_state=1;
    }

    if ($results->[$i]->status() == STATUS_TRANSACTION_ERROR) {
      #say("entering inconsistent state due to query".$results->[$i]->query());
      $inconsistent_state = 1;
    } elsif ($results->[$i]->query() =~ m{^\s*(COMMIT|START TRANSACTION|BEGIN)}sio) {
      if ($inconsistent_state > 0) {
        #say("leaving inconsistent state due to query ".$results->[$i]->query());
      }
      $inconsistent_state = 0;
    }

    if ($inconsistent_state == 1) {
      #say("$$ Rollback ".$results->[$i]->query());
      $executors->[$i]->dbh()->do("ROLLBACK /* Explicit ROLLBACK after a ".$results->[$i]->errstr()." error. */ ");
    } else {
      #say("$$ Execute: ".$results->[$i]->query()." Affected rows: ".$results->[0]->affectedRows());
    }
  }
  # End transactional consistency code

  # Start code that checks that the total amount of money across all
  # bank accounts has not changed
  $dbh = DBI->connect($dsn) if not defined $dbh;
  $tables = $dbh->selectcol_arrayref("SHOW TABLES") if not defined $tables;

  my $query="SELECT SUM(alltbls.int_not_null) as sum, 
                    COUNT(alltbls.int_not_null) as count 
             FROM (";

  my $tblno=0;
  foreach my $table (@$tables) {
    if ($tblno>0) {
      $query = $query . " UNION ALL ";
    }
    $query = $query . "SELECT int_not_null FROM " . $table;
    $tblno++;
  }
  $query = $query . ") AS alltbls";

  #say("The query: ".$query);
  my ($sum, $count) = $dbh->selectrow_array($query);

  if (($sum eq '') && ($count eq '')) {
    # Server probably crashed, the SELECT returned no data
    return STATUS_UNKNOWN_ERROR;
  }

  if (($sum ne '100000')) {
      say("Invariant: Bad sum for tables: sum: $sum; count: $count; affected_rows: ".$results->[0]->affectedRows()."; query: ".$results->[0]->query());
      return STATUS_DATABASE_CORRUPTION;
  }

  return STATUS_OK;
}

1;