Repository navigation
Conversation
… MDEV-28730; phrase search is quadratic The callbacks of the FTS query executor return STOP when a scan has passed the records it needs, such as the next FTS_DOC_ID or the next word of an auxiliary table. row_search_mvcc() turned the resulting DB_SUCCESS_LOCKED_REC into DB_SUCCESS, so read_by_index() continued to the end of the index. Repeated document fetches in phrase search, proximity search and query expansion therefore scanned the remaining records for each document. Preserve DB_SUCCESS_LOCKED_REC in both callback paths so that the executor stops the scan. This also restores the OPTIMIZE TABLE limit of innodb_ft_num_word_optimize words per pass, as in 11.4. Compare auxiliary word prefixes with the collation's strnncoll() prefix mode, as IB_LIKE_PREFIX did in 11.4. As there and in MyISAM, a prefix matches by collation weights, so in a Czech collation, where ch is a contraction, 'kac*' does not match 'kachna'. Comparing byte-truncated words can reject an accent variant within the matching range and stop before later matches. Process collation matches and skip records below the range. Stop above it only where the matches are adjacent in the index: keep scanning for collations with several levels, where secondary or tertiary weights can place nonmatching words between matches, and for non-UCA collations with a complex mapping (MY_CS_NON1TO1), such as tis620_thai_ci, which reorders Thai vowels before comparing. Keep full collation comparisons for EQUAL mode, which follows index order. Use ROW_SEL_EXACT for select_for_update() of a CONFIG key. Its comparator skips nonmatching keys and never returns STOP. A missing key now takes a gap lock on the next record instead of scanning all following CONFIG rows and taking next-key X locks up to the supremum. Count internal callback visits in the debug-only InnoDB status variable Innodb_row_search_callback_records, enabled by the session debug keyword fts_count_callback_records, and exclude it in innodb.innodb_status_variables like the other debug-only counters. Test visit-count deltas for document and auxiliary word lookups. Add separately synced accent and case variants in utf8mb4_uca1400_ai_ci and checks with utf8mb4_bin, verifying the returned rows. Cover interleaved prefix matches in utf8mb4_uca1400_as_ci and utf8mb4_uca1400_as_cs with indexes built after insertion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
futureweb
marked this pull request as ready for review
October 24, 2026 18:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Since MDEV-28730 (81c3836), InnoDB reads the FTS tables through QueryExecutor::read_by_index(). The callbacks of these reads return STOP when the scan has passed the records it needs (the next FTS_DOC_ID, or the next word of an auxiliary table), but row_search_mvcc() did not return the resulting DB_SUCCESS_LOCKED_REC: one callback path turned it into DB_SUCCESS, the other went on to the next record. So the scan went on to the end of the index. Each document fetch of phrase search, proximity search and query expansion (fts_query_fetch_document()) read from that document to the end of FTS_DOC_ID_INDEX, and each word lookup read to the end of the auxiliary table.
'"gamma delta"'on a table where every row matches, debug builds (from the Jira issue):Fix:
SET debug_dbug='+d,fts_count_callback_records', for the test. innodb.innodb_status_variables excludes it like the other debug-only counters.Notes for reviewers:
'kac*'does not match'kachna'.Release Notes
InnoDB fulltext searches no longer read to the end of the index for each matching row or word, which made phrase searches, proximity searches and searches WITH QUERY EXPANSION very slow on large tables.
How can this PR be tested?
New test
innodb_fts.read_to_end(debug build). It counts the internal record visits of phrase search, proximity search, query expansion, and word and prefix lookups. Except for the lookups of the last word and of a word after it, the limits cannot be met by reading to the end (for the 32 documents of the phrase search that would be 32*33/2 = 528 visits; the limit is 96). It also checks the rows returned by prefix searches with accent and case variants in utf8mb4_uca1400_ai_ci, utf8mb4_bin, utf8mb4_uca1400_as_ci and utf8mb4_uca1400_as_cs. Without the multi-level part of the fix,'aab*'in utf8mb4_uca1400_as_ci returns only 2 of the 4 matching words.innodb.innodb_status_variablesis updated for the new variable.The
innodb_ftssuite,innodb.innodb_status_variablesandinnodb.index_lock_upgradepass on 13.1 (debug build). On main with all our fixes and the ngram parser of MDEV-10267, theinnodb_fts,mariaandpluginssuites and themainfulltext, plugin and alter tests pass.Basing the PR against the correct MariaDB version
PR quality check
🤖 Generated with Claude Code