diff options
-rw-r--r-- | heap/_check.c | 57 | ||||
-rw-r--r-- | heap/hp_delete.c | 4 | ||||
-rw-r--r-- | heap/hp_scan.c | 1 | ||||
-rw-r--r-- | heap/hp_update.c | 4 | ||||
-rw-r--r-- | heap/hp_write.c | 5 | ||||
-rw-r--r-- | include/my_base.h | 3 | ||||
-rw-r--r-- | isam/extra.c | 6 | ||||
-rw-r--r-- | myisam/mi_check.c | 12 | ||||
-rw-r--r-- | myisam/mi_extra.c | 4 | ||||
-rw-r--r-- | myisam/sort.c | 22 | ||||
-rw-r--r-- | myisammrg/myrg_extra.c | 3 | ||||
-rw-r--r-- | mysql-test/r/multi_update.result | 48 | ||||
-rw-r--r-- | mysql-test/t/multi_update.test | 50 | ||||
-rw-r--r-- | mysys/mf_iocache.c | 2 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 8 | ||||
-rw-r--r-- | sql/mysql_priv.h | 20 | ||||
-rw-r--r-- | sql/sql_base.cc | 17 | ||||
-rw-r--r-- | sql/sql_class.h | 63 | ||||
-rw-r--r-- | sql/sql_delete.cc | 67 | ||||
-rw-r--r-- | sql/sql_insert.cc | 24 | ||||
-rw-r--r-- | sql/sql_olap.cc | 4 | ||||
-rw-r--r-- | sql/sql_parse.cc | 79 | ||||
-rw-r--r-- | sql/sql_select.cc | 85 | ||||
-rw-r--r-- | sql/sql_select.h | 1 | ||||
-rw-r--r-- | sql/sql_union.cc | 19 | ||||
-rw-r--r-- | sql/sql_update.cc | 768 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 11 | ||||
-rw-r--r-- | sql/table.h | 16 | ||||
-rw-r--r-- | sql/uniques.cc | 3 |
29 files changed, 773 insertions, 633 deletions
diff --git a/heap/_check.c b/heap/_check.c index 03fb664cba9..6cc19aba137 100644 --- a/heap/_check.c +++ b/heap/_check.c @@ -21,19 +21,70 @@ static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records, ulong blength, my_bool print_status); -/* Returns 0 if the HEAP is ok */ +/* + Check if keys and rows are ok in a heap table + + SYNOPSIS + heap_check_heap() + info Table handler + print_status Prints some extra status + + NOTES + Doesn't change the state of the table handler + + RETURN VALUES + 0 ok + 1 error +*/ int heap_check_heap(HP_INFO *info,my_bool print_status) { int error; uint key; + ulong records=0, deleted=0, pos, next_block; HP_SHARE *share=info->s; - DBUG_ENTER("heap_check_keys"); + HP_INFO save_info= *info; /* Needed because scan_init */ + DBUG_ENTER("heap_check_heap"); - for (error=key=0 ; key < share->keys ; key++) + for (error=key= 0 ; key < share->keys ; key++) error|=check_one_key(share->keydef+key,key, share->records,share->blength, print_status); + /* + This is basicly the same code as in hp_scan, but we repeat it here to + get shorter DBUG log file. + */ + for (pos=next_block= 0 ; ; pos++) + { + if (pos < next_block) + { + info->current_ptr+= share->block.recbuffer; + } + else + { + next_block+= share->block.records_in_block; + if (next_block >= share->records+share->deleted) + { + next_block= share->records+share->deleted; + if (pos >= next_block) + break; /* End of file */ + } + } + _hp_find_record(info,pos); + + if (!info->current_ptr[share->reclength]) + deleted++; + else + records++; + } + + if (records != share->records || deleted != share->deleted) + { + DBUG_PRINT("error",("Found rows: %lu (%lu) deleted %lu (%lu)", + records, share->records, deleted, share->deleted)); + error= 1; + } + *info= save_info; DBUG_RETURN(error); } diff --git a/heap/hp_delete.c b/heap/hp_delete.c index 3ac321d5fa2..6ed6a045543 100644 --- a/heap/hp_delete.c +++ b/heap/hp_delete.c @@ -48,6 +48,10 @@ int heap_delete(HP_INFO *info, const byte *record) pos[share->reclength]=0; /* Record deleted */ share->deleted++; info->current_hash_ptr=0; +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG) + DBUG_EXECUTE("check_heap",heap_check_heap(info, 0);); +#endif + DBUG_RETURN(0); err: if (++(share->records) == share->blength) diff --git a/heap/hp_scan.c b/heap/hp_scan.c index e74f8b43ba7..0bbe40ba773 100644 --- a/heap/hp_scan.c +++ b/heap/hp_scan.c @@ -62,6 +62,7 @@ int heap_scan(register HP_INFO *info, byte *record) } if (!info->current_ptr[share->reclength]) { + DBUG_PRINT("warning",("Found deleted record")); info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND; DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED); } diff --git a/heap/hp_update.c b/heap/hp_update.c index 8cf3e4d4b8d..b0a1926e14a 100644 --- a/heap/hp_update.c +++ b/heap/hp_update.c @@ -46,6 +46,10 @@ int heap_update(HP_INFO *info, const byte *old, const byte *heap_new) memcpy(pos,heap_new,(size_t) share->reclength); if (++(share->records) == share->blength) share->blength+= share->blength; + +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG) + DBUG_EXECUTE("check_heap",heap_check_heap(info, 0);); +#endif DBUG_RETURN(0); err: diff --git a/heap/hp_write.c b/heap/hp_write.c index 806f40e5be5..18fa95e7760 100644 --- a/heap/hp_write.c +++ b/heap/hp_write.c @@ -60,7 +60,11 @@ int heap_write(HP_INFO *info, const byte *record) info->current_ptr=pos; info->current_hash_ptr=0; info->update|=HA_STATE_AKTIV; +#if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG) + DBUG_EXECUTE("check_heap",heap_check_heap(info, 0);); +#endif DBUG_RETURN(0); + err: DBUG_PRINT("info",("Duplicate key: %d",key)); info->errkey= key; @@ -74,6 +78,7 @@ err: *((byte**) pos)=share->del_link; share->del_link=pos; pos[share->reclength]=0; /* Record deleted */ + DBUG_RETURN(my_errno); } /* heap_write */ diff --git a/include/my_base.h b/include/my_base.h index 2450ab33425..8ef8301b84d 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -109,7 +109,8 @@ enum ha_extra_function { HA_EXTRA_BULK_INSERT_BEGIN, HA_EXTRA_BULK_INSERT_FLUSH, /* Flush one index */ HA_EXTRA_BULK_INSERT_END, - HA_EXTRA_PREPARE_FOR_DELETE + HA_EXTRA_PREPARE_FOR_DELETE, + HA_EXTRA_PREPARE_FOR_UPDATE /* Remove read cache if problems */ }; /* The following is parameter to ha_panic() */ diff --git a/isam/extra.c b/isam/extra.c index 570c396955f..e2f13532ddf 100644 --- a/isam/extra.c +++ b/isam/extra.c @@ -123,6 +123,7 @@ int nisam_extra(N_INFO *info, enum ha_extra_function function) } #endif if (!(info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))) + { if (!(init_io_cache(&info->rec_cache,info->dfile,0, WRITE_CACHE,info->s->state.data_file_length, (pbool) (info->lock_type != F_UNLCK), @@ -131,7 +132,12 @@ int nisam_extra(N_INFO *info, enum ha_extra_function function) info->opt_flag|=WRITE_CACHE_USED; info->update&= ~HA_STATE_ROW_CHANGED; } + } break; + case HA_EXTRA_PREPARE_FOR_UPDATE: + if (info->s->data_file_type != DYNAMIC_RECORD) + break; + /* Remove read/write cache if dynamic rows */ case HA_EXTRA_NO_CACHE: if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) { diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 9e591325c1c..b984f0e6ee6 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -2329,13 +2329,13 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, for (i=0 ; i < sort_info.total_keys ; i++) { sort_param[i].read_cache=param->read_cache; + /* + two approaches: the same amount of memory for each thread + or the memory for the same number of keys for each thread... + In the second one all the threads will fill their sort_buffers + (and call write_keys) at the same time, putting more stress on i/o. + */ sort_param[i].sortbuff_size= - /* - two approaches: the same amount of memory for each thread - or the memory for the same number of keys for each thread... - In the second one all the threads will fill their sort_buffers - (and call write_keys) at the same time, putting more stress on i/o. - */ #ifndef USING_SECOND_APPROACH param->sort_buffer_length/sort_info.total_keys; #else diff --git a/myisam/mi_extra.c b/myisam/mi_extra.c index 39eb4b0bd99..d7a3aea516d 100644 --- a/myisam/mi_extra.c +++ b/myisam/mi_extra.c @@ -165,6 +165,10 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) HA_STATE_EXTEND_BLOCK); } break; + case HA_EXTRA_PREPARE_FOR_UPDATE: + if (info->s->data_file_type != DYNAMIC_RECORD) + break; + /* Remove read/write cache if dynamic rows */ case HA_EXTRA_NO_CACHE: if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) { diff --git a/myisam/sort.c b/myisam/sort.c index fd5622e1340..f45ecbaf3a1 100644 --- a/myisam/sort.c +++ b/myisam/sort.c @@ -344,7 +344,6 @@ pthread_handler_decl(thr_find_all_keys,arg) mi_check_print_error(info->sort_info->param,"Sort buffer to small"); /* purecov: tested */ goto err; /* purecov: tested */ } -// (*info->lock_in_memory)(info->sort_info->param);/* Everything is allocated */ if (info->sort_info->param->testflag & T_VERBOSE) printf("Key %d - Allocating buffer for %d keys\n",info->key+1,keys); @@ -424,9 +423,9 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) byte *mergebuf=0; LINT_INIT(length); - for (i=0, sinfo=sort_param ; i<sort_info->total_keys ; i++, - rec_per_key_part+=sinfo->keyinfo->keysegs, - sinfo++) + for (i= 0, sinfo= sort_param ; + i < sort_info->total_keys ; + i++, rec_per_key_part+=sinfo->keyinfo->keysegs, sinfo++) { if (!sinfo->sort_keys) { @@ -452,15 +451,18 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) } } my_free((gptr) sinfo->sort_keys,MYF(0)); - my_free(mi_get_rec_buff_ptr(info, sinfo->rec_buff), MYF(MY_ALLOW_ZERO_PTR)); + my_free(mi_get_rec_buff_ptr(info, sinfo->rec_buff), + MYF(MY_ALLOW_ZERO_PTR)); sinfo->sort_keys=0; } - for (i=0, sinfo=sort_param ; i<sort_info->total_keys ; i++, - delete_dynamic(&sinfo->buffpek), - close_cached_file(&sinfo->tempfile), - close_cached_file(&sinfo->tempfile_for_exceptions), - sinfo++) + for (i= 0, sinfo= sort_param ; + i < sort_info->total_keys ; + i++, + delete_dynamic(&sinfo->buffpek), + close_cached_file(&sinfo->tempfile), + close_cached_file(&sinfo->tempfile_for_exceptions), + sinfo++) { if (got_error) continue; diff --git a/myisammrg/myrg_extra.c b/myisammrg/myrg_extra.c index ad17b0f82f2..d375b45df99 100644 --- a/myisammrg/myrg_extra.c +++ b/myisammrg/myrg_extra.c @@ -38,7 +38,8 @@ int myrg_extra(MYRG_INFO *info,enum ha_extra_function function, } else { - if (function == HA_EXTRA_NO_CACHE || function == HA_EXTRA_RESET) + if (function == HA_EXTRA_NO_CACHE || function == HA_EXTRA_RESET || + function == HA_EXTRA_PREPARE_FOR_UPDATE) info->cache_in_use=0; if (function == HA_EXTRA_RESET || function == HA_EXTRA_RESET_STATE) { diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index ce3f7e90f6b..20921816f1e 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -20,7 +20,7 @@ count(*) 10 select count(*) from t2 where t = "bbb"; count(*) -10 +50 select count(*) from t2 where id2 > 90; count(*) 50 @@ -70,71 +70,61 @@ create table t1(id1 int not null primary key, t varchar(100)) pack_keys = 1; create table t2(id2 int not null, t varchar(100), index(id2)) pack_keys = 1; delete t1 from t1,t2 where t1.id1 = t2.id2 and t1.id1 > 500; drop table t1,t2; -DROP TABLE IF EXISTS a,b,c; -CREATE TABLE a ( +CREATE TABLE t1 ( id int(11) NOT NULL default '0', name varchar(10) default NULL, PRIMARY KEY (id) ) TYPE=MyISAM; -INSERT INTO a VALUES (1,'aaa'),(2,'aaa'),(3,'aaa'); -CREATE TABLE b ( +INSERT INTO t1 VALUES (1,'aaa'),(2,'aaa'),(3,'aaa'); +CREATE TABLE t2 ( id int(11) NOT NULL default '0', name varchar(10) default NULL, PRIMARY KEY (id) ) TYPE=MyISAM; -INSERT INTO b VALUES (2,'bbb'),(3,'bbb'),(4,'bbb'); -CREATE TABLE c ( +INSERT INTO t2 VALUES (2,'bbb'),(3,'bbb'),(4,'bbb'); +CREATE TABLE t3 ( id int(11) NOT NULL default '0', mydate datetime default NULL, PRIMARY KEY (id) ) TYPE=MyISAM; -INSERT INTO c VALUES (1,'2002-02-04 00:00:00'),(3,'2002-05-12 00:00:00'),(5,'2002-05-12 00:00:00'),(6,'2002-06-22 +INSERT INTO t3 VALUES (1,'2002-02-04 00:00:00'),(3,'2002-05-12 00:00:00'),(5,'2002-05-12 00:00:00'),(6,'2002-06-22 00:00:00'),(7,'2002-07-22 00:00:00'); -delete a,b,c from a,b,c -where to_days(now())-to_days(c.mydate)>=30 -and c.id=a.id and c.id=b.id; -select * from c; +delete t1,t2,t3 from t1,t2,t3 where to_days(now())-to_days(t3.mydate)>=30 and t3.id=t1.id and t3.id=t2.id; +select * from t3; id mydate 1 2002-02-04 00:00:00 5 2002-05-12 00:00:00 6 2002-06-22 00:00:00 7 2002-07-22 00:00:00 -DROP TABLE IF EXISTS a,b,c; -drop table if exists parent, child; -CREATE TABLE IF NOT EXISTS `parent` ( +DROP TABLE IF EXISTS t1,t2,t3; +CREATE TABLE IF NOT EXISTS `t1` ( `id` int(11) NOT NULL auto_increment, `tst` text, `tst1` text, PRIMARY KEY (`id`) ) TYPE=MyISAM; -CREATE TABLE IF NOT EXISTS `child` ( +CREATE TABLE IF NOT EXISTS `t2` ( `ID` int(11) NOT NULL auto_increment, `ParId` int(11) default NULL, `tst` text, `tst1` text, PRIMARY KEY (`ID`), -KEY `IX_ParId_child` (`ParId`), -FOREIGN KEY (`ParId`) REFERENCES `test.parent` (`id`) +KEY `IX_ParId_t2` (`ParId`), +FOREIGN KEY (`ParId`) REFERENCES `t1` (`id`) ) TYPE=MyISAM; -INSERT INTO parent(tst,tst1) -VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE"); -INSERT INTO child(ParId) -VALUES(1), (2), (3); -select * from child; +INSERT INTO t1(tst,tst1) VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE"); +INSERT INTO t2(ParId) VALUES(1), (2), (3); +select * from t2; ID ParId tst tst1 1 1 NULL NULL 2 2 NULL NULL 3 3 NULL NULL -UPDATE child, parent -SET child.tst = parent.tst, -child.tst1 = parent.tst1 -WHERE child.ParId = parent.Id; -select * from child; +UPDATE t2, t1 SET t2.tst = t1.tst, t2.tst1 = t1.tst1 WHERE t2.ParId = t1.Id; +select * from t2; ID ParId tst tst1 1 1 MySQL MySQL AB 2 2 MSSQL Microsoft 3 3 ORACLE ORACLE -drop table parent, child; drop table if exists t1, t2 ; create table t1 (n numeric(10)); create table t2 (n numeric(10)); diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index b3a51ff65bc..ec1ef76753b 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -80,67 +80,59 @@ while ($1) enable_query_log; delete t1 from t1,t2 where t1.id1 = t2.id2 and t1.id1 > 500; drop table t1,t2; -DROP TABLE IF EXISTS a,b,c; -CREATE TABLE a ( + +CREATE TABLE t1 ( id int(11) NOT NULL default '0', name varchar(10) default NULL, PRIMARY KEY (id) ) TYPE=MyISAM; -INSERT INTO a VALUES (1,'aaa'),(2,'aaa'),(3,'aaa'); -CREATE TABLE b ( +INSERT INTO t1 VALUES (1,'aaa'),(2,'aaa'),(3,'aaa'); +CREATE TABLE t2 ( id int(11) NOT NULL default '0', name varchar(10) default NULL, PRIMARY KEY (id) ) TYPE=MyISAM; -INSERT INTO b VALUES (2,'bbb'),(3,'bbb'),(4,'bbb'); -CREATE TABLE c ( +INSERT INTO t2 VALUES (2,'bbb'),(3,'bbb'),(4,'bbb'); +CREATE TABLE t3 ( id int(11) NOT NULL default '0', mydate datetime default NULL, PRIMARY KEY (id) ) TYPE=MyISAM; -INSERT INTO c VALUES (1,'2002-02-04 00:00:00'),(3,'2002-05-12 00:00:00'),(5,'2002-05-12 00:00:00'),(6,'2002-06-22 +INSERT INTO t3 VALUES (1,'2002-02-04 00:00:00'),(3,'2002-05-12 00:00:00'),(5,'2002-05-12 00:00:00'),(6,'2002-06-22 00:00:00'),(7,'2002-07-22 00:00:00'); -delete a,b,c from a,b,c -where to_days(now())-to_days(c.mydate)>=30 -and c.id=a.id and c.id=b.id; -select * from c; -DROP TABLE IF EXISTS a,b,c; -drop table if exists parent, child; -CREATE TABLE IF NOT EXISTS `parent` ( +delete t1,t2,t3 from t1,t2,t3 where to_days(now())-to_days(t3.mydate)>=30 and t3.id=t1.id and t3.id=t2.id; +select * from t3; +DROP TABLE IF EXISTS t1,t2,t3; + +CREATE TABLE IF NOT EXISTS `t1` ( `id` int(11) NOT NULL auto_increment, `tst` text, `tst1` text, PRIMARY KEY (`id`) ) TYPE=MyISAM; -CREATE TABLE IF NOT EXISTS `child` ( +CREATE TABLE IF NOT EXISTS `t2` ( `ID` int(11) NOT NULL auto_increment, `ParId` int(11) default NULL, `tst` text, `tst1` text, PRIMARY KEY (`ID`), - KEY `IX_ParId_child` (`ParId`), - FOREIGN KEY (`ParId`) REFERENCES `test.parent` (`id`) + KEY `IX_ParId_t2` (`ParId`), + FOREIGN KEY (`ParId`) REFERENCES `t1` (`id`) ) TYPE=MyISAM; -INSERT INTO parent(tst,tst1) -VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE"); - -INSERT INTO child(ParId) -VALUES(1), (2), (3); +INSERT INTO t1(tst,tst1) VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE"); -select * from child; +INSERT INTO t2(ParId) VALUES(1), (2), (3); -UPDATE child, parent -SET child.tst = parent.tst, -child.tst1 = parent.tst1 -WHERE child.ParId = parent.Id; +select * from t2; -select * from child; +UPDATE t2, t1 SET t2.tst = t1.tst, t2.tst1 = t1.tst1 WHERE t2.ParId = t1.Id; +select * from t2; -drop table parent, child; drop table if exists t1, t2 ; + create table t1 (n numeric(10)); create table t2 (n numeric(10)); insert into t2 values (1),(2),(4),(8),(16),(32); diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 43b3d30915f..e9c35175bf9 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -445,6 +445,8 @@ void init_io_cache_share(IO_CACHE *info, IO_CACHE_SHARE *s, uint num_threads) s->active=0; /* to catch errors */ info->share=s; info->read_function=_my_b_read_r; + /* Ensure that the code doesn't use pointer to the IO_CACHE object */ + info->current_pos= info->current_end= 0; } /* diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index ee587289168..b5f21c8772e 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1658,7 +1658,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const i -= u; } if (i < 0) - return true; + return 1; register const int v = plm1 - i; turboShift = u - v; @@ -1675,7 +1675,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const } j += shift; } - return false; + return 0; } else { @@ -1689,7 +1689,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const i -= u; } if (i < 0) - return true; + return 1; register const int v = plm1 - i; turboShift = u - v; @@ -1706,7 +1706,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const } j += shift; } - return false; + return 0; } } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 43de81cae00..7cfbd84a99d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -242,6 +242,20 @@ typedef struct st_sql_list { uint elements; byte *first; byte **next; + + inline void empty() + { + elements=0; + first=0; + next= &first; + } + inline void link_in_list(byte *element,byte **next_ptr) + { + elements++; + (*next)=element; + next= next_ptr; + *next=0; + } } SQL_LIST; @@ -415,6 +429,10 @@ int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, List<Item> &values,COND *conds, ORDER *order, ha_rows limit, enum enum_duplicates handle_duplicates); +int mysql_multi_update(THD *thd, TABLE_LIST *table_list, + List<Item> *fields, List<Item> *values, + COND *conds, ulong options, + enum enum_duplicates handle_duplicates); int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, List<List_item> &values, enum_duplicates flag); void kill_delayed_threads(void); @@ -498,7 +516,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table,LEX_STRING *alias, void set_lock_for_tables(thr_lock_type lock_type); void add_join_on(TABLE_LIST *b,Item *expr); void add_join_natural(TABLE_LIST *a,TABLE_LIST *b); -bool add_proc_to_list(Item *item); +bool add_proc_to_list(THD *thd, Item *item); TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find); SQL_SELECT *make_select(TABLE *head, table_map const_tables, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 032882080ff..043ce44c140 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1942,8 +1942,8 @@ static key_map get_key_map_from_key_list(TABLE *table, } /**************************************************************************** -** This just drops in all fields instead of current '*' field -** Returns pointer to last inserted field if ok + This just drops in all fields instead of current '*' field + Returns pointer to last inserted field if ok ****************************************************************************/ bool @@ -1957,21 +1957,26 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, for (; tables ; tables=tables->next) { TABLE *table=tables->table; - if (grant_option && !thd->master_access && - check_grant_all_columns(thd,SELECT_ACL,table) ) - DBUG_RETURN(-1); if (!table_name || (!strcmp(table_name,tables->alias) && (!db_name || !strcmp(tables->db,db_name)))) { + /* Ensure that we have access right to all columns */ + if (grant_option && !thd->master_access && + check_grant_all_columns(thd,SELECT_ACL,table) ) + DBUG_RETURN(-1); Field **ptr=table->field,*field; thd->used_tables|=table->map; while ((field = *ptr++)) { Item_field *item= new Item_field(field); if (!found++) - (void) it->replace(item); + (void) it->replace(item); // Replace '*' else it->after(item); + /* + Mark if field used before in this select. + Used by 'insert' to verify if a field name is used twice + */ if (field->query_id == thd->query_id) thd->dupp_field=field; field->query_id=thd->query_id; diff --git a/sql/sql_class.h b/sql/sql_class.h index dba2ad130bf..241b7d0f87a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -592,7 +592,7 @@ public: virtual int prepare(List<Item> &list) { return 0; } virtual bool send_fields(List<Item> &list,uint flag)=0; virtual bool send_data(List<Item> &items)=0; - virtual void initialize_tables (JOIN *join=0) {} + virtual bool initialize_tables (JOIN *join=0) { return 0; } virtual void send_error(uint errcode,const char *err) { ::send_error(&thd->net,errcode,err); @@ -656,10 +656,10 @@ class select_insert :public select_result { List<Item> *fields; ulonglong last_insert_id; COPY_INFO info; - uint save_time_stamp; select_insert(TABLE *table_par,List<Item> *fields_par,enum_duplicates duplic) - :table(table_par),fields(fields_par), last_insert_id(0), save_time_stamp(0) { + :table(table_par),fields(fields_par), last_insert_id(0) + { bzero((char*) &info,sizeof(info)); info.handle_duplicates=duplic; } @@ -703,8 +703,8 @@ class select_union :public select_result { public: TABLE *table; COPY_INFO info; - uint save_time_stamp; TMP_TABLE_PARAM *tmp_table_param; + bool not_describe; select_union(TABLE *table_par); ~select_union(); @@ -814,37 +814,36 @@ public: bool send_fields(List<Item> &list, uint flag) { return 0; } bool send_data(List<Item> &items); - void initialize_tables (JOIN *join); + bool initialize_tables (JOIN *join); void send_error(uint errcode,const char *err); int do_deletes (bool from_send_error); bool send_eof(); }; - class multi_update : public select_result { - TABLE_LIST *update_tables, *table_being_updated; -// Unique **tempfiles; - COPY_INFO *infos; - TABLE **tmp_tables; - THD *thd; - ha_rows updated, found; - List<Item> fields; - List <Item> **fields_by_tables; - enum enum_duplicates dupl; - uint num_of_tables, num_fields, num_updated, *save_time_stamps, *field_sequence; - int error; - bool do_update, not_trans_safe; - public: - multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs, - enum enum_duplicates handle_duplicates, - uint num); - ~multi_update(); - int prepare(List<Item> &list); - bool send_fields(List<Item> &list, - uint flag) { return 0; } - bool send_data(List<Item> &items); - void initialize_tables (JOIN *join); - void send_error(uint errcode,const char *err); - int do_updates (bool from_send_error); - bool send_eof(); - }; +class multi_update : public select_result +{ + TABLE_LIST *all_tables, *update_tables, *table_being_updated; + THD *thd; + TABLE **tmp_tables, *main_table; + TMP_TABLE_PARAM *tmp_table_param; + ha_rows updated, found; + List <Item> *fields, *values; + List <Item> **fields_for_table, **values_for_table; + uint table_count; + Copy_field *copy_field; + enum enum_duplicates handle_duplicates; + bool do_update, trans_safe, transactional_tables, log_delayed; + +public: + multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> *fields, + List<Item> *values, enum_duplicates handle_duplicates); + ~multi_update(); + int prepare(List<Item> &list); + bool send_fields(List<Item> &list, uint flag) { return 0; } + bool send_data(List<Item> &items); + bool initialize_tables (JOIN *join); + void send_error(uint errcode,const char *err); + int do_updates (bool from_send_error); + bool send_eof(); +}; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1361ff39388..4997305de6b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -213,12 +213,13 @@ cleanup: extern "C" int refposcmp2(void* arg, const void *a,const void *b) { + /* arg is a pointer to file->ref_length */ return memcmp(a,b, *(int*) arg); } multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables_arg) - : delete_tables (dt), thd(thd_arg), deleted(0), + : delete_tables(dt), thd(thd_arg), deleted(0), num_of_tables(num_of_tables_arg), error(0), do_delete(0), transactional_tables(0), log_delayed(0), normal_tables(0) { @@ -230,31 +231,22 @@ int multi_delete::prepare(List<Item> &values) { DBUG_ENTER("multi_delete::prepare"); - do_delete = true; + do_delete= 1; thd->proc_info="deleting from main table"; - - if (thd->options & OPTION_SAFE_UPDATES) - { - TABLE_LIST *table_ref; - for (table_ref=delete_tables; table_ref; table_ref=table_ref->next) - { - TABLE *table=table_ref->table; - if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys) - { - my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0)); - DBUG_RETURN(1); - } - } - } DBUG_RETURN(0); } -void +bool multi_delete::initialize_tables(JOIN *join) { - int counter=0; TABLE_LIST *walk; + Unique **tempfiles_ptr; + DBUG_ENTER("initialize_tables"); + + if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join)) + DBUG_RETURN(1); + table_map tables_to_delete_from=0; for (walk= delete_tables ; walk ; walk=walk->next) tables_to_delete_from|= walk->table->map; @@ -268,9 +260,10 @@ multi_delete::initialize_tables(JOIN *join) { /* We are going to delete from this table */ TABLE *tbl=walk->table=tab->table; + walk=walk->next; /* Don't use KEYREAD optimization on this table */ tbl->no_keyread=1; - walk=walk->next; + tbl->used_keys= 0; if (tbl->file->has_transactions()) log_delayed= transactional_tables= 1; else if (tbl->tmp_table != NO_TMP_TABLE) @@ -280,19 +273,17 @@ multi_delete::initialize_tables(JOIN *join) } } walk= delete_tables; - walk->table->used_keys=0; - for (walk=walk->next ; walk ; walk=walk->next, counter++) + tempfiles_ptr= tempfiles; + for (walk=walk->next ; walk ; walk=walk->next) { - tables_to_delete_from|= walk->table->map; TABLE *table=walk->table; - /* Don't use key read with MULTI-TABLE-DELETE */ - table->used_keys=0; - tempfiles[counter] = new Unique (refposcmp2, - (void *) &table->file->ref_length, - table->file->ref_length, - MEM_STRIP_BUF_SIZE); + *tempfiles_ptr++= new Unique (refposcmp2, + (void *) &table->file->ref_length, + table->file->ref_length, + MEM_STRIP_BUF_SIZE); } init_ftfuncs(thd,1); + DBUG_RETURN(thd->fatal_error != 0); } @@ -307,7 +298,7 @@ multi_delete::~multi_delete() t->no_keyread=0; } - for (uint counter = 0; counter < num_of_tables-1; counter++) + for (uint counter= 0; counter < num_of_tables-1; counter++) { if (tempfiles[counter]) delete tempfiles[counter]; @@ -414,7 +405,7 @@ int multi_delete::do_deletes(bool from_send_error) else table_being_deleted = delete_tables; - do_delete = false; + do_delete= 0; for (table_being_deleted=table_being_deleted->next; table_being_deleted ; table_being_deleted=table_being_deleted->next, counter++) @@ -468,7 +459,7 @@ bool multi_delete::send_eof() was a non-transaction-safe table involved, since modifications in it cannot be rolled back. */ - if (deleted) + if (deleted && (error <= 0 || normal_tables)) { mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) @@ -478,11 +469,17 @@ bool multi_delete::send_eof() if (mysql_bin_log.write(&qinfo) && !normal_tables) local_error=1; // Log write failed: roll back the SQL statement } - /* Commit or rollback the current SQL statement */ - VOID(ha_autocommit_or_rollback(thd,local_error > 0)); - - query_cache_invalidate3(thd, delete_tables, 1); + if (!log_delayed) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } + /* Commit or rollback the current SQL statement */ + if (transactional_tables) + if (ha_autocommit_or_rollback(thd,local_error > 0)) + local_error=1; + + if (deleted) + query_cache_invalidate3(thd, delete_tables, 1); + if (local_error) ::send_error(&thd->net); else diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 6ce2b50fc36..5ca08aa31b0 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -41,7 +41,8 @@ static void unlink_blobs(register TABLE *table); /* Check if insert fields are correct - Resets form->time_stamp if a timestamp value is set + Updates table->time_stamp to point to timestamp field or 0, depending on + if timestamp should be updated or not. */ static int @@ -87,11 +88,12 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name); return -1; } + table->time_stamp=0; if (table->timestamp_field && // Don't set timestamp if used - table->timestamp_field->query_id == thd->query_id) - table->time_stamp=0; // This should be saved + table->timestamp_field->query_id != thd->query_id) + table->time_stamp= table->timestamp_field->offset()+1; } - // For the values we need select_priv + // For the values we need select_priv table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); return 0; } @@ -105,7 +107,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, !(thd->master_access & SUPER_ACL)); bool transactional_table, log_delayed, bulk_insert=0; uint value_count; - uint save_time_stamp; ulong counter = 1; ulonglong id; COPY_INFO info; @@ -150,14 +151,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, DBUG_RETURN(-1); thd->proc_info="init"; thd->used_tables=0; - save_time_stamp=table->time_stamp; values= its++; if (check_insert_fields(thd,table,fields,*values,1) || setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0,0)) - { - table->time_stamp=save_time_stamp; goto abort; - } value_count= values->elements; while ((values = its++)) { @@ -167,14 +164,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, ER(ER_WRONG_VALUE_COUNT_ON_ROW), MYF(0),counter); - table->time_stamp=save_time_stamp; goto abort; } if (setup_fields(thd,table_list,*values,0,0,0)) - { - table->time_stamp=save_time_stamp; goto abort; - } } its.rewind (); /* @@ -333,7 +326,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, } } thd->proc_info="end"; - table->time_stamp=save_time_stamp; // Restore auto timestamp ptr table->next_number_field=0; thd->count_cuted_fields=0; thd->next_insert_id=0; // Reset this if wrongly used @@ -1287,7 +1279,6 @@ select_insert::prepare(List<Item> &values) { DBUG_ENTER("select_insert::prepare"); - save_time_stamp=table->time_stamp; if (check_insert_fields(thd,table,*fields,values,1)) DBUG_RETURN(1); @@ -1308,8 +1299,6 @@ select_insert::~select_insert() { if (table) { - if (save_time_stamp) - table->time_stamp=save_time_stamp; table->next_number_field=0; table->file->extra(HA_EXTRA_RESET); } @@ -1412,7 +1401,6 @@ select_create::prepare(List<Item> &values) /* First field to copy */ field=table->field+table->fields - values.elements; - save_time_stamp=table->time_stamp; if (table->timestamp_field) // Don't set timestamp if used { table->timestamp_field->set_time(); diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc index 930e052ab90..6eb4fbcaaf6 100644 --- a/sql/sql_olap.cc +++ b/sql/sql_olap.cc @@ -75,7 +75,7 @@ static int make_new_olap_select(LEX *lex, SELECT_LEX *select_lex, List<Item> new !strcmp(((Item_field*)new_item)->table_name,iif->table_name) && !strcmp(((Item_field*)new_item)->field_name,iif->field_name)) { - not_found=false; + not_found= 0; ((Item_field*)new_item)->db_name=iif->db_name; Item_field *new_one=new Item_field(iif->db_name, iif->table_name, iif->field_name); privlist.push_back(new_one); @@ -151,7 +151,7 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex) if (cursor->do_redirect) { cursor->table= ((TABLE_LIST*) cursor->table)->table; - cursor->do_redirect=false; + cursor->do_redirect= 0; } } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0cebde364f7..6b918d3565b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -422,7 +422,7 @@ end: } -static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them=false) +static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) { (void) pthread_mutex_lock(&LOCK_user_conn); @@ -1826,61 +1826,29 @@ mysql_execute_command(void) select_lex->select_limit, lex->duplicates); } - else + else { - multi_update *result; - uint table_count; - TABLE_LIST *auxi; - const char *msg=0; - - lex->sql_command=SQLCOM_MULTI_UPDATE; - for (auxi=(TABLE_LIST*) tables, table_count=0 ; auxi ; auxi=auxi->next) - table_count++; + const char *msg= 0; + lex->sql_command= SQLCOM_MULTI_UPDATE; if (select_lex->order_list.elements) msg="ORDER BY"; else if (select_lex->select_limit && select_lex->select_limit != HA_POS_ERROR) msg="LIMIT"; - if (msg) { net_printf(&thd->net, ER_WRONG_USAGE, "UPDATE", msg); res= 1; break; } - tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); - if ((res=open_and_lock_tables(thd,tables))) - break; - thd->select_limit=HA_POS_ERROR; - if (!setup_fields(thd,tables,select_lex->item_list,1,0,0) && - !setup_fields(thd,tables,lex->value_list,0,0,0) && - ! thd->fatal_error && - (result=new multi_update(thd,tables,select_lex->item_list, - lex->duplicates, table_count))) - { - List <Item> total_list; - List_iterator <Item> field_list(select_lex->item_list); - List_iterator <Item> value_list(lex->value_list); - Item *item; - while ((item=field_list++)) - total_list.push_back(item); - while ((item=value_list++)) - total_list.push_back(item); - - res=mysql_select(thd,tables,total_list, - select_lex->where, - (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL, - (ORDER *)NULL, - select_lex->options | thd->options | - SELECT_NO_JOIN_CACHE, - result); - delete result; - } - else - res= -1; // Error is not sent - close_thread_tables(thd); + res= mysql_multi_update(thd,tables, + &select_lex->item_list, + &lex->value_list, + select_lex->where, + select_lex->options, + lex->duplicates); } - break; + break; case SQLCOM_INSERT: if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege)) goto error; /* purecov: inspected */ @@ -2741,11 +2709,8 @@ mysql_init_select(LEX *lex) select_lex->olap= UNSPECIFIED_OLAP_TYPE; lex->exchange = 0; lex->proc_list.first=0; - select_lex->order_list.elements=select_lex->group_list.elements=0; - select_lex->order_list.first=0; - select_lex->order_list.next= (byte**) &select_lex->order_list.first; - select_lex->group_list.first=0; - select_lex->group_list.next= (byte**) &select_lex->group_list.first; + select_lex->order_list.empty(); + select_lex->group_list.empty(); select_lex->next = (SELECT_LEX *)NULL; } @@ -2818,16 +2783,6 @@ mysql_parse(THD *thd,char *inBuf,uint length) } -inline static void -link_in_list(SQL_LIST *list,byte *element,byte **next) -{ - list->elements++; - (*list->next)=element; - list->next=next; - *next=0; -} - - /***************************************************************************** ** Store field definition for create ** Return 0 if ok @@ -3102,7 +3057,7 @@ void store_position_for_column(const char *name) } bool -add_proc_to_list(Item *item) +add_proc_to_list(THD* thd, Item *item) { ORDER *order; Item **item_ptr; @@ -3113,7 +3068,7 @@ add_proc_to_list(Item *item) *item_ptr= item; order->item=item_ptr; order->free_me=0; - link_in_list(¤t_lex->proc_list,(byte*) order,(byte**) &order->next); + thd->lex.proc_list.link_in_list((byte*) order,(byte**) &order->next); return 0; } @@ -3167,7 +3122,7 @@ bool add_to_list(SQL_LIST &list,Item *item,bool asc) order->asc = asc; order->free_me=0; order->used=0; - link_in_list(&list,(byte*) order,(byte**) &order->next); + list.link_in_list((byte*) order,(byte**) &order->next); DBUG_RETURN(0); } @@ -3248,7 +3203,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, } } } - link_in_list(&thd->lex.select->table_list,(byte*) ptr,(byte**) &ptr->next); + thd->lex.select->table_list.link_in_list((byte*) ptr,(byte**) &ptr->next); DBUG_RETURN(ptr); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a7e378420f2..a97bd8bac0a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -437,7 +437,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, if (make_join_statistics(&join,tables,conds,&keyuse) || thd->fatal_error) goto err; thd->proc_info="preparing"; - result->initialize_tables(&join); + if (result->initialize_tables(&join)) + goto err; if (join.const_table_map != join.found_const_table_map && !(select_options & SELECT_DESCRIBE)) { @@ -2721,6 +2722,38 @@ make_join_readinfo(JOIN *join,uint options) } +/* + Give error if we some tables are done with a full join + + SYNOPSIS + error_if_full_join() + join Join condition + + USAGE + This is used by multi_table_update and multi_table_delete when running + in safe mode + + RETURN VALUES + 0 ok + 1 Error (full join used) +*/ + +bool error_if_full_join(JOIN *join) +{ + for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; + tab < end; + tab++) + { + if (tab->type == JT_ALL && !tab->select->quick) + { + my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0)); + return(1); + } + } + return(0); +} + + static void join_free(JOIN *join) { @@ -3401,12 +3434,34 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) /**************************************************************************** - Create a temp table according to a field list. - Set distinct if duplicates could be removed - Given fields field pointers are changed to point at tmp_table - for send_fields + Create internal temporary table ****************************************************************************/ +/* + Create field for temporary table + + SYNOPSIS + create_tmp_field() + thd Thread handler + table Temporary table + item Item to create a field for + type Type of item (normally item->type) + copy_func If set and item is a function, store copy of item + in this array + group 1 if we are going to do a relative group by on result + modify_item 1 if item->result_field should point to new item. + This is relevent for how fill_record() is going to + work: + If modify_item is 1 then fill_record() will update + the record in the original table. + If modify_item is 0 then fill_record() will update + the temporary table + + RETURN + 0 on error + new_created field +*/ + Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item_result_field ***copy_func, Field **from_field, bool group, bool modify_item) @@ -3515,6 +3570,13 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, } +/* + Create a temp table according to a field list. + Set distinct if duplicates could be removed + Given fields field pointers are changed to point at tmp_table + for send_fields +*/ + TABLE * create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, @@ -3675,9 +3737,19 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } else { + /* + The last parameter to create_tmp_field() is a bit tricky: + + We need to set it to 0 in union, to get fill_record() to modify the + temporary table. + We need to set it to 1 on multi-table-update and in select to + write rows to the temporary table. + We here distinguish between UNION and multi-table-updates by the fact + that in the later case group is set to the row pointer. + */ Field *new_field=create_tmp_field(thd, table, item,type, ©_func, tmp_from_field, group != 0, - not_all_columns); + not_all_columns || group !=0); if (!new_field) { if (thd->fatal_error) @@ -3991,7 +4063,6 @@ static bool open_tmp_table(TABLE *table) table->db_stat=0; return(1); } - /* VOID(ha_lock(table,F_WRLCK)); */ /* Single thread table */ (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */ return(0); } diff --git a/sql/sql_select.h b/sql/sql_select.h index befa1efde53..a90b2fe3582 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -301,3 +301,4 @@ public: }; bool cp_buffer_from_ref(TABLE_REF *ref); +bool error_if_full_join(JOIN *join); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 9d20a4bd7c9..8cead6596b4 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -33,7 +33,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) TABLE *table; int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0; int res; - bool found_rows_for_union=false; + bool found_rows_for_union= 0; TABLE_LIST result_table_list; TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first; TMP_TABLE_PARAM tmp_table_param; @@ -53,7 +53,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) if (cursor->do_redirect) // False if CUBE/ROLLUP { cursor->table= ((TABLE_LIST*) cursor->table)->table; - cursor->do_redirect=false; + cursor->do_redirect= 0; } } } @@ -138,7 +138,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) res= -1; goto exit; } - union_result->save_time_stamp=!describe; + union_result->not_describe= !describe; union_result->tmp_table_param=&tmp_table_param; for (sl= &lex->select_lex; sl; sl=sl->next) { @@ -150,14 +150,17 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) if (thd->select_limit == HA_POS_ERROR) sl->options&= ~OPTION_FOUND_ROWS; - res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ? first_table : (TABLE_LIST*) sl->table_list.first, + res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ? + first_table : (TABLE_LIST*) sl->table_list.first, sl->item_list, sl->where, - (sl->braces) ? (ORDER *)sl->order_list.first : (ORDER *) 0, + (sl->braces) ? (ORDER *)sl->order_list.first : + (ORDER *) 0, (ORDER*) sl->group_list.first, sl->having, (ORDER*) NULL, - sl->options | thd->options | SELECT_NO_UNLOCK | ((describe) ? SELECT_DESCRIBE : 0), + sl->options | thd->options | SELECT_NO_UNLOCK | + ((describe) ? SELECT_DESCRIBE : 0), union_result); if (res) goto exit; @@ -226,7 +229,7 @@ exit: ***************************************************************************/ select_union::select_union(TABLE *table_par) - :table(table_par) + :table(table_par), not_describe(0) { bzero((char*) &info,sizeof(info)); /* @@ -243,7 +246,7 @@ select_union::~select_union() int select_union::prepare(List<Item> &list) { - if (save_time_stamp && list.elements != table->fields) + if (not_describe && list.elements != table->fields) { my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0)); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 97e6ea43bfd..12d0ee8d7a2 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -29,10 +29,12 @@ static bool compare_record(TABLE *table, ulong query_id) { if (!table->blob_fields) return cmp_record(table,1); + /* Compare null bits */ if (memcmp(table->null_flags, table->null_flags+table->rec_buff_length, table->null_bytes)) return 1; // Diff in NULL value + /* Compare updated fields */ for (Field **ptr=table->field ; *ptr ; ptr++) { if ((*ptr)->query_id == query_id && @@ -52,10 +54,11 @@ int mysql_update(THD *thd, ha_rows limit, enum enum_duplicates handle_duplicates) { - bool using_limit=limit != HA_POS_ERROR, safe_update= thd->options & OPTION_SAFE_UPDATES; + bool using_limit=limit != HA_POS_ERROR; + bool safe_update= thd->options & OPTION_SAFE_UPDATES; bool used_key_is_modified, transactional_table, log_delayed; int error=0; - uint save_time_stamp, used_index, want_privilege; + uint used_index, want_privilege; ulong query_id=thd->query_id, timestamp_query_id; key_map old_used_keys; TABLE *table; @@ -67,7 +70,6 @@ int mysql_update(THD *thd, if (!(table = open_ltable(thd,table_list,table_list->lock_type))) DBUG_RETURN(-1); /* purecov: inspected */ - save_time_stamp=table->time_stamp; table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); thd->proc_info="init"; @@ -89,6 +91,7 @@ int mysql_update(THD *thd, { timestamp_query_id=table->timestamp_field->query_id; table->timestamp_field->query_id=thd->query_id-1; + table->time_stamp= table->timestamp_field->offset() +1; } /* Check the fields we are going to modify */ @@ -108,7 +111,6 @@ int mysql_update(THD *thd, table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); if (setup_fields(thd,table_list,values,0,0,0)) { - table->time_stamp=save_time_stamp; // Restore timestamp pointer DBUG_RETURN(-1); /* purecov: inspected */ } @@ -119,7 +121,6 @@ int mysql_update(THD *thd, (select && select->check_quick(safe_update, limit)) || !limit) { delete select; - table->time_stamp=save_time_stamp; // Restore timestamp pointer if (error) { DBUG_RETURN(-1); // Error in where @@ -134,7 +135,6 @@ int mysql_update(THD *thd, if (safe_update && !using_limit) { delete select; - table->time_stamp=save_time_stamp; send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); DBUG_RETURN(1); } @@ -153,8 +153,8 @@ int mysql_update(THD *thd, if (used_key_is_modified || order) { /* - ** We can't update table directly; We must first search after all - ** matching rows before updating the table! + We can't update table directly; We must first search after all + matching rows before updating the table! */ table->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE); IO_CACHE tempfile; @@ -162,7 +162,6 @@ int mysql_update(THD *thd, DISK_BUFFER_SIZE, MYF(MY_WME))) { delete select; /* purecov: inspected */ - table->time_stamp=save_time_stamp; // Restore timestamp pointer /* purecov: inspected */ DBUG_RETURN(-1); } if (old_used_keys & ((key_map) 1 << used_index)) @@ -193,7 +192,6 @@ int mysql_update(THD *thd, == HA_POS_ERROR) { delete select; - table->time_stamp=save_time_stamp; // Restore timestamp pointer DBUG_RETURN(-1); } } @@ -247,7 +245,6 @@ int mysql_update(THD *thd, if (error >= 0) { delete select; - table->time_stamp=save_time_stamp; // Restore timestamp pointer DBUG_RETURN(-1); } } @@ -297,7 +294,6 @@ int mysql_update(THD *thd, end_read_record(&info); thd->proc_info="end"; VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY)); - table->time_stamp=save_time_stamp; // Restore auto timestamp pointer transactional_table= table->file->has_transactions(); log_delayed= (transactional_table || table->tmp_table); if (updated && (error <= 0 || !transactional_table)) @@ -351,324 +347,338 @@ int mysql_update(THD *thd, DBUG_RETURN(0); } + /*************************************************************************** Update multiple tables from join ***************************************************************************/ -multi_update::multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs, - enum enum_duplicates handle_duplicates, - uint num) - : update_tables (ut), thd(thd_arg), updated(0), found(0), fields(fs), - dupl(handle_duplicates), num_of_tables(num), num_fields(0), num_updated(0), - error(0), do_update(false) +/* + Setup multi-update handling and call SELECT to do the join +*/ + +int mysql_multi_update(THD *thd, + TABLE_LIST *table_list, + List<Item> *fields, + List<Item> *values, + COND *conds, + ulong options, + enum enum_duplicates handle_duplicates) { - save_time_stamps = (uint *) sql_calloc (sizeof(uint) * num_of_tables); - tmp_tables = (TABLE **)NULL; - int counter=0; - ulong timestamp_query_id; - not_trans_safe=false; - for (TABLE_LIST *dt=ut ; dt ; dt=dt->next,counter++) + int res; + multi_update *result; + TABLE_LIST *tl; + DBUG_ENTER("mysql_multi_update"); + + table_list->grant.want_privilege=(SELECT_ACL & ~table_list->grant.privilege); + if ((res=open_and_lock_tables(thd,table_list))) + DBUG_RETURN(res); + + thd->select_limit=HA_POS_ERROR; + if (setup_fields(thd, table_list, *fields, 1, 0, 0)) + DBUG_RETURN(-1); + + /* + Count tables and setup timestamp handling + */ + for (tl= (TABLE_LIST*) table_list ; tl ; tl=tl->next) { - TABLE *table=ut->table; - // (void) ut->table->file->extra(HA_EXTRA_NO_KEYREAD); - dt->table->used_keys=0; + TABLE *table= tl->table; if (table->timestamp_field) { - // Don't set timestamp column if this is modified - timestamp_query_id=table->timestamp_field->query_id; - table->timestamp_field->query_id=thd->query_id-1; - if (table->timestamp_field->query_id == thd->query_id) - table->time_stamp=0; - else - table->timestamp_field->query_id=timestamp_query_id; + table->time_stamp=0; + // Only set timestamp column if this is not modified + if (table->timestamp_field->query_id != thd->query_id) + table->time_stamp= table->timestamp_field->offset() +1; } - save_time_stamps[counter]=table->time_stamp; } - error = 1; // In case we do not reach prepare we have to reset timestamps + + if (!(result=new multi_update(thd, table_list, fields, values, + handle_duplicates))) + DBUG_RETURN(-1); + + List<Item> total_list; + res= mysql_select(thd,table_list,total_list, + conds, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL, + (ORDER *)NULL, + options | SELECT_NO_JOIN_CACHE, + result); + +end: + delete result; + DBUG_RETURN(res); } -int -multi_update::prepare(List<Item> &values) + +multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, + List<Item> *field_list, List<Item> *value_list, + enum enum_duplicates handle_duplicates_arg) + :all_tables(table_list), thd(thd_arg), tmp_tables(0), updated(0), + found(0), fields(field_list), values(value_list), table_count(0), + handle_duplicates(handle_duplicates_arg), do_update(1) +{} + + +/* + Connect fields with tables and create list of tables that are updated +*/ + +int multi_update::prepare(List<Item> ¬_used_values) { + TABLE_LIST *table_ref; + SQL_LIST update; + table_map tables_to_update= 0; + Item_field *item; + List_iterator_fast<Item> field_it(*fields); + List_iterator_fast<Item> value_it(*values); + uint i, max_fields; DBUG_ENTER("multi_update::prepare"); - do_update = true; + thd->count_cuted_fields=1; thd->cuted_fields=0L; - thd->proc_info="updating the main table"; - TABLE_LIST *table_ref; + thd->proc_info="updating main table"; + + while ((item= (Item_field *) field_it++)) + tables_to_update|= item->used_tables(); - if (thd->options & OPTION_SAFE_UPDATES) + if (!tables_to_update) { - for (table_ref=update_tables; table_ref; table_ref=table_ref->next) - { - TABLE *table=table_ref->table; - if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys) - { - my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0)); - DBUG_RETURN(1); - } - } + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "You didn't specify any tables to UPDATE"); + DBUG_RETURN(1); } + /* - Here I have to connect fields with tables and only update tables that - need to be updated. - I calculate num_updated and fill-up table_sequence - Set table_list->shared to true or false, depending on whether table is - to be updated or not + We have to check values after setup_tables to get used_keys right in + reference tables */ - Item_field *item; - List_iterator<Item> it(fields); - num_fields=fields.elements; - field_sequence = (uint *) sql_alloc(sizeof(uint)*num_fields); - uint *int_ptr=field_sequence; - while ((item= (Item_field *)it++)) - { - unsigned int counter=0; - for (table_ref=update_tables; table_ref; - table_ref=table_ref->next, counter++) - { - if (table_ref->table == item->field->table) - { - if (!table_ref->shared) - { - TABLE *tbl=table_ref->table; - num_updated++; - table_ref->shared=1; - if (!not_trans_safe && !table_ref->table->file->has_transactions()) - not_trans_safe=true; - // to be moved if initialize_tables has to be used - tbl->no_keyread=1; - tbl->used_keys=0; - } - break; - } - } - if (!table_ref) - { - net_printf(&thd->net, ER_NOT_SUPPORTED_YET, "JOIN SYNTAX WITH MULTI-TABLE UPDATES"); - DBUG_RETURN(1); - } - else - *int_ptr++=counter; - } - if (!num_updated--) - { - net_printf(&thd->net, ER_NOT_SUPPORTED_YET, "SET CLAUSE MUST CONTAIN TABLE.FIELD REFERENCE"); + if (setup_fields(thd, all_tables, *values, 1,0,0)) DBUG_RETURN(1); - } /* - Here, I have to allocate the array of temporary tables - I have to treat a case of num_updated=1 differently in send_data() method. + Save tables beeing updated in update_tables + update_table->shared is position for table + Don't use key read on tables that are updated */ - if (num_updated) + + update.empty(); + for (table_ref= all_tables; table_ref; table_ref=table_ref->next) { - tmp_tables = (TABLE **) sql_calloc(sizeof(TABLE *) * num_updated); - infos = (COPY_INFO *) sql_calloc(sizeof(COPY_INFO) * num_updated); - fields_by_tables = (List_item **)sql_calloc(sizeof(List_item *) * (num_updated + 1)); - unsigned int counter; - List<Item> *temp_fields; - for (table_ref=update_tables, counter = 0; table_ref; table_ref=table_ref->next) + TABLE *table=table_ref->table; + if (tables_to_update & table->map) { - if (!table_ref->shared) - continue; - // Here we have to add row offset as an additional field ... - if (!(temp_fields = (List_item *)sql_calloc(sizeof(List_item)))) - { - error = 1; // A proper error message is due here + TABLE_LIST *tl= (TABLE_LIST*) thd->memdup((char*) table_ref, + sizeof(*tl)); + if (!tl) DBUG_RETURN(1); - } - temp_fields->empty(); - it.rewind(); int_ptr=field_sequence; - while ((item= (Item_field *)it++)) - { - if (*int_ptr++ == counter) - temp_fields->push_back(item); - } - if (counter) - { - Field_string offset(table_ref->table->file->ref_length,false,"offset",table_ref->table,true); - temp_fields->push_front(new Item_field(((Field *)&offset))); - // Here I make tmp tables - int cnt=counter-1; - TMP_TABLE_PARAM tmp_table_param; - bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); - tmp_table_param.field_count=temp_fields->elements; - if (!(tmp_tables[cnt]=create_tmp_table(thd, &tmp_table_param, - *temp_fields, - (ORDER*) 0, 1, 0, 0, - TMP_TABLE_ALL_COLUMNS))) - { - error = 1; // A proper error message is due here - DBUG_RETURN(1); - } - tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE); - tmp_tables[cnt]->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - infos[cnt].handle_duplicates=DUP_IGNORE; - temp_fields->pop(); // because we shall use those for values only ... - } - fields_by_tables[counter]=temp_fields; - counter++; + update.link_in_list((byte*) tl, (byte**) &tl->next); + tl->shared= table_count++; + table->no_keyread=1; + table->used_keys=0; + table->pos_in_table_list= tl; } } + table_count= update.elements; + update_tables= (TABLE_LIST*) update.first; + + tmp_tables = (TABLE **) thd->calloc(sizeof(TABLE *) * table_count); + tmp_table_param = (TMP_TABLE_PARAM*) thd->calloc(sizeof(TMP_TABLE_PARAM) * + table_count); + fields_for_table= (List_item **) thd->alloc(sizeof(List_item *) * + table_count); + values_for_table= (List_item **) thd->alloc(sizeof(List_item *) * + table_count); + if (thd->fatal_error) + DBUG_RETURN(1); + for (i=0 ; i < table_count ; i++) + { + fields_for_table[i]= new List_item; + values_for_table[i]= new List_item; + } + if (thd->fatal_error) + DBUG_RETURN(1); + + /* Split fields into fields_for_table[] and values_by_table[] */ + + field_it.rewind(); + while ((item= (Item_field *) field_it++)) + { + Item *value= value_it++; + uint offset= item->field->table->pos_in_table_list->shared; + fields_for_table[offset]->push_back(item); + values_for_table[offset]->push_back(value); + } + if (thd->fatal_error) + DBUG_RETURN(1); + + /* Allocate copy fields */ + max_fields=0; + for (i=0 ; i < table_count ; i++) + set_if_bigger(max_fields, fields_for_table[i]->elements); + copy_field= new Copy_field[max_fields]; init_ftfuncs(thd,1); - error = 0; // Timestamps do not need to be restored, so far ... - DBUG_RETURN(0); + DBUG_RETURN(thd->fatal_error != 0); } -void +/* + Store first used table in main_table as this should be updated first + This is because we know that no row in this table will be read twice. + + Create temporary tables to store changed values for all other tables + that are updated. +*/ + +bool multi_update::initialize_tables(JOIN *join) { -#ifdef NOT_YET - We skip it as it only makes a mess ........... - TABLE_LIST *walk; - table_map tables_to_update_from=0; - for (walk= update_tables ; walk ; walk=walk->next) - tables_to_update_from|= walk->table->map; - - walk= update_tables; - for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; - tab < end; - tab++) + TABLE_LIST *table_ref; + DBUG_ENTER("initialize_tables"); + + if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join)) + DBUG_RETURN(1); + main_table=join->join_tab->table; + trans_safe= transactional_tables= main_table->file->has_transactions(); + log_delayed= trans_safe || main_table->tmp_table != NO_TMP_TABLE; + + /* Create a temporary table for all tables after except main table */ + for (table_ref= update_tables; table_ref; table_ref=table_ref->next) { - if (tab->table->map & tables_to_update_from) + TABLE *table=table_ref->table; + if (table != main_table) { -// We are going to update from this table - TABLE *tbl=walk->table=tab->table; - /* Don't use KEYREAD optimization on this table */ - tbl->no_keyread=1; - walk=walk->next; + uint cnt= table_ref->shared; + ORDER group; + List<Item> temp_fields= *fields_for_table[cnt]; + TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt; + + /* + Create a temporary table to store all fields that are changed for this + table. The first field in the temporary table is a pointer to the + original row so that we can find and update it + */ + + /* ok to be on stack as this is not referenced outside of this func */ + Field_string offset(table->file->ref_length, 0, "offset", + table, 1); + if (temp_fields.push_front(new Item_field(((Field *) &offset)))) + DBUG_RETURN(1); + + /* Make an unique key over the first field to avoid duplicated updates */ + bzero((char*) &group, sizeof(group)); + group.asc= 1; + group.item= (Item**) temp_fields.head_ref(); + + tmp_param->quick_group=1; + tmp_param->field_count=temp_fields.elements; + tmp_param->group_parts=1; + tmp_param->group_length= table->file->ref_length; + if (!(tmp_tables[cnt]=create_tmp_table(thd, + tmp_param, + temp_fields, + (ORDER*) &group, 0, 0, 0, + TMP_TABLE_ALL_COLUMNS))) + DBUG_RETURN(1); + tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE); } } -#endif + DBUG_RETURN(0); } multi_update::~multi_update() { - int counter = 0; - for (table_being_updated=update_tables ; - table_being_updated ; - counter++, table_being_updated=table_being_updated->next) - { - TABLE *table=table_being_updated->table; - table->no_keyread=0; - if (error) - table->time_stamp=save_time_stamps[counter]; - } + TABLE_LIST *table; + for (table= update_tables ; table; table= table->next) + table->table->no_keyread=0; + if (tmp_tables) - for (uint counter = 0; counter < num_updated; counter++) + { + for (uint counter = 0; counter < table_count; counter++) if (tmp_tables[counter]) free_tmp_table(thd,tmp_tables[counter]); + } + if (copy_field) + delete [] copy_field; + thd->count_cuted_fields=0; // Restore this setting + if (!trans_safe) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } -bool multi_update::send_data(List<Item> &values) +bool multi_update::send_data(List<Item> ¬_used_values) { - List<Item> real_values(values); - for (uint counter = 0; counter < fields.elements; counter++) - real_values.pop(); - // We have skipped fields .... - if (!num_updated) + TABLE_LIST *cur_table; + DBUG_ENTER("multi_update::send_data"); + + found++; + for (cur_table= update_tables; cur_table ; cur_table= cur_table->next) { - for (table_being_updated=update_tables ; - table_being_updated ; - table_being_updated=table_being_updated->next) + TABLE *table= cur_table->table; + /* Check if we are using outer join and we didn't find the row */ + if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) + continue; + + uint offset= cur_table->shared; + table->file->position(table->record[0]); + if (table == main_table) { - if (!table_being_updated->shared) - continue; - TABLE *table=table_being_updated->table; - /* Check if we are using outer join and we didn't find the row */ - if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) - return 0; - table->file->position(table->record[0]); - // Only one table being updated receives a completely different treatment table->status|= STATUS_UPDATED; - store_record(table,1); - if (fill_record(fields,real_values)) - return 1; - found++; - if (/* compare_record(table, query_id) && */ - !(error=table->file->update_row(table->record[1], table->record[0]))) - updated++; - table->file->extra(HA_EXTRA_NO_CACHE); - return error; - } - } - else - { - int secure_counter= -1; - for (table_being_updated=update_tables ; - table_being_updated ; - table_being_updated=table_being_updated->next, secure_counter++) - { - if (!table_being_updated->shared) - continue; - - TABLE *table=table_being_updated->table; - /* Check if we are using outer join and we didn't find the row */ - if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) - continue; - table->file->position(table->record[0]); - Item *item; - List_iterator<Item> it(real_values); - List <Item> values_by_table; - uint *int_ptr=field_sequence; - while ((item= (Item *)it++)) - { - if (*int_ptr++ == (uint) (secure_counter + 1)) - values_by_table.push_back(item); - } - // Here I am breaking values as per each table - if (secure_counter < 0) + store_record(table,1); + if (fill_record(*fields_for_table[offset], *values_for_table[offset])) + DBUG_RETURN(1); + if (compare_record(table, thd->query_id)) { - table->status|= STATUS_UPDATED; - store_record(table,1); - if (fill_record(*fields_by_tables[0],values_by_table)) - return 1; - found++; - if (/*compare_record(table, query_id) && */ - !(error=table->file->update_row(table->record[1], table->record[0]))) + int error; + if (!updated++) { - updated++; - table->file->extra(HA_EXTRA_NO_CACHE); + /* + Inform the main table that we are going to update the table even + while we may be scanning it. This will flush the read cache + if it's used. + */ + main_table->file->extra(HA_EXTRA_PREPARE_FOR_UPDATE); } - else + if ((error=table->file->update_row(table->record[1], + table->record[0]))) { table->file->print_error(error,MYF(0)); - if (!error) error=1; - return 1; + updated--; + DBUG_RETURN(1); } } - else + } + else + { + int error; + TABLE *tmp_table= tmp_tables[offset]; + fill_record(tmp_table->field+1, *values_for_table[offset]); + + /* Store pointer to row */ + memcpy((char*) tmp_table->field[0]->ptr, + (char*) table->file->ref, table->file->ref_length); + /* Write row, ignoring duplicated updates to a row */ + if ((error= tmp_table->file->write_row(tmp_table->record[0])) && + (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE)) { - // Here we insert into each temporary table - values_by_table.push_front(new Item_string((char*) table->file->ref, - table->file->ref_length)); - fill_record(tmp_tables[secure_counter]->field,values_by_table); - error= write_record(tmp_tables[secure_counter], - &(infos[secure_counter])); - if (error) + if (create_myisam_from_heap(table, tmp_table_param + offset, error, 1)) { - error=-1; - return 1; + do_update=0; + DBUG_RETURN(1); // Not a table_is_full error } } } } - return 0; + DBUG_RETURN(0); } + void multi_update::send_error(uint errcode,const char *err) { /* First send error what ever it is ... */ ::send_error(&thd->net,errcode,err); - /* reset used flags */ - // update_tables->table->no_keyread=0; - /* If nothing updated return */ if (!updated) return; @@ -676,97 +686,124 @@ void multi_update::send_error(uint errcode,const char *err) /* Something already updated so we have to invalidate cache */ query_cache_invalidate3(thd, update_tables, 1); - /* Below can happen when thread is killed early ... */ - if (!table_being_updated) - table_being_updated=update_tables; - /* - If rows from the first table only has been updated and it is transactional, - just do rollback. - The same if all tables are transactional, regardless of where we are. - In all other cases do attempt updates ... + If all tables that has been updated are trans safe then just do rollback. + If not attempt to do remaining updates. */ - if ((table_being_updated->table->file->has_transactions() && - table_being_updated == update_tables) || !not_trans_safe) + + if (trans_safe) ha_rollback_stmt(thd); - else if (do_update && num_updated) - VOID(do_updates(true)); + else if (do_update && table_count > 1) + { + /* Add warning here */ + VOID(do_updates(0)); + } } -int multi_update::do_updates (bool from_send_error) +int multi_update::do_updates(bool from_send_error) { - int local_error= 0, counter= 0; - - if (from_send_error) + TABLE_LIST *cur_table; + int local_error; + ha_rows org_updated; + TABLE *table; + DBUG_ENTER("do_updates"); + + do_update= 0; // Don't retry this function + for (cur_table= update_tables; cur_table ; cur_table= cur_table->next) { - /* Found out table number for 'table_being_updated' */ - for (TABLE_LIST *aux=update_tables; - aux != table_being_updated; - aux=aux->next) - counter++; - } - else - table_being_updated = update_tables; - - do_update = false; - for (table_being_updated=table_being_updated->next; - table_being_updated ; - table_being_updated=table_being_updated->next, counter++) - { - if (!table_being_updated->shared) - continue; + table = cur_table->table; + if (table == main_table) + continue; // Already updated - TABLE *table = table_being_updated->table; - TABLE *tmp_table=tmp_tables[counter]; - if (tmp_table->file->extra(HA_EXTRA_NO_CACHE)) - { - local_error=1; - break; - } - List<Item> list; - Field **ptr=tmp_table->field,*field; - // This is supposed to be something like insert_fields - thd->used_tables|=tmp_table->map; - while ((field = *ptr++)) + org_updated= updated; + byte *ref_pos; + TABLE *tmp_table= tmp_tables[cur_table->shared]; + tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache + table->file->extra(HA_EXTRA_NO_CACHE); + + /* + Setup copy functions to copy fields from temporary table + */ + List_iterator_fast<Item> field_it(*fields_for_table[cur_table->shared]); + Field **field= tmp_table->field+1; // Skip row pointer + Copy_field *copy_field_ptr= copy_field, *copy_field_end; + for ( ; *field ; field++) { - list.push_back((Item *)new Item_field(field)); - if (field->query_id == thd->query_id) - thd->dupp_field=field; - field->query_id=thd->query_id; - tmp_table->used_keys&=field->part_of_key; + Item_field *item= (Item_field* ) field_it++; + (copy_field_ptr++)->set(item->field, *field, 0); } - tmp_table->used_fields=tmp_table->fields; - local_error=0; - list.pop(); // we get position some other way ... - local_error = tmp_table->file->rnd_init(1); - if (local_error) - return local_error; - while (!(local_error=tmp_table->file->rnd_next(tmp_table->record[0])) && - (!thd->killed || from_send_error || not_trans_safe)) + copy_field_end=copy_field_ptr; + + if ((local_error = tmp_table->file->rnd_init(1))) + goto err; + + ref_pos= (byte*) tmp_table->field[0]->ptr; + for (;;) { - found++; - local_error= table->file->rnd_pos(table->record[0], - (byte*) (*(tmp_table->field))->ptr); - if (local_error) - return local_error; + if (thd->killed && trans_safe) + goto err; + if ((local_error=tmp_table->file->rnd_next(tmp_table->record[0]))) + { + if (local_error == HA_ERR_END_OF_FILE) + break; + if (local_error == HA_ERR_RECORD_DELETED) + continue; // May happen on dup key + goto err; + } + found++; + if ((local_error= table->file->rnd_pos(table->record[0], ref_pos))) + goto err; table->status|= STATUS_UPDATED; - store_record(table,1); - local_error= (fill_record(*fields_by_tables[counter + 1],list) || - /* compare_record(table, query_id) || */ - table->file->update_row(table->record[1],table->record[0])); - if (local_error) + store_record(table,1); + + /* Copy data from temporary table to current table */ + for (copy_field_ptr=copy_field; + copy_field_ptr != copy_field_end; + copy_field_ptr++) + (*copy_field_ptr->do_copy)(copy_field_ptr); + + if (compare_record(table, thd->query_id)) { - table->file->print_error(local_error,MYF(0)); - break; + if ((local_error=table->file->update_row(table->record[1], + table->record[0]))) + { + if (local_error != HA_ERR_FOUND_DUPP_KEY || + handle_duplicates != DUP_IGNORE) + goto err; + } + updated++; + if (table->tmp_table != NO_TMP_TABLE) + log_delayed= 1; } + } + + if (updated != org_updated) + { + if (table->tmp_table != NO_TMP_TABLE) + log_delayed= 1; // Tmp tables forces delay log + if (table->file->has_transactions()) + log_delayed= transactional_tables= 1; else - updated++; + trans_safe= 0; // Can't do safe rollback } - if (local_error == HA_ERR_END_OF_FILE) - local_error = 0; } - return local_error; + DBUG_RETURN(0); + +err: + if (!from_send_error) + table->file->print_error(local_error,MYF(0)); + + if (updated != org_updated) + { + if (table->tmp_table != NO_TMP_TABLE) + log_delayed= 1; + if (table->file->has_transactions()) + log_delayed= transactional_tables= 1; + else + trans_safe= 0; + } + DBUG_RETURN(1); } @@ -774,60 +811,57 @@ int multi_update::do_updates (bool from_send_error) bool multi_update::send_eof() { - thd->proc_info="updating the reference tables"; + char buff[80]; + thd->proc_info="updating reference tables"; /* Does updates for the last n - 1 tables, returns 0 if ok */ - int local_error = (num_updated) ? do_updates(false) : 0; - - /* reset used flags */ -#ifndef NOT_USED - update_tables->table->no_keyread=0; -#endif - if (local_error == -1) - local_error= 0; + int local_error = (table_count) ? do_updates(0) : 0; thd->proc_info= "end"; - if (local_error) - send_error(local_error, "An error occured in multi-table update"); /* Write the SQL statement to the binlog if we updated - rows and we succeeded, or also in an error case when there - was a non-transaction-safe table involved, since - modifications in it cannot be rolled back. + rows and we succeeded or if we updated some non + transacational tables */ - if (updated || not_trans_safe) + if (updated && (local_error <= 0 || !trans_safe)) { mysql_update_log.write(thd,thd->query,thd->query_length); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0); - - /* - mysql_bin_log is not open if binlogging or replication - is not used - */ + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, + log_delayed); + if (mysql_bin_log.write(&qinfo) && trans_safe) + local_error=1; // Rollback update + } + if (!log_delayed) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; + } - if (mysql_bin_log.is_open() && mysql_bin_log.write(&qinfo) && - !not_trans_safe) - local_error=1; /* Log write failed: roll back the SQL statement */ + if (transactional_tables) + { + if (ha_autocommit_or_rollback(thd, local_error >= 0)) + local_error=1; + } - /* Commit or rollback the current SQL statement */ - VOID(ha_autocommit_or_rollback(thd, local_error > 0)); + if (local_error > 0) // if the above log write did not fail ... + { + /* Safety: If we haven't got an error before (should not happen) */ + my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update", + MYF(0)); + ::send_error(&thd->net); + return 1; } - else - local_error= 0; // this can happen only if it is end of file error - if (!local_error) // if the above log write did not fail ... + + + sprintf(buff,ER(ER_UPDATE_INFO), (long) found, (long) updated, + (long) thd->cuted_fields); + if (updated) { - char buff[80]; - sprintf(buff,ER(ER_UPDATE_INFO), (long) found, (long) updated, - (long) thd->cuted_fields); - if (updated) - { - query_cache_invalidate3(thd, update_tables, 1); - } - ::send_ok(&thd->net, - (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + query_cache_invalidate3(thd, update_tables, 1); } - thd->count_cuted_fields=0; + ::send_ok(&thd->net, + (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, + thd->insert_id_used ? thd->insert_id() : 0L,buff); return 0; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c3c6d8ad66b..807097f5173 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1436,9 +1436,9 @@ select: select_init { Lex->sql_command=SQLCOM_SELECT; }; select_init: - SELECT_SYM select_part2 { Select->braces=false; } opt_union + SELECT_SYM select_part2 { Select->braces= 0; } opt_union | - '(' SELECT_SYM select_part2 ')' { Select->braces=true;} union_opt; + '(' SELECT_SYM select_part2 ')' { Select->braces= 1;} union_opt; select_part2: @@ -2350,7 +2350,7 @@ procedure_clause: lex->proc_list.elements=0; lex->proc_list.first=0; lex->proc_list.next= (byte**) &lex->proc_list.first; - if (add_proc_to_list(new Item_field(NULL,NULL,$2.str))) + if (add_proc_to_list(lex->thd, new Item_field(NULL,NULL,$2.str))) YYABORT; current_thd->safe_to_cache_query=0; } @@ -2368,10 +2368,11 @@ procedure_list2: procedure_item: remember_name expr { - if (add_proc_to_list($2)) + LEX *lex= Lex; + if (add_proc_to_list(lex->thd, $2)) YYABORT; if (!$2->name) - $2->set_name($1,(uint) ((char*) Lex->tok_end - $1)); + $2->set_name($1,(uint) ((char*) lex->tok_end - $1)); }; opt_into: diff --git a/sql/table.h b/sql/table.h index f998a0fd4e6..3a08cd11a2a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -117,18 +117,22 @@ struct st_table { table_map map; /* ID bit of table (1,2,4,8,16...) */ ulong version,flush_version; uchar *null_flags; - IO_CACHE *io_cache; /* If sorted trough file*/ - byte *record_pointers; /* If sorted in memory */ - ha_rows found_records; /* How many records in sort */ + IO_CACHE *io_cache; /* If sorted trough file*/ + byte *record_pointers; /* If sorted in memory */ + ha_rows found_records; /* How many records in sort */ ORDER *group; ha_rows quick_rows[MAX_KEY]; uint quick_key_parts[MAX_KEY]; key_part_map const_key_parts[MAX_KEY]; ulong query_id; - uint temp_pool_slot; + union /* Temporary variables */ + { + uint temp_pool_slot; /* Used by intern temp tables */ + struct st_table_list *pos_in_table_list; + }; - THD *in_use; /* Which thread uses this */ + THD *in_use; /* Which thread uses this */ struct st_table *next,*prev; }; @@ -148,10 +152,10 @@ typedef struct st_table_list GRANT_INFO grant; thr_lock_type lock_type; uint outer_join; /* Which join type */ + uint shared; /* Used in union or in multi-upd */ uint32 db_length, real_name_length; bool straight; /* optimize with prev table */ bool updating; /* for replicate-do/ignore table */ - bool shared; /* Used twice in union */ bool do_redirect; /* To get the struct in UNION's */ } TABLE_LIST; diff --git a/sql/uniques.cc b/sql/uniques.cc index 60905567ba0..ed256a4b791 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -53,7 +53,8 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, :max_in_memory_size(max_in_memory_size_arg),elements(0) { my_b_clear(&file); - init_tree(&tree, max_in_memory_size / 16, 0, size, comp_func, 0, NULL, comp_func_fixed_arg); + init_tree(&tree, max_in_memory_size / 16, 0, size, comp_func, 0, NULL, + comp_func_fixed_arg); /* If the following fail's the next add will also fail */ my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16); max_elements= max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size); |