ut0mem.c, row0sel.c, row0mysql.c, ut0mem.h, row0sel.h, row0mysql.h:

  Test allocation of memory beforehand if we are trying to return a > 2 MB BLOB; normally InnoDB asserts if memory allocation fails
ha_innodb.cc:
  Do not fetch all columns if change_active_index() is called during a query; a sum(a), max(a) query seemed to do that, doing unnecessary copying (the change actually made in the previous bk ci)
  Free BLOB heap of handle when MySQL calls some ::extra()'s
parent 0d294e1b
...@@ -52,6 +52,14 @@ row_mysql_read_var_ref_noninline( ...@@ -52,6 +52,14 @@ row_mysql_read_var_ref_noninline(
ulint* len, /* out: variable-length field length */ ulint* len, /* out: variable-length field length */
byte* field); /* in: field */ 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. */ Stores a reference to a BLOB in the MySQL format. */
void void
......
...@@ -115,7 +115,8 @@ row_search_for_mysql( ...@@ -115,7 +115,8 @@ row_search_for_mysql(
/*=================*/ /*=================*/
/* out: DB_SUCCESS, /* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND, 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 byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */ row in the MySQL format */
ulint mode, /* in: search mode PAGE_CUR_L, ... */ ulint mode, /* in: search mode PAGE_CUR_L, ... */
......
...@@ -50,6 +50,16 @@ ut_malloc( ...@@ -50,6 +50,16 @@ ut_malloc(
/* out, own: allocated memory */ /* out, own: allocated memory */
ulint n); /* in: number of bytes to allocate */ 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. */ Frees a memory bloock allocated with ut_malloc. */
void void
......
...@@ -58,6 +58,19 @@ row_mysql_read_var_ref_noninline( ...@@ -58,6 +58,19 @@ row_mysql_read_var_ref_noninline(
return(row_mysql_read_var_ref(len, field)); return(row_mysql_read_var_ref(len, 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 */
{
mem_heap_free(prebuilt->blob_heap);
prebuilt->blob_heap = NULL;
}
/*********************************************************************** /***********************************************************************
Stores a reference to a BLOB in the MySQL format. */ Stores a reference to a BLOB in the MySQL format. */
......
...@@ -2039,9 +2039,12 @@ Note that the template in prebuilt may advise us to copy only a few ...@@ -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 columns to mysql_rec, other columns are left blank. All columns may not
be needed in the query. */ be needed in the query. */
static static
void ibool
row_sel_store_mysql_rec( 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 */ byte* mysql_rec, /* out: row in the MySQL format */
row_prebuilt_t* prebuilt, /* in: prebuilt struct */ row_prebuilt_t* prebuilt, /* in: prebuilt struct */
rec_t* rec) /* in: Innobase record in the index rec_t* rec) /* in: Innobase record in the index
...@@ -2093,6 +2096,18 @@ row_sel_store_mysql_rec( ...@@ -2093,6 +2096,18 @@ row_sel_store_mysql_rec(
ut_a(prebuilt->templ_contains_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 /* Copy the BLOB data to the BLOB
heap of prebuilt */ heap of prebuilt */
...@@ -2142,6 +2157,8 @@ row_sel_store_mysql_rec( ...@@ -2142,6 +2157,8 @@ row_sel_store_mysql_rec(
} }
} }
} }
return(TRUE);
} }
/************************************************************************* /*************************************************************************
...@@ -2526,7 +2543,8 @@ row_search_for_mysql( ...@@ -2526,7 +2543,8 @@ row_search_for_mysql(
/*=================*/ /*=================*/
/* out: DB_SUCCESS, /* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND, 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 byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */ row in the MySQL format */
ulint mode, /* in: search mode PAGE_CUR_L, ... */ ulint mode, /* in: search mode PAGE_CUR_L, ... */
...@@ -2758,7 +2776,12 @@ row_search_for_mysql( ...@@ -2758,7 +2776,12 @@ row_search_for_mysql(
#ifdef UNIV_SEARCH_DEBUG #ifdef UNIV_SEARCH_DEBUG
ut_a(0 == cmp_dtuple_rec(search_tuple, rec)); ut_a(0 == cmp_dtuple_rec(search_tuple, rec));
#endif #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); mtr_commit(&mtr);
...@@ -3200,7 +3223,11 @@ rec_loop: ...@@ -3200,7 +3223,11 @@ rec_loop:
rec_get_size(rec)); rec_get_size(rec));
mach_write_to_4(buf, rec_get_extra_size(rec) + 4); mach_write_to_4(buf, rec_get_extra_size(rec) + 4);
} else { } 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) { if (prebuilt->clust_index_was_generated) {
......
...@@ -77,8 +77,9 @@ ut_malloc_low( ...@@ -77,8 +77,9 @@ ut_malloc_low(
ret = malloc(n + sizeof(ut_mem_block_t)); ret = malloc(n + sizeof(ut_mem_block_t));
if (ret == NULL) { if (ret == NULL) {
ut_print_timestamp(stderr);
fprintf(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: memory with malloc! Total allocated memory\n"
"InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n" "InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n"
"InnoDB: Cannot continue operation!\n" "InnoDB: Cannot continue operation!\n"
...@@ -134,6 +135,40 @@ ut_malloc( ...@@ -134,6 +135,40 @@ ut_malloc(
return(ut_malloc_low(n, TRUE)); return(ut_malloc_low(n, TRUE));
} }
/**************************************************************************
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. */ Frees a memory block allocated with ut_malloc. */
......
...@@ -1624,6 +1624,12 @@ build_template( ...@@ -1624,6 +1624,12 @@ build_template(
} }
if (prebuilt->select_lock_type == LOCK_X) { 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 /* We always retrieve the whole clustered index record if we
use exclusive row level locks, for example, if the read is use exclusive row level locks, for example, if the read is
done in an UPDATE statement. */ done in an UPDATE statement. */
...@@ -1632,12 +1638,6 @@ build_template( ...@@ -1632,12 +1638,6 @@ build_template(
} }
if (templ_type == ROW_MYSQL_REC_FIELDS) { 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; index = prebuilt->index;
} else { } else {
index = clust_index; index = clust_index;
...@@ -2506,11 +2506,13 @@ ha_innobase::change_active_index( ...@@ -2506,11 +2506,13 @@ ha_innobase::change_active_index(
dict_index_copy_types(prebuilt->search_tuple, prebuilt->index, dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
prebuilt->index->n_fields); prebuilt->index->n_fields);
/* Maybe MySQL changes the active index for a handle also /* MySQL changes the active index for a handle also during some
during some queries, we do not know: then it is safest to build queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
the template such that all columns will be fetched */ 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); DBUG_RETURN(0);
} }
...@@ -3742,7 +3744,17 @@ ha_innobase::extra( ...@@ -3742,7 +3744,17 @@ ha_innobase::extra(
obsolete! */ obsolete! */
switch (operation) { 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:
if (prebuilt->blob_heap) {
row_mysql_prebuilt_free_blob_heap(prebuilt);
}
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_RESET_STATE: case HA_EXTRA_RESET_STATE:
prebuilt->read_just_key = 0; prebuilt->read_just_key = 0;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment