summaryrefslogtreecommitdiff
path: root/storage/innobase/row/row0ins.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/row/row0ins.cc')
-rw-r--r--storage/innobase/row/row0ins.cc443
1 files changed, 245 insertions, 198 deletions
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index 2200a2092bd..e87e857c812 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2016, 2017, MariaDB Corporation.
+Copyright (c) 2016, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -45,7 +45,6 @@ Created 4/20/1996 Heikki Tuuri
#include "log0log.h"
#include "eval0eval.h"
#include "data0data.h"
-#include "usr0sess.h"
#include "buf0lru.h"
#include "fts0fts.h"
#include "fts0types.h"
@@ -139,49 +138,46 @@ row_ins_alloc_sys_fields(
{
dtuple_t* row;
dict_table_t* table;
- mem_heap_t* heap;
const dict_col_t* col;
dfield_t* dfield;
- byte* ptr;
row = node->row;
table = node->table;
- heap = node->entry_sys_heap;
- ut_ad(row && table && heap);
ut_ad(dtuple_get_n_fields(row) == dict_table_get_n_cols(table));
/* allocate buffer to hold the needed system created hidden columns. */
- const uint len = DATA_ROW_ID_LEN + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN;
- ptr = static_cast<byte*>(mem_heap_zalloc(heap, len));
+ compile_time_assert(DATA_ROW_ID_LEN
+ + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN
+ == sizeof node->sys_buf);
+ memset(node->sys_buf, 0, sizeof node->sys_buf);
+ /* Assign DB_ROLL_PTR to 1 << ROLL_PTR_INSERT_FLAG_POS */
+ node->sys_buf[DATA_ROW_ID_LEN + DATA_TRX_ID_LEN] = 0x80;
+ ut_ad(!memcmp(node->sys_buf + DATA_ROW_ID_LEN, reset_trx_id,
+ sizeof reset_trx_id));
/* 1. Populate row-id */
col = dict_table_get_sys_col(table, DATA_ROW_ID);
dfield = dtuple_get_nth_field(row, dict_col_get_no(col));
- dfield_set_data(dfield, ptr, DATA_ROW_ID_LEN);
-
- node->row_id_buf = ptr;
-
- ptr += DATA_ROW_ID_LEN;
+ dfield_set_data(dfield, node->sys_buf, DATA_ROW_ID_LEN);
/* 2. Populate trx id */
col = dict_table_get_sys_col(table, DATA_TRX_ID);
dfield = dtuple_get_nth_field(row, dict_col_get_no(col));
- dfield_set_data(dfield, ptr, DATA_TRX_ID_LEN);
-
- node->trx_id_buf = ptr;
-
- ptr += DATA_TRX_ID_LEN;
+ dfield_set_data(dfield, &node->sys_buf[DATA_ROW_ID_LEN],
+ DATA_TRX_ID_LEN);
col = dict_table_get_sys_col(table, DATA_ROLL_PTR);
dfield = dtuple_get_nth_field(row, dict_col_get_no(col));
- dfield_set_data(dfield, ptr, DATA_ROLL_PTR_LEN);
+ dfield_set_data(dfield, &node->sys_buf[DATA_ROW_ID_LEN
+ + DATA_TRX_ID_LEN],
+ DATA_ROLL_PTR_LEN);
}
/*********************************************************************//**
@@ -429,7 +425,7 @@ row_ins_cascade_ancestor_updates_table(
upd_node = static_cast<upd_node_t*>(parent);
- if (upd_node->table == table && upd_node->is_delete == FALSE) {
+ if (upd_node->table == table && !upd_node->is_delete) {
return(TRUE);
}
@@ -464,12 +460,9 @@ row_ins_cascade_n_ancestors(
/******************************************************************//**
Calculates the update vector node->cascade->update for a child table in
a cascaded update.
-@return number of fields in the calculated update vector; the value
-can also be 0 if no foreign key fields changed; the returned value is
-ULINT_UNDEFINED if the column type in the child table is too short to
-fit the new value in the parent table: that means the update fails */
+@return whether any FULLTEXT INDEX is affected */
static MY_ATTRIBUTE((nonnull, warn_unused_result))
-ulint
+bool
row_ins_cascade_calc_update_vec(
/*============================*/
upd_node_t* node, /*!< in: update node of the parent
@@ -478,11 +471,9 @@ row_ins_cascade_calc_update_vec(
type is != 0 */
mem_heap_t* heap, /*!< in: memory heap to use as
temporary storage */
- trx_t* trx, /*!< in: update transaction */
- ibool* fts_col_affected,
- /*!< out: is FTS column affected */
- upd_node_t* cascade) /*!< in: cascade update node */
+ trx_t* trx) /*!< in: update transaction */
{
+ upd_node_t* cascade = node->cascade_node;
dict_table_t* table = foreign->foreign_table;
dict_index_t* index = foreign->foreign_index;
upd_t* update;
@@ -493,7 +484,7 @@ row_ins_cascade_calc_update_vec(
ulint parent_field_no;
ulint i;
ulint j;
- ibool doc_id_updated = FALSE;
+ bool doc_id_updated = false;
ulint doc_id_pos = 0;
doc_id_t new_doc_id = FTS_NULL_DOC_ID;
ulint prefix_col;
@@ -520,7 +511,7 @@ row_ins_cascade_calc_update_vec(
n_fields_updated = 0;
- *fts_col_affected = FALSE;
+ bool affects_fulltext = false;
if (table->fts) {
doc_id_pos = dict_table_get_nth_col_pos(
@@ -572,8 +563,7 @@ row_ins_cascade_calc_update_vec(
if (dfield_is_null(&ufield->new_val)
&& (col->prtype & DATA_NOT_NULL)) {
-
- return(ULINT_UNDEFINED);
+ goto err_exit;
}
/* If the new value would not fit in the
@@ -581,15 +571,15 @@ row_ins_cascade_calc_update_vec(
if (!dfield_is_null(&ufield->new_val)
&& dtype_get_at_most_n_mbchars(
- col->prtype, col->mbminmaxlen,
+ col->prtype,
+ col->mbminlen, col->mbmaxlen,
col->len,
ufield_len,
static_cast<char*>(
dfield_get_data(
&ufield->new_val)))
< ufield_len) {
-
- return(ULINT_UNDEFINED);
+ goto err_exit;
}
/* If the parent column type has a different
@@ -633,7 +623,7 @@ row_ins_cascade_calc_update_vec(
col->prtype)
== DATA_MYSQL_BINARY_CHARSET_COLL) {
/* Do not pad BINARY columns */
- return(ULINT_UNDEFINED);
+ goto err_exit;
}
row_mysql_pad_col(mbminlen,
@@ -650,7 +640,7 @@ row_ins_cascade_calc_update_vec(
dict_col_get_no(col),
dict_col_is_virtual(col))
!= ULINT_UNDEFINED) {
- *fts_col_affected = TRUE;
+ affects_fulltext = true;
}
/* If Doc ID is updated, check whether the
@@ -667,11 +657,14 @@ row_ins_cascade_calc_update_vec(
dfield_get_data(
&ufield->new_val)));
+ affects_fulltext = true;
+ doc_id_updated = true;
+
if (new_doc_id <= 0) {
ib::error() << "FTS Doc ID"
" must be larger than"
" 0";
- return(ULINT_UNDEFINED);
+ goto err_exit;
}
if (new_doc_id < n_doc_id) {
@@ -680,12 +673,8 @@ row_ins_cascade_calc_update_vec(
<< n_doc_id - 1
<< " for table "
<< table->name;
-
- return(ULINT_UNDEFINED);
+ goto err_exit;
}
-
- *fts_col_affected = TRUE;
- doc_id_updated = TRUE;
}
n_fields_updated++;
@@ -693,8 +682,9 @@ row_ins_cascade_calc_update_vec(
}
}
- /* Generate a new Doc ID if FTS index columns get updated */
- if (table->fts && *fts_col_affected) {
+ if (affects_fulltext) {
+ ut_ad(table->fts);
+
if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)) {
doc_id_t doc_id;
doc_id_t* next_doc_id;
@@ -708,24 +698,25 @@ row_ins_cascade_calc_update_vec(
fts_get_next_doc_id(table, next_doc_id);
doc_id = fts_update_doc_id(table, ufield, next_doc_id);
n_fields_updated++;
- cascade->fts_next_doc_id = doc_id;
+ fts_trx_add_op(trx, table, doc_id, FTS_INSERT, NULL);
} else {
if (doc_id_updated) {
ut_ad(new_doc_id);
- cascade->fts_next_doc_id = new_doc_id;
+ fts_trx_add_op(trx, table, new_doc_id,
+ FTS_INSERT, NULL);
} else {
- cascade->fts_next_doc_id = FTS_NULL_DOC_ID;
ib::error() << "FTS Doc ID must be updated"
" along with FTS indexed column for"
" table " << table->name;
- return(ULINT_UNDEFINED);
+err_exit:
+ n_fields_updated = ULINT_UNDEFINED;
}
}
}
update->n_fields = n_fields_updated;
- return(n_fields_updated);
+ return affects_fulltext;
}
/*********************************************************************//**
@@ -779,8 +770,6 @@ row_ins_foreign_trx_print(
heap_size = mem_heap_get_size(trx->lock.lock_heap);
lock_mutex_exit();
- trx_sys_mutex_enter();
-
mutex_enter(&dict_foreign_err_mutex);
rewind(dict_foreign_err_file);
ut_print_timestamp(dict_foreign_err_file);
@@ -789,8 +778,6 @@ row_ins_foreign_trx_print(
trx_print_low(dict_foreign_err_file, trx, 600,
n_rec_locks, n_trx_locks, heap_size);
- trx_sys_mutex_exit();
-
ut_ad(mutex_own(&dict_foreign_err_mutex));
}
@@ -1079,13 +1066,10 @@ row_ins_foreign_check_on_constraint(
const rec_t* clust_rec;
const buf_block_t* clust_block;
upd_t* update;
- ulint n_to_update;
dberr_t err;
- ulint i;
trx_t* trx;
mem_heap_t* tmp_heap = NULL;
doc_id_t doc_id = FTS_NULL_DOC_ID;
- ibool fts_col_affacted = FALSE;
DBUG_ENTER("row_ins_foreign_check_on_constraint");
ut_a(thr);
@@ -1129,27 +1113,22 @@ row_ins_foreign_check_on_constraint(
DBUG_RETURN(DB_ROW_IS_REFERENCED);
}
- cascade = row_create_update_node_for_mysql(table, node->cascade_heap);
- que_node_set_parent(cascade, node);
-
- /* For the cascaded operation, all the update nodes are allocated in
- the same heap. All the update nodes will point to the same heap.
- This heap is owned by the first update node. And it must be freed
- only in the first update node */
- cascade->cascade_heap = node->cascade_heap;
- cascade->cascade_upd_nodes = node->cascade_upd_nodes;
- cascade->new_upd_nodes = node->new_upd_nodes;
- cascade->processed_cascades = node->processed_cascades;
+ if (node->cascade_node == NULL) {
+ node->cascade_heap = mem_heap_create(128);
+ node->cascade_node = row_create_update_node_for_mysql(
+ table, node->cascade_heap);
+ que_node_set_parent(node->cascade_node, node);
+ }
+ cascade = node->cascade_node;
cascade->table = table;
-
cascade->foreign = foreign;
if (node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE)) {
- cascade->is_delete = TRUE;
+ cascade->is_delete = PLAIN_DELETE;
} else {
- cascade->is_delete = FALSE;
+ cascade->is_delete = NO_DELETE;
if (foreign->n_fields > cascade->update_n_fields) {
/* We have to make the update vector longer */
@@ -1158,31 +1137,32 @@ row_ins_foreign_check_on_constraint(
node->cascade_heap);
cascade->update_n_fields = foreign->n_fields;
}
- }
- /* We do not allow cyclic cascaded updating (DELETE is allowed,
- but not UPDATE) of the same table, as this can lead to an infinite
- cycle. Check that we are not updating the same table which is
- already being modified in this cascade chain. We have to check
- this also because the modification of the indexes of a 'parent'
- table may still be incomplete, and we must avoid seeing the indexes
- of the parent table in an inconsistent state! */
+ /* We do not allow cyclic cascaded updating (DELETE is
+ allowed, but not UPDATE) of the same table, as this
+ can lead to an infinite cycle. Check that we are not
+ updating the same table which is already being
+ modified in this cascade chain. We have to check this
+ also because the modification of the indexes of a
+ 'parent' table may still be incomplete, and we must
+ avoid seeing the indexes of the parent table in an
+ inconsistent state! */
- if (!cascade->is_delete
- && row_ins_cascade_ancestor_updates_table(cascade, table)) {
+ if (row_ins_cascade_ancestor_updates_table(cascade, table)) {
- /* We do not know if this would break foreign key
- constraints, but play safe and return an error */
+ /* We do not know if this would break foreign key
+ constraints, but play safe and return an error */
- err = DB_ROW_IS_REFERENCED;
+ err = DB_ROW_IS_REFERENCED;
- row_ins_foreign_report_err(
- "Trying an update, possibly causing a cyclic"
- " cascaded update\n"
- "in the child table,", thr, foreign,
- btr_pcur_get_rec(pcur), entry);
+ row_ins_foreign_report_err(
+ "Trying an update, possibly causing a cyclic"
+ " cascaded update\n"
+ "in the child table,", thr, foreign,
+ btr_pcur_get_rec(pcur), entry);
- goto nonstandard_exit_func;
+ goto nonstandard_exit_func;
+ }
}
if (row_ins_cascade_n_ancestors(cascade) >= FK_MAX_CASCADE_DEL) {
@@ -1287,17 +1267,8 @@ row_ins_foreign_check_on_constraint(
if (node->is_delete
? (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
: (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)) {
-
/* Build the appropriate update vector which sets
foreign->n_fields first fields in rec to SQL NULL */
- if (table->fts) {
-
- /* For the clause ON DELETE SET NULL, the cascade
- operation is actually an update operation with the new
- values being null. For FTS, this means that the old
- values be deleted and no new values to be added.*/
- cascade->fts_next_doc_id = FTS_NULL_DOC_ID;
- }
update = cascade->update;
@@ -1306,7 +1277,9 @@ row_ins_foreign_check_on_constraint(
UNIV_MEM_INVALID(update->fields,
update->n_fields * sizeof *update->fields);
- for (i = 0; i < foreign->n_fields; i++) {
+ bool affects_fulltext = false;
+
+ for (ulint i = 0; i < foreign->n_fields; i++) {
upd_field_t* ufield = &update->fields[i];
ulint col_no = dict_index_get_nth_col_no(
index, i);
@@ -1322,18 +1295,19 @@ row_ins_foreign_check_on_constraint(
ufield->exp = NULL;
dfield_set_null(&ufield->new_val);
- if (table->fts && dict_table_is_fts_column(
- table->fts->indexes,
- dict_index_get_nth_col_no(index, i),
- dict_col_is_virtual(
- dict_index_get_nth_col(index, i)))
+ if (!affects_fulltext
+ && table->fts && dict_table_is_fts_column(
+ table->fts->indexes,
+ dict_index_get_nth_col_no(index, i),
+ dict_col_is_virtual(
+ dict_index_get_nth_col(index, i)))
!= ULINT_UNDEFINED) {
- fts_col_affacted = TRUE;
+ affects_fulltext = true;
}
}
- if (fts_col_affacted) {
- cascade->fts_doc_id = doc_id;
+ if (affects_fulltext) {
+ fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
}
if (foreign->v_cols != NULL
@@ -1346,21 +1320,24 @@ row_ins_foreign_check_on_constraint(
goto nonstandard_exit_func;
}
}
- } else if (table->fts && cascade->is_delete) {
+ } else if (table->fts && cascade->is_delete == PLAIN_DELETE) {
/* DICT_FOREIGN_ON_DELETE_CASCADE case */
- for (i = 0; i < foreign->n_fields; i++) {
- if (table->fts && dict_table_is_fts_column(
+ bool affects_fulltext = false;
+
+ for (ulint i = 0; i < foreign->n_fields; i++) {
+ if (dict_table_is_fts_column(
table->fts->indexes,
dict_index_get_nth_col_no(index, i),
dict_col_is_virtual(
dict_index_get_nth_col(index, i)))
!= ULINT_UNDEFINED) {
- fts_col_affacted = TRUE;
+ affects_fulltext = true;
+ break;
}
}
- if (fts_col_affacted) {
- cascade->fts_doc_id = doc_id;
+ if (affects_fulltext) {
+ fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
}
}
@@ -1370,13 +1347,10 @@ row_ins_foreign_check_on_constraint(
/* Build the appropriate update vector which sets changing
foreign->n_fields first fields in rec to new values */
- n_to_update = row_ins_cascade_calc_update_vec(
- node, foreign, cascade->cascade_heap,
- trx, &fts_col_affacted, cascade);
+ bool affects_fulltext = row_ins_cascade_calc_update_vec(
+ node, foreign, tmp_heap, trx);
-
- if (foreign->v_cols != NULL
- && foreign->v_cols->size() > 0) {
+ if (foreign->v_cols && !foreign->v_cols->empty()) {
row_ins_foreign_fill_virtual(
cascade, clust_rec, clust_index,
node, foreign, &err);
@@ -1386,7 +1360,8 @@ row_ins_foreign_check_on_constraint(
}
}
- if (n_to_update == ULINT_UNDEFINED) {
+ switch (cascade->update->n_fields) {
+ case ULINT_UNDEFINED:
err = DB_ROW_IS_REFERENCED;
row_ins_foreign_report_err(
@@ -1399,10 +1374,7 @@ row_ins_foreign_check_on_constraint(
thr, foreign, btr_pcur_get_rec(pcur), entry);
goto nonstandard_exit_func;
- }
-
- if (cascade->update->n_fields == 0) {
-
+ case 0:
/* The update does not change any columns referred
to in this foreign key constraint: no need to do
anything */
@@ -1413,9 +1385,9 @@ row_ins_foreign_check_on_constraint(
}
/* Mark the old Doc ID as deleted */
- if (fts_col_affacted) {
+ if (affects_fulltext) {
ut_ad(table->fts);
- cascade->fts_doc_id = doc_id;
+ fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
}
}
@@ -1437,28 +1409,19 @@ row_ins_foreign_check_on_constraint(
cascade->state = UPD_NODE_UPDATE_CLUSTERED;
#ifdef WITH_WSREP
- err = wsrep_append_foreign_key(
- thr_get_trx(thr),
- foreign,
- clust_rec,
- clust_index,
- FALSE, FALSE);
+ err = wsrep_append_foreign_key(trx, foreign, clust_rec, clust_index,
+ FALSE, FALSE);
if (err != DB_SUCCESS) {
fprintf(stderr,
"WSREP: foreign key append failed: %d\n", err);
} else
#endif /* WITH_WSREP */
- node->new_upd_nodes->push_back(cascade);
-
- my_atomic_addlint(&table->n_foreign_key_checks_running, 1);
-
- ut_ad(foreign->foreign_table->n_foreign_key_checks_running > 0);
+ err = row_update_cascade_for_mysql(thr, cascade,
+ foreign->foreign_table);
/* Release the data dictionary latch for a while, so that we do not
starve other threads from doing CREATE TABLE etc. if we have a huge
- cascaded operation running. The counter n_foreign_key_checks_running
- will prevent other users from dropping or ALTERing the table when we
- release the latch. */
+ cascaded operation running. */
row_mysql_unfreeze_data_dictionary(thr_get_trx(thr));
@@ -1479,7 +1442,6 @@ row_ins_foreign_check_on_constraint(
DBUG_RETURN(err);
nonstandard_exit_func:
- que_graph_free_recursive(cascade);
if (tmp_heap) {
mem_heap_free(tmp_heap);
@@ -1557,17 +1519,6 @@ row_ins_set_exclusive_rec_lock(
return(err);
}
-/* Decrement a counter in the destructor. */
-class ib_dec_in_dtor {
-public:
- ib_dec_in_dtor(ulint& c): counter(c) {}
- ~ib_dec_in_dtor() {
- my_atomic_addlint(&counter, -1);
- }
-private:
- ulint& counter;
-};
-
/***************************************************************//**
Checks if foreign key constraint fails for an index entry. Sets shared locks
which lock either the success or the failure of the constraint. NOTE that
@@ -1625,8 +1576,14 @@ row_ins_check_foreign_constraint(
/* If any of the foreign key fields in entry is SQL NULL, we
suppress the foreign key check: this is compatible with Oracle,
for example */
- for (ulint i = 0; i < foreign->n_fields; i++) {
- if (dfield_is_null(dtuple_get_nth_field(entry, i))) {
+ for (ulint i = 0; i < entry->n_fields; i++) {
+ dfield_t* field = dtuple_get_nth_field(entry, i);
+ if (i < foreign->n_fields && dfield_is_null(field)) {
+ goto exit_func;
+ }
+ /* System Versioning: if row_end != Inf, we
+ suppress the foreign key check */
+ if (field->type.vers_sys_end() && field->vers_history_row()) {
goto exit_func;
}
}
@@ -1634,7 +1591,8 @@ row_ins_check_foreign_constraint(
if (que_node_get_type(thr->run_node) == QUE_NODE_UPDATE) {
upd_node = static_cast<upd_node_t*>(thr->run_node);
- if (!(upd_node->is_delete) && upd_node->foreign == foreign) {
+ if (upd_node->is_delete != PLAIN_DELETE
+ && upd_node->foreign == foreign) {
/* If a cascaded update is done as defined by a
foreign key constraint, do not check that
constraint for the child row. In ON UPDATE CASCADE
@@ -1757,6 +1715,23 @@ row_ins_check_foreign_constraint(
cmp = cmp_dtuple_rec(entry, rec, offsets);
if (cmp == 0) {
+ if (check_table->versioned()) {
+ bool history_row = false;
+
+ if (check_index->is_primary()) {
+ history_row = check_index->
+ vers_history_row(rec, offsets);
+ } else if (check_index->
+ vers_history_row(rec, history_row))
+ {
+ break;
+ }
+
+ if (history_row) {
+ continue;
+ }
+ }
+
if (rec_get_deleted_flag(rec,
rec_offs_comp(offsets))) {
/* In delete-marked records, DB_TRX_ID must
@@ -1892,19 +1867,13 @@ end_scan:
do_possible_lock_wait:
if (err == DB_LOCK_WAIT) {
- /* An object that will correctly decrement the FK check counter
- when it goes out of this scope. */
- ib_dec_in_dtor dec(check_table->n_foreign_key_checks_running);
-
trx->error_state = err;
que_thr_stop_for_mysql(thr);
thr->lock_state = QUE_THR_LOCK_ROW;
- /* To avoid check_table being dropped, increment counter */
- my_atomic_addlint(
- &check_table->n_foreign_key_checks_running, 1);
+ check_table->inc_fk_checks();
trx_kill_blocking(trx);
@@ -1912,9 +1881,12 @@ do_possible_lock_wait:
thr->lock_state = QUE_THR_LOCK_NOLOCK;
- err = check_table->to_be_dropped
- ? DB_LOCK_WAIT_TIMEOUT
- : trx->error_state;
+ if (check_table->to_be_dropped
+ || trx->error_state == DB_LOCK_WAIT_TIMEOUT) {
+ err = DB_LOCK_WAIT_TIMEOUT;
+ }
+
+ check_table->dec_fk_checks();
}
exit_func:
@@ -1975,6 +1947,10 @@ row_ins_check_foreign_constraints(
row_mysql_freeze_data_dictionary(trx);
}
+ if (referenced_table) {
+ foreign->foreign_table->inc_fk_checks();
+ }
+
/* NOTE that if the thread ends up waiting for a lock
we will release dict_operation_lock temporarily!
But the counter on the table protects the referenced
@@ -1983,6 +1959,10 @@ row_ins_check_foreign_constraints(
err = row_ins_check_foreign_constraint(
TRUE, foreign, table, entry, thr);
+ if (referenced_table) {
+ foreign->foreign_table->dec_fk_checks();
+ }
+
if (got_s_lock) {
row_mysql_unfreeze_data_dictionary(trx);
}
@@ -2898,21 +2878,18 @@ row_ins_sec_index_entry_low(
cursor.rtr_info = NULL;
ut_ad(thr_get_trx(thr)->id != 0);
- mtr_start(&mtr);
- mtr.set_named_space(index->space);
-
- if (dict_table_is_temporary(index->table)) {
- /* Disable REDO logging as the lifetime of temp-tables is
- limited to server or connection lifetime and so REDO
- information is not needed on restart for recovery.
- Disable locking as temp-tables are local to a connection. */
+ mtr.start();
+ if (index->table->is_temporary()) {
+ /* Disable locking, because temporary tables are never
+ shared between transactions or connections. */
ut_ad(flags & BTR_NO_LOCKING_FLAG);
mtr.set_log_mode(MTR_LOG_NO_REDO);
- } else if (!dict_index_is_spatial(index)) {
- /* Enable insert buffering if it's neither temp-table
- nor spatial index. */
- search_mode |= BTR_INSERT;
+ } else {
+ mtr.set_named_space(index->space);
+ if (!dict_index_is_spatial(index)) {
+ search_mode |= BTR_INSERT;
+ }
}
/* Ensure that we acquire index->lock when inserting into an
@@ -2984,10 +2961,8 @@ row_ins_sec_index_entry_low(
}
if (err != DB_SUCCESS) {
- trx_t* trx = thr_get_trx(thr);
-
if (err == DB_DECRYPTION_FAILED) {
- ib_push_warning(trx->mysql_thd,
+ ib_push_warning(thr_get_trx(thr)->mysql_thd,
DB_DECRYPTION_FAILED,
"Table %s is encrypted but encryption service or"
" used key_id is not available. "
@@ -3230,14 +3205,25 @@ row_ins_clust_index_entry(
n_uniq = dict_index_is_unique(index) ? index->n_uniq : 0;
- const ulint flags = index->table->is_temporary()
- ? BTR_NO_LOCKING_FLAG
- : index->table->no_rollback() ? BTR_NO_ROLLBACK : 0;
+ ulint flags = index->table->no_rollback() ? BTR_NO_ROLLBACK
+ : dict_table_is_temporary(index->table)
+ ? BTR_NO_LOCKING_FLAG : 0;
const ulint orig_n_fields = entry->n_fields;
/* Try first optimistic descent to the B-tree */
log_free_check();
+ /* For intermediate table during copy alter table,
+ skip the undo log and record lock checking for
+ insertion operation.
+ */
+ if (index->table->skip_alter_undo) {
+ flags |= BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG;
+ }
+
+ /* Try first optimistic descent to the B-tree */
+ log_free_check();
+
err = row_ins_clust_index_entry_low(
flags, BTR_MODIFY_LEAF, index, n_uniq, entry,
n_ext, thr, dup_chk_only);
@@ -3283,6 +3269,7 @@ row_ins_sec_index_entry(
dberr_t err;
mem_heap_t* offsets_heap;
mem_heap_t* heap;
+ trx_id_t trx_id = 0;
DBUG_EXECUTE_IF("row_ins_sec_index_entry_timeout", {
DBUG_SET("-d,row_ins_sec_index_entry_timeout");
@@ -3305,13 +3292,22 @@ row_ins_sec_index_entry(
/* Try first optimistic descent to the B-tree */
log_free_check();
- const ulint flags = dict_table_is_temporary(index->table)
+ ulint flags = dict_table_is_temporary(index->table)
? BTR_NO_LOCKING_FLAG
: 0;
+ /* For intermediate table during copy alter table,
+ skip the undo log and record lock checking for
+ insertion operation.
+ */
+ if (index->table->skip_alter_undo) {
+ trx_id = thr_get_trx(thr)->id;
+ flags |= BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG;
+ }
+
err = row_ins_sec_index_entry_low(
flags, BTR_MODIFY_LEAF, index, offsets_heap, heap, entry,
- 0, thr, dup_chk_only);
+ trx_id, thr, dup_chk_only);
if (err == DB_FAIL) {
mem_heap_empty(heap);
@@ -3348,13 +3344,13 @@ row_ins_index_entry(
dtuple_t* entry, /*!< in/out: index entry to insert */
que_thr_t* thr) /*!< in: query thread */
{
- ut_ad(thr_get_trx(thr)->id != 0);
+ ut_ad(thr_get_trx(thr)->id || index->table->no_rollback());
DBUG_EXECUTE_IF("row_ins_index_entry_timeout", {
DBUG_SET("-d,row_ins_index_entry_timeout");
return(DB_LOCK_WAIT);});
- if (index->is_clust()) {
+ if (index->is_primary()) {
return(row_ins_clust_index_entry(index, entry, thr, 0, false));
} else {
return(row_ins_sec_index_entry(index, entry, thr, false));
@@ -3449,7 +3445,7 @@ row_ins_index_entry_set_vals(
= dict_field_get_col(ind_field);
len = dtype_get_at_most_n_mbchars(
- col->prtype, col->mbminmaxlen,
+ col->prtype, col->mbminlen, col->mbmaxlen,
ind_field->prefix_len,
len,
static_cast<const char*>(
@@ -3536,7 +3532,7 @@ row_ins_alloc_row_id_step(
row_id = dict_sys_get_new_row_id();
- dict_sys_write_row_id(node->row_id_buf, row_id);
+ dict_sys_write_row_id(node->sys_buf, row_id);
}
/***********************************************************//**
@@ -3652,6 +3648,11 @@ row_ins(
switch (err) {
case DB_SUCCESS:
break;
+ case DB_NO_REFERENCED_ROW:
+ if (!dict_index_is_unique(node->index)) {
+ DBUG_RETURN(err);
+ }
+ /* fall through */
case DB_DUPLICATE_KEY:
ut_ad(dict_index_is_unique(node->index));
@@ -3668,7 +3669,55 @@ row_ins(
secondary indexes to block concurrent
transactions from inserting the
searched records. */
- if (!node->duplicate) {
+ if (err == DB_NO_REFERENCED_ROW
+ && node->duplicate) {
+ /* A foreign key check on a
+ unique index may fail to
+ find the record.
+
+ Consider as a example
+ following:
+ create table child(a int not null
+ primary key, b int not null,
+ c int,
+ unique key (b),
+ foreign key (b) references
+ parent (id)) engine=innodb;
+
+ insert into child values
+ (1,1,2);
+
+ insert into child(a) values
+ (1) on duplicate key update
+ c = 3;
+
+ Now primary key value 1
+ naturally causes duplicate
+ key error that will be
+ stored on node->duplicate.
+ If there was no duplicate
+ key error, we should return
+ the actual no referenced
+ row error.
+
+ As value for
+ column b used in both unique
+ key and foreign key is not
+ provided, server uses 0 as a
+ search value. This is
+ naturally, not found leading
+ to DB_NO_REFERENCED_ROW.
+ But, we should update the
+ row with primay key value 1
+ anyway.
+
+ Return the
+ original DB_DUPLICATE_KEY
+ error after
+ placing all gaplocks. */
+ err = DB_DUPLICATE_KEY;
+ break;
+ } else if (!node->duplicate) {
/* Save 1st dup error. Ignore
subsequent dup errors. */
node->duplicate = node->index;
@@ -3755,8 +3804,6 @@ row_ins_step(
trx = thr_get_trx(thr);
- trx_start_if_not_started_xa(trx, true);
-
node = static_cast<ins_node_t*>(thr->run_node);
ut_ad(que_node_get_type(node) == QUE_NODE_INSERT);
@@ -3776,7 +3823,7 @@ row_ins_step(
This happens, for example, when a row update moves it to another
partition. In that case, we have already set the IX lock on the
table during the search operation, and there is no need to set
- it again here. But we must write trx->id to node->trx_id_buf. */
+ it again here. But we must write trx->id to node->sys_buf. */
if (node->table->no_rollback()) {
/* No-rollback tables should only be written to by a
@@ -3791,15 +3838,15 @@ row_ins_step(
restarting here. In theory, we could allow resumption
from the INS_NODE_INSERT_ENTRIES state here. */
DBUG_ASSERT(node->state == INS_NODE_SET_IX_LOCK);
- memset(node->trx_id_buf, 0, DATA_TRX_ID_LEN);
- memset(node->row_id_buf, 0, DATA_ROW_ID_LEN);
node->index = dict_table_get_first_index(node->table);
node->entry = UT_LIST_GET_FIRST(node->entry_list);
node->state = INS_NODE_INSERT_ENTRIES;
goto do_insert;
}
- trx_write_trx_id(node->trx_id_buf, trx->id);
+ if (UNIV_LIKELY(!node->table->skip_alter_undo)) {
+ trx_write_trx_id(&node->sys_buf[DATA_ROW_ID_LEN], trx->id);
+ }
if (node->state == INS_NODE_SET_IX_LOCK) {