mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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
This commit is contained in:
@ -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 @@ rec_loop:
|
||||
/* 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);
|
||||
|
||||
|
Reference in New Issue
Block a user