summaryrefslogtreecommitdiff
path: root/storage/xtradb/row/row0upd.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/xtradb/row/row0upd.cc')
-rw-r--r--storage/xtradb/row/row0upd.cc3017
1 files changed, 0 insertions, 3017 deletions
diff --git a/storage/xtradb/row/row0upd.cc b/storage/xtradb/row/row0upd.cc
deleted file mode 100644
index 1156cbe4b4c..00000000000
--- a/storage/xtradb/row/row0upd.cc
+++ /dev/null
@@ -1,3017 +0,0 @@
-/*****************************************************************************
-
-Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-
-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
-Foundation; version 2 of the License.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
-
-*****************************************************************************/
-
-/**************************************************//**
-@file row/row0upd.cc
-Update of a row
-
-Created 12/27/1996 Heikki Tuuri
-*******************************************************/
-
-#include "m_string.h" /* for my_sys.h */
-#include "my_sys.h" /* DEBUG_SYNC_C */
-#include "row0upd.h"
-
-#ifdef UNIV_NONINL
-#include "row0upd.ic"
-#endif
-
-#include "ha_prototypes.h"
-#include "dict0dict.h"
-#include "trx0undo.h"
-#include "rem0rec.h"
-#ifndef UNIV_HOTBACKUP
-#include "dict0boot.h"
-#include "dict0crea.h"
-#include "mach0data.h"
-#include "btr0btr.h"
-#include "btr0cur.h"
-#include "que0que.h"
-#include "row0ext.h"
-#include "row0ins.h"
-#include "row0log.h"
-#include "row0row.h"
-#include "row0sel.h"
-#include "rem0cmp.h"
-#include "lock0lock.h"
-#include "log0log.h"
-#include "pars0sym.h"
-#include "eval0eval.h"
-#include "buf0lru.h"
-#include <algorithm>
-
-#include <mysql/plugin.h>
-#include <mysql/service_wsrep.h>
-
-/* What kind of latch and lock can we assume when the control comes to
- -------------------------------------------------------------------
-an update node?
---------------
-Efficiency of massive updates would require keeping an x-latch on a
-clustered index page through many updates, and not setting an explicit
-x-lock on clustered index records, as they anyway will get an implicit
-x-lock when they are updated. A problem is that the read nodes in the
-graph should know that they must keep the latch when passing the control
-up to the update node, and not set any record lock on the record which
-will be updated. Another problem occurs if the execution is stopped,
-as the kernel switches to another query thread, or the transaction must
-wait for a lock. Then we should be able to release the latch and, maybe,
-acquire an explicit x-lock on the record.
- Because this seems too complicated, we conclude that the less
-efficient solution of releasing all the latches when the control is
-transferred to another node, and acquiring explicit x-locks, is better. */
-
-/* How is a delete performed? If there is a delete without an
-explicit cursor, i.e., a searched delete, there are at least
-two different situations:
-the implicit select cursor may run on (1) the clustered index or
-on (2) a secondary index. The delete is performed by setting
-the delete bit in the record and substituting the id of the
-deleting transaction for the original trx id, and substituting a
-new roll ptr for previous roll ptr. The old trx id and roll ptr
-are saved in the undo log record. Thus, no physical changes occur
-in the index tree structure at the time of the delete. Only
-when the undo log is purged, the index records will be physically
-deleted from the index trees.
-
-The query graph executing a searched delete would consist of
-a delete node which has as a subtree a select subgraph.
-The select subgraph should return a (persistent) cursor
-in the clustered index, placed on page which is x-latched.
-The delete node should look for all secondary index records for
-this clustered index entry and mark them as deleted. When is
-the x-latch freed? The most efficient way for performing a
-searched delete is obviously to keep the x-latch for several
-steps of query graph execution. */
-
-/*************************************************************************
-IMPORTANT NOTE: Any operation that generates redo MUST check that there
-is enough space in the redo log before for that operation. This is
-done by calling log_free_check(). The reason for checking the
-availability of the redo log space before the start of the operation is
-that we MUST not hold any synchonization objects when performing the
-check.
-If you make a change in this module make sure that no codepath is
-introduced where a call to log_free_check() is bypassed. */
-
-/***********************************************************//**
-Checks if an update vector changes some of the first ordering fields of an
-index record. This is only used in foreign key checks and we can assume
-that index does not contain column prefixes.
-@return TRUE if changes */
-static
-ibool
-row_upd_changes_first_fields_binary(
-/*================================*/
- dtuple_t* entry, /*!< in: old value of index entry */
- dict_index_t* index, /*!< in: index of entry */
- const upd_t* update, /*!< in: update vector for the row */
- ulint n); /*!< in: how many first fields to check */
-
-
-/*********************************************************************//**
-Checks if index currently is mentioned as a referenced index in a foreign
-key constraint.
-
-NOTE that since we do not hold dict_operation_lock when leaving the
-function, it may be that the referencing table has been dropped when
-we leave this function: this function is only for heuristic use!
-
-@return TRUE if referenced */
-static
-ibool
-row_upd_index_is_referenced(
-/*========================*/
- dict_index_t* index, /*!< in: index */
- trx_t* trx) /*!< in: transaction */
-{
- dict_table_t* table = index->table;
- ibool froze_data_dict = FALSE;
- ibool is_referenced = FALSE;
-
- if (table->referenced_set.empty()) {
- return(FALSE);
- }
-
- if (trx->dict_operation_lock_mode == 0) {
- row_mysql_freeze_data_dictionary(trx);
- froze_data_dict = TRUE;
- }
-
- dict_foreign_set::iterator it
- = std::find_if(table->referenced_set.begin(),
- table->referenced_set.end(),
- dict_foreign_with_index(index));
-
- is_referenced = (it != table->referenced_set.end());
-
- if (froze_data_dict) {
- row_mysql_unfreeze_data_dictionary(trx);
- }
-
- return(is_referenced);
-}
-
-#ifdef WITH_WSREP
-static
-ibool
-wsrep_row_upd_index_is_foreign(
-/*========================*/
- dict_index_t* index, /*!< in: index */
- trx_t* trx) /*!< in: transaction */
-{
- dict_table_t* table = index->table;
- dict_foreign_t* foreign;
- ibool froze_data_dict = FALSE;
- ibool is_referenced = FALSE;
-
- if (table->foreign_set.empty()) {
-
- return(FALSE);
- }
-
- if (trx->dict_operation_lock_mode == 0) {
- row_mysql_freeze_data_dictionary(trx);
- froze_data_dict = TRUE;
- }
-
- for (dict_foreign_set::iterator it= table->foreign_set.begin();
- it != table->foreign_set.end();
- ++ it)
- {
- foreign= *it;
-
- if (foreign->foreign_index == index) {
-
- is_referenced = TRUE;
- goto func_exit;
- }
-
- }
-
-func_exit:
- if (froze_data_dict) {
- row_mysql_unfreeze_data_dictionary(trx);
- }
-
- return(is_referenced);
-}
-#endif /* WITH_WSREP */
-
-/*********************************************************************//**
-Checks if possible foreign key constraints hold after a delete of the record
-under pcur.
-
-NOTE that this function will temporarily commit mtr and lose the
-pcur position!
-
-@return DB_SUCCESS or an error code */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd_check_references_constraints(
-/*=================================*/
- upd_node_t* node, /*!< in: row update node */
- btr_pcur_t* pcur, /*!< in: cursor positioned on a record; NOTE: the
- cursor position is lost in this function! */
- dict_table_t* table, /*!< in: table in question */
- dict_index_t* index, /*!< in: index of the cursor */
- ulint* offsets,/*!< in/out: rec_get_offsets(pcur.rec, index) */
- que_thr_t* thr, /*!< in: query thread */
- mtr_t* mtr) /*!< in: mtr */
-{
- dict_foreign_t* foreign;
- mem_heap_t* heap;
- dtuple_t* entry;
- trx_t* trx;
- const rec_t* rec;
- ulint n_ext;
- dberr_t err;
- ibool got_s_lock = FALSE;
-
- if (table->referenced_set.empty()) {
-
- return(DB_SUCCESS);
- }
-
- trx = thr_get_trx(thr);
-
- rec = btr_pcur_get_rec(pcur);
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- heap = mem_heap_create(500);
-
- entry = row_rec_to_index_entry(rec, index, offsets, &n_ext, heap);
-
- mtr_commit(mtr);
-
- DEBUG_SYNC_C("foreign_constraint_check_for_update");
-
- mtr_start_trx(mtr, trx);
-
- if (trx->dict_operation_lock_mode == 0) {
- got_s_lock = TRUE;
-
- row_mysql_freeze_data_dictionary(trx);
- }
-
-run_again:
-
- for (dict_foreign_set::iterator it = table->referenced_set.begin();
- it != table->referenced_set.end();
- ++it) {
-
- foreign = *it;
-
- /* Note that we may have an update which updates the index
- record, but does NOT update the first fields which are
- referenced in a foreign key constraint. Then the update does
- NOT break the constraint. */
-
- if (foreign->referenced_index == index
- && (node->is_delete
- || row_upd_changes_first_fields_binary(
- entry, index, node->update,
- foreign->n_fields))) {
- dict_table_t* foreign_table = foreign->foreign_table;
-
- dict_table_t* ref_table = NULL;
-
- if (foreign_table == NULL) {
-
- ref_table = dict_table_open_on_name(
- foreign->foreign_table_name_lookup,
- FALSE, FALSE, DICT_ERR_IGNORE_NONE);
- }
-
- if (foreign_table) {
- os_inc_counter(dict_sys->mutex,
- foreign_table
- ->n_foreign_key_checks_running);
- }
-
- /* 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 'foreign' from
- being dropped while the check is running. */
-
- err = row_ins_check_foreign_constraint(
- FALSE, foreign, table, entry, thr);
-
- if (foreign_table) {
- os_dec_counter(dict_sys->mutex,
- foreign_table
- ->n_foreign_key_checks_running);
- }
-
- if (ref_table != NULL) {
- dict_table_close(ref_table, FALSE, FALSE);
- }
-
- /* Some table foreign key dropped, try again */
- if (err == DB_DICT_CHANGED) {
- goto run_again;
- } else if (err != DB_SUCCESS) {
- goto func_exit;
- }
- }
- }
-
- err = DB_SUCCESS;
-func_exit:
- if (got_s_lock) {
- row_mysql_unfreeze_data_dictionary(trx);
- }
-
- mem_heap_free(heap);
-
- return(err);
-}
-#ifdef WITH_WSREP
-static
-dberr_t
-wsrep_row_upd_check_foreign_constraints(
-/*=================================*/
- upd_node_t* node, /*!< in: row update node */
- btr_pcur_t* pcur, /*!< in: cursor positioned on a record; NOTE: the
- cursor position is lost in this function! */
- dict_table_t* table, /*!< in: table in question */
- dict_index_t* index, /*!< in: index of the cursor */
- ulint* offsets,/*!< in/out: rec_get_offsets(pcur.rec, index) */
- que_thr_t* thr, /*!< in: query thread */
- mtr_t* mtr) /*!< in: mtr */
-{
- dict_foreign_t* foreign;
- mem_heap_t* heap;
- dtuple_t* entry;
- trx_t* trx;
- const rec_t* rec;
- ulint n_ext;
- dberr_t err;
- ibool got_s_lock = FALSE;
- ibool opened = FALSE;
-
- if (table->foreign_set.empty()) {
-
- return(DB_SUCCESS);
- }
-
- trx = thr_get_trx(thr);
-
- /* TODO: make native slave thread bail out here */
-
- rec = btr_pcur_get_rec(pcur);
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- heap = mem_heap_create(500);
-
- entry = row_rec_to_index_entry(rec, index, offsets,
- &n_ext, heap);
-
- mtr_commit(mtr);
-
- mtr_start(mtr);
-
- if (trx->dict_operation_lock_mode == 0) {
- got_s_lock = TRUE;
-
- row_mysql_freeze_data_dictionary(trx);
- }
-
- for (dict_foreign_set::iterator it= table->foreign_set.begin();
- it != table->foreign_set.end();
- ++ it)
- {
- foreign= *it;
-
-
- /* Note that we may have an update which updates the index
- record, but does NOT update the first fields which are
- referenced in a foreign key constraint. Then the update does
- NOT break the constraint. */
-
- if (foreign->foreign_index == index
- && (node->is_delete
- || row_upd_changes_first_fields_binary(
- entry, index, node->update,
- foreign->n_fields))) {
-
- if (foreign->referenced_table == NULL) {
- foreign->referenced_table =
- dict_table_open_on_name(
- foreign->referenced_table_name_lookup,
- FALSE, FALSE, DICT_ERR_IGNORE_NONE);
- opened = (foreign->referenced_table) ? TRUE : FALSE;
- }
-
- if (foreign->referenced_table) {
- os_inc_counter(dict_sys->mutex,
- foreign->referenced_table
- ->n_foreign_key_checks_running);
- }
-
- /* 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 'foreign' from
- being dropped while the check is running. */
-
- err = row_ins_check_foreign_constraint(
- TRUE, foreign, table, entry, thr);
-
- if (foreign->referenced_table) {
- os_dec_counter(dict_sys->mutex,
- foreign->referenced_table
- ->n_foreign_key_checks_running);
-
- if (opened == TRUE) {
- dict_table_close(foreign->referenced_table, FALSE, FALSE);
- opened = FALSE;
- }
- }
-
- if (err != DB_SUCCESS) {
-
- goto func_exit;
- }
- }
-
- }
-
- err = DB_SUCCESS;
-func_exit:
- if (got_s_lock) {
- row_mysql_unfreeze_data_dictionary(trx);
- }
-
- mem_heap_free(heap);
-
- DEBUG_SYNC_C("foreign_constraint_check_for_update_done");
-
- return(err);
-}
-#endif /* WITH_WSREP */
-
-/*********************************************************************//**
-Creates an update node for a query graph.
-@return own: update node */
-UNIV_INTERN
-upd_node_t*
-upd_node_create(
-/*============*/
- mem_heap_t* heap) /*!< in: mem heap where created */
-{
- upd_node_t* node;
-
- node = static_cast<upd_node_t*>(
- mem_heap_alloc(heap, sizeof(upd_node_t)));
-
- node->common.type = QUE_NODE_UPDATE;
-
- node->state = UPD_NODE_UPDATE_CLUSTERED;
- node->in_mysql_interface = FALSE;
-
- node->row = NULL;
- node->ext = NULL;
- node->upd_row = NULL;
- node->upd_ext = NULL;
- node->index = NULL;
- node->update = NULL;
-
- node->foreign = NULL;
- node->cascade_heap = NULL;
- node->cascade_node = NULL;
-
- node->select = NULL;
-
- node->heap = mem_heap_create(128);
- node->magic_n = UPD_NODE_MAGIC_N;
-
- node->cmpl_info = 0;
-
- return(node);
-}
-#endif /* !UNIV_HOTBACKUP */
-
-/*********************************************************************//**
-Updates the trx id and roll ptr field in a clustered index record in database
-recovery. */
-UNIV_INTERN
-void
-row_upd_rec_sys_fields_in_recovery(
-/*===============================*/
- rec_t* rec, /*!< in/out: record */
- page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
- const ulint* offsets,/*!< in: array returned by rec_get_offsets() */
- ulint pos, /*!< in: TRX_ID position in rec */
- trx_id_t trx_id, /*!< in: transaction id */
- roll_ptr_t roll_ptr)/*!< in: roll ptr of the undo log record */
-{
- ut_ad(rec_offs_validate(rec, NULL, offsets));
-
- if (page_zip) {
- page_zip_write_trx_id_and_roll_ptr(
- page_zip, rec, offsets, pos, trx_id, roll_ptr);
- } else {
- byte* field;
- ulint len;
-
- field = rec_get_nth_field(rec, offsets, pos, &len);
- ut_ad(len == DATA_TRX_ID_LEN);
-#if DATA_TRX_ID + 1 != DATA_ROLL_PTR
-# error "DATA_TRX_ID + 1 != DATA_ROLL_PTR"
-#endif
- trx_write_trx_id(field, trx_id);
- trx_write_roll_ptr(field + DATA_TRX_ID_LEN, roll_ptr);
- }
-}
-
-#ifndef UNIV_HOTBACKUP
-/*********************************************************************//**
-Sets the trx id or roll ptr field of a clustered index entry. */
-UNIV_INTERN
-void
-row_upd_index_entry_sys_field(
-/*==========================*/
- dtuple_t* entry, /*!< in/out: index entry, where the memory
- buffers for sys fields are already allocated:
- the function just copies the new values to
- them */
- dict_index_t* index, /*!< in: clustered index */
- ulint type, /*!< in: DATA_TRX_ID or DATA_ROLL_PTR */
- ib_uint64_t val) /*!< in: value to write */
-{
- dfield_t* dfield;
- byte* field;
- ulint pos;
-
- ut_ad(dict_index_is_clust(index));
-
- pos = dict_index_get_sys_col_pos(index, type);
-
- dfield = dtuple_get_nth_field(entry, pos);
- field = static_cast<byte*>(dfield_get_data(dfield));
-
- if (type == DATA_TRX_ID) {
- trx_write_trx_id(field, val);
- } else {
- ut_ad(type == DATA_ROLL_PTR);
- trx_write_roll_ptr(field, val);
- }
-}
-
-/***********************************************************//**
-Returns TRUE if row update changes size of some field in index or if some
-field to be updated is stored externally in rec or update.
-@return TRUE if the update changes the size of some field in index or
-the field is external in rec or update */
-UNIV_INTERN
-ibool
-row_upd_changes_field_size_or_external(
-/*===================================*/
- dict_index_t* index, /*!< in: index */
- const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
- const upd_t* update) /*!< in: update vector */
-{
- const upd_field_t* upd_field;
- const dfield_t* new_val;
- ulint old_len;
- ulint new_len;
- ulint n_fields;
- ulint i;
-
- ut_ad(rec_offs_validate(NULL, index, offsets));
- n_fields = upd_get_n_fields(update);
-
- for (i = 0; i < n_fields; i++) {
- upd_field = upd_get_nth_field(update, i);
-
- new_val = &(upd_field->new_val);
- new_len = dfield_get_len(new_val);
-
- if (dfield_is_null(new_val) && !rec_offs_comp(offsets)) {
- /* A bug fixed on Dec 31st, 2004: we looked at the
- SQL NULL size from the wrong field! We may backport
- this fix also to 4.0. The merge to 5.0 will be made
- manually immediately after we commit this to 4.1. */
-
- new_len = dict_col_get_sql_null_size(
- dict_index_get_nth_col(index,
- upd_field->field_no),
- 0);
- }
-
- old_len = rec_offs_nth_size(offsets, upd_field->field_no);
-
- if (rec_offs_comp(offsets)
- && rec_offs_nth_sql_null(offsets,
- upd_field->field_no)) {
- /* Note that in the compact table format, for a
- variable length field, an SQL NULL will use zero
- bytes in the offset array at the start of the physical
- record, but a zero-length value (empty string) will
- use one byte! Thus, we cannot use update-in-place
- if we update an SQL NULL varchar to an empty string! */
-
- old_len = UNIV_SQL_NULL;
- }
-
- if (dfield_is_ext(new_val) || old_len != new_len
- || rec_offs_nth_extern(offsets, upd_field->field_no)) {
-
- return(TRUE);
- }
- }
-
- return(FALSE);
-}
-
-/***********************************************************//**
-Returns true if row update contains disowned external fields.
-@return true if the update contains disowned external fields. */
-UNIV_INTERN
-bool
-row_upd_changes_disowned_external(
-/*==============================*/
- const upd_t* update) /*!< in: update vector */
-{
- const upd_field_t* upd_field;
- const dfield_t* new_val;
- ulint new_len;
- ulint n_fields;
- ulint i;
-
- n_fields = upd_get_n_fields(update);
-
- for (i = 0; i < n_fields; i++) {
- const byte* field_ref;
-
- upd_field = upd_get_nth_field(update, i);
- new_val = &(upd_field->new_val);
- new_len = dfield_get_len(new_val);
-
- if (!dfield_is_ext(new_val)) {
- continue;
- }
-
- ut_ad(new_len >= BTR_EXTERN_FIELD_REF_SIZE);
-
- field_ref = static_cast<const byte*>(dfield_get_data(new_val))
- + new_len - BTR_EXTERN_FIELD_REF_SIZE;
-
- if (field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG) {
- return(true);
- }
- }
-
- return(false);
-}
-#endif /* !UNIV_HOTBACKUP */
-
-/***********************************************************//**
-Replaces the new column values stored in the update vector to the
-record given. No field size changes are allowed. This function is
-usually invoked on a clustered index. The only use case for a
-secondary index is row_ins_sec_index_entry_by_modify() or its
-counterpart in ibuf_insert_to_index_page(). */
-UNIV_INTERN
-void
-row_upd_rec_in_place(
-/*=================*/
- rec_t* rec, /*!< in/out: record where replaced */
- dict_index_t* index, /*!< in: the index the record belongs to */
- const ulint* offsets,/*!< in: array returned by rec_get_offsets() */
- const upd_t* update, /*!< in: update vector */
- page_zip_des_t* page_zip)/*!< in: compressed page with enough space
- available, or NULL */
-{
- const upd_field_t* upd_field;
- const dfield_t* new_val;
- ulint n_fields;
- ulint i;
-
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- if (rec_offs_comp(offsets)) {
- rec_set_info_bits_new(rec, update->info_bits);
- } else {
- rec_set_info_bits_old(rec, update->info_bits);
- }
-
- n_fields = upd_get_n_fields(update);
-
- for (i = 0; i < n_fields; i++) {
-#ifdef UNIV_BLOB_DEBUG
- btr_blob_dbg_t b;
- const byte* field_ref = NULL;
-#endif /* UNIV_BLOB_DEBUG */
-
- upd_field = upd_get_nth_field(update, i);
- new_val = &(upd_field->new_val);
- ut_ad(!dfield_is_ext(new_val) ==
- !rec_offs_nth_extern(offsets, upd_field->field_no));
-#ifdef UNIV_BLOB_DEBUG
- if (dfield_is_ext(new_val)) {
- ulint len;
- field_ref = rec_get_nth_field(rec, offsets, i, &len);
- ut_a(len != UNIV_SQL_NULL);
- ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
- field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
-
- b.ref_page_no = page_get_page_no(page_align(rec));
- b.ref_heap_no = page_rec_get_heap_no(rec);
- b.ref_field_no = i;
- b.blob_page_no = mach_read_from_4(
- field_ref + BTR_EXTERN_PAGE_NO);
- ut_a(b.ref_field_no >= index->n_uniq);
- btr_blob_dbg_rbt_delete(index, &b, "upd_in_place");
- }
-#endif /* UNIV_BLOB_DEBUG */
-
- rec_set_nth_field(rec, offsets, upd_field->field_no,
- dfield_get_data(new_val),
- dfield_get_len(new_val));
-
-#ifdef UNIV_BLOB_DEBUG
- if (dfield_is_ext(new_val)) {
- b.blob_page_no = mach_read_from_4(
- field_ref + BTR_EXTERN_PAGE_NO);
- b.always_owner = b.owner = !(field_ref[BTR_EXTERN_LEN]
- & BTR_EXTERN_OWNER_FLAG);
- b.del = rec_get_deleted_flag(
- rec, rec_offs_comp(offsets));
-
- btr_blob_dbg_rbt_insert(index, &b, "upd_in_place");
- }
-#endif /* UNIV_BLOB_DEBUG */
- }
-
- if (page_zip) {
- page_zip_write_rec(page_zip, rec, index, offsets, 0);
- }
-}
-
-#ifndef UNIV_HOTBACKUP
-/*********************************************************************//**
-Writes into the redo log the values of trx id and roll ptr and enough info
-to determine their positions within a clustered index record.
-@return new pointer to mlog */
-UNIV_INTERN
-byte*
-row_upd_write_sys_vals_to_log(
-/*==========================*/
- dict_index_t* index, /*!< in: clustered index */
- trx_id_t trx_id, /*!< in: transaction id */
- roll_ptr_t roll_ptr,/*!< in: roll ptr of the undo log record */
- byte* log_ptr,/*!< pointer to a buffer of size > 20 opened
- in mlog */
- mtr_t* mtr MY_ATTRIBUTE((unused))) /*!< in: mtr */
-{
- ut_ad(dict_index_is_clust(index));
- ut_ad(mtr);
-
- log_ptr += mach_write_compressed(log_ptr,
- dict_index_get_sys_col_pos(
- index, DATA_TRX_ID));
-
- trx_write_roll_ptr(log_ptr, roll_ptr);
- log_ptr += DATA_ROLL_PTR_LEN;
-
- log_ptr += mach_ull_write_compressed(log_ptr, trx_id);
-
- return(log_ptr);
-}
-#endif /* !UNIV_HOTBACKUP */
-
-/*********************************************************************//**
-Parses the log data of system field values.
-@return log data end or NULL */
-UNIV_INTERN
-byte*
-row_upd_parse_sys_vals(
-/*===================*/
- byte* ptr, /*!< in: buffer */
- byte* end_ptr,/*!< in: buffer end */
- ulint* pos, /*!< out: TRX_ID position in record */
- trx_id_t* trx_id, /*!< out: trx id */
- roll_ptr_t* roll_ptr)/*!< out: roll ptr */
-{
- ptr = mach_parse_compressed(ptr, end_ptr, pos);
-
- if (ptr == NULL) {
-
- return(NULL);
- }
-
- if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {
-
- return(NULL);
- }
-
- *roll_ptr = trx_read_roll_ptr(ptr);
- ptr += DATA_ROLL_PTR_LEN;
-
- ptr = mach_ull_parse_compressed(ptr, end_ptr, trx_id);
-
- return(ptr);
-}
-
-#ifndef UNIV_HOTBACKUP
-/***********************************************************//**
-Writes to the redo log the new values of the fields occurring in the index. */
-UNIV_INTERN
-void
-row_upd_index_write_log(
-/*====================*/
- const upd_t* update, /*!< in: update vector */
- byte* log_ptr,/*!< in: pointer to mlog buffer: must
- contain at least MLOG_BUF_MARGIN bytes
- of free space; the buffer is closed
- within this function */
- mtr_t* mtr) /*!< in: mtr into whose log to write */
-{
- const upd_field_t* upd_field;
- const dfield_t* new_val;
- ulint len;
- ulint n_fields;
- byte* buf_end;
- ulint i;
-
- n_fields = upd_get_n_fields(update);
-
- buf_end = log_ptr + MLOG_BUF_MARGIN;
-
- mach_write_to_1(log_ptr, update->info_bits);
- log_ptr++;
- log_ptr += mach_write_compressed(log_ptr, n_fields);
-
- for (i = 0; i < n_fields; i++) {
-
-#if MLOG_BUF_MARGIN <= 30
-# error "MLOG_BUF_MARGIN <= 30"
-#endif
-
- if (log_ptr + 30 > buf_end) {
- mlog_close(mtr, log_ptr);
-
- log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
- buf_end = log_ptr + MLOG_BUF_MARGIN;
- }
-
- upd_field = upd_get_nth_field(update, i);
-
- new_val = &(upd_field->new_val);
-
- len = dfield_get_len(new_val);
-
- log_ptr += mach_write_compressed(log_ptr, upd_field->field_no);
- log_ptr += mach_write_compressed(log_ptr, len);
-
- if (len != UNIV_SQL_NULL) {
- if (log_ptr + len < buf_end) {
- memcpy(log_ptr, dfield_get_data(new_val), len);
-
- log_ptr += len;
- } else {
- mlog_close(mtr, log_ptr);
-
- mlog_catenate_string(
- mtr,
- static_cast<byte*>(
- dfield_get_data(new_val)),
- len);
-
- log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
- buf_end = log_ptr + MLOG_BUF_MARGIN;
- }
- }
- }
-
- mlog_close(mtr, log_ptr);
-}
-#endif /* !UNIV_HOTBACKUP */
-
-/*********************************************************************//**
-Parses the log data written by row_upd_index_write_log.
-@return log data end or NULL */
-UNIV_INTERN
-byte*
-row_upd_index_parse(
-/*================*/
- byte* ptr, /*!< in: buffer */
- byte* end_ptr,/*!< in: buffer end */
- mem_heap_t* heap, /*!< in: memory heap where update vector is
- built */
- upd_t** update_out)/*!< out: update vector */
-{
- upd_t* update;
- upd_field_t* upd_field;
- dfield_t* new_val;
- ulint len;
- ulint n_fields;
- ulint info_bits;
- ulint i;
-
- if (end_ptr < ptr + 1) {
-
- return(NULL);
- }
-
- info_bits = mach_read_from_1(ptr);
- ptr++;
- ptr = mach_parse_compressed(ptr, end_ptr, &n_fields);
-
- if (ptr == NULL) {
-
- return(NULL);
- }
-
- update = upd_create(n_fields, heap);
- update->info_bits = info_bits;
-
- for (i = 0; i < n_fields; i++) {
- ulint field_no;
- upd_field = upd_get_nth_field(update, i);
- new_val = &(upd_field->new_val);
-
- ptr = mach_parse_compressed(ptr, end_ptr, &field_no);
-
- if (ptr == NULL) {
-
- return(NULL);
- }
-
- upd_field->field_no = field_no;
-
- ptr = mach_parse_compressed(ptr, end_ptr, &len);
-
- if (ptr == NULL) {
-
- return(NULL);
- }
-
- if (len != UNIV_SQL_NULL) {
-
- if (end_ptr < ptr + len) {
-
- return(NULL);
- }
-
- dfield_set_data(new_val,
- mem_heap_dup(heap, ptr, len), len);
- ptr += len;
- } else {
- dfield_set_null(new_val);
- }
- }
-
- *update_out = update;
-
- return(ptr);
-}
-
-#ifndef UNIV_HOTBACKUP
-/***************************************************************//**
-Builds an update vector from those fields which in a secondary index entry
-differ from a record that has the equal ordering fields. NOTE: we compare
-the fields as binary strings!
-@return own: update vector of differing fields */
-UNIV_INTERN
-upd_t*
-row_upd_build_sec_rec_difference_binary(
-/*====================================*/
- const rec_t* rec, /*!< in: secondary index record */
- dict_index_t* index, /*!< in: index */
- const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
- const dtuple_t* entry, /*!< in: entry to insert */
- mem_heap_t* heap) /*!< in: memory heap from which allocated */
-{
- upd_field_t* upd_field;
- const dfield_t* dfield;
- const byte* data;
- ulint len;
- upd_t* update;
- ulint n_diff;
- ulint i;
-
- /* This function is used only for a secondary index */
- ut_a(!dict_index_is_clust(index));
- ut_ad(rec_offs_validate(rec, index, offsets));
- ut_ad(rec_offs_n_fields(offsets) == dtuple_get_n_fields(entry));
- ut_ad(!rec_offs_any_extern(offsets));
-
- update = upd_create(dtuple_get_n_fields(entry), heap);
-
- n_diff = 0;
-
- for (i = 0; i < dtuple_get_n_fields(entry); i++) {
-
- data = rec_get_nth_field(rec, offsets, i, &len);
-
- dfield = dtuple_get_nth_field(entry, i);
-
- /* NOTE that it may be that len != dfield_get_len(dfield) if we
- are updating in a character set and collation where strings of
- different length can be equal in an alphabetical comparison,
- and also in the case where we have a column prefix index
- and the last characters in the index field are spaces; the
- latter case probably caused the assertion failures reported at
- row0upd.cc line 713 in versions 4.0.14 - 4.0.16. */
-
- /* NOTE: we compare the fields as binary strings!
- (No collation) */
-
- if (!dfield_data_is_binary_equal(dfield, len, data)) {
-
- upd_field = upd_get_nth_field(update, n_diff);
-
- dfield_copy(&(upd_field->new_val), dfield);
-
- upd_field_set_field_no(upd_field, i, index, NULL);
-
- n_diff++;
- }
- }
-
- update->n_fields = n_diff;
-
- return(update);
-}
-
-/***************************************************************//**
-Builds an update vector from those fields, excluding the roll ptr and
-trx id fields, which in an index entry differ from a record that has
-the equal ordering fields. NOTE: we compare the fields as binary strings!
-@return own: update vector of differing fields, excluding roll ptr and
-trx id */
-UNIV_INTERN
-const upd_t*
-row_upd_build_difference_binary(
-/*============================*/
- dict_index_t* index, /*!< in: clustered index */
- const dtuple_t* entry, /*!< in: entry to insert */
- const rec_t* rec, /*!< in: clustered index record */
- const ulint* offsets,/*!< in: rec_get_offsets(rec,index), or NULL */
- bool no_sys, /*!< in: skip the system columns
- DB_TRX_ID and DB_ROLL_PTR */
- trx_t* trx, /*!< in: transaction */
- mem_heap_t* heap) /*!< in: memory heap from which allocated */
-{
- upd_field_t* upd_field;
- const dfield_t* dfield;
- const byte* data;
- ulint len;
- upd_t* update;
- ulint n_diff;
- ulint trx_id_pos;
- ulint i;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- rec_offs_init(offsets_);
-
- /* This function is used only for a clustered index */
- ut_a(dict_index_is_clust(index));
-
- update = upd_create(dtuple_get_n_fields(entry), heap);
-
- n_diff = 0;
-
- trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
- ut_ad(dict_index_get_sys_col_pos(index, DATA_ROLL_PTR)
- == trx_id_pos + 1);
-
- if (!offsets) {
- offsets = rec_get_offsets(rec, index, offsets_,
- ULINT_UNDEFINED, &heap);
- } else {
- ut_ad(rec_offs_validate(rec, index, offsets));
- }
-
- for (i = 0; i < dtuple_get_n_fields(entry); i++) {
-
- data = rec_get_nth_field(rec, offsets, i, &len);
-
- dfield = dtuple_get_nth_field(entry, i);
-
- /* NOTE: we compare the fields as binary strings!
- (No collation) */
-
- if (no_sys && (i == trx_id_pos || i == trx_id_pos + 1)) {
-
- continue;
- }
-
- if (!dfield_is_ext(dfield)
- != !rec_offs_nth_extern(offsets, i)
- || !dfield_data_is_binary_equal(dfield, len, data)) {
-
- upd_field = upd_get_nth_field(update, n_diff);
-
- dfield_copy(&(upd_field->new_val), dfield);
-
- upd_field_set_field_no(upd_field, i, index, trx);
-
- n_diff++;
- }
- }
-
- update->n_fields = n_diff;
-
- return(update);
-}
-
-/***********************************************************//**
-Fetch a prefix of an externally stored column. This is similar
-to row_ext_lookup(), but the row_ext_t holds the old values
-of the column and must not be poisoned with the new values.
-@return BLOB prefix */
-static
-byte*
-row_upd_ext_fetch(
-/*==============*/
- const byte* data, /*!< in: 'internally' stored part of the
- field containing also the reference to
- the external part */
- ulint local_len, /*!< in: length of data, in bytes */
- ulint zip_size, /*!< in: nonzero=compressed BLOB
- page size, zero for uncompressed
- BLOBs */
- ulint* len, /*!< in: length of prefix to fetch;
- out: fetched length of the prefix */
- mem_heap_t* heap) /*!< in: heap where to allocate */
-{
- byte* buf = static_cast<byte*>(mem_heap_alloc(heap, *len));
-
- *len = btr_copy_externally_stored_field_prefix(
- buf, *len, zip_size, data, local_len, NULL);
-
- /* We should never update records containing a half-deleted BLOB. */
- ut_a(*len);
-
- return(buf);
-}
-
-/***********************************************************//**
-Replaces the new column value stored in the update vector in
-the given index entry field. */
-static
-void
-row_upd_index_replace_new_col_val(
-/*==============================*/
- dfield_t* dfield, /*!< in/out: data field
- of the index entry */
- const dict_field_t* field, /*!< in: index field */
- const dict_col_t* col, /*!< in: field->col */
- const upd_field_t* uf, /*!< in: update field */
- mem_heap_t* heap, /*!< in: memory heap for allocating
- and copying the new value */
- ulint zip_size)/*!< in: compressed page
- size of the table, or 0 */
-{
- ulint len;
- const byte* data;
-
- dfield_copy_data(dfield, &uf->new_val);
-
- if (dfield_is_null(dfield)) {
- return;
- }
-
- len = dfield_get_len(dfield);
- data = static_cast<const byte*>(dfield_get_data(dfield));
-
- if (field->prefix_len > 0) {
- ibool fetch_ext = dfield_is_ext(dfield)
- && len < (ulint) field->prefix_len
- + BTR_EXTERN_FIELD_REF_SIZE;
-
- if (fetch_ext) {
- ulint l = len;
-
- len = field->prefix_len;
-
- data = row_upd_ext_fetch(data, l, zip_size,
- &len, heap);
- }
-
- len = dtype_get_at_most_n_mbchars(col->prtype,
- col->mbminmaxlen,
- field->prefix_len, len,
- (const char*) data);
-
- dfield_set_data(dfield, data, len);
-
- if (!fetch_ext) {
- dfield_dup(dfield, heap);
- }
-
- return;
- }
-
- switch (uf->orig_len) {
- byte* buf;
- case BTR_EXTERN_FIELD_REF_SIZE:
- /* Restore the original locally stored
- part of the column. In the undo log,
- InnoDB writes a longer prefix of externally
- stored columns, so that column prefixes
- in secondary indexes can be reconstructed. */
- dfield_set_data(dfield,
- data + len - BTR_EXTERN_FIELD_REF_SIZE,
- BTR_EXTERN_FIELD_REF_SIZE);
- dfield_set_ext(dfield);
- /* fall through */
- case 0:
- dfield_dup(dfield, heap);
- break;
- default:
- /* Reconstruct the original locally
- stored part of the column. The data
- will have to be copied. */
- ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
- buf = static_cast<byte*>(mem_heap_alloc(heap, uf->orig_len));
-
- /* Copy the locally stored prefix. */
- memcpy(buf, data,
- uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE);
-
- /* Copy the BLOB pointer. */
- memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE,
- data + len - BTR_EXTERN_FIELD_REF_SIZE,
- BTR_EXTERN_FIELD_REF_SIZE);
-
- dfield_set_data(dfield, buf, uf->orig_len);
- dfield_set_ext(dfield);
- break;
- }
-}
-
-/***********************************************************//**
-Replaces the new column values stored in the update vector to the index entry
-given. */
-UNIV_INTERN
-void
-row_upd_index_replace_new_col_vals_index_pos(
-/*=========================================*/
- dtuple_t* entry, /*!< in/out: index entry where replaced;
- the clustered index record must be
- covered by a lock or a page latch to
- prevent deletion (rollback or purge) */
- dict_index_t* index, /*!< in: index; NOTE that this may also be a
- non-clustered index */
- const upd_t* update, /*!< in: an update vector built for the index so
- that the field number in an upd_field is the
- index position */
- ibool order_only,
- /*!< in: if TRUE, limit the replacement to
- ordering fields of index; note that this
- does not work for non-clustered indexes. */
- mem_heap_t* heap) /*!< in: memory heap for allocating and
- copying the new values */
-{
- ulint i;
- ulint n_fields;
- const ulint zip_size = dict_table_zip_size(index->table);
-
- dtuple_set_info_bits(entry, update->info_bits);
-
- if (order_only) {
- n_fields = dict_index_get_n_unique(index);
- } else {
- n_fields = dict_index_get_n_fields(index);
- }
-
- for (i = 0; i < n_fields; i++) {
- const dict_field_t* field;
- const dict_col_t* col;
- const upd_field_t* uf;
-
- field = dict_index_get_nth_field(index, i);
- col = dict_field_get_col(field);
- uf = upd_get_field_by_field_no(update, i);
-
- if (uf) {
- row_upd_index_replace_new_col_val(
- dtuple_get_nth_field(entry, i),
- field, col, uf, heap, zip_size);
- }
- }
-}
-
-/***********************************************************//**
-Replaces the new column values stored in the update vector to the index entry
-given. */
-UNIV_INTERN
-void
-row_upd_index_replace_new_col_vals(
-/*===============================*/
- dtuple_t* entry, /*!< in/out: index entry where replaced;
- the clustered index record must be
- covered by a lock or a page latch to
- prevent deletion (rollback or purge) */
- dict_index_t* index, /*!< in: index; NOTE that this may also be a
- non-clustered index */
- const upd_t* update, /*!< in: an update vector built for the
- CLUSTERED index so that the field number in
- an upd_field is the clustered index position */
- mem_heap_t* heap) /*!< in: memory heap for allocating and
- copying the new values */
-{
- ulint i;
- const dict_index_t* clust_index
- = dict_table_get_first_index(index->table);
- const ulint zip_size
- = dict_table_zip_size(index->table);
-
- dtuple_set_info_bits(entry, update->info_bits);
-
- for (i = 0; i < dict_index_get_n_fields(index); i++) {
- const dict_field_t* field;
- const dict_col_t* col;
- const upd_field_t* uf;
-
- field = dict_index_get_nth_field(index, i);
- col = dict_field_get_col(field);
- uf = upd_get_field_by_field_no(
- update, dict_col_get_clust_pos(col, clust_index));
-
- if (uf) {
- row_upd_index_replace_new_col_val(
- dtuple_get_nth_field(entry, i),
- field, col, uf, heap, zip_size);
- }
- }
-}
-
-/***********************************************************//**
-Replaces the new column values stored in the update vector. */
-UNIV_INTERN
-void
-row_upd_replace(
-/*============*/
- dtuple_t* row, /*!< in/out: row where replaced,
- indexed by col_no;
- the clustered index record must be
- covered by a lock or a page latch to
- prevent deletion (rollback or purge) */
- row_ext_t** ext, /*!< out, own: NULL, or externally
- stored column prefixes */
- const dict_index_t* index, /*!< in: clustered index */
- const upd_t* update, /*!< in: an update vector built for the
- clustered index */
- mem_heap_t* heap) /*!< in: memory heap */
-{
- ulint col_no;
- ulint i;
- ulint n_cols;
- ulint n_ext_cols;
- ulint* ext_cols;
- const dict_table_t* table;
-
- ut_ad(row);
- ut_ad(ext);
- ut_ad(index);
- ut_ad(dict_index_is_clust(index));
- ut_ad(update);
- ut_ad(heap);
-
- n_cols = dtuple_get_n_fields(row);
- table = index->table;
- ut_ad(n_cols == dict_table_get_n_cols(table));
-
- ext_cols = static_cast<ulint*>(
- mem_heap_alloc(heap, n_cols * sizeof *ext_cols));
-
- n_ext_cols = 0;
-
- dtuple_set_info_bits(row, update->info_bits);
-
- for (col_no = 0; col_no < n_cols; col_no++) {
-
- const dict_col_t* col
- = dict_table_get_nth_col(table, col_no);
- const ulint clust_pos
- = dict_col_get_clust_pos(col, index);
- dfield_t* dfield;
-
- if (UNIV_UNLIKELY(clust_pos == ULINT_UNDEFINED)) {
-
- continue;
- }
-
- dfield = dtuple_get_nth_field(row, col_no);
-
- for (i = 0; i < upd_get_n_fields(update); i++) {
-
- const upd_field_t* upd_field
- = upd_get_nth_field(update, i);
-
- if (upd_field->field_no != clust_pos) {
-
- continue;
- }
-
- dfield_copy_data(dfield, &upd_field->new_val);
- break;
- }
-
- if (dfield_is_ext(dfield) && col->ord_part) {
- ext_cols[n_ext_cols++] = col_no;
- }
- }
-
- if (n_ext_cols) {
- *ext = row_ext_create(n_ext_cols, ext_cols, table->flags, row,
- heap);
- } else {
- *ext = NULL;
- }
-}
-
-/***********************************************************//**
-Checks if an update vector changes an ordering field of an index record.
-
-This function is fast if the update vector is short or the number of ordering
-fields in the index is small. Otherwise, this can be quadratic.
-NOTE: we compare the fields as binary strings!
-@return TRUE if update vector changes an ordering field in the index record */
-UNIV_INTERN
-ibool
-row_upd_changes_ord_field_binary_func(
-/*==================================*/
- dict_index_t* index, /*!< in: index of the record */
- const upd_t* update, /*!< in: update vector for the row; NOTE: the
- field numbers in this MUST be clustered index
- positions! */
-#ifdef UNIV_DEBUG
- const que_thr_t*thr, /*!< in: query thread */
-#endif /* UNIV_DEBUG */
- const dtuple_t* row, /*!< in: old value of row, or NULL if the
- row and the data values in update are not
- known when this function is called, e.g., at
- compile time */
- const row_ext_t*ext) /*!< NULL, or prefixes of the externally
- stored columns in the old row */
-{
- ulint n_unique;
- ulint i;
- const dict_index_t* clust_index;
-
- ut_ad(thr);
- ut_ad(thr->graph);
- ut_ad(thr->graph->trx);
-
- n_unique = dict_index_get_n_unique(index);
-
- clust_index = dict_table_get_first_index(index->table);
-
- for (i = 0; i < n_unique; i++) {
-
- const dict_field_t* ind_field;
- const dict_col_t* col;
- ulint col_no;
- const upd_field_t* upd_field;
- const dfield_t* dfield;
- dfield_t dfield_ext;
- ulint dfield_len= 0;
- const byte* buf;
-
- ind_field = dict_index_get_nth_field(index, i);
- col = dict_field_get_col(ind_field);
- col_no = dict_col_get_no(col);
-
- upd_field = upd_get_field_by_field_no(
- update, dict_col_get_clust_pos(col, clust_index));
-
- if (upd_field == NULL) {
- continue;
- }
-
- if (row == NULL) {
- ut_ad(ext == NULL);
- return(TRUE);
- }
-
- dfield = dtuple_get_nth_field(row, col_no);
-
- /* This treatment of column prefix indexes is loosely
- based on row_build_index_entry(). */
-
- if (UNIV_LIKELY(ind_field->prefix_len == 0)
- || dfield_is_null(dfield)) {
- /* do nothing special */
- } else if (ext) {
- /* Silence a compiler warning without
- silencing a Valgrind error. */
- dfield_len = 0;
- UNIV_MEM_INVALID(&dfield_len, sizeof dfield_len);
- /* See if the column is stored externally. */
- buf = row_ext_lookup(ext, col_no, &dfield_len);
-
- ut_ad(col->ord_part);
-
- if (UNIV_LIKELY_NULL(buf)) {
- if (UNIV_UNLIKELY(buf == field_ref_zero)) {
- /* The externally stored field
- was not written yet. This
- record should only be seen by
- recv_recovery_rollback_active(),
- when the server had crashed before
- storing the field. */
- ut_ad(thr->graph->trx->is_recovered);
- ut_ad(trx_is_recv(thr->graph->trx));
- return(TRUE);
- }
-
- goto copy_dfield;
- }
- } else if (dfield_is_ext(dfield)) {
- dfield_len = dfield_get_len(dfield);
- ut_a(dfield_len > BTR_EXTERN_FIELD_REF_SIZE);
- dfield_len -= BTR_EXTERN_FIELD_REF_SIZE;
- ut_a(dict_index_is_clust(index)
- || ind_field->prefix_len <= dfield_len);
-
- buf = static_cast<byte*>(dfield_get_data(dfield));
-copy_dfield:
- ut_a(dfield_len > 0);
- dfield_copy(&dfield_ext, dfield);
- dfield_set_data(&dfield_ext, buf, dfield_len);
- dfield = &dfield_ext;
- }
-
- if (!dfield_datas_are_binary_equal(
- dfield, &upd_field->new_val,
- ind_field->prefix_len)) {
-
- return(TRUE);
- }
- }
-
- return(FALSE);
-}
-
-/***********************************************************//**
-Checks if an update vector changes an ordering field of an index record.
-NOTE: we compare the fields as binary strings!
-@return TRUE if update vector may change an ordering field in an index
-record */
-UNIV_INTERN
-ibool
-row_upd_changes_some_index_ord_field_binary(
-/*========================================*/
- const dict_table_t* table, /*!< in: table */
- const upd_t* update) /*!< in: update vector for the row */
-{
- upd_field_t* upd_field;
- dict_index_t* index;
- ulint i;
-
- index = dict_table_get_first_index(table);
-
- for (i = 0; i < upd_get_n_fields(update); i++) {
-
- upd_field = upd_get_nth_field(update, i);
-
- if (dict_field_get_col(dict_index_get_nth_field(
- index, upd_field->field_no))
- ->ord_part) {
-
- return(TRUE);
- }
- }
-
- return(FALSE);
-}
-
-/***********************************************************//**
-Checks if an FTS Doc ID column is affected by an UPDATE.
-@return whether the Doc ID column is changed */
-UNIV_INTERN
-bool
-row_upd_changes_doc_id(
-/*===================*/
- dict_table_t* table, /*!< in: table */
- upd_field_t* upd_field) /*!< in: field to check */
-{
- ulint col_no;
- dict_index_t* clust_index;
- fts_t* fts = table->fts;
-
- clust_index = dict_table_get_first_index(table);
-
- /* Convert from index-specific column number to table-global
- column number. */
- col_no = dict_index_get_nth_col_no(clust_index, upd_field->field_no);
-
- return(col_no == fts->doc_col);
-}
-/***********************************************************//**
-Checks if an FTS indexed column is affected by an UPDATE.
-@return offset within fts_t::indexes if FTS indexed column updated else
-ULINT_UNDEFINED */
-UNIV_INTERN
-ulint
-row_upd_changes_fts_column(
-/*=======================*/
- dict_table_t* table, /*!< in: table */
- upd_field_t* upd_field) /*!< in: field to check */
-{
- ulint col_no;
- dict_index_t* clust_index;
- fts_t* fts = table->fts;
-
- clust_index = dict_table_get_first_index(table);
-
- /* Convert from index-specific column number to table-global
- column number. */
- col_no = dict_index_get_nth_col_no(clust_index, upd_field->field_no);
-
- return(dict_table_is_fts_column(fts->indexes, col_no));
-}
-
-/***********************************************************//**
-Checks if an update vector changes some of the first ordering fields of an
-index record. This is only used in foreign key checks and we can assume
-that index does not contain column prefixes.
-@return TRUE if changes */
-static
-ibool
-row_upd_changes_first_fields_binary(
-/*================================*/
- dtuple_t* entry, /*!< in: index entry */
- dict_index_t* index, /*!< in: index of entry */
- const upd_t* update, /*!< in: update vector for the row */
- ulint n) /*!< in: how many first fields to check */
-{
- ulint n_upd_fields;
- ulint i, j;
- dict_index_t* clust_index;
-
- ut_ad(update && index);
- ut_ad(n <= dict_index_get_n_fields(index));
-
- n_upd_fields = upd_get_n_fields(update);
- clust_index = dict_table_get_first_index(index->table);
-
- for (i = 0; i < n; i++) {
-
- const dict_field_t* ind_field;
- const dict_col_t* col;
- ulint col_pos;
-
- ind_field = dict_index_get_nth_field(index, i);
- col = dict_field_get_col(ind_field);
- col_pos = dict_col_get_clust_pos(col, clust_index);
-
- ut_a(ind_field->prefix_len == 0);
-
- for (j = 0; j < n_upd_fields; j++) {
-
- upd_field_t* upd_field
- = upd_get_nth_field(update, j);
-
- if (col_pos == upd_field->field_no
- && !dfield_datas_are_binary_equal(
- dtuple_get_nth_field(entry, i),
- &upd_field->new_val, 0)) {
-
- return(TRUE);
- }
- }
- }
-
- return(FALSE);
-}
-
-/*********************************************************************//**
-Copies the column values from a record. */
-UNIV_INLINE
-void
-row_upd_copy_columns(
-/*=================*/
- rec_t* rec, /*!< in: record in a clustered index */
- const ulint* offsets,/*!< in: array returned by rec_get_offsets() */
- sym_node_t* column) /*!< in: first column in a column list, or
- NULL */
-{
- byte* data;
- ulint len;
-
- while (column) {
- data = rec_get_nth_field(rec, offsets,
- column->field_nos[SYM_CLUST_FIELD_NO],
- &len);
- eval_node_copy_and_alloc_val(column, data, len);
-
- column = UT_LIST_GET_NEXT(col_var_list, column);
- }
-}
-
-/*********************************************************************//**
-Calculates the new values for fields to update. Note that row_upd_copy_columns
-must have been called first. */
-UNIV_INLINE
-void
-row_upd_eval_new_vals(
-/*==================*/
- upd_t* update) /*!< in/out: update vector */
-{
- que_node_t* exp;
- upd_field_t* upd_field;
- ulint n_fields;
- ulint i;
-
- n_fields = upd_get_n_fields(update);
-
- for (i = 0; i < n_fields; i++) {
- upd_field = upd_get_nth_field(update, i);
-
- exp = upd_field->exp;
-
- eval_exp(exp);
-
- dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
- }
-}
-
-/***********************************************************//**
-Stores to the heap the row on which the node->pcur is positioned. */
-static
-void
-row_upd_store_row(
-/*==============*/
- upd_node_t* node) /*!< in: row update node */
-{
- 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_);
-
- ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
-
- if (node->row != NULL) {
- mem_heap_empty(node->heap);
- }
-
- clust_index = dict_table_get_first_index(node->table);
-
- rec = btr_pcur_get_rec(node->pcur);
-
- offsets = rec_get_offsets(rec, clust_index, offsets_,
- ULINT_UNDEFINED, &heap);
-
- if (dict_table_get_format(node->table) >= UNIV_FORMAT_B) {
- /* 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, NULL, NULL, ext, node->heap);
- if (node->is_delete) {
- node->upd_row = NULL;
- node->upd_ext = NULL;
- } else {
- node->upd_row = dtuple_copy(node->row, node->heap);
- row_upd_replace(node->upd_row, &node->upd_ext,
- clust_index, node->update, node->heap);
- }
-
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
-}
-
-/***********************************************************//**
-Updates a secondary index entry of a row.
-@return DB_SUCCESS if operation successfully completed, else error
-code or DB_LOCK_WAIT */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd_sec_index_entry(
-/*====================*/
- upd_node_t* node, /*!< in: row update node */
- que_thr_t* thr) /*!< in: query thread */
-{
- mtr_t mtr;
- const rec_t* rec;
- btr_pcur_t pcur;
- mem_heap_t* heap;
- dtuple_t* entry;
- dict_index_t* index;
- btr_cur_t* btr_cur;
- ibool referenced;
- dberr_t err = DB_SUCCESS;
- trx_t* trx = thr_get_trx(thr);
- ulint mode;
- enum row_search_result search_result;
-
- ut_ad(trx->id);
-
- index = node->index;
-
- referenced = row_upd_index_is_referenced(index, trx);
-#ifdef WITH_WSREP
- ibool foreign = wsrep_row_upd_index_is_foreign(index, trx);
-#endif /* WITH_WSREP */
-
- heap = mem_heap_create(1024);
-
- /* Build old index entry */
- entry = row_build_index_entry(node->row, node->ext, index, heap);
- ut_a(entry);
-
- log_free_check();
-
-#ifdef UNIV_DEBUG
- /* Work around Bug#14626800 ASSERTION FAILURE IN DEBUG_SYNC().
- Once it is fixed, remove the 'ifdef', 'if' and this comment. */
- if (!trx->ddl) {
- DEBUG_SYNC_C_IF_THD(trx->mysql_thd,
- "before_row_upd_sec_index_entry");
- }
-#endif /* UNIV_DEBUG */
-
- mtr_start_trx(&mtr, trx);
-
- if (*index->name == TEMP_INDEX_PREFIX) {
- /* The index->online_status may change if the
- index->name starts with TEMP_INDEX_PREFIX (meaning
- that the index is or was being created online). It is
- protected by index->lock. */
-
- mtr_s_lock(dict_index_get_lock(index), &mtr);
-
- switch (dict_index_get_online_status(index)) {
- case ONLINE_INDEX_COMPLETE:
- /* This is a normal index. Do not log anything.
- Perform the update on the index tree directly. */
- break;
- case ONLINE_INDEX_CREATION:
- /* Log a DELETE and optionally INSERT. */
- row_log_online_op(index, entry, 0);
-
- if (!node->is_delete) {
- mem_heap_empty(heap);
- entry = row_build_index_entry(
- node->upd_row, node->upd_ext,
- index, heap);
- ut_a(entry);
- row_log_online_op(index, entry, trx->id);
- }
- /* fall through */
- case ONLINE_INDEX_ABORTED:
- case ONLINE_INDEX_ABORTED_DROPPED:
- mtr_commit(&mtr);
- goto func_exit;
- }
-
- /* We can only buffer delete-mark operations if there
- are no foreign key constraints referring to the index. */
- mode = referenced
- ? BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
- : BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
- | BTR_DELETE_MARK;
- } else {
- /* For secondary indexes,
- index->online_status==ONLINE_INDEX_CREATION unless
- index->name starts with TEMP_INDEX_PREFIX. */
- ut_ad(!dict_index_is_online_ddl(index));
-
- /* We can only buffer delete-mark operations if there
- are no foreign key constraints referring to the index. */
- mode = referenced
- ? BTR_MODIFY_LEAF
- : BTR_MODIFY_LEAF | BTR_DELETE_MARK;
- }
-
- /* Set the query thread, so that ibuf_insert_low() will be
- able to invoke thd_get_trx(). */
- btr_pcur_get_btr_cur(&pcur)->thr = thr;
-
- search_result = row_search_index_entry(index, entry,
- UNIV_UNLIKELY(trx->fake_changes)
- ? BTR_SEARCH_LEAF
- : (btr_latch_mode)mode,
- &pcur, &mtr);
-
- btr_cur = btr_pcur_get_btr_cur(&pcur);
-
- rec = btr_cur_get_rec(btr_cur);
-
- switch (search_result) {
- case ROW_NOT_DELETED_REF: /* should only occur for BTR_DELETE */
- ut_error;
- break;
- case ROW_BUFFERED:
- /* Entry was delete marked already. */
- break;
-
- case ROW_NOT_FOUND:
- if (*index->name == TEMP_INDEX_PREFIX) {
- /* When online CREATE INDEX copied the update
- that we already made to the clustered index,
- and completed the secondary index creation
- before we got here, the old secondary index
- record would not exist. The CREATE INDEX
- should be waiting for a MySQL meta-data lock
- upgrade at least until this UPDATE
- returns. After that point, the
- TEMP_INDEX_PREFIX would be dropped from the
- index name in commit_inplace_alter_table(). */
- break;
- }
-
- fputs("InnoDB: error in sec index entry update in\n"
- "InnoDB: ", stderr);
- dict_index_name_print(stderr, trx, index);
- fputs("\n"
- "InnoDB: tuple ", stderr);
- dtuple_print(stderr, entry);
- fputs("\n"
- "InnoDB: record ", stderr);
- rec_print(stderr, rec, index);
- putc('\n', stderr);
- trx_print(stderr, trx, 0);
- fputs("\n"
- "InnoDB: Submit a detailed bug report"
- " to http://bugs.mysql.com\n", stderr);
- ut_ad(0);
- break;
- case ROW_FOUND:
- /* Delete mark the old index record; it can already be
- delete marked if we return after a lock wait in
- row_ins_sec_index_entry() below */
- if (!rec_get_deleted_flag(
- rec, dict_table_is_comp(index->table))) {
-#ifdef WITH_WSREP
- que_node_t *parent = que_node_get_parent(node);
-#endif /* WITH_WSREP */
- err = btr_cur_del_mark_set_sec_rec(
- 0, btr_cur, TRUE, thr, &mtr);
-
- if (err == DB_SUCCESS && referenced) {
-
- ulint* offsets;
-
- offsets = rec_get_offsets(
- rec, index, NULL, ULINT_UNDEFINED,
- &heap);
-
- /* NOTE that the following call loses
- the position of pcur ! */
- err = row_upd_check_references_constraints(
- node, &pcur, index->table,
- index, offsets, thr, &mtr);
- }
-#ifdef WITH_WSREP
- if (err == DB_SUCCESS && !referenced &&
- !(parent && que_node_get_type(parent) ==
- QUE_NODE_UPDATE &&
- ((upd_node_t*)parent)->cascade_node == node) &&
- foreign
- ) {
- ulint* offsets =
- rec_get_offsets(
- rec, index, NULL, ULINT_UNDEFINED,
- &heap);
- err = wsrep_row_upd_check_foreign_constraints(
- node, &pcur, index->table,
- index, offsets, thr, &mtr);
- switch (err) {
- case DB_SUCCESS:
- case DB_NO_REFERENCED_ROW:
- err = DB_SUCCESS;
- break;
- case DB_DEADLOCK:
- if (wsrep_debug) fprintf (stderr,
- "WSREP: sec index FK check fail for deadlock");
- break;
- default:
- fprintf (stderr,
- "WSREP: referenced FK check fail: %d",
- (int)err);
- break;
- }
- }
-#endif /* WITH_WSREP */
- }
- break;
- }
-
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
-
- if (node->is_delete || err != DB_SUCCESS) {
-
- goto func_exit;
- }
-
- mem_heap_empty(heap);
-
- /* Build a new index entry */
- entry = row_build_index_entry(node->upd_row, node->upd_ext,
- index, heap);
- ut_a(entry);
-
- /* Insert new index entry */
- err = row_ins_sec_index_entry(index, entry, thr);
-
-func_exit:
- mem_heap_free(heap);
-
- return(err);
-}
-
-/***********************************************************//**
-Updates the secondary index record if it is changed in the row update or
-deletes it if this is a delete.
-@return DB_SUCCESS if operation successfully completed, else error
-code or DB_LOCK_WAIT */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd_sec_step(
-/*=============*/
- upd_node_t* node, /*!< in: row update node */
- que_thr_t* thr) /*!< in: query thread */
-{
- ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
- || (node->state == UPD_NODE_UPDATE_SOME_SEC));
- ut_ad(!dict_index_is_clust(node->index));
-
- if (node->state == UPD_NODE_UPDATE_ALL_SEC
- || row_upd_changes_ord_field_binary(node->index, node->update,
- thr, node->row, node->ext)) {
- return(row_upd_sec_index_entry(node, thr));
- }
-
- return(DB_SUCCESS);
-}
-
-#ifdef UNIV_DEBUG
-# define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update) \
- row_upd_clust_rec_by_insert_inherit_func(rec,offsets,entry,update)
-#else /* UNIV_DEBUG */
-# define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update) \
- row_upd_clust_rec_by_insert_inherit_func(entry,update)
-#endif /* UNIV_DEBUG */
-/*******************************************************************//**
-Mark non-updated off-page columns inherited when the primary key is
-updated. We must mark them as inherited in entry, so that they are not
-freed in a rollback. A limited version of this function used to be
-called btr_cur_mark_dtuple_inherited_extern().
-@return TRUE if any columns were inherited */
-static MY_ATTRIBUTE((warn_unused_result))
-ibool
-row_upd_clust_rec_by_insert_inherit_func(
-/*=====================================*/
-#ifdef UNIV_DEBUG
- const rec_t* rec, /*!< in: old record, or NULL */
- const ulint* offsets,/*!< in: rec_get_offsets(rec), or NULL */
-#endif /* UNIV_DEBUG */
- dtuple_t* entry, /*!< in/out: updated entry to be
- inserted into the clustered index */
- const upd_t* update) /*!< in: update vector */
-{
- ibool inherit = FALSE;
- ulint i;
-
- ut_ad(!rec == !offsets);
- ut_ad(!rec || rec_offs_any_extern(offsets));
-
- for (i = 0; i < dtuple_get_n_fields(entry); i++) {
- dfield_t* dfield = dtuple_get_nth_field(entry, i);
- byte* data;
- ulint len;
-
- ut_ad(!offsets
- || !rec_offs_nth_extern(offsets, i)
- == !dfield_is_ext(dfield)
- || upd_get_field_by_field_no(update, i));
- if (!dfield_is_ext(dfield)
- || upd_get_field_by_field_no(update, i)) {
- continue;
- }
-
-#ifdef UNIV_DEBUG
- if (UNIV_LIKELY(rec != NULL)) {
- const byte* rec_data
- = rec_get_nth_field(rec, offsets, i, &len);
- ut_ad(len == dfield_get_len(dfield));
- ut_ad(len != UNIV_SQL_NULL);
- ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE);
-
- rec_data += len - BTR_EXTERN_FIELD_REF_SIZE;
-
- /* The pointer must not be zero. */
- ut_ad(memcmp(rec_data, field_ref_zero,
- BTR_EXTERN_FIELD_REF_SIZE));
- /* The BLOB must be owned. */
- ut_ad(!(rec_data[BTR_EXTERN_LEN]
- & BTR_EXTERN_OWNER_FLAG));
- }
-#endif /* UNIV_DEBUG */
-
- len = dfield_get_len(dfield);
- ut_a(len != UNIV_SQL_NULL);
- ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
-
- data = static_cast<byte*>(dfield_get_data(dfield));
-
- data += len - BTR_EXTERN_FIELD_REF_SIZE;
- /* The pointer must not be zero. */
- ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE));
- data[BTR_EXTERN_LEN] &= ~BTR_EXTERN_OWNER_FLAG;
- data[BTR_EXTERN_LEN] |= BTR_EXTERN_INHERITED_FLAG;
- /* The BTR_EXTERN_INHERITED_FLAG only matters in
- rollback. Purge will always free the extern fields of
- a delete-marked row. */
-
- inherit = TRUE;
- }
-
- return(inherit);
-}
-
-/***********************************************************//**
-Marks the clustered index record deleted and inserts the updated version
-of the record to the index. This function should be used when the ordering
-fields of the clustered index record change. This should be quite rare in
-database applications.
-@return DB_SUCCESS if operation successfully completed, else error
-code or DB_LOCK_WAIT */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd_clust_rec_by_insert(
-/*========================*/
- upd_node_t* node, /*!< in/out: row update node */
- dict_index_t* index, /*!< in: clustered index of the record */
- que_thr_t* thr, /*!< in: query thread */
- ibool referenced,/*!< in: TRUE if index may be referenced in
- a foreign key constraint */
-#ifdef WITH_WSREP
- ibool foreign, /*!< in: TRUE if index is foreign key index */
-#endif /* WITH_WSREP */
- mtr_t* mtr) /*!< in/out: mtr; gets committed here */
-{
- mem_heap_t* heap;
- btr_pcur_t* pcur;
- btr_cur_t* btr_cur;
- trx_t* trx;
- dict_table_t* table;
- dtuple_t* entry;
- dberr_t err;
- ibool change_ownership = FALSE;
- rec_t* rec;
- ulint* offsets = NULL;
-
-#ifdef WITH_WSREP
- que_node_t *parent = que_node_get_parent(node);
-#endif /* WITH_WSREP */
- ut_ad(node);
- ut_ad(dict_index_is_clust(index));
-
- trx = thr_get_trx(thr);
- table = node->table;
- pcur = node->pcur;
- btr_cur = btr_pcur_get_btr_cur(pcur);
-
- heap = mem_heap_create(1000);
-
- entry = row_build_index_entry(node->upd_row, node->upd_ext,
- index, heap);
- ut_a(entry);
-
- row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
-
- switch (node->state) {
- default:
- ut_error;
- case UPD_NODE_INSERT_BLOB:
- /* A lock wait occurred in row_ins_clust_index_entry() in
- the previous invocation of this function. Mark the
- off-page columns in the entry inherited. */
-
- if (UNIV_LIKELY(!trx->fake_changes)) {
- change_ownership = row_upd_clust_rec_by_insert_inherit(
- NULL, NULL, entry, node->update);
- ut_a(change_ownership);
- }
- /* fall through */
- case UPD_NODE_INSERT_CLUSTERED:
- /* A lock wait occurred in row_ins_clust_index_entry() in
- the previous invocation of this function. */
- break;
- case UPD_NODE_UPDATE_CLUSTERED:
- /* This is the first invocation of the function where
- we update the primary key. Delete-mark the old record
- in the clustered index and prepare to insert a new entry. */
- rec = btr_cur_get_rec(btr_cur);
- offsets = rec_get_offsets(rec, index, NULL,
- ULINT_UNDEFINED, &heap);
- ut_ad(page_rec_is_user_rec(rec));
-
- err = btr_cur_del_mark_set_clust_rec(
- btr_cur_get_block(btr_cur), rec, index, offsets,
- thr, mtr);
- if (err != DB_SUCCESS) {
-err_exit:
- mtr_commit(mtr);
- mem_heap_free(heap);
- return(err);
- }
-
- /* If the the new row inherits externally stored
- fields (off-page columns a.k.a. BLOBs) from the
- delete-marked old record, mark them disowned by the
- old record and owned by the new entry. */
-
- if (rec_offs_any_extern(offsets)
- && UNIV_LIKELY(!(trx->fake_changes))) {
- change_ownership = row_upd_clust_rec_by_insert_inherit(
- rec, offsets, entry, node->update);
-
- if (change_ownership) {
- /* The blobs are disowned here, expecting the
- insert down below to inherit them. But if the
- insert fails, then this disown will be undone
- when the operation is rolled back. */
- btr_cur_disown_inherited_fields(
- btr_cur_get_page_zip(btr_cur),
- rec, index, offsets, node->update, mtr);
- }
- }
-
- if (referenced) {
- /* NOTE that the following call loses
- the position of pcur ! */
-
- err = row_upd_check_references_constraints(
- node, pcur, table, index, offsets, thr, mtr);
-
- if (err != DB_SUCCESS) {
- goto err_exit;
- }
- }
-#ifdef WITH_WSREP
- if (!referenced &&
- !(parent && que_node_get_type(parent) == QUE_NODE_UPDATE &&
- ((upd_node_t*)parent)->cascade_node == node) &&
- foreign
- ) {
- err = wsrep_row_upd_check_foreign_constraints(
- node, pcur, table, index, offsets, thr, mtr);
- switch (err) {
- case DB_SUCCESS:
- case DB_NO_REFERENCED_ROW:
- err = DB_SUCCESS;
- break;
- case DB_DEADLOCK:
- if (wsrep_debug) fprintf (stderr,
- "WSREP: insert FK check fail for deadlock");
- break;
- default:
- fprintf (stderr,
- "WSREP: referenced FK check fail: %d",
- (int)err);
- break;
- }
- if (err != DB_SUCCESS) {
- goto err_exit;
- }
- }
-#endif /* WITH_WSREP */
- }
-
- mtr_commit(mtr);
-
- err = row_ins_clust_index_entry(
- index, entry, thr,
- node->upd_ext ? node->upd_ext->n_ext : 0);
- node->state = change_ownership
- ? UPD_NODE_INSERT_BLOB
- : UPD_NODE_INSERT_CLUSTERED;
-
- mem_heap_free(heap);
-
- return(err);
-}
-
-/***********************************************************//**
-Updates a clustered index record of a row when the ordering fields do
-not change.
-@return DB_SUCCESS if operation successfully completed, else error
-code or DB_LOCK_WAIT */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd_clust_rec(
-/*==============*/
- upd_node_t* node, /*!< in: row update node */
- dict_index_t* index, /*!< in: clustered index */
- ulint* offsets,/*!< in: rec_get_offsets() on node->pcur */
- mem_heap_t** offsets_heap,
- /*!< in/out: memory heap, can be emptied */
- que_thr_t* thr, /*!< in: query thread */
- mtr_t* mtr) /*!< in: mtr; gets committed here */
-{
- mem_heap_t* heap = NULL;
- big_rec_t* big_rec = NULL;
- btr_pcur_t* pcur;
- btr_cur_t* btr_cur;
- dberr_t err;
- const dtuple_t* rebuilt_old_pk = NULL;
-
- ut_ad(node);
- ut_ad(dict_index_is_clust(index));
-
- pcur = node->pcur;
- btr_cur = btr_pcur_get_btr_cur(pcur);
-
- ut_ad(btr_cur_get_index(btr_cur) == index);
- ut_ad(!rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
- dict_table_is_comp(index->table)));
- ut_ad(rec_offs_validate(btr_cur_get_rec(btr_cur), index, offsets));
-
- if (dict_index_is_online_ddl(index)) {
- rebuilt_old_pk = row_log_table_get_pk(
- btr_cur_get_rec(btr_cur), index, offsets, NULL, &heap);
- }
-
- /* Try optimistic updating of the record, keeping changes within
- the page; we do not check locks because we assume the x-lock on the
- record to update */
-
- if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
- err = btr_cur_update_in_place(
- BTR_NO_LOCKING_FLAG, btr_cur,
- offsets, node->update,
- node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
- } else {
- err = btr_cur_optimistic_update(
- BTR_NO_LOCKING_FLAG, btr_cur,
- &offsets, offsets_heap, node->update,
- node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
- }
-
- if (err == DB_SUCCESS && dict_index_is_online_ddl(index)) {
- row_log_table_update(btr_cur_get_rec(btr_cur),
- index, offsets, rebuilt_old_pk);
- }
-
- mtr_commit(mtr);
-
- if (UNIV_LIKELY(err == DB_SUCCESS)) {
-
- goto func_exit;
- }
-
- if (buf_LRU_buf_pool_running_out()) {
-
- err = DB_LOCK_TABLE_FULL;
- goto func_exit;
- }
- /* We may have to modify the tree structure: do a pessimistic descent
- down the index tree */
-
- mtr_start_trx(mtr, thr_get_trx(thr));
-
- /* NOTE: this transaction has an s-lock or x-lock on the record and
- therefore other transactions cannot modify the record when we have no
- latch on the page. In addition, we assume that other query threads of
- the same transaction do not modify the record in the meantime.
- Therefore we can assert that the restoration of the cursor succeeds. */
-
- ut_a(btr_pcur_restore_position(
- UNIV_UNLIKELY(thr_get_trx(thr)->fake_changes)
- ? BTR_SEARCH_TREE : BTR_MODIFY_TREE,
- pcur, mtr));
-
- ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
- dict_table_is_comp(index->table)));
-
- if (!heap) {
- heap = mem_heap_create(1024);
- }
-
- err = btr_cur_pessimistic_update(
- BTR_NO_LOCKING_FLAG | BTR_KEEP_POS_FLAG, btr_cur,
- &offsets, offsets_heap, heap, &big_rec,
- node->update, node->cmpl_info,
- thr, thr_get_trx(thr)->id, mtr);
- if (big_rec && UNIV_LIKELY(!(thr_get_trx(thr)->fake_changes))) {
- ut_a(err == DB_SUCCESS);
- /* Write out the externally stored
- columns while still x-latching
- index->lock and block->lock. Allocate
- pages for big_rec in the mtr that
- modified the B-tree, but be sure to skip
- any pages that were freed in mtr. We will
- write out the big_rec pages before
- committing the B-tree mini-transaction. If
- the system crashes so that crash recovery
- will not replay the mtr_commit(&mtr), the
- big_rec pages will be left orphaned until
- the pages are allocated for something else.
-
- TODO: If the allocation extends the tablespace, it
- will not be redo logged, in either mini-transaction.
- Tablespace extension should be redo-logged in the
- big_rec mini-transaction, so that recovery will not
- fail when the big_rec was written to the extended
- portion of the file, in case the file was somehow
- truncated in the crash. */
-
- DEBUG_SYNC_C("before_row_upd_extern");
- err = btr_store_big_rec_extern_fields(
- index, btr_cur_get_block(btr_cur),
- btr_cur_get_rec(btr_cur), offsets,
- big_rec, mtr, BTR_STORE_UPDATE);
- DEBUG_SYNC_C("after_row_upd_extern");
- /* If writing big_rec fails (for example, because of
- DB_OUT_OF_FILE_SPACE), the record will be corrupted.
- Even if we did not update any externally stored
- columns, our update could cause the record to grow so
- that a non-updated column was selected for external
- storage. This non-update would not have been written
- to the undo log, and thus the record cannot be rolled
- back.
-
- However, because we have not executed mtr_commit(mtr)
- yet, the update will not be replayed in crash
- recovery, and the following assertion failure will
- effectively "roll back" the operation. */
- ut_a(err == DB_SUCCESS);
- }
-
- if (err == DB_SUCCESS && dict_index_is_online_ddl(index)) {
- row_log_table_update(btr_cur_get_rec(btr_cur),
- index, offsets, rebuilt_old_pk);
- }
-
- mtr_commit(mtr);
-func_exit:
- if (heap) {
- mem_heap_free(heap);
- }
-
- if (big_rec) {
- dtuple_big_rec_free(big_rec);
- }
-
- return(err);
-}
-
-/***********************************************************//**
-Delete marks a clustered index record.
-@return DB_SUCCESS if operation successfully completed, else error code */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd_del_mark_clust_rec(
-/*=======================*/
- upd_node_t* node, /*!< in: row update node */
- dict_index_t* index, /*!< in: clustered index */
- ulint* offsets,/*!< in/out: rec_get_offsets() for the
- record under the cursor */
- que_thr_t* thr, /*!< in: query thread */
- ibool referenced,
- /*!< in: TRUE if index may be referenced in
- a foreign key constraint */
-#ifdef WITH_WSREP
- ibool foreign,/*!< in: TRUE if index is foreign key index */
-#endif /* WITH_WSREP */
- mtr_t* mtr) /*!< in: mtr; gets committed here */
-{
- btr_pcur_t* pcur;
- btr_cur_t* btr_cur;
- dberr_t err;
-#ifdef WITH_WSREP
- rec_t* rec;
- que_node_t *parent = que_node_get_parent(node);
-#endif /* WITH_WSREP */
-
- ut_ad(node);
- ut_ad(dict_index_is_clust(index));
- ut_ad(node->is_delete);
-
- pcur = node->pcur;
- btr_cur = btr_pcur_get_btr_cur(pcur);
-
- /* Store row because we have to build also the secondary index
- entries */
-
- row_upd_store_row(node);
-
- /* Mark the clustered index record deleted; we do not have to check
- locks, because we assume that we have an x-lock on the record */
-
-#ifdef WITH_WSREP
- rec = btr_cur_get_rec(btr_cur);
-#endif /* WITH_WSREP */
-
- err = btr_cur_del_mark_set_clust_rec(
-#ifdef WITH_WSREP
- btr_cur_get_block(btr_cur), rec,
-#else
- btr_cur_get_block(btr_cur), btr_cur_get_rec(btr_cur),
-#endif /* WITH_WSREP */
- index, offsets, thr, mtr);
- if (err == DB_SUCCESS && referenced) {
- /* NOTE that the following call loses the position of pcur ! */
-
- err = row_upd_check_references_constraints(
- node, pcur, index->table, index, offsets, thr, mtr);
- }
-#ifdef WITH_WSREP
- if (err == DB_SUCCESS && !referenced &&
- !(parent && que_node_get_type(parent) == QUE_NODE_UPDATE &&
- ((upd_node_t*)parent)->cascade_node == node) &&
- thr_get_trx(thr) &&
- foreign
- ) {
- err = wsrep_row_upd_check_foreign_constraints(
- node, pcur, index->table, index, offsets, thr, mtr);
- switch (err) {
- case DB_SUCCESS:
- case DB_NO_REFERENCED_ROW:
- err = DB_SUCCESS;
- break;
- case DB_DEADLOCK:
- if (wsrep_debug) fprintf (stderr,
- "WSREP: clust rec FK check fail for deadlock");
- break;
- default:
- fprintf (stderr,
- "WSREP: clust rec referenced FK check fail: %d",
- (int)err);
- break;
- }
- }
-#endif /* WITH_WSREP */
-
- mtr_commit(mtr);
-
- return(err);
-}
-
-/***********************************************************//**
-Updates the clustered index record.
-@return DB_SUCCESS if operation successfully completed, DB_LOCK_WAIT
-in case of a lock wait, else error code */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd_clust_step(
-/*===============*/
- upd_node_t* node, /*!< in: row update node */
- que_thr_t* thr) /*!< in: query thread */
-{
- dict_index_t* index;
- btr_pcur_t* pcur;
- ibool success;
- dberr_t err;
- mtr_t mtr;
- rec_t* rec;
- mem_heap_t* heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- ulint* offsets;
- ibool referenced;
- rec_offs_init(offsets_);
-
- index = dict_table_get_first_index(node->table);
-
- referenced = row_upd_index_is_referenced(index, thr_get_trx(thr));
-#ifdef WITH_WSREP
- ibool foreign = wsrep_row_upd_index_is_foreign(
- index, thr_get_trx(thr));
-#endif /* WITH_WSREP */
-
- pcur = node->pcur;
-
- /* We have to restore the cursor to its position */
-
- mtr_start_trx(&mtr, thr_get_trx(thr));
-
- /* If the restoration does not succeed, then the same
- transaction has deleted the record on which the cursor was,
- and that is an SQL error. If the restoration succeeds, it may
- still be that the same transaction has successively deleted
- and inserted a record with the same ordering fields, but in
- that case we know that the transaction has at least an
- implicit x-lock on the record. */
-
- ut_a(pcur->rel_pos == BTR_PCUR_ON);
-
- ulint mode;
- ulint search_mode;
-
-#ifdef UNIV_DEBUG
- /* Work around Bug#14626800 ASSERTION FAILURE IN DEBUG_SYNC().
- Once it is fixed, remove the 'ifdef', 'if' and this comment. */
- if (!thr_get_trx(thr)->ddl) {
- DEBUG_SYNC_C_IF_THD(
- thr_get_trx(thr)->mysql_thd,
- "innodb_row_upd_clust_step_enter");
- }
-#endif /* UNIV_DEBUG */
-
- /* If running with fake_changes mode on then switch from modify to
- search so that code takes only s-latch and not x-latch.
- For dry-run (fake-changes) s-latch is acceptable. Taking x-latch will
- make it more restrictive and will block real changes/workflow. */
- if (UNIV_UNLIKELY(thr_get_trx(thr)->fake_changes)) {
- mode = BTR_MODIFY_LEAF;
- search_mode = BTR_SEARCH_LEAF;
- } else {
- mode = BTR_MODIFY_LEAF;
- search_mode = BTR_MODIFY_LEAF;
- }
-
- if (dict_index_is_online_ddl(index)) {
-
- ut_ad(node->table->id != DICT_INDEXES_ID);
-
- mode |= BTR_ALREADY_S_LATCHED;
- search_mode |= BTR_ALREADY_S_LATCHED;
-
- mtr_s_lock(dict_index_get_lock(index), &mtr);
- }
-
- success = btr_pcur_restore_position(search_mode, pcur, &mtr);
-
- if (!success) {
- err = DB_RECORD_NOT_FOUND;
-
- mtr_commit(&mtr);
-
- return(err);
- }
-
- /* If this is a row in SYS_INDEXES table of the data dictionary,
- then we have to free the file segments of the index tree associated
- with the index */
-
- if (node->is_delete && node->table->id == DICT_INDEXES_ID) {
-
- ut_ad(!dict_index_is_online_ddl(index));
-
- /* Action in fake change mode shouldn't cause changes
- in system tables. */
- ut_ad(UNIV_LIKELY(!thr_get_trx(thr)->fake_changes));
-
- dict_drop_index_tree(btr_pcur_get_rec(pcur), &mtr);
-
- mtr_commit(&mtr);
-
- mtr_start_trx(&mtr, thr_get_trx(thr));
-
- success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
- &mtr);
- if (!success) {
- err = DB_ERROR;
-
- mtr_commit(&mtr);
-
- return(err);
- }
- }
-
- rec = btr_pcur_get_rec(pcur);
- offsets = rec_get_offsets(rec, index, offsets_,
- ULINT_UNDEFINED, &heap);
-
- if (!node->has_clust_rec_x_lock) {
- err = lock_clust_rec_modify_check_and_lock(
- 0, btr_pcur_get_block(pcur),
- rec, index, offsets, thr);
- if (err != DB_SUCCESS) {
- mtr_commit(&mtr);
- goto exit_func;
- }
- }
-
- /* This check passes as the function manipulates x-lock to s-lock
- if operating in fake-change mode. */
- ut_ad(lock_trx_has_rec_x_lock(thr_get_trx(thr), index->table,
- btr_pcur_get_block(pcur),
- page_rec_get_heap_no(rec)));
-
- /* NOTE: the following function calls will also commit mtr */
-
- if (node->is_delete) {
- err = row_upd_del_mark_clust_rec(
-#ifdef WITH_WSREP
- node, index, offsets, thr, referenced, foreign, &mtr);
-#else
- node, index, offsets, thr, referenced, &mtr);
-#endif /* WITH_WSREP */
-
- if (err == DB_SUCCESS) {
- node->state = UPD_NODE_UPDATE_ALL_SEC;
- node->index = dict_table_get_next_index(index);
- }
-
- goto exit_func;
- }
-
- /* If the update is made for MySQL, we already have the update vector
- ready, else we have to do some evaluation: */
-
- if (UNIV_UNLIKELY(!node->in_mysql_interface)) {
- /* Copy the necessary columns from clust_rec and calculate the
- new values to set */
- row_upd_copy_columns(rec, offsets,
- UT_LIST_GET_FIRST(node->columns));
- row_upd_eval_new_vals(node->update);
- }
-
- if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
-
- err = row_upd_clust_rec(
- node, index, offsets, &heap, thr, &mtr);
- goto exit_func;
- }
-
- row_upd_store_row(node);
-
- if (row_upd_changes_ord_field_binary(index, node->update, thr,
- node->row, node->ext)) {
-
- /* Update causes an ordering field (ordering fields within
- the B-tree) of the clustered index record to change: perform
- the update by delete marking and inserting.
-
- TODO! What to do to the 'Halloween problem', where an update
- moves the record forward in index so that it is again
- updated when the cursor arrives there? Solution: the
- read operation must check the undo record undo number when
- choosing records to update. MySQL solves now the problem
- externally! */
-
- err = row_upd_clust_rec_by_insert(
-#ifdef WITH_WSREP
- node, index, thr, referenced, foreign, &mtr);
-#else
- node, index, thr, referenced, &mtr);
-#endif /* WITH_WSREP */
-
- if (err != DB_SUCCESS) {
-
- goto exit_func;
- }
-
- node->state = UPD_NODE_UPDATE_ALL_SEC;
- } else {
- err = row_upd_clust_rec(
- node, index, offsets, &heap, thr, &mtr);
-
- if (err != DB_SUCCESS) {
-
- goto exit_func;
- }
-
- node->state = UPD_NODE_UPDATE_SOME_SEC;
- }
-
- node->index = dict_table_get_next_index(index);
-
-exit_func:
- if (heap) {
- mem_heap_free(heap);
- }
- return(err);
-}
-
-/***********************************************************//**
-Updates the affected index records of a row. When the control is transferred
-to this node, we assume that we have a persistent cursor which was on a
-record, and the position of the cursor is stored in the cursor.
-@return DB_SUCCESS if operation successfully completed, else error
-code or DB_LOCK_WAIT */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
-dberr_t
-row_upd(
-/*====*/
- upd_node_t* node, /*!< in: row update node */
- que_thr_t* thr) /*!< in: query thread */
-{
- dberr_t err = DB_SUCCESS;
-
- if (UNIV_LIKELY(node->in_mysql_interface)) {
-
- /* We do not get the cmpl_info value from the MySQL
- interpreter: we must calculate it on the fly: */
-
- if (node->is_delete
- || row_upd_changes_some_index_ord_field_binary(
- node->table, node->update)) {
- node->cmpl_info = 0;
- } else {
- node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
- }
- }
-
- switch (node->state) {
- case UPD_NODE_UPDATE_CLUSTERED:
- case UPD_NODE_INSERT_CLUSTERED:
- case UPD_NODE_INSERT_BLOB:
- log_free_check();
- err = row_upd_clust_step(node, thr);
-
- if (err != DB_SUCCESS) {
-
- return(err);
- }
- }
-
- if (node->index == NULL
- || (!node->is_delete
- && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE))) {
-
- return(DB_SUCCESS);
- }
-
-#ifdef UNIV_DEBUG
- /* Work around Bug#14626800 ASSERTION FAILURE IN DEBUG_SYNC().
- Once it is fixed, remove the 'ifdef', 'if' and this comment. */
- if (!thr_get_trx(thr)->ddl) {
- DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd,
- "after_row_upd_clust");
- }
-#endif /* UNIV_DEBUG */
-
- DBUG_EXECUTE_IF("row_upd_skip_sec", node->index = NULL;);
-
- do {
- /* Skip corrupted index */
- dict_table_skip_corrupt_index(node->index);
-
- if (!node->index) {
- break;
- }
-
- if (node->index->type != DICT_FTS) {
- err = row_upd_sec_step(node, thr);
-
- if (err != DB_SUCCESS) {
-
- return(err);
- }
- }
-
- node->index = dict_table_get_next_index(node->index);
- } while (node->index != NULL);
-
- ut_ad(err == DB_SUCCESS);
-
- /* Do some cleanup */
-
- if (node->row != NULL) {
- node->row = NULL;
- node->ext = NULL;
- node->upd_row = NULL;
- node->upd_ext = NULL;
- mem_heap_empty(node->heap);
- }
-
- node->state = UPD_NODE_UPDATE_CLUSTERED;
-
- return(err);
-}
-
-/***********************************************************//**
-Updates a row in a table. This is a high-level function used in SQL execution
-graphs.
-@return query thread to run next or NULL */
-UNIV_INTERN
-que_thr_t*
-row_upd_step(
-/*=========*/
- que_thr_t* thr) /*!< in: query thread */
-{
- upd_node_t* node;
- sel_node_t* sel_node;
- que_node_t* parent;
- dberr_t err = DB_SUCCESS;
- trx_t* trx;
-
- ut_ad(thr);
-
- trx = thr_get_trx(thr);
-
- trx_start_if_not_started_xa(trx);
-
- node = static_cast<upd_node_t*>(thr->run_node);
-
- sel_node = node->select;
-
- parent = que_node_get_parent(node);
-
- ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
-
- if (thr->prev_node == parent) {
- node->state = UPD_NODE_SET_IX_LOCK;
- }
-
- if (node->state == UPD_NODE_SET_IX_LOCK) {
-
- if (!node->has_clust_rec_x_lock) {
- /* It may be that the current session has not yet
- started its transaction, or it has been committed: */
-
- err = lock_table(0, node->table, LOCK_IX, thr);
-
- if (err != DB_SUCCESS) {
-
- goto error_handling;
- }
- }
-
- node->state = UPD_NODE_UPDATE_CLUSTERED;
-
- if (node->searched_update) {
- /* Reset the cursor */
- sel_node->state = SEL_NODE_OPEN;
-
- /* Fetch a row to update */
-
- thr->run_node = sel_node;
-
- return(thr);
- }
- }
-
- /* sel_node is NULL if we are in the MySQL interface */
-
- if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
-
- if (!node->searched_update) {
- /* An explicit cursor should be positioned on a row
- to update */
-
- ut_error;
-
- err = DB_ERROR;
-
- goto error_handling;
- }
-
- ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
-
- /* No more rows to update, or the select node performed the
- updates directly in-place */
-
- thr->run_node = parent;
-
- return(thr);
- }
-
- /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
-
- err = row_upd(node, thr);
-
-error_handling:
- trx->error_state = err;
-
- if (err != DB_SUCCESS) {
- return(NULL);
- }
-
- /* DO THE TRIGGER ACTIONS HERE */
-
- if (node->searched_update) {
- /* Fetch next row to update */
-
- thr->run_node = sel_node;
- } else {
- /* It was an explicit cursor update */
-
- thr->run_node = parent;
- }
-
- node->state = UPD_NODE_UPDATE_CLUSTERED;
-
- return(thr);
-}
-#endif /* !UNIV_HOTBACKUP */