5026 lines
		
	
	
		
			155 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
		
		
			
		
	
	
			5026 lines
		
	
	
		
			155 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
|  | #!/usr/bin/perl | |||
|  | # -*- perl -*- | |||
|  | # Copyright (c) 2000-2006 MySQL AB, 2009 Sun Microsystems, Inc. | |||
|  | # Use is subject to license terms. | |||
|  | # | |||
|  | # This library is free software; you can redistribute it and/or | |||
|  | # modify it under the terms of the GNU Library General Public | |||
|  | # License as published by the Free Software Foundation; version 2 | |||
|  | # of the License. | |||
|  | # | |||
|  | # This library is distributed in the hope that it will be useful, | |||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | |||
|  | # Library General Public License for more details. | |||
|  | # | |||
|  | # You should have received a copy of the GNU Library General Public | |||
|  | # License along with this library; if not, write to the Free | |||
|  | # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |||
|  | # MA 02110-1301, USA | |||
|  | 
 | |||
|  | # Written by Monty for the TCX/Monty Program/Detron benchmark suite. | |||
|  | # Empress and PostgreSQL patches by Luuk de Boer | |||
|  | # Extensions for ANSI SQL and Mimer by Bengt Gunne | |||
|  | # Some additions and corrections by Matthias Urlich | |||
|  | # | |||
|  | # This programs tries to find all limits for a sql server | |||
|  | # It gets the name from what it does to most servers :) | |||
|  | # | |||
|  | # Be sure to use --help before running this! | |||
|  | # | |||
|  | # If you want to add support for another server, add a new package for the | |||
|  | # server in server-cfg.  You only have to support the 'new' and 'version' | |||
|  | # functions. new doesn't need to have any limits if one doesn't want to | |||
|  | # use the benchmarks. | |||
|  | # | |||
|  | 
 | |||
|  | # TODO: | |||
|  | # CMT includes types and functions which are synonyms for other types | |||
|  | # and functions, including those in SQL9x. It should label those synonyms | |||
|  | # as such, and clarify ones such as "mediumint" with comments such as | |||
|  | # "3-byte int" or "same as xxx". | |||
|  | 
 | |||
|  | $version="1.61"; | |||
|  | 
 | |||
|  | use Cwd; | |||
|  | use DBI; | |||
|  | use Getopt::Long; | |||
|  | use POSIX; | |||
|  | $pwd = cwd(); $pwd = "." if ($pwd eq ''); | |||
|  | require "$pwd/server-cfg" || die "Can't read Configuration file: $!\n"; | |||
|  | 
 | |||
|  | $opt_server="mysql"; $opt_host="localhost"; $opt_database="test"; | |||
|  | $opt_dir="limits"; | |||
|  | $opt_user=$opt_password="";$opt_verbose=1; | |||
|  | $opt_debug=$opt_help=$opt_Information=$opt_restart=$opt_force=$opt_quick=0; | |||
|  | $opt_log_all_queries=$opt_fix_limit_file=$opt_batch_mode=$opt_version=0; | |||
|  | $opt_db_start_cmd="";           # the db server start command | |||
|  | $opt_check_server=0;		# Check if server is alive before each query | |||
|  | $opt_sleep=10;                  # time to sleep while starting the db server | |||
|  | $limit_changed=0;               # For configure file | |||
|  | $reconnect_count=0; | |||
|  | $opt_suffix=""; | |||
|  | $opt_comment=$opt_config_file=$opt_log_queries_to_file=""; | |||
|  | $limits{'crash_me_safe'}='yes'; | |||
|  | $prompts{'crash_me_safe'}='crash me safe'; | |||
|  | $limits{'operating_system'}= machine(); | |||
|  | $prompts{'operating_system'}='crash-me tested on'; | |||
|  | $retry_limit=3; | |||
|  | 
 | |||
|  | GetOptions("Information","help","server=s","debug","user=s","password=s", | |||
|  | "database=s","restart","force","quick","log-all-queries","comment=s", | |||
|  | "host=s","fix-limit-file","dir=s","db-start-cmd=s","sleep=s","suffix=s", | |||
|  | "batch-mode","config-file=s","log-queries-to-file=s","check-server", | |||
|  | "version", | |||
|  | "verbose!" => \$opt_verbose) || usage(); | |||
|  | usage() if ($opt_help || $opt_Information); | |||
|  | version() && exit(0) if ($opt_version); | |||
|  | 
 | |||
|  | $opt_suffix = '-'.$opt_suffix if (length($opt_suffix) != 0); | |||
|  | $opt_config_file = "$pwd/$opt_dir/$opt_server$opt_suffix.cfg" | |||
|  |   if (length($opt_config_file) == 0); | |||
|  | $log_prefix='   ###';  # prefix for log lines in result file | |||
|  | $safe_query_log=''; | |||
|  | $safe_query_result_log=''; | |||
|  | $log{"crash-me"}=""; | |||
|  | 
 | |||
|  | #!!! | |||
|  | 
 | |||
|  | if ($opt_fix_limit_file) | |||
|  | { | |||
|  |   print "Fixing limit file for $opt_server\n"; | |||
|  |   read_config_data(); | |||
|  |   $limit_changed=1; | |||
|  |   save_all_config_data(); | |||
|  |   exit 0; | |||
|  | } | |||
|  | 
 | |||
|  | $server=get_server($opt_server,$opt_host,$opt_database); | |||
|  | $opt_server=$server->{'cmp_name'}; | |||
|  | 
 | |||
|  | $|=1;                           # For debugging | |||
|  | 
 | |||
|  | print "Running $0 $version on '",($server_version=$server->version()),"'\n\n"; | |||
|  | print "I hope you didn't have anything important running on this server....\n"; | |||
|  | read_config_data(); | |||
|  | if ($limit_changed)             # Must have been restarted | |||
|  | { | |||
|  |   save_config_data('crash_me_safe','no',"crash me safe"); | |||
|  | } | |||
|  | 
 | |||
|  | if (!$opt_force && !$opt_batch_mode) | |||
|  | { | |||
|  |   server_info(); | |||
|  | } | |||
|  | else | |||
|  | { | |||
|  |   print "Using --force.  I assume you know what you are doing...\n"; | |||
|  | } | |||
|  | print "\n"; | |||
|  | 
 | |||
|  | save_config_data('crash_me_version',$version,"crash me version"); | |||
|  | if ($server_version) | |||
|  | { | |||
|  |   save_config_data('server_version',$server_version,"server version"); | |||
|  | } | |||
|  | if (length($opt_comment)) | |||
|  | { | |||
|  |   save_config_data('user_comment',$opt_comment,"comment"); | |||
|  | } | |||
|  | 
 | |||
|  | $opt_log=0; | |||
|  | if (length($opt_log_queries_to_file)) | |||
|  | { | |||
|  |   open(LOG,">$opt_log_queries_to_file") ||  | |||
|  |     die "Can't open file $opt_log_queries_to_file\n"; | |||
|  |   $opt_log=1; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Set up some limits that's regared as unlimited | |||
|  | # We don't want to take up all resources from the server... | |||
|  | # | |||
|  | 
 | |||
|  | $max_connections="+1000";       # Number of simultaneous connections | |||
|  | $max_buffer_size="+16000000";   # size of communication buffer. | |||
|  | $max_string_size="+8000000";    # Enough for this test | |||
|  | $max_name_length="+512";        # Actually 256, but ... | |||
|  | $max_keys="+64";                # Probably too big. | |||
|  | $max_join_tables="+64";         # Probably too big. | |||
|  | $max_columns="+8192";           # Probably too big. | |||
|  | $max_row_length=$max_string_size; | |||
|  | $max_key_length="+8192";        # Big enough | |||
|  | $max_order_by="+64";		# Big enough | |||
|  | $max_expressions="+10000"; | |||
|  | $max_big_expressions="+100"; | |||
|  | $max_stacked_expressions="+2000"; | |||
|  | $query_size=$max_buffer_size; | |||
|  | $longreadlen=16000000;		# For retrieval buffer | |||
|  | 
 | |||
|  | 
 | |||
|  | # | |||
|  | # First do some checks that needed for the rest of the benchmark | |||
|  | # | |||
|  | use sigtrap;		       # Must be removed with perl5.005_2 on Win98 | |||
|  | $SIG{PIPE} = 'IGNORE'; | |||
|  | $problem_counter=0; | |||
|  | $SIG{SEGV} = sub { | |||
|  |   $problem_counter +=1; | |||
|  |   if ($problem_counter >= 100) { | |||
|  |     die("Too many problems, try to restart"); | |||
|  |   } else { | |||
|  |     warn('SEGFAULT'); | |||
|  |   };     | |||
|  | }; | |||
|  | $dbh=safe_connect(); | |||
|  | 
 | |||
|  | # | |||
|  | # Test if the database require RESTRICT/CASCADE after DROP TABLE | |||
|  | # | |||
|  | 
 | |||
|  | # Really remove the crash_me table | |||
|  | $prompt="drop table require cascade/restrict"; | |||
|  | $drop_attr=""; | |||
|  | $dbh->do("drop table crash_me"); | |||
|  | $dbh->do("drop table crash_me cascade"); | |||
|  | if (!safe_query_l('drop_requires_cascade', | |||
|  |          ["create table crash_me (a integer not null)", | |||
|  | 		 "drop table crash_me"])) | |||
|  | { | |||
|  |   $dbh->do("drop table crash_me cascade");   | |||
|  |   if (safe_query_l('drop_requires_cascade', | |||
|  |         ["create table crash_me (a integer not null)", | |||
|  | 		  "drop table crash_me cascade"])) | |||
|  |   { | |||
|  |     save_config_data('drop_requires_cascade',"yes","$prompt"); | |||
|  |     $drop_attr="cascade"; | |||
|  |   } | |||
|  |   else | |||
|  |   { | |||
|  |     die "Can't create and drop table 'crash_me'\n"; | |||
|  |   } | |||
|  | } | |||
|  | else | |||
|  | { | |||
|  |   save_config_data('drop_requires_cascade',"no","$prompt"); | |||
|  |   $drop_attr=""; | |||
|  | } | |||
|  | 
 | |||
|  | # Remove tables from old runs | |||
|  | $dbh->do("drop table crash_me $drop_attr"); | |||
|  | $dbh->do("drop table crash_me2 $drop_attr"); | |||
|  | $dbh->do("drop table crash_me3 $drop_attr"); | |||
|  | $dbh->do("drop table crash_q $drop_attr"); | |||
|  | $dbh->do("drop table crash_q1 $drop_attr"); | |||
|  | 
 | |||
|  | $prompt="Tables without primary key"; | |||
|  | if (!safe_query_l('no_primary_key', | |||
|  |       ["create table crash_me (a integer not null,b char(10) not null)", | |||
|  | 		 "insert into crash_me (a,b) values (1,'a')"])) | |||
|  | { | |||
|  |   if (!safe_query_l('no_primary_key', | |||
|  |       ["create table crash_me (a integer not null,b char(10) not null". | |||
|  |         ", primary key (a))", | |||
|  | 	 "insert into crash_me (a,b) values (1,'a')"])) | |||
|  |   { | |||
|  |     die "Can't create table 'crash_me' with one record: $DBI::errstr\n"; | |||
|  |   } | |||
|  |   save_config_data('no_primary_key',"no",$prompt); | |||
|  | } | |||
|  | else | |||
|  | { | |||
|  |   save_config_data('no_primary_key',"yes",$prompt); | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | #  Define strings for character NULL and numeric NULL used in expressions | |||
|  | # | |||
|  | $char_null=$server->{'char_null'}; | |||
|  | $numeric_null=$server->{'numeric_null'}; | |||
|  | if ($char_null eq '') | |||
|  | { | |||
|  |   $char_null="NULL"; | |||
|  | } | |||
|  | if ($numeric_null eq '') | |||
|  | { | |||
|  |   $numeric_null="NULL"; | |||
|  | } | |||
|  | 
 | |||
|  | print "$prompt: $limits{'no_primary_key'}\n"; | |||
|  | 
 | |||
|  | report("SELECT without FROM",'select_without_from',"select 1"); | |||
|  | if ($limits{'select_without_from'} ne "yes") | |||
|  | { | |||
|  |   $end_query=" from crash_me"; | |||
|  |   $check_connect="select a from crash_me"; | |||
|  | } | |||
|  | else | |||
|  | { | |||
|  |   $end_query=""; | |||
|  |   $check_connect="select 1"; | |||
|  | } | |||
|  | 
 | |||
|  | assert($check_connect); | |||
|  | assert("select a from crash_me where b<'b'"); | |||
|  | 
 | |||
|  | report("Select constants",'select_constants',"select 1 $end_query"); | |||
|  | report("Select table_name.*",'table_wildcard', | |||
|  |        "select crash_me.* from crash_me"); | |||
|  | report("Allows \' and \" as string markers",'quote_with_"', | |||
|  |        'select a from crash_me where b<"c"'); | |||
|  | check_and_report("Double '' as ' in strings",'double_quotes',[], | |||
|  | 		 "select 'Walker''s' $end_query",[],"Walker's",1); | |||
|  | check_and_report("Multiple line strings","multi_strings",[], | |||
|  | 		 "select a from crash_me where b < 'a'\n'b'",[],"1",0); | |||
|  | check_and_report("\" as identifier quote (ANSI SQL)",'quote_ident_with_"',[], | |||
|  | 		 'select "A" from crash_me',[],"1",0); | |||
|  | check_and_report("\` as identifier quote",'quote_ident_with_`',[], | |||
|  | 		 'select `A` from crash_me',[],"1",0); | |||
|  | check_and_report("[] as identifier quote",'quote_ident_with_[',[], | |||
|  | 		 'select [A] from crash_me',[],"1",0); | |||
|  | report('Double "" in identifiers as "','quote_ident_with_dbl_"', | |||
|  |         'create table crash_me1 ("abc""d" integer)', | |||
|  | 	'drop table crash_me1');		  | |||
|  | 
 | |||
|  | report("Column alias","column_alias","select a as ab from crash_me"); | |||
|  | report("Table alias","table_alias","select b.a from crash_me as b"); | |||
|  | report("Functions",'functions',"select 1+1 $end_query"); | |||
|  | report("Group functions",'group_functions',"select count(*) from crash_me"); | |||
|  | report("Group functions with distinct",'group_distinct_functions', | |||
|  |        "select count(distinct a) from crash_me"); | |||
|  | report("Group functions with several distinct",'group_many_distinct_functions', | |||
|  |        "select count(distinct a), count(distinct b) from crash_me"); | |||
|  | report("Group by",'group_by',"select a from crash_me group by a"); | |||
|  | report("Group by position",'group_by_position', | |||
|  |        "select a from crash_me group by 1"); | |||
|  | report("Group by alias",'group_by_alias', | |||
|  |        "select a as ab from crash_me group by ab"); | |||
|  | report("Group on unused column",'group_on_unused', | |||
|  |        "select count(*) from crash_me group by a"); | |||
|  | 
 | |||
|  | report("Order by",'order_by',"select a from crash_me order by a"); | |||
|  | report("Order by position",'order_by_position', | |||
|  |        "select a from crash_me order by 1"); | |||
|  | report("Order by function","order_by_function", | |||
|  |        "select a from crash_me order by a+1"); | |||
|  | report("Order by on unused column",'order_on_unused', | |||
|  |        "select b from crash_me order by a"); | |||
|  | # little bit deprecated | |||
|  | #check_and_report("Order by DESC is remembered",'order_by_remember_desc', | |||
|  | #		 ["create table crash_q (s int,s1 int)", | |||
|  | #		  "insert into crash_q values(1,1)", | |||
|  | #		  "insert into crash_q values(3,1)", | |||
|  | #		  "insert into crash_q values(2,1)"], | |||
|  | #		 "select s,s1 from crash_q order by s1 DESC,s", | |||
|  | #		 ["drop table crash_q $drop_attr"],[3,2,1],7,undef(),3); | |||
|  | report("Compute",'compute', | |||
|  |        "select a from crash_me order by a compute sum(a) by a"); | |||
|  | report("INSERT with Value lists",'insert_multi_value', | |||
|  |        "create table crash_q (s char(10))", | |||
|  |        "insert into crash_q values ('a'),('b')", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | report("INSERT with set syntax",'insert_with_set', | |||
|  |        "create table crash_q (a integer)", | |||
|  |        "insert into crash_q SET a=1", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | report("INSERT with DEFAULT","insert_with_default", | |||
|  |        "create table crash_me_q (a int)", | |||
|  |        "insert into crash_me_q (a) values (DEFAULT)", | |||
|  |        "drop table crash_me_q $drop_attr"); | |||
|  | 
 | |||
|  | report("INSERT with empty value list","insert_with_empty_value_list", | |||
|  |        "create table crash_me_q (a int)", | |||
|  |        "insert into crash_me_q (a) values ()", | |||
|  |        "drop table crash_me_q $drop_attr"); | |||
|  | 
 | |||
|  | report("INSERT DEFAULT VALUES","insert_default_values", | |||
|  |        "create table crash_me_q (a int)", | |||
|  |        "insert into crash_me_q  DEFAULT VALUES", | |||
|  |        "drop table crash_me_q $drop_attr"); | |||
|  |         | |||
|  | report("allows end ';'","end_colon", "select * from crash_me;"); | |||
|  | try_and_report("LIMIT number of rows","select_limit", | |||
|  | 	       ["with LIMIT", | |||
|  | 		"select * from crash_me limit 1"], | |||
|  | 	       ["with TOP", | |||
|  | 		"select TOP 1 * from crash_me"]); | |||
|  | report("SELECT with LIMIT #,#","select_limit2",  | |||
|  |       "select * from crash_me limit 1,1"); | |||
|  | report("SELECT with LIMIT # OFFSET #", | |||
|  |       "select_limit3", "select * from crash_me limit 1 offset 1"); | |||
|  | 
 | |||
|  | # The following alter table commands MUST be kept together! | |||
|  | if ($dbh->do("create table crash_q (a integer, b integer,c1 CHAR(10))")) | |||
|  | { | |||
|  |   report("Alter table add column",'alter_add_col', | |||
|  | 	 "alter table crash_q add d integer"); | |||
|  |   report_one("Alter table add many columns",'alter_add_multi_col', | |||
|  | 	     [["alter table crash_q add (f integer,g integer)","yes"], | |||
|  | 	      ["alter table crash_q add f integer, add g integer","with add"], | |||
|  | 	      ["alter table crash_q add f integer,g integer","without add"]] ); | |||
|  |   report("Alter table change column",'alter_change_col', | |||
|  | 	 "alter table crash_q change a e char(50)"); | |||
|  | 
 | |||
|  |   # informix can only change data type with modify | |||
|  |   report_one("Alter table modify column",'alter_modify_col', | |||
|  | 	     [["alter table crash_q modify c1 CHAR(20)","yes"], | |||
|  | 	      ["alter table crash_q alter c1 CHAR(20)","with alter"]]); | |||
|  |   report("Alter table alter column default",'alter_alter_col', | |||
|  | 	 "alter table crash_q alter b set default 10"); | |||
|  |   report_one("Alter table drop column",'alter_drop_col', | |||
|  | 	     [["alter table crash_q drop column b","yes"], | |||
|  | 	      ["alter table crash_q drop column b restrict", | |||
|  | 	      "with restrict/cascade"]]); | |||
|  |   report("Alter table rename table",'alter_rename_table', | |||
|  | 	 "alter table crash_q rename to crash_q1"); | |||
|  | } | |||
|  | # Make sure both tables will be dropped, even if rename fails. | |||
|  | $dbh->do("drop table crash_q1 $drop_attr"); | |||
|  | $dbh->do("drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("rename table","rename_table", | |||
|  |        "create table crash_q (a integer, b integer,c1 CHAR(10))", | |||
|  |        "rename table crash_q to crash_q1", | |||
|  |        "drop table crash_q1 $drop_attr"); | |||
|  | # Make sure both tables will be dropped, even if rename fails. | |||
|  | $dbh->do("drop table crash_q1 $drop_attr"); | |||
|  | $dbh->do("drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("truncate","truncate_table", | |||
|  |        "create table crash_q (a integer, b integer,c1 CHAR(10))", | |||
|  |        "truncate table crash_q", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | if ($dbh->do("create table crash_q (a integer, b integer,c1 CHAR(10))") && | |||
|  |  $dbh->do("create table crash_q1 (a integer, b integer,c1 CHAR(10) not null)")) | |||
|  | { | |||
|  |   report("Alter table add constraint",'alter_add_constraint', | |||
|  | 	 "alter table crash_q add constraint c2 check(a > b)"); | |||
|  |   report_one("Alter table drop constraint",'alter_drop_constraint', | |||
|  | 	     [["alter table crash_q drop constraint c2","yes"], | |||
|  | 	      ["alter table crash_q drop constraint c2 restrict", | |||
|  | 	      "with restrict/cascade"]]); | |||
|  |   report("Alter table add unique",'alter_add_unique', | |||
|  | 	 "alter table crash_q add constraint u1 unique(c1)"); | |||
|  |   try_and_report("Alter table drop unique",'alter_drop_unique', | |||
|  | 		 ["with constraint", | |||
|  | 		  "alter table crash_q drop constraint u1"], | |||
|  | 		 ["with constraint and restrict/cascade", | |||
|  | 		  "alter table crash_q drop constraint u1 restrict"], | |||
|  | 		 ["with drop key", | |||
|  | 		  "alter table crash_q drop key u1"]); | |||
|  |   try_and_report("Alter table add primary key",'alter_add_primary_key', | |||
|  | 		 ["with constraint", | |||
|  | 		  "alter table crash_q1 add constraint p1 primary key(c1)"], | |||
|  | 		 ["with add primary key", | |||
|  | 		  "alter table crash_q1 add primary key(c1)"]); | |||
|  |   report("Alter table add foreign key",'alter_add_foreign_key', | |||
|  | 	 "alter table crash_q add constraint f1 foreign key(c1) references crash_q1(c1)"); | |||
|  |   try_and_report("Alter table drop foreign key",'alter_drop_foreign_key', | |||
|  | 		 ["with drop constraint", | |||
|  | 		  "alter table crash_q drop constraint f1"], | |||
|  | 		 ["with drop constraint and restrict/cascade", | |||
|  | 		  "alter table crash_q drop constraint f1 restrict"], | |||
|  | 		 ["with drop foreign key", | |||
|  | 		  "alter table crash_q drop foreign key f1"]); | |||
|  |   try_and_report("Alter table drop primary key",'alter_drop_primary_key', | |||
|  | 		 ["drop constraint", | |||
|  | 		  "alter table crash_q1 drop constraint p1 restrict"], | |||
|  | 		 ["drop primary key", | |||
|  | 		  "alter table crash_q1 drop primary key"]); | |||
|  | } | |||
|  | $dbh->do("drop table crash_q $drop_attr"); | |||
|  | $dbh->do("drop table crash_q1 $drop_attr"); | |||
|  | 
 | |||
|  | check_and_report("Case insensitive compare","case_insensitive_strings", | |||
|  | 		 [],"select b from crash_me where b = 'A'",[],'a',1); | |||
|  | check_and_report("Ignore end space in compare","ignore_end_space", | |||
|  | 		 [],"select b from crash_me where b = 'a '",[],'a',1); | |||
|  | check_and_report("Group on column with null values",'group_by_null', | |||
|  | 		 ["create table crash_q (s char(10))", | |||
|  | 		  "insert into crash_q values(null)", | |||
|  | 		  "insert into crash_q values(null)"], | |||
|  | 		 "select count(*),s from crash_q group by s", | |||
|  | 		 ["drop table crash_q $drop_attr"],2,0); | |||
|  | 
 | |||
|  | $prompt="Having"; | |||
|  | if (!defined($limits{'having'})) | |||
|  | {                               # Complicated because of postgreSQL | |||
|  |   if (!safe_query_result_l("having", | |||
|  |       "select a from crash_me group by a having a > 0",1,0)) | |||
|  |   { | |||
|  |     if (!safe_query_result_l("having", | |||
|  |            "select a from crash_me group by a having a < 0", | |||
|  | 	    1,0)) | |||
|  |     { save_config_data("having","error",$prompt); } | |||
|  |     else | |||
|  |     { save_config_data("having","yes",$prompt); } | |||
|  |   } | |||
|  |   else | |||
|  |   { save_config_data("having","no",$prompt); } | |||
|  | } | |||
|  | print "$prompt: $limits{'having'}\n"; | |||
|  | 
 | |||
|  | if ($limits{'having'} eq 'yes') | |||
|  | { | |||
|  |   report("Having with group function","having_with_group", | |||
|  | 	 "select a from crash_me group by a having count(*) = 1"); | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'column_alias'} eq 'yes') | |||
|  | { | |||
|  |   report("Order by alias",'order_by_alias', | |||
|  | 	 "select a as ab from crash_me order by ab"); | |||
|  |   if ($limits{'having'} eq 'yes') | |||
|  |   { | |||
|  |     report("Having on alias","having_with_alias", | |||
|  | 	   "select a as ab from crash_me group by a having ab > 0"); | |||
|  |   } | |||
|  | } | |||
|  | report("binary numbers (0b1001)","binary_numbers","select 0b1001 $end_query"); | |||
|  | report("hex numbers (0x41)","hex_numbers","select 0x41 $end_query"); | |||
|  | report("binary strings (b'0110')","binary_strings","select b'0110' $end_query"); | |||
|  | report("hex strings (x'1ace')","hex_strings","select x'1ace' $end_query"); | |||
|  | 
 | |||
|  | report_result("Value of logical operation (1=1)","logical_value", | |||
|  | 	      "select (1=1) $end_query"); | |||
|  | 
 | |||
|  | report_result("Value of TRUE","value_of_true","select TRUE $end_query"); | |||
|  | report_result("Value of FALSE","value_of_false","select FALSE $end_query"); | |||
|  | 
 | |||
|  | $logical_value= $limits{'logical_value'}; | |||
|  | 
 | |||
|  | $false=0; | |||
|  | $result="no"; | |||
|  | if ($res=safe_query_l('has_true_false',"select (1=1)=true $end_query")) { | |||
|  |   $false="false"; | |||
|  |   $result="yes"; | |||
|  | } | |||
|  | save_config_data('has_true_false',$result,"TRUE and FALSE"); | |||
|  | 
 | |||
|  | # | |||
|  | # Check how many connections the server can handle: | |||
|  | # We can't test unlimited connections, because this may take down the | |||
|  | # server... | |||
|  | # | |||
|  | 
 | |||
|  | $prompt="Simultaneous connections (installation default)"; | |||
|  | print "$prompt: "; | |||
|  | if (defined($limits{'connections'})) | |||
|  | { | |||
|  |   print "$limits{'connections'}\n"; | |||
|  | } | |||
|  | else | |||
|  | { | |||
|  |   @connect=($dbh); | |||
|  | 
 | |||
|  |   for ($i=1; $i < $max_connections ; $i++) | |||
|  |   { | |||
|  |     if (!($dbh=DBI->connect($server->{'data_source'},$opt_user,$opt_password, | |||
|  | 			  { PrintError => 0}))) | |||
|  |     { | |||
|  |       print "Last connect error: $DBI::errstr\n" if ($opt_debug); | |||
|  |       last; | |||
|  |     } | |||
|  |     $dbh->{LongReadLen}= $longreadlen; # Set retrieval buffer | |||
|  |     print "." if ($opt_debug); | |||
|  |     push(@connect,$dbh); | |||
|  |   } | |||
|  |   print "$i\n"; | |||
|  |   save_config_data('connections',$i,$prompt); | |||
|  |   foreach $dbh (@connect) | |||
|  |   { | |||
|  |     print "#" if ($opt_debug); | |||
|  |     $dbh->disconnect || warn $dbh->errstr;           # close connection | |||
|  |   } | |||
|  | 
 | |||
|  |   $#connect=-1;                 # Free connections | |||
|  | 
 | |||
|  |   if ($i == 0) | |||
|  |   { | |||
|  |     print "Can't connect to server: $DBI::errstr.". | |||
|  |           "  Please start it and try again\n"; | |||
|  |     exit 1; | |||
|  |   } | |||
|  |   $dbh=retry_connect(); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # | |||
|  | # Check size of communication buffer, strings... | |||
|  | # | |||
|  | 
 | |||
|  | $prompt="query size"; | |||
|  | print "$prompt: "; | |||
|  | if (!defined($limits{'query_size'})) | |||
|  | { | |||
|  |   $query="select "; | |||
|  |   $first=64; | |||
|  |   $end=$max_buffer_size; | |||
|  |   $select= $limits{'select_without_from'} eq 'yes' ? 1 : 'a'; | |||
|  | 
 | |||
|  |   assert($query . "$select$end_query"); | |||
|  | 
 | |||
|  |   $first=$limits{'restart'}{'low'} if ($limits{'restart'}{'low'}); | |||
|  | 
 | |||
|  |   if ($limits{'restart'}{'tohigh'}) | |||
|  |   { | |||
|  |     $end = $limits{'restart'}{'tohigh'} - 1; | |||
|  |     print "\nRestarting this with low limit: $first and high limit: $end\n"; | |||
|  |     delete $limits{'restart'}; | |||
|  |     $first=$first+int(($end-$first+4)/5);           # Prefere lower on errors | |||
|  |   } | |||
|  |   for ($i=$first ; $i < $end ; $i*=2) | |||
|  |   { | |||
|  |     last if (!safe_query($query .  | |||
|  |             (" " x ($i - length($query)-length($end_query) -1))  | |||
|  | 	      . "$select$end_query")); | |||
|  |     $first=$i; | |||
|  |     save_config_data("restart",$i,"") if ($opt_restart); | |||
|  |   } | |||
|  |   $end=$i; | |||
|  | 
 | |||
|  |   if ($i < $max_buffer_size) | |||
|  |   { | |||
|  |     while ($first != $end) | |||
|  |     { | |||
|  |       $i=int(($first+$end+1)/2); | |||
|  |       if (safe_query($query . | |||
|  | 		     (" " x ($i - length($query)-length($end_query) -1)) . | |||
|  | 		     "$select$end_query")) | |||
|  |       { | |||
|  | 	$first=$i; | |||
|  |       } | |||
|  |       else | |||
|  |       { | |||
|  | 	$end=$i-1; | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  |   save_config_data('query_size',$end,$prompt); | |||
|  | } | |||
|  | $query_size=$limits{'query_size'}; | |||
|  | 
 | |||
|  | print "$limits{'query_size'}\n"; | |||
|  | 
 | |||
|  | # | |||
|  | # Check for reserved words | |||
|  | # | |||
|  | 
 | |||
|  | check_reserved_words($dbh); | |||
|  | 
 | |||
|  | # | |||
|  | # Test database types | |||
|  | # | |||
|  | 
 | |||
|  | @sql_types=("character(1)","char(1)","char varying(1)", "character varying(1)", | |||
|  | 	    "boolean", | |||
|  | 	    "varchar(1)", | |||
|  | 	    "integer","int","smallint", | |||
|  | 	    "numeric(9,2)","decimal(6,2)","dec(6,2)", | |||
|  | 	    "bit", "bit(2)","bit varying(2)","float","float(8)","real", | |||
|  | 	    "double precision", "date","time","timestamp", | |||
|  | 	    "interval year", "interval year to month", | |||
|  |             "interval month", | |||
|  |             "interval day", "interval day to hour", "interval day to minute", | |||
|  |             "interval day to second", | |||
|  |             "interval hour", "interval hour to minute", | |||
|  | 	    "interval hour to second", | |||
|  |             "interval minute", "interval minute to second", | |||
|  |             "interval second", | |||
|  | 	    "national character varying(20)", | |||
|  | 	    "national character(20)","nchar(1)", | |||
|  | 	    "national char varying(20)","nchar varying(20)", | |||
|  | 	    "national character varying(20)", | |||
|  | 	    "timestamp with time zone"); | |||
|  | @odbc_types=("binary(1)","varbinary(1)","tinyint","bigint", | |||
|  | 	     "datetime"); | |||
|  | @extra_types=("blob","byte","long varbinary","image","text","text(10)", | |||
|  | 	      "mediumtext", | |||
|  | 	      "long varchar(1)", "varchar2(257)", | |||
|  | 	      "mediumint","middleint","int unsigned", | |||
|  | 	      "int1","int2","int3","int4","int8","uint", | |||
|  | 	      "money","smallmoney","float4","float8","smallfloat", | |||
|  | 	      "float(6,2)","double", | |||
|  | 	      "enum('red')","set('red')", "int(5) zerofill", "serial", | |||
|  | 	      "char(10) binary","int not null auto_increment,unique(q)", | |||
|  | 	      "abstime","year","datetime","smalldatetime","timespan","reltime", | |||
|  | 	      # Sybase types | |||
|  | 	      "int not null identity,unique(q)", | |||
|  | 	      # postgres types | |||
|  | 	      "box","bool","circle","polygon","point","line","lseg","path", | |||
|  | 	      "interval", "inet", "cidr", "macaddr", | |||
|  | 
 | |||
|  | 	      # oracle types | |||
|  | 	      "varchar2(16)","nvarchar2(16)","number(9,2)","number(9)", | |||
|  | 	      "number", "long","raw(16)","long raw","rowid","mlslabel","clob", | |||
|  | 	      "nclob","bfile" | |||
|  | 	      ); | |||
|  | 
 | |||
|  | @types=(["sql",\@sql_types], | |||
|  | 	["odbc",\@odbc_types], | |||
|  | 	["extra",\@extra_types]); | |||
|  | 
 | |||
|  | foreach $types (@types) | |||
|  | { | |||
|  |   print "\nSupported $types->[0] types\n"; | |||
|  |   $tmp=@$types->[1]; | |||
|  |   foreach $use_type (@$tmp) | |||
|  |   { | |||
|  |     $type=$use_type; | |||
|  |     $type =~ s/\(.*\)/(1 arg)/; | |||
|  |     if (index($use_type,",")>= 0) | |||
|  |     { | |||
|  |       $type =~ s/\(1 arg\)/(2 arg)/; | |||
|  |     } | |||
|  |     if (($tmp2=index($type,",unique")) >= 0) | |||
|  |     { | |||
|  |       $type=substr($type,0,$tmp2); | |||
|  |     } | |||
|  |     $tmp2=$type; | |||
|  |     $tmp2 =~ s/ /_/g; | |||
|  |     $tmp2 =~ s/_not_null//g; | |||
|  |     report("Type $type","type_$types->[0]_$tmp2", | |||
|  | 	   "create table crash_q (q $use_type)", | |||
|  | 	   "drop table crash_q $drop_attr"); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Test some type limits | |||
|  | # | |||
|  | 
 | |||
|  | 
 | |||
|  | check_and_report("Remembers end space in char()","remember_end_space", | |||
|  | 		 ["create table crash_q (a char(10))", | |||
|  | 		  "insert into crash_q values('hello ')"], | |||
|  | 		 "select a from crash_q where a = 'hello '", | |||
|  | 		 ["drop table crash_q $drop_attr"], | |||
|  | 		 'hello ',6); | |||
|  | 
 | |||
|  | check_and_report("Remembers end space in varchar()", | |||
|  | 		 "remember_end_space_varchar", | |||
|  | 		 ["create table crash_q (a varchar(10))", | |||
|  | 		  "insert into crash_q values('hello ')"], | |||
|  | 		 "select a from crash_q where a = 'hello '", | |||
|  | 		 ["drop table crash_q $drop_attr"], | |||
|  | 		 'hello ',6); | |||
|  | 
 | |||
|  | if (($limits{'type_extra_float(2_arg)'} eq "yes" || | |||
|  |     $limits{'type_sql_decimal(2_arg)'} eq "yes") && | |||
|  |     (!defined($limits{'storage_of_float'}))) | |||
|  | { | |||
|  |   my $type=$limits{'type_extra_float(2_arg)'} eq "yes" ? "float(4,1)" : | |||
|  |     "decimal(4,1)"; | |||
|  |   my $result="undefined"; | |||
|  |   if (execute_and_check("storage_of_float",["create table crash_q (q1 $type)", | |||
|  | 			 "insert into crash_q values(1.14)"], | |||
|  | 			"select q1 from crash_q", | |||
|  | 			["drop table crash_q $drop_attr"],1.1,0) && | |||
|  |       execute_and_check("storage_of_float",["create table crash_q (q1 $type)", | |||
|  | 			 "insert into crash_q values(1.16)"], | |||
|  | 			"select q1 from crash_q", | |||
|  | 			["drop table crash_q $drop_attr"],1.1,0)) | |||
|  |   { | |||
|  |     $result="truncate"; | |||
|  |   } | |||
|  |   elsif (execute_and_check("storage_of_float",["create table crash_q (q1 $type)", | |||
|  | 			    "insert into crash_q values(1.14)"], | |||
|  | 			   "select q1 from crash_q", | |||
|  | 			   ["drop table crash_q $drop_attr"],1.1,0) && | |||
|  | 	 execute_and_check("storage_of_float",["create table crash_q (q1 $type)", | |||
|  | 			    "insert into crash_q values(1.16)"], | |||
|  | 			   "select q1 from crash_q", | |||
|  | 			   ["drop table crash_q $drop_attr"],1.2,0)) | |||
|  |   { | |||
|  |     $result="round"; | |||
|  |   } | |||
|  |   elsif (execute_and_check("storage_of_float",["create table crash_q (q1 $type)", | |||
|  | 			    "insert into crash_q values(1.14)"], | |||
|  | 			   "select q1 from crash_q", | |||
|  | 			   ["drop table crash_q $drop_attr"],1.14,0) && | |||
|  | 	 execute_and_check("storage_of_float",["create table crash_q (q1 $type)", | |||
|  | 			    "insert into crash_q values(1.16)"], | |||
|  | 			   "select q1 from crash_q", | |||
|  | 			   ["drop table crash_q $drop_attr"],1.16,0)) | |||
|  |   { | |||
|  |     $result="exact"; | |||
|  |   } | |||
|  |   $prompt="Storage of float values"; | |||
|  |   print "$prompt: $result\n"; | |||
|  |   save_config_data("storage_of_float", $result, $prompt); | |||
|  | } | |||
|  | 
 | |||
|  | try_and_report("Type for row id", "rowid", | |||
|  | 	       ["rowid", | |||
|  | 		"create table crash_q (a rowid)", | |||
|  | 		"drop table crash_q $drop_attr"], | |||
|  | 	       ["auto_increment", | |||
|  | 		"create table crash_q (a int not null auto_increment". | |||
|  | 		", primary key(a))","drop table crash_q $drop_attr"], | |||
|  | 	       ["oid", | |||
|  | 		"create table crash_q (a oid, primary key(a))", | |||
|  | 		"drop table crash_q $drop_attr"], | |||
|  | 	       ["serial", | |||
|  | 		"create table crash_q (a serial, primary key(a))", | |||
|  | 		"drop table crash_q $drop_attr"]); | |||
|  | 
 | |||
|  | try_and_report("Automatic row id", "automatic_rowid", | |||
|  | 	       ["_rowid", | |||
|  | 		"create table crash_q (a int not null, primary key(a))", | |||
|  | 		"insert into crash_q values (1)", | |||
|  | 		"select _rowid from crash_q", | |||
|  | 		"drop table crash_q $drop_attr"]); | |||
|  | 
 | |||
|  | # | |||
|  | # Test functions | |||
|  | # | |||
|  | 
 | |||
|  | @sql_functions= | |||
|  |   (["+, -, * and /","+","5*3-4/2+1",14,0], | |||
|  |    ["ANSI SQL SUBSTRING","substring","substring('abcd' from 2 for 2)","bc",1], | |||
|  |    ["BIT_LENGTH","bit_length","bit_length('abc')",24,0], | |||
|  |    ["searched CASE","searched_case", | |||
|  |      "case when 1 > 2 then 'false' when 2 > 1 then 'true' end", "true",1], | |||
|  |    ["simple CASE","simple_case", | |||
|  |      "case 2 when 1 then 'false' when 2 then 'true' end", "true",1], | |||
|  |    ["CAST","cast","CAST(1 as CHAR)","1",1], | |||
|  |    ["CHARACTER_LENGTH","character_length","character_length('abcd')","4",0], | |||
|  |    ["CHAR_LENGTH","char_length","char_length(b)","10",0], | |||
|  |    ["CHAR_LENGTH(constant)","char_length(constant)", | |||
|  |      "char_length('abcd')","4",0], | |||
|  |    ["COALESCE","coalesce","coalesce($char_null,'bcd','qwe')","bcd",1], | |||
|  |    ["CURRENT_DATE","current_date","current_date",0,2], | |||
|  |    ["CURRENT_TIME","current_time","current_time",0,2], | |||
|  |    ["CURRENT_TIMESTAMP","current_timestamp","current_timestamp",0,2], | |||
|  |    ["EXTRACT","extract_sql", | |||
|  |      "extract(minute from timestamp '2000-02-23 18:43:12.987')",43,0], | |||
|  |    ["LOCALTIME","localtime","localtime",0,2], | |||
|  |    ["LOCALTIMESTAMP","localtimestamp","localtimestamp",0,2], | |||
|  |    ["LOWER","lower","LOWER('ABC')","abc",1], | |||
|  |    ["NULLIF with strings","nullif_string", | |||
|  |        "NULLIF(NULLIF('first','second'),'first')",undef(),4], | |||
|  |    ["NULLIF with numbers","nullif_num","NULLIF(NULLIF(1,2),1)",undef(),4], | |||
|  |    ["OCTET_LENGTH","octet_length","octet_length('abc')",3,0], | |||
|  |    ["POSITION","position","position('ll' in 'hello')",3,0], | |||
|  |    ["TRIM","trim","trim(trailing from trim(LEADING FROM ' abc '))","abc",3], | |||
|  |    ["UPPER","upper","UPPER('abc')","ABC",1], | |||
|  |    ["concatenation with ||","concat_as_||","'abc' || 'def'","abcdef",1], | |||
|  |    ); | |||
|  | 
 | |||
|  | @odbc_functions= | |||
|  |   (["ASCII", "ascii", "ASCII('A')","65",0], | |||
|  |    ["CHAR", "char", "CHAR(65)"  ,"A",1], | |||
|  |    ["CONCAT(2 arg)","concat", "concat('a','b')","ab",1], | |||
|  |    ["DIFFERENCE()","difference","difference('abc','abe')",3,0], | |||
|  |    ["INSERT","insert","insert('abcd',2,2,'ef')","aefd",1], | |||
|  |    ["LEFT","left","left('abcd',2)","ab",1], | |||
|  |    ["LTRIM","ltrim","ltrim('   abcd')","abcd",1], | |||
|  |    ["REAL LENGTH","length","length('abcd ')","5",0], | |||
|  |    ["ODBC LENGTH","length_without_space","length('abcd ')","4",0], | |||
|  |    ["LOCATE(2 arg)","locate_2","locate('bcd','abcd')","2",0], | |||
|  |    ["LOCATE(3 arg)","locate_3","locate('bcd','abcd',3)","0",0], | |||
|  |    ["LCASE","lcase","lcase('ABC')","abc",1], | |||
|  |    ["REPEAT","repeat","repeat('ab',3)","ababab",1], | |||
|  |    ["REPLACE","replace","replace('abbaab','ab','ba')","bababa",1], | |||
|  |    ["RIGHT","right","right('abcd',2)","cd",1], | |||
|  |    ["RTRIM","rtrim","rtrim(' abcd  ')"," abcd",1], | |||
|  |    ["SPACE","space","space(5)","     ",3], | |||
|  |    ["SOUNDEX","soundex","soundex('hello')",0,2], | |||
|  |    ["ODBC SUBSTRING","substring","substring('abcd',3,2)","cd",1], | |||
|  |    ["UCASE","ucase","ucase('abc')","ABC",1], | |||
|  | 
 | |||
|  |    ["ABS","abs","abs(-5)",5,0], | |||
|  |    ["ACOS","acos","acos(0)","1.570796",0], | |||
|  |    ["ASIN","asin","asin(1)","1.570796",0], | |||
|  |    ["ATAN","atan","atan(1)","0.785398",0], | |||
|  |    ["ATAN2","atan2","atan2(1,0)","1.570796",0], | |||
|  |    ["CEILING","ceiling","ceiling(-4.5)",-4,0], | |||
|  |    ["COS","cos","cos(0)","1.00000",0], | |||
|  |    ["COT","cot","cot(1)","0.64209262",0], | |||
|  |    ["DEGREES","degrees","degrees(6.283185)","360",0], | |||
|  |    ["EXP","exp","exp(1.0)","2.718282",0], | |||
|  |    ["FLOOR","floor","floor(2.5)","2",0], | |||
|  |    ["LOG","log","log(2)","0.693147",0], | |||
|  |    ["LOG10","log10","log10(10)","1",0], | |||
|  |    ["MOD","mod","mod(11,7)","4",0], | |||
|  |    ["PI","pi","pi()","3.141593",0], | |||
|  |    ["POWER","power","power(2,4)","16",0], | |||
|  |    ["RAND","rand","rand(1)",0,2],       # Any value is acceptable | |||
|  |    ["RADIANS","radians","radians(360)","6.283185",0], | |||
|  |    ["ROUND(2 arg)","round","round(5.63,2)","5.6",0], | |||
|  |    ["SIGN","sign","sign(-5)",-1,0], | |||
|  |    ["SIN","sin","sin(1)","0.841471",0], | |||
|  |    ["SQRT","sqrt","sqrt(4)",2,0], | |||
|  |    ["TAN","tan","tan(1)","1.557408",0], | |||
|  |    ["TRUNCATE","truncate","truncate(18.18,-1)",10,0], | |||
|  |    ["NOW","now","now()",0,2],           # Any value is acceptable | |||
|  |    ["CURDATE","curdate","curdate()",0,2], | |||
|  |    ["CURTIME","curtime","curtime()",0,2], | |||
|  |    ["TIMESTAMPADD","timestampadd", | |||
|  |     "timestampadd(SQL_TSI_SECOND,1,'1997-01-01 00:00:00')", | |||
|  |     "1997-01-01 00:00:01",1], | |||
|  |    ["TIMESTAMPDIFF","timestampdiff", | |||
|  |     "timestampdiff(SQL_TSI_SECOND,'1997-01-01 00:00:02',". | |||
|  |      " '1997-01-01 00:00:01')","1",0], | |||
|  |    ["USER()","user()","user()",0,2], | |||
|  |    ["DATABASE","database","database()",0,2], | |||
|  |    ["IFNULL","ifnull","ifnull(2,3)",2,0], | |||
|  |    ["ODBC syntax LEFT & RIGHT", "fn_left", | |||
|  |     "{ fn LEFT( { fn RIGHT('abcd',2) },1) }","c",1], | |||
|  |    ); | |||
|  | 
 | |||
|  | @extra_functions= | |||
|  |   ( | |||
|  |    ["& (bitwise and)",'&',"5 & 3",1,0], | |||
|  |    ["| (bitwise or)",'|',"1 | 2",3,0], | |||
|  |    ["<< and >> (bitwise shifts)",'binary_shifts',"(1 << 4) >> 2",4,0], | |||
|  |    ["<> in SELECT","<>","1<>1","0",0], | |||
|  |    ["=","=","(1=1)",1,$logical_value], | |||
|  |    ["~* (case insensitive compare)","~*","'hi' ~* 'HI'",1,$logical_value], | |||
|  |    ["AND and OR in SELECT","and_or","1=1 AND 2=2",$logical_value,0], | |||
|  |    ["AND as '&&'",'&&',"1=1 && 2=2",$logical_value,0], | |||
|  |    ["ASCII_CHAR", "ascii_char", "ASCII_CHAR(65)","A",1], | |||
|  |    ["ASCII_CODE", "ascii_code", "ASCII_CODE('A')","65",0], | |||
|  |    ["ATN2","atn2","atn2(1,0)","1.570796",0], | |||
|  |    ["BETWEEN in SELECT","between","5 between 4 and 6",$logical_value,0], | |||
|  |    ["BIT_COUNT","bit_count","bit_count(5)",2,0], | |||
|  |    ["CEIL","ceil","ceil(-4.5)",-4,0], # oracle | |||
|  |    ["CHARINDEX","charindex","charindex('a','crash')",3,0], | |||
|  |    ["CHR", "chr", "CHR(65)"  ,"A",1], # oracle | |||
|  |    ["CONCAT(list)","concat_list", "concat('a','b','c','d')","abcd",1], | |||
|  |    ["CONVERT","convert","convert(CHAR,5)","5",1], | |||
|  |    ["COSH","cosh","cosh(0)","1",0], # oracle hyperbolic cosine of n. | |||
|  |    ["ELT","elt","elt(2,'ONE','TWO','THREE')","TWO",1], | |||
|  |    ["ENCRYPT","encrypt","encrypt('hello')",0,2], | |||
|  |    ["FIELD","field","field('IBM','NCA','ICL','SUN','IBM','DIGITAL')",4,0], | |||
|  |    ["FORMAT","format","format(1234.5555,2)","1,234.56",1], | |||
|  |    ["GETDATE","getdate","getdate()",0,2], | |||
|  |    ["GREATEST","greatest","greatest('HARRY','HARRIOT','HAROLD')","HARRY",1], | |||
|  |    ["IF","if", "if(5,6,7)",6,0], | |||
|  |    ["IN on numbers in SELECT","in_num","2 in (3,2,5,9,5,1)",$logical_value,0], | |||
|  |    ["IN on strings in SELECT","in_str","'monty' in ('david','monty','allan')", $logical_value,0], | |||
|  |    ["INITCAP","initcap","initcap('the soap')","The Soap",1],  | |||
|  |        # oracle Returns char, with the first letter of each word in uppercase | |||
|  |    ["INSTR (Oracle syntax)", "instr_oracle", "INSTR('CORPORATE FLOOR','OR',3,2)"  ,"14",0], # oracle instring | |||
|  |    ["INSTRB", "instrb", "INSTRB('CORPORATE FLOOR','OR',5,2)"  ,"27",0],  | |||
|  |       # oracle instring in bytes | |||
|  |    ["INTERVAL","interval","interval(55,10,20,30,40,50,60,70,80,90,100)",5,0], | |||
|  |    ["LAST_INSERT_ID","last_insert_id","last_insert_id()",0,2], | |||
|  |    ["LEAST","least","least('HARRY','HARRIOT','HAROLD')","HAROLD",1],  | |||
|  |       # oracle | |||
|  |    ["LENGTHB","lengthb","lengthb('CANDIDE')","14",0],  | |||
|  |       # oracle length in bytes | |||
|  |    ["LIKE ESCAPE in SELECT","like_escape", | |||
|  |      "'%' like 'a%' escape 'a'",$logical_value,0], | |||
|  |    ["LIKE in SELECT","like","'a' like 'a%'",$logical_value,0], | |||
|  |    ["LN","ln","ln(95)","4.55387689",0],  | |||
|  |       # oracle natural logarithm of n | |||
|  |    ["LOCATE as INSTR","instr","instr('hello','ll')",3,0], | |||
|  |    ["LOG(m,n)","log(m_n)","log(10,100)","2",0],  | |||
|  |       # oracle logarithm, base m, of n | |||
|  |    ["LOGN","logn","logn(2)","0.693147",0],  | |||
|  |       # informix | |||
|  |    ["LPAD","lpad","lpad('hi',4,'??')",'??hi',3], | |||
|  |    ["MOD as %","%","10%7","3",0], | |||
|  |    ["NOT BETWEEN in SELECT","not_between","5 not between 4 and 6",0,0], | |||
|  |    ["NOT LIKE in SELECT","not_like","'a' not like 'a%'",0,0], | |||
|  |    ["NOT as '!' in SELECT","!","! 1",0,0], | |||
|  |    ["NOT in SELECT","not","not $false",$logical_value,0], | |||
|  |    ["ODBC CONVERT","odbc_convert","convert(5,SQL_CHAR)","5",1], | |||
|  |    ["OR as '||'",'||',"1=0 || 1=1",$logical_value,0], | |||
|  |    ["PASSWORD","password","password('hello')",0,2], | |||
|  |    ["PASTE", "paste", "paste('ABCDEFG',3,2,'1234')","AB1234EFG",1], | |||
|  |    ["PATINDEX","patindex","patindex('%a%','crash')",3,0], | |||
|  |    ["POW","pow","pow(3,2)",9,0], | |||
|  |    ["RANGE","range","range(a)","0.0",0],  | |||
|  |        # informix range(a) = max(a) - min(a) | |||
|  |    ["REGEXP in SELECT","regexp","'a' regexp '^(a|b)*\$'",$logical_value,0], | |||
|  |    ["REPLICATE","replicate","replicate('a',5)","aaaaa",1], | |||
|  |    ["REVERSE","reverse","reverse('abcd')","dcba",1], | |||
|  |    ["ROOT","root","root(4)",2,0], # informix | |||
|  |    ["ROUND(1 arg)","round1","round(5.63)","6",0], | |||
|  |    ["RPAD","rpad","rpad('hi',4,'??')",'hi??',3], | |||
|  |    ["SINH","sinh","sinh(1)","1.17520119",0], # oracle hyperbolic sine of n | |||
|  |    ["STR","str","str(123.45,5,1)",123.5,3], | |||
|  |    ["STRCMP","strcmp","strcmp('abc','adc')",-1,0], | |||
|  |    ["STUFF","stuff","stuff('abc',2,3,'xyz')",'axyz',3], | |||
|  |    ["SUBSTRB", "substrb", "SUBSTRB('ABCDEFG',5,4.2)"  ,"CD",1],  | |||
|  |       # oracle substring with bytes | |||
|  |    ["SUBSTRING as MID","mid","mid('hello',3,2)","ll",1], | |||
|  |    ["SUBSTRING_INDEX","substring_index", | |||
|  |      "substring_index('www.tcx.se','.',-2)", "tcx.se",1], | |||
|  |    ["SYSDATE","sysdate","sysdate()",0,2], | |||
|  |    ["TAIL","tail","tail('ABCDEFG',3)","EFG",0], | |||
|  |    ["TANH","tanh","tanh(1)","0.462117157",0],  | |||
|  |       # oracle hyperbolic tangent of n | |||
|  |    ["TRANSLATE","translate","translate('abc','bc','de')",'ade',3], | |||
|  |    ["TRIM; Many char extension", | |||
|  |      "trim_many_char","trim(':!' FROM ':abc!')","abc",3], | |||
|  |    ["TRIM; Substring extension", | |||
|  |      "trim_substring","trim('cb' FROM 'abccb')","abc",3], | |||
|  |    ["TRUNC","trunc","trunc(18.18,-1)",10,0], # oracle | |||
|  |    ["UID","uid","uid",0,2], # oracle uid from user | |||
|  |    ["UNIX_TIMESTAMP","unix_timestamp","unix_timestamp()",0,2], | |||
|  |    ["USERENV","userenv","userenv",0,2], # oracle user enviroment | |||
|  |    ["VERSION","version","version()",0,2], | |||
|  |    ["automatic num->string convert","auto_num2string","concat('a',2)","a2",1], | |||
|  |    ["automatic string->num convert","auto_string2num","'1'+2",3,0], | |||
|  |    ["concatenation with +","concat_as_+","'abc' + 'def'","abcdef",1], | |||
|  |    ["SUBSTR (2 arg)",'substr2arg',"substr('abcd',2)",'bcd',1],  #sapdb func | |||
|  |    ["SUBSTR (3 arg)",'substr3arg',"substr('abcd',2,2)",'bc',1], | |||
|  |    ["LFILL (3 arg)",'lfill3arg',"lfill('abcd','.',6)",'..abcd',1], | |||
|  |    ["RFILL (3 arg)",'rfill3arg',"rfill('abcd','.',6)",'abcd..',1], | |||
|  |    ["RPAD (4 arg)",'rpad4arg',"rpad('abcd',2,'+-',8)",'abcd+-+-',1], | |||
|  |    ["LPAD (4 arg)",'rpad4arg',"lpad('abcd',2,'+-',8)",'+-+-abcd',1], | |||
|  |    ["TRIM (1 arg)",'trim1arg',"trim(' abcd ')",'abcd',1], | |||
|  |    ["TRIM (2 arg)",'trim2arg',"trim('..abcd..','.')",'abcd',1], | |||
|  |    ["LTRIM (2 arg)",'ltrim2arg',"ltrim('..abcd..','.')",'abcd..',1], | |||
|  |    ["RTRIM (2 arg)",'rtrim2arg',"rtrim('..abcd..','.')",'..abcd',1], | |||
|  |    ["EXPAND",'expand2arg',"expand('abcd',6)",'abcd  ',0], | |||
|  |    ["REPLACE (2 arg) ",'replace2arg',"replace('AbCd','bC')",'Ad',1], | |||
|  |    ["MAPCHAR",'mapchar',"mapchar('A<>')",'Aa',1], | |||
|  |    ["ALPHA",'alpha',"alpha('A<>',2)",'AA',1], | |||
|  |    ["ASCII in string cast",'ascii_string',"ascii('a')",'a',1], | |||
|  |    ["EBCDIC in string cast",'ebcdic_string',"ebcdic('a')",'a',1], | |||
|  |    ["TRUNC (1 arg)",'trunc1arg',"trunc(222.6)",222,0], | |||
|  |    ["FIXED",'fixed',"fixed(222.6666,10,2)",'222.67',0], | |||
|  |    ["FLOAT",'float',"float(6666.66,4)",6667,0], | |||
|  |    ["LENGTH",'length',"length(1)",2,0], | |||
|  |    ["INDEX",'index',"index('abcdefg','cd',1,1)",3,0], | |||
|  |    ["MICROSECOND",'microsecond', | |||
|  |       "MICROSECOND('19630816200212111111')",'111111',0], | |||
|  |    ["TIMESTAMP",'timestamp', | |||
|  |       "timestamp('19630816','00200212')",'19630816200212000000',0], | |||
|  |    ["VALUE",'value',"value(NULL,'WALRUS')",'WALRUS',0], | |||
|  |    ["DECODE",'decode',"DECODE('S-103','T72',1,'S-103',2,'Leopard',3)",2,0], | |||
|  |    ["NUM",'num',"NUM('2123')",2123,0], | |||
|  |    ["CHR (any type to string)",'chr_str',"CHR(67)",'67',0], | |||
|  |    ["HEX",'hex',"HEX('A')",41,0], | |||
|  |    ); | |||
|  | 
 | |||
|  | 
 | |||
|  | @sql_group_functions= | |||
|  |   ( | |||
|  |    ["AVG","avg","avg(a)",1,0], | |||
|  |    ["COUNT (*)","count_*","count(*)",1,0], | |||
|  |    ["COUNT column name","count_column","count(a)",1,0], | |||
|  |    ["COUNT(DISTINCT expr)","count_distinct","count(distinct a)",1,0], | |||
|  |    ["MAX on numbers","max","max(a)",1,0], | |||
|  |    ["MAX on strings","max_str","max(b)","a",1], | |||
|  |    ["MIN on numbers","min","min(a)",1,0], | |||
|  |    ["MIN on strings","min_str","min(b)","a",1], | |||
|  |    ["SUM","sum","sum(a)",1,0], | |||
|  |    ["ANY","any","any(a)",$logical_value,0], | |||
|  |    ["EVERY","every","every(a)",$logical_value,0], | |||
|  |    ["SOME","some","some(a)",$logical_value,0], | |||
|  |    ); | |||
|  | 
 | |||
|  | @extra_group_functions= | |||
|  |   ( | |||
|  |    ["BIT_AND",'bit_and',"bit_and(a)",1,0], | |||
|  |    ["BIT_OR", 'bit_or', "bit_or(a)",1,0], | |||
|  |    ["COUNT(DISTINCT expr,expr,...)", | |||
|  |      "count_distinct_list","count(distinct a,b)",1,0], | |||
|  |    ["STD","std","std(a)",0,0], | |||
|  |    ["STDDEV","stddev","stddev(a)",0,0], | |||
|  |    ["VARIANCE","variance","variance(a)",0,0], | |||
|  |    ); | |||
|  | 
 | |||
|  | @where_functions= | |||
|  | ( | |||
|  |  ["= ALL","eq_all","b =all (select b from crash_me)",1,0], | |||
|  |  ["= ANY","eq_any","b =any (select b from crash_me)",1,0], | |||
|  |  ["= SOME","eq_some","b =some (select b from crash_me)",1,0], | |||
|  |  ["BETWEEN","between","5 between 4 and 6",1,0], | |||
|  |  ["EXISTS","exists","exists (select * from crash_me)",1,0], | |||
|  |  ["IN on numbers","in_num","2 in (3,2,5,9,5,1)",1,0], | |||
|  |  ["LIKE ESCAPE","like_escape","b like '%' escape 'a'",1,0], | |||
|  |  ["LIKE","like","b like 'a%'",1,0], | |||
|  |  ["MATCH UNIQUE","match_unique", | |||
|  |    "1 match unique (select a from crash_me)",1,0], | |||
|  |  ["MATCH","match","1 match (select a from crash_me)",1,0], | |||
|  |  ["MATCHES","matches","b matches 'a*'",1,0], | |||
|  |  ["NOT BETWEEN","not_between","7 not between 4 and 6",1,0], | |||
|  |  ["NOT EXISTS","not_exists", | |||
|  |    "not exists (select * from crash_me where a = 2)",1,0], | |||
|  |  ["NOT LIKE","not_like","b not like 'b%'",1,0], | |||
|  |  ["NOT UNIQUE","not_unique", | |||
|  |    "not unique (select * from crash_me where a = 2)",1,0], | |||
|  |  ["UNIQUE","unique","unique (select * from crash_me)",1,0], | |||
|  |  ); | |||
|  | 
 | |||
|  | @types=(["sql",\@sql_functions,0], | |||
|  | 	["odbc",\@odbc_functions,0], | |||
|  | 	["extra",\@extra_functions,0], | |||
|  | 	["where",\@where_functions,0]); | |||
|  | 
 | |||
|  | @group_types=(["sql",\@sql_group_functions,0], | |||
|  | 	      ["extra",\@extra_group_functions,0]); | |||
|  | 
 | |||
|  | 
 | |||
|  | foreach $types (@types) | |||
|  | { | |||
|  |   print "\nSupported $types->[0] functions\n"; | |||
|  |   $tmp=@$types->[1]; | |||
|  |   foreach $type (@$tmp) | |||
|  |   { | |||
|  |     if (defined($limits{"func_$types->[0]_$type->[1]"})) | |||
|  |     { | |||
|  |       next; | |||
|  |     } | |||
|  |     if ($types->[0] eq "where") | |||
|  |     { | |||
|  |       check_and_report("Function $type->[0]","func_$types->[0]_$type->[1]", | |||
|  | 		       [],"select a from crash_me where $type->[2]",[], | |||
|  | 		       $type->[3],$type->[4]); | |||
|  |     } | |||
|  |     elsif ($limits{'functions'} eq 'yes') | |||
|  |     { | |||
|  |       if (($type->[2] =~ /char_length\(b\)/) && (!$end_query)) | |||
|  |       { | |||
|  | 	my $tmp= $type->[2]; | |||
|  | 	$tmp .= " from crash_me "; | |||
|  | 	undef($limits{"func_$types->[0]_$type->[1]"}); | |||
|  | 	check_and_report("Function $type->[0]", | |||
|  | 			 "func_$types->[0]_$type->[1]", | |||
|  | 			 [],"select $tmp ",[], | |||
|  | 			 $type->[3],$type->[4]); | |||
|  |       } | |||
|  |       else | |||
|  |       { | |||
|  | 	undef($limits{"func_$types->[0]_$type->[1]"}); | |||
|  | 	$result = check_and_report("Function $type->[0]", | |||
|  | 			    "func_$types->[0]_$type->[1]", | |||
|  | 			    [],"select $type->[2] $end_query",[], | |||
|  | 			    $type->[3],$type->[4]); | |||
|  | 	if (!$result) | |||
|  | 	{ | |||
|  | 	  # check without type specifyer | |||
|  | 	  if ($type->[2] =~ /DATE /) | |||
|  | 	  { | |||
|  | 	    my $tmp= $type->[2]; | |||
|  | 	    $tmp =~ s/DATE //; | |||
|  | 	    undef($limits{"func_$types->[0]_$type->[1]"}); | |||
|  | 	    $result = check_and_report("Function $type->[0]", | |||
|  | 				  "func_$types->[0]_$type->[1]", | |||
|  | 				  [],"select $tmp $end_query",[], | |||
|  | 				  $type->[3],$type->[4]); | |||
|  | 	  } | |||
|  | 	  if (!$result) | |||
|  | 	  { | |||
|  | 	    if ($types->[0] eq "odbc" && ! ($type->[2] =~ /\{fn/)) | |||
|  | 	    { | |||
|  | 	     my $tmp= $type->[2]; | |||
|  | 	     # Check by converting to ODBC format | |||
|  | 	     undef($limits{"func_$types->[0]_$type->[1]"}); | |||
|  | 	     $tmp= "{fn $tmp }"; | |||
|  | 	     $tmp =~ s/('1997-\d\d-\d\d \d\d:\d\d:\d\d')/{ts $1}/g; | |||
|  | 	     $tmp =~ s/(DATE '1997-\d\d-\d\d')/{d $1}/g; | |||
|  | 	     $tmp =~ s/(TIME '12:13:14')/{t $1}/g; | |||
|  | 	     $tmp =~ s/DATE //; | |||
|  | 	     $tmp =~ s/TIME //; | |||
|  | 	     check_and_report("Function $type->[0]", | |||
|  | 			      "func_$types->[0]_$type->[1]", | |||
|  | 			      [],"select $tmp $end_query",[], | |||
|  | 			      $type->[3],$type->[4]); | |||
|  | 	    } | |||
|  | 	  } | |||
|  |         } | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'functions'} eq 'yes') | |||
|  | { | |||
|  |   foreach $types (@group_types) | |||
|  |   { | |||
|  |     print "\nSupported $types->[0] group functions\n"; | |||
|  |     $tmp=@$types->[1]; | |||
|  |     foreach $type (@$tmp) | |||
|  |     { | |||
|  |       check_and_report("Group function $type->[0]", | |||
|  | 		       "group_func_$types->[0]_$type->[1]", | |||
|  | 		       [],"select $type->[2],a from crash_me group by a",[], | |||
|  | 		       $type->[3],$type->[4]); | |||
|  |     } | |||
|  |   } | |||
|  |   print "\n"; | |||
|  |   report("mixing of integer and float in expression","float_int_expr", | |||
|  | 	 "select 1+1.0 $end_query"); | |||
|  |   if ($limits{'func_odbc_exp'} eq 'yes') | |||
|  |   { | |||
|  |     report("No need to cast from integer to float", | |||
|  | 	   "dont_require_cast_to_float", "select exp(1) $end_query"); | |||
|  |   } | |||
|  |   check_and_report("Is 1+NULL = NULL","null_num_expr", | |||
|  | 		   [],"select 1+$numeric_null $end_query",[],undef(),4); | |||
|  |   $tmp=sql_concat("'a'",$char_null); | |||
|  |   if (defined($tmp)) | |||
|  |   { | |||
|  |     check_and_report("Is $tmp = NULL", "null_concat_expr", [], | |||
|  | 		     "select $tmp $end_query",[], undef(),4); | |||
|  |   } | |||
|  |   $prompt="Need to cast NULL for arithmetic"; | |||
|  |   add_log("Need_cast_for_null", | |||
|  |     " Check if numeric_null ($numeric_null) is 'NULL'"); | |||
|  |   save_config_data("Need_cast_for_null", | |||
|  | 		   ($numeric_null eq "NULL") ? "no" : "yes", | |||
|  | 		   $prompt); | |||
|  | } | |||
|  | else | |||
|  | { | |||
|  |   print "\n"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | #  Test: NOROUND  | |||
|  | { | |||
|  |  my $result = 'undefined'; | |||
|  |  my $error; | |||
|  |  print "NOROUND: "; | |||
|  |  save_incomplete('func_extra_noround','Function NOROUND'); | |||
|  | 
 | |||
|  | # 1) check if noround() function is supported | |||
|  |  $error = safe_query_l('func_extra_noround',"select noround(22.6) $end_query"); | |||
|  |  if ($error ne 1)         # syntax error -- noround is not supported  | |||
|  |  { | |||
|  |    $result = 'no' | |||
|  |  } | |||
|  |  else                   # Ok, now check if it really works | |||
|  |  { | |||
|  |    $error=safe_query_l('func_extra_noround',  | |||
|  |      ["create table crash_me_nr (a int)", | |||
|  |     "insert into crash_me_nr values(noround(10.2))", | |||
|  |     "drop table crash_me_nr $drop_attr"]); | |||
|  |    if ($error == 1) | |||
|  |    { | |||
|  |      $result= "syntax only"; | |||
|  |    } | |||
|  |    else | |||
|  |    { | |||
|  |      $result= 'yes'; | |||
|  |    } | |||
|  |  } | |||
|  |  print "$result\n"; | |||
|  |  save_config_data('func_extra_noround',$result,"Function NOROUND"); | |||
|  | } | |||
|  | 
 | |||
|  | check_parenthesis("func_sql_","CURRENT_USER"); | |||
|  | check_parenthesis("func_sql_","SESSION_USER"); | |||
|  | check_parenthesis("func_sql_","SYSTEM_USER"); | |||
|  | check_parenthesis("func_sql_","USER"); | |||
|  | 
 | |||
|  | 
 | |||
|  | if ($limits{'type_sql_date'} eq 'yes') | |||
|  | {  #  | |||
|  |    # Checking the format of date in result.  | |||
|  |     | |||
|  |     safe_query("drop table crash_me_d $drop_attr"); | |||
|  |     assert("create table crash_me_d (a date)"); | |||
|  |     # find the example of date | |||
|  |     my $dateexample; | |||
|  |     if ($limits{'func_extra_sysdate'} eq 'yes') { | |||
|  |      $dateexample=' sysdate() '; | |||
|  |     }  | |||
|  |     elsif ($limits{'func_sql_current_date'} eq 'yes') { | |||
|  |      $dateexample='CURRENT_DATE'; | |||
|  |     }  | |||
|  |     elsif ($limits{'func_odbc_curdate'} eq 'yes') { | |||
|  |      $dateexample='curdate()'; | |||
|  |     }  | |||
|  |     elsif ($limits{'func_extra_getdate'} eq 'yes') { | |||
|  | 	$dateexample='getdate()'; | |||
|  |     } | |||
|  |     elsif ($limits{'func_odbc_now'} eq 'yes') { | |||
|  | 	$dateexample='now()'; | |||
|  |     } else { | |||
|  | 	#try to guess  | |||
|  | 	$dateexample="DATE '1963-08-16'"; | |||
|  |     } ; | |||
|  |      | |||
|  |     my $key = 'date_format_inresult'; | |||
|  |     my $prompt = "Date format in result"; | |||
|  |     if (! safe_query_l('date_format_inresult', | |||
|  |        "insert into crash_me_d values($dateexample) ")) | |||
|  |     {  | |||
|  | 	die "Cannot insert date ($dateexample):".$last_error;  | |||
|  |     }; | |||
|  |     my $sth= $dbh->prepare("select a from crash_me_d"); | |||
|  |     add_log('date_format_inresult',"< select a from crash_me_d"); | |||
|  |     $sth->execute; | |||
|  |     $_= $sth->fetchrow_array; | |||
|  |     add_log('date_format_inresult',"> $_"); | |||
|  |     safe_query_l($key,"delete from crash_me_d");    | |||
|  |     if (/\d{4}-\d{2}-\d{2}/){ save_config_data($key,"iso",$prompt);}  | |||
|  |     elsif (/\d{2}-\d{2}-\d{2}/){ save_config_data($key,"short iso",$prompt);} | |||
|  |     elsif (/\d{2}\.\d{2}\.\d{4}/){ save_config_data($key,"euro",$prompt);} | |||
|  |     elsif (/\d{2}\.\d{2}\.\d{2}/){ save_config_data($key,"short euro",$prompt);} | |||
|  |     elsif (/\d{2}\/\d{2}\/\d{4}/){ save_config_data($key,"usa",$prompt);} | |||
|  |     elsif (/\d{2}\/\d{2}\/\d{2}/){ save_config_data($key,"short usa",$prompt);} | |||
|  |     elsif (/\d*/){ save_config_data($key,"YYYYMMDD",$prompt);} | |||
|  |     else  { save_config_data($key,"unknown",$prompt);}; | |||
|  |     $sth->finish; | |||
|  | 
 | |||
|  |     check_and_report("Supports YYYY-MM-DD (ISO) format","date_format_ISO", | |||
|  | 		     [ "insert into crash_me_d(a)  values ('1963-08-16')"], | |||
|  | 		     "select a from crash_me_d", | |||
|  | 		     ["delete from crash_me_d"], | |||
|  | 		     make_date_r(1963,8,16),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports DATE 'YYYY-MM-DD' (ISO) format", | |||
|  | 		     "date_format_ISO_with_date", | |||
|  | 		     [ "insert into crash_me_d(a) values (DATE '1963-08-16')"], | |||
|  | 		     "select a from crash_me_d", | |||
|  | 		     ["delete from crash_me_d"], | |||
|  | 		     make_date_r(1963,8,16),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports DD.MM.YYYY (EUR) format","date_format_EUR", | |||
|  | 		     [ "insert into crash_me_d(a) values ('16.08.1963')"], | |||
|  | 		     "select a from crash_me_d", | |||
|  | 		     ["delete from crash_me_d"], | |||
|  | 		     make_date_r(1963,8,16),1); | |||
|  |     check_and_report("Supports DATE 'DD.MM.YYYY' (EUR) format", | |||
|  | 		     "date_format_EUR_with_date", | |||
|  | 		     [ "insert into crash_me_d(a) values (DATE '16.08.1963')"], | |||
|  | 		     "select a from crash_me_d", | |||
|  | 		     ["delete from crash_me_d"], | |||
|  | 		     make_date_r(1963,8,16),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports YYYYMMDD format", | |||
|  | 	 "date_format_YYYYMMDD", | |||
|  | 	 [ "insert into crash_me_d(a) values ('19630816')"], | |||
|  | 	 "select a from crash_me_d", | |||
|  | 	 ["delete from crash_me_d"], | |||
|  | 	 make_date_r(1963,8,16),1); | |||
|  |     check_and_report("Supports DATE 'YYYYMMDD' format", | |||
|  | 	 "date_format_YYYYMMDD_with_date", | |||
|  | 	 [ "insert into crash_me_d(a) values (DATE '19630816')"], | |||
|  | 	 "select a from crash_me_d", | |||
|  | 	 ["delete from crash_me_d"], | |||
|  | 	 make_date_r(1963,8,16),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports MM/DD/YYYY format", | |||
|  | 	 "date_format_USA", | |||
|  | 	 [ "insert into crash_me_d(a) values ('08/16/1963')"], | |||
|  | 	 "select a from crash_me_d", | |||
|  | 	 ["delete from crash_me_d"], | |||
|  | 	 make_date_r(1963,8,16),1); | |||
|  |     check_and_report("Supports DATE 'MM/DD/YYYY' format", | |||
|  | 	 "date_format_USA_with_date", | |||
|  | 	 [ "insert into crash_me_d(a) values (DATE '08/16/1963')"], | |||
|  | 	 "select a from crash_me_d", | |||
|  | 	 ["delete from crash_me_d"], | |||
|  | 	 make_date_r(1963,8,16),1); | |||
|  | 
 | |||
|  | 
 | |||
|  |   | |||
|  | 
 | |||
|  |     check_and_report("Supports 0000-00-00 dates","date_zero", | |||
|  | 	 ["create table crash_me2 (a date not null)", | |||
|  | 	  "insert into crash_me2 values (".make_date(0,0,0).")"], | |||
|  | 	 "select a from crash_me2", | |||
|  | 	 ["drop table crash_me2 $drop_attr"], | |||
|  | 	 make_date_r(0,0,0),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports 0001-01-01 dates","date_one", | |||
|  | 	 ["create table crash_me2 (a date not null)", | |||
|  | 	  "insert into crash_me2 values (".make_date(1,1,1).")"], | |||
|  | 	 "select a from crash_me2", | |||
|  | 	 ["drop table crash_me2 $drop_attr"], | |||
|  | 	 make_date_r(1,1,1),1); | |||
|  |      | |||
|  |     check_and_report("Supports 9999-12-31 dates","date_last", | |||
|  | 	["create table crash_me2 (a date not null)", | |||
|  |         "insert into crash_me2 values (".make_date(9999,12,31).")"], | |||
|  |         "select a from crash_me2", | |||
|  | 	["drop table crash_me2 $drop_attr"], | |||
|  | 	make_date_r(9999,12,31),1); | |||
|  |      | |||
|  |     check_and_report("Supports 'infinity dates","date_infinity", | |||
|  | 	 ["create table crash_me2 (a date not null)", | |||
|  | 	 "insert into crash_me2 values ('infinity')"], | |||
|  | 	 "select a from crash_me2", | |||
|  | 	 ["drop table crash_me2 $drop_attr"], | |||
|  | 	 "infinity",1); | |||
|  |      | |||
|  |     if (!defined($limits{'date_with_YY'})) | |||
|  |     { | |||
|  | 	check_and_report("Supports YY-MM-DD dates","date_with_YY", | |||
|  | 	   ["create table crash_me2 (a date not null)", | |||
|  | 	   "insert into crash_me2 values ('98-03-03')"], | |||
|  | 	   "select a from crash_me2", | |||
|  | 	   ["drop table crash_me2 $drop_attr"], | |||
|  | 	   make_date_r(1998,3,3),5); | |||
|  | 	if ($limits{'date_with_YY'} eq "yes") | |||
|  | 	{ | |||
|  | 	    undef($limits{'date_with_YY'}); | |||
|  | 	    check_and_report("Supports YY-MM-DD 2000 compilant dates", | |||
|  | 	       "date_with_YY", | |||
|  | 	       ["create table crash_me2 (a date not null)", | |||
|  | 	       "insert into crash_me2 values ('10-03-03')"], | |||
|  | 	       "select a from crash_me2", | |||
|  | 	       ["drop table crash_me2 $drop_attr"], | |||
|  | 	       make_date_r(2010,3,3),5); | |||
|  | 	} | |||
|  |     } | |||
|  |      | |||
|  | # Test: WEEK() | |||
|  |     { | |||
|  | 	my $result="no"; | |||
|  | 	my $error; | |||
|  | 	print "WEEK:"; | |||
|  | 	save_incomplete('func_odbc_week','WEEK'); | |||
|  | 	$error = safe_query_result_l('func_odbc_week', | |||
|  | 	     "select week(".make_date(1997,2,1).") $end_query",5,0); | |||
|  | 	# actually this query must return 4 or 5 in the $last_result, | |||
|  | 	# $error can be 1 (not supported at all) , -1 ( probably USA weeks) | |||
|  | 	# and 0 - EURO weeks | |||
|  | 	if ($error == -1) {  | |||
|  | 	    if ($last_result == 4) { | |||
|  | 		$result = 'USA'; | |||
|  | 	    } else { | |||
|  | 		$result='error'; | |||
|  | 		add_log('func_odbc_week', | |||
|  | 		  " must return 4 or 5, but $last_result"); | |||
|  | 	    } | |||
|  | 	} elsif ($error == 0) { | |||
|  | 	    $result = 'EURO'; | |||
|  | 	} | |||
|  | 	print " $result\n"; | |||
|  | 	save_config_data('func_odbc_week',$result,"WEEK"); | |||
|  |     } | |||
|  |      | |||
|  |     my $insert_query ='insert into crash_me_d values('. | |||
|  |         make_date(1997,2,1).')'; | |||
|  |     safe_query($insert_query); | |||
|  |      | |||
|  |     foreach $fn ( ( | |||
|  | 		   ["DAYNAME","dayname","dayname(a)","",2], | |||
|  | 		   ["MONTH","month","month(a)","",2], | |||
|  | 		   ["MONTHNAME","monthname","monthname(a)","",2], | |||
|  | 		   ["DAYOFMONTH","dayofmonth","dayofmonth(a)",1,0], | |||
|  | 		   ["DAYOFWEEK","dayofweek","dayofweek(a)",7,0], | |||
|  | 		   ["DAYOFYEAR","dayofyear","dayofyear(a)",32,0], | |||
|  | 		   ["QUARTER","quarter","quarter(a)",1,0], | |||
|  | 		   ["YEAR","year","year(a)",1997,0])) | |||
|  |     { | |||
|  | 	$prompt='Function '.$fn->[0]; | |||
|  | 	$key='func_odbc_'.$fn->[1]; | |||
|  | 	add_log($key,"< ".$insert_query); | |||
|  | 	check_and_report($prompt,$key, | |||
|  | 			 [],"select ".$fn->[2]." from crash_me_d",[], | |||
|  | 			 $fn->[3],$fn->[4] | |||
|  | 			 ); | |||
|  | 	 | |||
|  |     }; | |||
|  |     safe_query(['delete from crash_me_d',  | |||
|  | 		'insert into crash_me_d values('.make_date(1963,8,16).')']); | |||
|  |     foreach $fn (( | |||
|  | 	  ["DATEADD","dateadd","dateadd(day,3,make_date(1997,11,30))",0,2], | |||
|  | 	  ["MDY","mdy","mdy(7,1,1998)","make_date_r(1998,07,01)",0], # informix | |||
|  | 	  ["DATEDIFF","datediff", | |||
|  | 	     "datediff(month,'Oct 21 1997','Nov 30 1997')",0,2], | |||
|  | 	  ["DATENAME","datename","datename(month,'Nov 30 1997')",0,2], | |||
|  | 	  ["DATEPART","datepart","datepart(month,'July 20 1997')",0,2], | |||
|  | 	  ["DATE_FORMAT","date_format",  | |||
|  | 	    "date_format('1997-01-02 03:04:05','M W D Y y m d h i s w')", 0,2], | |||
|  | 	  ["FROM_DAYS","from_days", | |||
|  | 	    "from_days(729024)","make_date_r(1996,1,1)",1], | |||
|  | 	  ["FROM_UNIXTIME","from_unixtime","from_unixtime(0)",0,2], | |||
|  | 	  ["MONTHS_BETWEEN","months_between", | |||
|  | 	   "months_between(make_date(1997,2,2),make_date(1997,1,1))", | |||
|  | 	   "1.03225806",0], # oracle number of months between 2 dates | |||
|  | 	  ["PERIOD_ADD","period_add","period_add(9602,-12)",199502,0], | |||
|  | 	  ["PERIOD_DIFF","period_diff","period_diff(199505,199404)",13,0], | |||
|  | 	  ["WEEKDAY","weekday","weekday(make_date(1997,11,29))",5,0], | |||
|  | 	  ["ADDDATE",'adddate', | |||
|  | 	   "ADDDATE(make_date(2002,12,01),3)",'make_date_r(2002,12,04)',0], | |||
|  | 	  ["SUBDATE",'subdate', | |||
|  | 	   "SUBDATE(make_date(2002,12,04),3)",'make_date_r(2002,12,01)',0], | |||
|  | 	  ["DATEDIFF (2 arg)",'datediff2arg', | |||
|  | 	   "DATEDIFF(make_date(2002,12,04),make_date(2002,12,01))",'3',0], | |||
|  | 	  ["WEEKOFYEAR",'weekofyear', | |||
|  | 	   "WEEKOFYEAR(make_date(1963,08,16))",'33',0], | |||
|  | # table crash_me_d must contain  record with 1963-08-16 (for CHAR) | |||
|  | 	  ["CHAR (conversation date)",'char_date', | |||
|  | 	   "CHAR(a,EUR)",'16.08.1963',0], | |||
|  | 	  ["MAKEDATE",'makedate',"MAKEDATE(1963,228)" | |||
|  | 	   ,'make_date_r(1963,08,16)',0], | |||
|  | 	  ["TO_DAYS","to_days", | |||
|  | 	   "to_days(make_date(1996,01,01))",729024,0], | |||
|  | 	  ["ADD_MONTHS","add_months", | |||
|  | 	   "add_months(make_date(1997,01,01),1)","make_date_r(1997,02,01)",0],  | |||
|  | 	      # oracle the date plus n months | |||
|  | 	  ["LAST_DAY","last_day", | |||
|  | 	  "last_day(make_date(1997,04,01))","make_date_r(1997,04,30)",0],  | |||
|  | 	      # oracle last day of month of date | |||
|  | 	  ["DATE",'date',"date(make_date(1963,8,16))", | |||
|  | 	     'make_date_r(1963,8,16)',0], | |||
|  | 	  ["DAY",'day',"DAY(make_date(2002,12,01))",1,0])) | |||
|  |     { | |||
|  | 	$prompt='Function '.$fn->[0]; | |||
|  | 	$key='func_extra_'.$fn->[1]; | |||
|  | 	my $qry="select ".$fn->[2]." from crash_me_d"; | |||
|  | 	while( $qry =~ /^(.*)make_date\((\d+),(\d+),(\d+)\)(.*)$/) | |||
|  | 	{ | |||
|  | 	    my $dt= &make_date($2,$3,$4); | |||
|  | 	    $qry=$1.$dt.$5; | |||
|  | 	}; | |||
|  | 	my $result=$fn->[3]; | |||
|  | 	while( $result =~ /^(.*)make_date_r\((\d+),(\d+),(\d+)\)(.*)$/) | |||
|  | 	{ | |||
|  | 	    my $dt= &make_date_r($2,$3,$4); | |||
|  | 	    $result=$1.$dt.$5; | |||
|  | 	}; | |||
|  | 	check_and_report($prompt,$key, | |||
|  | 			 [],$qry,[], | |||
|  | 			 $result,$fn->[4] | |||
|  | 			 ); | |||
|  | 	 | |||
|  |     } | |||
|  |      | |||
|  |     safe_query("drop table crash_me_d $drop_attr");     | |||
|  |      | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'type_sql_time'} eq 'yes') | |||
|  | {  #  | |||
|  |    # Checking the format of date in result.  | |||
|  |     | |||
|  |     safe_query("drop table crash_me_t $drop_attr"); | |||
|  |     assert("create table crash_me_t (a time)"); | |||
|  |     # find the example of time | |||
|  |     my $timeexample; | |||
|  |     if ($limits{'func_sql_current_time'} eq 'yes') { | |||
|  |      $timeexample='CURRENT_TIME'; | |||
|  |     }  | |||
|  |     elsif ($limits{'func_odbc_curtime'} eq 'yes') { | |||
|  |      $timeexample='curtime()'; | |||
|  |     }  | |||
|  |     elsif ($limits{'func_sql_localtime'} eq 'yes') { | |||
|  | 	$timeexample='localtime'; | |||
|  |     } | |||
|  |     elsif ($limits{'func_odbc_now'} eq 'yes') { | |||
|  | 	$timeexample='now()'; | |||
|  |     } else { | |||
|  | 	#try to guess  | |||
|  | 	$timeexample="'02:55:12'"; | |||
|  |     } ; | |||
|  |      | |||
|  |     my $key = 'time_format_inresult'; | |||
|  |     my $prompt = "Time format in result"; | |||
|  |     if (! safe_query_l('time_format_inresult', | |||
|  |        "insert into crash_me_t values($timeexample) ")) | |||
|  |     {  | |||
|  | 	die "Cannot insert time ($timeexample):".$last_error;  | |||
|  |     }; | |||
|  |     my $sth= $dbh->prepare("select a from crash_me_t"); | |||
|  |     add_log('time_format_inresult',"< select a from crash_me_t"); | |||
|  |     $sth->execute; | |||
|  |     $_= $sth->fetchrow_array; | |||
|  |     add_log('time_format_inresult',"> $_"); | |||
|  |     safe_query_l($key,"delete from crash_me_t");    | |||
|  |     if (/\d{2}:\d{2}:\d{2}/){ save_config_data($key,"iso",$prompt);}  | |||
|  |     elsif (/\d{2}\.\d{2}\.\d{2}/){ save_config_data($key,"euro",$prompt);} | |||
|  |     elsif (/\d{2}:\d{2}\s+(AM|PM)/i){ save_config_data($key,"usa",$prompt);} | |||
|  |     elsif (/\d{8}$/){ save_config_data($key,"HHHHMMSS",$prompt);} | |||
|  |     elsif (/\d{4}$/){ save_config_data($key,"HHMMSS",$prompt);} | |||
|  |     else  { save_config_data($key,"unknown",$prompt);}; | |||
|  |     $sth->finish; | |||
|  | 
 | |||
|  |     check_and_report("Supports HH:MM:SS (ISO) time format","time_format_ISO", | |||
|  | 		     [ "insert into crash_me_t(a)  values ('20:08:16')"], | |||
|  | 		     "select a from crash_me_t", | |||
|  | 		     ["delete from crash_me_t"], | |||
|  | 		     make_time_r(20,8,16),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports HH.MM.SS (EUR) time format","time_format_EUR", | |||
|  | 		     [ "insert into crash_me_t(a) values ('20.08.16')"], | |||
|  | 		     "select a from crash_me_t", | |||
|  | 		     ["delete from crash_me_t"], | |||
|  | 		     make_time_r(20,8,16),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports HHHHmmSS time format", | |||
|  | 	 "time_format_HHHHMMSS", | |||
|  | 	 [ "insert into crash_me_t(a) values ('00200816')"], | |||
|  | 	 "select a from crash_me_t", | |||
|  | 	 ["delete from crash_me_t"], | |||
|  | 	 make_time_r(20,8,16),1); | |||
|  | 
 | |||
|  |     check_and_report("Supports HHmmSS time format", | |||
|  | 	 "time_format_HHHHMMSS", | |||
|  | 	 [ "insert into crash_me_t(a) values ('200816')"], | |||
|  | 	 "select a from crash_me_t", | |||
|  | 	 ["delete from crash_me_t"], | |||
|  | 	 make_time_r(20,8,16),1); | |||
|  | 	  | |||
|  |     check_and_report("Supports HH:MM:SS (AM|PM) time format", | |||
|  | 	 "time_format_USA", | |||
|  | 	 [ "insert into crash_me_t(a) values ('08:08:16 PM')"], | |||
|  | 	 "select a from crash_me_t", | |||
|  | 	 ["delete from crash_me_t"], | |||
|  | 	 make_time_r(20,8,16),1);	  | |||
|  |      | |||
|  |     my $insert_query ='insert into crash_me_t values('. | |||
|  |         make_time(20,8,16).')'; | |||
|  |     safe_query($insert_query); | |||
|  |      | |||
|  |     foreach $fn ( ( | |||
|  |             ["HOUR","hour","hour('".make_time(12,13,14)."')",12,0], | |||
|  |             ["ANSI HOUR","hour_time","hour(TIME '".make_time(12,13,14)."')",12,0], | |||
|  |             ["MINUTE","minute","minute('".make_time(12,13,14)."')",13,0], | |||
|  |             ["SECOND","second","second('".make_time(12,13,14)."')",14,0] | |||
|  | 
 | |||
|  |     )) | |||
|  |     { | |||
|  | 	$prompt='Function '.$fn->[0]; | |||
|  | 	$key='func_odbc_'.$fn->[1]; | |||
|  | 	add_log($key,"< ".$insert_query); | |||
|  | 	check_and_report($prompt,$key, | |||
|  | 			 [],"select ".$fn->[2]." $end_query",[], | |||
|  | 			 $fn->[3],$fn->[4] | |||
|  | 			 ); | |||
|  | 	 | |||
|  |     }; | |||
|  | #    safe_query(['delete from crash_me_t',  | |||
|  | #		'insert into crash_me_t values('.make_time(20,8,16).')']); | |||
|  |     foreach $fn (( | |||
|  |          ["TIME_TO_SEC","time_to_sec","time_to_sec('". | |||
|  | 	          make_time(1,23,21)."')","5001",0], | |||
|  |          ["SEC_TO_TIME","sec_to_time","sec_to_time(5001)", | |||
|  | 	      make_time_r(01,23,21),1], | |||
|  |          ["ADDTIME",'addtime',"ADDTIME('".make_time(20,2,12). | |||
|  | 	    "','".make_time(0,0,3)."')",make_time_r(20,2,15),0], | |||
|  |          ["SUBTIME",'subtime',"SUBTIME('".make_time(20,2,15) | |||
|  | 	          ."','".make_time(0,0,3)."')",make_time_r(20,2,12),0], | |||
|  |          ["TIMEDIFF",'timediff',"TIMEDIFF('".make_time(20,2,15)."','". | |||
|  | 	 make_time(20,2,12)."')",make_time_r(0,0,3),0], | |||
|  |          ["MAKETIME",'maketime',"MAKETIME(20,02,12)",make_time_r(20,2,12),0], | |||
|  |          ["TIME",'time',"time('".make_time(20,2,12)."')",make_time_r(20,2,12),0] | |||
|  |     )) | |||
|  |     { | |||
|  | 	$prompt='Function '.$fn->[0]; | |||
|  | 	$key='func_extra_'.$fn->[1]; | |||
|  | 	my $qry="select ".$fn->[2]." $end_query"; | |||
|  | 	my $result=$fn->[3]; | |||
|  | 	check_and_report($prompt,$key, | |||
|  | 			 [],$qry,[], | |||
|  | 			 $result,$fn->[4] | |||
|  | 			 ); | |||
|  | 	 | |||
|  |     } | |||
|  |      | |||
|  |     safe_query("drop table crash_me_t $drop_attr");     | |||
|  |      | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # NOT id BETWEEN a and b | |||
|  | if ($limits{'func_where_not_between'} eq 'yes') | |||
|  | { | |||
|  |    my $result = 'error'; | |||
|  |    my $err; | |||
|  |    my $key='not_id_between'; | |||
|  |    my $prompt='NOT ID BETWEEN interprets as ID NOT BETWEEN'; | |||
|  |    print "$prompt:"; | |||
|  |    save_incomplete($key,$prompt); | |||
|  |    safe_query_l($key,["create table crash_me_b (i int)", | |||
|  |          "insert into crash_me_b values(2)", | |||
|  |          "insert into crash_me_b values(5)"]); | |||
|  |    $err =safe_query_result_l($key, | |||
|  |     "select i from crash_me_b where not i between 1 and 3", | |||
|  |      5,0); | |||
|  |    if ($err eq 1) { | |||
|  |       if (not defined($last_result)) { | |||
|  |         $result='no'; | |||
|  |       }; | |||
|  |    }; | |||
|  |    if ( $err eq 0) { | |||
|  |       $result = 'yes'; | |||
|  |    }; | |||
|  |    safe_query_l($key,["drop table crash_me_b"]); | |||
|  |    save_config_data($key,$result,$prompt); | |||
|  |    print "$result\n"; | |||
|  | }; | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | report("LIKE on numbers","like_with_number", | |||
|  |        "create table crash_q (a int,b int)", | |||
|  |        "insert into crash_q values(10,10)", | |||
|  |        "select * from crash_q where a like '10'", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("column LIKE column","like_with_column", | |||
|  |        "create table crash_q (a char(10),b char(10))", | |||
|  |        "insert into crash_q values('abc','abc')", | |||
|  |        "select * from crash_q where a like b", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("update of column= -column","NEG", | |||
|  |        "create table crash_q (a integer)", | |||
|  |        "insert into crash_q values(10)", | |||
|  |        "update crash_q set a=-a", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | if ($limits{'func_odbc_left'} eq 'yes' || | |||
|  |     $limits{'func_odbc_substring'} eq 'yes') | |||
|  | { | |||
|  |   my $type= ($limits{'func_odbc_left'} eq 'yes' ? | |||
|  | 	     "left(a,4)" : "substring(a for 4)"); | |||
|  | 
 | |||
|  |     check_and_report("String functions on date columns","date_as_string", | |||
|  | 		     ["create table crash_me2 (a date not null)", | |||
|  | 		      "insert into crash_me2 values ('1998-03-03')"], | |||
|  | 		     "select $type from crash_me2", | |||
|  | 		     ["drop table crash_me2 $drop_attr"], | |||
|  | 		     "1998",1); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | $tmp=sql_concat("b","b"); | |||
|  | if (defined($tmp)) | |||
|  | { | |||
|  |   check_and_report("char are space filled","char_is_space_filled", | |||
|  | 		   [],"select $tmp from crash_me where b = 'a         '",[], | |||
|  | 		   'a         a         ',6); | |||
|  | } | |||
|  | 
 | |||
|  | if (!defined($limits{'multi_table_update'})) | |||
|  | { | |||
|  |   if (check_and_report("Update with many tables","multi_table_update", | |||
|  | 	   ["create table crash_q (a integer,b char(10))", | |||
|  | 	    "insert into crash_q values(1,'c')", | |||
|  | 	    "update crash_q left join crash_me on crash_q.a=crash_me.a set crash_q.b=crash_me.b"], | |||
|  |             "select b from crash_q", | |||
|  | 	   ["drop table crash_q $drop_attr"], | |||
|  | 	   "a",1,undef(),2)) | |||
|  |   { | |||
|  |     check_and_report("Update with many tables","multi_table_update", | |||
|  | 	     ["create table crash_q (a integer,b char(10))", | |||
|  | 	      "insert into crash_q values(1,'c')", | |||
|  | 	      "update crash_q,crash_me set crash_q.b=crash_me.b ". | |||
|  | 	      "where crash_q.a=crash_me.a"], | |||
|  | 	     "select b from crash_q", | |||
|  | 	     ["drop table crash_q $drop_attr"], | |||
|  | 		     "a",1, | |||
|  | 		    1); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | report("DELETE FROM table1,table2...","multi_table_delete", | |||
|  |        "create table crash_q (a integer,b char(10))", | |||
|  |        "insert into crash_q values(1,'c')", | |||
|  |        "delete crash_q.* from crash_q,crash_me where crash_q.a=crash_me.a", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | check_and_report("Update with sub select","select_table_update", | |||
|  | 		 ["create table crash_q (a integer,b char(10))", | |||
|  | 		  "insert into crash_q values(1,'c')", | |||
|  | 		  "update crash_q set b= ". | |||
|  | 		  "(select b from crash_me where crash_q.a = crash_me.a)"], | |||
|  | 		 "select b from crash_q", | |||
|  | 		 ["drop table crash_q $drop_attr"], | |||
|  | 		 "a",1); | |||
|  | 
 | |||
|  | check_and_report("Calculate 1--1","minus_neg",[], | |||
|  | 		 "select a--1 from crash_me",[],0,2); | |||
|  | 
 | |||
|  | report("ANSI SQL simple joins","simple_joins", | |||
|  |        "select crash_me.a from crash_me, crash_me t0"); | |||
|  | 
 | |||
|  | # | |||
|  | # Check max string size, and expression limits | |||
|  | # | |||
|  | $found=undef; | |||
|  | foreach $type (('mediumtext','text','text()','blob','long')) | |||
|  | { | |||
|  |   if ($limits{"type_extra_$type"} eq 'yes') | |||
|  |   { | |||
|  |     $found=$type; | |||
|  |     last; | |||
|  |   } | |||
|  | } | |||
|  | if (defined($found)) | |||
|  | { | |||
|  |   $found =~ s/\(\)/\(%d\)/; | |||
|  |   find_limit("max text or blob size","max_text_size", | |||
|  | 	     new query_many(["create table crash_q (q $found)", | |||
|  | 			     "insert into crash_q values ('%s')"], | |||
|  | 			    "select * from crash_q","%s", | |||
|  | 			    ["drop table crash_q $drop_attr"], | |||
|  | 			    min($max_string_size,$limits{'query_size'}-30))); | |||
|  | 
 | |||
|  | } | |||
|  | 
 | |||
|  | # It doesn't make lots of sense to check for string lengths much bigger than | |||
|  | # what can be stored... | |||
|  | 
 | |||
|  | find_limit(($prompt="constant string size in where"),"where_string_size", | |||
|  | 	   new query_repeat([],"select a from crash_me where b >='", | |||
|  | 			    "","","1","","'")); | |||
|  | if ($limits{'where_string_size'} == 10) | |||
|  | { | |||
|  |   save_config_data('where_string_size','nonstandard',$prompt); | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'select_constants'} eq 'yes') | |||
|  | { | |||
|  |   find_limit("constant string size in SELECT","select_string_size", | |||
|  | 	     new query_repeat([],"select '","","","a","","'$end_query")); | |||
|  | } | |||
|  | 
 | |||
|  | goto no_functions if ($limits{'functions'} ne "yes"); | |||
|  | 
 | |||
|  | if ($limits{'func_odbc_repeat'} eq 'yes') | |||
|  | { | |||
|  |   find_limit("return string size from function","repeat_string_size", | |||
|  | 	     new query_many([], | |||
|  | 			    "select repeat('a',%d) $end_query","%s", | |||
|  | 			    [], | |||
|  | 			    $max_string_size,0)); | |||
|  | } | |||
|  | 
 | |||
|  | $tmp=find_limit("simple expressions","max_expressions", | |||
|  | 		new query_repeat([],"select 1","","","+1","",$end_query, | |||
|  | 				 undef(),$max_expressions)); | |||
|  | 
 | |||
|  | if ($tmp > 10) | |||
|  | { | |||
|  |   $tmp= "(1" . ( '+1' x ($tmp-10) ) . ")"; | |||
|  |   find_limit("big expressions", "max_big_expressions", | |||
|  | 	     new query_repeat([],"select 0","","","+$tmp","",$end_query, | |||
|  | 			      undef(),$max_big_expressions)); | |||
|  | } | |||
|  | 
 | |||
|  | find_limit("stacked expressions", "max_stack_expression", | |||
|  | 	   new query_repeat([],"select 1","","","+(1",")",$end_query, | |||
|  | 				undef(),$max_stacked_expressions)); | |||
|  | 
 | |||
|  | no_functions: | |||
|  | 
 | |||
|  | if (!defined($limits{'max_conditions'})) | |||
|  | { | |||
|  |   find_limit("OR and AND in WHERE","max_conditions", | |||
|  | 	     new query_repeat([], | |||
|  | 			      "select a from crash_me where a=1 and b='a'","", | |||
|  | 			      "", " or a=%d and b='%d'","","","", | |||
|  | 			      [],($query_size-42)/29,undef,2)); | |||
|  |   $limits{'max_conditions'}*=2; | |||
|  | } | |||
|  | # The 42 is the length of the constant part. | |||
|  | # The 29 is the length of the variable part, plus two seven-digit numbers. | |||
|  | 
 | |||
|  | find_limit("tables in join", "join_tables", | |||
|  | 	   new query_repeat([], | |||
|  | 			    "select crash_me.a",",t%d.a","from crash_me", | |||
|  | 			    ",crash_me t%d","","",[],$max_join_tables,undef, | |||
|  | 			    1)); | |||
|  | 
 | |||
|  | # Different CREATE TABLE options | |||
|  | 
 | |||
|  | report("primary key in create table",'primary_key_in_create', | |||
|  |        "create table crash_q (q integer not null,primary key (q))", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("unique in create table",'unique_in_create', | |||
|  |        "create table crash_q (q integer not null,unique (q))", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | if ($limits{'unique_in_create'} eq 'yes') | |||
|  | { | |||
|  |   report("unique null in create",'unique_null_in_create', | |||
|  | 	 "create table crash_q (q integer,unique (q))", | |||
|  | 	 "insert into crash_q (q) values (NULL)", | |||
|  | 	 "insert into crash_q (q) values (NULL)", | |||
|  | 	 "insert into crash_q (q) values (1)", | |||
|  | 	 "drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | report("default value for column",'create_default', | |||
|  |        "create table crash_q (q integer default 10 not null)", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("default value function for column",'create_default_func', | |||
|  |        "create table crash_q (q integer not null,q1 integer default (1+1))", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("temporary tables",'temporary_table', | |||
|  |        "create temporary table crash_q (q integer not null)", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report_one("create table from select",'create_table_select', | |||
|  | 	   [["create table crash_q SELECT * from crash_me","yes"], | |||
|  | 	    ["create table crash_q AS SELECT * from crash_me","with AS"]]); | |||
|  | $dbh->do("drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("index in create table",'index_in_create', | |||
|  |        "create table crash_q (q integer not null,index (q))", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | # The following must be executed as we need the value of end_drop_keyword | |||
|  | # later | |||
|  | if (!(defined($limits{'create_index'}) && defined($limits{'drop_index'}))) | |||
|  | { | |||
|  |   if ($res=safe_query_l('create_index',"create index crash_q on crash_me (a)")) | |||
|  |   { | |||
|  |     $res="yes"; | |||
|  |     $drop_res="yes"; | |||
|  |     $end_drop_keyword=""; | |||
|  |     if (!safe_query_l('drop_index',"drop index crash_q")) | |||
|  |     { | |||
|  |       # Can't drop the standard way; Check if mSQL | |||
|  |       if (safe_query_l('drop_index',"drop index crash_q from crash_me")) | |||
|  |       { | |||
|  |         $drop_res="with 'FROM'";	# Drop is not ANSI SQL | |||
|  |         $end_drop_keyword="drop index %i from %t"; | |||
|  |       } | |||
|  |       # else check if Access or MySQL | |||
|  |       elsif (safe_query_l('drop_index',"drop index crash_q on crash_me")) | |||
|  |       { | |||
|  |         $drop_res="with 'ON'";	# Drop is not ANSI SQL | |||
|  |         $end_drop_keyword="drop index %i on %t"; | |||
|  |       } | |||
|  |       # else check if MS-SQL | |||
|  |       elsif (safe_query_l('drop_index',"drop index crash_me.crash_q")) | |||
|  |       { | |||
|  |         $drop_res="with 'table.index'"; # Drop is not ANSI SQL | |||
|  |         $end_drop_keyword="drop index %t.%i"; | |||
|  |       } | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       # Old MySQL 3.21 supports only the create index syntax | |||
|  |       # This means that the second create doesn't give an error. | |||
|  |       $res=safe_query_l('create_index',["create index crash_q on crash_me (a)", | |||
|  |       		     "create index crash_q on crash_me (a)", | |||
|  |       		     "drop index crash_q"]); | |||
|  |       $res= $res ? 'ignored' : 'yes'; | |||
|  |     } | |||
|  |   } | |||
|  |   else | |||
|  |   { | |||
|  |     $drop_res=$res='no'; | |||
|  |   } | |||
|  |   save_config_data('create_index',$res,"create index"); | |||
|  |   save_config_data('drop_index',$drop_res,"drop index"); | |||
|  | 
 | |||
|  |   print "create index: $limits{'create_index'}\n"; | |||
|  |   print "drop index: $limits{'drop_index'}\n"; | |||
|  | } | |||
|  | 
 | |||
|  | # check if we can have 'NULL' as a key | |||
|  | check_and_report("null in index","null_in_index", | |||
|  | 		 [create_table("crash_q",["a char(10)"],["(a)"]), | |||
|  | 		  "insert into crash_q values (NULL)"], | |||
|  | 		 "select * from crash_q", | |||
|  | 		 ["drop table crash_q $drop_attr"], | |||
|  | 		 undef(),4); | |||
|  | 
 | |||
|  | if ($limits{'unique_in_create'} eq 'yes') | |||
|  | { | |||
|  |   report("null in unique index",'null_in_unique', | |||
|  |           create_table("crash_q",["q integer"],["unique(q)"]), | |||
|  | 	 "insert into crash_q (q) values(NULL)", | |||
|  | 	 "insert into crash_q (q) values(NULL)", | |||
|  | 	 "drop table crash_q $drop_attr"); | |||
|  |   report("null combination in unique index",'nulls_in_unique', | |||
|  |           create_table("crash_q",["q integer,q1 integer"],["unique(q,q1)"]), | |||
|  | 	 "insert into crash_q (q,q1) values(1,NULL)", | |||
|  | 	 "insert into crash_q (q,q1) values(1,NULL)", | |||
|  | 	 "drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'null_in_unique'} eq 'yes') | |||
|  | { | |||
|  |   report("null in unique index",'multi_null_in_unique', | |||
|  |           create_table("crash_q",["q integer, x integer"],["unique(q)"]), | |||
|  | 	 "insert into crash_q(x) values(1)", | |||
|  | 	 "insert into crash_q(x) values(2)", | |||
|  | 	 "drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'create_index'} ne 'no') | |||
|  | { | |||
|  |   $end_drop=$end_drop_keyword; | |||
|  |   $end_drop =~ s/%i/crash_q/; | |||
|  |   $end_drop =~ s/%t/crash_me/; | |||
|  |   report("index on column part (extension)","index_parts",, | |||
|  | 	 "create index crash_q on crash_me (b(5))", | |||
|  | 	 $end_drop); | |||
|  |   $end_drop=$end_drop_keyword; | |||
|  |   $end_drop =~ s/%i/crash_me/; | |||
|  |   $end_drop =~ s/%t/crash_me/; | |||
|  |   report("different namespace for index", | |||
|  | 	 "index_namespace", | |||
|  | 	 "create index crash_me on crash_me (b)", | |||
|  | 	 $end_drop); | |||
|  | } | |||
|  | 
 | |||
|  | if (!report("case independent table names","table_name_case", | |||
|  | 	    "create table crash_q (q integer)", | |||
|  | 	    "drop table CRASH_Q $drop_attr")) | |||
|  | { | |||
|  |   safe_query("drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | if (!report("case independent field names","field_name_case", | |||
|  | 	    "create table crash_q (q integer)", | |||
|  | 	    "insert into crash_q(Q) values (1)", | |||
|  | 	    "drop table crash_q $drop_attr")) | |||
|  | { | |||
|  |   safe_query("drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | if (!report("drop table if exists","drop_if_exists", | |||
|  | 	    "create table crash_q (q integer)", | |||
|  | 	    "drop table if exists crash_q $drop_attr")) | |||
|  | { | |||
|  |   safe_query("drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | report("create table if not exists","create_if_not_exists", | |||
|  |        "create table crash_q (q integer)", | |||
|  |        "create table if not exists crash_q (q integer)"); | |||
|  | safe_query("drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | # | |||
|  | # test of different join types | |||
|  | # | |||
|  | 
 | |||
|  | assert("create table crash_me2 (a integer not null,b char(10) not null,". | |||
|  |        " c1 integer)"); | |||
|  | assert("insert into crash_me2 (a,b,c1) values (1,'b',1)"); | |||
|  | assert("create table crash_me3 (a integer not null,b char(10) not null)"); | |||
|  | assert("insert into crash_me3 (a,b) values (1,'b')"); | |||
|  | 
 | |||
|  | report("inner join","inner_join", | |||
|  |        "select crash_me.a from crash_me inner join crash_me2 ON ". | |||
|  |        "crash_me.a=crash_me2.a"); | |||
|  | report("left outer join","left_outer_join", | |||
|  |        "select crash_me.a from crash_me left join crash_me2 ON ". | |||
|  |        "crash_me.a=crash_me2.a"); | |||
|  | report("natural left outer join","natural_left_outer_join", | |||
|  |        "select c1 from crash_me natural left join crash_me2"); | |||
|  | report("left outer join using","left_outer_join_using", | |||
|  |        "select c1 from crash_me left join crash_me2 using (a)"); | |||
|  | report("left outer join odbc style","odbc_left_outer_join", | |||
|  |        "select crash_me.a from { oj crash_me left outer join crash_me2 ON". | |||
|  |        " crash_me.a=crash_me2.a }"); | |||
|  | report("right outer join","right_outer_join", | |||
|  |        "select crash_me.a from crash_me right join crash_me2 ON ". | |||
|  |        "crash_me.a=crash_me2.a"); | |||
|  | report("full outer join","full_outer_join", | |||
|  |        "select crash_me.a from crash_me full join crash_me2 ON "." | |||
|  |        crash_me.a=crash_me2.a"); | |||
|  | report("cross join (same as from a,b)","cross_join", | |||
|  |        "select crash_me.a from crash_me cross join crash_me3"); | |||
|  | report("natural join","natural_join", | |||
|  |        "select * from crash_me natural join crash_me3"); | |||
|  | report("union","union", | |||
|  |        "select * from crash_me union select a,b from crash_me3"); | |||
|  | report("union all","union_all", | |||
|  |        "select * from crash_me union all select a,b from crash_me3"); | |||
|  | report("intersect","intersect", | |||
|  |        "select * from crash_me intersect select * from crash_me3"); | |||
|  | report("intersect all","intersect_all", | |||
|  |        "select * from crash_me intersect all select * from crash_me3"); | |||
|  | report("except","except", | |||
|  |        "select * from crash_me except select * from crash_me3"); | |||
|  | report("except all","except_all", | |||
|  |        "select * from crash_me except all select * from crash_me3"); | |||
|  | report("except","except", | |||
|  |        "select * from crash_me except select * from crash_me3"); | |||
|  | report("except all","except_all", | |||
|  |        "select * from crash_me except all select * from crash_me3"); | |||
|  | report("minus","minus", | |||
|  |        "select * from crash_me minus select * from crash_me3"); # oracle ... | |||
|  | 
 | |||
|  | report("natural join (incompatible lists)","natural_join_incompat", | |||
|  |        "select c1 from crash_me natural join crash_me2"); | |||
|  | report("union (incompatible lists)","union_incompat", | |||
|  |        "select * from crash_me union select a,b from crash_me2"); | |||
|  | report("union all (incompatible lists)","union_all_incompat", | |||
|  |        "select * from crash_me union all select a,b from crash_me2"); | |||
|  | report("intersect (incompatible lists)","intersect_incompat", | |||
|  |        "select * from crash_me intersect select * from crash_me2"); | |||
|  | report("intersect all (incompatible lists)","intersect_all_incompat", | |||
|  |        "select * from crash_me intersect all select * from crash_me2"); | |||
|  | report("except (incompatible lists)","except_incompat", | |||
|  |        "select * from crash_me except select * from crash_me2"); | |||
|  | report("except all (incompatible lists)","except_all_incompat", | |||
|  |        "select * from crash_me except all select * from crash_me2"); | |||
|  | report("except (incompatible lists)","except_incompat", | |||
|  |        "select * from crash_me except select * from crash_me2"); | |||
|  | report("except all (incompatible lists)","except_all_incompat", | |||
|  |        "select * from crash_me except all select * from crash_me2"); | |||
|  | report("minus (incompatible lists)","minus_incompat", | |||
|  |        "select * from crash_me minus select * from crash_me2"); # oracle ... | |||
|  | 
 | |||
|  | assert("drop table crash_me2 $drop_attr"); | |||
|  | assert("drop table crash_me3 $drop_attr"); | |||
|  | 
 | |||
|  | # somethings to be added here .... | |||
|  | # FOR UNION - INTERSECT - EXCEPT -> CORRESPONDING [ BY ] | |||
|  | # after subqueries: | |||
|  | # >ALL | ANY | SOME - EXISTS - UNIQUE | |||
|  | 
 | |||
|  | if (report("subqueries","subqueries", | |||
|  | 	   "select a from crash_me where crash_me.a in ". | |||
|  | 	   "(select max(a) from crash_me)")) | |||
|  | { | |||
|  |     $tmp=new query_repeat([],"select a from crash_me","","", | |||
|  | 			  " where a in (select a from crash_me",")", | |||
|  | 			  "",[],$max_join_tables); | |||
|  |     find_limit("recursive subqueries", "recursive_subqueries",$tmp); | |||
|  | } | |||
|  | 
 | |||
|  | report("insert INTO ... SELECT ...","insert_select", | |||
|  |        "create table crash_q (a int)", | |||
|  |        "insert into crash_q (a) SELECT crash_me.a from crash_me", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | if (!defined($limits{"transactions"})) | |||
|  | { | |||
|  |   my ($limit,$type); | |||
|  |   $limit="transactions"; | |||
|  |   $limit_r="rollback_metadata"; | |||
|  |   print "$limit: "; | |||
|  |   foreach $type (('', 'type=bdb', 'type=innodb', 'type=gemini')) | |||
|  |   { | |||
|  |     undef($limits{$limit}); | |||
|  |     if (!report_trans($limit, | |||
|  | 			   [create_table("crash_q",["a integer not null"],[], | |||
|  | 					 $type), | |||
|  | 			    "insert into crash_q values (1)"], | |||
|  | 			   "select * from crash_q", | |||
|  | 			   "drop table crash_q $drop_attr" | |||
|  | 			  )) | |||
|  |      { | |||
|  |        report_rollback($limit_r, | |||
|  |               [create_table("crash_q",["a integer not null"],[], | |||
|  | 				 $type)], | |||
|  | 			    "insert into crash_q values (1)", | |||
|  | 			   "drop table crash_q $drop_attr" ); | |||
|  |      last; | |||
|  |      }; | |||
|  |   } | |||
|  |   print "$limits{$limit}\n"; | |||
|  |   print "$limit_r: $limits{$limit_r}\n"; | |||
|  | } | |||
|  | 
 | |||
|  | report("atomic updates","atomic_updates", | |||
|  |        create_table("crash_q",["a integer not null"],["primary key (a)"]), | |||
|  |        "insert into crash_q values (2)", | |||
|  |        "insert into crash_q values (3)", | |||
|  |        "insert into crash_q values (1)", | |||
|  |        "update crash_q set a=a+1", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | if ($limits{'atomic_updates'} eq 'yes') | |||
|  | { | |||
|  |   report_fail("atomic_updates_with_rollback","atomic_updates_with_rollback", | |||
|  | 	      create_table("crash_q",["a integer not null"], | |||
|  | 			   ["primary key (a)"]), | |||
|  | 	      "insert into crash_q values (2)", | |||
|  | 	      "insert into crash_q values (3)", | |||
|  | 	      "insert into crash_q values (1)", | |||
|  | 	      "update crash_q set a=a+1 where a < 3", | |||
|  | 	      "drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | # To add with the views: | |||
|  | # DROP VIEW - CREAT VIEW *** [ WITH [ CASCADE | LOCAL ] CHECK OPTION ] | |||
|  | report("views","views", | |||
|  |        "create view crash_q as select a from crash_me", | |||
|  |        "drop view crash_q $drop_attr"); | |||
|  | 
 | |||
|  | #  Test: foreign key | |||
|  | { | |||
|  |  my $result = 'undefined'; | |||
|  |  my $error; | |||
|  |  print "foreign keys: "; | |||
|  |  save_incomplete('foreign_key','foreign keys'); | |||
|  | 
 | |||
|  | # 1) check if foreign keys are supported | |||
|  |  safe_query_l('foreign_key', | |||
|  | 	      create_table("crash_me_qf", | |||
|  | 			   ["a integer not null"], | |||
|  | 			   ["primary key (a)"])); | |||
|  |  $error= safe_query_l('foreign_key', | |||
|  | 		      create_table("crash_me_qf2", | |||
|  | 				   ["a integer not null", | |||
|  | 				    "foreign key (a) references crash_me_qf (a)"], | |||
|  | 				   [])); | |||
|  | 
 | |||
|  |  if ($error == 1)         # OK  -- syntax is supported  | |||
|  |  { | |||
|  |    $result = 'error'; | |||
|  |    # now check if foreign key really works | |||
|  |    safe_query_l('foreign_key', "insert into crash_me_qf values (1)"); | |||
|  |    if (safe_query_l('foreign_key', "insert into crash_me_qf2 values (2)") eq 1) | |||
|  |    { | |||
|  |      $result = 'syntax only'; | |||
|  |    } | |||
|  |    else | |||
|  |    { | |||
|  |      $result = 'yes'; | |||
|  |    } | |||
|  |  } | |||
|  |  else | |||
|  |  { | |||
|  |    $result = "no"; | |||
|  |  } | |||
|  |  safe_query_l('foreign_key', "drop table crash_me_qf2 $drop_attr"); | |||
|  |  safe_query_l('foreign_key', "drop table crash_me_qf $drop_attr"); | |||
|  |  print "$result\n"; | |||
|  |  save_config_data('foreign_key',$result,"foreign keys"); | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'foreign_key'} eq 'yes') | |||
|  | { | |||
|  |   report("allows to update of foreign key values",'foreign_update', | |||
|  |    "create table crash_me1 (a int not null primary key)", | |||
|  |    "create table crash_me2 (a int not null," . | |||
|  |       " foreign key (a) references crash_me1 (a))", | |||
|  |    "insert into crash_me1 values (1)", | |||
|  |    "insert into crash_me2 values (1)", | |||
|  |    "update crash_me1 set a = 2",       ## <- must fail  | |||
|  |    "drop table crash_me2 $drop_attr",  | |||
|  |    "drop table crash_me1 $drop_attr"  | |||
|  |   ); | |||
|  | } | |||
|  | 
 | |||
|  | report("Create SCHEMA","create_schema", | |||
|  |        "create schema crash_schema create table crash_q (a int) ". | |||
|  |        "create table crash_q2(b int)", | |||
|  |        "drop schema crash_schema cascade"); | |||
|  | 
 | |||
|  | if ($limits{'foreign_key'} eq 'yes') | |||
|  | { | |||
|  |   if ($limits{'create_schema'} eq 'yes') | |||
|  |   { | |||
|  |     report("Circular foreign keys","foreign_key_circular", | |||
|  |            "create schema crash_schema create table crash_q ". | |||
|  | 	   "(a int primary key, b int, foreign key (b) references ". | |||
|  | 	   "crash_q2(a)) create table crash_q2(a int, b int, ". | |||
|  | 	   "primary key(a), foreign key (b) references crash_q(a))", | |||
|  |            "drop schema crash_schema cascade"); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | if ($limits{'func_sql_character_length'} eq 'yes') | |||
|  | { | |||
|  |   my $result = 'error'; | |||
|  |   my ($resultset); | |||
|  |   my $key = 'length_of_varchar_field'; | |||
|  |   my $prompt='CHARACTER_LENGTH(varchar_field)'; | |||
|  |   print $prompt," = "; | |||
|  |   if (!defined($limits{$key})) { | |||
|  |     save_incomplete($key,$prompt); | |||
|  |     safe_query_l($key,[ | |||
|  | 		       "CREATE TABLE crash_me1 (S1 VARCHAR(100))", | |||
|  | 		       "INSERT INTO crash_me1 VALUES ('X')" | |||
|  | 		       ]); | |||
|  |     my $recset = get_recordset($key, | |||
|  | 			       "SELECT CHARACTER_LENGTH(S1) FROM crash_me1"); | |||
|  |     print_recordset($key,$recset); | |||
|  |     if (defined($recset)){ | |||
|  |       if ( $recset->[0][0] eq 1 ) { | |||
|  | 		$result = 'actual length'; | |||
|  | 	      } elsif( $recset->[0][0] eq 100 ) { | |||
|  | 		$result = 'defined length'; | |||
|  | 	      }; | |||
|  |     } else { | |||
|  |       add_log($key,$DBI::errstr); | |||
|  |     } | |||
|  |     safe_query_l($key, "drop table crash_me1 $drop_attr"); | |||
|  |     save_config_data($key,$result,$prompt); | |||
|  |   } else { | |||
|  |     $result = $limits{$key}; | |||
|  |   }; | |||
|  |   print "$result\n"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | check_constraint("Column constraints","constraint_check", | |||
|  |            "create table crash_q (a int check (a>0))", | |||
|  |            "insert into crash_q values(0)", | |||
|  |            "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | 
 | |||
|  | check_constraint("Table constraints","constraint_check_table", | |||
|  |        "create table crash_q (a int ,b int, check (a>b))", | |||
|  |        "insert into crash_q values(0,0)", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | check_constraint("Named constraints","constraint_check_named", | |||
|  |        "create table crash_q (a int ,b int, constraint abc check (a>b))", | |||
|  |        "insert into crash_q values(0,0)", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | 
 | |||
|  | report("NULL constraint (SyBase style)","constraint_null", | |||
|  |        "create table crash_q (a int null)", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("Triggers (ANSI SQL)","psm_trigger", | |||
|  |        "create table crash_q (a int ,b int)", | |||
|  |        "create trigger crash_trigger after insert on crash_q referencing ". | |||
|  |        "new table as new_a when (localtime > time '18:00:00') ". | |||
|  |        "begin atomic end", | |||
|  |        "insert into crash_q values(1,2)", | |||
|  |        "drop trigger crash_trigger", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("PSM procedures (ANSI SQL)","psm_procedures", | |||
|  |        "create table crash_q (a int,b int)", | |||
|  |        "create procedure crash_proc(in a1 int, in b1 int) language ". | |||
|  |        "sql modifies sql data begin declare c1 int; set c1 = a1 + b1;". | |||
|  |        " insert into crash_q(a,b) values (a1,c1); end", | |||
|  |        "call crash_proc(1,10)", | |||
|  |        "drop procedure crash_proc", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("PSM modules (ANSI SQL)","psm_modules", | |||
|  |        "create table crash_q (a int,b int)", | |||
|  |        "create module crash_m declare procedure ". | |||
|  |          "crash_proc(in a1 int, in b1 int) language sql modifies sql ". | |||
|  |          "data begin declare c1 int; set c1 = a1 + b1; ". | |||
|  |          "insert into crash_q(a,b) values (a1,c1); end; ". | |||
|  |          "declare procedure crash_proc2(INOUT a int, in b int) ". | |||
|  |          "contains sql set a = b + 10; end module", | |||
|  |        "call crash_proc(1,10)", | |||
|  |        "drop module crash_m cascade", | |||
|  |        "drop table crash_q cascade $drop_attr"); | |||
|  | 
 | |||
|  | report("PSM functions (ANSI SQL)","psm_functions", | |||
|  |        "create table crash_q (a int)", | |||
|  |        "create function crash_func(in a1 int, in b1 int) returns int". | |||
|  |          " language sql deterministic contains sql ". | |||
|  | 	 " begin return a1 * b1; end", | |||
|  |        "insert into crash_q values(crash_func(2,4))", | |||
|  |        "select a,crash_func(a,2) from crash_q", | |||
|  |        "drop function crash_func cascade", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("Domains (ANSI SQL)","domains", | |||
|  |        "create domain crash_d as varchar(10) default 'Empty' ". | |||
|  |          "check (value <> 'abcd')", | |||
|  |        "create table crash_q(a crash_d, b int)", | |||
|  |        "insert into crash_q(a,b) values('xyz',10)", | |||
|  |        "insert into crash_q(b) values(10)", | |||
|  |        "drop table crash_q $drop_attr", | |||
|  |        "drop domain crash_d"); | |||
|  | 
 | |||
|  | 
 | |||
|  | if (!defined($limits{'lock_tables'})) | |||
|  | { | |||
|  |   report("lock table","lock_tables", | |||
|  | 	 "lock table crash_me READ", | |||
|  | 	 "unlock tables"); | |||
|  |   if ($limits{'lock_tables'} eq 'no') | |||
|  |   { | |||
|  |     delete $limits{'lock_tables'}; | |||
|  |     report("lock table","lock_tables", | |||
|  | 	   "lock table crash_me IN SHARE MODE"); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | if (!report("many tables to drop table","multi_drop", | |||
|  | 	   "create table crash_q (a int)", | |||
|  | 	   "create table crash_q2 (a int)", | |||
|  | 	   "drop table crash_q,crash_q2 $drop_attr")) | |||
|  | { | |||
|  |   $dbh->do("drop table crash_q $drop_attr"); | |||
|  |   $dbh->do("drop table crash_q2 $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | if (!report("drop table with cascade/restrict","drop_restrict", | |||
|  | 	   "create table crash_q (a int)", | |||
|  | 	   "drop table crash_q restrict")) | |||
|  | { | |||
|  |   $dbh->do("drop table crash_q $drop_attr"); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | report("-- as comment (ANSI)","comment_--", | |||
|  |        "select * from crash_me -- Testing of comments"); | |||
|  | report("// as comment","comment_//", | |||
|  |        "select * from crash_me // Testing of comments"); | |||
|  | report("# as comment","comment_#", | |||
|  |        "select * from crash_me # Testing of comments"); | |||
|  | report("/* */ as comment","comment_/**/", | |||
|  |        "select * from crash_me /* Testing of comments */"); | |||
|  | 
 | |||
|  | # | |||
|  | # Check things that fails one some servers | |||
|  | # | |||
|  | 
 | |||
|  | # Empress can't insert empty strings in a char() field | |||
|  | report("insert empty string","insert_empty_string", | |||
|  |        create_table("crash_q",["a char(10) not null,b char(10)"],[]), | |||
|  |        "insert into crash_q values ('','')", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | report("Having with alias","having_with_alias", | |||
|  |        create_table("crash_q",["a integer"],[]), | |||
|  |        "insert into crash_q values (10)", | |||
|  |        "select sum(a) as b from crash_q group by a having b > 0", | |||
|  |        "drop table crash_q $drop_attr"); | |||
|  | 
 | |||
|  | # | |||
|  | # test name limits | |||
|  | # | |||
|  | 
 | |||
|  | find_limit("table name length","max_table_name", | |||
|  | 	   new query_many(["create table crash_q%s (q integer)", | |||
|  | 			   "insert into crash_q%s values(1)"], | |||
|  | 			   "select * from crash_q%s",1, | |||
|  | 			   ["drop table crash_q%s $drop_attr"], | |||
|  | 			   $max_name_length,7,1)); | |||
|  | 
 | |||
|  | find_limit("column name length","max_column_name", | |||
|  | 	   new query_many(["create table crash_q (q%s integer)", | |||
|  | 			  "insert into crash_q (q%s) values(1)"], | |||
|  | 			  "select q%s from crash_q",1, | |||
|  | 			  ["drop table crash_q $drop_attr"], | |||
|  | 			   $max_name_length,1)); | |||
|  | 
 | |||
|  | if ($limits{'column_alias'} eq 'yes') | |||
|  | { | |||
|  |   find_limit("select alias name length","max_select_alias_name", | |||
|  | 	   new query_many(undef, | |||
|  | 			  "select b as %s from crash_me",undef, | |||
|  | 			  undef, $max_name_length)); | |||
|  | } | |||
|  | 
 | |||
|  | find_limit("table alias name length","max_table_alias_name", | |||
|  | 	   new query_many(undef, | |||
|  | 			  "select %s.b from crash_me %s", | |||
|  | 			  undef, | |||
|  | 			  undef, $max_name_length)); | |||
|  | 
 | |||
|  | $end_drop_keyword = "drop index %i" if (!$end_drop_keyword); | |||
|  | $end_drop=$end_drop_keyword; | |||
|  | $end_drop =~ s/%i/crash_q%s/; | |||
|  | $end_drop =~ s/%t/crash_me/; | |||
|  | 
 | |||
|  | if ($limits{'create_index'} ne 'no') | |||
|  | { | |||
|  |   find_limit("index name length","max_index_name", | |||
|  | 	     new query_many(["create index crash_q%s on crash_me (a)"], | |||
|  | 			    undef,undef, | |||
|  | 			    [$end_drop], | |||
|  | 			    $max_name_length,7)); | |||
|  | } | |||
|  | 
 | |||
|  | find_limit("max char() size","max_char_size", | |||
|  | 	   new query_many(["create table crash_q (q char(%d))", | |||
|  | 			   "insert into crash_q values ('%s')"], | |||
|  | 			  "select * from crash_q","%s", | |||
|  | 			  ["drop table crash_q $drop_attr"], | |||
|  | 			  min($max_string_size,$limits{'query_size'}))); | |||
|  | 
 | |||
|  | if ($limits{'type_sql_varchar(1_arg)'} eq 'yes') | |||
|  | { | |||
|  |   find_limit("max varchar() size","max_varchar_size", | |||
|  | 	     new query_many(["create table crash_q (q varchar(%d))", | |||
|  | 			     "insert into crash_q values ('%s')"], | |||
|  | 			    "select * from crash_q","%s", | |||
|  | 			    ["drop table crash_q $drop_attr"], | |||
|  | 			    min($max_string_size,$limits{'query_size'}))); | |||
|  | } | |||
|  | 
 | |||
|  | $found=undef; | |||
|  | foreach $type (('mediumtext','text','text()','blob','long')) | |||
|  | { | |||
|  |   if ($limits{"type_extra_$type"} eq 'yes') | |||
|  |   { | |||
|  |     $found=$type; | |||
|  |     last; | |||
|  |   } | |||
|  | } | |||
|  | if (defined($found)) | |||
|  | { | |||
|  |   $found =~ s/\(\)/\(%d\)/; | |||
|  |   find_limit("max text or blob size","max_text_size", | |||
|  | 	     new query_many(["create table crash_q (q $found)", | |||
|  | 			     "insert into crash_q values ('%s')"], | |||
|  | 			    "select * from crash_q","%s", | |||
|  | 			    ["drop table crash_q $drop_attr"], | |||
|  | 			    min($max_string_size,$limits{'query_size'}-30))); | |||
|  | 
 | |||
|  | } | |||
|  | 
 | |||
|  | $tmp=new query_repeat([],"create table crash_q (a integer","","", | |||
|  | 		      ",a%d integer","",")",["drop table crash_q $drop_attr"], | |||
|  | 		      $max_columns); | |||
|  | $tmp->{'offset'}=1; | |||
|  | find_limit("Columns in table","max_columns",$tmp); | |||
|  | 
 | |||
|  | # Make a field definition to be used when testing keys | |||
|  | 
 | |||
|  | $key_definitions="q0 integer not null"; | |||
|  | $key_fields="q0"; | |||
|  | for ($i=1; $i < min($limits{'max_columns'},$max_keys) ; $i++) | |||
|  | { | |||
|  |   $key_definitions.=",q$i integer not null"; | |||
|  |   $key_fields.=",q$i"; | |||
|  | } | |||
|  | $key_values="1," x $i; | |||
|  | chop($key_values); | |||
|  | 
 | |||
|  | if ($limits{'unique_in_create'} eq 'yes') | |||
|  | { | |||
|  |   find_limit("unique indexes","max_unique_index", | |||
|  | 	     new query_table("create table crash_q (q integer", | |||
|  | 			     ",q%d integer not null,unique (q%d)",")", | |||
|  | 			     ["insert into crash_q (q,%f) values (1,%v)"], | |||
|  | 			     "select q from crash_q",1, | |||
|  | 			     "drop table crash_q $drop_attr", | |||
|  | 			     $max_keys,0)); | |||
|  | 
 | |||
|  |   find_limit("index parts","max_index_parts", | |||
|  | 	     new query_table("create table crash_q ". | |||
|  | 	         "($key_definitions,unique (q0", | |||
|  | 			     ",q%d","))", | |||
|  |  	     ["insert into crash_q ($key_fields) values ($key_values)"], | |||
|  | 	     "select q0 from crash_q",1, | |||
|  | 	     "drop table crash_q $drop_attr", | |||
|  | 	     $max_keys,1)); | |||
|  | 
 | |||
|  |   find_limit("max index part length","max_index_part_length", | |||
|  | 	     new query_many(["create table crash_q (q char(%d) not null,". | |||
|  | 	           "unique(q))", | |||
|  | 		     "insert into crash_q (q) values ('%s')"], | |||
|  | 		    "select q from crash_q","%s", | |||
|  | 		    ["drop table crash_q $drop_attr"], | |||
|  | 		    $limits{'max_char_size'},0)); | |||
|  | 
 | |||
|  |   if ($limits{'type_sql_varchar(1_arg)'} eq 'yes') | |||
|  |   { | |||
|  |     find_limit("index varchar part length","max_index_varchar_part_length", | |||
|  | 	     new query_many(["create table crash_q (q varchar(%d) not null,". | |||
|  | 	                "unique(q))", | |||
|  | 			 "insert into crash_q (q) values ('%s')"], | |||
|  | 			"select q from crash_q","%s", | |||
|  | 			["drop table crash_q $drop_attr"], | |||
|  | 			$limits{'max_varchar_size'},0)); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | if ($limits{'create_index'} ne 'no') | |||
|  | { | |||
|  |   if ($limits{'create_index'} eq 'ignored' || | |||
|  |       $limits{'unique_in_create'} eq 'yes') | |||
|  |   {                                     # This should be true | |||
|  |     add_log('max_index', | |||
|  |      " max_unique_index=$limits{'max_unique_index'} ,". | |||
|  |      "so max_index must be same"); | |||
|  |     save_config_data('max_index',$limits{'max_unique_index'},"max index"); | |||
|  |     print "indexes: $limits{'max_index'}\n"; | |||
|  |   } | |||
|  |   else | |||
|  |   { | |||
|  |     if (!defined($limits{'max_index'})) | |||
|  |     { | |||
|  |       safe_query_l('max_index',"create table crash_q ($key_definitions)"); | |||
|  |       for ($i=1; $i <= min($limits{'max_columns'},$max_keys) ; $i++) | |||
|  |       { | |||
|  | 	last if (!safe_query_l('max_index', | |||
|  | 	     "create index crash_q$i on crash_q (q$i)")); | |||
|  |       } | |||
|  |       save_config_data('max_index',$i == $max_keys ? $max_keys : $i, | |||
|  | 		       "max index"); | |||
|  |       while ( --$i > 0) | |||
|  |       { | |||
|  | 	$end_drop=$end_drop_keyword; | |||
|  | 	$end_drop =~ s/%i/crash_q$i/; | |||
|  | 	$end_drop =~ s/%t/crash_q/; | |||
|  | 	assert($end_drop); | |||
|  |       } | |||
|  |       assert("drop table crash_q $drop_attr"); | |||
|  |     } | |||
|  |     print "indexs: $limits{'max_index'}\n"; | |||
|  |     if (!defined($limits{'max_unique_index'})) | |||
|  |     { | |||
|  |       safe_query_l('max_unique_index', | |||
|  |            "create table crash_q ($key_definitions)"); | |||
|  |       for ($i=0; $i < min($limits{'max_columns'},$max_keys) ; $i++) | |||
|  |       { | |||
|  | 	last if (!safe_query_l('max_unique_index', | |||
|  | 	    "create unique index crash_q$i on crash_q (q$i)")); | |||
|  |       } | |||
|  |       save_config_data('max_unique_index',$i == $max_keys ? $max_keys : $i, | |||
|  | 		       "max unique index"); | |||
|  |       while ( --$i >= 0) | |||
|  |       { | |||
|  | 	$end_drop=$end_drop_keyword; | |||
|  | 	$end_drop =~ s/%i/crash_q$i/; | |||
|  | 	$end_drop =~ s/%t/crash_q/; | |||
|  | 	assert($end_drop); | |||
|  |       } | |||
|  |       assert("drop table crash_q $drop_attr"); | |||
|  |     } | |||
|  |     print "unique indexes: $limits{'max_unique_index'}\n"; | |||
|  |     if (!defined($limits{'max_index_parts'})) | |||
|  |     { | |||
|  |       safe_query_l('max_index_parts', | |||
|  |             "create table crash_q ($key_definitions)"); | |||
|  |       $end_drop=$end_drop_keyword; | |||
|  |       $end_drop =~ s/%i/crash_q1%d/; | |||
|  |       $end_drop =~ s/%t/crash_q/; | |||
|  |       find_limit("index parts","max_index_parts", | |||
|  | 		 new query_table("create index crash_q1%d on crash_q (q0", | |||
|  | 				 ",q%d",")", | |||
|  | 				 [], | |||
|  | 				 undef,undef, | |||
|  | 				 $end_drop, | |||
|  | 				 $max_keys,1)); | |||
|  |       assert("drop table crash_q $drop_attr"); | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       print "index parts: $limits{'max_index_parts'}\n"; | |||
|  |     } | |||
|  |     $end_drop=$end_drop_keyword; | |||
|  |     $end_drop =~ s/%i/crash_q2%d/; | |||
|  |     $end_drop =~ s/%t/crash_me/; | |||
|  | 
 | |||
|  |     find_limit("index part length","max_index_part_length", | |||
|  | 	       new query_many(["create table crash_q (q char(%d))", | |||
|  | 			       "create index crash_q2%d on crash_q (q)", | |||
|  | 			       "insert into crash_q values('%s')"], | |||
|  | 			      "select q from crash_q", | |||
|  | 			      "%s", | |||
|  | 			      [ $end_drop, | |||
|  | 			       "drop table crash_q $drop_attr"], | |||
|  | 			      min($limits{'max_char_size'},"+8192"))); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | find_limit("index length","max_index_length", | |||
|  | 	   new query_index_length("create table crash_q ", | |||
|  | 				  "drop table crash_q $drop_attr", | |||
|  | 				  $max_key_length)); | |||
|  | 
 | |||
|  | find_limit("max table row length (without blobs)","max_row_length", | |||
|  | 	   new query_row_length("crash_q ", | |||
|  | 				"not null", | |||
|  | 				"drop table crash_q $drop_attr", | |||
|  | 				min($max_row_length, | |||
|  | 				    $limits{'max_columns'}* | |||
|  | 				    min($limits{'max_char_size'},255)))); | |||
|  | 
 | |||
|  | find_limit("table row length with nulls (without blobs)", | |||
|  | 	   "max_row_length_with_null", | |||
|  | 	   new query_row_length("crash_q ", | |||
|  | 				"", | |||
|  | 				"drop table crash_q $drop_attr", | |||
|  | 				$limits{'max_row_length'}*2)); | |||
|  | 
 | |||
|  | find_limit("number of columns in order by","columns_in_order_by", | |||
|  | 	   new query_many(["create table crash_q (%F)", | |||
|  | 			   "insert into crash_q values(%v)", | |||
|  | 			   "insert into crash_q values(%v)"], | |||
|  | 			  "select * from crash_q order by %f", | |||
|  | 			  undef(), | |||
|  | 			  ["drop table crash_q $drop_attr"], | |||
|  | 			  $max_order_by)); | |||
|  | 
 | |||
|  | find_limit("number of columns in group by","columns_in_group_by", | |||
|  | 	   new query_many(["create table crash_q (%F)", | |||
|  | 			   "insert into crash_q values(%v)", | |||
|  | 			   "insert into crash_q values(%v)"], | |||
|  | 			  "select %f from crash_q group by %f", | |||
|  | 			  undef(), | |||
|  | 			  ["drop table crash_q $drop_attr"], | |||
|  | 			  $max_order_by)); | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | # Safe arithmetic test | |||
|  | 
 | |||
|  | $prompt="safe decimal arithmetic"; | |||
|  | $key="safe_decimal_arithmetic"; | |||
|  | if (!defined($limits{$key})) | |||
|  | { | |||
|  |    print "$prompt="; | |||
|  |    save_incomplete($key,$prompt);	 | |||
|  |    if (!safe_query_l($key,$server->create("crash_me_a", | |||
|  |          ["a decimal(10,2)","b decimal(10,2)"])))  | |||
|  |      { | |||
|  |        print DBI->errstr(); | |||
|  |        die "Can't create table 'crash_me_a' $DBI::errstr\n"; | |||
|  |      }; | |||
|  |     | |||
|  |    if (!safe_query_l($key, | |||
|  |        ["insert into crash_me_a (a,b) values (11.4,18.9)"])) | |||
|  |      { | |||
|  |        die "Can't insert into table 'crash_me_a' a  record: $DBI::errstr\n"; | |||
|  |      }; | |||
|  |       | |||
|  |    $arithmetic_safe = 'no';  | |||
|  |    $arithmetic_safe = 'yes'  | |||
|  |    if ( (safe_query_result_l($key, | |||
|  |             'select count(*) from crash_me_a where a+b=30.3',1,0) == 0)  | |||
|  |       and (safe_query_result_l($key, | |||
|  |             'select count(*) from crash_me_a where a+b-30.3 = 0',1,0) == 0)   | |||
|  |       and (safe_query_result_l($key, | |||
|  |             'select count(*) from crash_me_a where a+b-30.3 < 0',0,0) == 0) | |||
|  |       and (safe_query_result_l($key, | |||
|  |             'select count(*) from crash_me_a where a+b-30.3 > 0',0,0) == 0)); | |||
|  |    save_config_data($key,$arithmetic_safe,$prompt); | |||
|  |    print "$arithmetic_safe\n"; | |||
|  |    assert("drop table crash_me_a $drop_attr"); | |||
|  | } | |||
|  |  else | |||
|  | { | |||
|  |   print "$prompt=$limits{$key} (cached)\n"; | |||
|  | } | |||
|  | 
 | |||
|  | # Check where is null values in sorted recordset | |||
|  | if (!safe_query($server->create("crash_me_n",["i integer","r integer"])))  | |||
|  |  { | |||
|  |    print DBI->errstr(); | |||
|  |    die "Can't create table 'crash_me_n' $DBI::errstr\n"; | |||
|  |  }; | |||
|  |   | |||
|  | safe_query_l("position_of_null",["insert into crash_me_n (i) values(1)", | |||
|  | "insert into crash_me_n values(2,2)", | |||
|  | "insert into crash_me_n values(3,3)", | |||
|  | "insert into crash_me_n values(4,4)", | |||
|  | "insert into crash_me_n (i) values(5)"]); | |||
|  | 
 | |||
|  | $key = "position_of_null"; | |||
|  | $prompt ="Where is null values in sorted recordset"; | |||
|  | if (!defined($limits{$key})) | |||
|  | { | |||
|  |  save_incomplete($key,$prompt);	 | |||
|  |  print "$prompt="; | |||
|  |  $sth=$dbh->prepare("select r from crash_me_n order by r "); | |||
|  |  $sth->execute; | |||
|  |  add_log($key,"< select r from crash_me_n order by r "); | |||
|  |  $limit= detect_null_position($key,$sth); | |||
|  |  $sth->finish; | |||
|  |  print "$limit\n"; | |||
|  |  save_config_data($key,$limit,$prompt); | |||
|  | } else { | |||
|  |   print "$prompt=$limits{$key} (cache)\n"; | |||
|  | } | |||
|  | 
 | |||
|  | $key = "position_of_null_desc"; | |||
|  | $prompt ="Where is null values in sorted recordset (DESC)"; | |||
|  | if (!defined($limits{$key})) | |||
|  | { | |||
|  |  save_incomplete($key,$prompt);	 | |||
|  |  print "$prompt="; | |||
|  |  $sth=$dbh->prepare("select r from crash_me_n order by r desc"); | |||
|  |  $sth->execute; | |||
|  |  add_log($key,"< select r from crash_me_n order by r  desc"); | |||
|  |  $limit= detect_null_position($key,$sth); | |||
|  |  $sth->finish; | |||
|  |  print "$limit\n"; | |||
|  |  save_config_data($key,$limit,$prompt); | |||
|  | } else { | |||
|  |   print "$prompt=$limits{$key} (cache)\n"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | assert("drop table  crash_me_n $drop_attr"); | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | $key = 'sorted_group_by'; | |||
|  | $prompt = 'Group by always sorted'; | |||
|  | if (!defined($limits{$key})) | |||
|  | { | |||
|  |  save_incomplete($key,$prompt); | |||
|  |  print "$prompt="; | |||
|  |  safe_query_l($key,[   | |||
|  | 			 "create table crash_me_t1 (a int not null, b int not null)", | |||
|  | 			 "insert into crash_me_t1 values (1,1)", | |||
|  | 			 "insert into crash_me_t1 values (1,2)", | |||
|  | 			 "insert into crash_me_t1 values (3,1)", | |||
|  | 			 "insert into crash_me_t1 values (3,2)", | |||
|  | 			 "insert into crash_me_t1 values (2,2)", | |||
|  | 			 "insert into crash_me_t1 values (2,1)", | |||
|  | 			 "create table crash_me_t2 (a int not null, b int not null)", | |||
|  | 			 "create index crash_me_t2_ind on crash_me_t2 (a)", | |||
|  | 			 "insert into crash_me_t2 values (1,3)", | |||
|  | 			 "insert into crash_me_t2 values (3,1)", | |||
|  | 			 "insert into crash_me_t2 values (2,2)", | |||
|  | 			 "insert into crash_me_t2 values (1,1)"]); | |||
|  | 
 | |||
|  |  my $bigqry = "select crash_me_t1.a,crash_me_t2.b from ". | |||
|  | 	     "crash_me_t1,crash_me_t2 where crash_me_t1.a=crash_me_t2.a ". | |||
|  | 	     "group by crash_me_t1.a,crash_me_t2.b"; | |||
|  | 
 | |||
|  |  my $limit='no'; | |||
|  |  my $rs = get_recordset($key,$bigqry); | |||
|  |  print_recordset($key,$rs);  | |||
|  |  if ( defined ($rs)) {  | |||
|  |    if (compare_recordset($key,$rs,[[1,1],[1,3],[2,2],[3,1]]) eq 0) | |||
|  |    { | |||
|  |      $limit='yes' | |||
|  |    } | |||
|  |  } else { | |||
|  |   add_log($key,"error: ".$DBI::errstr); | |||
|  |  }  | |||
|  | 
 | |||
|  |  print "$limit\n"; | |||
|  |  safe_query_l($key,["drop table crash_me_t1", | |||
|  | 		       "drop table crash_me_t2"]); | |||
|  |  save_config_data($key,$limit,$prompt);	         | |||
|  |   | |||
|  | } else { | |||
|  |  print "$prompt=$limits{$key} (cashed)\n"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # | |||
|  | # End of test | |||
|  | # | |||
|  | 
 | |||
|  | $dbh->do("drop table crash_me $drop_attr");        # Remove temporary table | |||
|  | 
 | |||
|  | print "crash-me safe: $limits{'crash_me_safe'}\n"; | |||
|  | print "reconnected $reconnect_count times\n"; | |||
|  | 
 | |||
|  | $dbh->disconnect || warn $dbh->errstr; | |||
|  | save_all_config_data(); | |||
|  | exit 0; | |||
|  | 
 | |||
|  | # End of test | |||
|  | # | |||
|  | 
 | |||
|  | $dbh->do("drop table crash_me $drop_attr");        # Remove temporary table | |||
|  | 
 | |||
|  | print "crash-me safe: $limits{'crash_me_safe'}\n"; | |||
|  | print "reconnected $reconnect_count times\n"; | |||
|  | 
 | |||
|  | $dbh->disconnect || warn $dbh->errstr; | |||
|  | save_all_config_data(); | |||
|  | exit 0; | |||
|  | 
 | |||
|  | # Check where is nulls in the sorted result (for) | |||
|  | # it expects exactly 5 rows in the result | |||
|  | 
 | |||
|  | sub detect_null_position | |||
|  | { | |||
|  |   my $key = shift; | |||
|  |   my $sth = shift; | |||
|  |   my ($z,$r1,$r2,$r3,$r4,$r5); | |||
|  |  $r1 = $sth->fetchrow_array; add_log($key,"> $r1"); | |||
|  |  $r2 = $sth->fetchrow_array; add_log($key,"> $r2"); | |||
|  |  $r3 = $sth->fetchrow_array; add_log($key,"> $r3"); | |||
|  |  $r4 = $sth->fetchrow_array; add_log($key,"> $r4"); | |||
|  |  $r5 = $sth->fetchrow_array; add_log($key,"> $r5"); | |||
|  |  return "first" if ( !defined($r1) && !defined($r2) && defined($r3)); | |||
|  |  return "last" if ( !defined($r5) && !defined($r4) && defined($r3)); | |||
|  |  return "random"; | |||
|  | } | |||
|  | 
 | |||
|  | sub check_parenthesis { | |||
|  |  my $prefix=shift; | |||
|  |  my $fn=shift; | |||
|  |  my $result='no'; | |||
|  |  my $param_name=$prefix.lc($fn); | |||
|  |  my $r; | |||
|  |   | |||
|  |  save_incomplete($param_name,$fn); | |||
|  |  $r = safe_query("select $fn $end_query");  | |||
|  |  add_log($param_name,$safe_query_log); | |||
|  |  if ($r == 1) | |||
|  |   { | |||
|  |     $result="yes"; | |||
|  |   }  | |||
|  |   else{ | |||
|  |    $r = safe_query("select $fn() $end_query"); | |||
|  |    add_log($param_name,$safe_query_log); | |||
|  |    if ( $r  == 1)    | |||
|  |     {     | |||
|  |        $result="with_parenthesis"; | |||
|  |     } | |||
|  |   } | |||
|  | 
 | |||
|  |   save_config_data($param_name,$result,$fn); | |||
|  | } | |||
|  | 
 | |||
|  | sub check_constraint { | |||
|  |  my $prompt = shift; | |||
|  |  my $key = shift; | |||
|  |  my $create = shift; | |||
|  |  my $check = shift; | |||
|  |  my $drop = shift; | |||
|  |  save_incomplete($key,$prompt); | |||
|  |  print "$prompt="; | |||
|  |  my $res = 'no'; | |||
|  |  my $t; | |||
|  |  $t=safe_query($create); | |||
|  |  add_log($key,$safe_query_log); | |||
|  |  if ( $t == 1) | |||
|  |  { | |||
|  |    $res='yes'; | |||
|  |    $t= safe_query($check); | |||
|  |    add_log($key,$safe_query_log); | |||
|  |    if ($t == 1) | |||
|  |    { | |||
|  |      $res='syntax only'; | |||
|  |    } | |||
|  |  }         | |||
|  |  safe_query($drop); | |||
|  |  add_log($key,$safe_query_log); | |||
|  |   | |||
|  |  save_config_data($key,$res,$prompt); | |||
|  |  print "$res\n"; | |||
|  | } | |||
|  | 
 | |||
|  | sub make_time_r { | |||
|  |   my $hour=shift; | |||
|  |   my $minute=shift; | |||
|  |   my $second=shift; | |||
|  |   $_ = $limits{'time_format_inresult'}; | |||
|  |   return sprintf "%02d:%02d:%02d", ($hour%24),$minute,$second if (/^iso$/); | |||
|  |   return sprintf "%02d.%02d.%02d", ($hour%24),$minute,$second if (/^euro/); | |||
|  |   return sprintf "%02d:%02d %s",  | |||
|  |         ($hour >= 13? ($hour-12) : $hour),$minute,($hour >=13 ? 'PM':'AM')  | |||
|  | 	                if (/^usa/); | |||
|  |   return sprintf "%02d%02d%02d", ($hour%24),$minute,$second if (/^HHMMSS/); | |||
|  |   return sprintf "%04d%02d%02d", ($hour%24),$minute,$second if (/^HHHHMMSS/); | |||
|  |   return "UNKNOWN FORMAT"; | |||
|  | } | |||
|  | 
 | |||
|  | sub make_time { | |||
|  |   my $hour=shift; | |||
|  |   my $minute=shift; | |||
|  |   my $second=shift; | |||
|  |   return sprintf "%02d:%02d:%02d", ($hour%24),$minute,$second  | |||
|  |       if ($limits{'time_format_ISO'} eq "yes"); | |||
|  |   return sprintf "%02d.%02d.%02d", ($hour%24),$minute,$second  | |||
|  |       if ($limits{'time_format_EUR'} eq "yes"); | |||
|  |   return sprintf "%02d:%02d %s",  | |||
|  |         ($hour >= 13? ($hour-12) : $hour),$minute,($hour >=13 ? 'PM':'AM')  | |||
|  |       if ($limits{'time_format_USA'} eq "yes"); | |||
|  |   return sprintf "%02d%02d%02d", ($hour%24),$minute,$second  | |||
|  |       if ($limits{'time_format_HHMMSS'} eq "yes"); | |||
|  |   return sprintf "%04d%02d%02d", ($hour%24),$minute,$second  | |||
|  |       if ($limits{'time_format_HHHHMMSS'} eq "yes"); | |||
|  |   return "UNKNOWN FORMAT"; | |||
|  | } | |||
|  | 
 | |||
|  | sub make_date_r { | |||
|  |   my $year=shift; | |||
|  |   my $month=shift; | |||
|  |   my $day=shift; | |||
|  |   $_ = $limits{'date_format_inresult'}; | |||
|  |   return sprintf "%02d-%02d-%02d", ($year%100),$month,$day if (/^short iso$/); | |||
|  |   return sprintf "%04d-%02d-%02d", $year,$month,$day if (/^iso/); | |||
|  |   return sprintf "%02d.%02d.%02d", $day,$month,($year%100) if (/^short euro/); | |||
|  |   return sprintf "%02d.%02d.%04d", $day,$month,$year if (/^euro/); | |||
|  |   return sprintf "%02d/%02d/%02d", $month,$day,($year%100) if (/^short usa/); | |||
|  |   return sprintf "%02d/%02d/%04d", $month,$day,$year if (/^usa/); | |||
|  |   return sprintf "%04d%02d%02d", $year,$month,$day if (/^YYYYMMDD/); | |||
|  |   return "UNKNOWN FORMAT"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub make_date { | |||
|  |   my $year=shift; | |||
|  |   my $month=shift; | |||
|  |   my $day=shift; | |||
|  |   return sprintf "'%04d-%02d-%02d'", $year,$month,$day  | |||
|  |       if ($limits{'date_format_ISO'} eq yes); | |||
|  |   return sprintf "DATE '%04d-%02d-%02d'", $year,$month,$day  | |||
|  |       if ($limits{'date_format_ISO_with_date'} eq yes); | |||
|  |   return sprintf "'%02d.%02d.%04d'", $day,$month,$year  | |||
|  |       if ($limits{'date_format_EUR'} eq 'yes'); | |||
|  |   return sprintf "DATE '%02d.%02d.%04d'", $day,$month,$year  | |||
|  |       if ($limits{'date_format_EUR_with_date'} eq 'yes'); | |||
|  |   return sprintf "'%02d/%02d/%04d'", $month,$day,$year  | |||
|  |       if ($limits{'date_format_USA'} eq 'yes'); | |||
|  |   return sprintf "DATE '%02d/%02d/%04d'", $month,$day,$year  | |||
|  |       if ($limits{'date_format_USA_with_date'} eq 'yes'); | |||
|  |   return sprintf "'%04d%02d%02d'", $year,$month,$day  | |||
|  |       if ($limits{'date_format_YYYYMMDD'} eq 'yes'); | |||
|  |   return sprintf "DATE '%04d%02d%02d'", $year,$month,$day  | |||
|  |       if ($limits{'date_format_YYYYMMDD_with_date'} eq 'yes'); | |||
|  |   return "UNKNOWN FORMAT"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub print_recordset{ | |||
|  |   my ($key,$recset) = @_; | |||
|  |   my $rec; | |||
|  |   foreach $rec (@$recset) | |||
|  |   { | |||
|  |     add_log($key, " > ".join(',', map(repr($_), @$rec))); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # read result recordset from sql server.  | |||
|  | # returns arrayref to (arrayref to) values | |||
|  | # or undef (in case of sql errors) | |||
|  | # | |||
|  | sub get_recordset{ | |||
|  |   my ($key,$query) = @_; | |||
|  |   add_log($key, "< $query"); | |||
|  |   return $dbh->selectall_arrayref($query); | |||
|  | } | |||
|  | 
 | |||
|  | # function for comparing recordset (that was returned by get_recordset) | |||
|  | # and arrayref of (arrayref of) values. | |||
|  | # | |||
|  | # returns : zero if recordset equal that array, 1 if it doesn't equal | |||
|  | # | |||
|  | # parameters: | |||
|  | # $key - current operation (for logging) | |||
|  | # $recset - recordset | |||
|  | # $mustbe - array of values that we expect | |||
|  | # | |||
|  | # example: $a=get_recordset('some_parameter','select a,b from c'); | |||
|  | # if (compare_recordset('some_parameter',$a,[[1,1],[1,2],[1,3]]) neq 0)  | |||
|  | # { | |||
|  | #   print "unexpected result\n"; | |||
|  | # } ; | |||
|  | # | |||
|  | sub compare_recordset { | |||
|  |   my ($key,$recset,$mustbe) = @_; | |||
|  |   my $rec,$recno,$fld,$fldno,$fcount; | |||
|  |   add_log($key,"\n Check recordset:"); | |||
|  |   $recno=0; | |||
|  |   foreach $rec (@$recset) | |||
|  |   { | |||
|  |     add_log($key," " . join(',', map(repr($_),@$rec)) . " expected: " . | |||
|  | 	    join(',', map(repr($_), @{$mustbe->[$recno]} ) )); | |||
|  |     $fcount = @$rec; | |||
|  |     $fcount--; | |||
|  |     foreach $fldno (0 .. $fcount ) | |||
|  |     { | |||
|  |       if ($mustbe->[$recno][$fldno] ne $rec->[$fldno]) | |||
|  |       { | |||
|  | 	add_log($key," Recordset doesn't correspond with template"); | |||
|  | 	return 1; | |||
|  |       }; | |||
|  |     } | |||
|  |     $recno++; | |||
|  |   } | |||
|  |   add_log($key," Recordset corresponds with template"); | |||
|  |   return 0; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # converts inner perl value to printable representation | |||
|  | # for example: undef maps to 'NULL', | |||
|  | # string -> 'string' | |||
|  | # int -> int | |||
|  | #  | |||
|  | sub repr { | |||
|  |   my $s = shift; | |||
|  |   return "'$s'"if ($s =~ /\D/); | |||
|  |   return 'NULL'if ( not defined($s)); | |||
|  |   return $s; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub version | |||
|  | { | |||
|  |   print "$0  Ver $version\n"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub usage | |||
|  | { | |||
|  |   version(); | |||
|  |     print <<EOF; | |||
|  | 
 | |||
|  | This program tries to find all limits and capabilities for a SQL | |||
|  | server.  As it will use the server in some 'unexpected' ways, one | |||
|  | shouldn\'t have anything important running on it at the same time this | |||
|  | program runs!  There is a slight chance that something unexpected may | |||
|  | happen.... | |||
|  | 
 | |||
|  | As all used queries are legal according to some SQL standard. any | |||
|  | reasonable SQL server should be able to run this test without any | |||
|  | problems. | |||
|  | 
 | |||
|  | All questions is cached in $opt_dir/'server_name'[-suffix].cfg that | |||
|  | future runs will use limits found in previous runs. Remove this file | |||
|  | if you want to find the current limits for your version of the | |||
|  | database server. | |||
|  | 
 | |||
|  | This program uses some table names while testing things. If you have any | |||
|  | tables with the name of 'crash_me' or 'crash_qxxxx' where 'x' is a number, | |||
|  | they will be deleted by this test! | |||
|  | 
 | |||
|  | $0 takes the following options: | |||
|  | 
 | |||
|  | --help or --Information | |||
|  |   Shows this help | |||
|  | 
 | |||
|  | --batch-mode | |||
|  |   Don\'t ask any questions, quit on errors. | |||
|  | 
 | |||
|  | --config-file='filename' | |||
|  |   Read limit results from specific file | |||
|  | 
 | |||
|  | --comment='some comment' | |||
|  |   Add this comment to the crash-me limit file | |||
|  | 
 | |||
|  | --check-server | |||
|  |   Do a new connection to the server every time crash-me checks if the server | |||
|  |   is alive.  This can help in cases where the server starts returning wrong | |||
|  |   data because of an earlier select. | |||
|  | 
 | |||
|  | --database='database' (Default $opt_database) | |||
|  |   Create test tables in this database. | |||
|  | 
 | |||
|  | --dir='limits' | |||
|  |   Save crash-me output in this directory | |||
|  | 
 | |||
|  | --debug | |||
|  |   Lots of printing to help debugging if something goes wrong. | |||
|  | 
 | |||
|  | --fix-limit-file | |||
|  |   Reformat the crash-me limit file.  crash-me is not run! | |||
|  | 
 | |||
|  | --force | |||
|  |   Start test at once, without a warning screen and without questions. | |||
|  |   This is a option for the very brave. | |||
|  |   Use this in your cron scripts to test your database every night. | |||
|  | 
 | |||
|  | --log-all-queries | |||
|  |   Prints all queries that are executed. Mostly used for debugging crash-me. | |||
|  | 
 | |||
|  | --log-queries-to-file='filename' | |||
|  |   Log full queries to file. | |||
|  | 
 | |||
|  | --host='hostname' (Default $opt_host) | |||
|  |   Run tests on this host. | |||
|  | 
 | |||
|  | --password='password' | |||
|  |   Password for the current user. | |||
|  |     | |||
|  | --restart | |||
|  |   Save states during each limit tests. This will make it possible to continue | |||
|  |   by restarting with the same options if there is some bug in the DBI or | |||
|  |   DBD driver that caused $0 to die! | |||
|  | 
 | |||
|  | --server='server name'  (Default $opt_server) | |||
|  |   Run the test on the given server. | |||
|  |   Known servers names are: Access, Adabas, AdabasD, Empress, Oracle,  | |||
|  |   Informix, DB2, Mimer, mSQL, MS-SQL, MySQL, Pg, Solid or Sybase. | |||
|  |   For others $0 can\'t report the server version. | |||
|  | 
 | |||
|  | --suffix='suffix' (Default '') | |||
|  |   Add suffix to the output filename. For instance if you run crash-me like | |||
|  |   "crash-me --suffix="myisam", | |||
|  |   then output filename will look "mysql-myisam.cfg". | |||
|  | 
 | |||
|  | --user='user_name' | |||
|  |   User name to log into the SQL server. | |||
|  | 
 | |||
|  | --db-start-cmd='command to restart server' | |||
|  |   Automaticly restarts server with this command if the database server dies. | |||
|  | 
 | |||
|  | --sleep='time in seconds' (Default $opt_sleep) | |||
|  |   Wait this long before restarting server. | |||
|  | 
 | |||
|  | --verbose | |||
|  | --noverbose | |||
|  |   Log into the result file queries performed for determination parameter value | |||
|  | 
 | |||
|  | EOF | |||
|  |   exit(0); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub server_info | |||
|  | { | |||
|  |   my ($ok,$tmp); | |||
|  |   $ok=0; | |||
|  |   print "\nNOTE: You should be familiar with '$0 --help' before continuing!\n\n"; | |||
|  |   if (lc($opt_server) eq "mysql") | |||
|  |   { | |||
|  |     $ok=1; | |||
|  |     print <<EOF; | |||
|  | This test should not crash MySQL if it was distributed together with the | |||
|  | running MySQL version. | |||
|  | If this is the case you can probably continue without having to worry about | |||
|  | destroying something. | |||
|  | EOF | |||
|  |   } | |||
|  |   elsif (lc($opt_server) eq "msql") | |||
|  |   { | |||
|  |     print <<EOF; | |||
|  | This test will take down mSQL repeatedly while finding limits. | |||
|  | To make this test easier, start mSQL in another terminal with something like: | |||
|  | 
 | |||
|  | while (true); do /usr/local/mSQL/bin/msql2d ; done | |||
|  | 
 | |||
|  | You should be sure that no one is doing anything important with mSQL and that | |||
|  | you have privileges to restart it! | |||
|  | It may take awhile to determinate the number of joinable tables, so prepare to | |||
|  | wait! | |||
|  | EOF | |||
|  |   } | |||
|  |   elsif (lc($opt_server) eq "solid") | |||
|  |   { | |||
|  |     print <<EOF; | |||
|  | This test will take down Solid server repeatedly while finding limits. | |||
|  | You should be sure that no one is doing anything important with Solid | |||
|  | and that you have privileges to restart it! | |||
|  | 
 | |||
|  | If you are running Solid without logging and/or backup YOU WILL LOSE! | |||
|  | Solid does not write data from the cache often enough. So if you continue | |||
|  | you may lose tables and data that you entered hours ago! | |||
|  | 
 | |||
|  | Solid will also take a lot of memory running this test. You will nead | |||
|  | at least 234M free! | |||
|  | 
 | |||
|  | When doing the connect test Solid server or the perl api will hang when | |||
|  | freeing connections. Kill this program and restart it to continue with the | |||
|  | test. You don\'t have to use --restart for this case. | |||
|  | EOF | |||
|  |     if (!$opt_restart) | |||
|  |     { | |||
|  |       print "\nWhen DBI/Solid dies you should run this program repeatedly\n"; | |||
|  |       print "with --restart until all tests have completed\n"; | |||
|  |     } | |||
|  |   } | |||
|  |   elsif (lc($opt_server) eq "pg") | |||
|  |   { | |||
|  |     print <<EOF; | |||
|  | This test will crash postgreSQL when calculating the number of joinable tables! | |||
|  | You should be sure that no one is doing anything important with postgreSQL | |||
|  | and that you have privileges to restart it! | |||
|  | EOF | |||
|  |   } | |||
|  |   else | |||
|  |   { | |||
|  |     print <<EOF; | |||
|  | This test may crash $opt_server repeatedly while finding limits! | |||
|  | You should be sure that no one is doing anything important with $opt_server | |||
|  | and that you have privileges to restart it! | |||
|  | EOF | |||
|  |   } | |||
|  |   print <<EOF; | |||
|  | 
 | |||
|  | Some of the tests you are about to execute may require a lot of | |||
|  | memory.  Your tests WILL adversely affect system performance. It\'s | |||
|  | not uncommon that either this crash-me test program, or the actual | |||
|  | database back-end, will DIE with an out-of-memory error. So might | |||
|  | any other program on your system if it requests more memory at the | |||
|  | wrong time. | |||
|  | 
 | |||
|  | Note also that while crash-me tries to find limits for the database server | |||
|  | it will make a lot of queries that can\'t be categorized as \'normal\'.  It\'s | |||
|  | not unlikely that crash-me finds some limit bug in your server so if you | |||
|  | run this test you have to be prepared that your server may die during it! | |||
|  | 
 | |||
|  | We, the creators of this utility, are not responsible in any way if your | |||
|  | database server unexpectedly crashes while this program tries to find the | |||
|  | limitations of your server. By accepting the following question with \'yes\', | |||
|  | you agree to the above! | |||
|  | 
 | |||
|  | You have been warned! | |||
|  | 
 | |||
|  | EOF | |||
|  | 
 | |||
|  |   # | |||
|  |   # No default reply here so no one can blame us for starting the test | |||
|  |   # automaticly. | |||
|  |   # | |||
|  |   for (;;) | |||
|  |   { | |||
|  |     print "Start test (yes/no) ? "; | |||
|  |     $tmp=<STDIN>; chomp($tmp); $tmp=lc($tmp); | |||
|  |     last if ($tmp =~ /^yes$/i); | |||
|  |     exit 1 if ($tmp =~ /^n/i); | |||
|  |     print "\n"; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | sub machine | |||
|  | { | |||
|  |   my @name = POSIX::uname(); | |||
|  |   my $name= $name[0] . " " . $name[2] . " " . $name[4]; | |||
|  |   return $name; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # | |||
|  | # Help functions that we need | |||
|  | # | |||
|  | 
 | |||
|  | sub safe_connect | |||
|  | { | |||
|  |   my ($object)=@_; | |||
|  |   my ($dbh,$tmp); | |||
|  | 
 | |||
|  |   for (;;) | |||
|  |   { | |||
|  |     if (($dbh=DBI->connect($server->{'data_source'},$opt_user,$opt_password, | |||
|  | 			   { PrintError => 0, AutoCommit => 1}))) | |||
|  |     { | |||
|  |       $dbh->{LongReadLen}= 16000000; # Set max retrieval buffer | |||
|  |       return $dbh; | |||
|  |     } | |||
|  |     print "Error: $DBI::errstr;  $server->{'data_source'} ". | |||
|  |         " - '$opt_user' - '$opt_password'\n"; | |||
|  |     print "I got the above error when connecting to $opt_server\n"; | |||
|  |     if (defined($object) && defined($object->{'limit'})) | |||
|  |     { | |||
|  |       print "This check was done with limit: $object->{'limit'}.". | |||
|  |           "\nNext check will be done with a smaller limit!\n"; | |||
|  |       $object=undef(); | |||
|  |     } | |||
|  |     save_config_data('crash_me_safe','no',"crash me safe"); | |||
|  |     if ($opt_db_start_cmd) | |||
|  |     { | |||
|  |       print "Restarting the db server with:\n'$opt_db_start_cmd'\n"; | |||
|  |       system("$opt_db_start_cmd"); | |||
|  |       print "Waiting $opt_sleep seconds so the server can initialize\n"; | |||
|  |       sleep $opt_sleep; | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       exit(1) if ($opt_batch_mode); | |||
|  |       print "Can you check/restart it so I can continue testing?\n"; | |||
|  |       for (;;) | |||
|  |       { | |||
|  | 	print "Continue test (yes/no) ? [yes] "; | |||
|  | 	$tmp=<STDIN>; chomp($tmp); $tmp=lc($tmp); | |||
|  | 	$tmp = "yes" if ($tmp eq ""); | |||
|  | 	last if (index("yes",$tmp) >= 0); | |||
|  | 	exit 1 if (index("no",$tmp) >= 0); | |||
|  | 	print "\n"; | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Test connecting a couple of times before giving an error | |||
|  | # This is needed to get the server time to free old connections | |||
|  | # after the connect test | |||
|  | # | |||
|  | 
 | |||
|  | sub retry_connect | |||
|  | { | |||
|  |   my ($dbh, $i); | |||
|  |   for ($i=0 ; $i < 10 ; $i++) | |||
|  |   { | |||
|  |     if (($dbh=DBI->connect($server->{'data_source'},$opt_user,$opt_password, | |||
|  | 			 { PrintError => 0, AutoCommit => 1}))) | |||
|  |     { | |||
|  |       $dbh->{LongReadLen}= 16000000; # Set max retrieval buffer | |||
|  |       return $dbh; | |||
|  |     } | |||
|  |     sleep(1); | |||
|  |   } | |||
|  |   return safe_connect(); | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Check if the server is up and running. If not, ask the user to restart it | |||
|  | # | |||
|  | 
 | |||
|  | sub check_connect | |||
|  | { | |||
|  |   my ($object)=@_; | |||
|  |   my ($sth); | |||
|  |   print "Checking connection\n" if ($opt_log_all_queries); | |||
|  |   # The following line will not work properly with interbase | |||
|  |   if ($opt_check_server && defined($check_connect) && $dbh->{AutoCommit} != 0) | |||
|  |   { | |||
|  |      | |||
|  |     $dbh->disconnect; | |||
|  |     $dbh=safe_connect($object); | |||
|  |     return; | |||
|  |   } | |||
|  |   return if (defined($check_connect) && defined($dbh->do($check_connect))); | |||
|  |   $dbh->disconnect || warn $dbh->errstr; | |||
|  |   print "\nreconnecting\n" if ($opt_debug); | |||
|  |   $reconnect_count++; | |||
|  |   undef($dbh); | |||
|  |   $dbh=safe_connect($object); | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # print query if debugging | |||
|  | # | |||
|  | sub repr_query { | |||
|  |   my $query=shift; | |||
|  |  if (length($query) > 130) | |||
|  |  { | |||
|  |    $query=substr($query,0,120) . "...(" . (length($query)-120) . ")"; | |||
|  |  } | |||
|  |  return $query; | |||
|  | }   | |||
|  | 
 | |||
|  | sub print_query | |||
|  | { | |||
|  |   my ($query)=@_; | |||
|  |   $last_error=$DBI::errstr; | |||
|  |   if ($opt_debug) | |||
|  |   { | |||
|  |     if (length($query) > 130) | |||
|  |     { | |||
|  |       $query=substr($query,0,120) . "...(" . (length($query)-120) . ")"; | |||
|  |     } | |||
|  |     printf "\nGot error from query: '%s'\n%s\n",$query,$DBI::errstr; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Do one or many queries. Return 1 if all was ok | |||
|  | # Note that all rows are executed  | |||
|  | # (to ensure that we execute drop table commands) | |||
|  | # | |||
|  | 
 | |||
|  | sub safe_query_l { | |||
|  |   my $key = shift; | |||
|  |   my $q = shift; | |||
|  |   my $r = safe_query($q); | |||
|  |   add_log($key,$safe_query_log); | |||
|  |   return $r; | |||
|  | } | |||
|  | 
 | |||
|  | sub safe_query | |||
|  | { | |||
|  |   my($queries)=@_; | |||
|  |   my($query,$ok,$retry_ok,$retry,@tmp,$sth); | |||
|  |   $safe_query_log=""; | |||
|  |   $ok=1; | |||
|  |   if (ref($queries) ne "ARRAY") | |||
|  |   { | |||
|  |     push(@tmp,$queries); | |||
|  |     $queries= \@tmp; | |||
|  |   } | |||
|  |   foreach $query (@$queries) | |||
|  |   { | |||
|  |     printf "query1: %-80.80s ...(%d - %d)\n",$query, | |||
|  |           length($query),$retry_limit  if ($opt_log_all_queries); | |||
|  |     print LOG "$query;\n" if ($opt_log); | |||
|  |     $safe_query_log .= "< $query\n"; | |||
|  |     if (length($query) > $query_size) | |||
|  |     { | |||
|  |       $ok=0; | |||
|  |       $safe_query_log .= "Query is too long\n"; | |||
|  |       next; | |||
|  |     } | |||
|  | 
 | |||
|  |     $retry_ok=0; | |||
|  |     for ($retry=0; $retry < $retry_limit ; $retry++) | |||
|  |     { | |||
|  |       if (! ($sth=$dbh->prepare($query))) | |||
|  |       { | |||
|  | 	print_query($query); | |||
|  |         $safe_query_log .= "> couldn't prepare:". $dbh->errstr. "\n"; | |||
|  | 	$retry=100 if (!$server->abort_if_fatal_error()); | |||
|  | 	# Force a reconnect because of Access drop table bug! | |||
|  | 	if ($retry == $retry_limit-2) | |||
|  | 	{ | |||
|  | 	  print "Forcing disconnect to retry query\n" if ($opt_debug); | |||
|  | 	  $dbh->disconnect || warn $dbh->errstr; | |||
|  | 	} | |||
|  | 	check_connect();        # Check that server is still up | |||
|  |       } | |||
|  |       else | |||
|  |       { | |||
|  |         if (!$sth->execute()) | |||
|  |         { | |||
|  |  	  print_query($query); | |||
|  |           $safe_query_log .= "> execute error:". $dbh->errstr. "\n"; | |||
|  | 	  $retry=100 if (!$server->abort_if_fatal_error()); | |||
|  | 	  # Force a reconnect because of Access drop table bug! | |||
|  | 	  if ($retry == $retry_limit-2) | |||
|  | 	  { | |||
|  | 	    print "Forcing disconnect to retry query\n" if ($opt_debug); | |||
|  | 	    $dbh->disconnect || warn $dbh->errstr; | |||
|  | 	  } | |||
|  | 	  check_connect();        # Check that server is still up | |||
|  |         } | |||
|  |         else | |||
|  |         { | |||
|  | 	  $retry = $retry_limit; | |||
|  | 	  $retry_ok = 1; | |||
|  |           $safe_query_log .= "> OK\n"; | |||
|  |         } | |||
|  |         $sth->finish; | |||
|  |       } | |||
|  |     } | |||
|  |     $ok=0 if (!$retry_ok); | |||
|  |     if ($query =~ /create/i && $server->reconnect_on_errors()) | |||
|  |     { | |||
|  |       print "Forcing disconnect to retry query\n" if ($opt_debug); | |||
|  |       $dbh->disconnect || warn $dbh->errstr; | |||
|  |       $dbh=safe_connect(); | |||
|  |     } | |||
|  |   } | |||
|  |   return $ok; | |||
|  | } | |||
|  | 
 | |||
|  | sub check_reserved_words | |||
|  | { | |||
|  |   my ($dbh)= @_; | |||
|  | 
 | |||
|  |   my $answer, $prompt, $config, $keyword_type; | |||
|  | 
 | |||
|  |   my @keywords_ext  = ( "ansi-92/99", "ansi92", "ansi99", "extra"); | |||
|  | 
 | |||
|  |   my %reserved_words = ( | |||
|  |         'ABSOLUTE' =>  0,          'ACTION' =>  0,             'ADD' =>  0, | |||
|  |            'AFTER' =>  0,           'ALIAS' =>  0,             'ALL' =>  0, | |||
|  |         'ALLOCATE' =>  0,           'ALTER' =>  0,             'AND' =>  0, | |||
|  |              'ANY' =>  0,             'ARE' =>  0,              'AS' =>  0, | |||
|  |              'ASC' =>  0,       'ASSERTION' =>  0,              'AT' =>  0, | |||
|  |    'AUTHORIZATION' =>  0,          'BEFORE' =>  0,           'BEGIN' =>  0, | |||
|  |              'BIT' =>  0,         'BOOLEAN' =>  0,            'BOTH' =>  0, | |||
|  |          'BREADTH' =>  0,              'BY' =>  0,            'CALL' =>  0, | |||
|  |          'CASCADE' =>  0,        'CASCADED' =>  0,            'CASE' =>  0, | |||
|  |             'CAST' =>  0,         'CATALOG' =>  0,            'CHAR' =>  0, | |||
|  |        'CHARACTER' =>  0,           'CHECK' =>  0,           'CLOSE' =>  0, | |||
|  |          'COLLATE' =>  0,       'COLLATION' =>  0,          'COLUMN' =>  0, | |||
|  |           'COMMIT' =>  0,      'COMPLETION' =>  0,         'CONNECT' =>  0, | |||
|  |       'CONNECTION' =>  0,      'CONSTRAINT' =>  0,     'CONSTRAINTS' =>  0, | |||
|  |         'CONTINUE' =>  0,   'CORRESPONDING' =>  0,          'CREATE' =>  0, | |||
|  |            'CROSS' =>  0,         'CURRENT' =>  0,    'CURRENT_DATE' =>  0, | |||
|  |     'CURRENT_TIME' =>  0,'CURRENT_TIMESTAMP' =>  0,   'CURRENT_USER' =>  0, | |||
|  |           'CURSOR' =>  0,           'CYCLE' =>  0,            'DATA' =>  0, | |||
|  |             'DATE' =>  0,             'DAY' =>  0,      'DEALLOCATE' =>  0, | |||
|  |              'DEC' =>  0,         'DECIMAL' =>  0,         'DECLARE' =>  0, | |||
|  |          'DEFAULT' =>  0,      'DEFERRABLE' =>  0,        'DEFERRED' =>  0, | |||
|  |           'DELETE' =>  0,           'DEPTH' =>  0,            'DESC' =>  0, | |||
|  |         'DESCRIBE' =>  0,      'DESCRIPTOR' =>  0,     'DIAGNOSTICS' =>  0, | |||
|  |       'DICTIONARY' =>  0,      'DISCONNECT' =>  0,        'DISTINCT' =>  0, | |||
|  |           'DOMAIN' =>  0,          'DOUBLE' =>  0,            'DROP' =>  0, | |||
|  |             'EACH' =>  0,            'ELSE' =>  0,          'ELSEIF' =>  0, | |||
|  |              'END' =>  0,        'END-EXEC' =>  0,          'EQUALS' =>  0, | |||
|  |           'ESCAPE' =>  0,          'EXCEPT' =>  0,       'EXCEPTION' =>  0, | |||
|  |             'EXEC' =>  0,         'EXECUTE' =>  0,        'EXTERNAL' =>  0, | |||
|  |            'FALSE' =>  0,           'FETCH' =>  0,           'FIRST' =>  0, | |||
|  |            'FLOAT' =>  0,             'FOR' =>  0,         'FOREIGN' =>  0, | |||
|  |            'FOUND' =>  0,            'FROM' =>  0,            'FULL' =>  0, | |||
|  |          'GENERAL' =>  0,             'GET' =>  0,          'GLOBAL' =>  0, | |||
|  |               'GO' =>  0,            'GOTO' =>  0,           'GRANT' =>  0, | |||
|  |            'GROUP' =>  0,          'HAVING' =>  0,            'HOUR' =>  0, | |||
|  |         'IDENTITY' =>  0,              'IF' =>  0,          'IGNORE' =>  0, | |||
|  |        'IMMEDIATE' =>  0,              'IN' =>  0,       'INDICATOR' =>  0, | |||
|  |        'INITIALLY' =>  0,           'INNER' =>  0,           'INPUT' =>  0, | |||
|  |           'INSERT' =>  0,             'INT' =>  0,         'INTEGER' =>  0, | |||
|  |        'INTERSECT' =>  0,        'INTERVAL' =>  0,            'INTO' =>  0, | |||
|  |               'IS' =>  0,       'ISOLATION' =>  0,            'JOIN' =>  0, | |||
|  |              'KEY' =>  0,        'LANGUAGE' =>  0,            'LAST' =>  0, | |||
|  |          'LEADING' =>  0,           'LEAVE' =>  0,            'LEFT' =>  0, | |||
|  |             'LESS' =>  0,           'LEVEL' =>  0,            'LIKE' =>  0, | |||
|  |            'LIMIT' =>  0,           'LOCAL' =>  0,            'LOOP' =>  0, | |||
|  |            'MATCH' =>  0,          'MINUTE' =>  0,          'MODIFY' =>  0, | |||
|  |           'MODULE' =>  0,           'MONTH' =>  0,           'NAMES' =>  0, | |||
|  |         'NATIONAL' =>  0,         'NATURAL' =>  0,           'NCHAR' =>  0, | |||
|  |              'NEW' =>  0,            'NEXT' =>  0,              'NO' =>  0, | |||
|  |             'NONE' =>  0,             'NOT' =>  0,            'NULL' =>  0, | |||
|  |          'NUMERIC' =>  0,          'OBJECT' =>  0,              'OF' =>  0, | |||
|  |              'OFF' =>  0,             'OLD' =>  0,              'ON' =>  0, | |||
|  |             'ONLY' =>  0,            'OPEN' =>  0,       'OPERATION' =>  0, | |||
|  |           'OPTION' =>  0,              'OR' =>  0,           'ORDER' =>  0, | |||
|  |            'OUTER' =>  0,          'OUTPUT' =>  0,             'PAD' =>  0, | |||
|  |       'PARAMETERS' =>  0,         'PARTIAL' =>  0,       'PRECISION' =>  0, | |||
|  |         'PREORDER' =>  0,         'PREPARE' =>  0,        'PRESERVE' =>  0, | |||
|  |          'PRIMARY' =>  0,           'PRIOR' =>  0,      'PRIVILEGES' =>  0, | |||
|  |        'PROCEDURE' =>  0,          'PUBLIC' =>  0,            'READ' =>  0, | |||
|  |             'REAL' =>  0,       'RECURSIVE' =>  0,             'REF' =>  0, | |||
|  |       'REFERENCES' =>  0,     'REFERENCING' =>  0,        'RELATIVE' =>  0, | |||
|  |         'RESIGNAL' =>  0,        'RESTRICT' =>  0,          'RETURN' =>  0, | |||
|  |          'RETURNS' =>  0,          'REVOKE' =>  0,           'RIGHT' =>  0, | |||
|  |             'ROLE' =>  0,        'ROLLBACK' =>  0,         'ROUTINE' =>  0, | |||
|  |              'ROW' =>  0,            'ROWS' =>  0,       'SAVEPOINT' =>  0, | |||
|  |           'SCHEMA' =>  0,          'SCROLL' =>  0,          'SEARCH' =>  0, | |||
|  |           'SECOND' =>  0,         'SECTION' =>  0,          'SELECT' =>  0, | |||
|  |         'SEQUENCE' =>  0,         'SESSION' =>  0,    'SESSION_USER' =>  0, | |||
|  |              'SET' =>  0,          'SIGNAL' =>  0,            'SIZE' =>  0, | |||
|  |         'SMALLINT' =>  0,            'SOME' =>  0,           'SPACE' =>  0, | |||
|  |              'SQL' =>  0,    'SQLEXCEPTION' =>  0,        'SQLSTATE' =>  0, | |||
|  |       'SQLWARNING' =>  0,       'STRUCTURE' =>  0,     'SYSTEM_USER' =>  0, | |||
|  |            'TABLE' =>  0,       'TEMPORARY' =>  0,            'THEN' =>  0, | |||
|  |             'TIME' =>  0,       'TIMESTAMP' =>  0,   'TIMEZONE_HOUR' =>  0, | |||
|  |  'TIMEZONE_MINUTE' =>  0,              'TO' =>  0,        'TRAILING' =>  0, | |||
|  |      'TRANSACTION' =>  0,     'TRANSLATION' =>  0,         'TRIGGER' =>  0, | |||
|  |             'TRUE' =>  0,           'UNDER' =>  0,           'UNION' =>  0, | |||
|  |           'UNIQUE' =>  0,         'UNKNOWN' =>  0,          'UPDATE' =>  0, | |||
|  |            'USAGE' =>  0,            'USER' =>  0,           'USING' =>  0, | |||
|  |            'VALUE' =>  0,          'VALUES' =>  0,         'VARCHAR' =>  0, | |||
|  |         'VARIABLE' =>  0,         'VARYING' =>  0,            'VIEW' =>  0, | |||
|  |             'WHEN' =>  0,        'WHENEVER' =>  0,           'WHERE' =>  0, | |||
|  |            'WHILE' =>  0,            'WITH' =>  0,         'WITHOUT' =>  0, | |||
|  |             'WORK' =>  0,           'WRITE' =>  0,            'YEAR' =>  0, | |||
|  |             'ZONE' =>  0, | |||
|  | 
 | |||
|  |            'ASYNC' =>  1,             'AVG' =>  1,         'BETWEEN' =>  1, | |||
|  |       'BIT_LENGTH' =>  1,'CHARACTER_LENGTH' =>  1,     'CHAR_LENGTH' =>  1, | |||
|  |         'COALESCE' =>  1,         'CONVERT' =>  1,           'COUNT' =>  1, | |||
|  |           'EXISTS' =>  1,         'EXTRACT' =>  1,     'INSENSITIVE' =>  1, | |||
|  |            'LOWER' =>  1,             'MAX' =>  1,             'MIN' =>  1, | |||
|  |           'NULLIF' =>  1,    'OCTET_LENGTH' =>  1,             'OID' =>  1, | |||
|  |        'OPERATORS' =>  1,          'OTHERS' =>  1,        'OVERLAPS' =>  1, | |||
|  |          'PENDANT' =>  1,        'POSITION' =>  1,         'PRIVATE' =>  1, | |||
|  |        'PROTECTED' =>  1,         'REPLACE' =>  1,       'SENSITIVE' =>  1, | |||
|  |          'SIMILAR' =>  1,         'SQLCODE' =>  1,        'SQLERROR' =>  1, | |||
|  |        'SUBSTRING' =>  1,             'SUM' =>  1,            'TEST' =>  1, | |||
|  |            'THERE' =>  1,       'TRANSLATE' =>  1,            'TRIM' =>  1, | |||
|  |             'TYPE' =>  1,           'UPPER' =>  1,         'VIRTUAL' =>  1, | |||
|  |          'VISIBLE' =>  1,            'WAIT' =>  1, | |||
|  | 
 | |||
|  |            'ADMIN' =>  2,       'AGGREGATE' =>  2,           'ARRAY' =>  2, | |||
|  |           'BINARY' =>  2,            'BLOB' =>  2,           'CLASS' =>  2, | |||
|  |             'CLOB' =>  2,       'CONDITION' =>  2,     'CONSTRUCTOR' =>  2, | |||
|  |         'CONTAINS' =>  2,            'CUBE' =>  2,    'CURRENT_PATH' =>  2, | |||
|  |     'CURRENT_ROLE' =>  2,        'DATALINK' =>  2,           'DEREF' =>  2, | |||
|  |          'DESTROY' =>  2,      'DESTRUCTOR' =>  2,   'DETERMINISTIC' =>  2, | |||
|  |               'DO' =>  2,         'DYNAMIC' =>  2,           'EVERY' =>  2, | |||
|  |             'EXIT' =>  2,          'EXPAND' =>  2,       'EXPANDING' =>  2, | |||
|  |             'FREE' =>  2,        'FUNCTION' =>  2,        'GROUPING' =>  2, | |||
|  |          'HANDLER' =>  2,            'HAST' =>  2,            'HOST' =>  2, | |||
|  |       'INITIALIZE' =>  2,           'INOUT' =>  2,         'ITERATE' =>  2, | |||
|  |            'LARGE' =>  2,         'LATERAL' =>  2,       'LOCALTIME' =>  2, | |||
|  |   'LOCALTIMESTAMP' =>  2,         'LOCATOR' =>  2,           'MEETS' =>  2, | |||
|  |         'MODIFIES' =>  2,           'NCLOB' =>  2,       'NORMALIZE' =>  2, | |||
|  |       'ORDINALITY' =>  2,             'OUT' =>  2,       'PARAMETER' =>  2, | |||
|  |             'PATH' =>  2,          'PERIOD' =>  2,         'POSTFIX' =>  2, | |||
|  |         'PRECEDES' =>  2,          'PREFIX' =>  2,           'READS' =>  2, | |||
|  |             'REDO' =>  2,          'REPEAT' =>  2,          'RESULT' =>  2, | |||
|  |           'ROLLUP' =>  2,            'SETS' =>  2,        'SPECIFIC' =>  2, | |||
|  |     'SPECIFICTYPE' =>  2,           'START' =>  2,           'STATE' =>  2, | |||
|  |           'STATIC' =>  2,        'SUCCEEDS' =>  2,       'TERMINATE' =>  2, | |||
|  |             'THAN' =>  2,           'TREAT' =>  2,            'UNDO' =>  2, | |||
|  |            'UNTIL' =>  2, | |||
|  | 
 | |||
|  |           'ACCESS' =>  3,         'ANALYZE' =>  3,           'AUDIT' =>  3, | |||
|  |   'AUTO_INCREMENT' =>  3,          'BACKUP' =>  3,             'BDB' =>  3, | |||
|  |       'BERKELEYDB' =>  3,          'BIGINT' =>  3,           'BREAK' =>  3, | |||
|  |           'BROWSE' =>  3,           'BTREE' =>  3,            'BULK' =>  3, | |||
|  |           'CHANGE' =>  3,      'CHECKPOINT' =>  3,         'CLUSTER' =>  3, | |||
|  |        'CLUSTERED' =>  3,         'COLUMNS' =>  3,         'COMMENT' =>  3, | |||
|  |         'COMPRESS' =>  3,         'COMPUTE' =>  3,   'CONTAINSTABLE' =>  3, | |||
|  |         'DATABASE' =>  3,       'DATABASES' =>  3,        'DAY_HOUR' =>  3, | |||
|  |       'DAY_MINUTE' =>  3,      'DAY_SECOND' =>  3,            'DBCC' =>  3, | |||
|  |          'DELAYED' =>  3,            'DENY' =>  3,            'DISK' =>  3, | |||
|  |      'DISTINCTROW' =>  3,     'DISTRIBUTED' =>  3,           'DUMMY' =>  3, | |||
|  |             'DUMP' =>  3,        'ENCLOSED' =>  3,          'ERRLVL' =>  3, | |||
|  |           'ERRORS' =>  3,         'ESCAPED' =>  3,       'EXCLUSIVE' =>  3, | |||
|  |          'EXPLAIN' =>  3,          'FIELDS' =>  3,            'FILE' =>  3, | |||
|  |       'FILLFACTOR' =>  3,        'FREETEXT' =>  3,   'FREETEXTTABLE' =>  3, | |||
|  |         'FULLTEXT' =>  3,        'GEOMETRY' =>  3,            'HASH' =>  3, | |||
|  |    'HIGH_PRIORITY' =>  3,        'HOLDLOCK' =>  3,     'HOUR_MINUTE' =>  3, | |||
|  |      'HOUR_SECOND' =>  3,      'IDENTIFIED' =>  3,     'IDENTITYCOL' =>  3, | |||
|  |  'IDENTITY_INSERT' =>  3,       'INCREMENT' =>  3,           'INDEX' =>  3, | |||
|  |           'INFILE' =>  3,         'INITIAL' =>  3,          'INNODB' =>  3, | |||
|  |             'KEYS' =>  3,            'KILL' =>  3,          'LINENO' =>  3, | |||
|  |            'LINES' =>  3,            'LOAD' =>  3,            'LOCK' =>  3, | |||
|  |             'LONG' =>  3,        'LONGBLOB' =>  3,        'LONGTEXT' =>  3, | |||
|  |     'LOW_PRIORITY' =>  3, 'MASTER_SERVER_ID' =>  3,      'MAXEXTENTS' =>  3, | |||
|  |       'MEDIUMBLOB' =>  3,       'MEDIUMINT' =>  3,      'MEDIUMTEXT' =>  3, | |||
|  |        'MIDDLEINT' =>  3,           'MINUS' =>  3,   'MINUTE_SECOND' =>  3, | |||
|  |         'MLSLABEL' =>  3,            'MODE' =>  3,      'MRG_MYISAM' =>  3, | |||
|  |          'NOAUDIT' =>  3,         'NOCHECK' =>  3,      'NOCOMPRESS' =>  3, | |||
|  |     'NONCLUSTERED' =>  3,          'NOWAIT' =>  3,          'NUMBER' =>  3, | |||
|  |          'OFFLINE' =>  3,         'OFFSETS' =>  3,          'ONLINE' =>  3, | |||
|  |   'OPENDATASOURCE' =>  3,       'OPENQUERY' =>  3,      'OPENROWSET' =>  3, | |||
|  |          'OPENXML' =>  3,        'OPTIMIZE' =>  3,      'OPTIONALLY' =>  3, | |||
|  |          'OUTFILE' =>  3,            'OVER' =>  3,         'PCTFREE' =>  3, | |||
|  |          'PERCENT' =>  3,            'PLAN' =>  3,           'PRINT' =>  3, | |||
|  |             'PROC' =>  3,           'PURGE' =>  3,       'RAISERROR' =>  3, | |||
|  |              'RAW' =>  3,        'READTEXT' =>  3,     'RECONFIGURE' =>  3, | |||
|  |           'REGEXP' =>  3,          'RENAME' =>  3,     'REPLICATION' =>  3, | |||
|  |          'REQUIRE' =>  3,        'RESOURCE' =>  3,         'RESTORE' =>  3, | |||
|  |            'RLIKE' =>  3,        'ROWCOUNT' =>  3,      'ROWGUIDCOL' =>  3, | |||
|  |            'ROWID' =>  3,          'ROWNUM' =>  3,           'RTREE' =>  3, | |||
|  |             'RULE' =>  3,            'SAVE' =>  3,         'SETUSER' =>  3, | |||
|  |            'SHARE' =>  3,            'SHOW' =>  3,        'SHUTDOWN' =>  3, | |||
|  |           'SONAME' =>  3,         'SPATIAL' =>  3,  'SQL_BIG_RESULT' =>  3, | |||
|  | 'SQL_CALC_FOUND_ROWS' =>  3,'SQL_SMALL_RESULT' =>  3,        'SSL' =>  3, | |||
|  |         'STARTING' =>  3,      'STATISTICS' =>  3,   'STRAIGHT_JOIN' =>  3, | |||
|  |          'STRIPED' =>  3,      'SUCCESSFUL' =>  3,         'SYNONYM' =>  3, | |||
|  |          'SYSDATE' =>  3,          'TABLES' =>  3,      'TERMINATED' =>  3, | |||
|  |         'TEXTSIZE' =>  3,        'TINYBLOB' =>  3,         'TINYINT' =>  3, | |||
|  |         'TINYTEXT' =>  3,             'TOP' =>  3,            'TRAN' =>  3, | |||
|  |         'TRUNCATE' =>  3,         'TSEQUAL' =>  3,           'TYPES' =>  3, | |||
|  |              'UID' =>  3,          'UNLOCK' =>  3,        'UNSIGNED' =>  3, | |||
|  |       'UPDATETEXT' =>  3,             'USE' =>  3,  'USER_RESOURCES' =>  3, | |||
|  |         'VALIDATE' =>  3,       'VARBINARY' =>  3,        'VARCHAR2' =>  3, | |||
|  |          'WAITFOR' =>  3,        'WARNINGS' =>  3,       'WRITETEXT' =>  3, | |||
|  |              'XOR' =>  3,      'YEAR_MONTH' =>  3,        'ZEROFILL' =>  3 | |||
|  | ); | |||
|  | 
 | |||
|  | 
 | |||
|  |   safe_query("drop table crash_me10 $drop_attr"); | |||
|  | 
 | |||
|  |   foreach my $keyword (sort {$a cmp $b} keys %reserved_words) | |||
|  |   { | |||
|  |     $keyword_type= $reserved_words{$keyword}; | |||
|  | 
 | |||
|  |     $prompt= "Keyword ".$keyword; | |||
|  |     $config= "reserved_word_".$keywords_ext[$keyword_type]."_".lc($keyword); | |||
|  | 
 | |||
|  |     report_fail($prompt,$config, | |||
|  |       "create table crash_me10 ($keyword int not null)", | |||
|  |       "drop table crash_me10 $drop_attr" | |||
|  |     ); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Do a query on a query package object. | |||
|  | # | |||
|  | 
 | |||
|  | sub limit_query | |||
|  | { | |||
|  |   my($object,$limit)=@_; | |||
|  |   my ($query,$result,$retry,$sth); | |||
|  | 
 | |||
|  |   $query=$object->query($limit); | |||
|  |   $result=safe_query($query); | |||
|  |   if (!$result) | |||
|  |   { | |||
|  |     $object->cleanup(); | |||
|  |     return 0; | |||
|  |   } | |||
|  |   if (defined($query=$object->check_query())) | |||
|  |   { | |||
|  |     for ($retry=0 ; $retry < $retry_limit ; $retry++) | |||
|  |     { | |||
|  |       printf "query2: %-80.80s\n",$query if ($opt_log_all_queries); | |||
|  |       print LOG "$query;\n" if ($opt_log); | |||
|  |       if (($sth= $dbh->prepare($query))) | |||
|  |       { | |||
|  | 	if ($sth->execute) | |||
|  | 	{ | |||
|  | 	  $result= $object->check($sth); | |||
|  | 	  $sth->finish; | |||
|  | 	  $object->cleanup(); | |||
|  | 	  return $result; | |||
|  | 	} | |||
|  | 	print_query($query); | |||
|  | 	$sth->finish; | |||
|  |       } | |||
|  |       else | |||
|  |       { | |||
|  | 	print_query($query); | |||
|  |       } | |||
|  |       $retry=100 if (!$server->abort_if_fatal_error()); # No need to continue | |||
|  |       if ($retry == $retry_limit-2) | |||
|  |       { | |||
|  | 	print "Forcing discoennect to retry query\n" if ($opt_debug); | |||
|  | 	$dbh->disconnect || warn $dbh->errstr; | |||
|  |       } | |||
|  |       check_connect($object);   # Check that server is still up | |||
|  |     } | |||
|  |     $result=0;                  # Query failed | |||
|  |   } | |||
|  |   $object->cleanup(); | |||
|  |   return $result;               # Server couldn't handle the query | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub report | |||
|  | { | |||
|  |   my ($prompt,$limit,@queries)=@_; | |||
|  |   print "$prompt: "; | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     my $queries_result = safe_query(\@queries); | |||
|  |     add_log($limit, $safe_query_log); | |||
|  |     my $report_result; | |||
|  |     if ( $queries_result) { | |||
|  |       $report_result= "yes"; | |||
|  |       add_log($limit,"As far as all queries returned OK, result is YES"); | |||
|  |     } else { | |||
|  |       $report_result= "no"; | |||
|  |       add_log($limit,"As far as some queries didnt return OK, result is NO"); | |||
|  |     }  | |||
|  |     save_config_data($limit,$report_result,$prompt); | |||
|  |   } | |||
|  |   print "$limits{$limit}\n"; | |||
|  |   return $limits{$limit} ne "no"; | |||
|  | } | |||
|  | 
 | |||
|  | sub report_fail | |||
|  | { | |||
|  |   my ($prompt,$limit,@queries)=@_; | |||
|  |   print "$prompt: "; | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     my $queries_result = safe_query(\@queries); | |||
|  |     add_log($limit, $safe_query_log); | |||
|  |     my $report_result; | |||
|  |     if ( $queries_result) { | |||
|  |       $report_result= "no"; | |||
|  |       add_log($limit,"As far as all queries returned OK, result is NO"); | |||
|  |     } else { | |||
|  |       $report_result= "yes"; | |||
|  |       add_log($limit,"As far as some queries didnt return OK, result is YES"); | |||
|  |     }  | |||
|  |     save_config_data($limit,$report_result,$prompt); | |||
|  |   } | |||
|  |   print "$limits{$limit}\n"; | |||
|  |   return $limits{$limit} ne "no"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # Return true if one of the queries is ok | |||
|  | 
 | |||
|  | sub report_one | |||
|  | { | |||
|  |   my ($prompt,$limit,$queries)=@_; | |||
|  |   my ($query,$res,$result); | |||
|  |   print "$prompt: "; | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     save_incomplete($limit,$prompt); | |||
|  |     $result="no"; | |||
|  |     foreach $query (@$queries) | |||
|  |     { | |||
|  |       if (safe_query_l($limit,$query->[0])) | |||
|  |       { | |||
|  | 	$result= $query->[1]; | |||
|  | 	last; | |||
|  |       } | |||
|  |     } | |||
|  |     save_config_data($limit,$result,$prompt); | |||
|  |   } | |||
|  |   print "$limits{$limit}\n"; | |||
|  |   return $limits{$limit} ne "no"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # Execute query and save result as limit value. | |||
|  | 
 | |||
|  | sub report_result | |||
|  | { | |||
|  |   my ($prompt,$limit,$query)=@_; | |||
|  |   my($error); | |||
|  |   print "$prompt: "; | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     save_incomplete($limit,$prompt); | |||
|  |     $error=safe_query_result($query,"1",2); | |||
|  |     add_log($limit,$safe_query_result_log); | |||
|  |     save_config_data($limit,$error ? "not supported" :$last_result,$prompt); | |||
|  |   } | |||
|  |   print "$limits{$limit}\n"; | |||
|  |   return $limits{$limit} ne "not supported"; | |||
|  | } | |||
|  | 
 | |||
|  | sub report_trans | |||
|  | { | |||
|  |   my ($limit,$queries,$check,$clear)=@_; | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     save_incomplete($limit,$prompt); | |||
|  |     eval {undef($dbh->{AutoCommit})}; | |||
|  |     if (!$@) | |||
|  |     { | |||
|  |       if (safe_query(\@$queries)) | |||
|  |       { | |||
|  | 	  $dbh->rollback; | |||
|  |           $dbh->{AutoCommit} = 1; | |||
|  | 	    if (safe_query_result($check,"","")) { | |||
|  |               add_log($limit,$safe_query_result_log);	     | |||
|  | 	      save_config_data($limit,"yes",$limit); | |||
|  | 	    } | |||
|  | 	    safe_query($clear); | |||
|  |       } else { | |||
|  |         add_log($limit,$safe_query_log); | |||
|  |         save_config_data($limit,"error",$limit); | |||
|  |       } | |||
|  |       $dbh->{AutoCommit} = 1; | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       add_log($limit,"Couldnt undef autocommit ?? "); | |||
|  |       save_config_data($limit,"no",$limit); | |||
|  |     } | |||
|  |     safe_query($clear); | |||
|  |   } | |||
|  |   return $limits{$limit} ne "yes"; | |||
|  | } | |||
|  | 
 | |||
|  | sub report_rollback | |||
|  | { | |||
|  |   my ($limit,$queries,$check,$clear)=@_; | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     save_incomplete($limit,$prompt); | |||
|  |     eval {undef($dbh->{AutoCommit})}; | |||
|  |     if (!$@) | |||
|  |     { | |||
|  |       if (safe_query(\@$queries)) | |||
|  |       { | |||
|  |           add_log($limit,$safe_query_log); | |||
|  | 
 | |||
|  | 	  $dbh->rollback; | |||
|  |            $dbh->{AutoCommit} = 1; | |||
|  |            if (safe_query($check)) { | |||
|  | 	      add_log($limit,$safe_query_log); | |||
|  | 	      save_config_data($limit,"no",$limit); | |||
|  | 	    }  else  { | |||
|  | 	      add_log($limit,$safe_query_log); | |||
|  | 	      save_config_data($limit,"yes",$limit); | |||
|  | 	    }; | |||
|  | 	    safe_query($clear); | |||
|  |       } else { | |||
|  |         add_log($limit,$safe_query_log); | |||
|  |         save_config_data($limit,"error",$limit); | |||
|  |       } | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       add_log($limit,'Couldnt undef Autocommit??'); | |||
|  |       save_config_data($limit,"error",$limit); | |||
|  |     } | |||
|  |     safe_query($clear); | |||
|  |   } | |||
|  |   $dbh->{AutoCommit} = 1; | |||
|  |   return $limits{$limit} ne "yes"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub check_and_report | |||
|  | { | |||
|  |   my ($prompt,$limit,$pre,$query,$post,$answer,$string_type,$skip_prompt, | |||
|  |       $function)=@_; | |||
|  |   my ($tmp); | |||
|  |   $function=0 if (!defined($function)); | |||
|  | 
 | |||
|  |   print "$prompt: " if (!defined($skip_prompt)); | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     save_incomplete($limit,$prompt); | |||
|  |     $tmp=1-safe_query(\@$pre); | |||
|  |     add_log($limit,$safe_query_log); | |||
|  |     if (!$tmp)  | |||
|  |     { | |||
|  |         $tmp=safe_query_result($query,$answer,$string_type) ; | |||
|  |         add_log($limit,$safe_query_result_log); | |||
|  |     };	 | |||
|  |     safe_query(\@$post); | |||
|  |     add_log($limit,$safe_query_log); | |||
|  |     delete $limits{$limit}; | |||
|  |     if ($function == 3)		# Report error as 'no'. | |||
|  |     { | |||
|  |       $function=0; | |||
|  |       $tmp= -$tmp; | |||
|  |     } | |||
|  |     if ($function == 0 || | |||
|  | 	$tmp != 0 && $function == 1 || | |||
|  | 	$tmp == 0 && $function== 2) | |||
|  |     { | |||
|  |       save_config_data($limit, $tmp == 0 ? "yes" : $tmp == 1 ? "no" : "error", | |||
|  | 		       $prompt); | |||
|  |       print "$limits{$limit}\n"; | |||
|  |       return $function == 0 ? $limits{$limit} eq "yes" : 0; | |||
|  |     } | |||
|  |     return 1;			# more things to check | |||
|  |   } | |||
|  |   print "$limits{$limit}\n"; | |||
|  |   return 0 if ($function); | |||
|  |   return $limits{$limit} eq "yes"; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub try_and_report | |||
|  | { | |||
|  |   my ($prompt,$limit,@tests)=@_; | |||
|  |   my ($tmp,$test,$type); | |||
|  | 
 | |||
|  |   print "$prompt: "; | |||
|  | 
 | |||
|  |   if (!defined($limits{$limit})) | |||
|  |   { | |||
|  |     save_incomplete($limit,$prompt); | |||
|  |     $type="no";			# Not supported | |||
|  |     foreach $test (@tests) | |||
|  |     { | |||
|  |       my $tmp_type= shift(@$test); | |||
|  |       if (safe_query_l($limit,\@$test)) | |||
|  |       { | |||
|  | 	$type=$tmp_type; | |||
|  | 	goto outer; | |||
|  |       } | |||
|  |     } | |||
|  |   outer: | |||
|  |     save_config_data($limit, $type, $prompt); | |||
|  |   } | |||
|  |   print "$limits{$limit}\n"; | |||
|  |   return $limits{$limit} ne "no"; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Just execute the query and check values;  Returns 1 if ok | |||
|  | # | |||
|  | 
 | |||
|  | sub execute_and_check | |||
|  | { | |||
|  |   my ($key,$pre,$query,$post,$answer,$string_type)=@_; | |||
|  |   my ($tmp); | |||
|  | 
 | |||
|  |   $tmp=safe_query_l($key,\@$pre); | |||
|  | 
 | |||
|  |   $tmp=safe_query_result_l($key,$query,$answer,$string_type) == 0 if ($tmp); | |||
|  |   safe_query_l($key,\@$post); | |||
|  |   return $tmp; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # returns 0 if ok, 1 if error, -1 if wrong answer | |||
|  | # Sets $last_result to value of query | |||
|  | sub safe_query_result_l{ | |||
|  |   my ($key,$query,$answer,$result_type)=@_; | |||
|  |   my $r = safe_query_result($query,$answer,$result_type); | |||
|  |   add_log($key,$safe_query_result_log); | |||
|  |   return $r; | |||
|  | }   | |||
|  | 
 | |||
|  | sub safe_query_result | |||
|  | { | |||
|  | # result type can be  | |||
|  | #  8 (must be empty), 2 (Any value), 0 (number) | |||
|  | #  1 (char, endspaces can differ), 3 (exact char), 4 (NULL) | |||
|  | #  5 (char with prefix), 6 (exact, errors are ignored) | |||
|  | #  7 (array of numbers) | |||
|  |   my ($query,$answer,$result_type)=@_; | |||
|  |   my ($sth,$row,$result,$retry); | |||
|  |   undef($last_result); | |||
|  |   $safe_query_result_log=""; | |||
|  |    | |||
|  |   printf "\nquery3: %-80.80s\n",$query  if ($opt_log_all_queries); | |||
|  |   print LOG "$query;\n" if ($opt_log); | |||
|  |   $safe_query_result_log="<".$query."\n"; | |||
|  | 
 | |||
|  |   for ($retry=0; $retry < $retry_limit ; $retry++) | |||
|  |   { | |||
|  |     if (!($sth=$dbh->prepare($query))) | |||
|  |     { | |||
|  |       print_query($query); | |||
|  |       $safe_query_result_log .= "> prepare failed:".$dbh->errstr."\n"; | |||
|  |        | |||
|  |       if ($server->abort_if_fatal_error()) | |||
|  |       { | |||
|  | 	check_connect();	# Check that server is still up | |||
|  | 	next;			# Retry again | |||
|  |       } | |||
|  |       check_connect();		# Check that server is still up | |||
|  |       return 1; | |||
|  |     } | |||
|  |     if (!$sth->execute) | |||
|  |     { | |||
|  |       print_query($query); | |||
|  |       $safe_query_result_log .= "> execute failed:".$dbh->errstr."\n"; | |||
|  |       if ($server->abort_if_fatal_error()) | |||
|  |       { | |||
|  | 	check_connect();	# Check that server is still up | |||
|  | 	next;			# Retry again | |||
|  |       } | |||
|  |       check_connect();		# Check that server is still up | |||
|  |       return 1; | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       last; | |||
|  |     } | |||
|  |   } | |||
|  |   if (!($row=$sth->fetchrow_arrayref)) | |||
|  |   { | |||
|  |     print "\nquery: $query didn't return any result\n" if ($opt_debug); | |||
|  |     $safe_query_result_log .= "> didn't return any result:".$dbh->errstr."\n";     | |||
|  |     $sth->finish; | |||
|  |     return ($result_type == 8) ? 0 : 1; | |||
|  |   } | |||
|  |   if ($result_type == 8) | |||
|  |   { | |||
|  |     $sth->finish; | |||
|  |     return 1; | |||
|  |   } | |||
|  |   $result=0;                  	# Ok | |||
|  |   $last_result= $row->[0];	# Save for report_result; | |||
|  |   $safe_query_result_log .= ">".$last_result."\n";     | |||
|  |   # Note: | |||
|  |   # if ($result_type == 2)        We accept any return value as answer | |||
|  | 
 | |||
|  |   if ($result_type == 0)	# Compare numbers | |||
|  |   { | |||
|  |     $row->[0] =~ s/,/./;	# Fix if ',' is used instead of '.' | |||
|  |     if ($row->[0] != $answer && (abs($row->[0]- $answer)/ | |||
|  | 				 (abs($row->[0]) + abs($answer))) > 0.01) | |||
|  |     { | |||
|  |       $result=-1; | |||
|  |       $safe_query_result_log .=  | |||
|  |           "We expected '$answer' but got '$last_result' \n";     | |||
|  |     } | |||
|  |   } | |||
|  |   elsif ($result_type == 1)	# Compare where end space may differ | |||
|  |   { | |||
|  |     $row->[0] =~ s/\s+$//; | |||
|  |     if ($row->[0] ne $answer) | |||
|  |     { | |||
|  |      $result=-1; | |||
|  |      $safe_query_result_log .=  | |||
|  |          "We expected '$answer' but got '$last_result' \n";     | |||
|  |     } ; | |||
|  |   } | |||
|  |   elsif ($result_type == 3)	# This should be a exact match | |||
|  |   { | |||
|  |      if ($row->[0] ne $answer) | |||
|  |      {  | |||
|  |       $result= -1;  | |||
|  |       $safe_query_result_log .=  | |||
|  |           "We expected '$answer' but got '$last_result' \n";     | |||
|  |     }; | |||
|  |   } | |||
|  |   elsif ($result_type == 4)	# If results should be NULL | |||
|  |   { | |||
|  |     if (defined($row->[0])) | |||
|  |     {  | |||
|  |      $result= -1;  | |||
|  |      $safe_query_result_log .=  | |||
|  |          "We expected NULL but got '$last_result' \n";     | |||
|  |     }; | |||
|  |   } | |||
|  |   elsif ($result_type == 5)	# Result should have given prefix | |||
|  |   { | |||
|  |      if (length($row->[0]) < length($answer) && | |||
|  | 		    substr($row->[0],1,length($answer)) ne $answer) | |||
|  |      {  | |||
|  |       $result= -1 ; | |||
|  |       $safe_query_result_log .=  | |||
|  |         "Result must have prefix '$answer', but  '$last_result' \n";     | |||
|  |      }; | |||
|  |   } | |||
|  |   elsif ($result_type == 6)	# Exact match but ignore errors | |||
|  |   { | |||
|  |     if ($row->[0] ne $answer)     | |||
|  |     { $result= 1; | |||
|  |       $safe_query_result_log .=  | |||
|  |           "We expected '$answer' but got '$last_result' \n";     | |||
|  |     } ; | |||
|  |   } | |||
|  |   elsif ($result_type == 7)	# Compare against array of numbers | |||
|  |   { | |||
|  |     if ($row->[0] != $answer->[0]) | |||
|  |     { | |||
|  |       $safe_query_result_log .= "must be '$answer->[0]' \n";     | |||
|  |       $result= -1; | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       my ($value); | |||
|  |       shift @$answer; | |||
|  |       while (($row=$sth->fetchrow_arrayref)) | |||
|  |       { | |||
|  |        $safe_query_result_log .= ">$row\n";     | |||
|  | 
 | |||
|  | 	$value=shift(@$answer); | |||
|  | 	if (!defined($value)) | |||
|  | 	{ | |||
|  | 	  print "\nquery: $query returned to many results\n" | |||
|  | 	    if ($opt_debug); | |||
|  |           $safe_query_result_log .= "It returned to many results \n";    	     | |||
|  | 	  $result= 1; | |||
|  | 	  last; | |||
|  | 	} | |||
|  | 	if ($row->[0] != $value) | |||
|  | 	{ | |||
|  |           $safe_query_result_log .= "Must return $value here \n";    	     | |||
|  | 	  $result= -1; | |||
|  | 	  last; | |||
|  | 	} | |||
|  |       } | |||
|  |       if ($#$answer != -1) | |||
|  |       { | |||
|  | 	print "\nquery: $query returned too few results\n" | |||
|  | 	  if ($opt_debug); | |||
|  |         $safe_query_result_log .= "It returned too few results \n";    	     | |||
|  | 	$result= 1; | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  |   $sth->finish; | |||
|  |   print "\nquery: '$query' returned '$row->[0]' instead of '$answer'\n" | |||
|  |     if ($opt_debug && $result && $result_type != 7); | |||
|  |   return $result; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Find limit using binary search.  This is a weighed binary search that | |||
|  | # will prefere lower limits to get the server to crash as  | |||
|  | # few times as possible | |||
|  | 
 | |||
|  | 
 | |||
|  | sub find_limit() | |||
|  | { | |||
|  |   my ($prompt,$limit,$query)=@_; | |||
|  |   my ($first,$end,$i,$tmp,@tmp_array, $queries); | |||
|  |   print "$prompt: "; | |||
|  |   if (defined($end=$limits{$limit})) | |||
|  |   { | |||
|  |     print "$end (cache)\n"; | |||
|  |     return $end; | |||
|  |   } | |||
|  |   save_incomplete($limit,$prompt); | |||
|  |   add_log($limit,"We are trying (example with N=5):"); | |||
|  |   $queries = $query->query(5); | |||
|  |   if (ref($queries) ne "ARRAY") | |||
|  |   { | |||
|  |     push(@tmp_array,$queries); | |||
|  |     $queries= \@tmp_array; | |||
|  |   } | |||
|  |   foreach $tmp (@$queries) | |||
|  |   {   add_log($limit,repr_query($tmp));  }     | |||
|  | 
 | |||
|  |   if (defined($queries = $query->check_query())) | |||
|  |   {  | |||
|  |     if (ref($queries) ne "ARRAY") | |||
|  |     { | |||
|  |       @tmp_array=(); | |||
|  |       push(@tmp_array,$queries);  | |||
|  |       $queries= \@tmp_array; | |||
|  |     } | |||
|  |     foreach $tmp (@$queries) | |||
|  |       {   add_log($limit,repr_query($tmp));  }     | |||
|  |   } | |||
|  |   if (defined($query->{'init'}) && !defined($end=$limits{'restart'}{'tohigh'})) | |||
|  |   { | |||
|  |     if (!safe_query_l($limit,$query->{'init'})) | |||
|  |     { | |||
|  |       $query->cleanup(); | |||
|  |       return "error"; | |||
|  |     } | |||
|  |   } | |||
|  | 
 | |||
|  |   if (!limit_query($query,1))           # This must work | |||
|  |   { | |||
|  |     print "\nMaybe fatal error: Can't check '$prompt' for limit=1\n". | |||
|  |     "error: $last_error\n"; | |||
|  |     return "error"; | |||
|  |   } | |||
|  | 
 | |||
|  |   $first=0; | |||
|  |   $first=$limits{'restart'}{'low'} if ($limits{'restart'}{'low'}); | |||
|  | 
 | |||
|  |   if (defined($end=$limits{'restart'}{'tohigh'})) | |||
|  |   { | |||
|  |     $end--; | |||
|  |     print "\nRestarting this with low limit: $first and high limit: $end\n"; | |||
|  |     delete $limits{'restart'}; | |||
|  |     $i=$first+int(($end-$first+4)/5);           # Prefere lower on errors | |||
|  |   } | |||
|  |   else | |||
|  |   { | |||
|  |     $end= $query->max_limit(); | |||
|  |     $i=int(($end+$first)/2); | |||
|  |   } | |||
|  |   my $log_str = ""; | |||
|  |   unless(limit_query($query,0+$end)) { | |||
|  |     while ($first < $end) | |||
|  |     { | |||
|  |       print "." if ($opt_debug); | |||
|  |       save_config_data("restart",$i,"") if ($opt_restart); | |||
|  |       if (limit_query($query,$i)) | |||
|  |       { | |||
|  |         $first=$i; | |||
|  | 	$log_str .= " $i:OK"; | |||
|  |         $i=$first+int(($end-$first+1)/2); # to be a bit faster to go up | |||
|  |       } | |||
|  |       else | |||
|  |       {  | |||
|  |         $end=$i-1; | |||
|  | 	$log_str .= " $i:FAIL"; | |||
|  |         $i=$first+int(($end-$first+4)/5); # Prefere lower on errors | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  |   $end+=$query->{'offset'} if ($end && defined($query->{'offset'})); | |||
|  |   if ($end >= $query->{'max_limit'} && | |||
|  |       substr($query->{'max_limit'},0,1) eq '+') | |||
|  |   { | |||
|  |     $end= $query->{'max_limit'}; | |||
|  |   } | |||
|  |   print "$end\n"; | |||
|  |   add_log($limit,$log_str); | |||
|  |   save_config_data($limit,$end,$prompt); | |||
|  |   delete $limits{'restart'}; | |||
|  |   return $end; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Check that the query works! | |||
|  | # | |||
|  | 
 | |||
|  | sub assert | |||
|  | { | |||
|  |   my($query)=@_; | |||
|  | 
 | |||
|  |   if (!safe_query($query)) | |||
|  |   { | |||
|  |     $query=join("; ",@$query) if (ref($query) eq "ARRAY"); | |||
|  |     print "\nFatal error:\nquery: '$query'\nerror: $DBI::errstr\n"; | |||
|  |     exit 1; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub read_config_data | |||
|  | { | |||
|  |   my ($key,$limit,$prompt); | |||
|  |   if (-e $opt_config_file) | |||
|  |   { | |||
|  |     open(CONFIG_FILE,"+<$opt_config_file") || | |||
|  |       die "Can't open configure file $opt_config_file\n"; | |||
|  |     print "Reading old values from cache: $opt_config_file\n"; | |||
|  |   } | |||
|  |   else | |||
|  |   { | |||
|  |     open(CONFIG_FILE,"+>>$opt_config_file") || | |||
|  |       die "Can't create configure file $opt_config_file: $!\n"; | |||
|  |   } | |||
|  |   select CONFIG_FILE; | |||
|  |   $|=1; | |||
|  |   select STDOUT; | |||
|  |   while (<CONFIG_FILE>) | |||
|  |   { | |||
|  |     chomp; | |||
|  |     if (/^(\S+)=([^\#]*[^\#\s])\s*(\# .*)*$/) | |||
|  |     { | |||
|  |       $key=$1; $limit=$2 ; $prompt=$3; | |||
|  |       if (!$opt_quick || $limit =~ /\d/ || $key =~ /crash_me/) | |||
|  |       { | |||
|  | 	if ($key !~ /restart/i) | |||
|  | 	{ | |||
|  | 	  $limits{$key}=$limit eq "null"? undef : $limit; | |||
|  | 	  $prompts{$key}=length($prompt) ? substr($prompt,2) : ""; | |||
|  | 	  $last_read=$key; | |||
|  | 	  delete $limits{'restart'}; | |||
|  | 	} | |||
|  | 	else | |||
|  | 	{ | |||
|  | 	  $limit_changed=1; | |||
|  | 	  if ($limit > $limits{'restart'}{'tohigh'}) | |||
|  | 	  { | |||
|  | 	    $limits{'restart'}{'low'} = $limits{'restart'}{'tohigh'}; | |||
|  | 	  } | |||
|  | 	  $limits{'restart'}{'tohigh'} = $limit; | |||
|  | 	} | |||
|  |       } | |||
|  |     } | |||
|  |     elsif (/\s*###(.*)$/)    # log line | |||
|  |     { | |||
|  |        # add log line for previously read key | |||
|  |        $log{$last_read} .= "$1\n"; | |||
|  |     } | |||
|  |     elsif (!/^\s*$/ && !/^\#/) | |||
|  |     { | |||
|  |       die "Wrong config row: $_\n"; | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub save_config_data | |||
|  | { | |||
|  |   my ($key,$limit,$prompt)=@_; | |||
|  |   $prompts{$key}=$prompt; | |||
|  |   return if (defined($limits{$key}) && $limits{$key} eq $limit); | |||
|  |   if (!defined($limit) || $limit eq "") | |||
|  |   { | |||
|  | #    die "Undefined limit for $key\n"; | |||
|  |      $limit = 'null';  | |||
|  |   } | |||
|  |   print CONFIG_FILE "$key=$limit\t# $prompt\n"; | |||
|  |   $limits{$key}=$limit; | |||
|  |   $limit_changed=1; | |||
|  | # now write log lines (immediatelly after limits) | |||
|  |   my $line; | |||
|  |   my $last_line_was_empty=0; | |||
|  |   foreach $line (split /\n/, $log{$key}) | |||
|  |   { | |||
|  |     print CONFIG_FILE "   ###$line\n"  | |||
|  | 	unless ( ($last_line_was_empty eq 1)   | |||
|  | 	         && ($line =~ /^\s+$/)  ); | |||
|  |     $last_line_was_empty= ($line =~ /^\s+$/)?1:0; | |||
|  |   };      | |||
|  | 
 | |||
|  |   if (($opt_restart && $limits{'operating_system'} =~ /windows/i) || | |||
|  | 		       ($limits{'operating_system'} =~ /NT/)) | |||
|  |   { | |||
|  |     # If perl crashes in windows, everything is lost (Wonder why? :) | |||
|  |     close CONFIG_FILE; | |||
|  |     open(CONFIG_FILE,"+>>$opt_config_file") || | |||
|  |       die "Can't reopen configure file $opt_config_file: $!\n"; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | sub add_log | |||
|  | { | |||
|  |   my $key = shift; | |||
|  |   my $line = shift; | |||
|  |   $log{$key} .= $line . "\n" if ($opt_verbose);;   | |||
|  | } | |||
|  | 
 | |||
|  | sub save_all_config_data | |||
|  | { | |||
|  |   my ($key,$tmp); | |||
|  |   close CONFIG_FILE; | |||
|  |   return if (!$limit_changed); | |||
|  |   open(CONFIG_FILE,">$opt_config_file") || | |||
|  |     die "Can't create configure file $opt_config_file: $!\n"; | |||
|  |   select CONFIG_FILE; | |||
|  |   $|=1; | |||
|  |   select STDOUT; | |||
|  |   delete $limits{'restart'}; | |||
|  | 
 | |||
|  |   print CONFIG_FILE  | |||
|  |        "#This file is automaticly generated by crash-me $version\n\n"; | |||
|  |   foreach $key (sort keys %limits) | |||
|  |   { | |||
|  |     $tmp="$key=$limits{$key}"; | |||
|  |     print CONFIG_FILE $tmp . ("\t" x (int((32-min(length($tmp),32)+7)/8)+1)) . | |||
|  |       "# $prompts{$key}\n"; | |||
|  |      my $line; | |||
|  |      my $last_line_was_empty=0; | |||
|  |      foreach $line (split /\n/, $log{$key}) | |||
|  |      { | |||
|  |         print CONFIG_FILE "   ###$line\n" unless  | |||
|  | 	      ( ($last_line_was_empty eq 1) && ($line =~ /^\s*$/)); | |||
|  |         $last_line_was_empty= ($line =~ /^\s*$/)?1:0; | |||
|  |      };      | |||
|  |   } | |||
|  |   close CONFIG_FILE; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Save 'incomplete' in the limits file to be able to continue if | |||
|  | # crash-me dies because of a bug in perl/DBI | |||
|  | 
 | |||
|  | sub save_incomplete | |||
|  | { | |||
|  |   my ($limit,$prompt)= @_; | |||
|  |   save_config_data($limit,"incompleted",$prompt) if ($opt_restart); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub check_repeat | |||
|  | { | |||
|  |   my ($sth,$limit)=@_; | |||
|  |   my ($row); | |||
|  | 
 | |||
|  |   return 0 if (!($row=$sth->fetchrow_arrayref)); | |||
|  |   return (defined($row->[0]) && ('a' x $limit) eq $row->[0]) ? 1 : 0; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub min | |||
|  | { | |||
|  |   my($min)=$_[0]; | |||
|  |   my($i); | |||
|  |   for ($i=1 ; $i <= $#_; $i++) | |||
|  |   { | |||
|  |     $min=$_[$i] if ($min > $_[$i]); | |||
|  |   } | |||
|  |   return $min; | |||
|  | } | |||
|  | 
 | |||
|  | sub sql_concat | |||
|  | { | |||
|  |   my ($a,$b)= @_; | |||
|  |   return "$a || $b" if ($limits{'func_sql_concat_as_||'} eq 'yes'); | |||
|  |   return "concat($a,$b)" if ($limits{'func_odbc_concat'} eq 'yes'); | |||
|  |   return "$a + $b" if ($limits{'func_extra_concat_as_+'} eq 'yes'); | |||
|  |   return undef; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Returns a list of statements to create a table in a portable manner | |||
|  | # but still utilizing features in the databases. | |||
|  | # | |||
|  | 
 | |||
|  | sub create_table | |||
|  | { | |||
|  |   my($table_name,$fields,$index,$extra) = @_; | |||
|  |   my($query,$nr,$parts,@queries,@index); | |||
|  | 
 | |||
|  |   $extra="" if (!defined($extra)); | |||
|  | 
 | |||
|  |   $query="create table $table_name ("; | |||
|  |   $nr=0; | |||
|  |   foreach $field (@$fields) | |||
|  |   { | |||
|  |     $query.= $field . ','; | |||
|  |   } | |||
|  |   foreach $index (@$index) | |||
|  |   { | |||
|  |     $index =~ /\(([^\(]*)\)$/i; | |||
|  |     $parts=$1; | |||
|  |     if ($index =~ /^primary key/) | |||
|  |     { | |||
|  |       if ($limits{'primary_key_in_create'} eq 'yes') | |||
|  |       { | |||
|  | 	$query.= $index . ','; | |||
|  |       } | |||
|  |       else | |||
|  |       { | |||
|  | 	push(@queries, | |||
|  | 	     "create unique index ${table_name}_prim on $table_name ($parts)"); | |||
|  |       } | |||
|  |     } | |||
|  |     elsif ($index =~ /^unique/) | |||
|  |     { | |||
|  |       if ($limits{'unique_in_create'} eq 'yes') | |||
|  |       { | |||
|  | 	$query.= "unique ($parts),"; | |||
|  |       } | |||
|  |       else | |||
|  |       { | |||
|  | 	$nr++; | |||
|  | 	push(@queries, | |||
|  | 	     "create unique index ${table_name}_$nr on $table_name ($parts)"); | |||
|  | 
 | |||
|  |       } | |||
|  |     } | |||
|  |     else | |||
|  |     { | |||
|  |       if ($limits{'index_in_create'} eq 'yes') | |||
|  |       { | |||
|  | 	$query.= "index ($parts),"; | |||
|  |       } | |||
|  |       else | |||
|  |       { | |||
|  | 	$nr++; | |||
|  | 	push(@queries, | |||
|  | 	     "create index ${table_name}_$nr on $table_name ($1)"); | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  |   chop($query); | |||
|  |   $query.= ") $extra"; | |||
|  |   unshift(@queries,$query); | |||
|  |   return @queries; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # | |||
|  | # This is used by some query packages to change: | |||
|  | # %d -> limit | |||
|  | # %s -> 'a' x limit | |||
|  | # %v -> "1,1,1,1,1" where there are 'limit' number of ones | |||
|  | # %f -> q1,q2,q3.... | |||
|  | # %F -> q1 integer,q2 integer,q3 integer.... | |||
|  | 
 | |||
|  | sub fix_query | |||
|  | { | |||
|  |   my ($query,$limit)=@_; | |||
|  |   my ($repeat,$i); | |||
|  | 
 | |||
|  |   return $query if !(defined($query)); | |||
|  |   $query =~ s/%d/$limit/g; | |||
|  |   if ($query =~ /%s/) | |||
|  |   { | |||
|  |     $repeat= 'a' x $limit; | |||
|  |     $query =~ s/%s/$repeat/g; | |||
|  |   } | |||
|  |   if ($query =~ /%v/) | |||
|  |   { | |||
|  |     $repeat= '1,' x $limit; | |||
|  |     chop($repeat); | |||
|  |     $query =~ s/%v/$repeat/g; | |||
|  |   } | |||
|  |   if ($query =~ /%f/) | |||
|  |   { | |||
|  |     $repeat=""; | |||
|  |     for ($i=1 ; $i <= $limit ; $i++) | |||
|  |     { | |||
|  |       $repeat.="q$i,"; | |||
|  |     } | |||
|  |     chop($repeat); | |||
|  |     $query =~ s/%f/$repeat/g; | |||
|  |   } | |||
|  |   if ($query =~ /%F/) | |||
|  |   { | |||
|  |     $repeat=""; | |||
|  |     for ($i=1 ; $i <= $limit ; $i++) | |||
|  |     { | |||
|  |       $repeat.="q$i integer,"; | |||
|  |     } | |||
|  |     chop($repeat); | |||
|  |     $query =~ s/%F/$repeat/g; | |||
|  |   } | |||
|  |   return $query; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # | |||
|  | # Different query packages | |||
|  | # | |||
|  | 
 | |||
|  | package query_repeat; | |||
|  | 
 | |||
|  | sub new | |||
|  | { | |||
|  |   my ($type,$init,$query,$add1,$add_mid,$add,$add_end,$end_query,$cleanup, | |||
|  |       $max_limit, $check, $offset)=@_; | |||
|  |   my $self={}; | |||
|  |   if (defined($init) && $#$init != -1) | |||
|  |   { | |||
|  |     $self->{'init'}=$init; | |||
|  |   } | |||
|  |   $self->{'query'}=$query; | |||
|  |   $self->{'add1'}=$add1; | |||
|  |   $self->{'add_mid'}=$add_mid; | |||
|  |   $self->{'add'}=$add; | |||
|  |   $self->{'add_end'}=$add_end; | |||
|  |   $self->{'end_query'}=$end_query; | |||
|  |   $self->{'cleanup'}=$cleanup; | |||
|  |   $self->{'max_limit'}=(defined($max_limit) ? $max_limit : $main::query_size); | |||
|  |   $self->{'check'}=$check; | |||
|  |   $self->{'offset'}=$offset; | |||
|  |   $self->{'printf'}= ($add =~ /%d/); | |||
|  |   bless $self; | |||
|  | } | |||
|  | 
 | |||
|  | sub query | |||
|  | { | |||
|  |   my ($self,$limit)=@_; | |||
|  |   if (!$self->{'printf'}) | |||
|  |   { | |||
|  |     return $self->{'query'} . ($self->{'add'} x $limit) . | |||
|  |       ($self->{'add_end'} x $limit) . $self->{'end_query'}; | |||
|  |   } | |||
|  |   my ($tmp,$tmp2,$tmp3,$i); | |||
|  |   $tmp=$self->{'query'}; | |||
|  |   if ($self->{'add1'}) | |||
|  |   { | |||
|  |     for ($i=0; $i < $limit ; $i++) | |||
|  |     { | |||
|  |       $tmp3 = $self->{'add1'}; | |||
|  |       $tmp3 =~ s/%d/$i/g; | |||
|  |       $tmp  .= $tmp3; | |||
|  |     } | |||
|  |   } | |||
|  |   $tmp .= " ".$self->{'add_mid'}; | |||
|  |   if ($self->{'add'}) | |||
|  |   { | |||
|  |     for ($i=0; $i < $limit ; $i++) | |||
|  |     { | |||
|  |       $tmp2 = $self->{'add'}; | |||
|  |       $tmp2 =~ s/%d/$i/g; | |||
|  |       $tmp  .= $tmp2; | |||
|  |     } | |||
|  |   } | |||
|  |   return ($tmp . | |||
|  | 	  ($self->{'add_end'} x $limit) . $self->{'end_query'}); | |||
|  | } | |||
|  | 
 | |||
|  | sub max_limit | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   my $tmp; | |||
|  |   $tmp=int(($main::limits{"query_size"}-length($self->{'query'}) | |||
|  | 	    -length($self->{'add_mid'})-length($self->{'end_query'}))/ | |||
|  | 	   (length($self->{'add1'})+ | |||
|  | 	   length($self->{'add'})+length($self->{'add_end'}))); | |||
|  |   return main::min($self->{'max_limit'},$tmp); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub cleanup | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   my($tmp,$statement); | |||
|  |   $tmp=$self->{'cleanup'}; | |||
|  |   foreach $statement (@$tmp) | |||
|  |   { | |||
|  |     main::safe_query($statement) if (defined($statement) && length($statement)); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | sub check | |||
|  | { | |||
|  |   my ($self,$sth)=@_; | |||
|  |   my $check=$self->{'check'}; | |||
|  |   return &$check($sth,$self->{'limit'}) if (defined($check)); | |||
|  |   return 1; | |||
|  | } | |||
|  | 
 | |||
|  | sub check_query | |||
|  | { | |||
|  |   return undef; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | package query_num; | |||
|  | 
 | |||
|  | sub new | |||
|  | { | |||
|  |   my ($type,$query,$end_query,$cleanup,$max_limit,$check)=@_; | |||
|  |   my $self={}; | |||
|  |   $self->{'query'}=$query; | |||
|  |   $self->{'end_query'}=$end_query; | |||
|  |   $self->{'cleanup'}=$cleanup; | |||
|  |   $self->{'max_limit'}=$max_limit; | |||
|  |   $self->{'check'}=$check; | |||
|  |   bless $self; | |||
|  | } | |||
|  | 
 | |||
|  | sub query | |||
|  | { | |||
|  |   my ($self,$i)=@_; | |||
|  |   $self->{'limit'}=$i; | |||
|  |   return "$self->{'query'}$i$self->{'end_query'}"; | |||
|  | } | |||
|  | 
 | |||
|  | sub max_limit | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   return $self->{'max_limit'}; | |||
|  | } | |||
|  | 
 | |||
|  | sub cleanup | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   my($statement); | |||
|  |   foreach $statement ($self->{'$cleanup'}) | |||
|  |   { | |||
|  |     main::safe_query($statement) if (defined($statement) && length($statement)); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub check | |||
|  | { | |||
|  |   my ($self,$sth)=@_; | |||
|  |   my $check=$self->{'check'}; | |||
|  |   return &$check($sth,$self->{'limit'}) if (defined($check)); | |||
|  |   return 1; | |||
|  | } | |||
|  | 
 | |||
|  | sub check_query | |||
|  | { | |||
|  |   return undef; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # This package is used when testing CREATE TABLE! | |||
|  | # | |||
|  | 
 | |||
|  | package query_table; | |||
|  | 
 | |||
|  | sub new | |||
|  | { | |||
|  |   my ($type,$query, $add, $end_query, $extra_init, $safe_query, $check, | |||
|  |       $cleanup, $max_limit, $offset)=@_; | |||
|  |   my $self={}; | |||
|  |   $self->{'query'}=$query; | |||
|  |   $self->{'add'}=$add; | |||
|  |   $self->{'end_query'}=$end_query; | |||
|  |   $self->{'extra_init'}=$extra_init; | |||
|  |   $self->{'safe_query'}=$safe_query; | |||
|  |   $self->{'check'}=$check; | |||
|  |   $self->{'cleanup'}=$cleanup; | |||
|  |   $self->{'max_limit'}=$max_limit; | |||
|  |   $self->{'offset'}=$offset; | |||
|  |   bless $self; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub query | |||
|  | { | |||
|  |   my ($self,$limit)=@_; | |||
|  |   $self->{'limit'}=$limit; | |||
|  |   $self->cleanup();     # Drop table before create | |||
|  | 
 | |||
|  |   my ($tmp,$tmp2,$i,$query,@res); | |||
|  |   $tmp =$self->{'query'}; | |||
|  |   $tmp =~ s/%d/$limit/g; | |||
|  |   for ($i=1; $i <= $limit ; $i++) | |||
|  |   { | |||
|  |     $tmp2 = $self->{'add'}; | |||
|  |     $tmp2 =~ s/%d/$i/g; | |||
|  |     $tmp  .= $tmp2; | |||
|  |   } | |||
|  |   push(@res,$tmp . $self->{'end_query'}); | |||
|  |   $tmp=$self->{'extra_init'}; | |||
|  |   foreach $query (@$tmp) | |||
|  |   { | |||
|  |     push(@res,main::fix_query($query,$limit)); | |||
|  |   } | |||
|  |   return \@res; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub max_limit | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   return $self->{'max_limit'}; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub check_query | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   return main::fix_query($self->{'safe_query'},$self->{'limit'}); | |||
|  | } | |||
|  | 
 | |||
|  | sub check | |||
|  | { | |||
|  |   my ($self,$sth)=@_; | |||
|  |   my $check=$self->{'check'}; | |||
|  |   return 0 if (!($row=$sth->fetchrow_arrayref)); | |||
|  |   if (defined($check)) | |||
|  |   { | |||
|  |     return (defined($row->[0]) && | |||
|  | 	    $row->[0] eq main::fix_query($check,$self->{'limit'})) ? 1 : 0; | |||
|  |   } | |||
|  |   return 1; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | # Remove table before and after create table query | |||
|  | 
 | |||
|  | sub cleanup() | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   main::safe_query(main::fix_query($self->{'cleanup'},$self->{'limit'})); | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Package to do many queries with %d, and %s substitution | |||
|  | # | |||
|  | 
 | |||
|  | package query_many; | |||
|  | 
 | |||
|  | sub new | |||
|  | { | |||
|  |   my ($type,$query,$safe_query,$check_result,$cleanup,$max_limit,$offset, | |||
|  |       $safe_cleanup)=@_; | |||
|  |   my $self={}; | |||
|  |   $self->{'query'}=$query; | |||
|  |   $self->{'safe_query'}=$safe_query; | |||
|  |   $self->{'check'}=$check_result; | |||
|  |   $self->{'cleanup'}=$cleanup; | |||
|  |   $self->{'max_limit'}=$max_limit; | |||
|  |   $self->{'offset'}=$offset; | |||
|  |   $self->{'safe_cleanup'}=$safe_cleanup; | |||
|  |   bless $self; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub query | |||
|  | { | |||
|  |   my ($self,$limit)=@_; | |||
|  |   my ($queries,$query,@res); | |||
|  |   $self->{'limit'}=$limit; | |||
|  |   $self->cleanup() if (defined($self->{'safe_cleanup'})); | |||
|  |   $queries=$self->{'query'}; | |||
|  |   foreach $query (@$queries) | |||
|  |   { | |||
|  |     push(@res,main::fix_query($query,$limit)); | |||
|  |   } | |||
|  |   return \@res; | |||
|  | } | |||
|  | 
 | |||
|  | sub check_query | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   return main::fix_query($self->{'safe_query'},$self->{'limit'}); | |||
|  | } | |||
|  | 
 | |||
|  | sub cleanup | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   my($tmp,$statement); | |||
|  |   return if (!defined($self->{'cleanup'})); | |||
|  |   $tmp=$self->{'cleanup'}; | |||
|  |   foreach $statement (@$tmp) | |||
|  |   { | |||
|  |     if (defined($statement) && length($statement)) | |||
|  |     { | |||
|  |       main::safe_query(main::fix_query($statement,$self->{'limit'})); | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub check | |||
|  | { | |||
|  |   my ($self,$sth)=@_; | |||
|  |   my ($check,$row); | |||
|  |   return 0 if (!($row=$sth->fetchrow_arrayref)); | |||
|  |   $check=$self->{'check'}; | |||
|  |   if (defined($check)) | |||
|  |   { | |||
|  |     return (defined($row->[0]) && | |||
|  | 	    $row->[0] eq main::fix_query($check,$self->{'limit'})) ? 1 : 0; | |||
|  |   } | |||
|  |   return 1; | |||
|  | } | |||
|  | 
 | |||
|  | sub max_limit | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   return $self->{'max_limit'}; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Used to find max supported row length | |||
|  | # | |||
|  | 
 | |||
|  | package query_row_length; | |||
|  | 
 | |||
|  | sub new | |||
|  | { | |||
|  |   my ($type,$create,$null,$drop,$max_limit)=@_; | |||
|  |   my $self={}; | |||
|  |   $self->{'table_name'}=$create; | |||
|  |   $self->{'null'}=$null; | |||
|  |   $self->{'cleanup'}=$drop; | |||
|  |   $self->{'max_limit'}=$max_limit; | |||
|  |   bless $self; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub query | |||
|  | { | |||
|  |   my ($self,$limit)=@_; | |||
|  |   my ($res,$values,$size,$length,$i); | |||
|  |   $self->{'limit'}=$limit; | |||
|  | 
 | |||
|  |   $res=""; | |||
|  |   $size=main::min($main::limits{'max_char_size'},255); | |||
|  |   $size = 255 if (!$size); # Safety | |||
|  |   for ($length=$i=0; $length + $size <= $limit ; $length+=$size, $i++) | |||
|  |   { | |||
|  |     $res.= "q$i char($size) $self->{'null'},"; | |||
|  |     $values.="'" . ('a' x $size) . "',"; | |||
|  |   } | |||
|  |   if ($length < $limit) | |||
|  |   { | |||
|  |     $size=$limit-$length; | |||
|  |     $res.= "q$i char($size) $self->{'null'},"; | |||
|  |     $values.="'" . ('a' x $size) . "',"; | |||
|  |   } | |||
|  |   chop($res); | |||
|  |   chop($values); | |||
|  |   return ["create table " . $self->{'table_name'} . " ($res)", | |||
|  | 	  "insert into " . $self->{'table_name'} . " values ($values)"]; | |||
|  | } | |||
|  | 
 | |||
|  | sub max_limit | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   return $self->{'max_limit'}; | |||
|  | } | |||
|  | 
 | |||
|  | sub cleanup | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   main::safe_query($self->{'cleanup'}); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub check | |||
|  | { | |||
|  |   return 1; | |||
|  | } | |||
|  | 
 | |||
|  | sub check_query | |||
|  | { | |||
|  |   return undef; | |||
|  | } | |||
|  | 
 | |||
|  | # | |||
|  | # Used to find max supported index length | |||
|  | # | |||
|  | 
 | |||
|  | package query_index_length; | |||
|  | 
 | |||
|  | sub new | |||
|  | { | |||
|  |   my ($type,$create,$drop,$max_limit)=@_; | |||
|  |   my $self={}; | |||
|  |   $self->{'create'}=$create; | |||
|  |   $self->{'cleanup'}=$drop; | |||
|  |   $self->{'max_limit'}=$max_limit; | |||
|  |   bless $self; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub query | |||
|  | { | |||
|  |   my ($self,$limit)=@_; | |||
|  |   my ($res,$size,$length,$i,$parts,$values); | |||
|  |   $self->{'limit'}=$limit; | |||
|  | 
 | |||
|  |   $res=$parts=$values=""; | |||
|  |   $size=main::min($main::limits{'max_index_part_length'}, | |||
|  |        $main::limits{'max_char_size'}); | |||
|  |   $size=1 if ($size == 0);	# Avoid infinite loop errors | |||
|  |   for ($length=$i=0; $length + $size <= $limit ; $length+=$size, $i++) | |||
|  |   { | |||
|  |     $res.= "q$i char($size) not null,"; | |||
|  |     $parts.= "q$i,"; | |||
|  |     $values.= "'" . ('a' x $size) . "',"; | |||
|  |   } | |||
|  |   if ($length < $limit) | |||
|  |   { | |||
|  |     $size=$limit-$length; | |||
|  |     $res.= "q$i char($size) not null,"; | |||
|  |     $parts.="q$i,"; | |||
|  |     $values.= "'" . ('a' x $size) . "',"; | |||
|  |   } | |||
|  |   chop($parts); | |||
|  |   chop($res); | |||
|  |   chop($values); | |||
|  |   if ($main::limits{'unique_in_create'} eq 'yes') | |||
|  |   { | |||
|  |     return [$self->{'create'} . "($res,unique ($parts))", | |||
|  | 	    "insert into crash_q values($values)"]; | |||
|  |   } | |||
|  |   return [$self->{'create'} . "($res)", | |||
|  | 	  "create index crash_q_index on crash_q ($parts)", | |||
|  | 	  "insert into crash_q values($values)"]; | |||
|  | } | |||
|  | 
 | |||
|  | sub max_limit | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   return $self->{'max_limit'}; | |||
|  | } | |||
|  | 
 | |||
|  | sub cleanup | |||
|  | { | |||
|  |   my ($self)=@_; | |||
|  |   main::safe_query($self->{'cleanup'}); | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | sub check | |||
|  | { | |||
|  |   return 1; | |||
|  | } | |||
|  | 
 | |||
|  | sub check_query | |||
|  | { | |||
|  |   return undef; | |||
|  | } | |||
|  | 
 | |||
|  | 
 | |||
|  | 
 | |||
|  | ### TODO: | |||
|  | # OID test instead of / in addition to _rowid |