diff options
author | ndbdev@dl145b.mysql.com <> | 2005-09-09 15:26:47 +0200 |
---|---|---|
committer | ndbdev@dl145b.mysql.com <> | 2005-09-09 15:26:47 +0200 |
commit | 46cc62e216e72f51bf82c283082996cbc2ce2eb5 (patch) | |
tree | 8b8e4544543c49e86d358196df4f951aa55502dd | |
parent | beedf0eb9b8f940347b3ca7de50ea0400444cc42 (diff) | |
parent | 39efb8c865bcfb7eb29824f6140e713fe61a229b (diff) | |
download | mariadb-git-46cc62e216e72f51bf82c283082996cbc2ce2eb5.tar.gz |
Merge tulin@bk-internal.mysql.com:/home/bk/mysql-5.0
into dl145b.mysql.com:/home/ndbdev/tomas/mysql-5.1
76 files changed, 1901 insertions, 442 deletions
diff --git a/BitKeeper/etc/RESYNC_TREE b/BitKeeper/etc/RESYNC_TREE new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/BitKeeper/etc/RESYNC_TREE diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 43b0672e03e..ca0a751e963 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -37,8 +37,9 @@ static char *add_load_option(char *ptr,const char *object, const char *statement); static my_bool verbose=0,lock_tables=0,ignore_errors=0,opt_delete=0, - replace=0,silent=0,ignore=0,opt_compress=0,opt_local_file=0, + replace=0,silent=0,ignore=0,opt_compress=0, opt_low_priority= 0, tty_password= 0; +static uint opt_local_file=0; static MYSQL mysql_connection; static char *opt_password=0, *current_user=0, *current_host=0, *current_db=0, *fields_terminated=0, diff --git a/extra/yassl/taocrypt/include/misc.hpp b/extra/yassl/taocrypt/include/misc.hpp index 5a31510911e..f705cc99970 100644 --- a/extra/yassl/taocrypt/include/misc.hpp +++ b/extra/yassl/taocrypt/include/misc.hpp @@ -91,7 +91,7 @@ public: // no gas on these systems ?, disable for now -#if defined(__sun__) || defined (__QNX__) +#if defined(__sun__) || defined (__QNX__) || defined (__APPLE__) #define TAOCRYPT_DISABLE_X86ASM #endif diff --git a/extra/yassl/taocrypt/include/runtime.hpp b/extra/yassl/taocrypt/include/runtime.hpp index f506040f0d8..254e67a7f64 100644 --- a/extra/yassl/taocrypt/include/runtime.hpp +++ b/extra/yassl/taocrypt/include/runtime.hpp @@ -25,7 +25,8 @@ -#if !defined(yaSSL_NEW_HPP) && defined(__GNUC__) && !defined(__ICC) +#if !defined(yaSSL_NEW_HPP) && defined(__GNUC__) +#if !(defined(__ICC) || defined(__INTEL_COMPILER)) #define yaSSL_NEW_HPP @@ -46,5 +47,6 @@ static int __cxa_pure_virtual() } // extern "C" #endif // __GNUC__ > 2 +#endif // ! _ICC #endif // yaSSL_NEW_HPP && __GNUC__ diff --git a/extra/yassl/taocrypt/include/types.hpp b/extra/yassl/taocrypt/include/types.hpp index 92164eaaab4..db9c3792bbd 100644 --- a/extra/yassl/taocrypt/include/types.hpp +++ b/extra/yassl/taocrypt/include/types.hpp @@ -61,9 +61,10 @@ typedef unsigned int word32; // compilers we've found 64-bit multiply insructions for #if defined(__GNUC__) || defined(_MSC_VER) || defined(__DECCXX) +#if !(defined(__ICC) || defined(__INTEL_COMPILER)) #define HAVE_64_MULTIPLY #endif - +#endif #if defined(HAVE_64_MULTIPLY) && (defined(__alpha__) || defined(__ia64__) \ || defined(_ARCH_PPC64) || defined(__mips64) || defined(__x86_64__)) diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index fef813371c8..2508d751b46 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -1,3 +1,4 @@ +drop table if exists t1,t2; show tables; Tables_in_mysql columns_priv @@ -71,3 +72,8 @@ show tables; Tables_in_test delete from mysql.user where user=_binary"test"; flush privileges; +create table t1 (id integer not null auto_increment primary key); +create temporary table t2(id integer not null auto_increment primary key); +set @id := 1; +delete from t1 where id like @id; +drop table t1; diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 64c693a292a..48ff82aeeb2 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -955,6 +955,10 @@ char_length(a) length(a) a 2 4 ан drop table t1; set names utf8; +select 'andre%' like 'andreñ%' escape 'ñ'; +'andre%' like 'andreñ%' escape 'ñ' +1 +set names utf8; select 'a\\' like 'a\\'; 'a\\' like 'a\\' 1 diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index c4e8e08929d..6461c393d51 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -469,6 +469,15 @@ select collation(group_concat(a,b)) from t1; ERROR HY000: Illegal mix of collations (cp1250_general_ci,IMPLICIT) and (koi8r_general_ci,IMPLICIT) for operation 'group_concat' drop table t1; drop table t2; +CREATE TABLE t1 (a CHAR(10) CHARACTER SET cp850); +INSERT INTO t1 VALUES ('À'); +SELECT a FROM t1; +a +À +SELECT GROUP_CONCAT(a) FROM t1; +GROUP_CONCAT(a) +À +DROP TABLE t1; CREATE TABLE t1 (id int); SELECT GROUP_CONCAT(id) AS gc FROM t1 HAVING gc IS NULL; gc @@ -567,3 +576,23 @@ group_concat('x') NULL 1 drop table t1; +CREATE TABLE t1 (id int, a varchar(9)); +INSERT INTO t1 VALUES +(2, ''), (1, ''), (2, 'x'), (1, 'y'), (3, 'z'), (3, ''); +SELECT GROUP_CONCAT(a) FROM t1; +GROUP_CONCAT(a) +,,x,y,z, +SELECT GROUP_CONCAT(a ORDER BY a) FROM t1; +GROUP_CONCAT(a ORDER BY a) +,,,x,y,z +SELECT GROUP_CONCAT(a) FROM t1 GROUP BY id; +GROUP_CONCAT(a) +,y +,x +z, +SELECT GROUP_CONCAT(a ORDER BY a) FROM t1 GROUP BY id; +GROUP_CONCAT(a ORDER BY a) +,y +,x +,z +DROP TABLE t1; diff --git a/mysql-test/r/func_like.result b/mysql-test/r/func_like.result index ac8e5eda8e8..7e6fedb9403 100644 --- a/mysql-test/r/func_like.result +++ b/mysql-test/r/func_like.result @@ -158,3 +158,10 @@ DROP TABLE t1; select _cp866'aaaaaaaaa' like _cp866'%aaaa%' collate cp866_bin; _cp866'aaaaaaaaa' like _cp866'%aaaa%' collate cp866_bin 1 +set names koi8r; +select 'andre%' like 'andreÊ%' escape 'Ê'; +'andre%' like 'andreÊ%' escape 'Ê' +1 +select _cp1251'andre%' like convert('andreÊ%' using cp1251) escape 'Ê'; +_cp1251'andre%' like convert('andreÊ%' using cp1251) escape 'Ê' +1 diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index f4cf5217fa7..7bc886022cc 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -766,6 +766,15 @@ n Warnings: Warning 1052 Column 'n' in group statement is ambiguous DROP TABLE t1; +create table t1(f1 varchar(5) key); +insert into t1 values (1),(2); +select sql_buffer_result max(f1) is null from t1; +max(f1) is null +0 +select sql_buffer_result max(f1)+1 from t1; +max(f1)+1 +3 +drop table t1; create table t1 (c1 char(3), c2 char(3)); create table t2 (c3 char(3), c4 char(3)); insert into t1 values ('aaa', 'bb1'), ('aaa', 'bb2'); diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 20b2f12f0a8..9a7a0b48f47 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -979,3 +979,14 @@ WHERE TABLE_SCHEMA='test' AND TABLE_TYPE='BASE TABLE'); Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL t2 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL +DROP TABLE t1,t2; +create table t1(f1 int); +create view v1 (c) as select f1 from t1; +select database(); +database() +NULL +show fields from test.v1; +Field Type Null Key Default Extra +c int(11) YES NULL +drop view v1; +drop table t1; diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index d4a20209162..92b352aa608 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -200,7 +200,7 @@ INSERT INTO t1 VALUES (10363,'Tecniques de Comunicacio Oral i Escrita','Tecnicas INSERT INTO t1 VALUES (11403,'Projecte Fi de Carrera','Proyecto Fin de Carrera','Projecte Fi de Carrera','PFC',9.0,NULL,NULL,NULL); INSERT INTO t1 VALUES (11404,'+lgebra lineal','Algebra lineal','+lgebra lineal','+lgebra lineal',15.0,NULL,NULL,NULL); INSERT INTO t1 VALUES (11405,'+lgebra lineal','Algebra lineal','+lgebra lineal','+lgebra lineal',18.0,NULL,NULL,NULL); -INSERT INTO t1 VALUES (11406,'Calcul Infinitesimal','Cßlculo Infinitesimal','Calcul Infinitesimal','Calcul Infinitesimal',15.0,NULL,NULL,NULL); +INSERT INTO t1 VALUES (11406,'Calcul Infinitesimal','Cßlculo Infinitesimal','Calcul Infinitesimal','Calcul Infinitesimal',15.0,NULL,NULL,NULL); CREATE TABLE t2 ( idAssignatura int(11) DEFAULT '0' NOT NULL, Grup int(11) DEFAULT '0' NOT NULL, @@ -1001,3 +1001,136 @@ SELECT * FROM t1 LEFT JOIN t2 ON (c11=c21 AND c21=30) WHERE c11=40; c11 c21 40 NULL DROP TABLE t1, t2; +CREATE TABLE t1 (a int PRIMARY KEY, b int); +CREATE TABLE t2 (a int PRIMARY KEY, b int); +INSERT INTO t1 VALUES (1,2), (2,1), (3,2), (4,3), (5,6), (6,5), (7,8), (8,7), (9,10); +INSERT INTO t2 VALUES (3,0), (4,1), (6,4), (7,5); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.b <= t1.a AND t1.a <= t1.b; +a b a b +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a BETWEEN t2.b AND t1.b; +a b a b +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a NOT BETWEEN t2.b AND t1.b); +a b a b +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.b > t1.a OR t1.a > t1.b; +a b a b +2 1 NULL NULL +3 2 3 0 +4 3 4 1 +6 5 6 4 +8 7 NULL NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a NOT BETWEEN t2.b AND t1.b; +a b a b +2 1 NULL NULL +3 2 3 0 +4 3 4 1 +6 5 6 4 +8 7 NULL NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a BETWEEN t2.b AND t1.b); +a b a b +2 1 NULL NULL +3 2 3 0 +4 3 4 1 +6 5 6 4 +8 7 NULL NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t2.b > t1.a OR t1.a > t1.b; +a b a b +2 1 NULL NULL +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +8 7 NULL NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a != t2.a AND t1.a BETWEEN t2.b AND t1.b); +a b a b +2 1 NULL NULL +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +8 7 NULL NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a AND (t2.b > t1.a OR t1.a > t1.b); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a != t2.a OR t1.a BETWEEN t2.b AND t1.b); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t1.a = t2.b; +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a IN(t2.a, t2.b); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a NOT IN(t2.a, t2.b)); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a != t1.b AND t1.a != t2.b; +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a NOT IN(t1.b, t2.b); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a IN(t1.b, t2.b)); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.a != t2.b OR (t1.a != t2.a AND t1.a != t2.b); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t2.a = t2.b AND t1.a IN(t2.a, t2.b)); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.a != t2.b AND t1.a != t1.b AND t1.a != t2.b; +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t2.a = t2.b OR t1.a IN(t1.b, t2.b)); +a b a b +3 2 3 0 +4 3 4 1 +6 5 6 4 +7 8 7 5 +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t1.a = t2.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 4 Using where +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.a 1 +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a IN(t2.a, t2.b); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 4 Using where +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.a 1 +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a > IF(t1.a = t2.b-2, t2.b, t2.b-1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 4 Using where +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.a 1 +DROP TABLE t1,t2; diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index 69871b2110b..df0ee09ea8e 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -555,6 +555,31 @@ IFNULL(a, 'TEST') COALESCE(b, 'TEST') 4 TEST TEST TEST DROP TABLE t1,t2; +CREATE TABLE t1 (a INT(10) NOT NULL, b INT(10) NOT NULL); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t1 VALUES (1, 2); +SELECT a, b, a AS c, COUNT(*) AS count FROM t1 GROUP BY a, b, c WITH ROLLUP; +a b c count +1 1 1 1 +1 1 NULL 1 +1 2 1 1 +1 2 NULL 1 +1 NULL NULL 2 +NULL NULL NULL 2 +DROP TABLE t1; +CREATE TABLE t1 (a int(11) NOT NULL); +INSERT INTO t1 VALUES (1),(2); +SELECT * FROM (SELECT a, a + 1, COUNT(*) FROM t1 GROUP BY a WITH ROLLUP) t; +a a + 1 COUNT(*) +1 2 1 +2 3 1 +NULL NULL 2 +SELECT * FROM (SELECT a, LENGTH(a), COUNT(*) FROM t1 GROUP BY a WITH ROLLUP) t; +a LENGTH(a) COUNT(*) +1 1 1 +2 1 1 +NULL NULL 2 +DROP TABLE t1; CREATE TABLE t1(id int, type char(1)); INSERT INTO t1 VALUES (1,"A"),(2,"C"),(3,"A"),(4,"A"),(5,"B"), @@ -577,15 +602,19 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 10 Using filesort DROP VIEW v1; DROP TABLE t1; -CREATE TABLE t1 (a INT(10) NOT NULL, b INT(10) NOT NULL); -INSERT INTO t1 VALUES (1, 1); -INSERT INTO t1 VALUES (1, 2); -SELECT a, b, a AS c, COUNT(*) AS count FROM t1 GROUP BY a, b, c WITH ROLLUP; -a b c count -1 1 1 1 -1 1 NULL 1 -1 2 1 1 -1 2 NULL 1 -1 NULL NULL 2 -NULL NULL NULL 2 +CREATE TABLE t1 (a int(11) NOT NULL); +INSERT INTO t1 VALUES (1),(2); +CREATE VIEW v1 AS +SELECT a, LENGTH(a), COUNT(*) FROM t1 GROUP BY a WITH ROLLUP; +DESC v1; +Field Type Null Key Default Extra +a bigint(11) YES NULL +LENGTH(a) bigint(10) YES NULL +COUNT(*) bigint(21) NO 0 +SELECT * FROM v1; +a LENGTH(a) COUNT(*) +1 1 1 +2 1 1 +NULL NULL 2 +DROP VIEW v1; DROP TABLE t1; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index dc78f8d04ea..f1c3672083d 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -773,6 +773,14 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp select ? from t1; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? from t1' at line 1 drop table t1; +CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; +CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; +CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; +PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; +EXECUTE b12651; +1 +DROP VIEW b12651_V1; +DROP TABLE b12651_T1, b12651_T2; prepare stmt from "select @@time_zone"; execute stmt; @@time_zone diff --git a/mysql-test/r/rpl_sp_effects.result b/mysql-test/r/rpl_sp_effects.result index 8bcbf1a60d0..bf8128d9385 100644 --- a/mysql-test/r/rpl_sp_effects.result +++ b/mysql-test/r/rpl_sp_effects.result @@ -156,3 +156,60 @@ slave: 6 drop procedure p1; drop function f1; drop table t1,t2; +create table t1 (a int); +create procedure p1() +begin +insert into t1 values(@x); +set @x=@x+1; +insert into t1 values(@x); +if (f2()) then +insert into t1 values(1243); +end if; +end// +create function f2() returns int +begin +insert into t1 values(@z); +set @z=@z+1; +insert into t1 values(@z); +return 0; +end// +create function f1() returns int +begin +insert into t1 values(@y); +call p1(); +return 0; +end// +set @x=10; +set @y=20; +set @z=100; +select f1(); +f1() +0 +set @x=30; +call p1(); +select 'master', a from t1; +master a +master 20 +master 10 +master 11 +master 100 +master 101 +master 30 +master 31 +master 101 +master 102 +select 'slave', a from t1; +slave a +slave 20 +slave 10 +slave 11 +slave 100 +slave 101 +slave 30 +slave 31 +slave 101 +slave 102 +drop table t1; +drop function f1; +drop function f2; +drop procedure p1; diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 0d5c1aed485..24c89039566 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2897,3 +2897,18 @@ select * from t1 natural join t2 where a = 'b'; a b drop table t1, t2; +CREATE TABLE t1 (`id` TINYINT); +CREATE TABLE t2 (`id` TINYINT); +CREATE TABLE t3 (`id` TINYINT); +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +SELECT t1.id,t3.id FROM t1 JOIN t2 ON (t2.id=t1.id) LEFT JOIN t3 USING (id); +ERROR 23000: Column 'id' in from clause is ambiguous +SELECT t1.id,t3.id FROM t1 JOIN t2 ON (t2.notacolumn=t1.id) LEFT JOIN t3 USING (id); +ERROR 23000: Column 'id' in from clause is ambiguous +SELECT id,t3.id FROM t1 JOIN t2 ON (t2.id=t1.id) LEFT JOIN t3 USING (id); +ERROR 23000: Column 'id' in from clause is ambiguous +SELECT id,t3.id FROM (t1 JOIN t2 ON (t2.id=t1.id)) LEFT JOIN t3 USING (id); +ERROR 23000: Column 'id' in from clause is ambiguous +drop table t1, t2, t3; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 1a061529fb0..09d829e9d12 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -765,3 +765,24 @@ OPTIMIZE TABLE t1; RETURN 1; END| ERROR 0A000: OPTIMIZE TABLE is not allowed in stored procedures +DROP FUNCTION IF EXISTS bug12995| +CREATE FUNCTION bug12995() RETURNS INT +BEGIN +HANDLER t1 OPEN; +RETURN 1; +END| +ERROR 0A000: HANDLER is not allowed in stored procedures +CREATE FUNCTION bug12995() RETURNS INT +BEGIN +HANDLER t1 READ FIRST; +RETURN 1; +END| +ERROR 0A000: HANDLER is not allowed in stored procedures +CREATE FUNCTION bug12995() RETURNS INT +BEGIN +HANDLER t1 CLOSE; +RETURN 1; +END| +ERROR 0A000: HANDLER is not allowed in stored procedures +SELECT bug12995()| +ERROR 42000: FUNCTION test.bug12995 does not exist diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 4424f4e6ad4..cb696f93f79 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3085,6 +3085,19 @@ column_name bug10055(t.column_name) id id data data drop function bug10055| +drop procedure if exists bug12297| +create procedure bug12297(lim int) +begin +set @x = 0; +repeat +insert into t1(id,data) +values('aa', @x); +set @x = @x + 1; +until @x >= lim +end repeat; +end| +call bug12297(10)| +drop procedure bug12297| drop function if exists f_bug11247| drop procedure if exists p_bug11247| create function f_bug11247(param int) @@ -3193,4 +3206,23 @@ set f1= concat( 'hello', f1 ); return f1; end| drop function bug9048| +drop procedure if exists bug12849_1| +create procedure bug12849_1(inout x char) select x into x| +set @var='a'| +call bug12849_1(@var)| +select @var| +@var +a +drop procedure bug12849_1| +drop procedure if exists bug12849_2| +create procedure bug12849_2(inout foo varchar(15)) +begin +select concat(foo, foo) INTO foo; +end| +set @var='abcd'| +call bug12849_2(@var)| +select @var| +@var +abcdabcd +drop procedure bug12849_2| drop table t1,t2; diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 4980e2a73d2..f06e290a49b 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -984,3 +984,35 @@ t1 CREATE TABLE `t1` ( `f1` decimal(10,0) unsigned zerofill NOT NULL default '0000000000' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; +drop procedure if exists wg2; +Warnings: +Note 1305 PROCEDURE wg2 does not exist +create procedure wg2() +begin +declare v int default 1; +declare tdec decimal(5) default 0; +while v <= 9 do set tdec =tdec * 10; +select v, tdec; +set v = v + 1; +end while; +end// +call wg2()// +v tdec +1 0 +v tdec +2 0 +v tdec +3 0 +v tdec +4 0 +v tdec +5 0 +v tdec +6 0 +v tdec +7 0 +v tdec +8 0 +v tdec +9 0 +drop procedure wg2; diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index a5b76c03b29..265f353ae3c 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -545,3 +545,10 @@ select @@max_heap_table_size > 0; select @@have_innodb; @@have_innodb # +select @@character_set_system; +@@character_set_system +utf8 +set global character_set_system = latin1; +ERROR HY000: Variable 'character_set_system' is a read only variable +set @@global.version_compile_os='234'; +ERROR HY000: Variable 'version_compile_os' is a read only variable diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index e141393176c..558977a6d2d 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2151,6 +2151,15 @@ select * from v1; strcmp(f1,'a') drop view v1; drop table t1; +create table t1 (f1 int, f2 int,f3 int); +insert into t1 values (1,10,20),(2,0,0); +create view v1 as select * from t1; +select if(sum(f1)>1,f2,f3) from v1 group by f1; +if(sum(f1)>1,f2,f3) +20 +0 +drop view v1; +drop table t1; create table t1 ( r_object_id char(16) NOT NULL, group_name varchar(32) NOT NULL diff --git a/mysql-test/t/connect.test b/mysql-test/t/connect.test index 64b170970ca..60ac7b88bbe 100644 --- a/mysql-test/t/connect.test +++ b/mysql-test/t/connect.test @@ -6,6 +6,10 @@ # This test makes no sense with the embedded server --source include/not_embedded.inc +--disable_warnings +drop table if exists t1,t2; +--enable_warnings + #connect (con1,localhost,root,,""); #show tables; connect (con1,localhost,root,,mysql); @@ -77,4 +81,18 @@ show tables; delete from mysql.user where user=_binary"test"; flush privileges; +# +# Bug#12517: Clear user variables and replication events before +# closing temp tables in thread cleanup. +connect (con2,localhost,root,,test); +connection con2; +create table t1 (id integer not null auto_increment primary key); +create temporary table t2(id integer not null auto_increment primary key); +set @id := 1; +delete from t1 where id like @id; +disconnect con2; +--sleep 5 +connection default; +drop table t1; + # End of 4.1 tests diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index ce259f465d9..041451272d4 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -810,6 +810,12 @@ alter table t1 modify a char(2) character set utf8; select char_length(a), length(a), a from t1 order by a; drop table t1; +# +# Bugs#12611 +# ESCAPE + LIKE do not work when the escape char is a multibyte one +# +set names utf8; +select 'andre%' like 'andreñ%' escape 'ñ'; # # Bugs#11754: SET NAMES utf8 followed by SELECT "A\\" LIKE "A\\" returns 0 diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 0b61a445270..a519d51e0b5 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -282,6 +282,16 @@ drop table t1; drop table t2; # +# Bug #12829 +# Cannot convert the charset of a GROUP_CONCAT result +# +CREATE TABLE t1 (a CHAR(10) CHARACTER SET cp850); +INSERT INTO t1 VALUES ('À'); +SELECT a FROM t1; +SELECT GROUP_CONCAT(a) FROM t1; +DROP TABLE t1; + +# # bug #7769: group_concat returning null is checked in having # CREATE TABLE t1 (id int); @@ -363,4 +373,20 @@ select * from (select group_concat(a) from t1) t2; select group_concat('x') UNION ALL select 1; drop table t1; +# +# Bug #12863 : missing separators after first empty cancatanated elements +# + +CREATE TABLE t1 (id int, a varchar(9)); +INSERT INTO t1 VALUES + (2, ''), (1, ''), (2, 'x'), (1, 'y'), (3, 'z'), (3, ''); + +SELECT GROUP_CONCAT(a) FROM t1; +SELECT GROUP_CONCAT(a ORDER BY a) FROM t1; + +SELECT GROUP_CONCAT(a) FROM t1 GROUP BY id; +SELECT GROUP_CONCAT(a ORDER BY a) FROM t1 GROUP BY id; + +DROP TABLE t1; + # End of 4.1 tests diff --git a/mysql-test/t/func_like.test b/mysql-test/t/func_like.test index 684d7032038..4e1183afeff 100644 --- a/mysql-test/t/func_like.test +++ b/mysql-test/t/func_like.test @@ -96,4 +96,21 @@ DROP TABLE t1; # select _cp866'aaaaaaaaa' like _cp866'%aaaa%' collate cp866_bin; +# +# Check 8bit escape character +# +set names koi8r; +select 'andre%' like 'andreÊ%' escape 'Ê'; + +# Check 8bit escape character with charset conversion: +# For "a LIKE b ESCAPE c" expressions, +# escape character is converted into the operation character set, +# which is result of aggregation of character sets of "a" and "b". +# "c" itself doesn't take part in aggregation, because its collation +# doesn't matter, escape character is always compared binary. +# In the example below, escape character is converted from koi8r into cp1251: +# +select _cp1251'andre%' like convert('andreÊ%' using cp1251) escape 'Ê'; + +# # End of 4.1 tests diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index 8300b502518..bf557029a55 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -580,7 +580,6 @@ SELECT COUNT(DISTINCT(t1.id)), LEFT(err_comment, 256) AS comment DROP TABLE t1, t2; - # # Bug #12266 GROUP BY expression on DATE column produces result with # reduced length @@ -602,6 +601,16 @@ SELECT n+1 AS n FROM t1 GROUP BY n; --enable_ps_protocol DROP TABLE t1; +# +# BUG#12695: Item_func_isnull::update_used_tables +# did not update const_item_cache +# +create table t1(f1 varchar(5) key); +insert into t1 values (1),(2); +select sql_buffer_result max(f1) is null from t1; +select sql_buffer_result max(f1)+1 from t1; +drop table t1; + # End of 4.1 tests # diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 6be193e0e0c..aa1b632f919 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -665,4 +665,16 @@ SHOW TABLE STATUS FROM test WHERE name IN ( SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='test' AND TABLE_TYPE='BASE TABLE'); -DROP TABLE t1,t2 +DROP TABLE t1,t2; + +# +# Bug #12905 show fields from view behaving erratically with current database +# +create table t1(f1 int); +create view v1 (c) as select f1 from t1; +connect (con5,localhost,root,,*NO-ONE*); +select database(); +show fields from test.v1; +connection default; +drop view v1; +drop table t1; diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test index aabc32c009a..367b98f2485 100644 --- a/mysql-test/t/join_outer.test +++ b/mysql-test/t/join_outer.test @@ -135,7 +135,7 @@ INSERT INTO t1 VALUES (10363,'Tecniques de Comunicacio Oral i Escrita','Tecnicas INSERT INTO t1 VALUES (11403,'Projecte Fi de Carrera','Proyecto Fin de Carrera','Projecte Fi de Carrera','PFC',9.0,NULL,NULL,NULL); INSERT INTO t1 VALUES (11404,'+lgebra lineal','Algebra lineal','+lgebra lineal','+lgebra lineal',15.0,NULL,NULL,NULL); INSERT INTO t1 VALUES (11405,'+lgebra lineal','Algebra lineal','+lgebra lineal','+lgebra lineal',18.0,NULL,NULL,NULL); -INSERT INTO t1 VALUES (11406,'Calcul Infinitesimal','Cßlculo Infinitesimal','Calcul Infinitesimal','Calcul Infinitesimal',15.0,NULL,NULL,NULL); +INSERT INTO t1 VALUES (11406,'Calcul Infinitesimal','Cßlculo Infinitesimal','Calcul Infinitesimal','Calcul Infinitesimal',15.0,NULL,NULL,NULL); CREATE TABLE t2 ( idAssignatura int(11) DEFAULT '0' NOT NULL, @@ -590,7 +590,6 @@ INSERT INTO t2 VALUES("0", "EN", "0-EN"); INSERT INTO t2 VALUES("0", "SV", "0-SV"); INSERT INTO t2 VALUES("10", "EN", "10-EN"); INSERT INTO t2 VALUES("10", "SV", "10-SV"); - SELECT t1.id, t1.text_id, t2.text_data FROM t1 LEFT JOIN t2 ON t1.text_id = t2.text_id @@ -713,3 +712,49 @@ INSERT INTO t1 VALUES (30), (40), (50); INSERT INTO t2 VALUES (300), (400), (500); SELECT * FROM t1 LEFT JOIN t2 ON (c11=c21 AND c21=30) WHERE c11=40; DROP TABLE t1, t2; +# +# Test for bugs +# #12101: erroneously applied outer join elimination in case of WHERE NOT BETWEEN +# #12102: erroneously missing outer join elimination in case of WHERE IN/IF +# + +CREATE TABLE t1 (a int PRIMARY KEY, b int); +CREATE TABLE t2 (a int PRIMARY KEY, b int); + +INSERT INTO t1 VALUES (1,2), (2,1), (3,2), (4,3), (5,6), (6,5), (7,8), (8,7), (9,10); +INSERT INTO t2 VALUES (3,0), (4,1), (6,4), (7,5); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.b <= t1.a AND t1.a <= t1.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a BETWEEN t2.b AND t1.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a NOT BETWEEN t2.b AND t1.b); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.b > t1.a OR t1.a > t1.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a NOT BETWEEN t2.b AND t1.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a BETWEEN t2.b AND t1.b); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t2.b > t1.a OR t1.a > t1.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a != t2.a AND t1.a BETWEEN t2.b AND t1.b); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a AND (t2.b > t1.a OR t1.a > t1.b); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a != t2.a OR t1.a BETWEEN t2.b AND t1.b); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t1.a = t2.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a IN(t2.a, t2.b); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a NOT IN(t2.a, t2.b)); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a != t1.b AND t1.a != t2.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a NOT IN(t1.b, t2.b); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t1.a IN(t1.b, t2.b)); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.a != t2.b OR (t1.a != t2.a AND t1.a != t2.b); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t2.a = t2.b AND t1.a IN(t2.a, t2.b)); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.a != t2.b AND t1.a != t1.b AND t1.a != t2.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE NOT(t2.a = t2.b OR t1.a IN(t1.b, t2.b)); + +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t1.a = t2.b; +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a IN(t2.a, t2.b); +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a > IF(t1.a = t2.b-2, t2.b, t2.b-1); + +DROP TABLE t1,t2; + diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test index 2e09bc5b3a3..adaf6883d43 100644 --- a/mysql-test/t/olap.test +++ b/mysql-test/t/olap.test @@ -251,6 +251,32 @@ SELECT IFNULL(a, 'TEST'), COALESCE(b, 'TEST') FROM t2 DROP TABLE t1,t2; # +# Test for bug #11543: ROLLUP query with a repeated column in GROUP BY +# + +CREATE TABLE t1 (a INT(10) NOT NULL, b INT(10) NOT NULL); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t1 VALUES (1, 2); + +SELECT a, b, a AS c, COUNT(*) AS count FROM t1 GROUP BY a, b, c WITH ROLLUP; + +DROP TABLE t1; + +# Bug #12885(1): derived table specified by a subquery with +# ROLLUP over expressions on not nullable group by attributes +# + +CREATE TABLE t1 (a int(11) NOT NULL); +INSERT INTO t1 VALUES (1),(2); + +SELECT * FROM (SELECT a, a + 1, COUNT(*) FROM t1 GROUP BY a WITH ROLLUP) t; +SELECT * FROM (SELECT a, LENGTH(a), COUNT(*) FROM t1 GROUP BY a WITH ROLLUP) t; + +DROP TABLE t1; + +# End of 4.1 tests + +# # Tests for bug #11639: ROLLUP over view executed through filesort # @@ -266,15 +292,20 @@ EXPLAIN SELECT type FROM v1 GROUP BY type WITH ROLLUP; DROP VIEW v1; DROP TABLE t1; -# Test for bug #11543: ROLLUP query with a repeated column in GROUP BY + +# +# Bug #12885(2): view specified by a subquery with +# ROLLUP over expressions on not nullable group by attributes # -CREATE TABLE t1 (a INT(10) NOT NULL, b INT(10) NOT NULL); -INSERT INTO t1 VALUES (1, 1); -INSERT INTO t1 VALUES (1, 2); +CREATE TABLE t1 (a int(11) NOT NULL); +INSERT INTO t1 VALUES (1),(2); -SELECT a, b, a AS c, COUNT(*) AS count FROM t1 GROUP BY a, b, c WITH ROLLUP; +CREATE VIEW v1 AS + SELECT a, LENGTH(a), COUNT(*) FROM t1 GROUP BY a WITH ROLLUP; -DROP TABLE t1; +DESC v1; +SELECT * FROM v1; -# End of 4.1 tests +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 94596fbbc0e..94ee2b1ca39 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -809,6 +809,21 @@ select ??; select ? from t1; --enable_ps_protocol drop table t1; + +# +# Bug#12651 +# (Crash on a PS including a subquery which is a select from a simple view) +# +CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; +CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; +CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; + +PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; +EXECUTE b12651; + +DROP VIEW b12651_V1; +DROP TABLE b12651_T1, b12651_T2; + # # Bug#9359 "Prepared statements take snapshot of system vars at PREPARE # time" diff --git a/mysql-test/t/rpl_sp_effects.test b/mysql-test/t/rpl_sp_effects.test index f8e83eabe90..9da5723b993 100644 --- a/mysql-test/t/rpl_sp_effects.test +++ b/mysql-test/t/rpl_sp_effects.test @@ -152,4 +152,52 @@ drop procedure p1; drop function f1; drop table t1,t2; +# BUG#12637: User variables + SPs replication +create table t1 (a int); +delimiter //; +create procedure p1() +begin + insert into t1 values(@x); + set @x=@x+1; + insert into t1 values(@x); + if (f2()) then + insert into t1 values(1243); + end if; +end// + +create function f2() returns int +begin + insert into t1 values(@z); + set @z=@z+1; + insert into t1 values(@z); + return 0; +end// + +create function f1() returns int +begin + insert into t1 values(@y); + call p1(); + return 0; +end// + +delimiter ;// + +set @x=10; +set @y=20; +set @z=100; +select f1(); + +set @x=30; +call p1(); + +select 'master', a from t1; +sync_slave_with_master; +connection slave; +select 'slave', a from t1; + +connection master; +drop table t1; +drop function f1; +drop function f2; +drop procedure p1; sync_slave_with_master; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index fad01ac9acf..62687a869b7 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2465,3 +2465,25 @@ insert into t2 values ('b'),('c'),('d'); select a from t1 natural join t2; select * from t1 natural join t2 where a = 'b'; drop table t1, t2; + +# +# Bug #12977 Compare table names with qualifying field tables only +# for base tables, search all nested join operands of natural joins. +# + +CREATE TABLE t1 (`id` TINYINT); +CREATE TABLE t2 (`id` TINYINT); +CREATE TABLE t3 (`id` TINYINT); +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +-- error 1052 +SELECT t1.id,t3.id FROM t1 JOIN t2 ON (t2.id=t1.id) LEFT JOIN t3 USING (id); +-- error 1052 +SELECT t1.id,t3.id FROM t1 JOIN t2 ON (t2.notacolumn=t1.id) LEFT JOIN t3 USING (id); +-- error 1052 +SELECT id,t3.id FROM t1 JOIN t2 ON (t2.id=t1.id) LEFT JOIN t3 USING (id); +-- error 1052 +SELECT id,t3.id FROM (t1 JOIN t2 ON (t2.id=t1.id)) LEFT JOIN t3 USING (id); + +drop table t1, t2, t3; diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index abb927ab3b8..9f91c32c104 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1099,6 +1099,36 @@ BEGIN OPTIMIZE TABLE t1; RETURN 1; END| +delimiter ;| + +# +# Bug##12995 "Inside function "Table 't4' was not locked with LOCK TABLES" +# +delimiter |; +--disable_warnings +DROP FUNCTION IF EXISTS bug12995| +--enable_warnings +--error ER_SP_BADSTATEMENT +CREATE FUNCTION bug12995() RETURNS INT +BEGIN + HANDLER t1 OPEN; + RETURN 1; +END| +--error ER_SP_BADSTATEMENT +CREATE FUNCTION bug12995() RETURNS INT +BEGIN + HANDLER t1 READ FIRST; + RETURN 1; +END| +--error ER_SP_BADSTATEMENT +CREATE FUNCTION bug12995() RETURNS INT +BEGIN + HANDLER t1 CLOSE; + RETURN 1; +END| +--error 1305 +SELECT bug12995()| +delimiter ;| # # BUG#NNNN: New bug synopsis diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index d52ebbbbf67..3d315fa12df 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3877,29 +3877,23 @@ drop function bug10055| # consumption by passing large input parameter. # -# -# Note: the test is currenly disabled because of the -# Bug #12637: SP crashes the server if it has update query with user var -# & binlog is enabled. -# - --disable_warnings -#drop procedure if exists bug12297| +drop procedure if exists bug12297| --enable_warnings -#create procedure bug12297(lim int) -#begin -# set @x = 0; -# repeat -# insert into t1(id,data) -# values('aa', @x); -# set @x = @x + 1; -# until @x >= lim -# end repeat; -#end| +create procedure bug12297(lim int) +begin + set @x = 0; + repeat + insert into t1(id,data) + values('aa', @x); + set @x = @x + 1; + until @x >= lim + end repeat; +end| -#call bug12297(10)| -#drop procedure bug12297| +call bug12297(10)| +drop procedure bug12297| # # Bug #11247 "Stored procedures: Function calls in long loops leak memory" @@ -4044,6 +4038,32 @@ end| drop function bug9048| # +# Bug #12849 Stored Procedure: Crash on procedure call with CHAR type +# 'INOUT' parameter +# + +--disable_warnings +drop procedure if exists bug12849_1| +--enable_warnings +create procedure bug12849_1(inout x char) select x into x| +set @var='a'| +call bug12849_1(@var)| +select @var| +drop procedure bug12849_1| + +--disable_warnings +drop procedure if exists bug12849_2| +--enable_warnings +create procedure bug12849_2(inout foo varchar(15)) +begin +select concat(foo, foo) INTO foo; +end| +set @var='abcd'| +call bug12849_2(@var)| +select @var| +drop procedure bug12849_2| + +# # BUG#NNNN: New bug synopsis # #--disable_warnings diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index f3be64506c7..55e0618a3e5 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1015,3 +1015,25 @@ create table t1 ( f1 decimal (0,0) zerofill not null default 0); show create table t1; drop table t1; + +# +# Bug 12938 (arithmetic loop's zero) +# +--disable-warnings +drop procedure if exists wg2; +--enable-warnings +delimiter //; +create procedure wg2() +begin + declare v int default 1; + declare tdec decimal(5) default 0; + while v <= 9 do set tdec =tdec * 10; + select v, tdec; + set v = v + 1; + end while; +end// + +call wg2()// + +delimiter ;// +drop procedure wg2; diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index 372e865467e..afd0fe23805 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -435,3 +435,12 @@ select @@max_heap_table_size > 0; --replace_column 1 # select @@have_innodb; + +# +# Bug #11775 Variable character_set_system does not exist (sometimes) +# +select @@character_set_system; +--error 1238 +set global character_set_system = latin1; +--error 1238 +set @@global.version_compile_os='234'; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index c5984f726f4..7cca98391a8 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2020,6 +2020,14 @@ drop view v1; drop table t1; # +# Bug #12922 if(sum(),...) with group from view returns wrong results +# +create table t1 (f1 int, f2 int,f3 int); +insert into t1 values (1,10,20),(2,0,0); +create view v1 as select * from t1; +select if(sum(f1)>1,f2,f3) from v1 group by f1; +drop view v1; +drop table t1; # BUG#12941 # create table t1 ( diff --git a/ndb/src/ndbapi/SignalSender.cpp b/ndb/src/ndbapi/SignalSender.cpp new file mode 100644 index 00000000000..6314361e55c --- /dev/null +++ b/ndb/src/ndbapi/SignalSender.cpp @@ -0,0 +1,269 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "SignalSender.hpp" +#include <NdbSleep.h> +#include <SignalLoggerManager.hpp> +#include <signaldata/NFCompleteRep.hpp> +#include <signaldata/NodeFailRep.hpp> + +SimpleSignal::SimpleSignal(bool dealloc){ + memset(this, 0, sizeof(* this)); + deallocSections = dealloc; +} + +SimpleSignal::~SimpleSignal(){ + if(!deallocSections) + return; + if(ptr[0].p != 0) delete []ptr[0].p; + if(ptr[1].p != 0) delete []ptr[1].p; + if(ptr[2].p != 0) delete []ptr[2].p; +} + +void +SimpleSignal::set(class SignalSender& ss, + Uint8 trace, Uint16 recBlock, Uint16 gsn, Uint32 len){ + + header.theTrace = trace; + header.theReceiversBlockNumber = recBlock; + header.theVerId_signalNumber = gsn; + header.theLength = len; + header.theSendersBlockRef = refToBlock(ss.getOwnRef()); +} + +void +SimpleSignal::print(FILE * out){ + fprintf(out, "---- Signal ----------------\n"); + SignalLoggerManager::printSignalHeader(out, header, 0, 0, false); + SignalLoggerManager::printSignalData(out, header, theData); + for(Uint32 i = 0; i<header.m_noOfSections; i++){ + Uint32 len = ptr[i].sz; + fprintf(out, " --- Section %d size=%d ---\n", i, len); + Uint32 * signalData = ptr[i].p; + while(len >= 7){ + fprintf(out, + " H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n", + signalData[0], signalData[1], signalData[2], signalData[3], + signalData[4], signalData[5], signalData[6]); + len -= 7; + signalData += 7; + } + if(len > 0){ + fprintf(out, " H\'%.8x", signalData[0]); + for(Uint32 i = 1; i<len; i++) + fprintf(out, " H\'%.8x", signalData[i]); + fprintf(out, "\n"); + } + } +} + +SignalSender::SignalSender(TransporterFacade *facade) + : m_lock(0) +{ + m_cond = NdbCondition_Create(); + theFacade = facade; + m_blockNo = theFacade->open(this, execSignal, execNodeStatus); + assert(m_blockNo > 0); +} + +SignalSender::~SignalSender(){ + int i; + if (m_lock) + unlock(); + theFacade->close(m_blockNo,0); + // free these _after_ closing theFacade to ensure that + // we delete all signals + for (i= m_jobBuffer.size()-1; i>= 0; i--) + delete m_jobBuffer[i]; + for (i= m_usedBuffer.size()-1; i>= 0; i--) + delete m_usedBuffer[i]; + NdbCondition_Destroy(m_cond); +} + +int SignalSender::lock() +{ + if (NdbMutex_Lock(theFacade->theMutexPtr)) + return -1; + m_lock= 1; + return 0; +} + +int SignalSender::unlock() +{ + if (NdbMutex_Unlock(theFacade->theMutexPtr)) + return -1; + m_lock= 0; + return 0; +} + +Uint32 +SignalSender::getOwnRef() const { + return numberToRef(m_blockNo, theFacade->ownId()); +} + +Uint32 +SignalSender::getAliveNode() const{ + return theFacade->get_an_alive_node(); +} + +const ClusterMgr::Node & +SignalSender::getNodeInfo(Uint16 nodeId) const { + return theFacade->theClusterMgr->getNodeInfo(nodeId); +} + +Uint32 +SignalSender::getNoOfConnectedNodes() const { + return theFacade->theClusterMgr->getNoOfConnectedNodes(); +} + +SendStatus +SignalSender::sendSignal(Uint16 nodeId, const SimpleSignal * s){ + return theFacade->theTransporterRegistry->prepareSend(&s->header, + 1, // JBB + &s->theData[0], + nodeId, + &s->ptr[0]); +} + +template<class T> +SimpleSignal * +SignalSender::waitFor(Uint32 timeOutMillis, T & t) +{ + SimpleSignal * s = t.check(m_jobBuffer); + if(s != 0){ + return s; + } + + NDB_TICKS now = NdbTick_CurrentMillisecond(); + NDB_TICKS stop = now + timeOutMillis; + Uint32 wait = (timeOutMillis == 0 ? 10 : timeOutMillis); + do { + NdbCondition_WaitTimeout(m_cond, + theFacade->theMutexPtr, + wait); + + + SimpleSignal * s = t.check(m_jobBuffer); + if(s != 0){ + m_usedBuffer.push_back(s); + return s; + } + + now = NdbTick_CurrentMillisecond(); + wait = (timeOutMillis == 0 ? 10 : stop - now); + } while(stop > now || timeOutMillis == 0); + + return 0; +} + +class WaitForAny { +public: + SimpleSignal * check(Vector<SimpleSignal*> & m_jobBuffer){ + if(m_jobBuffer.size() > 0){ + SimpleSignal * s = m_jobBuffer[0]; + m_jobBuffer.erase(0); + return s; + } + return 0; + } +}; + +SimpleSignal * +SignalSender::waitFor(Uint32 timeOutMillis){ + + WaitForAny w; + return waitFor(timeOutMillis, w); +} + +class WaitForNode { +public: + Uint32 m_nodeId; + SimpleSignal * check(Vector<SimpleSignal*> & m_jobBuffer){ + Uint32 len = m_jobBuffer.size(); + for(Uint32 i = 0; i<len; i++){ + if(refToNode(m_jobBuffer[i]->header.theSendersBlockRef) == m_nodeId){ + SimpleSignal * s = m_jobBuffer[i]; + m_jobBuffer.erase(i); + return s; + } + } + return 0; + } +}; + +SimpleSignal * +SignalSender::waitFor(Uint16 nodeId, Uint32 timeOutMillis){ + + WaitForNode w; + w.m_nodeId = nodeId; + return waitFor(timeOutMillis, w); +} + +#include <NdbApiSignal.hpp> + +void +SignalSender::execSignal(void* signalSender, + NdbApiSignal* signal, + class LinearSectionPtr ptr[3]){ + SimpleSignal * s = new SimpleSignal(true); + s->header = * signal; + memcpy(&s->theData[0], signal->getDataPtr(), 4 * s->header.theLength); + for(Uint32 i = 0; i<s->header.m_noOfSections; i++){ + s->ptr[i].p = new Uint32[ptr[i].sz]; + s->ptr[i].sz = ptr[i].sz; + memcpy(s->ptr[i].p, ptr[i].p, 4 * ptr[i].sz); + } + SignalSender * ss = (SignalSender*)signalSender; + ss->m_jobBuffer.push_back(s); + NdbCondition_Signal(ss->m_cond); +} + +void +SignalSender::execNodeStatus(void* signalSender, + Uint32 nodeId, + bool alive, + bool nfCompleted){ + if (alive) { + // node connected + return; + } + + SimpleSignal * s = new SimpleSignal(true); + SignalSender * ss = (SignalSender*)signalSender; + + // node disconnected + if(nfCompleted) + { + // node shutdown complete + s->header.theVerId_signalNumber = GSN_NF_COMPLETEREP; + NFCompleteRep *rep = (NFCompleteRep *)s->getDataPtrSend(); + rep->failedNodeId = nodeId; + } + else + { + // node failure + s->header.theVerId_signalNumber = GSN_NODE_FAILREP; + NodeFailRep *rep = (NodeFailRep *)s->getDataPtrSend(); + rep->failNo = nodeId; + } + + ss->m_jobBuffer.push_back(s); + NdbCondition_Signal(ss->m_cond); +} + +template SimpleSignal* SignalSender::waitFor<WaitForNode>(unsigned, WaitForNode&); +template SimpleSignal* SignalSender::waitFor<WaitForAny>(unsigned, WaitForAny&); +template class Vector<SimpleSignal*>; diff --git a/ndb/src/ndbapi/SignalSender.hpp b/ndb/src/ndbapi/SignalSender.hpp new file mode 100644 index 00000000000..4b991460034 --- /dev/null +++ b/ndb/src/ndbapi/SignalSender.hpp @@ -0,0 +1,83 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef SIGNAL_SENDER_HPP +#define SIGNAL_SENDER_HPP + +#include <ndb_global.h> +#include "TransporterFacade.hpp" +#include <Vector.hpp> + +struct SimpleSignal { +public: + SimpleSignal(bool dealloc = false); + ~SimpleSignal(); + + void set(class SignalSender&, + Uint8 trace, Uint16 recBlock, Uint16 gsn, Uint32 len); + + struct SignalHeader header; + Uint32 theData[25]; + LinearSectionPtr ptr[3]; + + int readSignalNumber() {return header.theVerId_signalNumber; } + Uint32 *getDataPtrSend() { return theData; } + const Uint32 *getDataPtr() const { return theData; } + + void print(FILE * out = stdout); +private: + bool deallocSections; +}; + +class SignalSender { +public: + SignalSender(TransporterFacade *facade); + virtual ~SignalSender(); + + int lock(); + int unlock(); + + Uint32 getOwnRef() const; + Uint32 getAliveNode() const; + const ClusterMgr::Node &getNodeInfo(Uint16 nodeId) const; + Uint32 getNoOfConnectedNodes() const; + + SendStatus sendSignal(Uint16 nodeId, const SimpleSignal *); + + SimpleSignal * waitFor(Uint32 timeOutMillis = 0); + SimpleSignal * waitFor(Uint16 nodeId, Uint32 timeOutMillis = 0); + SimpleSignal * waitFor(Uint16 nodeId, Uint16 gsn, Uint32 timeOutMillis = 0); +private: + int m_blockNo; + TransporterFacade * theFacade; + + static void execSignal(void* signalSender, + NdbApiSignal* signal, + class LinearSectionPtr ptr[3]); + + static void execNodeStatus(void* signalSender, Uint32 nodeId, + bool alive, bool nfCompleted); + + int m_lock; + struct NdbCondition * m_cond; + Vector<SimpleSignal *> m_jobBuffer; + Vector<SimpleSignal *> m_usedBuffer; + + template<class T> + SimpleSignal * waitFor(Uint32 timeOutMillis, T & t); +}; + +#endif diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index a336a4379bd..3a9302483b4 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -652,7 +652,8 @@ int ha_tina::rnd_init(bool scan) records= 0; chain_ptr= chain; #ifdef HAVE_MADVISE - (void)madvise(share->mapped_file,share->file_stat.st_size,MADV_SEQUENTIAL); + if (scan) + (void)madvise(share->mapped_file,share->file_stat.st_size,MADV_SEQUENTIAL); #endif DBUG_RETURN(0); diff --git a/sql/handler.cc b/sql/handler.cc index 451615bead1..3e85e73cab5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -632,12 +632,20 @@ int ha_prepare(THD *thd) { int err; statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status); - if ((err= (*(*ht)->prepare)(thd, all))) + if ((*ht)->prepare) { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); - ha_rollback_trans(thd, all); - error=1; - break; + if ((err= (*(*ht)->prepare)(thd, all))) + { + my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); + ha_rollback_trans(thd, all); + error=1; + break; + } + } + else + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), (*ht)->name); } } } diff --git a/sql/item.cc b/sql/item.cc index 66552fa7b04..1cb6734d373 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -818,8 +818,25 @@ String *Item_splocal::val_str(String *sp) DBUG_ASSERT(fixed); Item *it= this_item(); String *ret= it->val_str(sp); + /* + This way we mark returned value of val_str as const, + so that various functions (e.g. CONCAT) won't try to + modify the value of the Item. Analogous mechanism is + implemented for Item_param. + Without this trick Item_splocal could be changed as a + side-effect of expression computation. Here is an example + of what happens without it: suppose x is varchar local + variable in a SP with initial value 'ab' Then + select concat(x,'c'); + would change x's value to 'abc', as Item_func_concat::val_str() + would use x's internal buffer to compute the result. + This is intended behaviour of Item_func_concat. Comments to + Item_param class contain some more details on the topic. + */ + str_value_ptr.set(ret->ptr(), ret->length(), + ret->charset()); null_value= it->null_value; - return ret; + return &str_value_ptr; } @@ -1022,9 +1039,9 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, /* Will split complicated items and ignore simple ones */ split_sum_func(thd, ref_pointer_array, fields); } - else if ((type() == SUM_FUNC_ITEM || - (used_tables() & ~PARAM_TABLE_BIT)) && - type() != REF_ITEM) + else if ((type() == SUM_FUNC_ITEM || (used_tables() & ~PARAM_TABLE_BIT)) && + (type() != REF_ITEM || + ((Item_ref*)this)->ref_type() == Item_ref::VIEW_REF)) { /* Replace item with a reference so that we can easily calculate @@ -1033,15 +1050,17 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, The test above is to ensure we don't do a reference for things that are constants (PARAM_TABLE_BIT is in effect a constant) or already referenced (for example an item in HAVING) + Exception is Item_direct_view_ref which we need to convert to + Item_ref to allow fields from view being stored in tmp table. */ uint el= fields.elements; - Item *new_item; - ref_pointer_array[el]= this; + Item *new_item, *real_itm= real_item(); + + ref_pointer_array[el]= real_itm; if (!(new_item= new Item_ref(&thd->lex->current_select->context, ref_pointer_array + el, 0, name))) return; // fatal_error is set - fields.push_front(this); - ref_pointer_array[el]= this; + fields.push_front(real_itm); thd->change_item_tree(ref, new_item); } } diff --git a/sql/item.h b/sql/item.h index b934e1f9f3f..381ba98e193 100644 --- a/sql/item.h +++ b/sql/item.h @@ -715,9 +715,17 @@ public: class Item_splocal : public Item { uint m_offset; + public: LEX_STRING m_name; + /* + Buffer, pointing to the string value of the item. We need it to + protect internal buffer from changes. See comment to analogous + member in Item_param for more details. + */ + String str_value_ptr; + /* Position of this reference to SP variable in the statement (the statement itself is in sp_instr_stmt::m_query). @@ -1537,6 +1545,7 @@ class Item_ref :public Item_ident protected: void set_properties(); public: + enum Ref_Type { REF, DIRECT_REF, VIEW_REF }; Field *result_field; /* Save result here */ Item **ref; Item_ref(Name_resolution_context *context_arg, @@ -1617,6 +1626,7 @@ public: void cleanup(); Item_field *filed_for_view_update() { return (*ref)->filed_for_view_update(); } + virtual Ref_Type ref_type() { return REF; } }; @@ -1641,6 +1651,7 @@ public: bool val_bool(); bool is_null(); bool get_date(TIME *ltime,uint fuzzydate); + virtual Ref_Type ref_type() { return DIRECT_REF; } }; /* @@ -1660,6 +1671,7 @@ public: bool fix_fields(THD *, Item **); bool eq(const Item *item, bool binary_cmp) const; + virtual Ref_Type ref_type() { return VIEW_REF; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index cc2849ff7e6..5079c462ac0 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -988,6 +988,53 @@ longlong Item_func_interval::val_int() } +/* + Perform context analysis of a BETWEEN item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_between as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(e BETWEEN e1 AND e2) = union(T1(e),T1(e1),T1(e2)) + T1(e BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + T0(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + T1(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_between::fix_fields(THD *thd, Item **ref) +{ + if (Item_func_opt_neg::fix_fields(thd, ref)) + return 1; + + /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */ + if (pred_level && !negated) + return 0; + + /* not_null_tables_cache == union(T1(e), intersection(T1(e1),T1(e2))) */ + not_null_tables_cache= args[0]->not_null_tables() | + (args[1]->not_null_tables() & args[2]->not_null_tables()); + + return 0; +} + + void Item_func_between::fix_length_and_dec() { max_length= 1; @@ -1040,8 +1087,9 @@ longlong Item_func_between::val_int() a=args[1]->val_str(&value1); b=args[2]->val_str(&value2); if (!args[1]->null_value && !args[2]->null_value) - return (sortcmp(value,a,cmp_collation.collation) >= 0 && - sortcmp(value,b,cmp_collation.collation) <= 0) ? 1 : 0; + return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 && + sortcmp(value,b,cmp_collation.collation) <= 0) != + negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1063,7 +1111,7 @@ longlong Item_func_between::val_int() a=args[1]->val_int(); b=args[2]->val_int(); if (!args[1]->null_value && !args[2]->null_value) - return (value >= a && value <= b) ? 1 : 0; + return (longlong) ((value >= a && value <= b) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1084,8 +1132,8 @@ longlong Item_func_between::val_int() a_dec= args[1]->val_decimal(&a_buf); b_dec= args[2]->val_decimal(&b_buf); if (!args[1]->null_value && !args[2]->null_value) - return (my_decimal_cmp(dec, a_dec)>=0) && (my_decimal_cmp(dec, b_dec)<=0); - + return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 && + my_decimal_cmp(dec, b_dec) <= 0) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1101,7 +1149,7 @@ longlong Item_func_between::val_int() a= args[1]->val_real(); b= args[2]->val_real(); if (!args[1]->null_value && !args[2]->null_value) - return (value >= a && value <= b) ? 1 : 0; + return (longlong) ((value >= a && value <= b) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1113,7 +1161,7 @@ longlong Item_func_between::val_int() null_value= value >= a; } } - return 0; + return (longlong) (!null_value && negated); } @@ -1244,6 +1292,49 @@ Item_func_ifnull::str_op(String *str) } +/* + Perform context analysis of an IF item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_if as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(IF(e,e1,e2) = T1(IF(e,e1,e2)) + T1(IF(e,e1,e2)) = intersection(T1(e1),T1(e2)) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_if::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + args[0]->top_level_item(); + + if (Item_func::fix_fields(thd, ref)) + return 1; + + not_null_tables_cache= (args[1]->not_null_tables() + & args[2]->not_null_tables()); + + return 0; +} + + void Item_func_if::fix_length_and_dec() { @@ -2184,6 +2275,56 @@ bool Item_func_in::nulls_in_row() } +/* + Perform context analysis of an IN item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_in as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + T1(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + T0(e NOT IN(e1,...,en)) = union(T1(e),union(T1(ei))) + T1(e NOT IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_in::fix_fields(THD *thd, Item **ref) +{ + Item **arg, **arg_end; + + if (Item_func_opt_neg::fix_fields(thd, ref)) + return 1; + + /* not_null_tables_cache == union(T1(e),union(T1(ei))) */ + if (pred_level && negated) + return 0; + + /* not_null_tables_cache = union(T1(e),intersection(T1(ei))) */ + not_null_tables_cache= ~(table_map) 0; + for (arg= args + 1, arg_end= args + arg_count; arg != arg_end; arg++) + not_null_tables_cache&= (*arg)->not_null_tables(); + not_null_tables_cache|= (*args)->not_null_tables(); + return 0; +} + + static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) { return cs->coll->strnncollsp(cs, @@ -2283,7 +2424,7 @@ longlong Item_func_in::val_int() { int tmp=array->find(args[0]); null_value=args[0]->null_value || (!tmp && have_null); - return tmp; + return (longlong) (!null_value && tmp != negated); } in_item->store_value(args[0]); if ((null_value=args[0]->null_value)) @@ -2292,11 +2433,11 @@ longlong Item_func_in::val_int() for (uint i=1 ; i < arg_count ; i++) { if (!in_item->cmp(args[i]) && !args[i]->null_value) - return 1; // Would maybe be nice with i ? + return (longlong) (!negated); have_null|= args[i]->null_value; } null_value= have_null; - return 0; + return (longlong) (!null_value && negated); } @@ -2811,7 +2952,42 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) { /* If we are on execution stage */ String *escape_str= escape_item->val_str(&tmp_value1); - escape= escape_str ? *(escape_str->ptr()) : '\\'; + if (escape_str) + { + CHARSET_INFO *cs= cmp.cmp_collation.collation; + if (use_mb(cs)) + { + my_wc_t wc; + int rc= cs->cset->mb_wc(cs, &wc, + (const uchar*) escape_str->ptr(), + (const uchar*) escape_str->ptr() + + escape_str->length()); + escape= (int) (rc > 0 ? wc : '\\'); + } + else + { + /* + In the case of 8bit character set, we pass native + code instead of Unicode code as "escape" argument. + Convert to "cs" if charset of escape differs. + */ + uint32 unused; + if (escape_str->needs_conversion(escape_str->length(), + escape_str->charset(), cs, &unused)) + { + char ch; + uint errors; + uint32 cnvlen= copy_and_convert(&ch, 1, cs, escape_str->ptr(), + escape_str->length(), + escape_str->charset(), &errors); + escape= cnvlen ? ch : '\\'; + } + else + escape= *(escape_str->ptr()); + } + } + else + escape= '\\'; /* We could also do boyer-more for non-const items, but as we would have to diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1915dbaabf6..09a0fa8c357 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -401,17 +401,49 @@ public: }; -class Item_func_between :public Item_int_func +/* + The class Item_func_opt_neg is defined to factor out the functionality + common for the classes Item_func_between and Item_func_in. The objects + of these classes can express predicates or there negations. + The alternative approach would be to create pairs Item_func_between, + Item_func_notbetween and Item_func_in, Item_func_notin. + +*/ + +class Item_func_opt_neg :public Item_int_func +{ +public: + bool negated; /* <=> the item represents NOT <func> */ + bool pred_level; /* <=> [NOT] <func> is used on a predicate level */ +public: + Item_func_opt_neg(Item *a, Item *b, Item *c) + :Item_int_func(a, b, c), negated(0), pred_level(0) {} + Item_func_opt_neg(List<Item> &list) + :Item_int_func(list), negated(0), pred_level(0) {} +public: + inline void negate() { negated= !negated; } + inline void top_level_item() { pred_level= 1; } + Item *neg_transformer(THD *thd) + { + negated= !negated; + return this; + } +}; + + +class Item_func_between :public Item_func_opt_neg { DTCollation cmp_collation; public: Item_result cmp_type; String value0,value1,value2; - Item_func_between(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {} + Item_func_between(Item *a, Item *b, Item *c) + :Item_func_opt_neg(a, b, c) {} longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_KEY; } enum Functype functype() const { return BETWEEN; } const char *func_name() const { return "between"; } + bool fix_fields(THD *, Item **); void fix_length_and_dec(); void print(String *str); bool is_bool_func() { return 1; } @@ -505,16 +537,10 @@ public: String *val_str(String *str); my_decimal *val_decimal(my_decimal *); enum Item_result result_type () const { return cached_result_type; } - bool fix_fields(THD *thd, Item **ref) - { - DBUG_ASSERT(fixed == 0); - args[0]->top_level_item(); - return Item_func::fix_fields(thd, ref); - } + bool fix_fields(THD *, Item **); void fix_length_and_dec(); uint decimal_precision() const; const char *func_name() const { return "if"; } - table_map not_null_tables() const { return 0; } }; @@ -819,7 +845,7 @@ public: } }; -class Item_func_in :public Item_int_func +class Item_func_in :public Item_func_opt_neg { Item_result cmp_type; in_vector *array; @@ -828,11 +854,12 @@ class Item_func_in :public Item_int_func DTCollation cmp_collation; public: Item_func_in(List<Item> &list) - :Item_int_func(list), array(0), in_item(0), have_null(0) + :Item_func_opt_neg(list), array(0), in_item(0), have_null(0) { allowed_arg_cols= 0; // Fetch this value from first argument } longlong val_int(); + bool fix_fields(THD *, Item **); void fix_length_and_dec(); uint decimal_precision() const { return 1; } void cleanup() @@ -853,12 +880,6 @@ class Item_func_in :public Item_int_func bool nulls_in_row(); bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } - /* - IN() protect from NULL only first argument, if construction like - "expression IN ()" will be allowed, we will need to check number of - argument here, because "NOT(NULL IN ())" is TRUE. - */ - table_map not_null_tables() const { return args[0]->not_null_tables(); } }; /* Functions used by where clause */ @@ -889,7 +910,7 @@ public: else { args[0]->update_used_tables(); - if (!(used_tables_cache=args[0]->used_tables())) + if ((const_item_cache= !(used_tables_cache= args[0]->used_tables()))) { /* Remember if the value is always NULL or never NULL */ cached_value= (longlong) args[0]->is_null(); @@ -966,7 +987,7 @@ class Item_func_like :public Item_bool_func2 Item *escape_item; public: - char escape; + int escape; Item_func_like(Item *a,Item *b, Item *escape_arg) :Item_bool_func2(a,b), canDoTurboBM(FALSE), pattern(0), pattern_len(0), diff --git a/sql/item_func.cc b/sql/item_func.cc index 7400a569342..b47d7d19fbd 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3884,7 +3884,8 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, if (!(var_entry= get_variable(&thd->user_vars, name, 0))) goto err; } - else if (var_entry->used_query_id == thd->query_id) + else if (var_entry->used_query_id == thd->query_id || + mysql_bin_log.is_query_in_union(thd, var_entry->used_query_id)) { /* If this variable was already stored in user_var_events by this query @@ -3901,10 +3902,16 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, appears: > set @a:=1; > insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1); - We have to write to binlog value @a= 1; + We have to write to binlog value @a= 1. + + We allocate the user_var_event on user_var_events_alloc pool, not on + the this-statement-execution pool because in SPs user_var_event objects + may need to be valid after current [SP] statement execution pool is + destroyed. */ - size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length; - if (!(user_var_event= (BINLOG_USER_VAR_EVENT *) thd->alloc(size))) + size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length; + if (!(user_var_event= (BINLOG_USER_VAR_EVENT *) + alloc_root(thd->user_var_events_alloc, size))) goto err; user_var_event->value= (char*) user_var_event + diff --git a/sql/item_sum.cc b/sql/item_sum.cc index f6544d76504..4f991615bfa 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2740,7 +2740,9 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)), String *result= &item->result; Item **arg= item->args, **arg_end= item->args + item->arg_count_field; - if (result->length()) + if (item->no_appended) + item->no_appended= FALSE; + else result->append(*item->separator); tmp.length(0); @@ -2925,6 +2927,7 @@ void Item_func_group_concat::clear() result.copy(); null_value= TRUE; warning_for_row= FALSE; + no_appended= TRUE; if (tree) reset_tree(tree); /* No need to reset the table as we never call write_row */ @@ -3001,6 +3004,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) args, arg_count, MY_COLL_ALLOW_CONV)) return 1; + result.set_charset(collation.collation); result_field= 0; null_value= 1; thd->allow_sum_func= 1; diff --git a/sql/item_sum.h b/sql/item_sum.h index 0da9178eabf..87cc248e5e4 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -881,6 +881,7 @@ class Item_func_group_concat : public Item_sum bool distinct; bool warning_for_row; bool always_null; + bool no_appended; /* Following is 0 normal object and pointer to original one for copy (to correctly free resources) @@ -912,8 +913,8 @@ public: virtual Item_result result_type () const { return STRING_RESULT; } void clear(); bool add(); - void reset_field() {} // not used - void update_field() {} // not used + void reset_field() { DBUG_ASSERT(0); } // not used + void update_field() { DBUG_ASSERT(0); } // not used bool fix_fields(THD *,Item **); bool setup(THD *thd); void make_unique(); diff --git a/sql/log.cc b/sql/log.cc index d3bb44b3083..9d9f500fe80 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1560,6 +1560,7 @@ void MYSQL_LOG::start_union_events(THD *thd) thd->binlog_evt_union.do_union= TRUE; thd->binlog_evt_union.unioned_events= FALSE; thd->binlog_evt_union.unioned_events_trans= FALSE; + thd->binlog_evt_union.first_query_id= thd->query_id; } void MYSQL_LOG::stop_union_events(THD *thd) @@ -1568,6 +1569,12 @@ void MYSQL_LOG::stop_union_events(THD *thd) thd->binlog_evt_union.do_union= FALSE; } +bool MYSQL_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) +{ + return (thd->binlog_evt_union.do_union && + query_id_param >= thd->binlog_evt_union.first_query_id); +} + /* Write an event to the binary log */ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 31b55219636..b69822d201b 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3529,18 +3529,9 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) } Item_func *cond_func= (Item_func*) cond; - if (cond_func->functype() == Item_func::NOT_FUNC) - { - /* Optimize NOT BETWEEN and NOT IN */ - Item *arg= cond_func->arguments()[0]; - if (arg->type() != Item::FUNC_ITEM) - DBUG_RETURN(0); - cond_func= (Item_func*) arg; - if (cond_func->functype() != Item_func::BETWEEN && - cond_func->functype() != Item_func::IN_FUNC) - DBUG_RETURN(0); - inv= TRUE; - } + if (cond_func->functype() == Item_func::BETWEEN || + cond_func->functype() == Item_func::IN_FUNC) + inv= ((Item_func_opt_neg *) cond_func)->negated; else if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) DBUG_RETURN(0); diff --git a/sql/set_var.cc b/sql/set_var.cc index 0f1b523529f..f1ab86ace51 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -142,11 +142,8 @@ sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size", &SV::bulk_insert_buff_size); sys_var_character_set_server sys_character_set_server("character_set_server"); -sys_var_str sys_charset_system("character_set_system", - sys_check_charset, - sys_update_charset, - sys_set_default_charset, - (char *)my_charset_utf8_general_ci.name); +sys_var_const_str sys_charset_system("character_set_system", + (char *)my_charset_utf8_general_ci.name); sys_var_character_set_database sys_character_set_database("character_set_database"); sys_var_character_set_client sys_character_set_client("character_set_client"); sys_var_character_set_connection sys_character_set_connection("character_set_connection"); @@ -571,6 +568,7 @@ sys_var *sys_variables[]= &sys_character_set_client, &sys_character_set_connection, &sys_character_set_results, + &sys_charset_system, &sys_collation_connection, &sys_collation_database, &sys_collation_server, @@ -1122,27 +1120,6 @@ static void sys_default_ftb_syntax(THD *thd, enum_var_type type) sizeof(ft_boolean_syntax)-1); } -/* - The following 3 functions need to be changed in 4.1 when we allow - one to change character sets -*/ - -static int sys_check_charset(THD *thd, set_var *var) -{ - return 0; -} - - -static bool sys_update_charset(THD *thd, set_var *var) -{ - return 0; -} - - -static void sys_set_default_charset(THD *thd, enum_var_type type) -{ -} - /* If one sets the LOW_PRIORIY UPDATES flag, we also must change the diff --git a/sql/set_var.h b/sql/set_var.h index b1c847973c1..8c1444870eb 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -190,6 +190,7 @@ public: return 1; } bool check_default(enum_var_type type) { return 1; } + bool is_readonly() const { return 1; } }; @@ -901,7 +902,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list); bool not_all_support_one_shot(List<set_var_base> *var_list); void fix_delay_key_write(THD *thd, enum_var_type type); ulong fix_sql_mode(ulong sql_mode); -extern sys_var_str sys_charset_system; +extern sys_var_const_str sys_charset_system; extern sys_var_str sys_init_connect; extern sys_var_str sys_init_slave; extern sys_var_thd_time_zone sys_time_zone; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 14956138cbf..1ab8e0c205c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -275,8 +275,19 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, } DBUG_PRINT("info",("STRING_RESULT: %*s", s->length(), s->c_ptr_quick())); - CHARSET_INFO *itcs= it->collation.collation; - CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_string(itcs), + /* + Reuse mechanism in sp_eval_func_item() is only employed for assignments + to local variables and OUT/INOUT SP parameters repsesented by + Item_splocal. Usually we have some expression, which needs + to be calculated and stored into the local variable. However in the + case if "it" equals to "reuse", there is no "calculation" step. So, + no reason to employ reuse mechanism to save variable into itself. + */ + if (it == reuse) + DBUG_RETURN(it); + + CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) + Item_string(it->collation.collation), use_callers_arena, &backup_arena); /* We have to use special constructor and allocate string @@ -678,10 +689,35 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) * If this function invocation is done from a statement that is written into the binary log. * If there were any attempts to write events to the binary log during - function execution. + function execution (grep for start_union_events and stop_union_events) + If the answers are No and Yes, we write the function call into the binary log as "DO spfunc(<param1value>, <param2value>, ...)" - + + + 4. Miscellaneous issues. + + 4.1 User variables. + + When we call mysql_bin_log.write() for an SP statement, thd->user_var_events + must hold set<{var_name, value}> pairs for all user variables used during + the statement execution. + This set is produced by tracking user variable reads during statement + execution. + + Fo SPs, this has the following implications: + 1) thd->user_var_events may contain events from several SP statements and + needs to be valid after exection of these statements was finished. In + order to achieve that, we + * Allocate user_var_events array elements on appropriate mem_root (grep + for user_var_events_alloc). + * Use is_query_in_union() to determine if user_var_event is created. + + 2) We need to empty thd->user_var_events after we have wrote a function + call. This is currently done by making + reset_dynamic(&thd->user_var_events); + calls in several different places. (TODO cosider moving this into + mysql_bin_log.write() function) */ @@ -897,6 +933,7 @@ int sp_head::execute(THD *thd) /* Don't change NOW() in FUNCTION or TRIGGER */ if (!thd->in_sub_stmt) thd->set_time(); // Make current_time() et al work + /* We have to set thd->stmt_arena before executing the instruction to store in the instruction free_list all new items, created @@ -904,6 +941,13 @@ int sp_head::execute(THD *thd) items made during other permanent subquery transformations). */ thd->stmt_arena= i; + + /* will binlog this separately */ + if (thd->prelocked_mode == NON_PRELOCKED) //TODO: change to event union? + { + thd->user_var_events_alloc= thd->mem_root; + } + ret= i->execute(thd, &ip); /* @@ -918,15 +962,6 @@ int sp_head::execute(THD *thd) /* we should cleanup free_list and memroot, used by instruction */ thd->free_items(); - /* - FIXME: we must free user var events only if the routine is executed - in non-prelocked mode and statement-by-statement replication is used. - But if we don't free them now, the server crashes because user var - events are allocated in execute_mem_root. This is Bug#12637, and when - it's fixed, please add if (thd->options & OPTION_BIN_LOG) here. - */ - if (opt_bin_log) - reset_dynamic(&thd->user_var_events); free_root(&execute_mem_root, MYF(0)); /* @@ -1084,7 +1119,10 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) binlog_save_options= thd->options; need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG); if (need_binlog_call) + { + reset_dynamic(&thd->user_var_events); mysql_bin_log.start_union_events(thd); + } thd->options&= ~OPTION_BIN_LOG; ret= execute(thd); @@ -1118,6 +1156,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) "Invoked ROUTINE modified a transactional table but MySQL " "failed to reflect this change in the binary log"); } + reset_dynamic(&thd->user_var_events); } if (m_type == TYPE_ENUM_FUNCTION && ret == 0) @@ -1827,17 +1866,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, thd->query_id= next_query_id(); VOID(pthread_mutex_unlock(&LOCK_thread_count)); - /* - FIXME. Resetting statement (and using it) is not reentrant, thus recursive - functions which try to use the same LEX twice will crash server. - We should prevent such situations by tracking if LEX is already - in use and throwing error about unallowed recursion if needed. - OTOH it is nice to allow recursion in cases when LEX is not really - used (e.g. in mathematical functions), so such tracking should be - implemented at the same time as ability not to store LEX for - instruction if it is not really used. - */ - if (thd->prelocked_mode == NON_PRELOCKED) { /* diff --git a/sql/sp_head.h b/sql/sp_head.h index 1c54b1a567d..271119ff2fb 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -108,13 +108,14 @@ class sp_head :private Query_arena MEM_ROOT main_mem_root; public: /* Possible values of m_flags */ - const static int + enum { HAS_RETURN= 1, // For FUNCTIONs only: is set if has RETURN IN_SIMPLE_CASE= 2, // Is set if parsing a simple CASE IN_HANDLER= 4, // Is set if the parser is in a handler body MULTI_RESULTS= 8, // Is set if a procedure with SELECT(s) CONTAINS_DYNAMIC_SQL= 16, // Is set if a procedure with PREPARE/EXECUTE - IS_INVOKED= 32; // Is set if this sp_head is being used. + IS_INVOKED= 32 // Is set if this sp_head is being used. + }; int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE uint m_flags; // Boolean attributes of a stored routine diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b7831a2a5b5..f578876bc5e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2617,6 +2617,8 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, table_list->alias, name, item_name, (ulong) ref)); Field_iterator_view field_it; field_it.set(table_list); + Query_arena *arena, backup; + DBUG_ASSERT(table_list->schema_table_reformed || (ref != 0 && table_list->view != 0)); for (; !field_it.end_of_fields(); field_it.next()) @@ -2638,7 +2640,13 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, name, length)) DBUG_RETURN(WRONG_GRANT); #endif + // in PS use own arena or data will be freed after prepare + if (register_tree_change) + arena= thd->activate_stmt_arena_if_needed(&backup); Item *item= field_it.create_item(thd); + if (register_tree_change && arena) + thd->restore_active_arena(arena, &backup); + if (!item) DBUG_RETURN(0); /* @@ -2700,6 +2708,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, field_it(*(table_ref->join_columns)); Natural_join_column *nj_col; Field *found_field; + Query_arena *arena, backup; + DBUG_ENTER("find_field_in_natural_join"); DBUG_PRINT("enter", ("field name: '%s', ref 0x%lx", name, (ulong) ref)); @@ -2728,7 +2738,14 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, The found field is a view field, we do as in find_field_in_view() and return a pointer to pointer to the Item of that field. */ + if (register_tree_change) + arena= thd->activate_stmt_arena_if_needed(&backup); + Item *item= nj_col->create_item(thd); + + if (register_tree_change && arena) + thd->restore_active_arena(arena, &backup); + if (!item) DBUG_RETURN(NULL); DBUG_ASSERT(nj_col->table_field == NULL); @@ -2882,14 +2899,15 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, /* Check that the table and database that qualify the current field name are the same as the table we are going to search for the field. - This is done differently for NATURAL/USING joins because there we can't - simply compare the qualifying table and database names with the ones of + This is done differently for NATURAL/USING joins or nested joins that + are operands of NATURAL/USING joins because there we can't simply + compare the qualifying table and database names with the ones of 'table_list' because each field in such a join may originate from a different table. TODO: Ensure that table_name, db_name and tables->db always points to something ! */ - if (!table_list->is_natural_join && + if (!(table_list->nested_join && table_list->join_columns) && table_name && table_name[0] && (my_strcasecmp(table_alias_charset, table_list->alias, table_name) || (db_name && db_name[0] && table_list->db && table_list->db[0] && @@ -2904,8 +2922,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, register_tree_change))) *actual_table= table_list; } - else if (table_list->is_natural_join) + else if (table_list->nested_join && table_list->join_columns) { + /* + If this is a NATURAL/USING join, or an operand of such join which is a + join itself, and the field name is qualified, then search for the field + in the operands of the join. + */ if (table_name && table_name[0]) { /* @@ -2927,7 +2950,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, } /* Non-qualified field, search directly in the result columns of the - natural join. + natural join. The condition of the outer IF is true for the top-most + natural join, thus if the field is not qualified, we will search + directly the top-most NATURAL/USING join. */ fld= find_field_in_natural_join(thd, table_list, name, length, ref, /* TIMOUR_TODO: check this with Sanja */ @@ -3569,10 +3594,16 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, if (add_columns && is_created_2) table_ref_2->join_columns->push_back(cur_nj_col_2); - /* Compare the two columns and check for duplicate common fields. */ + /* + Compare the two columns and check for duplicate common fields. + A common field is duplicate either if it was already found in + table_ref_2 (then found == TRUE), or if a field in table_ref_2 + was already matched by some previous field in table_ref_1 + (then cur_nj_col_2->is_common == TRUE). + */ if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) { - if (found) + if (found || cur_nj_col_2->is_common) { my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); goto err; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 2ff0413e05e..975014b9780 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -377,14 +377,16 @@ void THD::cleanup(void) mysql_ha_flush(this, (TABLE_LIST*) 0, MYSQL_HA_CLOSE_FINAL | MYSQL_HA_FLUSH_ALL); hash_free(&handler_tables_hash); + delete_dynamic(&user_var_events); + hash_free(&user_vars); close_temporary_tables(this); my_free((char*) variables.time_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.date_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR)); - delete_dynamic(&user_var_events); - hash_free(&user_vars); + sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); + if (global_read_lock) unlock_global_read_lock(this); if (ull) @@ -424,9 +426,6 @@ THD::~THD() ha_close_connection(this); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); - DBUG_PRINT("info", ("freeing host")); if (host != my_localhost) // If not pointer to constant safeFree(host); diff --git a/sql/sql_class.h b/sql/sql_class.h index 4f2b3dad5a3..5ce2f7d8847 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -313,6 +313,7 @@ public: void start_union_events(THD *thd); void stop_union_events(THD *thd); + bool is_query_in_union(THD *thd, query_id_t query_id_param); /* v stands for vector @@ -1312,8 +1313,9 @@ public: /* variables.transaction_isolation is reset to this after each commit */ enum_tx_isolation session_tx_isolation; enum_check_fields count_cuted_fields; - /* for user variables replication*/ - DYNAMIC_ARRAY user_var_events; + + DYNAMIC_ARRAY user_var_events; /* For user variables replication */ + MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */ enum killed_state { NOT_KILLED=0, KILL_BAD_DATA=1, KILL_CONNECTION=ER_SERVER_SHUTDOWN, KILL_QUERY=ER_QUERY_INTERRUPTED }; killed_state volatile killed; @@ -1375,6 +1377,12 @@ public: mysql_bin_log.start_union_events() call. */ bool unioned_events_trans; + + /* + 'queries' (actually SP statements) that run under inside this binlog + union have thd->query_id >= first_query_id. + */ + query_id_t first_query_id; } binlog_evt_union; THD(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 38a8d6fd3bb..c2616ff3ef3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2442,6 +2442,12 @@ mysql_execute_command(THD *thd) { if (lex->describe) { + /* + We always use select_send for EXPLAIN, even if it's an EXPLAIN + for SELECT ... INTO OUTFILE: a user application should be able + to prepend EXPLAIN to any query and receive output for it, + even if the query itself redirects the output. + */ if (!(result= new select_send())) goto error; else @@ -5166,7 +5172,10 @@ void mysql_reset_thd_for_next_command(THD *thd) if (!thd->in_sub_stmt) { if (opt_bin_log) + { reset_dynamic(&thd->user_var_events); + thd->user_var_events_alloc= thd->mem_root; + } thd->clear_error(); thd->total_warn_count=0; // Warnings for this query thd->rand_used= 0; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dbf8ea62e88..8d415419336 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2874,19 +2874,6 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, if (cond->type() != Item::FUNC_ITEM) return; Item_func *cond_func= (Item_func*) cond; - if (cond_func->functype() == Item_func::NOT_FUNC) - { - Item *item= cond_func->arguments()[0]; - /* - At this moment all NOT before simple comparison predicates - are eliminated. NOT IN and NOT BETWEEN are treated similar - IN and BETWEEN respectively. - */ - if (item->type() == Item::FUNC_ITEM && - ((Item_func *) item)->select_optimize() == Item_func::OPTIMIZE_KEY) - add_key_fields(key_fields,and_level,item,usable_tables); - return; - } switch (cond_func->select_optimize()) { case Item_func::OPTIMIZE_NONE: break; @@ -13087,6 +13074,8 @@ void free_underlaid_joins(THD *thd, SELECT_LEX *select) The function replaces occurrences of group by fields in expr by ref objects for these fields unless they are under aggregate functions. + The function also corrects value of the the maybe_null attribute + for the items of all subexpressions containing group by fields. IMPLEMENTATION The function recursively traverses the tree of the expr expression, @@ -13097,6 +13086,9 @@ void free_underlaid_joins(THD *thd, SELECT_LEX *select) This substitution is needed GROUP BY queries with ROLLUP if SELECT list contains expressions over group by attributes. + TODO: Some functions are not null-preserving. For those functions + updating of the maybe_null attribute is an overkill. + EXAMPLES SELECT a+1 FROM t1 GROUP BY a WITH ROLLUP SELECT SUM(a)+a FROM t1 GROUP BY a WITH ROLLUP @@ -13118,6 +13110,7 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list, arg != arg_end; arg++) { Item *item= *arg; + bool arg_changed= FALSE; if (item->type() == Item::FIELD_ITEM || item->type() == Item::REF_ITEM) { ORDER *group_tmp; @@ -13130,15 +13123,20 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list, item->name))) return 1; // fatal_error is set thd->change_item_tree(arg, new_item); - *changed= TRUE; + arg_changed= TRUE; } } } else if (item->type() == Item::FUNC_ITEM) { - if (change_group_ref(thd, (Item_func *) item, group_list, changed)) + if (change_group_ref(thd, (Item_func *) item, group_list, &arg_changed)) return 1; } + if (arg_changed) + { + expr->maybe_null= 1; + *changed= TRUE; + } } } return 0; @@ -13201,7 +13199,7 @@ bool JOIN::rollup_init() } if (item->type() == Item::FUNC_ITEM) { - bool changed= 0; + bool changed= FALSE; if (change_group_ref(thd, (Item_func *) item, group_list, &changed)) return 1; /* diff --git a/sql/sql_select.h b/sql/sql_select.h index 77a3f872064..ce40f657a8e 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -231,7 +231,7 @@ class JOIN :public Sql_alloc /* Is set if we have a GROUP BY and we have ORDER BY on a constant. */ bool skip_sort_order; - bool need_tmp, hidden_group_fields, buffer_result; + bool need_tmp, hidden_group_fields; DYNAMIC_ARRAY keyuse; Item::cond_result cond_value; List<Item> all_fields; // to store all fields that used in query @@ -300,8 +300,6 @@ class JOIN :public Sql_alloc skip_sort_order= 0; need_tmp= 0; hidden_group_fields= 0; /*safety*/ - buffer_result= test(select_options & OPTION_BUFFER_RESULT) && - !test(select_options & OPTION_FOUND_ROWS); error= 0; select= 0; return_tab= 0; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 934f8bc9952..c346f4cc291 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2010,10 +2010,20 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) /* get_all_tables() returns 1 on failure and 0 on success thus return only these and not the result code of ::process_table() + + We should use show_table_list->alias instead of + show_table_list->table_name because table_name + could be changed during opening of I_S tables. It's safe + to use alias because alias contains original table name + in this case(this part of code is used only for + 'show columns' & 'show statistics' commands). */ error= test(schema_table->process_table(thd, show_table_list, - table, res, show_table_list->db, - show_table_list->alias)); + table, res, + (show_table_list->view ? + show_table_list->view_db.str : + show_table_list->db), + show_table_list->alias)); close_thread_tables(thd); show_table_list->table= 0; goto err; @@ -2114,6 +2124,13 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) lex->derived_tables= 0; res= open_normal_and_derived_tables(thd, show_table_list, MYSQL_LOCK_IGNORE_FLUSH); + /* + We should use show_table_list->alias instead of + show_table_list->table_name because table_name + could be changed during opening of I_S tables. It's safe + to use alias because alias contains original table name + in this case. + */ res= schema_table->process_table(thd, show_table_list, table, res, base_name, show_table_list->alias); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 892663b4220..fc877511357 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4866,7 +4866,9 @@ predicate: else { $5->push_front($1); - $$= negate_expression(YYTHD, new Item_func_in(*$5)); + Item_func_in *item = new Item_func_in(*$5); + item->negate(); + $$= item; } } | bit_expr IN_SYM in_subselect @@ -4876,7 +4878,11 @@ predicate: | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate { $$= new Item_func_between($1,$3,$5); } | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate - { $$= negate_expression(YYTHD, new Item_func_between($1,$4,$6)); } + { + Item_func_between *item= new Item_func_between($1,$4,$6); + item->negate(); + $$= item; + } | bit_expr SOUNDS_SYM LIKE bit_expr { $$= new Item_func_eq(new Item_func_soundex($1), new Item_func_soundex($4)); } @@ -8755,6 +8761,11 @@ handler: HANDLER_SYM table_ident OPEN_SYM opt_table_alias { LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } lex->sql_command = SQLCOM_HA_OPEN; if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) YYABORT; @@ -8762,6 +8773,11 @@ handler: | HANDLER_SYM table_ident_nodb CLOSE_SYM { LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } lex->sql_command = SQLCOM_HA_CLOSE; if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) YYABORT; @@ -8769,6 +8785,11 @@ handler: | HANDLER_SYM table_ident_nodb READ_SYM { LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } lex->sql_command = SQLCOM_HA_READ; lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ lex->current_select->select_limit= new Item_int((int32) 1); diff --git a/storage/ndb/include/kernel/signaldata/BackupImpl.hpp b/storage/ndb/include/kernel/signaldata/BackupImpl.hpp index 2032e2347b5..298440ad377 100644 --- a/storage/ndb/include/kernel/signaldata/BackupImpl.hpp +++ b/storage/ndb/include/kernel/signaldata/BackupImpl.hpp @@ -33,7 +33,7 @@ class DefineBackupReq { friend bool printDEFINE_BACKUP_REQ(FILE *, const Uint32 *, Uint32, Uint16); public: - STATIC_CONST( SignalLength = 8 + NdbNodeBitmask::Size); + STATIC_CONST( SignalLength = 9 + NdbNodeBitmask::Size); private: /** @@ -60,6 +60,13 @@ private: * Length of backup data */ Uint32 backupDataLen; + + /** + * Backup flags + */ + /* & 0x3 - waitCompleted + */ + Uint32 flags; }; class DefineBackupRef { diff --git a/storage/ndb/include/kernel/signaldata/BackupSignalData.hpp b/storage/ndb/include/kernel/signaldata/BackupSignalData.hpp index b38dd8d14b2..e1b8c6203a1 100644 --- a/storage/ndb/include/kernel/signaldata/BackupSignalData.hpp +++ b/storage/ndb/include/kernel/signaldata/BackupSignalData.hpp @@ -36,11 +36,14 @@ class BackupReq { friend bool printBACKUP_REQ(FILE *, const Uint32 *, Uint32, Uint16); public: - STATIC_CONST( SignalLength = 2 ); + STATIC_CONST( SignalLength = 3 ); private: Uint32 senderData; Uint32 backupDataLen; + /* & 0x3 - waitCompleted + */ + Uint32 flags; }; class BackupData { diff --git a/storage/ndb/include/kernel/signaldata/NFCompleteRep.hpp b/storage/ndb/include/kernel/signaldata/NFCompleteRep.hpp index c8bde705a86..764da85b163 100644 --- a/storage/ndb/include/kernel/signaldata/NFCompleteRep.hpp +++ b/storage/ndb/include/kernel/signaldata/NFCompleteRep.hpp @@ -30,28 +30,12 @@ * from the failed NDB node * */ -class NFCompleteRep { - /** - * Sender(s) - */ - friend class Dbdict; - friend class Dblqh; - friend class Dbtc; - friend class Qmgr; - - /** - * Sender/Reciver - */ - friend class Dbdih; - friend class ClusterMgr; +struct NFCompleteRep { friend bool printNF_COMPLETE_REP(FILE *, const Uint32 *, Uint32, Uint16); -public: STATIC_CONST( SignalLength = 5 ); -private: - /** * Which block has completed... * diff --git a/storage/ndb/include/kernel/signaldata/NodeFailRep.hpp b/storage/ndb/include/kernel/signaldata/NodeFailRep.hpp index 060acd6a3e2..fe57ba1a712 100644 --- a/storage/ndb/include/kernel/signaldata/NodeFailRep.hpp +++ b/storage/ndb/include/kernel/signaldata/NodeFailRep.hpp @@ -24,34 +24,8 @@ * This signals is sent by Qmgr to NdbCntr * and then from NdbCntr sent to: dih, dict, lqh, tc & API */ -class NodeFailRep { - /** - * Sender(s) - */ - friend class Qmgr; - - /** - * Sender(s) / Reciver(s) - */ - friend class Ndbcntr; - friend class Dbdict; - - /** - * Reciver(s) - */ - friend class Dbdih; - friend class Dblqh; - friend class Dbtc; - friend class ClusterMgr; - friend class Trix; - friend class Backup; - friend class Suma; - friend class Grep; - friend class SafeCounterManager; - -public: +struct NodeFailRep { STATIC_CONST( SignalLength = 3 + NodeBitmask::Size ); -private: Uint32 failNo; diff --git a/storage/ndb/src/kernel/blocks/backup/Backup.cpp b/storage/ndb/src/kernel/blocks/backup/Backup.cpp index 34545d4c7ff..063e36a775f 100644 --- a/storage/ndb/src/kernel/blocks/backup/Backup.cpp +++ b/storage/ndb/src/kernel/blocks/backup/Backup.cpp @@ -69,6 +69,9 @@ static const Uint32 BACKUP_SEQUENCE = 0x1F000000; static Uint32 g_TypeOfStart = NodeState::ST_ILLEGAL_TYPE; +#define SEND_BACKUP_STARTED_FLAG(A) (((A) & 0x3) > 0) +#define SEND_BACKUP_COMPLETED_FLAG(A) (((A) & 0x3) > 1) + void Backup::execSTTOR(Signal* signal) { @@ -852,23 +855,24 @@ Backup::execBACKUP_REQ(Signal* signal) const Uint32 senderData = req->senderData; const BlockReference senderRef = signal->senderBlockRef(); const Uint32 dataLen32 = req->backupDataLen; // In 32 bit words - + const Uint32 flags = signal->getLength() > 2 ? req->flags : 2; + if(getOwnNodeId() != getMasterNodeId()) { jam(); - sendBackupRef(senderRef, signal, senderData, BackupRef::IAmNotMaster); + sendBackupRef(senderRef, flags, signal, senderData, BackupRef::IAmNotMaster); return; }//if if (m_diskless) { - sendBackupRef(senderRef, signal, senderData, + sendBackupRef(senderRef, flags, signal, senderData, BackupRef::CannotBackupDiskless); return; } if(dataLen32 != 0) { jam(); - sendBackupRef(senderRef, signal, senderData, + sendBackupRef(senderRef, flags, signal, senderData, BackupRef::BackupDefinitionNotImplemented); return; }//if @@ -883,7 +887,7 @@ Backup::execBACKUP_REQ(Signal* signal) c_backups.seize(ptr); if(ptr.i == RNIL) { jam(); - sendBackupRef(senderRef, signal, senderData, BackupRef::OutOfBackupRecord); + sendBackupRef(senderRef, flags, signal, senderData, BackupRef::OutOfBackupRecord); return; }//if @@ -894,6 +898,7 @@ Backup::execBACKUP_REQ(Signal* signal) ptr.p->errorCode = 0; ptr.p->clientRef = senderRef; ptr.p->clientData = senderData; + ptr.p->flags = flags; ptr.p->masterRef = reference(); ptr.p->nodes = c_aliveNodes; ptr.p->backupId = 0; @@ -931,20 +936,23 @@ void Backup::sendBackupRef(Signal* signal, BackupRecordPtr ptr, Uint32 errorCode) { jam(); - sendBackupRef(ptr.p->clientRef, signal, ptr.p->clientData, errorCode); + sendBackupRef(ptr.p->clientRef, ptr.p->flags, signal, ptr.p->clientData, errorCode); cleanup(signal, ptr); } void -Backup::sendBackupRef(BlockReference senderRef, Signal *signal, +Backup::sendBackupRef(BlockReference senderRef, Uint32 flags, Signal *signal, Uint32 senderData, Uint32 errorCode) { jam(); - BackupRef* ref = (BackupRef*)signal->getDataPtrSend(); - ref->senderData = senderData; - ref->errorCode = errorCode; - ref->masterRef = numberToRef(BACKUP, getMasterNodeId()); - sendSignal(senderRef, GSN_BACKUP_REF, signal, BackupRef::SignalLength, JBB); + if (SEND_BACKUP_STARTED_FLAG(flags)) + { + BackupRef* ref = (BackupRef*)signal->getDataPtrSend(); + ref->senderData = senderData; + ref->errorCode = errorCode; + ref->masterRef = numberToRef(BACKUP, getMasterNodeId()); + sendSignal(senderRef, GSN_BACKUP_REF, signal, BackupRef::SignalLength, JBB); + } if(errorCode != BackupRef::IAmNotMaster){ signal->theData[0] = NDB_LE_BackupFailedToStart; @@ -1098,6 +1106,7 @@ Backup::sendDefineBackupReq(Signal *signal, BackupRecordPtr ptr) req->backupKey[1] = ptr.p->backupKey[1]; req->nodes = ptr.p->nodes; req->backupDataLen = ptr.p->backupDataLen; + req->flags = ptr.p->flags; ptr.p->masterData.gsn = GSN_DEFINE_BACKUP_REQ; ptr.p->masterData.sendCounter = ptr.p->nodes; @@ -1193,13 +1202,18 @@ Backup::defineBackupReply(Signal* signal, BackupRecordPtr ptr, Uint32 nodeId) /** * Reply to client */ - BackupConf * conf = (BackupConf*)signal->getDataPtrSend(); - conf->backupId = ptr.p->backupId; - conf->senderData = ptr.p->clientData; - conf->nodes = ptr.p->nodes; - sendSignal(ptr.p->clientRef, GSN_BACKUP_CONF, signal, - BackupConf::SignalLength, JBB); - + CRASH_INSERTION((10034)); + + if (SEND_BACKUP_STARTED_FLAG(ptr.p->flags)) + { + BackupConf * conf = (BackupConf*)signal->getDataPtrSend(); + conf->backupId = ptr.p->backupId; + conf->senderData = ptr.p->clientData; + conf->nodes = ptr.p->nodes; + sendSignal(ptr.p->clientRef, GSN_BACKUP_CONF, signal, + BackupConf::SignalLength, JBB); + } + signal->theData[0] = NDB_LE_BackupStarted; signal->theData[1] = ptr.p->clientRef; signal->theData[2] = ptr.p->backupId; @@ -2080,19 +2094,22 @@ Backup::stopBackupReply(Signal* signal, BackupRecordPtr ptr, Uint32 nodeId) if(!ptr.p->checkError()) { - BackupCompleteRep * rep = (BackupCompleteRep*)signal->getDataPtrSend(); - rep->backupId = ptr.p->backupId; - rep->senderData = ptr.p->clientData; - rep->startGCP = ptr.p->startGCP; - rep->stopGCP = ptr.p->stopGCP; - rep->noOfBytes = ptr.p->noOfBytes; - rep->noOfRecords = ptr.p->noOfRecords; - rep->noOfLogBytes = ptr.p->noOfLogBytes; - rep->noOfLogRecords = ptr.p->noOfLogRecords; - rep->nodes = ptr.p->nodes; - sendSignal(ptr.p->clientRef, GSN_BACKUP_COMPLETE_REP, signal, - BackupCompleteRep::SignalLength, JBB); - + if (SEND_BACKUP_COMPLETED_FLAG(ptr.p->flags)) + { + BackupCompleteRep * rep = (BackupCompleteRep*)signal->getDataPtrSend(); + rep->backupId = ptr.p->backupId; + rep->senderData = ptr.p->clientData; + rep->startGCP = ptr.p->startGCP; + rep->stopGCP = ptr.p->stopGCP; + rep->noOfBytes = ptr.p->noOfBytes; + rep->noOfRecords = ptr.p->noOfRecords; + rep->noOfLogBytes = ptr.p->noOfLogBytes; + rep->noOfLogRecords = ptr.p->noOfLogRecords; + rep->nodes = ptr.p->nodes; + sendSignal(ptr.p->clientRef, GSN_BACKUP_COMPLETE_REP, signal, + BackupCompleteRep::SignalLength, JBB); + } + signal->theData[0] = NDB_LE_BackupCompleted; signal->theData[1] = ptr.p->clientRef; signal->theData[2] = ptr.p->backupId; @@ -2129,13 +2146,15 @@ Backup::masterAbort(Signal* signal, BackupRecordPtr ptr) return; } - BackupAbortRep* rep = (BackupAbortRep*)signal->getDataPtrSend(); - rep->backupId = ptr.p->backupId; - rep->senderData = ptr.p->clientData; - rep->reason = ptr.p->errorCode; - sendSignal(ptr.p->clientRef, GSN_BACKUP_ABORT_REP, signal, - BackupAbortRep::SignalLength, JBB); - + if (SEND_BACKUP_COMPLETED_FLAG(ptr.p->flags)) + { + BackupAbortRep* rep = (BackupAbortRep*)signal->getDataPtrSend(); + rep->backupId = ptr.p->backupId; + rep->senderData = ptr.p->clientData; + rep->reason = ptr.p->errorCode; + sendSignal(ptr.p->clientRef, GSN_BACKUP_ABORT_REP, signal, + BackupAbortRep::SignalLength, JBB); + } signal->theData[0] = NDB_LE_BackupAborted; signal->theData[1] = ptr.p->clientRef; signal->theData[2] = ptr.p->backupId; @@ -2267,6 +2286,13 @@ Backup::execDEFINE_BACKUP_REQ(Signal* signal) ptr.p->errorCode = 0; ptr.p->clientRef = req->clientRef; ptr.p->clientData = req->clientData; + if(senderRef == reference()) + ptr.p->flags = req->flags; + else + ptr.p->flags = req->flags & ~((Uint32)0x3); /* remove waitCompleted flags + * as non master should never + * reply + */ ptr.p->masterRef = senderRef; ptr.p->nodes = req->nodes; ptr.p->backupId = backupId; diff --git a/storage/ndb/src/kernel/blocks/backup/Backup.hpp b/storage/ndb/src/kernel/blocks/backup/Backup.hpp index ab2bec7dad7..67b53d3eccd 100644 --- a/storage/ndb/src/kernel/blocks/backup/Backup.hpp +++ b/storage/ndb/src/kernel/blocks/backup/Backup.hpp @@ -412,6 +412,7 @@ public: Uint32 clientRef; Uint32 clientData; + Uint32 flags; Uint32 backupId; Uint32 backupKey[2]; Uint32 masterRef; @@ -592,7 +593,7 @@ public: bool insertFileHeader(BackupFormat::FileType, BackupRecord*, BackupFile*); void sendBackupRef(Signal* signal, BackupRecordPtr ptr, Uint32 errorCode); - void sendBackupRef(BlockReference ref, Signal *signal, + void sendBackupRef(BlockReference ref, Uint32 flags, Signal *signal, Uint32 senderData, Uint32 errorCode); void dumpUsedResources(); void cleanup(Signal*, BackupRecordPtr ptr); diff --git a/storage/ndb/src/kernel/main.cpp b/storage/ndb/src/kernel/main.cpp index 850cdf37044..bec9c8b28f4 100644 --- a/storage/ndb/src/kernel/main.cpp +++ b/storage/ndb/src/kernel/main.cpp @@ -410,5 +410,5 @@ handler_sigusr1(int signum) failed_startups++; failed_startup_flag = true; } - g_eventLogger.info("Received signal %d. Ndbd failed startup (%u).", signum, failed_startups); + g_eventLogger.info("Angel received ndbd startup failure count %u.", failed_startups); } diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp index 374d1f8de04..fae0941beb8 100644 --- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -39,6 +39,8 @@ #include <signaldata/BackupSignalData.hpp> #include <signaldata/GrepImpl.hpp> #include <signaldata/ManagementServer.hpp> +#include <signaldata/NFCompleteRep.hpp> +#include <signaldata/NodeFailRep.hpp> #include <NdbSleep.h> #include <EventLogger.hpp> #include <DebuggerNames.hpp> @@ -56,6 +58,8 @@ #include <mgmapi_config_parameters.h> #include <m_string.h> +#include <SignalSender.hpp> + //#define MGM_SRV_DEBUG #ifdef MGM_SRV_DEBUG #define DEBUG(x) do ndbout << x << endl; while(0) @@ -727,6 +731,15 @@ int MgmtSrvr::okToSendTo(NodeId processId, bool unCond) } } +void report_unknown_signal(SimpleSignal *signal) +{ + g_eventLogger.error("Unknown signal received. SignalNumber: " + "%i from (%d, %x)", + signal->readSignalNumber(), + refToNode(signal->header.theSendersBlockRef), + refToBlock(signal->header.theSendersBlockRef)); +} + /***************************************************************************** * Starting and stopping database nodes ****************************************************************************/ @@ -1927,81 +1940,6 @@ MgmtSrvr::handleReceivedSignal(NdbApiSignal* signal) } break; - case GSN_BACKUP_CONF:{ - const BackupConf * const conf = - CAST_CONSTPTR(BackupConf, signal->getDataPtr()); - BackupEvent event; - event.Event = BackupEvent::BackupStarted; - event.Started.BackupId = conf->backupId; - event.Nodes = conf->nodes; -#ifdef VM_TRACE - ndbout_c("Backup master is %d", refToNode(signal->theSendersBlockRef)); -#endif - backupCallback(event); - } - break; - - case GSN_BACKUP_REF:{ - const BackupRef * const ref = - CAST_CONSTPTR(BackupRef, signal->getDataPtr()); - Uint32 errCode = ref->errorCode; - if(ref->errorCode == BackupRef::IAmNotMaster){ - const Uint32 aNodeId = refToNode(ref->masterRef); -#ifdef VM_TRACE - ndbout_c("I'm not master resending to %d", aNodeId); -#endif - theWaitNode= aNodeId; - NdbApiSignal aSignal(_ownReference); - BackupReq* req = CAST_PTR(BackupReq, aSignal.getDataPtrSend()); - aSignal.set(TestOrd::TraceAPI, BACKUP, GSN_BACKUP_REQ, - BackupReq::SignalLength); - req->senderData = 19; - req->backupDataLen = 0; - - int i = theFacade->sendSignalUnCond(&aSignal, aNodeId); - if(i == 0){ - return; - } - errCode = 5030; - } - BackupEvent event; - event.Event = BackupEvent::BackupFailedToStart; - event.FailedToStart.ErrorCode = errCode; - backupCallback(event); - break; - } - - case GSN_BACKUP_ABORT_REP:{ - const BackupAbortRep * const rep = - CAST_CONSTPTR(BackupAbortRep, signal->getDataPtr()); - BackupEvent event; - event.Event = BackupEvent::BackupAborted; - event.Aborted.Reason = rep->reason; - event.Aborted.BackupId = rep->backupId; - event.Aborted.ErrorCode = rep->reason; - backupCallback(event); - } - break; - - case GSN_BACKUP_COMPLETE_REP:{ - const BackupCompleteRep * const rep = - CAST_CONSTPTR(BackupCompleteRep, signal->getDataPtr()); - BackupEvent event; - event.Event = BackupEvent::BackupCompleted; - event.Completed.BackupId = rep->backupId; - - event.Completed.NoOfBytes = rep->noOfBytes; - event.Completed.NoOfLogBytes = rep->noOfLogBytes; - event.Completed.NoOfRecords = rep->noOfRecords; - event.Completed.NoOfLogRecords = rep->noOfLogRecords; - event.Completed.stopGCP = rep->stopGCP; - event.Completed.startGCP = rep->startGCP; - event.Nodes = rep->nodes; - - backupCallback(event); - } - break; - case GSN_MGM_LOCK_CONFIG_REP: case GSN_MGM_LOCK_CONFIG_REQ: case GSN_MGM_UNLOCK_CONFIG_REP: @@ -2466,6 +2404,9 @@ MgmtSrvr::eventReport(NodeId nodeId, const Uint32 * theData) int MgmtSrvr::startBackup(Uint32& backupId, int waitCompleted) { + SignalSender ss(theFacade); + ss.lock(); // lock will be released on exit + bool next; NodeId nodeId = 0; while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true && @@ -2473,50 +2414,127 @@ MgmtSrvr::startBackup(Uint32& backupId, int waitCompleted) if(!next) return NO_CONTACT_WITH_DB_NODES; - NdbApiSignal* signal = getSignal(); - if (signal == NULL) { - return COULD_NOT_ALLOCATE_MEMORY; - } + SimpleSignal ssig; - BackupReq* req = CAST_PTR(BackupReq, signal->getDataPtrSend()); - signal->set(TestOrd::TraceAPI, BACKUP, GSN_BACKUP_REQ, - BackupReq::SignalLength); + BackupReq* req = CAST_PTR(BackupReq, ssig.getDataPtrSend()); + ssig.set(ss, TestOrd::TraceAPI, BACKUP, GSN_BACKUP_REQ, + BackupReq::SignalLength); req->senderData = 19; req->backupDataLen = 0; + assert(waitCompleted < 3); + req->flags = waitCompleted & 0x3; - int result; - if (waitCompleted == 2) { - result = sendRecSignal(nodeId, WAIT_BACKUP_COMPLETED, - signal, true, 48*60*60*1000 /* 48 hours */); - } - else if (waitCompleted == 1) { - result = sendRecSignal(nodeId, WAIT_BACKUP_STARTED, - signal, true, 5*60*1000 /*5 mins*/); - } - else { - result = sendRecSignal(nodeId, NO_WAIT, signal, true); - } - if (result == -1) { - return SEND_OR_RECEIVE_FAILED; - } - - if (waitCompleted){ - switch(m_lastBackupEvent.Event){ - case BackupEvent::BackupCompleted: - backupId = m_lastBackupEvent.Completed.BackupId; + BackupEvent event; + int do_send = 1; + while (1) { + if (do_send) + { + SendStatus result = ss.sendSignal(nodeId, &ssig); + if (result != SEND_OK) { + return SEND_OR_RECEIVE_FAILED; + } + if (waitCompleted == 0) + return 0; + do_send = 0; + } + SimpleSignal *signal = ss.waitFor(); + + int gsn = signal->readSignalNumber(); + switch (gsn) { + case GSN_BACKUP_CONF:{ + const BackupConf * const conf = + CAST_CONSTPTR(BackupConf, signal->getDataPtr()); + event.Event = BackupEvent::BackupStarted; + event.Started.BackupId = conf->backupId; + event.Nodes = conf->nodes; +#ifdef VM_TRACE + ndbout_c("Backup(%d) master is %d", conf->backupId, + refToNode(signal->header.theSendersBlockRef)); +#endif + backupId = conf->backupId; + if (waitCompleted == 1) + return 0; + // wait for next signal break; - case BackupEvent::BackupStarted: - backupId = m_lastBackupEvent.Started.BackupId; + } + case GSN_BACKUP_COMPLETE_REP:{ + const BackupCompleteRep * const rep = + CAST_CONSTPTR(BackupCompleteRep, signal->getDataPtr()); +#ifdef VM_TRACE + ndbout_c("Backup(%d) completed %d", rep->backupId); +#endif + event.Event = BackupEvent::BackupCompleted; + event.Completed.BackupId = rep->backupId; + + event.Completed.NoOfBytes = rep->noOfBytes; + event.Completed.NoOfLogBytes = rep->noOfLogBytes; + event.Completed.NoOfRecords = rep->noOfRecords; + event.Completed.NoOfLogRecords = rep->noOfLogRecords; + event.Completed.stopGCP = rep->stopGCP; + event.Completed.startGCP = rep->startGCP; + event.Nodes = rep->nodes; + + backupId = rep->backupId; + return 0; + } + case GSN_BACKUP_REF:{ + const BackupRef * const ref = + CAST_CONSTPTR(BackupRef, signal->getDataPtr()); + if(ref->errorCode == BackupRef::IAmNotMaster){ + nodeId = refToNode(ref->masterRef); +#ifdef VM_TRACE + ndbout_c("I'm not master resending to %d", nodeId); +#endif + do_send = 1; // try again + continue; + } + event.Event = BackupEvent::BackupFailedToStart; + event.FailedToStart.ErrorCode = ref->errorCode; + return ref->errorCode; + } + case GSN_BACKUP_ABORT_REP:{ + const BackupAbortRep * const rep = + CAST_CONSTPTR(BackupAbortRep, signal->getDataPtr()); + event.Event = BackupEvent::BackupAborted; + event.Aborted.Reason = rep->reason; + event.Aborted.BackupId = rep->backupId; + event.Aborted.ErrorCode = rep->reason; +#ifdef VM_TRACE + ndbout_c("Backup %d aborted", rep->backupId); +#endif + return rep->reason; + } + case GSN_NF_COMPLETEREP:{ + const NFCompleteRep * const rep = + CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); +#ifdef VM_TRACE + ndbout_c("Node %d fail completed", rep->failedNodeId); +#endif + if (rep->failedNodeId == nodeId || + waitCompleted == 1) + return 1326; + // wait for next signal + // master node will report aborted backup break; - case BackupEvent::BackupFailedToStart: - return m_lastBackupEvent.FailedToStart.ErrorCode; - case BackupEvent::BackupAborted: - return m_lastBackupEvent.Aborted.ErrorCode; - default: - return -1; + } + case GSN_NODE_FAILREP:{ + const NodeFailRep * const rep = + CAST_CONSTPTR(NodeFailRep, signal->getDataPtr()); +#ifdef VM_TRACE + ndbout_c("Node %d failed", rep->failNo); +#endif + if (rep->failNo == nodeId || + waitCompleted == 1) + return 1326; + // wait for next signal + // master node will report aborted backup break; } + default: + report_unknown_signal(signal); + return SEND_OR_RECEIVE_FAILED; + } } return 0; @@ -2555,36 +2573,6 @@ MgmtSrvr::abortBackup(Uint32 backupId) return 0; } -void -MgmtSrvr::backupCallback(BackupEvent & event) -{ - DBUG_ENTER("MgmtSrvr::backupCallback"); - m_lastBackupEvent = event; - switch(event.Event){ - case BackupEvent::BackupFailedToStart: - DBUG_PRINT("info",("BackupEvent::BackupFailedToStart")); - theWaitState = NO_WAIT; - break; - case BackupEvent::BackupAborted: - DBUG_PRINT("info",("BackupEvent::BackupAborted")); - theWaitState = NO_WAIT; - break; - case BackupEvent::BackupCompleted: - DBUG_PRINT("info",("BackupEvent::BackupCompleted")); - theWaitState = NO_WAIT; - break; - case BackupEvent::BackupStarted: - if(theWaitState == WAIT_BACKUP_STARTED) - { - DBUG_PRINT("info",("BackupEvent::BackupStarted NO_WAIT")); - theWaitState = NO_WAIT; - } else { - DBUG_PRINT("info",("BackupEvent::BackupStarted")); - } - } - DBUG_VOID_RETURN; -} - /***************************************************************************** * Global Replication diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp index b7983e6b441..de1af1286ff 100644 --- a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp @@ -768,9 +768,6 @@ private: static void *signalRecvThread_C(void *); void signalRecvThreadRun(); - void backupCallback(BackupEvent &); - BackupEvent m_lastBackupEvent; - Config *_props; int send(class NdbApiSignal* signal, Uint32 node, Uint32 node_type); diff --git a/storage/ndb/src/ndbapi/Makefile.am b/storage/ndb/src/ndbapi/Makefile.am index 0656aad7ed3..a4a0b8098a2 100644 --- a/storage/ndb/src/ndbapi/Makefile.am +++ b/storage/ndb/src/ndbapi/Makefile.am @@ -34,7 +34,8 @@ libndbapi_la_SOURCES = \ NdbDictionaryImpl.cpp \ DictCache.cpp \ ndb_cluster_connection.cpp \ - NdbBlob.cpp + NdbBlob.cpp \ + SignalSender.cpp INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/mgmapi diff --git a/storage/ndb/src/ndbapi/NdbTransaction.cpp b/storage/ndb/src/ndbapi/NdbTransaction.cpp index 675c9383c6e..294012d780c 100644 --- a/storage/ndb/src/ndbapi/NdbTransaction.cpp +++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp @@ -264,6 +264,7 @@ NdbTransaction::execute(ExecType aTypeOfExec, AbortOption abortOption, int forceSend) { + NdbError savedError= theError; DBUG_ENTER("NdbTransaction::execute"); DBUG_PRINT("enter", ("aTypeOfExec: %d, abortOption: %d", aTypeOfExec, abortOption)); @@ -293,7 +294,11 @@ NdbTransaction::execute(ExecType aTypeOfExec, NdbBlob* tBlob = tPrepOp->theBlobList; while (tBlob != NULL) { if (tBlob->preExecute(tExecType, batch) == -1) + { ret = -1; + if(savedError.code==0) + savedError= theError; + } tBlob = tBlob->theNext; } if (batch) { @@ -322,7 +327,11 @@ NdbTransaction::execute(ExecType aTypeOfExec, NdbBlob* tBlob = tOp->theBlobList; while (tBlob != NULL) { if (tBlob->preCommit() == -1) - ret = -1; + { + ret = -1; + if(savedError.code==0) + savedError= theError; + } tBlob = tBlob->theNext; } } @@ -344,7 +353,12 @@ NdbTransaction::execute(ExecType aTypeOfExec, } if (executeNoBlobs(tExecType, abortOption, forceSend) == -1) - ret = -1; + { + ret = -1; + if(savedError.code==0) + savedError= theError; + } + #ifdef ndb_api_crash_on_complex_blob_abort assert(theFirstOpInList == NULL && theLastOpInList == NULL); #else @@ -359,7 +373,11 @@ NdbTransaction::execute(ExecType aTypeOfExec, while (tBlob != NULL) { // may add new operations if batch if (tBlob->postExecute(tExecType) == -1) + { ret = -1; + if(savedError.code==0) + savedError= theError; + } tBlob = tBlob->theNext; } } @@ -390,6 +408,10 @@ NdbTransaction::execute(ExecType aTypeOfExec, ndbout << "completed ops: " << n << endl; } #endif + + if(savedError.code!=0 && theError.code==4350) // Trans already aborted + theError= savedError; + DBUG_RETURN(ret); } diff --git a/storage/ndb/test/src/NdbBackup.cpp b/storage/ndb/test/src/NdbBackup.cpp index fe101b9c80b..9f65fe6b3bc 100644 --- a/storage/ndb/test/src/NdbBackup.cpp +++ b/storage/ndb/test/src/NdbBackup.cpp @@ -50,14 +50,17 @@ NdbBackup::start(unsigned int & _backup_id){ 2, // wait until completed &_backup_id, &reply) == -1) { - g_err << "Could not start backup " << endl; - g_err << "Error: " << reply.message << endl; + g_err << "Error: " << ndb_mgm_get_latest_error(handle) << endl; + g_err << "Error msg: " << ndb_mgm_get_latest_error_msg(handle) << endl; + g_err << "Error desc: " << ndb_mgm_get_latest_error_desc(handle) << endl; return -1; } if(reply.return_code != 0){ g_err << "PLEASE CHECK CODE NdbBackup.cpp line=" << __LINE__ << endl; - g_err << "Error: " << reply.message << endl; + g_err << "Error: " << ndb_mgm_get_latest_error(handle) << endl; + g_err << "Error msg: " << ndb_mgm_get_latest_error_msg(handle) << endl; + g_err << "Error desc: " << ndb_mgm_get_latest_error_desc(handle) << endl; return reply.return_code; } return 0; diff --git a/strings/decimal.c b/strings/decimal.c index 4dc5fa91e0a..7816f340eef 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -1933,7 +1933,7 @@ int decimal_mul(decimal_t *from1, decimal_t *from2, decimal_t *to) int intg1=ROUND_UP(from1->intg), intg2=ROUND_UP(from2->intg), frac1=ROUND_UP(from1->frac), frac2=ROUND_UP(from2->frac), intg0=ROUND_UP(from1->intg+from2->intg), - frac0=frac1+frac2, error, i, j; + frac0=frac1+frac2, error, i, j, d_to_move; dec1 *buf1=from1->buf+intg1, *buf2=from2->buf+intg2, *buf0, *start2, *stop2, *stop1, *start0, carry; @@ -2007,6 +2007,20 @@ int decimal_mul(decimal_t *from1, decimal_t *from2, decimal_t *to) } } } + buf1= to->buf; + d_to_move= intg0 + ROUND_UP(to->frac); + while (!*buf1 && (to->intg > DIG_PER_DEC1)) + { + buf1++; + to->intg-= DIG_PER_DEC1; + d_to_move--; + } + if (to->buf < buf1) + { + dec1 *cur_d= to->buf; + for (; d_to_move; d_to_move--, cur_d++, buf1++) + *cur_d= *buf1; + } return error; } diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index 6757a1052a8..6dbffdc9778 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -39,7 +39,8 @@ # If you want to affect other MySQL variables, you should make your changes # in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files. -# If you change base dir, you must also change datadir +# If you change base dir, you must also change datadir. These may get +# overwritten by settings in the MySQL configuration files. basedir= datadir= @@ -61,8 +62,8 @@ then else bindir="$basedir/bin" datadir="$basedir/data" - sbindir="$basedir/bin" - libexecdir="$basedir/bin" + sbindir="$basedir/sbin" + libexecdir="$basedir/libexec" fi # @@ -99,8 +100,8 @@ parse_server_arguments() { --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'` bindir="$basedir/bin" datadir="$basedir/data" - sbindir="$basedir/bin" - libexecdir="$basedir/bin" + sbindir="$basedir/sbin" + libexecdir="$basedir/libexec" ;; --datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --user=*) user=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; @@ -240,7 +241,7 @@ case "$mode" in if test -x $libexecdir/mysqlmanager then manager=$libexecdir/mysqlmanager - elif test -x $bindir/mysqlmanager + elif test -x $sbindir/mysqlmanager then manager=$sbindir/mysqlmanager fi |