Skip to content

MDEV-40032: Promote wide VARCHAR to BLOB for HEAP internal temp tables - #5230

Open
arcivanov wants to merge 1 commit into
MariaDB:bb-blob-main-montyfrom
arcivanov:MDEV-40032-main
Open

MDEV-40032: Promote wide VARCHAR to BLOB for HEAP internal temp tables#5230
arcivanov wants to merge 1 commit into
MariaDB:bb-blob-main-montyfrom
arcivanov:MDEV-40032-main

Conversation

@arcivanov

@arcivanov arcivanov commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Automatically promote VARCHAR fields with octet_length > HEAP_CONVERT_IF_BIGGER_TO_BLOB (32 bytes) to BLOB when the temporary table uses the HEAP engine, storing only actual data in continuation chains (from MDEV-38975) instead of reserving the full declared width in every row
  • Uses Field_blob_key as the single unified mechanism for ALL blob columns in HEAP temp tables -- both GROUP BY/DISTINCT keys and derived table ref access
  • Fixes three latent Field_blob_key bugs exposed by ref access: key_cmp() pointer-vs-data comparison, cmp_buffer_with_ref() stale cache, and new_key_field() memory leak
  • New heap_store_key_blob_ref store_key subclass bypasses SQL-layer key buffer for BLOB key parts, enabling ref access for all BLOB sizes including LONGBLOB/JSON on HEAP derived tables

Based on bb-blob-main-monty, which already carries all prerequisites (MDEV-38975 #5222, MDEV-40030, MDEV-40029, MDEV-40033) and the HEAP fix stack (MDEV-40523, MDEV-40376, MDEV-40378, MDEV-40431, MDEV-40448, MDEV-40447); this PR is the single promotion commit on top of that branch.

Note on heap.blob_window_overflow result changes: CONCAT(@c:=@c+1, ':', PERCENTILE_DISC(...)) declares a width of only 39 chars (Item_sum_percentile_disc::fix_length_and_dec() never sets max_length from the ORDER BY argument), so before promotion the 2908-char percentile value was silently truncated to 39 when materialized through the VARCHAR(39) temp column. With promotion the column becomes a BLOB and the full value survives (min_len 39 -> 2910); the COUNT(DISTINCT e) dedup temp table then also converts to disk under the test's 4MB max_heap_table_size (Created_tmp_disk_tables 1 -> 2).

Testing

  • heap suite 38/38, hp_* unit tests 9/9, main suite 1392/1392 on the rebased branch

JIRA: https://jira.mariadb.org/browse/MDEV-40032

@CLAassistant

CLAassistant commented Jun 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements support for BLOB, TEXT, JSON, and GEOMETRY columns in the HEAP (MEMORY) storage engine (MDEV-38975), allowing operations like GROUP BY, DISTINCT, and joins on blob columns to remain in memory without immediately forcing a conversion to Aria on disk. The code review identified several critical and high-severity issues, including a potential server crash due to a missing NULL check on ftfunc_list in sql_derived.cc, a compilation error from the missing definition of Item_type_holder::create_tmp_field_ex, and a potential infinite loop in heap_check_heap() under corrupted data. Additionally, the reviewer provided actionable suggestions to clean up redundant assignments, use typed pointer arithmetic instead of raw byte manipulation, and parenthesize macro operands to prevent precedence issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread sql/sql_derived.cc
Comment thread sql/item.h
Comment thread storage/heap/_check.c
Comment thread sql/sql_select.h
Comment thread sql/sql_show.cc
Comment thread sql/field.cc
@gkodinov gkodinov added the External Contribution All PRs from entities outside of MariaDB Foundation, Corporation, Codership agreements. label Jun 15, 2026

@gkodinov gkodinov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after reverting the space only changes. Please stand by for the final review.

@gkodinov
gkodinov requested a review from montywi June 15, 2026 07:31
@arcivanov
arcivanov force-pushed the MDEV-40032-main branch 3 times, most recently from 62b84f6 to 45a781a Compare July 19, 2026 23:55
Use `Field_blob_key` as the single unified mechanism for ALL blob
columns in HEAP temp tables, replacing Phase 1's dual approach of
`Field_blob_key` for GROUP BY/DISTINCT + `rebuild_blob_key_from_segments`
for derived table ref access.

Key changes:
- `Tmp_field_param::is_heap_engine()` gates `Field_blob_key` creation
  for all blob fields in HEAP temp tables (not just `part_of_unique_key`)
- `varstring_type_handler()` promotes VARCHAR > `HEAP_CONVERT_IF_BIGGER_TO_BLOB`
  to blob via `blob_type_handler()` -> `type_handler_blob_key`
- `Type_handler_blob_common::type_handler_for_tmp_table()` returns
  `blob_key_type_handler()` when `is_heap_engine()`
- `Item_field::create_tmp_field_from_item_field()` redirects HEAP blob
  fields through the type handler system
- `Item_type_holder::create_tmp_field_ex()` extended for UNION/CTE blobs

Fix three latent bugs in `Field_blob_key` exposed by ref access:

1. `Field_blob_key::key_cmp()` treated key bytes at offset 4 as inline
   data, but the key format is `[4B length][8B pointer_to_data]` --
   comparison was against raw pointer bytes instead of actual data.

2. `cmp_buffer_with_ref()` eq_ref cache compared raw key buffer bytes.
   When `Field_blob_key::value` buffer is reused across lookups, the
   `[4B length][8B pointer]` bytes don't change even when the pointed-to
   data differs, causing stale result reuse. Disable the cache for all
   HEAP blob key parts (remove the `length == 0` guard).

3. `Field_blob_key::new_key_field()` returns `Field_blob_key` (unlike
   `Field_blob::new_key_field()` which returns `Field_varstring`). The
   `store_key` mechanism stores into `to_field->value` String, which
   leaks because `store_key` is `Sql_alloc` with no destructor. Add
   `store_key::cleanup()` called from `JOIN_TAB::cleanup()` and
   `subselect_uniquesubquery_engine::cleanup()`.

`Field_blob_key::key_part_length_bytes()` changed from 4 to 0 so
`store_length = key_length (12) + null_byte + 0`, matching HEAP's
`seg->length (12) + null` for correct multi-part key alignment.

`hp_key_cmp()` blob packlength changed from hardcoded 4 to
`seg->bit_start` (actual field packlength) for TEXT (packlength=2).

Re-record GROUP_CONCAT-related results: the `Tmp_field_param` threading
through `tmp_table_field_from_field_type()` (base branch) closed a
plumbing gap where `Item_sum` and literal items dropped the param, so
they now reach the HEAP promotion gates like all other expression
items. `GROUP_CONCAT` results in HEAP temp tables become
`Field_blob_key` (longtext metadata, 12-byte blob key parts),
consistent with the `Item_func` path that was already recorded
(e.g. `substring()` sj-materialization keys).
@arcivanov
arcivanov changed the base branch from main to bb-blob-main-monty July 30, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

External Contribution All PRs from entities outside of MariaDB Foundation, Corporation, Codership agreements.

Development

Successfully merging this pull request may close these issues.

4 participants