Commit 064b0220 authored by bell@sanja.is.com.ua's avatar bell@sanja.is.com.ua

new lock for multiupdate:

- open and create derived tables
- detect which tables should be locked for write
- lock and fill derived tables
some unitialized variables fixed
parent b2dd380b
...@@ -26,7 +26,7 @@ lock table t1 read; ...@@ -26,7 +26,7 @@ lock table t1 read;
update t1,t2 set c=a where b=d; update t1,t2 set c=a where b=d;
select c from t2; select c from t2;
c c
1 2
drop table t1; drop table t1;
drop table t2; drop table t2;
create table t1 (a int); create table t1 (a int);
......
...@@ -155,7 +155,6 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated ...@@ -155,7 +155,6 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
UPDATE t1,t2 SET t1.d=t2.d,t2.d=30 WHERE t1.n=t2.n; UPDATE t1,t2 SET t1.d=t2.d,t2.d=30 WHERE t1.n=t2.n;
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
UPDATE t1,t2 SET t1.d=t2.d WHERE t1.n=t2.n; UPDATE t1,t2 SET t1.d=t2.d WHERE t1.n=t2.n;
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
unlock tables; unlock tables;
LOCK TABLES t1 write, t2 write; LOCK TABLES t1 write, t2 write;
UPDATE t1,t2 SET t1.d=t2.d WHERE t1.n=t2.n; UPDATE t1,t2 SET t1.d=t2.d WHERE t1.n=t2.n;
...@@ -461,6 +460,7 @@ drop table t1, t2, t3; ...@@ -461,6 +460,7 @@ drop table t1, t2, t3;
create table t1 (col1 int); create table t1 (col1 int);
create table t2 (col1 int); create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
ERROR HY000: You can't specify target table 't1' for update in FROM clause
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1; delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
ERROR HY000: You can't specify target table 't1' for update in FROM clause ERROR HY000: You can't specify target table 't1' for update in FROM clause
drop table t1,t2; drop table t1,t2;
......
...@@ -1339,16 +1339,14 @@ c ...@@ -1339,16 +1339,14 @@ c
prepare stmt1 from "update v1,t1 set v1.s1=? where t1.s1=v1.s1"; prepare stmt1 from "update v1,t1 set v1.s1=? where t1.s1=v1.s1";
set @arg='d'; set @arg='d';
execute stmt1 using @arg; execute stmt1 using @arg;
ERROR HY000: Table 't1' is read only
select * from v1; select * from v1;
s1 s1
c d
set @arg='e'; set @arg='e';
execute stmt1 using @arg; execute stmt1 using @arg;
ERROR HY000: Table 't1' is read only
select * from v1; select * from v1;
s1 s1
c e
deallocate prepare stmt1; deallocate prepare stmt1;
drop view v1; drop view v1;
drop table t1; drop table t1;
......
...@@ -159,8 +159,6 @@ LOCK TABLES t1 write, t2 read; ...@@ -159,8 +159,6 @@ LOCK TABLES t1 write, t2 read;
DELETE t1.*, t2.* FROM t1,t2 where t1.n=t2.n; DELETE t1.*, t2.* FROM t1,t2 where t1.n=t2.n;
--error 1099 --error 1099
UPDATE t1,t2 SET t1.d=t2.d,t2.d=30 WHERE t1.n=t2.n; UPDATE t1,t2 SET t1.d=t2.d,t2.d=30 WHERE t1.n=t2.n;
--QQ This should not generate an error
--error 1099
UPDATE t1,t2 SET t1.d=t2.d WHERE t1.n=t2.n; UPDATE t1,t2 SET t1.d=t2.d WHERE t1.n=t2.n;
unlock tables; unlock tables;
LOCK TABLES t1 write, t2 write; LOCK TABLES t1 write, t2 write;
...@@ -428,7 +426,7 @@ drop table t1, t2, t3; ...@@ -428,7 +426,7 @@ drop table t1, t2, t3;
# #
create table t1 (col1 int); create table t1 (col1 int);
create table t2 (col1 int); create table t2 (col1 int);
-- QQ The following should give error 1093 -- error 1093
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
-- error 1093 -- error 1093
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1; delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
......
...@@ -1300,13 +1300,9 @@ update v1,t1 set v1.s1='c' where t1.s1=v1.s1; ...@@ -1300,13 +1300,9 @@ update v1,t1 set v1.s1='c' where t1.s1=v1.s1;
select * from v1; select * from v1;
prepare stmt1 from "update v1,t1 set v1.s1=? where t1.s1=v1.s1"; prepare stmt1 from "update v1,t1 set v1.s1=? where t1.s1=v1.s1";
set @arg='d'; set @arg='d';
-- QQ This should not generate an error
--error 1036
execute stmt1 using @arg; execute stmt1 using @arg;
select * from v1; select * from v1;
set @arg='e'; set @arg='e';
-- QQ This should not generate an error
--error 1036
execute stmt1 using @arg; execute stmt1 using @arg;
select * from v1; select * from v1;
deallocate prepare stmt1; deallocate prepare stmt1;
......
...@@ -565,7 +565,11 @@ int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type, ...@@ -565,7 +565,11 @@ int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
select_result *result); select_result *result);
int mysql_union(THD *thd, LEX *lex, select_result *result, int mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit); SELECT_LEX_UNIT *unit);
int mysql_handle_derived(LEX *lex); int mysql_handle_derived(LEX *lex, int (*processor)(THD *thd,
st_lex *lex,
st_table_list *table));
int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t);
int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t);
Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item ***copy_func, Field **from_field, Item ***copy_func, Field **from_field,
bool group, bool modify_item, uint convert_blob_length); bool group, bool modify_item, uint convert_blob_length);
...@@ -792,7 +796,6 @@ void wait_for_refresh(THD *thd); ...@@ -792,7 +796,6 @@ void wait_for_refresh(THD *thd);
int open_tables(THD *thd, TABLE_LIST *tables, uint *counter); int open_tables(THD *thd, TABLE_LIST *tables, uint *counter);
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
int open_and_lock_tables(THD *thd,TABLE_LIST *tables); int open_and_lock_tables(THD *thd,TABLE_LIST *tables);
void relink_tables_for_derived(THD *thd);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db, TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
const char *table_name, bool link_in_list); const char *table_name, bool link_in_list);
......
...@@ -44,6 +44,7 @@ static my_bool open_new_frm(const char *path, const char *alias, ...@@ -44,6 +44,7 @@ static my_bool open_new_frm(const char *path, const char *alias,
uint db_stat, uint prgflag, uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam, uint ha_open_flags, TABLE *outparam,
TABLE_LIST *table_desc, MEM_ROOT *mem_root); TABLE_LIST *table_desc, MEM_ROOT *mem_root);
static void relink_tables_for_multidelete(THD *thd);
extern "C" byte *table_cache_key(const byte *record,uint *length, extern "C" byte *table_cache_key(const byte *record,uint *length,
my_bool not_used __attribute__((unused))) my_bool not_used __attribute__((unused)))
...@@ -1859,19 +1860,21 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -1859,19 +1860,21 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
uint counter; uint counter;
if (open_tables(thd, tables, &counter) || if (open_tables(thd, tables, &counter) ||
lock_tables(thd, tables, counter) || lock_tables(thd, tables, counter) ||
mysql_handle_derived(thd->lex)) mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() &&
mysql_handle_derived(thd->lex, &mysql_derived_filling)))
DBUG_RETURN(thd->net.report_error ? -1 : 1); /* purecov: inspected */ DBUG_RETURN(thd->net.report_error ? -1 : 1); /* purecov: inspected */
relink_tables_for_derived(thd); relink_tables_for_multidelete(thd);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
/* /*
Let us propagate pointers to open tables from global table list Let us propagate pointers to open tables from global table list
to table lists in particular selects if needed. to table lists for multi-delete
*/ */
void relink_tables_for_derived(THD *thd) static void relink_tables_for_multidelete(THD *thd)
{ {
if (thd->lex->all_selects_list->next_select_in_list() || if (thd->lex->all_selects_list->next_select_in_list() ||
thd->lex->time_zone_tables_used) thd->lex->time_zone_tables_used)
......
...@@ -1131,6 +1131,10 @@ public: ...@@ -1131,6 +1131,10 @@ public:
{ {
return command == COM_PREPARE; return command == COM_PREPARE;
} }
inline bool fill_derived_tables()
{
return !only_prepare() && !lex->only_view_structure();
}
inline gptr trans_alloc(unsigned int size) inline gptr trans_alloc(unsigned int size)
{ {
return alloc_root(&transaction.mem_root,size); return alloc_root(&transaction.mem_root,size);
......
...@@ -25,15 +25,15 @@ ...@@ -25,15 +25,15 @@
#include "sql_select.h" #include "sql_select.h"
#include "sql_acl.h" #include "sql_acl.h"
static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *s,
TABLE_LIST *t);
/* /*
Resolve derived tables in all queries call given derived table processor (preparing or filling tables)
SYNOPSIS SYNOPSIS
mysql_handle_derived() mysql_handle_derived()
lex LEX for this thread lex LEX for this thread
processor procedure of derived table processing
RETURN RETURN
0 ok 0 ok
...@@ -42,7 +42,7 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *s, ...@@ -42,7 +42,7 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *s,
*/ */
int int
mysql_handle_derived(LEX *lex) mysql_handle_derived(LEX *lex, int (*processor)(THD*, LEX*, TABLE_LIST*))
{ {
if (lex->derived_tables) if (lex->derived_tables)
{ {
...@@ -55,15 +55,9 @@ mysql_handle_derived(LEX *lex) ...@@ -55,15 +55,9 @@ mysql_handle_derived(LEX *lex)
cursor= cursor->next_local) cursor= cursor->next_local)
{ {
int res; int res;
if (cursor->derived && (res= mysql_derived(lex->thd, lex, if ((res= (*processor)(lex->thd, lex, cursor)))
cursor->derived,
cursor)))
{
return res; return res;
} }
else if (cursor->ancestor)
cursor->set_ancestor();
}
if (lex->describe) if (lex->describe)
{ {
/* /*
...@@ -80,20 +74,16 @@ mysql_handle_derived(LEX *lex) ...@@ -80,20 +74,16 @@ mysql_handle_derived(LEX *lex)
/* /*
Resolve derived tables in all queries Create temporary table structure (but do not fill it)
SYNOPSIS SYNOPSIS
mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t) mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t)
thd Thread handle thd Thread handle
lex LEX for this thread lex LEX for this thread
unit node that contains all SELECT's for derived tables orig_table_list TABLE_LIST for the upper SELECT
t TABLE_LIST for the upper SELECT
IMPLEMENTATION IMPLEMENTATION
Derived table is resolved with temporary table. It is created based on the Derived table is resolved with temporary table.
queries defined. After temporary table is created, if this is not EXPLAIN,
then the entire unit / node is deleted. unit is deleted if UNION is used
for derived table and node is deleted is it is a simple SELECT.
After table creation, the above TABLE_LIST is updated with a new table. After table creation, the above TABLE_LIST is updated with a new table.
...@@ -107,19 +97,19 @@ mysql_handle_derived(LEX *lex) ...@@ -107,19 +97,19 @@ mysql_handle_derived(LEX *lex)
0 ok 0 ok
1 Error 1 Error
-1 Error and error message given -1 Error and error message given
*/ */
static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
TABLE_LIST *org_table_list)
{ {
SELECT_LEX_UNIT *unit= orig_table_list->derived;
int res= 0;
if (unit)
{
SELECT_LEX *first_select= unit->first_select(); SELECT_LEX *first_select= unit->first_select();
TABLE *table; TABLE *table= 0;
int res;
select_union *derived_result; select_union *derived_result;
bool is_union= first_select->next_select() && bool is_union= first_select->next_select() &&
first_select->next_select()->linkage == UNION_TYPE; first_select->next_select()->linkage == UNION_TYPE;
SELECT_LEX *save_current_select= lex->current_select;
DBUG_ENTER("mysql_derived"); DBUG_ENTER("mysql_derived");
if (!(derived_result= new select_union(0))) if (!(derived_result= new select_union(0)))
...@@ -142,24 +132,90 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, ...@@ -142,24 +132,90 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
(first_select->options | thd->options | (first_select->options | thd->options |
TMP_TABLE_ALL_COLUMNS), TMP_TABLE_ALL_COLUMNS),
HA_POS_ERROR, HA_POS_ERROR,
org_table_list->alias))) orig_table_list->alias)))
{ {
res= -1; res= -1;
goto exit; goto exit;
} }
derived_result->set_table(table); derived_result->set_table(table);
exit:
/* /*
if it is preparation PS only or commands that need only VIEW structure if it is preparation PS only or commands that need only VIEW structure
then we do not need real data and we can skip execution (and parameters then we do not need real data and we can skip execution (and parameters
is not defined, too) is not defined, too)
*/ */
if (!thd->only_prepare() && !lex->only_view_structure()) if (res)
{ {
if (table)
free_tmp_table(thd, table);
delete derived_result;
}
else
{
if (!thd->fill_derived_tables())
delete derived_result;
orig_table_list->derived_result= derived_result;
orig_table_list->table= table;
orig_table_list->real_name= table->real_name;
table->derived_select_number= first_select->select_number;
table->tmp_table= TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.privilege= SELECT_ACL;
#endif
orig_table_list->db= (char *)"";
// Force read of table stats in the optimizer
table->file->info(HA_STATUS_VARIABLE);
/* Add new temporary table to list of open derived tables */
table->next= thd->derived_tables;
thd->derived_tables= table;
}
}
else if (orig_table_list->ancestor)
orig_table_list->set_ancestor();
return (res);
}
/*
fill derived table
SYNOPSIS
mysql_derived_filling()
thd Thread handle
lex LEX for this thread
unit node that contains all SELECT's for derived tables
orig_table_list TABLE_LIST for the upper SELECT
IMPLEMENTATION
Derived table is resolved with temporary table. It is created based on the
queries defined. After temporary table is filled, if this is not EXPLAIN,
then the entire unit / node is deleted. unit is deleted if UNION is used
for derived table and node is deleted is it is a simple SELECT.
RETURN
0 ok
1 Error
-1 Error and error message given
*/
int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
TABLE *table= orig_table_list->table;
SELECT_LEX_UNIT *unit= orig_table_list->derived;
int res= 0;
/*check that table creation pass without problem and it is derived table */
if (table && unit)
{
SELECT_LEX *first_select= unit->first_select();
select_union *derived_result= orig_table_list->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
bool is_union= first_select->next_select() &&
first_select->next_select()->linkage == UNION_TYPE;
if (is_union) if (is_union)
{ {
// execute union without clean up // execute union without clean up
if (!(res= unit->prepare(thd, derived_result, SELECT_NO_UNLOCK)))
res= unit->exec(); res= unit->exec();
} }
else else
...@@ -186,7 +242,6 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, ...@@ -186,7 +242,6 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
SELECT_NO_UNLOCK), SELECT_NO_UNLOCK),
derived_result, unit, first_select); derived_result, unit, first_select);
} }
}
if (!res) if (!res)
{ {
...@@ -196,39 +251,18 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, ...@@ -196,39 +251,18 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
*/ */
if (derived_result->flush()) if (derived_result->flush())
res= 1; res= 1;
else
{
org_table_list->real_name= table->real_name;
org_table_list->table= table;
table->derived_select_number= first_select->select_number;
table->tmp_table= TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.privilege= SELECT_ACL;
#endif
org_table_list->db= (char *)"";
// Force read of table stats in the optimizer
table->file->info(HA_STATUS_VARIABLE);
}
if (!lex->describe) if (!lex->describe)
unit->cleanup(); unit->cleanup();
if (res)
free_tmp_table(thd, table);
else
{
/* Add new temporary table to list of open derived tables */
table->next= thd->derived_tables;
thd->derived_tables= table;
}
} }
else else
{ {
free_tmp_table(thd, table); free_tmp_table(thd, table);
unit->cleanup(); unit->cleanup();
} }
exit:
delete derived_result;
lex->current_select= save_current_select; lex->current_select= save_current_select;
DBUG_RETURN(res); if (res)
free_tmp_table(thd, table);
}
return res;
} }
...@@ -176,6 +176,7 @@ void lex_start(THD *thd, uchar *buf,uint length) ...@@ -176,6 +176,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->sphead= NULL; lex->sphead= NULL;
lex->spcont= NULL; lex->spcont= NULL;
lex->trg_table= NULL; lex->trg_table= NULL;
lex->proc_list.first= 0;
extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first); extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first);
hash_free(&lex->spfuns); hash_free(&lex->spfuns);
......
...@@ -91,7 +91,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -91,7 +91,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
int error; int error;
String *field_term=ex->field_term,*escaped=ex->escaped; String *field_term=ex->field_term,*escaped=ex->escaped;
String *enclosed=ex->enclosed; String *enclosed=ex->enclosed;
Item *unused_conds; Item *unused_conds= 0;
bool is_fifo=0; bool is_fifo=0;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
LOAD_FILE_INFO lf_info; LOAD_FILE_INFO lf_info;
......
...@@ -4472,7 +4472,6 @@ mysql_init_select(LEX *lex) ...@@ -4472,7 +4472,6 @@ mysql_init_select(LEX *lex)
{ {
DBUG_ASSERT(lex->result == 0); DBUG_ASSERT(lex->result == 0);
lex->exchange= 0; lex->exchange= 0;
lex->proc_list.first= 0;
} }
} }
......
...@@ -1314,7 +1314,11 @@ static int mysql_test_multiupdate(Prepared_statement *stmt, ...@@ -1314,7 +1314,11 @@ static int mysql_test_multiupdate(Prepared_statement *stmt,
int res; int res;
if ((res= multi_update_precheck(stmt->thd, tables))) if ((res= multi_update_precheck(stmt->thd, tables)))
return res; return res;
return select_like_statement_test(stmt, tables, &mysql_multi_update_prepare); /*
here we do not pass tables for opening, tables will be opened and locked
by mysql_multi_update_prepare
*/
return select_like_statement_test(stmt, 0, &mysql_multi_update_prepare);
} }
......
...@@ -578,10 +578,17 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -578,10 +578,17 @@ int mysql_multi_update_prepare(THD *thd)
TABLE_LIST *table_list= lex->query_tables; TABLE_LIST *table_list= lex->query_tables;
List<Item> *fields= &lex->select_lex.item_list; List<Item> *fields= &lex->select_lex.item_list;
TABLE_LIST *tl; TABLE_LIST *tl;
table_map tables_for_update= 0, readonly_tables= 0; table_map tables_for_update;
int res; int res;
bool update_view= 0; bool update_view= 0;
uint table_count;
const bool using_lock_tables= thd->locked_tables != 0;
DBUG_ENTER("mysql_multi_update_prepare"); DBUG_ENTER("mysql_multi_update_prepare");
/* open tables and create derived ones, but do not lock and fill them */
if (open_tables(thd, table_list, & table_count) ||
mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(thd->net.report_error ? -1 : 1);
/* /*
Ensure that we have update privilege for all tables and columns in the Ensure that we have update privilege for all tables and columns in the
SET part SET part
...@@ -606,9 +613,9 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -606,9 +613,9 @@ int mysql_multi_update_prepare(THD *thd)
call in setup_tables()). call in setup_tables()).
*/ */
if (setup_tables(thd, table_list, &lex->select_lex.where) || if (setup_tables(thd, table_list, &lex->select_lex.where) ||
(thd->lex->select_lex.no_wrap_view_item= 1, (lex->select_lex.no_wrap_view_item= 1,
res= setup_fields(thd, 0, table_list, *fields, 1, 0, 0), res= setup_fields(thd, 0, table_list, *fields, 1, 0, 0),
thd->lex->select_lex.no_wrap_view_item= 0, lex->select_lex.no_wrap_view_item= 0,
res)) res))
DBUG_RETURN(-1); DBUG_RETURN(-1);
...@@ -626,18 +633,10 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -626,18 +633,10 @@ int mysql_multi_update_prepare(THD *thd)
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
{ tables_for_update= get_table_map(fields);
// Find tables used in items
List_iterator_fast<Item> it(*fields);
Item *item;
while ((item= it++))
{
tables_for_update|= item->used_tables();
}
}
/* /*
Count tables and setup timestamp handling Setup timestamp handling and locking mode
*/ */
for (tl= table_list; tl ; tl= tl->next_local) for (tl= table_list; tl ; tl= tl->next_local)
{ {
...@@ -651,22 +650,43 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -651,22 +650,43 @@ int mysql_multi_update_prepare(THD *thd)
table->timestamp_field->query_id == thd->query_id) table->timestamp_field->query_id == thd->query_id)
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
if (!tl->updatable || check_key_in_view(thd, tl)) /* if table will be updated then check that it is unique */
readonly_tables|= table->map; if (table->map & tables_for_update)
}
if (tables_for_update & readonly_tables)
{ {
// find readonly table/view which cause error if (!tl->updatable || check_key_in_view(thd, tl))
for (tl= table_list; tl ; tl= tl->next_local)
{
if ((readonly_tables & tl->table->map) &&
(tables_for_update & tl->table->map))
{ {
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), tl->alias, "UPDATE"); my_error(ER_NON_UPDATABLE_TABLE, MYF(0), tl->alias, "UPDATE");
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
/*
Multi-update can't be constructed over-union => we always have
single SELECT on top and have to check underlaying SELECTs of it
*/
if (lex->select_lex.check_updateable_in_subqueries(tl->db,
tl->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), tl->real_name);
DBUG_RETURN(-1);
} }
DBUG_PRINT("info",("setting table `%s` for update", tl->alias));
tl->lock_type= lex->multi_lock_option;
tl->updating= 1;
} }
else
{
DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias));
tl->lock_type= TL_READ;
tl->updating= 0;
}
if (!using_lock_tables)
tl->table->reginfo.lock_type= tl->lock_type;
}
/* now lock and fill tables */
if (lock_tables(thd, table_list, table_count) ||
(thd->fill_derived_tables() &&
mysql_handle_derived(lex, &mysql_derived_filling)))
DBUG_RETURN(thd->net.report_error ? -1 : 1);
DBUG_RETURN (0); DBUG_RETURN (0);
} }
...@@ -688,11 +708,6 @@ int mysql_multi_update(THD *thd, ...@@ -688,11 +708,6 @@ int mysql_multi_update(THD *thd,
multi_update *result; multi_update *result;
DBUG_ENTER("mysql_multi_update"); DBUG_ENTER("mysql_multi_update");
/* QQ: This should be fixed soon to get lower granularity locks */
select_lex->set_lock_for_tables(thd->lex->multi_lock_option);
if ((res= open_and_lock_tables(thd, table_list)))
DBUG_RETURN(res);
if ((res= mysql_multi_update_prepare(thd))) if ((res= mysql_multi_update_prepare(thd)))
DBUG_RETURN(res); DBUG_RETURN(res);
......
...@@ -222,6 +222,7 @@ struct st_table { ...@@ -222,6 +222,7 @@ struct st_table {
#define VIEW_CHECK_SKIP 2 #define VIEW_CHECK_SKIP 2
struct st_lex; struct st_lex;
struct select_union;
typedef struct st_table_list typedef struct st_table_list
{ {
...@@ -237,6 +238,11 @@ typedef struct st_table_list ...@@ -237,6 +238,11 @@ typedef struct st_table_list
/* ... join ... USE INDEX ... IGNORE INDEX */ /* ... join ... USE INDEX ... IGNORE INDEX */
List<String> *use_index, *ignore_index; List<String> *use_index, *ignore_index;
TABLE *table; /* opened table */ TABLE *table; /* opened table */
/*
select_result for derived table to pass it from table creation to table
filling procedure
*/
select_union *derived_result;
/* /*
Reference from aux_tables to local list entry of main select of Reference from aux_tables to local list entry of main select of
multi-delete statement: multi-delete statement:
......
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