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

8442 Commits

Author SHA1 Message Date
PranavKTiwari
885587d369 MDEV-28610 : Assertion marked_for_read() failed upon range select with virtual column in index.
Problem:
Executing a range select using an index that includes virtual/generated columns
(e.g. SELECT ... WHERE c BETWEEN ... ORDER BY d) could trigger:

Assertion 'marked_for_read()' failed in Field access during execution.

Cause:
The keyread initialization path was populating table->read_set using
mark_index_columns(), which follows the non-recursive index marking path.
This only marks direct index fields and does not resolve dependencies of
virtual/generated columns.

As a result, when an index contained virtual columns, their dependent base
columns were not included in read_set, leading to invalid access during
later evaluation of virtual columns.

Fix:
Replace mark_index_columns() with mark_index_columns_for_read() in the
keyread initialization path. This ensures the recursive read-marking logic is
used, so virtual/generated column dependencies are correctly propagated into
read_set before execution.
2026-10-12 10:03:59 +05:30
Jaeheon Shim
b3830bce9a MDEV-40698 Fix ROLLUP query results with empty result set
ROLLUP is defined as the UNION of grouping by every prefix of fields in
the original GROUP BY list. A ROLLUP query with an empty result set
returned zero rows when it should return a summary NULL, since grouping
by the empty prefix returns a single summary row.

The empty row case is handled in two separate locations. First, if the
optimizer is able to determine that no rows will be produced, e.g. due
to the table being empty or the WHERE condition resolving to false,
JOIN::send_row_on_empty_set is used to determine whether or not to send
an empty result row. Therefore, send_row_on_empty_set is modified to
include select_lex->olap == ROLLUP_TYPE.

Second, it may be the case that the absence of rows is not confirmed
until the execution phase. For instance when the WHERE condition is not
constant, or in the case of InnoDB where an empty table is not detected
during optimization. This is handled in both end_send_group and
end_write_group by this expression

    join->first_record ||
        (end_of_records && !join->group && !join->group_optimized_away)

The condition is extracted into need_empty_set_row and a second variable
empty_set_send_rollup_total is recorded to prevent running the default
rollup_send_data/rollup_write_data on the empty row case. This is
because the null summary row is already handled by send_data_with_check.
2026-09-02 06:30:40 +12:00
Dave Gosselin
bc8ef06ee9 MDEV-39441: Add dbug_print() helpers for join-related types
Introduces new dbug_print() functions for the following types:
  dbug_print(NESTED_JOIN)
  dbug_print(JOIN)
  dbug_print(TABLE_LIST)
  dbug_print(JOIN_TAB)
  dbug_print(List<Item>)

Typically pointers to instances of the given types are passed.
The dbug_print(List<Item>) is intended to show result row before
sending to client (e.g., invoke in debugger while tracing end_send)
but may work in other contexts.

These functions produce nicely formatted output, please use them
in conjunction with the formatted printfs available in GDB or LLDB:
   (gdb) printf "%s", dbug_print(join_tab)
  (lldb) p printf("%s", dbug_print(table))

Example output from dbug_print(JOIN) in LLDB.  The last line with
value 1302 is the number of characters produced by the printf
command.

(lldb) p printf("%s",dbug_print(join))
JOIN [0x15801bfe8]  table_count=3  const_tables=0
join_list 0x158018340 [1 element(s)]:
--- #0 ---
  [0x15801af10] "(nest_last_join)"  join=INNER (outer_join=0)  nested_join=0x15801b618 (elements=2)
    NESTED_JOIN [0x15801b618]
      used_tables:     0x0
      not_null_tables: 0x0
      n_tables:        0
      counter:         0
      nest_type:       1 (JOIN_OP_NEST)
      nj_map:          0x0
      join_list:
        join_list 0x15801b618 [2 element(s)]:
        --- #0 ---
          [0x15801a308] "t3"  join=LEFT (outer_join=1)  map=0x4
            ON: `test`.`t2`.`a` = `test`.`t3`.`a`
        --- #1 ---
          [0x158019b30] "(nest_last_join)"  join=INNER (outer_join=0)  nested_join=0x15801a238 (elements=2)
            NESTED_JOIN [0x15801a238]
              used_tables:     0x0
              not_null_tables: 0x0
              n_tables:        0
              counter:         0
              nest_type:       0
              nj_map:          0x0
              join_list:
                join_list 0x15801a238 [2 element(s)]:
                --- #0 ---
                  [0x1580187c0] "t1"  join=RIGHT (outer_join=2)  map=0x1
                    ON: `test`.`t1`.`a` = `test`.`t2`.`a`
                --- #1 ---
                  [0x158018f08] "t2"  join=INNER (outer_join=0)  map=0x2
(int) 1302
2026-08-14 09:47:46 -04:00
Abdelrahman Hedia
787c5cf6c0 MDEV-24931: Fix Bitmap<64>::is_prefix assertion with >64-column NATURAL JOIN on derived table
When a NATURAL JOIN operates on a derived table (or view with
derived_merge=off) having more than 64 columns, the optimizer's
generate_derived_keys_for_table() would:

1) Overflow a 32-bit shift: (key_part_map)(1 << parts) when parts >= 32
2) Create a derived key with more parts than Bitmap<64>/key_part_map can hold
3) Crash on DBUG_ASSERT(prefix_size <= width) in Bitmap<64>::is_prefix(65)

Fix: cap the number of hash-join KEYUSE entries for a materialized derived
table at the handler's max_key_parts(). Excess columns are excluded from the
key, so generate_derived_keys_for_table() never builds a key with more parts
than key_part_map (64 bits) or Bitmap<64> can represent. Also fix the shift
to cast before shifting: (key_part_map) 1 << parts.
2026-06-26 14:44:16 +12:00
Oleksandr Byelkin
6828ff7390 Merge branch '10.6' into 10.11 2026-05-08 07:38:22 +02:00
Sergei Golubchik
32327e572b MDEV-39540 crash due to narrowing cast in update_ref_and_keys() 2026-05-06 13:28:33 +02:00
Alexey Botchkov
de14631413 MDEV-15479 Empty string is erroneously allowed as a GEOMETRY column value.
Empty string disallowed.
2026-04-28 00:41:52 +04:00
Mikhail Pochatkin
cbcff6324a MDEV-39173 Replace sprintf with snprintf, remove deprecated pragma
Remove the unscoped #pragma GCC diagnostic ignored
"-Wdeprecated-declarations" from include/violite.h and replace all
sprintf/vsprintf calls with snprintf/vsnprintf across the codebase.

Where possible, pass actual buffer sizes as function parameters
instead of using hardcoded constants or recomputing strlen().
Notable API changes:
- get_date(): added to_len parameter for buffer size
- nice_time(), end_timer() in client/mysql.cc: added buff_size
- generate_new_name(): added name_size parameter
- calc_md5(): added buffer_size parameter (fixes off-by-one)
- MD5_HASH_TO_STRING macro: added _size parameter
- Added MYSQL_UDF_MAX_RESULT_LENGTH constant in mysql_com.h

Also fixed a pre-existing bug in sql/rpl_mi.cc where the ellipsis
"..." was written to the wrong buffer (dbuff instead of buff).

Vendored code is excluded: extra/readline, extra/wolfssl,
wsrep-lib, libmariadb, storage/mroonga/vendor, zlib.
2026-04-16 19:00:12 +10:00
Oleg Smirnov
ceaef36bab MDEV-32868 Cleanup (no change of logic)
Switch branches of the `if` condition to make the code
flow more naturally.
2026-04-16 14:40:56 +07:00
Oleg Smirnov
cfea659cf0 MDEV-32868 SELECT NULL,NULL IN (SUBQUERY) returns 0 instead of NULL
When evaluating (SELECT NULL, NULL) IN (SELECT 1, 2 FROM t), the result
was incorrectly 0 instead of NULL.

The IN-to-EXISTS transformation wraps comparison predicates with
trigcond() guards:

  WHERE trigcond(NULL = 1) AND trigcond(NULL = 2)

During optimization, make_join_select() evaluated this as a "constant
condition". With guards ON, the condition evaluated to FALSE (NULL
treated as FALSE), triggering "Impossible WHERE". At runtime, guards
would be turned OFF for NULL columns, but the optimizer had already
marked the subquery as returning no rows.

Fix: check can_eval_in_optimize() instead of is_expensive() in
make_join_select(). Unlike is_expensive(), can_eval_in_optimize()
also verifies const_item(), which returns FALSE for Item_func_trig_cond
2026-04-16 14:40:56 +07:00
Sergei Petrunia
4e8c17534d MDEV-38072 Optimizer choosing the wrong plan
(Variant 3, more comments)
When multiple indexes produce ordering that matches the
ORDER BY ... LIMIT clause, like

  INDEX idx1(kp1, kp2)
  INDEX idx2(kp2)
and the query with
  WHERE kp1=const ORDER BY kp2 LIMIT 2

then test_if_cheaper_ordering() in pre-11.0 versions will choose
the index with the smallest KEY::user_defined_key_parts (idx2 in
this example). This can produce a much worse query plan.

The fix is to do what MariaDB 11.0 would do: use the index for which
we have the cheapest access method. The cost of access method here
takes into account that we will stop after producing #LIMIT rows.

The fix is controlled by the
  @@optimizer_adjust_secondary_key_costs=fix_order_by_index_choice
flag and is OFF by default.
2026-04-16 09:26:39 +10:00
Sergei Golubchik
b29d3779e4 Merge branch '10.6' into 10.11 2026-01-28 14:22:20 +01:00
bsrikanth-mariadb
dc28140c89 MDEV-31255: Crash with fulltext search subquery in explain delete/update
ft_handler isn't getting initialized for subqueries inside explain
delete/update queries. However, ft_handler is accessed inside ha_ft_read(),
and is the reason for NULL pointer exception.
This is not the case with non-explain delete/update queries, as
well as explain/non-explain select queries.

Follow the approach the SELECT statements are using in
JOIN::optimize_constant_subqueries(): remove SELECT_DESCRIBE
flag when invoking optimization of constant subqueries.

Single-table UPDATE/DELETEs have SELECT_LEX but don't have JOIN.
So, we make optimize_constant_subqueries() not to be a member
of JOIN class, and instead move it to SELECT_LEX, and then
invoke it from single-table UPDATE/DELETE as well as for SELECT queries.
2026-01-27 16:42:41 +05:30
Oleg Smirnov
cd02709a31 MDEV-38574 Rename cloning functions of class Item and descendants
Rename `Item::clone_item()` to `clone_constant()`, and do
the same for any overloads in descendant items.
The function returns non-NULL only for items that represent
constant literals.
2026-01-22 12:57:48 +01:00
Sergei Golubchik
0bfe10e2b7 MDEV-38087 Unexpected error Unknown column '???' in 'GROUP BY'
with `ORDER BY number` if the number doesn't refer to a valid
result column, use this number in the error message not '???'.
2026-01-14 19:44:30 +01:00
Aleksey Midenkov
9f70e0a76c MDEV-32299 Segfault when preparing unreferenced select in recursive CTE
With_element::prepare_unreferenced() contains query from recursive CTE
which is upper owner of unreferenced CTE itself. Such recursive
derived table is resolved into its expression and it tries to resolve
fields or any other data from subqueries which are not yet
prepared. These subqueries are preparing in upper frames of the same
stack after prepare_unreferenced() finishes.

That does not happen when recursion happens in referenced CTE because
in that case there are links between the derived tables defined by the
parser and descending into such recursion descends further into all
the dependency queries. No such link exists in unreferenced query
therefore it misses its upper dependencies.

The fix moves prepare_unreferenced() later after JOIN::prepare() so it
has the chance to access all the prepared data.

There may be another fix of failing unreferenced queries with error as
such kind of queries seem to be not usable.
2025-12-23 13:42:45 +03:00
ethanxhzhao
a9006bdf29 MDEV-37157 When using rownum in having clause without group by or in subquery, the result may be wrong
Fix rownum in HAVING clause or subquery

Description
==========
When rownum used in HAVING without GROUP BY, accepted_rows should be
incremented even if the having cond's value is 0.

When rownum used in subquery
1. Forbid subquery transformed to semi join.
2. New conditions must be added into having clause when doing in2exists.
2025-12-18 10:59:09 +01:00
Sergei Petrunia
68432a0bc3 MDEV-29638 Crash when considering Split-Materialized plan
Variant 2: "Don't call optimize_stage2 too early"

The affected query had Split-Materialized derived table in another
derived table:

  select *   -- select#1
  from (
    select *  -- select#2
    from t1,
         (select * from t2 ... group by t2.group_col) DT -- select#3
    where t1.col=t2.group_col
  ) TBL;

The optimization went as follows:
JOIN::optimize() calls select_lex->handle_derived(DT_OPTIMIZE) which
calls JOIN::optimize() for all (direct and indirect) children SELECTs.

select#1->handle_derived() calls JOIN::optimize() for select#2 and #3;
select#2->handle_derived() calls JOIN::optimize() for select#3 the second
time. That call would call JOIN::optimize_stage2(), assuming the query
plan choice has been finalized for select#3.

But after that, JOIN::optimize() for select#2 would continue and consider
Split-Materialized for select#3. This would attempt to pick another join
order and cause a crash.

The fix is to have JOIN::optimize() not call optimize_stage2() ever.
Finalizing the query plan choice is now done by a separate call:
  select_lex->handle_derived(thd->lex, DT_OPTIMIZE_STAGE2)

which invokes JOIN::optimize_stage2() and saves the query plan.
2025-10-28 10:04:40 +02:00
bsrikanth-mariadb
f84f2ce534 MDEV-31887: wrong result with split optimization
Wrong result is produced when split-materialized optimization is used for
grouping with order by and limit.

The fix is to not let Split-Materialized optimization to happen
when the sub-query has an ORDER BY with LIMIT, by returning FALSE early
in the method opt_split.cc#check_for_splittable_materialized()

However, with just the above change, there is a side-effect of
NOT "using index for group by" in the scenario when
all the following conditions are met: -
  1. The query has derived table with GROUP BY and ORDER BY LIMIT
  2. joined in a way that would allow Split-Materialized
     if ORDER BY LIMIT wasn't present
  3. An index suitable for using "index for group-by"
  4. No where clause so that, "using for group by" is applicable,
     but the index is not included in "possible_keys".
The reason being, join_tab's "keys" field wasn't being set in
sql_select.cc#make_join_select(). So, made this change as well
as part of this PR.
2025-10-24 20:08:59 +05:30
bsrikanth-mariadb
21be9fbca0 MDEV-37901: Wrong result on QUICK_GROUP_MIN_MAX_SELECT WITH TIES
The "FETCH FIRST n ROWS WITH TIES" was not enforced when the SELECT used
"using index for group-by".

This was caused by an optimization which removed the ORDER BY clause
when the GROUP BY clause prescribed a compatible ordering.

Other GROUP BY strategies used workarounds to still handle WITH TIES,
see comment to using_with_ties_and_group_min_max() in this patch for
details. QUICK_GROUP_MIN_MAX_SELECT didn't have a workaround.

Fix this by disabling removal of ORDER BY when
QUICK_GROUP_MIN_MAX_SELECT is used.
2025-10-24 17:12:16 +05:30
bsrikanth-mariadb
8be6d2540b MDEV-35206: Assertion failure in JOIN::dbug_verify_sj_inner_tables
A nested select query is crashing in when optimizer_join_limit_pref_ratio=10
and optimizer_search_depth=1 due to an assertion failure in
JOIN::dbug_verify_sj_inner_tables().

In sql_select.cc#choose_plan(), there are 2 back-2-back calls to
greedy_search(). The first one is invoked to build a join plan
that can short-cut ORDER BY...LIMIT, while the second invocation
to not consider short-cut.

The greedy_search() should start with a value of join->cur_sj_inner_tables
set to 0. However, the first greedy_search() call left the value of
join->cur_sj_inner_tables to "6". This caused the assert to fail in
dbug_verify_sj_inner_tables() as soon as the second greedy_search() started,
where in it was expecting a value of 0.

Similar problem is noticed with cur_embedding_map in the case of nested
joins, and nested_join counter.

introduced init_join_plan_search_state() which is called
from start of greedy_search() and optimize_straight join
that does all the needed initialization by :
  1. setting 0's to cur_sj_inner_tables, and cur_embedding_map
  2. invoking reset_nj_counters().
2025-10-20 09:56:05 +05:30
Oleg Smirnov
b1c2963256 MDEV-22241 Revert renaming of LEX::set_limit_rows_examined() as it breaks compatibility with ColumnStore 2025-10-03 13:07:56 +03:00
Oleg Smirnov
f64e3fe3f6 MDEV-22241 Refactoring and preparation for the fix 2025-10-02 16:14:06 +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
Sergei Golubchik
053f9bcb5b Merge branch '10.6' into 10.11 2025-07-28 18:06:31 +02:00
Rex
67745e4dbf MDEV-30334 Optimizer trace produces invalid JSON with WHERE subquery
Simple code rearrangement to stop it displaying an unsigned int in a String.
2025-07-17 09:18:18 +02:00
Aleksey Midenkov
c4a2688328 MDEV-24726 Assertion on compressed varstring as key field in optimizer
temporary table

Compressed field cannot be part of a key by its nature: there is no
data to order, only the compressed data.

For optimizer temporary table we create uncompressed substitute.

In all other cases (MDEV-16808) we don't use key: add_keyuse() is
skipped by !field->compression_method() condition.
2025-07-08 11:44:49 +03:00
bsrikanth-mariadb
0b16d7871c MDEV-37195:
For all degenerate select queries having sub-queries in them,
the field rows_examined in the slow query log is always being set to 0.

The problem is that, although sub-queries increment the rows_examined field
of the thd object correctly, the degenerate outer select query is resetting
the rows_examined to zero after it has finished execution, by
invoking thd->set_examined_row_count(0).

The solution is to remove the thd->set_examined_row_count(0) in the
degenerate select queries.
2025-06-23 01:47:21 -04:00
Daniel Black
96045fb53a MDEV-37052: JSON_TABLE stack overflow handling errors
The recursive nature of add_table_function_dependencies
resolution meant that the detection of a stack overrun
would continue to recursively call itself.
Its quite possible that a user SQL could get multiple
ER_STACK_OVERRUN_NEED_MORE errors.

Additionaly the results of the stack overrrun check
result was incorrectly assigned to a table_map result.

Its only because of the "if error" check after
add_table_function_dependencies is called, that would
detected the stack overrun error, prevented a
potential corruped tablemap is from being processed.

Corrected add_table_function_dependencies to stop and
return on the detection of a stack overrun error.

The add_extra_deps call also was true on a stack overrun.
2025-07-09 08:57:47 +10:00
Aleksey Midenkov
fc465596ea MDEV-37164 Assertion `vers_conditions.delete_history' failed upon PREPARE
Wrong assertion was added by f1f9284181 (MDEV-34046) because PS
parameter is applicable not only to DELETE HISTORY.

Keeping value of select_lex->where for DELETE HISTORY was remade via
prep_where which is read by reinit_stmt_before_use(). For SELECT
prep_where is set in JOIN::optimize_inner() and that is not called for
DELETE.
2025-07-08 17:44:11 +03:00
Aleksey Midenkov
f1f9284181 MDEV-34046 Parameterized PS converts error to warning, causes
replication problems

DELETE HISTORY did not process parameterized PS properly as the
history expression was checked on prepare stage when the parameters
was not yet substituted. In that case check_units() succeeded as there
is no invalid type: Item_param has type_handler_null which is
inherited from string type and this is valid type for history
expression. The warning was thrown when the expression was evaluated
for comparison on delete execution (when the parameter was already
substituted).

The fix postpones check_units() until the first PS execution. We have
to postpone where conditions processing until the first execution and
update select_lex.where on every execution as it is reset to the state
after prepare.
2025-06-12 14:52:00 +03:00
bsrikanth-mariadb
2263c8a1f7 MDEV-36461: Optimizer trace: remove join_execution node
In non-EXPLAIN queries with subqueries, the trace was flooded
with empty "join_execution":{} nodes. Now, they are gone.

The "Range checked for each record" optimization still prints
content into trace on join execution. Now, we wrap it into
"range-checked-for-each-record" to delimit the invocations.
This new object has fields "select_id" which corresponds to
the outer query block, and the "loop" which corresponds to
the  inner query block iteration number. Additionally,
the field  "row_estimation" which itself is an object has
"table", and "range_analysis" fields that were moved
from the old "join_execution"'s steps array.
2025-04-28 01:25:05 -04:00
Sergei Golubchik
ab71860161 cleanup: check_column_name(const Lex_ident &name) 2025-04-22 12:03:05 +02:00
Rex
07b442aa68 MDEV-36607 find_order_in_list mismatch when order item needs fixing()
This bug is exposed by MDEV-30073, causing bogus warning messages to
be pushed by find_order_in_list(), but which is otherwise benign.

An existing test case in show_explain.test, MDEV-238 can be used together
with an assert to find a query which exposes the issue.

  if (resolution == RESOLVED_BEHIND_ALIAS &&
      order_item->fix_fields_if_needed_for_order_by(thd, order->item))
    return TRUE;

  /* Lookup the current GROUP field in the FROM clause. */
  order_item_type= order_item->type();
+ DBUG_ASSERT( order_item_type == (*order->item)->type() );

This will fail here

CREATE TABLE t2 ( a INT );
INSERT INTO t2 VALUES (1),(2),(1),(4),(2);
explain SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias
GROUP BY alias.a;

This assert makes little sense after the patch.

DaveGosselin-MariaDB approved these changes Apr 18, 2025
2025-04-18 07:47:56 +11:00
Julius Goryavsky
1a013cea95 Merge branch '10.6' into '10.11' 2025-04-16 03:34:40 +02:00
Julius Goryavsky
88dfa6bcee Merge branch '10.5' into '10.6' 2025-04-15 01:49:48 +02:00
Oleksandr Byelkin
ba34657cd2 MDEV-35238 (MDEV-34922) Wrong results from a tables with a single record and an aggregate
The problem is that copy function was used in field list but never
copied in this execution path.

So copy should be performed before returning result.

Protection against uninitialized copy usage added.
2025-04-14 10:47:27 +02:00
Sergei Petrunia
3b4de4c281 MDEV-32084: Assertion in best_extension_by_limited_search() ...
When subquery with LEFT JOIN is converted into semi-join, it is possible
to construct cases where the LEFT JOIN's ON expression refers to a table
in the current select but not in the current join nest. For example:

  t1 SEMI JOIN (
    t2
    LEFT JOIN (t3 LEFT JOIN t4 ON t4.col=t1.col) ON expr
  )

here, ON t4.col=t1.col" has this property. Let's denote it as
ON-EXPR-HAS-REF-OUTSIDE-NEST.

The optimizer handles LEFT JOINs like so:
- Outer join runtime requires that "inner tables follow outer" in
  any join order.
- Join optimizer enforces this by constructing join orders that follow
  table dependencies as they are specified in TABLE_LIST::dep_tables.
- The dep_tables are set in simplify_joins() according to the contents
  of ON expressions and LEFT JOIN structure.

However, the logic in simplify_joins() failed to account for possible
ON-EXPR-HAS-REF-OUTSIDE-NEST. It assumed that references outside of the
current join nest could only be OUTER_REF_TABLE_BIT or RAND_TABLE_BIT.

The fix was to add the missing logic.
2025-03-26 15:52:54 +02:00
Sergei Petrunia
47d11328c9 MDEV-36381: Comment "Procedure of keys generation ..." is in the wrong place
Move it from the middle of table.cc to sql_select.cc:generate_derived_keys()
2025-03-25 19:05:32 +02:00
Oleg Smirnov
733852d4c3 BKA join cache buffer is employed despite join_cache_level=3 (flat BNLH)
In the `check_join_cache_usage()` function there is a branching issue
where an accidental fall-through to BKA/BKAH buffers may occur, even
when the join_cache_level setting does not permit their use.

This patch corrects the condition to ensure that BKA/BKAH join caching
is only enabled when explicitly allowed by join_cache_level

Reviewer: Sergei Petrunia <sergey@mariadb.com>
2025-02-27 16:47:25 +07:00
Sergei Petrunia
43c5d1303f MDEV-35958 Cost estimates for materialized derived tables are poor
Backport of commit 74f70c3944 to 10.11.
The new logic is disabled by default, to enable, use
optimizer_adjust_secondary_key_costs=fix_derived_table_read_cost.

== Original commit comment ==
Fixed costs in JOIN_TAB::estimate_scan_time() and HEAP

Estimate_scan_time() calculates the cost of scanning a derivied table.
The old code did not take into account that the temporary table heap table
may be converted to Aria.

  Things fixed:
  - Added checking if the temporary tables data will fit in the heap.
    If not, then calculate the cost based on the designated internal
    temporary table engine (Aria).
  - Removed MY_MAX(records, 1000) and instead trust the optimizer's
    estimate of records. This reduces the cost of temporary tables a bit
    for small tables, which caused a few changes in mtr results.
  - Fixed cost calculation for HEAP.
  - HEAP costs->row_next_find_cost was not set. This does not affect old
    costs calculation as this cost slot was not used anywhere.
    Now HEAP cost->row_next_find_cost is set, which allowed me to remove
    some duplicated computation in ha_heap::scan_time()
2025-02-10 21:14:01 +02:00
Sergei Petrunia
5f68fd52a9 MDEV-35955 Wrong result for UPDATE ... ORDER BY LIMIT which uses tmp.table
(Variant 2)
Multi-table UPDATE ... ORDER BY ... LIMIT could update the wrong rows when
ORDER BY was resolved by Using temporary + Using filesort.

== Background: ref_pointer_array ==
join->order[->next*]->item point into join->ref_pointer_array, which
has pointers to the used Item objects.

This indirection is employed so that we can switch the ORDER BY expressions
from using the original Items to using the values of their "image" fields
in the temporary table.
The variant of ref_pointer_array that has pointers to temp table fields
is created when JOIN::make_aggr_tables_info() calls
change_refs_to_tmp_fields().

== The problem ==
The created array didn't match element-by-element the original
ref_pointer_array. When arrays were switched, ORDER BY elements started
to point to the wrong temp.table fields, causing the wrong sorting.

== The cause ==
The cause is JOIN::add_fields_for_current_rowid(). This function is
called for UPDATE statements to make the rowids of rows in the original
tables to be saved in the temporary tables.

It adds extra columns to the select list in table_fields argument.
However, select lists are organized in a way that extra elements must
be added *to the front* of the list, and then change_refs_to_tmp_fields()
will add extra fields *to the end* of ref_pointer_array.

So, add_fields_for_current_rowid() adds new fields to the back of
table_fields list. This caused change_refs_to_tmp_fields() to produce
ref_pointer_array slice with extra elements in the front, causing any
references through ref_pointer_array to come to the wrong values.

== The fix ==
Make JOIN::add_fields_for_current_rowid() add fields to the front of
the select list.
2025-01-31 11:45:16 +02:00
Marko Mäkelä
420d9eb27f Merge 10.6 into 10.11 2025-01-08 12:51:26 +02:00
Monty
996e7fd7d5 Avoid printing "rowid_filter_skipped" in optimizer trace if no rowid filter
There is no point in saying something is skipped when it does not exists.
2025-01-05 16:40:11 +02:00
Monty
e600f9aebb MDEV-35750 Change MEM_ROOT allocation sizes to reduse calls to malloc() and avoid memory fragmentation
This commit updates default memory allocations size used with MEM_ROOT
objects to minimize the number of calls to malloc().

Changes:
- Updated MEM_ROOT block sizes in sql_const.h
- Updated MALLOC_OVERHEAD to also take into account the extra memory
  allocated by my_malloc()
- Updated init_alloc_root() to only take MALLOC_OVERHEAD into account as
  buffer size, not MALLOC_OVERHEAD + sizeof(USED_MEM).
- Reset mem_root->first_block_usage if and only if first block was used.
- Increase MEM_ROOT buffers sized used by my_load_defaults, plugin_init,
  Create_tmp_table, allocate_table_share, TABLE and TABLE_SHARE.
  This decreases number of malloc calls during queries.
- Use a small buffer for THD->main_mem_root in THD::THD. This avoids
  multiple malloc() call for new connections.

I tried the above changes on a complex select query with 12 tables.
The following shows the number of extra allocations that where used
to increase the size of the MEM_ROOT buffers.

Original code:
- Connection to MariaDB:   9 allocations
- First query run:       146 allocations
- Second query run:       24 allocations

Max memory allocated for thd when using with heap table:  61,262,408
Max memory allocated for thd when using Aria tmp table:      419,464

After changes:
Connection to MariaDB:     0 allocations
- First run:              25 allocations
- Second run:              7 allocations

Max memory allocated for thd when using with heap table:  61,347,424
Max memory allocated for thd when using Aria table:          529,168

The new code uses slightly more memory, but avoids memory fragmentation
and is slightly faster thanks to much fewer calls to malloc().

Reviewed-by: Sergei Golubchik <serg@mariadb.org>
2025-01-05 16:40:11 +02:00
Monty
52c29f3bdc MDEV-35469 Heap tables are calling mallocs to often
Heap tables are allocated blocks to store rows according to
my_default_record_cache (mapped to the server global variable
 read_buffer_size).
This causes performance issues when the record length is big
(> 1000 bytes) and the my_default_record_cache is small.

Changed to instead split the default heap allocation to 1/16 of the
allowed space and not use my_default_record_cache anymore when creating
the heap. The allocation is also aligned to be just under a power of 2.

For some test that I have been running, which was using record length=633,
the speed of the query doubled thanks to this change.

Other things:
- Fixed calculation of max_records passed to hp_create() to take
  into account padding between records.
- Updated calculation of memory needed by heap tables. Before we
  did not take into account internal structures needed to access rows.
- Changed block sized for memory_table from 1 to 16384 to get less
  fragmentation. This also avoids a problem where we need 1K
  to manage index and row storage which was not counted for before.
- Moved heap memory usage to a separate test for 32 bit.
- Allocate all data blocks in heap in powers of 2. Change reported
  memory usage for heap to reflect this.

Reviewed-by: Sergei Golubchik <serg@mariadb.org>
2025-01-05 16:40:11 +02:00
Marko Mäkelä
3f914afd3a Merge 10.6 into 10.11 2025-01-02 12:39:56 +02:00
Yuchen Pei
671f80c738 Merge branch '10.5' into 10.6 2024-12-17 11:06:09 +11:00
Oleg Smirnov
e640373389 Revert "MDEV-26427 MariaDB Server SEGV on INSERT .. SELECT"
This reverts commit 49e14000ee
as it introduces regression MDEV-29935 and has to be reconsidered
in general
2024-12-14 13:08:17 +07:00
Kristian Nielsen
e7c6cdd842 Merge 10.6 -> 10.11
Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>
2024-12-05 10:11:58 +01:00