mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 18:12:02 +00:00
153 lines
5.0 KiB
Perl
Executable File
153 lines
5.0 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
|
|
#
|
|
# Generate some random sql queries
|
|
#
|
|
|
|
package sqlfuzz;
|
|
use 5.16.0;
|
|
use strict;
|
|
use warnings;
|
|
no if $] >= 5.017011, warnings => 'experimental::smartmatch';
|
|
use Data::Dumper;
|
|
|
|
sub shuffle(@) { #@$deck = map{ splice @$deck, rand(@$deck), 1 } 0..$#$deck;
|
|
my $deck = shift;
|
|
$deck = [$deck, @_] unless ref $deck eq 'ARRAY';
|
|
my $i = @$deck;
|
|
while ($i--) {
|
|
my $j = int rand($i + 1);
|
|
@$deck[$i, $j] = @$deck[$j, $i];
|
|
}
|
|
return wantarray ? @$deck : $deck;
|
|
}
|
|
|
|
sub rand_pick ($) {
|
|
my ($arr) = @_;
|
|
return $arr->[rand(@$arr)];
|
|
}
|
|
|
|
sub rand_word {
|
|
my ($l) = shift || 8;
|
|
my $minl = 1;
|
|
@_ = ('a' .. 'z') unless @_;
|
|
join '', @_[map { rand @_ } $minl .. $minl + rand($l)];
|
|
}
|
|
|
|
sub rand_string {
|
|
my ($w) = shift || 5;
|
|
join ' ', map { rand_word((), @_) } 1 .. rand($w);
|
|
}
|
|
|
|
sub one ($$) {
|
|
my ($state, $value) = @_;
|
|
return ref $value ~~ 'CODE' ? $value->() : $value;
|
|
}
|
|
|
|
sub one_of ($$) {
|
|
my ($state, $hash) = @_;
|
|
#state $last_selected;
|
|
#my $last_n = $last_selected->{"$hash"};
|
|
my $value;
|
|
if ('ARRAY' ~~ ref $hash) {
|
|
$value = rand_pick $hash;
|
|
} else {
|
|
my $keys_array = [sort keys %$hash];
|
|
$value = $hash->{rand_pick $keys_array};
|
|
}
|
|
|
|
#$last_selected->{"$hash"} = !exists $last_selected->{"$hash"} ? 0 : $last_selected->{"$hash"} < @keys_array - 1 ? $last_selected->{"$hash"} + 1 : 0;
|
|
##warn join ' : ',$last_selected->{"$hash"}, $keys_array[$last_selected->{"$hash"}], $hash->{$keys_array[$last_selected->{"$hash"}]};
|
|
#my $value = $hash->{$keys_array[$last_selected->{"$hash"}]};
|
|
|
|
return one($state, $value);
|
|
}
|
|
|
|
sub list_of ($$@) {
|
|
my ($state, $opt) = (shift, shift);
|
|
my $max = ($opt->{min} // 1) + int rand($opt->{max} || 4);
|
|
my @ret;
|
|
while (@ret < $max) {
|
|
for my $hash (shuffle \@_) {
|
|
push @ret, one_of($state, $hash);
|
|
}
|
|
}
|
|
return join ', ', @ret;
|
|
}
|
|
|
|
sub file_read ($) {
|
|
open my $f, '<', $_[0] or return;
|
|
local $/ = undef;
|
|
my $ret = <$f>;
|
|
close $f;
|
|
return $ret;
|
|
}
|
|
|
|
our ($query, $query_select, $expression_cast, $expression, $type, $type_cast, $functions, $table_functions);
|
|
$type_cast = {map { $_ => $_ } qw(DateTime Date String)};
|
|
$type = {%$type_cast, (map { $_ => $_ } qw(Int8 Int16 Int32 Int64 UInt8 UInt16 UInt32 UInt64 Float32 Float64))};
|
|
$type->{"Nullable($_)"} = "Nullable($_)" for values %$type;
|
|
# AS, LIKE, NOT LIKE, IN, NOT IN, GLOBAL IN, GLOBAL NOT IN, BETWEEN, IS, ClosingRoundBracket, Comma, Dot, Arrow, QuestionMark, OR, AND
|
|
|
|
$expression_cast = {
|
|
'CAST' => sub { my ($state) = @_; '(CAST((' . one_of($state, $expression_cast) . ') AS ' . one_of($state, $type_cast) . '))' },
|
|
'SELECT' => sub {
|
|
my ($state) = @_;
|
|
list_of(
|
|
$state, {max => 2},
|
|
#[sub { '( ' . $query->{SELECT}->() . ' ) AS ' . rand_word() }, sub { '( ' . $query->{SELECT}->() . ' ) ' }]
|
|
[sub { '( ' . one_of($state, $query_select) . ' ) AS ' . rand_word() }, sub { '( ' . one_of($state, $query_select) . ' ) ' }]
|
|
);
|
|
},
|
|
'number' => sub { my ($state) = @_; return rand_pick(['', '-']) . rand_word(8, 0 .. 9) . rand_pick(['', '.' . rand_word(6, 0 .. 9)]) },
|
|
'string' => sub {
|
|
my ($state) = @_;
|
|
return q{'} . rand_word(8, map { $_ ~~ q{'} ? '\\' . $_ : $_ } map {chr} 32 .. 127) . q{'};
|
|
},
|
|
'[]' => '[]',
|
|
'[x]' => sub { my ($state) = @_; return '[' . one_of($state, $expression) . ']' },
|
|
'function()' =>
|
|
sub { my ($state) = @_; return one_of($state, $functions) . '(' . list_of($state, {min => 0, max => 3}, $expression) . ')' },
|
|
"'\\0'" => "'\\0'",
|
|
"''" => "''",
|
|
'NULL' => 'NULL',
|
|
};
|
|
$expression = {
|
|
%$expression_cast,
|
|
};
|
|
$query_select = {
|
|
'SELECT' => sub { my ($state) = @_; return 'SELECT ' . list_of($state, {max => 5}, $expression) },
|
|
'SELECT function()' => sub { my ($state) = @_; return 'SELECT ' . one($state, $expression->{'function()'}) },
|
|
'SELECT table_function()' => sub {
|
|
my ($state) = @_;
|
|
return 'SELECT * FROM ' . one_of($state, $table_functions) . '(' . list_of($state, {min => 0, max => 3}, $expression) . ')';
|
|
},
|
|
};
|
|
|
|
$query = {%$query_select};
|
|
|
|
sub main {
|
|
srand($ENV{SQL_FUZZY_SRAND} + $ENV{SQL_FUZZY_RUN}) if $ENV{SQL_FUZZY_SRAND};
|
|
# select name from system.functions format TSV;
|
|
$functions = [
|
|
split /[\s;,]+/,
|
|
$ENV{SQL_FUZZY_FUNCTIONS}
|
|
|| file_read($ENV{SQL_FUZZY_FILE_FUNCTIONS} || 'clickhouse-functions')
|
|
];
|
|
# $functions = [grep { not $_ ~~ [qw( )] } @$functions]; # will be removed
|
|
# select name from system.table_functions format TSV;
|
|
$table_functions = [
|
|
split /[\s;,]+/,
|
|
$ENV{SQL_FUZZY_TABLE_FUNCTIONS}
|
|
|| file_read($ENV{SQL_FUZZY_FILE_TABLE_FUNCTIONS} || 'clickhouse-table-functions')
|
|
];
|
|
$table_functions = [grep { not $_ ~~ [qw(numbers)] } @$table_functions]; # too slow
|
|
say one_of({}, $query), ';' for 1 .. ($ENV{SQL_FUZZY_LINES} || 100);
|
|
}
|
|
|
|
main() unless caller;
|
|
|
|
#say rand_word() for 1..10000;
|
|
#say rand_word(8, 0..9) for 1..1000;
|
|
|