62
30
exit(1) if !$opt_result;
64
my $prng = GenTest::Random->new(
65
seed => $seed eq 'time' ? time() : $seed,
66
varchar_length => $varchar_len
69
my $executor = GenTest::Executor->newFromDSN($dsn);
72
help() if not defined $executor;
75
# The configuration file is actually a perl script, so we read it by eval()-ing it
78
my ($tables, $fields, $data); # Configuration as read from the config file.
79
my (@table_perms, @field_perms, @data_perms); # Configuration after defaults have been substituted
81
if ($config_file ne '') {
82
open(CONF , $config_file) or die "unable to open config file '$config_file': $!";
83
read(CONF, my $config_text, -s $config_file);
85
die "Unable to load $config_file: $@" if $@;
88
$executor->execute("SET SQL_MODE= 'NO_ENGINE_SUBSTITUTION'") if $executor->type == DB_MYSQL;
89
$executor->execute("SET STORAGE_ENGINE='$engine'") if $engine ne '';
91
$table_perms[TABLE_ROW] = $tables->{rows} || (defined $rows ? [ $rows ] : undef ) || [0, 1, 2, 10, 100];
92
$table_perms[TABLE_ENGINE] = $tables->{engines} || [ $engine ];
93
$table_perms[TABLE_CHARSET] = $tables->{charsets} || [ undef ];
94
$table_perms[TABLE_COLLATION] = $tables->{collations} || [ undef ];
95
$table_perms[TABLE_PARTITION] = $tables->{partitions} || [ undef ];
96
$table_perms[TABLE_PK] = $tables->{pk} || $tables->{primary_key} || [ 'integer auto_increment' ];
97
$table_perms[TABLE_ROW_FORMAT] = $tables->{row_formats} || [ undef ];
99
$table_perms[TABLE_VIEWS] = $tables->{views} || (defined $views ? [ "" ] : undef );
100
$table_perms[TABLE_MERGES] = $tables->{merges} || undef ;
102
$table_perms[TABLE_NAMES] = $tables->{names} || [ ];
104
$field_perms[FIELD_TYPE] = $fields->{types} || [ 'int', 'varchar', 'date', 'time', 'datetime' ];
105
$field_perms[FIELD_NULLABILITY] = $fields->{null} || $fields->{nullability} || [ undef ];
106
$field_perms[FIELD_SIGN] = $fields->{sign} || [ undef ];
107
$field_perms[FIELD_INDEX] = $fields->{indexes} || $fields->{keys} || [ undef, 'KEY' ];
108
$field_perms[FIELD_CHARSET] = $fields->{charsets} || [ undef ];
109
$field_perms[FIELD_COLLATION] = $fields->{collations} || [ undef ];
111
$data_perms[DATA_NUMBER] = $data->{numbers} || ['digit', 'digit', 'digit', 'digit', 'null' ]; # 20% NULL values
112
$data_perms[DATA_STRING] = $data->{strings} || ['letter', 'letter', 'letter', 'letter', 'null' ];
113
$data_perms[DATA_BLOB] = $data->{blobs} || [ 'data', 'data', 'data', 'data', 'null' ];
114
$data_perms[DATA_TEMPORAL] = $data->{temporals} || [ 'date', 'time', 'datetime', 'year', 'timestamp', 'null' ];
115
$data_perms[DATA_ENUM] = $data->{enum} || ['letter', 'letter', 'letter', 'letter', 'null' ];
117
my @tables = (undef);
120
foreach my $cycle (TABLE_ROW, TABLE_ENGINE, TABLE_CHARSET, TABLE_COLLATION, TABLE_PARTITION, TABLE_PK, TABLE_ROW_FORMAT) {
123
if (not defined $table_perms[$cycle]) {
124
$old_table; # Retain old table, no permutations at this stage.
126
# Create several new tables, one for each allowed value in the current $cycle
129
my @new_table = defined $old_table ? @$old_table : [];
130
$new_table[$cycle] = lc($new_perm);
132
} @{$table_perms[$cycle]};
138
# Iteratively build the array of tables. We start with an empty array, and on each iteration
139
# we increase the size of the array to contain more combinations.
141
# Then we do the same for fields.
144
my @fields = (undef);
146
foreach my $cycle (FIELD_TYPE, FIELD_NULLABILITY, FIELD_SIGN, FIELD_INDEX, FIELD_CHARSET, FIELD_COLLATION) {
149
if (not defined $field_perms[$cycle]) {
150
$old_field; # Retain old field, no permutations at this stage.
152
($cycle == FIELD_SIGN) &&
153
($old_field->[FIELD_TYPE] !~ m{int|float|double|dec|numeric|fixed}sio)
155
$old_field; # Retain old field, sign does not apply to non-integer types
157
($cycle == FIELD_CHARSET) &&
158
($old_field->[FIELD_TYPE] =~ m{bit|int|bool|float|double|dec|numeric|fixed|blob|date|time|year|binary}sio)
160
$old_field; # Retain old field, charset does not apply to integer types
162
# Create several new fields, one for each allowed value in the current $cycle
165
my @new_field = defined $old_field ? @$old_field : [];
166
$new_field[$cycle] = lc($new_perm);
168
} @{$field_perms[$cycle]};
173
# If no fields were defined, continue with just the primary key.
174
@fields = () if ($#fields == 0) && ($fields[0]->[FIELD_TYPE] eq '');
176
foreach my $field_id (0..$#fields) {
177
my $field = $fields[$field_id];
178
next if not defined $field;
179
my @field_copy = @$field;
181
# $field_copy[FIELD_INDEX] = 'nokey' if $field_copy[FIELD_INDEX] eq '';
184
$field_name = join('_', grep { $_ ne '' } @field_copy);
185
$field_name =~ s{[^A-Za-z0-9]}{_}sgio;
186
$field_name =~ s{ }{_}sgio;
187
$field_name =~ s{_+}{_}sgio;
188
$field_name =~ s{_+$}{}sgio;
190
$field->[FIELD_NAME] = $field_name;
193
($field_copy[FIELD_TYPE] =~ m{set|enum}sio) &&
194
($field_copy[FIELD_TYPE] !~ m{\(}sio )
196
$field_copy[FIELD_TYPE] .= " (".join(',', map { "'$_'" } ('a'..'z') ).")";
200
($field_copy[FIELD_TYPE] =~ m{char}sio) &&
201
($field_copy[FIELD_TYPE] !~ m{\(}sio)
203
$field_copy[FIELD_TYPE] .= ' (1)';
206
$field_copy[FIELD_CHARSET] = "CHARACTER SET ".$field_copy[FIELD_CHARSET] if $field_copy[FIELD_CHARSET] ne '';
207
$field_copy[FIELD_COLLATION] = "COLLATE ".$field_copy[FIELD_COLLATION] if $field_copy[FIELD_COLLATION] ne '';
212
($field_copy[FIELD_TYPE] =~ m{blob|text|binary}sio ) &&
213
($field_copy[FIELD_TYPE] !~ m{\(}sio )
219
($field_copy[FIELD_INDEX] ne 'nokey') &&
220
($field_copy[FIELD_INDEX] ne '')
222
$field->[FIELD_INDEX_SQL] = $field_copy[FIELD_INDEX]." (`$field_name` $key_len)";
225
delete $field_copy[FIELD_INDEX]; # do not include FIELD_INDEX in the field description
227
$fields[$field_id]->[FIELD_SQL] = "`$field_name` ". join(' ' , grep { $_ ne '' } @field_copy);
229
if ($field_copy[FIELD_TYPE] =~ m{timestamp}sio ) {
230
$field->[FIELD_SQL] .= ' NULL DEFAULT 0';
234
foreach my $table_id (0..$#tables) {
235
my $table = $tables[$table_id];
236
my @table_copy = @$table;
238
if ($#{$table_perms[TABLE_NAMES]} > -1) {
239
$table->[TABLE_NAME] = shift @{$table_perms[TABLE_NAMES]};
242
$table_name = "table".join('_', grep { $_ ne '' } @table_copy);
243
$table_name =~ s{[^A-Za-z0-9]}{_}sgio;
244
$table_name =~ s{ }{_}sgio;
245
$table_name =~ s{_+}{_}sgio;
246
$table_name =~ s{auto_increment}{autoinc}siog;
247
$table_name =~ s{partition_by}{part_by}siog;
248
$table_name =~ s{partition}{part}siog;
249
$table_name =~ s{partitions}{parts}siog;
250
$table_name =~ s{values_less_than}{}siog;
251
$table_name =~ s{integer}{int}siog;
254
(uc($table_copy[TABLE_ENGINE]) eq 'MYISAM') ||
255
($table_copy[TABLE_ENGINE] eq '')
257
push @myisam_tables, $table_name;
260
$table->[TABLE_NAME] = $table_name;
263
$table_copy[TABLE_ENGINE] = "ENGINE=".$table_copy[TABLE_ENGINE] if $table_copy[TABLE_ENGINE] ne '';
264
$table_copy[TABLE_ROW_FORMAT] = "ROW_FORMAT=".$table_copy[TABLE_ROW_FORMAT] if $table_copy[TABLE_ROW_FORMAT] ne '';
265
$table_copy[TABLE_CHARSET] = "CHARACTER SET ".$table_copy[TABLE_CHARSET] if $table_copy[TABLE_CHARSET] ne '';
266
$table_copy[TABLE_COLLATION] = "COLLATE ".$table_copy[TABLE_COLLATION] if $table_copy[TABLE_COLLATION] ne '';
267
$table_copy[TABLE_PARTITION] = "/*!50100 PARTITION BY ".$table_copy[TABLE_PARTITION]." */" if $table_copy[TABLE_PARTITION] ne '';
269
delete $table_copy[TABLE_ROW]; # Do not include number of rows in the CREATE TABLE
270
delete $table_copy[TABLE_PK]; # Do not include PK definition at the end of CREATE TABLE
272
$table->[TABLE_SQL] = join(' ' , grep { $_ ne '' } @table_copy);
275
foreach my $table_id (0..$#tables) {
276
my $table = $tables[$table_id];
277
my @table_copy = @$table;
278
my @fields_copy = @fields;
280
if (uc($table->[TABLE_ENGINE]) eq 'FALCON') {
281
@fields_copy = grep {
282
!($_->[FIELD_TYPE] =~ m{blob|text}io && $_->[FIELD_INDEX] ne '')
286
say("# Creating table $table_copy[TABLE_NAME] .");
288
if ($table_copy[TABLE_PK] ne '') {
290
$pk_field->[FIELD_NAME] = 'pk';
291
$pk_field->[FIELD_TYPE] = $table_copy[TABLE_PK];
292
$pk_field->[FIELD_INDEX] = 'primary key';
293
$pk_field->[FIELD_INDEX_SQL] = 'primary key (pk)';
294
$pk_field->[FIELD_SQL] = 'pk '.$table_copy[TABLE_PK];
295
push @fields_copy, $pk_field;
298
# Make field ordering in every table different.
299
# This exposes bugs caused by different physical field placement
301
$prng->shuffleArray(\@fields_copy);
303
$executor->execute("DROP TABLE IF EXISTS $table->[TABLE_NAME]");
305
# Compose the CREATE TABLE statement by joining all fields and indexes and appending the table options
307
my @field_sqls = join(",\n", map { $_->[FIELD_SQL] } @fields_copy);
309
my @index_fields = grep { $_->[FIELD_INDEX_SQL] ne '' } @fields_copy;
311
my $index_sqls = $#index_fields > -1 ? join(",\n", map { $_->[FIELD_INDEX_SQL] } @index_fields) : undef;
313
$executor->execute("CREATE TABLE `$table->[TABLE_NAME]` (\n".join(",\n\t", grep { defined $_ } (@field_sqls, $index_sqls) ).") $table->[TABLE_SQL] ");
315
if (defined $table_perms[TABLE_VIEWS]) {
316
foreach my $view_id (0..$#{$table_perms[TABLE_VIEWS]}) {
317
my $view_name = 'v'.$table->[TABLE_NAME]."_$view_id";
318
$executor->execute("CREATE OR REPLACE ".uc($table_perms[TABLE_VIEWS]->[$view_id])." VIEW `$view_name` AS SELECT * FROM `$table->[TABLE_NAME]`");
322
if ($table->[TABLE_ROW] > 1000) {
323
$executor->execute("SET AUTOCOMMIT=OFF");
324
$executor->execute("START TRANSACTION");
328
foreach my $row_id (1..$table->[TABLE_ROW]) {
330
foreach my $field (@fields_copy) {
333
if ($field->[FIELD_INDEX] eq 'primary key') {
334
if ($field->[FIELD_TYPE] =~ m{auto_increment}sio) {
335
$value = undef; # Trigger auto-increment by inserting NULLS for PK
337
$value = $row_id; # Otherwise, insert sequential numbers
340
my (@possible_values, $value_type);
342
if ($field->[FIELD_TYPE] =~ m{date|time|year}sio) {
343
$value_type = DATA_TEMPORAL;
344
} elsif ($field->[FIELD_TYPE] =~ m{blob|text|binary}sio) {
345
$value_type = DATA_BLOB;
346
} elsif ($field->[FIELD_TYPE] =~ m{int|float|double|dec|numeric|fixed|bool|bit}sio) {
347
$value_type = DATA_NUMBER;
348
} elsif ($field->[FIELD_TYPE] eq 'enum') {
349
$value_type = DATA_ENUM;
351
$value_type = DATA_STRING;
354
if ($field->[FIELD_NULLABILITY] eq 'not null') {
355
# Remove NULL from the list of allowed values
356
@possible_values = grep { lc($_) ne 'null' } @{$data_perms[$value_type]};
358
@possible_values = @{$data_perms[$value_type]};
361
die("# Unable to generate data for field '$field->[FIELD_TYPE] $field->[FIELD_NULLABILITY]'") if $#possible_values == -1;
363
my $possible_value = $prng->arrayElement(\@possible_values);
364
$possible_value = $field->[FIELD_TYPE] if not defined $possible_value;
366
if ($prng->isFieldType($possible_value)) {
367
$value = $prng->fieldType($possible_value);
369
$value = $possible_value; # A simple string literal as specified
373
# Blob values are generated as LOAD_FILE , so do not quote them.
374
if ($value =~ m{load_file}sio) {
375
push @data, defined $value ? $value : "NULL";
377
$value =~ s{'}{\\'}sgio;
378
push @data, defined $value ? "'$value'" : "NULL";
382
push @row_buffer, " (".join(', ', @data).") ";
385
(($row_id % 10) == 0) ||
386
($row_id == $table->[TABLE_ROW])
388
$executor->execute("INSERT IGNORE INTO $table->[TABLE_NAME] VALUES ".join(', ', @row_buffer));
392
if (($row_id % 10000) == 0) {
393
$executor->execute("COMMIT");
394
say("# Progress: loaded $row_id out of $table->[TABLE_ROW] rows");
397
$executor->execute("COMMIT");
400
$executor->execute("COMMIT");
403
(defined $table_perms[TABLE_MERGES]) &&
404
($#myisam_tables > -1)
406
foreach my $merge_id (0..$#{$table_perms[TABLE_MERGES]}) {
407
my $merge_name = 'merge_'.$merge_id;
408
$executor->execute("CREATE TABLE `$merge_name` LIKE `".$myisam_tables[0]."`");
409
$executor->execute("ALTER TABLE `$merge_name` ENGINE=MERGE UNION(".join(',',@myisam_tables).") ".uc($table_perms[TABLE_MERGES]->[$merge_id]));
33
my $app = GenTest::App::Gendata->new(config_file => $config_file,
40
varchar_length => $varchar_len,
41
server_id => $server_id);
44
my $status = $app->run();
46
if ($status > STATUS_OK) {