diff options
author | Michael Widenius <monty@askmonty.org> | 2011-02-18 17:43:59 +0200 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2011-02-18 17:43:59 +0200 |
commit | b12e3796dc70466eca5ef3f25d51234b32af5113 (patch) | |
tree | 21c8dd65d7fd61dfbae3cc317ef961b850ff0886 /storage/maria | |
parent | 6c7360b540315a85ed6011dd4b31471c345e886a (diff) | |
download | mariadb-git-b12e3796dc70466eca5ef3f25d51234b32af5113.tar.gz |
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
Diffstat (limited to 'storage/maria')
-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 | 58 | ||||
-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 |
10 files changed, 164 insertions, 35 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..01a7f64e431 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,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; +} + 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 *); + |