From 5b1fdbec535ac9efa1d3dd6eb886f6dbdec4b73d Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Thu, 26 Jan 2006 13:29:46 +0100 Subject: [PATCH 01/24] Fixed BUG#16303: erroneus stored procedures and functions should be droppable Use a special lookup function for DROP, which doesn't attempt to parse the definition. --- mysql-test/r/sp-destruct.result | 8 ++++- mysql-test/t/sp-destruct.test | 11 ++++++- sql/sp.cc | 55 ++++++++++++++++++++++++++++----- sql/sp.h | 5 ++- sql/sql_acl.cc | 2 +- sql/sql_parse.cc | 18 +++++------ 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/mysql-test/r/sp-destruct.result b/mysql-test/r/sp-destruct.result index 1b720be9403..4df8086c84e 100644 --- a/mysql-test/r/sp-destruct.result +++ b/mysql-test/r/sp-destruct.result @@ -72,6 +72,12 @@ drop trigger t1_ai; create trigger t1_ai after insert on t1 for each row call bug14233_3(); insert into t1 values (0); ERROR HY000: Failed to load routine test.bug14233_3. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6) -delete from mysql.proc where name like 'bug14233%'; drop trigger t1_ai; drop table t1; +drop function bug14233_1; +drop function bug14233_2; +drop procedure bug14233_3; +show procedure status; +Db Name Type Definer Modified Created Security_type Comment +show function status; +Db Name Type Definer Modified Created Security_type Comment diff --git a/mysql-test/t/sp-destruct.test b/mysql-test/t/sp-destruct.test index a2a66090866..07df3b06c21 100644 --- a/mysql-test/t/sp-destruct.test +++ b/mysql-test/t/sp-destruct.test @@ -119,6 +119,15 @@ create trigger t1_ai after insert on t1 for each row call bug14233_3(); insert into t1 values (0); # Clean-up -delete from mysql.proc where name like 'bug14233%'; drop trigger t1_ai; drop table t1; + +# +# BUG#16303: erroneus stored procedures and functions should be droppable +# +drop function bug14233_1; +drop function bug14233_2; +drop procedure bug14233_3; +# Assert: These should show nothing. +show procedure status; +show function status; diff --git a/sql/sp.cc b/sql/sp.cc index 37a9c02124e..d2aaa5646a8 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1002,22 +1002,26 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, } +/* + This is used by sql_acl.cc:mysql_routine_grant() and is used to find + the routines in 'routines'. +*/ int -sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error) +sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error) { - TABLE_LIST *table; + TABLE_LIST *routine; bool result= 0; DBUG_ENTER("sp_exists_routine"); - for (table= tables; table; table= table->next_global) + for (routine= routines; routine; routine= routine->next_global) { sp_name *name; LEX_STRING lex_db; LEX_STRING lex_name; - lex_db.length= strlen(table->db); - lex_name.length= strlen(table->table_name); - lex_db.str= thd->strmake(table->db, lex_db.length); - lex_name.str= thd->strmake(table->table_name, lex_name.length); + lex_db.length= strlen(routine->db); + lex_name.length= strlen(routine->table_name); + lex_db.str= thd->strmake(routine->db, lex_db.length); + lex_name.str= thd->strmake(routine->table_name, lex_name.length); name= new sp_name(lex_db, lex_name); name->init_qname(thd); if (sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, @@ -1034,7 +1038,7 @@ sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error) if (!no_error) { my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE", - table->table_name); + routine->table_name); DBUG_RETURN(-1); } DBUG_RETURN(0); @@ -1044,6 +1048,41 @@ sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error) } +/* + Check if a routine exists in the mysql.proc table, without actually + parsing the definition. (Used for dropping) + + SYNOPSIS + sp_routine_exists_in_table() + thd - thread context + name - name of procedure + + RETURN VALUE + 0 - Success + non-0 - Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND +*/ + +int +sp_routine_exists_in_table(THD *thd, int type, sp_name *name) +{ + TABLE *table; + int ret; + Open_tables_state open_tables_state_backup; + + if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) + ret= SP_OPEN_TABLE_FAILED; + else + { + if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) + ret= SP_OK; + else + ret= SP_KEY_NOT_FOUND; + close_proc_table(thd, &open_tables_state_backup); + } + return ret; +} + + int sp_create_procedure(THD *thd, sp_head *sp) { diff --git a/sql/sp.h b/sql/sp.h index 53343e0fb25..2587a9b115a 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -40,7 +40,10 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, bool cache_only); int -sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error); +sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any, bool no_error); + +int +sp_routine_exists_in_table(THD *thd, int type, sp_name *name); int sp_create_procedure(THD *thd, sp_head *sp); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 7bc5aac270b..c67ce383398 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3030,7 +3030,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, if (!revoke_grant) { - if (sp_exists_routine(thd, table_list, is_proc, no_error)<0) + if (sp_exist_routines(thd, table_list, is_proc, no_error)<0) DBUG_RETURN(TRUE); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1cbc616f63f..2b76024105e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4445,21 +4445,17 @@ end_with_restore_list: case SQLCOM_DROP_PROCEDURE: case SQLCOM_DROP_FUNCTION: { - sp_head *sp; int result; - char *db, *name; + int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? + TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - if (lex->sql_command == SQLCOM_DROP_PROCEDURE) - sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname, - &thd->sp_proc_cache, FALSE); - else - sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname, - &thd->sp_func_cache, FALSE); + result= sp_routine_exists_in_table(thd, type, lex->spname); mysql_reset_errors(thd, 0); - if (sp) + if (result == SP_OK) { - db= thd->strdup(sp->m_db.str); - name= thd->strdup(sp->m_name.str); + char *db= lex->spname->m_db.str; + char *name= lex->spname->m_name.str; + if (check_routine_access(thd, ALTER_PROC_ACL, db, name, lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) goto error; From ff4e2892b752c7cdd0392f4992b0b4c79312a2a5 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Thu, 26 Jan 2006 17:26:25 +0100 Subject: [PATCH 02/24] Fixed on BUG#16568: Continue handler with simple CASE not working correctly After trying multiple inheritance (to messy and hard make it work) and sublassing jump_if_not (worked, but ugly), decided to on this solution instead: Inserting an abstract sp_instr_opt_meta class as parent for all instructions with destinations makes it possible to handle a continuation pointer for sp_instr_set_case_expr too. Note: No special test case; the fix is captured by the changed behaviour of bug14643_2, and bug14498_4 (formerly disabled), in sp.test. --- mysql-test/r/sp.result | 7 ++- mysql-test/t/sp.test | 5 +-- sql/sp_head.cc | 85 ++++++++++++++++++++++++++++--------- sql/sp_head.h | 96 ++++++++++++++++++++++++++++++------------ sql/sql_parse.cc | 2 +- sql/sql_yacc.yy | 17 ++++---- 6 files changed, 149 insertions(+), 63 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 904cd7d8642..1266def8673 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4089,8 +4089,6 @@ NULL 1 call bug14643_2()| Handler boo -2 -2 Handler boo drop procedure bug14643_1| @@ -4432,6 +4430,11 @@ Handler error End done +call bug14498_4()| +Handler +error +End +done call bug14498_5()| Handler error diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 8235686a74a..1bec3ad2fbd 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5210,10 +5210,7 @@ end| call bug14498_1()| call bug14498_2()| call bug14498_3()| -# We couldn't call this before, due to a known bug (BUG#14643) -# QQ We still can't since the new set_case_expr instruction breaks -# the semantics of case; it won't crash, but will get the wrong result. -#call bug14498_4()| +call bug14498_4()| call bug14498_5()| drop procedure bug14498_1| diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ae27b910304..a62c83e0caf 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1761,7 +1761,7 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, void -sp_head::new_cont_backpatch(sp_instr_jump_if_not *i) +sp_head::new_cont_backpatch(sp_instr_opt_meta *i) { m_cont_level+= 1; if (i) @@ -1773,7 +1773,7 @@ sp_head::new_cont_backpatch(sp_instr_jump_if_not *i) } void -sp_head::add_cont_backpatch(sp_instr_jump_if_not *i) +sp_head::add_cont_backpatch(sp_instr_opt_meta *i) { i->m_cont_dest= m_cont_level; (void)m_cont_backpatch.push_front(i); @@ -1784,7 +1784,7 @@ sp_head::do_cont_backpatch() { uint dest= instructions(); uint lev= m_cont_level--; - sp_instr_jump_if_not *i; + sp_instr_opt_meta *i; while ((i= m_cont_backpatch.head()) && i->m_cont_dest == lev) { @@ -2039,10 +2039,10 @@ void sp_head::optimize() set_dynamic(&m_instr, (gptr)&i, dst); while ((ibp= li++)) - { - sp_instr_jump *ji= static_cast(ibp); - ji->set_destination(src, dst); - } + { + sp_instr_opt_meta *im= static_cast(ibp); + im->set_destination(src, dst); + } } i->opt_move(dst, &bp); src+= 1; @@ -2064,6 +2064,10 @@ sp_head::opt_mark(uint ip) #ifndef DBUG_OFF +/* + Return the routine instructions as a result set. + Returns 0 if ok, !=0 on error. +*/ int sp_head::show_routine_code(THD *thd) { @@ -2091,6 +2095,22 @@ sp_head::show_routine_code(THD *thd) for (ip= 0; (i = get_instr(ip)) ; ip++) { + /* + Consistency check. If these are different something went wrong + during optimization. + */ + if (ip != i->m_ip) + { + const char *format= "Instruction at position %u has m_ip=%u"; + char tmp[sizeof(format) + 2*SP_INSTR_UINT_MAXLEN + 1]; + + sprintf(tmp, format, ip, i->m_ip); + /* + Since this is for debugging purposes only, we don't bother to + introduce a special error code for it. + */ + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp); + } protocol->prepare_for_resend(); protocol->store((longlong)ip); @@ -2515,14 +2535,14 @@ sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp) void sp_instr_jump_if_not::print(String *str) { - /* jump_if_not dest ... */ + /* jump_if_not dest(cont) ... */ if (str->reserve(2*SP_INSTR_UINT_MAXLEN+14+32)) // Add some for the expr. too return; str->qs_append(STRING_WITH_LEN("jump_if_not ")); str->qs_append(m_dest); - str->append('('); + str->qs_append('('); str->qs_append(m_cont_dest); - str->append(") "); + str->qs_append(STRING_WITH_LEN(") ")); m_expr->print(str); } @@ -3080,30 +3100,53 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) spcont->clear_handler(); thd->spcont= spcont; } + *nextp= m_cont_dest; /* For continue handler */ } + else + *nextp= m_ip+1; - *nextp = m_ip+1; - - return res; /* no error */ + return res; } void sp_instr_set_case_expr::print(String *str) { - const char CASE_EXPR_TAG[]= "set_case_expr "; - const int CASE_EXPR_TAG_LEN= sizeof(CASE_EXPR_TAG) - 1; - const int INT_STRING_MAX_LEN= 10; - - /* We must call reserve(), because qs_append() doesn't care about memory. */ - str->reserve(CASE_EXPR_TAG_LEN + INT_STRING_MAX_LEN + 2); - - str->qs_append(CASE_EXPR_TAG, CASE_EXPR_TAG_LEN); + /* set_case_expr (cont) id ... */ + str->reserve(2*SP_INSTR_UINT_MAXLEN+18+32); // Add some extra for expr too + str->qs_append(STRING_WITH_LEN("set_case_expr (")); + str->qs_append(m_cont_dest); + str->qs_append(STRING_WITH_LEN(") ")); str->qs_append(m_case_expr_id); str->qs_append(' '); m_case_expr->print(str); } +uint +sp_instr_set_case_expr::opt_mark(sp_head *sp) +{ + sp_instr *i; + + marked= 1; + if ((i= sp->get_instr(m_cont_dest))) + { + m_cont_dest= i->opt_shortcut_jump(sp, this); + m_cont_optdest= sp->get_instr(m_cont_dest); + } + sp->opt_mark(m_cont_dest); + return m_ip+1; +} + +void +sp_instr_set_case_expr::opt_move(uint dst, List *bp) +{ + if (m_cont_dest > m_ip) + bp->push_back(this); // Forward + else if (m_cont_optdest) + m_cont_dest= m_cont_optdest->m_ip; // Backward + m_ip= dst; +} + /* ------------------------------------------------------------------ */ diff --git a/sql/sp_head.h b/sql/sp_head.h index 858bf523a07..e1dd469d82c 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -41,6 +41,7 @@ sp_get_flags_for_command(LEX *lex); struct sp_label; class sp_instr; +class sp_instr_opt_meta; class sp_instr_jump_if_not; struct sp_cond_type; struct sp_pvar; @@ -271,11 +272,11 @@ public: // Start a new cont. backpatch level. If 'i' is NULL, the level is just incr. void - new_cont_backpatch(sp_instr_jump_if_not *i); + new_cont_backpatch(sp_instr_opt_meta *i); // Add an instruction to the current level void - add_cont_backpatch(sp_instr_jump_if_not *i); + add_cont_backpatch(sp_instr_opt_meta *i); // Backpatch (and pop) the current level to the current position. void @@ -372,15 +373,15 @@ private: } bp_t; List m_backpatch; // Instructions needing backpatching /* - We need a special list for backpatching of conditional jump's continue + We need a special list for backpatching of instructions with a continue destination (in the case of a continue handler catching an error in the test), since it would otherwise interfere with the normal backpatch - mechanism - jump_if_not instructions have two different destination + mechanism - e.g. jump_if_not instructions have two different destinations which are to be patched differently. Since these occur in a more restricted way (always the same "level" in the code), we don't need the label. */ - List m_cont_backpatch; + List m_cont_backpatch; uint m_cont_level; // The current cont. backpatch level /* @@ -660,21 +661,55 @@ private: }; // class sp_instr_trigger_field : public sp_instr -class sp_instr_jump : public sp_instr +/* + An abstract class for all instructions with destinations that + needs to be updated by the optimizer. + Even if not all subclasses will use both the normal destination and + the continuation destination, we put them both here for simplicity. + */ +class sp_instr_opt_meta : public sp_instr +{ +public: + + uint m_dest; // Where we will go + uint m_cont_dest; // Where continue handlers will go + + sp_instr_opt_meta(uint ip, sp_pcontext *ctx) + : sp_instr(ip, ctx), + m_dest(0), m_cont_dest(0), m_optdest(0), m_cont_optdest(0) + {} + + sp_instr_opt_meta(uint ip, sp_pcontext *ctx, uint dest) + : sp_instr(ip, ctx), + m_dest(dest), m_cont_dest(0), m_optdest(0), m_cont_optdest(0) + {} + + virtual ~sp_instr_opt_meta() + {} + + virtual void set_destination(uint old_dest, uint new_dest) + = 0; + +protected: + + sp_instr *m_optdest; // Used during optimization + sp_instr *m_cont_optdest; // Used during optimization + +}; // class sp_instr_opt_meta : public sp_instr + +class sp_instr_jump : public sp_instr_opt_meta { sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */ void operator=(sp_instr_jump &); public: - uint m_dest; // Where we will go - sp_instr_jump(uint ip, sp_pcontext *ctx) - : sp_instr(ip, ctx), m_dest(0), m_optdest(0) + : sp_instr_opt_meta(ip, ctx) {} sp_instr_jump(uint ip, sp_pcontext *ctx, uint dest) - : sp_instr(ip, ctx), m_dest(dest), m_optdest(0) + : sp_instr_opt_meta(ip, ctx, dest) {} virtual ~sp_instr_jump() @@ -702,11 +737,7 @@ public: m_dest= new_dest; } -protected: - - sp_instr *m_optdest; // Used during optimization - -}; // class sp_instr_jump : public sp_instr +}; // class sp_instr_jump : public sp_instr_opt_meta class sp_instr_jump_if_not : public sp_instr_jump @@ -716,16 +747,14 @@ class sp_instr_jump_if_not : public sp_instr_jump public: - uint m_cont_dest; // Where continue handlers will go - sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, LEX *lex) - : sp_instr_jump(ip, ctx), m_cont_dest(0), m_expr(i), - m_lex_keeper(lex, TRUE), m_cont_optdest(0) + : sp_instr_jump(ip, ctx), m_expr(i), + m_lex_keeper(lex, TRUE) {} sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest, LEX *lex) - : sp_instr_jump(ip, ctx, dest), m_cont_dest(0), m_expr(i), - m_lex_keeper(lex, TRUE), m_cont_optdest(0) + : sp_instr_jump(ip, ctx, dest), m_expr(i), + m_lex_keeper(lex, TRUE) {} virtual ~sp_instr_jump_if_not() @@ -757,7 +786,6 @@ private: Item *m_expr; // The condition sp_lex_keeper m_lex_keeper; - sp_instr *m_cont_optdest; // Used during optimization }; // class sp_instr_jump_if_not : public sp_instr_jump @@ -899,7 +927,7 @@ private: uint m_frame; -}; // class sp_instr_hreturn : public sp_instr +}; // class sp_instr_hreturn : public sp_instr_jump /* This is DECLARE CURSOR */ @@ -1085,14 +1113,18 @@ private: }; // class sp_instr_error : public sp_instr -class sp_instr_set_case_expr :public sp_instr +class sp_instr_set_case_expr : public sp_instr_opt_meta { public: sp_instr_set_case_expr(uint ip, sp_pcontext *ctx, uint case_expr_id, Item *case_expr, LEX *lex) - :sp_instr(ip, ctx), m_case_expr_id(case_expr_id), m_case_expr(case_expr), - m_lex_keeper(lex, TRUE) + : sp_instr_opt_meta(ip, ctx), + m_case_expr_id(case_expr_id), m_case_expr(case_expr), + m_lex_keeper(lex, TRUE) + {} + + virtual ~sp_instr_set_case_expr() {} virtual int execute(THD *thd, uint *nextp); @@ -1101,13 +1133,23 @@ public: virtual void print(String *str); + virtual uint opt_mark(sp_head *sp); + + virtual void opt_move(uint dst, List *ibp); + + virtual void set_destination(uint old_dest, uint new_dest) + { + if (m_cont_dest == old_dest) + m_cont_dest= new_dest; + } + private: uint m_case_expr_id; Item *m_case_expr; sp_lex_keeper m_lex_keeper; -}; // class sp_instr_set_case_expr : public sp_instr +}; // class sp_instr_set_case_expr : public sp_instr_opt_meta #ifndef NO_EMBEDDED_ACCESS_CHECKS diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1cbc616f63f..a61815c8202 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4599,7 +4599,7 @@ end_with_restore_list: else sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname, &thd->sp_func_cache, FALSE); - if (!sp || !sp->show_routine_code(thd)) + if (!sp || sp->show_routine_code(thd)) { /* We don't distinguish between errors for now */ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0ef389950fc..40c7f0edd93 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2018,17 +2018,18 @@ sp_proc_stmt: sp_head *sp= lex->sphead; sp_pcontext *parsing_ctx= lex->spcont; int case_expr_id= parsing_ctx->register_case_expr(); + sp_instr_set_case_expr *i; if (parsing_ctx->push_case_expr_id(case_expr_id)) YYABORT; - - sp->add_instr( - new sp_instr_set_case_expr(sp->instructions(), - parsing_ctx, - case_expr_id, - $3, - lex)); - + + i= new sp_instr_set_case_expr(sp->instructions(), + parsing_ctx, + case_expr_id, + $3, + lex); + sp->add_cont_backpatch(i); + sp->add_instr(i); sp->m_flags|= sp_head::IN_SIMPLE_CASE; sp->restore_lex(YYTHD); } From d6370b48a7761b7e8716c92267458561dc77124c Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Tue, 31 Jan 2006 21:48:32 -0800 Subject: [PATCH 03/24] FIxed bug #14927. A query with a group by and having clauses could return a wrong result set if the having condition contained a constant conjunct evaluated to FALSE. It happened because the pushdown condition for table with grouping columns lost its constant conjuncts. Pushdown conditions are always built by the function make_cond_for_table that ignores constant conjuncts. This is apparently not correct when constant false conjuncts are present. --- mysql-test/r/having.result | 17 +++++++++++++++++ mysql-test/t/having.test | 16 ++++++++++++++++ sql/sql_lex.cc | 2 ++ sql/sql_lex.h | 1 + sql/sql_prepare.cc | 10 ++++++++-- sql/sql_select.cc | 22 +++++++++++++--------- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result index 9730f9f81bf..ccd1f0e61e7 100644 --- a/mysql-test/r/having.result +++ b/mysql-test/r/having.result @@ -141,3 +141,20 @@ SUM(a) 6 4 DROP TABLE t1; +CREATE TABLE t1 (a int); +INSERT INTO t1 VALUES (1), (2), (1), (3), (2), (1); +SELECT a FROM t1 GROUP BY a HAVING a > 1; +a +2 +3 +SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1; +a +SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1; +x a +EXPLAIN SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible HAVING +EXPLAIN SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible HAVING +DROP table t1; diff --git a/mysql-test/t/having.test b/mysql-test/t/having.test index 3dd9ace6a1b..8b39e3bd454 100644 --- a/mysql-test/t/having.test +++ b/mysql-test/t/having.test @@ -135,4 +135,20 @@ SELECT SUM(a) FROM t1 GROUP BY a HAVING SUM(a); DROP TABLE t1; +# +# Bug #14927: HAVING clause containing constant false conjunct +# + +CREATE TABLE t1 (a int); +INSERT INTO t1 VALUES (1), (2), (1), (3), (2), (1); + +SELECT a FROM t1 GROUP BY a HAVING a > 1; +SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1; +SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1; + +EXPLAIN SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1; +EXPLAIN SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1; + +DROP table t1; + # End of 4.1 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 16641ad6dd5..7348816ea27 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1074,6 +1074,7 @@ void st_select_lex::init_query() item_list.empty(); join= 0; where= 0; + having= 0; olap= UNSPECIFIED_OLAP_TYPE; having_fix_field= 0; resolve_mode= NOMATTER_MODE; @@ -1081,6 +1082,7 @@ void st_select_lex::init_query() ref_pointer_array= 0; select_n_having_items= 0; prep_where= 0; + prep_having= 0; subquery_in_having= explicit_limit= 0; parsing_place= NO_MATTER; is_item_list_lookup= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 47908ba8685..f5eb94ac93e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -421,6 +421,7 @@ public: char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */ Item *where, *having; /* WHERE & HAVING clauses */ Item *prep_where; /* saved WHERE clause for prepared statement processing */ + Item *prep_having;/* saved HAVING clause for prepared statement processing */ enum olap_type olap; SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ List item_list; /* list of fields & expressions */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 8a50d0bd50e..ec5fa92f689 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1665,10 +1665,11 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, for (; sl; sl= sl->next_select_in_list()) { /* - Save WHERE clause pointers, because they may be changed + Save WHERE, HAVING clause pointers, because they may be changed during query optimisation. */ sl->prep_where= sl->where; + sl->prep_having= sl->having; /* Switch off a temporary flag that prevents evaluation of subqueries in statement prepare. @@ -1694,13 +1695,18 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) /* remove option which was put by mysql_explain_union() */ sl->options&= ~SELECT_DESCRIBE; /* - Copy WHERE clause pointers to avoid damaging they by optimisation + Copy WHERE, HAVING clause pointers to avoid damaging they by optimisation */ if (sl->prep_where) { sl->where= sl->prep_where->copy_andor_structure(thd); sl->where->cleanup(); } + if (sl->prep_having) + { + sl->having= sl->prep_having->copy_andor_structure(thd); + sl->having->cleanup(); + } DBUG_ASSERT(sl->join == 0); ORDER *order; /* Fix GROUP list */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ec62e80ba13..27c8ac126fa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -501,12 +501,18 @@ JOIN::optimize() DBUG_RETURN(1); } - if (cond_value == Item::COND_FALSE || - (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) - { /* Impossible cond */ - zero_result_cause= "Impossible WHERE"; - error= 0; - DBUG_RETURN(0); + { + Item::cond_result having_value; + having= optimize_cond(thd, having, &having_value); + + if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE || + (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) + { /* Impossible cond */ + zero_result_cause= having_value == Item::COND_FALSE ? + "Impossible HAVING" : "Impossible WHERE"; + error= 0; + DBUG_RETURN(0); + } } /* Optimize count(*), min() and max() */ @@ -4611,10 +4617,8 @@ optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) DBUG_EXECUTE("info", print_where(conds, "after remove");); } else - { *cond_value= Item::COND_TRUE; - select->prep_where= 0; - } + DBUG_RETURN(conds); } From 332be5bdd9ab556d612a6a4a1b276f9546ebab90 Mon Sep 17 00:00:00 2001 From: "svoj@april.(none)" <> Date: Wed, 1 Feb 2006 20:40:12 +0400 Subject: [PATCH 04/24] BUG#14496: Crash or strange results with prepared statement, MATCH and FULLTEXT Fixed that fulltext query using PS results in unexpected behaviour when executed 2 or more times. --- mysql-test/r/fulltext.result | 11 +++++++++++ mysql-test/t/fulltext.test | 12 ++++++++++++ sql/item_func.h | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index ebd6880a10e..c1dd5f80d5c 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -436,3 +436,14 @@ SELECT a FROM t1 WHERE MATCH a AGAINST('testword\'\'' IN BOOLEAN MODE); a testword'' DROP TABLE t1; +CREATE TABLE t1 (a TEXT, FULLTEXT KEY(a)); +INSERT INTO t1 VALUES('test'),('test1'),('test'); +PREPARE stmt from "SELECT a, MATCH(a) AGAINST('test1 test') FROM t1 WHERE MATCH(a) AGAINST('test1 test')"; +EXECUTE stmt; +a MATCH(a) AGAINST('test1 test') +test1 0.68526661396027 +EXECUTE stmt; +a MATCH(a) AGAINST('test1 test') +test1 0.68526661396027 +DEALLOCATE PREPARE stmt; +DROP TABLE t1; diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test index 1399b9bfdff..d5ce6241490 100644 --- a/mysql-test/t/fulltext.test +++ b/mysql-test/t/fulltext.test @@ -357,4 +357,16 @@ SELECT a FROM t1 WHERE MATCH a AGAINST('testword' IN BOOLEAN MODE); SELECT a FROM t1 WHERE MATCH a AGAINST('testword\'\'' IN BOOLEAN MODE); DROP TABLE t1; +# +# BUG#14496: Crash or strange results with prepared statement, +# MATCH and FULLTEXT +# +CREATE TABLE t1 (a TEXT, FULLTEXT KEY(a)); +INSERT INTO t1 VALUES('test'),('test1'),('test'); +PREPARE stmt from "SELECT a, MATCH(a) AGAINST('test1 test') FROM t1 WHERE MATCH(a) AGAINST('test1 test')"; +EXECUTE stmt; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/item_func.h b/sql/item_func.h index 2c4976d1152..53d113c8b6f 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1056,7 +1056,6 @@ public: if (!master && ft_handler) { ft_handler->please->close_search(ft_handler); - ft_handler=0; if (join_key) table->file->ft_handler=0; table->fulltext_searched=0; @@ -1066,6 +1065,7 @@ public: delete concat; concat= 0; } + ft_handler= 0; DBUG_VOID_RETURN; } enum Functype functype() const { return FT_FUNC; } From 86606615abc487ca12770f6ca19717896cc977c4 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Wed, 1 Feb 2006 20:43:43 -0800 Subject: [PATCH 05/24] Fixed bug #16382. When an ambiguous field name is used in a group by clause a warning is issued in the find_order_in_list function by a call to push_warning_printf. An expression that was not always valid was passed to this call as the field name parameter. --- mysql-test/r/view.result | 34 ++++++++++++++++++++++++++++++++++ mysql-test/t/view.test | 17 +++++++++++++++++ sql/sql_select.cc | 3 ++- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 2be1f5e5edd..e209e393d00 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2504,3 +2504,37 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away DROP VIEW v1; DROP TABLE t1; +CREATE TABLE t1 (x varchar(10)); +INSERT INTO t1 VALUES (null), ('foo'), ('bar'), (null); +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT IF(x IS NULL, 'blank', 'not blank') FROM v1 GROUP BY x; +IF(x IS NULL, 'blank', 'not blank') +blank +not blank +not blank +SELECT IF(x IS NULL, 'blank', 'not blank') AS x FROM t1 GROUP BY x; +x +blank +not blank +not blank +Warnings: +Warning 1052 Column 'x' in group statement is ambiguous +SELECT IF(x IS NULL, 'blank', 'not blank') AS x FROM v1; +x +blank +not blank +not blank +blank +SELECT IF(x IS NULL, 'blank', 'not blank') AS y FROM v1 GROUP BY y; +y +blank +not blank +SELECT IF(x IS NULL, 'blank', 'not blank') AS x FROM v1 GROUP BY x; +x +blank +not blank +not blank +Warnings: +Warning 1052 Column 'x' in group statement is ambiguous +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index c028889184e..ed44bf3b7c0 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2363,3 +2363,20 @@ EXPLAIN SELECT MIN(a) FROM v1; DROP VIEW v1; DROP TABLE t1; + +# +# Bug#16382: grouping name is resolved against a view column name +# which coincides with a select column name + +CREATE TABLE t1 (x varchar(10)); +INSERT INTO t1 VALUES (null), ('foo'), ('bar'), (null); +CREATE VIEW v1 AS SELECT * FROM t1; + +SELECT IF(x IS NULL, 'blank', 'not blank') FROM v1 GROUP BY x; +SELECT IF(x IS NULL, 'blank', 'not blank') AS x FROM t1 GROUP BY x; +SELECT IF(x IS NULL, 'blank', 'not blank') AS x FROM v1; +SELECT IF(x IS NULL, 'blank', 'not blank') AS y FROM v1 GROUP BY y; +SELECT IF(x IS NULL, 'blank', 'not blank') AS x FROM v1 GROUP BY x; + +DROP VIEW v1; +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 63d46934555..f23260e6bf8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12381,7 +12381,8 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, overshadows the column reference from the SELECT list. */ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, - ER(ER_NON_UNIQ_ERROR), from_field->field_name, + ER(ER_NON_UNIQ_ERROR), + ((Item_ident*) order_item)->field_name, current_thd->where); } } From c5abd52a47bde6c4e7e8db5fe9cf874979f06d0e Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Thu, 2 Feb 2006 15:37:29 +0100 Subject: [PATCH 06/24] Minor fix for BUG#16303 in sp.cc: Simplified code in sp_routine_exists_in_table(). (No change of functionality) --- sql/sp.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sql/sp.cc b/sql/sp.cc index d2aaa5646a8..fe249141fea 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1073,9 +1073,7 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name) ret= SP_OPEN_TABLE_FAILED; else { - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) - ret= SP_OK; - else + if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) ret= SP_KEY_NOT_FOUND; close_proc_table(thd, &open_tables_state_backup); } From 4abab51c2463090011460324caaf93b9acec99a0 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 2 Feb 2006 20:37:58 -0800 Subject: [PATCH 07/24] Post-review fix for bug #14927. --- sql/sql_select.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 27c8ac126fa..f996ca82970 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -504,6 +504,12 @@ JOIN::optimize() { Item::cond_result having_value; having= optimize_cond(thd, having, &having_value); + if (thd->net.report_error) + { + error= 1; + DBUG_PRINT("error",("Error from optimize_cond")); + DBUG_RETURN(1); + } if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE || (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) From 7b58b91fe425eb07abcf877b27ab803dc0bc2c59 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 2 Feb 2006 23:56:08 -0800 Subject: [PATCH 08/24] Fixes after manual merge --- mysql-test/r/having.result | 17 ------ sql/sql_prepare.cc | 103 ++----------------------------------- sql/sql_select.cc | 4 +- 3 files changed, 7 insertions(+), 117 deletions(-) diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result index 981813c5c0d..e54f6d7f2a4 100644 --- a/mysql-test/r/having.result +++ b/mysql-test/r/having.result @@ -141,23 +141,6 @@ SUM(a) 6 4 DROP TABLE t1; - - - - - - - - - - - - - - - - - CREATE TABLE t1 (a int); INSERT INTO t1 VALUES (1), (2), (1), (3), (2), (1); SELECT a FROM t1 GROUP BY a HAVING a > 1; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index bd5dd4234fe..efe73dbe275 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1862,96 +1862,6 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) { /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } else mysql_log.write(thd, COM_STMT_PREPARE, "[%lu] %s", stmt->id, packet); @@ -2045,14 +1955,11 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len) } else { - stmt->setup_set_params(); - SELECT_LEX *sl= stmt->lex->all_selects_list; - for (; sl; sl= sl->next_select_in_list()) - { - /* - during query optimisation. - */ - sl->prep_where= sl->where; + query_str= lex->prepared_stmt_code.str; + *query_len= lex->prepared_stmt_code.length; + } +end: + return query_str; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6d3c32b1239..9160c023576 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -628,7 +628,7 @@ JOIN::optimize() { Item::cond_result having_value; - having= optimize_cond(thd, having, &having_value); + having= optimize_cond(this, having, join_list, &having_value); if (thd->net.report_error) { error= 1; @@ -641,7 +641,7 @@ JOIN::optimize() { /* Impossible cond */ DBUG_PRINT("info", (having_value == Item::COND_FALSE ? "Impossible HAVING" : "Impossible WHERE")); - zero_result_cause= ? + zero_result_cause= having_value == Item::COND_FALSE ? "Impossible HAVING" : "Impossible WHERE"; error= 0; DBUG_RETURN(0); From 7c76ae4c135c1356a6a9bcb0c2acb47cbf26941f Mon Sep 17 00:00:00 2001 From: "svoj@april.(none)" <> Date: Fri, 3 Feb 2006 14:09:33 +0400 Subject: [PATCH 09/24] BUG#16893: Crash in test 'fulltext_order_by' Fixed that fulltext query + union results in unexpected behaviour. --- sql/item_func.h | 3 --- sql/sql_base.cc | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sql/item_func.h b/sql/item_func.h index 2c4976d1152..5bf57a713c4 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1057,9 +1057,6 @@ public: { ft_handler->please->close_search(ft_handler); ft_handler=0; - if (join_key) - table->file->ft_handler=0; - table->fulltext_searched=0; } if (concat) { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index ea013bb4e1e..223e8482f49 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -988,6 +988,8 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->status=STATUS_NO_RECORD; table->keys_in_use_for_query= table->keys_in_use; table->used_keys= table->keys_for_keyread; + table->file->ft_handler=0; + table->fulltext_searched=0; if (table->timestamp_field) table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); DBUG_ASSERT(table->key_read == 0); From 7b892368c36cb003f9a69bbbb0e4b702a910edcf Mon Sep 17 00:00:00 2001 From: "pekka@mysql.com" <> Date: Fri, 3 Feb 2006 14:35:29 +0100 Subject: [PATCH 10/24] ndb - bugfix: tinyblob + replace => mysqld crash (no bug#) --- mysql-test/r/ndb_blob.result | 7 +++++++ mysql-test/t/ndb_blob.test | 6 +++++- ndb/src/ndbapi/NdbBlob.cpp | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ndb_blob.result b/mysql-test/r/ndb_blob.result index f806cf08ea9..b1269564558 100644 --- a/mysql-test/r/ndb_blob.result +++ b/mysql-test/r/ndb_blob.result @@ -428,6 +428,13 @@ delete from t1; select * from t1; a b commit; +replace t1 set a=2, b='y'; +select * from t1; +a b +2 y +delete from t1; +select * from t1; +a b drop table t1; set autocommit=0; create table t1 ( diff --git a/mysql-test/t/ndb_blob.test b/mysql-test/t/ndb_blob.test index a12ebee2f0d..f80b7f71281 100644 --- a/mysql-test/t/ndb_blob.test +++ b/mysql-test/t/ndb_blob.test @@ -338,7 +338,7 @@ select * from t1 order by a; drop table t1; drop database test2; -# -- bug-5252 tinytext crashes plus no-commit result -- +# -- bug-5252 tinytext crashes + no-commit result + replace -- set autocommit=0; create table t1 ( @@ -352,6 +352,10 @@ select * from t1; delete from t1; select * from t1; commit; +replace t1 set a=2, b='y'; +select * from t1; +delete from t1; +select * from t1; drop table t1; # -- bug-5013 insert empty string to text -- diff --git a/ndb/src/ndbapi/NdbBlob.cpp b/ndb/src/ndbapi/NdbBlob.cpp index d1aa4e61c40..35cf71b5e5a 100644 --- a/ndb/src/ndbapi/NdbBlob.cpp +++ b/ndb/src/ndbapi/NdbBlob.cpp @@ -947,6 +947,8 @@ NdbBlob::deletePartsUnknown(Uint32 part) { DBUG_ENTER("NdbBlob::deletePartsUnknown"); DBUG_PRINT("info", ("part=%u count=all", part)); + if (thePartSize == 0) // tinyblob + DBUG_RETURN(0); static const unsigned maxbat = 256; static const unsigned minbat = 1; unsigned bat = minbat; From c696582df47c942f038a562b4966d2a0642dcecc Mon Sep 17 00:00:00 2001 From: "pekka@mysql.com" <> Date: Fri, 3 Feb 2006 15:23:58 +0100 Subject: [PATCH 11/24] ndb - replace+tinyblob back-patch from 5.0 [ discard on 4.1->5.0 merge ] --- mysql-test/r/ndb_blob.result | 7 +++++++ mysql-test/t/ndb_blob.test | 6 +++++- ndb/src/ndbapi/NdbBlob.cpp | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ndb_blob.result b/mysql-test/r/ndb_blob.result index 6f25ec95c80..bcf867a4edd 100644 --- a/mysql-test/r/ndb_blob.result +++ b/mysql-test/r/ndb_blob.result @@ -428,6 +428,13 @@ delete from t1; select * from t1; a b commit; +replace t1 set a=2, b='y'; +select * from t1; +a b +2 y +delete from t1; +select * from t1; +a b drop table t1; set autocommit=0; create table t1 ( diff --git a/mysql-test/t/ndb_blob.test b/mysql-test/t/ndb_blob.test index a12ebee2f0d..f80b7f71281 100644 --- a/mysql-test/t/ndb_blob.test +++ b/mysql-test/t/ndb_blob.test @@ -338,7 +338,7 @@ select * from t1 order by a; drop table t1; drop database test2; -# -- bug-5252 tinytext crashes plus no-commit result -- +# -- bug-5252 tinytext crashes + no-commit result + replace -- set autocommit=0; create table t1 ( @@ -352,6 +352,10 @@ select * from t1; delete from t1; select * from t1; commit; +replace t1 set a=2, b='y'; +select * from t1; +delete from t1; +select * from t1; drop table t1; # -- bug-5013 insert empty string to text -- diff --git a/ndb/src/ndbapi/NdbBlob.cpp b/ndb/src/ndbapi/NdbBlob.cpp index c5692d79e83..416e983ec14 100644 --- a/ndb/src/ndbapi/NdbBlob.cpp +++ b/ndb/src/ndbapi/NdbBlob.cpp @@ -959,6 +959,8 @@ int NdbBlob::deletePartsUnknown(Uint32 part) { DBG("deletePartsUnknown [in] part=" << part << " count=all"); + if (thePartSize == 0) // tinyblob + return 0; static const unsigned maxbat = 256; static const unsigned minbat = 1; unsigned bat = minbat; From 8ed86030b40298f09f77b5bdf78af905dd89c3f2 Mon Sep 17 00:00:00 2001 From: "pekka@mysql.com" <> Date: Sun, 5 Feb 2006 22:12:06 +0100 Subject: [PATCH 12/24] ndb - bug#16693 (4.1) test + workaround, analyze later --- ndb/test/ndbapi/testBlobs.cpp | 82 +++++++++++++++++++++++++++-------- ndb/tools/delete_all.cpp | 30 ++++++++++--- 2 files changed, 88 insertions(+), 24 deletions(-) diff --git a/ndb/test/ndbapi/testBlobs.cpp b/ndb/test/ndbapi/testBlobs.cpp index cd2287df876..4c82f718788 100644 --- a/ndb/test/ndbapi/testBlobs.cpp +++ b/ndb/test/ndbapi/testBlobs.cpp @@ -841,9 +841,6 @@ insertPk(int style) CHK(g_con->execute(NoCommit) == 0); CHK(writeBlobData(tup) == 0); } - // just another trap - if (urandom(10) == 0) - CHK(g_con->execute(NoCommit) == 0); if (++n == g_opt.m_batch) { CHK(g_con->execute(Commit) == 0); g_ndb->closeTransaction(g_con); @@ -965,21 +962,31 @@ static int deletePk() { DBG("--- deletePk ---"); + unsigned n = 0; + CHK((g_con = g_ndb->startTransaction()) != 0); for (unsigned k = 0; k < g_opt.m_rows; k++) { Tup& tup = g_tups[k]; DBG("deletePk pk1=" << hex << tup.m_pk1); - CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0); CHK(g_opr->deleteTuple() == 0); CHK(g_opr->equal("PK1", tup.m_pk1) == 0); if (g_opt.m_pk2len != 0) CHK(g_opr->equal("PK2", tup.m_pk2) == 0); - CHK(g_con->execute(Commit) == 0); - g_ndb->closeTransaction(g_con); + if (++n == g_opt.m_batch) { + CHK(g_con->execute(Commit) == 0); + g_ndb->closeTransaction(g_con); + CHK((g_con = g_ndb->startTransaction()) != 0); + n = 0; + } g_opr = 0; - g_con = 0; tup.m_exists = false; } + if (n != 0) { + CHK(g_con->execute(Commit) == 0); + n = 0; + } + g_ndb->closeTransaction(g_con); + g_con = 0; return 0; } @@ -1082,19 +1089,27 @@ static int deleteIdx() { DBG("--- deleteIdx ---"); + unsigned n = 0; + CHK((g_con = g_ndb->startTransaction()) != 0); for (unsigned k = 0; k < g_opt.m_rows; k++) { Tup& tup = g_tups[k]; DBG("deleteIdx pk1=" << hex << tup.m_pk1); - CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0); CHK(g_opx->deleteTuple() == 0); CHK(g_opx->equal("PK2", tup.m_pk2) == 0); - CHK(g_con->execute(Commit) == 0); - g_ndb->closeTransaction(g_con); + if (++n == g_opt.m_batch) { + CHK(g_con->execute(Commit) == 0); + g_ndb->closeTransaction(g_con); + CHK((g_con = g_ndb->startTransaction()) != 0); + n = 0; + } g_opx = 0; - g_con = 0; tup.m_exists = false; } + if (n != 0) { + CHK(g_con->execute(Commit) == 0); + n = 0; + } return 0; } @@ -1225,20 +1240,49 @@ deleteScan(bool idx) CHK(g_ops->getValue("PK2", tup.m_pk2) != 0); CHK(g_con->execute(NoCommit) == 0); unsigned rows = 0; + unsigned n = 0; while (1) { int ret; tup.m_pk1 = (Uint32)-1; memset(tup.m_pk2, 'x', g_opt.m_pk2len); - CHK((ret = rs->nextResult()) == 0 || ret == 1); + CHK((ret = rs->nextResult(true)) == 0 || ret == 1); if (ret == 1) break; - DBG("deleteScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1); - CHK(rs->deleteTuple() == 0); - CHK(g_con->execute(NoCommit) == 0); - Uint32 k = tup.m_pk1 - g_opt.m_pk1off; - CHK(k < g_opt.m_rows && g_tups[k].m_exists); - g_tups[k].m_exists = false; - rows++; + while (1) { + DBG("deleteScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1); + Uint32 k = tup.m_pk1 - g_opt.m_pk1off; + CHK(k < g_opt.m_rows && g_tups[k].m_exists); + g_tups[k].m_exists = false; + CHK(rs->deleteTuple() == 0); + rows++; + tup.m_pk1 = (Uint32)-1; + memset(tup.m_pk2, 'x', g_opt.m_pk2len); + CHK((ret = rs->nextResult(false)) == 0 || ret == 1 || ret == 2); + if (++n == g_opt.m_batch || ret == 2) { + DBG("execute batch: n=" << n << " ret=" << ret); + switch (0) { + case 0: // works normally + CHK(g_con->execute(NoCommit) == 0); + CHK(true || g_con->restart() == 0); + break; + case 1: // nonsense - g_con is invalid for 2nd batch + CHK(g_con->execute(Commit) == 0); + CHK(true || g_con->restart() == 0); + break; + case 2: // DBTC sendSignalErrorRefuseLab + CHK(g_con->execute(NoCommit) == 0); + CHK(g_con->restart() == 0); + break; + case 3: // 266 time-out + CHK(g_con->execute(Commit) == 0); + CHK(g_con->restart() == 0); + break; + } + n = 0; + } + if (ret == 2) + break; + } } CHK(g_con->execute(Commit) == 0); g_ndb->closeTransaction(g_con); diff --git a/ndb/tools/delete_all.cpp b/ndb/tools/delete_all.cpp index 21e0c2ac089..d4a07fdf505 100644 --- a/ndb/tools/delete_all.cpp +++ b/ndb/tools/delete_all.cpp @@ -22,7 +22,8 @@ #include #include -static int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism=240); +static int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, + bool commit_across_open_cursor, int parallelism=240); NDB_STD_OPTS_VARS; @@ -81,8 +82,18 @@ int main(int argc, char** argv){ ndbout << " Table " << argv[i] << " does not exist!" << endl; return NDBT_ProgramExit(NDBT_WRONGARGS); } + // Check if we have any blobs + bool commit_across_open_cursor = true; + for (int j = 0; j < pTab->getNoOfColumns(); j++) { + NdbDictionary::Column::Type t = pTab->getColumn(j)->getType(); + if (t == NdbDictionary::Column::Blob || + t == NdbDictionary::Column::Text) { + commit_across_open_cursor = false; + break; + } + } ndbout << "Deleting all from " << argv[i] << "..."; - if(clear_table(&MyNdb, pTab) == NDBT_FAILED){ + if(clear_table(&MyNdb, pTab, commit_across_open_cursor) == NDBT_FAILED){ res = NDBT_FAILED; ndbout << "FAILED" << endl; } @@ -91,7 +102,8 @@ int main(int argc, char** argv){ } -int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism) +int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, + bool commit_across_open_cursor, int parallelism) { // Scan all records exclusive and delete // them one by one @@ -153,8 +165,12 @@ int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism) } while((check = rs->nextResult(false)) == 0); if(check != -1){ - check = pTrans->execute(Commit); - pTrans->restart(); + if (commit_across_open_cursor) { + check = pTrans->execute(Commit); + pTrans->restart(); // new tx id + } else { + check = pTrans->execute(NoCommit); + } } err = pTrans->getNdbError(); @@ -180,6 +196,10 @@ int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism) } goto failed; } + if (! commit_across_open_cursor && pTrans->execute(Commit) != 0) { + err = pTrans->getNdbError(); + goto failed; + } pNdb->closeTransaction(pTrans); return NDBT_OK; } From 810cbaf157da9e36a699a6dceacc4cd80844da81 Mon Sep 17 00:00:00 2001 From: "pekka@mysql.com" <> Date: Sun, 5 Feb 2006 23:06:08 +0100 Subject: [PATCH 13/24] ndb - testBlobs needs more tx in 5.0 ? --- ndb/test/ndbapi/testBlobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndb/test/ndbapi/testBlobs.cpp b/ndb/test/ndbapi/testBlobs.cpp index 3f3b23f7e83..fff5ac247df 100644 --- a/ndb/test/ndbapi/testBlobs.cpp +++ b/ndb/test/ndbapi/testBlobs.cpp @@ -1300,7 +1300,7 @@ static int testmain() { g_ndb = new Ndb(g_ncc, "TEST_DB"); - CHK(g_ndb->init() == 0); + CHK(g_ndb->init(20) == 0); CHK(g_ndb->waitUntilReady() == 0); g_dic = g_ndb->getDictionary(); g_tups = new Tup [g_opt.m_rows]; From 5debbc9e8a0de60c2d6a63da9414b0a870c2c900 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Mon, 6 Feb 2006 16:05:46 +0100 Subject: [PATCH 14/24] Updated sp-code.result after merge (BUG#16568) --- mysql-test/r/sp-code.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result index c9fe170dda6..8bdb9a724d7 100644 --- a/mysql-test/r/sp-code.result +++ b/mysql-test/r/sp-code.result @@ -172,7 +172,7 @@ Pos Instruction 17 set v_col@8 NULL 18 stmt 0 "select row,col into v_row,v_col from ..." 19 stmt 0 "select dig into v_dig from sudoku_wor..." -20 set_case_expr 0 v_dig@4 +20 set_case_expr (34) 0 v_dig@4 21 jump_if_not 25(34) (case_expr@0 = 0) 22 set v_dig@4 1 23 stmt 4 "update sudoku_work set dig = 1 where ..." From 3af0eabc7f0dae5830fba4ee5993549102ee9e0f Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Mon, 6 Feb 2006 11:35:13 -0800 Subject: [PATCH 15/24] Fixed bug #16203. If check_quick_select returns non-empty range then the function cost_group_min_max cannot return 0 as an estimate of the number of retrieved records. Yet the function erroneously returned 0 as the estimate in some situations. --- mysql-test/r/group_min_max.result | 27 +++++++++++++++++++++++++++ mysql-test/t/group_min_max.test | 21 +++++++++++++++++++++ sql/opt_range.cc | 1 + 3 files changed, 49 insertions(+) diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index 91579a7ea42..b1703c51f4e 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -2043,3 +2043,30 @@ c1 c2 30 8 30 9 drop table t1; +CREATE TABLE t1 (a varchar(5), b int(11), PRIMARY KEY (a,b)); +INSERT INTO t1 VALUES ('AA',1), ('AA',2), ('AA',3), ('BB',1), ('AA',4); +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +SELECT a FROM t1 WHERE a='AA' GROUP BY a; +a +AA +SELECT a FROM t1 WHERE a='BB' GROUP BY a; +a +BB +EXPLAIN SELECT a FROM t1 WHERE a='AA' GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY PRIMARY 7 const 3 Using where; Using index +EXPLAIN SELECT a FROM t1 WHERE a='BB' GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY PRIMARY 7 const 1 Using where; Using index +SELECT DISTINCT a FROM t1 WHERE a='BB'; +a +BB +SELECT DISTINCT a FROM t1 WHERE a LIKE 'B%'; +a +BB +SELECT a FROM t1 WHERE a LIKE 'B%' GROUP BY a; +a +BB +DROP TABLE t1; diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test index e15ef92116c..8dc55532bbf 100644 --- a/mysql-test/t/group_min_max.test +++ b/mysql-test/t/group_min_max.test @@ -715,3 +715,24 @@ select distinct c1, c2 from t1 order by c2; select c1,min(c2) as c2 from t1 group by c1 order by c2; select c1,c2 from t1 group by c1,c2 order by c2; drop table t1; + +# +# Bug #16203: Analysis for possible min/max optimization erroneously +# returns impossible range +# + +CREATE TABLE t1 (a varchar(5), b int(11), PRIMARY KEY (a,b)); +INSERT INTO t1 VALUES ('AA',1), ('AA',2), ('AA',3), ('BB',1), ('AA',4); +OPTIMIZE TABLE t1; + +SELECT a FROM t1 WHERE a='AA' GROUP BY a; +SELECT a FROM t1 WHERE a='BB' GROUP BY a; + +EXPLAIN SELECT a FROM t1 WHERE a='AA' GROUP BY a; +EXPLAIN SELECT a FROM t1 WHERE a='BB' GROUP BY a; + +SELECT DISTINCT a FROM t1 WHERE a='BB'; +SELECT DISTINCT a FROM t1 WHERE a LIKE 'B%'; +SELECT a FROM t1 WHERE a LIKE 'B%' GROUP BY a; + +DROP TABLE t1; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 7c4f5fbe218..1b712700b18 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -7780,6 +7780,7 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, quick_prefix_selectivity= (double) quick_prefix_records / (double) table_records; num_groups= (uint) rint(num_groups * quick_prefix_selectivity); + set_if_bigger(num_groups, 1); } if (used_key_parts > group_key_parts) From 5bbb5c29b79432ccc76b43ad0019012f007b3670 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Tue, 7 Feb 2006 00:03:39 +0100 Subject: [PATCH 16/24] Bug #17154 load data infile of char values into a table of char(PK) hangs Bug #17158 load data infile of char values into table of char with no (PK) fails to load Bug #17081 Doing "LOAD DATA INFILE" directly after delete can cause missing data --- mysql-test/r/ndb_load.result | 80 ++++++++++++++++++++++++++++++++++++ mysql-test/t/ndb_load.test | 24 +++++++++++ sql/ha_ndbcluster.cc | 22 ++++++++-- sql/sql_load.cc | 7 +++- 4 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 mysql-test/r/ndb_load.result create mode 100644 mysql-test/t/ndb_load.test diff --git a/mysql-test/r/ndb_load.result b/mysql-test/r/ndb_load.result new file mode 100644 index 00000000000..76da5b2a215 --- /dev/null +++ b/mysql-test/r/ndb_load.result @@ -0,0 +1,80 @@ +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (word CHAR(20) NOT NULL PRIMARY KEY) ENGINE=NDB; +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +ERROR 23000: Can't write; duplicate key in table 't1' +DROP TABLE t1; +CREATE TABLE t1 (word CHAR(20) NOT NULL) ENGINE=NDB; +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +SELECT * FROM t1 ORDER BY word; +word +Aarhus +Aarhus +Aaron +Aaron +Ababa +Ababa +aback +aback +abaft +abaft +abandon +abandon +abandoned +abandoned +abandoning +abandoning +abandonment +abandonment +abandons +abandons +abase +abased +abasement +abasements +abases +abash +abashed +abashes +abashing +abasing +abate +abated +abatement +abatements +abater +abates +abating +Abba +abbe +abbey +abbeys +abbot +abbots +Abbott +abbreviate +abbreviated +abbreviates +abbreviating +abbreviation +abbreviations +Abby +abdomen +abdomens +abdominal +abduct +abducted +abduction +abductions +abductor +abductors +abducts +Abe +abed +Abel +Abelian +Abelson +Aberdeen +Abernathy +aberrant +aberration +DROP TABLE t1; diff --git a/mysql-test/t/ndb_load.test b/mysql-test/t/ndb_load.test new file mode 100644 index 00000000000..72a5b53eaad --- /dev/null +++ b/mysql-test/t/ndb_load.test @@ -0,0 +1,24 @@ +-- source include/have_ndb.inc +-- source include/not_embedded.inc + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +# +# Basic test for different types of loading data +# + +# should give duplicate key +CREATE TABLE t1 (word CHAR(20) NOT NULL PRIMARY KEY) ENGINE=NDB; +--error 1022 +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +DROP TABLE t1; + +# now without a primary key we should be ok +CREATE TABLE t1 (word CHAR(20) NOT NULL) ENGINE=NDB; +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +SELECT * FROM t1 ORDER BY word; +DROP TABLE t1; + +# End of 4.1 tests diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 594551b918a..f5c0f3dc173 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3046,9 +3046,23 @@ int ha_ndbcluster::end_bulk_insert() "rows_inserted:%d, bulk_insert_rows: %d", (int) m_rows_inserted, (int) m_bulk_insert_rows)); m_bulk_insert_not_flushed= FALSE; - if (execute_no_commit(this,trans) != 0) { - no_uncommitted_rows_execute_failure(); - my_errno= error= ndb_err(trans); + if (m_transaction_on) + { + if (execute_no_commit(this, trans) != 0) + { + no_uncommitted_rows_execute_failure(); + my_errno= error= ndb_err(trans); + } + } + else + { + if (execute_commit(this, trans) != 0) + { + no_uncommitted_rows_execute_failure(); + my_errno= error= ndb_err(trans); + } + int res= trans->restart(); + DBUG_ASSERT(res == 0); } } @@ -4867,7 +4881,7 @@ bool ha_ndbcluster::low_byte_first() const } bool ha_ndbcluster::has_transactions() { - return m_transaction_on; + return TRUE; } const char* ha_ndbcluster::index_type(uint key_number) { diff --git a/sql/sql_load.cc b/sql/sql_load.cc index aa4ea3e6c8c..4e6c458cc43 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -285,8 +285,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else error=read_sep_field(thd,info,table,fields,read_info,*enclosed, skip_lines); - if (table->file->end_bulk_insert()) - error=1; /* purecov: inspected */ + if (table->file->end_bulk_insert() && !error) + { + table->file->print_error(my_errno, MYF(0)); + error= 1; + } table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->next_number_field=0; } From c445b29d4d92d6902d595b9e136cf238532eb961 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Tue, 7 Feb 2006 00:26:47 +0100 Subject: [PATCH 17/24] ndb, transaction should be refgistred even if m_transaction_on is not set --- sql/ha_ndbcluster.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index a09ad561327..2310ace2d46 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3288,8 +3288,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) ERR_RETURN(ndb->getNdbError()); no_uncommitted_rows_reset(thd); thd_ndb->stmt= trans; - if (m_transaction_on) - trans_register_ha(thd, FALSE, &ndbcluster_hton); + trans_register_ha(thd, FALSE, &ndbcluster_hton); } else { @@ -3304,8 +3303,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) ERR_RETURN(ndb->getNdbError()); no_uncommitted_rows_reset(thd); thd_ndb->all= trans; - if (m_transaction_on) - trans_register_ha(thd, TRUE, &ndbcluster_hton); + trans_register_ha(thd, TRUE, &ndbcluster_hton); /* If this is the start of a LOCK TABLE, a table look From 06a57659d8492899f6ce4b06755a993495bd1a3d Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Tue, 7 Feb 2006 11:30:42 +0100 Subject: [PATCH 18/24] Bug #17081 "LOAD DATA INFILE" may not load all the data --- sql/ha_ndbcluster.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index f5c0f3dc173..6a80ba83017 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3061,8 +3061,11 @@ int ha_ndbcluster::end_bulk_insert() no_uncommitted_rows_execute_failure(); my_errno= error= ndb_err(trans); } - int res= trans->restart(); - DBUG_ASSERT(res == 0); + else + { + int res= trans->restart(); + DBUG_ASSERT(res == 0); + } } } From 001224c09d2423a4c0357e37089de8bd03ad1f8e Mon Sep 17 00:00:00 2001 From: "gunnar@mysql.com." <> Date: Tue, 7 Feb 2006 13:26:35 +0100 Subject: [PATCH 19/24] item_func.cc: fix for bug#8461 BUG 8461 - TRUNCATE returns incorrect result if 2nd argument is negative Reason: Both TRUNCATE/ROUND converts INTEGERS to DOUBLE and back to INTEGERS Changed the integer routine to work on integers only. This bug affects 4.1, 5.0 and 5.1 Fixing in 4.1 will need to change the routine to handle different types individually. 5.0 did had different routines for different types already just the INTEGER routine was bad. --- mysql-test/r/func_math.result | 15 +++++++++++++++ mysql-test/t/func_math.test | 14 ++++++++++++++ sql/item_func.cc | 34 ++++++++++++++++++---------------- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index fba274b9bb1..1507f959ae6 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -203,3 +203,18 @@ NULL Warnings: Error 1365 Division by 0 set sql_mode=''; +select round(111,-10); +round(111,-10) +0 +select round(-5000111000111000155,-1); +round(-5000111000111000155,-1) +-5000111000111000160 +select round(15000111000111000155,-1); +round(15000111000111000155,-1) +15000111000111000160 +select truncate(-5000111000111000155,-1); +truncate(-5000111000111000155,-1) +-5000111000111000150 +select truncate(15000111000111000155,-1); +truncate(15000111000111000155,-1) +15000111000111000150 diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 9cf0ee452cd..8dc4eb215c7 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -141,3 +141,17 @@ select log(2,-1); select log(-2,1); set sql_mode=''; +# +# Bug #8461 truncate() and round() return false results 2nd argument negative. +# +# round(a,-b) log_10(b) > a +select round(111,-10); +# round on bigint +select round(-5000111000111000155,-1); +# round on unsigned bigint +select round(15000111000111000155,-1); +# truncate on bigint +select truncate(-5000111000111000155,-1); +# truncate on unsigned bigint +select truncate(15000111000111000155,-1); + diff --git a/sql/item_func.cc b/sql/item_func.cc index 2d595e5fdc8..a85f05c2e22 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1863,28 +1863,30 @@ longlong Item_func_round::int_op() return value; // integer have not digits after point abs_dec= -dec; - double tmp; - /* - tmp2 is here to avoid return the value with 80 bit precision - This will fix that the test round(0.1,1) = round(0.1,1) is true - */ - volatile double tmp2; - - tmp= (abs_dec < array_elements(log_10) ? - log_10[abs_dec] : pow(10.0, (double) abs_dec)); - + longlong tmp; + + if(abs_dec >= array_elements(log_10_int)) + return 0; + + tmp= log_10_int[abs_dec]; + if (truncate) { if (unsigned_flag) - tmp2= floor(ulonglong2double(value)/tmp)*tmp; - else if (value >= 0) - tmp2= floor(((double)value)/tmp)*tmp; + value= (ulonglong(value)/tmp)*tmp; else - tmp2= ceil(((double)value)/tmp)*tmp; + value= (value/tmp)*tmp; } else - tmp2= rint(((double)value)/tmp)*tmp; - return (longlong)tmp2; + { + if (unsigned_flag) + value= ((ulonglong(value)+(tmp>>1))/tmp)*tmp; + else if ( value >= 0) + value= ((value+(tmp>>1))/tmp)*tmp; + else + value= ((value-(tmp>>1))/tmp)*tmp; + } + return value; } From 9b25294bb4412b595099906af7051bbcda5cd3ed Mon Sep 17 00:00:00 2001 From: "pekka@mysql.com" <> Date: Tue, 7 Feb 2006 14:03:25 +0100 Subject: [PATCH 20/24] ndb - main.cpp: avoid SEGV if fetch_configuration fails before fork --- ndb/src/kernel/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ndb/src/kernel/main.cpp b/ndb/src/kernel/main.cpp index 4bc9fbf76e5..d0b0994cc21 100644 --- a/ndb/src/kernel/main.cpp +++ b/ndb/src/kernel/main.cpp @@ -63,8 +63,10 @@ extern "C" void handler_sigusr1(int signum); // child signalling failed restart void systemInfo(const Configuration & conf, const LogLevel & ll); -static FILE *child_info_file_r= 0; -static FILE *child_info_file_w= 0; +// These are used already before fork if fetch_configuration() fails +// (e.g. Unable to alloc node id). Set them to something reasonable. +static FILE *child_info_file_r= stdin; +static FILE *child_info_file_w= stdout; static void writeChildInfo(const char *token, int val) { From fb0d37bccf0a40d16b5f82de47380cc2a05251c2 Mon Sep 17 00:00:00 2001 From: "pekka@mysql.com" <> Date: Tue, 7 Feb 2006 18:10:42 +0100 Subject: [PATCH 21/24] ndb - add option ndbd --foreground for manual debugging --- ndb/src/kernel/main.cpp | 18 +++++++++++------- ndb/src/kernel/vm/Configuration.cpp | 17 ++++++++++++++--- ndb/src/kernel/vm/Configuration.hpp | 10 +++++++++- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ndb/src/kernel/main.cpp b/ndb/src/kernel/main.cpp index d0b0994cc21..7c1763485ce 100644 --- a/ndb/src/kernel/main.cpp +++ b/ndb/src/kernel/main.cpp @@ -272,8 +272,8 @@ int main(int argc, char** argv) #ifndef NDB_WIN32 signal(SIGUSR1, handler_sigusr1); - pid_t child; - while (1) + pid_t child = -1; + while (! theConfig->getForegroundMode()) // the cond is const { // setup reporting between child and parent int filedes[2]; @@ -395,8 +395,10 @@ int main(int argc, char** argv) if (child >= 0) g_eventLogger.info("Angel pid: %d ndb pid: %d", getppid(), getpid()); - else + else if (child > 0) g_eventLogger.info("Ndb pid: %d", getpid()); + else + g_eventLogger.info("Ndb started in foreground"); #else g_eventLogger.info("Ndb started"); #endif @@ -571,10 +573,7 @@ catchsigs(bool ignore){ #ifdef SIGPOLL SIGPOLL, #endif - SIGSEGV, -#ifdef SIGTRAP - SIGTRAP -#endif + SIGSEGV }; static const int signals_ignore[] = { @@ -588,6 +587,11 @@ catchsigs(bool ignore){ handler_register(signals_error[i], handler_error, ignore); for(i = 0; i < sizeof(signals_ignore)/sizeof(signals_ignore[0]); i++) handler_register(signals_ignore[i], SIG_IGN, ignore); +#ifdef SIGTRAP + Configuration* theConfig = globalEmulatorData.theConfiguration; + if (! theConfig->getForegroundMode()) + handler_register(SIGTRAP, handler_error, ignore); +#endif #endif } diff --git a/ndb/src/kernel/vm/Configuration.cpp b/ndb/src/kernel/vm/Configuration.cpp index 0fca1841a15..831145a7a41 100644 --- a/ndb/src/kernel/vm/Configuration.cpp +++ b/ndb/src/kernel/vm/Configuration.cpp @@ -48,11 +48,13 @@ extern EventLogger g_eventLogger; enum ndbd_options { OPT_INITIAL = NDB_STD_OPTIONS_LAST, - OPT_NODAEMON + OPT_NODAEMON, + OPT_FOREGROUND }; NDB_STD_OPTS_VARS; -static int _daemon, _no_daemon, _initial, _no_start; +// XXX should be my_bool ??? +static int _daemon, _no_daemon, _foreground, _initial, _no_start; /** * Arguments to NDB process */ @@ -75,6 +77,11 @@ static struct my_option my_long_options[] = "Do not start ndbd as daemon, provided for testing purposes", (gptr*) &_no_daemon, (gptr*) &_no_daemon, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "foreground", OPT_FOREGROUND, + "Run real ndbd in foreground, provided for debugging purposes" + " (implies --nodaemon)", + (gptr*) &_foreground, (gptr*) &_foreground, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; static void short_usage_sub(void) @@ -103,13 +110,14 @@ Configuration::init(int argc, char** argv) ndb_std_get_one_option))) exit(ho_error); - if (_no_daemon) { + if (_no_daemon || _foreground) { _daemon= 0; } DBUG_PRINT("info", ("no_start=%d", _no_start)); DBUG_PRINT("info", ("initial=%d", _initial)); DBUG_PRINT("info", ("daemon=%d", _daemon)); + DBUG_PRINT("info", ("foreground=%d", _foreground)); DBUG_PRINT("info", ("connect_str=%s", opt_connect_str)); ndbSetOwnVersion(); @@ -131,6 +139,8 @@ Configuration::init(int argc, char** argv) // Check daemon flag if (_daemon) _daemonMode = true; + if (_foreground) + _foregroundMode = true; // Save programname if(argc > 0 && argv[0] != 0) @@ -151,6 +161,7 @@ Configuration::Configuration() _backupPath = 0; _initialStart = false; _daemonMode = false; + _foregroundMode = false; m_config_retriever= 0; m_clusterConfig= 0; m_clusterConfigIter= 0; diff --git a/ndb/src/kernel/vm/Configuration.hpp b/ndb/src/kernel/vm/Configuration.hpp index 243ecbee4e7..6315209ddbb 100644 --- a/ndb/src/kernel/vm/Configuration.hpp +++ b/ndb/src/kernel/vm/Configuration.hpp @@ -64,6 +64,7 @@ public: bool getInitialStart() const; void setInitialStart(bool val); bool getDaemonMode() const; + bool getForegroundMode() const; const ndb_mgm_configuration_iterator * getOwnConfigIterator() const; @@ -105,7 +106,8 @@ private: char * _connectString; Uint32 m_mgmd_port; BaseString m_mgmd_host; - bool _daemonMode; + bool _daemonMode; // if not, angel in foreground + bool _foregroundMode; // no angel, raw ndbd in foreground void calcSizeAlt(class ConfigValues * ); }; @@ -140,4 +142,10 @@ Configuration::getDaemonMode() const { return _daemonMode; } +inline +bool +Configuration::getForegroundMode() const { + return _foregroundMode; +} + #endif From 9aa06f3bc17d0b4c17aa7b293e0e82d5c3238cfe Mon Sep 17 00:00:00 2001 From: "pekka@mysql.com" <> Date: Tue, 7 Feb 2006 19:57:31 +0100 Subject: [PATCH 22/24] ndb - bug#15918 fix --- mysql-test/r/ndb_index_unique.result | 9 +++++++++ mysql-test/t/ndb_index_unique.test | 14 ++++++++++++++ ndb/src/kernel/blocks/dbacc/Dbacc.hpp | 2 +- sql/ha_ndbcluster.cc | 4 ++++ sql/ha_ndbcluster.h | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ndb_index_unique.result b/mysql-test/r/ndb_index_unique.result index 1401ae26ddb..7864a5d1354 100644 --- a/mysql-test/r/ndb_index_unique.result +++ b/mysql-test/r/ndb_index_unique.result @@ -626,3 +626,12 @@ select * from t1 where code = '12' and month = 4 and year = 2004 ; id month year code 1 4 2004 12 drop table t1; +create table t1 (a int primary key, b varchar(1000) not null, unique key (b)) +engine=ndb charset=utf8; +insert into t1 values (1, repeat(_utf8 0xe288ab6474, 200)); +insert into t1 values (2, repeat(_utf8 0xe288ab6474, 200)); +ERROR 23000: Duplicate entry '2' for key 1 +select a, sha1(b) from t1; +a sha1(b) +1 08f5d02c8b8bc244f275bdfc22c42c5cab0d9d7d +drop table t1; diff --git a/mysql-test/t/ndb_index_unique.test b/mysql-test/t/ndb_index_unique.test index 2185276c2c6..8561b3794c4 100644 --- a/mysql-test/t/ndb_index_unique.test +++ b/mysql-test/t/ndb_index_unique.test @@ -309,4 +309,18 @@ select * from t1 where code = '12' and month = 4 and year = 2004 ; drop table t1; +# bug#15918 Unique Key Limit in NDB Engine + +create table t1 (a int primary key, b varchar(1000) not null, unique key (b)) +engine=ndb charset=utf8; + +insert into t1 values (1, repeat(_utf8 0xe288ab6474, 200)); +--error 1062 +insert into t1 values (2, repeat(_utf8 0xe288ab6474, 200)); +select a, sha1(b) from t1; + +# perl -e 'print pack("H2000","e288ab6474"x200)' | sha1sum + +drop table t1; + # End of 4.1 tests diff --git a/ndb/src/kernel/blocks/dbacc/Dbacc.hpp b/ndb/src/kernel/blocks/dbacc/Dbacc.hpp index afec0b9e3b1..7f51a281f37 100644 --- a/ndb/src/kernel/blocks/dbacc/Dbacc.hpp +++ b/ndb/src/kernel/blocks/dbacc/Dbacc.hpp @@ -1446,7 +1446,7 @@ private: Uint32 cexcPrevforward; Uint32 clocalkey[32]; union { - Uint32 ckeys[2048]; + Uint32 ckeys[2048 * MAX_XFRM_MULTIPLY]; Uint64 ckeys_align; }; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 699b3f05a70..e348826ac10 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -5083,6 +5083,10 @@ uint ha_ndbcluster::max_supported_key_length() const { return NDB_MAX_KEY_SIZE; } +uint ha_ndbcluster::max_supported_key_part_length() const +{ + return NDB_MAX_KEY_SIZE; +} bool ha_ndbcluster::low_byte_first() const { #ifdef WORDS_BIGENDIAN diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 466d7b7044b..b01adcb3156 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -510,6 +510,7 @@ class ha_ndbcluster: public handler uint max_supported_keys() const; uint max_supported_key_parts() const; uint max_supported_key_length() const; + uint max_supported_key_part_length() const; int rename_table(const char *from, const char *to); int delete_table(const char *name); From 6295103f960df93e91424befc58f855204a3a2c4 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Wed, 8 Feb 2006 14:07:52 +0100 Subject: [PATCH 23/24] Bug#17233, disabled test in waiting for bug fix --- mysql-test/t/disabled.def | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 46f15983dc3..09a11578096 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -12,3 +12,4 @@ sp-goto : GOTO is currently is disabled - will be fixed in the future subselect : Bug#15706 +ndb_load : Bug #17233 From 16edce1e5d6ee0353aa72468ec30060267773963 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Wed, 8 Feb 2006 17:17:27 +0100 Subject: [PATCH 24/24] Bug #17235 perror prints wrong prefix for ndb error codes --- extra/perror.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/extra/perror.c b/extra/perror.c index 919088ba42e..82311c1b2c9 100644 --- a/extra/perror.c +++ b/extra/perror.c @@ -239,10 +239,24 @@ int main(int argc,char *argv[]) if ((ndb_error_string(code, ndb_string, sizeof(ndb_string)) < 0) && (ndbd_exit_string(code, ndb_string, sizeof(ndb_string)) < 0)) { - msg= 0; + msg= 0; } else msg= ndb_string; + if (msg) + { + if (verbose) + printf("NDB error code %3d: %s\n",code,msg); + else + puts(msg); + } + else + { + fprintf(stderr,"Illegal ndb error code: %d\n",code); + error= 1; + } + found= 1; + msg= 0; } else #endif