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

2941 Commits

Author SHA1 Message Date
Khaled Riyad
9be46dd6f1 MDEV-37563 Crash on a FOR loop with a row comparison in the upper bound
Both bounds of a FOR loop, and all parameters of a cursor FOR loop,
are parsed before their instructions are added. add_instr() then gave
all their items to the first instruction, and in a trigger also all
NEW and OLD fields. So a row comparison used by a later instruction
was cleaned up by the wrong one, on the next CALL, after its memory
had already been freed. This crashed in Arg_comparator::cleanup().

Give each instruction only the items of its own expression, and its
own trigger fields. To find them, cut THD::free_list at the newest
item of each expression, starting with the oldest expression.
2026-10-21 17:15:51 +03:00
Sergei Petrunia
0c9a924040 MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables
Alternative, more general fix, Variant 2.

The problem was as follows: Suppose we are running a PS/SP statement and
we get an error while doing optimization that is done once per statement
life. This may leave the statement data structures in an undefined state,
where it is not safe to execute it again.

The fix: introduce LEX::needs_reprepare and set it in such cases.
Make PS and SP runtime check it and re-prepare the statement before
executing it again.

We do not use Reprepare_observer, because it turns out it is tightly tied
to watching versions of statement's objects. For example, it must not be
used when running the statement for the first time, exactly when the
once-per-statement-lifetime optimizations are done.

(cherry picked from commit 0e21ff8ca4)
2026-10-21 17:13:53 +03:00
Sergei Golubchik
50dd408f81 Merge branch '10.11' into 11.4 2026-10-15 20:25:34 +02:00
Marko Mäkelä
7ee06258b5 Merge 10.11 into 11.4 2026-10-13 07:04:14 +03:00
Rex Johnston
060012b3b7 MDEV-41225 Parse the ORDER BY tail of a wrapped parenthesized query in the wrapping select
A parenthesized query expression that already has its own ORDER BY or
LIMIT and is followed by another ORDER BY, e.g.

  (SELECT c1 FROM t1 ORDER BY c1 LIMIT 2) ORDER BY <expr> LIMIT 1

is wrapped into a derived table, and the outer ORDER BY/LIMIT is attached
to the wrapping select.  The grammar pushed the inner select before
parsing the tail, and add_tail_to_query_expression_body_ext_parens()
created the wrapper only after the tail had been parsed.  Anything that
is attached to the current select while the tail is being parsed was
therefore attached to the inner select, while the ORDER BY list itself
was moved to the wrapper:

- Window functions and their window specs were added to the inner
  select's window_funcs/window_specs.  The same Item_window_func was then
  in the wrapper's ORDER BY and in the inner select's window function
  list, which triggers the original assertion reported in MDEV-41225.

  (SELECT 1 FROM t1 LIMIT 1)
    ORDER BY PERCENTILE_DISC(1) WITHIN GROUP(ORDER BY TIME'0') OVER();

  The function is computed by the inner select over the rows before its
  LIMIT, so the outer ORDER BY sorts on wrong values
  (e.g. COUNT(*) OVER () returned 3 for a 2-row result).

- Subqueries were registered as units of the inner select.  An
  uncorrelated subquery in the tail crashed the server with SIGSEGV when
  it was executed by the outer filesort.  A correlated one resolved its
  outer references in the inner select (t1.c1) instead of the derived
  table.

The decision to wrap depends only on whether the inner select already
has a tail and on whether the outer tail has ORDER BY.  LIMIT and locking
clauses cannot contain window functions or subqueries.
We replace query_expression_tail with
query_expression_tail_with_order and query_expression_tail_no_order
so the mid-rule action knows whether an ORDER BY follows.
The new LEX::push_select_for_ext_parens_tail() creates the wrapper before
an ORDER BY tail is parsed when wrapping is required, and pushes it instead
of the inner select.  The tail's items are then created in the wrapper's
context, and window functions and subqueries are attached to the wrapper.
add_tail_to_query_expression_body_ext_parens() takes the pushed select and
skips wrapping when it has already happened; the other cases keep the
previous logic.  The grammar has the same number of conflicts as before.

A subquery in such a tail can no longer refer to the tables of the wrapped
query expression (e.g. t1.c1 above) and gets ER_BAD_FIELD_ERROR,
as those tables are not visible outside the derived table.

This commit was prepared with Claude Code (Opus 5.5). It traced the
wrong-result and crash cases to the parse-time registration of window
functions and subqueries in the inner select, tested the grammar split
with bison, wrote the code change and the brackets test.
2026-10-07 13:30:32 +13:00
KhaledR57
abd14722f2 MDEV-37167 Nested BEGINs (4600+) cause a segmentation fault
Each nested BEGIN adds one sp_pcontext. Two walks over the finished
tree recursed once per nesting level and overran the thread stack.

~sp_pcontext() freed its children recursively. sp_head now threads a
single linked list through the contexts and frees them iteratively, so
teardown depth is constant and a context no longer frees its children.

retrieve_field_definitions() descends the children to build the
run-time frame. It emits them in run-time offset order, so it stays
recursive, but it now checks the stack and returns an error instead
of crashing.
2026-09-10 17:00:23 +03:00
forkfun
08eeb6a417 Merge branch '10.11' into '11.4' 2026-07-28 13:14:38 +02:00
forkfun
9d8ed0c0a2 MDEV-39522 Query with UNION fails in Oracle sql_mode with ER_BAD_FIELD_ERROR/ER_UNKNOWN_TABLE
MDEV-37325 unconditionally wrapped union subqueries in derived tables,
breaking name resolution of outer-scope/correlated references inside
the unions (producing ER_UNKNOWN_TABLE errors).

Delay the wrap until a following operator has a different linkage.
In Oracle mode all set operators share one priority and bind
left-to-right, so wrapping the accumulated prefix on each operator
change enforces it. This also corrects the row multiplicity of mixed
set operations toward left-to-right order.

create_priority_nest(): when the nest covers the whole prefix, point
the wrapper's first_nested at itself. Cut the prefix with
cut_next() before wrapping and re-register it on the outer unit;

Aleksey Midenkov:

In Oracle mode optimize_bag_operation() returns early, so union_distinct is
never recomputed and the stale value reaches execution.
Register the wrapper first, then run fix_distinct(): reset_distinct() now
scans the new outer chain (the wrapper alone) and correctly leaves
union_distinct NULL; a following DISTINCT operand re-establishes it.
2026-07-27 12:55:17 +02:00
Sergei Golubchik
2f92126946 Revert "MDEV-17677: Keywords followed by .number parsed as identifiers"
This reverts commit 895b28d672.
2026-07-24 17:06:40 +02:00
Dmitry Shulga
b5aa486a1c MDEV-36481: Abnormal server termination on second execution of stored routine that runs a cursor after table metadata in the query was changed
On parsing the statement 'DECLARE var1,...,varN TYPE OF cur DEFAULT value'
an instance of the class Item_splocal that created on handling the clause
'DEFAULT value' is shared between two classes sp_instr_cursor_copy_struct and
sp_instr_set. In result, in case metadata changed and cursor has to be re-parsed,
items associated with the SP instruction sp_instr_cursor_copy_struct
are deallocated and one of deallocated items is the one representing the value
the `DEFAULT value` clause. On the other hand, the data member
sp_instr_set::m_value still references the deallocated item. It leads to server
abnormal termination on second run of a stored routine containing the statement
'DECLARE ... TYPE OF ... DEFAULT value'

To fix the issue pass owning of an instance of the class Item_splocal to
the class sp_instr_cursor_copy_struct and deallocated it explicitly on
closing the stored routine.
2026-07-20 14:12:21 +02:00
bsrikanth-mariadb
c0f4063df3 MDEV-39916: Crash when pushing "NOT col" from HAVING into WHERE
"NOT column" rewrites itself into "column = 0" in fix_fields().
However the rewrite is not done when the "column" is wrapped in an Item_ref.

Later, pushdown_from_having_into_where() will strip the Item_ref object
and will call
  Item_func_not(Item_field())->fix_fields(thd, ref=NULL);
This call will attempt the rewrite and crash when writing into NULL pointer.

Fixed this in two places:
* Make Item_func_not::fix_fields() use real_item() and so recognize fields
  wrapped in Item_ref-like objects.

* Make pushdown_from_having_into_where() pass the right pointer as
  fix_fields() argument.
2026-07-15 19:25:30 +05:30
Oleksandr Byelkin
7598e4e95d Merge branch '10.11' into 11.4 2026-05-08 08:00:01 +02:00
Mahmoud Khaled
895b28d672 MDEV-17677: Keywords followed by .number parsed as identifiers
Problem:for example SELECT.1 treated as identifier instead of keyword + 0.1
     from starting identifiers after dot
2026-04-24 11:40:37 +03:00
Aleksey Midenkov
024bf19d89 MDEV-36362 MariaDB crashes when parsing fuzzer generated PARTITION
Function calls in INTERVAL expression of DDL have little
sense. SETVAL() fails because sequences require opened tables and
vers_set_interval() is called at earlier stage.

The fix throws ER_SUBQUERIES_NOT_SUPPORTED for sequence functions
SETVAL(), NEXTVAL(), LASTVAL() when the context does not allow
subselect (determined by clause_that_disallows_subselect).
2026-04-22 15:04:34 +03:00
Dave Gosselin
7f8dc5004d MDEV-39209: use iterative cleanup for merged units to avoid stack overflow
Query optimization can merge derived tables (VIEWs being a type of derived
table) into outer queries, leaving behind stranded  st_select_lex_unit objects
("stranded units") for post-query cleanup.

Previously, these were cleaned up recursively. For queries with many merged
derived tables, the deep recursion over the list of stranded units could
exhaust the stack. This change replaces the recursive cleanup with an
iterative loop to prevent stack overflows.
2026-04-20 09:34:09 -04:00
Abhishek Bansal
c79060ad79 MDEV-38474: ASAN heap-use-after-free in st_select_lex_unit::cleanup
cleanup_stranded_units() was added at the start of
st_select_lex_unit::cleanup() by 34a8209d66. This causes a
use-after-free when nested subqueries are merged into their parent
unit. With nested subqueries like:

  SELECT * FROM t1
  WHERE a IN (SELECT b FROM t2
              WHERE a IN (SELECT c FROM t3 WHERE FALSE HAVING c < 0));

the stranded_clean_list chains the units as: Unit1 -> Unit2 -> Unit3.
Because cleanup_stranded_units() was called first, Unit1->cleanup()
would recursively trigger Unit2->cleanup(), which in turn would
trigger Unit3->cleanup(). Unit3's cleanup frees its heap-allocated
join structures. But since Unit3 was merged into Unit2, Unit2 still
holds references to Unit3's structures (e.g., st_join_table). When
control returns to Unit2 for its own local cleanup, it accesses
already-freed memory.

Fix: move cleanup_stranded_units() to the end of cleanup(). This way,
each unit completes its own local cleanup first—clearing its
references to any child structures—before triggering cleanup of its
stranded (child) units. This enforces a parent-first cleanup order.
2026-04-20 09:34:09 -04:00
Sergei Golubchik
e95d77c18d MDEV-39154 wrong OOM handling in collect_grouping_fields()
correct (and document) the return values for collect_grouping_fields()
2026-04-19 11:36:16 +02:00
Yuchen Pei
c6fadc6e33 Merge branch '10.11' into 11.4 2026-04-16 11:28:26 +10:00
Sergei Petrunia
ba774a0a90 Fix in condition pushdown code: don't pass List<Field_pair> by value.
Affected functions:
  find_matching_field_pair(Item *item, List<Field_pair> pair_list)
  get_corresponding_field_pair(Item *item, List<Field_pair> pair_list)

Both only traverse the pair_list.
They use List_iterator so we can't easily switch to using const-reference.
2026-04-09 12:33:00 +03:00
Marko Mäkelä
7c5cac0d51 Merge 10.11 into 11.4 2026-04-01 10:49:05 +03:00
Aleksey Midenkov
e955e6c8ea MDEV-28619 Server crash and UBSAN null-pointer-use in Window_funcs_sort::setup
Optimization in st_select_lex_unit::prepare() removes ORDER BY for
certain subqueries. That excludes ORDER BY items from being fixed, but
sl->window_funcs still contains window function items and those
related to optimized out ORDER BY are unfixed. The error about missing
window spec is thrown when the item is fixed. Hence we get redundant
processing of window function items without checking window spec
existence.

The fix removes the related window function items when ORDER BY is
optimized out. ORDER accumulates window_funcs at parser stage which
are then removed from SELECT_LEX::window_funcs. The fix also updates
similar optimization in mysql_make_view().
2026-03-28 23:27:41 +03:00
Abhishek Bansal
e0edd07c75 MDEV-38474: ASAN heap-use-after-free in st_select_lex_unit::cleanup
cleanup_stranded_units() was added at the start of
st_select_lex_unit::cleanup() by 34a8209d66. This causes a
use-after-free when nested subqueries are merged into their parent
unit. With nested subqueries like:

  SELECT * FROM t1
  WHERE a IN (SELECT b FROM t2
              WHERE a IN (SELECT c FROM t3 WHERE FALSE HAVING c < 0));

the stranded_clean_list chains the units as: Unit1 -> Unit2 -> Unit3.
Because cleanup_stranded_units() was called first, Unit1->cleanup()
would recursively trigger Unit2->cleanup(), which in turn would
trigger Unit3->cleanup(). Unit3's cleanup frees its heap-allocated
join structures. But since Unit3 was merged into Unit2, Unit2 still
holds references to Unit3's structures (e.g., st_join_table). When
control returns to Unit2 for its own local cleanup, it accesses
already-freed memory.

Fix: move cleanup_stranded_units() to the end of cleanup(). This way,
each unit completes its own local cleanup first—clearing its
references to any child structures—before triggering cleanup of its
stranded (child) units. This enforces a parent-first cleanup order.
2026-03-09 11:37:04 -04:00
Marko Mäkelä
f55deca2b2 Merge 10.11 into 11.4 2026-02-19 11:19:00 +02:00
Aleksey Midenkov
9aa8cf3420 MDEV-38799 Some views are broken in Oracle mode after upgrade to Q1 2026
wrap_select_chain_into_derived() inserts wildcard asterisk over
wrapped SELECT. setup_wild() expands the item wildcard but it does not
update field_translation name which still contains the asterisk. Field
translation was implemented for BUG#5147 to keep view column names
intact, find_field_in_view() uses these field translation names.

Now, the fix uses full field list instead of the wildcard in
wrap_select_chain_into_derived() whenever it is possible.
2026-02-16 17:03:06 +03:00
Sergei Golubchik
40f7084661 Merge branch '10.11' into 11.4 2026-01-28 21:52:18 +01:00
Sergei Golubchik
b29d3779e4 Merge branch '10.6' into 10.11 2026-01-28 14:22:20 +01:00
Aleksey Midenkov
8906e6a214 MDEV-32317 ref_ptrs exhaust on multiple ORDER by func from winfunc
Each ORDER and WHERE slot may generate split, see code like this:

  if ((item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) ||
    item->with_window_func())
  item->split_sum_func(thd, ref_ptrs, all_fields, SPLIT_SUM_SELECT);

Such kind of code is done in JOIN::prepare(), setup_order(),
setup_fields(), setup_group() and split_sum_func2() itself.

Since we are at the phase of ref_ptrs allocation, items are not fixed
yet and we cannot calculate precisely how much ref_ptrs is needed. We
can estimate at most how much is needed. In the worst case each window
function generates split on each ORDER BY field, GROUP BY field and
WHERE field, so the counts of these should be multiplied by window
funcs count.

As the split can be done in both setup_without_group() and
JOIN::prepare() simultaneously, the factor of window funcs should be
multiplied by 2.

The similar case may be with inner sumfunc items as of the condition

  item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM

but factor of these is harder to predict at the stage of unfixed
items.
2026-01-27 16:19:46 +01:00
Rucha Deodhar
ca39e66060 MDEV-38620: Server crashes in setup_returning_fields upon 2nd execution
of multi-table-styled DELETE from a view

Analysis:
The item_list of builtin_select stores the fields that are there in the
RETURNING clause.
During the "EXECUTE" command, a "dummy item" is added into the item_list
of the select_lex(builtin_select) representing DELETE during
Sql_cmd_delete::precheck(). This snippet that adds a dummy item is added
because columnstore needs for temporary table. Results are put into a
temporary table and to create a temporary table we need to know what
columns are there which we get from the select_lex->item_list.
As a result, the item_list now has an item even when there is not really
RETURNING clause, resulting in execution of the setup_returning_fields()
when it should have exited already.

Fix:
Instead of checking whether builint_select's item_list is empty to
determine whether there is RETURNING clause, use a flag.
2026-01-26 23:32:38 +05:30
Aleksey Midenkov
b68f878fdc MDEV-36876 Crash during the Item_subselect::init - outer_select is NULL
Comparison between vector and scalar is invalid (ER_OPERAND_COLUMNS)
and handled by the parser. The problem is outer_context is missing
because relink_hack() cannot recover it due to
!builtin_select.first_inner_unit() condition. This condition was set
by previous relink hack called for previous expression some(select 1).

Since there can be arbitrary number of such expressions there seems to
be no point in such a limitation. MTR test do not fail without that
condition, so the fix proposes to remove it.
2026-01-23 14:48:57 +03:00
Oleg Smirnov
f2b48e565c MDEV-38574 Rename cloning functions of class Item and descendants
Rename cloning methods of class Item and its descendants
in the following way:

   (from)            (to)
do_build_clone  -> deep_copy
   build_clone  -> deep_copy_with_checks

do_get_copy  -> shallow_copy
   get_copy  -> shallow_copy_with_checks

to better reflect their functionality.

Also make Item::deep_copy() and shallow_copy() protected.
Outside users should call deep_copy_with_checks()
and shallow_copy_with_checks().
2026-01-22 12:57:48 +01:00
Alexander Barkov
4de773e90c MDEV-38451 SHOW FUNCTION CODE package_func crashes on the second execution
Routine name resolution performed on a temporary memory root
during execution. So on the second execution LEX::spname members
pointed to a cleared memory.

Fixing to peform the resolution to parse time, like the CALL statement does.
During the execution time LEX::spname members now stay untouched.
2026-01-13 15:37:08 +04:00
Marko Mäkelä
678ff03ee7 Merge 10.11 into 11.4 2026-01-02 11:53:09 +02:00
Aleksey Midenkov
10643cffe9 MDEV-37325 Incorrect results for INTERSECT ALL in ORACLE mode
Oracle mode has no priority between set-operators but the current
implementation with disable_index_if_needed() can not work with
arbitrary mix of distinct/non-distinct set-operators as the algorithm
is tied to the properly wrapped version of expression. Index can be
disabled only once and cannot be reenabled afterwards.

To adapt Oracle mode for the aforementioned implementation we
prioritize each set operator with parens in the order of
appearance. So the expression:

  S1 op1 S2 op2 S3 op3 S4

will be rewritten like this:

  (((S1 op1 S2) op2 S3) op3 S4).

The rewritten expression is valid for both Oracle and default mode and
renders the same result. Prioritizing is done with
create_priority_nest() but it was tweaked to work with derived to be
the first operand. Originally create_priority_nest() could wrap only
second operand, like this:

  S1 op1 S2 op2 S3 -> S1 op1 (S2 op2 S3).

Now this behaviour is kept by non-NULL attach_to argument. With NULL
attach_to argument it produces this transformation:

  S1 op1 S2 -> (S1 op1 S2), but to be strict it will look like this:
  SELECT .. FROM (S1 op1 S2).

This expression is treated as the first one in the select list, so
nothing to "attach to".

On importance of not making fake_select_lex after
create_priority_nest() in parsed_select_expr_start(). This will fail
nastily in Protocol::valid_handler() check in store_longlong() for
EXPLAIN. This happens because select_options are inherited from newly
made fake_select_lex but without SELECT_DESCRIBE flag which is meant
to be assigned by mysql_explain_union() but union_needs_tmp_table() is
false (because now top-level unit has no set-operator, it is just
select from derived), so SELECT_DESCRIBE assignment is
skipped. Depending on that flag send_result_set_metadata() is
conditionally executed in JOIN::exec_inner(). And originally it is
done via select_describe() branch for top-level select when there is
no set operator (it is select from derived as explained above), not
via direct send_result_set_metadata().

On IS_OR_WAS_ORACLE. Sql_mode_save_for_frm_handling removes MODE_ORACLE
when view is registered or opened. We cannot return it back as this
conflicts with MDEV-12478 (tested by compat/oracle.func_concat). So we
are temporarily adding WAS_ORACLE flag instead. WAS_MODE_ORACLE is for
testing both WAS_ORACLE and MODE_ORACLE simultaneously.
2025-12-23 14:02:55 +03:00
Sergei Golubchik
f230c0ff6e Merge branch '10.11' into 11.4 2025-11-04 13:44:16 +01:00
Oleksandr Byelkin
99a862c54f Merge branch '10.6' into bb-10.11-release 2025-10-27 14:34:43 +01:00
Sergei Golubchik
e5994025be MDEV-37938 very long query cannot be killed quickly
check for thd->killed in the lexer
2025-10-25 18:07:53 +02:00
Oleksandr Byelkin
029d6a0b2e Merge branch '10.11' into bb-11.4-release 2025-10-22 10:21:16 +02:00
Dave Gosselin
9f64b2913f MDEV-32397, MDEV-32403 Crashes during join processing.
Queries having the following form may cause a crash
  SELECT t1.a FROM ( SELECT a AS a1 FROM t1 ) dt
  JOIN t1 ON a1 LIKE EXISTS ( SELECT a + RAND () FROM t1 UNION SELECT a FROM t1);
because the table t1 has some JOIN cleanup operations performed prematurely
during the subselect.

In this particular case, the presence of RAND() makes the subquery
uncacheable, necessitating the need to execute the subquery multiple
times during join record evaluation.  Each time the subquery runs, it
creates its own JOIN structure which has references to the table t1.
When the subquery completes, JOIN::cleanup and functions called by it
result in ha_end_keyread() being called on table t1.  However, we are
not done with table t1 because the upper level `select t1.a from...`
query requires table t1 to be open.  To solve this, we make the executor
aware of when we're in subqueries like this and delay JOIN cleanup
until the end of the query.
2025-10-20 10:08:19 -04:00
Dave Gosselin
34a8209d66 MDEV-35816 ASAN use-after-poison in st_select_lex::print
For prepared statements with derived tables defined by CTEs, and
during second execution, there is a dangling pointer to an instance
of a JOIN object that no longer exists.

Normally, the join member of a SELECT_LEX is freed during a call to
st_select_lex::cleanup() which recursively traverses the query tree.
But due to CTE merging during mysql_derived_merge, the unit containing
the SELECT_LEX that would've been freed is cutoff from the query tree.

We now remember all such units in a linked list so that they're
cleaned up at the end of the lifetime of the query.
2025-10-17 08:51:46 -04:00
Oleksandr Byelkin
15b1426c3a Merge branch '10.11' into bb-11.4-release 2025-09-15 16:17:33 +02:00
Michael Widenius
5a35fff422 Fixed typos
- Some of the typos was because a Russian character was
  accidentally used instead of an ascii one.
2025-09-04 18:08:39 +03:00
Monty
882f6fa3aa Fixed typos
- Removed duplicate words, like "the the" and "to to"
- Removed duplicate lines (one double sort line found in mysql.cc)
- Fixed some typos found while searching for duplicate words.

Command used to find duplicate words:
egrep -rI "\s([a-zA-Z]+)\s+\1\s" | grep -v param

Thanks to Artjoms Rimdjonoks for the command and pointing out the
spelling errors.
2025-09-04 18:08:39 +03:00
Marko Mäkelä
257f4b30ef Merge 10.11 into 11.4 2025-09-03 10:32:56 +03:00
Rex
00d3dc97f8 MDEV-32294 fix_fields() problem with inconsistent outer context paths
As part of optimization, we can simplify queries by merging a derived
table into it's parent instead of materializing it.  When this happens
the context paths, describing how each SELECT_LEX is positioned in our
query needs updating.  The best place to do this is in the call to
SELECT_LEX::exclude_level().

We follow MySQLs example here.

Approved by Sanja Byelkin (sanja@mariadb.com)
2025-08-29 12:20:38 +11:00
Sergei Golubchik
c4ed889b74 Merge branch '10.11' into 11.4 2025-07-28 19:40:10 +02:00
Sergei Golubchik
053f9bcb5b Merge branch '10.6' into 10.11 2025-07-28 18:06:31 +02:00
Justin Jose
1735807448 Bug#37117875 Binlog record error when delimiter is set to other symbols
Description:
------------
When the delimiter is set to a non-default symbol and the SQL
statement contains an unquoted semicolon (;) within a MySQL-specific
comment, the SQL executes successfully in the source database.
However, the binlog record becomes incomplete, leading to a syntax
error in the replica database.

Analysis:
------------
When the delimiter is set to a non-default symbol and an SQL
statement contains an unquoted semicolon within a MySQL-specific
comment, the client transmits the entire SQL statement, including
the MySQL-specific comment, up to the delimiter to the server.
During parsing, the server interprets the semicolon as the end of the
command while processing the comment, resulting in the execution of
a partial statement. The truncated statement is then recorded in
the binary log and propagated to the replica, leading to an error.

Fix:
------------
When the delimiter is set to a non-default symbol, treat
MySQL-specific comments containing unquoted semicolons as syntax
errors and return a parser error.

Change-Id: I00d6b4ced89e79a7350c94218bf2527553054aed
2025-07-25 12:28:30 +02:00
Dmitry Shulga
ef9adb569e MDEV-32694: ASAN errors in Binary_string::alloced_length / reset_stmt_params
Anonymous block is represented internally by the class sp_head,
so every statement inside an anonymous block is a SP instruction.
On the other hand, the anonymous block specified in the FROM clause of
the PREPARE statement is treated as a single statement. In result,
all parameter markers (represented by the character ?) are parts of
the anonymous block specified in the prepared statement and at the same
time parameter are markers, internally represented by instances of
the class Item_param and distributed among SP instructions representing
SQL statements (every SQL statement is represented by an instance of
the class sp_instr_stmt)

In case table metadata changed on running an anonymous block in prepared
statement mode, only SP instruction's statement is re-parsed. Before
re-parsing a SP's statement, all items are cleaned up including
instances of the class Item_param that represent positional parameters.

Unfortunately, this leads to presence of a dangling pointer in
Prepared_statement::param_array that references to the deleted
Item_param while invoking reset_stmt_params happening on every execution
of a prepared statement.

To fix the issue, no instances of Item_param created on re-parsings
a statement for failed SP instruction, rather instances of Item_param
left from first time parsing are re-used. As a consequence, all pointers
to instances of the class Item_param stored in the array
Prepared_statememt::param_array and possibly spread along the code base
  (e.g. select_lex->limit_params.select_limit)
still point to valid Items.
2025-07-02 17:50:24 +07:00
Oleksandr Byelkin
a8d4642375 Merge branch '10.11' into 11.4 2025-04-26 10:53:02 +02:00
Oleksandr Byelkin
4d41ec081e Merge branch '10.6' into 10.11 2025-04-26 10:47:03 +02:00