mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
CSV: Remove the partial row that a failed INSERT leaves
Cut the data file of a CSV table back to its length before the write when an INSERT fails to write its row. When the write of row 2 stops after its first byte, INSERT INTO t1 VALUES (2) fails, but that byte stays at the end of the data file. The next INSERT INTO t1 VALUES (3) appends its row after it. Once the table is opened again, SELECT * FROM t1 returns the rows 1 and 23, and CHECK TABLE reports the table as OK. If the cut fails too, the table is marked as crashed. A new debug injection point writes the first byte of a row and then fails as if the disk were full, and the new test uses it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
29
mysql-test/suite/csv/insert_write_error.result
Normal file
29
mysql-test/suite/csv/insert_write_error.result
Normal file
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# A failed write of a row during INSERT leaves no part of the row in
|
||||
# the data file
|
||||
#
|
||||
CREATE TABLE t1 (v INT NOT NULL) ENGINE=CSV;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
SET @save_debug_dbug= @@debug_dbug;
|
||||
SET debug_dbug='+d,tina_write_row_short_write';
|
||||
INSERT INTO t1 VALUES (2);
|
||||
ERROR HY000: Error writing file 'FILE' (Errcode: 28)
|
||||
SET debug_dbug= @save_debug_dbug;
|
||||
INSERT INTO t1 VALUES (3);
|
||||
SELECT * FROM t1;
|
||||
v
|
||||
1
|
||||
3
|
||||
CHECK TABLE t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
# The next open reads the length of the data file
|
||||
FLUSH TABLES t1;
|
||||
SELECT * FROM t1;
|
||||
v
|
||||
1
|
||||
3
|
||||
CHECK TABLE t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
DROP TABLE t1;
|
||||
24
mysql-test/suite/csv/insert_write_error.test
Normal file
24
mysql-test/suite/csv/insert_write_error.test
Normal file
@@ -0,0 +1,24 @@
|
||||
--source include/have_debug.inc
|
||||
|
||||
--echo #
|
||||
--echo # A failed write of a row during INSERT leaves no part of the row in
|
||||
--echo # the data file
|
||||
--echo #
|
||||
CREATE TABLE t1 (v INT NOT NULL) ENGINE=CSV;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
SET @save_debug_dbug= @@debug_dbug;
|
||||
SET debug_dbug='+d,tina_write_row_short_write';
|
||||
--replace_regex /'[^']*'/'FILE'/ /Errcode: .*/Errcode: 28)/
|
||||
--error 3
|
||||
INSERT INTO t1 VALUES (2);
|
||||
SET debug_dbug= @save_debug_dbug;
|
||||
INSERT INTO t1 VALUES (3);
|
||||
--sorted_result
|
||||
SELECT * FROM t1;
|
||||
CHECK TABLE t1;
|
||||
--echo # The next open reads the length of the data file
|
||||
FLUSH TABLES t1;
|
||||
--sorted_result
|
||||
SELECT * FROM t1;
|
||||
CHECK TABLE t1;
|
||||
DROP TABLE t1;
|
||||
@@ -1069,9 +1069,30 @@ static size_t short_write(File file, const uchar *buf, size_t count)
|
||||
|
||||
|
||||
/*
|
||||
This is an INSERT. At the moment this handler just seeks to the end
|
||||
of the file and appends the data. In an error case it really should
|
||||
just truncate to the original position (this is not done yet).
|
||||
Append a row of size bytes from buf to the data file. A failed write
|
||||
can leave part of the row at the end of the file, where the next row
|
||||
would be appended to it, so cut the file back to its length before the
|
||||
write. If that fails too, mark the table crashed.
|
||||
*/
|
||||
static int append_row(TINA_SHARE *share, const uchar *buf, size_t size)
|
||||
{
|
||||
File file= share->tina_write_filedes;
|
||||
my_off_t end= mysql_file_seek(file, 0, MY_SEEK_END, MYF(MY_WME));
|
||||
|
||||
if (end == MY_FILEPOS_ERROR)
|
||||
return -1;
|
||||
if (!(DBUG_IF("tina_write_row_short_write") ?
|
||||
short_write(file, buf, 1) :
|
||||
mysql_file_write(file, buf, size, MYF(MY_WME | MY_NABP))))
|
||||
return 0;
|
||||
if (mysql_file_chsize(file, end, 0, MYF(0)))
|
||||
share->crashed= TRUE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This is an INSERT. The row is appended to the end of the data file.
|
||||
*/
|
||||
int ha_tina::write_row(const uchar * buf)
|
||||
{
|
||||
@@ -1087,9 +1108,7 @@ int ha_tina::write_row(const uchar * buf)
|
||||
if (init_tina_writer())
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* use pwrite, as concurrent reader could have changed the position */
|
||||
if (mysql_file_write(share->tina_write_filedes, (uchar*)buffer.ptr(), size,
|
||||
MYF(MY_WME | MY_NABP)))
|
||||
if (append_row(share, (uchar*) buffer.ptr(), size))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* update local copy of the max position to see our own changes */
|
||||
|
||||
Reference in New Issue
Block a user