diff options
author | Michael Widenius <monty@askmonty.org> | 2011-02-18 21:45:32 +0200 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2011-02-18 21:45:32 +0200 |
commit | 2813f568b74a65d5d2ce8bbcada2c99d1a01815a (patch) | |
tree | f952fbb67d84e7ee8fe7bf9b7d271a6bebc35c78 /storage | |
parent | bbd4bb310d7b500a57be56b8f0501fa8eee6d40f (diff) | |
parent | b12e3796dc70466eca5ef3f25d51234b32af5113 (diff) | |
download | mariadb-git-2813f568b74a65d5d2ce8bbcada2c99d1a01815a.tar.gz |
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()
Diffstat (limited to 'storage')
-rw-r--r-- | storage/maria/ha_maria.cc | 15 | ||||
-rw-r--r-- | storage/maria/ma_extra.c | 6 | ||||
-rw-r--r-- | storage/maria/ma_key.c | 28 | ||||
-rw-r--r-- | storage/maria/ma_rkey.c | 60 | ||||
-rw-r--r-- | storage/maria/ma_rnext.c | 25 | ||||
-rw-r--r-- | storage/maria/ma_rnext_same.c | 23 | ||||
-rw-r--r-- | storage/maria/ma_rprev.c | 26 | ||||
-rw-r--r-- | storage/maria/ma_search.c | 6 | ||||
-rw-r--r-- | storage/maria/ma_static.c | 3 | ||||
-rw-r--r-- | storage/maria/maria_def.h | 9 | ||||
-rw-r--r-- | storage/myisam/ha_myisam.cc | 11 | ||||
-rw-r--r-- | storage/myisam/mi_extra.c | 5 | ||||
-rw-r--r-- | storage/myisam/mi_key.c | 19 | ||||
-rw-r--r-- | storage/myisam/mi_rkey.c | 98 | ||||
-rw-r--r-- | storage/myisam/mi_rnext.c | 26 | ||||
-rw-r--r-- | storage/myisam/mi_rnext_same.c | 22 | ||||
-rw-r--r-- | storage/myisam/mi_rprev.c | 31 | ||||
-rw-r--r-- | storage/myisam/mi_search.c | 5 | ||||
-rw-r--r-- | storage/myisam/mi_static.c | 1 | ||||
-rw-r--r-- | storage/myisam/myisamdef.h | 6 | ||||
-rw-r--r-- | storage/xtradb/handler/ha_innodb.cc | 22 | ||||
-rw-r--r-- | storage/xtradb/handler/ha_innodb.h | 3 | ||||
-rw-r--r-- | storage/xtradb/include/db0err.h | 3 | ||||
-rw-r--r-- | storage/xtradb/include/row0mysql.h | 11 | ||||
-rw-r--r-- | storage/xtradb/row/row0sel.c | 12 |
25 files changed, 383 insertions, 93 deletions
diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 9acabf29774..406def92808 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -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; } diff --git a/storage/maria/ma_extra.c b/storage/maria/ma_extra.c index 7a30b613ea5..a2d40b1c828 100644 --- a/storage/maria/ma_extra.c +++ b/storage/maria/ma_extra.c @@ -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; +} + diff --git a/storage/maria/ma_key.c b/storage/maria/ma_key.c index ac23bf5fef6..df15d029ce6 100644 --- a/storage/maria/ma_key.c +++ b/storage/maria/ma_key.c @@ -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; } diff --git a/storage/maria/ma_rkey.c b/storage/maria/ma_rkey.c index 5da541005f8..415f20cc631 100644 --- a/storage/maria/ma_rkey.c +++ b/storage/maria/ma_rkey.c @@ -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; +} + diff --git a/storage/maria/ma_rnext.c b/storage/maria/ma_rnext.c index bdba5ff3a17..2fd8bf4e603 100644 --- a/storage/maria/ma_rnext.c +++ b/storage/maria/ma_rnext.c @@ -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)) diff --git a/storage/maria/ma_rnext_same.c b/storage/maria/ma_rnext_same.c index f67a76a366f..c35d8ae0222 100644 --- a/storage/maria/ma_rnext_same.c +++ b/storage/maria/ma_rnext_same.c @@ -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)) diff --git a/storage/maria/ma_rprev.c b/storage/maria/ma_rprev.c index b9f46d7c405..c4bcb9de967 100644 --- a/storage/maria/ma_rprev.c +++ b/storage/maria/ma_rprev.c @@ -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)) diff --git a/storage/maria/ma_search.c b/storage/maria/ma_search.c index 8d3b5721336..4ac6dfeb15f 100644 --- a/storage/maria/ma_search.c +++ b/storage/maria/ma_search.c @@ -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; diff --git a/storage/maria/ma_static.c b/storage/maria/ma_static.c index 917385f9568..37814b42dc2 100644 --- a/storage/maria/ma_static.c +++ b/storage/maria/ma_static.c @@ -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; + diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index 65898666686..beb286c7d4b 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -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 *); + diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 781ce39a99a..b32cce9e43c 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -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; } diff --git a/storage/myisam/mi_extra.c b/storage/myisam/mi_extra.c index 648e5fb0024..2f8af89c27d 100644 --- a/storage/myisam/mi_extra.c +++ b/storage/myisam/mi_extra.c @@ -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; +} diff --git a/storage/myisam/mi_key.c b/storage/myisam/mi_key.c index 52273f013ec..a3d38269e61 100644 --- a/storage/myisam/mi_key.c +++ b/storage/myisam/mi_key.c @@ -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; } /* diff --git a/storage/myisam/mi_rkey.c b/storage/myisam/mi_rkey.c index da757d270bc..dbe4d59ee90 100644 --- a/storage/myisam/mi_rkey.c +++ b/storage/myisam/mi_rkey.c @@ -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; +} diff --git a/storage/myisam/mi_rnext.c b/storage/myisam/mi_rnext.c index b1c261dd6b1..79db5fb992d 100644 --- a/storage/myisam/mi_rnext.c +++ b/storage/myisam/mi_rnext.c @@ -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)) diff --git a/storage/myisam/mi_rnext_same.c b/storage/myisam/mi_rnext_same.c index df3b678d663..3aa7e93dfd5 100644 --- a/storage/myisam/mi_rnext_same.c +++ b/storage/myisam/mi_rnext_same.c @@ -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)) diff --git a/storage/myisam/mi_rprev.c b/storage/myisam/mi_rprev.c index 93bd224bd56..040b08b428c 100644 --- a/storage/myisam/mi_rprev.c +++ b/storage/myisam/mi_rprev.c @@ -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)) diff --git a/storage/myisam/mi_search.c b/storage/myisam/mi_search.c index 1f8399f25ca..13ee552e7ec 100644 --- a/storage/myisam/mi_search.c +++ b/storage/myisam/mi_search.c @@ -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; diff --git a/storage/myisam/mi_static.c b/storage/myisam/mi_static.c index 27485e101ff..2d297ddc907 100644 --- a/storage/myisam/mi_static.c +++ b/storage/myisam/mi_static.c @@ -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))) { diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 20974e0b717..cde46e9dd96 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -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, ...)); diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index 6eeaa4187ac..39621852c4c 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -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 diff --git a/storage/xtradb/handler/ha_innodb.h b/storage/xtradb/handler/ha_innodb.h index 8945ce95ee5..c3d90491f31 100644 --- a/storage/xtradb/handler/ha_innodb.h +++ b/storage/xtradb/handler/ha_innodb.h @@ -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 diff --git a/storage/xtradb/include/db0err.h b/storage/xtradb/include/db0err.h index c7fa6d2a444..220878629fb 100644 --- a/storage/xtradb/include/db0err.h +++ b/storage/xtradb/include/db0err.h @@ -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 diff --git a/storage/xtradb/include/row0mysql.h b/storage/xtradb/include/row0mysql.h index fbb9ee92a90..88de6421cf9 100644 --- a/storage/xtradb/include/row0mysql.h +++ b/storage/xtradb/include/row0mysql.h @@ -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. */ diff --git a/storage/xtradb/row/row0sel.c b/storage/xtradb/row/row0sel.c index 4c6a91f3c69..79cf88904eb 100644 --- a/storage/xtradb/row/row0sel.c +++ b/storage/xtradb/row/row0sel.c @@ -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 |