Commit 2813f568 authored by Michael Widenius's avatar Michael Widenius

Merge with bugfix

sql/multi_range_read.cc:
  Added printing of error if something goes wrong in get_next()
  (Not critical for this bug fix, but this was something that I noticed while testing and found missing)
storage/myisam/mi_rkey.c:
  Fixed wrong error number in mi_yield_and_check_if_killed()
parents bbd4bb31 b12e3796
......@@ -445,7 +445,8 @@ enum ha_base_keytype {
#define HA_ERR_WRONG_CRC 176 /* Wrong CRC on page */
#define HA_ERR_ROW_NOT_VISIBLE 177
#define HA_ERR_TOO_MANY_CONCURRENT_TRXS 178 /*Too many active concurrent transactions */
#define HA_ERR_LAST 178 /* Copy of last error nr */
#define HA_ERR_ABORTED_BY_USER 179
#define HA_ERR_LAST 179 /* Copy of last error nr */
/* Number of different errors */
#define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1)
......
......@@ -135,6 +135,9 @@ extern void my_handler_error_unregister(void);
if we're scanning "t.key BETWEEN 10 AND 20" and got a
"t.key=21" tuple (the engine should stop scanning and return
HA_ERR_END_OF_FILE right away).
-1= ICP_ERROR - Reserved for internal errors in engines. Should not be
returned by index_cond_func_xxx
*/
typedef enum icp_result {
......
......@@ -198,3 +198,4 @@ COUNT(*)
12
DROP PROCEDURE insert_data;
DROP TABLE t1, t2, t3;
drop table if exists t0, t1, t1i, t1m;
......@@ -4,3 +4,191 @@
--source include/icp_tests.inc
--disable_warnings
drop table if exists t0, t1, t1i, t1m;
--enable_warnings
#
# BUG#711565 Index Condition Pushdown can make a thread hold MyISAM locks as well as be unKILLable for long time
# This is not a ready mysqltest testcase but rather a set of queries that
# allow to check the bug[fix] manually. Making this testcase automatic is
# difficult because
# - big tables are involved
# - it's difficult to have automatic checks of whether the query could be
# KILLed that would reliably work on fast/slow buildslaves, etc.
--disable_parsing
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (
kp1 int, kp2 int,
filler char(100),
col int,
key(kp1, kp2)
);
set myisam_sort_buffer_size=32*1000*1000;
insert into t1
select
1000 + A.a + 10*B.a + 100*C.a + 1000*D.a + 10000 * F.a,
1,
'filler-data filler-data filler-data filler-data filler-data',
1
from
t0 A, t0 B, t0 C, t0 D, t0 E, t0 F, t0 G
;
insert into t1 values (990, 100, 'a record to test index_next checks',100);
update t1 set kp2=10 where kp1 between 20000+100 and 28000;
update t1 set kp1=20000 where kp1 between 20000 and 28000;
insert into t1 values (20000, 100, 'last-20K-record',1);
create table t1i like t1;
alter table t1i engine=innodb;
insert into t1i select * from t1;
create table t1m like t1;
alter table t1m engine=maria;
insert into t1m select * from t1;
#
# XtraDB has one check for all kinds of ranges.
#
explain
select * from t1i
where
kp1 < 8000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 10;
#
# MyISAM, check range access + mi_rnext():
# (will return HA_ERR_END_OF_FILE)
explain
select * from t1
where
kp1 < 10000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 10;
#
# MyISAM, check range access + mi_rkey():
# (will return HA_ERR_END_OF_FILE)
explain
select * from t1
where
kp1 >= 999 and kp1 < 10000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 10;
#
# MyISAM, check mi_rnext_same:
#
explain
select * from t1
where
kp1 = 20000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 < 10;
#
# MyISAM, check mi_rprev:
#
explain
select * from t1
where
kp1 = 20000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 20
order by kp1, kp2 desc;
#
# Maria, check range access + maria_rkey():
#
explain
select * from t1m
where
kp1 >= 999 and kp1 < 10000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 10;
#
# Maria, check range access + maria_rnext():
#
explain
select * from t1m
where
kp1 < 10000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 10;
#
# Maria, check maria_rnext_same:
#
explain
select * from t1m
where
kp1 = 20000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 100;
#
# Maria, check maria_rprev:
#
explain
select * from t1m
where
kp1 = 20000 and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
concat(repeat('foo-bar-', 100000), kp2) like '%bar-1%' and
kp2 +1 > 20
order by kp1, kp2 desc;
drop table t0, t1, t1i, t1m;
--enable_parsing
......@@ -64,6 +64,6 @@ static const char *handler_error_messages[]=
"File too short; Expected more data in file",
"Read page with wrong checksum",
"Too many active concurrent transactions",
"Row is not visible by the current transaction"
"Row is not visible by the current transaction",
"Operation was interrupted by end user (probably kill command?)"
};
......@@ -2688,6 +2688,12 @@ void handler::print_error(int error, myf errflag)
SET_FATAL_ERROR;
textno=ER_KEY_NOT_FOUND;
break;
case HA_ERR_ABORTED_BY_USER:
{
DBUG_ASSERT(table->in_use->killed);
table->in_use->send_kill_message();
DBUG_VOID_RETURN;
}
case HA_ERR_WRONG_MRG_TABLE_DEF:
textno=ER_WRONG_MRG_TABLE;
break;
......
......@@ -316,6 +316,8 @@ int Mrr_simple_index_reader::get_next(char **range_info)
!file->mrr_funcs.skip_index_tuple(file->mrr_iter, curr_range->ptr))
break;
}
if (res && res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
file->print_error(res, MYF(0)); // Fatal error
return res;
}
......@@ -333,7 +335,7 @@ int Mrr_simple_index_reader::get_next(char **range_info)
@retval 0 OK, next record was successfully read
@retval HA_ERR_END_OF_FILE End of records
@retval Other Some other error
@retval Other Some other error; Error is printed
*/
int Mrr_ordered_index_reader::get_next(char **range_info)
......@@ -349,7 +351,7 @@ int Mrr_ordered_index_reader::get_next(char **range_info)
{
if ((res != HA_ERR_KEY_NOT_FOUND && res != HA_ERR_END_OF_FILE))
DBUG_RETURN(res); /* Some fatal error */
if (key_buffer->is_empty())
{
DBUG_RETURN(HA_ERR_END_OF_FILE);
......@@ -902,7 +904,7 @@ error:
close_second_handler();
/* Safety, not really needed but: */
strategy= NULL;
DBUG_RETURN(1);
DBUG_RETURN(res);
use_default_impl:
DBUG_ASSERT(primary_file->inited == handler::INDEX);
......
......@@ -758,7 +758,7 @@ void _ma_check_print_warning(HA_CHECK *param, const char *fmt, ...)
static int maria_create_trn_for_mysql(MARIA_HA *info)
{
THD *thd= (THD*) info->external_ptr;
THD *thd= ((TABLE*) info->external_ref)->in_use;
TRN *trn= THD_TRN;
DBUG_ENTER("maria_create_trn_for_mysql");
......@@ -797,6 +797,11 @@ static int maria_create_trn_for_mysql(MARIA_HA *info)
DBUG_RETURN(0);
}
my_bool ma_killed_in_mariadb(MARIA_HA *info)
{
return (((TABLE*) (info->external_ref))->in_use->killed != 0);
}
} /* extern "C" */
/**
......@@ -1013,6 +1018,8 @@ int ha_maria::open(const char *name, int mode, uint test_if_locked)
return (my_errno ? my_errno : -1);
file->s->chst_invalidator= query_cache_invalidate_by_MyISAM_filename_ref;
/* Set external_ref, mainly for temporary tables */
file->external_ref= (void*) table; // For ma_killed()
if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
VOID(maria_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0));
......@@ -2525,6 +2532,7 @@ void ha_maria::drop_table(const char *name)
int ha_maria::external_lock(THD *thd, int lock_type)
{
DBUG_ENTER("ha_maria::external_lock");
file->external_ref= (void*) table; // For ma_killed()
/*
We don't test now_transactional because it may vary between lock/unlock
and thus confuse our reference counting.
......@@ -2543,8 +2551,6 @@ int ha_maria::external_lock(THD *thd, int lock_type)
/* Transactional table */
if (lock_type != F_UNLCK)
{
file->external_ptr= thd; // For maria_register_trn()
if (!file->s->lock_key_trees) // If we don't use versioning
{
/*
......@@ -3392,6 +3398,9 @@ static int ha_maria_init(void *p)
#endif
if (res)
maria_hton= 0;
ma_killed= ma_killed_in_mariadb;
return res ? HA_ERR_INITIALIZATION : 0;
}
......
......@@ -635,3 +635,9 @@ int _ma_flush_table_files(MARIA_HA *info, uint flush_data_or_index,
return 1;
}
my_bool ma_killed_standalone(MARIA_HA *info __attribute__((unused)))
{
return 0;
}
......@@ -669,25 +669,39 @@ int _ma_read_key_record(MARIA_HA *info, uchar *buf, MARIA_RECORD_POS filepos)
will look for column values there)
RETURN
ICP_ERROR Error
ICP_ERROR Error ; my_errno set to HA_ERR_CRASHED
ICP_NO_MATCH Index condition is not satisfied, continue scanning
ICP_MATCH Index condition is satisfied
ICP_OUT_OF_RANGE Index condition is not satisfied, end the scan.
ICP_OUT_OF_RANGE Index condition is not satisfied, end the scan.
my_errno set to HA_ERR_END_OF_FILE
info->cur_row.lastpos is set to HA_OFFSET_ERROR in case of ICP_ERROR or
ICP_OUT_OF_RANGE to indicate that we don't have any active row.
*/
int ma_check_index_cond(register MARIA_HA *info, uint keynr, uchar *record)
ICP_RESULT ma_check_index_cond(register MARIA_HA *info, uint keynr,
uchar *record)
{
ICP_RESULT res= ICP_MATCH;
if (info->index_cond_func)
{
if (_ma_put_key_in_record(info, keynr, FALSE, record))
{
/* Impossible case; Can only happen if bug in code */
maria_print_error(info->s, HA_ERR_CRASHED);
my_errno=HA_ERR_CRASHED;
return -1;
info->cur_row.lastpos= HA_OFFSET_ERROR; /* No active record */
my_errno= HA_ERR_CRASHED;
res= ICP_ERROR;
}
else if ((res= info->index_cond_func(info->index_cond_func_arg)) ==
ICP_OUT_OF_RANGE)
{
/* We got beyond the end of scanned range */
info->cur_row.lastpos= HA_OFFSET_ERROR; /* No active record */
my_errno= HA_ERR_END_OF_FILE;
}
return info->index_cond_func(info->index_cond_func_arg);
}
return 1;
return res;
}
......
......@@ -34,7 +34,7 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
HA_KEYSEG *last_used_keyseg;
uint32 nextflag;
MARIA_KEY key;
int icp_res= 1;
ICP_RESULT icp_res= ICP_MATCH;
DBUG_ENTER("maria_rkey");
DBUG_PRINT("enter", ("base: 0x%lx buf: 0x%lx inx: %d search_flag: %d",
(long) info, (long) buf, inx, search_flag));
......@@ -115,7 +115,7 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
not satisfied with an out-of-range condition.
*/
if ((*share->row_is_visible)(info) &&
((icp_res= ma_check_index_cond(info, inx, buf)) != 0))
((icp_res= ma_check_index_cond(info, inx, buf)) != ICP_NO_MATCH))
break;
/* The key references a concurrently inserted record. */
......@@ -145,6 +145,18 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
if (_ma_search_next(info, &lastkey, maria_readnext_vec[search_flag],
info->s->state.key_root[inx]))
break; /* purecov: inspected */
/*
If we are at the last key on the key page, allow writers to
access the index.
*/
if (info->int_keypos >= info->int_maxpos &&
ma_yield_and_check_if_killed(info, inx))
{
DBUG_ASSERT(info->cur_row.lastpos == HA_OFFSET_ERROR);
break;
}
/*
Check that the found key does still match the search.
_ma_search_next() delivers the next key regardless of its
......@@ -164,15 +176,19 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
} while (!(*share->row_is_visible)(info) ||
((icp_res= ma_check_index_cond(info, inx, buf)) == 0));
}
else
{
DBUG_ASSERT(info->cur_row.lastpos);
}
}
if (share->lock_key_trees)
rw_unlock(&keyinfo->root_lock);
if (info->cur_row.lastpos == HA_OFFSET_ERROR || (icp_res != 1))
if (info->cur_row.lastpos == HA_OFFSET_ERROR)
{
if (icp_res == 2)
if (icp_res == ICP_OUT_OF_RANGE)
{
info->cur_row.lastpos= HA_OFFSET_ERROR;
/* We don't want HA_ERR_END_OF_FILE in this particular case */
my_errno= HA_ERR_KEY_NOT_FOUND;
}
fast_ma_writeinfo(info);
......@@ -214,3 +230,37 @@ err:
info->update|=HA_STATE_NEXT_FOUND; /* Previous gives last row */
DBUG_RETURN(my_errno);
} /* _ma_rkey */
/*
Yield to possible other writers during a index scan.
Check also if we got killed by the user and if yes, return
HA_ERR_LOCK_WAIT_TIMEOUT
return 0 ok
return 1 Query has been requested to be killed
*/
my_bool ma_yield_and_check_if_killed(MARIA_HA *info, int inx)
{
MARIA_SHARE *share;
if (ma_killed(info))
{
/* purecov: begin tested */
/* Mark that we don't have an active row */
info->cur_row.lastpos= HA_OFFSET_ERROR;
/* Set error that we where aborted by kill from application */
my_errno= HA_ERR_ABORTED_BY_USER;
return 1;
/* purecov: end */
}
if ((share= info->s)->lock_key_trees)
{
/* Give writers a chance to access index */
rw_unlock(&share->keyinfo[inx].root_lock);
rw_rdlock(&share->keyinfo[inx].root_lock);
}
return 0;
}
......@@ -30,7 +30,7 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
uint flag;
MARIA_SHARE *share= info->s;
MARIA_KEYDEF *keyinfo;
int icp_res= 1;
ICP_RESULT icp_res= ICP_MATCH;
DBUG_ENTER("maria_rnext");
if ((inx = _ma_check_index(info,inx)) < 0)
......@@ -92,8 +92,20 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
if (!error)
{
while (!(*share->row_is_visible)(info) ||
((icp_res= ma_check_index_cond(info, inx, buf)) == 0))
((icp_res= ma_check_index_cond(info, inx, buf)) == ICP_NO_MATCH))
{
/*
If we are at the last key on the key page, allow writers to
access the index.
*/
if (info->int_keypos >= info->int_maxpos &&
ma_yield_and_check_if_killed(info, inx))
{
/* my_errno is set by ma_yield_and_check_if_killed() */
error= 1;
break;
}
/* Skip rows inserted by other threads since we got a lock */
if ((error= _ma_search_next(info, &info->last_key,
SEARCH_BIGGER,
......@@ -108,16 +120,15 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= HA_STATE_NEXT_FOUND;
if (icp_res == 2)
my_errno=HA_ERR_END_OF_FILE; /* got beyond the end of scanned range */
if (error || icp_res != 1)
if (error || icp_res != ICP_MATCH)
{
fast_ma_writeinfo(info);
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
my_errno= HA_ERR_END_OF_FILE;
}
else if (!buf)
{
fast_ma_writeinfo(info);
DBUG_RETURN(info->cur_row.lastpos == HA_OFFSET_ERROR ? my_errno : 0);
}
else if (!(*info->read_record)(info, buf, info->cur_row.lastpos))
......
......@@ -30,7 +30,7 @@ int maria_rnext_same(MARIA_HA *info, uchar *buf)
int error;
uint inx,not_used[2];
MARIA_KEYDEF *keyinfo;
int icp_res= 1;
ICP_RESULT icp_res= ICP_MATCH;
DBUG_ENTER("maria_rnext_same");
if ((int) (inx= info->lastinx) < 0 ||
......@@ -80,9 +80,19 @@ int maria_rnext_same(MARIA_HA *info, uchar *buf)
info->cur_row.lastpos= HA_OFFSET_ERROR;
break;
}
/*
If we are at the last key on the key page, allow writers to
access the index.
*/
if (info->int_keypos >= info->int_maxpos &&
ma_yield_and_check_if_killed(info, inx))
{
error= 1;
break;
}
/* Skip rows that are inserted by other threads since we got a lock */
if ((info->s->row_is_visible)(info) &&
((icp_res= ma_check_index_cond(info, inx, buf)) != 0))
((icp_res= ma_check_index_cond(info, inx, buf)) != ICP_NO_MATCH))
break;
}
}
......@@ -92,16 +102,15 @@ int maria_rnext_same(MARIA_HA *info, uchar *buf)
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= HA_STATE_NEXT_FOUND | HA_STATE_RNEXT_SAME;
if (icp_res == 2)
my_errno=HA_ERR_END_OF_FILE; /* got beyond the end of scanned range */
if (error || icp_res != 1)
if (error || icp_res != ICP_MATCH)
{
fast_ma_writeinfo(info);
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
my_errno= HA_ERR_END_OF_FILE;
}
else if (!buf)
{
fast_ma_writeinfo(info);
DBUG_RETURN(info->cur_row.lastpos == HA_OFFSET_ERROR ? my_errno : 0);
}
else if (!(*info->read_record)(info, buf, info->cur_row.lastpos))
......
......@@ -28,6 +28,7 @@ int maria_rprev(MARIA_HA *info, uchar *buf, int inx)
register uint flag;
MARIA_SHARE *share= info->s;
MARIA_KEYDEF *keyinfo;
ICP_RESULT icp_res= ICP_MATCH;
DBUG_ENTER("maria_rprev");
if ((inx = _ma_check_index(info,inx)) < 0)
......@@ -55,8 +56,24 @@ int maria_rprev(MARIA_HA *info, uchar *buf, int inx)
if (!error)
{
while (!(*share->row_is_visible)(info))
my_off_t cur_keypage= info->last_keypage;
while (!(*share->row_is_visible)(info) ||
((icp_res= ma_check_index_cond(info, inx, buf)) == ICP_NO_MATCH))
{
/*
If we are at the last (i.e. first?) key on the key page,
allow writers to access the index.
*/
if (info->last_keypage != cur_keypage)
{
cur_keypage= info->last_keypage;
if (ma_yield_and_check_if_killed(info, inx))
{
error= 1;
break;
}
}
/* Skip rows that are inserted by other threads since we got a lock */
if ((error= _ma_search_next(info, &info->last_key,
SEARCH_SMALLER,
......@@ -68,13 +85,16 @@ int maria_rprev(MARIA_HA *info, uchar *buf, int inx)
rw_unlock(&keyinfo->root_lock);
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= HA_STATE_PREV_FOUND;
if (error)
if (error || icp_res != ICP_MATCH)
{
fast_ma_writeinfo(info);
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
my_errno= HA_ERR_END_OF_FILE;
}
else if (!buf)
{
fast_ma_writeinfo(info);
DBUG_RETURN(info->cur_row.lastpos == HA_OFFSET_ERROR ? my_errno : 0);
}
else if (!(*info->read_record)(info, buf, info->cur_row.lastpos))
......
......@@ -141,7 +141,11 @@ static int _ma_search_no_save(register MARIA_HA *info, MARIA_KEY *key,
flag= (*keyinfo->bin_search)(key, &page, nextflag, &keypos, lastkey,
&last_key_not_used);
if (flag == MARIA_FOUND_WRONG_KEY)
DBUG_RETURN(-1);
{
maria_print_error(info->s, HA_ERR_CRASHED);
my_errno= HA_ERR_CRASHED;
goto err;
}
page_flag= page.flag;
used_length= page.size;
nod_flag= page.node;
......
......@@ -107,3 +107,6 @@ static int always_valid(const char *filename __attribute__((unused)))
}
int (*maria_test_invalid_symlink)(const char *filename)= always_valid;
my_bool (*ma_killed)(MARIA_HA *)= ma_killed_standalone;
......@@ -492,7 +492,6 @@ struct st_maria_handler
{
MARIA_SHARE *s; /* Shared between open:s */
struct st_ma_transaction *trn; /* Pointer to active transaction */
void *external_ptr; /* Pointer to THD in mysql */
MARIA_STATUS_INFO *state, state_save;
MARIA_STATUS_INFO *state_start; /* State at start of transaction */
MARIA_ROW cur_row; /* The active row that we just read */
......@@ -509,6 +508,7 @@ struct st_maria_handler
DYNAMIC_ARRAY *ft1_to_ft2; /* used only in ft1->ft2 conversion */
MEM_ROOT ft_memroot; /* used by the parser */
MYSQL_FTPARSER_PARAM *ftparser_param; /* share info between init/deinit */
void *external_ref; /* For MariaDB TABLE */
uchar *buff; /* page buffer */
uchar *keyread_buff; /* Buffer for last key read */
uchar *lastkey_buff; /* Last used search key */
......@@ -813,6 +813,7 @@ extern my_bool maria_inited, maria_in_ha_maria, maria_recovery_changed_data;
extern my_bool maria_recovery_verbose;
extern HASH maria_stored_state;
extern int (*maria_create_trn_hook)(MARIA_HA *);
extern my_bool (*ma_killed)(MARIA_HA *);
/* This is used by _ma_calc_xxx_key_length och _ma_store_key */
typedef struct st_maria_s_param
......@@ -1281,4 +1282,8 @@ extern my_bool maria_flush_log_for_page_none(uchar *page,
extern PAGECACHE *maria_log_pagecache;
extern void ma_set_index_cond_func(MARIA_HA *info, index_cond_func_t func,
void *func_arg);
int ma_check_index_cond(register MARIA_HA *info, uint keynr, uchar *record);
ICP_RESULT ma_check_index_cond(register MARIA_HA *info, uint keynr, uchar *record);
extern my_bool ma_yield_and_check_if_killed(MARIA_HA *info, int inx);
extern my_bool ma_killed_standalone(MARIA_HA *);
......@@ -538,6 +538,13 @@ void mi_check_print_warning(HA_CHECK *param, const char *fmt,...)
va_end(args);
}
/* Return 1 if user have requested query to be killed */
my_bool mi_killed_in_mariadb(MI_INFO *info)
{
return (((TABLE*) (info->external_ref))->in_use->killed != 0);
}
}
......@@ -699,6 +706,8 @@ int ha_myisam::open(const char *name, int mode, uint test_if_locked)
return (my_errno ? my_errno : -1);
file->s->chst_invalidator= query_cache_invalidate_by_MyISAM_filename_ref;
/* Set external_ref, mainly for temporary tables */
file->external_ref= (void*) table; // For mi_killed()
if (!table->s->tmp_table) /* No need to perform a check for tmp table */
{
......@@ -1971,6 +1980,7 @@ int ha_myisam::delete_table(const char *name)
int ha_myisam::external_lock(THD *thd, int lock_type)
{
file->external_ref= (void*) table; // For mi_killed()
return mi_lock_database(file, !table->s->tmp_table ?
lock_type : ((lock_type == F_UNLCK) ?
F_UNLCK : F_EXTRA_LCK));
......@@ -2219,6 +2229,7 @@ static int myisam_init(void *p)
myisam_hton->create= myisam_create_handler;
myisam_hton->panic= myisam_panic;
myisam_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES;
mi_killed= mi_killed_in_mariadb;
return 0;
}
......
......@@ -473,3 +473,8 @@ int mi_reset(MI_INFO *info)
HA_STATE_PREV_FOUND);
DBUG_RETURN(error);
}
my_bool mi_killed_standalone(MI_INFO *info __attribute__((unused)))
{
return 0;
}
......@@ -510,15 +510,26 @@ int _mi_read_key_record(MI_INFO *info, my_off_t filepos, uchar *buf)
ICP_OUT_OF_RANGE Index condition is not satisfied, end the scan.
*/
int mi_check_index_cond(register MI_INFO *info, uint keynr, uchar *record)
ICP_RESULT mi_check_index_cond(register MI_INFO *info, uint keynr,
uchar *record)
{
ICP_RESULT res;
if (_mi_put_key_in_record(info, keynr, FALSE, record))
{
/* Impossible case; Can only happen if bug in code */
mi_print_error(info->s, HA_ERR_CRASHED);
my_errno=HA_ERR_CRASHED;
return ICP_ERROR;
info->lastpos= HA_OFFSET_ERROR; /* No active record */
my_errno= HA_ERR_CRASHED;
res= ICP_ERROR;
}
return info->index_cond_func(info->index_cond_func_arg);
else if ((res= info->index_cond_func(info->index_cond_func_arg)) ==
ICP_OUT_OF_RANGE)
{
/* We got beyond the end of scanned range */
info->lastpos= HA_OFFSET_ERROR; /* No active record */
my_errno= HA_ERR_END_OF_FILE;
}
return res;
}
/*
......
......@@ -88,6 +88,7 @@ int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
my_errno=HA_ERR_CRASHED;
if (share->concurrent_insert)
rw_unlock(&share->key_root_lock[inx]);
fast_mi_writeinfo(info);
goto err;
}
break;
......@@ -131,7 +132,10 @@ int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
info->lastkey_length,
myisam_readnext_vec[search_flag],
info->s->state.key_root[inx]))
{
info->lastpos= HA_OFFSET_ERROR;
break;
}
/*
Check that the found key does still match the search.
_mi_search_next() delivers the next key regardless of its
......@@ -145,13 +149,22 @@ int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
info->lastpos= HA_OFFSET_ERROR;
break;
}
/*
If we are at the last key on the key page, allow writers to
access the index.
*/
if (info->int_keypos >= info->int_maxpos &&
mi_yield_and_check_if_killed(info, inx))
{
/* Aborted by user */
buf= 0; /* Fast abort */
}
}
if (res == ICP_OUT_OF_RANGE)
{
info->lastpos= HA_OFFSET_ERROR;
if (share->concurrent_insert)
rw_unlock(&share->key_root_lock[inx]);
DBUG_RETURN((my_errno= HA_ERR_KEY_NOT_FOUND));
/* Change error from HA_ERR_END_OF_FILE */
DBUG_ASSERT(info->lastpos == HA_OFFSET_ERROR);
my_errno= HA_ERR_KEY_NOT_FOUND;
}
/*
Error if no row found within the data file. (Bug #29838)
......@@ -164,29 +177,43 @@ int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
my_errno= HA_ERR_KEY_NOT_FOUND;
}
}
else
{
DBUG_ASSERT(info->lastpos= HA_OFFSET_ERROR);
}
}
if (share->concurrent_insert)
rw_unlock(&share->key_root_lock[inx]);
/* Calculate length of the found key; Used by mi_rnext_same */
if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg &&
info->lastpos != HA_OFFSET_ERROR)
info->last_rkey_length= _mi_keylength_part(keyinfo, info->lastkey,
last_used_keyseg);
else
info->last_rkey_length= pack_key_length;
/* Check if we don't want to have record back, only error message */
if (!buf)
DBUG_RETURN(info->lastpos == HA_OFFSET_ERROR ? my_errno : 0);
info->last_rkey_length= pack_key_length;
if (!(*info->read_record)(info,info->lastpos,buf))
if (info->lastpos == HA_OFFSET_ERROR) /* No such record */
{
info->update|= HA_STATE_AKTIV; /* Record is read */
DBUG_RETURN(0);
fast_mi_writeinfo(info);
if (!buf)
DBUG_RETURN(my_errno);
}
else
{
/* Calculate length of the found key; Used by mi_rnext_same */
if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg)
info->last_rkey_length= _mi_keylength_part(keyinfo, info->lastkey,
last_used_keyseg);
info->lastpos = HA_OFFSET_ERROR; /* Didn't find key */
/* Check if we don't want to have record back, only error message */
if (!buf)
{
fast_mi_writeinfo(info);
DBUG_RETURN(0);
}
if (!(*info->read_record)(info,info->lastpos,buf))
{
info->update|= HA_STATE_AKTIV; /* Record is read */
DBUG_RETURN(0);
}
DBUG_PRINT("error", ("Didn't find row. Error %d", my_errno));
info->lastpos= HA_OFFSET_ERROR; /* Didn't find row */
}
/* Store last used key as a base for read next */
memcpy(info->lastkey,key_buff,pack_key_length);
......@@ -199,3 +226,36 @@ int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
err:
DBUG_RETURN(my_errno);
} /* _mi_rkey */
/*
Yield to possible other writers during a index scan.
Check also if we got killed by the user and if yes, return
HA_ERR_LOCK_WAIT_TIMEOUT
return 0 ok
return 1 Query has been requested to be killed
*/
my_bool mi_yield_and_check_if_killed(MI_INFO *info, int inx)
{
MYISAM_SHARE *share;
if (mi_killed(info))
{
/* purecov: begin tested */
info->lastpos= HA_OFFSET_ERROR;
/* Set error that we where aborted by kill from application */
my_errno= HA_ERR_ABORTED_BY_USER;
return 1;
/* purecov: end */
}
if ((share= info->s)->concurrent_insert)
{
/* Give writers a chance to access index */
rw_unlock(&share->key_root_lock[inx]);
rw_rdlock(&share->key_root_lock[inx]);
}
return 0;
}
......@@ -28,7 +28,7 @@ int mi_rnext(MI_INFO *info, uchar *buf, int inx)
{
int error,changed;
uint flag;
ICP_RESULT res= 0;
ICP_RESULT icp_res= ICP_MATCH;
uint update_mask= HA_STATE_NEXT_FOUND;
DBUG_ENTER("mi_rnext");
......@@ -102,8 +102,19 @@ int mi_rnext(MI_INFO *info, uchar *buf, int inx)
while ((info->s->concurrent_insert &&
info->lastpos >= info->state->data_file_length) ||
(info->index_cond_func &&
(res= mi_check_index_cond(info, inx, buf)) == ICP_NO_MATCH))
(icp_res= mi_check_index_cond(info, inx, buf)) == ICP_NO_MATCH))
{
/*
If we are at the last key on the key page, allow writers to
access the index.
*/
if (info->int_keypos >= info->int_maxpos &&
mi_yield_and_check_if_killed(info, inx))
{
error= 1;
break;
}
/*
Skip rows that are either inserted by other threads since
we got a lock or do not match pushed index conditions
......@@ -115,13 +126,6 @@ int mi_rnext(MI_INFO *info, uchar *buf, int inx)
info->s->state.key_root[inx])))
break;
}
if (!error && res == ICP_OUT_OF_RANGE)
{
if (info->s->concurrent_insert)
rw_unlock(&info->s->key_root_lock[inx]);
info->lastpos= HA_OFFSET_ERROR;
DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
}
}
if (info->s->concurrent_insert)
......@@ -131,13 +135,15 @@ int mi_rnext(MI_INFO *info, uchar *buf, int inx)
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= update_mask;
if (error)
if (error || icp_res != ICP_MATCH)
{
fast_mi_writeinfo(info);
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
}
else if (!buf)
{
fast_mi_writeinfo(info);
DBUG_RETURN(info->lastpos==HA_OFFSET_ERROR ? my_errno : 0);
}
else if (!(*info->read_record)(info,info->lastpos,buf))
......
......@@ -29,6 +29,7 @@ int mi_rnext_same(MI_INFO *info, uchar *buf)
int error;
uint inx,not_used[2];
MI_KEYDEF *keyinfo;
ICP_RESULT icp_res= ICP_MATCH;
DBUG_ENTER("mi_rnext_same");
if ((int) (inx=info->lastinx) < 0 || info->lastpos == HA_OFFSET_ERROR)
......@@ -63,6 +64,17 @@ int mi_rnext_same(MI_INFO *info, uchar *buf)
}
for (;;)
{
/*
If we are at the last key on the key page, allow writers to
access the index.
*/
if (info->int_keypos >= info->int_maxpos &&
mi_yield_and_check_if_killed(info, inx))
{
error=1;
break;
}
if ((error=_mi_search_next(info,keyinfo,info->lastkey,
info->lastkey_length,SEARCH_BIGGER,
info->s->state.key_root[inx])))
......@@ -78,26 +90,30 @@ int mi_rnext_same(MI_INFO *info, uchar *buf)
/*
Skip
- rows that are inserted by other threads since we got a lock
- rows that don't match index condition */
- rows that don't match index condition
*/
if (info->lastpos < info->state->data_file_length &&
(!info->index_cond_func ||
mi_check_index_cond(info, inx, buf) != ICP_NO_MATCH))
(icp_res= mi_check_index_cond(info, inx, buf)) != ICP_NO_MATCH))
break;
}
}
if (info->s->concurrent_insert)
rw_unlock(&info->s->key_root_lock[inx]);
/* Don't clear if database-changed */
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= HA_STATE_NEXT_FOUND | HA_STATE_RNEXT_SAME;
if (error)
if (error || icp_res != ICP_MATCH)
{
fast_mi_writeinfo(info);
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
}
else if (!buf)
{
fast_mi_writeinfo(info);
DBUG_RETURN(info->lastpos==HA_OFFSET_ERROR ? my_errno : 0);
}
else if (!(*info->read_record)(info,info->lastpos,buf))
......
......@@ -27,6 +27,7 @@ int mi_rprev(MI_INFO *info, uchar *buf, int inx)
int error,changed;
register uint flag;
MYISAM_SHARE *share=info->s;
ICP_RESULT icp_res= ICP_MATCH;
DBUG_ENTER("mi_rprev");
if ((inx = _mi_check_index(info,inx)) < 0)
......@@ -53,12 +54,26 @@ int mi_rprev(MI_INFO *info, uchar *buf, int inx)
if (!error)
{
int res= 0;
my_off_t cur_keypage= info->last_keypage;
while ((share->concurrent_insert &&
info->lastpos >= info->state->data_file_length) ||
(info->index_cond_func &&
!(res= mi_check_index_cond(info, inx, buf))))
(icp_res= mi_check_index_cond(info, inx, buf)) == ICP_NO_MATCH))
{
/*
If we are at the last (i.e. first?) key on the key page,
allow writers to access the index.
*/
if (info->last_keypage != cur_keypage)
{
cur_keypage= info->last_keypage;
if (mi_yield_and_check_if_killed(info, inx))
{
error= 1;
break;
}
}
/*
Skip rows that are either inserted by other threads since
we got a lock or do not match pushed index conditions
......@@ -69,13 +84,6 @@ int mi_rprev(MI_INFO *info, uchar *buf, int inx)
share->state.key_root[inx])))
break;
}
if (!error && res == 2)
{
if (share->concurrent_insert)
rw_unlock(&share->key_root_lock[inx]);
info->lastpos= HA_OFFSET_ERROR;
DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
}
}
if (share->concurrent_insert)
......@@ -83,13 +91,16 @@ int mi_rprev(MI_INFO *info, uchar *buf, int inx)
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= HA_STATE_PREV_FOUND;
if (error)
if (error || icp_res != ICP_MATCH)
{
fast_mi_writeinfo(info);
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
}
else if (!buf)
{
fast_mi_writeinfo(info);
DBUG_RETURN(info->lastpos==HA_OFFSET_ERROR ? my_errno : 0);
}
else if (!(*info->read_record)(info,info->lastpos,buf))
......
......@@ -89,7 +89,10 @@ int _mi_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag,
&keypos,lastkey, &last_key);
if (flag == MI_FOUND_WRONG_KEY)
DBUG_RETURN(-1);
{
my_errno= HA_ERR_CRASHED;
goto err;
}
nod_flag=mi_test_if_nod(buff);
maxpos=buff+mi_getint(buff)-1;
......
......@@ -41,6 +41,7 @@ my_off_t myisam_max_temp_length= MAX_FILE_SIZE;
ulong myisam_bulk_insert_tree_size=8192*1024;
ulong myisam_data_pointer_size=4;
ulonglong myisam_mmap_size= SIZE_T_MAX, myisam_mmap_used= 0;
my_bool (*mi_killed)(MI_INFO *)= mi_killed_standalone;
static int always_valid(const char *filename __attribute__((unused)))
{
......
......@@ -248,6 +248,7 @@ struct st_myisam_info
DYNAMIC_ARRAY *ft1_to_ft2; /* used only in ft1->ft2 conversion */
MEM_ROOT ft_memroot; /* used by the parser */
MYSQL_FTPARSER_PARAM *ftparser_param; /* share info between init/deinit */
void *external_ref; /* For MariaDB TABLE */
char *filename; /* parameter to open filename */
uchar *buff, /* Temp area for key */
*lastkey, *lastkey2; /* Last used search key */
......@@ -433,6 +434,7 @@ extern uint NEAR myisam_read_vec[], NEAR myisam_readnext_vec[];
extern uint myisam_quick_table_bits;
extern File myisam_log_file;
extern ulong myisam_pid;
extern my_bool (*mi_killed)(MI_INFO *);
/* This is used by _mi_calc_xxx_key_length och _mi_store_key */
......@@ -593,6 +595,8 @@ extern ulonglong mi_safe_mul(ulonglong a, ulonglong b);
extern int _mi_ft_update(MI_INFO *info, uint keynr, uchar *keybuf,
const uchar *oldrec, const uchar *newrec,
my_off_t pos);
extern my_bool mi_yield_and_check_if_killed(MI_INFO *info, int inx);
extern my_bool mi_killed_standalone(MI_INFO *);
struct st_sort_info;
......@@ -729,7 +733,7 @@ my_bool mi_dynmap_file(MI_INFO *info, my_off_t size);
int mi_munmap_file(MI_INFO *info);
void mi_remap_file(MI_INFO *info, my_off_t size);
int mi_check_index_cond(register MI_INFO *info, uint keynr, uchar *record);
ICP_RESULT mi_check_index_cond(register MI_INFO *info, uint keynr, uchar *record);
/* Functions needed by mi_check */
int killed_ptr(HA_CHECK *param);
void mi_check_print_error _VARARGS((HA_CHECK *param, const char *fmt, ...));
......
......@@ -126,7 +126,7 @@ static pthread_mutex_t commit_cond_m;
static bool innodb_inited = 0;
C_MODE_START
static int index_cond_func_innodb(void *arg);
static xtradb_icp_result_t index_cond_func_innodb(void *arg);
C_MODE_END
......@@ -853,6 +853,9 @@ convert_error_code_to_mysql(
case DB_RECORD_NOT_FOUND:
return(HA_ERR_NO_ACTIVE_RECORD);
case DB_SEARCH_ABORTED_BY_USER:
return(HA_ERR_ABORTED_BY_USER);
case DB_DEADLOCK:
/* Since we rolled back the whole transaction, we must
tell it also to MySQL so that MySQL knows to empty the
......@@ -12082,6 +12085,14 @@ ha_rows ha_innobase::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
}
/*
A helper function used only in index_cond_func_innodb
*/
bool ha_innobase::is_thd_killed()
{
return test(user_thd->killed);
}
/**
* Index Condition Pushdown interface implementation
......@@ -12094,15 +12105,18 @@ C_MODE_START
See note on ICP_RESULT for return values description.
*/
static int index_cond_func_innodb(void *arg)
static xtradb_icp_result_t index_cond_func_innodb(void *arg)
{
ha_innobase *h= (ha_innobase*)arg;
if (h->is_thd_killed())
return XTRADB_ICP_ERROR;
if (h->end_range)
{
if (h->compare_key2(h->end_range) > 0)
return ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
return XTRADB_ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
}
return h->pushed_idx_cond->val_int()? ICP_MATCH : ICP_NO_MATCH;
return h->pushed_idx_cond->val_int()? XTRADB_ICP_MATCH : XTRADB_ICP_NO_MATCH;
}
C_MODE_END
......
......@@ -240,6 +240,9 @@ public:
DsMrr_impl ds_mrr;
Item *idx_cond_push(uint keyno, Item* idx_cond);
/* An helper function for index_cond_func_innodb: */
bool is_thd_killed();
};
/* Some accessor functions which the InnoDB plugin needs, but which
......
......@@ -105,7 +105,8 @@ enum db_err {
DB_STRONG_FAIL,
DB_ZIP_OVERFLOW,
DB_RECORD_NOT_FOUND = 1500,
DB_END_OF_INDEX
DB_END_OF_INDEX,
DB_SEARCH_ABORTED_BY_USER= 1533
};
#endif
......@@ -577,7 +577,16 @@ struct mysql_row_templ_struct {
#define ROW_PREBUILT_ALLOCATED 78540783
#define ROW_PREBUILT_FREED 26423527
typedef int (*index_cond_func_t)(void *param);
typedef enum xtradb_icp_result {
XTRADB_ICP_ERROR=-1,
XTRADB_ICP_NO_MATCH=0,
XTRADB_ICP_MATCH=1,
XTRADB_ICP_OUT_OF_RANGE=2,
XTRADB_ICP_ABORTED_BY_USER=3,
} xtradb_icp_result_t;
typedef xtradb_icp_result_t (*index_cond_func_t)(void *param);
/** A struct for (sometimes lazily) prebuilt structures in an Innobase table
handle used within MySQL; these are used to save CPU time. */
......
......@@ -3348,7 +3348,8 @@ and fetch prev. NOTE that if we do a search with a full key value
from a unique index (ROW_SEL_EXACT), then we will not store the cursor
position and fetch next or fetch prev must not be tried to the cursor!
@return DB_SUCCESS, DB_RECORD_NOT_FOUND, DB_END_OF_INDEX, DB_DEADLOCK,
DB_LOCK_TABLE_FULL, DB_CORRUPTION, or DB_TOO_BIG_RECORD */
DB_LOCK_TABLE_FULL, DB_CORRUPTION, DB_SEARCH_ABORTED_BY_USER or
DB_TOO_BIG_RECORD */
UNIV_INTERN
ulint
row_search_for_mysql(
......@@ -4396,12 +4397,15 @@ idx_cond_check:
*/
ut_ad(ib_res);
res= prebuilt->idx_cond_func(prebuilt->idx_cond_func_arg);
if (res == 0)
if (res == XTRADB_ICP_NO_MATCH)
goto next_rec;
if (res == 2) {
err = DB_RECORD_NOT_FOUND;
else if (res != XTRADB_ICP_MATCH) {
err= (res == XTRADB_ICP_ABORTED_BY_USER ?
DB_SEARCH_ABORTED_BY_USER :
DB_RECORD_NOT_FOUND);
goto idx_cond_failed;
}
/* res == XTRADB_ICP_MATCH */
}
/* Get the clustered index record if needed, if we did not do the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment