Skip to content

Commit 4c57188

Browse files
author
Marko Mäkelä
committed
Bug#12547647 UPDATE LOGGING COULD EXCEED LOG PAGE SIZE
This fix was accidentally pushed to mysql-5.1 after the 5.1.59 clone-off in bzr revision id [email protected] with the fix of Bug#12704861 Corruption after a crash during BLOB update but not merged to mysql-5.5 and upwards. In the Barracuda formats, the clustered index record no longer contains a prefix of off-page columns. Because of this, the undo log must contain these prefixes, so that purge and multi-versioning will continue to work. However, this also means that an undo log record can become too big to fit in an undo log page. (It is a limitation of the undo log that undo records cannot span across multiple pages.) In case the checks for undo log size fail when CREATE TABLE or CREATE INDEX is executed, we need a fallback that blocks a modification operation when the undo log record would exceed the maximum size. trx_undo_free_last_page_func(): Renamed from trx_undo_free_page_in_rollback(). Define the trx_t parameter only in debug builds. trx_undo_free_last_page(): Wrapper for trx_undo_free_last_page_func(). Pass the trx_t parameter only in debug builds. trx_undo_truncate_end_func(): Renamed from trx_undo_truncate_end(). Define the trx_t parameter only in debug builds. Rewrite a for(;;) loop as a while loop for clarity. trx_undo_truncate_end(): Wrapper for from trx_undo_truncate_end_func(). Pass the trx_t parameter only in debug builds. trx_undo_erase_page_end(): Return TRUE if the page was non-empty to begin with. Refuse to erase empty pages. trx_undo_report_row_operation(): If the page for which the undo log was too big was empty, free the undo page and return DB_TOO_BIG_RECORD. rb:749 approved by Inaam Rana
1 parent 739b8be commit 4c57188

13 files changed

Lines changed: 158 additions & 65 deletions

File tree

include/my_base.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,8 @@ enum ha_base_keytype {
448448
#define HA_ERR_TOO_MANY_CONCURRENT_TRXS 177 /*Too many active concurrent transactions */
449449
#define HA_ERR_INDEX_COL_TOO_LONG 178 /* Index column length exceeds limit */
450450
#define HA_ERR_INDEX_CORRUPT 179 /* Index corrupted */
451-
#define HA_ERR_LAST 179 /* Copy of last error nr */
451+
#define HA_ERR_UNDO_REC_TOO_BIG 180 /* Undo log record too big */
452+
#define HA_ERR_LAST 180 /* Copy of last error nr */
452453

453454
/* Number of different errors */
454455
#define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1)

mysql-test/suite/innodb/r/innodb-index.result

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,15 @@ INSERT INTO t1 VALUES(9,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r,@r);
975975
UPDATE t1 SET a=1000;
976976
DELETE FROM t1;
977977
DROP TABLE t1;
978+
CREATE TABLE bug12547647(
979+
a INT NOT NULL, b BLOB NOT NULL, c TEXT,
980+
PRIMARY KEY (b(10), a), INDEX (c(767)), INDEX(b(767))
981+
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
982+
INSERT INTO bug12547647 VALUES (5,repeat('khdfo5AlOq',1900),repeat('g',7751));
983+
COMMIT;
984+
UPDATE bug12547647 SET c = REPEAT('b',16928);
985+
ERROR HY000: Undo log record is too big.
986+
DROP TABLE bug12547647;
978987
set global innodb_file_per_table=0;
979988
set global innodb_file_format=Antelope;
980989
set global innodb_file_format_max=Antelope;

mysql-test/suite/innodb/t/innodb-index.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,19 @@ DELETE FROM t1;
477477
-- sleep 10
478478
DROP TABLE t1;
479479

480+
# Bug#12547647 UPDATE LOGGING COULD EXCEED LOG PAGE SIZE
481+
CREATE TABLE bug12547647(
482+
a INT NOT NULL, b BLOB NOT NULL, c TEXT,
483+
PRIMARY KEY (b(10), a), INDEX (c(767)), INDEX(b(767))
484+
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
485+
486+
INSERT INTO bug12547647 VALUES (5,repeat('khdfo5AlOq',1900),repeat('g',7751));
487+
COMMIT;
488+
# The following used to cause infinite undo log allocation.
489+
--error ER_UNDO_RECORD_TOO_BIG
490+
UPDATE bug12547647 SET c = REPEAT('b',16928);
491+
DROP TABLE bug12547647;
492+
480493
eval set global innodb_file_per_table=$per_table;
481494
eval set global innodb_file_format=$format;
482495
eval set global innodb_file_format_max=$format;

mysys/my_handler_errors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ static const char *handler_error_messages[]=
8282
"Read page with wrong checksum",
8383
"Too many active concurrent transactions",
8484
"Index column length exceeds limit",
85-
"Index corrupted"
85+
"Index corrupted",
86+
"Undo record too big"
8687
};
8788

8889
extern void my_handler_error_register(void);

sql/handler.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2869,6 +2869,9 @@ void handler::print_error(int error, myf errflag)
28692869
case HA_ERR_INDEX_CORRUPT:
28702870
textno= ER_INDEX_CORRUPT;
28712871
break;
2872+
case HA_ERR_UNDO_REC_TOO_BIG:
2873+
textno= ER_UNDO_RECORD_TOO_BIG;
2874+
break;
28722875
default:
28732876
{
28742877
/* The error was "unknown" to this function.

sql/share/errmsg-utf8.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6417,3 +6417,6 @@ ER_ERROR_IN_UNKNOWN_TRIGGER_BODY
64176417

64186418
ER_INDEX_CORRUPT
64196419
eng "Index %s is corrupted"
6420+
6421+
ER_UNDO_RECORD_TOO_BIG
6422+
eng "Undo log record is too big."

storage/innobase/handler/ha_innodb.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,8 @@ convert_error_code_to_mysql(
10451045
return(HA_ERR_UNSUPPORTED);
10461046
case DB_INDEX_CORRUPT:
10471047
return(HA_ERR_INDEX_CORRUPT);
1048+
case DB_UNDO_RECORD_TOO_BIG:
1049+
return(HA_ERR_UNDO_REC_TOO_BIG);
10481050
}
10491051
}
10501052

storage/innobase/include/db0err.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*****************************************************************************
22
3-
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved.
3+
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
44
55
This program is free software; you can redistribute it and/or modify it under
66
the terms of the GNU General Public License as published by the Free Software
@@ -111,6 +111,7 @@ enum db_err {
111111
DB_TOO_BIG_INDEX_COL, /* index column size exceeds maximum
112112
limit */
113113
DB_INDEX_CORRUPT, /* we have corrupted index */
114+
DB_UNDO_RECORD_TOO_BIG, /* the undo log record is too big */
114115

115116
/* The following are partial failure codes */
116117
DB_FAIL = 1000,

storage/innobase/include/trx0undo.h

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*****************************************************************************
22
3-
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved.
3+
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
44
55
This program is free software; you can redistribute it and/or modify it under
66
the terms of the GNU General Public License as published by the Free Software
@@ -204,17 +204,51 @@ trx_undo_add_page(
204204
mtr_t* mtr); /*!< in: mtr which does not have a latch to any
205205
undo log page; the caller must have reserved
206206
the rollback segment mutex */
207+
/********************************************************************//**
208+
Frees the last undo log page.
209+
The caller must hold the rollback segment mutex. */
210+
UNIV_INTERN
211+
void
212+
trx_undo_free_last_page_func(
213+
/*==========================*/
214+
#ifdef UNIV_DEBUG
215+
const trx_t* trx, /*!< in: transaction */
216+
#endif /* UNIV_DEBUG */
217+
trx_undo_t* undo, /*!< in/out: undo log memory copy */
218+
mtr_t* mtr) /*!< in/out: mini-transaction which does not
219+
have a latch to any undo log page or which
220+
has allocated the undo log page */
221+
__attribute__((nonnull));
222+
#ifdef UNIV_DEBUG
223+
# define trx_undo_free_last_page(trx,undo,mtr) \
224+
trx_undo_free_last_page_func(trx,undo,mtr)
225+
#else /* UNIV_DEBUG */
226+
# define trx_undo_free_last_page(trx,undo,mtr) \
227+
trx_undo_free_last_page_func(undo,mtr)
228+
#endif /* UNIV_DEBUG */
229+
207230
/***********************************************************************//**
208231
Truncates an undo log from the end. This function is used during a rollback
209232
to free space from an undo log. */
210233
UNIV_INTERN
211234
void
212-
trx_undo_truncate_end(
213-
/*==================*/
214-
trx_t* trx, /*!< in: transaction whose undo log it is */
215-
trx_undo_t* undo, /*!< in: undo log */
216-
undo_no_t limit); /*!< in: all undo records with undo number
235+
trx_undo_truncate_end_func(
236+
/*=======================*/
237+
#ifdef UNIV_DEBUG
238+
const trx_t* trx, /*!< in: transaction whose undo log it is */
239+
#endif /* UNIV_DEBUG */
240+
trx_undo_t* undo, /*!< in/out: undo log */
241+
undo_no_t limit) /*!< in: all undo records with undo number
217242
>= this value should be truncated */
243+
__attribute__((nonnull));
244+
#ifdef UNIV_DEBUG
245+
# define trx_undo_truncate_end(trx,undo,limit) \
246+
trx_undo_truncate_end_func(trx,undo,limit)
247+
#else /* UNIV_DEBUG */
248+
# define trx_undo_truncate_end(trx,undo,limit) \
249+
trx_undo_truncate_end_func(undo,limit)
250+
#endif /* UNIV_DEBUG */
251+
218252
/***********************************************************************//**
219253
Truncates an undo log from the start. This function is used during a purge
220254
operation. */

storage/innobase/row/row0mysql.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ row_mysql_handle_errors(
576576
case DB_DUPLICATE_KEY:
577577
case DB_FOREIGN_DUPLICATE_KEY:
578578
case DB_TOO_BIG_RECORD:
579+
case DB_UNDO_RECORD_TOO_BIG:
579580
case DB_ROW_IS_REFERENCED:
580581
case DB_NO_REFERENCED_ROW:
581582
case DB_CANNOT_ADD_CONSTRAINT:

0 commit comments

Comments
 (0)