1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-41281 Unbounded Recursion in InnoDB FTS Boolean-Mode causes Stack-Overflow fts_ast_visit

expr_lst: expr_lst sub_expr wrapped the expression list parsed so
far in a newly created FTS_AST_LIST for every sub-expression,
so a query such as '(word)' repeated N times built N nested lists.
fts_ast_visit() recurses once per FTS_AST_LIST and overflowed the
thread stack; the default thread_stack was exhausted at about
2300 repetitions.

Append the sub-expression to the list that is already being built,
and create a list only when there is none yet, the way
the neighbouring expr_lst: expr_lst expr rule has always done.
The sub-expressions now become siblings of a single list and
the depth no longer grows.

This also corrects '+term (subexpr)'. The FTS_EXIST pass used to
run to completion in the inner list before the sub-expression was
unioned at the outer level, so rows that matched only the
sub-expression were returned.

Based on mysql/mysql-server@e46e9db484
This commit is contained in:
Thirunarayanan Balathandayuthapani
2026-10-21 21:47:32 +05:30
parent 54fa15e8ea
commit 8d50ec772c
6 changed files with 32 additions and 7 deletions

View File

@@ -444,7 +444,6 @@ a
City Of God
SELECT a FROM t1 WHERE MATCH a AGAINST ('+city (of*)' IN BOOLEAN MODE);
a
Offside
City Of God
SELECT a FROM t1 WHERE MATCH a AGAINST ('+city* of*' IN BOOLEAN MODE);
a

View File

@@ -194,3 +194,12 @@ b
2
DROP TABLE t1,t2;
End of 5.1 tests
#
# MDEV-41281 Unbounded Recursion in InnoDB FTS Boolean-Mode
# causes Stack-Overflow fts_ast_visit
#
CREATE TABLE t1 (col TEXT, FULLTEXT KEY ftidx (col)) ENGINE=InnoDB;
INSERT INTO t1 (col) VALUES ('word appears here for the fulltext');
COUNT(*)
1
DROP TABLE t1;

View File

@@ -0,0 +1 @@
--thread-stack=299008

View File

@@ -211,3 +211,21 @@ DROP TABLE t1,t2;
--echo End of 5.1 tests
--echo #
--echo # MDEV-41281 Unbounded Recursion in InnoDB FTS Boolean-Mode
--echo # causes Stack-Overflow fts_ast_visit
--echo #
CREATE TABLE t1 (col TEXT, FULLTEXT KEY ftidx (col)) ENGINE=InnoDB;
INSERT INTO t1 (col) VALUES ('word appears here for the fulltext');
--disable_query_log
--perl
my $payload = '(word)' x 5000;
open(FILE, ">", "$ENV{MYSQLTEST_VARDIR}/tmp/fts_query.inc") or die;
print FILE "SELECT COUNT(*) FROM t1 WHERE MATCH(col) AGAINST('$payload' IN BOOLEAN MODE);\n";
close(FILE);
EOF
--source $MYSQLTEST_VARDIR/tmp/fts_query.inc
--remove_file $MYSQLTEST_VARDIR/tmp/fts_query.inc
--enable_query_log
DROP TABLE t1;

View File

@@ -1462,10 +1462,9 @@ yyreduce:
/* Line 1806 of yacc.c */
{
(yyval.node) = (yyvsp[(1) - (2)].node);
(yyval.node) = fts_ast_create_node_list(state, (yyvsp[(1) - (2)].node));
if (!(yyval.node)) {
(yyval.node) = (yyvsp[(2) - (2)].node);
if (!(yyval.node) && (yyvsp[(2) - (2)].node)) {
(yyval.node) = fts_ast_create_node_list(state, (yyvsp[(2) - (2)].node));
} else {
fts_ast_add_node((yyval.node), (yyvsp[(2) - (2)].node));
}

View File

@@ -99,10 +99,9 @@ expr_lst: /* Empty */ {
| expr_lst sub_expr {
$$ = $1;
$$ = fts_ast_create_node_list(state, $1);
if (!$$) {
$$ = $2;
if (!$$ && $2) {
$$ = fts_ast_create_node_list(state, $2);
} else {
fts_ast_add_node($$, $2);
}