From 4e9e038ff97f5c7a6588a42e97c18c827ed97e8c Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Tue, 1 Nov 2005 14:58:52 +0100 Subject: [PATCH 1/5] Fixed BUG#14376: MySQL crash on scoped variable (re)initialization Added finer scope control for default clauses of local variable declarations. --- mysql-test/r/sp.result | 29 +++++++++++++++++++++++++++ mysql-test/t/sp.test | 45 ++++++++++++++++++++++++++++++++++++++++++ sql/sp_pcontext.cc | 5 +++-- sql/sp_pcontext.h | 16 +++++++++++++++ sql/sql_yacc.yy | 8 +++++++- 5 files changed, 100 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 1f3f7dba7da..1e4d3de5dfa 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3575,4 +3575,33 @@ DROP VIEW bug13095_v1 DROP PROCEDURE IF EXISTS bug13095; DROP VIEW IF EXISTS bug13095_v1; DROP TABLE IF EXISTS bug13095_t1; +drop procedure if exists bug14376| +create procedure bug14376() +begin +declare x int default x; +end| +call bug14376()| +ERROR 42S22: Unknown column 'x' in 'field list' +drop procedure bug14376| +create procedure bug14376() +begin +declare x int default 42; +begin +declare x int default x; +select x; +end; +end| +call bug14376()| +x +42 +drop procedure bug14376| +create procedure bug14376(x int) +begin +declare x int default x; +select x; +end| +call bug14376(4711)| +x +4711 +drop procedure bug14376| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index ab57139bb77..f1b76686c4a 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -4489,6 +4489,51 @@ DROP TABLE IF EXISTS bug13095_t1; delimiter |; +# +# BUG#14376: MySQL crash on scoped variable (re)initialization +# +--disable_warnings +drop procedure if exists bug14376| +--enable_warnings + +create procedure bug14376() +begin + declare x int default x; +end| + +# Not the error we want, but that's what we got for now... +--error ER_BAD_FIELD_ERROR +call bug14376()| +drop procedure bug14376| + +create procedure bug14376() +begin + declare x int default 42; + + begin + declare x int default x; + + select x; + end; +end| + +call bug14376()| + +drop procedure bug14376| + +create procedure bug14376(x int) +begin + declare x int default x; + + select x; +end| + +call bug14376(4711)| + +drop procedure bug14376| + + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index f873b676925..cca3e03d69c 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -52,7 +52,7 @@ sp_cond_check(LEX_STRING *sqlstate) sp_pcontext::sp_pcontext(sp_pcontext *prev) : Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0), - m_handlers(0), m_parent(prev) + m_handlers(0), m_parent(prev), m_pboundary(0) { VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8)); VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8)); @@ -150,7 +150,7 @@ sp_pcontext::diff_cursors(sp_pcontext *ctx) sp_pvar_t * sp_pcontext::find_pvar(LEX_STRING *name, my_bool scoped) { - uint i= m_pvar.elements; + uint i= m_pboundary; while (i--) { @@ -186,6 +186,7 @@ sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type, p->offset= current_pvars(); p->dflt= NULL; insert_dynamic(&m_pvar, (gptr)&p); + m_pboundary= m_pvar.elements; } } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index bd2259cb6fb..fbeac37c058 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -164,6 +164,7 @@ class sp_pcontext : public Sql_alloc { while (num--) pop_dynamic(&m_pvar); + m_pboundary= m_pvar.elements; } // Find by name @@ -183,6 +184,14 @@ class sp_pcontext : public Sql_alloc return p; } + // Set the current scope boundary (for default values) + // The argument is the number of variables to skip. + inline void + declare_var_boundary(uint n) + { + m_pboundary= m_pvar.elements-n; + } + // // Labels // @@ -287,6 +296,13 @@ private: uint m_poffset; // Variable offset for this context uint m_coffset; // Cursor offset for this context + /* + Boundary for finding variables in this in this context. + This is normally the same as m_pvar.elements, but differs during + parsing of DECLARE ... DEFAULT, to get the scope right for DEFAULT + values. + */ + uint m_pboundary; DYNAMIC_ARRAY m_pvar; // Parameters/variables DYNAMIC_ARRAY m_cond; // Conditions diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 109dcd7e86a..c3c53b2f611 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1660,7 +1660,12 @@ sp_decls: sp_decl: DECLARE_SYM sp_decl_idents type - { Lex->sphead->reset_lex(YYTHD); } + { + LEX *lex= Lex; + + lex->sphead->reset_lex(YYTHD); + lex->spcont->declare_var_boundary($2); + } sp_opt_default { LEX *lex= Lex; @@ -1690,6 +1695,7 @@ sp_decl: lex->sphead->add_instr(in); ctx->set_default(i, it); } + ctx->declare_var_boundary(0); lex->sphead->restore_lex(YYTHD); $$.vars= $2; $$.conds= $$.hndlrs= $$.curs= 0; From 3f72e7645f93454ed34cbd8b0ca9cdbae05a59d0 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Thu, 1 Dec 2005 23:22:20 +0300 Subject: [PATCH 2/5] Fix bug#15028 Multitable update returns different numbers of matched rows depending on table order multi_update::send_data() was counting updates, not updated rows. Thus if one record have several updates it will be counted several times in 'rows matched' but updated only once. multi_update::send_data() now counts only unique rows. --- mysql-test/r/update.result | 13 +++++++++++++ mysql-test/t/update.test | 17 +++++++++++++++++ sql/sql_update.cc | 13 +++++++------ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/update.result b/mysql-test/r/update.result index 74c628f96c4..7854fd773a1 100644 --- a/mysql-test/r/update.result +++ b/mysql-test/r/update.result @@ -345,3 +345,16 @@ f1 2000-01-01 2002-02-02 drop table t1; +create table t1 (f1 int); +create table t2 (f2 int); +insert into t1 values(1),(2); +insert into t2 values(1),(1); +update t1,t2 set f1=3,f2=3 where f1=f2 and f1=1; +affected rows: 3 +info: Rows matched: 3 Changed: 3 Warnings: 0 +update t2 set f2=1; +update t1 set f1=1 where f1=3; +update t2,t1 set f1=3,f2=3 where f1=f2 and f1=1; +affected rows: 3 +info: Rows matched: 3 Changed: 3 Warnings: 0 +drop table t1,t2; diff --git a/mysql-test/t/update.test b/mysql-test/t/update.test index a21d10b6571..95adb40962c 100644 --- a/mysql-test/t/update.test +++ b/mysql-test/t/update.test @@ -270,4 +270,21 @@ insert into t1 values('2000-01-01'),('0000-00-00'); update t1 set f1='2002-02-02' where f1 is null; select * from t1; drop table t1; + +# +# Bug#15028 Multitable update returns different numbers of matched rows +# depending on table order +create table t1 (f1 int); +create table t2 (f2 int); +insert into t1 values(1),(2); +insert into t2 values(1),(1); +--enable_info +update t1,t2 set f1=3,f2=3 where f1=f2 and f1=1; +--disable_info +update t2 set f2=1; +update t1 set f1=1 where f1=3; +--enable_info +update t2,t1 set f1=3,f2=3 where f1=f2 and f1=1; +--disable_info +drop table t1,t2; # End of 4.1 tests diff --git a/sql/sql_update.cc b/sql/sql_update.cc index cb8064bef87..05e13c64aa7 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1082,22 +1082,23 @@ bool multi_update::send_data(List ¬_used_values) int error; TABLE *tmp_table= tmp_tables[offset]; fill_record(tmp_table->field+1, *values_for_table[offset], 1); - found++; /* Store pointer to row */ memcpy((char*) tmp_table->field[0]->ptr, (char*) table->file->ref, table->file->ref_length); /* Write row, ignoring duplicated updates to a row */ - if ((error= tmp_table->file->write_row(tmp_table->record[0])) && - (error != HA_ERR_FOUND_DUPP_KEY && - error != HA_ERR_FOUND_DUPP_UNIQUE)) + if (error= tmp_table->file->write_row(tmp_table->record[0])) { - if (create_myisam_from_heap(thd, tmp_table, tmp_table_param + offset, - error, 1)) + if (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE && + create_myisam_from_heap(thd, tmp_table, + tmp_table_param + offset, error, 1)) { do_update=0; DBUG_RETURN(1); // Not a table_is_full error } } + else + found++; } } DBUG_RETURN(0); From 00d95a8bea72eacef6264e1168783d9ecc26b8be Mon Sep 17 00:00:00 2001 From: "stewart@mysql.com" <> Date: Fri, 2 Dec 2005 09:27:06 +0100 Subject: [PATCH 3/5] BUG#15425 Small window for NF during backup failing without error --- ndb/src/kernel/blocks/backup/Backup.cpp | 15 ++++++++++----- ndb/src/kernel/blocks/dbdict/Dbdict.cpp | 1 - 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ndb/src/kernel/blocks/backup/Backup.cpp b/ndb/src/kernel/blocks/backup/Backup.cpp index 56af24c5cf0..9997d365fa4 100644 --- a/ndb/src/kernel/blocks/backup/Backup.cpp +++ b/ndb/src/kernel/blocks/backup/Backup.cpp @@ -786,13 +786,17 @@ Backup::checkNodeFail(Signal* signal, pos= &ref->nodeId - signal->getDataPtr(); break; } + case GSN_WAIT_GCP_REQ: + case GSN_DROP_TRIG_REQ: case GSN_CREATE_TRIG_REQ: case GSN_ALTER_TRIG_REQ: - case GSN_WAIT_GCP_REQ: + ptr.p->setErrorCode(AbortBackupOrd::BackupFailureDueToNodeFail); + return; case GSN_UTIL_SEQUENCE_REQ: case GSN_UTIL_LOCK_REQ: - case GSN_DROP_TRIG_REQ: return; + default: + ndbrequire(false); } for(Uint32 i = 0; (i = mask.find(i+1)) != NdbNodeBitmask::NotFound; ) @@ -1803,7 +1807,7 @@ Backup::execBACKUP_FRAGMENT_CONF(Signal* signal) const Uint32 nodeId = refToNode(signal->senderBlockRef()); const Uint32 noOfBytes = conf->noOfBytes; const Uint32 noOfRecords = conf->noOfRecords; - + BackupRecordPtr ptr; c_backupPool.getPtr(ptr, ptrI); @@ -1880,7 +1884,7 @@ Backup::execBACKUP_FRAGMENT_REF(Signal* signal) } } } - ndbrequire(false); + goto err; done: ptr.p->masterData.sendCounter--; @@ -1892,7 +1896,8 @@ done: masterAbort(signal, ptr); return; }//if - + +err: AbortBackupOrd *ord = (AbortBackupOrd*)signal->getDataPtrSend(); ord->backupId = ptr.p->backupId; ord->backupPtr = ptr.i; diff --git a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp index 5bd35812b47..6564963f61a 100644 --- a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp +++ b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp @@ -11694,7 +11694,6 @@ Dbdict::alterTrigger_toDropLocal(Signal* signal, OpAlterTriggerPtr opPtr) // broken trigger allowed if force if (! (triggerPtr.p->triggerLocal & TriggerRecord::TL_CREATED_LQH)) { jam(); - ndbrequire(opPtr.p->m_requestFlag & RequestFlag::RF_FORCE); alterTrigger_sendReply(signal, opPtr, false); return; } From ed6e09a38c6871cf7592394050fd787816597cbc Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Fri, 2 Dec 2005 14:30:42 +0100 Subject: [PATCH 4/5] Post-review fixes for BUG#14376: MySQL crash on scoped variable (re)initialization --- sql/sp_pcontext.cc | 3 +-- sql/sp_pcontext.h | 17 +++++++++-------- sql/sql_yacc.yy | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index cca3e03d69c..f5ad7e31b43 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -150,7 +150,7 @@ sp_pcontext::diff_cursors(sp_pcontext *ctx) sp_pvar_t * sp_pcontext::find_pvar(LEX_STRING *name, my_bool scoped) { - uint i= m_pboundary; + uint i= m_pvar.elements - m_pboundary; while (i--) { @@ -186,7 +186,6 @@ sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type, p->offset= current_pvars(); p->dflt= NULL; insert_dynamic(&m_pvar, (gptr)&p); - m_pboundary= m_pvar.elements; } } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index fbeac37c058..24fd76f52fa 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -164,7 +164,6 @@ class sp_pcontext : public Sql_alloc { while (num--) pop_dynamic(&m_pvar); - m_pboundary= m_pvar.elements; } // Find by name @@ -184,12 +183,14 @@ class sp_pcontext : public Sql_alloc return p; } - // Set the current scope boundary (for default values) - // The argument is the number of variables to skip. + /* + Set the current scope boundary (for default values) + The argument is the number of variables to skip. + */ inline void declare_var_boundary(uint n) { - m_pboundary= m_pvar.elements-n; + m_pboundary= n; } // @@ -297,10 +298,10 @@ private: uint m_poffset; // Variable offset for this context uint m_coffset; // Cursor offset for this context /* - Boundary for finding variables in this in this context. - This is normally the same as m_pvar.elements, but differs during - parsing of DECLARE ... DEFAULT, to get the scope right for DEFAULT - values. + Boundary for finding variables in this context. This is the number + of variables currently "invisible" to default clauses. + This is normally 0, but will be larger during parsing of + DECLARE ... DEFAULT, to get the scope right for DEFAULT values. */ uint m_pboundary; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c3c53b2f611..a2b6529663c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1661,11 +1661,11 @@ sp_decls: sp_decl: DECLARE_SYM sp_decl_idents type { - LEX *lex= Lex; + LEX *lex= Lex; - lex->sphead->reset_lex(YYTHD); - lex->spcont->declare_var_boundary($2); - } + lex->sphead->reset_lex(YYTHD); + lex->spcont->declare_var_boundary($2); + } sp_opt_default { LEX *lex= Lex; @@ -1695,7 +1695,7 @@ sp_decl: lex->sphead->add_instr(in); ctx->set_default(i, it); } - ctx->declare_var_boundary(0); + ctx->declare_var_boundary(0); lex->sphead->restore_lex(YYTHD); $$.vars= $2; $$.conds= $$.hndlrs= $$.curs= 0; From b7d7f78fd077a74af20bbe3756a07e5e5ced4706 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 2 Dec 2005 21:05:23 +0300 Subject: [PATCH 5/5] query_cache.result: After merge fix --- mysql-test/r/query_cache.result | 50 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 590f325cee7..405669e95f8 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1022,6 +1022,31 @@ Variable_name Value Qcache_hits 1 drop table t1; create table t1 (a int); +flush status; +(select a from t1) union (select a from t1); +a +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +(select a from t1) union (select a from t1); +a +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +drop table t1; +create table t1 (a int); insert into t1 values (1),(2); CREATE PROCEDURE `p1`() begin @@ -1287,28 +1312,3 @@ Variable_name Value Last_query_cost 0.000000 drop table t1; SET GLOBAL query_cache_size=0; -create table t1 (a int); -flush status; -(select a from t1) union (select a from t1); -a -show status like "Qcache_queries_in_cache"; -Variable_name Value -Qcache_queries_in_cache 1 -show status like "Qcache_inserts"; -Variable_name Value -Qcache_inserts 1 -show status like "Qcache_hits"; -Variable_name Value -Qcache_hits 0 -(select a from t1) union (select a from t1); -a -show status like "Qcache_queries_in_cache"; -Variable_name Value -Qcache_queries_in_cache 1 -show status like "Qcache_inserts"; -Variable_name Value -Qcache_inserts 1 -show status like "Qcache_hits"; -Variable_name Value -Qcache_hits 1 -drop table t1;