Bug #120437 information_schema.partitions returns UINT64_MAX (18446744073709551615) for DATA_LENGTH of partition dropped during conc
Submitted: 11 May 8:47 Modified: 11 May 8:57
Reporter: Yakir Gibraltar (OCA) Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: Information schema Severity:S2 (Serious)
Version:8.0.35, 8.4.8, 9.3.0, 9.7.0 OS:Any
Assigned to: CPU Architecture:Any

[11 May 8:47] Yakir Gibraltar
Description:
When a partition is dropped while an information_schema.partitions query is executing, the DATA_LENGTH column returns 18446744073709551615 (= (ulonglong)-1 = UINT64_MAX = 2^64-1) instead of NULL for that partition row.

Confirmed affected: 8.0.35, 8.4.8, 9.3.0, 9.7.0 (all versions tested).

--- Observed symptom ---
In production monitoring queries such as:

  SELECT partition_name,
         (data_length + index_length) / 1024 / 1024 / 1024 AS size_gb
  FROM   information_schema.partitions
  WHERE  table_schema = 'mydb';

the size_gb column shows 17179869183.999999999069 (~17 GB) for a recently-dropped partition, triggering false disk-space alerts.

--- Root cause ---
Item_func_internal_data_length::val_int() and Item_func_internal_index_length::val_int() in sql/item_func.cc call get_table_statistics() and return its raw result without checking for the (ulonglong)-1 error sentinel. When the partition does not exist (ER_UNKNOWN_PARTITION is raised), get_table_statistics() returns (ulonglong)-1 but does NOT set null_value=true, and neither does the caller — so raw UINT64_MAX leaks into the result set.

Item_func_internal_data_free::val_int() has the correct guard:
  if (null_value == false && result == (ulonglong)-1) null_value = true;
but data_length and index_length are both missing it.

--- Race mechanism ---
The outer I_S scan reads mysql.table_partitions rows using a REPEATABLE READ snapshot. For each row, INTERNAL_DATA_LENGTH() is evaluated via an attachable transaction that opens a NEWER snapshot — one where the partition may already have been dropped and committed. This triggers ER_UNKNOWN_PARTITION in the storage layer, which returns (ulonglong)-1 without setting null_value=true.

How to repeat:
Guaranteed reproduction using the REPEATABLE READ.

-- Setup (run once)
CREATE TABLE parts (
    id INT NOT NULL, d DATE NOT NULL, PRIMARY KEY (id, d)
) PARTITION BY RANGE (TO_DAYS(d)) (
    PARTITION p0 VALUES LESS THAN (TO_DAYS('2024-01-01')),
    PARTITION p1 VALUES LESS THAN (TO_DAYS('2024-02-01')),
    PARTITION p2 VALUES LESS THAN (TO_DAYS('2024-03-01')),
    PARTITION p3 VALUES LESS THAN (MAXVALUE)
);
INSERT INTO parts VALUES (1,'2023-12-15'),(2,'2024-01-15'),(3,'2024-02-15'),(4,'2024-03-15');

-- Session A: lock RR snapshot showing p1, then sleep
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT partition_name, data_length
FROM   information_schema.partitions
WHERE  table_schema = DATABASE() AND table_name = 'parts' AND partition_name = 'p1';
SELECT SLEEP(10);   -- Session B drops p1 during this sleep
SELECT partition_name, data_length, index_length
FROM   information_schema.partitions
WHERE  table_schema = DATABASE() AND table_name = 'parts';
ROLLBACK;

-- Session B: run while Session A is inside SLEEP(10)
ALTER TABLE parts DROP PARTITION p1;

-- Expected result (after fix):
-- p1 row: DATA_LENGTH = NULL, INDEX_LENGTH = NULL

-- Actual result (bug):
-- p1 row: DATA_LENGTH = 18446744073709551615, INDEX_LENGTH = 0

A self-contained bash repro script (docker + mysql client only) is attached to this report.

Suggested fix:
Add the missing null_value guard to both functions in sql/item_func.cc.
The identical guard already exists in Item_func_internal_data_free::val_int().

--- sql/item_func.cc ---

longlong Item_func_internal_data_length::val_int() {
  DBUG_TRACE;
  const ulonglong result = get_table_statistics(
      args, arg_count,
      dd::info_schema::enum_table_stats_type::DATA_LENGTH,
      &null_value);
  if (null_value == false && result == (ulonglong)-1) null_value = true;  // ADD
  return result;
}

longlong Item_func_internal_index_length::val_int() {
  DBUG_TRACE;
  const ulonglong result = get_table_statistics(
      args, arg_count,
      dd::info_schema::enum_table_stats_type::INDEX_LENGTH,
      &null_value);
  if (null_value == false && result == (ulonglong)-1) null_value = true;  // ADD
  return result;
}

After the fix, DATA_LENGTH and INDEX_LENGTH will correctly return NULL for partitions dropped during the I_S scan, preventing UINT64_MAX from leaking into monitoring queries.
[11 May 8:49] Yakir Gibraltar
repreduce file

Attachment: repro_partition_ullong.sh (text/x-sh), 7.12 KiB.

[11 May 8:50] Yakir Gibraltar
You can easy reproduce with the attached script:
➜  ~ bash /Users/yakir.g/git/percona-server/repro_partition_ullong.sh --docker mysql:9.7
==> Pulling mysql:9.7 ...
9.7: Pulling from library/mysql
Digest: sha256:f0ef1d92fa650fcfa5b85f1d82bb1a56a6dd579bf256b8f8f2a5a0b1b61c8b0b
Status: Image is up to date for mysql:9.7
docker.io/library/mysql:9.7
==> Starting container ...
3dd00ff0a06dc6817622ccb76a4f6e708db48f6c5468830ff7fe534d283b70d0
==> Waiting for mysqld ...... ready.
==> Server version:
+-----------+-------------------------+
| VERSION() | @@transaction_isolation |
+-----------+-------------------------+
| 9.7.0     | REPEATABLE-READ         |
+-----------+-------------------------+

==> Creating partitioned table ...
    Table ready with partitions p0/p1/p2/p3.

==> Running two-session REPEATABLE READ race ...
    Session A: lock RR snapshot (p1 visible) → SLEEP(5s) → query I_S.PARTITIONS
    Session B: DROP PARTITION p1 while Session A sleeps

    Session A sleeping — dropping p1 now ...
    p1 DROPPED (committed). Waiting for Session A to finish ...

==> Session A I_S.PARTITIONS output:
------------------------------------------------------------
is_query  PARTITION_NAME  DATA_LENGTH           INDEX_LENGTH
is_query  p0              16384                 0
is_query  p1              18446744073709551615  0
is_query  p2              16384                 0
is_query  p3              16384                 0
------------------------------------------------------------

Thank you, Yakir Gibraltar.
[11 May 8:57] Yakir Gibraltar
Add option to edit a comment..