Commit c04a7ea5 authored by unknown's avatar unknown

row0mysql.c:

  Add some diagnostic code to track BLOB bugs if they were not already fixed with the change in row0sel.c
row0sel.c:
  Fix a seg fault which was caused inside MySQL because InnoDB when returning a BLOB value did not initialize the length and the data pointer of a BLOB whose value is the SQL NULL; also fix a very improbable race condition which could occur if a row with an externally stored BLOB was fetched using the adaptive hash index


innobase/row/row0sel.c:
  Fix a seg fault which was caused inside MySQL because InnoDB when returning a BLOB value did not initialize the length and the data pointer of a BLOB whose value is the SQL NULL; also fix a very improbable race condition which could occur if a row with an externally stored BLOB was fetched using the adaptive hash index
innobase/row/row0mysql.c:
  Add some diagnostic code to track BLOB bugs if they were not already fixed with the change in row0sel.c
parent 15bf792f
......@@ -28,6 +28,9 @@ Created 9/17/2000 Heikki Tuuri
#include "rem0cmp.h"
#include "log0log.h"
/* A dummy variable used to fool the compiler */
ibool row_mysql_identically_false = FALSE;
/* List of tables we should drop in background. ALTER TABLE in MySQL requires
that the table handler can drop the table in background when there are no
queries to it any more. Protected by the kernel mutex. */
......@@ -67,11 +70,34 @@ row_mysql_store_blob_ref(
byte* data, /* in: BLOB data */
ulint len) /* in: BLOB length */
{
ulint sum = 0;
ulint i;
/* In dest there are 1 - 4 bytes reserved for the BLOB length,
and after that 8 bytes reserved for the pointer to the data.
In 32-bit architectures we only use the first 4 bytes of the pointer
slot. */
ut_a(col_len - 8 > 1 || len < 256);
ut_a(col_len - 8 > 2 || len < 256 * 256);
ut_a(col_len - 8 > 3 || len < 256 * 256 * 256);
/* We try to track an elusive bug which probably was fixed
May 9, 2002, but better be sure: we probe the data buffer
to make sure it is in valid allocated memory */
for (i = 0; i < len; i++) {
sum += (ulint)(data + i);
}
/* The variable below is identically false, we just fool the
compiler to not optimize away our loop */
if (row_mysql_identically_false) {
printf("Sum %lu\n", sum);
}
mach_write_to_n_little_endian(dest, col_len - 8, len);
ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*));
......
......@@ -31,6 +31,8 @@ Created 12/19/1997 Heikki Tuuri
#include "pars0pars.h"
#include "row0mysql.h"
byte row_sel_dummy_byte;
/* Maximum number of rows to prefetch; MySQL interface has another parameter */
#define SEL_MAX_N_PREFETCH 16
......@@ -2070,13 +2072,11 @@ row_sel_store_mysql_rec(
data = rec_get_nth_field(rec, templ->rec_field_no, &len);
if (rec_get_nth_field_extern_bit(rec, templ->rec_field_no)) {
/* Copy an externally stored field to the temporary
heap */
if (prebuilt->trx->has_search_latch) {
rw_lock_s_unlock(&btr_search_latch);
prebuilt->trx->has_search_latch = FALSE;
}
ut_a(!prebuilt->trx->has_search_latch);
extern_field_heap = mem_heap_create(UNIV_PAGE_SIZE);
......@@ -2090,6 +2090,8 @@ row_sel_store_mysql_rec(
if (len != UNIV_SQL_NULL) {
if (templ->type == DATA_BLOB) {
ut_a(prebuilt->templ_contains_blob);
/* Copy the BLOB data to the BLOB
heap of prebuilt */
......@@ -2115,6 +2117,21 @@ row_sel_store_mysql_rec(
extern_field_heap = NULL;
}
} else {
/* MySQL sometimes seems to copy the 'data'
pointed to by a BLOB field even if the field
has been marked to contain the SQL NULL value.
This caused seg faults reported by two users.
Set the BLOB length to 0 and the data pointer
to a dummy allocated mem address to avoid
a seg fault. */
if (templ->type == DATA_BLOB) {
row_sel_field_store_in_mysql_format(
mysql_rec + templ->mysql_col_offset,
templ->mysql_col_len, &row_sel_dummy_byte,
0, templ->type, templ->is_unsigned);
}
if (!templ->mysql_null_bit_mask) {
fprintf(stderr,
"InnoDB: Error: trying to return an SQL NULL field in a non-null\n"
......@@ -2368,6 +2385,7 @@ row_sel_push_cache_row_for_mysql(
ulint i;
ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE);
ut_a(!prebuilt->templ_contains_blob);
if (prebuilt->fetch_cache[0] == NULL) {
/* Allocate memory for the fetch cache */
......@@ -2408,6 +2426,7 @@ row_sel_try_search_shortcut_for_mysql(
rec_t* rec;
ut_ad(index->type & DICT_CLUSTERED);
ut_ad(!prebuilt->templ_contains_blob);
btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE,
BTR_SEARCH_LEAF, pcur,
......@@ -2590,8 +2609,16 @@ row_search_for_mysql(
mtr_start(&mtr);
if (match_mode == ROW_SEL_EXACT && index->type & DICT_UNIQUE
/* Since we must release the search system latch when we retrieve an
externally stored field, we cannot use the adaptive hash index in a
search in the case the row may be long and there may be externally
stored fields */
if (match_mode == ROW_SEL_EXACT
&& index->type & DICT_UNIQUE
&& index->type & DICT_CLUSTERED
&& !prebuilt->templ_contains_blob
&& (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)
&& dtuple_get_n_fields(search_tuple)
== dict_index_get_n_unique(index)) {
......@@ -2944,15 +2971,18 @@ row_search_for_mysql(
/* We found a qualifying row */
if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD
&& !prebuilt->templ_contains_blob
&& prebuilt->select_lock_type == LOCK_NONE
&& !prebuilt->templ_contains_blob
&& !prebuilt->clust_index_was_generated
&& prebuilt->template_type
!= ROW_MYSQL_DUMMY_TEMPLATE) {
/* Inside an update, for example, we do not cache rows,
since we may use the cursor position to do the actual
update, that is why we require ...lock_type == LOCK_NONE */
update, that is why we require ...lock_type == LOCK_NONE.
Since we keep space in prebuilt only for the BLOBs of
a single row, we cannot cache rows in the case there
are BLOBs in the fields to be fetched. */
row_sel_push_cache_row_for_mysql(prebuilt, rec);
......
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