mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +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 "rem0cmp.h"
|
||||||
#include "log0log.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
|
/* 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
|
that the table handler can drop the table in background when there are no
|
||||||
queries to it any more. Protected by the kernel mutex. */
|
queries to it any more. Protected by the kernel mutex. */
|
||||||
@ -67,11 +70,34 @@ row_mysql_store_blob_ref(
|
|||||||
byte* data, /* in: BLOB data */
|
byte* data, /* in: BLOB data */
|
||||||
ulint len) /* in: BLOB length */
|
ulint len) /* in: BLOB length */
|
||||||
{
|
{
|
||||||
|
ulint sum = 0;
|
||||||
|
ulint i;
|
||||||
|
|
||||||
/* In dest there are 1 - 4 bytes reserved for the BLOB length,
|
/* In dest there are 1 - 4 bytes reserved for the BLOB length,
|
||||||
and after that 8 bytes reserved for the pointer to the data.
|
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
|
In 32-bit architectures we only use the first 4 bytes of the pointer
|
||||||
slot. */
|
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);
|
mach_write_to_n_little_endian(dest, col_len - 8, len);
|
||||||
|
|
||||||
ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*));
|
ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*));
|
||||||
|
@ -31,6 +31,8 @@ Created 12/19/1997 Heikki Tuuri
|
|||||||
#include "pars0pars.h"
|
#include "pars0pars.h"
|
||||||
#include "row0mysql.h"
|
#include "row0mysql.h"
|
||||||
|
|
||||||
|
byte row_sel_dummy_byte;
|
||||||
|
|
||||||
/* Maximum number of rows to prefetch; MySQL interface has another parameter */
|
/* Maximum number of rows to prefetch; MySQL interface has another parameter */
|
||||||
#define SEL_MAX_N_PREFETCH 16
|
#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);
|
data = rec_get_nth_field(rec, templ->rec_field_no, &len);
|
||||||
|
|
||||||
if (rec_get_nth_field_extern_bit(rec, templ->rec_field_no)) {
|
if (rec_get_nth_field_extern_bit(rec, templ->rec_field_no)) {
|
||||||
|
|
||||||
/* Copy an externally stored field to the temporary
|
/* Copy an externally stored field to the temporary
|
||||||
heap */
|
heap */
|
||||||
|
|
||||||
if (prebuilt->trx->has_search_latch) {
|
ut_a(!prebuilt->trx->has_search_latch);
|
||||||
rw_lock_s_unlock(&btr_search_latch);
|
|
||||||
prebuilt->trx->has_search_latch = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern_field_heap = mem_heap_create(UNIV_PAGE_SIZE);
|
extern_field_heap = mem_heap_create(UNIV_PAGE_SIZE);
|
||||||
|
|
||||||
@ -2090,6 +2090,8 @@ row_sel_store_mysql_rec(
|
|||||||
if (len != UNIV_SQL_NULL) {
|
if (len != UNIV_SQL_NULL) {
|
||||||
if (templ->type == DATA_BLOB) {
|
if (templ->type == DATA_BLOB) {
|
||||||
|
|
||||||
|
ut_a(prebuilt->templ_contains_blob);
|
||||||
|
|
||||||
/* Copy the BLOB data to the BLOB
|
/* Copy the BLOB data to the BLOB
|
||||||
heap of prebuilt */
|
heap of prebuilt */
|
||||||
|
|
||||||
@ -2115,6 +2117,21 @@ row_sel_store_mysql_rec(
|
|||||||
extern_field_heap = NULL;
|
extern_field_heap = NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (!templ->mysql_null_bit_mask) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"InnoDB: Error: trying to return an SQL NULL field in a non-null\n"
|
"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;
|
ulint i;
|
||||||
|
|
||||||
ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE);
|
ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE);
|
||||||
|
ut_a(!prebuilt->templ_contains_blob);
|
||||||
|
|
||||||
if (prebuilt->fetch_cache[0] == NULL) {
|
if (prebuilt->fetch_cache[0] == NULL) {
|
||||||
/* Allocate memory for the fetch cache */
|
/* Allocate memory for the fetch cache */
|
||||||
@ -2408,6 +2426,7 @@ row_sel_try_search_shortcut_for_mysql(
|
|||||||
rec_t* rec;
|
rec_t* rec;
|
||||||
|
|
||||||
ut_ad(index->type & DICT_CLUSTERED);
|
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_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE,
|
||||||
BTR_SEARCH_LEAF, pcur,
|
BTR_SEARCH_LEAF, pcur,
|
||||||
@ -2590,8 +2609,16 @@ row_search_for_mysql(
|
|||||||
|
|
||||||
mtr_start(&mtr);
|
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
|
&& index->type & DICT_CLUSTERED
|
||||||
|
&& !prebuilt->templ_contains_blob
|
||||||
|
&& (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)
|
||||||
&& dtuple_get_n_fields(search_tuple)
|
&& dtuple_get_n_fields(search_tuple)
|
||||||
== dict_index_get_n_unique(index)) {
|
== dict_index_get_n_unique(index)) {
|
||||||
|
|
||||||
@ -2944,15 +2971,18 @@ rec_loop:
|
|||||||
/* We found a qualifying row */
|
/* We found a qualifying row */
|
||||||
|
|
||||||
if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD
|
if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD
|
||||||
&& !prebuilt->templ_contains_blob
|
|
||||||
&& prebuilt->select_lock_type == LOCK_NONE
|
&& prebuilt->select_lock_type == LOCK_NONE
|
||||||
|
&& !prebuilt->templ_contains_blob
|
||||||
&& !prebuilt->clust_index_was_generated
|
&& !prebuilt->clust_index_was_generated
|
||||||
&& prebuilt->template_type
|
&& prebuilt->template_type
|
||||||
!= ROW_MYSQL_DUMMY_TEMPLATE) {
|
!= ROW_MYSQL_DUMMY_TEMPLATE) {
|
||||||
|
|
||||||
/* Inside an update, for example, we do not cache rows,
|
/* Inside an update, for example, we do not cache rows,
|
||||||
since we may use the cursor position to do the actual
|
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);
|
row_sel_push_cache_row_for_mysql(prebuilt, rec);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user