summaryrefslogtreecommitdiff
path: root/storage/xtradb/row
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2010-10-28 19:04:23 +0200
committerSergei Golubchik <sergii@pisem.net>2010-10-28 19:04:23 +0200
commitde1e2c287dbd2c2e219c0735a064424c7c74d8ec (patch)
tree9f9950feb45222c7df4f37c841f4af593ec75ee5 /storage/xtradb/row
parent7371ecc8c0c1022e2535315f936d9af783f2e49c (diff)
parentc626a024fdce68bf32b728779f9fa98660ee9f29 (diff)
downloadmariadb-git-de1e2c287dbd2c2e219c0735a064424c7c74d8ec.tar.gz
5.2 merge
Diffstat (limited to 'storage/xtradb/row')
-rw-r--r--storage/xtradb/row/row0merge.c7
-rw-r--r--storage/xtradb/row/row0mysql.c35
-rw-r--r--storage/xtradb/row/row0row.c8
-rw-r--r--storage/xtradb/row/row0sel.c126
-rw-r--r--storage/xtradb/row/row0undo.c18
-rw-r--r--storage/xtradb/row/row0upd.c29
6 files changed, 185 insertions, 38 deletions
diff --git a/storage/xtradb/row/row0merge.c b/storage/xtradb/row/row0merge.c
index 47c03c77850..65102851bdf 100644
--- a/storage/xtradb/row/row0merge.c
+++ b/storage/xtradb/row/row0merge.c
@@ -1787,6 +1787,11 @@ row_merge_copy_blobs(
(below). */
data = btr_rec_copy_externally_stored_field(
mrec, offsets, zip_size, i, &len, heap);
+ /* Because we have locked the table, any records
+ written by incomplete transactions must have been
+ rolled back already. There must not be any incomplete
+ BLOB columns. */
+ ut_a(data);
dfield_set_data(field, data, len);
}
@@ -2399,7 +2404,7 @@ row_merge_rename_tables(
goto err_exit;
}
- err = dict_load_foreigns(old_name, TRUE);
+ err = dict_load_foreigns(old_name, FALSE, TRUE);
if (err != DB_SUCCESS) {
err_exit:
diff --git a/storage/xtradb/row/row0mysql.c b/storage/xtradb/row/row0mysql.c
index 62221fa456d..56754404b65 100644
--- a/storage/xtradb/row/row0mysql.c
+++ b/storage/xtradb/row/row0mysql.c
@@ -577,6 +577,13 @@ handle_new_error:
"InnoDB: " REFMAN "forcing-recovery.html"
" for help.\n", stderr);
break;
+ case DB_FOREIGN_EXCEED_MAX_CASCADE:
+ fprintf(stderr, "InnoDB: Cannot delete/update rows with"
+ " cascading foreign key constraints that exceed max"
+ " depth of %lu\n"
+ "Please drop excessive foreign constraints"
+ " and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD);
+ break;
default:
fprintf(stderr, "InnoDB: unknown error code %lu\n",
(ulong) err);
@@ -1392,11 +1399,15 @@ row_update_for_mysql(
run_again:
thr->run_node = node;
thr->prev_node = node;
+ thr->fk_cascade_depth = 0;
row_upd_step(thr);
err = trx->error_state;
+ /* Reset fk_cascade_depth back to 0 */
+ thr->fk_cascade_depth = 0;
+
if (err != DB_SUCCESS) {
que_thr_stop_for_mysql(thr);
@@ -1587,6 +1598,12 @@ row_update_cascade_for_mysql(
trx_t* trx;
trx = thr_get_trx(thr);
+
+ thr->fk_cascade_depth++;
+
+ if (thr->fk_cascade_depth > FK_MAX_CASCADE_DEL) {
+ return (DB_FOREIGN_EXCEED_MAX_CASCADE);
+ }
run_again:
thr->run_node = node;
thr->prev_node = node;
@@ -2106,7 +2123,7 @@ row_table_add_foreign_constraints(
name, reject_fks);
if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
- err = dict_load_foreigns(name, TRUE);
+ err = dict_load_foreigns(name, FALSE, TRUE);
}
if (err != DB_SUCCESS) {
@@ -2799,6 +2816,15 @@ row_truncate_table_for_mysql(
trx->table_id = table->id;
+ /* Lock all index trees for this table, as we will
+ truncate the table/index and possibly change their metadata.
+ All DML/DDL are blocked by table level lock, with
+ a few exceptions such as queries into information schema
+ about the table, MySQL could try to access index stats
+ for this kind of query, we need to use index locks to
+ sync up */
+ dict_table_x_lock_indexes(table);
+
if (table->space && !table->dir_path_of_temp_table) {
/* Discard and create the single-table tablespace. */
ulint space = table->space;
@@ -2815,6 +2841,7 @@ row_truncate_table_for_mysql(
|| fil_create_new_single_table_tablespace(
space, table->name, FALSE, flags,
FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) {
+ dict_table_x_unlock_indexes(table);
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: TRUNCATE TABLE %s failed to"
@@ -2918,6 +2945,10 @@ next_rec:
mem_heap_free(heap);
+ /* Done with index truncation, release index tree locks,
+ subsequent work relates to table level metadata change */
+ dict_table_x_unlock_indexes(table);
+
dict_hdr_get_new_id(&new_id, NULL, NULL);
info = pars_info_create();
@@ -3967,7 +3998,7 @@ end:
an ALTER, not in a RENAME. */
err = dict_load_foreigns(
- new_name, !old_is_tmp || trx->check_foreigns);
+ new_name, FALSE, !old_is_tmp || trx->check_foreigns);
if (err != DB_SUCCESS) {
ut_print_timestamp(stderr);
diff --git a/storage/xtradb/row/row0row.c b/storage/xtradb/row/row0row.c
index cb7dfa2b7c9..8e806a14a98 100644
--- a/storage/xtradb/row/row0row.c
+++ b/storage/xtradb/row/row0row.c
@@ -294,7 +294,13 @@ row_build(
ut_ad(dtuple_check_typed(row));
- if (j) {
+ if (!ext) {
+ /* REDUNDANT and COMPACT formats store a local
+ 768-byte prefix of each externally stored
+ column. No cache is needed. */
+ ut_ad(dict_table_get_format(index->table)
+ < DICT_TF_FORMAT_ZIP);
+ } else if (j) {
*ext = row_ext_create(j, ext_cols, row,
dict_table_zip_size(index->table),
heap);
diff --git a/storage/xtradb/row/row0sel.c b/storage/xtradb/row/row0sel.c
index 0c728154257..2839d935167 100644
--- a/storage/xtradb/row/row0sel.c
+++ b/storage/xtradb/row/row0sel.c
@@ -416,7 +416,7 @@ row_sel_fetch_columns(
field_no))) {
/* Copy an externally stored field to the
- temporary heap */
+ temporary heap, if possible. */
heap = mem_heap_create(1);
@@ -425,6 +425,17 @@ row_sel_fetch_columns(
dict_table_zip_size(index->table),
field_no, &len, heap);
+ /* data == NULL means that the
+ externally stored field was not
+ written yet. This record
+ should only be seen by
+ recv_recovery_rollback_active() or any
+ TRX_ISO_READ_UNCOMMITTED
+ transactions. The InnoDB SQL parser
+ (the sole caller of this function)
+ does not implement READ UNCOMMITTED,
+ and it is not involved during rollback. */
+ ut_a(data);
ut_a(len != UNIV_SQL_NULL);
needs_copy = TRUE;
@@ -926,6 +937,7 @@ row_sel_get_clust_rec(
when plan->clust_pcur was positioned. The latch will not be
released until mtr_commit(mtr). */
+ ut_ad(!rec_get_deleted_flag(clust_rec, rec_offs_comp(offsets)));
row_sel_fetch_columns(index, clust_rec, offsets,
UT_LIST_GET_FIRST(plan->columns));
*out_rec = clust_rec;
@@ -1628,6 +1640,13 @@ skip_lock:
}
if (old_vers == NULL) {
+ /* The record does not exist
+ in our read view. Skip it, but
+ first attempt to determine
+ whether the index segment we
+ are searching through has been
+ exhausted. */
+
offsets = rec_get_offsets(
rec, index, offsets,
ULINT_UNDEFINED, &heap);
@@ -2647,9 +2666,8 @@ Convert a row in the Innobase format to a row in the MySQL format.
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
be needed in the query.
-@return TRUE if success, FALSE if could not allocate memory for a BLOB
-(though we may also assert in that case) */
-static
+@return TRUE on success, FALSE if not all columns could be retrieved */
+static __attribute__((warn_unused_result))
ibool
row_sel_store_mysql_rec(
/*====================*/
@@ -2725,6 +2743,21 @@ row_sel_store_mysql_rec(
dict_table_zip_size(prebuilt->table),
templ->rec_field_no, &len, heap);
+ if (UNIV_UNLIKELY(!data)) {
+ /* The externally stored field
+ was not written yet. This
+ record should only be seen by
+ recv_recovery_rollback_active()
+ or any TRX_ISO_READ_UNCOMMITTED
+ transactions. */
+
+ if (extern_field_heap) {
+ mem_heap_free(extern_field_heap);
+ }
+
+ return(FALSE);
+ }
+
ut_a(len != UNIV_SQL_NULL);
} else {
/* Field is stored in the row. */
@@ -3146,9 +3179,10 @@ row_sel_pop_cached_row_for_mysql(
}
/********************************************************************//**
-Pushes a row for MySQL to the fetch cache. */
-UNIV_INLINE
-void
+Pushes a row for MySQL to the fetch cache.
+@return TRUE on success, FALSE if the record contains incomplete BLOBs */
+UNIV_INLINE __attribute__((warn_unused_result))
+ibool
row_sel_push_cache_row_for_mysql(
/*=============================*/
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */
@@ -3197,7 +3231,7 @@ row_sel_push_cache_row_for_mysql(
offsets,
start_field_no,
prebuilt->n_template))) {
- ut_error;
+ return(FALSE);
}
if (start_field_no) {
@@ -3228,6 +3262,7 @@ row_sel_push_cache_row_for_mysql(
}
prebuilt->n_fetch_cached++;
+ return(TRUE);
}
/*********************************************************************//**
@@ -3622,15 +3657,26 @@ row_search_for_mysql(
row_sel_try_search_shortcut_for_mysql().
The latch will not be released until
mtr_commit(&mtr). */
+ ut_ad(!rec_get_deleted_flag(rec, comp));
if (!row_sel_store_mysql_rec(buf, prebuilt,
rec, offsets, 0,
prebuilt->n_template)) {
- err = DB_TOO_BIG_RECORD;
-
- /* We let the main loop to do the
- error handling */
- goto shortcut_fails_too_big_rec;
+ /* Only fresh inserts may contain
+ incomplete externally stored
+ columns. Pretend that such
+ records do not exist. Such
+ records may only be accessed
+ at the READ UNCOMMITTED
+ isolation level or when
+ rolling back a recovered
+ transaction. Rollback happens
+ at a lower level, not here. */
+ ut_a(trx->isolation_level
+ == TRX_ISO_READ_UNCOMMITTED);
+
+ /* Proceed as in case SEL_RETRY. */
+ break;
}
mtr_commit(&mtr);
@@ -3670,7 +3716,7 @@ release_search_latch_if_needed:
default:
ut_ad(0);
}
-shortcut_fails_too_big_rec:
+
mtr_commit(&mtr);
mtr_start(&mtr);
}
@@ -4264,7 +4310,7 @@ no_gap_lock:
rec = old_vers;
}
- } else if (!lock_sec_rec_cons_read_sees(rec, trx->read_view)) {
+ } else {
/* We are looking into a non-clustered index,
and to get the right version of the record we
have to look also into the clustered index: this
@@ -4272,8 +4318,13 @@ no_gap_lock:
information via the clustered index record. */
ut_ad(index != clust_index);
- get_clust_rec = TRUE;
- goto idx_cond_check;
+ ut_ad(!dict_index_is_clust(index));
+
+ if (!lock_sec_rec_cons_read_sees(
+ rec, trx->read_view)) {
+ get_clust_rec = TRUE;
+ goto idx_cond_check;
+ }
}
}
@@ -4412,8 +4463,13 @@ idx_cond_check:
ULINT_UNDEFINED, &heap);
result_rec = rec;
}
+
+ /* result_rec can legitimately be delete-marked
+ now that it has been established that it points to a
+ clustered index record that exists in the read view. */
} else {
result_rec = rec;
+ ut_ad(!rec_get_deleted_flag(rec, comp));
}
/* We found a qualifying record 'result_rec'. At this point,
@@ -4447,13 +4503,21 @@ idx_cond_check:
some_fields_in_buffer = (index != clust_index
&& prebuilt->idx_cond_func);
- row_sel_push_cache_row_for_mysql(prebuilt,
- result_rec,
- offsets,
- some_fields_in_buffer?
- prebuilt->n_index_fields : 0,
- buf);
- if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) {
+ if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec,
+ offsets,
+ some_fields_in_buffer?
+ prebuilt->n_index_fields : 0,
+ buf)) {
+ /* Only fresh inserts may contain incomplete
+ externally stored columns. Pretend that such
+ records do not exist. Such records may only be
+ accessed at the READ UNCOMMITTED isolation
+ level or when rolling back a recovered
+ transaction. Rollback happens at a lower
+ level, not here. */
+ ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED);
+ } else if (prebuilt->n_fetch_cached
+ == MYSQL_FETCH_CACHE_SIZE) {
goto got_row;
}
@@ -4472,9 +4536,17 @@ idx_cond_check:
prebuilt->idx_cond_func?
prebuilt->n_index_fields: 0,
prebuilt->n_template)) {
- err = DB_TOO_BIG_RECORD;
-
- goto lock_wait_or_error;
+ /* Only fresh inserts may contain
+ incomplete externally stored
+ columns. Pretend that such records do
+ not exist. Such records may only be
+ accessed at the READ UNCOMMITTED
+ isolation level or when rolling back a
+ recovered transaction. Rollback
+ happens at a lower level, not here. */
+ ut_a(trx->isolation_level
+ == TRX_ISO_READ_UNCOMMITTED);
+ goto next_rec;
}
}
diff --git a/storage/xtradb/row/row0undo.c b/storage/xtradb/row/row0undo.c
index 9ef842b5114..fd28a4f6520 100644
--- a/storage/xtradb/row/row0undo.c
+++ b/storage/xtradb/row/row0undo.c
@@ -199,8 +199,24 @@ row_undo_search_clust_to_pcur(
ret = FALSE;
} else {
+ row_ext_t** ext;
+
+ if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) {
+ /* In DYNAMIC or COMPRESSED format, there is
+ no prefix of externally stored columns in the
+ clustered index record. Build a cache of
+ column prefixes. */
+ ext = &node->ext;
+ } else {
+ /* REDUNDANT and COMPACT formats store a local
+ 768-byte prefix of each externally stored
+ column. No cache is needed. */
+ ext = NULL;
+ node->ext = NULL;
+ }
+
node->row = row_build(ROW_COPY_DATA, clust_index, rec,
- offsets, NULL, &node->ext, node->heap);
+ offsets, NULL, ext, node->heap);
if (node->update) {
node->undo_row = dtuple_copy(node->row, node->heap);
row_upd_replace(node->undo_row, &node->undo_ext,
diff --git a/storage/xtradb/row/row0upd.c b/storage/xtradb/row/row0upd.c
index d0aaecd3dae..04c3139fcc7 100644
--- a/storage/xtradb/row/row0upd.c
+++ b/storage/xtradb/row/row0upd.c
@@ -1398,6 +1398,7 @@ row_upd_store_row(
dict_index_t* clust_index;
rec_t* rec;
mem_heap_t* heap = NULL;
+ row_ext_t** ext;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
const ulint* offsets;
rec_offs_init(offsets_);
@@ -1414,8 +1415,22 @@ row_upd_store_row(
offsets = rec_get_offsets(rec, clust_index, offsets_,
ULINT_UNDEFINED, &heap);
+
+ if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) {
+ /* In DYNAMIC or COMPRESSED format, there is no prefix
+ of externally stored columns in the clustered index
+ record. Build a cache of column prefixes. */
+ ext = &node->ext;
+ } else {
+ /* REDUNDANT and COMPACT formats store a local
+ 768-byte prefix of each externally stored column.
+ No cache is needed. */
+ ext = NULL;
+ node->ext = NULL;
+ }
+
node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
- NULL, &node->ext, node->heap);
+ NULL, ext, node->heap);
if (node->is_delete) {
node->upd_row = NULL;
node->upd_ext = NULL;
@@ -1583,6 +1598,7 @@ row_upd_clust_rec_by_insert(
dict_table_t* table;
dtuple_t* entry;
ulint err;
+ ibool change_ownership = FALSE;
ut_ad(node);
ut_ad(dict_index_is_clust(index));
@@ -1615,9 +1631,9 @@ row_upd_clust_rec_by_insert(
index = dict_table_get_first_index(table);
offsets = rec_get_offsets(rec, index, offsets_,
ULINT_UNDEFINED, &heap);
- btr_cur_mark_extern_inherited_fields(
- btr_cur_get_page_zip(btr_cur),
- rec, index, offsets, node->update, mtr);
+ change_ownership = btr_cur_mark_extern_inherited_fields(
+ btr_cur_get_page_zip(btr_cur), rec, index, offsets,
+ node->update, mtr);
if (check_ref) {
/* NOTE that the following call loses
the position of pcur ! */
@@ -1646,10 +1662,11 @@ row_upd_clust_rec_by_insert(
row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
- if (node->upd_ext) {
+ if (change_ownership) {
/* If we return from a lock wait, for example, we may have
extern fields marked as not-owned in entry (marked in the
- if-branch above). We must unmark them. */
+ if-branch above). We must unmark them, take the ownership
+ back. */
btr_cur_unmark_dtuple_extern_fields(entry);