diff options
author | unknown <baker@bk-internal.mysql.com> | 2006-11-30 01:45:16 +0100 |
---|---|---|
committer | unknown <baker@bk-internal.mysql.com> | 2006-11-30 01:45:16 +0100 |
commit | 46fd7deccbf9fcf75640ecdaa52667fc0d8e5832 (patch) | |
tree | 5160560731f3ca95f2e5c117e3f25aaf3534caf2 | |
parent | 2477bd5a03a3f578f17d35c96d26d34f418878b8 (diff) | |
parent | 6949b04246beb07f03f9e5b16e45a54b12d453a8 (diff) | |
download | mariadb-git-46fd7deccbf9fcf75640ecdaa52667fc0d8e5832.tar.gz |
Merge bk-internal.mysql.com:/data0/bk/mysql-5.1
into bk-internal.mysql.com:/data0/bk/mysql-5.1-arch
sql/mysqld.cc:
Auto merged
sql/set_var.cc:
Auto merged
96 files changed, 3436 insertions, 1636 deletions
diff --git a/BitKeeper/etc/collapsed b/BitKeeper/etc/collapsed index 10bc7a2182a..f934fa44580 100644 --- a/BitKeeper/etc/collapsed +++ b/BitKeeper/etc/collapsed @@ -15,7 +15,8 @@ 45214442pBGT9KuZEGixBH71jTzbOA 45214a07hVsIGwvwa-WrO-jpeaSwVw 452a92d0-31-8wSzSfZi165fcGcXPA -452c6c6dAjuNghfc1ObZ_UQ5SCl85g -4538a7b0EbDHHkWPbIwxO6ZIDdg6Dg 454a7ef8gdvE_ddMlJyghvOAkKPNOQ 454f8960jsVT_kMKJtZ9OCgXoba0xQ +4561b2ecZbhuAc0TTDdCdultxUYaMw +4561bde4qWhz1I8tkItXKex5uniipA +4562ba016dYH0JzszOqZ8p6psbKfnQ diff --git a/config/ac-macros/ssl.m4 b/config/ac-macros/ssl.m4 index 0f2f207c36f..4ee58318a62 100644 --- a/config/ac-macros/ssl.m4 +++ b/config/ac-macros/ssl.m4 @@ -28,10 +28,9 @@ AC_DEFUN([MYSQL_USE_BUNDLED_YASSL], [ yassl_dir="yassl" AC_SUBST([yassl_dir]) - yassl_libs="-L\$(top_srcdir)/extra/yassl/src -lyassl -L\$(top_srcdir)/extra/yassl/taocrypt/src -ltaocrypt" + yassl_libs="\$(top_builddir)/extra/yassl/src/libyassl.la \ + \$(top_builddir)/extra/yassl/taocrypt/src/libtaocrypt.la" AC_SUBST(yassl_libs) - yassl_includes="-I\$(top_srcdir)/extra/yassl/include" - AC_SUBST(yassl_includes) AC_DEFINE([HAVE_OPENSSL], [1], [Defined by configure. Using yaSSL for SSL.]) AC_DEFINE([HAVE_YASSL], [1], [Defined by configure. Using yaSSL for SSL.]) diff --git a/include/my_pthread.h b/include/my_pthread.h index 0cb38d29be8..8a0e1fc2b01 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -130,6 +130,9 @@ void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/ #define my_pthread_setprio(A,B) SetThreadPriority(GetCurrentThread(), (B)) #define pthread_kill(A,B) pthread_dummy(0) +#define pthread_join(A,B) \ + ((WaitForSingleObject((A), INFINITE) != WAIT_OBJECT_0) || !CloseHandle(A)) + /* Dummy defines for easier code */ #define pthread_attr_setdetachstate(A,B) pthread_dummy(0) #define my_pthread_attr_setprio(A,B) pthread_attr_setprio(A,B) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index ac4548f7301..898b583a58b 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -2471,6 +2471,8 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) NET *net= &mysql->net; char buff[4 /* size of stmt id */ + 5 /* execution flags */]; + my_bool res; + DBUG_ENTER("execute"); DBUG_DUMP("packet", packet, length); @@ -2478,16 +2480,18 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) int4store(buff, stmt->stmt_id); /* Send stmt id to server */ buff[4]= (char) stmt->flags; int4store(buff+5, 1); /* iteration count */ - if (cli_advanced_command(mysql, COM_STMT_EXECUTE, buff, sizeof(buff), - packet, length, 1, NULL) || - (*mysql->methods->read_query_result)(mysql)) + + res= test(cli_advanced_command(mysql, COM_STMT_EXECUTE, buff, sizeof(buff), + packet, length, 1, NULL) || + (*mysql->methods->read_query_result)(mysql)); + stmt->affected_rows= mysql->affected_rows; + stmt->server_status= mysql->server_status; + stmt->insert_id= mysql->insert_id; + if (res) { set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); DBUG_RETURN(1); } - stmt->affected_rows= mysql->affected_rows; - stmt->server_status= mysql->server_status; - stmt->insert_id= mysql->insert_id; DBUG_RETURN(0); } diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 7eb160776e0..cf4f90d99c9 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -94,8 +94,8 @@ INC_LIB= $(top_builddir)/regex/libregex.a \ $(yassl_inc_libs) if HAVE_YASSL -yassl_inc_libs= $(top_srcdir)/extra/yassl/src/.libs/libyassl.a \ - $(top_srcdir)/extra/yassl/taocrypt/src/.libs/libtaocrypt.a +yassl_inc_libs= $(top_builddir)/extra/yassl/src/.libs/libyassl.a \ + $(top_builddir)/extra/yassl/taocrypt/src/.libs/libtaocrypt.a endif # Storage engine specific compilation options diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index a3edc310677..9251ddd421a 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -279,24 +279,26 @@ static int emb_stmt_execute(MYSQL_STMT *stmt) DBUG_ENTER("emb_stmt_execute"); char header[5]; THD *thd; + my_bool res; int4store(header, stmt->stmt_id); header[4]= stmt->flags; thd= (THD*)stmt->mysql->thd; thd->client_param_count= stmt->param_count; thd->client_params= stmt->params; - if (emb_advanced_command(stmt->mysql, COM_STMT_EXECUTE,0,0, - header, sizeof(header), 1, stmt) || - emb_read_query_result(stmt->mysql)) + + res= test(emb_advanced_command(stmt->mysql, COM_STMT_EXECUTE, 0, 0, + header, sizeof(header), 1, stmt) || + emb_read_query_result(stmt->mysql)); + stmt->affected_rows= stmt->mysql->affected_rows; + stmt->insert_id= stmt->mysql->insert_id; + stmt->server_status= stmt->mysql->server_status; + if (res) { NET *net= &stmt->mysql->net; set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); DBUG_RETURN(1); } - stmt->affected_rows= stmt->mysql->affected_rows; - stmt->insert_id= stmt->mysql->insert_id; - stmt->server_status= stmt->mysql->server_status; - DBUG_RETURN(0); } diff --git a/mysql-test/lib/mtr_im.pl b/mysql-test/lib/mtr_im.pl index ca17516278e..967e92dfcdd 100644 --- a/mysql-test/lib/mtr_im.pl +++ b/mysql-test/lib/mtr_im.pl @@ -582,7 +582,7 @@ sub mtr_im_start($$) { mtr_add_arg($args, $opt); } - $im->{'pid'} = + $im->{'spawner_pid'} = mtr_spawn( $::exe_im, # path to the executable $args, # cmd-line args @@ -593,7 +593,7 @@ sub mtr_im_start($$) { { append_log_file => 1 } # append log files ); - unless ( $im->{'pid'} ) + unless ( $im->{'spawner_pid'} ) { mtr_error('Could not start Instance Manager.') } diff --git a/mysql-test/lib/mtr_io.pl b/mysql-test/lib/mtr_io.pl index 984d834486c..5be1d2ffddb 100644 --- a/mysql-test/lib/mtr_io.pl +++ b/mysql-test/lib/mtr_io.pl @@ -39,6 +39,7 @@ sub mtr_get_pid_from_file ($) { # Read pid number from file my $pid= <FILE>; + chomp $pid; close FILE; return $pid if $pid=~ /^(\d+)/; diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index 9d0c1f601ba..79285de88a7 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -937,6 +937,12 @@ sub check_expected_crash_and_restart($) } } } + + if ($::instance_manager->{'spawner_pid'} eq $ret_pid) + { + return; + } + mtr_warning("check_expected_crash_and_restart couldn't find an entry for pid: $ret_pid"); } diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index b88e5a66f96..6578af7cd8b 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -202,6 +202,14 @@ select count(*) from t1 where id not in (1,2); count(*) 1 drop table t1; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 SELECT 1 IN (2, NULL); +SELECT should return NULL. +SELECT * FROM t1; +1 IN (2, NULL) +NULL +DROP TABLE t1; +End of 4.1 tests CREATE TABLE t1 (a int PRIMARY KEY); INSERT INTO t1 VALUES (44), (45), (46); SELECT * FROM t1 WHERE a IN (45); @@ -343,6 +351,7 @@ some_id 1 2 drop table t1; +End of 5.0 tests create table t1(f1 char(1)); insert into t1 values ('a'),('b'),('1'); select f1 from t1 where f1 in ('a',1); @@ -411,3 +420,4 @@ explain select f2 from t2 where f2 in (1,'b'); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 index NULL t2f2 5 NULL 3 Using where; Using index drop table t1, t2; +End of 5.1 tests diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 733cb5ca5f0..4e63d4d8482 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -813,11 +813,231 @@ drop table t7; select substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2),substring_index("1abcd;2abcd;3abcd;4abcd", ';', -2); substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2) substring_index("1abcd;2abcd;3abcd;4abcd", ';', -2) 1abcd;2abcd 3abcd;4abcd -explain extended select md5('hello'), sha('abc'), sha1('abc'), soundex(''), 'mood' sounds like 'mud', aes_decrypt(aes_encrypt('abc','1'),'1'),concat('*',space(5),'*'), reverse('abc'), rpad('a',4,'1'), lpad('a',4,'1'), concat_ws(',','',NULL,'a'),make_set(255,_latin2'a',_latin2'b',_latin2'c'),elt(2,1),locate("a","b",2),format(130,10),char(0),conv(130,16,10),hex(130),binary 'HE', export_set(255,_latin2'y',_latin2'n',_latin2' '),FIELD('b' COLLATE latin1_bin,'A','B'),FIND_IN_SET(_latin1'B',_latin1'a,b,c,d'),collation(conv(130,16,10)), coercibility(conv(130,16,10)),length('\n\t\r\b\0\_\%\\'),bit_length('\n\t\r\b\0\_\%\\'),bit_length('\n\t\r\b\0\_\%\\'),concat('monty',' was here ','again'),length('hello'),char(ascii('h')),ord('h'),quote(1/0),crc32("123"),replace('aaaa','a','b'),insert('txs',2,1,'hi'),left(_latin2'a',1),right(_latin2'a',1),lcase(_latin2'a'),ucase(_latin2'a'),SUBSTR('abcdefg',3,2),substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2),trim(_latin2' a '),ltrim(_latin2' a '),rtrim(_latin2' a '), decode(encode(repeat("a",100000),"monty"),"monty"); +explain extended select md5('hello'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 select md5(_latin1'hello') AS `md5('hello')`,sha(_latin1'abc') AS `sha('abc')`,sha(_latin1'abc') AS `sha1('abc')`,soundex(_latin1'') AS `soundex('')`,(soundex(_latin1'mood') = soundex(_latin1'mud')) AS `'mood' sounds like 'mud'`,aes_decrypt(aes_encrypt(_latin1'abc',_latin1'1'),_latin1'1') AS `aes_decrypt(aes_encrypt('abc','1'),'1')`,concat(_latin1'*',repeat(_latin1' ',5),_latin1'*') AS `concat('*',space(5),'*')`,reverse(_latin1'abc') AS `reverse('abc')`,rpad(_latin1'a',4,_latin1'1') AS `rpad('a',4,'1')`,lpad(_latin1'a',4,_latin1'1') AS `lpad('a',4,'1')`,concat_ws(_latin1',',_latin1'',NULL,_latin1'a') AS `concat_ws(',','',NULL,'a')`,make_set(255,_latin2'a',_latin2'b',_latin2'c') AS `make_set(255,_latin2'a',_latin2'b',_latin2'c')`,elt(2,1) AS `elt(2,1)`,locate(_latin1'a',_latin1'b',2) AS `locate("a","b",2)`,format(130,10) AS `format(130,10)`,char(0) AS `char(0)`,conv(130,16,10) AS `conv(130,16,10)`,hex(130) AS `hex(130)`,cast(_latin1'HE' as char charset binary) AS `binary 'HE'`,export_set(255,_latin2'y',_latin2'n',_latin2' ') AS `export_set(255,_latin2'y',_latin2'n',_latin2' ')`,field((_latin1'b' collate latin1_bin),_latin1'A',_latin1'B') AS `FIELD('b' COLLATE latin1_bin,'A','B')`,find_in_set(_latin1'B',_latin1'a,b,c,d') AS `FIND_IN_SET(_latin1'B',_latin1'a,b,c,d')`,collation(conv(130,16,10)) AS `collation(conv(130,16,10))`,coercibility(conv(130,16,10)) AS `coercibility(conv(130,16,10))`,length(_latin1'\n \r\0\\_\\%\\') AS `length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,concat(_latin1'monty',_latin1' was here ',_latin1'again') AS `concat('monty',' was here ','again')`,length(_latin1'hello') AS `length('hello')`,char(ascii(_latin1'h')) AS `char(ascii('h'))`,ord(_latin1'h') AS `ord('h')`,quote((1 / 0)) AS `quote(1/0)`,crc32(_latin1'123') AS `crc32("123")`,replace(_latin1'aaaa',_latin1'a',_latin1'b') AS `replace('aaaa','a','b')`,insert(_latin1'txs',2,1,_latin1'hi') AS `insert('txs',2,1,'hi')`,left(_latin2'a',1) AS `left(_latin2'a',1)`,right(_latin2'a',1) AS `right(_latin2'a',1)`,lcase(_latin2'a') AS `lcase(_latin2'a')`,ucase(_latin2'a') AS `ucase(_latin2'a')`,substr(_latin1'abcdefg',3,2) AS `SUBSTR('abcdefg',3,2)`,substring_index(_latin1'1abcd;2abcd;3abcd;4abcd',_latin1';',2) AS `substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2)`,trim(_latin2' a ') AS `trim(_latin2' a ')`,ltrim(_latin2' a ') AS `ltrim(_latin2' a ')`,rtrim(_latin2' a ') AS `rtrim(_latin2' a ')`,decode(encode(repeat(_latin1'a',100000))) AS `decode(encode(repeat("a",100000),"monty"),"monty")` +Note 1003 select md5(_latin1'hello') AS `md5('hello')` +explain extended select sha('abc'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select sha(_latin1'abc') AS `sha('abc')` +explain extended select sha1('abc'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select sha(_latin1'abc') AS `sha1('abc')` +explain extended select soundex(''); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select soundex(_latin1'') AS `soundex('')` +explain extended select 'mood' sounds like 'mud'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select (soundex(_latin1'mood') = soundex(_latin1'mud')) AS `'mood' sounds like 'mud'` +explain extended select aes_decrypt(aes_encrypt('abc','1'),'1'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select aes_decrypt(aes_encrypt(_latin1'abc',_latin1'1'),_latin1'1') AS `aes_decrypt(aes_encrypt('abc','1'),'1')` +explain extended select concat('*',space(5),'*'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat(_latin1'*',repeat(_latin1' ',5),_latin1'*') AS `concat('*',space(5),'*')` +explain extended select reverse('abc'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select reverse(_latin1'abc') AS `reverse('abc')` +explain extended select rpad('a',4,'1'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select rpad(_latin1'a',4,_latin1'1') AS `rpad('a',4,'1')` +explain extended select lpad('a',4,'1'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select lpad(_latin1'a',4,_latin1'1') AS `lpad('a',4,'1')` +explain extended select concat_ws(',','',NULL,'a'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_ws(_latin1',',_latin1'',NULL,_latin1'a') AS `concat_ws(',','',NULL,'a')` +explain extended select make_set(255,_latin2'a', _latin2'b', _latin2'c'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select make_set(255,_latin2'a',_latin2'b',_latin2'c') AS `make_set(255,_latin2'a', _latin2'b', _latin2'c')` +explain extended select elt(2,1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select elt(2,1) AS `elt(2,1)` +explain extended select locate("a","b",2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select locate(_latin1'a',_latin1'b',2) AS `locate("a","b",2)` +explain extended select format(130,10); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select format(130,10) AS `format(130,10)` +explain extended select char(0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select char(0) AS `char(0)` +explain extended select conv(130,16,10); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select conv(130,16,10) AS `conv(130,16,10)` +explain extended select hex(130); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select hex(130) AS `hex(130)` +explain extended select binary 'HE'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select cast(_latin1'HE' as char charset binary) AS `binary 'HE'` +explain extended select export_set(255,_latin2'y', _latin2'n', _latin2' '); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select export_set(255,_latin2'y',_latin2'n',_latin2' ') AS `export_set(255,_latin2'y', _latin2'n', _latin2' ')` +explain extended select FIELD('b' COLLATE latin1_bin,'A','B'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select field((_latin1'b' collate latin1_bin),_latin1'A',_latin1'B') AS `FIELD('b' COLLATE latin1_bin,'A','B')` +explain extended select FIND_IN_SET(_latin1'B', _latin1'a,b,c,d'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select find_in_set(_latin1'B',_latin1'a,b,c,d') AS `FIND_IN_SET(_latin1'B', _latin1'a,b,c,d')` +explain extended select collation(conv(130,16,10)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select collation(conv(130,16,10)) AS `collation(conv(130,16,10))` +explain extended select coercibility(conv(130,16,10)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select coercibility(conv(130,16,10)) AS `coercibility(conv(130,16,10))` +explain extended select length('\n\t\r\b\0\_\%\\'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select length(_latin1'\n \r\0\\_\\%\\') AS `length('\n\t\r\b\0\_\%\\')` +explain extended select bit_length('\n\t\r\b\0\_\%\\'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')` +explain extended select bit_length('\n\t\r\b\0\_\%\\'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')` +explain extended select concat('monty',' was here ','again'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat(_latin1'monty',_latin1' was here ',_latin1'again') AS `concat('monty',' was here ','again')` +explain extended select length('hello'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select length(_latin1'hello') AS `length('hello')` +explain extended select char(ascii('h')); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select char(ascii(_latin1'h')) AS `char(ascii('h'))` +explain extended select ord('h'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select ord(_latin1'h') AS `ord('h')` +explain extended select quote(1/0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select quote((1 / 0)) AS `quote(1/0)` +explain extended select crc32("123"); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select crc32(_latin1'123') AS `crc32("123")` +explain extended select replace('aaaa','a','b'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select replace(_latin1'aaaa',_latin1'a',_latin1'b') AS `replace('aaaa','a','b')` +explain extended select insert('txs',2,1,'hi'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select insert(_latin1'txs',2,1,_latin1'hi') AS `insert('txs',2,1,'hi')` +explain extended select left(_latin2'a',1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select left(_latin2'a',1) AS `left(_latin2'a',1)` +explain extended select right(_latin2'a',1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select right(_latin2'a',1) AS `right(_latin2'a',1)` +explain extended select lcase(_latin2'a'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select lcase(_latin2'a') AS `lcase(_latin2'a')` +explain extended select ucase(_latin2'a'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select ucase(_latin2'a') AS `ucase(_latin2'a')` +explain extended select SUBSTR('abcdefg',3,2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select substr(_latin1'abcdefg',3,2) AS `SUBSTR('abcdefg',3,2)` +explain extended select substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select substring_index(_latin1'1abcd;2abcd;3abcd;4abcd',_latin1';',2) AS `substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2)` +explain extended select trim(_latin2' a '); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select trim(_latin2' a ') AS `trim(_latin2' a ')` +explain extended select ltrim(_latin2' a '); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select ltrim(_latin2' a ') AS `ltrim(_latin2' a ')` +explain extended select rtrim(_latin2' a '); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select rtrim(_latin2' a ') AS `rtrim(_latin2' a ')` +explain extended select decode(encode(repeat("a",100000),"monty"),"monty"); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select decode(encode(repeat(_latin1'a',100000),_latin1'monty'),_latin1'monty') AS `decode(encode(repeat("a",100000),"monty"),"monty")` SELECT lpad(12345, 5, "#"); lpad(12345, 5, "#") 12345 @@ -1148,6 +1368,119 @@ id select_type table type possible_keys key key_len ref rows filtered Extra Warnings: Note 1003 select `test`.`t1`.`code` AS `code`,`test`.`t2`.`id` AS `id` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`code` = _latin1'a12') and (length(`test`.`t1`.`code`) = 5)) DROP TABLE t1,t2; +select encode(NULL, NULL); +encode(NULL, NULL) +NULL +select encode("data", NULL); +encode("data", NULL) +NULL +select encode(NULL, "password"); +encode(NULL, "password") +NULL +select decode(NULL, NULL); +decode(NULL, NULL) +NULL +select decode("data", NULL); +decode("data", NULL) +NULL +select decode(NULL, "password"); +decode(NULL, "password") +NULL +select format(NULL, NULL); +format(NULL, NULL) +NULL +select format(pi(), NULL); +format(pi(), NULL) +NULL +select format(NULL, 2); +format(NULL, 2) +NULL +select benchmark(NULL, NULL); +benchmark(NULL, NULL) +NULL +select benchmark(0, NULL); +benchmark(0, NULL) +0 +select benchmark(100, NULL); +benchmark(100, NULL) +0 +select benchmark(NULL, 1+1); +benchmark(NULL, 1+1) +NULL +set @password="password"; +set @my_data="clear text to encode"; +select md5(encode(@my_data, "password")); +md5(encode(@my_data, "password")) +44320fd2b4a0ec92faa2da2122def917 +select md5(encode(@my_data, _utf8 "password")); +md5(encode(@my_data, _utf8 "password")) +44320fd2b4a0ec92faa2da2122def917 +select md5(encode(@my_data, binary "password")); +md5(encode(@my_data, binary "password")) +44320fd2b4a0ec92faa2da2122def917 +select md5(encode(@my_data, _latin1 "password")); +md5(encode(@my_data, _latin1 "password")) +44320fd2b4a0ec92faa2da2122def917 +select md5(encode(@my_data, _koi8r "password")); +md5(encode(@my_data, _koi8r "password")) +44320fd2b4a0ec92faa2da2122def917 +select md5(encode(@my_data, (select "password" from dual))); +md5(encode(@my_data, (select "password" from dual))) +44320fd2b4a0ec92faa2da2122def917 +select md5(encode(@my_data, concat("pass", "word"))); +md5(encode(@my_data, concat("pass", "word"))) +44320fd2b4a0ec92faa2da2122def917 +select md5(encode(@my_data, @password)); +md5(encode(@my_data, @password)) +44320fd2b4a0ec92faa2da2122def917 +set @my_data="binary encoded data"; +select md5(decode(@my_data, "password")); +md5(decode(@my_data, "password")) +5bea8c394368dbc03b76684483b7756b +select md5(decode(@my_data, _utf8 "password")); +md5(decode(@my_data, _utf8 "password")) +5bea8c394368dbc03b76684483b7756b +select md5(decode(@my_data, binary "password")); +md5(decode(@my_data, binary "password")) +5bea8c394368dbc03b76684483b7756b +select md5(decode(@my_data, _latin1 "password")); +md5(decode(@my_data, _latin1 "password")) +5bea8c394368dbc03b76684483b7756b +select md5(decode(@my_data, _koi8r "password")); +md5(decode(@my_data, _koi8r "password")) +5bea8c394368dbc03b76684483b7756b +select md5(decode(@my_data, (select "password" from dual))); +md5(decode(@my_data, (select "password" from dual))) +5bea8c394368dbc03b76684483b7756b +select md5(decode(@my_data, concat("pass", "word"))); +md5(decode(@my_data, concat("pass", "word"))) +5bea8c394368dbc03b76684483b7756b +select md5(decode(@my_data, @password)); +md5(decode(@my_data, @password)) +5bea8c394368dbc03b76684483b7756b +set @dec=5; +select format(pi(), (1+1)); +format(pi(), (1+1)) +3.14 +select format(pi(), (select 3 from dual)); +format(pi(), (select 3 from dual)) +3.142 +select format(pi(), @dec); +format(pi(), @dec) +3.14159 +set @bench_count=10; +select benchmark(10, pi()); +benchmark(10, pi()) +0 +select benchmark(5+5, pi()); +benchmark(5+5, pi()) +0 +select benchmark((select 10 from dual), pi()); +benchmark((select 10 from dual), pi()) +0 +select benchmark(@bench_count, pi()); +benchmark(@bench_count, pi()) +0 select locate('he','hello',-2); locate('he','hello',-2) 0 @@ -1903,4 +2236,17 @@ Warnings: Error 1292 Truncated incorrect DECIMAL value: '' Error 1292 Truncated incorrect DECIMAL value: '' Warning 1301 Result of lpad() was larger than max_allowed_packet (1048576) - truncated +SET @orig_sql_mode = @@SQL_MODE; +SET SQL_MODE=traditional; +SELECT CHAR(0xff,0x8f USING utf8); +CHAR(0xff,0x8f USING utf8) +NULL +Warnings: +Error 1300 Invalid utf8 character string: 'FF8F' +SELECT CHAR(0xff,0x8f USING utf8) IS NULL; +CHAR(0xff,0x8f USING utf8) IS NULL +1 +Warnings: +Error 1300 Invalid utf8 character string: 'FF8F' +SET SQL_MODE=@orig_sql_mode; End of 5.0 tests diff --git a/mysql-test/r/im_daemon_life_cycle.result b/mysql-test/r/im_daemon_life_cycle.result index 4fe15460ae5..a2baeac5f14 100644 --- a/mysql-test/r/im_daemon_life_cycle.result +++ b/mysql-test/r/im_daemon_life_cycle.result @@ -6,7 +6,7 @@ instance_name state mysqld1 XXXXX mysqld2 offline Killing the process... -Sleeping... +Waiting... Success: the process was restarted. Success: server is ready to accept connection on socket. @@ -16,7 +16,7 @@ Success: server is ready to accept connection on socket. START INSTANCE mysqld2; Success: the process has been started. Killing the process... -Sleeping... +Waiting... Success: the process was restarted. Success: server is ready to accept connection on socket. SHOW INSTANCE STATUS mysqld1; diff --git a/mysql-test/r/im_life_cycle.result b/mysql-test/r/im_life_cycle.result index fb25f4a0768..dcabc8cf4f3 100644 --- a/mysql-test/r/im_life_cycle.result +++ b/mysql-test/r/im_life_cycle.result @@ -39,7 +39,7 @@ ERROR HY000: Unknown instance name -- 1.1.6. -------------------------------------------------------------------- Killing the process... -Sleeping... +Waiting... Success: the process was restarted. SHOW INSTANCES; instance_name state @@ -52,7 +52,7 @@ mysqld2 offline START INSTANCE mysqld2; Success: the process has been started. Killing the process... -Sleeping... +Waiting... Success: the process was killed. -------------------------------------------------------------------- diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result index afd78561898..df446e01601 100644 --- a/mysql-test/r/parser.result +++ b/mysql-test/r/parser.result @@ -296,11 +296,6 @@ select atan(); ERROR 42000: Incorrect parameter count in the call to native function 'atan' select atan2(1, 2, 3); ERROR 42000: Incorrect parameter count in the call to native function 'atan2' -select benchmark(10, 1+1); -benchmark(10, 1+1) -0 -select benchmark(5+5, 2); -ERROR 42000: Incorrect parameters in the call to native function 'BENCHMARK' select concat(); ERROR 42000: Incorrect parameter count in the call to native function 'concat' select concat("foo"); @@ -310,11 +305,6 @@ select concat_ws(); ERROR 42000: Incorrect parameter count in the call to native function 'concat_ws' select concat_ws("foo"); ERROR 42000: Incorrect parameter count in the call to native function 'concat_ws' -set @pwd="my password"; -select encode("secret", @pwd); -ERROR 42000: Incorrect parameters in the call to native function 'ENCODE' -select decode("encoded-secret", @pwd); -ERROR 42000: Incorrect parameters in the call to native function 'DECODE' select encrypt(); ERROR 42000: Incorrect parameter count in the call to native function 'encrypt' select encrypt(1, 2, 3); @@ -339,9 +329,6 @@ select field(); ERROR 42000: Incorrect parameter count in the call to native function 'field' select field("p1"); ERROR 42000: Incorrect parameter count in the call to native function 'field' -set @dec=2; -select format(pi(), @dec); -ERROR 42000: Incorrect parameters in the call to native function 'FORMAT' select from_unixtime(); ERROR 42000: Incorrect parameter count in the call to native function 'from_unixtime' select from_unixtime(1, 2, 3); diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 694a934f8b7..e968d3de6e9 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -796,12 +796,12 @@ set @old_max_prepared_stmt_count= @@max_prepared_stmt_count; show variables like 'max_prepared_stmt_count'; Variable_name Value max_prepared_stmt_count 16382 -show variables like 'prepared_stmt_count'; +show status like 'prepared_stmt_count'; Variable_name Value -prepared_stmt_count 0 -select @@max_prepared_stmt_count, @@prepared_stmt_count; -@@max_prepared_stmt_count @@prepared_stmt_count -16382 0 +Prepared_stmt_count 0 +select @@max_prepared_stmt_count; +@@max_prepared_stmt_count +16382 set global max_prepared_stmt_count=-1; select @@max_prepared_stmt_count; @@max_prepared_stmt_count @@ -820,67 +820,70 @@ set max_prepared_stmt_count=1; ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL set local max_prepared_stmt_count=1; ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL -set local prepared_stmt_count=0; -ERROR HY000: Variable 'prepared_stmt_count' is a read only variable -set @@prepared_stmt_count=0; -ERROR HY000: Variable 'prepared_stmt_count' is a read only variable -set global prepared_stmt_count=1; -ERROR HY000: Variable 'prepared_stmt_count' is a read only variable set global max_prepared_stmt_count=1; select @@max_prepared_stmt_count; @@max_prepared_stmt_count 1 set global max_prepared_stmt_count=0; -select @@max_prepared_stmt_count, @@prepared_stmt_count; -@@max_prepared_stmt_count @@prepared_stmt_count -0 0 +select @@max_prepared_stmt_count; +@@max_prepared_stmt_count +0 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 0 prepare stmt from "select 1"; ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0) -select @@prepared_stmt_count; -@@prepared_stmt_count -0 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 0 set global max_prepared_stmt_count=1; prepare stmt from "select 1"; -select @@prepared_stmt_count; -@@prepared_stmt_count -1 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 1 prepare stmt1 from "select 1"; ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 1) -select @@prepared_stmt_count; -@@prepared_stmt_count -1 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 1 deallocate prepare stmt; -select @@prepared_stmt_count; -@@prepared_stmt_count -0 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 0 prepare stmt from "select 1"; -select @@prepared_stmt_count; -@@prepared_stmt_count -1 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 1 prepare stmt from "select 2"; -select @@prepared_stmt_count; -@@prepared_stmt_count +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 1 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 1 +select @@max_prepared_stmt_count; +@@max_prepared_stmt_count 1 -select @@prepared_stmt_count, @@max_prepared_stmt_count; -@@prepared_stmt_count @@max_prepared_stmt_count -1 1 set global max_prepared_stmt_count=0; prepare stmt from "select 1"; ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0) execute stmt; ERROR HY000: Unknown prepared statement handler (stmt) given to EXECUTE -select @@prepared_stmt_count; -@@prepared_stmt_count -0 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 0 prepare stmt from "select 1"; ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0) -select @@prepared_stmt_count; -@@prepared_stmt_count -0 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 0 set global max_prepared_stmt_count=3; -select @@max_prepared_stmt_count, @@prepared_stmt_count; -@@max_prepared_stmt_count @@prepared_stmt_count -3 0 +select @@max_prepared_stmt_count; +@@max_prepared_stmt_count +3 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 0 prepare stmt from "select 1"; prepare stmt from "select 2"; prepare stmt1 from "select 3"; @@ -888,13 +891,19 @@ prepare stmt2 from "select 4"; ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 3) prepare stmt2 from "select 4"; ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 3) -select @@max_prepared_stmt_count, @@prepared_stmt_count; -@@max_prepared_stmt_count @@prepared_stmt_count -3 3 +select @@max_prepared_stmt_count; +@@max_prepared_stmt_count +3 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 3 deallocate prepare stmt; -select @@max_prepared_stmt_count, @@prepared_stmt_count; -@@max_prepared_stmt_count @@prepared_stmt_count -3 0 +select @@max_prepared_stmt_count; +@@max_prepared_stmt_count +3 +show status like 'prepared_stmt_count'; +Variable_name Value +Prepared_stmt_count 0 set global max_prepared_stmt_count= @old_max_prepared_stmt_count; drop table if exists t1; create temporary table if not exists t1 (a1 int); @@ -2377,3 +2386,49 @@ Level Code Message Note 1051 Unknown table 't1' Note 1051 Unknown table 't2' deallocate prepare abc; +set @my_password="password"; +set @my_data="clear text to encode"; +prepare stmt1 from 'select decode(encode(?, ?), ?)'; +execute stmt1 using @my_data, @my_password, @my_password; +decode(encode(?, ?), ?) +clear text to encode +set @my_data="more text to encode"; +execute stmt1 using @my_data, @my_password, @my_password; +decode(encode(?, ?), ?) +more text to encode +set @my_password="new password"; +execute stmt1 using @my_data, @my_password, @my_password; +decode(encode(?, ?), ?) +more text to encode +deallocate prepare stmt1; +set @to_format="123456789.123456789"; +set @dec=0; +prepare stmt2 from 'select format(?, ?)'; +execute stmt2 using @to_format, @dec; +format(?, ?) +123,456,789 +set @dec=4; +execute stmt2 using @to_format, @dec; +format(?, ?) +123,456,789.1235 +set @dec=6; +execute stmt2 using @to_format, @dec; +format(?, ?) +123,456,789.123457 +set @dec=2; +execute stmt2 using @to_format, @dec; +format(?, ?) +123,456,789.12 +set @to_format="100"; +execute stmt2 using @to_format, @dec; +format(?, ?) +100.00 +set @to_format="1000000"; +execute stmt2 using @to_format, @dec; +format(?, ?) +1,000,000.00 +set @to_format="10000"; +execute stmt2 using @to_format, @dec; +format(?, ?) +10,000.00 +deallocate prepare stmt2; diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index 50bcd071d23..815a87eede5 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -944,3 +944,30 @@ c ---> Cleaning up... DROP TABLE t1; DROP TABLE t2; +drop table if exists t1; +create table t1(a int, b varchar(50)); +drop trigger not_a_trigger; +ERROR HY000: Trigger does not exist +drop trigger if exists not_a_trigger; +Warnings: +Note 1360 Trigger does not exist +create trigger t1_bi before insert on t1 +for each row set NEW.b := "In trigger t1_bi"; +insert into t1 values (1, "a"); +drop trigger if exists t1_bi; +insert into t1 values (2, "b"); +drop trigger if exists t1_bi; +Warnings: +Note 1360 Trigger does not exist +insert into t1 values (3, "c"); +select * from t1; +a b +1 In trigger t1_bi +2 b +3 c +select * from t1; +a b +1 In trigger t1_bi +2 b +3 c +drop table t1; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index aeccfd9c951..60363b06ea0 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -2178,6 +2178,7 @@ set @stamped_time=in_time; set x=2; end if; end| +set time_zone='+03:00'; call bug3426(1000, @i)| select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time| @i time @@ -5627,4 +5628,111 @@ Called B drop procedure proc_21462_a| drop procedure proc_21462_b| End of 5.0 tests +Begin of 5.1 tests +drop function if exists pi; +create function pi() returns varchar(50) +return "pie, my favorite desert."; +Warnings: +Note 1578 This function 'pi' has the same name as a native function. +SET @save_sql_mode=@@sql_mode; +SET SQL_MODE='IGNORE_SPACE'; +select pi(), pi (); +pi() pi () +3.141593 3.141593 +select test.pi(), test.pi (); +test.pi() test.pi () +pie, my favorite desert. pie, my favorite desert. +SET SQL_MODE=''; +select pi(), pi (); +pi() pi () +3.141593 3.141593 +select test.pi(), test.pi (); +test.pi() test.pi () +pie, my favorite desert. pie, my favorite desert. +SET @@sql_mode=@save_sql_mode; +drop function pi; +drop function if exists test.database; +drop function if exists test.current_user; +drop function if exists test.md5; +create database nowhere; +use nowhere; +drop database nowhere; +SET @save_sql_mode=@@sql_mode; +SET SQL_MODE='IGNORE_SPACE'; +select database(), database (); +database() database () +NULL NULL +select current_user(), current_user (); +current_user() current_user () +root@localhost root@localhost +select md5("aaa"), md5 ("aaa"); +md5("aaa") md5 ("aaa") +47bce5c74f589f4867dbd57e9ca9f808 47bce5c74f589f4867dbd57e9ca9f808 +SET SQL_MODE=''; +select database(), database (); +database() database () +NULL NULL +select current_user(), current_user (); +current_user() current_user () +root@localhost root@localhost +select md5("aaa"), md5 ("aaa"); +md5("aaa") md5 ("aaa") +47bce5c74f589f4867dbd57e9ca9f808 47bce5c74f589f4867dbd57e9ca9f808 +use test; +create function `database`() returns varchar(50) +return "Stored function database"; +Warnings: +Note 1578 This function 'database' has the same name as a native function. +create function `current_user`() returns varchar(50) +return "Stored function current_user"; +Warnings: +Note 1578 This function 'current_user' has the same name as a native function. +create function md5(x varchar(50)) returns varchar(50) +return "Stored function md5"; +Warnings: +Note 1578 This function 'md5' has the same name as a native function. +SET SQL_MODE='IGNORE_SPACE'; +select database(), database (); +database() database () +test test +select current_user(), current_user (); +current_user() current_user () +root@localhost root@localhost +select md5("aaa"), md5 ("aaa"); +md5("aaa") md5 ("aaa") +47bce5c74f589f4867dbd57e9ca9f808 47bce5c74f589f4867dbd57e9ca9f808 +select test.database(), test.database (); +test.database() test.database () +Stored function database Stored function database +select test.current_user(), test.current_user (); +test.current_user() test.current_user () +Stored function current_user Stored function current_user +select test.md5("aaa"), test.md5 ("aaa"); +test.md5("aaa") test.md5 ("aaa") +Stored function md5 Stored function md5 +SET SQL_MODE=''; +select database(), database (); +database() database () +test test +select current_user(), current_user (); +current_user() current_user () +root@localhost root@localhost +select md5("aaa"), md5 ("aaa"); +md5("aaa") md5 ("aaa") +47bce5c74f589f4867dbd57e9ca9f808 47bce5c74f589f4867dbd57e9ca9f808 +select test.database(), test.database (); +test.database() test.database () +Stored function database Stored function database +select test.current_user(), test.current_user (); +test.current_user() test.current_user () +Stored function current_user Stored function current_user +select test.md5("aaa"), test.md5 ("aaa"); +test.md5("aaa") test.md5 ("aaa") +Stored function md5 Stored function md5 +SET @@sql_mode=@save_sql_mode; +drop function test.database; +drop function test.current_user; +drop function md5; +use test; +End of 5.1 tests drop table t1,t2; diff --git a/mysql-test/r/sp_gis.result b/mysql-test/r/sp_gis.result new file mode 100644 index 00000000000..ea2cdd0d29d --- /dev/null +++ b/mysql-test/r/sp_gis.result @@ -0,0 +1,30 @@ +use test; +drop function if exists a; +drop function if exists x; +drop function if exists y; +create function a() returns int +return 1; +create function x() returns int +return 2; +Warnings: +Note 1578 This function 'x' has the same name as a native function. +create function y() returns int +return 3; +Warnings: +Note 1578 This function 'y' has the same name as a native function. +select a(); +a() +1 +select x(); +ERROR 42000: Incorrect parameter count in the call to native function 'x' +select y(); +ERROR 42000: Incorrect parameter count in the call to native function 'y' +select x(PointFromText("POINT(10 20)")), y(PointFromText("POINT(10 20)")); +x(PointFromText("POINT(10 20)")) y(PointFromText("POINT(10 20)")) +10 20 +select test.a(), test.x(), test.y(); +test.a() test.x() test.y() +1 2 3 +drop function a; +drop function x; +drop function y; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 52123885772..e302f5def1b 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1241,4 +1241,41 @@ i j 2 2 13 13 drop table t1; +drop table if exists t1; +drop function if exists f1; +create table t1 (i int); +create function f1() returns int return 10; +create trigger t1_bi before insert on t1 for each row set @a:= f1() + 10; +insert into t1 values (); +select @a; +@a +20 +insert into t1 values (); +select @a; +@a +20 +drop table t1; +drop function f1; +drop table if exists t1; +create table t1(a int, b varchar(50)); +drop trigger not_a_trigger; +ERROR HY000: Trigger does not exist +drop trigger if exists not_a_trigger; +Warnings: +Note 1360 Trigger does not exist +create trigger t1_bi before insert on t1 +for each row set NEW.b := "In trigger t1_bi"; +insert into t1 values (1, "a"); +drop trigger if exists t1_bi; +insert into t1 values (2, "b"); +drop trigger if exists t1_bi; +Warnings: +Note 1360 Trigger does not exist +insert into t1 values (3, "c"); +select * from t1; +a b +1 In trigger t1_bi +2 b +3 c +drop table t1; End of 5.0 tests diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 16a1c73af80..1308980ca96 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -185,6 +185,22 @@ DROP VIEW v1; DROP TABLE t1; DROP FUNCTION fn; End of 5.0 tests. +drop function if exists pi; +CREATE FUNCTION pi RETURNS STRING SONAME "should_not_parse.so"; +ERROR HY000: This function 'pi' has the same name as a native function. +DROP FUNCTION IF EXISTS metaphon; +CREATE FUNCTION metaphon(a int) RETURNS int +return 0; +CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; +ERROR HY000: Function 'metaphon' already exists +DROP FUNCTION metaphon; +CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; +CREATE FUNCTION metaphon(a int) RETURNS int +return 0; +ERROR HY000: Function 'metaphon' already exists +CREATE FUNCTION test.metaphon(a int) RETURNS int +return 0; +ERROR HY000: Function 'metaphon' already exists DROP FUNCTION metaphon; DROP FUNCTION myfunc_double; DROP FUNCTION myfunc_nonexist; diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index 6223395bfd9..81988818809 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -280,7 +280,7 @@ create event e22830_3 on schedule every 1 hour do alter event e22830_3 on schedu create event e22830_4 on schedule every 1 hour do alter event e22830_4 on schedule every (select f22830() from dual) hour; select event_name, event_definition, interval_value, interval_field from information_schema.events order by event_name; set global event_scheduler=on; ---sleep 0.7 +--sleep 2.0 set global event_scheduler=off; select event_name, event_definition, interval_value, interval_field from information_schema.events order by event_name; drop function f22830; diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index 906747c2f78..ee344d0958b 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -109,7 +109,28 @@ select count(*) from t1 where id not in (1); select count(*) from t1 where id not in (1,2); drop table t1; -# End of 4.1 tests + +# +# BUG#17047: CHAR() and IN() can return NULL without signaling NULL +# result +# +# The problem was in the IN() function that ignored maybe_null flags +# of all arguments except the first (the one _before_ the IN +# keyword, '1' in the test case below). +# +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 SELECT 1 IN (2, NULL); +--echo SELECT should return NULL. +SELECT * FROM t1; + +DROP TABLE t1; + + +--echo End of 4.1 tests + # # Bug #11885: WHERE condition with NOT IN (one element) @@ -233,6 +254,10 @@ select some_id from t1 where some_id not in(-4,-1,-4); select some_id from t1 where some_id not in(-4,-1,3423534,2342342); drop table t1; + +--echo End of 5.0 tests + + # # Bug#18360: Type aggregation for IN and CASE may lead to a wrong result # @@ -256,3 +281,6 @@ explain select f2 from t2 where f2 in ('a','b'); select f2 from t2 where f2 in (1,'b'); explain select f2 from t2 where f2 in (1,'b'); drop table t1, t2; + + +--echo End of 5.1 tests diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 94190ff519f..2ec1afc70ee 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -468,7 +468,51 @@ drop table t7; select substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2),substring_index("1abcd;2abcd;3abcd;4abcd", ';', -2); -explain extended select md5('hello'), sha('abc'), sha1('abc'), soundex(''), 'mood' sounds like 'mud', aes_decrypt(aes_encrypt('abc','1'),'1'),concat('*',space(5),'*'), reverse('abc'), rpad('a',4,'1'), lpad('a',4,'1'), concat_ws(',','',NULL,'a'),make_set(255,_latin2'a',_latin2'b',_latin2'c'),elt(2,1),locate("a","b",2),format(130,10),char(0),conv(130,16,10),hex(130),binary 'HE', export_set(255,_latin2'y',_latin2'n',_latin2' '),FIELD('b' COLLATE latin1_bin,'A','B'),FIND_IN_SET(_latin1'B',_latin1'a,b,c,d'),collation(conv(130,16,10)), coercibility(conv(130,16,10)),length('\n\t\r\b\0\_\%\\'),bit_length('\n\t\r\b\0\_\%\\'),bit_length('\n\t\r\b\0\_\%\\'),concat('monty',' was here ','again'),length('hello'),char(ascii('h')),ord('h'),quote(1/0),crc32("123"),replace('aaaa','a','b'),insert('txs',2,1,'hi'),left(_latin2'a',1),right(_latin2'a',1),lcase(_latin2'a'),ucase(_latin2'a'),SUBSTR('abcdefg',3,2),substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2),trim(_latin2' a '),ltrim(_latin2' a '),rtrim(_latin2' a '), decode(encode(repeat("a",100000),"monty"),"monty"); +explain extended select md5('hello'); +explain extended select sha('abc'); +explain extended select sha1('abc'); +explain extended select soundex(''); +explain extended select 'mood' sounds like 'mud'; +explain extended select aes_decrypt(aes_encrypt('abc','1'),'1'); +explain extended select concat('*',space(5),'*'); +explain extended select reverse('abc'); +explain extended select rpad('a',4,'1'); +explain extended select lpad('a',4,'1'); +explain extended select concat_ws(',','',NULL,'a'); +explain extended select make_set(255,_latin2'a', _latin2'b', _latin2'c'); +explain extended select elt(2,1); +explain extended select locate("a","b",2); +explain extended select format(130,10); +explain extended select char(0); +explain extended select conv(130,16,10); +explain extended select hex(130); +explain extended select binary 'HE'; +explain extended select export_set(255,_latin2'y', _latin2'n', _latin2' '); +explain extended select FIELD('b' COLLATE latin1_bin,'A','B'); +explain extended select FIND_IN_SET(_latin1'B', _latin1'a,b,c,d'); +explain extended select collation(conv(130,16,10)); +explain extended select coercibility(conv(130,16,10)); +explain extended select length('\n\t\r\b\0\_\%\\'); +explain extended select bit_length('\n\t\r\b\0\_\%\\'); +explain extended select bit_length('\n\t\r\b\0\_\%\\'); +explain extended select concat('monty',' was here ','again'); +explain extended select length('hello'); +explain extended select char(ascii('h')); +explain extended select ord('h'); +explain extended select quote(1/0); +explain extended select crc32("123"); +explain extended select replace('aaaa','a','b'); +explain extended select insert('txs',2,1,'hi'); +explain extended select left(_latin2'a',1); +explain extended select right(_latin2'a',1); +explain extended select lcase(_latin2'a'); +explain extended select ucase(_latin2'a'); +explain extended select SUBSTR('abcdefg',3,2); +explain extended select substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2); +explain extended select trim(_latin2' a '); +explain extended select ltrim(_latin2' a '); +explain extended select rtrim(_latin2' a '); +explain extended select decode(encode(repeat("a",100000),"monty"),"monty"); # # lpad returns incorrect result (Bug #2182) @@ -789,6 +833,70 @@ SELECT * FROM t1 INNER JOIN t2 ON code=id DROP TABLE t1,t2; # +# Bug#22684: The Functions ENCODE, DECODE and FORMAT are not real functions +# + +select encode(NULL, NULL); +select encode("data", NULL); +select encode(NULL, "password"); + +select decode(NULL, NULL); +select decode("data", NULL); +select decode(NULL, "password"); + +select format(NULL, NULL); +select format(pi(), NULL); +select format(NULL, 2); + +select benchmark(NULL, NULL); +select benchmark(0, NULL); +select benchmark(100, NULL); +select benchmark(NULL, 1+1); + +# +# Please note: +# 1) The collation of the password is irrelevant, the encryption uses +# the binary representation of the string without charset/collation. +# 2) These tests can not print the encoded text directly, because it's binary, +# and doing this would cause problems with source control. +# Instead, an md5() checksum is used, to verify the result indirectly. +# 3) Each md5() result must be identical. +# 4) The md5() result must never change, and must be stable across releases. +# +set @password="password"; +set @my_data="clear text to encode"; +select md5(encode(@my_data, "password")); +select md5(encode(@my_data, _utf8 "password")); +select md5(encode(@my_data, binary "password")); +select md5(encode(@my_data, _latin1 "password")); +select md5(encode(@my_data, _koi8r "password")); +select md5(encode(@my_data, (select "password" from dual))); +select md5(encode(@my_data, concat("pass", "word"))); +select md5(encode(@my_data, @password)); + +set @my_data="binary encoded data"; +select md5(decode(@my_data, "password")); +select md5(decode(@my_data, _utf8 "password")); +select md5(decode(@my_data, binary "password")); +select md5(decode(@my_data, _latin1 "password")); +select md5(decode(@my_data, _koi8r "password")); +select md5(decode(@my_data, (select "password" from dual))); +select md5(decode(@my_data, concat("pass", "word"))); +select md5(decode(@my_data, @password)); + +set @dec=5; +select format(pi(), (1+1)); +select format(pi(), (select 3 from dual)); +select format(pi(), @dec); + +set @bench_count=10; +select benchmark(10, pi()); +select benchmark(5+5, pi()); +select benchmark((select 10 from dual), pi()); +select benchmark(@bench_count, pi()); + + +# # Bug #10963 # 4294967296 18446744073709551616 @@ -977,4 +1085,18 @@ select lpad('hello', 18446744073709551616, '1'); select lpad('hello', -18446744073709551617, '1'); select lpad('hello', 18446744073709551617, '1'); + +# +# BUG#17047: CHAR() and IN() can return NULL without signaling NULL +# result +# +SET @orig_sql_mode = @@SQL_MODE; +SET SQL_MODE=traditional; + +SELECT CHAR(0xff,0x8f USING utf8); +SELECT CHAR(0xff,0x8f USING utf8) IS NULL; + +SET SQL_MODE=@orig_sql_mode; + + --echo End of 5.0 tests diff --git a/mysql-test/t/im_daemon_life_cycle.imtest b/mysql-test/t/im_daemon_life_cycle.imtest index fd19b6f8527..c5b8ee16d9a 100644 --- a/mysql-test/t/im_daemon_life_cycle.imtest +++ b/mysql-test/t/im_daemon_life_cycle.imtest @@ -6,59 +6,73 @@ # ########################################################################### +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle im_daemon_life_cycle.imtest started. + +########################################################################### + --source include/im_check_env.inc -# Turn on reconnect, not on by default anymore +# Turn on reconnect, not on by default anymore. --enable_reconnect ########################################################################### +# +# The main daemon-life-cycle test case -- check that IM-angel will restart +# IM-main if it got killed: +# - kill IM-main and check that IM-angel will restart it; +# - wait for IM-main to start accepting connections before continue test +# case; +# +########################################################################### -# Kill the IM main process and check that the IM Angel will restart the main -# process. - ---exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 30 +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Main-test: starting... -########################################################################### +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Killing IM-main... +--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 30 im_daemon_life_cycle -# Wait for IM to start accepting connections. +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Waiting for IM-main to start accepting connections... +--exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 30 im_daemon_life_cycle ---exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 30 +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Main-test: done. ########################################################################### - # # BUG#12751: Instance Manager: client hangs +# - start nonguarded instance (mysqld2); +# - kill IM-main and get it restarted by IM-angel; +# - check that guarded instance (mysqld1) is accepting connections. +# - check that non-guarded instance (mysqld2) were not stopped. # +########################################################################### --echo --echo -------------------------------------------------------------------- --echo -- Test for BUG#12751 --echo -------------------------------------------------------------------- -# Give some time to begin accepting connections after restart. -# FIXME: race condition here. - ---sleep 3 +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle BUG12751: starting... # 1. Start mysqld; +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: starting... START INSTANCE mysqld2; -# FIXME: START INSTANCE should be synchronous. ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started -# 2. Restart IM-main: kill it and IM-angel will restart it; wait for IM to -# start accepting connections again. +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: waiting to start... +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_daemon_life_cycle ---exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 30 +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: started. ---exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 30 +# 2. Restart IM-main; -# 3. Issue some statement -- connection should be re-established. +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Killing IM-main... +--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 30 im_daemon_life_cycle -# Give some time to begin accepting connections after restart. -# FIXME: race condition here. +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Waiting for IM-main to start accepting connections... +--exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 30 im_daemon_life_cycle + +# 3. Issue some statement -- connection should be re-established. ---sleep 3 +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Checking that IM-main processing commands... --replace_column 3 VERSION_NUMBER 4 VERSION SHOW INSTANCE STATUS mysqld1; @@ -67,6 +81,13 @@ SHOW INSTANCE STATUS mysqld1; # So, if it we do not stop it, it will be stopped by mysql-test-run.pl with # warning. +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: stopping... STOP INSTANCE mysqld2; -# FIXME: STOP INSTANCE should be synchronous. ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped + +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: waiting to stop... +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped im_daemon_life_cycle +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: stopped. + +########################################################################### + +--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle BUG12751: done. diff --git a/mysql-test/t/im_life_cycle.imtest b/mysql-test/t/im_life_cycle.imtest index ddfb62d312e..3721b92e2b7 100644 --- a/mysql-test/t/im_life_cycle.imtest +++ b/mysql-test/t/im_life_cycle.imtest @@ -25,7 +25,7 @@ START INSTANCE mysqld2; # FIXME: START INSTANCE should be synchronous. ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_life_cycle # FIXME: Result of SHOW INSTANCES here is not deterministic unless START # INSTANCE is synchronous. Even waiting for mysqld to start by looking at @@ -58,7 +58,7 @@ SHOW VARIABLES LIKE 'port'; STOP INSTANCE mysqld2; # FIXME: STOP INSTANCE should be synchronous. ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped im_life_cycle # FIXME: Result of SHOW INSTANCES here is not deterministic unless START # INSTANCE is synchronous. Even waiting for mysqld to start by looking at @@ -121,7 +121,7 @@ STOP INSTANCE mysqld3; --echo -- 1.1.6. --echo -------------------------------------------------------------------- ---exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD1_PATH_PID restarted 30 +--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD1_PATH_PID restarted 30 im_life_cycle # Give some time to IM to detect that mysqld was restarted. It should be # longer than monitoring interval. @@ -143,7 +143,7 @@ SHOW INSTANCES; START INSTANCE mysqld2; # FIXME: START INSTANCE should be synchronous. ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_life_cycle # FIXME: Result of SHOW INSTANCES here is not deterministic unless START # INSTANCE is synchronous. Even waiting for mysqld to start by looking at @@ -151,7 +151,7 @@ START INSTANCE mysqld2; # mysqld has started. # SHOW INSTANCES; ---exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD2_PATH_PID killed 10 +--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD2_PATH_PID killed 10 im_life_cycle # FIXME: Result of SHOW INSTANCES here is not deterministic unless START # INSTANCE is synchronous. Even waiting for mysqld to start by looking at diff --git a/mysql-test/t/im_options.imtest b/mysql-test/t/im_options.imtest index b3932c446da..8f9bed16473 100644 --- a/mysql-test/t/im_options.imtest +++ b/mysql-test/t/im_options.imtest @@ -32,33 +32,7 @@ # ########################################################################### ---source include/im_check_os.inc - -########################################################################### -# -# Check starting conditions. -# -########################################################################### - -# - check the configuration file; - ---echo -------------------------------------------------------------------- ---exec grep '^server_id[^a-zA-Z0-9_-]' $MYSQLTEST_VARDIR/im.cnf ; ---echo -------------------------------------------------------------------- - -# - check the running instances. - ---connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check the internal cache. - -SHOW INSTANCES; +--source include/im_check_env.inc ########################################################################### # diff --git a/mysql-test/t/im_utils.imtest b/mysql-test/t/im_utils.imtest index 52878f6c2b5..0866b87204a 100644 --- a/mysql-test/t/im_utils.imtest +++ b/mysql-test/t/im_utils.imtest @@ -31,10 +31,10 @@ SHOW INSTANCE OPTIONS mysqld2; # START INSTANCE mysqld2; ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_utils STOP INSTANCE mysqld2; ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped im_utils # # Check 'SHOW LOG FILES' command: diff --git a/mysql-test/t/kill_n_check.sh b/mysql-test/t/kill_n_check.sh index a54fb6ef8bb..96c402a638c 100755 --- a/mysql-test/t/kill_n_check.sh +++ b/mysql-test/t/kill_n_check.sh @@ -2,74 +2,101 @@ ########################################################################### -# NOTE: this script returns 0 (success) even in case of failure. This is -# because this script is executed under mysql-test-run[.pl] and it's better to -# examine particular problem in log file, than just having said that the test -# case has failed. +# NOTE: this script returns 0 (success) even in case of failure (except for +# usage-error). This is because this script is executed under +# mysql-test-run[.pl] and it's better to examine particular problem in log +# file, than just having said that the test case has failed. + +########################################################################### + +basename=`basename "$0"` +dirname=`dirname "$0"` + +########################################################################### + +. "$dirname/utils.sh" ########################################################################### check_restart() { if [ ! -r "$pid_path" ]; then + log_debug "No '$pid_path' found." user_msg='the process was killed' return 1 fi new_pid=`cat "$pid_path" 2>/dev/null` + err_code=$? - if [ $? -eq 0 -a "$original_pid" = "$new_pid" ]; then + log_debug "err_code: $err_code; original_pid: $original_pid; new_pid: $new_pid." + + if [ $err_code -eq 0 -a "$original_pid" = "$new_pid" ]; then + log_debug "The process was not restarted." user_msg='the process was not restarted' return 1 fi + log_debug "The process was restarted." user_msg='the process was restarted' return 0 } ########################################################################### -if [ $# -ne 3 ]; then - echo "Usage: kill_n_check.sh <pid file path> killed|restarted <timeout>" - exit 0 +if [ $# -ne 4 ]; then + echo "Usage: $basename <pid file path> killed|restarted <timeout> <test id>" + exit 1 fi pid_path="$1" expected_result="$2" total_timeout="$3" +test_id="$4" +log_file="$MYSQLTEST_VARDIR/log/$test_id.log" + +log_debug "-- $basename: starting --" +log_debug "pid_path: '$pid_path'" +log_debug "expected_result: '$expected_result'" +log_debug "total_timeout: '$total_timeout'" +log_debug "test_id: '$test_id'" +log_debug "log_file: '$log_file'" + +########################################################################### if [ "$expected_result" != 'killed' -a \ "$expected_result" != 'restarted' ]; then - echo "Error: invalid second argument ('killed' or 'restarted' expected)." - exit 0 + log_error "Invalid second argument ($expected_result): 'killed' or 'restarted' expected." + quit 0 fi if [ -z "$pid_path" ]; then - echo "Error: invalid PID path ($pid_path)." - exit 0 + log_error "Invalid PID path ($pid_path)." + quit 0 fi if [ ! -r "$pid_path" ]; then - echo "Error: PID file ($pid_path) does not exist." - exit 0 + log_error "PID file ($pid_path) does not exist." + quit 0 fi if [ -z "$total_timeout" ]; then - echo "Error: timeout is not specified." - exit 0 + log_error "Timeout is not specified." + quit 0 fi ########################################################################### original_pid=`cat "$pid_path"` +log_debug "original_pid: $original_pid." -echo "Killing the process..." +log_info "Killing the process..." kill -9 $original_pid ########################################################################### -echo "Sleeping..." +log_info "Waiting..." if [ "$expected_result" = "restarted" ]; then @@ -79,37 +106,42 @@ if [ "$expected_result" = "restarted" ]; then while true; do + log_debug "cur_attempt: $cur_attempt." + if check_restart; then - echo "Success: $user_msg." - exit 0 + log_info "Success: $user_msg." + quit 0 fi [ $cur_attempt -ge $total_timeout ] && break + log_debug "Sleeping for 1 second..." sleep 1 cur_attempt=`expr $cur_attempt + 1` done - echo "Error: $user_msg." - exit 0 + log_error "$user_msg." + quit 0 else # $expected_result == killed # Here we have to sleep for some long time to ensure that the process will # not be restarted. + log_debug "Sleeping for $total_timeout seconds..." sleep $total_timeout new_pid=`cat "$pid_path" 2>/dev/null` + log_debug "new_pid: $new_pid." if [ "$new_pid" -a "$new_pid" -ne "$original_pid" ]; then - echo "Error: the process was restarted." + log_error "The process was restarted." else - echo "Success: the process was killed." + log_info "Success: the process was killed." fi - exit 0 + quit 0 fi diff --git a/mysql-test/t/log.sh b/mysql-test/t/log.sh new file mode 100755 index 00000000000..20b265087cc --- /dev/null +++ b/mysql-test/t/log.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +########################################################################### + +basename=`basename "$0"` +dirname=`dirname "$0"` + +########################################################################### + +. "$dirname/utils.sh" + +########################################################################### + +if [ $# -lt 2 ]; then + echo "Usage: $basename <test id> log message ..." + exit 1 +fi + +test_id="$1" +log_file="$MYSQLTEST_VARDIR/log/$test_id.log" + +shift + +log_debug "$*" diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test index 11af7c691d8..39c8d8e2bf4 100644 --- a/mysql-test/t/parser.test +++ b/mysql-test/t/parser.test @@ -399,11 +399,6 @@ select atan(); -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT select atan2(1, 2, 3); -select benchmark(10, 1+1); - --- error ER_WRONG_PARAMETERS_TO_NATIVE_FCT -select benchmark(5+5, 2); - -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT select concat(); select concat("foo"); @@ -413,12 +408,6 @@ select concat_ws(); -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT select concat_ws("foo"); -set @pwd="my password"; --- error ER_WRONG_PARAMETERS_TO_NATIVE_FCT -select encode("secret", @pwd); --- error ER_WRONG_PARAMETERS_TO_NATIVE_FCT -select decode("encoded-secret", @pwd); - -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT select encrypt(); -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT @@ -448,10 +437,6 @@ select field(); -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT select field("p1"); -set @dec=2; --- error ER_WRONG_PARAMETERS_TO_NATIVE_FCT -select format(pi(), @dec); - -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT select from_unixtime(); -- error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 827f46664bf..39e4e65c6bf 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -849,6 +849,9 @@ drop table t1; # Bug#16365 Prepared Statements: DoS with too many open statements # Check that the limit @@max_prpeared_stmt_count works. # +# This is also the test for bug#23159 prepared_stmt_count should be +# status variable. +# # Save the old value set @old_max_prepared_stmt_count= @@max_prepared_stmt_count; # @@ -858,17 +861,17 @@ set @old_max_prepared_stmt_count= @@max_prepared_stmt_count; # --disable_ps_protocol # -# A. Check that the new variables are present in SHOW VARIABLES list. +# A. Check that the new variables are present in SHOW VARIABLES and +# SHOW STATUS lists. # show variables like 'max_prepared_stmt_count'; -show variables like 'prepared_stmt_count'; +show status like 'prepared_stmt_count'; # -# B. Check that the new variables are selectable. +# B. Check that the new system variable is selectable. # -select @@max_prepared_stmt_count, @@prepared_stmt_count; +select @@max_prepared_stmt_count; # -# C. Check that max_prepared_stmt_count is settable (global only), -# whereas prepared_stmt_count is readonly. +# C. Check that max_prepared_stmt_count is settable (global only). # set global max_prepared_stmt_count=-1; select @@max_prepared_stmt_count; @@ -882,12 +885,6 @@ set @@max_prepared_stmt_count=1; set max_prepared_stmt_count=1; --error ER_GLOBAL_VARIABLE set local max_prepared_stmt_count=1; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set local prepared_stmt_count=0; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set @@prepared_stmt_count=0; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set global prepared_stmt_count=1; # set to a reasonable limit works set global max_prepared_stmt_count=1; select @@max_prepared_stmt_count; @@ -895,47 +892,50 @@ select @@max_prepared_stmt_count; # D. Check that the variables actually work. # set global max_prepared_stmt_count=0; -select @@max_prepared_stmt_count, @@prepared_stmt_count; +select @@max_prepared_stmt_count; +show status like 'prepared_stmt_count'; --error ER_MAX_PREPARED_STMT_COUNT_REACHED prepare stmt from "select 1"; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; set global max_prepared_stmt_count=1; prepare stmt from "select 1"; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; --error ER_MAX_PREPARED_STMT_COUNT_REACHED prepare stmt1 from "select 1"; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; deallocate prepare stmt; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; # # E. Check that we can prepare a statement with the same name # successfully, without hitting the limit. # prepare stmt from "select 1"; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; prepare stmt from "select 2"; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; # # F. We can set the max below the current count. In this case no new # statements should be allowed to prepare. # -select @@prepared_stmt_count, @@max_prepared_stmt_count; +show status like 'prepared_stmt_count'; +select @@max_prepared_stmt_count; set global max_prepared_stmt_count=0; --error ER_MAX_PREPARED_STMT_COUNT_REACHED prepare stmt from "select 1"; # Result: the old statement is deallocated, the new is not created. ---error 1243 # ER_UNKNOWN_STMT_HANDLER +--error ER_UNKNOWN_STMT_HANDLER execute stmt; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; --error ER_MAX_PREPARED_STMT_COUNT_REACHED prepare stmt from "select 1"; -select @@prepared_stmt_count; +show status like 'prepared_stmt_count'; # # G. Show that the variables are up to date even after a connection with all # statements in it was terminated. # set global max_prepared_stmt_count=3; -select @@max_prepared_stmt_count, @@prepared_stmt_count; +select @@max_prepared_stmt_count; +show status like 'prepared_stmt_count'; prepare stmt from "select 1"; connect (con1,localhost,root,,); connection con1; @@ -944,26 +944,31 @@ prepare stmt1 from "select 3"; --error ER_MAX_PREPARED_STMT_COUNT_REACHED prepare stmt2 from "select 4"; connection default; ---error ER_MAX_PREPARED_STMT_COUNT_REACHED +--error ER_MAX_PREPARED_STMT_COUNT_REACHED prepare stmt2 from "select 4"; -select @@max_prepared_stmt_count, @@prepared_stmt_count; +select @@max_prepared_stmt_count; +show status like 'prepared_stmt_count'; disconnect con1; connection default; # Wait for the connection to die: deal with a possible race deallocate prepare stmt; -let $count= `select @@prepared_stmt_count`; +let $query= select variable_value from information_schema.global_status + where variable_name = 'prepared_stmt_count'; +let $count= `$query`; if ($count) { ---sleep 2 - let $count= `select @@prepared_stmt_count`; +--sleep 1 + let $count= `$query`; } -select @@max_prepared_stmt_count, @@prepared_stmt_count; +select @@max_prepared_stmt_count; +show status like 'prepared_stmt_count'; # # Restore the old value. # set global max_prepared_stmt_count= @old_max_prepared_stmt_count; --enable_ps_protocol + # # Bug#19399 "Stored Procedures 'Lost Connection' when dropping/creating # tables" @@ -2400,3 +2405,38 @@ execute abc; drop table if exists t1, t2; execute abc; deallocate prepare abc; + +# +# Bug#22684: The Functions ENCODE, DECODE and FORMAT are not real functions +# + +set @my_password="password"; +set @my_data="clear text to encode"; + +prepare stmt1 from 'select decode(encode(?, ?), ?)'; +execute stmt1 using @my_data, @my_password, @my_password; +set @my_data="more text to encode"; +execute stmt1 using @my_data, @my_password, @my_password; +set @my_password="new password"; +execute stmt1 using @my_data, @my_password, @my_password; +deallocate prepare stmt1; + +set @to_format="123456789.123456789"; +set @dec=0; + +prepare stmt2 from 'select format(?, ?)'; +execute stmt2 using @to_format, @dec; +set @dec=4; +execute stmt2 using @to_format, @dec; +set @dec=6; +execute stmt2 using @to_format, @dec; +set @dec=2; +execute stmt2 using @to_format, @dec; +set @to_format="100"; +execute stmt2 using @to_format, @dec; +set @to_format="1000000"; +execute stmt2 using @to_format, @dec; +set @to_format="10000"; +execute stmt2 using @to_format, @dec; +deallocate prepare stmt2; + diff --git a/mysql-test/t/rpl_trigger.test b/mysql-test/t/rpl_trigger.test index 6ec0021635a..9f5f6fc9b4c 100644 --- a/mysql-test/t/rpl_trigger.test +++ b/mysql-test/t/rpl_trigger.test @@ -431,6 +431,43 @@ DROP TABLE t2; --sync_with_master --connection master +# +# BUG#23703: DROP TRIGGER needs an IF EXISTS +# + +connection master; + +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1(a int, b varchar(50)); + +-- error ER_TRG_DOES_NOT_EXIST +drop trigger not_a_trigger; + +drop trigger if exists not_a_trigger; + +create trigger t1_bi before insert on t1 +for each row set NEW.b := "In trigger t1_bi"; + +insert into t1 values (1, "a"); +drop trigger if exists t1_bi; +insert into t1 values (2, "b"); +drop trigger if exists t1_bi; +insert into t1 values (3, "c"); + +select * from t1; + +save_master_pos; +connection slave; +sync_with_master; + +select * from t1; + +connection master; + +drop table t1; # # End of tests diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index bb18c0e5858..8f4763c636a 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -11,6 +11,7 @@ # Tests that uses 'goto' to into sp-goto.test (currently disabled) # Tests that destroys system tables (e.g. mysql.proc) for error testing # go to sp-destruct. +# Tests that require --with-geometry go into sp_gis.test use test; @@ -2584,6 +2585,9 @@ begin end if; end| +# so that from_unixtime() has a deterministic result +set time_zone='+03:00'; + call bug3426(1000, @i)| select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time| call bug3426(NULL, @i)| @@ -6585,6 +6589,122 @@ drop procedure proc_21462_b| --echo End of 5.0 tests +--echo Begin of 5.1 tests + +# +# BUG#18239: Possible to overload internal functions with stored functions +# + +delimiter ;| + +--disable_warnings +drop function if exists pi; +--enable_warnings + +create function pi() returns varchar(50) +return "pie, my favorite desert."; + +SET @save_sql_mode=@@sql_mode; + +SET SQL_MODE='IGNORE_SPACE'; + +select pi(), pi (); + +# Non deterministic warnings from db_load_routine +--disable_warnings +select test.pi(), test.pi (); +--enable_warnings + +SET SQL_MODE=''; + +select pi(), pi (); + +# Non deterministic warnings from db_load_routine +--disable_warnings +select test.pi(), test.pi (); +--enable_warnings + +SET @@sql_mode=@save_sql_mode; + +drop function pi; +# End of BUG#18239 + +# +# BUG#22619: Spaces considered harmful +# + +--disable_warnings +drop function if exists test.database; +drop function if exists test.current_user; +drop function if exists test.md5; +--enable_warnings + +create database nowhere; +use nowhere; +drop database nowhere; + +SET @save_sql_mode=@@sql_mode; + +SET SQL_MODE='IGNORE_SPACE'; + +select database(), database (); +select current_user(), current_user (); +select md5("aaa"), md5 ("aaa"); + +SET SQL_MODE=''; + +select database(), database (); +select current_user(), current_user (); +select md5("aaa"), md5 ("aaa"); + +use test; + +create function `database`() returns varchar(50) +return "Stored function database"; + +create function `current_user`() returns varchar(50) +return "Stored function current_user"; + +create function md5(x varchar(50)) returns varchar(50) +return "Stored function md5"; + +SET SQL_MODE='IGNORE_SPACE'; + +select database(), database (); +select current_user(), current_user (); +select md5("aaa"), md5 ("aaa"); + +# Non deterministic warnings from db_load_routine +--disable_warnings +select test.database(), test.database (); +select test.current_user(), test.current_user (); +select test.md5("aaa"), test.md5 ("aaa"); +--enable_warnings + +SET SQL_MODE=''; + +select database(), database (); +select current_user(), current_user (); +select md5("aaa"), md5 ("aaa"); + +# Non deterministic warnings from db_load_routine +--disable_warnings +select test.database(), test.database (); +select test.current_user(), test.current_user (); +select test.md5("aaa"), test.md5 ("aaa"); +--enable_warnings + +SET @@sql_mode=@save_sql_mode; + +drop function test.database; +drop function test.current_user; +drop function md5; + +use test; +delimiter |; +# End of BUG#22619 + +--echo End of 5.1 tests # # BUG#NNNN: New bug synopsis diff --git a/mysql-test/t/sp_gis.test b/mysql-test/t/sp_gis.test new file mode 100644 index 00000000000..51ed78b27d5 --- /dev/null +++ b/mysql-test/t/sp_gis.test @@ -0,0 +1,39 @@ +-- source include/have_geometry.inc + +use test; + +# +# BUG#21025: misleading error message when creating functions named 'x', or 'y' +# + +--disable_warnings +drop function if exists a; +drop function if exists x; +drop function if exists y; +--enable_warnings + +create function a() returns int +return 1; + +create function x() returns int +return 2; + +create function y() returns int +return 3; + +select a(); +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +select x(); +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +select y(); +select x(PointFromText("POINT(10 20)")), y(PointFromText("POINT(10 20)")); + +# Non deterministic warnings from db_load_routine +--disable_warnings +select test.a(), test.x(), test.y(); +--enable_warnings + +drop function a; +drop function x; +drop function y; + diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 8242c614d0a..12262894e5b 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -1499,4 +1499,52 @@ select * from t1; drop table t1; +# +# Bug #23651 "Server crashes when trigger which uses stored function +# invoked from different connections". +# +--disable_warnings +drop table if exists t1; +drop function if exists f1; +--enable_warnings +create table t1 (i int); +create function f1() returns int return 10; +create trigger t1_bi before insert on t1 for each row set @a:= f1() + 10; +insert into t1 values (); +select @a; +connection addconroot1; +insert into t1 values (); +select @a; +connection default; +drop table t1; +drop function f1; + +# +# Bug#23703: DROP TRIGGER needs an IF EXISTS +# + +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1(a int, b varchar(50)); + +-- error ER_TRG_DOES_NOT_EXIST +drop trigger not_a_trigger; + +drop trigger if exists not_a_trigger; + +create trigger t1_bi before insert on t1 +for each row set NEW.b := "In trigger t1_bi"; + +insert into t1 values (1, "a"); +drop trigger if exists t1_bi; +insert into t1 values (2, "b"); +drop trigger if exists t1_bi; +insert into t1 values (3, "c"); + +select * from t1; + +drop table t1; + --echo End of 5.0 tests diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 37358a292be..01aac24ded4 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -174,6 +174,42 @@ DROP FUNCTION fn; --echo End of 5.0 tests. # +# BUG#18239: Possible to overload internal functions with stored functions +# + +--disable_warnings +drop function if exists pi; +--enable_warnings + +--error ER_NATIVE_FCT_NAME_COLLISION +CREATE FUNCTION pi RETURNS STRING SONAME "should_not_parse.so"; + +# Verify that Stored Functions and UDF are mutually exclusive +DROP FUNCTION IF EXISTS metaphon; + +CREATE FUNCTION metaphon(a int) RETURNS int +return 0; + +--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB +--error ER_UDF_EXISTS +eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_LIB"; + +DROP FUNCTION metaphon; + +--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB +eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_LIB"; + +--error ER_UDF_EXISTS +CREATE FUNCTION metaphon(a int) RETURNS int +return 0; + +--error ER_UDF_EXISTS +CREATE FUNCTION test.metaphon(a int) RETURNS int +return 0; + +# End of Bug#18239 + +# # Drop the example functions from udf_example # diff --git a/mysql-test/t/utils.sh b/mysql-test/t/utils.sh new file mode 100644 index 00000000000..b3f4744947d --- /dev/null +++ b/mysql-test/t/utils.sh @@ -0,0 +1,55 @@ +########################################################################### +# +# This file provides utility functions and is included by other scripts. +# +# The following global variables must be set before calling functions from this +# file: +# - basename -- base name of the calling script (main application); +# - log_file -- where to store log records; +# +########################################################################### + +log() +{ + [ -z "$log_file" ] && return; + + log_level="$1" + log_msg="$2" + ts=`date` + + echo "[$ts] [$basename] [$log_level] $log_msg" >> "$log_file"; +} + +########################################################################### + +log_debug() +{ + log 'DEBUG' "$1" +} + +########################################################################### + +log_info() +{ + log 'INFO' "$1" + echo "$1" +} + +########################################################################### + +log_error() +{ + log 'ERROR' "$1" + echo "Error: $1" +} + +########################################################################### + +quit() +{ + exit_status="$1" + + log_debug "-- $basename: finished (exit_status: $exit_status) --" + + exit $exit_status +} diff --git a/mysql-test/t/wait_for_process.sh b/mysql-test/t/wait_for_process.sh index df0f4a17e3a..4c2d89cfea6 100755 --- a/mysql-test/t/wait_for_process.sh +++ b/mysql-test/t/wait_for_process.sh @@ -2,34 +2,40 @@ ########################################################################### -pid_path="$1" -total_attempts="$2" -event="$3" +# NOTE: this script returns 0 (success) even in case of failure (except for +# usage-error). This is because this script is executed under +# mysql-test-run[.pl] and it's better to examine particular problem in log +# file, than just having said that the test case has failed. -case "$3" in - started) - check_fn='check_started'; - ;; +########################################################################### - stopped) - check_fn='check_stopped'; - ;; +basename=`basename "$0"` +dirname=`dirname "$0"` - *) - echo "Error: invalid third argument ('started' or 'stopped' expected)." - exit 0 -esac +########################################################################### + +. "$dirname/utils.sh" ########################################################################### check_started() { - [ ! -r "$pid_path" ] && return 1 + if [ ! -r "$pid_path" ]; then + log_debug "No PID-file ($pid_path) found -- not started." + return 1 + fi new_pid=`cat "$pid_path" 2>/dev/null` + err_code=$? + + log_debug "err_code: $err_code; new_pid: $new_pid." - [ $? -eq 0 -a "$original_pid" = "$new_pid" ] && return 1 + if [ $? -ne 0 -o -z "$new_pid" ]; then + log_debug "The process was not started." + return 1 + fi + log_debug "The process was started." return 0 } @@ -37,30 +43,72 @@ check_started() check_stopped() { - [ -r "$pid_path" ] && return 1 + if [ -r "$pid_path" ]; then + log_debug "PID-file '$pid_path' exists -- not stopped." + return 1 + fi + log_debug "No PID-file ($pid_path) found -- stopped." return 0 } ########################################################################### +if [ $# -ne 4 ]; then + echo "Usage: $basename <pid file path> <total attempts> started|stopped <test id>" + exit 1 +fi + +pid_path="$1" +total_attempts="$2" +event="$3" +test_id="$4" +log_file="$MYSQLTEST_VARDIR/log/$test_id.log" + +log_debug "-- $basename: starting --" +log_debug "pid_path: '$pid_path'" +log_debug "total_attempts: '$total_attempts'" +log_debug "event: '$event'" +log_debug "test_id: '$test_id'" +log_debug "log_file: '$log_file'" + +########################################################################### + +case "$event" in + started) + check_fn='check_started'; + ;; + + stopped) + check_fn='check_stopped'; + ;; + + *) + log_error "Invalid third argument ('started' or 'stopped' expected)." + quit 0 +esac + +########################################################################### + cur_attempt=1 while true; do + log_debug "cur_attempt: $cur_attempt." + if ( eval $check_fn ); then - echo "Success: the process has been $event." - exit 0 + log_info "Success: the process has been $event." + quit 0 fi [ $cur_attempt -ge $total_attempts ] && break + log_debug "Sleeping for 1 second..." sleep 1 cur_attempt=`expr $cur_attempt + 1` done -echo "Error: the process has not been $event in $total_attempts secs." -exit 0 - +log_error "The process has not been $event in $total_attempts secs." +quit 0 diff --git a/mysql-test/t/wait_for_socket.sh b/mysql-test/t/wait_for_socket.sh index b6526b7d19c..1bce74dfd3a 100755 --- a/mysql-test/t/wait_for_socket.sh +++ b/mysql-test/t/wait_for_socket.sh @@ -2,9 +2,25 @@ ########################################################################### -if [ $# -ne 6 ]; then - echo "Usage: wait_for_socket.sh <executable path> <socket path> <username> <password> <db> <timeout>" - exit 0 +# NOTE: this script returns 0 (success) even in case of failure (except for +# usage-error). This is because this script is executed under +# mysql-test-run[.pl] and it's better to examine particular problem in log +# file, than just having said that the test case has failed. + +########################################################################### + +basename=`basename "$0"` +dirname=`dirname "$0"` + +########################################################################### + +. "$dirname/utils.sh" + +########################################################################### + +if [ $# -ne 7 ]; then + echo "Usage: wait_for_socket.sh <executable path> <socket path> <username> <password> <db> <timeout> <test id>" + exit 1 fi client_exe="$1" @@ -13,22 +29,34 @@ username="$3" password="$4" db="$5" total_timeout="$6" +test_id="$7" +log_file="$MYSQLTEST_VARDIR/log/$test_id.log" + +log_debug "-- $basename: starting --" +log_debug "client_exe: '$client_exe'" +log_debug "socket_path: '$socket_path'" +log_debug "username: '$username'" +log_debug "password: '$password'" +log_debug "db: '$db'" +log_debug "total_timeout: '$total_timeout'" +log_debug "test_id: '$test_id'" +log_debug "log_file: '$log_file'" ########################################################################### if [ -z "$client_exe" ]; then - echo "Error: invalid path to client executable ($client_exe)." - exit 0; + log_error "Invalid path to client executable ($client_exe)." + quit 0; fi if [ ! -x "$client_exe" ]; then - echo "Error: client by path '$client_exe' is not available." - exit 0; + log_error "Client by path '$client_exe' is not available." + quit 0; fi if [ -z "$socket_path" ]; then - echo "Error: invalid socket patch." - exit 0 + log_error "Invalid socket patch ($socket_path)." + quit 0 fi ########################################################################### @@ -39,15 +67,19 @@ client_args="--silent --socket=$socket_path --connect_timeout=1 " [ -n "$password" ] && client_args="$client_args --password=$password " [ -n "$db" ] && client_args="$client_args $db" +log_debug "client_args: '$client_args'" + ########################################################################### cur_attempt=1 while true; do + log_debug "cur_attempt: $cur_attempt." + if ( echo 'quit' | "$client_exe" $client_args >/dev/null 2>&1 ); then - echo "Success: server is ready to accept connection on socket." - exit 0 + log_info "Success: server is ready to accept connection on socket." + quit 0 fi [ $cur_attempt -ge $total_timeout ] && break @@ -58,5 +90,5 @@ while true; do done -echo "Error: server does not accept connections after $total_timeout seconds." -exit 0 +log_error "Server does not accept connections after $total_timeout seconds." +quit 0 diff --git a/server-tools/instance-manager/IMService.cpp b/server-tools/instance-manager/IMService.cpp index f9ea7ee471d..c8968fe9c16 100644 --- a/server-tools/instance-manager/IMService.cpp +++ b/server-tools/instance-manager/IMService.cpp @@ -21,22 +21,22 @@ void IMService::Stop() { ReportStatus(SERVICE_STOP_PENDING); - // stop the IM work + /* stop the IM work */ raise(SIGTERM); } void IMService::Run(DWORD argc, LPTSTR *argv) { - // report to the SCM that we're about to start + /* report to the SCM that we're about to start */ ReportStatus((DWORD)SERVICE_START_PENDING); Options::load(argc, argv); - // init goes here + /* init goes here */ ReportStatus((DWORD)SERVICE_RUNNING); - // wait for main loop to terminate - manager(); + /* wait for main loop to terminate */ + (void) Manager::main(); Options::cleanup(); } @@ -54,24 +54,24 @@ int HandleServiceOptions() if (Options::Service::install_as_service) { if (winService.IsInstalled()) - log_info("Service is already installed"); + log_info("Service is already installed."); else if (winService.Install()) - log_info("Service installed successfully"); + log_info("Service installed successfully."); else { - log_info("Service failed to install"); + log_error("Service failed to install."); ret_val= 1; } } else if (Options::Service::remove_service) { if (! winService.IsInstalled()) - log_info("Service is not installed"); + log_info("Service is not installed."); else if (winService.Remove()) - log_info("Service removed successfully"); + log_info("Service removed successfully."); else { - log_info("Service failed to remove"); + log_error("Service failed to remove."); ret_val= 1; } } @@ -81,7 +81,7 @@ int HandleServiceOptions() if (!winService.Init()) { - log_info("Service failed to initialize."); + log_error("Service failed to initialize."); fprintf(stderr, "The service should be started by Windows Service Manager.\n" "The MySQL Manager should be started with '--standalone'\n" diff --git a/server-tools/instance-manager/IMService.h b/server-tools/instance-manager/IMService.h index 94d59c2af31..aacc957e195 100644 --- a/server-tools/instance-manager/IMService.h +++ b/server-tools/instance-manager/IMService.h @@ -19,7 +19,7 @@ #pragma once #include "windowsservice.h" -class IMService : public WindowsService +class IMService: public WindowsService { public: IMService(void); diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 4719828081a..2d2365caa44 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -61,6 +61,8 @@ client_settings.h: libexec_PROGRAMS= mysqlmanager +mysqlmanager_CXXFLAGS= -Wall -W + mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ manager.h manager.cc log.h log.cc \ thread_registry.h thread_registry.cc \ diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 8039ab24481..e1a57a53087 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -27,7 +27,7 @@ const uint Buffer::MAX_BUFFER_SIZE= 16777216; /* Puts the given string to the buffer. - SYNOPSYS + SYNOPSIS append() position start position in the buffer string string to be put in the buffer @@ -59,7 +59,7 @@ int Buffer::append(uint position, const char *string, uint len_arg) Checks whether the current buffer size is ok to put a string of the length "len_arg" starting from "position" and reallocs it if no. - SYNOPSYS + SYNOPSIS reserve() position the number starting byte on the buffer to store a buffer len_arg the length of the string. diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc index f76366d5661..0ada11e8865 100644 --- a/server-tools/instance-manager/command.cc +++ b/server-tools/instance-manager/command.cc @@ -19,10 +19,12 @@ #endif #include "command.h" +#include "manager.h" -Command::Command(Instance_map *instance_map_arg) - :instance_map(instance_map_arg) +Command::Command() + :guardian(Manager::get_guardian()), + instance_map(Manager::get_instance_map()) {} Command::~Command() diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h index f31ea404867..e5f7d6e97e6 100644 --- a/server-tools/instance-manager/command.h +++ b/server-tools/instance-manager/command.h @@ -24,6 +24,7 @@ /* Class responsible for allocation of IM commands. */ +class Guardian; class Instance_map; struct st_net; @@ -36,13 +37,13 @@ struct st_net; class Command { public: - Command(Instance_map *instance_map_arg= 0); + Command(); virtual ~Command(); /* This operation incapsulates behaviour of the command. - SYNOPSYS + SYNOPSIS net The network connection to the client. connection_id Client connection ID @@ -53,6 +54,7 @@ public: virtual int execute(st_net *net, ulong connection_id) = 0; protected: + Guardian *guardian; Instance_map *instance_map; }; diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 66140b8b049..15738f8ebb3 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -29,7 +29,6 @@ #include "guardian.h" #include "instance_map.h" #include "log.h" -#include "manager.h" #include "messages.h" #include "mysqld_error.h" #include "mysql_manager_error.h" @@ -50,7 +49,7 @@ static const int modify_defaults_to_im_error[]= { 0, ER_OUT_OF_RESOURCES, /* Add a string to a buffer. - SYNOPSYS + SYNOPSIS put_to_buff() buff buffer to add the string str string to add @@ -130,7 +129,7 @@ Instance_name::Instance_name(const LEX_STRING *name) ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Show_instances::execute(st_net *net, ulong connection_id) +int Show_instances::execute(st_net *net, ulong /* connection_id */) { int err_code; @@ -242,10 +241,8 @@ int Flush_instances::execute(st_net *net, ulong connection_id) Implementation of Abstract_instance_cmd. **************************************************************************/ -Abstract_instance_cmd::Abstract_instance_cmd( - Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg) - :Command(instance_map_arg), - instance_name(instance_name_arg) +Abstract_instance_cmd::Abstract_instance_cmd(const LEX_STRING *instance_name_arg) + :instance_name(instance_name_arg) { /* MT-NOTE: we can not make a search for Instance object here, @@ -285,9 +282,8 @@ int Abstract_instance_cmd::execute(st_net *net, ulong connection_id) Implementation of Show_instance_status. **************************************************************************/ -Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg) - :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +Show_instance_status::Show_instance_status(const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_name_arg) { } @@ -312,7 +308,8 @@ int Show_instance_status::execute_impl(st_net *net, Instance *instance) } -int Show_instance_status::send_ok_response(st_net *net, ulong connection_id) +int Show_instance_status::send_ok_response(st_net *net, + ulong /* connection_id */) { if (send_eof(net) || net_flush(net)) return ER_OUT_OF_RESOURCES; @@ -406,8 +403,8 @@ int Show_instance_status::write_data(st_net *net, Instance *instance) **************************************************************************/ Show_instance_options::Show_instance_options( - Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg) - :Abstract_instance_cmd(instance_map_arg, instance_name_arg) + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_name_arg) { } @@ -432,7 +429,8 @@ int Show_instance_options::execute_impl(st_net *net, Instance *instance) } -int Show_instance_options::send_ok_response(st_net *net, ulong connection_id) +int Show_instance_options::send_ok_response(st_net *net, + ulong /* connection_id */) { if (send_eof(net) || net_flush(net)) return ER_OUT_OF_RESOURCES; @@ -501,9 +499,8 @@ int Show_instance_options::write_data(st_net *net, Instance *instance) Implementation of Start_instance. **************************************************************************/ -Start_instance::Start_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg) - :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +Start_instance::Start_instance(const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_name_arg) { } @@ -516,7 +513,7 @@ Start_instance::Start_instance(Instance_map *instance_map_arg, ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Start_instance::execute_impl(st_net *net, Instance *instance) +int Start_instance::execute_impl(st_net * /* net */, Instance *instance) { int err_code; @@ -543,9 +540,8 @@ int Start_instance::send_ok_response(st_net *net, ulong connection_id) Implementation of Stop_instance. **************************************************************************/ -Stop_instance::Stop_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg) - :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +Stop_instance::Stop_instance(const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_name_arg) { } @@ -558,7 +554,7 @@ Stop_instance::Stop_instance(Instance_map *instance_map_arg, ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Stop_instance::execute_impl(st_net *net, Instance *instance) +int Stop_instance::execute_impl(st_net * /* net */, Instance *instance) { int err_code; @@ -585,10 +581,8 @@ int Stop_instance::send_ok_response(st_net *net, ulong connection_id) Implementation for Create_instance. **************************************************************************/ -Create_instance::Create_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg) - :Command(instance_map_arg), - instance_name(instance_name_arg) +Create_instance::Create_instance(const LEX_STRING *instance_name_arg) + :instance_name(instance_name_arg) { } @@ -596,7 +590,7 @@ Create_instance::Create_instance(Instance_map *instance_map_arg, /* This operation initializes Create_instance object. - SYNOPSYS + SYNOPSIS text [IN/OUT] a pointer to the text containing instance options. RETURN @@ -613,7 +607,7 @@ bool Create_instance::init(const char **text) /* This operation parses CREATE INSTANCE options. - SYNOPSYS + SYNOPSIS text [IN/OUT] a pointer to the text containing instance options. RETURN @@ -795,9 +789,8 @@ int Create_instance::execute(st_net *net, ulong connection_id) Implementation for Drop_instance. **************************************************************************/ -Drop_instance::Drop_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg) - :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +Drop_instance::Drop_instance(const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_name_arg) { } @@ -811,7 +804,7 @@ Drop_instance::Drop_instance(Instance_map *instance_map_arg, ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Drop_instance::execute_impl(st_net *net, Instance *instance) +int Drop_instance::execute_impl(st_net * /* net */, Instance *instance) { int err_code; @@ -863,11 +856,10 @@ int Drop_instance::send_ok_response(st_net *net, ulong connection_id) Implementation for Show_instance_log. **************************************************************************/ -Show_instance_log::Show_instance_log(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg, +Show_instance_log::Show_instance_log(const LEX_STRING *instance_name_arg, Log_type log_type_arg, uint size_arg, uint offset_arg) - :Abstract_instance_cmd(instance_map_arg, instance_name_arg), + :Abstract_instance_cmd(instance_name_arg), log_type(log_type_arg), size(size_arg), offset(offset_arg) @@ -908,7 +900,8 @@ int Show_instance_log::execute_impl(st_net *net, Instance *instance) } -int Show_instance_log::send_ok_response(st_net *net, ulong connection_id) +int Show_instance_log::send_ok_response(st_net *net, + ulong /* connection_id */) { if (send_eof(net) || net_flush(net)) return ER_OUT_OF_RESOURCES; @@ -1013,9 +1006,8 @@ int Show_instance_log::write_data(st_net *net, Instance *instance) **************************************************************************/ Show_instance_log_files::Show_instance_log_files - (Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg) - :Abstract_instance_cmd(instance_map_arg, instance_name_arg) + (const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_name_arg) { } @@ -1040,7 +1032,8 @@ int Show_instance_log_files::execute_impl(st_net *net, Instance *instance) } -int Show_instance_log_files::send_ok_response(st_net *net, ulong connection_id) +int Show_instance_log_files::send_ok_response(st_net *net, + ulong /* connection_id */) { if (send_eof(net) || net_flush(net)) return ER_OUT_OF_RESOURCES; @@ -1214,9 +1207,8 @@ C_MODE_END /**************************************************************************/ -Abstract_option_cmd::Abstract_option_cmd(Instance_map *instance_map_arg) - :Command(instance_map_arg), - initialized(FALSE) +Abstract_option_cmd::Abstract_option_cmd() + :initialized(FALSE) { } @@ -1263,7 +1255,7 @@ bool Abstract_option_cmd::init(const char **text) Correct the option file. The "skip" option is used to remove the found option. - SYNOPSYS + SYNOPSIS Abstract_option_cmd::correct_file() skip Skip the option, being searched while writing the result file. That is, to delete it. @@ -1400,16 +1392,10 @@ int Abstract_option_cmd::execute_impl(st_net *net, ulong connection_id) Implementation of Set_option. **************************************************************************/ -Set_option::Set_option(Instance_map *instance_map_arg) - :Abstract_option_cmd(instance_map_arg) -{ -} - - /* This operation parses SET options. - SYNOPSYS + SYNOPSIS text [IN/OUT] a pointer to the text containing options. RETURN @@ -1553,7 +1539,7 @@ int Set_option::process_option(Instance *instance, Named_value *option) if (instance->is_mysqld_compatible() && Instance_options::is_option_im_specific(option->get_name())) { - log_error("Error: IM-option (%s) can not be used " + log_error("IM-option (%s) can not be used " "in the configuration of mysqld-compatible instance (%s).", (const char *) option->get_name(), (const char *) instance->get_name()->str); @@ -1580,16 +1566,10 @@ int Set_option::process_option(Instance *instance, Named_value *option) Implementation of Unset_option. **************************************************************************/ -Unset_option::Unset_option(Instance_map *instance_map_arg) - :Abstract_option_cmd(instance_map_arg) -{ -} - - /* This operation parses UNSET options. - SYNOPSYS + SYNOPSIS text [IN/OUT] a pointer to the text containing options. RETURN @@ -1712,7 +1692,7 @@ int Unset_option::process_option(Instance *instance, Named_value *option) Implementation of Syntax_error. **************************************************************************/ -int Syntax_error::execute(st_net *net, ulong connection_id) +int Syntax_error::execute(st_net * /* net */, ulong /* connection_id */) { return ER_SYNTAX_ERROR; } diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index 9a9911f2358..8768aaab121 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -32,15 +32,16 @@ /* Print all instances of this instance manager. - Grammar: SHOW ISTANCES + Grammar: SHOW INSTANCES */ -class Show_instances : public Command +class Show_instances: public Command { public: - Show_instances(Instance_map *instance_map_arg): Command(instance_map_arg) - {} + Show_instances() + { } +public: int execute(st_net *net, ulong connection_id); private: @@ -54,12 +55,13 @@ private: Grammar: FLUSH INSTANCES */ -class Flush_instances : public Command +class Flush_instances: public Command { public: - Flush_instances(Instance_map *instance_map_arg): Command(instance_map_arg) - {} + Flush_instances() + { } +public: int execute(st_net *net, ulong connection_id); }; @@ -68,11 +70,10 @@ public: Abstract class for Instance-specific commands. */ -class Abstract_instance_cmd : public Command +class Abstract_instance_cmd: public Command { public: - Abstract_instance_cmd(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Abstract_instance_cmd(const LEX_STRING *instance_name_arg); public: virtual int execute(st_net *net, ulong connection_id); @@ -102,14 +103,13 @@ private: /* Print status of an instance. - Grammar: SHOW ISTANCE STATUS <instance_name> + Grammar: SHOW INSTANCE STATUS <instance_name> */ -class Show_instance_status : public Abstract_instance_cmd +class Show_instance_status: public Abstract_instance_cmd { public: - Show_instance_status(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Show_instance_status(const LEX_STRING *instance_name_arg); protected: virtual int execute_impl(st_net *net, Instance *instance); @@ -126,11 +126,10 @@ private: Grammar: SHOW INSTANCE OPTIONS <instance_name> */ -class Show_instance_options : public Abstract_instance_cmd +class Show_instance_options: public Abstract_instance_cmd { public: - Show_instance_options(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Show_instance_options(const LEX_STRING *instance_name_arg); protected: virtual int execute_impl(st_net *net, Instance *instance); @@ -147,11 +146,10 @@ private: Grammar: START INSTANCE <instance_name> */ -class Start_instance : public Abstract_instance_cmd +class Start_instance: public Abstract_instance_cmd { public: - Start_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Start_instance(const LEX_STRING *instance_name_arg); protected: virtual int execute_impl(st_net *net, Instance *instance); @@ -164,11 +162,10 @@ protected: Grammar: STOP INSTANCE <instance_name> */ -class Stop_instance : public Abstract_instance_cmd +class Stop_instance: public Abstract_instance_cmd { public: - Stop_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Stop_instance(const LEX_STRING *instance_name_arg); protected: virtual int execute_impl(st_net *net, Instance *instance); @@ -181,11 +178,10 @@ protected: Grammar: CREATE INSTANCE <instance_name> [<options>] */ -class Create_instance : public Command +class Create_instance: public Command { public: - Create_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Create_instance(const LEX_STRING *instance_name_arg); public: bool init(const char **text); @@ -217,11 +213,10 @@ private: is removed from the instance map. */ -class Drop_instance : public Abstract_instance_cmd +class Drop_instance: public Abstract_instance_cmd { public: - Drop_instance(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Drop_instance(const LEX_STRING *instance_name_arg); protected: virtual int execute_impl(st_net *net, Instance *instance); @@ -235,11 +230,10 @@ protected: SHOW <instance_name> LOG {ERROR | SLOW | GENERAL} size[, offset_from_end] */ -class Show_instance_log : public Abstract_instance_cmd +class Show_instance_log: public Abstract_instance_cmd { public: - Show_instance_log(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg, + Show_instance_log(const LEX_STRING *instance_name_arg, Log_type log_type_arg, uint size_arg, uint offset_arg); protected: @@ -263,11 +257,10 @@ private: Grammar: SHOW <instance_name> LOG FILES */ -class Show_instance_log_files : public Abstract_instance_cmd +class Show_instance_log_files: public Abstract_instance_cmd { public: - Show_instance_log_files(Instance_map *instance_map_arg, - const LEX_STRING *instance_name_arg); + Show_instance_log_files(const LEX_STRING *instance_name_arg); protected: virtual int execute_impl(st_net *net, Instance *instance); @@ -285,7 +278,7 @@ private: class Instance_options_list; -class Abstract_option_cmd : public Command +class Abstract_option_cmd: public Command { public: ~Abstract_option_cmd(); @@ -299,7 +292,7 @@ public: virtual int execute(st_net *net, ulong connection_id); protected: - Abstract_option_cmd(Instance_map *instance_map_arg); + Abstract_option_cmd(); int correct_file(Instance *instance, Named_value *option, bool skip); @@ -324,10 +317,11 @@ private: Grammar: SET instance_name.option[=option_value][, ...] */ -class Set_option : public Abstract_option_cmd +class Set_option: public Abstract_option_cmd { public: - Set_option(Instance_map *instance_map_arg); + Set_option() + { } protected: virtual bool parse_args(const char **text); @@ -343,7 +337,8 @@ protected: class Unset_option: public Abstract_option_cmd { public: - Unset_option(Instance_map *instance_map_arg); + Unset_option() + { } protected: virtual bool parse_args(const char **text); @@ -360,12 +355,11 @@ protected: just returns NULL. */ -class Syntax_error : public Command +class Syntax_error: public Command { public: - /* This is just to avoid compiler warning. */ - Syntax_error() :Command(NULL) - {} + Syntax_error() + { } public: int execute(st_net *net, ulong connection_id); diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 03bfadd8571..e601ce0111c 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -20,7 +20,6 @@ #endif #include "guardian.h" - #include <string.h> #include <sys/types.h> #include <signal.h> @@ -30,17 +29,8 @@ #include "log.h" #include "mysql_manager_error.h" - -pthread_handler_t guardian(void *arg) -{ - Guardian_thread *guardian_thread= (Guardian_thread *) arg; - guardian_thread->run(); - return 0; -} - - const char * -Guardian_thread::get_instance_state_name(enum_instance_state state) +Guardian::get_instance_state_name(enum_instance_state state) { switch (state) { case NOT_STARTED: @@ -68,23 +58,24 @@ Guardian_thread::get_instance_state_name(enum_instance_state state) return NULL; /* just to ignore compiler warning. */ } +/* {{{ Constructor & destructor. */ -Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, - Instance_map *instance_map_arg, - uint monitoring_interval_arg) : - Guardian_thread_args(thread_registry_arg, instance_map_arg, - monitoring_interval_arg), - thread_info(pthread_self(), TRUE), guarded_instances(0) +Guardian::Guardian(Thread_registry *thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg) + :stopped(FALSE), + monitoring_interval(monitoring_interval_arg), + thread_registry(thread_registry_arg), + instance_map(instance_map_arg), + shutdown_requested(FALSE) { pthread_mutex_init(&LOCK_guardian, 0); pthread_cond_init(&COND_guardian, 0); - shutdown_requested= FALSE; - stopped= FALSE; init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); } -Guardian_thread::~Guardian_thread() +Guardian::~Guardian() { /* delay guardian destruction to the moment when no one needs it */ pthread_mutex_lock(&LOCK_guardian); @@ -94,21 +85,23 @@ Guardian_thread::~Guardian_thread() pthread_cond_destroy(&COND_guardian); } +/* }}} */ -void Guardian_thread::request_shutdown() + +void Guardian::request_shutdown() { pthread_mutex_lock(&LOCK_guardian); - /* stop instances or just clean up Guardian repository */ + /* STOP Instances or just clean up Guardian repository */ stop_instances(); shutdown_requested= TRUE; pthread_mutex_unlock(&LOCK_guardian); } -void Guardian_thread::process_instance(Instance *instance, - GUARD_NODE *current_node, - LIST **guarded_instances, - LIST *node) +void Guardian::process_instance(Instance *instance, + GUARD_NODE *current_node, + LIST **guarded_instances, + LIST *node) { uint waitchild= (uint) Instance::DEFAULT_SHUTDOWN_DELAY; /* The amount of times, Guardian attempts to restart an instance */ @@ -117,23 +110,14 @@ void Guardian_thread::process_instance(Instance *instance, if (current_node->state == STOPPING) { - /* this brach is executed during shutdown */ - if (instance->options.shutdown_delay) - { - /* - NOTE: it is important to check shutdown_delay here, but use - shutdown_delay_val. The idea is that if the option is unset, - shutdown_delay will be NULL, but shutdown_delay_val will not be reset. - */ - waitchild= instance->options.shutdown_delay_val; - } + waitchild= instance->options.get_shutdown_delay(); /* this returns TRUE if and only if an instance was stopped for sure */ if (instance->is_crashed()) *guarded_instances= list_delete(*guarded_instances, node); else if ( (uint) (current_time - current_node->last_checked) > waitchild) { - instance->kill_instance(SIGKILL); + instance->kill_mysqld(SIGKILL); /* Later we do node= node->next. This is ok, as we are only removing the node from the list. The pointer to the next one is still valid. @@ -144,20 +128,20 @@ void Guardian_thread::process_instance(Instance *instance, return; } - if (instance->is_running()) + if (instance->is_mysqld_running()) { /* The instance can be contacted on it's port */ /* If STARTING also check that pidfile has been created */ if (current_node->state == STARTING && - current_node->instance->options.get_pid() == 0) + current_node->instance->options.load_pid() == 0) { /* Pid file not created yet, don't go to STARTED state yet */ } else if (current_node->state != STARTED) { /* clear status fields */ - log_info("guardian: instance '%s' is running, set state to STARTED.", + log_info("Guardian: '%s' is running, set state to STARTED.", (const char *) instance->options.instance_name.str); current_node->restart_counter= 0; current_node->crash_moment= 0; @@ -168,7 +152,7 @@ void Guardian_thread::process_instance(Instance *instance, { switch (current_node->state) { case NOT_STARTED: - log_info("guardian: starting instance '%s'...", + log_info("Guardian: starting '%s'...", (const char *) instance->options.instance_name.str); /* NOTE, set state to STARTING _before_ start() is called */ @@ -193,7 +177,7 @@ void Guardian_thread::process_instance(Instance *instance, if (instance->is_crashed()) { instance->start(); - log_info("guardian: starting instance '%s'...", + log_info("Guardian: starting '%s'...", (const char *) instance->options.instance_name.str); } } @@ -211,14 +195,15 @@ void Guardian_thread::process_instance(Instance *instance, instance->start(); current_node->last_checked= current_time; current_node->restart_counter++; - log_info("guardian: restarting instance '%s'...", + log_info("Guardian: restarting '%s'...", (const char *) instance->options.instance_name.str); } } else { - log_info("guardian: cannot start instance %s. Abandoning attempts " - "to (re)start it", instance->options.instance_name.str); + log_info("Guardian: can not start '%s'. " + "Abandoning attempts to (re)start it", + (const char *) instance->options.instance_name.str); current_node->state= CRASHED_AND_ABANDONED; } } @@ -233,18 +218,17 @@ void Guardian_thread::process_instance(Instance *instance, /* - Run guardian thread + Main function of Guardian thread. - SYNOPSYS + SYNOPSIS run() DESCRIPTION - Check for all guarded instances and restart them if needed. If everything is fine go and sleep for some time. */ -void Guardian_thread::run() +void Guardian::run() { Instance *instance; LIST *node; @@ -252,9 +236,8 @@ void Guardian_thread::run() log_info("Guardian: started."); - thread_registry.register_thread(&thread_info); + thread_registry->register_thread(&thread_info); - my_thread_init(); pthread_mutex_lock(&LOCK_guardian); /* loop, until all instances were shut down at the end */ @@ -275,8 +258,8 @@ void Guardian_thread::run() /* check the loop predicate before sleeping */ if (!(shutdown_requested && (!(guarded_instances)))) - thread_registry.cond_timedwait(&thread_info, &COND_guardian, - &LOCK_guardian, &timeout); + thread_registry->cond_timedwait(&thread_info, &COND_guardian, + &LOCK_guardian, &timeout); } log_info("Guardian: stopped."); @@ -284,15 +267,14 @@ void Guardian_thread::run() stopped= TRUE; pthread_mutex_unlock(&LOCK_guardian); /* now, when the Guardian is stopped we can stop the IM */ - thread_registry.unregister_thread(&thread_info); - thread_registry.request_shutdown(); - my_thread_end(); + thread_registry->unregister_thread(&thread_info); + thread_registry->request_shutdown(); log_info("Guardian: finished."); } -int Guardian_thread::is_stopped() +int Guardian::is_stopped() { int var; pthread_mutex_lock(&LOCK_guardian); @@ -306,19 +288,19 @@ int Guardian_thread::is_stopped() Initialize the list of guarded instances: loop through the Instance_map and add all of the instances, which don't have 'nonguarded' option specified. - SYNOPSYS - Guardian_thread::init() + SYNOPSIS + Guardian::init() NOTE: The operation should be invoked with the following locks acquired: - - Guardian_thread; + - Guardian; - Instance_map; RETURN 0 - ok - 1 - error occured + 1 - error occurred */ -int Guardian_thread::init() +int Guardian::init() { Instance *instance; Instance_map::Iterator iterator(instance_map); @@ -344,7 +326,7 @@ int Guardian_thread::init() /* Add instance to the Guardian list - SYNOPSYS + SYNOPSIS guard() instance the instance to be guarded nolock whether we prefer do not lock Guardian here, @@ -357,10 +339,10 @@ int Guardian_thread::init() RETURN 0 - ok - 1 - error occured + 1 - error occurred */ -int Guardian_thread::guard(Instance *instance, bool nolock) +int Guardian::guard(Instance *instance, bool nolock) { LIST *node; GUARD_NODE *content; @@ -397,7 +379,7 @@ int Guardian_thread::guard(Instance *instance, bool nolock) a piece of the MEM_ROOT). */ -int Guardian_thread::stop_guard(Instance *instance) +int Guardian::stop_guard(Instance *instance) { LIST *node; @@ -418,7 +400,7 @@ int Guardian_thread::stop_guard(Instance *instance) An internal method which is called at shutdown to unregister instances and attempt to stop them if requested. - SYNOPSYS + SYNOPSIS stop_instances() DESCRIPTION @@ -431,10 +413,10 @@ int Guardian_thread::stop_guard(Instance *instance) RETURN 0 - ok - 1 - error occured + 1 - error occurred */ -int Guardian_thread::stop_instances() +int Guardian::stop_instances() { LIST *node; node= guarded_instances; @@ -445,7 +427,7 @@ int Guardian_thread::stop_instances() If instance is running or was running (and now probably hanging), request stop. */ - if (current_node->instance->is_running() || + if (current_node->instance->is_mysqld_running() || (current_node->state == STARTED)) { current_node->state= STOPPING; @@ -455,26 +437,26 @@ int Guardian_thread::stop_instances() /* otherwise remove it from the list */ guarded_instances= list_delete(guarded_instances, node); /* But try to kill it anyway. Just in case */ - current_node->instance->kill_instance(SIGTERM); + current_node->instance->kill_mysqld(SIGTERM); node= node->next; } return 0; } -void Guardian_thread::lock() +void Guardian::lock() { pthread_mutex_lock(&LOCK_guardian); } -void Guardian_thread::unlock() +void Guardian::unlock() { pthread_mutex_unlock(&LOCK_guardian); } -LIST *Guardian_thread::find_instance_node(Instance *instance) +LIST *Guardian::find_instance_node(Instance *instance) { LIST *node= guarded_instances; @@ -494,7 +476,7 @@ LIST *Guardian_thread::find_instance_node(Instance *instance) } -bool Guardian_thread::is_active(Instance *instance) +bool Guardian::is_active(Instance *instance) { bool guarded; @@ -508,5 +490,5 @@ bool Guardian_thread::is_active(Instance *instance) if (guarded) return true; - return instance->is_running(); + return instance->is_mysqld_running(); } diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 27ca155fd67..0eee1dc631d 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -16,11 +16,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <my_global.h> -#include <my_sys.h> -#include <my_list.h> #include "thread_registry.h" +#include <my_sys.h> +#include <my_list.h> #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface @@ -31,30 +30,12 @@ class Instance_map; class Thread_registry; struct GUARD_NODE; -pthread_handler_t guardian(void *arg); - -struct Guardian_thread_args -{ - Thread_registry &thread_registry; - Instance_map *instance_map; - int monitoring_interval; - - Guardian_thread_args(Thread_registry &thread_registry_arg, - Instance_map *instance_map_arg, - uint monitoring_interval_arg) : - thread_registry(thread_registry_arg), - instance_map(instance_map_arg), - monitoring_interval(monitoring_interval_arg) - {} -}; - - -/* +/** The guardian thread is responsible for monitoring and restarting of guarded instances. */ -class Guardian_thread: public Guardian_thread_args +class Guardian: public Thread { public: /* states of an instance */ @@ -82,12 +63,10 @@ public: /* Return client state name. */ static const char *get_instance_state_name(enum_instance_state state); - Guardian_thread(Thread_registry &thread_registry_arg, - Instance_map *instance_map_arg, - uint monitoring_interval_arg); - ~Guardian_thread(); - /* Main funtion of the thread */ - void run(); + Guardian(Thread_registry *thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg); + virtual ~Guardian(); /* Initialize or refresh the list of guarded instances */ int init(); /* Request guardian shutdown. Stop instances if needed */ @@ -117,6 +96,9 @@ public: a valid list node. */ inline enum_instance_state get_instance_state(LIST *instance_node); +protected: + /* Main funtion of the thread */ + virtual void run(); public: pthread_cond_t COND_guardian; @@ -133,6 +115,9 @@ private: private: pthread_mutex_t LOCK_guardian; Thread_info thread_info; + int monitoring_interval; + Thread_registry *thread_registry; + Instance_map *instance_map; LIST *guarded_instances; MEM_ROOT alloc; /* this variable is set to TRUE when we want to stop Guardian thread */ @@ -140,8 +125,8 @@ private: }; -inline Guardian_thread::enum_instance_state -Guardian_thread::get_instance_state(LIST *instance_node) +inline Guardian::enum_instance_state +Guardian::get_instance_state(LIST *instance_node) { return ((GUARD_NODE *) instance_node->data)->state; } diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 3927363a3e5..6b4289c5b29 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -20,7 +20,6 @@ #include "instance.h" -#include <my_global.h> #include <mysql.h> #include <signal.h> @@ -29,24 +28,15 @@ #endif #include "guardian.h" -#include "instance_map.h" +#include "manager.h" #include "log.h" #include "mysql_manager_error.h" #include "portability.h" #include "priv.h" #include "thread_registry.h" +#include "instance_map.h" - -const LEX_STRING -Instance::DFLT_INSTANCE_NAME= { C_STRING_WITH_LEN("mysqld") }; - -static const char * const INSTANCE_NAME_PREFIX= Instance::DFLT_INSTANCE_NAME.str; -static const int INSTANCE_NAME_PREFIX_LEN= Instance::DFLT_INSTANCE_NAME.length; - - -static void start_and_monitor_instance(Instance_options *old_instance_options, - Instance_map *instance_map, - Thread_registry *thread_registry); +/* {{{ Platform-specific functions. */ #ifndef __WIN__ typedef pid_t My_process_info; @@ -61,19 +51,31 @@ typedef PROCESS_INFORMATION My_process_info; to do it in a portable way. */ -pthread_handler_t proxy(void *arg) +class Instance_monitor: public Thread +{ +public: + Instance_monitor(Instance *instance_arg) :instance(instance_arg) {} +protected: + virtual void run(); + void start_and_monitor_instance(Instance_options *old_instance_options, + Instance_map *instance_map, + Thread_registry *thread_registry); +private: + Instance *instance; +}; + +void Instance_monitor::run() { - Instance *instance= (Instance *) arg; start_and_monitor_instance(&instance->options, - instance->get_map(), - &instance->thread_registry); - return 0; + Manager::get_instance_map(), + Manager::get_thread_registry()); + delete this; } /* Wait for an instance - SYNOPSYS + SYNOPSIS wait_process() pi Pointer to the process information structure (platform-dependent). @@ -103,7 +105,7 @@ static int wait_process(My_process_info *pi) couldn't use wait(), because it could return in any wait() in the program. */ - if (linuxthreads) + if (Manager::is_linux_threads()) wait(NULL); /* LinuxThreads were detected */ else waitpid(*pi, NULL, 0); @@ -131,11 +133,10 @@ static int wait_process(My_process_info *pi) } #endif - /* Launch an instance - SYNOPSYS + SYNOPSIS start_process() instance_options Pointer to the options of the instance to be launched. @@ -143,13 +144,13 @@ static int wait_process(My_process_info *pi) (platform-dependent). RETURN - 0 - Success - 1 - Cannot create an instance + FALSE - Success + TRUE - Cannot create an instance */ #ifndef __WIN__ -static int start_process(Instance_options *instance_options, - My_process_info *pi) +static bool start_process(Instance_options *instance_options, + My_process_info *pi) { #ifndef __QNX__ *pi= fork(); @@ -169,15 +170,16 @@ static int start_process(Instance_options *instance_options, /* exec never returns */ exit(1); case -1: - log_info("cannot create a new process to start instance '%s'.", - (const char *) instance_options->instance_name.str); - return 1; + log_error("Instance '%s': can not start mysqld: fork() failed.", + (const char *) instance_options->instance_name.str); + return TRUE; } - return 0; + + return FALSE; } #else -static int start_process(Instance_options *instance_options, - My_process_info *pi) +static bool start_process(Instance_options *instance_options, + My_process_info *pi) { STARTUPINFO si; @@ -192,7 +194,7 @@ static int start_process(Instance_options *instance_options, char *cmdline= new char[cmdlen]; if (cmdline == NULL) - return 1; + return TRUE; cmdline[0]= 0; for (int i= 0; instance_options->argv[i] != 0; i++) @@ -216,14 +218,87 @@ static int start_process(Instance_options *instance_options, pi); /* Pointer to PROCESS_INFORMATION structure */ delete cmdline; - return (!result); + return !result; } #endif +#ifdef __WIN__ + +BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode) +{ + DWORD dwTID, dwCode, dwErr= 0; + HANDLE hProcessDup= INVALID_HANDLE_VALUE; + HANDLE hRT= NULL; + HINSTANCE hKernel= GetModuleHandle("Kernel32"); + BOOL bSuccess= FALSE; + + BOOL bDup= DuplicateHandle(GetCurrentProcess(), + hProcess, GetCurrentProcess(), &hProcessDup, + PROCESS_ALL_ACCESS, FALSE, 0); + + // Detect the special case where the process is + // already dead... + if (GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) && + (dwCode == STILL_ACTIVE)) + { + FARPROC pfnExitProc; + + pfnExitProc= GetProcAddress(hKernel, "ExitProcess"); + + hRT= CreateRemoteThread((bDup) ? hProcessDup : hProcess, NULL, 0, + (LPTHREAD_START_ROUTINE)pfnExitProc, + (PVOID)uExitCode, 0, &dwTID); + + if (hRT == NULL) + dwErr= GetLastError(); + } + else + dwErr= ERROR_PROCESS_ABORTED; + + if (hRT) + { + // Must wait process to terminate to + // guarantee that it has exited... + WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE); + + CloseHandle(hRT); + bSuccess= TRUE; + } + + if (bDup) + CloseHandle(hProcessDup); + + if (!bSuccess) + SetLastError(dwErr); + + return bSuccess; +} + +int kill(pid_t pid, int signum) +{ + HANDLE processhandle= ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (signum == SIGTERM) + ::SafeTerminateProcess(processhandle, 0); + else + ::TerminateProcess(processhandle, -1); + return 0; +} +#endif + +/* }}} */ + +/* {{{ Static constants. */ + +const LEX_STRING +Instance::DFLT_INSTANCE_NAME= { C_STRING_WITH_LEN("mysqld") }; + +/* }}} */ + + /* Fork child, exec an instance and monitor it. - SYNOPSYS + SYNOPSIS start_and_monitor_instance() old_instance_options Pointer to the options of the instance to be launched. This info is likely to become obsolete @@ -238,32 +313,39 @@ static int start_process(Instance_options *instance_options, set appropriate flags and wake all threads waiting for instance to stop. + NOTE + A separate thread for starting/monitoring instance is a simple way + to avoid all pitfalls of the threads implementation in the OS (e.g. + LinuxThreads). For one, with such a thread we don't have to process + SIGCHLD, which is a tricky business if we want to do it in a + portable way. + RETURN Function returns no value */ -static void start_and_monitor_instance(Instance_options *old_instance_options, - Instance_map *instance_map, - Thread_registry *thread_registry) +void +Instance_monitor:: +start_and_monitor_instance(Instance_options *old_instance_options, + Instance_map *instance_map, + Thread_registry *thread_registry) { Instance_name instance_name(&old_instance_options->instance_name); Instance *current_instance; My_process_info process_info; - Thread_info thread_info(pthread_self(), FALSE); + Thread_info thread_info; - log_info("Monitoring thread (instance: '%s'): started.", - (const char *) instance_name.get_c_str()); + log_info("Instance '%s': Monitor: started.", + (const char *) instance->get_name()->str); if (!old_instance_options->nonguarded) { /* Register thread in Thread_registry to wait for it to stop on shutdown - only if instance is nuarded. If instance is guarded, the thread will not + only if instance is guarded. If instance is guarded, the thread will not finish, because nonguarded instances are not stopped on shutdown. */ - - thread_registry->register_thread(&thread_info); - my_thread_init(); + thread_registry->register_thread(&thread_info, FALSE); } /* @@ -277,8 +359,8 @@ static void start_and_monitor_instance(Instance_options *old_instance_options, are using is destroyed. (E.g. by "FLUSH INSTANCES") */ - log_info("starting instance %s...", - (const char *) instance_name.get_c_str()); + log_info("Instance '%s': Monitor: starting mysqld...", + (const char *) instance->get_name()->str); if (start_process(old_instance_options, &process_info)) { @@ -289,8 +371,10 @@ static void start_and_monitor_instance(Instance_options *old_instance_options, /* allow users to delete instances */ instance_map->unlock(); - /* don't check for return value */ - wait_process(&process_info); + log_info("Instance '%s': Monitor: waiting for mysqld to stop...", + (const char *) instance->get_name()->str); + + wait_process(&process_info); /* Don't check for return value. */ instance_map->lock(); @@ -302,21 +386,19 @@ static void start_and_monitor_instance(Instance_options *old_instance_options, instance_map->unlock(); if (!old_instance_options->nonguarded) - { thread_registry->unregister_thread(&thread_info); - my_thread_end(); - } - log_info("Monitoring thread (instance: '%s'): finished.", - (const char *) instance_name.get_c_str()); + log_info("Instance '%s': Monitor: finished.", + (const char *) instance->get_name()->str); } bool Instance::is_name_valid(const LEX_STRING *name) { - const char *name_suffix= name->str + INSTANCE_NAME_PREFIX_LEN; + const char *name_suffix= name->str + DFLT_INSTANCE_NAME.length; - if (strncmp(name->str, INSTANCE_NAME_PREFIX, INSTANCE_NAME_PREFIX_LEN) != 0) + if (strncmp(name->str, Instance::DFLT_INSTANCE_NAME.str, + Instance::DFLT_INSTANCE_NAME.length) != 0) return FALSE; return *name_suffix == 0 || my_isdigit(default_charset_info, *name_suffix); @@ -325,31 +407,139 @@ bool Instance::is_name_valid(const LEX_STRING *name) bool Instance::is_mysqld_compatible_name(const LEX_STRING *name) { - return strcmp(name->str, INSTANCE_NAME_PREFIX) == 0; + return strcmp(name->str, DFLT_INSTANCE_NAME.str) == 0; } -Instance_map *Instance::get_map() + +/* {{{ Constructor & destructor */ + +Instance::Instance() + :crashed(FALSE), + configured(FALSE) { - return instance_map; + pthread_mutex_init(&LOCK_instance, 0); + pthread_cond_init(&COND_instance_stopped, 0); } -void Instance::remove_pid() +Instance::~Instance() +{ + log_info("Instance '%s': destroying...", (const char *) get_name()->str); + + pthread_cond_destroy(&COND_instance_stopped); + pthread_mutex_destroy(&LOCK_instance); +} + +/* }}} */ + +/* + Initialize instance options. + + SYNOPSIS + init() + name_arg name of the instance + + RETURN: + FALSE - ok + TRUE - error +*/ + +bool Instance::init(const LEX_STRING *name_arg) { - int pid; - if ((pid= options.get_pid()) != 0) /* check the pidfile */ - if (options.unlink_pidfile()) /* remove stalled pidfile */ - log_error("cannot remove pidfile for instance '%s', this might be " - "since IM lacks permmissions or hasn't found the pidifle", - (const char *) options.instance_name.str); + mysqld_compatible= is_mysqld_compatible_name(name_arg); + + return options.init(name_arg); } /* + Complete instance options initialization. + + SYNOPSIS + complete_initialization() + + RETURN + FALSE - ok + TRUE - error +*/ + +bool Instance::complete_initialization() +{ + configured= ! options.complete_initialization(); + return FALSE; + /* + TODO: return actual status (from + Instance_options::complete_initialization()) here. + */ +} + +/* + Determine if mysqld is accepting connections. + + SYNOPSIS + is_mysqld_running() + + DESCRIPTION + Try to connect to mysqld with fake login/password to check whether it is + accepting connections or not. + + MT-NOTE: this operation must be called under acquired LOCK_instance. + + RETURN + TRUE - mysqld is alive and accept connections + FALSE - otherwise. +*/ + +bool Instance::is_mysqld_running() +{ + MYSQL mysql; + uint port= options.get_mysqld_port(); /* 0 if not specified. */ + const char *socket= NULL; + static const char *password= "check_connection"; + static const char *username= "MySQL_Instance_Manager"; + static const char *access_denied_message= "Access denied for user"; + bool return_val; + + if (options.mysqld_socket) + socket= options.mysqld_socket; + + /* no port was specified => instance falled back to default value */ + if (!port && !options.mysqld_socket) + port= SERVER_DEFAULT_PORT; + + pthread_mutex_lock(&LOCK_instance); + + mysql_init(&mysql); + /* try to connect to a server with a fake username/password pair */ + if (mysql_real_connect(&mysql, LOCAL_HOST, username, + password, + NullS, port, + socket, 0)) + { + /* + We have successfully connected to the server using fake + username/password. Write a warning to the logfile. + */ + log_error("Instance '%s': was able to log into mysqld.", + (const char *) get_name()->str); + pthread_mutex_unlock(&LOCK_instance); + return_val= TRUE; /* server is alive */ + } + else + return_val= test(!strncmp(access_denied_message, mysql_error(&mysql), + sizeof(access_denied_message) - 1)); + + mysql_close(&mysql); + pthread_mutex_unlock(&LOCK_instance); + + return return_val; +} + +/* The method starts an instance. - SYNOPSYS + SYNOPSIS start() RETURN @@ -367,24 +557,21 @@ int Instance::start() pthread_mutex_unlock(&LOCK_instance); - if (configured && !is_running()) + if (configured && !is_mysqld_running()) { + Instance_monitor *instance_monitor; remove_pid(); - pthread_t proxy_thd_id; - pthread_attr_t proxy_thd_attr; - int rc; + instance_monitor= new Instance_monitor(this); - pthread_attr_init(&proxy_thd_attr); - pthread_attr_setdetachstate(&proxy_thd_attr, PTHREAD_CREATE_DETACHED); - rc= pthread_create(&proxy_thd_id, &proxy_thd_attr, proxy, - this); - pthread_attr_destroy(&proxy_thd_attr); - if (rc) + if (instance_monitor == NULL || instance_monitor->start(Thread::DETACHED)) { - log_error("Instance::start(): pthread_create(proxy) failed"); + delete instance_monitor; + log_error("Instance::start(): failed to create the monitoring thread" + " to start an instance"); return ER_CANNOT_START_INSTANCE; } + /* The monitoring thread will delete itself when it's finished. */ return 0; } @@ -397,7 +584,7 @@ int Instance::start() The method sets the crash flag and wakes all waiters on COND_instance_stopped and COND_guardian - SYNOPSYS + SYNOPSIS set_crash_flag_n_wake_all() DESCRIPTION @@ -423,97 +610,14 @@ void Instance::set_crash_flag_n_wake_all() */ pthread_cond_signal(&COND_instance_stopped); /* wake guardian */ - pthread_cond_signal(&instance_map->guardian->COND_guardian); -} - - - -Instance::Instance(Thread_registry &thread_registry_arg): - crashed(FALSE), configured(FALSE), thread_registry(thread_registry_arg) -{ - pthread_mutex_init(&LOCK_instance, 0); - pthread_cond_init(&COND_instance_stopped, 0); -} - - -Instance::~Instance() -{ - pthread_cond_destroy(&COND_instance_stopped); - pthread_mutex_destroy(&LOCK_instance); -} - - -bool Instance::is_crashed() -{ - bool val; - pthread_mutex_lock(&LOCK_instance); - val= crashed; - pthread_mutex_unlock(&LOCK_instance); - return val; -} - - -bool Instance::is_running() -{ - MYSQL mysql; - uint port= 0; - const char *socket= NULL; - static const char *password= "check_connection"; - static const char *username= "MySQL_Instance_Manager"; - static const char *access_denied_message= "Access denied for user"; - bool return_val; - - if (options.mysqld_port) - { - /* - NOTE: it is important to check mysqld_port here, but use - mysqld_port_val. The idea is that if the option is unset, mysqld_port - will be NULL, but mysqld_port_val will not be reset. - */ - port= options.mysqld_port_val; - } - - if (options.mysqld_socket) - socket= options.mysqld_socket; - - /* no port was specified => instance falled back to default value */ - if (!options.mysqld_port && !options.mysqld_socket) - port= SERVER_DEFAULT_PORT; - - pthread_mutex_lock(&LOCK_instance); - - mysql_init(&mysql); - /* try to connect to a server with a fake username/password pair */ - if (mysql_real_connect(&mysql, LOCAL_HOST, username, - password, - NullS, port, - socket, 0)) - { - /* - We have successfully connected to the server using fake - username/password. Write a warning to the logfile. - */ - log_info("The Instance Manager was able to log into you server " - "with faked compiled-in password while checking server status. " - "Looks like something is wrong."); - pthread_mutex_unlock(&LOCK_instance); - return_val= TRUE; /* server is alive */ - } - else - return_val= test(!strncmp(access_denied_message, mysql_error(&mysql), - sizeof(access_denied_message) - 1)); - - mysql_close(&mysql); - pthread_mutex_unlock(&LOCK_instance); - - return return_val; + pthread_cond_signal(&Manager::get_guardian()->COND_guardian); } /* Stop an instance. - SYNOPSYS + SYNOPSIS stop() RETURN: @@ -527,19 +631,11 @@ int Instance::stop() struct timespec timeout; uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY; - if (is_running()) + if (is_mysqld_running()) { - if (options.shutdown_delay) - { - /* - NOTE: it is important to check shutdown_delay here, but use - shutdown_delay_val. The idea is that if the option is unset, - shutdown_delay will be NULL, but shutdown_delay_val will not be reset. - */ - waitchild= options.shutdown_delay_val; - } + waitchild= options.get_shutdown_delay(); - kill_instance(SIGTERM); + kill_mysqld(SIGTERM); /* sleep on condition to wait for SIGCHLD */ timeout.tv_sec= time(NULL) + waitchild; @@ -547,7 +643,7 @@ int Instance::stop() if (pthread_mutex_lock(&LOCK_instance)) return ER_STOP_INSTANCE; - while (options.get_pid() != 0) /* while server isn't stopped */ + while (options.load_pid() != 0) /* while server isn't stopped */ { int status; @@ -560,7 +656,7 @@ int Instance::stop() pthread_mutex_unlock(&LOCK_instance); - kill_instance(SIGKILL); + kill_mysqld(SIGKILL); return 0; } @@ -568,120 +664,83 @@ int Instance::stop() return ER_INSTANCE_IS_NOT_STARTED; } -#ifdef __WIN__ -BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode) -{ - DWORD dwTID, dwCode, dwErr= 0; - HANDLE hProcessDup= INVALID_HANDLE_VALUE; - HANDLE hRT= NULL; - HINSTANCE hKernel= GetModuleHandle("Kernel32"); - BOOL bSuccess= FALSE; +/* + Send signal to mysqld. - BOOL bDup= DuplicateHandle(GetCurrentProcess(), - hProcess, GetCurrentProcess(), &hProcessDup, - PROCESS_ALL_ACCESS, FALSE, 0); + SYNOPSIS + kill_mysqld() +*/ - // Detect the special case where the process is - // already dead... - if (GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) && - (dwCode == STILL_ACTIVE)) +void Instance::kill_mysqld(int signum) +{ + pid_t mysqld_pid= options.load_pid(); + + if (mysqld_pid == 0) { - FARPROC pfnExitProc; + log_info("Instance '%s': no pid file to send a signal (%d).", + (const char *) get_name()->str, + (int) signum); + return; + } - pfnExitProc= GetProcAddress(hKernel, "ExitProcess"); + log_info("Instance '%s': sending %d to %d...", + (const char *) get_name()->str, + (int) signum, + (int) mysqld_pid); - hRT= CreateRemoteThread((bDup) ? hProcessDup : hProcess, NULL, 0, - (LPTHREAD_START_ROUTINE)pfnExitProc, - (PVOID)uExitCode, 0, &dwTID); - - if (hRT == NULL) - dwErr= GetLastError(); + if (kill(mysqld_pid, signum)) + { + log_info("Instance '%s': kill() failed.", + (const char *) get_name()->str); + return; } - else - dwErr= ERROR_PROCESS_ABORTED; - if (hRT) + /* Kill suceeded */ + if (signum == SIGKILL) /* really killed instance with SIGKILL */ { - // Must wait process to terminate to - // guarantee that it has exited... - WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE); + log_error("Instance '%s': killed.", + (const char *) options.instance_name.str); - CloseHandle(hRT); - bSuccess= TRUE; + /* After sucessful hard kill the pidfile need to be removed */ + options.unlink_pidfile(); } +} - if (bDup) - CloseHandle(hProcessDup); - - if (!bSuccess) - SetLastError(dwErr); +/* + Return crashed flag. - return bSuccess; -} + SYNOPSIS + is_crashed() -int kill(pid_t pid, int signum) -{ - HANDLE processhandle= ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); - if (signum == SIGTERM) - ::SafeTerminateProcess(processhandle, 0); - else - ::TerminateProcess(processhandle, -1); - return 0; -} -#endif + RETURN + TRUE - mysqld crashed + FALSE - mysqld hasn't crashed yet +*/ -void Instance::kill_instance(int signum) +bool Instance::is_crashed() { - pid_t pid; - /* if there are no pid, everything seems to be fine */ - if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ - { - if (kill(pid, signum) == 0) - { - /* Kill suceeded */ - if (signum == SIGKILL) /* really killed instance with SIGKILL */ - { - log_error("The instance '%s' is being stopped forcibly. Normally" - "it should not happen. Probably the instance has been" - "hanging. You should also check your IM setup", - (const char *) options.instance_name.str); - /* After sucessful hard kill the pidfile need to be removed */ - options.unlink_pidfile(); - } - } - } - return; + bool val; + pthread_mutex_lock(&LOCK_instance); + val= crashed; + pthread_mutex_unlock(&LOCK_instance); + return val; } /* - Initialize instance parameters. - - SYNOPSYS - Instance::init() - name_arg name of the instance - - RETURN: - 0 ok - !0 error + Remove pid file. */ -int Instance::init(const LEX_STRING *name_arg) +void Instance::remove_pid() { - mysqld_compatible= is_mysqld_compatible_name(name_arg); - - return options.init(name_arg); -} + int mysqld_pid= options.load_pid(); + if (mysqld_pid == 0) + return; -int Instance::complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path) -{ - instance_map= instance_map_arg; - configured= !options.complete_initialization(mysqld_path); - return 0; - /* - TODO: return actual status (from - Instance_options::complete_initialization()) here. - */ + if (options.unlink_pidfile()) + { + log_error("Instance '%s': can not unlink pid file.", + (const char *) options.instance_name.str); + } } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 329eaa68b1a..412d01acc46 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -88,21 +88,19 @@ public: static bool is_mysqld_compatible_name(const LEX_STRING *name); public: - Instance(Thread_registry &thread_registry_arg); + Instance(); ~Instance(); - int init(const LEX_STRING *name_arg); - int complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path); + bool init(const LEX_STRING *name_arg); + bool complete_initialization(); - bool is_running(); + bool is_mysqld_running(); int start(); int stop(); /* send a signal to the instance */ - void kill_instance(int signo); + void kill_mysqld(int signo); bool is_crashed(); void set_crash_flag_n_wake_all(); - Instance_map *get_map(); /* The operation is intended to check if the instance is mysqld-compatible @@ -121,7 +119,6 @@ public: public: enum { DEFAULT_SHUTDOWN_DELAY= 35 }; Instance_options options; - Thread_registry &thread_registry; private: /* This attributes is a flag, specifies if the instance has been crashed. */ @@ -155,7 +152,6 @@ private: stop in Instance::stop() */ pthread_cond_t COND_instance_stopped; - Instance_map *instance_map; void remove_pid(); }; diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 2f830e616c4..a356e308e44 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -63,7 +63,7 @@ static void delete_instance(void *u) /* The option handler to pass to the process_default_option_files finction. - SYNOPSYS + SYNOPSIS process_option() ctx Handler context. Here it is an instance_map structure. group_name The name of the group the option belongs to. @@ -169,7 +169,7 @@ int Instance_map::process_one_option(const LEX_STRING *group, if (!(instance= (Instance *) hash_search(&hash, (byte *) group->str, group->length))) { - if (!(instance= new Instance(thread_registry))) + if (!(instance= new Instance())) return 1; if (instance->init(group) || add_instance(instance)) @@ -213,16 +213,13 @@ int Instance_map::process_one_option(const LEX_STRING *group, } -Instance_map::Instance_map(const char *default_mysqld_path_arg, - Thread_registry &thread_registry_arg): - mysqld_path(default_mysqld_path_arg), - thread_registry(thread_registry_arg) +Instance_map::Instance_map() { pthread_mutex_init(&LOCK_instance_map, 0); } -int Instance_map::init() +bool Instance_map::init() { return hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_instance_key, delete_instance, 0); @@ -276,7 +273,7 @@ void Instance_map::unlock() FLUSH INSTANCES without prior stop of all running instances. NOTE: The operation should be invoked with the following locks acquired: - - Guardian_thread; + - Guardian; - Instance_map; */ @@ -310,7 +307,7 @@ bool Instance_map::is_there_active_instance() while ((instance= iterator.next())) { if (guardian->find_instance_node(instance) != NULL || - instance->is_running()) + instance->is_mysqld_running()) { return TRUE; } @@ -335,18 +332,18 @@ int Instance_map::remove_instance(Instance *instance) int Instance_map::create_instance(const LEX_STRING *instance_name, const Named_value_arr *options) { - Instance *instance= new Instance(thread_registry); + Instance *instance= new Instance(); if (!instance) { - log_error("Error: can not initialize (name: '%s').", + log_error("Can not allocate instance (name: '%s').", (const char *) instance_name->str); return ER_OUT_OF_RESOURCES; } if (instance->init(instance_name)) { - log_error("Error: can not initialize (name: '%s').", + log_error("Can not initialize instance (name: '%s').", (const char *) instance_name->str); delete instance; return ER_OUT_OF_RESOURCES; @@ -359,7 +356,7 @@ int Instance_map::create_instance(const LEX_STRING *instance_name, if (instance->is_mysqld_compatible() && Instance_options::is_option_im_specific(option.get_name())) { - log_error("Error: IM-option (%s) can not be used " + log_error("IM-option (%s) can not be used " "in configuration of mysqld-compatible instance (%s).", (const char *) option.get_name(), (const char *) instance_name->str); @@ -374,9 +371,9 @@ int Instance_map::create_instance(const LEX_STRING *instance_name, log_info("Warning: instance name '%s' is mysqld-compatible.", (const char *) instance_name->str); - if (instance->complete_initialization(this, mysqld_path)) + if (instance->complete_initialization()) { - log_error("Error: can not complete initialization of instance (name: '%s').", + log_error("Can not complete initialization of instance (name: '%s').", (const char *) instance_name->str); delete instance; return ER_OUT_OF_RESOURCES; @@ -385,7 +382,7 @@ int Instance_map::create_instance(const LEX_STRING *instance_name, if (add_instance(instance)) { - log_error("Error: can not register instance (name: '%s').", + log_error("Can not register instance (name: '%s').", (const char *) instance_name->str); delete instance; return ER_OUT_OF_RESOURCES; @@ -411,7 +408,7 @@ bool Instance_map::complete_initialization() { Instance *instance= (Instance *) hash_element(&hash, i); - if (instance->complete_initialization(this, mysqld_path)) + if (instance->complete_initialization()) return TRUE; } @@ -429,7 +426,7 @@ bool Instance_map::complete_initialization() if (create_instance(&Instance::DFLT_INSTANCE_NAME, NULL)) { - log_error("Error: could not create default instance."); + log_error("Can not create default instance."); return TRUE; } @@ -444,7 +441,7 @@ bool Instance_map::complete_initialization() break; default: - log_error("Error: could not add default instance to the config file."); + log_error("Can not add default instance to the config file."); Instance *instance= find(&Instance::DFLT_INSTANCE_NAME); @@ -502,7 +499,7 @@ int Instance_map::load() if (my_search_option_files(Options::Main::config_file, &argc, (char ***) &argv, &args_used, process_option, (void*) this)) - log_info("Falling back to compiled-in defaults"); + log_info("Falling back to compiled-in defaults."); return complete_initialization(); } @@ -537,20 +534,20 @@ const char *Instance_map::get_instance_state_name(Instance *instance) { /* The instance is managed by Guardian: we can report precise state. */ - return Guardian_thread::get_instance_state_name( + return Guardian::get_instance_state_name( guardian->get_instance_state(instance_node)); } /* The instance is not managed by Guardian: we can report status only. */ - return instance->is_running() ? "online" : "offline"; + return instance->is_mysqld_running() ? "online" : "offline"; } /* Create a new configuration section for mysqld-instance in the config file. - SYNOPSYS + SYNOPSIS create_instance_in_file() instance_name mysqld-instance name options options for the new mysqld-instance @@ -569,7 +566,7 @@ int create_instance_in_file(const LEX_STRING *instance_name, if (my_access(Options::Main::config_file, W_OK)) { - log_error("Error: configuration file (%s) does not exist.", + log_error("Configuration file (%s) does not exist.", (const char *) Options::Main::config_file); return ER_CONF_FILE_DOES_NOT_EXIST; } @@ -578,7 +575,7 @@ int create_instance_in_file(const LEX_STRING *instance_name, if (cnf_file <= 0) { - log_error("Error: can not open configuration file (%s): %s.", + log_error("Can not open configuration file (%s): %s.", (const char *) Options::Main::config_file, (const char *) strerror(errno)); return ER_ACCESS_OPTION_FILE; @@ -591,7 +588,7 @@ int create_instance_in_file(const LEX_STRING *instance_name, my_write(cnf_file, (byte*)"]", 1, MYF(MY_NABP)) || my_write(cnf_file, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP))) { - log_error("Error: can not write to configuration file (%s): %s.", + log_error("Can not write to configuration file (%s): %s.", (const char *) Options::Main::config_file, (const char *) strerror(errno)); my_close(cnf_file, MYF(0)); @@ -615,7 +612,7 @@ int create_instance_in_file(const LEX_STRING *instance_name, if (my_write(cnf_file, (byte*)option_str, option_str_len, MYF(MY_NABP)) || my_write(cnf_file, (byte*)NEWLINE, NEWLINE_LEN, MYF(MY_NABP))) { - log_error("Error: can not write to configuration file (%s): %s.", + log_error("Can not write to configuration file (%s): %s.", (const char *) Options::Main::config_file, (const char *) strerror(errno)); my_close(cnf_file, MYF(0)); diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 9de40e35e0f..69d225c89f7 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -25,7 +25,7 @@ #pragma interface #endif -class Guardian_thread; +class Guardian; class Instance; class Named_value_arr; class Thread_registry; @@ -75,7 +75,7 @@ public: void lock(); void unlock(); - int init(); + bool init(); /* Process a given option and assign it to appropricate instance. This is @@ -105,8 +105,7 @@ public: int create_instance(const LEX_STRING *instance_name, const Named_value_arr *options); - Instance_map(const char *default_mysqld_path_arg, - Thread_registry &thread_registry_arg); + Instance_map(); ~Instance_map(); /* @@ -115,13 +114,13 @@ public: MT-NOTE: the options must be called under acquired locks of the following objects: - Instance_map; - - Guardian_thread; + - Guardian; */ const char *get_instance_state_name(Instance *instance); public: const char *mysqld_path; - Guardian_thread *guardian; + Guardian *guardian; private: /* loads options from config files */ @@ -132,8 +131,6 @@ private: enum { START_HASH_SIZE = 16 }; pthread_mutex_t LOCK_instance_map; HASH hash; - - Thread_registry &thread_registry; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 00da6660703..58e7b630fe9 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -29,13 +29,14 @@ #include "buffer.h" #include "instance.h" #include "log.h" +#include "options.h" #include "parse_output.h" #include "priv.h" /* Create "mysqld ..." command in the buffer */ -static inline int create_mysqld_command(Buffer *buf, +static inline bool create_mysqld_command(Buffer *buf, const LEX_STRING *mysqld_path, const LEX_STRING *option) { @@ -54,12 +55,35 @@ static inline int create_mysqld_command(Buffer *buf, /* here the '\0' character is copied from the option string */ buf->append(position, option->str, option->length + 1); - return buf->is_error(); + return buf->is_error() ? TRUE : FALSE; } - return 1; + return TRUE; +} + +static inline bool is_path_separator(char ch) +{ +#if defined(__WIN__) || defined(__NETWARE__) + /* On windows and netware more delimiters are possible */ + return ch == FN_LIBCHAR || ch == FN_DEVCHAR || ch == '/'; +#else + return ch == FN_LIBCHAR; /* Unixes */ +#endif } +static char *find_last_path_separator(char *path, uint length) +{ + while (length) + { + if (is_path_separator(path[length])) + return path + length; + length--; + } + return NULL; /* No path separator found */ +} + + + bool Instance_options::is_option_im_specific(const char *option_name) { static const char *IM_SPECIFIC_OPTIONS[] = @@ -82,8 +106,12 @@ bool Instance_options::is_option_im_specific(const char *option_name) Instance_options::Instance_options() :mysqld_version(NULL), mysqld_socket(NULL), mysqld_datadir(NULL), - mysqld_pid_file(NULL), mysqld_port(NULL), mysqld_port_val(0), - nonguarded(NULL), shutdown_delay(NULL), shutdown_delay_val(0), + mysqld_pid_file(NULL), + nonguarded(NULL), + mysqld_port(NULL), + mysqld_port_val(0), + shutdown_delay(NULL), + shutdown_delay_val(0), filled_default_options(0) { mysqld_path.str= NULL; @@ -99,7 +127,7 @@ Instance_options::Instance_options() /* Get compiled-in value of default_option - SYNOPSYS + SYNOPSIS get_default_option() result buffer to put found value result_len buffer size @@ -139,7 +167,7 @@ err: /* Fill mysqld_version option (used at initialization stage) - SYNOPSYS + SYNOPSIS fill_instance_version() DESCRIPTION @@ -147,27 +175,36 @@ err: Get mysqld version string from "mysqld --version" output. RETURN - 0 - ok - 1 - error occured + FALSE - ok + TRUE - error occured */ -int Instance_options::fill_instance_version() +bool Instance_options::fill_instance_version() { char result[MAX_VERSION_LENGTH]; LEX_STRING version_option= { C_STRING_WITH_LEN(" --no-defaults --version") }; - int rc= 1; Buffer cmd(mysqld_path.length + version_option.length + 1); if (create_mysqld_command(&cmd, &mysqld_path, &version_option)) - goto err; + { + log_error("Failed to get version of '%s': out of memory.", + (const char *) mysqld_path.str); + return TRUE; + } bzero(result, MAX_VERSION_LENGTH); - rc= parse_output_and_get_value(cmd.buffer, "Ver", result, - MAX_VERSION_LENGTH, GET_LINE); + if (parse_output_and_get_value(cmd.buffer, "Ver", result, + MAX_VERSION_LENGTH, GET_LINE)) + { + log_error("Failed to get version of '%s': unexpected output.", + (const char *) mysqld_path.str); + return TRUE; + } + + DBUG_ASSERT(*result != '\0'); - if (*result != '\0') { char *start; /* chop the newline from the end of the version string */ @@ -179,18 +216,15 @@ int Instance_options::fill_instance_version() mysqld_version= strdup_root(&alloc, start); } -err: - if (rc) - log_error("fill_instance_version: Failed to get version of '%s'", - mysqld_path.str); - return rc; + + return FALSE; } /* Fill mysqld_real_path - SYNOPSYS + SYNOPSIS fill_mysqld_real_path() DESCRIPTION @@ -202,28 +236,37 @@ err: script(for example libtool) or a symlink. RETURN - 0 - ok - 1 - error occured + FALSE - ok + TRUE - error occured */ -int Instance_options::fill_mysqld_real_path() +bool Instance_options::fill_mysqld_real_path() { char result[FN_REFLEN]; LEX_STRING help_option= { C_STRING_WITH_LEN(" --no-defaults --help") }; - int rc= 1; Buffer cmd(mysqld_path.length + help_option.length); if (create_mysqld_command(&cmd, &mysqld_path, &help_option)) - goto err; + { + log_error("Failed to get real path of '%s': out of memory.", + (const char *) mysqld_path.str); + return TRUE; + } bzero(result, FN_REFLEN); - rc= parse_output_and_get_value(cmd.buffer, "Usage: ", + if (parse_output_and_get_value(cmd.buffer, "Usage: ", result, FN_REFLEN, - GET_LINE); + GET_LINE)) + { + log_error("Failed to get real path of '%s': unexpected output.", + (const char *) mysqld_path.str); + return TRUE; + } + + DBUG_ASSERT(*result != '\0'); - if (*result != '\0') { char* options_str; /* chop the path of at [OPTIONS] */ @@ -232,17 +275,15 @@ int Instance_options::fill_mysqld_real_path() mysqld_real_path.str= strdup_root(&alloc, result); mysqld_real_path.length= strlen(mysqld_real_path.str); } -err: - if (rc) - log_error("fill_mysqld_real_path: Failed to get real path of mysqld"); - return rc; + + return FALSE; } /* Fill various log options - SYNOPSYS + SYNOPSIS fill_log_options() DESCRIPTION @@ -252,11 +293,11 @@ err: file name and placement. RETURN - 0 - ok - 1 - error occured + FALSE - ok + TRUE - error occured */ -int Instance_options::fill_log_options() +bool Instance_options::fill_log_options() { Buffer buff; enum { MAX_LOG_OPTION_LENGTH= 256 }; @@ -282,7 +323,7 @@ int Instance_options::fill_log_options() if (mysqld_datadir == NULL) { if (get_default_option(datadir, MAX_LOG_OPTION_LENGTH, "--datadir")) - goto err; + return TRUE; } else { @@ -320,7 +361,7 @@ int Instance_options::fill_log_options() if ((MAX_LOG_OPTION_LENGTH - strlen(full_name)) <= strlen(log_files->default_suffix)) - goto err; + return TRUE; strmov(full_name + strlen(full_name), log_files->default_suffix); @@ -340,22 +381,20 @@ int Instance_options::fill_log_options() datadir, "", MY_UNPACK_FILENAME | MY_SAFE_PATH); if (!(*(log_files->value)= strdup_root(&alloc, full_name))) - goto err; + return TRUE; } } } } - return 0; -err: - return 1; + return FALSE; } /* Get the full pid file name with path - SYNOPSYS + SYNOPSIS get_pid_filaname() result buffer to sotre the pidfile value @@ -396,7 +435,7 @@ int Instance_options::unlink_pidfile() } -pid_t Instance_options::get_pid() +pid_t Instance_options::load_pid() { FILE *pid_file_stream; @@ -415,26 +454,56 @@ pid_t Instance_options::get_pid() } -int Instance_options::complete_initialization(const char *default_path) +bool Instance_options::complete_initialization() { int arg_idx; const char *tmp; char *end; + char bin_name_firstchar; if (!mysqld_path.str) { - // Need one extra byte, as convert_dirname() adds a slash at the end. - if (!(mysqld_path.str= alloc_root(&alloc, strlen(default_path) + 2))) - goto err; - strcpy(mysqld_path.str, default_path); - } + /* + Need to copy the path to allocated memory, as convert_dirname() might + need to change it + */ + mysqld_path.str= + alloc_root(&alloc, strlen(Options::Main::default_mysqld_path) + 1); + + if (!mysqld_path.str) + return TRUE; - // it's safe to cast this to char* since this is a buffer we are allocating - end= convert_dirname((char*)mysqld_path.str, mysqld_path.str, NullS); - end[-1]= 0; + strcpy(mysqld_path.str, Options::Main::default_mysqld_path); + } mysqld_path.length= strlen(mysqld_path.str); + /* + If we found path with no slashes (end == NULL), we should not call + convert_dirname() at all. As we have got relative path to the binary. + That is, user supposes that mysqld resides in the same dir as + mysqlmanager. + */ + if ((end= find_last_path_separator(mysqld_path.str, mysqld_path.length))) + { + bin_name_firstchar= end[1]; + + /* + Below we will conver the path to mysqld in the case, it was given + in a format of another OS (e.g. uses '/' instead of '\' etc). + Here we strip the path to get rid of the binary name ("mysqld"), + we do it by removing first letter of the binary name (e.g. 'm' + in "mysqld"). Later we put it back. + */ + end[1]= 0; + + /* convert dirname to the format of current OS */ + convert_dirname((char*)mysqld_path.str, mysqld_path.str, NullS); + + /* put back the first character of the binary name*/ + end[1]= bin_name_firstchar; + } + if (mysqld_port) mysqld_port_val= atoi(mysqld_port); @@ -442,7 +511,7 @@ int Instance_options::complete_initialization(const char *default_path) shutdown_delay_val= atoi(shutdown_delay); if (!(tmp= strdup_root(&alloc, "--no-defaults"))) - goto err; + return TRUE; if (!mysqld_pid_file) { @@ -477,21 +546,21 @@ int Instance_options::complete_initialization(const char *default_path) } if (get_pid_filename(pid_file_with_path)) - goto err; + return TRUE; /* we need to reserve space for the final zero + possible default options */ if (!(argv= (char**) alloc_root(&alloc, (get_num_options() + 1 + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) - goto err; + return TRUE; filled_default_options= 0; /* the path must be first in the argv */ if (add_to_argv(mysqld_path.str)) - goto err; + return TRUE; if (add_to_argv(tmp)) - goto err; + return TRUE; arg_idx= filled_default_options; for (int opt_idx= 0; opt_idx < get_num_options(); ++opt_idx) @@ -514,12 +583,9 @@ int Instance_options::complete_initialization(const char *default_path) argv[arg_idx]= 0; if (fill_log_options() || fill_mysqld_real_path() || fill_instance_version()) - goto err; - - return 0; + return TRUE; -err: - return 1; + return FALSE; } @@ -636,26 +702,26 @@ void Instance_options::print_argv() /* We execute this function to initialize some options. - Return value: 0 - ok. 1 - unable to allocate memory. + + RETURN + FALSE - ok + TRUE - memory allocation error */ -int Instance_options::init(const LEX_STRING *instance_name_arg) +bool Instance_options::init(const LEX_STRING *instance_name_arg) { instance_name.length= instance_name_arg->length; init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); if (options.init()) - goto err; + return TRUE; if (!(instance_name.str= strmake_root(&alloc, instance_name_arg->str, instance_name_arg->length))) - goto err; - - return 0; + return TRUE; -err: - return 1; + return FALSE; } @@ -663,3 +729,29 @@ Instance_options::~Instance_options() { free_root(&alloc, MYF(0)); } + + +uint Instance_options::get_shutdown_delay() const +{ + static const uint DEFAULT_SHUTDOWN_DELAY= 35; + + /* + NOTE: it is important to check shutdown_delay here, but use + shutdown_delay_val. The idea is that if the option is unset, + shutdown_delay will be NULL, but shutdown_delay_val will not be reset. + */ + + return shutdown_delay ? shutdown_delay_val : DEFAULT_SHUTDOWN_DELAY; +} + +int Instance_options::get_mysqld_port() const +{ + /* + NOTE: it is important to check mysqld_port here, but use mysqld_port_val. + The idea is that if the option is unset, mysqld_port will be NULL, but + mysqld_port_val will not be reset. + */ + + return mysqld_port ? mysqld_port_val : 0; +} + diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index c3b0a16a40d..7c1e1a8dcf3 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -45,8 +45,9 @@ public: public: Instance_options(); ~Instance_options(); + /* fills in argv */ - int complete_initialization(const char *default_path); + bool complete_initialization(); bool set_option(Named_value *option); void unset_option(const char *option_name); @@ -55,12 +56,15 @@ public: inline Named_value get_option(int idx) const; public: - int init(const LEX_STRING *instance_name_arg); - pid_t get_pid(); + bool init(const LEX_STRING *instance_name_arg); + pid_t load_pid(); int get_pid_filename(char *result); int unlink_pidfile(); void print_argv(); + uint get_shutdown_delay() const; + int get_mysqld_port() const; + public: /* We need this value to be greater or equal then FN_REFLEN found in @@ -79,21 +83,17 @@ public: const char *mysqld_socket; const char *mysqld_datadir; const char *mysqld_pid_file; - const char *mysqld_port; - uint mysqld_port_val; LEX_STRING instance_name; LEX_STRING mysqld_path; LEX_STRING mysqld_real_path; const char *nonguarded; - const char *shutdown_delay; - uint shutdown_delay_val; /* log enums are defined in parse.h */ char *logs[3]; private: - int fill_log_options(); - int fill_instance_version(); - int fill_mysqld_real_path(); + bool fill_log_options(); + bool fill_instance_version(); + bool fill_mysqld_real_path(); int add_to_argv(const char *option); int get_default_option(char *result, size_t result_len, const char *option_name); @@ -102,6 +102,11 @@ private: int find_option(const char *option_name); private: + const char *mysqld_port; + uint mysqld_port_val; + const char *shutdown_delay; + uint shutdown_delay_val; + uint filled_default_options; MEM_ROOT alloc; diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 62962c00957..f18a594985c 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -29,7 +29,6 @@ #include <sys/un.h> #endif -#include "instance_map.h" #include "log.h" #include "mysql_connection.h" #include "options.h" @@ -59,47 +58,18 @@ static void set_no_inherit(int socket) } -/* - Listener_thread - incapsulates listening functionality -*/ - -class Listener_thread: public Listener_thread_args -{ -public: - Listener_thread(const Listener_thread_args &args); - ~Listener_thread(); - void run(); -private: - static const int LISTEN_BACK_LOG_SIZE= 5; /* standard backlog size */ - ulong total_connection_count; - Thread_info thread_info; - - int sockets[2]; - int num_sockets; - fd_set read_fds; -private: - void handle_new_mysql_connection(Vio *vio); - int create_tcp_socket(); - int create_unix_socket(struct sockaddr_un &unix_socket_address); -}; - - -Listener_thread::Listener_thread(const Listener_thread_args &args) : - Listener_thread_args(args.thread_registry, args.user_map, args.instance_map) - ,total_connection_count(0) - ,thread_info(pthread_self(), TRUE) - ,num_sockets(0) -{ -} - - -Listener_thread::~Listener_thread() +Listener::Listener(Thread_registry *thread_registry_arg, + User_map *user_map_arg) + :thread_registry(thread_registry_arg), + user_map(user_map_arg), + total_connection_count(0), + num_sockets(0) { } /* - Listener_thread::run() - listen all supported sockets and spawn a thread + Listener::run() - listen all supported sockets and spawn a thread to handle incoming connection. Using 'die' in case of syscall failure is OK now - we don't hold any resources and 'die' kills the signal thread automatically. To be rewritten @@ -108,26 +78,17 @@ Listener_thread::~Listener_thread() architecture. */ -void Listener_thread::run() +void Listener::run() { int i, n= 0; - log_info("Listener_thread: started."); - #ifndef __WIN__ - /* we use this var to check whether we are running on LinuxThreads */ - pid_t thread_pid; - - thread_pid= getpid(); - struct sockaddr_un unix_socket_address; - /* set global variable */ - linuxthreads= (thread_pid != manager_pid); #endif - thread_registry.register_thread(&thread_info); + log_info("Listener: started."); - my_thread_init(); + thread_registry->register_thread(&thread_info); FD_ZERO(&read_fds); @@ -146,7 +107,7 @@ void Listener_thread::run() n++; timeval tv; - while (!thread_registry.is_shutdown()) + while (!thread_registry->is_shutdown()) { fd_set read_fds_arg= read_fds; /* @@ -166,8 +127,8 @@ void Listener_thread::run() if (rc == 0 || rc == -1) { if (rc == -1 && errno != EINTR) - log_error("Listener_thread: select() failed, %s", - strerror(errno)); + log_error("Listener: select() failed: %s.", + (const char *) strerror(errno)); continue; } @@ -183,10 +144,12 @@ void Listener_thread::run() { set_no_inherit(client_fd); - Vio *vio= vio_new(client_fd, socket_index == 0 ? - VIO_TYPE_SOCKET : VIO_TYPE_TCPIP, - socket_index == 0 ? 1 : 0); - if (vio != 0) + struct st_vio *vio= + vio_new(client_fd, + socket_index == 0 ? VIO_TYPE_SOCKET : VIO_TYPE_TCPIP, + socket_index == 0 ? 1 : 0); + + if (vio != NULL) handle_new_mysql_connection(vio); else { @@ -200,7 +163,7 @@ void Listener_thread::run() /* III. Release all resources and exit */ - log_info("Listener_thread: shutdown requested, exiting..."); + log_info("Listener: shutdown requested, exiting..."); for (i= 0; i < num_sockets; i++) close(sockets[i]); @@ -209,10 +172,9 @@ void Listener_thread::run() unlink(unix_socket_address.sun_path); #endif - thread_registry.unregister_thread(&thread_info); - my_thread_end(); + thread_registry->unregister_thread(&thread_info); - log_info("Listener_thread: finished."); + log_info("Listener: finished."); return; err: @@ -220,13 +182,12 @@ err: for (i= 0; i < num_sockets; i++) close(sockets[i]); - thread_registry.unregister_thread(&thread_info); - thread_registry.request_shutdown(); - my_thread_end(); + thread_registry->unregister_thread(&thread_info); + thread_registry->request_shutdown(); return; } -int Listener_thread::create_tcp_socket() +int Listener::create_tcp_socket() { /* value to be set by setsockopt */ int arg= 1; @@ -234,8 +195,8 @@ int Listener_thread::create_tcp_socket() int ip_socket= socket(AF_INET, SOCK_STREAM, 0); if (ip_socket == INVALID_SOCKET) { - log_error("Listener_thead: socket(AF_INET) failed, %s", - strerror(errno)); + log_error("Listener: socket(AF_INET) failed: %s.", + (const char *) strerror(errno)); return -1; } @@ -247,7 +208,7 @@ int Listener_thread::create_tcp_socket() { im_bind_addr= (ulong) inet_addr(Options::Main::bind_address); - if (im_bind_addr == INADDR_NONE) + if (im_bind_addr == (ulong) INADDR_NONE) im_bind_addr= htonl(INADDR_ANY); } else @@ -265,16 +226,16 @@ int Listener_thread::create_tcp_socket() if (bind(ip_socket, (struct sockaddr *) &ip_socket_address, sizeof(ip_socket_address))) { - log_error("Listener_thread: bind(ip socket) failed, '%s'", - strerror(errno)); + log_error("Listener: bind(ip socket) failed: %s.", + (const char *) strerror(errno)); close(ip_socket); return -1; } if (listen(ip_socket, LISTEN_BACK_LOG_SIZE)) { - log_error("Listener_thread: listen(ip socket) failed, %s", - strerror(errno)); + log_error("Listener: listen(ip socket) failed: %s.", + (const char *) strerror(errno)); close(ip_socket); return -1; } @@ -287,19 +248,20 @@ int Listener_thread::create_tcp_socket() FD_SET(ip_socket, &read_fds); sockets[num_sockets++]= ip_socket; - log_info("accepting connections on ip socket (port: %d)", (int) im_port); + log_info("Listener: accepting connections on ip socket (port: %d)...", + (int) im_port); return 0; } #ifndef __WIN__ -int Listener_thread:: +int Listener:: create_unix_socket(struct sockaddr_un &unix_socket_address) { int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); if (unix_socket == INVALID_SOCKET) { - log_error("Listener_thead: socket(AF_UNIX) failed, %s", - strerror(errno)); + log_error("Listener: socket(AF_UNIX) failed: %s.", + (const char *) strerror(errno)); return -1; } @@ -318,9 +280,9 @@ create_unix_socket(struct sockaddr_un &unix_socket_address) if (bind(unix_socket, (struct sockaddr *) &unix_socket_address, sizeof(unix_socket_address))) { - log_error("Listener_thread: bind(unix socket) failed, " - "socket file name is '%s', error '%s'", - unix_socket_address.sun_path, strerror(errno)); + log_error("Listener: bind(unix socket) failed for '%s': %s.", + (const char *) unix_socket_address.sun_path, + (const char *) strerror(errno)); close(unix_socket); return -1; } @@ -329,8 +291,8 @@ create_unix_socket(struct sockaddr_un &unix_socket_address) if (listen(unix_socket, LISTEN_BACK_LOG_SIZE)) { - log_error("Listener_thread: listen(unix socket) failed, %s", - strerror(errno)); + log_error("Listener: listen(unix socket) failed: %s.", + (const char *) strerror(errno)); close(unix_socket); return -1; } @@ -341,8 +303,8 @@ create_unix_socket(struct sockaddr_un &unix_socket_address) /* make sure that instances won't be listening our sockets */ set_no_inherit(unix_socket); - log_info("accepting connections on unix socket '%s'", - unix_socket_address.sun_path); + log_info("Listener: accepting connections on unix socket '%s'...", + (const char *) unix_socket_address.sun_path); sockets[num_sockets++]= unix_socket; FD_SET(unix_socket, &read_fds); return 0; @@ -352,51 +314,21 @@ create_unix_socket(struct sockaddr_un &unix_socket_address) /* Create new mysql connection. Created thread is responsible for deletion of - the Mysql_connection_thread_args and Vio instances passed to it. - SYNOPSYS + the Mysql_connection and Vio instances passed to it. + SYNOPSIS handle_new_mysql_connection() */ -void Listener_thread::handle_new_mysql_connection(Vio *vio) +void Listener::handle_new_mysql_connection(struct st_vio *vio) { - if (Mysql_connection_thread_args *mysql_thread_args= - new Mysql_connection_thread_args(vio, thread_registry, user_map, - ++total_connection_count, - instance_map) - ) + Mysql_connection *mysql_connection= + new Mysql_connection(thread_registry, user_map, + vio, ++total_connection_count); + if (mysql_connection == NULL || mysql_connection->start(Thread::DETACHED)) { - /* - Initialize thread attributes to create detached thread; it seems - easier to do it ad-hoc than have a global variable for attributes. - */ - pthread_t mysql_thd_id; - pthread_attr_t mysql_thd_attr; - pthread_attr_init(&mysql_thd_attr); - pthread_attr_setdetachstate(&mysql_thd_attr, PTHREAD_CREATE_DETACHED); - if (set_stacksize_n_create_thread(&mysql_thd_id, &mysql_thd_attr, - mysql_connection, mysql_thread_args)) - { - delete mysql_thread_args; - vio_delete(vio); - log_error("handle_one_mysql_connection():" - "set_stacksize_n_create_thread(mysql) failed"); - } - pthread_attr_destroy(&mysql_thd_attr); - } - else + log_error("Listener: can not start connection handler."); + delete mysql_connection; vio_delete(vio); + } + /* The connection will delete itself when the thread is finished */ } - - -pthread_handler_t listener(void *arg) -{ - Listener_thread_args *args= (Listener_thread_args *) arg; - Listener_thread listener(*args); - listener.run(); - /* - args is a stack variable because listener thread lives as long as the - manager process itself - */ - return 0; -} - diff --git a/server-tools/instance-manager/listener.h b/server-tools/instance-manager/listener.h index c28ab0649d7..811744f8ea6 100644 --- a/server-tools/instance-manager/listener.h +++ b/server-tools/instance-manager/listener.h @@ -16,33 +16,46 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <my_global.h> -#include <my_pthread.h> +#include "thread_registry.h" #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface #endif - -pthread_handler_t listener(void *arg); - class Thread_registry; class User_map; -class Instance_map; -struct Listener_thread_args +/** + Listener - a thread listening on sockets and spawning + connection threads. +*/ + +class Listener: public Thread { - Thread_registry &thread_registry; - const User_map &user_map; - Instance_map &instance_map; - - Listener_thread_args(Thread_registry &thread_registry_arg, - const User_map &user_map_arg, - Instance_map &instance_map_arg) : - thread_registry(thread_registry_arg) - ,user_map(user_map_arg) - ,instance_map(instance_map_arg) - {} +public: + Listener(Thread_registry *thread_registry_arg, User_map *user_map_arg); + +protected: + virtual void run(); + +private: + static const int LISTEN_BACK_LOG_SIZE= 5; /* standard backlog size */ + +private: + Thread_info thread_info; + Thread_registry *thread_registry; + User_map *user_map; + + ulong total_connection_count; + + int sockets[2]; + int num_sockets; + fd_set read_fds; + +private: + void handle_new_mysql_connection(struct st_vio *vio); + int create_tcp_socket(); + int create_unix_socket(struct sockaddr_un &unix_socket_address); }; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc index 7214cde7193..7ff45a15432 100644 --- a/server-tools/instance-manager/log.cc +++ b/server-tools/instance-manager/log.cc @@ -33,11 +33,12 @@ /* Format log entry and write it to the given stream. - SYNOPSYS + SYNOPSIS log() */ -static inline void log(FILE *file, const char *format, va_list args) +static void log(FILE *file,const char *level_tag, const char *format, + va_list args) { /* log() should be thread-safe; it implies that we either call fprintf() @@ -53,15 +54,16 @@ static inline void log(FILE *file, const char *format, va_list args) localtime_r(&now, &bd_time); char buff_date[128]; - sprintf(buff_date, "[%d/%lu] [%02d/%02d/%02d %02d:%02d:%02d] ", + sprintf(buff_date, "[%d/%lu] [%02d/%02d/%02d %02d:%02d:%02d] [%s] ", (int) getpid(), (unsigned long) pthread_self(), - bd_time.tm_year % 100, - bd_time.tm_mon + 1, - bd_time.tm_mday, - bd_time.tm_hour, - bd_time.tm_min, - bd_time.tm_sec); + (int) bd_time.tm_year % 100, + (int) bd_time.tm_mon + 1, + (int) bd_time.tm_mday, + (int) bd_time.tm_hour, + (int) bd_time.tm_min, + (int) bd_time.tm_sec, + (const char *) level_tag); /* Format the message */ char buff_stack[256]; @@ -109,57 +111,73 @@ static inline void log(FILE *file, const char *format, va_list args) /* don't fflush() the file: buffering strategy is set in log_init() */ } +/************************************************************************** + Logging: implementation of public interface. +**************************************************************************/ -void log_error(const char *format, ...) -{ - va_list args; - va_start(args, format); - log(stderr, format, args); - va_end(args); -} +/* + The function initializes logging sub-system. + SYNOPSIS + log_init() +*/ -void log_info(const char *format, ...) +void log_init() { - va_list args; - va_start(args, format); - log(stdout, format, args); - va_end(args); + /* + stderr is unbuffered by default; there is no good of line buffering, + as all logging is performed linewise - so remove buffering from stdout + also + */ + setbuf(stdout, 0); } -/* TODO: rewrite with buffering print */ -void print_info(const char *format, ...) + +/* + The function is intended to log error messages. It precedes a message + with date, time and [ERROR] tag and print it to the stderr. + + SYNOPSIS + log_error() + format [IN] format string + ... [IN] arguments to format +*/ + +void log_error(const char *format, ...) { va_list args; va_start(args, format); - vfprintf(stdout, format, args); + log(stderr, "ERROR", format, args); va_end(args); } -void print_error(const char *format, ...) + +/* + The function is intended to log information messages. It precedes + a message with date, time and [INFO] tag and print it to the stdout. + + SYNOPSIS + log_error() + format [IN] format string + ... [IN] arguments to format +*/ + +void log_info(const char *format, ...) { va_list args; va_start(args, format); - vfprintf(stderr, format, args); + log(stdout, "INFO", format, args); va_end(args); } /* - log_init() - RETURN VALUE - 0 ok - !0 error -*/ + The function prints information to the error log and eixt(1). -void log_init() -{ - /* - stderr is unbuffered by default; there is no good of line buffering, - as all logging is performed linewise - so remove buffering from stdout - also - */ - setbuf(stdout, 0); -} + SYNOPSIS + die() + format [IN] format string + ... [IN] arguments to format +*/ void die(const char *format, ...) { diff --git a/server-tools/instance-manager/log.h b/server-tools/instance-manager/log.h index 825d7515513..9c47dbe04f5 100644 --- a/server-tools/instance-manager/log.h +++ b/server-tools/instance-manager/log.h @@ -19,20 +19,23 @@ /* Logging facilities. - Two logging streams are supported: error log and info log. Additionally - libdbug may be used for debug information output. + Two logging streams are supported: error log and info log. + Additionally libdbug may be used for debug information output. + ANSI C buffered I/O is used to perform logging. + Logging is performed via stdout/stder, so one can reopen them to point to - ordinary files. To initialize loggin environment log_init() must be called. + ordinary files. To initialize logging environment log_init() must be called. Rationale: - no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h - no constructors/desctructors to make logging available all the time - Function names are subject to change. */ -/* Precede error message with date and time and print it to the stdout */ +void log_init(); + + void log_info(const char *format, ...) #ifdef __GNUC__ __attribute__ ((format(printf, 1, 2))) @@ -40,7 +43,6 @@ void log_info(const char *format, ...) ; -/* Precede error message with date and time and print it to the stderr */ void log_error(const char *format, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) @@ -48,30 +50,6 @@ void log_error(const char *format, ...) ; -/* - Now this is simple catchouts for printf (no date/time is logged), to be - able to replace underlying streams in future. -*/ - -void print_info(const char *format, ...) -#ifdef __GNUC__ - __attribute__ ((format (printf, 1, 2))) -#endif - ; - - -void print_error(const char *format, ...) -#ifdef __GNUC__ - __attribute__ ((format (printf, 1, 2))) -#endif - ; - -/* initialize logs */ -void log_init(); - - -/* print information to the error log and eixt(1) */ - void die(const char *format, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 4bd298eedec..a002902bd56 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -37,33 +37,6 @@ #include "user_map.h" -int create_pid_file(const char *pid_file_name, int pid) -{ - FILE *pid_file; - - if (!(pid_file= my_fopen(pid_file_name, O_WRONLY | O_CREAT | O_BINARY, - MYF(0)))) - { - log_error("Error: can not create pid file '%s': %s (errno: %d)", - (const char *) pid_file_name, - (const char *) strerror(errno), - (int) errno); - return 1; - } - - if (fprintf(pid_file, "%d\n", (int) pid) <= 0) - { - log_error("Error: can not write to pid file '%s': %s (errno: %d)", - (const char *) pid_file_name, - (const char *) strerror(errno), - (int) errno); - return 1; - } - - my_fclose(pid_file, MYF(0)); - - return 0; -} #ifndef __WIN__ void set_signals(sigset_t *mask) @@ -120,19 +93,79 @@ int my_sigwait(const sigset_t *set, int *sig) #endif -void stop_all(Guardian_thread *guardian, Thread_registry *registry) +/********************************************************************** + Implementation of checking the actual thread model. +***********************************************************************/ + +namespace { /* no-indent */ + +class ThreadModelChecker: public Thread +{ +public: + ThreadModelChecker() + :main_pid(getpid()) + { } + +public: + inline bool is_linux_threads() const + { + return linux_threads; + } + +protected: + virtual void run() + { + linux_threads= main_pid != getpid(); + } + +private: + pid_t main_pid; + bool linux_threads; +}; + +bool check_if_linux_threads(bool *linux_threads) +{ + ThreadModelChecker checker; + + if (checker.start() || checker.join()) + return TRUE; + + *linux_threads= checker.is_linux_threads(); + + return FALSE; +} + +} + + +/********************************************************************** + Manager implementation +***********************************************************************/ + +Guardian *Manager::p_guardian; +Instance_map *Manager::p_instance_map; +Thread_registry *Manager::p_thread_registry; +User_map *Manager::p_user_map; + +#ifndef __WIN__ +bool Manager::linux_threads; +#endif // __WIN__ + + +void Manager::stop_all_threads() { /* Let guardian thread know that it should break it's processing cycle, once it wakes up. */ - guardian->request_shutdown(); + p_guardian->request_shutdown(); /* wake guardian */ - pthread_cond_signal(&guardian->COND_guardian); + pthread_cond_signal(&p_guardian->COND_guardian); /* stop all threads */ - registry->deliver_shutdown(); + p_thread_registry->deliver_shutdown(); } + /* manager - entry point to the main instance manager process: start listener thread, write pid file and enter into signal handling. @@ -142,11 +175,24 @@ void stop_all(Guardian_thread *guardian, Thread_registry *registry) TODO: how about returning error status. */ -void manager() +int Manager::main() { int err_code; + int rc= 1; const char *err_msg; bool shutdown_complete= FALSE; + pid_t manager_pid= getpid(); + +#ifndef __WIN__ + if (check_if_linux_threads(&linux_threads)) + { + log_error("Can not determine thread model."); + return 1; + } + + log_info("Detected threads model: %s.", + (const char *) (linux_threads ? "LINUX threads" : "POSIX threads")); +#endif // __WIN__ Thread_registry thread_registry; /* @@ -156,31 +202,31 @@ void manager() */ User_map user_map; - Instance_map instance_map(Options::Main::default_mysqld_path, - thread_registry); - Guardian_thread guardian_thread(thread_registry, - &instance_map, - Options::Main::monitoring_interval); + Instance_map instance_map; + Guardian guardian(&thread_registry, &instance_map, + Options::Main::monitoring_interval); - Listener_thread_args listener_args(thread_registry, user_map, instance_map); + Listener listener(&thread_registry, &user_map); - manager_pid= getpid(); - instance_map.guardian= &guardian_thread; + p_instance_map= &instance_map; + p_guardian= instance_map.guardian= &guardian; + p_thread_registry= &thread_registry; + p_user_map= &user_map; /* Initialize instance map. */ if (instance_map.init()) { - log_error("Error: can not initialize instance list: out of memory."); - return; + log_error("Can not initialize instance list: out of memory."); + return 1; } /* Initialize user map and load password file. */ if (user_map.init()) { - log_error("Error: can not initialize user list: out of memory."); - return; + log_error("Can not initialize user list: out of memory."); + return 1; } if ((err_code= user_map.load(Options::Main::password_file_name, &err_msg))) @@ -193,13 +239,13 @@ void manager() mysqld_safe-compatible mode. Continue, but complain in log. */ - log_error("Warning: password file does not exist, " - "nobody will be able to connect to Instance Manager."); + log_info("Warning: password file does not exist, " + "nobody will be able to connect to Instance Manager."); } else { - log_error("Error: %s.", (const char *) err_msg); - return; + log_error("%s.", (const char *) err_msg); + return 1; } } @@ -210,7 +256,7 @@ void manager() (int) manager_pid); if (create_pid_file(Options::Main::pid_file_name, manager_pid)) - return; /* necessary logging has been already done. */ + return 1; /* necessary logging has been already done. */ /* Initialize signals and alarm-infrastructure. @@ -218,49 +264,36 @@ void manager() NOTE: To work nicely with LinuxThreads, the signal thread is the first thread in the process. - NOTE: - After init_thr_alarm() call it's possible to call thr_alarm() (from - different threads), that results in sending ALARM signal to the alarm - thread (which can be the main thread). That signal can interrupt - blocking calls. - - In other words, a blocking call can be interrupted in the main thread - after init_thr_alarm(). + NOTE: After init_thr_alarm() call it's possible to call thr_alarm() + (from different threads), that results in sending ALARM signal to the + alarm thread (which can be the main thread). That signal can interrupt + blocking calls. In other words, a blocking call can be interrupted in + the main thread after init_thr_alarm(). */ sigset_t mask; set_signals(&mask); - /* create guardian thread */ + /* + Create the guardian thread. The newly started thread will block until + we actually load instances. + + NOTE: Guardian should be shutdown first. Only then all other threads + can be stopped. This should be done in this order because the guardian + is responsible for shutting down all the guarded instances, and this + is a long operation. + + NOTE: Guardian uses thr_alarm() when detects the current state of an + instance (is_running()), but this does not interfere with + flush_instances() call later in the code, because until + flush_instances() completes in the main thread, Guardian thread is not + permitted to process instances. And before flush_instances() has + completed, there are no instances to guard. + */ + if (guardian.start(Thread::DETACHED)) { - pthread_t guardian_thd_id; - pthread_attr_t guardian_thd_attr; - int rc; - - /* - NOTE: Guardian should be shutdown first. Only then all other threads - need to be stopped. This should be done, as guardian is responsible - for shutting down the instances, and this is a long operation. - - NOTE: Guardian uses thr_alarm() when detects current state of - instances (is_running()), but it is not interfere with - flush_instances() later in the code, because until flush_instances() - complete in the main thread, Guardian thread is not permitted to - process instances. And before flush_instances() there is no instances - to proceed. - */ - - pthread_attr_init(&guardian_thd_attr); - pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED); - rc= set_stacksize_n_create_thread(&guardian_thd_id, &guardian_thd_attr, - guardian, &guardian_thread); - pthread_attr_destroy(&guardian_thd_attr); - if (rc) - { - log_error("manager(): set_stacksize_n_create_thread(guardian) failed"); - goto err; - } - + log_error("Can not start Guardian thread."); + goto err; } /* Load instances. */ @@ -276,40 +309,30 @@ void manager() if (flush_instances_status) { - log_error("Cannot init instances repository. This might be caused by " - "the wrong config file options. For instance, missing mysqld " - "binary. Aborting."); - stop_all(&guardian_thread, &thread_registry); + log_error("Can not init instances repository."); + stop_all_threads(); goto err; } } - /* create the listener */ + /* Initialize the Listener. */ + + if (listener.start(Thread::DETACHED)) { - pthread_t listener_thd_id; - pthread_attr_t listener_thd_attr; - int rc; - - pthread_attr_init(&listener_thd_attr); - pthread_attr_setdetachstate(&listener_thd_attr, PTHREAD_CREATE_DETACHED); - rc= set_stacksize_n_create_thread(&listener_thd_id, &listener_thd_attr, - listener, &listener_args); - pthread_attr_destroy(&listener_thd_attr); - if (rc) - { - log_error("manager(): set_stacksize_n_create_thread(listener) failed"); - stop_all(&guardian_thread, &thread_registry); - goto err; - } + log_error("Can not start Listener thread."); + stop_all_threads(); + goto err; } /* After the list of guarded instances have been initialized, Guardian should start them. */ - pthread_cond_signal(&guardian_thread.COND_guardian); + pthread_cond_signal(&guardian.COND_guardian); - log_info("Main loop: started."); + /* Main loop. */ + + log_info("Manager: started."); while (!shutdown_complete) { @@ -319,7 +342,7 @@ void manager() if ((status= my_sigwait(&mask, &signo)) != 0) { log_error("sigwait() failed"); - stop_all(&guardian_thread, &thread_registry); + stop_all_threads(); goto err; } @@ -328,8 +351,8 @@ void manager() - we are waiting for SIGINT, SIGTERM -- signals that mean we should shutdown; - as shutdown signal is caught, we stop Guardian thread (by calling - Guardian_thread::request_shutdown()); - - as Guardian_thread is stopped, it sends SIGTERM to this thread + Guardian::request_shutdown()); + - as Guardian is stopped, it sends SIGTERM to this thread (by calling Thread_registry::request_shutdown()), so that the my_sigwait() above returns; - as we catch the second SIGTERM, we send signals to all threads @@ -345,7 +368,7 @@ void manager() Bug #14164 IM tests fail on MacOS X (powermacg5) */ #ifdef IGNORE_SIGHUP_SIGQUIT - if ( SIGHUP == signo ) + if (SIGHUP == signo) continue; #endif if (THR_SERVER_ALARM == signo) @@ -353,12 +376,12 @@ void manager() else #endif { - log_info("Main loop: got shutdown signal."); + log_info("Manager: got shutdown signal."); - if (!guardian_thread.is_stopped()) + if (!guardian.is_stopped()) { - guardian_thread.request_shutdown(); - pthread_cond_signal(&guardian_thread.COND_guardian); + guardian.request_shutdown(); + pthread_cond_signal(&guardian.COND_guardian); } else { @@ -368,7 +391,9 @@ void manager() } } - log_info("Main loop: finished."); + log_info("Manager: finished."); + + rc= 0; err: /* delete the pid file */ @@ -379,4 +404,5 @@ err: end_thr_alarm(1); /* don't pthread_exit to kill all threads who did not shut down in time */ #endif + return rc; } diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h index 7aa4b3e1a96..a77809cca6d 100644 --- a/server-tools/instance-manager/manager.h +++ b/server-tools/instance-manager/manager.h @@ -16,8 +16,49 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -void manager(); +#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) +#pragma interface +#endif +#include <my_global.h> -int create_pid_file(const char *pid_file_name, int pid); +class Guardian; +class Instance_map; +class Thread_registry; +class User_map; + +class Manager +{ +public: + static int main(); + /** + These methods return a non-zero value only for the duration + of main(). + */ + static Instance_map *get_instance_map() { return p_instance_map; } + static Guardian *get_guardian() { return p_guardian; } + static Thread_registry *get_thread_registry() { return p_thread_registry; } + static User_map *get_user_map() { return p_user_map; } + +#ifndef __WIN__ + static bool is_linux_threads() { return linux_threads; } +#endif // __WIN__ + +private: + static void stop_all_threads(); + +private: + static Guardian *p_guardian; + static Instance_map *p_instance_map; + static Thread_registry *p_thread_registry; + static User_map *p_user_map; + +#ifndef __WIN__ + /* + This flag is set if Instance Manager is running on the system using + LinuxThreads. + */ + static bool linux_threads; +#endif // __WIN__ +}; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index 4ffee83ddc3..952bdcd0e73 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -23,7 +23,6 @@ #include <m_string.h> #include <m_string.h> #include <my_global.h> -#include <mysql_com.h> #include <mysql.h> #include <my_sys.h> #include <violite.h> @@ -40,66 +39,15 @@ #include "user_map.h" -Mysql_connection_thread_args::Mysql_connection_thread_args( - struct st_vio *vio_arg, - Thread_registry &thread_registry_arg, - const User_map &user_map_arg, - ulong connection_id_arg, - Instance_map &instance_map_arg) : - vio(vio_arg) - ,thread_registry(thread_registry_arg) - ,user_map(user_map_arg) - ,connection_id(connection_id_arg) - ,instance_map(instance_map_arg) - {} - -/* - MySQL connection - handle one connection with mysql command line client - See also comments in mysqlmanager.cc to picture general Instance Manager - architecture. - We use conventional technique to work with classes without exceptions: - class acquires all vital resource in init(); Thus if init() succeed, - a user must call cleanup(). All other methods are valid only between - init() and cleanup(). -*/ - -class Mysql_connection_thread: public Mysql_connection_thread_args -{ -public: - Mysql_connection_thread(const Mysql_connection_thread_args &args); - - int init(); - void cleanup(); - - void run(); - - ~Mysql_connection_thread(); -private: - Thread_info thread_info; - NET net; - struct rand_struct rand_st; - char scramble[SCRAMBLE_LENGTH + 1]; - uint status; - ulong client_capabilities; -private: - /* Names are conventionally the same as in mysqld */ - int check_connection(); - int do_command(); - int dispatch_command(enum enum_server_command command, - const char *text, uint len); -}; - - -Mysql_connection_thread::Mysql_connection_thread( - const Mysql_connection_thread_args &args) : - Mysql_connection_thread_args(args.vio, - args.thread_registry, - args.user_map, - args.connection_id, - args.instance_map) - ,thread_info(pthread_self(), TRUE) +Mysql_connection::Mysql_connection(Thread_registry *thread_registry_arg, + User_map *user_map_arg, + struct st_vio *vio_arg, ulong + connection_id_arg) + :vio(vio_arg), + connection_id(connection_id_arg), + thread_registry(thread_registry_arg), + user_map(user_map_arg) { - thread_registry.register_thread(&thread_info); } @@ -129,67 +77,73 @@ C_MODE_END This function is complementary to cleanup(). */ -int Mysql_connection_thread::init() +bool Mysql_connection::init() { /* Allocate buffers for network I/O */ if (my_net_init(&net, vio)) - return 1; + return TRUE; + net.return_status= &status; + /* Initialize random number generator */ { ulong seed1= (ulong) &rand_st + rand(); ulong seed2= (ulong) rand() + time(0); randominit(&rand_st, seed1, seed2); } + /* Fill scramble - server's random message used for handshake */ create_random_string(scramble, SCRAMBLE_LENGTH, &rand_st); + /* We don't support transactions, every query is atomic */ status= SERVER_STATUS_AUTOCOMMIT; - return 0; + + thread_registry->register_thread(&thread_info); + + return FALSE; } -void Mysql_connection_thread::cleanup() +void Mysql_connection::cleanup() { net_end(&net); + thread_registry->unregister_thread(&thread_info); } -Mysql_connection_thread::~Mysql_connection_thread() +Mysql_connection::~Mysql_connection() { /* vio_delete closes the socket if necessary */ vio_delete(vio); - thread_registry.unregister_thread(&thread_info); } -void Mysql_connection_thread::run() +void Mysql_connection::main() { - log_info("accepted mysql connection %lu", connection_id); - - my_thread_init(); + log_info("Connection %lu: accepted.", (unsigned long) connection_id); if (check_connection()) { - my_thread_end(); + log_info("Connection %lu: failed to authorize the user.", + (unsigned long) connection_id); + return; } - log_info("connection %lu is checked successfully", connection_id); + log_info("Connection %lu: the user was authorized successfully.", + (unsigned long) connection_id); vio_keepalive(vio, TRUE); - while (!net.error && net.vio && !thread_registry.is_shutdown()) + while (!net.error && net.vio && !thread_registry->is_shutdown()) { if (do_command()) break; } - - my_thread_end(); } -int Mysql_connection_thread::check_connection() +int Mysql_connection::check_connection() { ulong pkt_len=0; // to hold client reply length @@ -278,7 +232,7 @@ int Mysql_connection_thread::check_connection() net_send_error(&net, ER_ACCESS_DENIED_ERROR); return 1; } - if (user_map.authenticate(&user_name, password, scramble)) + if (user_map->authenticate(&user_name, password, scramble)) { net_send_error(&net, ER_ACCESS_DENIED_ERROR); return 1; @@ -288,7 +242,7 @@ int Mysql_connection_thread::check_connection() } -int Mysql_connection_thread::do_command() +int Mysql_connection::do_command() { char *packet; ulong packet_length; @@ -301,7 +255,7 @@ int Mysql_connection_thread::do_command() /* Check if we can continue without closing the connection */ if (net.error != 3) // what is 3 - find out return 1; - if (thread_registry.is_shutdown()) + if (thread_registry->is_shutdown()) return 1; net_send_error(&net, net.last_errno); net.error= 0; @@ -309,76 +263,101 @@ int Mysql_connection_thread::do_command() } else { - if (thread_registry.is_shutdown()) + if (thread_registry->is_shutdown()) return 1; packet= (char*) net.read_pos; enum enum_server_command command= (enum enum_server_command) (uchar) *packet; - log_info("connection: %lu packet_length: %lu command: %d", - connection_id, packet_length, command); - return dispatch_command(command, packet + 1, packet_length - 1); + log_info("Connection %lu: received packet (length: %lu; command: %d).", + (unsigned long) connection_id, + (unsigned long) packet_length, + (int) command); + + return dispatch_command(command, packet + 1); } } -int Mysql_connection_thread::dispatch_command(enum enum_server_command command, - const char *packet, uint len) +int Mysql_connection::dispatch_command(enum enum_server_command command, + const char *packet) { switch (command) { case COM_QUIT: // client exit - log_info("query for connection %lu received quit command", connection_id); + log_info("Connection %lu: received QUIT command.", + (unsigned long) connection_id); return 1; + case COM_PING: - log_info("query for connection %lu received ping command", connection_id); + log_info("Connection %lu: received PING command.", + (unsigned long) connection_id); net_send_ok(&net, connection_id, NULL); - break; + return 0; + case COM_QUERY: { - log_info("query for connection %lu : ----\n%s\n-------------------------", - connection_id,packet); - if (Command *command= parse_command(&instance_map, packet)) + log_info("Connection %lu: received QUERY command: '%s'.", + (unsigned long) connection_id, + (const char *) packet); + + if (Command *command= parse_command(packet)) { int res= 0; - log_info("query for connection %lu successefully parsed",connection_id); + + log_info("Connection %lu: query parsed successfully.", + (unsigned long) connection_id); + res= command->execute(&net, connection_id); delete command; + if (!res) - log_info("query for connection %lu executed ok",connection_id); + { + log_info("Connection %lu: query executed successfully", + (unsigned long) connection_id); + } else { - log_info("query for connection %lu executed err=%d",connection_id,res); + log_info("Connection %lu: can not execute query (error: %d).", + (unsigned long) connection_id, + (int) res); + net_send_error(&net, res); - return 0; } } else { + log_error("Connection %lu: can not parse query: out ot resources.", + (unsigned long) connection_id); + net_send_error(&net,ER_OUT_OF_RESOURCES); - return 0; } - break; + + return 0; } + default: - log_info("query for connection %lu received unknown command",connection_id); + log_info("Connection %lu: received unsupported command (%d).", + (unsigned long) connection_id, + (int) command); + net_send_error(&net, ER_UNKNOWN_COM_ERROR); - break; + return 0; } - return 0; + + return 0; /* Just to make compiler happy. */ } -pthread_handler_t mysql_connection(void *arg) +void Mysql_connection::run() { - Mysql_connection_thread_args *args= (Mysql_connection_thread_args *) arg; - Mysql_connection_thread mysql_connection_thread(*args); - delete args; - if (mysql_connection_thread.init()) - log_info("mysql_connection(): error initializing thread"); + if (init()) + log_error("Connection %lu: can not init handler.", + (unsigned long) connection_id); else { - mysql_connection_thread.run(); - mysql_connection_thread.cleanup(); + main(); + cleanup(); } - return 0; + + delete this; } /* diff --git a/server-tools/instance-manager/mysql_connection.h b/server-tools/instance-manager/mysql_connection.h index 3496cc05815..ef58d921637 100644 --- a/server-tools/instance-manager/mysql_connection.h +++ b/server-tools/instance-manager/mysql_connection.h @@ -16,33 +16,59 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <my_global.h> -#include <my_pthread.h> +#include "thread_registry.h" +#include <mysql_com.h> #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #pragma interface #endif -pthread_handler_t mysql_connection(void *arg); - -class Thread_registry; -class User_map; -class Instance_map; struct st_vio; +class User_map; -struct Mysql_connection_thread_args +/* + MySQL connection - handle one connection with mysql command line client + See also comments in mysqlmanager.cc to picture general Instance Manager + architecture. + We use conventional technique to work with classes without exceptions: + class acquires all vital resource in init(); Thus if init() succeed, + a user must call cleanup(). All other methods are valid only between + init() and cleanup(). +*/ + +class Mysql_connection: public Thread { +public: + Mysql_connection(Thread_registry *thread_registry_arg, + User_map *user_map_arg, + struct st_vio *vio_arg, + ulong connection_id_arg); + virtual ~Mysql_connection(); + +protected: + virtual void run(); + +private: struct st_vio *vio; - Thread_registry &thread_registry; - const User_map &user_map; ulong connection_id; - Instance_map &instance_map; + Thread_info thread_info; + Thread_registry *thread_registry; + User_map *user_map; + NET net; + struct rand_struct rand_st; + char scramble[SCRAMBLE_LENGTH + 1]; + uint status; + ulong client_capabilities; +private: + /* The main loop implementation triad */ + bool init(); + void main(); + void cleanup(); - Mysql_connection_thread_args(struct st_vio *vio_arg, - Thread_registry &thread_registry_arg, - const User_map &user_map_arg, - ulong connection_id_arg, - Instance_map &instance_map_arg); + /* Names are conventionally the same as in mysqld */ + int check_connection(); + int do_command(); + int dispatch_command(enum enum_server_command command, const char *text); }; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index 177b761b419..8ee8321fffc 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -31,6 +31,7 @@ #include "log.h" #include "manager.h" #include "options.h" +#include "priv.h" #include "user_management_commands.h" #ifdef __WIN__ @@ -117,7 +118,7 @@ int main(int argc, char *argv[]) angel(); } - manager(); + (void) Manager::main(); /* ignore the return value for now */ #else @@ -131,7 +132,7 @@ int main(int argc, char *argv[]) } else { - manager(); + (void) Manager::main(); /* ignore the return value for now */ } #endif @@ -190,7 +191,8 @@ static struct passwd *check_user(const char *user) return user_info; err: - log_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n", user); + log_error("Can not start under user '%s'.", + (const char *) user); return NULL; } @@ -231,7 +233,7 @@ static void init_environment(char *progname) #ifndef __WIN__ /* Become a UNIX service - SYNOPSYS + SYNOPSIS daemonize() */ @@ -384,11 +386,10 @@ spawn: } /* mysqlmanager successfully exited, let's silently evaporate - If we return to main we fall into the manager() function, so let's - simply exit(). + If we return to main we will fall into the manager functionality, + so let's simply exit(). */ exit(0); } } - #endif diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc index 048f7983b32..e81320584ea 100644 --- a/server-tools/instance-manager/parse.cc +++ b/server-tools/instance-manager/parse.cc @@ -269,12 +269,11 @@ void skip_spaces(const char **text) } -Command *parse_command(Instance_map *map, const char *text) +Command *parse_command(const char *text) { uint word_len; LEX_STRING instance_name; Command *command= 0; - const char *saved_text= text; Token tok1= shift_token(&text, &word_len); @@ -294,7 +293,7 @@ Command *parse_command(Instance_map *map, const char *text) if (tok1 == TOK_CREATE) { - Create_instance *cmd= new Create_instance(map, &instance_name); + Create_instance *cmd= new Create_instance(&instance_name); if (!cmd) return NULL; /* Report ER_OUT_OF_RESOURCES. */ @@ -317,16 +316,16 @@ Command *parse_command(Instance_map *map, const char *text) switch (tok1) { case TOK_START: - command= new Start_instance(map, &instance_name); + command= new Start_instance(&instance_name); break; case TOK_STOP: - command= new Stop_instance(map, &instance_name); + command= new Stop_instance(&instance_name); break; case TOK_CREATE: ; /* command already initialized. */ break; case TOK_DROP: - command= new Drop_instance(map, &instance_name); + command= new Drop_instance(&instance_name); break; default: /* this is impossible, but nevertheless... */ DBUG_ASSERT(0); @@ -340,7 +339,7 @@ Command *parse_command(Instance_map *map, const char *text) if (word_len) goto syntax_error; - command= new Flush_instances(map); + command= new Flush_instances(); break; case TOK_UNSET: case TOK_SET: @@ -348,9 +347,9 @@ Command *parse_command(Instance_map *map, const char *text) Abstract_option_cmd *cmd; if (tok1 == TOK_SET) - cmd= new Set_option(map); + cmd= new Set_option(); else - cmd= new Unset_option(map); + cmd= new Unset_option(); if (!cmd) return NULL; /* Report ER_OUT_OF_RESOURCES. */ @@ -371,7 +370,7 @@ Command *parse_command(Instance_map *map, const char *text) get_word(&text, &word_len, NONSPACE); if (word_len) goto syntax_error; - command= new Show_instances(map); + command= new Show_instances(); break; case TOK_INSTANCE: switch (Token tok2= shift_token(&text, &word_len)) { @@ -385,9 +384,9 @@ Command *parse_command(Instance_map *map, const char *text) if (word_len) goto syntax_error; if (tok2 == TOK_STATUS) - command= new Show_instance_status(map, &instance_name); + command= new Show_instance_status(&instance_name); else - command= new Show_instance_options(map, &instance_name); + command= new Show_instance_options(&instance_name); break; default: goto syntax_error; @@ -414,7 +413,7 @@ Command *parse_command(Instance_map *map, const char *text) /* check that this is the end of the command */ if (word_len) goto syntax_error; - command= new Show_instance_log_files(map, &instance_name); + command= new Show_instance_log_files(&instance_name); break; case TOK_ERROR: case TOK_GENERAL: @@ -484,7 +483,7 @@ Command *parse_command(Instance_map *map, const char *text) goto syntax_error; } - command= new Show_instance_log(map, &instance_name, + command= new Show_instance_log(&instance_name, log_type, log_size, log_offset); break; default: @@ -504,5 +503,8 @@ Command *parse_command(Instance_map *map, const char *text) syntax_error: command= new Syntax_error(); } + + DBUG_ASSERT(command); + return command; } diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h index fd970f54d29..7e954b4918f 100644 --- a/server-tools/instance-manager/parse.h +++ b/server-tools/instance-manager/parse.h @@ -21,7 +21,6 @@ #include <m_string.h> class Command; -class Instance_map; enum Log_type { @@ -30,7 +29,7 @@ enum Log_type IM_LOG_SLOW }; -Command *parse_command(Instance_map *map, const char *text); +Command *parse_command(const char *text); bool parse_option_value(const char *text, uint *text_len, char **value); diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 643a50625a1..9213de82e1d 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -43,7 +43,7 @@ void trim_space(const char **text, uint *word_len) /* Parse output of the given command - SYNOPSYS + SYNOPSIS parse_output_and_get_value() command the command to execue with popen. diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc index 3dae900d84b..7695b585169 100644 --- a/server-tools/instance-manager/priv.cc +++ b/server-tools/instance-manager/priv.cc @@ -18,26 +18,9 @@ #include <my_global.h> #include <mysql_com.h> +#include <my_sys.h> -#if defined(__ia64__) || defined(__ia64) -/* - We can live with 32K, but reserve 64K. Just to be safe. - On ia64 we need to reserve double of the size. -*/ -#define IM_THREAD_STACK_SIZE (128*1024L) -#else -#define IM_THREAD_STACK_SIZE (64*1024) -#endif - - -/* the pid of the manager process (of the signal thread on the LinuxThreads) */ -pid_t manager_pid; - -/* - This flag is set if mysqlmanager has detected that it is running on the - system using LinuxThreads -*/ -bool linuxthreads; +#include "log.h" /* The following string must be less then 80 characters, as @@ -63,30 +46,32 @@ unsigned long bytes_sent = 0L, bytes_received = 0L; unsigned long mysqld_net_retry_count = 10L; unsigned long open_files_limit; -/* - Change the stack size and start a thread. Return an error if either - pthread_attr_setstacksize or pthread_create fails. - Arguments are the same as for pthread_create(). -*/ -int set_stacksize_n_create_thread(pthread_t *thread, pthread_attr_t *attr, - void *(*start_routine)(void *), void *arg) + +int create_pid_file(const char *pid_file_name, int pid) { - int rc= 0; - -#ifndef __WIN__ -#ifndef PTHREAD_STACK_MIN -#define PTHREAD_STACK_MIN 32768 -#endif - /* - Set stack size to be safe on the platforms with too small - default thread stack. - */ - rc= pthread_attr_setstacksize(attr, - (size_t) (PTHREAD_STACK_MIN + - IM_THREAD_STACK_SIZE)); -#endif - if (!rc) - rc= pthread_create(thread, attr, start_routine, arg); - return rc; + FILE *pid_file; + + if (!(pid_file= my_fopen(pid_file_name, O_WRONLY | O_CREAT | O_BINARY, + MYF(0)))) + { + log_error("Can not create pid file '%s': %s (errno: %d)", + (const char *) pid_file_name, + (const char *) strerror(errno), + (int) errno); + return 1; + } + + if (fprintf(pid_file, "%d\n", (int) pid) <= 0) + { + log_error("Can not write to pid file '%s': %s (errno: %d)", + (const char *) pid_file_name, + (const char *) strerror(errno), + (int) errno); + return 1; + } + + my_fclose(pid_file, MYF(0)); + + return 0; } diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 0b393c17ac2..4d434213781 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -50,17 +50,6 @@ const int MAX_VERSION_LENGTH= 160; const int MAX_INSTANCE_NAME_SIZE= FN_REFLEN; -/* the pid of the manager process (of the signal thread on the LinuxThreads) */ -extern pid_t manager_pid; - -#ifndef __WIN__ -/* - This flag is set if mysqlmanager has detected that it is running on the - system using LinuxThreads -*/ -extern bool linuxthreads; -#endif - extern const LEX_STRING mysqlmanager_version; /* MySQL client-server protocol version: substituted from configure */ @@ -105,8 +94,6 @@ extern unsigned long bytes_sent, bytes_received; extern unsigned long mysqld_net_retry_count; extern unsigned long open_files_limit; - -int set_stacksize_n_create_thread(pthread_t *thread, pthread_attr_t *attr, - void *(*start_routine)(void *), void *arg); +int create_pid_file(const char *pid_file_name, int pid); #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc index 10370e0981e..5cbf1eb044e 100644 --- a/server-tools/instance-manager/thread_registry.cc +++ b/server-tools/instance-manager/thread_registry.cc @@ -25,8 +25,6 @@ #include <signal.h> -#include "log.h" - #ifndef __WIN__ /* Kick-off signal handler */ @@ -38,15 +36,13 @@ static void handle_signal(int __attribute__((unused)) sig_no) } #endif -/* - Thread_info initializer methods -*/ +/* Thread_info initializer methods */ -Thread_info::Thread_info() {} -Thread_info::Thread_info(pthread_t thread_id_arg, - bool send_signal_on_shutdown_arg) : - thread_id(thread_id_arg), - send_signal_on_shutdown(send_signal_on_shutdown_arg) {} +void Thread_info::init(bool send_signal_on_shutdown_arg) +{ + thread_id= pthread_self(); + send_signal_on_shutdown= send_signal_on_shutdown_arg; +} /* TODO: think about moving signal information (now it's shutdown_in_progress) @@ -86,10 +82,13 @@ Thread_registry::~Thread_registry() points to the last node. */ -void Thread_registry::register_thread(Thread_info *info) +void Thread_registry::register_thread(Thread_info *info, + bool send_signal_on_shutdown) { - log_info("Thread_registry: registering thread %d...", - (int) info->thread_id); + info->init(send_signal_on_shutdown); + + DBUG_PRINT("info", ("Thread_registry: registering thread %lu...", + (unsigned long) info->thread_id)); #ifndef __WIN__ struct sigaction sa; @@ -117,8 +116,8 @@ void Thread_registry::register_thread(Thread_info *info) void Thread_registry::unregister_thread(Thread_info *info) { - log_info("Thread_registry: unregistering thread %d...", - (int) info->thread_id); + DBUG_PRINT("info", ("Thread_registry: unregistering thread %lu...", + (unsigned long) info->thread_id)); pthread_mutex_lock(&LOCK_thread_registry); info->prev->next= info->next; @@ -126,7 +125,7 @@ void Thread_registry::unregister_thread(Thread_info *info) if (head.next == &head) { - log_info("Thread_registry: thread registry is empty!"); + DBUG_PRINT("info", ("Thread_registry: thread registry is empty!")); pthread_cond_signal(&COND_thread_registry_is_empty); } @@ -230,6 +229,7 @@ void Thread_registry::deliver_shutdown() wait_for_threads_to_unregister(); +#ifndef DBUG_OFF /* Print out threads, that didn't stopped. Thread_registry destructor will probably abort the program if there is still any alive thread. @@ -237,15 +237,16 @@ void Thread_registry::deliver_shutdown() if (head.next != &head) { - log_info("Thread_registry: non-stopped threads:"); + DBUG_PRINT("info", ("Thread_registry: non-stopped threads:")); for (Thread_info *info= head.next; info != &head; info= info->next) - log_info(" - %ld", (long int) info->thread_id); + DBUG_PRINT("info", (" - %lu", (unsigned long) info->thread_id)); } else { - log_info("Thread_registry: all threads stopped."); + DBUG_PRINT("info", ("Thread_registry: all threads stopped.")); } +#endif // DBUG_OFF pthread_mutex_unlock(&LOCK_thread_registry); } @@ -277,13 +278,13 @@ void Thread_registry::wait_for_threads_to_unregister() set_timespec(shutdown_time, 1); - log_info("Thread_registry: joining threads..."); + DBUG_PRINT("info", ("Thread_registry: joining threads...")); while (true) { if (head.next == &head) { - log_info("Thread_registry: emptied."); + DBUG_PRINT("info", ("Thread_registry: emptied.")); return; } @@ -293,8 +294,101 @@ void Thread_registry::wait_for_threads_to_unregister() if (error == ETIMEDOUT || error == ETIME) { - log_info("Thread_registry: threads shutdown timed out."); + DBUG_PRINT("info", ("Thread_registry: threads shutdown timed out.")); return; } } } + + +/********************************************************************* + class Thread +*********************************************************************/ + +#if defined(__ia64__) || defined(__ia64) +/* + We can live with 32K, but reserve 64K. Just to be safe. + On ia64 we need to reserve double of the size. +*/ +#define IM_THREAD_STACK_SIZE (128*1024L) +#else +#define IM_THREAD_STACK_SIZE (64*1024) +#endif + +/* + Change the stack size and start a thread. Return an error if either + pthread_attr_setstacksize or pthread_create fails. + Arguments are the same as for pthread_create(). +*/ + +static +int set_stacksize_and_create_thread(pthread_t *thread, pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) +{ + int rc= 0; + +#ifndef __WIN__ +#ifndef PTHREAD_STACK_MIN +#define PTHREAD_STACK_MIN 32768 +#endif + /* + Set stack size to be safe on the platforms with too small + default thread stack. + */ + rc= pthread_attr_setstacksize(attr, + (size_t) (PTHREAD_STACK_MIN + + IM_THREAD_STACK_SIZE)); +#endif + if (!rc) + rc= pthread_create(thread, attr, start_routine, arg); + return rc; +} + + +Thread::~Thread() +{ +} + + +void *Thread::thread_func(void *arg) +{ + Thread *thread= (Thread *) arg; + my_thread_init(); + + thread->run(); + + my_thread_end(); + return NULL; +} + + +bool Thread::start(enum_thread_type thread_type) +{ + pthread_attr_t attr; + int rc; + + pthread_attr_init(&attr); + + if (thread_type == DETACHED) + { + detached = TRUE; + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + else + { + detached = FALSE; + } + + rc= set_stacksize_and_create_thread(&id, &attr, Thread::thread_func, this); + pthread_attr_destroy(&attr); + + return rc != 0; +} + + +bool Thread::join() +{ + DBUG_ASSERT(!detached); + + return pthread_join(id, NULL) != 0; +} diff --git a/server-tools/instance-manager/thread_registry.h b/server-tools/instance-manager/thread_registry.h index 503d24e5fb0..b9ece271c21 100644 --- a/server-tools/instance-manager/thread_registry.h +++ b/server-tools/instance-manager/thread_registry.h @@ -57,7 +57,7 @@ #pragma interface #endif -/* +/** Thread_info - repository entry for each worker thread All entries comprise double-linked list like: 0 -- entry -- entry -- entry - 0 @@ -67,12 +67,10 @@ class Thread_info { public: - Thread_info(pthread_t thread_id_arg, bool send_signal_on_shutdown_arg); + Thread_info() {} friend class Thread_registry; - private: - Thread_info(); - + void init(bool send_signal_on_shutdown); private: pthread_cond_t *current_cond; Thread_info *prev, *next; @@ -81,7 +79,51 @@ private: }; -/* +/** + A base class for a detached thread. +*/ + +class Thread +{ +public: + enum enum_thread_type + { + DETACHED, + JOINABLE + }; +public: + Thread() + { } + +public: + inline bool is_detached() const; + + bool start(enum_thread_type thread_type = JOINABLE); + bool join(); + +protected: + virtual void run()= 0; + virtual ~Thread(); + +private: + pthread_t id; + bool detached; + +private: + static void *thread_func(void *arg); + +private: + Thread(const Thread & /* rhs */); /* not implemented */ + Thread &operator=(const Thread & /* rhs */); /* not implemented */ +}; + +inline bool Thread::is_detached() const +{ + return detached; +} + + +/** Thread_registry - contains handles for each worker thread to deliver signal information to workers. */ @@ -92,7 +134,7 @@ public: Thread_registry(); ~Thread_registry(); - void register_thread(Thread_info *info); + void register_thread(Thread_info *info, bool send_signal_on_shutdown= TRUE); void unregister_thread(Thread_info *info); void deliver_shutdown(); void request_shutdown(); @@ -101,6 +143,7 @@ public: pthread_mutex_t *mutex); int cond_timedwait(Thread_info *info, pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *wait_time); + private: void interrupt_threads(); void wait_for_threads_to_unregister(); @@ -111,6 +154,10 @@ private: pthread_mutex_t LOCK_thread_registry; pthread_cond_t COND_thread_registry_is_empty; pthread_t sigwait_thread_pid; + +private: + Thread_registry(const Thread_registry &); + Thread_registry &operator =(const Thread_registry &); }; diff --git a/server-tools/instance-manager/user_management_commands.cc b/server-tools/instance-manager/user_management_commands.cc index 20ebeb0d6bf..a32a4e94415 100644 --- a/server-tools/instance-manager/user_management_commands.cc +++ b/server-tools/instance-manager/user_management_commands.cc @@ -20,7 +20,7 @@ This function must not be used in user-management command implementations. Use get_user_name() instead. - SYNOPSYS + SYNOPSIS get_user_name_impl() RETURN @@ -58,7 +58,7 @@ static char *get_user_name_impl() (not empty, not exceeds USERNAME_LENGTH). Report to stderr if something is wrong. - SYNOPSYS + SYNOPSIS get_user_name() user_name [OUT] on success contains user name @@ -102,7 +102,7 @@ static bool get_user_name(LEX_STRING *user_name) The password is retrieved from command-line options (if specified) or from console. - SYNOPSYS + SYNOPSIS get_password() RETURN @@ -131,7 +131,7 @@ static const char *get_password() /* Load password file into user map. - SYNOPSYS + SYNOPSIS load_password_file() user_map target user map @@ -160,7 +160,7 @@ static int load_password_file(User_map *user_map) /* Save user map into password file. - SYNOPSYS + SYNOPSIS save_password_file() user_map user map diff --git a/server-tools/instance-manager/user_management_commands.h b/server-tools/instance-manager/user_management_commands.h index 8d820be5ec7..17c11ccfef0 100644 --- a/server-tools/instance-manager/user_management_commands.h +++ b/server-tools/instance-manager/user_management_commands.h @@ -49,7 +49,7 @@ public: /* Executes user-management command. - SYNOPSYS + SYNOPSIS execute() RETURN @@ -65,7 +65,7 @@ public: option. *************************************************************************/ -class Print_password_line_cmd : public User_management_cmd +class Print_password_line_cmd: public User_management_cmd { public: Print_password_line_cmd() @@ -80,7 +80,7 @@ public: Add_user_cmd: support for --add-user command-line option. *************************************************************************/ -class Add_user_cmd : public User_management_cmd +class Add_user_cmd: public User_management_cmd { public: Add_user_cmd() @@ -95,7 +95,7 @@ public: Drop_user_cmd: support for --drop-user command-line option. *************************************************************************/ -class Drop_user_cmd : public User_management_cmd +class Drop_user_cmd: public User_management_cmd { public: Drop_user_cmd() @@ -110,7 +110,7 @@ public: Edit_user_cmd: support for --edit-user command-line option. *************************************************************************/ -class Edit_user_cmd : public User_management_cmd +class Edit_user_cmd: public User_management_cmd { public: Edit_user_cmd() @@ -125,7 +125,7 @@ public: Clean_db_cmd: support for --clean-db command-line option. *************************************************************************/ -class Clean_db_cmd : public User_management_cmd +class Clean_db_cmd: public User_management_cmd { public: Clean_db_cmd() @@ -140,7 +140,7 @@ public: Check_db_cmd: support for --check-db command-line option. *************************************************************************/ -class Check_db_cmd : public User_management_cmd +class Check_db_cmd: public User_management_cmd { public: Check_db_cmd() @@ -155,7 +155,7 @@ public: List_users_cmd: support for --list-users command-line option. *************************************************************************/ -class List_users_cmd : public User_management_cmd +class List_users_cmd: public User_management_cmd { public: List_users_cmd() diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index c1439e983d8..f3a6e3cd76c 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -41,8 +41,8 @@ int User::init(const char *line) name_end= strchr(name_begin, line[0]); if (name_end == 0 || name_end[1] != ':') { - log_info("Error: invalid format (unmatched quote) of user line (%s).", - (const char *) line); + log_error("Invalid format (unmatched quote) of user line (%s).", + (const char *) line); return 1; } password= name_end + 2; @@ -53,8 +53,8 @@ int User::init(const char *line) name_end= strchr(name_begin, ':'); if (name_end == 0) { - log_info("Error: invalid format (no delimiter) of user line (%s).", - (const char *) line); + log_error("Invalid format (no delimiter) of user line (%s).", + (const char *) line); return 1; } password= name_end + 1; @@ -63,18 +63,19 @@ int User::init(const char *line) user_length= name_end - name_begin; if (user_length > USERNAME_LENGTH) { - log_info("Error: user name is too long (%d). Max length: %d. " - "User line: '%s'.", - (int) user_length, - (int) USERNAME_LENGTH, - (const char *) line); + log_error("User name is too long (%d). Max length: %d. " + "User line: '%s'.", + (int) user_length, + (int) USERNAME_LENGTH, + (const char *) line); return 1; } password_length= strlen(password); if (password_length > SCRAMBLED_PASSWORD_CHAR_LENGTH) { - log_info("Error: password is too long (%d). Max length: %d. User line: '%s'.", + log_error("Password is too long (%d). Max length: %d." + "User line: '%s'.", (int) password_length, (int) SCRAMBLED_PASSWORD_CHAR_LENGTH, line); @@ -89,7 +90,7 @@ int User::init(const char *line) get_salt_from_password(salt, password); - log_info("loaded user '%s'.", user); + log_info("Loaded user '%s'.", (const char *) user); return 0; } @@ -158,7 +159,7 @@ User_map::~User_map() /* Load password database. - SYNOPSYS + SYNOPSIS load() password_file_name [IN] password file path err_msg [OUT] error message @@ -185,7 +186,6 @@ int User_map::load(const char *password_file_name, const char **err_msg) 2 + /* for newline */ 1]; /* for trailing zero */ User *user; - int rc= 1; if (my_access(password_file_name, F_OK) != 0) { @@ -214,7 +214,7 @@ int User_map::load(const char *password_file_name, const char **err_msg) return ERR_IO_ERROR; } - log_info("loading the password database..."); + log_info("Loading the password database..."); while (fgets(line, sizeof(line), file)) { @@ -292,7 +292,7 @@ int User_map::load(const char *password_file_name, const char **err_msg) } } - log_info("the password database loaded successfully."); + log_info("The password database loaded successfully."); my_fclose(file, MYF(0)); diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h index de207c11e65..5325af12058 100644 --- a/server-tools/instance-manager/user_map.h +++ b/server-tools/instance-manager/user_map.h @@ -60,7 +60,7 @@ public: { public: Iterator(User_map *user_map_arg) : - cur_idx(0), user_map(user_map_arg) + user_map(user_map_arg), cur_idx(0) { } public: diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 39c63e9a46a..b1b73a523be 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2551,7 +2551,6 @@ void Item_func_in::fix_length_and_dec() } } } - maybe_null= args[0]->maybe_null; max_length= 1; } diff --git a/sql/item_create.cc b/sql/item_create.cc index 7722ce28d4a..da8954910c8 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -2612,15 +2612,8 @@ Create_func_benchmark Create_func_benchmark::s_singleton; Item* Create_func_benchmark::create(THD *thd, Item *arg1, Item *arg2) { - /* TODO: Known limitation, see Bug#22684 */ - if ((arg1->type() != Item::INT_ITEM) || ! arg1->basic_const_item()) - { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), "BENCHMARK"); - return NULL; - } - thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return new (thd->mem_root) Item_func_benchmark(arg1->val_int(), arg2); + return new (thd->mem_root) Item_func_benchmark(arg1, arg2); } @@ -2887,17 +2880,7 @@ Create_func_decode Create_func_decode::s_singleton; Item* Create_func_decode::create(THD *thd, Item *arg1, Item *arg2) { - /* TODO: Known limitation, see Bug#22684 */ - if ((arg2->type() != Item::STRING_ITEM) || ! arg2->basic_const_item()) - { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), "DECODE"); - return NULL; - } - - String dummy; - String *val = arg2->val_str(& dummy); - DBUG_ASSERT(val); - return new (thd->mem_root) Item_func_decode(arg1, val->c_ptr()); + return new (thd->mem_root) Item_func_decode(arg1, arg2); } @@ -3033,17 +3016,7 @@ Create_func_encode Create_func_encode::s_singleton; Item* Create_func_encode::create(THD *thd, Item *arg1, Item *arg2) { - /* TODO: Known limitation, see Bug#22684 */ - if ((arg2->type() != Item::STRING_ITEM) || ! arg2->basic_const_item()) - { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), "ENCODE"); - return NULL; - } - - String dummy; - String *val = arg2->val_str(& dummy); - DBUG_ASSERT(val); - return new (thd->mem_root) Item_func_encode(arg1, val->c_ptr()); + return new (thd->mem_root) Item_func_encode(arg1, arg2); } @@ -3235,14 +3208,7 @@ Create_func_format Create_func_format::s_singleton; Item* Create_func_format::create(THD *thd, Item *arg1, Item *arg2) { - /* TODO: Known limitation, see Bug#22684 */ - if ((arg2->type() != Item::INT_ITEM) || ! arg2->basic_const_item()) - { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), "FORMAT"); - return NULL; - } - - return new (thd->mem_root) Item_func_format(arg1, arg2->val_int()); + return new (thd->mem_root) Item_func_format(arg1, arg2); } diff --git a/sql/item_func.cc b/sql/item_func.cc index 0cdf8c5a561..e658a70d7cb 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3424,18 +3424,28 @@ longlong Item_func_benchmark::val_int() char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff), &my_charset_bin); THD *thd=current_thd; + ulong loop_count; + loop_count= args[0]->val_int(); + + if (args[0]->null_value) + { + null_value= 1; + return 0; + } + + null_value=0; for (ulong loop=0 ; loop < loop_count && !thd->killed; loop++) { - switch (args[0]->result_type()) { + switch (args[1]->result_type()) { case REAL_RESULT: - (void) args[0]->val_real(); + (void) args[1]->val_real(); break; case INT_RESULT: - (void) args[0]->val_int(); + (void) args[1]->val_int(); break; case STRING_RESULT: - (void) args[0]->val_str(&tmp); + (void) args[1]->val_str(&tmp); break; case ROW_RESULT: default: @@ -3451,13 +3461,9 @@ longlong Item_func_benchmark::val_int() void Item_func_benchmark::print(String *str) { str->append(STRING_WITH_LEN("benchmark(")); - char buffer[20]; - // my_charset_bin is good enough for numbers - String st(buffer, sizeof(buffer), &my_charset_bin); - st.set((ulonglong)loop_count, &my_charset_bin); - str->append(st); - str->append(','); args[0]->print(str); + str->append(','); + args[1]->print(str); str->append(')'); } @@ -4873,6 +4879,7 @@ Item_func_sp::cleanup() result_field= NULL; } m_sp= NULL; + dummy_table->alias= NULL; Item_func::cleanup(); } diff --git a/sql/item_func.h b/sql/item_func.h index 903179d48f5..5dca1705f9a 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -925,10 +925,9 @@ public: class Item_func_benchmark :public Item_int_func { - ulong loop_count; public: - Item_func_benchmark(ulong loop_count_arg,Item *expr) - :Item_int_func(expr), loop_count(loop_count_arg) + Item_func_benchmark(Item *count_expr, Item *expr) + :Item_int_func(count_expr, expr) {} longlong val_int(); const char *func_name() const { return "benchmark"; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 2a022d4af71..32b283fca57 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -80,6 +80,20 @@ String *Item_str_func::check_well_formed_result(String *str) } +bool Item_str_func::fix_fields(THD *thd, Item **ref) +{ + bool res= Item_func::fix_fields(thd, ref); + /* + In Item_str_func::check_well_formed_result() we may set null_value + flag on the same condition as in test() below. + */ + maybe_null= (maybe_null || + test(thd->variables.sql_mode & + (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES))); + return res; +} + + my_decimal *Item_str_func::val_decimal(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); @@ -1659,21 +1673,33 @@ String *Item_func_encrypt::val_str(String *str) void Item_func_encode::fix_length_and_dec() { max_length=args[0]->max_length; - maybe_null=args[0]->maybe_null; + maybe_null=args[0]->maybe_null || args[1]->maybe_null; collation.set(&my_charset_bin); } String *Item_func_encode::val_str(String *str) { - DBUG_ASSERT(fixed == 1); String *res; + char pw_buff[80]; + String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info); + String *password; + DBUG_ASSERT(fixed == 1); + if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } + + if (!(password=args[1]->val_str(& tmp_pw_value))) + { + null_value=1; + return 0; + } + null_value=0; res=copy_if_not_alloced(str,res,res->length()); + SQL_CRYPT sql_crypt(password->ptr()); sql_crypt.init(); sql_crypt.encode((char*) res->ptr(),res->length()); res->set_charset(&my_charset_bin); @@ -1682,15 +1708,27 @@ String *Item_func_encode::val_str(String *str) String *Item_func_decode::val_str(String *str) { - DBUG_ASSERT(fixed == 1); String *res; + char pw_buff[80]; + String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info); + String *password; + DBUG_ASSERT(fixed == 1); + if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } + + if (!(password=args[1]->val_str(& tmp_pw_value))) + { + null_value=1; + return 0; + } + null_value=0; res=copy_if_not_alloced(str,res,res->length()); + SQL_CRYPT sql_crypt(password->ptr()); sql_crypt.init(); sql_crypt.decode((char*) res->ptr(),res->length()); return res; @@ -1860,9 +1898,19 @@ String *Item_func_soundex::val_str(String *str) ** This should be 'internationalized' sometimes. */ -Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org) +const int FORMAT_MAX_DECIMALS= 30; + +Item_func_format::Item_func_format(Item *org, Item *dec) +: Item_str_func(org, dec) +{ +} + +void Item_func_format::fix_length_and_dec() { - decimals=(uint) set_zone(dec,0,30); + collation.set(default_charset()); + uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; + max_length= ((char_length + (char_length-args[0]->decimals)/3) * + collation.collation->mbmaxlen); } @@ -1873,10 +1921,25 @@ Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org) String *Item_func_format::val_str(String *str) { - uint32 length, str_length ,dec; + uint32 length; + uint32 str_length; + /* Number of decimal digits */ + int dec; + /* Number of characters used to represent the decimals, including '.' */ + uint32 dec_length; int diff; DBUG_ASSERT(fixed == 1); - dec= decimals ? decimals+1 : 0; + + dec= args[1]->val_int(); + if (args[1]->null_value) + { + null_value=1; + return NULL; + } + + dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS); + dec_length= dec ? dec+1 : 0; + null_value=0; if (args[0]->result_type() == DECIMAL_RESULT || args[0]->result_type() == INT_RESULT) @@ -1885,7 +1948,7 @@ String *Item_func_format::val_str(String *str) res= args[0]->val_decimal(&dec_val); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ - my_decimal_round(E_DEC_FATAL_ERROR, res, decimals, false, &rnd_dec); + my_decimal_round(E_DEC_FATAL_ERROR, res, dec, false, &rnd_dec); my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str); str_length= str->length(); if (rnd_dec.sign()) @@ -1896,9 +1959,9 @@ String *Item_func_format::val_str(String *str) double nr= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ - nr= my_double_round(nr, decimals, FALSE); + nr= my_double_round(nr, dec, FALSE); /* Here default_charset() is right as this is not an automatic conversion */ - str->set_real(nr,decimals, default_charset()); + str->set_real(nr, dec, default_charset()); if (isnan(nr)) return str; str_length=str->length(); @@ -1906,13 +1969,13 @@ String *Item_func_format::val_str(String *str) str_length--; // Don't count sign } /* We need this test to handle 'nan' values */ - if (str_length >= dec+4) + if (str_length >= dec_length+4) { char *tmp,*pos; - length= str->length()+(diff=((int)(str_length- dec-1))/3); + length= str->length()+(diff=((int)(str_length- dec_length-1))/3); str= copy_if_not_alloced(&tmp_str,str,length); str->length(length); - tmp= (char*) str->ptr()+length - dec-1; + tmp= (char*) str->ptr()+length - dec_length-1; for (pos= (char*) str->ptr()+length-1; pos != tmp; pos--) pos[0]= pos[-diff]; while (diff) @@ -1936,12 +1999,8 @@ void Item_func_format::print(String *str) { str->append(STRING_WITH_LEN("format(")); args[0]->print(str); - str->append(','); - // my_charset_bin is good enough for numbers - char buffer[20]; - String st(buffer, sizeof(buffer), &my_charset_bin); - st.set((ulonglong)decimals, &my_charset_bin); - str->append(st); + str->append(','); + args[1]->print(str); str->append(')'); } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 2adccd212b3..3b171b5d50f 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -37,6 +37,7 @@ public: enum Item_result result_type () const { return STRING_RESULT; } void left_right_max_length(); String *check_well_formed_result(String *str); + bool fix_fields(THD *thd, Item **ref); }; class Item_func_md5 :public Item_str_func @@ -360,11 +361,9 @@ public: class Item_func_encode :public Item_str_func { - protected: - SQL_CRYPT sql_crypt; public: - Item_func_encode(Item *a, char *seed): - Item_str_func(a),sql_crypt(seed) {} + Item_func_encode(Item *a, Item *seed): + Item_str_func(a, seed) {} String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "encode"; } @@ -374,7 +373,7 @@ public: class Item_func_decode :public Item_func_encode { public: - Item_func_decode(Item *a, char *seed): Item_func_encode(a,seed) {} + Item_func_decode(Item *a, Item *seed): Item_func_encode(a, seed) {} String *val_str(String *); const char *func_name() const { return "decode"; } }; @@ -507,15 +506,9 @@ class Item_func_format :public Item_str_func { String tmp_str; public: - Item_func_format(Item *org,int dec); + Item_func_format(Item *org, Item *dec); String *val_str(String *); - void fix_length_and_dec() - { - collation.set(default_charset()); - uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; - max_length= ((char_length + (char_length-args[0]->decimals)/3) * - collation.collation->mbmaxlen); - } + void fix_length_and_dec(); const char *func_name() const { return "format"; } void print(String *); }; @@ -530,9 +523,8 @@ public: { collation.set(cs); } String *val_str(String *); void fix_length_and_dec() - { - maybe_null=0; - max_length=arg_count * collation.collation->mbmaxlen; + { + max_length= arg_count * collation.collation->mbmaxlen; } const char *func_name() const { return "char"; } }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index ea6a950f07c..cfa4b2ce16c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -322,9 +322,13 @@ void Item_sum::make_field(Send_field *tmp_field) if (args[0]->type() == Item::FIELD_ITEM && keep_field_type()) { ((Item_field*) args[0])->field->make_field(tmp_field); - tmp_field->db_name=(char*)""; - tmp_field->org_table_name=tmp_field->table_name=(char*)""; - tmp_field->org_col_name=tmp_field->col_name=name; + /* For expressions only col_name should be non-empty string. */ + char *empty_string= (char*)""; + tmp_field->db_name= empty_string; + tmp_field->org_table_name= empty_string; + tmp_field->table_name= empty_string; + tmp_field->org_col_name= empty_string; + tmp_field->col_name= name; if (maybe_null) tmp_field->flags&= ~NOT_NULL_FLAG; } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index db38753dc74..77679a9795e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6326,6 +6326,16 @@ static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff) return 0; } +static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff) +{ + var->type= SHOW_LONG; + var->value= buff; + pthread_mutex_lock(&LOCK_prepared_stmt_count); + *((long *)buff)= (long)prepared_stmt_count; + pthread_mutex_unlock(&LOCK_prepared_stmt_count); + return 0; +} + static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff) { var->type= SHOW_LONG; @@ -6736,6 +6746,7 @@ SHOW_VAR status_vars[]= { {"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC}, {"Open_tables", (char*) &show_open_tables, SHOW_FUNC}, {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}, + {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC}, #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH}, @@ -6915,6 +6926,7 @@ static void mysql_init_variables(void) binlog_cache_use= binlog_cache_disk_use= 0; max_used_connections= slow_launch_threads = 0; mysqld_user= mysqld_chroot= opt_init_file= opt_bin_logname = 0; + prepared_stmt_count= 0; errmesg= 0; mysqld_unix_port= opt_mysql_tmpdir= my_bind_addr_str= NullS; bzero((gptr) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list)); diff --git a/sql/set_var.cc b/sql/set_var.cc index 980e60de502..daca62400e2 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -155,7 +155,6 @@ static KEY_CACHE *create_key_cache(const char *name, uint length); void fix_sql_mode_var(THD *thd, enum_var_type type); static byte *get_error_count(THD *thd); static byte *get_warning_count(THD *thd); -static byte *get_prepared_stmt_count(THD *thd); static byte *get_tmpdir(THD *thd); static int sys_check_log_path(THD *thd, set_var *var); static bool sys_update_general_log_path(THD *thd, set_var * var); @@ -639,9 +638,6 @@ static sys_var_readonly sys_warning_count("warning_count", OPT_SESSION, SHOW_LONG, get_warning_count); -static sys_var_readonly sys_prepared_stmt_count("prepared_stmt_count", - OPT_GLOBAL, SHOW_LONG, - get_prepared_stmt_count); /* alias for last_insert_id() to be compatible with Sybase */ #ifdef HAVE_REPLICATION @@ -943,7 +939,6 @@ SHOW_VAR init_vars[]= { {"plugin_dir", (char*) opt_plugin_dir, SHOW_CHAR}, {"port", (char*) &mysqld_port, SHOW_INT}, {sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS}, - {sys_prepared_stmt_count.name, (char*) &sys_prepared_stmt_count, SHOW_SYS}, {"protocol_version", (char*) &protocol_version, SHOW_INT}, {sys_query_alloc_block_size.name, (char*) &sys_query_alloc_block_size, SHOW_SYS}, @@ -3138,14 +3133,6 @@ static byte *get_error_count(THD *thd) return (byte*) &thd->sys_var_tmp.long_value; } -static byte *get_prepared_stmt_count(THD *thd) -{ - pthread_mutex_lock(&LOCK_prepared_stmt_count); - thd->sys_var_tmp.ulong_value= prepared_stmt_count; - pthread_mutex_unlock(&LOCK_prepared_stmt_count); - return (byte*) &thd->sys_var_tmp.ulong_value; -} - /* Get the tmpdir that was specified or chosen by default diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 3022967eeeb..5f9b9e6d563 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6010,4 +6010,6 @@ ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT 42000 eng "Incorrect parameter count in the call to native function '%-.64s'" ER_WRONG_PARAMETERS_TO_NATIVE_FCT 42000 eng "Incorrect parameters in the call to native function '%-.64s'" +ER_NATIVE_FCT_NAME_COLLISION + eng "This function '%-.64s' has the same name as a native function." diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 3362ec76fc2..a6e15c55641 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -737,7 +737,11 @@ void query_cache_end_of_result(THD *thd) header->query())); query_cache.wreck(__LINE__, ""); - BLOCK_UNLOCK_WR(query_block); + /* + We do not need call of BLOCK_UNLOCK_WR(query_block); here because + query_cache.wreck() switched query cache off but left content + untouched for investigation (it is debugging method). + */ goto end; } #endif @@ -3523,7 +3527,7 @@ uint Query_cache::filename_2_table_key (char *key, const char *path, #if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK) -void wreck(uint line, const char *message) {} +void wreck(uint line, const char *message) { query_cache_size = 0; } void bins_dump() {} void cache_dump() {} void queries_dump() {} @@ -3535,6 +3539,17 @@ my_bool in_blocks(Query_cache_block * point) { return 0; } #else + +/* + Debug method which switch query cache off but left content for + investigation. + + SYNOPSIS + Query_cache::wreck() + line line of the wreck() call + message message for logging +*/ + void Query_cache::wreck(uint line, const char *message) { THD *thd=current_thd; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ca40dc9013a..47704570720 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -244,6 +244,12 @@ bool is_keyword(const char *name, uint len) return get_hash_symbol(name,len,0)!=0; } +bool is_lex_native_function(const LEX_STRING *name) +{ + DBUG_ASSERT(name != NULL); + return (get_hash_symbol(name->str, name->length, 1) != 0); +} + /* make a copy of token before ptr and set yytoklen */ static LEX_STRING get_token(LEX *lex,uint length) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index dd00018f5f8..3166928420a 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1235,4 +1235,6 @@ extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); extern const uchar *skip_rear_comments(const uchar *ubegin, const uchar *uend); +extern bool is_lex_native_function(const LEX_STRING *name); + #endif /* MYSQL_SERVER */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 8baf84585b2..e835de4dedd 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -107,7 +107,9 @@ const LEX_STRING trg_event_type_names[]= }; -static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig); +static int +add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, + TABLE_LIST ** table); class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook { @@ -156,6 +158,13 @@ private: */ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) { + /* + FIXME: The code below takes too many different paths depending on the + 'create' flag, so that the justification for a single function + 'mysql_create_or_drop_trigger', compared to two separate functions + 'mysql_create_trigger' and 'mysql_drop_trigger' is not apparent. + This is a good candidate for a minor refactoring. + */ TABLE *table; bool result= TRUE; String stmt_query; @@ -181,10 +190,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); } - if (!create && - !(tables= add_table_for_trigger(thd, thd->lex->spname))) - DBUG_RETURN(TRUE); - /* We don't allow creating triggers on tables in the 'mysql' schema */ @@ -194,30 +199,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); } - /* We should have only one table in table list. */ - DBUG_ASSERT(tables->next_global == 0); - - /* - Check that the user has TRIGGER privilege on the subject table. - */ - { - bool err_status; - TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; - thd->lex->query_tables_own_last= 0; - - err_status= check_table_access(thd, TRIGGER_ACL, tables, 0); - - thd->lex->query_tables_own_last= save_query_tables_own_last; - - if (err_status) - DBUG_RETURN(TRUE); - } - /* There is no DETERMINISTIC clause for triggers, so can't check it. But a trigger can in theory be used to do nasty things (if it supported - DROP for example) so we do the check for privileges. Triggers have the - same nature as functions regarding binlogging: their body is implicitely + DROP for example) so we do the check for privileges. For now there is + already a stronger test right above; but when this stronger test will + be removed, the test below will hold. Because triggers have the same + nature as functions regarding binlogging: their body is implicitly binlogged, so they share the same danger, so trust_function_creators applies to them too. */ @@ -228,24 +216,68 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); } - /* We do not allow creation of triggers on temporary tables. */ - if (create && find_temporary_table(thd, tables)) - { - my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); - DBUG_RETURN(TRUE); - } - /* We don't want perform our operations while global read lock is held - so we have to wait until its end and then prevent it from occuring + so we have to wait until its end and then prevent it from occurring again until we are done. (Acquiring LOCK_open is not enough because - global read lock is held without helding LOCK_open). + global read lock is held without holding LOCK_open). */ if (wait_if_global_read_lock(thd, 0, 1)) DBUG_RETURN(TRUE); VOID(pthread_mutex_lock(&LOCK_open)); + if (!create) + { + bool if_exists= thd->lex->drop_if_exists; + + if (add_table_for_trigger(thd, thd->lex->spname, if_exists, & tables)) + goto end; + + if (!tables) + { + DBUG_ASSERT(if_exists); + /* + Since the trigger does not exist, there is no associated table, + and therefore : + - no TRIGGER privileges to check, + - no trigger to drop, + - no table to lock/modify, + so the drop statement is successful. + */ + result= FALSE; + /* Still, we need to log the query ... */ + stmt_query.append(thd->query, thd->query_length); + goto end; + } + } + + /* + Check that the user has TRIGGER privilege on the subject table. + */ + { + bool err_status; + TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; + thd->lex->query_tables_own_last= 0; + + err_status= check_table_access(thd, TRIGGER_ACL, tables, 0); + + thd->lex->query_tables_own_last= save_query_tables_own_last; + + if (err_status) + goto end; + } + + /* We should have only one table in table list. */ + DBUG_ASSERT(tables->next_global == 0); + + /* We do not allow creation of triggers on temporary tables. */ + if (create && find_temporary_table(thd, tables->db, tables->table_name)) + { + my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); + goto end; + } + if (lock_table_names(thd, tables)) goto end; @@ -1141,13 +1173,17 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, mysql_table_for_trigger() thd - current thread context trig - identifier for trigger + if_exists - treat a not existing trigger as a warning if TRUE + table - pointer to TABLE_LIST object for the table trigger (output) RETURN VALUE - 0 - error - # - pointer to TABLE_LIST object for the table + 0 Success + 1 Error */ -static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) +static int +add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, + TABLE_LIST **table) { LEX *lex= thd->lex; char path_buff[FN_REFLEN]; @@ -1158,6 +1194,7 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) path_buff, &trigname.trigger_table); DBUG_ENTER("add_table_for_trigger"); + DBUG_ASSERT(table != NULL); path.length= build_table_filename(path_buff, FN_REFLEN-1, trig->m_db.str, trig->m_name.str, @@ -1166,30 +1203,45 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) if (access(path_buff, F_OK)) { + if (if_exists) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TRG_DOES_NOT_EXIST, + ER(ER_TRG_DOES_NOT_EXIST)); + *table= NULL; + DBUG_RETURN(0); + } + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); - DBUG_RETURN(0); + DBUG_RETURN(1); } if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1))) - DBUG_RETURN(0); + DBUG_RETURN(1); if (!is_equal(&trigname_file_type, parser->type())) { my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext+1, "TRIGGERNAME"); - DBUG_RETURN(0); + DBUG_RETURN(1); } if (parser->parse((gptr)&trigname, thd->mem_root, trigname_file_parameters, 1, &trigger_table_hook)) - DBUG_RETURN(0); + DBUG_RETURN(1); /* We need to reset statement table list to be PS/SP friendly. */ lex->query_tables= 0; lex->query_tables_last= &lex->query_tables; - DBUG_RETURN(sp_add_to_query_tables(thd, lex, trig->m_db.str, - trigname.trigger_table.str, TL_IGNORE)); + *table= sp_add_to_query_tables(thd, lex, trig->m_db.str, + trigname.trigger_table.str, TL_IGNORE); + + if (! *table) + DBUG_RETURN(1); + + DBUG_RETURN(0); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f5ad6f1e278..ec3094ca721 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -97,6 +97,17 @@ void turn_parser_debug_on() } #endif +static bool is_native_function(THD *thd, const LEX_STRING *name) +{ + if (find_native_function_builder(thd, *name)) + return true; + + if (is_lex_native_function(name)) + return true; + + return false; +} + %} %union { int num; @@ -1537,6 +1548,7 @@ sp_name: create_function_tail: RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys { + THD *thd= YYTHD; LEX *lex=Lex; if (lex->definer != NULL) { @@ -1549,6 +1561,12 @@ create_function_tail: my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); YYABORT; } + if (is_native_function(thd, & lex->spname->m_name)) + { + my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), + lex->spname->m_name.str); + YYABORT; + } lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->udf.name = lex->spname->m_name; lex->udf.returns=(Item_result) $2; @@ -1637,6 +1655,7 @@ create_function_tail: } sp_proc_stmt { + THD *thd= YYTHD; LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -1644,15 +1663,50 @@ create_function_tail: YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->init_strings(YYTHD, lex); + sp->init_strings(thd, lex); if (!(sp->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); YYABORT; } + if (is_native_function(thd, & sp->m_name)) + { + /* + This warning will be printed when + [1] A client query is parsed, + [2] A stored function is loaded by db_load_routine. + Printing the warning for [2] is intentional, to cover the + following scenario: + - A user define a SF 'foo' using MySQL 5.N + - An application uses select foo(), and works. + - MySQL 5.{N+1} defines a new native function 'foo', as + part of a new feature. + - MySQL 5.{N+1} documentation is updated, and should mention + that there is a potential incompatible change in case of + existing stored function named 'foo'. + - The user deploys 5.{N+1}. At this point, 'select foo()' + means something different, and the user code is most likely + broken (it's only safe if the code is 'select db.foo()'). + With a warning printed when the SF is loaded (which has to occur + before the call), the warning will provide a hint explaining + the root cause of a later failure of 'select foo()'. + With no warning printed, the user code will fail with no + apparent reason. + Printing a warning each time db_load_routine is executed for + an ambiguous function is annoying, since that can happen a lot, + but in practice should not happen unless there *are* name + collisions. + If a collision exists, it should not be silenced but fixed. + */ + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NATIVE_FCT_NAME_COLLISION, + ER(ER_NATIVE_FCT_NAME_COLLISION), + sp->m_name.str); + } /* Restore flag if it was cleared above */ - YYTHD->client_capabilities |= $<ulong_num>2; - sp->restore_thd_mem_root(YYTHD); + thd->client_capabilities |= $<ulong_num>2; + sp->restore_thd_mem_root(thd); } ; @@ -7687,11 +7741,12 @@ drop: Lex->spname= $4; Lex->sql_command = SQLCOM_DROP_EVENT; } - | DROP TRIGGER_SYM sp_name + | DROP TRIGGER_SYM if_exists sp_name { LEX *lex= Lex; lex->sql_command= SQLCOM_DROP_TRIGGER; - lex->spname= $3; + lex->drop_if_exists= $3; + lex->spname= $4; } | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait { @@ -7702,7 +7757,7 @@ drop: { LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP; - } + } ; table_list: @@ -8658,6 +8713,8 @@ load_data_lock: Ignore this option in SP to avoid problem with query cache */ if (Lex->sphead != 0) + $$= YYTHD->update_lock_default; + else #endif $$= TL_WRITE_CONCURRENT_INSERT; } diff --git a/storage/ndb/config/type_kernel.mk.am b/storage/ndb/config/type_kernel.mk.am index 1b9d49d8088..c2a602d4e19 100644 --- a/storage/ndb/config/type_kernel.mk.am +++ b/storage/ndb/config/type_kernel.mk.am @@ -2,7 +2,7 @@ INCLUDES += \ -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/storage/ndb/include \ -I$(top_srcdir)/storage/ndb/src/kernel/vm \ diff --git a/storage/ndb/config/type_ndbapi.mk.am b/storage/ndb/config/type_ndbapi.mk.am index 9e1b83c0cad..258322d6d9a 100644 --- a/storage/ndb/config/type_ndbapi.mk.am +++ b/storage/ndb/config/type_ndbapi.mk.am @@ -2,7 +2,7 @@ INCLUDES += \ -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/mysys \ -I$(top_srcdir)/storage/ndb/include \ diff --git a/storage/ndb/config/type_ndbapitest.mk.am b/storage/ndb/config/type_ndbapitest.mk.am index e385e7bc07e..9a5a7d5a778 100644 --- a/storage/ndb/config/type_ndbapitest.mk.am +++ b/storage/ndb/config/type_ndbapitest.mk.am @@ -7,7 +7,7 @@ LDADD += $(top_builddir)/storage/ndb/test/src/libNDBT.a \ INCLUDES += -I$(top_srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/storage/ndb/include \ -I$(top_srcdir)/storage/ndb/include/ndbapi \ diff --git a/storage/ndb/config/type_ndbapitools.mk.am b/storage/ndb/config/type_ndbapitools.mk.am index 1bb8339a390..6003489f46e 100644 --- a/storage/ndb/config/type_ndbapitools.mk.am +++ b/storage/ndb/config/type_ndbapitools.mk.am @@ -7,7 +7,7 @@ LDADD += \ INCLUDES += -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/storage/ndb/include \ -I$(top_srcdir)/storage/ndb/include/ndbapi \ diff --git a/storage/ndb/config/type_util.mk.am b/storage/ndb/config/type_util.mk.am index 02ce2cfc969..2ce75a2ce31 100644 --- a/storage/ndb/config/type_util.mk.am +++ b/storage/ndb/config/type_util.mk.am @@ -1,7 +1,7 @@ INCLUDES += -I$(srcdir) \ -I$(top_builddir)/include \ - -I$(top_builddir)/ndb/include \ + -I$(top_builddir)/storage/ndb/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/mysys \ -I$(top_srcdir)/storage/ndb/include \ diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 56b665b098e..d67ecb4c5e0 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15541,6 +15541,151 @@ static void test_bug21726() /* + BUG#23383: mysql_affected_rows() returns different values than + mysql_stmt_affected_rows() + + Test that both mysql_affected_rows() and mysql_stmt_affected_rows() + return -1 on error, 0 when no rows were affected, and (positive) row + count when some rows were affected. +*/ +static void test_bug23383() +{ + const char *insert_query= "INSERT INTO t1 VALUES (1), (2)"; + const char *update_query= "UPDATE t1 SET i= 4 WHERE i = 3"; + MYSQL_STMT *stmt; + my_ulonglong row_count; + int rc; + + DBUG_ENTER("test_bug23383"); + myheader("test_bug23383"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE t1 (i INT UNIQUE)"); + myquery(rc); + + rc= mysql_query(mysql, insert_query); + myquery(rc); + row_count= mysql_affected_rows(mysql); + DIE_UNLESS(row_count == 2); + + rc= mysql_query(mysql, insert_query); + DIE_UNLESS(rc != 0); + row_count= mysql_affected_rows(mysql); + DIE_UNLESS(row_count == (my_ulonglong)-1); + + rc= mysql_query(mysql, update_query); + myquery(rc); + row_count= mysql_affected_rows(mysql); + DIE_UNLESS(row_count == 0); + + rc= mysql_query(mysql, "DELETE FROM t1"); + myquery(rc); + + stmt= mysql_stmt_init(mysql); + DIE_UNLESS(stmt != 0); + + rc= mysql_stmt_prepare(stmt, insert_query, strlen(insert_query)); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + row_count= mysql_stmt_affected_rows(stmt); + DIE_UNLESS(row_count == 2); + + rc= mysql_stmt_execute(stmt); + DIE_UNLESS(rc != 0); + row_count= mysql_stmt_affected_rows(stmt); + DIE_UNLESS(row_count == (my_ulonglong)-1); + + rc= mysql_stmt_prepare(stmt, update_query, strlen(update_query)); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + row_count= mysql_stmt_affected_rows(stmt); + DIE_UNLESS(row_count == 0); + + rc= mysql_stmt_close(stmt); + check_execute(stmt, rc); + + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + + DBUG_VOID_RETURN; +} + + +/* + BUG#21635: MYSQL_FIELD struct's member strings seem to misbehave for + expression cols + + Check that for MIN(), MAX(), COUNT() only MYSQL_FIELD::name is set + to either expression or its alias, and db, org_table, table, + org_name fields are empty strings. +*/ +static void test_bug21635() +{ + const char *expr[]= + { + "MIN(i)", "MIN(i)", + "MIN(i) AS A1", "A1", + "MAX(i)", "MAX(i)", + "MAX(i) AS A2", "A2", + "COUNT(i)", "COUNT(i)", + "COUNT(i) AS A3", "A3", + }; + char query[MAX_TEST_QUERY_LENGTH]; + char *query_end; + MYSQL_RES *result; + MYSQL_FIELD *field; + unsigned int field_count, i; + int rc; + + DBUG_ENTER("test_bug21635"); + myheader("test_bug21635"); + + query_end= strxmov(query, "SELECT ", NullS); + for (i= 0; i < sizeof(expr) / sizeof(*expr) / 2; ++i) + query_end= strxmov(query_end, expr[i * 2], ", ", NullS); + query_end= strxmov(query_end - 2, " FROM t1 GROUP BY i", NullS); + DIE_UNLESS(query_end - query < MAX_TEST_QUERY_LENGTH); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE TABLE t1 (i INT)"); + myquery(rc); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + myquery(rc); + + rc= mysql_real_query(mysql, query, query_end - query); + myquery(rc); + + result= mysql_use_result(mysql); + DIE_UNLESS(result); + + field_count= mysql_field_count(mysql); + for (i= 0; i < field_count; ++i) + { + field= mysql_fetch_field_direct(result, i); + printf("%s -> %s ... ", expr[i * 2], field->name); + fflush(stdout); + DIE_UNLESS(field->db[0] == 0 && field->org_table[0] == 0 && + field->table[0] == 0 && field->org_name[0] == 0); + DIE_UNLESS(strcmp(field->name, expr[i * 2 + 1]) == 0); + puts("OK"); + } + + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + + DBUG_VOID_RETURN; +} + + +/* Read and parse arguments and MySQL options from my.cnf */ @@ -15818,6 +15963,8 @@ static struct my_tests_st my_tests[]= { { "test_bug19671", test_bug19671 }, { "test_bug21206", test_bug21206 }, { "test_bug21726", test_bug21726 }, + { "test_bug23383", test_bug23383 }, + { "test_bug21635", test_bug21635 }, { "test_status", test_status}, { 0, 0 } }; |