mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Fix for lp:711565 "Index Condition Pushdown can make a thread hold MyISAM locks as well as be unKILLable for long time"
- In Maria/MyISAM: Release/re-acquire locks to give queries that wait on them a chance to make progress - In Maria/MyISAM: Change from numeric constants to ICP_RES values. - In Maria: Do check index condition in maria_rprev() (was lost in the merge/backport?) - In Maria/MyISAM/XtraDB: Check if the query was killed, and return immediately if it was. Added new storage engine error: HA_ERR_ABORTED_BY_USER, for handler to signal that it detected a kill of the query and aborted Authors: Sergey Petrunia & Monty include/my_base.h: Added HA_ERR_ABORTED_BY_USER, for handler to signal that it detected a kill of the query and aborted include/my_handler.h: Added comment mysql-test/r/myisam_icp.result: Updated test mysql-test/t/myisam_icp.test: Drop used tables at start of test Added test case that can help with manual testing of killing index condition pushdown query. mysys/my_handler_errors.h: Text for new storage engine error sql/handler.cc: If engine got HA_ERR_ABORTED_BY_USER, send kill message. sql/multi_range_read.cc: Return error code storage/maria/ha_maria.cc: Added ma_killed_in_mariadb() to detect kill. Ensure that file->external_ref points to TABLE object. storage/maria/ma_extra.c: Dummy test-if-killed for standalone storage/maria/ma_key.c: If ma_check_index_cond() fails, set my_errno and info->cur_row.lastpos storage/maria/ma_rkey.c: Release/re-acquire locks to give queries that wait on them a chance to make progress Check if the query was killed, and return immediately if it was storage/maria/ma_rnext.c: Check if the query was killed, and return immediately if it was Added missing fast_ma_writeinfo(info) storage/maria/ma_rnext_same.c: Check if the query was killed, and return immediately if it was Added missing fast_ma_writeinfo(info) storage/maria/ma_rprev.c: Check if the query was killed, and return immediately if it was Added missing fast_ma_writeinfo(info) and ma_check_index_cond() storage/maria/ma_search.c: Give error message if we find a wrong key storage/maria/ma_static.c: Added pointer to test-if-killed function storage/maria/maria_def.h: New prototypes storage/myisam/ha_myisam.cc: Added mi_killed_in_mariadb() Ensure that file->external_ref points to TABLE object. storage/myisam/mi_extra.c: Dummy test-if-killed for standalone storage/myisam/mi_key.c: If ma_check_index_cond() fails, set my_errno and info->lastpos storage/myisam/mi_rkey.c: Ensure that info->lastpos= HA_OFFSET_ERROR in case of error Release/re-acquire locks to give queries that wait on them a chance to make progress Check if the query was killed, and return immediately if it was Reorder code to do less things in case of error. Added missing fast_mi_writeinfo() storage/myisam/mi_rnext.c: Check if the query was killed, and return immediately if it was Simplify old ICP code Added missing fast_ma_writeinfo(info) storage/myisam/mi_rnext_same.c: Check if the query was killed, and return immediately if it was Added missing fast_mi_writeinfo(info) storage/myisam/mi_rprev.c: Check if the query was killed, and return immediately if it was Simplify error handling of ICP Added missing fast_mi_writeinfo(info) storage/myisam/mi_search.c: Give error message if we find a wrong key storage/myisam/mi_static.c: Added pointer to test-if-killed function storage/myisam/myisamdef.h: New prototypes storage/xtradb/handler/ha_innodb.cc: Added DB_SEARCH_ABORTED_BY_USER and ha_innobase::is_thd_killed() Check if the query was killed, and return immediately if it was storage/xtradb/handler/ha_innodb.h: Added prototype storage/xtradb/include/db0err.h: Added DB_SEARCH_ABORTED_BY_USER storage/xtradb/include/row0mysql.h: Added possible ICP errors storage/xtradb/row/row0sel.c: Use ICP errors instead of constants. Detect if killed and return B_SEARCH_ABORTED_BY_USER
This commit is contained in:
@ -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 {
|
||||
|
@ -148,3 +148,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;
|
||||
|
@ -902,7 +902,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.
|
||||
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);
|
||||
info->cur_row.lastpos= HA_OFFSET_ERROR; /* No active record */
|
||||
my_errno= HA_ERR_CRASHED;
|
||||
return -1;
|
||||
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->cur_row.lastpos= HA_OFFSET_ERROR; /* No active record */
|
||||
my_errno= HA_ERR_END_OF_FILE;
|
||||
}
|
||||
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,35 @@ 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))
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
info->lastpos= HA_OFFSET_ERROR; /* No active record */
|
||||
my_errno= HA_ERR_CRASHED;
|
||||
return ICP_ERROR;
|
||||
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]);
|
||||
|
||||
info->last_rkey_length= pack_key_length;
|
||||
|
||||
if (info->lastpos == HA_OFFSET_ERROR) /* No such record */
|
||||
{
|
||||
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->lastpos != HA_OFFSET_ERROR)
|
||||
if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg)
|
||||
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);
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
info->lastpos = HA_OFFSET_ERROR; /* Didn't find key */
|
||||
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,32 @@ 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))
|
||||
{
|
||||
info->lastpos= HA_OFFSET_ERROR;
|
||||
my_errno= HA_ERR_KEY_NOT_FOUND;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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
|
||||
|
Reference in New Issue
Block a user