1# Copyright (c) 2008, 2021, Oracle and/or its affiliates.
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License, version 2.0,
5# as published by the Free Software Foundation.
6#
7# This program is also distributed with certain software (including
8# but not limited to OpenSSL) that is licensed under separate terms,
9# as designated in a particular file or component or in included license
10# documentation.  The authors of MySQL hereby grant you an additional
11# permission to link the program and your derivative works with the
12# separately licensed software that they have included with MySQL.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License, version 2.0, for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
22
23################################################################################
24# outer_join.yy
25# Purpose:  Random Query Generator grammar for testing larger (6 - 10 tables) JOINs
26# Tuning:   Please tweak the rule table_ref ratio of table:join for larger joins
27#           NOTE:  be aware that larger (15-20 tables) queries can take far too
28#                  long to run to be of much interest for fast, automated testing
29#
30# Notes:    This grammar is designed to be used with gendata=conf/outer_join.zz
31#           It can be altered, but one will likely need field names
32#           Additionally, it is not recommended to use the standard RQG-produced
33#           tables as they way we pick tables can result in the use of
34#           several large tables that will bog down a generated query
35#
36#           Please rely largely on the _portable variant of this grammar if
37#           doing 3-way comparisons as it has altered code that will produce
38#           more standards-compliant queries for use with other DBMS's
39#
40#           We keep the grammar here as it is in order to also test certain
41#           MySQL-specific syntax variants.
42################################################################################
43
44query:
45   { @nonaggregates = (); @aggregates = (); $tables = 0; $ext = ""; $fields = 0; @outer_tables = (); $stack->push(); "" }
46   query_expr order_by_clause
47   { $stack->pop(undef) }
48 ;
49
50
51###############################################
52## Ref: ISO 9075, Chap 6.3 <table reference> ##
53###############################################
54################################################################################
55# recommend 4 tables : 1 joins for smaller queries, 3:1 for larger ones
56################################################################################
57table_ref:
58   table | table | table | table
59 | joined_table
60#| derived_table      # See note where 'derived_table:' is defined.
61 ;
62
63##
64# There are two problems with subqry as derived_tables which are the reason for not
65# allowing them in this grammar:
66#
67# 1. Name resolving of outer referrence columns seems to be broken.
68# 2. Create correct references to columns from the derived tables are complicated within RQG.
69#    It might be doable with logic similar to 'lookahead_for_table_alias' in order,
70#    but I want invest time in this right now, and probably never.
71#
72# Testcoverage of the different flavours of subqueries are assumed to be sufficient
73# throught scalar_subqry and IN/EXISTS
74##
75derived_table_unused:
76   { $stack->push(); undef }
77   table_subquery
78   {  my $x = " AS table".++$tables.$ext; my @s=($x); $stack->pop(\@s); $x }
79 ;
80
81
82##############################################
83## Ref: ISO 9075, Chap 6.x <value_expr_xxx> ##
84##############################################
85
86value_expr:
87   int_value_expr
88 | char_value_expr
89 ;
90
91int_value_expr:
92   _digit
93 | int_column
94#| int_aggregate
95 | int_value_expr + int_value_expr
96 | int_value_expr - int_value_expr
97 | CHAR_LENGTH(char_value_expr)
98 | (int_value_expr)
99#| int_scalar_subquery
100 ;
101
102char_value_expr:
103   char_column
104 | {"'".$prng->string(4)."'"}
105#| {"'"}_letter{"'"}
106#| char_aggregate
107#| UPPER(char_value_expr)
108#| LOWER(char_value_expr)
109#| TRIM(char_value_expr)
110 | (char_value_expr)
111#| char_scalar_subquery
112 ;
113
114char_string_literal:
115   {"'".$prng->string(4)."'"}
116 ;
117
118int_column:
119   table_alias.int_field_name
120 ;
121
122char_column:
123   table_alias.char_field_name
124 ;
125
126#######################################################
127## Ref: ISO 9075, Chap 7.2 <table value constructor> ##
128#######################################################
129table_value_constr:
130   VALUES table_value_list
131;
132
133table_value_list:
134   (1, 2, 3)
135 ;
136
137################################################
138## Ref: ISO 9075, Chap 7.3 <table expression> ##
139################################################
140table_expr:
141   from_clause where_clause group_by_clause having_clause
142 ;
143
144###########################################
145## Ref: ISO 9075, Chap 7.4 <from clause> ##
146###########################################
147from_clause:
148   FROM table_list
149   { scalar(@tables) == $table_refs or die "Table lookahead predicted ".scalar(@tables)." tables, found: ".$tables."\n"; undef }
150 ;
151
152####
153# <table referrence> has been inlined into <table list>, and decomposed
154#  in order to increase frequency of:
155#  - <table>, <table>, <table>...
156#  - single <joined tables>
157#  - single <table subquery>
158# Particularly we want to avoid to many <joined table>, <joined table> ....
159# being produced, they are allowed though.
160# Tweak the rule 'table_ref_list' to adjust this.
161####
162table_list:
163   table_ref_list
164#| table_only_list
165 ;
166
167outer_join_pattern_unused:
168   { $stack->push() }
169   table_ref { $stack->set("left",$stack->get("result")); }
170   outer_join_type JOIN table_ref join_spec
171   { my $left = $stack->get("left");  my $right = $stack->get("result"); my @n = (); push(@n,@$right); push(@n,@$left); $stack->pop(\@n); return undef }
172 ;
173
174table_only_list:
175   table
176 | table, table_only_list
177
178#  { $stack->push() }
179#  table, { $stack->set("left",$stack->get("result")); }
180#  table_only_list
181 ;
182
183table_ref_list:
184   table
185 | composite_table_ref
186 | composite_table_ref
187 | composite_table_ref
188 | composite_table_ref
189 | composite_table_ref
190#| composite_table_ref, table_list
191 ;
192
193composite_table_ref:
194   joined_table
195#| derived_table      # See note where 'derived_table:' is defined.
196 ;
197
198table:
199   # We use the "AS table" bit here so we can have unique aliases if we use the same table many times
200   { $stack->push(); my $x = $prng->arrayElement($executors->[0]->tables())." AS table".++$tables.$ext; my @s=($x); $stack->pop(\@s); $x }
201 ;
202
203lookahead_for_table_alias:
204   { @table_refs = (); my $tmp = join('', @sentence); my $lookahead = substr($tmp,index($tmp,$_)+length($_)); while ($lookahead =~ s/[(](?!SELECT)([^()]*)[)]/$1/g or $lookahead =~ s/[(]SELECT[^()]*[)]/<subqry>/g or $lookahead =~ s/(^[^(()]*)[)].*/$1/) {}; my @tab=(1..($lookahead =~ s/AS table//g)); foreach $id (@tab) { push (@table_refs,"table".$id.$ext) }; "" }
205 ;
206
207# NOTE can't make use of 'existing_table_item' as <table_expr> has not been produced yet
208table_alias:
209   # If there are 'outer tables': choose either inner or outer table_ref
210   { $prng->arrayElement(\@table_refs) }
211 | { scalar(@outer_tables) > 0 ? $prng->arrayElement(\@outer_tables) : $prng->arrayElement(\@table_refs) }
212 ;
213
214outer_table_unused:
215   { $prng->arrayElement(\@outer_tables) }
216 ;
217
218
219############################################
220## Ref: ISO 9075, Chap 7.5 <joined_table> ##
221############################################
222joined_table:
223   qualified_join
224 | qualified_join
225 | qualified_join
226 | qualified_join
227 | qualified_join
228 | qualified_join
229#| cross_join
230 | (joined_table)
231 ;
232
233qualified_join:
234   inner_join
235 | outer_join
236 ;
237
238cross_join:
239   table_ref CROSS JOIN table_ref
240   # Stupid MySQL extension where missing 'cross' join is determined based on no join_condition
241 | table_ref INNER JOIN table_ref
242 | table_ref JOIN table_ref
243 ;
244
245# MySQL has the (stupid) cross_join extension (above) where it actually is the presence of the join_cond which
246# determines if the join is a cross- or inner-join. This creates ambigous parser precedence rules which requires
247# us to enclose right side argument in paranthesis - Else the 'outer_join:' rule is more in accordance
248# with the ISO 9075 standards.
249inner_join:
250   { $stack->push() }
251   table_ref { $stack->set("left",$stack->get("result")); }
252   inner_join_type JOIN (table_ref) join_spec
253   { my $left = $stack->get("left");  my $right = $stack->get("result"); my @n = (); push(@n,@$right); push(@n,@$left); $stack->pop(\@n); return undef }
254 ;
255
256outer_join:
257   { $stack->push() }
258   table_ref { $stack->set("left",$stack->get("result")); }
259   outer_join_type JOIN table_ref join_spec
260   { my $left = $stack->get("left");  my $right = $stack->get("result"); my @n = (); push(@n,@$right); push(@n,@$left); $stack->pop(\@n); return undef }
261 ;
262
263join_spec:
264   ON join_condition
265#| USING (pk)         # Hard to handle, need to refer a avail common column from left / right
266 ;
267
268inner_join_type:
269   # Unspecified -> <join_type> implies 'INNER'
270 | INNER
271 ;
272
273outer_join_type:
274   LEFT outer
275 | RIGHT outer
276 ;
277
278outer:
279 | OUTER
280 ;
281
282# Prefer int_condition as char_condition will hardly ever match.
283join_condition:
284   int_condition
285 | int_condition
286 | int_condition
287 | int_condition
288 | int_condition
289 | int_condition
290 | char_condition
291 | other_condition
292 ;
293
294int_condition:
295   existing_left_table.int_field_name = existing_right_table.int_field_name     # General rule
296 | existing_left_table.int_indexed = existing_right_table.int_indexed           # Want more joins on indexed field
297 | int_multi_conditions
298 ;
299
300# Most of these join conditions crafted to match specific unique indexes
301int_multi_conditions:
302   # ix1(col_int,col_int_unique)
303   existing_left_table.col_int = existing_right_table.col_int AND
304   existing_left_table.col_int_unique = existing_right_table.col_int_unique
305 |
306   # ix2((col_int_key,col_int_unique))
307   existing_left_table.col_int_key = existing_right_table.col_int_key AND
308   existing_left_table.col_int_unique = existing_right_table.col_int_unique
309 |
310   # Variant of ix2() above
311   existing_left_table.col_int_key = existing_right_table.int_field_name AND
312   existing_left_table.col_int_unique = existing_right_table.int_field_name
313 |
314   # ix3(col_int,col_int_key,col_int_unique)
315   existing_left_table.col_int = existing_right_table.col_int AND
316   existing_left_table.col_int_key = existing_right_table.col_int_key AND
317   existing_left_table.col_int_unique = existing_right_table.col_int_unique
318#|
319#  int_condition AND int_condition
320 ;
321
322char_condition:
323   existing_left_table.char_field_name = existing_right_table.char_field_name   # General rule
324 | existing_left_table.char_indexed = existing_right_table.char_indexed         # Want more joins on indexed field
325 | char_multi_conditions
326 ;
327
328char_multi_conditions:
329   # ix1(col_char_16,col_char_16_unique)
330   existing_left_table.col_char_16 = existing_right_table.col_char_16 AND
331   existing_left_table.col_char_16_unique = existing_right_table.col_char_16_unique
332 |
333   # ix2(col_varchar_256,col_char_16_unique)
334   existing_left_table.col_varchar_256 = existing_right_table.col_varchar_256 AND
335   existing_left_table.col_varchar_10_unique = existing_right_table.col_varchar_10_unique
336#|
337#  char_condition AND char_condition
338 ;
339
340other_condition:
341   existing_left_table.col_int comparison_operator existing_right_table.col_int
342 | existing_left_table.col_int IS not NULL
343 | existing_right_table.col_int IS not NULL
344 | existing_left_table.col_int not IN (number_list)
345 | existing_right_table.col_int not IN (number_list)
346 | join_condition and_or join_condition
347 | not (join_condition) is_truth_value
348 ;
349
350existing_left_table:
351   { my $left = $stack->get("left"); my %s=map{$_=>1} @$left; my @r=(keys %s); my $table_string = $prng->arrayElement(\@r); my @table_array = split(/AS/, $table_string); $table_array[1] }
352 ;
353
354existing_right_table:
355   { my $right = $stack->get("result"); my %s=map{$_=>1} @$right; my @r=(keys %s); my $table_string = $prng->arrayElement(\@r); my @table_array = split(/AS/, $table_string); $table_array[1] }
356 ;
357
358
359############################################
360## Ref: ISO 9075, Chap 7.6 <where clause> ##
361############################################
362where_clause:
363   # <where_clause> is optional
364 | WHERE search_condition
365 ;
366
367################################################################################
368# We ensure that a GROUP BY statement includes all nonaggregates.              #
369# This helps to ensure the query is more useful in detecting real errors /     #
370# that the query doesn't lend itself to variable result sets                   #
371################################################################################
372###############################################
373## Ref: ISO 9075, Chap 7.7 <group by clause> ##
374###############################################
375group_by_clause:
376   group_by_iff_aggregate
377 | group_by_iff_aggregate
378 | group_by_iff_aggregate
379 | group_by_iff_aggregate
380 | group_by_iff_aggregate
381 | group_by_iff_aggregate
382 | group_by
383 ;
384
385## Conditionaly  specify 'GROUP BY' if table expression is a grouped table
386group_by_iff_aggregate:
387   { scalar(@aggregates) > 0 and scalar(@nonaggregates) > 0 ? " GROUP BY ".join (', ' , @nonaggregates ) : "" }
388 ;
389
390group_by:
391   { scalar(@nonaggregates) > 0 ? " GROUP BY ".join (', ' , @nonaggregates ) : "" }
392 ;
393
394#############################################
395## Ref: ISO 9075, Chap 7.8 <having clause> ##
396#############################################
397having_clause:
398   HAVING having_list
399 |||||
400 ;
401
402having_list:
403   having_item
404 | having_item
405 | (having_list and_or having_item)
406 ;
407
408having_item:
409   existing_select_item comparison_operator _digit
410 ;
411
412and_or:
413   AND
414 | AND
415 | OR
416 ;
417
418###################################################
419## Ref: ISO 9075, Chap 7.9 <query specification> ##
420###################################################
421query_spec:
422   SELECT lookahead_for_table_alias
423   distinct straight_join select_option select_list table_expr
424 ;
425
426distinct:  |||| DISTINCT ;
427
428select_option:
429 | SQL_SMALL_RESULT
430#| SQL_BIG_RESULT     # Need fix for Bug#53534
431 |||||||
432 ;
433
434straight_join:  ||||||||||| STRAIGHT_JOIN ;
435
436# Tuned to prefer short <select list>'s
437select_list:
438   select_item
439 | select_item, select_item
440 | select_item, select_item
441 | select_item, select_list
442 ;
443
444select_item:
445   int_select_item
446 | char_select_item
447 ;
448
449int_select_item:
450   int_value | int_value  | int_value | int_value | int_value
451#| int_aggregate     # Need fix for bug#53485
452 ;
453
454char_select_item:
455   char_value | char_value | char_value | char_value | char_value
456#| char_aggregate    # Need fix for bug#53485
457 ;
458
459int_value:
460   int_value_expr AS nonaggregate_field
461 | int_column AS nonaggregate_field
462 | int_column AS nonaggregate_field
463 | int_column AS nonaggregate_field
464 | int_column AS nonaggregate_field
465 | int_column AS nonaggregate_field
466 | int_column AS nonaggregate_field
467 | int_column AS nonaggregate_field
468 | int_column AS nonaggregate_field
469 | int_column AS nonaggregate_field
470 ;
471
472char_value:
473   char_value_expr AS nonaggregate_field
474 | char_column AS nonaggregate_field
475 | char_column AS nonaggregate_field
476 | char_column AS nonaggregate_field
477 | char_column AS nonaggregate_field
478 | char_column AS nonaggregate_field
479 | char_column AS nonaggregate_field
480 | char_column AS nonaggregate_field
481 | char_column AS nonaggregate_field
482 | char_column AS nonaggregate_field
483 ;
484
485int_aggregate:
486   COUNT(*) AS aggregate_field
487 | COUNT(distinct value_expr) AS aggregate_field
488 | SUM(distinct int_value_expr) AS aggregate_field
489 | MIN(distinct int_value_expr) AS aggregate_field
490 | MAX(distinct int_value_expr) AS aggregate_field
491 ;
492
493char_aggregate:
494   MIN(distinct char_value_expr) AS aggregate_field
495 | MAX(distinct char_value_expr) AS aggregate_field
496 ;
497
498nonaggregate_field:
499   { my $f = "field".++$fields; push @nonaggregates, $f ; $f }
500 ;
501
502aggregate_field:
503   { my $f = "field".++$fields; push @aggregates, $f ; $f }
504 ;
505
506#################################################
507## Ref: ISO 9075, Chap 7.10 <query expression> ##
508#################################################
509query_expr:
510   non_join_query_expr
511#| joined_table              # MySql unsupported
512 ;
513
514non_join_query_expr:
515   non_join_query_term
516#| query_expr UNION query_term
517#| query_expr EXCEPT query_term
518 ;
519
520query_term:
521   non_join_query_term
522#| joined_table              # MySql unsupported
523 ;
524
525non_join_query_term:
526   non_join_query_primary
527#| query_term INTERSECT query_primary
528 ;
529
530query_primary:
531   non_join_query_primary
532#| joined_table              # MySql unsupported
533 ;
534
535non_join_query_primary:
536   simple_table
537 | simple_table
538 | simple_table
539 | (non_join_query_expr)
540 ;
541
542simple_table:
543   query_spec
544#| table_value_constr        # MySql unsupported
545#| explicit_table            # MySql unsupported
546 ;
547
548explicit_table:
549   TABLE table
550 ;
551
552corresponding_spec:
553   CORRESPONDING
554 ;
555
556#########################################
557## Ref: ISO 9075, Chap 7.11 <subquery> ##
558#########################################
559
560int_scalar_subquery:
561   int_subquery_1row
562 ;
563
564char_scalar_subquery:
565   char_subquery_1row
566 ;
567
568int_table_subquery:
569   int_subquery_1row
570 ;
571
572char_table_subquery:
573   char_subquery_1row
574 ;
575
576int_subquery_1row:
577   subqry_enter
578   (SELECT lookahead_for_table_alias
579    distinct straight_join select_option int_select_item table_expr)
580   subqry_leave
581 ;
582
583char_subquery_1row:
584   subqry_enter
585   (SELECT lookahead_for_table_alias
586    distinct straight_join select_option char_select_item table_expr)
587   subqry_leave
588 ;
589
590table_subquery:
591   subquery
592 ;
593
594subquery:
595   subqry_enter
596   (query_expr)
597   subqry_leave
598 ;
599
600subqry_enter:
601   { $stack->push(); undef }
602   { $stack->set("tables",$tables); $tables = 0; $ext = $ext."s"; undef}
603   { my @tmp = @outer_tables; $stack->set("outer_tables",\@tmp); @outer_tables = @table_refs; undef}
604   { my @tmp = @table_refs; $stack->set("table_refs",\@tmp); @table_refs = (); undef}
605   { my @tmp = @nonaggregates; $stack->set("nonaggregates",\@tmp); @nonaggregates = (); undef}
606   { my @tmp = @aggregates; $stack->set("aggregates",\@tmp); @aggregates = (); undef}
607 ;
608
609subqry_leave:
610   { $tables = $stack->get("tables"); chop $ext; undef }
611   { @outer_tables = @{$stack->get("outer_tables")}; undef }
612   { @table_refs = @{$stack->get("table_refs")}; undef }
613   { @aggregates = @{$stack->get("aggregates")}; undef }
614   { @nonaggregates = @{$stack->get("nonaggregates")}; undef}
615   { $stack->pop(); undef}
616 ;
617
618correlate_iff_subquery_unused:
619   { scalar(@outer_tables) > 0 ? $prng->arrayElement(\@table_refs).".pk "."= ".$prng->arrayElement(\@outer_tables).".pk " : "" }
620 ;
621
622
623#################################################
624## Ref: ISO 9075, Chap 8.12 <search condition> ##
625#################################################
626search_condition:
627   boolean_factor
628 | boolean_factor
629 | boolean_factor
630 | boolean_factor
631 | boolean_factor
632 | search_condition and_or boolean_factor
633 ;
634
635boolean_factor:
636   not boolean_primary is_truth_value
637 ;
638
639is_truth_value:
640   IS truth_value
641   ||||||||||||||||
642 ;
643
644truth_value:
645   TRUE | FALSE | UNKNOWN
646 ;
647
648boolean_primary:
649   predicate
650 | predicate
651 | predicate
652 | predicate
653 | (search_condition)
654 ;
655
656predicate:
657   common_int_predicate
658 | common_int_predicate
659 | common_int_predicate
660 | common_int_predicate
661 | common_int_predicate
662 | common_char_predicate
663 | other_predicate
664 ;
665
666common_int_predicate:
667   int_column comparison_operator _digit
668 | int_column comparison_operator _digit
669 | int_column comparison_operator _digit
670 | int_column comparison_operator int_column
671 | int_column not BETWEEN _digit[invariant] AND _digit[invariant]+_digit
672 ;
673
674common_char_predicate:
675   char_column comparison_operator char_column
676 ;
677
678other_predicate:
679   int_value_expr comparison_operator int_value_expr
680 | char_value_expr comparison_operator char_value_expr
681 | value_expr IS not NULL
682 | int_value_expr not BETWEEN int_value_expr AND int_value_expr
683 | char_value_expr not LIKE {"'%".$prng->string(3)."%'"}
684 | EXISTS table_subquery
685 | int_value_expr not IN (number_list)
686 | int_value_expr not IN int_table_subquery
687 | char_value_expr not IN char_table_subquery
688 | int_value_expr comparison_operator quantifier int_table_subquery
689 | char_value_expr comparison_operator quantifier char_table_subquery
690#| UNIQUE table_subquery              # MySql unsupported
691 ;
692
693number_list:
694   _digit | number_list, _digit
695 ;
696
697quantifier:
698   ALL | ANY
699 ;
700
701################################################################################
702# We use the total_order_by rule when using the LIMIT operator to ensure that  #
703# we have a consistent result set - server1 and server2 should not differ      #
704################################################################################
705
706order_by_clause:
707   ORDER BY total_order_by desc limit
708 | ORDER BY order_by_list
709 |||||
710 ;
711
712total_order_by:
713   { my @tmp = (@nonaggregates, @aggregates); $prng->shuffleArray(\@tmp); join(',', @tmp) }
714 ;
715
716order_by_list:
717   order_by_item
718 | order_by_item, order_by_list
719 ;
720
721order_by_item:
722   existing_select_item desc
723 ;
724
725desc:
726   ASC
727 | DESC
728 |||||
729 ;
730
731################################################################################
732# We mix digit and _digit here.  We want to alter the possible values of LIMIT #
733# To ensure we hit varying EXPLAIN plans, but the OFFSET can be smaller        #
734################################################################################
735
736limit:
737   LIMIT limit_size
738 | LIMIT limit_size OFFSET _digit
739 |||
740 ;
741
742field_name:
743   int_field_name
744 | char_field_name
745 | datetime_field_name
746 ;
747
748int_field_name:
749   pk | col_int_key | col_int_unique | col_int
750 ;
751
752int_indexed:
753   pk | col_int_key | col_int_unique
754 ;
755
756#################################################################
757# Charset and collation should be specified when starting mysqld
758# or creating the database.
759# In order to get a deterministic resultset for 'ORDER BY ... LIMIT'
760# ensure that the collation is case sensitive (or binary).
761#################################################################
762char_field_name:
763   col_char_16
764 | col_char_16_key
765 | col_char_16_unique
766 | col_varchar_10
767 | col_varchar_10_key
768 | col_varchar_10_unique
769 | col_varchar_256
770 | col_varchar_256_key
771 | col_varchar_256_unique
772 ;
773
774char_indexed:
775   col_char_16_key
776 | col_char_16_key
777 | col_varchar_10_key
778 | col_varchar_10_unique
779 | col_varchar_256_key
780 | col_varchar_256_unique
781 ;
782
783datetime_field_name:
784   col_datetime
785 | col_datetime_key
786 | col_datetime_unique
787 ;
788
789datetime_indexed:
790   col_datetime_key
791 | col_datetime_unique
792 ;
793
794existing_table_item:
795   { "table".$prng->int(1,$tables) }
796 ;
797
798existing_select_item:
799   { my @tmp = (@nonaggregates, @aggregates); $prng->arrayElement(\@tmp) }
800 ;
801
802_digit:
803    1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
804    1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | _tinyint_unsigned
805 ;
806
807comparison_operator:
808   = | = | = | = | =
809 | > | < | != | <> | <= | >=
810 ;
811
812not:
813   NOT ||||||||||||
814;
815
816################################################################################
817# We define LIMIT_rows in this fashion as LIMIT values can differ depending on #
818# how large the LIMIT is - LIMIT 2 = LIMIT 9 != LIMIT 19                       #
819################################################################################
820
821limit_size:
822    1 | 2 | 10 | 100 | 1000;
823
824