From ffebdee6426d8145b3aacd6274ca5e507103daab Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 24 Jan 2006 13:58:28 +0400 Subject: [PATCH 01/12] Fix for bug #15756: incorrect ip address matching in ACL due to use of latin1 collation. Thanks Deomid Ryabkov for the great help! sql/hostname.cc: Fix for bug #15756: incorrect ip address matching in ACL due to use of latin1 collation. - use my_charset_bin instead of my_charset_latin1 to properly compare IP addresses. --- sql/hostname.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/hostname.cc b/sql/hostname.cc index 32e1d84fac3..32c4bb8533d 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -61,7 +61,7 @@ bool hostname_cache_init() if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset, sizeof(struct in_addr),NULL, (hash_free_key) free, - &my_charset_latin1))) + &my_charset_bin))) return 1; hostname_cache->clear(); (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); From 7f02b0a01b64070562d5d768b5709127714732a4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Feb 2006 12:11:29 +0100 Subject: [PATCH 02/12] Fixed BUG#16887: Cursor causes server segfault The problem was a code generation bug: cpop instructions were not generated when using ITERATE back to an outer block from a context with a declared cursor; this would make it push a new cursor without popping in-between, eventually overrunning the cursor stack with a crash as the result. Fixed the calculation of how many cursors to pop (in sp_pcontext.cc: diff_cursors()), and also corrected diff_cursors() and diff_handlers() to when doing a "leave"; don't include the last context we're leaving (we are then jumping to the appropriate pop instructions). mysql-test/r/sp.result: Updated result for new test case (BUG#16887) mysql-test/t/sp.test: New test case for BUG#16887 sql/sp_pcontext.cc: Added new parameter to sp_pcontext::diff_handlers() and diff_cursors(): They can either include (for iterate jumps) or exclude (for leave jumps) the outer context. Fixed bug in diff_cursors(); it was just plain wrong and would return zero in some situations when it shouldn't. sql/sp_pcontext.h: Added new parameter to sp_pcontext::diff_handlers() and diff_cursors(): They can either include (for iterate jumps) or exclude (for leave jumps) the outer context. sql/sql_yacc.yy: Added parameter to diff_handlers/diff_cursors depending on if it's an iterate or leave jump. For "leave", we don't have to include the last context we're leaving since we will jump to the appropriate pop instructions. --- mysql-test/r/sp.result | 56 ++++++++++++++++++++++++++++++++++++++++++ mysql-test/t/sp.test | 54 ++++++++++++++++++++++++++++++++++++++++ sql/sp_pcontext.cc | 16 +++++++++--- sql/sp_pcontext.h | 10 +++++--- sql/sql_yacc.yy | 8 +++--- 5 files changed, 133 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index bec2f049bc4..e4168f67125 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4519,4 +4519,60 @@ Handler Inner drop procedure bug15011| drop table t3| +drop table if exists t3| +drop procedure if exists bug16887| +create table t3 ( c varchar(1) )| +insert into t3 values +(' '),('.'),(';'),(','),('-'),('_'),('('),(')'),('/'),('\\')| +create procedure bug16887() +begin +declare i int default 10; +again: +while i > 0 do +begin +declare breakchar varchar(1); +declare done int default 0; +declare t3_cursor cursor for select c from t3; +declare continue handler for not found set done = 1; +set i = i - 1; +select i; +if i = 3 then +iterate again; +end if; +open t3_cursor; +loop +fetch t3_cursor into breakchar; +if done = 1 then +begin +close t3_cursor; +iterate again; +end; +end if; +end loop; +end; +end while; +end| +call bug16887()| +i +9 +i +8 +i +7 +i +6 +i +5 +i +4 +i +3 +i +2 +i +1 +i +0 +drop table t3| +drop procedure bug16887| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 53c15ffd05b..b86c39e3109 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5311,6 +5311,60 @@ drop procedure bug15011| drop table t3| +# +# BUG#16887: Cursor causes server segfault +# +--disable_warnings +drop table if exists t3| +drop procedure if exists bug16887| +--enable_warnings + +create table t3 ( c varchar(1) )| + +insert into t3 values + (' '),('.'),(';'),(','),('-'),('_'),('('),(')'),('/'),('\\')| + +create procedure bug16887() +begin + declare i int default 10; + + again: + while i > 0 do + begin + declare breakchar varchar(1); + declare done int default 0; + declare t3_cursor cursor for select c from t3; + declare continue handler for not found set done = 1; + + set i = i - 1; + select i; + + if i = 3 then + iterate again; + end if; + + open t3_cursor; + + loop + fetch t3_cursor into breakchar; + + if done = 1 then + begin + close t3_cursor; + iterate again; + end; + end if; + end loop; + end; + end while; +end| + +call bug16887()| + +drop table t3| +drop procedure bug16887| + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index a8bd8cd2aa0..f69053a7c88 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -122,30 +122,38 @@ sp_pcontext::pop_context() } uint -sp_pcontext::diff_handlers(sp_pcontext *ctx) +sp_pcontext::diff_handlers(sp_pcontext *ctx, bool exclusive) { uint n= 0; sp_pcontext *pctx= this; + sp_pcontext *last_ctx= NULL; while (pctx && pctx != ctx) { n+= pctx->m_handlers; + last_ctx= pctx; pctx= pctx->parent_context(); } if (pctx) - return n; + return (exclusive && last_ctx ? n - last_ctx->m_handlers : n); return 0; // Didn't find ctx } uint -sp_pcontext::diff_cursors(sp_pcontext *ctx) +sp_pcontext::diff_cursors(sp_pcontext *ctx, bool exclusive) { + uint n= 0; sp_pcontext *pctx= this; + sp_pcontext *last_ctx= NULL; while (pctx && pctx != ctx) + { + n+= pctx->m_cursor.elements; + last_ctx= pctx; pctx= pctx->parent_context(); + } if (pctx) - return ctx->current_cursors() - pctx->current_cursors(); + return (exclusive && last_ctx ? n - last_ctx->m_cursor.elements : n); return 0; // Didn't find ctx } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index d1cd7b964c2..872c7c1d505 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -119,11 +119,15 @@ class sp_pcontext : public Sql_alloc return m_parent; } + /* + Number of handlers/cursors to pop between this context and 'ctx'. + If 'exclusive' is true, don't count the last block we are leaving; + this is used for LEAVE where we will jump to the cpop/hpop instructions. + */ uint - diff_handlers(sp_pcontext *ctx); - + diff_handlers(sp_pcontext *ctx, bool exclusive); uint - diff_cursors(sp_pcontext *ctx); + diff_cursors(sp_pcontext *ctx, bool exclusive); // diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4f4ec5e92de..b68b822b1ac 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2079,10 +2079,10 @@ sp_proc_stmt: uint ip= sp->instructions(); uint n; - n= ctx->diff_handlers(lab->ctx); + n= ctx->diff_handlers(lab->ctx, TRUE); /* Exclusive the dest. */ if (n) sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); - n= ctx->diff_cursors(lab->ctx); + n= ctx->diff_cursors(lab->ctx, TRUE); /* Exclusive the dest. */ if (n) sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); i= new sp_instr_jump(ip, ctx); @@ -2108,10 +2108,10 @@ sp_proc_stmt: uint ip= sp->instructions(); uint n; - n= ctx->diff_handlers(lab->ctx); + n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ if (n) sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); - n= ctx->diff_cursors(lab->ctx); + n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ if (n) sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ From 701b69914421935de129ea9c7e4ce8cc0afe1969 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 28 Feb 2006 11:17:40 +0100 Subject: [PATCH 03/12] Bug#17377 Federated Engine returns wrong Data, always the rows with the highest ID - Always make rnd_pos reposition the cursor in the fetched result set. mysql-test/r/federated.result: Update test result mysql-test/t/federated.test: Add test for bug#17377 sql/ha_federated.cc: Remove the "scan_flag" variable. rnd_pos should always move to a new position in the result set. The "scan" flag in rnd_init will protect from the old result set being closed and a new fecthed. I think the "scan_flag" was added before check of the "scan" argument in rnd_init was added. sql/ha_federated.h: Remove class variable "scan_flag" --- mysql-test/r/federated.result | 43 +++++++++++++++++++++++++++ mysql-test/t/federated.test | 55 +++++++++++++++++++++++++++++++++++ sql/ha_federated.cc | 29 ++++++------------ sql/ha_federated.h | 1 - 4 files changed, 107 insertions(+), 21 deletions(-) diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index c1e7533bcee..f11da4ee62f 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -1558,6 +1558,49 @@ id 3 4 5 +DROP TABLE IF EXISTS federated.bug_17377_table; +CREATE TABLE federated.bug_17377_table ( +`fld_cid` bigint(20) NOT NULL auto_increment, +`fld_name` varchar(255) NOT NULL default '', +`fld_parentid` bigint(20) NOT NULL default '0', +`fld_delt` int(1) NOT NULL default '0', +PRIMARY KEY (`fld_cid`), +KEY `fld_parentid` (`fld_parentid`), +KEY `fld_delt` (`fld_delt`), +KEY `fld_cid` (`fld_cid`) +) ENGINE=MyISAM; +insert into federated.bug_17377_table( fld_name ) +values +("Mats"), ("Sivert"), ("Sigvard"), ("Torgny"), ("Torkel"); +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`fld_cid` bigint(20) NOT NULL auto_increment, +`fld_name` varchar(255) NOT NULL default '', +`fld_parentid` bigint(20) NOT NULL default '0', +`fld_delt` int(1) NOT NULL default '0', +PRIMARY KEY (`fld_cid`), +KEY `fld_parentid` (`fld_parentid`), +KEY `fld_delt` (`fld_delt`), +KEY `fld_cid` (`fld_cid`) +) ENGINE=FEDERATED +CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/bug_17377_table'; +select * from federated.t1 where fld_parentid=0 and fld_delt=0 +order by fld_name; +fld_cid fld_name fld_parentid fld_delt +1 Mats 0 0 +3 Sigvard 0 0 +2 Sivert 0 0 +4 Torgny 0 0 +5 Torkel 0 0 +select * from federated.t1 where fld_parentid=0 and fld_delt=0; +fld_cid fld_name fld_parentid fld_delt +1 Mats 0 0 +2 Sivert 0 0 +3 Sigvard 0 0 +4 Torgny 0 0 +5 Torkel 0 0 +DROP TABLE federated.t1; +DROP TABLE federated.bug_17377_table; DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index d1e03f8a05a..80b31c610a2 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1255,4 +1255,59 @@ INSERT INTO federated.t1 VALUES (); SELECT LAST_INSERT_ID(); SELECT * FROM federated.t1; +# +# Bug#17377 Federated Engine returns wrong Data, always the rows +# with the highest ID +# + +connection slave; + +--disable_warnings +DROP TABLE IF EXISTS federated.bug_17377_table; +--enable_warnings + +CREATE TABLE federated.bug_17377_table ( +`fld_cid` bigint(20) NOT NULL auto_increment, +`fld_name` varchar(255) NOT NULL default '', +`fld_parentid` bigint(20) NOT NULL default '0', +`fld_delt` int(1) NOT NULL default '0', +PRIMARY KEY (`fld_cid`), +KEY `fld_parentid` (`fld_parentid`), +KEY `fld_delt` (`fld_delt`), +KEY `fld_cid` (`fld_cid`) +) ENGINE=MyISAM; + +# Insert some test-data +insert into federated.bug_17377_table( fld_name ) +values +("Mats"), ("Sivert"), ("Sigvard"), ("Torgny"), ("Torkel"); + +connection master; +--disable_warnings +DROP TABLE IF EXISTS federated.t1; +--enable_warnings + +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval CREATE TABLE federated.t1 ( +`fld_cid` bigint(20) NOT NULL auto_increment, +`fld_name` varchar(255) NOT NULL default '', +`fld_parentid` bigint(20) NOT NULL default '0', +`fld_delt` int(1) NOT NULL default '0', +PRIMARY KEY (`fld_cid`), +KEY `fld_parentid` (`fld_parentid`), +KEY `fld_delt` (`fld_delt`), +KEY `fld_cid` (`fld_cid`) +) ENGINE=FEDERATED +CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/bug_17377_table'; + +select * from federated.t1 where fld_parentid=0 and fld_delt=0 +order by fld_name; + +select * from federated.t1 where fld_parentid=0 and fld_delt=0; + +DROP TABLE federated.t1; +connection slave; +DROP TABLE federated.bug_17377_table; + + source include/federated_cleanup.inc; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index af7c987e477..c6d5c77803b 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -740,7 +740,7 @@ error: ha_federated::ha_federated(TABLE *table_arg) :handler(&federated_hton, table_arg), - mysql(0), stored_result(0), scan_flag(0), + mysql(0), stored_result(0), ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0) {} @@ -2243,7 +2243,7 @@ int ha_federated::rnd_init(bool scan) containing the correct record, hence update the wrong row! */ - scan_flag= scan; + if (scan) { DBUG_PRINT("info", ("share->select_query %s", share->select_query)); @@ -2365,24 +2365,13 @@ void ha_federated::position(const byte *record) int ha_federated::rnd_pos(byte *buf, byte *pos) { DBUG_ENTER("ha_federated::rnd_pos"); - /* - we do not need to do any of this if there has been a scan performed - already, or if this is an update and index_read_idx already has a result - set in which to build it's update query from - */ - if (scan_flag) - { - int retval; - statistic_increment(table->in_use->status_var.ha_read_rnd_count, - &LOCK_status); - memcpy_fixed(¤t_position, pos, sizeof(MYSQL_ROW_OFFSET)); // pos - /* is not aligned */ - stored_result->current_row= 0; - stored_result->data_cursor= current_position; - retval= rnd_next(buf); - DBUG_RETURN(retval); - } - DBUG_RETURN(0); + + statistic_increment(table->in_use->status_var.ha_read_rnd_count, + &LOCK_status); + memcpy_fixed(¤t_position, pos, sizeof(MYSQL_ROW_OFFSET)); + stored_result->current_row= 0; + stored_result->data_cursor= current_position; + DBUG_RETURN(rnd_next(buf)); } diff --git a/sql/ha_federated.h b/sql/ha_federated.h index 08203d7e51d..cafd1fe59a5 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -153,7 +153,6 @@ class ha_federated: public handler FEDERATED_SHARE *share; /* Shared lock info */ MYSQL *mysql; /* MySQL connection */ MYSQL_RES *stored_result; - bool scan_flag; uint ref_length; uint fetch_num; // stores the fetch num MYSQL_ROW_OFFSET current_position; // Current position used by ::position() From de1e87bbcd5ca4f830e7af42da34ef0d3228b28e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 2 Mar 2006 11:50:15 +0200 Subject: [PATCH 04/12] Fix for BUG#15229. The cause of this bug was a design flaw due to which the list of natural join columns was incorrectly computed and stored for nested joins that are not natural joins, but are operands (possibly indirect) of nested joins. The patch corrects the flaw in a such a way, that the result columns of a table reference are materialized only if it is a leaf table (that is, only if it is a view, stored table, or natural/using join). mysql-test/r/join.result: Added test for BUG#15229 and uncommented failing test cases of BUG#15357 (now fixed by this patch). mysql-test/t/join.test: Added test for BUG#15229 and uncommented failing test cases of BUG#15357 (now fixed by this patch). sql/sql_base.cc: - Do not materialize the result columns of regular nested joins (that are not natural/using joins). - Moved most of the code that creates/adds new natural join column references to the method 'get_or_create_column_ref', and simplified 'mark_common_columns'. - Replaced a call to 'get_or_create_column_ref' with 'get_natural_column_ref' where it is for sure all columns are alredy created. sql/table.cc: - Modified the method 'get_or_create_column_ref' so that it adds itself the newly created natural join columns to the respective table reference. sql/table.h: - Modified the method 'get_or_create_column_ref' so that it adds itself the newly created natural join columns to the respective table reference. --- mysql-test/r/join.result | 47 +++++++++++++++++++++++++++ mysql-test/t/join.test | 41 +++++++++++++++++------- sql/sql_base.cc | 63 +++++++++++++++--------------------- sql/table.cc | 69 ++++++++++++++++++++++++++++++++++++---- sql/table.h | 2 +- 5 files changed, 166 insertions(+), 56 deletions(-) diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index 724d1b1e39f..bd86942072c 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -475,6 +475,22 @@ b c a c b y 1 10 2 3 1 2 1 3 2 3 1 11 1 3 2 3 1 2 +select * from t5 natural join ((t1 natural join t2), (t3 natural join t4)); +y z b c a c b +11 4 1 10 2 3 1 +11 4 1 3 2 3 1 +select * from ((t1 natural join t2), (t3 natural join t4)) natural join t5; +y b c a c b z +11 1 10 2 3 1 4 +11 1 3 2 3 1 4 +select * from t5 natural join ((t1 natural join t2) cross join (t3 natural join t4)); +y z b c a c b +11 4 1 10 2 3 1 +11 4 1 3 2 3 1 +select * from ((t1 natural join t2) cross join (t3 natural join t4)) natural join t5; +y b c a c b z +11 1 10 2 3 1 4 +11 1 3 2 3 1 4 select * from (t1 join t2 using (b)) join (t3 join t4 using (c)) using (c); c b a b y 3 1 2 1 11 @@ -665,6 +681,8 @@ select * from ((t1 natural join t2), (t3 natural join t4)) natural join t6; ERROR 23000: Column 'c' in from clause is ambiguous select * from ((t1 natural join t2), (t3 natural join t4)) natural join t6; ERROR 23000: Column 'c' in from clause is ambiguous +select * from t6 natural join ((t1 natural join t2), (t3 natural join t4)); +ERROR 23000: Column 'c' in from clause is ambiguous select * from (t1 join t2 on t1.b=t2.b) natural join (t3 natural join t4); ERROR 23000: Column 'b' in from clause is ambiguous select * from (t3 natural join t4) natural join (t1 join t2 on t1.b=t2.b); @@ -673,6 +691,8 @@ select * from (t3 join (t4 natural join t5) on (b < z)) natural join (t1 natural join t2); ERROR 23000: Column 'c' in from clause is ambiguous +select * from (t1 natural join t2) natural join (t3 join (t4 natural join t5) on (b < z)); +ERROR 23000: Column 'c' in from clause is ambiguous select t1.b from v1a; ERROR 42S22: Unknown column 't1.b' in 'field list' select * from v1a join v1b on t1.b = t2.b; @@ -692,3 +712,30 @@ drop view v2b; drop view v3a; drop view v3b; drop view v4; +create table t1 (a1 int, a2 int); +create table t2 (a1 int, b int); +create table t3 (c1 int, c2 int); +create table t4 (c2 int); +insert into t1 values (1,1); +insert into t2 values (1,1); +insert into t3 values (1,1); +insert into t4 values (1); +select * from t1 join t2 using (a1) join t3 on b=c1 join t4 using (c2); +c2 a1 a2 b c1 +1 1 1 1 1 +select * from t3 join (t1 join t2 using (a1)) on b=c1 join t4 using (c2); +c2 c1 a1 a2 b +1 1 1 1 1 +select a2 from t1 join t2 using (a1) join t3 on b=c1 join t4 using (c2); +a2 +1 +select a2 from t3 join (t1 join t2 using (a1)) on b=c1 join t4 using (c2); +a2 +1 +select a2 from ((t1 join t2 using (a1)) join t3 on b=c1) join t4 using (c2); +a2 +1 +select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; +a2 +1 +drop table t1,t2,t3,t4; diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index 553aaf987bb..e58227df067 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -408,11 +408,10 @@ select * from t1 natural join (t2 natural join (t3 natural join t4)); select * from t5 natural right join (t4 natural right join ((t2 natural right join t1) natural right join t3)); select * from (t1 natural join t2), (t3 natural join t4); -- MySQL extension - nested comma ',' operator instead of cross join. --- BUG#15357 - natural join with nested cross-join results in incorrect columns --- select * from t5 natural join ((t1 natural join t2), (t3 natural join t4)); --- select * from ((t1 natural join t2), (t3 natural join t4)) natural join t5; --- select * from t5 natural join ((t1 natural join t2) cross join (t3 natural join t4)); --- select * from ((t1 natural join t2) cross join (t3 natural join t4)) natural join t5; +select * from t5 natural join ((t1 natural join t2), (t3 natural join t4)); +select * from ((t1 natural join t2), (t3 natural join t4)) natural join t5; +select * from t5 natural join ((t1 natural join t2) cross join (t3 natural join t4)); +select * from ((t1 natural join t2) cross join (t3 natural join t4)) natural join t5; select * from (t1 join t2 using (b)) join (t3 join t4 using (c)) using (c); select * from (t1 join t2 using (b)) natural join (t3 join t4 using (c)); @@ -500,8 +499,7 @@ select * from ((t1 natural join t2), (t3 natural join t4)) natural join t6; -- error 1052 select * from ((t1 natural join t2), (t3 natural join t4)) natural join t6; -- error 1052 --- BUG#15357: doesn't detect non-unique column 'c', as in the above query. --- select * from t6 natural join ((t1 natural join t2), (t3 natural join t4)); +select * from t6 natural join ((t1 natural join t2), (t3 natural join t4)); -- error 1052 select * from (t1 join t2 on t1.b=t2.b) natural join (t3 natural join t4); -- error 1052 @@ -512,11 +510,7 @@ select * from (t3 join (t4 natural join t5) on (b < z)) natural join (t1 natural join t2); -- error 1052 --- BUG#15357: this query should return an ambiguous column error --- Expected result: the query must return error with duplicate column 'c' ---select * from (t1 natural join t2) --- natural join --- (t3 join (t4 natural join t5) on (b < z)); +select * from (t1 natural join t2) natural join (t3 join (t4 natural join t5) on (b < z)); -- error 1054 select t1.b from v1a; @@ -540,4 +534,27 @@ drop view v3a; drop view v3b; drop view v4; +# +# BUG#15229 - columns of nested joins that are not natural joins incorrectly +# materialized +# +create table t1 (a1 int, a2 int); +create table t2 (a1 int, b int); +create table t3 (c1 int, c2 int); +create table t4 (c2 int); + +insert into t1 values (1,1); +insert into t2 values (1,1); +insert into t3 values (1,1); +insert into t4 values (1); + +select * from t1 join t2 using (a1) join t3 on b=c1 join t4 using (c2); +select * from t3 join (t1 join t2 using (a1)) on b=c1 join t4 using (c2); +select a2 from t1 join t2 using (a1) join t3 on b=c1 join t4 using (c2); +select a2 from t3 join (t1 join t2 using (a1)) on b=c1 join t4 using (c2); +select a2 from ((t1 join t2 using (a1)) join t3 on b=c1) join t4 using (c2); +select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; + +drop table t1,t2,t3,t4; + # End of tests for WL#2486 - natural/using join diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0e1c1525c9e..31ec95e3cc5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3608,8 +3608,18 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, Natural_join_column *nj_col_1, *nj_col_2; const char *field_name_1; Query_arena *arena, backup; - bool add_columns= TRUE; bool result= TRUE; + bool first_outer_loop= TRUE; + /* + Leaf table references to which new natural join columns are added + if the leaves are != NULL. + */ + TABLE_LIST *leaf_1= (table_ref_1->nested_join && + !table_ref_1->is_natural_join) ? + NULL : table_ref_1; + TABLE_LIST *leaf_2= (table_ref_2->nested_join && + !table_ref_2->is_natural_join) ? + NULL : table_ref_2; DBUG_ENTER("mark_common_columns"); DBUG_PRINT("info", ("operand_1: %s operand_2: %s", @@ -3618,35 +3628,13 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, *found_using_fields= 0; arena= thd->activate_stmt_arena_if_needed(&backup); - /* - TABLE_LIST::join_columns could be allocated by the previous call to - store_natural_using_join_columns() for the lower level of nested tables. - */ - if (!table_ref_1->join_columns) - { - if (!(table_ref_1->join_columns= new List)) - goto err; - table_ref_1->is_join_columns_complete= FALSE; - } - if (!table_ref_2->join_columns) - { - if (!(table_ref_2->join_columns= new List)) - goto err; - table_ref_2->is_join_columns_complete= FALSE; - } - for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next()) { - bool is_created_1; bool found= FALSE; - if (!(nj_col_1= it_1.get_or_create_column_ref(&is_created_1))) + if (!(nj_col_1= it_1.get_or_create_column_ref(leaf_1))) goto err; field_name_1= nj_col_1->name(); - /* If nj_col_1 was just created add it to the list of join columns. */ - if (is_created_1) - table_ref_1->join_columns->push_back(nj_col_1); - /* Find a field with the same name in table_ref_2. @@ -3657,17 +3645,12 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, nj_col_2= NULL; for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next()) { - bool is_created_2; Natural_join_column *cur_nj_col_2; const char *cur_field_name_2; - if (!(cur_nj_col_2= it_2.get_or_create_column_ref(&is_created_2))) + if (!(cur_nj_col_2= it_2.get_or_create_column_ref(leaf_2))) goto err; cur_field_name_2= cur_nj_col_2->name(); - /* If nj_col_1 was just created add it to the list of join columns. */ - 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. A common field is duplicate either if it was already found in @@ -3686,9 +3669,15 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, found= TRUE; } } - /* Force it_2.set() to use table_ref_2->join_columns. */ - table_ref_2->is_join_columns_complete= TRUE; - add_columns= FALSE; + if (first_outer_loop && leaf_2) + { + /* + Make sure that the next inner loop "knows" that all columns + are materialized already. + */ + leaf_2->is_join_columns_complete= TRUE; + first_outer_loop= FALSE; + } if (!found) continue; // No matching field @@ -3772,7 +3761,8 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, ++(*found_using_fields); } } - table_ref_1->is_join_columns_complete= TRUE; + if (leaf_1) + leaf_1->is_join_columns_complete= TRUE; /* Everything is OK. @@ -4625,16 +4615,15 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (tables->is_natural_join) { - bool is_created; TABLE *field_table; /* In this case we are sure that the column ref will not be created because it was already created and stored with the natural join. */ Natural_join_column *nj_col; - if (!(nj_col= field_iterator.get_or_create_column_ref(&is_created))) + if (!(nj_col= field_iterator.get_natural_column_ref())) DBUG_RETURN(TRUE); - DBUG_ASSERT(nj_col->table_field && !is_created); + DBUG_ASSERT(nj_col->table_field); field_table= nj_col->table_ref->table; if (field_table) { diff --git a/sql/table.cc b/sql/table.cc index 268d7a0be49..867ac4fb3bc 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2816,11 +2816,31 @@ GRANT_INFO *Field_iterator_table_ref::grant() SYNOPSIS Field_iterator_table_ref::get_or_create_column_ref() - is_created [out] set to TRUE if the column was created, - FALSE if we return an already created colum + parent_table_ref the parent table reference over which the + iterator is iterating DESCRIPTION - TODO + Create a new natural join column for the current field of the + iterator if no such column was created, or return an already + created natural join column. The former happens for base tables or + views, and the latter for natural/using joins. If a new field is + created, then the field is added to 'parent_table_ref' if it is + given, or to the original table referene of the field if + parent_table_ref == NULL. + + NOTES + This method is designed so that when a Field_iterator_table_ref + walks through the fields of a table reference, all its fields + are created and stored as follows: + - If the table reference being iterated is a stored table, view or + natural/using join, store all natural join columns in a list + attached to that table reference. + - If the table reference being iterated is a nested join that is + not natural/using join, then do not materialize its result + fields. This is OK because for such table references + Field_iterator_table_ref iterates over the fields of the nested + table references (recursively). In this way we avoid the storage + of unnecessay copies of result columns of nested joins. RETURN # Pointer to a column of a natural join (or its operand) @@ -2828,22 +2848,28 @@ GRANT_INFO *Field_iterator_table_ref::grant() */ Natural_join_column * -Field_iterator_table_ref::get_or_create_column_ref(bool *is_created) +Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref) { Natural_join_column *nj_col; + bool is_created= TRUE; + uint field_count; + TABLE_LIST *add_table_ref= parent_table_ref ? + parent_table_ref : table_ref; - *is_created= TRUE; if (field_it == &table_field_it) { /* The field belongs to a stored table. */ Field *field= table_field_it.field(); nj_col= new Natural_join_column(field, table_ref); + field_count= table_ref->table->s->fields; } else if (field_it == &view_field_it) { /* The field belongs to a merge view or information schema table. */ Field_translator *translated_field= view_field_it.field_translator(); nj_col= new Natural_join_column(translated_field, table_ref); + field_count= table_ref->field_translation_end - + table_ref->field_translation; } else { @@ -2852,12 +2878,43 @@ Field_iterator_table_ref::get_or_create_column_ref(bool *is_created) already created via one of the two constructor calls above. In this case we just return the already created column reference. */ - *is_created= FALSE; + DBUG_ASSERT(table_ref->is_join_columns_complete); + is_created= FALSE; nj_col= natural_join_it.column_ref(); DBUG_ASSERT(nj_col); } DBUG_ASSERT(!nj_col->table_field || nj_col->table_ref->table == nj_col->table_field->table); + + /* + If the natural join column was just created add it to the list of + natural join columns of either 'parent_table_ref' or to the table + reference that directly contains the original field. + */ + if (is_created) + { + /* Make sure not all columns were materialized. */ + DBUG_ASSERT(!add_table_ref->is_join_columns_complete); + if (!add_table_ref->join_columns) + { + /* Create a list of natural join columns on demand. */ + if (!(add_table_ref->join_columns= new List)) + return NULL; + add_table_ref->is_join_columns_complete= FALSE; + } + add_table_ref->join_columns->push_back(nj_col); + /* + If new fields are added to their original table reference, mark if + all fields were added. We do it here as the caller has no easy way + of knowing when to do it. + If the fields are being added to parent_table_ref, then the caller + must take care to mark when all fields are created/added. + */ + if (!parent_table_ref && + add_table_ref->join_columns->elements == field_count) + add_table_ref->is_join_columns_complete= TRUE; + } + return nj_col; } diff --git a/sql/table.h b/sql/table.h index ce0616a6833..65afcac130e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -782,7 +782,7 @@ public: GRANT_INFO *grant(); Item *create_item(THD *thd) { return field_it->create_item(thd); } Field *field() { return field_it->field(); } - Natural_join_column *get_or_create_column_ref(bool *is_created); + Natural_join_column *get_or_create_column_ref(TABLE_LIST *parent_table_ref); Natural_join_column *get_natural_column_ref(); }; From 500d6b851ca426ebbea8a36c7a24aa46beda1bab Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 3 Mar 2006 15:29:39 +0400 Subject: [PATCH 05/12] Fix for bug#17826 'type_decimal' fails with ps-protocol removed unnecessary calculation of cache value otherwise Join::preapre tries to calculate undefined values(filed values) mysql-test/r/type_decimal.result: Fix for bug#17826 'type_decimal' fails with ps-protocol test case, this test case reproduce the same bug but without PS protocol mysql-test/t/type_decimal.test: Fix for bug#17826 'type_decimal' fails with ps-protocol test case, this test case reproduce the same bug but without PS protocol --- mysql-test/r/type_decimal.result | 7 +++++++ mysql-test/t/type_decimal.test | 8 ++++++++ sql/item_cmpfunc.cc | 6 ------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/type_decimal.result b/mysql-test/r/type_decimal.result index d7f5f9fa328..8b2c08065e0 100644 --- a/mysql-test/r/type_decimal.result +++ b/mysql-test/r/type_decimal.result @@ -772,3 +772,10 @@ productid zlevelprice 003trans 39.98 004trans 31.18 drop table t1, t2; +create table t1 (f1 decimal(5)); +insert into t1 values (40); +flush tables; +select f1 from t1 where f1 in (select f1 from t1); +f1 +40 +drop table t1; diff --git a/mysql-test/t/type_decimal.test b/mysql-test/t/type_decimal.test index 07347322453..441d750004e 100644 --- a/mysql-test/t/type_decimal.test +++ b/mysql-test/t/type_decimal.test @@ -377,3 +377,11 @@ insert INTO t2 SELECT * FROM t1; select * from t2; drop table t1, t2; +# +# Bug #17826 'type_decimal' fails with ps-protocol +# +create table t1 (f1 decimal(5)); +insert into t1 values (40); +flush tables; +select f1 from t1 where f1 in (select f1 from t1); +drop table t1; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 7ba8a536ac7..304e9e8babe 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -698,12 +698,6 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) return 1; cache->setup(args[0]); - /* - If it is preparation PS only then we do not know values of parameters => - cant't get there values and do not need that values. - */ - if (!thd->stmt_arena->is_stmt_prepare()) - cache->store(args[0]); if (cache->cols() == 1) { if ((used_tables_cache= args[0]->used_tables())) From 0dcd02587dbcfee967e508da856c021398f2192d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 3 Mar 2006 20:30:28 +0100 Subject: [PATCH 06/12] mysql.spec.sh: Use installed libz.a to avoid hard to solve static linking problems support-files/mysql.spec.sh: Use installed libz.a to avoid hard to solve static linking problems --- support-files/mysql.spec.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 599f397e351..5860e612e66 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -369,8 +369,8 @@ BuildMySQL "--disable-shared \ --with-client-ldflags='-all-static' \ $USE_OTHER_LIBC_DIR \ %else -%endif --with-zlib-dir=bundled \ +%endif --with-comment=\"MySQL Community Edition - Standard (GPL)\" \ --with-server-suffix='%{server_suffix}' \ --with-archive-storage-engine \ @@ -690,8 +690,11 @@ fi %{_libdir}/mysql/libndbclient.a %{_libdir}/mysql/libndbclient.la %{_libdir}/mysql/libvio.a +%if %{STATIC_BUILD} +%else %{_libdir}/mysql/libz.a %{_libdir}/mysql/libz.la +%endif %files shared %defattr(-, root, root, 0755) @@ -723,6 +726,11 @@ fi * Fri Mar 03 2006 Kent Boortz - Don't output an embedded package as it is empty +- Can't use bundled zlib when doing static build. Might be a + automake/libtool problem, having two .la files, "libmysqlclient.la" + and "libz.la", on the same command line to link "thread_test" + expands to too many "-lc", "-lpthread" and other libs giving hard + to nail down duplicate symbol defintion problems. * Fri Jan 10 2006 Joerg Bruehe From 21d6e172f5cb182132f1869dac0707da98a462bb Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 3 Mar 2006 16:26:38 -0500 Subject: [PATCH 07/12] Bug#16859 involves truncating column data at NUL characters. Instead, the client will now substitute spaces for NULs, so that the grid isn't messed up due to silently consumed NULs and that the full field is shown. client/mysql.cc: For non-numbers, print each character at a time, instead of using the fprintf() facility, which interprets an array of chars as a C string, which is necessarily NUL terminated. We mustn't terminate on NULs, and since we know the length of the data, we needn't. mysql-test/r/mysql.result: Add a test. mysql-test/t/mysql.test: Add a test. --- client/mysql.cc | 55 +++++++++++++++++++++++++++++++++------ mysql-test/r/mysql.result | 7 +++++ mysql-test/t/mysql.test | 5 ++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index 8d578aac58b..eb6970691bb 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -185,6 +185,7 @@ void tee_fprintf(FILE *file, const char *fmt, ...); void tee_fputs(const char *s, FILE *file); void tee_puts(const char *s, FILE *file); void tee_putc(int c, FILE *file); +static void tee_print_sized_data(const char *data, unsigned int length, unsigned int width); /* The names of functions that actually do the manipulation. */ static int get_options(int argc,char **argv); static int com_quit(String *str,char*), @@ -2308,20 +2309,29 @@ print_table_data(MYSQL_RES *result) for (uint off= 0; off < mysql_num_fields(result); off++) { const char *str= cur[off] ? cur[off] : "NULL"; + uint currlength; + uint maxlength; + uint numcells; + field= mysql_fetch_field(result); - uint maxlength= field->max_length; + maxlength= field->max_length; + currlength= (uint) lengths[off]; + numcells= charset_info->cset->numcells(charset_info, + str, str + currlength); if (maxlength > MAX_COLUMN_LENGTH) { - tee_fputs(str, PAGER); - tee_fputs(" |", PAGER); + tee_print_sized_data(str, currlength, maxlength); + tee_fputs(" |", PAGER); } else { - uint currlength= (uint) lengths[off]; - uint numcells= charset_info->cset->numcells(charset_info, - str, str + currlength); - tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|", - maxlength + currlength - numcells, str); + if (num_flag[off] != 0) + tee_fprintf(PAGER, " %-*s|", maxlength + currlength - numcells, str); + else + { + tee_print_sized_data(str, currlength, maxlength); + tee_fputs(" |", PAGER); + } } } (void) tee_fputs("\n", PAGER); @@ -2331,6 +2341,35 @@ print_table_data(MYSQL_RES *result) } +static void +tee_print_sized_data(const char *data, unsigned int length, unsigned int width) +{ + /* + It is not a number, so print each character justified to the left. + For '\0's print ASCII spaces instead, as '\0' is eaten by (at + least my) console driver, and that messes up the pretty table + grid. (The \0 is also the reason we can't use fprintf() .) + */ + unsigned int i; + const char *p; + + tee_putc(' ', PAGER); + + for (i= 0, p= data; i < length; i+= 1, p+= 1) + { + if (*p == '\0') + tee_putc((int)' ', PAGER); + else + tee_putc((int)*p, PAGER); + } + + i+= 1; + for ( ; i < width; i+= 1) + tee_putc((int)' ', PAGER); +} + + + static void print_table_data_html(MYSQL_RES *result) { diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result index 611813d9c3f..57067bea36b 100644 --- a/mysql-test/r/mysql.result +++ b/mysql-test/r/mysql.result @@ -69,3 +69,10 @@ c_cp932 ソ ソ ソ ++----------------------+------------+--------+ +| concat('>',col1,'<') | col2 | col3 | ++----------------------+------------+--------+ +| >a < | b | 123421 | +| >a < | 0123456789 | 4 | +| >abcd< | | 4 | ++----------------------+------------+--------+ diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index 4712e7e8266..dbf65845e6a 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -56,3 +56,8 @@ drop table t1; --exec $MYSQL --default-character-set=utf8 test -e "charset cp932; set character_set_client= cp932; select 'ƒ\'" --exec $MYSQL --default-character-set=utf8 test -e "/*charset cp932 */; set character_set_client= cp932; select 'ƒ\'" --exec $MYSQL --default-character-set=utf8 test -e "/*!\C cp932 */; set character_set_client= cp932; select 'ƒ\'" + +# +# Bug#16859 -- NULLs in columns must not truncate data as if a C-language "string". +# +--exec $MYSQL -t test -e "create table t1 (col1 binary(4), col2 varchar(10), col3 int); insert into t1 values ('a', 'b', 123421),('a ', '0123456789', 4), ('abcd', '', 4); select concat('>',col1,'<'), col2, col3 from t1; drop table t1;" 2>&1 From 350475fae41d31c37214705a54bdd93bb435a4f3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 4 Mar 2006 16:55:06 +0300 Subject: [PATCH 08/12] Fix for bug #17866 "Problem with renaming table with triggers with fully qualified subject table" which was introduced during work on bug #13525 "Rename table does not keep info of triggers". The bug was caused by the fact that during reconstruction of CREATE TRIGGER statement stored in .TRG file which happened during RENAME TABLE we damaged trigger definition in case when it contained fully qualified name of subject table (see comment for sql_yacc.yy for more info). mysql-test/r/trigger.result: Added test for bug #17866 "Problem with renaming table with triggers with fully qualified subject table". mysql-test/t/trigger.test: Added test for bug #17866 "Problem with renaming table with triggers with fully qualified subject table". sql/sql_trigger.cc: Table_triggers_list::change_table_name_in_triggers(): Instead of trying to use pointer to the end of subject table identifier we use pointer to the beginning of FOR lexeme now, so during reconstruction of CREATE TRIGGER statement in this function we need to add extra space before part which begins with FOR to get nice trigger definition. sql/sql_yacc.yy: trigger_tail: In this rule we can't rely on using remember_end token after table_ident token, since value returned depends on whether table name is fully qualified or not. So instead of trying to get pointer to the end of table identifier we use pointer to the beginning of FOR lexeme. --- mysql-test/r/trigger.result | 30 ++++++++++++++++++------------ mysql-test/t/trigger.test | 14 +++++++++----- sql/sql_trigger.cc | 1 + sql/sql_yacc.yy | 4 ++-- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 320f4e5c3d9..c9a91758336 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -787,46 +787,52 @@ drop trigger t1_bi; ERROR 3D000: No database selected create table t1 (id int); create trigger t1_bi before insert on t1 for each row set @a:=new.id; +create trigger t1_ai after insert on test.t1 for each row set @b:=new.id; insert into t1 values (101); -select @a; -@a -101 +select @a, @b; +@a @b +101 101 select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; trigger_schema trigger_name event_object_schema event_object_table action_statement test t1_bi test t1 set @a:=new.id +test t1_ai test t1 set @b:=new.id rename table t1 to t2; insert into t2 values (102); -select @a; -@a -102 +select @a, @b; +@a @b +102 102 select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; trigger_schema trigger_name event_object_schema event_object_table action_statement test t1_bi test t2 set @a:=new.id +test t1_ai test t2 set @b:=new.id alter table t2 rename to t3; insert into t3 values (103); -select @a; -@a -103 +select @a, @b; +@a @b +103 103 select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; trigger_schema trigger_name event_object_schema event_object_table action_statement test t1_bi test t3 set @a:=new.id +test t1_ai test t3 set @b:=new.id alter table t3 rename to t4, add column val int default 0; insert into t4 values (104, 1); -select @a; -@a -104 +select @a, @b; +@a @b +104 104 select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; trigger_schema trigger_name event_object_schema event_object_table action_statement test t1_bi test t4 set @a:=new.id +test t1_ai test t4 set @b:=new.id drop trigger t1_bi; +drop trigger t1_ai; drop table t4; create database mysqltest; use mysqltest; diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 0ac57394c2f..1d68b519f1d 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -960,38 +960,42 @@ drop trigger t1_bi; connection default; # -# Test for bug #13525 "Rename table does not keep info of triggers" +# Tests for bug #13525 "Rename table does not keep info of triggers" +# and bug #17866 "Problem with renaming table with triggers with fully +# qualified subject table". # create table t1 (id int); create trigger t1_bi before insert on t1 for each row set @a:=new.id; +create trigger t1_ai after insert on test.t1 for each row set @b:=new.id; insert into t1 values (101); -select @a; +select @a, @b; select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; rename table t1 to t2; # Trigger should work after rename insert into t2 values (102); -select @a; +select @a, @b; select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; # Let us check that the same works for simple ALTER TABLE ... RENAME alter table t2 rename to t3; insert into t3 values (103); -select @a; +select @a, @b; select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; # And for more complex ALTER TABLE alter table t3 rename to t4, add column val int default 0; insert into t4 values (104, 1); -select @a; +select @a, @b; select trigger_schema, trigger_name, event_object_schema, event_object_table, action_statement from information_schema.triggers where event_object_schema = 'test'; # .TRN file should be updated with new table name drop trigger t1_bi; +drop trigger t1_ai; drop table t4; # Rename between different databases if triggers exist should fail create database mysqltest; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 4c3ae5c032d..cef8c4dd1dc 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1217,6 +1217,7 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, buff.append(def->str, before_on_len); buff.append(STRING_WITH_LEN("ON ")); append_identifier(thd, &buff, new_table_name->str, new_table_name->length); + buff.append(STRING_WITH_LEN(" ")); on_q_table_name_len= buff.length() - before_on_len; buff.append(on_table_name->str + on_table_name->length, def->length - (before_on_len + on_table_name->length)); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 468ade1bbbd..86d50bcd707 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9127,7 +9127,7 @@ view_check_option: trigger_tail: TRIGGER_SYM remember_name sp_name trg_action_time trg_event - ON remember_name table_ident remember_end FOR_SYM EACH_SYM ROW_SYM + ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM { LEX *lex= Lex; sp_head *sp; @@ -9145,7 +9145,7 @@ trigger_tail: lex->trigger_definition_begin= $2; lex->ident.str= $7; - lex->ident.length= $9 - $7; + lex->ident.length= $10 - $7; sp->m_type= TYPE_ENUM_TRIGGER; lex->sphead= sp; From 4391f938922a7f62831481c350ad4a680c64c827 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 5 Mar 2006 00:38:54 +0300 Subject: [PATCH 09/12] Revert the changeset for Bug#16144 "mysql_stmt_attr_get type error": it breaks binary compatibility. The patch will be left intact in 5.1. libmysql/libmysql.c: Revert the changeset for Bug#16144: it breaks binary compatibility. tests/mysql_client_test.c: Revert the changeset for Bug#16144: it breaks binary compatibility. --- libmysql/libmysql.c | 2 +- tests/mysql_client_test.c | 20 -------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 498881aa947..898e0ad3273 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -2733,7 +2733,7 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, { switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: - *(my_bool*) value= stmt->update_max_length; + *(unsigned long *) value= stmt->update_max_length; break; default: return TRUE; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index dbc2f582466..71ae5e4f052 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -11784,25 +11784,6 @@ static void test_bug12925() } -/* Bug #16144: mysql_stmt_attr_get type error */ - -static void test_bug16144() -{ - const my_bool flag_orig= (my_bool) 0xde; - my_bool flag= flag_orig; - MYSQL_STMT *stmt; - myheader("test_bug16144"); - - /* Check that attr_get returns correct data on little and big endian CPUs */ - stmt= mysql_stmt_init(mysql); - mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (const void*) &flag); - mysql_stmt_attr_get(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &flag); - DIE_UNLESS(flag == flag_orig); - - mysql_stmt_close(stmt); -} - - /* Bug #15613: "libmysqlclient API function mysql_stmt_prepare returns wrong field length" @@ -12089,7 +12070,6 @@ static struct my_tests_st my_tests[]= { { "test_bug12001", test_bug12001 }, { "test_bug11718", test_bug11718 }, { "test_bug12925", test_bug12925 }, - { "test_bug16144", test_bug16144 }, { "test_bug15613", test_bug15613 }, { 0, 0 } }; From bb453729b8860fca3f3064287fecf566faa35397 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 6 Mar 2006 12:52:38 +0400 Subject: [PATCH 10/12] Fix for bug#16678 FORMAT gives wrong result if client run with default-character-set=utf8 calculate Item_func_format::max_length using charset->mbmaxlen mysql-test/r/func_math.result: Fix for bug#16678 FORMAT gives wrong result if client run with default-character-set=utf8 test case mysql-test/t/func_math.test: Fix for bug#16678 FORMAT gives wrong result if client run with default-character-set=utf8 test case --- mysql-test/r/func_math.result | 20 ++++++++++++++++++++ mysql-test/t/func_math.test | 22 ++++++++++++++++++++++ sql/item_strfunc.h | 4 +++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index 1507f959ae6..43748257203 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -218,3 +218,23 @@ truncate(-5000111000111000155,-1) select truncate(15000111000111000155,-1); truncate(15000111000111000155,-1) 15000111000111000150 +set names utf8; +create table t1 +(f1 varchar(32) not null, +f2 smallint(5) unsigned not null, +f3 int(10) unsigned not null default '0') +engine=myisam default charset=utf8; +insert into t1 values ('zombie',0,0),('gold',1,10000),('silver',2,10000); +create table t2 +(f1 int(10) unsigned not null, +f2 int(10) unsigned not null, +f3 smallint(5) unsigned not null) +engine=myisam default charset=utf8; +insert into t2 values (16777216,16787215,1),(33554432,33564431,2); +select format(t2.f2-t2.f1+1,0) from t1,t2 +where t1.f2 = t2.f3 order by t1.f1; +format(t2.f2-t2.f1+1,0) +10,000 +10,000 +drop table t1, t2; +set names default; diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 8dc4eb215c7..4041c267134 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -155,3 +155,25 @@ select truncate(-5000111000111000155,-1); # truncate on unsigned bigint select truncate(15000111000111000155,-1); +# +# Bug#16678 FORMAT gives wrong result if client run with default-character-set=utf8 +# +set names utf8; +create table t1 +(f1 varchar(32) not null, + f2 smallint(5) unsigned not null, + f3 int(10) unsigned not null default '0') +engine=myisam default charset=utf8; +insert into t1 values ('zombie',0,0),('gold',1,10000),('silver',2,10000); + +create table t2 +(f1 int(10) unsigned not null, + f2 int(10) unsigned not null, + f3 smallint(5) unsigned not null) +engine=myisam default charset=utf8; +insert into t2 values (16777216,16787215,1),(33554432,33564431,2); + +select format(t2.f2-t2.f1+1,0) from t1,t2 +where t1.f2 = t2.f3 order by t1.f1; +drop table t1, t2; +set names default; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 50ec0b36ce8..6a95a9e5d1f 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -471,7 +471,9 @@ public: void fix_length_and_dec() { collation.set(default_charset()); - max_length=args[0]->max_length+(args[0]->max_length-args[0]->decimals)/3; + uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; + max_length= ((char_length + (char_length-args[0]->decimals)/3) * + collation.collation->mbmaxlen); } const char *func_name() const { return "format"; } void print(String *); From 3ecf9f040a7cbbbab243a31a9189ecbe94796d68 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 6 Mar 2006 14:03:40 +0400 Subject: [PATCH 11/12] Fix for bug#14385 GRANT and mapping to correct user account problems Check if the host of table hash record exactly matches host from GRANT command mysql-test/r/grant.result: Fix for bug#14385 GRANT and mapping to correct user account problems test case mysql-test/t/grant.test: Fix for bug#14385 GRANT and mapping to correct user account problems test case --- mysql-test/r/grant.result | 18 ++++++++++++++++++ mysql-test/t/grant.test | 16 ++++++++++++++++ sql/sql_acl.cc | 5 ++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 13593ec2a88..dffa4988ea7 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -473,3 +473,21 @@ GRANT USAGE ON *.* TO 'mysqltest_7'@'' IDENTIFIED BY PASSWORD '*2FB071A056F9BB74 drop user mysqltest_7@; show grants for mysqltest_7@; ERROR 42000: There is no such grant defined for user 'mysqltest_7' on host '' +create database mysqltest; +use mysqltest; +create table t1(f1 int); +GRANT DELETE ON mysqltest.t1 TO mysqltest1@'%'; +GRANT SELECT ON mysqltest.t1 TO mysqltest1@'192.%'; +show grants for mysqltest1@'192.%'; +Grants for mysqltest1@192.% +GRANT USAGE ON *.* TO 'mysqltest1'@'192.%' +GRANT SELECT ON `mysqltest`.`t1` TO 'mysqltest1'@'192.%' +show grants for mysqltest1@'%'; +Grants for mysqltest1@% +GRANT USAGE ON *.* TO 'mysqltest1'@'%' +GRANT DELETE ON `mysqltest`.`t1` TO 'mysqltest1'@'%' +delete from mysql.user where user='mysqltest1'; +delete from mysql.db where user='mysqltest1'; +delete from mysql.tables_priv where user='mysqltest1'; +flush privileges; +drop database mysqltest; diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index 805fa881399..60b60547fcc 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -433,4 +433,20 @@ drop user mysqltest_7@; --error 1141 show grants for mysqltest_7@; +# +# Bug#14385: GRANT and mapping to correct user account problems +# +create database mysqltest; +use mysqltest; +create table t1(f1 int); +GRANT DELETE ON mysqltest.t1 TO mysqltest1@'%'; +GRANT SELECT ON mysqltest.t1 TO mysqltest1@'192.%'; +show grants for mysqltest1@'192.%'; +show grants for mysqltest1@'%'; +delete from mysql.user where user='mysqltest1'; +delete from mysql.db where user='mysqltest1'; +delete from mysql.tables_priv where user='mysqltest1'; +flush privileges; +drop database mysqltest; + # End of 4.1 tests diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c1847d010c5..4626e5892a4 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2001,7 +2001,10 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, { if (exact) { - if (compare_hostname(&grant_table->host, host, ip)) + if ((host && + !my_strcasecmp(system_charset_info, host, + grant_table->host.hostname)) || + (ip && !strcmp(ip, grant_table->host.hostname))) return grant_table; } else From 76e5c1b4b6bb3e748e46a204157409f7ba73a7bf Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 6 Mar 2006 15:14:15 +0400 Subject: [PATCH 12/12] post-merge fix --- mysql-test/r/grant.result | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 603cf8bd675..3432de5179a 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -642,4 +642,3 @@ delete from mysql.db where user='mysqltest1'; delete from mysql.tables_priv where user='mysqltest1'; flush privileges; drop database mysqltest; -