1
# Copyright (c) 2008,2011 Oracle and/or its affiliates. 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
package GenTest::Transform;
27
use GenTest::Constants;
28
use GenTest::Executor::MySQL;
31
use constant TRANSFORMER_QUERIES_PROCESSED => 0;
32
use constant TRANSFORMER_QUERIES_TRANSFORMED => 1;
34
use constant TRANSFORM_OUTCOME_EXACT_MATCH => 1001;
35
use constant TRANSFORM_OUTCOME_UNORDERED_MATCH => 1002;
36
use constant TRANSFORM_OUTCOME_SUPERSET => 1003;
37
use constant TRANSFORM_OUTCOME_SUBSET => 1004;
38
use constant TRANSFORM_OUTCOME_SINGLE_ROW => 1005;
39
use constant TRANSFORM_OUTCOME_FIRST_ROW => 1006;
40
use constant TRANSFORM_OUTCOME_DISTINCT => 1007;
41
use constant TRANSFORM_OUTCOME_COUNT => 1008;
42
use constant TRANSFORM_OUTCOME_EMPTY_RESULT => 1009;
43
use constant TRANSFORM_OUTCOME_SINGLE_INTEGER_ONE => 1010;
45
my %transform_outcomes = (
46
'TRANSFORM_OUTCOME_EXACT_MATCH' => 1001,
47
'TRANSFORM_OUTCOME_UNORDERED_MATCH' => 1002,
48
'TRANSFORM_OUTCOME_SUPERSET' => 1003,
49
'TRANSFORM_OUTCOME_SUBSET' => 1004,
50
'TRANSFORM_OUTCOME_SINGLE_ROW' => 1005,
51
'TRANSFORM_OUTCOME_FIRST_ROW' => 1006,
52
'TRANSFORM_OUTCOME_DISTINCT' => 1007,
53
'TRANSFORM_OUTCOME_COUNT' => 1008,
54
'TRANSFORM_OUTCOME_EMPTY_RESULT' => 1009,
55
'TRANSFORM_OUTCOME_SINGLE_INTEGER_ONE' => 1010
58
# Subset of semantic errors that we may want to allow during transforms.
59
my %mysql_grouping_errors = (
60
1004 => 'ER_NON_GROUPING_FIELD_USED',
61
1055 => 'ER_WRONG_FIELD_WITH_GROUP',
62
1056 => 'ER_WRONG_GROUP_FIELD',
63
1140 => 'ER_MIX_OF_GROUP_FUNC_AND_FIELDS',
64
1317 => 'ER_QUERY_INTERRUPTED',
65
2013 => 'CR_SERVER_LOST',
66
2006 => 'CR_SERVER_GONE_ERROR',
67
1028 => 'ER_FILSORT_ABORT',
68
1111 => 'ER_INVALID_GROUP_FUNC_USE',
69
1615 => 'ER_NEED_REPREPARE',
70
1060 => 'DUPLICATE_COLUMN_NAME',
71
1104 => 'ER_TOO_BIG_SELECT'
74
# List of encountered errors that we want to suppress later in the test run.
75
my %suppressed_errors = ();
77
sub transformExecuteValidate {
78
my ($transformer, $original_query, $original_result, $executor) = @_;
80
$transformer->[TRANSFORMER_QUERIES_PROCESSED]++;
82
my $transformer_output = $transformer->transform($original_query, $executor, $original_result);
86
if ($transformer_output =~ m{^\d+$}sgio) {
87
if ($transformer_output == STATUS_WONT_HANDLE) {
90
return $transformer_output; # Error was returned and no queries
92
} elsif (ref($transformer_output) eq 'ARRAY') {
93
if (ref($transformer_output->[0]) eq 'ARRAY') {
94
# Transformation produced more than one block of queries
95
$transform_blocks = $transformer_output;
97
# Transformation produced a single block of queries
98
$transform_blocks = [ $transformer_output ];
101
# Transformation produced a single query, convert it to a single block
102
$transform_blocks = [ [ $transformer_output ] ];
105
foreach my $transform_block (@$transform_blocks) {
106
my @transformed_queries = @$transform_block;
107
my @transformed_results;
108
my $transform_outcome;
110
$transformed_queries[0] = "/* ".ref($transformer)." */ ".$transformed_queries[0];
112
foreach my $transformed_query_part (@transformed_queries) {
113
my $part_result = $executor->execute($transformed_query_part);
115
if ($part_result->status() == STATUS_SKIP) {
116
$transform_outcome = STATUS_OK;
119
($part_result->status() == STATUS_SYNTAX_ERROR) ||
120
($part_result->status() == STATUS_SEMANTIC_ERROR) ||
121
($part_result->status() == STATUS_SERVER_CRASHED)
123
# We normally return a critical error when a transformer returns
124
# a semantic or syntactic error, because we want to detect any
125
# faulty transformers, e.g. those which do not produce valid
126
# queries. However, some errors may need to be accepted in
127
# certain situations.
129
# For example, with MySQL's ONLY_FULL_GROUP_BY sql mode, some
130
# queries return grouping related errors, whereas they would
131
# not return such errors without this mode, and we want to
132
# continue the test even if such errors occur.
133
# We have logic in place to take care of this below.
136
($executor->type() == DB_MYSQL) &&
137
(exists $mysql_grouping_errors{$part_result->err()})
140
say("Ignoring transform ".ref($transformer)." that failed with the error: ".$part_result->errstr());
141
say("Offending query is: $transformed_query_part;");
143
if (not defined $suppressed_errors{$part_result->err()}) {
144
say("Ignoring transforms of the type ".ref($transformer)." that fail with an error like: ".$part_result->errstr());
145
$suppressed_errors{$part_result->err()}++;
149
# We "cheat" by returning STATUS_OK, as the validator would otherwise try to access the result.
152
say("---------- TRANSFORM ISSUE ----------");
153
say("Transform ".ref($transformer)." failed with a syntactic or semantic error: ".$part_result->err()." ".$part_result->errstr().
154
"; RQG Status: ".status2text($part_result->status())." (".$part_result->status().")");
155
say("Offending query is: $transformed_query_part;");
156
say("Original query is: $original_query;");
157
say("ERROR: Possible syntax or semantic error caused by code in transformer ".ref($transformer).
158
". Raising severity to STATUS_ENVIRONMENT_FAILURE.");
159
return STATUS_ENVIRONMENT_FAILURE;
160
} elsif ($part_result->status() != STATUS_OK) {
161
say("---------- TRANSFORM ISSUE ----------");
162
say("Transform ".$transformer->name()." failed with an error: ".$part_result->err().' '.$part_result->errstr());
163
say("Transformed query was: ".$transformed_query_part);
164
return $part_result->status();
165
} elsif (defined $part_result->data()) {
166
my $part_outcome = $transformer->validate($original_result, $part_result);
167
$transform_outcome = $part_outcome if (($part_outcome > $transform_outcome) || (! defined $transform_outcome));
168
push @transformed_results, $part_result if ($part_outcome != STATUS_WONT_HANDLE) && ($part_outcome != STATUS_OK);
173
(not defined $transform_outcome) ||
174
($transform_outcome == STATUS_WONT_HANDLE)
176
say("Transform ".ref($transformer)." produced no query which could be validated ($transform_outcome).");
177
say("The following queries were produced");
178
print Dumper \@transformed_queries;
179
return STATUS_ENVIRONMENT_FAILURE;
182
$transformer->[TRANSFORMER_QUERIES_TRANSFORMED]++;
184
if ($transform_outcome != STATUS_OK) {
185
return ($transform_outcome, \@transformed_queries, \@transformed_results);
194
my ($transformer, $original_result, $transformed_result) = @_;
196
my $transformed_query = $transformed_result->query();
198
my $transform_outcome;
200
foreach my $potential_outcome (keys %transform_outcomes) {
201
if ($transformed_query =~ m{$potential_outcome}s) {
202
$transform_outcome = $transform_outcomes{$potential_outcome};
207
if ($transform_outcome == TRANSFORM_OUTCOME_SINGLE_ROW) {
208
return $transformer->isSingleRow($original_result, $transformed_result);
209
} elsif ($transform_outcome == TRANSFORM_OUTCOME_DISTINCT) {
210
return $transformer->isDistinct($original_result, $transformed_result);
211
} elsif ($transform_outcome == TRANSFORM_OUTCOME_UNORDERED_MATCH) {
212
return GenTest::Comparator::compare($original_result, $transformed_result);
213
} elsif ($transform_outcome == TRANSFORM_OUTCOME_SUPERSET) {
214
return $transformer->isSuperset($original_result, $transformed_result);
215
} elsif ($transform_outcome == TRANSFORM_OUTCOME_FIRST_ROW) {
216
return $transformer->isFirstRow($original_result, $transformed_result);
217
} elsif ($transform_outcome == TRANSFORM_OUTCOME_COUNT) {
218
return $transformer->isCount($original_result, $transformed_result);
219
} elsif ($transform_outcome == TRANSFORM_OUTCOME_EMPTY_RESULT) {
220
return $transformer->isEmptyResult($original_result, $transformed_result);
221
} elsif ($transform_outcome == TRANSFORM_OUTCOME_SINGLE_INTEGER_ONE) {
222
return $transformer->isSingleIntegerOne($original_result, $transformed_result);
224
return STATUS_WONT_HANDLE;
229
my ($transformer, $original_result, $transformed_result) = @_;
232
($original_result->rows() == 0) &&
233
($transformed_result->rows() == 0)
237
my $row1 = join('<col>', @{$original_result->data()->[0]});
238
my $row2 = join('<col>', @{$transformed_result->data()->[0]});
239
return STATUS_CONTENT_MISMATCH if $row1 ne $row2;
245
my ($transformer, $original_result, $transformed_result) = @_;
248
my $transformed_rows;
250
foreach my $row_ref (@{$original_result->data()}) {
251
my $row = lc(join('<col>', @$row_ref));
252
$original_rows->{$row}++;
255
foreach my $row_ref (@{$transformed_result->data()}) {
256
my $row = lc(join('<col>', @$row_ref));
257
$transformed_rows->{$row}++;
258
return STATUS_LENGTH_MISMATCH if $transformed_rows->{$row} > 1;
262
my $distinct_original = join ('<row>', sort keys %{$original_rows} );
263
my $distinct_transformed = join ('<row>', sort keys %{$transformed_rows} );
265
if ($distinct_original ne $distinct_transformed) {
266
return STATUS_CONTENT_MISMATCH;
273
my ($transformer, $original_result, $transformed_result) = @_;
276
foreach my $row_ref (@{$original_result->data()}) {
277
my $row = join('<col>', @$row_ref);
281
foreach my $row_ref (@{$transformed_result->data()}) {
282
my $row = join('<col>', @$row_ref);
286
foreach my $row (keys %rows) {
287
return STATUS_LENGTH_MISMATCH if $rows{$row} > 0;
294
my ($transformer, $original_result, $transformed_result) = @_;
297
($original_result->rows() == 0) &&
298
($transformed_result->rows() == 0)
301
} elsif ($transformed_result->rows() == 1) {
302
my $transformed_row = join('<col>', @{$transformed_result->data()->[0]});
303
foreach my $original_row_ref (@{$original_result->data()}) {
304
my $original_row = join('<col>', @$original_row_ref);
305
return STATUS_OK if $original_row eq $transformed_row;
307
return STATUS_CONTENT_MISMATCH;
309
# More than one row, something is messed up
310
return STATUS_LENGTH_MISMATCH;
315
my ($transformer, $original_result, $transformed_result) = @_;
317
my ($large_result, $small_result) ;
320
($original_result->rows() == 0) ||
321
($transformed_result->rows() == 0)
325
($original_result->rows() == 1) &&
326
($transformed_result->rows() == 1)
330
($original_result->rows() == 1) &&
331
($transformed_result->rows() >= 1)
333
$small_result = $original_result;
334
$large_result = $transformed_result;
336
($transformed_result->rows() == 1) &&
337
($original_result->rows() >= 1)
339
$small_result = $transformed_result;
340
$large_result = $original_result;
342
return STATUS_LENGTH_MISMATCH;
345
if ($large_result->rows() != $small_result->data()->[0]->[0]) {
346
return STATUS_LENGTH_MISMATCH;
353
my ($transformer, $original_result, $transformed_result) = @_;
355
if ($transformed_result->rows() == 0) {
358
return STATUS_LENGTH_MISMATCH;
362
sub isSingleIntegerOne {
363
my ($transformer, $original_result, $transformed_result) = @_;
366
($transformed_result->rows() == 1) &&
367
($#{$transformed_result->data()->[0]} == 0) &&
368
($transformed_result->data()->[0]->[0] eq '1')
372
return STATUS_LENGTH_MISMATCH;
379
my $transformer = shift;
380
my ($name) = $transformer =~ m{.*::([a-z]*)}sgio;
385
my $transformer = shift;
386
print "# ".ref($transformer).": queries_processed: ".$transformer->[TRANSFORMER_QUERIES_PROCESSED]."; queries_transformed: ".$transformer->[TRANSFORMER_QUERIES_TRANSFORMED]."\n" if rqg_debug();