diff options
Diffstat (limited to 'innobase/row/row0sel.c')
-rw-r--r-- | innobase/row/row0sel.c | 75 |
1 files changed, 62 insertions, 13 deletions
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index abae7f373bf..f3dced15fdf 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2071,13 +2071,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); @@ -2091,6 +2089,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 */ @@ -2116,8 +2116,28 @@ row_sel_store_mysql_rec( extern_field_heap = NULL; } } else { - mysql_rec[templ->mysql_null_byte_offset] |= + /* 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 NULL 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, NULL, + 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" +"innoDB: column! Table name %s\n", prebuilt->table->name); + } else { + mysql_rec[templ->mysql_null_byte_offset] |= (byte) (templ->mysql_null_bit_mask); + } } } } @@ -2234,7 +2254,7 @@ row_sel_get_clust_rec_for_mysql( (or old_vers) is not rec; in that case we must ignore such row because in our snapshot rec would not have existed. Remember that from rec we cannot see directly which transaction - id corrsponds to it: we have to go to the clustered index + id corresponds to it: we have to go to the clustered index record. A query where we want to fetch all rows where the secondary index value is in some interval would return a wrong result if we would not drop rows which we come to @@ -2245,6 +2265,12 @@ row_sel_get_clust_rec_for_mysql( && !row_sel_sec_rec_is_for_clust_rec(rec, sec_index, clust_rec, clust_index)) { clust_rec = NULL; + } else { +#ifdef UNIV_SEARCH_DEBUG + ut_a(clust_rec == NULL || + row_sel_sec_rec_is_for_clust_rec(rec, sec_index, + clust_rec, clust_index)); +#endif } } @@ -2357,6 +2383,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 */ @@ -2397,10 +2424,16 @@ 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, - RW_S_LATCH, mtr); +#ifndef UNIV_SEARCH_DEBUG + RW_S_LATCH, +#else + 0, +#endif + mtr); rec = btr_pcur_get_rec(pcur); if (!page_rec_is_user_rec(rec)) { @@ -2574,8 +2607,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)) { @@ -2624,15 +2665,18 @@ row_search_for_mysql( goto no_shortcut; } - +#ifndef UNIV_SEARCH_DEBUG if (!trx->has_search_latch) { rw_lock_s_lock(&btr_search_latch); trx->has_search_latch = TRUE; } - +#endif shortcut = row_sel_try_search_shortcut_for_mysql(&rec, prebuilt, &mtr); if (shortcut == SEL_FOUND) { +#ifdef UNIV_SEARCH_DEBUG + ut_a(0 == cmp_dtuple_rec(search_tuple, rec)); +#endif row_sel_store_mysql_rec(buf, prebuilt, rec); mtr_commit(&mtr); @@ -2794,7 +2838,9 @@ rec_loop: /* The record matches enough */ ut_ad(mode == PAGE_CUR_GE); - +#ifdef UNIV_SEARCH_DEBUG + ut_a(0 == cmp_dtuple_rec(search_tuple, rec)); +#endif } else if (match_mode == ROW_SEL_EXACT) { /* Test if the index record matches completely to search_tuple in prebuilt: if not, then we return with DB_RECORD_NOT_FOUND */ @@ -2923,15 +2969,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); |