Skip to content
Draft
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
100 changes: 98 additions & 2 deletions include/heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,17 @@ extern "C" {
#define HP_PTRS_IN_NOD 128

/*
Value of HP_SHARE::max_records for a table with no row limit. 0 is a
Value of HP_SHARE::max_rows for a table with no row limit. 0 is a
limit of zero rows, which is how a table that is never written to is
created; only heap_create()'s argument uses 0 for "no limit".
*/
#define NO_LIMIT_ROWS ULONG_MAX

/*
Value of HP_SHARE::max_records when the caller stated no expectation of
how many records the table will hold. That field sizes blocks and is
what heap_info() reports; it limits nothing.
*/
#define NO_LIMIT_RECORDS ULONG_MAX

/* struct used with heap_functions */
Expand All @@ -56,8 +63,13 @@ typedef struct st_heapinfo /* Struct from heap_info */
{
ulong records; /* Records in database */
ulong deleted; /* Deleted records in database */
ulong deleted_entries; /* Free list entries; see heap_info() */
ulong max_records;
ulonglong data_length;
/* Ceiling data_length counts toward; see heap_info() */
ulonglong max_data_length;
/* Free space, in the bytes data_length counts; see heap_info() */
ulonglong delete_length;
ulonglong index_length;
uint reclength; /* Length of one record */
int errkey;
Expand Down Expand Up @@ -130,6 +142,15 @@ typedef struct st_hp_keydef /* Key definition with open */
uint length; /* Length of key (automatic) */
uint8 algorithm; /* HASH / BTREE */
HA_KEYSEG *seg;
/*
Key segments addressing a stored record rather than the SQL record
buffer. Equal to 'seg' unless the table has promoted columns, in
which case compaction has moved every segment that follows the first
promoted column. Only the HASH index needs this: it recomputes keys
from stored records, while BTREE materializes its keys into the tree
from the SQL record.
*/
HA_KEYSEG *seg_stored;
HP_BLOCK block; /* Where keys are saved */
/*
Number of buckets used in hash table. Used only to provide
Expand All @@ -144,12 +165,46 @@ typedef struct st_hp_keydef /* Key definition with open */
uint (*get_key_length)(struct st_hp_keydef *keydef, const uchar *key);
} HP_KEYDEF;

/*
Description of one out-of-line column.

A native blob has the same {length}{pointer} shape in the SQL record
buffer and in the stored record, so 'offset' and 'store_offset' differ
only by the compaction that promoted columns cause.

A promoted column is a VARCHAR that the engine stores as a blob. Its
shape differs between the two layouts: {length}{data inline} in the SQL
record buffer, {length}{continuation chain pointer} in the stored
record. 'length' is the declared payload size the SQL buffer reserves,
which is what the inline form occupies and the stored form does not.
*/

typedef struct st_hp_blob_desc
{
uint offset; /* Byte offset of blob descriptor within record buffer */
uint packlength; /* 1, 2, 3, or 4: length prefix size */
uint store_offset; /* Byte offset of the descriptor in a stored record */
uint length; /* Promoted only: declared payload bytes in record[0] */
my_bool promoted; /* VARCHAR represented internally as a blob */
} HP_BLOB_DESC;

/*
A range of bytes that is identical in the SQL record buffer and in the
stored record, and can therefore be moved with a single memcpy.

The spans are the gaps between promoted columns' payloads. A table
with no promoted column has exactly one span covering the whole
record, which is why packing and unpacking such a table costs the same
single memcpy it did before promotion existed.
*/

typedef struct st_hp_copy_span
{
uint offset; /* Start in the SQL record buffer */
uint store_offset; /* Start in the stored record */
uint length; /* Bytes copied verbatim */
} HP_COPY_SPAN;

/*
Bits for HP_SHARE::state_changed, modeled on the state.changed bitmaps
of Maria and MyISAM (see storage/maria/maria_def.h). A table marked
Expand All @@ -168,26 +223,60 @@ typedef struct st_heap_share
HP_KEYDEF *keydef;
ulonglong data_length,index_length,max_table_size;
ulonglong auto_increment;
ulong min_records,max_records; /* Params to open */
/*
Expected record counts, from open. These size the HP_BLOCKs and
max_records is what heap_info() reports; neither refuses anything.
*/
ulong min_records,max_records;
/*
Row limit. Counts logical rows, which is what MAX_ROWS names: a row
whose blob data lives in continuation records still counts once.
NO_LIMIT_ROWS means unlimited.

Memory is bounded separately, by max_table_size. That is the only
ceiling that can be correct for a table whose rows occupy a
data-dependent number of records, because it reads the bytes the
table actually holds instead of predicting them from a row count.
*/
ulong max_rows;
ulong records; /* Logical (primary) record count */
ulong total_records; /* All active records (primary + blob continuation) */
ulong blength; /* records rounded up to 2^n */
ulong deleted; /* Deleted records in database */
/*
Entries on the free list, where a coalesced block of any length
counts once. This is what a scan pays for the free records rather
than 'deleted': heap_scan() steps over a whole block in one go, so a
row whose blob data freed a run of a thousand records costs it the
same single step as a row that freed one.
*/
ulong deleted_entries;
uint key_stat_version; /* version to indicate insert/delete */
uint key_version; /* Updated on key change */
uint file_version; /* Update on clear */
uint reclength; /* Length of one record */
/*
Length of a record as it is held in HP_BLOCK. Equal to reclength
unless columns were promoted, in which case each promoted column
contributes a chain pointer instead of its declared payload and the
stored record is correspondingly shorter. This, not reclength, is
what the block geometry is built from.
*/
uint stored_reclength;
uint visible; /* Offset to the flags byte (active/deleted/continuation) */
uint changed;
uint state_changed; /* Bitmap of HEAP_STATE_* flags */
uint keys,max_key_length;
uint currently_disabled_keys; /* saved value from "keys" when disabled */
uint open_count;
uint blob_count; /* Number of blob columns */
uint promoted_count; /* Blob columns that are VARCHARs */
uint copy_span_count; /* Verbatim ranges, >= 1 */
uint auto_key;
uint auto_key_type; /* real type of the auto key segment */
uchar *del_link; /* Link to next block with del. rec */
HP_BLOB_DESC *blob_descs; /* Array of blob column descriptors */
HP_COPY_SPAN *copy_spans; /* Ranges shared by both layouts */
char * name; /* Name of "memory-file" */
time_t create_time;
THR_LOCK lock;
Expand Down Expand Up @@ -239,12 +328,19 @@ typedef struct st_heap_create_info
HP_BLOB_DESC *blob_descs;
ulonglong max_table_size;
ulonglong auto_increment;
/*
Expected number of records, used only to size the HP_BLOCK
allocations. It is an estimate, not a limit: nothing is refused for
exceeding it.
*/
ulong max_records;
ulong max_rows; /* Row limit, 0 means "no limit" */
ulong min_records;
uint auto_key; /* keynr [1 - maxkey] for auto key */
uint auto_key_type;
uint keys;
uint reclength;
uint stored_reclength; /* 0 means "same as reclength" */
uint blob_count;
my_bool with_auto_increment;
my_bool internal_table;
Expand Down
2 changes: 2 additions & 0 deletions mysql-test/main/disabled.def
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ mysql_embedded : Bug#12561297 2011-05-14 Anitha Dependent on PB2 chang
file_contents : MDEV-6526 these files are not installed anymore
max_statement_time : cannot possibly work, depends on timing
partition_open_files_limit : open_files_limit check broken by MDEV-18360
information_schema : MDEV-41068 unstable tie order over a MEMORY table
log_slow_innodb : MDEV-41068 unstable tie order over a MEMORY table
12 changes: 9 additions & 3 deletions mysql-test/main/gconcat_distinct_walk_fail.result
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
CREATE TABLE t1 (a VARCHAR(100));
INSERT INTO t1 SELECT LPAD(seq MOD 200, 100, '0') FROM seq_1_to_600;
INSERT INTO t1 SELECT LPAD(seq MOD 1000, 100, '0') FROM seq_1_to_2000;
#
# It is how many distinct values there are, not how wide they are,
# that fills the duplicate filter. A column this wide is stored
# outside the record of a heap table, so what the filter holds for
# each value is a pointer to it rather than the value itself.
#
#
# Starve the duplicate filter so that it spills and the walk has to
# merge, then make the merging walk fail.
Expand Down Expand Up @@ -55,8 +61,8 @@ SET @@tmp_memory_table_size=DEFAULT;
#
SELECT LENGTH(GROUP_CONCAT(DISTINCT a)) AS gc_len FROM t1;
gc_len
20199
100999
SELECT JSON_LENGTH(JSON_ARRAYAGG(DISTINCT a)) AS ja_len FROM t1;
ja_len
200
1000
DROP TABLE t1;
9 changes: 8 additions & 1 deletion mysql-test/main/gconcat_distinct_walk_fail.test
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
--source include/have_sequence.inc

CREATE TABLE t1 (a VARCHAR(100));
INSERT INTO t1 SELECT LPAD(seq MOD 200, 100, '0') FROM seq_1_to_600;
INSERT INTO t1 SELECT LPAD(seq MOD 1000, 100, '0') FROM seq_1_to_2000;

--echo #
--echo # It is how many distinct values there are, not how wide they are,
--echo # that fills the duplicate filter. A column this wide is stored
--echo # outside the record of a heap table, so what the filter holds for
--echo # each value is a pointer to it rather than the value itself.
--echo #

--echo #
--echo # Starve the duplicate filter so that it spills and the walk has to
Expand Down
5 changes: 2 additions & 3 deletions mysql-test/suite/heap/blob.result
Original file line number Diff line number Diff line change
Expand Up @@ -431,12 +431,11 @@ set @@max_heap_table_size= 65536;
create table t1 (a int not null, b blob, primary key(a)) engine=memory;
insert into t1 values (1, repeat('x', 30000));
insert into t1 values (2, repeat('y', 30000));
ERROR HY000: The table 't1' is full
insert into t1 values (3, repeat('z', 30000));
ERROR HY000: The table 't1' is full
select count(*) as row_count from t1;
row_count
1
2
select a, length(b) from t1 where a=1;
a length(b)
1 30000
Expand All @@ -447,7 +446,7 @@ corrupted
0
select count(*) as scan_count from t1;
scan_count
1
2
set @@max_heap_table_size= @save_max;
drop table t1;
#
Expand Down
16 changes: 8 additions & 8 deletions mysql-test/suite/heap/blob_update_overflow.result
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name IN ('Created_tmp_disk_tables', 'Created_tmp_tables')
ORDER BY variable_name;
variable_name variable_value
CREATED_TMP_DISK_TABLES 2
CREATED_TMP_TABLES 4
CREATED_TMP_DISK_TABLES 1
CREATED_TMP_TABLES 3
DROP TABLE t1;
#
# Test 2: Verify result correctness after overflow
Expand All @@ -67,8 +67,8 @@ SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name IN ('Created_tmp_disk_tables', 'Created_tmp_tables')
ORDER BY variable_name;
variable_name variable_value
CREATED_TMP_DISK_TABLES 2
CREATED_TMP_TABLES 4
CREATED_TMP_DISK_TABLES 1
CREATED_TMP_TABLES 3
DROP TABLE t1;
#
# Test 3: Multiple blob aggregates (two MAX columns)
Expand All @@ -91,8 +91,8 @@ SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name IN ('Created_tmp_disk_tables', 'Created_tmp_tables')
ORDER BY variable_name;
variable_name variable_value
CREATED_TMP_DISK_TABLES 2
CREATED_TMP_TABLES 4
CREATED_TMP_DISK_TABLES 1
CREATED_TMP_TABLES 3
DROP TABLE t1;
#
# Test 4: MIN(TEXT) with monotonically shrinking minimum
Expand Down Expand Up @@ -125,8 +125,8 @@ SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name IN ('Created_tmp_disk_tables', 'Created_tmp_tables')
ORDER BY variable_name;
variable_name variable_value
CREATED_TMP_DISK_TABLES 2
CREATED_TMP_TABLES 4
CREATED_TMP_DISK_TABLES 1
CREATED_TMP_TABLES 3
DROP TABLE t1;
#
# Cleanup
Expand Down
32 changes: 29 additions & 3 deletions mysql-test/suite/heap/count_distinct_blob_convert.result
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,14 @@ set @small_heap=65536;
# it has to handle is gone. Rows added past the overflow do not move
# it: it happens once the table is full, whatever follows.
#
CREATE TABLE t1 (id INT PRIMARY KEY, v TEXT, w TEXT, s VARCHAR(64))
CREATE TABLE t1 (id INT PRIMARY KEY, v TEXT, w TEXT, s VARCHAR(64),
n VARCHAR(6))
ENGINE=MyISAM;
INSERT INTO t1
SELECT seq,
LPAD((seq+1) DIV 2, 6, 'x'),
LPAD((seq+1) DIV 2, 10, 'y'),
LPAD((seq+1) DIV 2, 6, 'z'),
LPAD((seq+1) DIV 2, 6, 'z')
FROM seq_1_to_8000;
SELECT COUNT(*) AS rows_stored FROM t1;
Expand Down Expand Up @@ -108,7 +110,7 @@ FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME = 'CREATED_TMP_DISK_TABLES';
CONVERTED
ON
# --- a blob and a non-blob argument ---
# --- a blob and a VARCHAR argument ---
FLUSH STATUS;
SELECT COUNT(DISTINCT v, s) AS distinct_values FROM t1;
distinct_values
Expand All @@ -118,7 +120,13 @@ FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME = 'CREATED_TMP_DISK_TABLES';
CONVERTED
ON
# --- control: no blob argument, deduplicated by the in-memory tree ---
# --- a VARCHAR wide enough to be stored out of line ---
#
# A VARCHAR this wide is kept outside the record of a heap table, so
# the record holds a pointer to it just as it does for a blob. The
# aggregate reaches the same conclusion from the same evidence and
# takes the same path, the writes rather than the tree.
#
FLUSH STATUS;
SELECT COUNT(DISTINCT s) AS distinct_values FROM t1;
distinct_values
Expand All @@ -127,6 +135,21 @@ SELECT IF(VARIABLE_VALUE > 0, 'ON', 'OFF') AS CONVERTED
FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME = 'CREATED_TMP_DISK_TABLES';
CONVERTED
ON
# --- control: every value inside the record, deduplicated by the
# --- in-memory tree
#
# n holds the same values as s and differs only in being declared
# narrow enough to stay inside the record.
#
FLUSH STATUS;
SELECT COUNT(DISTINCT n) AS distinct_values FROM t1;
distinct_values
4000
SELECT IF(VARIABLE_VALUE > 0, 'ON', 'OFF') AS CONVERTED
FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME = 'CREATED_TMP_DISK_TABLES';
CONVERTED
OFF
# --- grouped: one temporary table, reused for every group ---
#
Expand Down Expand Up @@ -170,6 +193,9 @@ distinct_values
SELECT COUNT(DISTINCT s) AS distinct_values FROM t1;
distinct_values
4000
SELECT COUNT(DISTINCT n) AS distinct_values FROM t1;
distinct_values
4000
SELECT id MOD 2 AS g, COUNT(DISTINCT v) AS distinct_values
FROM t1 GROUP BY g ORDER BY g;
g distinct_values
Expand Down
Loading
Loading