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
94 changes: 94 additions & 0 deletions mysql-test/suite/heap/blob_window_overflow.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#
# Window function computation must spill HEAP tmp table to Aria
# when storing computed values fills the table
#
# Window function results are written back into the sorted tmp
# table with ha_update_row(). A blob result column (an expression
# wider than 512 characters is promoted to TEXT in the tmp table)
# makes each such update allocate a blob continuation record; once
# this crosses max_heap_table_size the update fails with "table is
# full" and the server must transparently convert the tmp table to
# Aria and re-run the window computation step, just as the write
# path already does.
#
# latin1 is used so that actual data size ~= declared column width:
# the memory arithmetic then holds whether or not the source VARCHAR
# column itself is stored as a blob in the HEAP tmp table.
#
SET @save_max_heap= @@max_heap_table_size;
SET @save_tmp= @@tmp_table_size;
SET max_heap_table_size = 4*1024*1024;
SET tmp_table_size = 4*1024*1024;
#
# Test 1: single window over all rows
#
# 800 rows of ~3KB fill the 4MB HEAP tmp table to ~2.4MB; storing
# the ~3KB computed value adds a blob continuation chain per row
# and overflows mid-computation.
#
CREATE TABLE t1 (a VARCHAR(3000)) CHARACTER SET latin1;
INSERT INTO t1 SELECT CONCAT(LPAD(seq, 8, '0'), REPEAT('x', 2900))
FROM seq_1_to_800;
FLUSH STATUS;
# Should overflow during computation and convert HEAP->Aria
SELECT COUNT(*) AS cnt, COUNT(DISTINCT p) AS distinct_p,
MIN(LENGTH(p)) AS min_len, LEFT(MAX(p), 12) AS max_prefix
FROM (SELECT PERCENTILE_DISC(1) WITHIN GROUP (ORDER BY a) OVER () AS p
FROM t1) dt;
cnt distinct_p min_len max_prefix
800 1 2908 00000800xxxx
Warnings:
Warning 4202 1600 values were longer than max_sort_length. Sorting used only the first 1024 bytes
# Verify HEAP->Aria conversion happened
SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name = 'Created_tmp_disk_tables';
variable_name variable_value
CREATED_TMP_DISK_TABLES 1
DROP TABLE t1;
#
# Test 2: partitioned window
#
# Same overflow, but the re-run must also correctly restart
# partition tracking (values recomputed per partition).
#
CREATE TABLE t2 (g INT, a VARCHAR(3000)) CHARACTER SET latin1;
INSERT INTO t2 SELECT seq % 4, CONCAT(LPAD(seq, 8, '0'), REPEAT('y', 2900))
FROM seq_1_to_800;
FLUSH STATUS;
SELECT COUNT(*) AS cnt, COUNT(DISTINCT p) AS partitions_seen,
MIN(LENGTH(p)) AS min_len, MAX(LENGTH(p)) AS max_len
FROM (SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY a)
OVER (PARTITION BY g) AS p
FROM t2) dt;
cnt partitions_seen min_len max_len
800 4 2908 2908
Warnings:
Warning 4202 1600 values were longer than max_sort_length. Sorting used only the first 1024 bytes
SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name = 'Created_tmp_disk_tables';
variable_name variable_value
CREATED_TMP_DISK_TABLES 1
DROP TABLE t2;
#
# Test 3: side-effecting window expression must not be re-run
#
# When a compound expression containing a window function has side
# effects (here: a user variable assignment), re-running the
# computation after the conversion would apply the side effects a
# second time for rows already processed, so the statement must
# fail with "table is full" instead.
#
CREATE TABLE t3 (a VARCHAR(3000)) CHARACTER SET latin1;
INSERT INTO t3 SELECT CONCAT(LPAD(seq, 8, '0'), REPEAT('z', 2900))
FROM seq_1_to_800;
SET @c= 0;
SELECT CONCAT(@c:= @c+1, ':',
PERCENTILE_DISC(1) WITHIN GROUP (ORDER BY a) OVER ()) AS e
FROM t3;
ERROR HY000: The table 'TMP_TABLE' is full
DROP TABLE t3;
#
# Cleanup
#
SET max_heap_table_size = @save_max_heap;
SET tmp_table_size = @save_tmp;
124 changes: 124 additions & 0 deletions mysql-test/suite/heap/blob_window_overflow.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
--source include/have_sequence.inc

--echo #
--echo # Window function computation must spill HEAP tmp table to Aria
--echo # when storing computed values fills the table
--echo #
--echo # Window function results are written back into the sorted tmp
--echo # table with ha_update_row(). A blob result column (an expression
--echo # wider than 512 characters is promoted to TEXT in the tmp table)
--echo # makes each such update allocate a blob continuation record; once
--echo # this crosses max_heap_table_size the update fails with "table is
--echo # full" and the server must transparently convert the tmp table to
--echo # Aria and re-run the window computation step, just as the write
--echo # path already does.
--echo #
--echo # latin1 is used so that actual data size ~= declared column width:
--echo # the memory arithmetic then holds whether or not the source VARCHAR
--echo # column itself is stored as a blob in the HEAP tmp table.
--echo #

SET @save_max_heap= @@max_heap_table_size;
SET @save_tmp= @@tmp_table_size;

SET max_heap_table_size = 4*1024*1024;
SET tmp_table_size = 4*1024*1024;

--echo #
--echo # Test 1: single window over all rows
--echo #
--echo # 800 rows of ~3KB fill the 4MB HEAP tmp table to ~2.4MB; storing
--echo # the ~3KB computed value adds a blob continuation chain per row
--echo # and overflows mid-computation.
--echo #

CREATE TABLE t1 (a VARCHAR(3000)) CHARACTER SET latin1;
INSERT INTO t1 SELECT CONCAT(LPAD(seq, 8, '0'), REPEAT('x', 2900))
FROM seq_1_to_800;

--disable_ps_protocol
--disable_ps2_protocol
--disable_view_protocol
--disable_cursor_protocol
FLUSH STATUS;

--echo # Should overflow during computation and convert HEAP->Aria
SELECT COUNT(*) AS cnt, COUNT(DISTINCT p) AS distinct_p,
MIN(LENGTH(p)) AS min_len, LEFT(MAX(p), 12) AS max_prefix
FROM (SELECT PERCENTILE_DISC(1) WITHIN GROUP (ORDER BY a) OVER () AS p
FROM t1) dt;

--echo # Verify HEAP->Aria conversion happened
--disable_warnings
SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name = 'Created_tmp_disk_tables';
--enable_warnings
--enable_cursor_protocol
--enable_view_protocol
--enable_ps2_protocol
--enable_ps_protocol

DROP TABLE t1;

--echo #
--echo # Test 2: partitioned window
--echo #
--echo # Same overflow, but the re-run must also correctly restart
--echo # partition tracking (values recomputed per partition).
--echo #

CREATE TABLE t2 (g INT, a VARCHAR(3000)) CHARACTER SET latin1;
INSERT INTO t2 SELECT seq % 4, CONCAT(LPAD(seq, 8, '0'), REPEAT('y', 2900))
FROM seq_1_to_800;

--disable_ps_protocol
--disable_ps2_protocol
--disable_view_protocol
--disable_cursor_protocol
FLUSH STATUS;

SELECT COUNT(*) AS cnt, COUNT(DISTINCT p) AS partitions_seen,
MIN(LENGTH(p)) AS min_len, MAX(LENGTH(p)) AS max_len
FROM (SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY a)
OVER (PARTITION BY g) AS p
FROM t2) dt;

--disable_warnings
SELECT variable_name, variable_value FROM information_schema.session_status
WHERE variable_name = 'Created_tmp_disk_tables';
--enable_warnings
--enable_cursor_protocol
--enable_view_protocol
--enable_ps2_protocol
--enable_ps_protocol

DROP TABLE t2;

--echo #
--echo # Test 3: side-effecting window expression must not be re-run
--echo #
--echo # When a compound expression containing a window function has side
--echo # effects (here: a user variable assignment), re-running the
--echo # computation after the conversion would apply the side effects a
--echo # second time for rows already processed, so the statement must
--echo # fail with "table is full" instead.
--echo #

CREATE TABLE t3 (a VARCHAR(3000)) CHARACTER SET latin1;
INSERT INTO t3 SELECT CONCAT(LPAD(seq, 8, '0'), REPEAT('z', 2900))
FROM seq_1_to_800;

SET @c= 0;
--replace_regex /table '[^']+' is full/table 'TMP_TABLE' is full/
--error ER_RECORD_FILE_FULL
SELECT CONCAT(@c:= @c+1, ':',
PERCENTILE_DISC(1) WITHIN GROUP (ORDER BY a) OVER ()) AS e
FROM t3;

DROP TABLE t3;

--echo #
--echo # Cleanup
--echo #
SET max_heap_table_size = @save_max_heap;
SET tmp_table_size = @save_tmp;
35 changes: 22 additions & 13 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24022,6 +24022,11 @@ bool create_internal_tmp_table(TABLE *table, KEY *org_keyinfo,
If a HEAP table gets full, create a internal table in MyISAM or Maria
and copy all rows to this

copy_pending_row is FALSE when the overflow happened while updating an
existing row: table->record[0] then holds the failed update's new image
of a row that is already in the table, and must not be appended as an
extra row.

In case of error, my_error() or handler::print_error() will be called.
Note that in case of error, table->file->ha_rnd_end() may have been called!
*/
Expand All @@ -24033,7 +24038,8 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
TMP_ENGINE_COLUMNDEF **recinfo,
int error,
bool ignore_last_dupp_key_error,
bool *is_duplicate)
bool *is_duplicate,
bool copy_pending_row)
{
TABLE new_table;
TABLE_SHARE share;
Expand Down Expand Up @@ -24112,19 +24118,22 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
}
if (!new_table.no_rows && (write_err= new_table.file->ha_end_bulk_insert()))
goto err;
/* copy row that filled HEAP table */
if (unlikely((write_err=new_table.file->ha_write_tmp_row(table->record[0]))))
{
if (new_table.file->is_fatal_error(write_err, HA_CHECK_DUP) ||
!ignore_last_dupp_key_error)
goto err;
if (is_duplicate)
*is_duplicate= TRUE;
}
else
if (copy_pending_row)
{
if (is_duplicate)
*is_duplicate= FALSE;
/* copy row that filled HEAP table */
if (unlikely((write_err=new_table.file->ha_write_tmp_row(table->record[0]))))
{
if (new_table.file->is_fatal_error(write_err, HA_CHECK_DUP) ||
!ignore_last_dupp_key_error)
goto err;
if (is_duplicate)
*is_duplicate= TRUE;
}
else
{
if (is_duplicate)
*is_duplicate= FALSE;
}
}

/* remove heap table and change to use myisam table */
Expand Down
3 changes: 2 additions & 1 deletion sql/sql_select.h
Original file line number Diff line number Diff line change
Expand Up @@ -2682,7 +2682,8 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
TMP_ENGINE_COLUMNDEF *start_recinfo,
TMP_ENGINE_COLUMNDEF **recinfo,
int error, bool ignore_last_dupp_key_error,
bool *is_duplicate);
bool *is_duplicate,
bool copy_pending_row= true);
bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
TMP_ENGINE_COLUMNDEF *start_recinfo,
TMP_ENGINE_COLUMNDEF **recinfo,
Expand Down
Loading
Loading