New multi-table-update code
New (simpler) internal timestamp handling. More debuging to heap tables. Small cleanups to multi-table-delete false -> 0 and true -> 1 (We should use TRUE and FALSE) heap/_check.c: Added checking of rows heap/hp_delete.c: Extra debugging heap/hp_scan.c: Extra debugging heap/hp_update.c: Extra debugging heap/hp_write.c: Extra debugging include/my_base.h: Added option to disable row cache when using updates isam/extra.c: Added option to disable row cache when using updates myisam/mi_check.c: Comment cleanup myisam/mi_extra.c: Added option to disable row cache when using updates myisam/sort.c: Indentaion cleanups myisammrg/myrg_extra.c: Added option to disable row cache when using updates mysql-test/r/multi_update.result: Updated results mysql-test/t/multi_update.test: Cleanup up to only use table names t1, t2,... mysys/mf_iocache.c: Safety fix sql/item_cmpfunc.cc: change true-> 1 and false -> 0 sql/mysql_priv.h: Cleaned up SQL_LIST handling sql/sql_base.cc: Fixed grant checking if SELECT tablename.* sql/sql_class.h: Cleaned up multi-table-update sql/sql_delete.cc: Fixed OPTION_SAFE_UPDATE checking in multi-table-delete. Fixed query-cache invalidation in multi-table-delete sql/sql_insert.cc: cleaned up timestamp handling sql/sql_olap.cc: false -> 0 sql/sql_parse.cc: Optimized some list handling. Moved multi-table-update to sql_update.cc sql/sql_select.cc: More comments Fixed create_tmp_table for multi-table-update sql/sql_select.h: New prototypes sql/sql_union.cc: false -> 0 Cleaned up timestamp handling sql/sql_update.cc: New multi-update-table code sql/sql_yacc.yy: false -> 0, true -> 1 Optimized some list handling sql/table.h: Added union for temporary values. Made shared int to be able to store counters. sql/uniques.cc: Indentation cleanup
This commit is contained in:
parent
7d2d7e3fea
commit
4653621909
@ -21,19 +21,70 @@
|
|||||||
static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records,
|
static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records,
|
||||||
ulong blength, my_bool print_status);
|
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 heap_check_heap(HP_INFO *info,my_bool print_status)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
uint key;
|
uint key;
|
||||||
|
ulong records=0, deleted=0, pos, next_block;
|
||||||
HP_SHARE *share=info->s;
|
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,
|
error|=check_one_key(share->keydef+key,key, share->records,share->blength,
|
||||||
print_status);
|
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);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,10 @@ int heap_delete(HP_INFO *info, const byte *record)
|
|||||||
pos[share->reclength]=0; /* Record deleted */
|
pos[share->reclength]=0; /* Record deleted */
|
||||||
share->deleted++;
|
share->deleted++;
|
||||||
info->current_hash_ptr=0;
|
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);
|
DBUG_RETURN(0);
|
||||||
err:
|
err:
|
||||||
if (++(share->records) == share->blength)
|
if (++(share->records) == share->blength)
|
||||||
|
@ -62,6 +62,7 @@ int heap_scan(register HP_INFO *info, byte *record)
|
|||||||
}
|
}
|
||||||
if (!info->current_ptr[share->reclength])
|
if (!info->current_ptr[share->reclength])
|
||||||
{
|
{
|
||||||
|
DBUG_PRINT("warning",("Found deleted record"));
|
||||||
info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
|
info->update= HA_STATE_PREV_FOUND | HA_STATE_NEXT_FOUND;
|
||||||
DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
|
DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
memcpy(pos,heap_new,(size_t) share->reclength);
|
||||||
if (++(share->records) == share->blength) share->blength+= share->blength;
|
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);
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
@ -60,7 +60,11 @@ int heap_write(HP_INFO *info, const byte *record)
|
|||||||
info->current_ptr=pos;
|
info->current_ptr=pos;
|
||||||
info->current_hash_ptr=0;
|
info->current_hash_ptr=0;
|
||||||
info->update|=HA_STATE_AKTIV;
|
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);
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
DBUG_PRINT("info",("Duplicate key: %d",key));
|
DBUG_PRINT("info",("Duplicate key: %d",key));
|
||||||
info->errkey= key;
|
info->errkey= key;
|
||||||
@ -74,6 +78,7 @@ err:
|
|||||||
*((byte**) pos)=share->del_link;
|
*((byte**) pos)=share->del_link;
|
||||||
share->del_link=pos;
|
share->del_link=pos;
|
||||||
pos[share->reclength]=0; /* Record deleted */
|
pos[share->reclength]=0; /* Record deleted */
|
||||||
|
|
||||||
DBUG_RETURN(my_errno);
|
DBUG_RETURN(my_errno);
|
||||||
} /* heap_write */
|
} /* heap_write */
|
||||||
|
|
||||||
|
@ -109,7 +109,8 @@ enum ha_extra_function {
|
|||||||
HA_EXTRA_BULK_INSERT_BEGIN,
|
HA_EXTRA_BULK_INSERT_BEGIN,
|
||||||
HA_EXTRA_BULK_INSERT_FLUSH, /* Flush one index */
|
HA_EXTRA_BULK_INSERT_FLUSH, /* Flush one index */
|
||||||
HA_EXTRA_BULK_INSERT_END,
|
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() */
|
/* The following is parameter to ha_panic() */
|
||||||
|
@ -123,6 +123,7 @@ int nisam_extra(N_INFO *info, enum ha_extra_function function)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!(info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)))
|
if (!(info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)))
|
||||||
|
{
|
||||||
if (!(init_io_cache(&info->rec_cache,info->dfile,0,
|
if (!(init_io_cache(&info->rec_cache,info->dfile,0,
|
||||||
WRITE_CACHE,info->s->state.data_file_length,
|
WRITE_CACHE,info->s->state.data_file_length,
|
||||||
(pbool) (info->lock_type != F_UNLCK),
|
(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->opt_flag|=WRITE_CACHE_USED;
|
||||||
info->update&= ~HA_STATE_ROW_CHANGED;
|
info->update&= ~HA_STATE_ROW_CHANGED;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
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:
|
case HA_EXTRA_NO_CACHE:
|
||||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||||
{
|
{
|
||||||
|
@ -2329,13 +2329,13 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
|
|||||||
for (i=0 ; i < sort_info.total_keys ; i++)
|
for (i=0 ; i < sort_info.total_keys ; i++)
|
||||||
{
|
{
|
||||||
sort_param[i].read_cache=param->read_cache;
|
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=
|
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
|
#ifndef USING_SECOND_APPROACH
|
||||||
param->sort_buffer_length/sort_info.total_keys;
|
param->sort_buffer_length/sort_info.total_keys;
|
||||||
#else
|
#else
|
||||||
|
@ -165,6 +165,10 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
|
|||||||
HA_STATE_EXTEND_BLOCK);
|
HA_STATE_EXTEND_BLOCK);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case HA_EXTRA_NO_CACHE:
|
||||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||||
{
|
{
|
||||||
|
@ -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 */
|
mi_check_print_error(info->sort_info->param,"Sort buffer to small"); /* purecov: tested */
|
||||||
goto err; /* 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)
|
if (info->sort_info->param->testflag & T_VERBOSE)
|
||||||
printf("Key %d - Allocating buffer for %d keys\n",info->key+1,keys);
|
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;
|
byte *mergebuf=0;
|
||||||
LINT_INIT(length);
|
LINT_INIT(length);
|
||||||
|
|
||||||
for (i=0, sinfo=sort_param ; i<sort_info->total_keys ; i++,
|
for (i= 0, sinfo= sort_param ;
|
||||||
rec_per_key_part+=sinfo->keyinfo->keysegs,
|
i < sort_info->total_keys ;
|
||||||
sinfo++)
|
i++, rec_per_key_part+=sinfo->keyinfo->keysegs, sinfo++)
|
||||||
{
|
{
|
||||||
if (!sinfo->sort_keys)
|
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((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;
|
sinfo->sort_keys=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0, sinfo=sort_param ; i<sort_info->total_keys ; i++,
|
for (i= 0, sinfo= sort_param ;
|
||||||
delete_dynamic(&sinfo->buffpek),
|
i < sort_info->total_keys ;
|
||||||
close_cached_file(&sinfo->tempfile),
|
i++,
|
||||||
close_cached_file(&sinfo->tempfile_for_exceptions),
|
delete_dynamic(&sinfo->buffpek),
|
||||||
sinfo++)
|
close_cached_file(&sinfo->tempfile),
|
||||||
|
close_cached_file(&sinfo->tempfile_for_exceptions),
|
||||||
|
sinfo++)
|
||||||
{
|
{
|
||||||
if (got_error)
|
if (got_error)
|
||||||
continue;
|
continue;
|
||||||
|
@ -38,7 +38,8 @@ int myrg_extra(MYRG_INFO *info,enum ha_extra_function function,
|
|||||||
}
|
}
|
||||||
else
|
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;
|
info->cache_in_use=0;
|
||||||
if (function == HA_EXTRA_RESET || function == HA_EXTRA_RESET_STATE)
|
if (function == HA_EXTRA_RESET || function == HA_EXTRA_RESET_STATE)
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@ count(*)
|
|||||||
10
|
10
|
||||||
select count(*) from t2 where t = "bbb";
|
select count(*) from t2 where t = "bbb";
|
||||||
count(*)
|
count(*)
|
||||||
10
|
50
|
||||||
select count(*) from t2 where id2 > 90;
|
select count(*) from t2 where id2 > 90;
|
||||||
count(*)
|
count(*)
|
||||||
50
|
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;
|
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;
|
delete t1 from t1,t2 where t1.id1 = t2.id2 and t1.id1 > 500;
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
DROP TABLE IF EXISTS a,b,c;
|
CREATE TABLE t1 (
|
||||||
CREATE TABLE a (
|
|
||||||
id int(11) NOT NULL default '0',
|
id int(11) NOT NULL default '0',
|
||||||
name varchar(10) default NULL,
|
name varchar(10) default NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
INSERT INTO a VALUES (1,'aaa'),(2,'aaa'),(3,'aaa');
|
INSERT INTO t1 VALUES (1,'aaa'),(2,'aaa'),(3,'aaa');
|
||||||
CREATE TABLE b (
|
CREATE TABLE t2 (
|
||||||
id int(11) NOT NULL default '0',
|
id int(11) NOT NULL default '0',
|
||||||
name varchar(10) default NULL,
|
name varchar(10) default NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
INSERT INTO b VALUES (2,'bbb'),(3,'bbb'),(4,'bbb');
|
INSERT INTO t2 VALUES (2,'bbb'),(3,'bbb'),(4,'bbb');
|
||||||
CREATE TABLE c (
|
CREATE TABLE t3 (
|
||||||
id int(11) NOT NULL default '0',
|
id int(11) NOT NULL default '0',
|
||||||
mydate datetime default NULL,
|
mydate datetime default NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
) TYPE=MyISAM;
|
) 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');
|
00:00:00'),(7,'2002-07-22 00:00:00');
|
||||||
delete a,b,c from a,b,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;
|
||||||
where to_days(now())-to_days(c.mydate)>=30
|
select * from t3;
|
||||||
and c.id=a.id and c.id=b.id;
|
|
||||||
select * from c;
|
|
||||||
id mydate
|
id mydate
|
||||||
1 2002-02-04 00:00:00
|
1 2002-02-04 00:00:00
|
||||||
5 2002-05-12 00:00:00
|
5 2002-05-12 00:00:00
|
||||||
6 2002-06-22 00:00:00
|
6 2002-06-22 00:00:00
|
||||||
7 2002-07-22 00:00:00
|
7 2002-07-22 00:00:00
|
||||||
DROP TABLE IF EXISTS a,b,c;
|
DROP TABLE IF EXISTS t1,t2,t3;
|
||||||
drop table if exists parent, child;
|
CREATE TABLE IF NOT EXISTS `t1` (
|
||||||
CREATE TABLE IF NOT EXISTS `parent` (
|
|
||||||
`id` int(11) NOT NULL auto_increment,
|
`id` int(11) NOT NULL auto_increment,
|
||||||
`tst` text,
|
`tst` text,
|
||||||
`tst1` text,
|
`tst1` text,
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
CREATE TABLE IF NOT EXISTS `child` (
|
CREATE TABLE IF NOT EXISTS `t2` (
|
||||||
`ID` int(11) NOT NULL auto_increment,
|
`ID` int(11) NOT NULL auto_increment,
|
||||||
`ParId` int(11) default NULL,
|
`ParId` int(11) default NULL,
|
||||||
`tst` text,
|
`tst` text,
|
||||||
`tst1` text,
|
`tst1` text,
|
||||||
PRIMARY KEY (`ID`),
|
PRIMARY KEY (`ID`),
|
||||||
KEY `IX_ParId_child` (`ParId`),
|
KEY `IX_ParId_t2` (`ParId`),
|
||||||
FOREIGN KEY (`ParId`) REFERENCES `test.parent` (`id`)
|
FOREIGN KEY (`ParId`) REFERENCES `t1` (`id`)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
INSERT INTO parent(tst,tst1)
|
INSERT INTO t1(tst,tst1) VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE");
|
||||||
VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE");
|
INSERT INTO t2(ParId) VALUES(1), (2), (3);
|
||||||
INSERT INTO child(ParId)
|
select * from t2;
|
||||||
VALUES(1), (2), (3);
|
|
||||||
select * from child;
|
|
||||||
ID ParId tst tst1
|
ID ParId tst tst1
|
||||||
1 1 NULL NULL
|
1 1 NULL NULL
|
||||||
2 2 NULL NULL
|
2 2 NULL NULL
|
||||||
3 3 NULL NULL
|
3 3 NULL NULL
|
||||||
UPDATE child, parent
|
UPDATE t2, t1 SET t2.tst = t1.tst, t2.tst1 = t1.tst1 WHERE t2.ParId = t1.Id;
|
||||||
SET child.tst = parent.tst,
|
select * from t2;
|
||||||
child.tst1 = parent.tst1
|
|
||||||
WHERE child.ParId = parent.Id;
|
|
||||||
select * from child;
|
|
||||||
ID ParId tst tst1
|
ID ParId tst tst1
|
||||||
1 1 MySQL MySQL AB
|
1 1 MySQL MySQL AB
|
||||||
2 2 MSSQL Microsoft
|
2 2 MSSQL Microsoft
|
||||||
3 3 ORACLE ORACLE
|
3 3 ORACLE ORACLE
|
||||||
drop table parent, child;
|
|
||||||
drop table if exists t1, t2 ;
|
drop table if exists t1, t2 ;
|
||||||
create table t1 (n numeric(10));
|
create table t1 (n numeric(10));
|
||||||
create table t2 (n numeric(10));
|
create table t2 (n numeric(10));
|
||||||
|
@ -80,67 +80,59 @@ while ($1)
|
|||||||
enable_query_log;
|
enable_query_log;
|
||||||
delete t1 from t1,t2 where t1.id1 = t2.id2 and t1.id1 > 500;
|
delete t1 from t1,t2 where t1.id1 = t2.id2 and t1.id1 > 500;
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
DROP TABLE IF EXISTS a,b,c;
|
|
||||||
CREATE TABLE a (
|
CREATE TABLE t1 (
|
||||||
id int(11) NOT NULL default '0',
|
id int(11) NOT NULL default '0',
|
||||||
name varchar(10) default NULL,
|
name varchar(10) default NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
INSERT INTO a VALUES (1,'aaa'),(2,'aaa'),(3,'aaa');
|
INSERT INTO t1 VALUES (1,'aaa'),(2,'aaa'),(3,'aaa');
|
||||||
CREATE TABLE b (
|
CREATE TABLE t2 (
|
||||||
id int(11) NOT NULL default '0',
|
id int(11) NOT NULL default '0',
|
||||||
name varchar(10) default NULL,
|
name varchar(10) default NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
INSERT INTO b VALUES (2,'bbb'),(3,'bbb'),(4,'bbb');
|
INSERT INTO t2 VALUES (2,'bbb'),(3,'bbb'),(4,'bbb');
|
||||||
CREATE TABLE c (
|
CREATE TABLE t3 (
|
||||||
id int(11) NOT NULL default '0',
|
id int(11) NOT NULL default '0',
|
||||||
mydate datetime default NULL,
|
mydate datetime default NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
) TYPE=MyISAM;
|
) 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');
|
00:00:00'),(7,'2002-07-22 00:00:00');
|
||||||
delete a,b,c from a,b,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;
|
||||||
where to_days(now())-to_days(c.mydate)>=30
|
select * from t3;
|
||||||
and c.id=a.id and c.id=b.id;
|
DROP TABLE IF EXISTS t1,t2,t3;
|
||||||
select * from c;
|
|
||||||
DROP TABLE IF EXISTS a,b,c;
|
CREATE TABLE IF NOT EXISTS `t1` (
|
||||||
drop table if exists parent, child;
|
|
||||||
CREATE TABLE IF NOT EXISTS `parent` (
|
|
||||||
`id` int(11) NOT NULL auto_increment,
|
`id` int(11) NOT NULL auto_increment,
|
||||||
`tst` text,
|
`tst` text,
|
||||||
`tst1` text,
|
`tst1` text,
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `child` (
|
CREATE TABLE IF NOT EXISTS `t2` (
|
||||||
`ID` int(11) NOT NULL auto_increment,
|
`ID` int(11) NOT NULL auto_increment,
|
||||||
`ParId` int(11) default NULL,
|
`ParId` int(11) default NULL,
|
||||||
`tst` text,
|
`tst` text,
|
||||||
`tst1` text,
|
`tst1` text,
|
||||||
PRIMARY KEY (`ID`),
|
PRIMARY KEY (`ID`),
|
||||||
KEY `IX_ParId_child` (`ParId`),
|
KEY `IX_ParId_t2` (`ParId`),
|
||||||
FOREIGN KEY (`ParId`) REFERENCES `test.parent` (`id`)
|
FOREIGN KEY (`ParId`) REFERENCES `t1` (`id`)
|
||||||
) TYPE=MyISAM;
|
) TYPE=MyISAM;
|
||||||
|
|
||||||
INSERT INTO parent(tst,tst1)
|
INSERT INTO t1(tst,tst1) VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE");
|
||||||
VALUES("MySQL","MySQL AB"), ("MSSQL","Microsoft"), ("ORACLE","ORACLE");
|
|
||||||
|
|
||||||
INSERT INTO child(ParId)
|
INSERT INTO t2(ParId) VALUES(1), (2), (3);
|
||||||
VALUES(1), (2), (3);
|
|
||||||
|
|
||||||
select * from child;
|
select * from t2;
|
||||||
|
|
||||||
UPDATE child, parent
|
UPDATE t2, t1 SET t2.tst = t1.tst, t2.tst1 = t1.tst1 WHERE t2.ParId = t1.Id;
|
||||||
SET child.tst = parent.tst,
|
|
||||||
child.tst1 = parent.tst1
|
|
||||||
WHERE child.ParId = parent.Id;
|
|
||||||
|
|
||||||
select * from child;
|
select * from t2;
|
||||||
|
|
||||||
|
|
||||||
drop table parent, child;
|
|
||||||
drop table if exists t1, t2 ;
|
drop table if exists t1, t2 ;
|
||||||
|
|
||||||
create table t1 (n numeric(10));
|
create table t1 (n numeric(10));
|
||||||
create table t2 (n numeric(10));
|
create table t2 (n numeric(10));
|
||||||
insert into t2 values (1),(2),(4),(8),(16),(32);
|
insert into t2 values (1),(2),(4),(8),(16),(32);
|
||||||
|
@ -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 */
|
s->active=0; /* to catch errors */
|
||||||
info->share=s;
|
info->share=s;
|
||||||
info->read_function=_my_b_read_r;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1658,7 +1658,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
|
|||||||
i -= u;
|
i -= u;
|
||||||
}
|
}
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return true;
|
return 1;
|
||||||
|
|
||||||
register const int v = plm1 - i;
|
register const int v = plm1 - i;
|
||||||
turboShift = u - v;
|
turboShift = u - v;
|
||||||
@ -1675,7 +1675,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
|
|||||||
}
|
}
|
||||||
j += shift;
|
j += shift;
|
||||||
}
|
}
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1689,7 +1689,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
|
|||||||
i -= u;
|
i -= u;
|
||||||
}
|
}
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return true;
|
return 1;
|
||||||
|
|
||||||
register const int v = plm1 - i;
|
register const int v = plm1 - i;
|
||||||
turboShift = u - v;
|
turboShift = u - v;
|
||||||
@ -1706,7 +1706,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
|
|||||||
}
|
}
|
||||||
j += shift;
|
j += shift;
|
||||||
}
|
}
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +242,20 @@ typedef struct st_sql_list {
|
|||||||
uint elements;
|
uint elements;
|
||||||
byte *first;
|
byte *first;
|
||||||
byte **next;
|
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;
|
} SQL_LIST;
|
||||||
|
|
||||||
|
|
||||||
@ -415,6 +429,10 @@ int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
|
|||||||
List<Item> &values,COND *conds,
|
List<Item> &values,COND *conds,
|
||||||
ORDER *order, ha_rows limit,
|
ORDER *order, ha_rows limit,
|
||||||
enum enum_duplicates handle_duplicates);
|
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,
|
int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
|
||||||
List<List_item> &values, enum_duplicates flag);
|
List<List_item> &values, enum_duplicates flag);
|
||||||
void kill_delayed_threads(void);
|
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 set_lock_for_tables(thr_lock_type lock_type);
|
||||||
void add_join_on(TABLE_LIST *b,Item *expr);
|
void add_join_on(TABLE_LIST *b,Item *expr);
|
||||||
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b);
|
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);
|
TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
|
||||||
|
|
||||||
SQL_SELECT *make_select(TABLE *head, table_map const_tables,
|
SQL_SELECT *make_select(TABLE *head, table_map const_tables,
|
||||||
|
@ -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
|
This just drops in all fields instead of current '*' field
|
||||||
** Returns pointer to last inserted field if ok
|
Returns pointer to last inserted field if ok
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -1957,21 +1957,26 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
|
|||||||
for (; tables ; tables=tables->next)
|
for (; tables ; tables=tables->next)
|
||||||
{
|
{
|
||||||
TABLE *table=tables->table;
|
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) &&
|
if (!table_name || (!strcmp(table_name,tables->alias) &&
|
||||||
(!db_name || !strcmp(tables->db,db_name))))
|
(!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;
|
Field **ptr=table->field,*field;
|
||||||
thd->used_tables|=table->map;
|
thd->used_tables|=table->map;
|
||||||
while ((field = *ptr++))
|
while ((field = *ptr++))
|
||||||
{
|
{
|
||||||
Item_field *item= new Item_field(field);
|
Item_field *item= new Item_field(field);
|
||||||
if (!found++)
|
if (!found++)
|
||||||
(void) it->replace(item);
|
(void) it->replace(item); // Replace '*'
|
||||||
else
|
else
|
||||||
it->after(item);
|
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)
|
if (field->query_id == thd->query_id)
|
||||||
thd->dupp_field=field;
|
thd->dupp_field=field;
|
||||||
field->query_id=thd->query_id;
|
field->query_id=thd->query_id;
|
||||||
|
@ -592,7 +592,7 @@ public:
|
|||||||
virtual int prepare(List<Item> &list) { return 0; }
|
virtual int prepare(List<Item> &list) { return 0; }
|
||||||
virtual bool send_fields(List<Item> &list,uint flag)=0;
|
virtual bool send_fields(List<Item> &list,uint flag)=0;
|
||||||
virtual bool send_data(List<Item> &items)=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)
|
virtual void send_error(uint errcode,const char *err)
|
||||||
{
|
{
|
||||||
::send_error(&thd->net,errcode,err);
|
::send_error(&thd->net,errcode,err);
|
||||||
@ -656,10 +656,10 @@ class select_insert :public select_result {
|
|||||||
List<Item> *fields;
|
List<Item> *fields;
|
||||||
ulonglong last_insert_id;
|
ulonglong last_insert_id;
|
||||||
COPY_INFO info;
|
COPY_INFO info;
|
||||||
uint save_time_stamp;
|
|
||||||
|
|
||||||
select_insert(TABLE *table_par,List<Item> *fields_par,enum_duplicates duplic)
|
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));
|
bzero((char*) &info,sizeof(info));
|
||||||
info.handle_duplicates=duplic;
|
info.handle_duplicates=duplic;
|
||||||
}
|
}
|
||||||
@ -703,8 +703,8 @@ class select_union :public select_result {
|
|||||||
public:
|
public:
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
COPY_INFO info;
|
COPY_INFO info;
|
||||||
uint save_time_stamp;
|
|
||||||
TMP_TABLE_PARAM *tmp_table_param;
|
TMP_TABLE_PARAM *tmp_table_param;
|
||||||
|
bool not_describe;
|
||||||
|
|
||||||
select_union(TABLE *table_par);
|
select_union(TABLE *table_par);
|
||||||
~select_union();
|
~select_union();
|
||||||
@ -814,37 +814,36 @@ public:
|
|||||||
bool send_fields(List<Item> &list,
|
bool send_fields(List<Item> &list,
|
||||||
uint flag) { return 0; }
|
uint flag) { return 0; }
|
||||||
bool send_data(List<Item> &items);
|
bool send_data(List<Item> &items);
|
||||||
void initialize_tables (JOIN *join);
|
bool initialize_tables (JOIN *join);
|
||||||
void send_error(uint errcode,const char *err);
|
void send_error(uint errcode,const char *err);
|
||||||
int do_deletes (bool from_send_error);
|
int do_deletes (bool from_send_error);
|
||||||
bool send_eof();
|
bool send_eof();
|
||||||
};
|
};
|
||||||
|
|
||||||
class multi_update : public select_result {
|
class multi_update : public select_result
|
||||||
TABLE_LIST *update_tables, *table_being_updated;
|
{
|
||||||
// Unique **tempfiles;
|
TABLE_LIST *all_tables, *update_tables, *table_being_updated;
|
||||||
COPY_INFO *infos;
|
THD *thd;
|
||||||
TABLE **tmp_tables;
|
TABLE **tmp_tables, *main_table;
|
||||||
THD *thd;
|
TMP_TABLE_PARAM *tmp_table_param;
|
||||||
ha_rows updated, found;
|
ha_rows updated, found;
|
||||||
List<Item> fields;
|
List <Item> *fields, *values;
|
||||||
List <Item> **fields_by_tables;
|
List <Item> **fields_for_table, **values_for_table;
|
||||||
enum enum_duplicates dupl;
|
uint table_count;
|
||||||
uint num_of_tables, num_fields, num_updated, *save_time_stamps, *field_sequence;
|
Copy_field *copy_field;
|
||||||
int error;
|
enum enum_duplicates handle_duplicates;
|
||||||
bool do_update, not_trans_safe;
|
bool do_update, trans_safe, transactional_tables, log_delayed;
|
||||||
public:
|
|
||||||
multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs,
|
public:
|
||||||
enum enum_duplicates handle_duplicates,
|
multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> *fields,
|
||||||
uint num);
|
List<Item> *values, enum_duplicates handle_duplicates);
|
||||||
~multi_update();
|
~multi_update();
|
||||||
int prepare(List<Item> &list);
|
int prepare(List<Item> &list);
|
||||||
bool send_fields(List<Item> &list,
|
bool send_fields(List<Item> &list, uint flag) { return 0; }
|
||||||
uint flag) { return 0; }
|
bool send_data(List<Item> &items);
|
||||||
bool send_data(List<Item> &items);
|
bool initialize_tables (JOIN *join);
|
||||||
void initialize_tables (JOIN *join);
|
void send_error(uint errcode,const char *err);
|
||||||
void send_error(uint errcode,const char *err);
|
int do_updates (bool from_send_error);
|
||||||
int do_updates (bool from_send_error);
|
bool send_eof();
|
||||||
bool send_eof();
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
@ -213,12 +213,13 @@ cleanup:
|
|||||||
|
|
||||||
extern "C" int refposcmp2(void* arg, const void *a,const void *b)
|
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);
|
return memcmp(a,b, *(int*) arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
|
multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
|
||||||
uint num_of_tables_arg)
|
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),
|
num_of_tables(num_of_tables_arg), error(0),
|
||||||
do_delete(0), transactional_tables(0), log_delayed(0), normal_tables(0)
|
do_delete(0), transactional_tables(0), log_delayed(0), normal_tables(0)
|
||||||
{
|
{
|
||||||
@ -230,31 +231,22 @@ int
|
|||||||
multi_delete::prepare(List<Item> &values)
|
multi_delete::prepare(List<Item> &values)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("multi_delete::prepare");
|
DBUG_ENTER("multi_delete::prepare");
|
||||||
do_delete = true;
|
do_delete= 1;
|
||||||
thd->proc_info="deleting from main table";
|
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);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
bool
|
||||||
multi_delete::initialize_tables(JOIN *join)
|
multi_delete::initialize_tables(JOIN *join)
|
||||||
{
|
{
|
||||||
int counter=0;
|
|
||||||
TABLE_LIST *walk;
|
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;
|
table_map tables_to_delete_from=0;
|
||||||
for (walk= delete_tables ; walk ; walk=walk->next)
|
for (walk= delete_tables ; walk ; walk=walk->next)
|
||||||
tables_to_delete_from|= walk->table->map;
|
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 */
|
/* We are going to delete from this table */
|
||||||
TABLE *tbl=walk->table=tab->table;
|
TABLE *tbl=walk->table=tab->table;
|
||||||
|
walk=walk->next;
|
||||||
/* Don't use KEYREAD optimization on this table */
|
/* Don't use KEYREAD optimization on this table */
|
||||||
tbl->no_keyread=1;
|
tbl->no_keyread=1;
|
||||||
walk=walk->next;
|
tbl->used_keys= 0;
|
||||||
if (tbl->file->has_transactions())
|
if (tbl->file->has_transactions())
|
||||||
log_delayed= transactional_tables= 1;
|
log_delayed= transactional_tables= 1;
|
||||||
else if (tbl->tmp_table != NO_TMP_TABLE)
|
else if (tbl->tmp_table != NO_TMP_TABLE)
|
||||||
@ -280,19 +273,17 @@ multi_delete::initialize_tables(JOIN *join)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
walk= delete_tables;
|
walk= delete_tables;
|
||||||
walk->table->used_keys=0;
|
tempfiles_ptr= tempfiles;
|
||||||
for (walk=walk->next ; walk ; walk=walk->next, counter++)
|
for (walk=walk->next ; walk ; walk=walk->next)
|
||||||
{
|
{
|
||||||
tables_to_delete_from|= walk->table->map;
|
|
||||||
TABLE *table=walk->table;
|
TABLE *table=walk->table;
|
||||||
/* Don't use key read with MULTI-TABLE-DELETE */
|
*tempfiles_ptr++= new Unique (refposcmp2,
|
||||||
table->used_keys=0;
|
(void *) &table->file->ref_length,
|
||||||
tempfiles[counter] = new Unique (refposcmp2,
|
table->file->ref_length,
|
||||||
(void *) &table->file->ref_length,
|
MEM_STRIP_BUF_SIZE);
|
||||||
table->file->ref_length,
|
|
||||||
MEM_STRIP_BUF_SIZE);
|
|
||||||
}
|
}
|
||||||
init_ftfuncs(thd,1);
|
init_ftfuncs(thd,1);
|
||||||
|
DBUG_RETURN(thd->fatal_error != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -307,7 +298,7 @@ multi_delete::~multi_delete()
|
|||||||
t->no_keyread=0;
|
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])
|
if (tempfiles[counter])
|
||||||
delete tempfiles[counter];
|
delete tempfiles[counter];
|
||||||
@ -414,7 +405,7 @@ int multi_delete::do_deletes(bool from_send_error)
|
|||||||
else
|
else
|
||||||
table_being_deleted = delete_tables;
|
table_being_deleted = delete_tables;
|
||||||
|
|
||||||
do_delete = false;
|
do_delete= 0;
|
||||||
for (table_being_deleted=table_being_deleted->next;
|
for (table_being_deleted=table_being_deleted->next;
|
||||||
table_being_deleted ;
|
table_being_deleted ;
|
||||||
table_being_deleted=table_being_deleted->next, counter++)
|
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
|
was a non-transaction-safe table involved, since
|
||||||
modifications in it cannot be rolled back.
|
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);
|
mysql_update_log.write(thd,thd->query,thd->query_length);
|
||||||
if (mysql_bin_log.is_open())
|
if (mysql_bin_log.is_open())
|
||||||
@ -478,11 +469,17 @@ bool multi_delete::send_eof()
|
|||||||
if (mysql_bin_log.write(&qinfo) && !normal_tables)
|
if (mysql_bin_log.write(&qinfo) && !normal_tables)
|
||||||
local_error=1; // Log write failed: roll back the SQL statement
|
local_error=1; // Log write failed: roll back the SQL statement
|
||||||
}
|
}
|
||||||
/* Commit or rollback the current SQL statement */
|
if (!log_delayed)
|
||||||
VOID(ha_autocommit_or_rollback(thd,local_error > 0));
|
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
|
||||||
|
|
||||||
query_cache_invalidate3(thd, delete_tables, 1);
|
|
||||||
}
|
}
|
||||||
|
/* 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)
|
if (local_error)
|
||||||
::send_error(&thd->net);
|
::send_error(&thd->net);
|
||||||
else
|
else
|
||||||
|
@ -41,7 +41,8 @@ static void unlink_blobs(register TABLE *table);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Check if insert fields are correct
|
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
|
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);
|
my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
table->time_stamp=0;
|
||||||
if (table->timestamp_field && // Don't set timestamp if used
|
if (table->timestamp_field && // Don't set timestamp if used
|
||||||
table->timestamp_field->query_id == thd->query_id)
|
table->timestamp_field->query_id != thd->query_id)
|
||||||
table->time_stamp=0; // This should be saved
|
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);
|
table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -105,7 +107,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields,
|
|||||||
!(thd->master_access & SUPER_ACL));
|
!(thd->master_access & SUPER_ACL));
|
||||||
bool transactional_table, log_delayed, bulk_insert=0;
|
bool transactional_table, log_delayed, bulk_insert=0;
|
||||||
uint value_count;
|
uint value_count;
|
||||||
uint save_time_stamp;
|
|
||||||
ulong counter = 1;
|
ulong counter = 1;
|
||||||
ulonglong id;
|
ulonglong id;
|
||||||
COPY_INFO info;
|
COPY_INFO info;
|
||||||
@ -150,14 +151,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields,
|
|||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
thd->proc_info="init";
|
thd->proc_info="init";
|
||||||
thd->used_tables=0;
|
thd->used_tables=0;
|
||||||
save_time_stamp=table->time_stamp;
|
|
||||||
values= its++;
|
values= its++;
|
||||||
if (check_insert_fields(thd,table,fields,*values,1) ||
|
if (check_insert_fields(thd,table,fields,*values,1) ||
|
||||||
setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0,0))
|
setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0,0))
|
||||||
{
|
|
||||||
table->time_stamp=save_time_stamp;
|
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
|
||||||
value_count= values->elements;
|
value_count= values->elements;
|
||||||
while ((values = its++))
|
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,
|
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
|
||||||
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
||||||
MYF(0),counter);
|
MYF(0),counter);
|
||||||
table->time_stamp=save_time_stamp;
|
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
if (setup_fields(thd,table_list,*values,0,0,0))
|
if (setup_fields(thd,table_list,*values,0,0,0))
|
||||||
{
|
|
||||||
table->time_stamp=save_time_stamp;
|
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
its.rewind ();
|
its.rewind ();
|
||||||
/*
|
/*
|
||||||
@ -333,7 +326,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
thd->proc_info="end";
|
thd->proc_info="end";
|
||||||
table->time_stamp=save_time_stamp; // Restore auto timestamp ptr
|
|
||||||
table->next_number_field=0;
|
table->next_number_field=0;
|
||||||
thd->count_cuted_fields=0;
|
thd->count_cuted_fields=0;
|
||||||
thd->next_insert_id=0; // Reset this if wrongly used
|
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");
|
DBUG_ENTER("select_insert::prepare");
|
||||||
|
|
||||||
save_time_stamp=table->time_stamp;
|
|
||||||
if (check_insert_fields(thd,table,*fields,values,1))
|
if (check_insert_fields(thd,table,*fields,values,1))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
@ -1308,8 +1299,6 @@ select_insert::~select_insert()
|
|||||||
{
|
{
|
||||||
if (table)
|
if (table)
|
||||||
{
|
{
|
||||||
if (save_time_stamp)
|
|
||||||
table->time_stamp=save_time_stamp;
|
|
||||||
table->next_number_field=0;
|
table->next_number_field=0;
|
||||||
table->file->extra(HA_EXTRA_RESET);
|
table->file->extra(HA_EXTRA_RESET);
|
||||||
}
|
}
|
||||||
@ -1412,7 +1401,6 @@ select_create::prepare(List<Item> &values)
|
|||||||
/* First field to copy */
|
/* First field to copy */
|
||||||
field=table->field+table->fields - values.elements;
|
field=table->field+table->fields - values.elements;
|
||||||
|
|
||||||
save_time_stamp=table->time_stamp;
|
|
||||||
if (table->timestamp_field) // Don't set timestamp if used
|
if (table->timestamp_field) // Don't set timestamp if used
|
||||||
{
|
{
|
||||||
table->timestamp_field->set_time();
|
table->timestamp_field->set_time();
|
||||||
|
@ -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)->table_name,iif->table_name) &&
|
||||||
!strcmp(((Item_field*)new_item)->field_name,iif->field_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_item)->db_name=iif->db_name;
|
||||||
Item_field *new_one=new Item_field(iif->db_name, iif->table_name, iif->field_name);
|
Item_field *new_one=new Item_field(iif->db_name, iif->table_name, iif->field_name);
|
||||||
privlist.push_back(new_one);
|
privlist.push_back(new_one);
|
||||||
@ -151,7 +151,7 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
|
|||||||
if (cursor->do_redirect)
|
if (cursor->do_redirect)
|
||||||
{
|
{
|
||||||
cursor->table= ((TABLE_LIST*) cursor->table)->table;
|
cursor->table= ((TABLE_LIST*) cursor->table)->table;
|
||||||
cursor->do_redirect=false;
|
cursor->do_redirect= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
(void) pthread_mutex_lock(&LOCK_user_conn);
|
||||||
@ -1826,61 +1826,29 @@ mysql_execute_command(void)
|
|||||||
select_lex->select_limit,
|
select_lex->select_limit,
|
||||||
lex->duplicates);
|
lex->duplicates);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
multi_update *result;
|
const char *msg= 0;
|
||||||
uint table_count;
|
lex->sql_command= SQLCOM_MULTI_UPDATE;
|
||||||
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++;
|
|
||||||
if (select_lex->order_list.elements)
|
if (select_lex->order_list.elements)
|
||||||
msg="ORDER BY";
|
msg="ORDER BY";
|
||||||
else if (select_lex->select_limit && select_lex->select_limit !=
|
else if (select_lex->select_limit && select_lex->select_limit !=
|
||||||
HA_POS_ERROR)
|
HA_POS_ERROR)
|
||||||
msg="LIMIT";
|
msg="LIMIT";
|
||||||
|
|
||||||
if (msg)
|
if (msg)
|
||||||
{
|
{
|
||||||
net_printf(&thd->net, ER_WRONG_USAGE, "UPDATE", msg);
|
net_printf(&thd->net, ER_WRONG_USAGE, "UPDATE", msg);
|
||||||
res= 1;
|
res= 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
|
res= mysql_multi_update(thd,tables,
|
||||||
if ((res=open_and_lock_tables(thd,tables)))
|
&select_lex->item_list,
|
||||||
break;
|
&lex->value_list,
|
||||||
thd->select_limit=HA_POS_ERROR;
|
select_lex->where,
|
||||||
if (!setup_fields(thd,tables,select_lex->item_list,1,0,0) &&
|
select_lex->options,
|
||||||
!setup_fields(thd,tables,lex->value_list,0,0,0) &&
|
lex->duplicates);
|
||||||
! 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);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SQLCOM_INSERT:
|
case SQLCOM_INSERT:
|
||||||
if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege))
|
if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege))
|
||||||
goto error; /* purecov: inspected */
|
goto error; /* purecov: inspected */
|
||||||
@ -2741,11 +2709,8 @@ mysql_init_select(LEX *lex)
|
|||||||
select_lex->olap= UNSPECIFIED_OLAP_TYPE;
|
select_lex->olap= UNSPECIFIED_OLAP_TYPE;
|
||||||
lex->exchange = 0;
|
lex->exchange = 0;
|
||||||
lex->proc_list.first=0;
|
lex->proc_list.first=0;
|
||||||
select_lex->order_list.elements=select_lex->group_list.elements=0;
|
select_lex->order_list.empty();
|
||||||
select_lex->order_list.first=0;
|
select_lex->group_list.empty();
|
||||||
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->next = (SELECT_LEX *)NULL;
|
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
|
** Store field definition for create
|
||||||
** Return 0 if ok
|
** Return 0 if ok
|
||||||
@ -3102,7 +3057,7 @@ void store_position_for_column(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
add_proc_to_list(Item *item)
|
add_proc_to_list(THD* thd, Item *item)
|
||||||
{
|
{
|
||||||
ORDER *order;
|
ORDER *order;
|
||||||
Item **item_ptr;
|
Item **item_ptr;
|
||||||
@ -3113,7 +3068,7 @@ add_proc_to_list(Item *item)
|
|||||||
*item_ptr= item;
|
*item_ptr= item;
|
||||||
order->item=item_ptr;
|
order->item=item_ptr;
|
||||||
order->free_me=0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3167,7 +3122,7 @@ bool add_to_list(SQL_LIST &list,Item *item,bool asc)
|
|||||||
order->asc = asc;
|
order->asc = asc;
|
||||||
order->free_me=0;
|
order->free_me=0;
|
||||||
order->used=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);
|
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);
|
DBUG_RETURN(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
if (make_join_statistics(&join,tables,conds,&keyuse) || thd->fatal_error)
|
||||||
goto err;
|
goto err;
|
||||||
thd->proc_info="preparing";
|
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 &&
|
if (join.const_table_map != join.found_const_table_map &&
|
||||||
!(select_options & SELECT_DESCRIBE))
|
!(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
|
static void
|
||||||
join_free(JOIN *join)
|
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.
|
Create internal temporary table
|
||||||
Set distinct if duplicates could be removed
|
|
||||||
Given fields field pointers are changed to point at tmp_table
|
|
||||||
for send_fields
|
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
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,
|
Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
|
||||||
Item_result_field ***copy_func, Field **from_field,
|
Item_result_field ***copy_func, Field **from_field,
|
||||||
bool group, bool modify_item)
|
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 *
|
TABLE *
|
||||||
create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
|
create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
|
||||||
ORDER *group, bool distinct, bool save_sum_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
|
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,
|
Field *new_field=create_tmp_field(thd, table, item,type, ©_func,
|
||||||
tmp_from_field, group != 0,
|
tmp_from_field, group != 0,
|
||||||
not_all_columns);
|
not_all_columns || group !=0);
|
||||||
if (!new_field)
|
if (!new_field)
|
||||||
{
|
{
|
||||||
if (thd->fatal_error)
|
if (thd->fatal_error)
|
||||||
@ -3991,7 +4063,6 @@ static bool open_tmp_table(TABLE *table)
|
|||||||
table->db_stat=0;
|
table->db_stat=0;
|
||||||
return(1);
|
return(1);
|
||||||
}
|
}
|
||||||
/* VOID(ha_lock(table,F_WRLCK)); */ /* Single thread table */
|
|
||||||
(void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
|
(void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
@ -301,3 +301,4 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool cp_buffer_from_ref(TABLE_REF *ref);
|
bool cp_buffer_from_ref(TABLE_REF *ref);
|
||||||
|
bool error_if_full_join(JOIN *join);
|
||||||
|
@ -33,7 +33,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result)
|
|||||||
TABLE *table;
|
TABLE *table;
|
||||||
int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0;
|
int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0;
|
||||||
int res;
|
int res;
|
||||||
bool found_rows_for_union=false;
|
bool found_rows_for_union= 0;
|
||||||
TABLE_LIST result_table_list;
|
TABLE_LIST result_table_list;
|
||||||
TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first;
|
TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first;
|
||||||
TMP_TABLE_PARAM tmp_table_param;
|
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
|
if (cursor->do_redirect) // False if CUBE/ROLLUP
|
||||||
{
|
{
|
||||||
cursor->table= ((TABLE_LIST*) cursor->table)->table;
|
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;
|
res= -1;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
union_result->save_time_stamp=!describe;
|
union_result->not_describe= !describe;
|
||||||
union_result->tmp_table_param=&tmp_table_param;
|
union_result->tmp_table_param=&tmp_table_param;
|
||||||
for (sl= &lex->select_lex; sl; sl=sl->next)
|
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)
|
if (thd->select_limit == HA_POS_ERROR)
|
||||||
sl->options&= ~OPTION_FOUND_ROWS;
|
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->item_list,
|
||||||
sl->where,
|
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,
|
(ORDER*) sl->group_list.first,
|
||||||
sl->having,
|
sl->having,
|
||||||
(ORDER*) NULL,
|
(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);
|
union_result);
|
||||||
if (res)
|
if (res)
|
||||||
goto exit;
|
goto exit;
|
||||||
@ -226,7 +229,7 @@ exit:
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
select_union::select_union(TABLE *table_par)
|
select_union::select_union(TABLE *table_par)
|
||||||
:table(table_par)
|
:table(table_par), not_describe(0)
|
||||||
{
|
{
|
||||||
bzero((char*) &info,sizeof(info));
|
bzero((char*) &info,sizeof(info));
|
||||||
/*
|
/*
|
||||||
@ -243,7 +246,7 @@ select_union::~select_union()
|
|||||||
|
|
||||||
int select_union::prepare(List<Item> &list)
|
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,
|
my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
|
||||||
ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
|
ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
|
||||||
|
@ -29,10 +29,12 @@ static bool compare_record(TABLE *table, ulong query_id)
|
|||||||
{
|
{
|
||||||
if (!table->blob_fields)
|
if (!table->blob_fields)
|
||||||
return cmp_record(table,1);
|
return cmp_record(table,1);
|
||||||
|
/* Compare null bits */
|
||||||
if (memcmp(table->null_flags,
|
if (memcmp(table->null_flags,
|
||||||
table->null_flags+table->rec_buff_length,
|
table->null_flags+table->rec_buff_length,
|
||||||
table->null_bytes))
|
table->null_bytes))
|
||||||
return 1; // Diff in NULL value
|
return 1; // Diff in NULL value
|
||||||
|
/* Compare updated fields */
|
||||||
for (Field **ptr=table->field ; *ptr ; ptr++)
|
for (Field **ptr=table->field ; *ptr ; ptr++)
|
||||||
{
|
{
|
||||||
if ((*ptr)->query_id == query_id &&
|
if ((*ptr)->query_id == query_id &&
|
||||||
@ -52,10 +54,11 @@ int mysql_update(THD *thd,
|
|||||||
ha_rows limit,
|
ha_rows limit,
|
||||||
enum enum_duplicates handle_duplicates)
|
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;
|
bool used_key_is_modified, transactional_table, log_delayed;
|
||||||
int error=0;
|
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;
|
ulong query_id=thd->query_id, timestamp_query_id;
|
||||||
key_map old_used_keys;
|
key_map old_used_keys;
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
@ -67,7 +70,6 @@ int mysql_update(THD *thd,
|
|||||||
|
|
||||||
if (!(table = open_ltable(thd,table_list,table_list->lock_type)))
|
if (!(table = open_ltable(thd,table_list,table_list->lock_type)))
|
||||||
DBUG_RETURN(-1); /* purecov: inspected */
|
DBUG_RETURN(-1); /* purecov: inspected */
|
||||||
save_time_stamp=table->time_stamp;
|
|
||||||
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
|
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
|
||||||
thd->proc_info="init";
|
thd->proc_info="init";
|
||||||
|
|
||||||
@ -89,6 +91,7 @@ int mysql_update(THD *thd,
|
|||||||
{
|
{
|
||||||
timestamp_query_id=table->timestamp_field->query_id;
|
timestamp_query_id=table->timestamp_field->query_id;
|
||||||
table->timestamp_field->query_id=thd->query_id-1;
|
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 */
|
/* 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);
|
table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
|
||||||
if (setup_fields(thd,table_list,values,0,0,0))
|
if (setup_fields(thd,table_list,values,0,0,0))
|
||||||
{
|
{
|
||||||
table->time_stamp=save_time_stamp; // Restore timestamp pointer
|
|
||||||
DBUG_RETURN(-1); /* purecov: inspected */
|
DBUG_RETURN(-1); /* purecov: inspected */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +121,6 @@ int mysql_update(THD *thd,
|
|||||||
(select && select->check_quick(safe_update, limit)) || !limit)
|
(select && select->check_quick(safe_update, limit)) || !limit)
|
||||||
{
|
{
|
||||||
delete select;
|
delete select;
|
||||||
table->time_stamp=save_time_stamp; // Restore timestamp pointer
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
DBUG_RETURN(-1); // Error in where
|
DBUG_RETURN(-1); // Error in where
|
||||||
@ -134,7 +135,6 @@ int mysql_update(THD *thd,
|
|||||||
if (safe_update && !using_limit)
|
if (safe_update && !using_limit)
|
||||||
{
|
{
|
||||||
delete select;
|
delete select;
|
||||||
table->time_stamp=save_time_stamp;
|
|
||||||
send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
|
send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
@ -153,8 +153,8 @@ int mysql_update(THD *thd,
|
|||||||
if (used_key_is_modified || order)
|
if (used_key_is_modified || order)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
** We can't update table directly; We must first search after all
|
We can't update table directly; We must first search after all
|
||||||
** matching rows before updating the table!
|
matching rows before updating the table!
|
||||||
*/
|
*/
|
||||||
table->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE);
|
table->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE);
|
||||||
IO_CACHE tempfile;
|
IO_CACHE tempfile;
|
||||||
@ -162,7 +162,6 @@ int mysql_update(THD *thd,
|
|||||||
DISK_BUFFER_SIZE, MYF(MY_WME)))
|
DISK_BUFFER_SIZE, MYF(MY_WME)))
|
||||||
{
|
{
|
||||||
delete select; /* purecov: inspected */
|
delete select; /* purecov: inspected */
|
||||||
table->time_stamp=save_time_stamp; // Restore timestamp pointer /* purecov: inspected */
|
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
if (old_used_keys & ((key_map) 1 << used_index))
|
if (old_used_keys & ((key_map) 1 << used_index))
|
||||||
@ -193,7 +192,6 @@ int mysql_update(THD *thd,
|
|||||||
== HA_POS_ERROR)
|
== HA_POS_ERROR)
|
||||||
{
|
{
|
||||||
delete select;
|
delete select;
|
||||||
table->time_stamp=save_time_stamp; // Restore timestamp pointer
|
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,7 +245,6 @@ int mysql_update(THD *thd,
|
|||||||
if (error >= 0)
|
if (error >= 0)
|
||||||
{
|
{
|
||||||
delete select;
|
delete select;
|
||||||
table->time_stamp=save_time_stamp; // Restore timestamp pointer
|
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,7 +294,6 @@ int mysql_update(THD *thd,
|
|||||||
end_read_record(&info);
|
end_read_record(&info);
|
||||||
thd->proc_info="end";
|
thd->proc_info="end";
|
||||||
VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY));
|
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();
|
transactional_table= table->file->has_transactions();
|
||||||
log_delayed= (transactional_table || table->tmp_table);
|
log_delayed= (transactional_table || table->tmp_table);
|
||||||
if (updated && (error <= 0 || !transactional_table))
|
if (updated && (error <= 0 || !transactional_table))
|
||||||
@ -351,324 +347,338 @@ int mysql_update(THD *thd,
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
Update multiple tables from join
|
Update multiple tables from join
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
multi_update::multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs,
|
/*
|
||||||
enum enum_duplicates handle_duplicates,
|
Setup multi-update handling and call SELECT to do the join
|
||||||
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),
|
int mysql_multi_update(THD *thd,
|
||||||
error(0), do_update(false)
|
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);
|
int res;
|
||||||
tmp_tables = (TABLE **)NULL;
|
multi_update *result;
|
||||||
int counter=0;
|
TABLE_LIST *tl;
|
||||||
ulong timestamp_query_id;
|
DBUG_ENTER("mysql_multi_update");
|
||||||
not_trans_safe=false;
|
|
||||||
for (TABLE_LIST *dt=ut ; dt ; dt=dt->next,counter++)
|
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;
|
TABLE *table= tl->table;
|
||||||
// (void) ut->table->file->extra(HA_EXTRA_NO_KEYREAD);
|
|
||||||
dt->table->used_keys=0;
|
|
||||||
if (table->timestamp_field)
|
if (table->timestamp_field)
|
||||||
{
|
{
|
||||||
// Don't set timestamp column if this is modified
|
table->time_stamp=0;
|
||||||
timestamp_query_id=table->timestamp_field->query_id;
|
// Only set timestamp column if this is not modified
|
||||||
table->timestamp_field->query_id=thd->query_id-1;
|
if (table->timestamp_field->query_id != thd->query_id)
|
||||||
if (table->timestamp_field->query_id == thd->query_id)
|
table->time_stamp= table->timestamp_field->offset() +1;
|
||||||
table->time_stamp=0;
|
|
||||||
else
|
|
||||||
table->timestamp_field->query_id=timestamp_query_id;
|
|
||||||
}
|
}
|
||||||
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");
|
DBUG_ENTER("multi_update::prepare");
|
||||||
do_update = true;
|
|
||||||
thd->count_cuted_fields=1;
|
thd->count_cuted_fields=1;
|
||||||
thd->cuted_fields=0L;
|
thd->cuted_fields=0L;
|
||||||
thd->proc_info="updating the main table";
|
thd->proc_info="updating main table";
|
||||||
TABLE_LIST *table_ref;
|
|
||||||
|
|
||||||
if (thd->options & OPTION_SAFE_UPDATES)
|
while ((item= (Item_field *) field_it++))
|
||||||
{
|
tables_to_update|= item->used_tables();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
|
|
||||||
Item_field *item;
|
if (!tables_to_update)
|
||||||
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;
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
|
||||||
for (table_ref=update_tables; table_ref;
|
"You didn't specify any tables to UPDATE");
|
||||||
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");
|
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Here, I have to allocate the array of temporary tables
|
We have to check values after setup_tables to get used_keys right in
|
||||||
I have to treat a case of num_updated=1 differently in send_data() method.
|
reference tables
|
||||||
*/
|
*/
|
||||||
if (num_updated)
|
|
||||||
|
if (setup_fields(thd, all_tables, *values, 1,0,0))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Save tables beeing updated in update_tables
|
||||||
|
update_table->shared is position for table
|
||||||
|
Don't use key read on tables that are updated
|
||||||
|
*/
|
||||||
|
|
||||||
|
update.empty();
|
||||||
|
for (table_ref= all_tables; table_ref; table_ref=table_ref->next)
|
||||||
{
|
{
|
||||||
tmp_tables = (TABLE **) sql_calloc(sizeof(TABLE *) * num_updated);
|
TABLE *table=table_ref->table;
|
||||||
infos = (COPY_INFO *) sql_calloc(sizeof(COPY_INFO) * num_updated);
|
if (tables_to_update & table->map)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
if (!table_ref->shared)
|
TABLE_LIST *tl= (TABLE_LIST*) thd->memdup((char*) table_ref,
|
||||||
continue;
|
sizeof(*tl));
|
||||||
// Here we have to add row offset as an additional field ...
|
if (!tl)
|
||||||
if (!(temp_fields = (List_item *)sql_calloc(sizeof(List_item))))
|
|
||||||
{
|
|
||||||
error = 1; // A proper error message is due here
|
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
update.link_in_list((byte*) tl, (byte**) &tl->next);
|
||||||
temp_fields->empty();
|
tl->shared= table_count++;
|
||||||
it.rewind(); int_ptr=field_sequence;
|
table->no_keyread=1;
|
||||||
while ((item= (Item_field *)it++))
|
table->used_keys=0;
|
||||||
{
|
table->pos_in_table_list= tl;
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
init_ftfuncs(thd,1);
|
||||||
error = 0; // Timestamps do not need to be restored, so far ...
|
DBUG_RETURN(thd->fatal_error != 0);
|
||||||
DBUG_RETURN(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)
|
multi_update::initialize_tables(JOIN *join)
|
||||||
{
|
{
|
||||||
#ifdef NOT_YET
|
TABLE_LIST *table_ref;
|
||||||
We skip it as it only makes a mess ...........
|
DBUG_ENTER("initialize_tables");
|
||||||
TABLE_LIST *walk;
|
|
||||||
table_map tables_to_update_from=0;
|
if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join))
|
||||||
for (walk= update_tables ; walk ; walk=walk->next)
|
DBUG_RETURN(1);
|
||||||
tables_to_update_from|= walk->table->map;
|
main_table=join->join_tab->table;
|
||||||
|
trans_safe= transactional_tables= main_table->file->has_transactions();
|
||||||
walk= update_tables;
|
log_delayed= trans_safe || main_table->tmp_table != NO_TMP_TABLE;
|
||||||
for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
|
|
||||||
tab < end;
|
/* Create a temporary table for all tables after except main table */
|
||||||
tab++)
|
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
|
uint cnt= table_ref->shared;
|
||||||
TABLE *tbl=walk->table=tab->table;
|
ORDER group;
|
||||||
/* Don't use KEYREAD optimization on this table */
|
List<Item> temp_fields= *fields_for_table[cnt];
|
||||||
tbl->no_keyread=1;
|
TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt;
|
||||||
walk=walk->next;
|
|
||||||
|
/*
|
||||||
|
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()
|
multi_update::~multi_update()
|
||||||
{
|
{
|
||||||
int counter = 0;
|
TABLE_LIST *table;
|
||||||
for (table_being_updated=update_tables ;
|
for (table= update_tables ; table; table= table->next)
|
||||||
table_being_updated ;
|
table->table->no_keyread=0;
|
||||||
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];
|
|
||||||
}
|
|
||||||
if (tmp_tables)
|
if (tmp_tables)
|
||||||
for (uint counter = 0; counter < num_updated; counter++)
|
{
|
||||||
|
for (uint counter = 0; counter < table_count; counter++)
|
||||||
if (tmp_tables[counter])
|
if (tmp_tables[counter])
|
||||||
free_tmp_table(thd,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);
|
TABLE_LIST *cur_table;
|
||||||
for (uint counter = 0; counter < fields.elements; counter++)
|
DBUG_ENTER("multi_update::send_data");
|
||||||
real_values.pop();
|
|
||||||
// We have skipped fields ....
|
found++;
|
||||||
if (!num_updated)
|
for (cur_table= update_tables; cur_table ; cur_table= cur_table->next)
|
||||||
{
|
{
|
||||||
for (table_being_updated=update_tables ;
|
TABLE *table= cur_table->table;
|
||||||
table_being_updated ;
|
/* Check if we are using outer join and we didn't find the row */
|
||||||
table_being_updated=table_being_updated->next)
|
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;
|
table->status|= STATUS_UPDATED;
|
||||||
store_record(table,1);
|
store_record(table,1);
|
||||||
if (fill_record(fields,real_values))
|
if (fill_record(*fields_for_table[offset], *values_for_table[offset]))
|
||||||
return 1;
|
DBUG_RETURN(1);
|
||||||
found++;
|
if (compare_record(table, thd->query_id))
|
||||||
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))
|
int error;
|
||||||
values_by_table.push_back(item);
|
if (!updated++)
|
||||||
}
|
|
||||||
// Here I am breaking values as per each table
|
|
||||||
if (secure_counter < 0)
|
|
||||||
{
|
|
||||||
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])))
|
|
||||||
{
|
{
|
||||||
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));
|
table->file->print_error(error,MYF(0));
|
||||||
if (!error) error=1;
|
updated--;
|
||||||
return 1;
|
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
|
if (create_myisam_from_heap(table, tmp_table_param + offset, error, 1))
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
error=-1;
|
do_update=0;
|
||||||
return 1;
|
DBUG_RETURN(1); // Not a table_is_full error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void multi_update::send_error(uint errcode,const char *err)
|
void multi_update::send_error(uint errcode,const char *err)
|
||||||
{
|
{
|
||||||
/* First send error what ever it is ... */
|
/* First send error what ever it is ... */
|
||||||
::send_error(&thd->net,errcode,err);
|
::send_error(&thd->net,errcode,err);
|
||||||
|
|
||||||
/* reset used flags */
|
|
||||||
// update_tables->table->no_keyread=0;
|
|
||||||
|
|
||||||
/* If nothing updated return */
|
/* If nothing updated return */
|
||||||
if (!updated)
|
if (!updated)
|
||||||
return;
|
return;
|
||||||
@ -676,97 +686,124 @@ void multi_update::send_error(uint errcode,const char *err)
|
|||||||
/* Something already updated so we have to invalidate cache */
|
/* Something already updated so we have to invalidate cache */
|
||||||
query_cache_invalidate3(thd, update_tables, 1);
|
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,
|
If all tables that has been updated are trans safe then just do rollback.
|
||||||
just do rollback.
|
If not attempt to do remaining updates.
|
||||||
The same if all tables are transactional, regardless of where we are.
|
|
||||||
In all other cases do attempt updates ...
|
|
||||||
*/
|
*/
|
||||||
if ((table_being_updated->table->file->has_transactions() &&
|
|
||||||
table_being_updated == update_tables) || !not_trans_safe)
|
if (trans_safe)
|
||||||
ha_rollback_stmt(thd);
|
ha_rollback_stmt(thd);
|
||||||
else if (do_update && num_updated)
|
else if (do_update && table_count > 1)
|
||||||
VOID(do_updates(true));
|
{
|
||||||
|
/* 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;
|
TABLE_LIST *cur_table;
|
||||||
|
int local_error;
|
||||||
|
ha_rows org_updated;
|
||||||
|
TABLE *table;
|
||||||
|
DBUG_ENTER("do_updates");
|
||||||
|
|
||||||
if (from_send_error)
|
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' */
|
table = cur_table->table;
|
||||||
for (TABLE_LIST *aux=update_tables;
|
if (table == main_table)
|
||||||
aux != table_being_updated;
|
continue; // Already updated
|
||||||
aux=aux->next)
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
table_being_updated = update_tables;
|
|
||||||
|
|
||||||
do_update = false;
|
org_updated= updated;
|
||||||
for (table_being_updated=table_being_updated->next;
|
byte *ref_pos;
|
||||||
table_being_updated ;
|
TABLE *tmp_table= tmp_tables[cur_table->shared];
|
||||||
table_being_updated=table_being_updated->next, counter++)
|
tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache
|
||||||
{
|
table->file->extra(HA_EXTRA_NO_CACHE);
|
||||||
if (!table_being_updated->shared)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
TABLE *table = table_being_updated->table;
|
/*
|
||||||
TABLE *tmp_table=tmp_tables[counter];
|
Setup copy functions to copy fields from temporary table
|
||||||
if (tmp_table->file->extra(HA_EXTRA_NO_CACHE))
|
*/
|
||||||
|
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++)
|
||||||
{
|
{
|
||||||
local_error=1;
|
Item_field *item= (Item_field* ) field_it++;
|
||||||
break;
|
(copy_field_ptr++)->set(item->field, *field, 0);
|
||||||
}
|
}
|
||||||
List<Item> list;
|
copy_field_end=copy_field_ptr;
|
||||||
Field **ptr=tmp_table->field,*field;
|
|
||||||
// This is supposed to be something like insert_fields
|
if ((local_error = tmp_table->file->rnd_init(1)))
|
||||||
thd->used_tables|=tmp_table->map;
|
goto err;
|
||||||
while ((field = *ptr++))
|
|
||||||
|
ref_pos= (byte*) tmp_table->field[0]->ptr;
|
||||||
|
for (;;)
|
||||||
{
|
{
|
||||||
list.push_back((Item *)new Item_field(field));
|
if (thd->killed && trans_safe)
|
||||||
if (field->query_id == thd->query_id)
|
goto err;
|
||||||
thd->dupp_field=field;
|
if ((local_error=tmp_table->file->rnd_next(tmp_table->record[0])))
|
||||||
field->query_id=thd->query_id;
|
|
||||||
tmp_table->used_keys&=field->part_of_key;
|
|
||||||
}
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
found++;
|
|
||||||
local_error= table->file->rnd_pos(table->record[0],
|
|
||||||
(byte*) (*(tmp_table->field))->ptr);
|
|
||||||
if (local_error)
|
|
||||||
return local_error;
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
table->file->print_error(local_error,MYF(0));
|
if (local_error == HA_ERR_END_OF_FILE)
|
||||||
break;
|
break;
|
||||||
|
if (local_error == HA_ERR_RECORD_DELETED)
|
||||||
|
continue; // May happen on dup key
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
else
|
found++;
|
||||||
|
if ((local_error= table->file->rnd_pos(table->record[0], ref_pos)))
|
||||||
|
goto err;
|
||||||
|
table->status|= STATUS_UPDATED;
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
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++;
|
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
|
||||||
|
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()
|
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 */
|
/* Does updates for the last n - 1 tables, returns 0 if ok */
|
||||||
int local_error = (num_updated) ? do_updates(false) : 0;
|
int local_error = (table_count) ? do_updates(0) : 0;
|
||||||
|
|
||||||
/* reset used flags */
|
|
||||||
#ifndef NOT_USED
|
|
||||||
update_tables->table->no_keyread=0;
|
|
||||||
#endif
|
|
||||||
if (local_error == -1)
|
|
||||||
local_error= 0;
|
|
||||||
thd->proc_info= "end";
|
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
|
Write the SQL statement to the binlog if we updated
|
||||||
rows and we succeeded, or also in an error case when there
|
rows and we succeeded or if we updated some non
|
||||||
was a non-transaction-safe table involved, since
|
transacational tables
|
||||||
modifications in it cannot be rolled back.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (updated || not_trans_safe)
|
if (updated && (local_error <= 0 || !trans_safe))
|
||||||
{
|
{
|
||||||
mysql_update_log.write(thd,thd->query,thd->query_length);
|
mysql_update_log.write(thd,thd->query,thd->query_length);
|
||||||
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
|
if (mysql_bin_log.is_open())
|
||||||
|
|
||||||
/*
|
|
||||||
mysql_bin_log is not open if binlogging or replication
|
|
||||||
is not used
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 */
|
|
||||||
|
|
||||||
/* Commit or rollback the current SQL statement */
|
|
||||||
VOID(ha_autocommit_or_rollback(thd, local_error > 0));
|
|
||||||
}
|
|
||||||
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 ...
|
|
||||||
{
|
|
||||||
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);
|
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
|
||||||
}
|
}
|
||||||
::send_ok(&thd->net,
|
if (!log_delayed)
|
||||||
(thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
|
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
|
||||||
thd->insert_id_used ? thd->insert_id() : 0L,buff);
|
|
||||||
}
|
}
|
||||||
thd->count_cuted_fields=0;
|
|
||||||
|
if (transactional_tables)
|
||||||
|
{
|
||||||
|
if (ha_autocommit_or_rollback(thd, local_error >= 0))
|
||||||
|
local_error=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1436,9 +1436,9 @@ select:
|
|||||||
select_init { Lex->sql_command=SQLCOM_SELECT; };
|
select_init { Lex->sql_command=SQLCOM_SELECT; };
|
||||||
|
|
||||||
select_init:
|
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:
|
select_part2:
|
||||||
@ -2350,7 +2350,7 @@ procedure_clause:
|
|||||||
lex->proc_list.elements=0;
|
lex->proc_list.elements=0;
|
||||||
lex->proc_list.first=0;
|
lex->proc_list.first=0;
|
||||||
lex->proc_list.next= (byte**) &lex->proc_list.first;
|
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;
|
YYABORT;
|
||||||
current_thd->safe_to_cache_query=0;
|
current_thd->safe_to_cache_query=0;
|
||||||
}
|
}
|
||||||
@ -2368,10 +2368,11 @@ procedure_list2:
|
|||||||
procedure_item:
|
procedure_item:
|
||||||
remember_name expr
|
remember_name expr
|
||||||
{
|
{
|
||||||
if (add_proc_to_list($2))
|
LEX *lex= Lex;
|
||||||
|
if (add_proc_to_list(lex->thd, $2))
|
||||||
YYABORT;
|
YYABORT;
|
||||||
if (!$2->name)
|
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:
|
opt_into:
|
||||||
|
16
sql/table.h
16
sql/table.h
@ -117,18 +117,22 @@ struct st_table {
|
|||||||
table_map map; /* ID bit of table (1,2,4,8,16...) */
|
table_map map; /* ID bit of table (1,2,4,8,16...) */
|
||||||
ulong version,flush_version;
|
ulong version,flush_version;
|
||||||
uchar *null_flags;
|
uchar *null_flags;
|
||||||
IO_CACHE *io_cache; /* If sorted trough file*/
|
IO_CACHE *io_cache; /* If sorted trough file*/
|
||||||
byte *record_pointers; /* If sorted in memory */
|
byte *record_pointers; /* If sorted in memory */
|
||||||
ha_rows found_records; /* How many records in sort */
|
ha_rows found_records; /* How many records in sort */
|
||||||
ORDER *group;
|
ORDER *group;
|
||||||
ha_rows quick_rows[MAX_KEY];
|
ha_rows quick_rows[MAX_KEY];
|
||||||
uint quick_key_parts[MAX_KEY];
|
uint quick_key_parts[MAX_KEY];
|
||||||
key_part_map const_key_parts[MAX_KEY];
|
key_part_map const_key_parts[MAX_KEY];
|
||||||
ulong query_id;
|
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;
|
struct st_table *next,*prev;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,10 +152,10 @@ typedef struct st_table_list
|
|||||||
GRANT_INFO grant;
|
GRANT_INFO grant;
|
||||||
thr_lock_type lock_type;
|
thr_lock_type lock_type;
|
||||||
uint outer_join; /* Which join type */
|
uint outer_join; /* Which join type */
|
||||||
|
uint shared; /* Used in union or in multi-upd */
|
||||||
uint32 db_length, real_name_length;
|
uint32 db_length, real_name_length;
|
||||||
bool straight; /* optimize with prev table */
|
bool straight; /* optimize with prev table */
|
||||||
bool updating; /* for replicate-do/ignore table */
|
bool updating; /* for replicate-do/ignore table */
|
||||||
bool shared; /* Used twice in union */
|
|
||||||
bool do_redirect; /* To get the struct in UNION's */
|
bool do_redirect; /* To get the struct in UNION's */
|
||||||
} TABLE_LIST;
|
} TABLE_LIST;
|
||||||
|
|
||||||
|
@ -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)
|
:max_in_memory_size(max_in_memory_size_arg),elements(0)
|
||||||
{
|
{
|
||||||
my_b_clear(&file);
|
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 */
|
/* If the following fail's the next add will also fail */
|
||||||
my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16);
|
my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16);
|
||||||
max_elements= max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size);
|
max_elements= max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user