-
Notifications
You must be signed in to change notification settings - Fork 20
RandomQueryGeneratorGrammar
This page describes the SQL grammar format as understood by the RandomQueryGenerator
The following built-in grammar elements are understood by the Random Query Generator:
- _int, _tinyint, _smallint, _mediumint, _bigint - returns an integer that will fit in the specified type. _unsigned may also be used, such as _int_unsigned
- _char(N), _varchar(N), _binary , _text - returns a random character string of the desired size, containing lowercase letters a to z. If no N is specified, e.g. just _char, 1 character is generated
- _digit[invariant] , _field[invariant], _table[invariant], _letter[invariant], etc. returns for this variable the same value throughout the query. For example:
select: SELECT _field[invariant] FROM _table[invariant] WHERE _table[invariant]._field[invariant] BETWEEN _digit[invariant] AND _digit[invariant] + _digit; update: UPDATE _table[invariant] SET _field = _digit;
This will generate two queries, both operating on the same table. In the first SELECT, the field being selected will be the same field that is used in the WHERE. In the BETWEEN, the same digit will appear before and after the AND, so that the second argument to BETWEEN is always larger than the first one.
- _<whatever></whatever>[invariant] is calculated once per query
- _<whatever></whatever> is calculated per every time it occurs in a query
- _<whatever></whatever>[invariant] does not contain the value of the last _<whatever></whatever> call.
- _bit(N) -a binary value of length N, such as b&amp;amp&#59;&amp;&#35;35&#59;39&amp;&#35;59&#59;11010000&amp;amp&#59;&amp;&#35;35&#59;39&amp;&#35;59&#59;;
- _bool, _boolean - a value of 0 or 1;
- _date - a valid date in the range from 2000 to 2010
- _year - a year in the range 2000 to 2010
- _time - a time in the range from 00&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;00&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;00 to 29&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;59&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;59
- _datetime - a date+time value in the ISO format 2000&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;01&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;01 00&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;00&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;00
- _timestamp - a date+time value in the MySQL format 20000101000000
- _set - a comma-separated set of letters, suitable for insertion into SET columns.
- _quid - a random 5-character string. Useful for tagging your database rows and queries so that you can see which query ran when and did what to the database:
UPDATE _table SET `char_5` = _quid WHERE _field = _digit ; SELECT /* _quid */ _field FROM _table ;
- _english a quoted English word from a list of 100 most common words. With certain queries, this is more useful than using _varchar(5), since there is a greater chance that two generated values will match. If you want a custom dictionary, place it in the dict/ directory. For example, if you have a file named dict/states.txt, you can then use _states to pick a random row from that file.
- _letter - an unquoted capital letter from A to Z. This is useful for generating database object names:
CREATE TABLE _letter SELECT * FROM _table ;
- _hex(N) - an up-to-N-digit hex number , such as x&amp;amp&#59;&amp;&#35;35&#59;39&amp;&#35;59&#59;1234&amp;amp&#59;&amp;&#35;35&#59;39&amp;&#35;59&#59;. If N is not specified, N = 4.
- _cwd - the current working directory of the script
- _tmpnam - a randomly generated file name located in the temporary directory. At the end of the grammar rule, the file will be unlinked. So, you can do things such as:
BACKUP DATABASE test TO _tmpnam ; RESTORE FROM _tmpnam ;
- _unix_timestamp - returns the current timestamp as an integer
- _tmptable - returns a random table name suitable for
CREATE TEMPORARY TABLE _tmptable (`f1` INTEGER);
- _pid - returns the operating system PID of the perl process generating the query.
- _thread_id - returns the internal ID of the process generating the query. For a test started with &amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;threads&amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59;10, _thread_id will return a distinct value between 1 and 10 for each individual testing thread.
- _thread_count - returns the number of threads the RQG is running. For a test started with &amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;threads&amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59;1, _thread_count will always return 10.
- _database - returns the quoted name of a random database, including mysql, INFORMATION_SCHEMA and PERFORMANCE_SCHEMA (if available).
- _table - returns the quoted name of a random table from the current database (e.g. test, or from the database that was returned by a previous use of _database) . Note that for performance reasons, the list of table names is cached at the start of the test, so any tables you create (or drop) during the test will not be selected;
- _field - returns the quoted name of a random field. If _table was previously used, the field will be from that table. Otherwise, it will be taken from the first table of the current database (or the database that was previously returned by _database).
- _field_indexed - returns a field that has an index on it.
- _field_no_pk - returns a field that is not a Primary Key. This is useful for creating valid UPDATE statements that will not cause duplicate key errors:
UPDATE _table SET _field_no_pk = _digit WHERE _field = _digit;
- _field_list - returns a comma-separated list of all fields. For example:
INSERT INTO _table SELECT _field_list FROM _table;
- _field_count - returns the total count of fields. e.g.:
SELECT * FROM _table ORDER BY _field_count - 1;
- _collation - returns a random collation name, prefixed by _&amp;amp&#59;lt&amp;&#35;59&#59;tt&amp;amp&#59;gt&amp;&#35;59&#59;. Use &amp;amp&#59;lt&amp;&#35;59&#59;tt&amp;amp&#59;gt&amp;&#35;59&#59;_collation_name to return a name without a leading _
- _charset - returns a random character set name, prefixed by _. Use _charset_name to return a name without a leading _
- _data - returns a LOAD_DATA() expression referencing one of the files in the data directory. For example:
INSERT INTO _table ( _field ) VALUE ( _data ) ;
will be expanded to:
INSERT INTO t1 ( f1 ) VALUE ( LOAD_DATA ('/path/to/rqg/data/earth51K.jpeg'));
The following types of comments are recognized and processed:
- &amp;amp&#59;&amp;&#35;35&#59;35&amp;&#35;59&#59; is treated as a grammar comment, and everything after a &amp;amp&#59;&amp;&#35;35&#59;35&amp;&#35;59&#59; is discarded and never sent to the server. This may not work properly with embedded Perl code containing &amp;amp&#59;&amp;&#35;35&#59;35&amp;&#35;59&#59;.
- /&amp;amp&#59;&amp;&#35;35&#59;42&amp;&#35;59&#59; ... &amp;amp&#59;&amp;&#35;35&#59;42&amp;&#35;59&#59;/ denotes a comment that will passed to the server intact. This can be used to annotate your queries so that the annotations end up in the query log. On the other hand, the automatic test simplification algorithms may not take such comments away, which may or may not be a good thing depending on how much you want to shorten your test case or grammar.
- /&amp;amp&#59;&amp;&#35;35&#59;42&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;33&amp;&#35;59&#59; ... &amp;amp&#59;&amp;&#35;35&#59;42&amp;&#35;59&#59;/ is a comment that is passed to the server intact, however is processed by MySQL as ordinary SQL, and ignored by all other databases. This is useful when using a MySQL-specific construct in a comparison test:
query: SELECT /*! HIGH_PRIORITY */ * FROM t1
- /&amp;amp&#59;&amp;&#35;35&#59;42&amp;&#35;59&#59;db1&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;db2&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59; ... &amp;amp&#59;&amp;&#35;35&#59;42&amp;&#35;59&#59;/ is a comment that will only be sent as an ordinary SQL to the databases listed at the start of the comment, and as a comment to all other databases. For example:
query: SELECT * FROM t1 ORDER BY f1 /*+JavaDB:Postgres: NULLS FIRST */
The grammar may contain embedded Perl code, delimited with &amp;amp&#59;&amp;&#35;35&#59;123&amp;&#35;59&#59; &amp;amp&#59;&amp;&#35;35&#59;125&amp;&#35;59&#59; . After the SQL statement has been built, the code is evaluated left to right. If any of the snippets return a value, it is substituted in-place. To avoid returning anything , use undef&amp;amp&#59;&amp;&#35;35&#59;59&amp;&#35;59&#59; as your last expression.
In addition, stand-alone variables such as $test will be replaced with their values.
Because the grammar is not parsed with a fully-featured lexer, the following constructs may not work:
- multi-line Perl code;
- Perl comments, with the &amp;amp&#59;&amp;&#35;35&#59;35&amp;&#35;59&#59; sign
- Expressions containing the # sign, such as $&amp;amp&#59;&amp;&#35;35&#59;35&amp;&#35;59&#59;array
- Expressions containing the | sign, such as $a &amp;amp&#59;&amp;&#35;35&#59;124&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;124&amp;&#35;59&#59; $b
CREATE DATABASE {$db++; "database".$db } ; USE $db ;
This increments a counter and generates a database name based on the counter. The same name is then used in the USE statement.
If you want to generate controlled pseudo-random values inside your Perl code, use the $prng object:
CREATE DATABASE { $db = $prng->int(0,9) } ;
see inside the GenTest&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59;Random .pm file for more information on the values that can be generated.
To pick a random table and store its name in $table:
pick_table: { $table = $prng->arrayElement($executors->[0]->tables()) } ;
To order by all selected columns (assuming "fieldNN" aliases are used and kept track of by the grammar):
order_by_all: { join(', ', map { "field".$_ } (1..$fields) ) } ;
To generate a list of all tables and views (though with limited usefulness):
all_tables: { join(', ', @{$executors->[0]->tables()}) }
A stack mechanism that allows passing of an argument to sub-rules and returning a result is also supplied. Also, you may keep variables on a certain recursion level (just as in local variables in subroutines). This stack mechanism is available through the $stack variable. The following functions are supported:
push pushes a new frame to the stack. If the previous frame has a value "arg", this will be copied to the new frame.
pop(result) will pop the frame off the stack and place the argument as "result" in the next frame.
set(name,value) will set a value in the current frame
get(name) will get a value (if it exists) from the current frame.
All routines return undef with the exception of get.
Example:
a&amp;amp&#59;&amp;&#35;35&#59;58&amp;&#35;59&#59; A &amp;amp&#59;&amp;&#35;35&#59;123&amp;&#35;59&#59; $stack&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;gt&amp;&#35;59&#59;push()&amp;amp&#59;&amp;&#35;35&#59;59&amp;&#35;59&#59; $stack&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;gt&amp;&#35;59&#59;pop($stack&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;gt&amp;&#35;59&#59;get(&amp;amp&#59;quot&amp;&#35;59&#59;arg&amp;amp&#59;quot&amp;&#35;59&#59;).&amp;amp&#59;quot&amp;&#35;59&#59;bar&amp;amp&#59;quot&amp;&#35;59&#59;)&amp;amp&#59;&amp;&#35;35&#59;59&amp;&#35;59&#59; return undef &amp;amp&#59;&amp;&#35;35&#59;125&amp;&#35;59&#59; &amp;amp&#59;&amp;&#35;35&#59;59&amp;&#35;59&#59;
This grammar will generate
A foobar
Conceptually, the example above can be visualized as follows:
get(&amp;amp&#59;quot&amp;&#35;59&#59;arg&amp;amp&#59;quot&amp;&#35;59&#59;) &amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59; &amp;amp&#59;quot&amp;&#35;59&#59;foo&amp;amp&#59;quot&amp;&#35;59&#59; get(&amp;amp&#59;quot&amp;&#35;59&#59;arg&amp;amp&#59;quot&amp;&#35;59&#59;).&amp;amp&#59;quot&amp;&#35;59&#59;bar&amp;amp&#59;quot&amp;&#35;59&#59; &amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59; &amp;amp&#59;quot&amp;&#35;59&#59;foobar&amp;amp&#59;quot&amp;&#35;59&#59; +&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;+ &amp;amp&#59;&amp;&#35;35&#59;124&amp;&#35;59&#59; &amp;amp&#59;quot&amp;&#35;59&#59;arg&amp;amp&#59;quot&amp;&#35;59&#59; &amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59; &amp;amp&#59;quot&amp;&#35;59&#59;foo&amp;amp&#59;quot&amp;&#35;59&#59; &amp;amp&#59;&amp;&#35;35&#59;124&amp;&#35;59&#59; &amp;amp&#59;lt&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59; pop(get(&amp;amp&#59;quot&amp;&#35;59&#59;arg&amp;amp&#59;quot&amp;&#35;59&#59;).&amp;amp&#59;quot&amp;&#35;59&#59;bar&amp;amp&#59;quot&amp;&#35;59&#59;) &amp;amp&#59;&amp;&#35;35&#59;124&amp;&#35;59&#59; &amp;amp&#59;quot&amp;&#35;59&#59;result&amp;amp&#59;quot&amp;&#35;59&#59; &amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59; &amp;amp&#59;quot&amp;&#35;59&#59;foobar&amp;amp&#59;quot&amp;&#35;59&#59; &amp;amp&#59;&amp;&#35;35&#59;124&amp;&#35;59&#59; +&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;+ get(&amp;amp&#59;quot&amp;&#35;59&#59;result&amp;amp&#59;quot&amp;&#35;59&#59;) &amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59; &amp;amp&#59;quot&amp;&#35;59&#59;foobar&amp;amp&#59;quot&amp;&#35;59&#59;
There is also a global frame $global with a get and a set routine which will survive one generated query to another.
-
$executors: the array of executors in use. The following methods are available:
- tables()
-
$prng: the random generator. The following methods are available:
- arrayElement()
- letter()
- int()
-
$stack: the stack
- get()
- set()
- push()
- pop()
-
$global: the global frame. The following methods are available:
- get()
- set()
By default, the tables created by the RQG all have an identical structure. Therefore, a field returned by _field will be present in all tables. If you have a variable database structure, then you can use constructs such as
SELECT _database . _table . _field ;
This will first pick a database, then a random table from the database that was just picked and finally a random field from the table that was just picked.
_database is of course optional. In addition, the last database and table picked are stored in the $last_database and $last_table perl variables, which you can read and write to from your perl code embedded in the grammar.
- the value generated by _table is with quotes and the value within $last_table not
- there is no predefined variable $last_field
To use joins, give static alias names to your tables. This way even if your grammar picks random tables, you will be able to refer to them using their constant names.
select_item: X . _field | Y . _field;
To generate a set of queries to be executed together, place them on a single line and separate them with a semicolon (;).
For example, to replicate a statement containing RAND() using statement-based replication:
replicate_rand: SET BINLOG_FORMAT = 'STATEMENT' ; INSERT INTO _table VALUES ( RAND() ) ; SET BINLOG_FORMAT = 'ROW' ;
To create a stored procedure that does something:
insert: INSERT INTO t1 VALUES ( _digit ) ; delete: DELETE FROM t1 LIMIT 1 ;
_letter returns a random letter from A to Z, and _digit returns a random digit from 0 to 9.
To facilitate the operation of multi-treaded tests, the framework provides a build-in _tmpfile rule which creates a temporary file. The file is destroyed before generating the next statement, so all references to _tmpfile must be contained in a a single statement. Within that line, all references to _tmpfile will return the same value. The file name being returned is located in the `pwd`/tmp directory, usually mysql&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;test&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;extra&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;6.0/mysql&amp;amp&#59;&amp;&#35;35&#59;45&amp;&#35;59&#59;test/gentest/tmp. For example:
select_into_outfile: SELECT * FROM _table INTO OUTFILE _tmpfile ; load_data_infile: LOAD DATA INFILE _tmpfile REPLACE INTO TABLE _table ;
By defining alias X in the outer SELECT and an alias Y in the inner select, you can generate correlated subqueries:
select: SELECT _field FROM _table AS X WHERE _field IN ( SELECT _field FROM _table AS Y WHERE X . _field = Y . _field );
To create a transaction containing SELECTs and UPDATEs:
transaction: BEGIN | COMMIT | ROLLBACK;
This will generate a stream of updates and selects with randomly dispersed transaction commits and rollbacks. When the query executor encounters a BEGIN, it will automatically execute SET AUTOCOMMIT&amp;amp&#59;&amp;&#35;35&#59;61&amp;&#35;59&#59;OFF for the connection.
To create fully-formed transactions with no orphan statements:
statement: insert | update | select ; commit_rollback: COMMIT | ROLLBACK ;
To create a variable-length list of items (e.g. fields, tables, etc.) use:
select_list: select_list , select_item | select_item; select_item: integer | _field;
The select_list rule will either expand to another select_list with a select_item appended, thus prolonging the list, or will expand to just select_item, thus terminating the list.
To test the performance of your grammar, you can run
$ perl runall.pl \ --basedir=/build/bzr/mysql-6.0 \ --grammar=conf/subquery_semijoin.conf \ --debug \ --threads=1
This will generate and execute 1000 queries and will then report:
- The number of rows returned by SELECT queries in your grammar. The following cause less or zero rows to be returned
- More complex and exclusive WHERE clauses - use more OR predicates vs. ANDs
- Small tables - do not use the A table (zero rows) but use tables E and F (1000 and 10000 rows).
- Microsoft report 50% of the queries with their tool return zero rows. We can expect an even higher percentage than that since our tool does not automatically match data types -- without carefully written grammar, you can have a query where a string is compared to a date.
- What optimizer notes were recored in the field EXTRA of EXPLAIN SELECT. Make sure all optimizer features you are interested in are shown in the output.
It is recommended to share code if possible because this can have significant advantages during grammar development and especially when using the grammar in production where
- - required maintenance
- - simplification of grammar for a given bad effect
- - resource consumption on testing box
- - SELECT and INSERT ... SELECT can share rules for the select part
- - SELECT, INSERT and DELETE can share rules for the where part
- - joins in SELECTs can be shared with multi table UPDATE and DELETE
- - the actions within PROCEDUREs, FUNCTIONs, TRIGGERS can borrow a lot from plain DML statement generation
Let's assume we have some test with the following architecture:
- - thread1 runs DDL and creates and/or drops objects like tables, views, triggers etc.
- - the other threads use these objects within DML statements
- - the code for the activity of thread1 is sufficient perfect (no generation of wrong syntax etc.)
- - RQG runs with this setting pass all time
thread1_init: SELECT /* RESULTSET_IS_SINGLE_INTEGER_ONE */ { $ENV{RQG_THREADS} } > 1 ; ''RQG output'' ''...'' ''# 2011-05-06T18:27:50 GenTest::ErrorFilter(23587) started'' ''# 2011-05-06T18:27:50 Query: SELECT /* RESULTSET_IS_SINGLE_INTEGER_ONE */ 1 > 1 does not have the declared properties: RESULTSET_IS_SINGLE_INTEGER_ONE'' ''# 2011-05-06T18:27:50 Started periodic reporting process...'' ''...'' ''# 2011-05-06T18:27:53 Child process completed with error code 33.'' ''...'' ''# 2011-05-06T18:27:54 gentest.pl exited with exit status STATUS_CONTENT_MISMATCH (33)'' ''...''
Solution 2 - cause a specific error message and abort the test immediate:
thread1_init: {if (not $ENV{RQG_THREADS} > 1) {print("!!! ERROR: The number of assigned threads must be > 1. Abort. !!! \n"); exit STATUS_ENVIRONMENT_FAILURE }; return undef}; ''RQG output'' ''...'' ''# 2011-05-06T18:48:57 GenTest::ErrorFilter(24621) started'' ''!!! ERROR: The number of assigned threads must be > 1. Abort. !!! '' ''# 2011-05-06T18:48:57 Started periodic reporting process...'' ''...' ''# 2011-05-06T18:48:58 Test completed with failure status STATUS_ENVIRONMENT_FAILURE (110)'' ''...''