Commit 79112423 authored by unknown's avatar unknown

Merge rkalimullin@bk-internal.mysql.com:/home/bk/mysql-5.0

into  mysql.com:/usr/home/ram/work/5.0.b16511

parents e071d7aa 2ba25676
......@@ -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;
......@@ -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 ..."
......
......@@ -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
......@@ -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
......
......@@ -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;
......@@ -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;
......@@ -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|
......
......@@ -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)
......
......@@ -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,39 @@ 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_KEY_NOT_FOUND;
close_proc_table(thd, &open_tables_state_backup);
}
return ret;
}
int
sp_create_procedure(THD *thd, sp_head *sp)
{
......
......@@ -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);
......
......@@ -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)
{
......@@ -2048,10 +2048,10 @@ void sp_head::optimize()
set_dynamic(&m_instr, (gptr)&i, dst);
while ((ibp= li++))
{
sp_instr_jump *ji= static_cast<sp_instr_jump *>(ibp);
ji->set_destination(src, dst);
}
{
sp_instr_opt_meta *im= static_cast<sp_instr_opt_meta *>(ibp);
im->set_destination(src, dst);
}
}
i->opt_move(dst, &bp);
src+= 1;
......@@ -2073,6 +2073,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)
{
......@@ -2100,6 +2104,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);
......@@ -2524,14 +2544,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);
}
......@@ -3077,30 +3097,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<sp_instr> *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;
}
/* ------------------------------------------------------------------ */
......
......@@ -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<bp_t> 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<sp_instr_jump_if_not> m_cont_backpatch;
List<sp_instr_opt_meta> m_cont_backpatch;
uint m_cont_level; // The current cont. backpatch level
/*
......@@ -677,21 +678,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()
......@@ -722,11 +757,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
......@@ -736,16 +767,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()
......@@ -778,7 +807,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
......@@ -912,7 +940,7 @@ private:
uint m_frame;
}; // class sp_instr_hreturn : public sp_instr
}; // class sp_instr_hreturn : public sp_instr_jump
/* This is DECLARE CURSOR */
......@@ -1089,14 +1117,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);
......@@ -1105,13 +1137,23 @@ public:
virtual void print(String *str);
virtual uint opt_mark(sp_head *sp);
virtual void opt_move(uint dst, List<sp_instr> *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
......
......@@ -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);
}
......
......@@ -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;
......@@ -4599,7 +4595,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),
......
......@@ -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);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment