slave_close_thread_tables() unconditionally called
trans_commit_stmt()/trans_rollback_stmt(), which assert
!thd->in_sub_stmt. A BINLOG statement with malformed base64 payload
executed from an AFTER INSERT trigger hits this: on decode failure,
mysql_client_binlog_statement() sets thd->is_error() and calls
slave_close_thread_tables() from within the trigger's sub-statement.
The fix refuses BINLOG in stored routines, triggers, events and
anonymous blocks with ER_SP_BADSTATEMENT at parse time, like LOCK,
HANDLER etc. Dynamic SQL (EXECUTE IMMEDIATE, EXECUTE) run from a
routine bypasses the parser check, so mysql_client_binlog_statement()
checks spcont as well: in a stored procedure in_sub_stmt is 0, but the
decoded events assert !spcont in reset_for_next_command().
1. BINLOG statement executed from a trigger, SF or SP is disabled
by the patch:
Row events (Rows_log_event) open their target table via a
one-shot check that only fires at the top of a fresh statement.
Inside a trigger, the table is never opened this way. It may be
fixed by reusing query_tables, but:
- find_locked_table() matches only by table name -- it could return
the TABLE instance the enclosing statement is actively writing
through, not an idle one. Reusing it would require pre-saving its
state: record[0]/bitmaps/handler/etc. (and would bypass
ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG)
- set_stmt_row_injection()/set_time() calls would mutate thd->lex,
which at that point is main_lex -- shared with the enclosing
statement, not something safe to touch.
Statement events (Query_log_event) run the embedded query via
mysql_parse(): it bundles lex_start(), reset_for_next_command() and
parse_sql() as one unit meant for a genuinely new top-level statement,
not a one-off nested parse.
Calling parse_sql() directly instead avoids that, but then we own
everything mysql_parse() was doing for us: a private LEX and a
private Query_arena (or allocations land on main_lex/whatever arena
is currently active, shared with the enclosing statement), plus
calling mysql_execute_command() ourselves afterwards.
In any case, DML for query_tables cannot be done due to
ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG reasons explained above.
2. PS for BINLOG statement still works and needs a leak fix for
statement events:
A BINLOG statement decoding to a Query_log_event, executed via
PREPARE/EXECUTE, leaked its nested query's allocations onto the PS's
own persistent arena: thd->stmt_arena pointed at it while mysql_parse()
ran the decoded query. Second EXECUTE asserted on ROOT_FLAG_READ_ONLY,
since PROTECT_STATEMENT_MEMROOT marks that arena read-only after a
successful execution.
The fix redirects thd->stmt_arena to thd itself for the duration of
the event application, so stmt_arena->is_conventional() reads
true and activate_stmt_arena_if_needed() (called e.g. from
save_leaf_tables()) never redirects allocations to the PS's arena in
the first place.
Harmless for what it's protecting: leaf_tables_exec is normally cached
on the persistent arena so a repeatedly-executed statement's
SELECT_LEX doesn't rebuild it every time, but our SELECT_LEX is torn
down and reparsed fresh (due to mysql_parse() semantics) on every
EXECUTE, so there's nothing to cache here regardless of which arena is
used.
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.
CREATE SERVER used to:
* support arbitrary options in backticks and not in backticks
* hard-coded historical options worked *only* without backticks
* PORT range was different when given as a number or a string
all that is fixed, unused keywords are removed
Changes done to increase MySQL compatibility:
- Use SHOW REPLICA STATUS instead of SHOW SLAVE STATUS for retrieving
slave status. If this fails with syntax error, retry with SHOW SLAVE
STATUS.
- If SHOW MASTER FAILS, retry with MySQL 8.4 syntax SHOW BINARY LOG STATUS
- Add in MariaDB server SHOW BINARY LOG STATUS as an alias for
SHOW MASTER STATUS.
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().
Fixed parser inconsistency where CHANGE MASTER TO master_heartbeat_period
rejected numeric values with an explicit '+' sign, while other parameters
like master_connect_retry accepted them.
The issue was in sql/sql_yacc.yy where master_heartbeat_period used
NUM_literal (which doesn't accept '+'), while other parameters used
ulong_num (which includes opt_plus).
Solution: Added opt_plus before NUM_literal in the master_heartbeat_period
grammar rule, making it consistent with other numeric parameters.
Added test case to verify:
- master_heartbeat_period=+60 now works (was broken)
- master_heartbeat_period=60 still works (backward compatible)
All new code of the whole pull request, including one or several files
that are either new files or modified ones, are contributed under the
BSD-new license. I am contributing on behalf of my employer Amazon Web
Services, Inc.
The Port field in the system table mysql.servers has type INT,
which translates to Field_long.
During parsing it is parsed as ulong_num, and in this patch we add
bound checks there.
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.
SHOW EXPLAIN FOR, and EXPLAIN/DESC FOR CONNECTION should behave
identically. However, for a query with an addition expression containing
INTERVAL and NOT IN sub-select SHOW EXPLAIN FOR was correctly throwing
parse error, where as EXPLAIN/DESC FOR CONNECTION was crashing.
The reason for the crash is that select block was not initialized and
was being accessed inside the NOT IN sub-select in the EXPLAIN/DESC FOR
CONNECTION case.
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.
mariadb from a git tree
Problems:
1. bison incorrectly issues a warning when it sees "b4_bin"
2. MacOSX linker issues a warning about duplicate mysys/dbug/etc libraries.
On 1: worked around by adding a compiler define with a different name
aliasing the character set variable used with a name that
won't trigger the bison warning
On 2: This is due to the fact that there's a circular dependency between
mysys and dbug (among others). Turned the warning off by adding a compiler
option to be applied to targets lined with mysys to turn the warning off.
This patch removes MASTER_SSL_VERIFY_SERVER_CERT from the list
reserved_keyword_udt_not_param_type and adds it to
keyword_func_sp_var_and_label in the parser. All other MASTER_SSL_* keywords
are in this list.
This allows MASTER_SSL_VERIFY_SERVER_CERT to be used unquoted when directly
accessed via INFORMATION_SCHEMA.slave_status, as well as opens it up to be
used as names in other places, e.g. stored procedures.
Reviewed-by: Brandon Nesterenko <brandon.nesterenko@mariadb.com>
forever, cannot be killed
mysql_rm_table_no_locks() does TDC_RT_REMOVE_ALL which waits while
share is closed. The table normally is open only as OPEN_STUB, this is
what parser does for CREATE TABLE. But for SELECT the table is opened
not as a stub. If it is the same table name we anyway have two
TABLE_LIST objects: stub and not stub. So for "not stub"
TDC_RT_REMOVE_ALL sees open count and decides to wait until it is
closed. And it hangs because that was opened in the same thread.
The fix disables subqueries in CHECK expression at parser
level. Thanks to Sergei Golubchik <serg@mariadb.org> for the patch.
It was not possible to use a package body variable as a
fetch target:
CREATE PACKAGE BODY pkg AS
vc INT := 0;
FUNCTION f1 RETURN INT AS
CURSOR cur IS SELECT 1 AS c FROM DUAL;
BEGIN
OPEN cur;
FETCH cur INTO vc; -- this returned "Undeclared variable: vc" error.
CLOSE cur;
RETURN vc;
END;
END;
FETCH assumed that all fetch targets reside of the same sp_rcontext
instance with the cursor. This patch fixes the problem.
Now a cursor and its fetch target can reside in different sp_rcontext
instances.
Details:
- Adding a helper class sp_rcontext_addr
(a combination of Sp_rcontext_handler pointer and an offset in the rcontext)
- Adding a new class sp_fetch_target deriving from sp_rcontext_addr.
Fetch targets in "FETCH cur INTO target1, target2 ..." are now collected
into this structure instead of sp_variable.
sp_variable cannot be used any more to store fetch targets,
because it does not have a pointer to Sp_rcontext_handler
(it only has the current rcontext offset).
- Removing members sp_instr_set members m_rcontext_handler and m_offset.
Deriving sp_instr_set from sp_rcontext_addr instead.
- Renaming sp_instr_cfetch member "List<sp_variable> m_varlist"
to "List<sp_fetch_target> m_fetch_target_list".
- Fixing LEX::sp_add_cfetch() to return the pointer to the
created sp_fetch_target instance (instead of returning bool).
This helps to make the grammar in sql_yacc.c simpler
- Renaming LEX::sp_add_cfetch() to LEX::sp_add_instr_cfetch(),
as `if(sp_add_cfetch())` changed its meaning to the opposite,
to avoid automatic wrong merge from earlier versions.
- Chaning the "List<sp_variable> *vars" parameter to sp_cursor::fetch
to have the data type "List<sp_fetch_target> *".
- Changing the data type of "List<sp_variable> &vars" in
sp_cursor::Select_fetch_into_spvars::send_data_to_variable_list()
to "List<sp_fetch_target> &".
- Adding THD helper methods get_rcontext() and get_variable().
- Moving the code from sql_yacc.yy into a new LEX method
LEX::make_fetch_target().
- Simplifying the grammar in sql_yacc.yy using the new LEX method.
Changing the data type of the bison rule sp_fetch_list from "void"
to "List<sp_fetch_target> *".
We now allow multitable queries with order by and limit, such as:
delete t1.*, t2.* from t1, t2 order by t1.id desc limit 3;
To predict what rows will be deleted, run the equivalent select:
select t1.*, t2.* from t1, t2 order by t1.id desc limit 3;
Additionally, index hints are now supported with single table delete statements:
delete from t2 use index(xid) order by (id) limit 2;
This approach changes the multi_delete SELECT result interceptor to use a temporary
table to collect row ids pertaining to the rows that will be deleted, rather than
directly deleting rows from the target table(s). Row ids are collected during
send_data, then read during send_eof to delete target rows. In the event that the
temporary table created in memory is not big enough for all matching rows, it is
converted to an aria table.
Other changes:
- Deleting from a sequence now affects zero rows instead of emitting an error
Limitations:
- The federated connector does not create implicit row ids, so we to use a key
when conditionally deleting. See the change in federated_maybe_16324629.test
Implement default values for parameters of stored routines
in both default and oracle mode.
- Default values for cursor parameters are *NOT* supported yet.
- An IN parameter with DEFAULT followed by an OUT param is not supported yet.
This combination will be enabled together with the arrow syntax:
sp1(v=>p2)
The default values can be either literals or expressions.
When it is an expression, it is only evaluated if the parameter
has not been supplied by the caller
(important if the expression has side effects).
Added support of the clause `UPDATE OF <columns>` for
BEFORE/AFTER UPDATE triggers. Triggers defined with this clause
are fired and run actions only in case an UPDATE statement affects
any of the listed columns. For columns not specified in the clause
`UPDATE OF <columns>`, an UPDATE statement with such columns as
targets don't result in running a trigger.
Output of SHOW TRIGGERS isn't affected by this task. Output of
the statement SHOW CREATE TRIGGER shows the clause `UPDATE OF <columns>`
if it was specified on trigger creation.
Tests accompany this task don't include tests that checking for cooperation of
the statement LOAD DATA and the clause `UPDATE OF <columns>` for
BEFORE/AFTER UPDATE triggers since the statement LOAD DATA is treated like
the statement INSERT INTO and therefore doesn't fire BEFORE/AFTER UPDATE
triggers.