Commit 601979f0 authored by serg@serg.mylan's avatar serg@serg.mylan

Merge bk-internal:/home/bk/mysql-5.0

into serg.mylan:/usr/home/serg/Abk/mysql-5.0
parents 81d070fd 36a4f39b
...@@ -2682,3 +2682,20 @@ AND FK_firma_id = 2; ...@@ -2682,3 +2682,20 @@ AND FK_firma_id = 2;
COUNT(*) COUNT(*)
0 0
drop table t1; drop table t1;
CREATE TABLE t1 (a int);
CREATE TABLE t2 (a int);
INSERT INTO t1 VALUES (1), (2), (3), (4), (5);
INSERT INTO t2 VALUES (2), (4), (6);
SELECT t1.a FROM t1 STRAIGHT_JOIN t2 ON t1.a=t2.a;
a
2
4
EXPLAIN SELECT t1.a FROM t1 STRAIGHT_JOIN t2 ON t1.a=t2.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5
1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
EXPLAIN SELECT t1.a FROM t1 INNER JOIN t2 ON t1.a=t2.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 ALL NULL NULL NULL NULL 3
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where
DROP TABLE t1,t2;
...@@ -2255,3 +2255,19 @@ AND FK_firma_id = 2; ...@@ -2255,3 +2255,19 @@ AND FK_firma_id = 2;
drop table t1; drop table t1;
#
# Test for bug #10084: STRAIGHT_JOIN with ON expression
#
CREATE TABLE t1 (a int);
CREATE TABLE t2 (a int);
INSERT INTO t1 VALUES (1), (2), (3), (4), (5);
INSERT INTO t2 VALUES (2), (4), (6);
SELECT t1.a FROM t1 STRAIGHT_JOIN t2 ON t1.a=t2.a;
EXPLAIN SELECT t1.a FROM t1 STRAIGHT_JOIN t2 ON t1.a=t2.a;
EXPLAIN SELECT t1.a FROM t1 INNER JOIN t2 ON t1.a=t2.a;
DROP TABLE t1,t2;
...@@ -844,7 +844,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length); ...@@ -844,7 +844,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
void reset_stmt_for_execute(THD *thd, LEX *lex); void reinit_stmt_before_use(THD *thd, LEX *lex);
void init_stmt_after_parse(THD*, LEX*); void init_stmt_after_parse(THD*, LEX*);
/* sql_handler.cc */ /* sql_handler.cc */
......
...@@ -1355,7 +1355,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, ...@@ -1355,7 +1355,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
implemented at the same time as ability not to store LEX for implemented at the same time as ability not to store LEX for
instruction if it is not really used. instruction if it is not really used.
*/ */
reset_stmt_for_execute(thd, m_lex); reinit_stmt_before_use(thd, m_lex);
/* /*
If requested check whenever we have access to tables in LEX's table list If requested check whenever we have access to tables in LEX's table list
......
...@@ -1656,6 +1656,17 @@ static bool init_param_array(Prepared_statement *stmt) ...@@ -1656,6 +1656,17 @@ static bool init_param_array(Prepared_statement *stmt)
return FALSE; return FALSE;
} }
/* Cleanup PS after execute/prepare and restore THD state */
static void cleanup_stmt_and_thd_after_use(Statement *stmt, THD *thd)
{
stmt->lex->unit.cleanup();
cleanup_items(stmt->free_list);
thd->rollback_item_tree_changes();
thd->cleanup_after_query();
}
/* /*
Given a query string with parameter markers, create a Prepared Statement Given a query string with parameter markers, create a Prepared Statement
from it and send PS info back to the client. from it and send PS info back to the client.
...@@ -1760,12 +1771,9 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1760,12 +1771,9 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
thd->lex->sphead= NULL; thd->lex->sphead= NULL;
} }
lex_end(lex); lex_end(lex);
lex->unit.cleanup();
close_thread_tables(thd); close_thread_tables(thd);
cleanup_stmt_and_thd_after_use(stmt, thd);
thd->restore_backup_statement(stmt, &thd->stmt_backup); thd->restore_backup_statement(stmt, &thd->stmt_backup);
cleanup_items(stmt->free_list);
thd->rollback_item_tree_changes();
thd->cleanup_after_query();
thd->current_arena= thd; thd->current_arena= thd;
if (error) if (error)
...@@ -1808,10 +1816,10 @@ void init_stmt_after_parse(THD *thd, LEX *lex) ...@@ -1808,10 +1816,10 @@ void init_stmt_after_parse(THD *thd, LEX *lex)
/* Reinit prepared statement/stored procedure before execution */ /* Reinit prepared statement/stored procedure before execution */
void reset_stmt_for_execute(THD *thd, LEX *lex) void reinit_stmt_before_use(THD *thd, LEX *lex)
{ {
SELECT_LEX *sl= lex->all_selects_list; SELECT_LEX *sl= lex->all_selects_list;
DBUG_ENTER("reset_stmt_for_execute"); DBUG_ENTER("reinit_stmt_before_use");
if (lex->empty_field_list_on_rset) if (lex->empty_field_list_on_rset)
{ {
...@@ -2007,7 +2015,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -2007,7 +2015,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
thd->stmt_backup.set_statement(thd); thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt); thd->set_statement(stmt);
thd->current_arena= stmt; thd->current_arena= stmt;
reset_stmt_for_execute(thd, stmt->lex); reinit_stmt_before_use(thd, stmt->lex);
/* From now cursors assume that thd->mem_root is clean */ /* From now cursors assume that thd->mem_root is clean */
if (expanded_query.length() && if (expanded_query.length() &&
alloc_query(thd, (char *)expanded_query.ptr(), alloc_query(thd, (char *)expanded_query.ptr(),
...@@ -2031,17 +2039,18 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -2031,17 +2039,18 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
if (cursor && cursor->is_open()) if (cursor && cursor->is_open())
{ {
/*
It's safer if we grab THD state after mysql_execute_command is
finished and not in Cursor::open(), because currently the call to
Cursor::open is buried deep in JOIN::exec of the top level join.
*/
cursor->init_from_thd(thd); cursor->init_from_thd(thd);
cursor->state= stmt->state;
} }
else else
{ {
thd->lex->unit.cleanup(); close_thread_tables(thd);
cleanup_items(stmt->free_list); cleanup_stmt_and_thd_after_use(stmt, thd);
reset_stmt_params(stmt); reset_stmt_params(stmt);
close_thread_tables(thd); /* to close derived tables */
thd->rollback_item_tree_changes();
thd->cleanup_after_query();
} }
thd->set_statement(&thd->stmt_backup); thd->set_statement(&thd->stmt_backup);
...@@ -2119,7 +2128,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, ...@@ -2119,7 +2128,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
{ {
DBUG_ENTER("execute_stmt"); DBUG_ENTER("execute_stmt");
reset_stmt_for_execute(thd, stmt->lex); reinit_stmt_before_use(thd, stmt->lex);
if (expanded_query->length() && if (expanded_query->length() &&
alloc_query(thd, (char *)expanded_query->ptr(), alloc_query(thd, (char *)expanded_query->ptr(),
...@@ -2141,14 +2150,11 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, ...@@ -2141,14 +2150,11 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR); my_pthread_setprio(pthread_self(), WAIT_PRIOR);
thd->lex->unit.cleanup();
thd->current_arena= thd;
cleanup_items(stmt->free_list);
thd->rollback_item_tree_changes();
reset_stmt_params(stmt);
close_thread_tables(thd); // to close derived tables close_thread_tables(thd); // to close derived tables
cleanup_stmt_and_thd_after_use(stmt, thd);
reset_stmt_params(stmt);
thd->set_statement(&thd->stmt_backup); thd->set_statement(&thd->stmt_backup);
thd->cleanup_after_query(); thd->current_arena= thd;
if (stmt->state == Item_arena::PREPARED) if (stmt->state == Item_arena::PREPARED)
stmt->state= Item_arena::EXECUTED; stmt->state= Item_arena::EXECUTED;
...@@ -2171,7 +2177,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) ...@@ -2171,7 +2177,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
/* assume there is always place for 8-16 bytes */ /* assume there is always place for 8-16 bytes */
ulong stmt_id= uint4korr(packet); ulong stmt_id= uint4korr(packet);
ulong num_rows= uint4korr(packet+4); ulong num_rows= uint4korr(packet+4);
Statement *stmt; Prepared_statement *stmt;
DBUG_ENTER("mysql_stmt_fetch"); DBUG_ENTER("mysql_stmt_fetch");
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_fetch"))) if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_fetch")))
...@@ -2185,7 +2191,6 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) ...@@ -2185,7 +2191,6 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
thd->current_arena= stmt; thd->current_arena= stmt;
thd->set_n_backup_statement(stmt, &thd->stmt_backup); thd->set_n_backup_statement(stmt, &thd->stmt_backup);
stmt->cursor->init_thd(thd);
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), QUERY_PRIOR); my_pthread_setprio(pthread_self(), QUERY_PRIOR);
...@@ -2197,11 +2202,16 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) ...@@ -2197,11 +2202,16 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR); my_pthread_setprio(pthread_self(), WAIT_PRIOR);
/* Restore THD state */
stmt->cursor->reset_thd(thd);
thd->restore_backup_statement(stmt, &thd->stmt_backup); thd->restore_backup_statement(stmt, &thd->stmt_backup);
thd->current_arena= thd; thd->current_arena= thd;
if (!stmt->cursor->is_open())
{
/* We're done with the fetch: reset PS for next execution */
cleanup_stmt_and_thd_after_use(stmt, thd);
reset_stmt_params(stmt);
}
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -1709,10 +1709,11 @@ Cursor::init_from_thd(THD *thd) ...@@ -1709,10 +1709,11 @@ Cursor::init_from_thd(THD *thd)
We need to save and reset thd->mem_root, otherwise it'll be freed We need to save and reset thd->mem_root, otherwise it'll be freed
later in mysql_parse. later in mysql_parse.
We can't just change the thd->mem_root here as we want to keep the things We can't just change the thd->mem_root here as we want to keep the
that is already allocated in thd->mem_root for Cursor::fetch() things that are already allocated in thd->mem_root for Cursor::fetch()
*/ */
main_mem_root= *thd->mem_root; main_mem_root= *thd->mem_root;
state= thd->current_arena->state;
/* Allocate new memory root for thd */ /* Allocate new memory root for thd */
init_sql_alloc(thd->mem_root, init_sql_alloc(thd->mem_root,
thd->variables.query_alloc_block_size, thd->variables.query_alloc_block_size,
...@@ -1735,24 +1736,6 @@ Cursor::init_from_thd(THD *thd) ...@@ -1735,24 +1736,6 @@ Cursor::init_from_thd(THD *thd)
What problems can we have with it if cursor is open? What problems can we have with it if cursor is open?
TODO: must be fixed because of the prelocked mode. TODO: must be fixed because of the prelocked mode.
*/ */
/*
TODO: grab thd->free_list here?
*/
}
void
Cursor::init_thd(THD *thd)
{
DBUG_ASSERT(thd->derived_tables == 0);
thd->derived_tables= derived_tables;
DBUG_ASSERT(thd->open_tables == 0);
thd->open_tables= open_tables;
DBUG_ASSERT(thd->lock== 0);
thd->lock= lock;
thd->query_id= query_id;
} }
...@@ -1828,6 +1811,13 @@ Cursor::fetch(ulong num_rows) ...@@ -1828,6 +1811,13 @@ Cursor::fetch(ulong num_rows)
DBUG_ENTER("Cursor::fetch"); DBUG_ENTER("Cursor::fetch");
DBUG_PRINT("enter",("rows: %lu", num_rows)); DBUG_PRINT("enter",("rows: %lu", num_rows));
DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 &&
thd->lock == 0);
thd->derived_tables= derived_tables;
thd->open_tables= open_tables;
thd->lock= lock;
thd->query_id= query_id;
/* save references to memory, allocated during fetch */ /* save references to memory, allocated during fetch */
thd->set_n_backup_item_arena(this, &thd->stmt_backup); thd->set_n_backup_item_arena(this, &thd->stmt_backup);
...@@ -1846,6 +1836,9 @@ Cursor::fetch(ulong num_rows) ...@@ -1846,6 +1836,9 @@ Cursor::fetch(ulong num_rows)
#endif #endif
thd->restore_backup_item_arena(this, &thd->stmt_backup); thd->restore_backup_item_arena(this, &thd->stmt_backup);
DBUG_ASSERT(thd->free_list == 0);
reset_thd(thd);
if (error == NESTED_LOOP_CURSOR_LIMIT) if (error == NESTED_LOOP_CURSOR_LIMIT)
{ {
/* Fetch limit worked, possibly more rows are there */ /* Fetch limit worked, possibly more rows are there */
...@@ -1886,8 +1879,8 @@ Cursor::close() ...@@ -1886,8 +1879,8 @@ Cursor::close()
join->cleanup(); join->cleanup();
delete join; delete join;
} }
/* XXX: Another hack: closing tables used in the cursor */
{ {
/* XXX: Another hack: closing tables used in the cursor */
DBUG_ASSERT(lock || open_tables || derived_tables); DBUG_ASSERT(lock || open_tables || derived_tables);
TABLE *tmp_derived_tables= thd->derived_tables; TABLE *tmp_derived_tables= thd->derived_tables;
......
...@@ -386,8 +386,6 @@ class Cursor: public Sql_alloc, public Item_arena ...@@ -386,8 +386,6 @@ class Cursor: public Sql_alloc, public Item_arena
/* Temporary implementation as now we replace THD state by value */ /* Temporary implementation as now we replace THD state by value */
/* Save THD state into cursor */ /* Save THD state into cursor */
void init_from_thd(THD *thd); void init_from_thd(THD *thd);
/* Restore THD from cursor to continue cursor execution */
void init_thd(THD *thd);
/* bzero cursor state in THD */ /* bzero cursor state in THD */
void reset_thd(THD *thd); void reset_thd(THD *thd);
......
...@@ -5108,10 +5108,12 @@ derived_table_list: ...@@ -5108,10 +5108,12 @@ derived_table_list:
join_table: join_table:
table_ref normal_join table_ref { TEST_ASSERT($1 && ($$=$3)); } table_ref normal_join table_ref { TEST_ASSERT($1 && ($$=$3)); }
| table_ref STRAIGHT_JOIN table_factor | table_ref STRAIGHT_JOIN table_ref
{ TEST_ASSERT($1 && ($$=$3)); $3->straight=1; } { TEST_ASSERT($1 && ($$=$3)); $3->straight=1; }
| table_ref normal_join table_ref ON expr | table_ref normal_join table_ref ON expr
{ TEST_ASSERT($1 && ($$=$3)); add_join_on($3,$5); } { TEST_ASSERT($1 && ($$=$3)); add_join_on($3,$5); }
| table_ref STRAIGHT_JOIN table_ref ON expr
{ TEST_ASSERT($1 && ($$=$3)); $3->straight=1; add_join_on($3,$5); }
| table_ref normal_join table_ref | table_ref normal_join table_ref
USING USING
{ {
......
...@@ -13145,6 +13145,67 @@ static void test_bug9643() ...@@ -13145,6 +13145,67 @@ static void test_bug9643()
myquery(rc); myquery(rc);
} }
/*
Check that proper cleanups are done for prepared statement when
fetching thorugh a cursor.
*/
static void test_bug10729()
{
MYSQL_STMT *stmt;
MYSQL_BIND bind[1];
char a[21];
int rc;
const char *stmt_text;
int i= 0;
char *name_array[3]= { "aaa", "bbb", "ccc" };
ulong type;
myheader("test_bug10729");
mysql_query(mysql, "drop table if exists t1");
mysql_query(mysql, "create table t1 (id integer not null primary key,"
"name VARCHAR(20) NOT NULL)");
rc= mysql_query(mysql, "insert into t1 (id, name) values "
"(1, 'aaa'), (2, 'bbb'), (3, 'ccc')");
myquery(rc);
stmt= mysql_stmt_init(mysql);
type= (ulong) CURSOR_TYPE_READ_ONLY;
rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
check_execute(stmt, rc);
stmt_text= "select name from t1";
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
check_execute(stmt, rc);
bzero(bind, sizeof(bind));
bind[0].buffer_type= MYSQL_TYPE_STRING;
bind[0].buffer= (void*) a;
bind[0].buffer_length= sizeof(a);
mysql_stmt_bind_result(stmt, bind);
for (i= 0; i < 3; i++)
{
int row_no= 0;
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
while ((rc= mysql_stmt_fetch(stmt)) == 0)
{
DIE_UNLESS(strcmp(a, name_array[row_no]) == 0);
if (!opt_silent)
printf("%d: %s\n", row_no, a);
++row_no;
}
DIE_UNLESS(rc == MYSQL_NO_DATA);
}
rc= mysql_stmt_close(stmt);
DIE_UNLESS(rc == 0);
rc= mysql_query(mysql, "drop table t1");
myquery(rc);
}
/* /*
Read and parse arguments and MySQL options from my.cnf Read and parse arguments and MySQL options from my.cnf
*/ */
...@@ -13377,6 +13438,7 @@ static struct my_tests_st my_tests[]= { ...@@ -13377,6 +13438,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug9520", test_bug9520 }, { "test_bug9520", test_bug9520 },
{ "test_bug9478", test_bug9478 }, { "test_bug9478", test_bug9478 },
{ "test_bug9643", test_bug9643 }, { "test_bug9643", test_bug9643 },
{ "test_bug10729", test_bug10729 },
{ 0, 0 } { 0, 0 }
}; };
......
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