diff options
Diffstat (limited to 'storage/innobase/fts')
-rw-r--r-- | storage/innobase/fts/fts0ast.cc | 34 | ||||
-rw-r--r-- | storage/innobase/fts/fts0fts.cc | 103 | ||||
-rw-r--r-- | storage/innobase/fts/fts0opt.cc | 54 | ||||
-rw-r--r-- | storage/innobase/fts/fts0que.cc | 63 |
4 files changed, 167 insertions, 87 deletions
diff --git a/storage/innobase/fts/fts0ast.cc b/storage/innobase/fts/fts0ast.cc index 14e52a99e18..8c542aab289 100644 --- a/storage/innobase/fts/fts0ast.cc +++ b/storage/innobase/fts/fts0ast.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -161,7 +161,7 @@ fts_ast_create_node_term_for_parser( /* '%' as first char is forbidden for LIKE in internal SQL parser; '%' as last char is reserved for wildcard search;*/ - if (len == 0 || len > fts_max_token_size + if (len == 0 || len > FTS_MAX_WORD_LEN || ptr[0] == '%' || ptr[len - 1] == '%') { return(NULL); } @@ -537,6 +537,36 @@ fts_ast_node_print( fts_ast_node_print_recursive(node, 0); } +/** Check only union operation involved in the node +@param[in] node ast node to check +@return true if the node contains only union else false. */ +bool +fts_ast_node_check_union( + fts_ast_node_t* node) +{ + if (node->type == FTS_AST_LIST + || node->type == FTS_AST_SUBEXP_LIST + || node->type == FTS_AST_PARSER_PHRASE_LIST) { + + for (node = node->list.head; node; node = node->next) { + if (!fts_ast_node_check_union(node)) { + return(false); + } + } + + } else if (node->type == FTS_AST_OPER + && (node->oper == FTS_IGNORE + || node->oper == FTS_EXIST)) { + + return(false); + } else if (node->type == FTS_AST_TEXT) { + /* Distance or phrase search query. */ + return(false); + } + + return(true); +} + /******************************************************************//** Traverse the AST - in-order traversal, except for the FTX_EXIST and FTS_IGNORE nodes, which will be ignored in the first pass of each level, and visited in a diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 5edee71a13a..5faa68be0ea 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -208,19 +208,20 @@ struct fts_tokenize_param_t { ulint add_pos; /*!< Added position for tokens */ }; -/****************************************************************//** -Run SYNC on the table, i.e., write out data from the cache to the +/** Run SYNC on the table, i.e., write out data from the cache to the FTS auxiliary INDEX table and clear the cache at the end. @param[in,out] sync sync state @param[in] unlock_cache whether unlock cache lock when write node @param[in] wait whether wait when a sync is in progress +@param[in] has_dict whether has dict operation lock @return DB_SUCCESS if all OK */ static dberr_t fts_sync( fts_sync_t* sync, bool unlock_cache, - bool wait); + bool wait, + bool has_dict); /****************************************************************//** Release all resources help by the words rb tree e.g., the node ilist. */ @@ -1979,7 +1980,6 @@ fts_create_common_tables( func_exit: if (error != DB_SUCCESS) { - for (it = common_tables.begin(); it != common_tables.end(); ++it) { row_drop_table_for_mysql( @@ -3648,7 +3648,7 @@ fts_add_doc_by_id( DBUG_EXECUTE_IF( "fts_instrument_sync_debug", - fts_sync(cache->sync, true, true); + fts_sync(cache->sync, true, true, false); ); DEBUG_SYNC_C("fts_instrument_sync_request"); @@ -3929,6 +3929,8 @@ fts_write_node( doc_id_t first_doc_id; char table_name[MAX_FULL_NAME_LEN]; + ut_a(node->ilist != NULL); + if (*graph) { info = (*graph)->info; } else { @@ -4446,7 +4448,7 @@ fts_sync_index( ut_ad(rbt_validate(index_cache->words)); - error = fts_sync_write_words(sync->trx, index_cache, sync->unlock_cache); + error = fts_sync_write_words(trx, index_cache, sync->unlock_cache); #ifdef FTS_DOC_STATS_DEBUG /* FTS_RESOLVE: the word counter info in auxiliary table "DOC_ID" @@ -4463,13 +4465,11 @@ fts_sync_index( } /** Check if index cache has been synced completely -@param[in,out] sync sync state @param[in,out] index_cache index cache @return true if index is synced, otherwise false. */ static bool fts_sync_index_check( - fts_sync_t* sync, fts_index_cache_t* index_cache) { const ib_rbt_node_t* rbt_node; @@ -4492,14 +4492,36 @@ fts_sync_index_check( return(true); } -/*********************************************************************//** -Commit the SYNC, change state of processed doc ids etc. +/** Reset synced flag in index cache when rollback +@param[in,out] index_cache index cache */ +static +void +fts_sync_index_reset( + fts_index_cache_t* index_cache) +{ + const ib_rbt_node_t* rbt_node; + + for (rbt_node = rbt_first(index_cache->words); + rbt_node != NULL; + rbt_node = rbt_next(index_cache->words, rbt_node)) { + + fts_tokenizer_word_t* word; + word = rbt_value(fts_tokenizer_word_t, rbt_node); + + fts_node_t* fts_node; + fts_node = static_cast<fts_node_t*>(ib_vector_last(word->nodes)); + + fts_node->synced = false; + } +} + +/** Commit the SYNC, change state of processed doc ids etc. +@param[in,out] sync sync state @return DB_SUCCESS if all OK */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) dberr_t fts_sync_commit( -/*============*/ - fts_sync_t* sync) /*!< in: sync state */ + fts_sync_t* sync) { dberr_t error; trx_t* trx = sync->trx; @@ -4550,6 +4572,8 @@ fts_sync_commit( << " ins/sec"; } + /* Avoid assertion in trx_free(). */ + trx->dict_operation_lock_mode = 0; trx_free_for_background(trx); return(error); @@ -4572,6 +4596,10 @@ fts_sync_rollback( index_cache = static_cast<fts_index_cache_t*>( ib_vector_get(cache->indexes, i)); + /* Reset synced flag so nodes will not be skipped + in the next sync, see fts_sync_write_words(). */ + fts_sync_index_reset(index_cache); + for (j = 0; fts_index_selector[j].value; ++j) { if (index_cache->ins_graph[j] != NULL) { @@ -4597,6 +4625,9 @@ fts_sync_rollback( rw_lock_x_unlock(&cache->lock); fts_sql_rollback(trx); + + /* Avoid assertion in trx_free(). */ + trx->dict_operation_lock_mode = 0; trx_free_for_background(trx); } @@ -4605,13 +4636,15 @@ FTS auxiliary INDEX table and clear the cache at the end. @param[in,out] sync sync state @param[in] unlock_cache whether unlock cache lock when write node @param[in] wait whether wait when a sync is in progress +@param[in] has_dict whether has dict operation lock @return DB_SUCCESS if all OK */ static dberr_t fts_sync( fts_sync_t* sync, bool unlock_cache, - bool wait) + bool wait, + bool has_dict) { ulint i; dberr_t error = DB_SUCCESS; @@ -4640,6 +4673,12 @@ fts_sync( DEBUG_SYNC_C("fts_sync_begin"); fts_sync_begin(sync); + /* When sync in background, we hold dict operation lock + to prevent DDL like DROP INDEX, etc. */ + if (has_dict) { + sync->trx->dict_operation_lock_mode = RW_S_LATCH; + } + begin_sync: if (cache->total_size > fts_max_cache_size) { /* Avoid the case: sync never finish when @@ -4676,7 +4715,7 @@ begin_sync: ib_vector_get(cache->indexes, i)); if (index_cache->index->to_be_dropped - || fts_sync_index_check(sync, index_cache)) { + || fts_sync_index_check(index_cache)) { continue; } @@ -4691,6 +4730,7 @@ end_sync: } rw_lock_x_lock(&cache->lock); + sync->interrupted = false; sync->in_progress = false; os_event_set(sync->event); rw_lock_x_unlock(&cache->lock); @@ -4714,19 +4754,23 @@ FTS auxiliary INDEX table and clear the cache at the end. @param[in,out] table fts table @param[in] unlock_cache whether unlock cache when write node @param[in] wait whether wait for existing sync to finish +@param[in] has_dict whether has dict operation lock @return DB_SUCCESS on success, error code on failure. */ dberr_t fts_sync_table( dict_table_t* table, bool unlock_cache, - bool wait) + bool wait, + bool has_dict) { dberr_t err = DB_SUCCESS; ut_ad(table->fts); - if (!dict_table_is_discarded(table) && table->fts->cache) { - err = fts_sync(table->fts->cache->sync, unlock_cache, wait); + if (!dict_table_is_discarded(table) && table->fts->cache + && !dict_table_is_corrupted(table)) { + err = fts_sync(table->fts->cache->sync, + unlock_cache, wait, has_dict); } return(err); @@ -5114,7 +5158,7 @@ fts_tokenize_document( ut_a(doc->charset); doc->tokens = rbt_create_arg_cmp( - sizeof(fts_token_t), innobase_fts_text_cmp, (void*) doc->charset); + sizeof(fts_token_t), innobase_fts_text_cmp, (void*) doc->charset); if (parser != NULL) { fts_tokenize_param_t fts_param; @@ -5809,6 +5853,8 @@ fts_update_doc_id( if (error == DB_SUCCESS) { dict_index_t* clust_index; + dict_col_t* col = dict_table_get_nth_col( + table, table->fts->doc_col); ufield->exp = NULL; @@ -5816,8 +5862,8 @@ fts_update_doc_id( clust_index = dict_table_get_first_index(table); - ufield->field_no = dict_col_get_clust_pos( - &table->cols[table->fts->doc_col], clust_index); + ufield->field_no = dict_col_get_clust_pos(col, clust_index); + dict_col_copy_type(col, dfield_get_type(&ufield->new_val)); /* It is possible we update record that has not yet be sync-ed from last crash. */ @@ -6784,11 +6830,7 @@ fts_fake_hex_to_dec( #ifdef UNIV_DEBUG ret = #endif /* UNIV_DEBUG */ -#ifdef _WIN32 - sscanf(tmp_id, "%016llu", &dec_id); -#else - sscanf(tmp_id, "%016llu", &dec_id); -#endif /* _WIN32 */ + sscanf(tmp_id, "%016" UINT64scan, &dec_id); ut_ad(ret == 1); return dec_id; @@ -7995,10 +8037,9 @@ func_exit: consistent state. For now consistency is check only by ensuring index->page_no != FIL_NULL @param[out] base_table table has host fts index -@param[in,out] trx trx handler -@return true if check certifies auxillary tables are sane false otherwise. */ -bool -fts_is_corrupt( +@param[in,out] trx trx handler */ +void +fts_check_corrupt( dict_table_t* base_table, trx_t* trx) { @@ -8016,7 +8057,7 @@ fts_is_corrupt( fts_get_table_name(&fts_table, table_name); dict_table_t* aux_table = dict_table_open_on_name( - table_name, FALSE, FALSE, DICT_ERR_IGNORE_NONE); + table_name, true, FALSE, DICT_ERR_IGNORE_NONE); if (aux_table == NULL) { dict_set_corrupted( @@ -8045,6 +8086,4 @@ fts_is_corrupt( dict_table_close(aux_table, FALSE, FALSE); } - - return(sane); } diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index 89cdcf26591..5989aff83f4 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -602,7 +602,7 @@ fts_zip_read_word( /* Finished decompressing block. */ if (zip->zp->avail_in == 0) { - /* Free the block that's been decompressed. */ + /* Free the block thats been decompressed. */ if (zip->pos > 0) { ulint prev = zip->pos - 1; @@ -1507,6 +1507,12 @@ fts_optimize_write_word( fts_node_t* node = (fts_node_t*) ib_vector_get(nodes, i); if (error == DB_SUCCESS) { + /* Skip empty node. */ + if (node->ilist == NULL) { + ut_ad(node->ilist_size == 0); + continue; + } + error = fts_write_node( trx, &graph, fts_table, word, node); @@ -2635,7 +2641,6 @@ fts_optimize_remove_table( /** Send sync fts cache for the table. @param[in] table table to sync */ -UNIV_INTERN void fts_optimize_request_sync_table( dict_table_t* table) @@ -2650,7 +2655,7 @@ fts_optimize_request_sync_table( /* FTS optimizer thread is already exited */ if (fts_opt_start_shutdown) { - ib::info() << "Try to remove table " << table->name + ib::info() << "Try to sync table " << table->name << " after FTS optimize thread exiting."; return; } @@ -2964,7 +2969,7 @@ fts_optimize_sync_table( if (table) { if (dict_table_has_fts_index(table) && table->fts->cache) { - fts_sync_table(table, true, false); + fts_sync_table(table, true, false, true); } dict_table_close(table, FALSE, FALSE); @@ -3122,26 +3127,7 @@ fts_optimize_thread( ib_vector_get(tables, i)); if (slot->state != FTS_STATE_EMPTY) { - dict_table_t* table = NULL; - - /*slot->table may be freed, so we try to open - table by slot->table_id.*/ - table = dict_table_open_on_id( - slot->table_id, FALSE, - DICT_TABLE_OP_NORMAL); - - if (table) { - - if (dict_table_has_fts_index(table)) { - fts_sync_table(table, false, true); - } - - if (table->fts) { - fts_free(table); - } - - dict_table_close(table, FALSE, FALSE); - } + fts_optimize_sync_table(slot->table_id); } } } @@ -3155,7 +3141,7 @@ fts_optimize_thread( /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. */ - os_thread_exit(NULL); + os_thread_exit(); OS_THREAD_DUMMY_RETURN; } @@ -3189,11 +3175,9 @@ fts_optimize_is_init(void) return(fts_optimize_wq != NULL); } -/**********************************************************************//** -Signal the optimize thread to prepare for shutdown. */ +/** Shutdown fts optimize thread. */ void -fts_optimize_start_shutdown(void) -/*=============================*/ +fts_optimize_shutdown() { ut_ad(!srv_read_only_mode); @@ -3222,17 +3206,5 @@ fts_optimize_start_shutdown(void) os_event_destroy(fts_opt_shutdown_event); ib_wqueue_free(fts_optimize_wq); -} - -/**********************************************************************//** -Reset the work queue. */ -void -fts_optimize_end(void) -/*==================*/ -{ - ut_ad(!srv_read_only_mode); - - // FIXME: Potential race condition here: We should wait for - // the optimize thread to confirm shutdown. fts_optimize_wq = NULL; } diff --git a/storage/innobase/fts/fts0que.cc b/storage/innobase/fts/fts0que.cc index 8abeb63f0a4..dee7c59a58b 100644 --- a/storage/innobase/fts/fts0que.cc +++ b/storage/innobase/fts/fts0que.cc @@ -153,6 +153,13 @@ struct fts_query_t { bool multi_exist; /*!< multiple FTS_EXIST oper */ st_mysql_ftparser* parser; /*!< fts plugin parser */ + + /** limit value for the fts query */ + ulonglong limit; + + /** number of docs fetched by query. This is to restrict the + result with limit value */ + ulonglong n_docs; }; /** For phrase matching, first we collect the documents and the positions @@ -2700,7 +2707,7 @@ fts_query_phrase_split( /*****************************************************************//** Text/Phrase search. @return DB_SUCCESS or error code */ -static __attribute__((warn_unused_result)) +static MY_ATTRIBUTE((warn_unused_result)) dberr_t fts_query_phrase_search( /*====================*/ @@ -3209,6 +3216,11 @@ fts_query_filter_doc_ids( ulint decoded = 0; ib_rbt_t* doc_freqs = word_freq->doc_freqs; + if (query->limit != ULONG_UNDEFINED + && query->n_docs >= query->limit) { + return(DB_SUCCESS); + } + /* Decode the ilist and add the doc ids to the query doc_id set. */ while (decoded < len) { ulint freq = 0; @@ -3296,11 +3308,17 @@ fts_query_filter_doc_ids( /* Add the word to the document's matched RB tree. */ fts_query_add_word_to_document(query, doc_id, word); } + + if (query->limit != ULONG_UNDEFINED + && query->limit <= ++query->n_docs) { + goto func_exit; + } } /* Some sanity checks. */ ut_a(doc_id == node->last_doc_id); +func_exit: if (query->total_size > fts_result_cache_limit) { return(DB_FTS_EXCEED_RESULT_CACHE_LIMIT); } else { @@ -3904,19 +3922,24 @@ fts_query_can_optimize( } } -/*******************************************************************//** -FTS Query entry point. +/** FTS Query entry point. +@param[in] trx transaction +@param[in] index fts index to search +@param[in] flags FTS search mode +@param[in] query_str FTS query +@param[in] query_len FTS query string len in bytes +@param[in,out] result result doc ids +@param[in] limit limit value @return DB_SUCCESS if successful otherwise error code */ dberr_t fts_query( -/*======*/ - trx_t* trx, /*!< in: transaction */ - dict_index_t* index, /*!< in: The FTS index to search */ - uint flags, /*!< in: FTS search mode */ - const byte* query_str, /*!< in: FTS query */ - ulint query_len, /*!< in: FTS query string len - in bytes */ - fts_result_t** result) /*!< in/out: result doc ids */ + trx_t* trx, + dict_index_t* index, + uint flags, + const byte* query_str, + ulint query_len, + fts_result_t** result, + ulonglong limit) { fts_query_t query; dberr_t error = DB_SUCCESS; @@ -3971,13 +3994,16 @@ fts_query( if (flags & FTS_EXPAND) { query.wildcard_words = rbt_create_arg_cmp( - sizeof(fts_string_t), innobase_fts_text_cmp, (void*)charset); + sizeof(fts_string_t), innobase_fts_text_cmp, (void *)charset); } query.total_size += SIZEOF_RBT_CREATE; query.total_docs = dict_table_get_n_rows(index->table); + query.limit = limit; + + query.n_docs = 0; #ifdef FTS_DOC_STATS_DEBUG if (ft_enable_diag_print) { error = fts_get_total_word_count( @@ -4053,6 +4079,19 @@ fts_query( fts_result_cache_limit = 2048; ); + /* Optimisation is allowed for limit value + when + i) No ranking involved + ii) Only FTS Union operations involved. */ + if (query.limit != ULONG_UNDEFINED + && !fts_ast_node_check_union(ast)) { + query.limit = ULONG_UNDEFINED; + } + + DBUG_EXECUTE_IF("fts_union_limit_off", + query.limit = ULONG_UNDEFINED; + ); + /* Traverse the Abstract Syntax Tree (AST) and execute the query. */ query.error = fts_ast_visit( |