Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions mysql-test/suite/heap/min_rows_alloc.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#
# A ceiling large enough for the block cap to be live.
# (a INT, b INT, KEY(a)) rows are 16 bytes, hash entries 24.
#
SET SESSION max_heap_table_size= 256*1024*1024;
#
# No MIN_ROWS: sizing comes from the ceiling alone and is capped.
#
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
4194272 4194272
DROP TABLE t1;
#
# MIN_ROWS below the cap does not raise the block above the cap.
#
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=1000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
4194272 4194272
DROP TABLE t1;
#
# MIN_ROWS above the cap but within the ceiling still pre-sizes
# past the cap: it is a real row count expectation.
#
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=1000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
16777184 33554400
DROP TABLE t1;
#
# MIN_ROWS beyond the ceiling is unreachable and must clamp to it
# instead of reserving memory the table can never use. Both of
# these hold more rows than 256M can fit, so both must produce the
# same, ceiling-derived, sizing.
#
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=20000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
134217696 268435424
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=40000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
134217696 268435424
DROP TABLE t1;
#
# A smaller ceiling clamps harder, and MIN_ROWS at the .frm
# maximum stays bounded even at the shipped default ceiling.
#
SET SESSION max_heap_table_size= 64*1024*1024;
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=20000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
33554400 67108832
DROP TABLE t1;
SET SESSION max_heap_table_size= 16*1024*1024;
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=4294967295;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
8388576 16777184
DROP TABLE t1;
#
# The clamp is a ceiling clamp, not a MIN_ROWS ban: raising the
# ceiling lets the same MIN_ROWS pre-size further.
#
SET SESSION max_heap_table_size= 1024*1024*1024;
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=8000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DATA_LENGTH INDEX_LENGTH
134217696 268435424
DROP TABLE t1;
SET SESSION max_heap_table_size= DEFAULT;
95 changes: 95 additions & 0 deletions mysql-test/suite/heap/min_rows_alloc.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# MIN_ROWS pre-sizes a MEMORY table's blocks, but it must never pre-size
# them past max_heap_table_size: max_records is derived from that ceiling
# and is the most rows the table can ever hold, so a larger MIN_ROWS is
# unreachable. Block allocations are also capped (see
# main.tmp_table_heap_alloc) when they come from the ceiling rather than
# from a caller row-count expectation. The two interact, so walk all of
# the regimes: no MIN_ROWS, MIN_ROWS below the cap, MIN_ROWS above the
# cap but reachable, and MIN_ROWS beyond the ceiling.
#
# Sizes are exact byte counts and depend on pointer width.

--source include/have_64bit.inc

--echo #
--echo # A ceiling large enough for the block cap to be live.
--echo # (a INT, b INT, KEY(a)) rows are 16 bytes, hash entries 24.
--echo #
SET SESSION max_heap_table_size= 256*1024*1024;

--echo #
--echo # No MIN_ROWS: sizing comes from the ceiling alone and is capped.
--echo #
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

--echo #
--echo # MIN_ROWS below the cap does not raise the block above the cap.
--echo #
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=1000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

--echo #
--echo # MIN_ROWS above the cap but within the ceiling still pre-sizes
--echo # past the cap: it is a real row count expectation.
--echo #
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=1000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

--echo #
--echo # MIN_ROWS beyond the ceiling is unreachable and must clamp to it
--echo # instead of reserving memory the table can never use. Both of
--echo # these hold more rows than 256M can fit, so both must produce the
--echo # same, ceiling-derived, sizing.
--echo #
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=20000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=40000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

--echo #
--echo # A smaller ceiling clamps harder, and MIN_ROWS at the .frm
--echo # maximum stays bounded even at the shipped default ceiling.
--echo #
SET SESSION max_heap_table_size= 64*1024*1024;
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=20000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

SET SESSION max_heap_table_size= 16*1024*1024;
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=4294967295;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

--echo #
--echo # The clamp is a ceiling clamp, not a MIN_ROWS ban: raising the
--echo # ceiling lets the same MIN_ROWS pre-size further.
--echo #
SET SESSION max_heap_table_size= 1024*1024*1024;
CREATE TABLE t1 (a INT, b INT, KEY(a)) ENGINE=MEMORY MIN_ROWS=8000000;
INSERT INTO t1 VALUES (1,1),(2,2);
SELECT DATA_LENGTH, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

SET SESSION max_heap_table_size= DEFAULT;
37 changes: 29 additions & 8 deletions storage/heap/hp_create.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

static int keys_compare(void *heap_rb, const void *key1, const void *key2);
static void init_block(HP_BLOCK *block, size_t reclength, ulong min_records,
ulong max_records);
const ulong max_records);


/*
Expand Down Expand Up @@ -63,6 +63,15 @@ int heap_create(const char *name, HP_CREATE_INFO *create_info,
ulong min_records= create_info->min_records;
ulong max_records= create_info->max_records;
uint visible_offset;
/*
max_records is the table's row limit and 0 means "no limit"; it is
stored as such in share->max_records, where hp_alloc_from_tail()
relies on 0 disabling the check. Block sizing needs a concrete row
count instead, so derive that ceiling here and leave max_records
itself untouched.
*/
ulong block_max_records= (max_records ? max_records :
MY_MAX(min_records, 1000));
DBUG_ENTER("heap_create");

if (!create_info->internal_table)
Expand Down Expand Up @@ -243,7 +252,7 @@ int heap_create(const char *name, HP_CREATE_INFO *create_info,
share->blob_count= create_info->blob_count;
}
init_block(&share->block, hp_memory_needed_per_row(reclength),
min_records, max_records);
min_records, block_max_records);
/* Fix keys */
memcpy(share->keydef, keydef, (size_t) (sizeof(keydef[0]) * keys));
for (i= 0, keyinfo= share->keydef; i < keys; i++, keyinfo++)
Expand Down Expand Up @@ -272,7 +281,7 @@ int heap_create(const char *name, HP_CREATE_INFO *create_info,
else
{
init_block(&keyinfo->block, sizeof(HASH_INFO), min_records,
max_records);
block_max_records);
keyinfo->delete_key= hp_delete_key;
keyinfo->write_key= hp_write_key;
keyinfo->hash_buckets= 0;
Expand Down Expand Up @@ -375,7 +384,7 @@ ha_rows hp_rows_in_memory(size_t reclength, size_t index_size,


static void init_block(HP_BLOCK *block, size_t reclength, ulong min_records,
ulong max_records)
const ulong max_records)
{
ulong i,records_in_block,cap_records;
ulong recbuffer= (ulong) MY_ALIGN(reclength, sizeof(uchar*));
Expand All @@ -385,13 +394,22 @@ static void init_block(HP_BLOCK *block, size_t reclength, ulong min_records,
size_t alloc_size;

/*
If not min_records and max_records are given, optimize for 1000 rows
If no min_records is given, optimize for 1000 rows. max_records is
the caller's sizing ceiling and is never changed here.
*/
if (!min_records)
min_records= MY_MIN(1000, max_records / heap_allocation_parts);
if (!max_records)
max_records= MY_MAX(min_records, 1000);
min_records= MY_MIN(min_records, max_records);
/*
An explicit min_records may override the cap below, but only as far
as the ceiling reaches: max_records is the most rows the table's
memory ceiling (max_heap_table_size / tmp_memory_table_size) can
ever hold, so a larger MIN_ROWS is unreachable and pre-sizing for it
would reserve memory the table can never use. MY_MIN keeps 0 at 0,
so "no min_records requested" stays distinguishable from an explicit
one.
*/
requested_min_records= MY_MIN(requested_min_records, max_records);

/*
We don't want too few records_in_block as otherwise the overhead of
Expand All @@ -417,7 +435,10 @@ static void init_block(HP_BLOCK *block, size_t reclength, ulong min_records,
heap_max_allocation_block. Only an explicit min_records from the
caller (CREATE TABLE ... MIN_ROWS) is a real row count expectation
and may pre-size beyond the cap; the 1000-row default min_records
is a heuristic and must not override the cap.
is a heuristic and must not override the cap. That override is
bounded by max_records (clamped above), so an unreachable MIN_ROWS
degrades to plain ceiling-derived sizing instead of reserving a
block the ceiling can never fill.
*/
cap_records= (heap_max_allocation_block - extra) / recbuffer;
if (records_in_block > cap_records)
Expand Down
Loading
Loading