summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--innobase/include/row0mysql.h8
-rw-r--r--innobase/include/row0sel.h3
-rw-r--r--innobase/include/ut0mem.h10
-rw-r--r--innobase/row/row0mysql.c13
-rw-r--r--innobase/row/row0sel.c37
-rw-r--r--innobase/ut/ut0mem.c37
-rw-r--r--sql/ha_innodb.cc34
7 files changed, 124 insertions, 18 deletions
diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h
index 25d2ab77007..972fabc74cf 100644
--- a/innobase/include/row0mysql.h
+++ b/innobase/include/row0mysql.h
@@ -52,6 +52,14 @@ row_mysql_read_var_ref_noninline(
ulint* len, /* out: variable-length field length */
byte* field); /* in: field */
/***********************************************************************
+Frees the blob heap in prebuilt when no longer needed. */
+
+void
+row_mysql_prebuilt_free_blob_heap(
+/*==============================*/
+ row_prebuilt_t* prebuilt); /* in: prebuilt struct of a
+ ha_innobase:: table handle */
+/***********************************************************************
Stores a reference to a BLOB in the MySQL format. */
void
diff --git a/innobase/include/row0sel.h b/innobase/include/row0sel.h
index aa2da6fe5f6..cfc30852b87 100644
--- a/innobase/include/row0sel.h
+++ b/innobase/include/row0sel.h
@@ -115,7 +115,8 @@ row_search_for_mysql(
/*=================*/
/* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND,
- DB_END_OF_INDEX, or DB_DEADLOCK */
+ DB_END_OF_INDEX, DB_DEADLOCK,
+ or DB_TOO_BIG_RECORD */
byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */
ulint mode, /* in: search mode PAGE_CUR_L, ... */
diff --git a/innobase/include/ut0mem.h b/innobase/include/ut0mem.h
index 09e0d800685..d3d04d58596 100644
--- a/innobase/include/ut0mem.h
+++ b/innobase/include/ut0mem.h
@@ -50,6 +50,16 @@ ut_malloc(
/* out, own: allocated memory */
ulint n); /* in: number of bytes to allocate */
/**************************************************************************
+Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs
+out. It cannot be used if we want to return an error message. Prints to
+stderr a message if fails. */
+
+ibool
+ut_test_malloc(
+/*===========*/
+ /* out: TRUE if succeeded */
+ ulint n); /* in: try to allocate this many bytes */
+/**************************************************************************
Frees a memory bloock allocated with ut_malloc. */
void
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index ba56b3071cd..7cef63d1337 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -59,6 +59,19 @@ row_mysql_read_var_ref_noninline(
}
/***********************************************************************
+Frees the blob heap in prebuilt when no longer needed. */
+
+void
+row_mysql_prebuilt_free_blob_heap(
+/*==============================*/
+ row_prebuilt_t* prebuilt) /* in: prebuilt struct of a
+ ha_innobase:: table handle */
+{
+ mem_heap_free(prebuilt->blob_heap);
+ prebuilt->blob_heap = NULL;
+}
+
+/***********************************************************************
Stores a reference to a BLOB in the MySQL format. */
void
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index ea5b3020c08..34f951b0c8a 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2039,9 +2039,12 @@ Note that the template in prebuilt may advise us to copy only a few
columns to mysql_rec, other columns are left blank. All columns may not
be needed in the query. */
static
-void
+ibool
row_sel_store_mysql_rec(
/*====================*/
+ /* out: TRUE if success, FALSE
+ if could not allocate memory for a
+ BLOB */
byte* mysql_rec, /* out: row in the MySQL format */
row_prebuilt_t* prebuilt, /* in: prebuilt struct */
rec_t* rec) /* in: Innobase record in the index
@@ -2092,7 +2095,19 @@ row_sel_store_mysql_rec(
if (templ->type == DATA_BLOB) {
ut_a(prebuilt->templ_contains_blob);
-
+
+ /* A heuristic test that we can allocate
+ the memory for a big BLOB. We have a safety
+ margin of 1000000 bytes. Since the test
+ takes some CPU time, we do not use for small
+ BLOBs. */
+
+ if (len > 2000000
+ && !ut_test_malloc(len + 1000000)) {
+
+ return(FALSE);
+ }
+
/* Copy the BLOB data to the BLOB
heap of prebuilt */
@@ -2142,6 +2157,8 @@ row_sel_store_mysql_rec(
}
}
}
+
+ return(TRUE);
}
/*************************************************************************
@@ -2526,7 +2543,8 @@ row_search_for_mysql(
/*=================*/
/* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND,
- DB_END_OF_INDEX, or DB_DEADLOCK */
+ DB_END_OF_INDEX, DB_DEADLOCK,
+ or DB_TOO_BIG_RECORD */
byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */
ulint mode, /* in: search mode PAGE_CUR_L, ... */
@@ -2758,7 +2776,12 @@ row_search_for_mysql(
#ifdef UNIV_SEARCH_DEBUG
ut_a(0 == cmp_dtuple_rec(search_tuple, rec));
#endif
- row_sel_store_mysql_rec(buf, prebuilt, rec);
+ if (!row_sel_store_mysql_rec(buf, prebuilt,
+ rec)) {
+ err = DB_TOO_BIG_RECORD;
+
+ goto lock_wait_or_error;
+ }
mtr_commit(&mtr);
@@ -3200,7 +3223,11 @@ rec_loop:
rec_get_size(rec));
mach_write_to_4(buf, rec_get_extra_size(rec) + 4);
} else {
- row_sel_store_mysql_rec(buf, prebuilt, rec);
+ if (!row_sel_store_mysql_rec(buf, prebuilt, rec)) {
+ err = DB_TOO_BIG_RECORD;
+
+ goto lock_wait_or_error;
+ }
}
if (prebuilt->clust_index_was_generated) {
diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c
index 03f15031fdf..a5991d5683d 100644
--- a/innobase/ut/ut0mem.c
+++ b/innobase/ut/ut0mem.c
@@ -77,8 +77,9 @@ ut_malloc_low(
ret = malloc(n + sizeof(ut_mem_block_t));
if (ret == NULL) {
+ ut_print_timestamp(stderr);
fprintf(stderr,
- "InnoDB: Fatal error: cannot allocate %lu bytes of\n"
+ " InnoDB: Fatal error: cannot allocate %lu bytes of\n"
"InnoDB: memory with malloc! Total allocated memory\n"
"InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n"
"InnoDB: Cannot continue operation!\n"
@@ -135,6 +136,40 @@ ut_malloc(
}
/**************************************************************************
+Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs
+out. It cannot be used if we want to return an error message. Prints to
+stderr a message if fails. */
+
+ibool
+ut_test_malloc(
+/*===========*/
+ /* out: TRUE if succeeded */
+ ulint n) /* in: try to allocate this many bytes */
+{
+ void* ret;
+
+ ret = malloc(n);
+
+ if (ret == NULL) {
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: Error: cannot allocate %lu bytes of memory for\n"
+ "InnoDB: a BLOB with malloc! Total allocated memory\n"
+ "InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n"
+ "InnoDB: Check if you should increase the swap file or\n"
+ "InnoDB: ulimits of your operating system.\n"
+ "InnoDB: On FreeBSD check you have compiled the OS with\n"
+ "InnoDB: a big enough maximum process size.\n",
+ n, ut_total_allocated_memory, errno);
+ return(FALSE);
+ }
+
+ free(ret);
+
+ return(TRUE);
+}
+
+/**************************************************************************
Frees a memory block allocated with ut_malloc. */
void
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 0d56f216b3b..cfec8282e42 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -1624,6 +1624,12 @@ build_template(
}
if (prebuilt->select_lock_type == LOCK_X) {
+ /* In versions < 3.23.50 we always retrieved the clustered
+ index record if prebuilt->select_lock_type == LOCK_S,
+ but there is really not need for that, and in some cases
+ performance could be seriously degraded because the MySQL
+ optimizer did not know about our convention! */
+
/* We always retrieve the whole clustered index record if we
use exclusive row level locks, for example, if the read is
done in an UPDATE statement. */
@@ -1632,12 +1638,6 @@ build_template(
}
if (templ_type == ROW_MYSQL_REC_FIELDS) {
- /* In versions < 3.23.50 we always retrieved the clustered
- index record if prebuilt->select_lock_type == LOCK_S,
- but there is really not need for that, and in some cases
- performance could be seriously degraded because the MySQL
- optimizer did not know about our convention! */
-
index = prebuilt->index;
} else {
index = clust_index;
@@ -2506,11 +2506,13 @@ ha_innobase::change_active_index(
dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
prebuilt->index->n_fields);
- /* Maybe MySQL changes the active index for a handle also
- during some queries, we do not know: then it is safest to build
- the template such that all columns will be fetched */
+ /* MySQL changes the active index for a handle also during some
+ queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
+ and then calculates te sum. Previously we played safe and used
+ the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
+ copying. Starting from MySQL-4.1 we use a more efficient flag here. */
- build_template(prebuilt, user_thd, table, ROW_MYSQL_WHOLE_ROW);
+ build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
DBUG_RETURN(0);
}
@@ -3742,8 +3744,18 @@ ha_innobase::extra(
obsolete! */
switch (operation) {
+ case HA_EXTRA_FLUSH:
+ if (prebuilt->blob_heap) {
+ row_mysql_prebuilt_free_blob_heap(prebuilt);
+ }
+ break;
case HA_EXTRA_RESET:
- case HA_EXTRA_RESET_STATE:
+ if (prebuilt->blob_heap) {
+ row_mysql_prebuilt_free_blob_heap(prebuilt);
+ }
+ prebuilt->read_just_key = 0;
+ break;
+ case HA_EXTRA_RESET_STATE:
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_NO_KEYREAD: