~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to lib/GenTest/Executor/MySQL.pm

initial import from internal tree

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package GenTest::Executor::MySQL;
 
2
 
 
3
require Exporter;
 
4
 
 
5
@ISA = qw(GenTest::Executor);
 
6
 
 
7
use strict;
 
8
use DBI;
 
9
use GenTest;
 
10
use GenTest::Constants;
 
11
use GenTest::Result;
 
12
use GenTest::Executor;
 
13
use Time::HiRes;
 
14
 
 
15
my %reported_errors;
 
16
 
 
17
my @errors = (
 
18
        "Duplicate entry '.*?' for key '.*?'",
 
19
        "Can't DROP '.*?'",
 
20
        "Duplicate key name '.*?'",
 
21
        "Duplicate column name '.*?'",
 
22
        "Record has changed since last read in table '.*?'",
 
23
        "savepoint does not exist",
 
24
        "'.*?' doesn't exist",
 
25
        " .*? does not exist",
 
26
        "'.*?' already exists",
 
27
        "Unknown database '.*?'",
 
28
        "Unknown table '.*?'",
 
29
        "Unknown column '.*?'",
 
30
        "Column '.*?' specified twice",
 
31
        "Column '.*?' cannot be null",
 
32
        "Duplicate partition name .*?",
 
33
        "Tablespace '.*?' not empty",
 
34
        "Tablespace '.*?' already exists",
 
35
        "Tablespace data file '.*?' already exists",
 
36
        "Can't find file: '.*?'",
 
37
        "Table '.*?' already exists",
 
38
        "You can't specify target table '.*?' for update",
 
39
        "Illegal mix of collations .*?, .*?, .*? for operation '.*?'",
 
40
        "Illegal mix of collations .*? and .*? for operation '.*?'",
 
41
        "Invalid .*? character string: '.*?'",
 
42
        "This version of MySQL doesn't yet support '.*?'",
 
43
        "PROCEDURE .*? already exists",
 
44
        "FUNCTION .*? already exists",
 
45
        "'.*?' isn't in GROUP BY",
 
46
        "non-grouping field '.*?' is used in HAVING clause",
 
47
        "Table has no partition for value .*?"
 
48
);
 
49
 
 
50
my @patterns = map { qr{$_}i } @errors;
 
51
 
 
52
use constant EXECUTOR_MYSQL_AUTOCOMMIT => 10;
 
53
 
 
54
#
 
55
# Column positions for SHOW SLAVES
 
56
 
57
 
 
58
use constant SLAVE_INFO_HOST => 1;
 
59
use constant SLAVE_INFO_PORT => 2;
 
60
 
 
61
#
 
62
# MySQL status codes taken from errmsg.h
 
63
#
 
64
 
 
65
# Server has crashed
 
66
 
 
67
use constant    ER_CONNECTION_ERROR     => 2002;
 
68
use constant    ER_CONN_HOST_ERROR      => 2003;
 
69
use constant    ER_SERVER_GONE_ERROR    => 2006;
 
70
use constant    ER_SERVER_LOST_EXTENDED => 2055;
 
71
use constant    ER_SERVER_LOST          => 2013;
 
72
 
 
73
# Syntax error
 
74
 
 
75
use constant    ER_PARSE_ERROR          => 1064;
 
76
use constant    ER_SYNTAX_ERROR         => 1149;
 
77
 
 
78
# Semantic errors
 
79
 
 
80
use constant    ER_UPDATE_TABLE_USED    => 1093;
 
81
use constant    ER_BAD_FIELD_ERROR      => 1054;
 
82
use constant    ER_NO_SUCH_TABLE        => 1146;
 
83
use constant    ER_BAD_TABLE_ERROR      => 1051;
 
84
use constant    ER_CANT_DROP_FIELD_OR_KEY       => 1091;
 
85
use constant    ER_FIELD_SPECIFIED_TWICE        => 1110;
 
86
use constant    ER_MULTIPLE_PRI_KEY     => 1068;
 
87
use constant    ER_DUP_FIELDNAME        => 1060;
 
88
use constant    ER_DUP_KEYNAME          => 1061;
 
89
use constant    ER_SAME_NAME_PARTITION  => 1517;
 
90
use constant    ER_PARTITION_WRONG_VALUES_ERROR => 1480;
 
91
use constant    ER_CANT_LOCK            => 1015;
 
92
use constant    ER_TABLESPACE_EXIST     => 1683;
 
93
use constant    ER_NO_SUCH_TABLESPACE   => 1684;
 
94
use constant    ER_SP_DOES_NOT_EXIST    => 1305;
 
95
use constant    ER_TABLESPACE_NOT_EMPTY => 1721;
 
96
use constant    ER_TABLESPACE_DATAFILE_EXIST => 1726;
 
97
use constant    ER_BAD_DB_ERROR         => 1049;
 
98
use constant    ER_PARTITION_MGMT_ON_NONPARTITIONED     => 1505;
 
99
use constant    ER_UNKNOWN_SYSTEM_VARIABLE      => 1193;
 
100
use constant    ER_VAR_CANT_BE_READ     => 1233;
 
101
use constant    ER_TRG_DOES_NOT_EXIST   => 1360;
 
102
use constant    ER_NO_DB_ERROR          => 1046;
 
103
use constant    ER_KEY_COLUMN_DOES_NOT_EXIST => 1072;
 
104
use constant    ER_SP_DOES_NOT_EXIST    => 1305;
 
105
use constant    ER_BAD_NULL_ERROR       => 1048;
 
106
use constant    ER_SAME_NAME_PARTITION  => 1517;
 
107
use constant    ER_TABLE_EXISTS_ERROR   => 1050;
 
108
use constant    ER_MULTIPLE_PRI_KEY     => 1068;
 
109
use constant    ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG        => 1336;
 
110
use constant    ER_NOT_SUPPORTED_YET    => 1235;
 
111
use constant    ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT        => 1560;
 
112
use constant    ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG       => 1542;
 
113
use constant    ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG => 1422;
 
114
use constant    ER_CANNOT_USER          => 1396;
 
115
use constant    ER_CHECK_NOT_IMPLEMENTED=> 1178;
 
116
use constant    ER_CANT_AGGREGATE_2COLLATIONS   => 1267;
 
117
use constant    ER_CANT_AGGREGATE_3COLLATIONS   => 1270;
 
118
use constant    ER_CANT_AGGREGATE_NCOLLATIONS   => 1271;
 
119
use constant    ER_INVALID_CHARACTER_STRING     => 1300;
 
120
use constant    ER_UNKNOWN_SYSTEM_VARIABLE      => 1193;
 
121
use constant    ER_SP_ALREADY_EXISTS            => 1304;
 
122
use constant    ER_EVENT_ALREADY_EXISTS         => 1537;
 
123
use constant    ER_TRG_ALREADY_EXISTS           => 1359;
 
124
use constant    ER_WRONG_FIELD_WITH_GROUP       => 1055;
 
125
use constant    ER_NON_GROUPING_FIELD_USED      => 1463;
 
126
 
 
127
use constant    ER_PARTITION_MGMT_ON_NONPARTITIONED     => 1505;
 
128
use constant    ER_DROP_LAST_PARTITION                  => 1508;
 
129
use constant    ER_COALESCE_ONLY_ON_HASH_PARTITION      => 1509;
 
130
use constant    ER_REORG_HASH_ONLY_ON_SAME_NO           => 1510;
 
131
use constant    ER_REORG_NO_PARAM_ERROR                 => 1511;
 
132
use constant    ER_ONLY_ON_RANGE_LIST_PARTITION         => 1512;
 
133
use constant    ER_NO_PARTITION_FOR_GIVEN_VALUE         => 1526;
 
134
 
 
135
use constant    ER_UNKNOWN_KEY_CACHE                    => 1284;
 
136
 
 
137
 
 
138
# Transaction errors
 
139
 
 
140
use constant    ER_LOCK_DEADLOCK        => 1213;
 
141
use constant    ER_LOCK_WAIT_TIMEOUT    => 1205;
 
142
use constant    ER_CHECKREAD            => 1020;
 
143
use constant    ER_DUP_KEY              => 1022;
 
144
use constant    ER_DUP_ENTRY            => 1062;
 
145
 
 
146
# Storage engine failures
 
147
 
 
148
use constant    ER_GET_ERRNO            => 1030;
 
149
 
 
150
# Database corruption
 
151
 
 
152
use constant    ER_CRASHED1             => 126;
 
153
use constant    ER_CRASHED2             => 145;
 
154
use constant    ER_CRASHED_ON_USAGE     => 1194;
 
155
use constant    ER_NOT_KEYFILE          => 1034;
 
156
use constant    ER_UNEXPECTED_EOF       => 1039;
 
157
use constant    ER_SP_PROC_TABLE_CORRUPT=> 1457;
 
158
# Backup
 
159
 
 
160
use constant    ER_BACKUP_SEND_DATA     => 1670;
 
161
 
 
162
# Out of disk space, quotas, etc.
 
163
 
 
164
use constant    ER_RECORD_FILE_FULL     => 1114;
 
165
use constant    ER_DISK_FULL            => 1021;
 
166
use constant    ER_OUTOFMEMORY          => 1037;
 
167
use constant    ER_CON_COUNT_ERROR      => 1040;
 
168
use constant    ER_OUT_OF_RESOURCES     => 1041;
 
169
use constant    ER_CANT_CREATE_THREAD   => 1135;
 
170
use constant    ER_STACK_OVERRUN        => 1119;
 
171
 
 
172
use constant   ER_SERVER_SHUTDOWN      => 1053;
 
173
 
 
174
my %err2type = (
 
175
        ER_GET_ERRNO()          => STATUS_SEMANTIC_ERROR,
 
176
 
 
177
        ER_CONNECTION_ERROR()   => STATUS_SERVER_CRASHED,
 
178
        ER_CONN_HOST_ERROR()    => STATUS_SERVER_CRASHED,
 
179
        ER_SERVER_GONE_ERROR()  => STATUS_SERVER_CRASHED,
 
180
        ER_SERVER_LOST_EXTENDED()       => STATUS_SERVER_CRASHED,
 
181
        ER_SERVER_LOST()        => STATUS_SERVER_CRASHED,
 
182
 
 
183
        ER_PARSE_ERROR()        => STATUS_SYNTAX_ERROR,
 
184
        ER_SYNTAX_ERROR()       => STATUS_SYNTAX_ERROR,
 
185
 
 
186
        ER_UPDATE_TABLE_USED()  => STATUS_SEMANTIC_ERROR,
 
187
        ER_NO_SUCH_TABLE()      => STATUS_SEMANTIC_ERROR,
 
188
        ER_BAD_TABLE_ERROR()    => STATUS_SEMANTIC_ERROR,
 
189
        ER_BAD_FIELD_ERROR()    => STATUS_SEMANTIC_ERROR,
 
190
        ER_CANT_DROP_FIELD_OR_KEY()     => STATUS_SEMANTIC_ERROR,
 
191
        ER_FIELD_SPECIFIED_TWICE()      => STATUS_SEMANTIC_ERROR,
 
192
        ER_MULTIPLE_PRI_KEY()   => STATUS_SEMANTIC_ERROR,
 
193
        ER_DUP_FIELDNAME()      => STATUS_SEMANTIC_ERROR,
 
194
        ER_DUP_KEYNAME()        => STATUS_SEMANTIC_ERROR,
 
195
        ER_SAME_NAME_PARTITION()=> STATUS_SEMANTIC_ERROR,
 
196
        ER_PARTITION_WRONG_VALUES_ERROR() => STATUS_SEMANTIC_ERROR,
 
197
        ER_CANT_LOCK()          => STATUS_SEMANTIC_ERROR,
 
198
        ER_TABLESPACE_EXIST()   => STATUS_SEMANTIC_ERROR,
 
199
        ER_NO_SUCH_TABLESPACE() => STATUS_SEMANTIC_ERROR,
 
200
        ER_SP_DOES_NOT_EXIST()  => STATUS_SEMANTIC_ERROR,
 
201
        ER_TABLESPACE_NOT_EMPTY()       => STATUS_SEMANTIC_ERROR,
 
202
        ER_TABLESPACE_DATAFILE_EXIST()  => STATUS_SEMANTIC_ERROR,
 
203
        ER_BAD_DB_ERROR()       => STATUS_SEMANTIC_ERROR,
 
204
        ER_PARTITION_MGMT_ON_NONPARTITIONED()   => STATUS_SEMANTIC_ERROR,
 
205
        ER_UNKNOWN_SYSTEM_VARIABLE() => STATUS_SEMANTIC_ERROR,
 
206
        ER_VAR_CANT_BE_READ()   => STATUS_SEMANTIC_ERROR,
 
207
        ER_TRG_DOES_NOT_EXIST() => STATUS_SEMANTIC_ERROR,
 
208
        ER_NO_DB_ERROR()        => STATUS_SEMANTIC_ERROR,
 
209
        ER_KEY_COLUMN_DOES_NOT_EXIST()  => STATUS_SEMANTIC_ERROR,
 
210
        ER_SP_DOES_NOT_EXIST()  => STATUS_SEMANTIC_ERROR,
 
211
        ER_BAD_NULL_ERROR()     => STATUS_SEMANTIC_ERROR,
 
212
        ER_SAME_NAME_PARTITION() => STATUS_SEMANTIC_ERROR,
 
213
        ER_TABLE_EXISTS_ERROR() => STATUS_SEMANTIC_ERROR,
 
214
        ER_MULTIPLE_PRI_KEY()   => STATUS_SEMANTIC_ERROR,
 
215
        ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG() => STATUS_SEMANTIC_ERROR,
 
216
        ER_NOT_SUPPORTED_YET()  => STATUS_SEMANTIC_ERROR,
 
217
        ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT() => STATUS_SEMANTIC_ERROR,
 
218
        ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG() => STATUS_SEMANTIC_ERROR,
 
219
        ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG() => STATUS_SEMANTIC_ERROR,
 
220
        ER_CANNOT_USER() => STATUS_SEMANTIC_ERROR,
 
221
        ER_CHECK_NOT_IMPLEMENTED() => STATUS_SEMANTIC_ERROR,
 
222
        ER_CANT_AGGREGATE_2COLLATIONS() => STATUS_SEMANTIC_ERROR,
 
223
        ER_CANT_AGGREGATE_3COLLATIONS() => STATUS_SEMANTIC_ERROR,
 
224
        ER_CANT_AGGREGATE_NCOLLATIONS() => STATUS_SEMANTIC_ERROR,
 
225
        ER_INVALID_CHARACTER_STRING()   => STATUS_SEMANTIC_ERROR,
 
226
        ER_UNKNOWN_SYSTEM_VARIABLE()    => STATUS_SEMANTIC_ERROR,
 
227
        ER_SP_ALREADY_EXISTS()          => STATUS_SEMANTIC_ERROR,
 
228
        ER_EVENT_ALREADY_EXISTS()       => STATUS_SEMANTIC_ERROR,
 
229
        ER_TRG_ALREADY_EXISTS()         => STATUS_SEMANTIC_ERROR,
 
230
        ER_WRONG_FIELD_WITH_GROUP()     => STATUS_SEMANTIC_ERROR,
 
231
        ER_NON_GROUPING_FIELD_USED()    => STATUS_SEMANTIC_ERROR,
 
232
 
 
233
        ER_PARTITION_MGMT_ON_NONPARTITIONED()   => STATUS_SEMANTIC_ERROR,
 
234
        ER_DROP_LAST_PARTITION()                => STATUS_SEMANTIC_ERROR,
 
235
        ER_COALESCE_ONLY_ON_HASH_PARTITION()    => STATUS_SEMANTIC_ERROR,
 
236
        ER_REORG_HASH_ONLY_ON_SAME_NO()         => STATUS_SEMANTIC_ERROR,
 
237
        ER_REORG_NO_PARAM_ERROR()               => STATUS_SEMANTIC_ERROR,
 
238
        ER_ONLY_ON_RANGE_LIST_PARTITION()       => STATUS_SEMANTIC_ERROR,
 
239
        ER_NO_PARTITION_FOR_GIVEN_VALUE()       => STATUS_SEMANTIC_ERROR,
 
240
 
 
241
        ER_UNKNOWN_KEY_CACHE()  => STATUS_SEMANTIC_ERROR,
 
242
 
 
243
        ER_LOCK_DEADLOCK()      => STATUS_TRANSACTION_ERROR,
 
244
        ER_LOCK_WAIT_TIMEOUT()  => STATUS_TRANSACTION_ERROR,
 
245
        ER_CHECKREAD()          => STATUS_TRANSACTION_ERROR,
 
246
        ER_DUP_KEY()            => STATUS_TRANSACTION_ERROR,
 
247
        ER_DUP_ENTRY()          => STATUS_TRANSACTION_ERROR,
 
248
        
 
249
        ER_NOT_KEYFILE()        => STATUS_DATABASE_CORRUPTION,
 
250
        ER_CRASHED_ON_USAGE()   => STATUS_DATABASE_CORRUPTION,
 
251
        ER_CRASHED1()           => STATUS_DATABASE_CORRUPTION,
 
252
        ER_CRASHED2()           => STATUS_DATABASE_CORRUPTION,
 
253
        ER_UNEXPECTED_EOF()     => STATUS_DATABASE_CORRUPTION,
 
254
        ER_SP_PROC_TABLE_CORRUPT() => STATUS_DATABASE_CORRUPTION,
 
255
 
 
256
        ER_BACKUP_SEND_DATA()   => STATUS_BACKUP_FAILURE,
 
257
 
 
258
        ER_CANT_CREATE_THREAD() => STATUS_ENVIRONMENT_FAILURE,
 
259
        ER_OUT_OF_RESOURCES()   => STATUS_ENVIRONMENT_FAILURE,
 
260
        ER_CON_COUNT_ERROR()    => STATUS_ENVIRONMENT_FAILURE,
 
261
        ER_RECORD_FILE_FULL()   => STATUS_ENVIRONMENT_FAILURE,
 
262
        ER_DISK_FULL()          => STATUS_ENVIRONMENT_FAILURE,
 
263
        ER_OUTOFMEMORY()        => STATUS_ENVIRONMENT_FAILURE,
 
264
        ER_STACK_OVERRUN()      => STATUS_ENVIRONMENT_FAILURE,
 
265
 
 
266
        ER_SERVER_SHUTDOWN()    => STATUS_SERVER_KILLED
 
267
);
 
268
 
 
269
my %caches;
 
270
        
 
271
sub init {
 
272
        my $executor = shift;
 
273
        my $dbh = DBI->connect($executor->dsn(), undef, undef, {
 
274
                PrintError => 0,
 
275
                RaiseError => 0,
 
276
                AutoCommit => 1,
 
277
                mysql_multi_statements => 1
 
278
        } );
 
279
 
 
280
        if (not defined $dbh) {
 
281
                say("connect() to dsn ".$executor->dsn()." failed: ".$DBI::errstr);
 
282
                return STATUS_ENVIRONMENT_FAILURE;
 
283
        }
 
284
 
 
285
        $executor->setDbh($dbh);
 
286
 
 
287
        # 
 
288
        # Hack around bug 35676, optiimzer_switch must be set sesson-wide in order to have effect
 
289
        # So we read it from the GLOBAL_VARIABLE table and set it locally to the session
 
290
        #
 
291
 
 
292
        $dbh->do("
 
293
                SET optimizer_switch = (
 
294
                        SELECT variable_value
 
295
                        FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
 
296
                        WHERE VARIABLE_NAME = 'optimizer_switch'
 
297
                )
 
298
        ");
 
299
 
 
300
#       say("Executor initialized, id ".$executor->id());
 
301
 
 
302
        return STATUS_OK;
 
303
}
 
304
 
 
305
sub execute {
 
306
        my ($executor, $query, $silent) = @_;
 
307
 
 
308
        my $dbh = $executor->dbh();
 
309
 
 
310
        return GenTest::Result->new( query => $query, status => STATUS_UNKNOWN_ERROR ) if not defined $dbh;
 
311
 
 
312
        if (
 
313
                (not defined $executor->[EXECUTOR_MYSQL_AUTOCOMMIT]) &&
 
314
                (
 
315
                        ($query =~ m{^\s*start transaction}io) ||
 
316
                        ($query =~ m{^\s*begin}io) 
 
317
                )
 
318
        ) {     
 
319
                $dbh->do("SET AUTOCOMMIT=OFF");
 
320
                $executor->[EXECUTOR_MYSQL_AUTOCOMMIT] = 0;
 
321
        }
 
322
 
 
323
        my $start_time = Time::HiRes::time();
 
324
        my $sth = $dbh->prepare($query);
 
325
 
 
326
        if (not defined $sth) {                 # Error on PREPARE
 
327
                my $errstr = $executor->normalizeError($sth->errstr());
 
328
                $executor->[EXECUTOR_ERROR_STATS]->{$errstr}++ if $executor->debug();
 
329
                return GenTest::Result->new(
 
330
                        query           => $query,
 
331
                        status          => $executor->getStatusFromErr($dbh->err()) || STATUS_UNKNOWN_ERROR,
 
332
                        err             => $dbh->err(),
 
333
                        errstr          => $dbh->errstr(),
 
334
                        sqlstate        => $dbh->state(),
 
335
                        start_time      => $start_time,
 
336
                        end_time        => Time::HiRes::time()
 
337
                );
 
338
        }
 
339
 
 
340
        my $affected_rows = $sth->execute();
 
341
        my $end_time = Time::HiRes::time();
 
342
 
 
343
        my $err = $sth->err();
 
344
        my $result;
 
345
 
 
346
        if (defined $err) {                     # Error on EXECUTE
 
347
                my $err_type = $err2type{$err};
 
348
 
 
349
                if (
 
350
                        ($err_type == STATUS_SYNTAX_ERROR) ||
 
351
                        ($err_type == STATUS_SEMANTIC_ERROR) ||
 
352
                        ($err_type == STATUS_TRANSACTION_ERROR)
 
353
                ) {
 
354
                        my $errstr = $executor->normalizeError($sth->errstr());
 
355
                        $executor->[EXECUTOR_ERROR_STATS]->{$errstr}++ if $executor->debug();
 
356
                        if (not defined $reported_errors{$errstr}) {
 
357
                                say("Query: $query failed: $err $errstr. Further errors of this kind will be suppressed.") if !$silent;
 
358
                                $reported_errors{$errstr}++;
 
359
                        }
 
360
                } else {
 
361
                        $executor->[EXECUTOR_ERROR_STATS]->{$sth->errstr()}++ if $executor->debug();
 
362
                        say("Query: $query failed: $err ".$sth->errstr()) if !$silent;
 
363
                }
 
364
 
 
365
                $result = GenTest::Result->new(
 
366
                        query           => $query,
 
367
                        status          => $err2type{$err} || STATUS_UNKNOWN_ERROR,
 
368
                        err             => $err,
 
369
                        errstr          => $sth->errstr(),
 
370
                        sqlstate        => $sth->state(),
 
371
                        start_time      => $start_time,
 
372
                        end_time        => $end_time
 
373
                );
 
374
        } elsif ((not defined $sth->{NUM_OF_FIELDS}) || ($sth->{NUM_OF_FIELDS} == 0)) {
 
375
                $result = GenTest::Result->new(
 
376
                        query           => $query,
 
377
                        status          => STATUS_OK,
 
378
                        affected_rows   => $affected_rows,
 
379
                        start_time      => $start_time,
 
380
                        end_time        => $end_time
 
381
                );
 
382
                $executor->[EXECUTOR_ERROR_STATS]->{'(no error)'}++ if $executor->debug();
 
383
        } else {
 
384
                #
 
385
                # We do not use fetchall_arrayref() due to a memory leak
 
386
                # We also copy the row explicitly into a fresh array
 
387
                # otherwise the entire @data array ends up referencing row #1 only
 
388
                #
 
389
                my @data;
 
390
                while (my $row = $sth->fetchrow_arrayref()) {
 
391
                        my @row = @$row;
 
392
                        push @data, \@row;
 
393
                }       
 
394
 
 
395
                $result = GenTest::Result->new(
 
396
                        query           => $query,
 
397
                        status          => STATUS_OK,
 
398
                        affected_rows   => $affected_rows,
 
399
                        data            => \@data,
 
400
                        start_time      => $start_time,
 
401
                        end_time        => $end_time
 
402
                );
 
403
 
 
404
                $executor->[EXECUTOR_ERROR_STATS]->{'(no error)'}++ if $executor->debug();
 
405
        }
 
406
 
 
407
        $sth->finish();
 
408
 
 
409
        if ($sth->{mysql_warning_count} > 0) {
 
410
                my $warnings = $dbh->selectcol_arrayref("SHOW WARNINGS");
 
411
                $result->setWarnings($warnings);
 
412
        }
 
413
 
 
414
        if (
 
415
                ($executor->debug()) &&
 
416
                ($query =~ m{^\s*select}sio)
 
417
        ) {
 
418
                $executor->explain($query);
 
419
                $executor->[EXECUTOR_ROW_STATS]->{sprintf("%5d",$sth->rows())}++;
 
420
        }
 
421
 
 
422
        return $result;
 
423
}
 
424
 
 
425
#
 
426
# Run EXPLAIN on the query in question, recording all notes in the EXPLAIN's Extra field into the statistics
 
427
#
 
428
 
 
429
sub id {
 
430
        my $executor = shift;
 
431
 
 
432
        # if no ID string has been defined yet, define one.
 
433
 
 
434
        if ($executor->SUPER::id() eq '') {
 
435
                my $dbh = $executor->dbh();
 
436
                my $version = $dbh->selectrow_array("SELECT VERSION()");
 
437
 
 
438
                my @capabilities;
 
439
 
 
440
                push @capabilities, "master" if $dbh->selectrow_array("SHOW SLAVE HOSTS");
 
441
                push @capabilities, "slave" if $dbh->selectrow_array("SHOW SLAVE STATUS");
 
442
                push @capabilities, "no_semijoin" if $dbh->selectrow_array('SELECT @@optimizer_switch') =~ m{no_semijoin}sio;
 
443
                push @capabilities, "no_materialization" if $dbh->selectrow_array('SELECT @@optimizer_switch') =~ m{no_materialization}sio;
 
444
                push @capabilities, "mo_mrr" if $dbh->selectrow_array('SELECT @@optimizer_use_mrr') eq '0';
 
445
                push @capabilities, "no_condition_pushdown" if $dbh->selectrow_array('SELECT @@engine_condition_pushdown') eq '0';
 
446
                $executor->setId(ref($executor)." ".$version." (".join('; ', @capabilities).")");
 
447
        }
 
448
        
 
449
        # Pass the call back to the parent class. It will respond with the id that was (just) defined.
 
450
 
 
451
        return $executor->SUPER::id();
 
452
}
 
453
 
 
454
sub version {
 
455
        my $executor = shift;
 
456
        my $dbh = $executor->dbh();
 
457
        return $dbh->selectrow_array("SELECT VERSION()");
 
458
}
 
459
 
 
460
sub slaveInfo {
 
461
        my $executor = shift;
 
462
        my $slave_info = $executor->dbh()->selectrow_arrayref("SHOW SLAVE HOSTS");
 
463
        return ($slave_info->[SLAVE_INFO_HOST], $slave_info->[SLAVE_INFO_PORT]);
 
464
}
 
465
 
 
466
sub masterStatus {
 
467
        my $executor = shift;
 
468
        return $executor->dbh()->selectrow_array("SHOW MASTER STATUS");
 
469
}
 
470
 
 
471
sub explain {
 
472
        my ($executor, $query) = @_;
 
473
        my $explain_output = $executor->dbh()->selectall_arrayref("EXPLAIN $query");
 
474
        foreach my $explain_row (@$explain_output) {
 
475
                my $explain_string = $explain_row->[9];
 
476
                my @explain_items = split('; ', $explain_string);
 
477
                foreach my $explain_item (@explain_items) {
 
478
                        $explain_item =~ s{0x.*?\)}{%d\)}sgio;
 
479
                        $executor->[EXECUTOR_EXPLAIN_STATS]->{$explain_item}++;
 
480
                }
 
481
        }
 
482
}
 
483
 
 
484
sub DESTROY {
 
485
        my $executor = shift;
 
486
        if ($executor->debug()) {
 
487
                say("Statistics for Executor ".$executor->dsn());
 
488
                use Data::Dumper;
 
489
                $Data::Dumper::Sortkeys = 1;
 
490
                print Dumper $executor->[EXECUTOR_ROW_STATS];
 
491
                print Dumper $executor->[EXECUTOR_EXPLAIN_STATS];
 
492
                print Dumper $executor->[EXECUTOR_ERROR_STATS];
 
493
        }
 
494
        $executor->dbh()->disconnect();
 
495
}
 
496
 
 
497
sub tables {
 
498
        my $executor = shift;
 
499
        return [] if not defined $executor->dbh();
 
500
        $caches{tables} = $executor->dbh()->selectcol_arrayref("SHOW TABLES") if not exists $caches{tables};
 
501
        return $caches{tables};
 
502
}
 
503
 
 
504
sub fields {
 
505
        my $executor = shift;
 
506
        if (defined $executor->dbh()) {
 
507
                $caches{fields} = $executor->dbh()->selectcol_arrayref("
 
508
                        SHOW FIELDS FROM ".$executor->tables()->[0]
 
509
                ) if not defined $caches{fields};
 
510
                return $caches{fields};
 
511
        } else {
 
512
                return [];
 
513
        }
 
514
}
 
515
 
 
516
sub fieldsNoPK {
 
517
        my $executor = shift;
 
518
        $caches{fieldsNoPK} = $executor->dbh()->selectcol_arrayref("
 
519
                SELECT COLUMN_NAME
 
520
                FROM INFORMATION_SCHEMA.COLUMNS
 
521
                WHERE TABLE_SCHEMA = DATABASE() AND
 
522
                TABLE_NAME = '".$executor->tables()->[0]."'
 
523
                AND COLUMN_KEY != 'PRI'
 
524
        ") if not defined $caches{fieldsNoPK};
 
525
 
 
526
        return $caches{fieldsNoPK};
 
527
}
 
528
 
 
529
sub fieldsIndexed {
 
530
        my $executor = shift;
 
531
        $caches{fields_indexed} = $executor->dbh()->selectcol_arrayref("
 
532
                SHOW INDEX FROM ".$executor->tables()->[0]
 
533
        , { Columns=>[5] }) if not defined $caches{fields_indexed};
 
534
 
 
535
        return $caches{fields_indexed};
 
536
}
 
537
 
 
538
sub fields_indexed {
 
539
        my $executor = shift;
 
540
        $caches{fields_indexed} = $executor->dbh()->selectcol_arrayref("
 
541
                SHOW INDEX  FROM ".$executor->tables()->[0]
 
542
        , { Columns=>[5] } ) if not defined $caches{fields_indexed};
 
543
 
 
544
        return $caches{fields_indexed};
 
545
 
 
546
}
 
547
 
 
548
sub collations {
 
549
        my $executor = shift;
 
550
        return $executor->dbh()->selectcol_arrayref("
 
551
                SELECT COLLATION_NAME
 
552
                FROM INFORMATION_SCHEMA.COLLATIONS
 
553
        ");
 
554
}
 
555
 
 
556
sub charsets {
 
557
        my $executor = shift;
 
558
        return $executor->dbh()->selectcol_arrayref("
 
559
                SELECT DISTINCT CHARACTER_SET_NAME
 
560
                FROM INFORMATION_SCHEMA.COLLATIONS
 
561
        ");
 
562
}
 
563
 
 
564
sub database {
 
565
        my $executor = shift;
 
566
        return $executor->dbh()->selectrow_array("SELECT DATABASE()");
 
567
}
 
568
 
 
569
sub errorType {
 
570
        return undef if not defined $_[0];
 
571
        return $err2type{$_[0]} || STATUS_UNKNOWN_ERROR ;
 
572
}
 
573
 
 
574
sub normalizeError {
 
575
        my ($executor, $errstr) = @_;
 
576
 
 
577
        $errstr =~ s{\d+}{%d}sgio if $errstr !~ m{from storage engine}sio; # Make all errors involving numbers the same, e.g. duplicate key errors
 
578
 
 
579
        foreach my $i (0..$#errors) {
 
580
                last if $errstr =~ s{$patterns[$i]}{$errors[$i]}si;
 
581
        }
 
582
 
 
583
        $errstr =~ s{\.\*\?}{%s}sgio;
 
584
 
 
585
        return $errstr;
 
586
}
 
587
 
 
588
1;