row0upd.cc 91.7 KB
Newer Older
1 2
/*****************************************************************************

3
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
4
Copyright (c) 2015, 2018, MariaDB Corporation.
5 6 7 8 9 10 11 12 13 14

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
15 16
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17 18 19

*****************************************************************************/

20
/**************************************************//**
21
@file row/row0upd.cc
osku's avatar
osku committed
22 23 24 25 26
Update of a row

Created 12/27/1996 Heikki Tuuri
*******************************************************/

27 28
#include "ha_prototypes.h"

osku's avatar
osku committed
29 30
#include "row0upd.h"
#include "dict0dict.h"
31
#include "dict0mem.h"
32 33
#include "trx0undo.h"
#include "rem0rec.h"
osku's avatar
osku committed
34 35 36 37 38 39
#include "dict0boot.h"
#include "dict0crea.h"
#include "mach0data.h"
#include "btr0btr.h"
#include "btr0cur.h"
#include "que0que.h"
40
#include "row0ext.h"
osku's avatar
osku committed
41
#include "row0ins.h"
42
#include "row0log.h"
osku's avatar
osku committed
43
#include "row0row.h"
44
#include "row0sel.h"
osku's avatar
osku committed
45 46 47 48 49
#include "rem0cmp.h"
#include "lock0lock.h"
#include "log0log.h"
#include "pars0sym.h"
#include "eval0eval.h"
50
#include "buf0lru.h"
51 52 53
#include "trx0rec.h"
#include "fts0fts.h"
#include "fts0types.h"
Sergei Golubchik's avatar
Sergei Golubchik committed
54
#include <algorithm>
55 56
#include <mysql/plugin.h>
#include <mysql/service_wsrep.h>
osku's avatar
osku committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

/* 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. */

Inaam Rana's avatar
Inaam Rana committed
99 100 101 102 103 104 105 106 107 108
/*************************************************************************
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. */

109
/***********************************************************//**
osku's avatar
osku committed
110 111
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
112
that index does not contain column prefixes.
113
@return TRUE if changes */
osku's avatar
osku committed
114 115 116 117
static
ibool
row_upd_changes_first_fields_binary(
/*================================*/
118 119 120 121
	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 */
osku's avatar
osku committed
122

123
/*********************************************************************//**
osku's avatar
osku committed
124
Checks if index currently is mentioned as a referenced index in a foreign
125
key constraint.
126 127 128 129 130 131

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 */
osku's avatar
osku committed
132 133 134 135
static
ibool
row_upd_index_is_referenced(
/*========================*/
136 137
	dict_index_t*	index,	/*!< in: index */
	trx_t*		trx)	/*!< in: transaction */
osku's avatar
osku committed
138 139 140
{
	dict_table_t*	table		= index->table;
	ibool		froze_data_dict	= FALSE;
141
	ibool		is_referenced	= FALSE;
osku's avatar
osku committed
142

Sergei Golubchik's avatar
Sergei Golubchik committed
143
	if (table->referenced_set.empty()) {
osku's avatar
osku committed
144 145 146 147 148 149 150 151
		return(FALSE);
	}

	if (trx->dict_operation_lock_mode == 0) {
		row_mysql_freeze_data_dictionary(trx);
		froze_data_dict = TRUE;
	}

Sergei Golubchik's avatar
Sergei Golubchik committed
152 153 154 155
	dict_foreign_set::iterator	it
		= std::find_if(table->referenced_set.begin(),
			       table->referenced_set.end(),
			       dict_foreign_with_index(index));
osku's avatar
osku committed
156

Sergei Golubchik's avatar
Sergei Golubchik committed
157
	is_referenced = (it != table->referenced_set.end());
158

osku's avatar
osku committed
159 160 161 162
	if (froze_data_dict) {
		row_mysql_unfreeze_data_dictionary(trx);
	}

163
	return(is_referenced);
osku's avatar
osku committed
164 165
}

166 167 168 169 170 171 172 173 174 175 176 177
#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;
	ibool		froze_data_dict	= FALSE;
	ibool		is_referenced	= FALSE;

Sergei Golubchik's avatar
Sergei Golubchik committed
178
	if (table->foreign_set.empty()) {
179 180 181 182 183 184 185 186
		return(FALSE);
	}

	if (trx->dict_operation_lock_mode == 0) {
		row_mysql_freeze_data_dictionary(trx);
		froze_data_dict = TRUE;
	}

187 188 189 190
	dict_foreign_set::iterator	it
		= std::find_if(table->foreign_set.begin(),
			       table->foreign_set.end(),
			       dict_foreign_with_foreign_index(index));
191

192
	is_referenced = (it != table->foreign_set.end());
193 194 195 196 197 198 199 200 201

	if (froze_data_dict) {
		row_mysql_unfreeze_data_dictionary(trx);
	}

	return(is_referenced);
}
#endif /* WITH_WSREP */

202
/*********************************************************************//**
osku's avatar
osku committed
203
Checks if possible foreign key constraints hold after a delete of the record
204 205 206
under pcur.

NOTE that this function will temporarily commit mtr and lose the
207
pcur position!
208

209
@return DB_SUCCESS or an error code */
Sergei Golubchik's avatar
Sergei Golubchik committed
210
static MY_ATTRIBUTE((nonnull, warn_unused_result))
211
dberr_t
osku's avatar
osku committed
212 213
row_upd_check_references_constraints(
/*=================================*/
214 215
	upd_node_t*	node,	/*!< in: row update node */
	btr_pcur_t*	pcur,	/*!< in: cursor positioned on a record; NOTE: the
osku's avatar
osku committed
216
				cursor position is lost in this function! */
217 218 219 220 221
	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 */
osku's avatar
osku committed
222 223 224 225 226
{
	dict_foreign_t*	foreign;
	mem_heap_t*	heap;
	dtuple_t*	entry;
	trx_t*		trx;
227 228
	const rec_t*	rec;
	ulint		n_ext;
229
	dberr_t		err;
osku's avatar
osku committed
230 231
	ibool		got_s_lock	= FALSE;

232
	DBUG_ENTER("row_upd_check_references_constraints");
osku's avatar
osku committed
233

234 235
	if (table->referenced_set.empty()) {
		DBUG_RETURN(DB_SUCCESS);
osku's avatar
osku committed
236 237 238 239 240
	}

	trx = thr_get_trx(thr);

	rec = btr_pcur_get_rec(pcur);
241
	ut_ad(rec_offs_validate(rec, index, offsets));
osku's avatar
osku committed
242 243 244

	heap = mem_heap_create(500);

245
	entry = row_rec_to_index_entry(rec, index, offsets, &n_ext, heap);
osku's avatar
osku committed
246

247 248
	mtr_commit(mtr);

249 250
	DEBUG_SYNC_C("foreign_constraint_check_for_update");

251
	mtr->start();
osku's avatar
osku committed
252 253 254 255 256 257

	if (trx->dict_operation_lock_mode == 0) {
		got_s_lock = TRUE;

		row_mysql_freeze_data_dictionary(trx);
	}
258

259 260 261
	DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd,
			    "foreign_constraint_check_for_insert");

Sergei Golubchik's avatar
Sergei Golubchik committed
262 263 264 265 266 267
	for (dict_foreign_set::iterator it = table->referenced_set.begin();
	     it != table->referenced_set.end();
	     ++it) {

		foreign = *it;

osku's avatar
osku committed
268 269 270 271 272 273
		/* 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
274
		    && (node->is_delete
275 276 277
			|| row_upd_changes_first_fields_binary(
				entry, index, node->update,
				foreign->n_fields))) {
278
			dict_table_t*	foreign_table = foreign->foreign_table;
279

280 281
			dict_table_t*	ref_table = NULL;

282
			if (foreign_table == NULL) {
283 284

				ref_table = dict_table_open_on_name(
285 286
					foreign->foreign_table_name_lookup,
					FALSE, FALSE, DICT_ERR_IGNORE_NONE);
osku's avatar
osku committed
287 288
			}

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
			/* dict_operation_lock is held both here
			(UPDATE or DELETE with FOREIGN KEY) and by TRUNCATE
			TABLE operations.
			If a TRUNCATE TABLE operation is in progress,
			there can be 2 possible conditions:
			1) row_truncate_table_for_mysql() is not yet called.
			2) Truncate releases dict_operation_lock
			during eviction of pages from buffer pool
			for a file-per-table tablespace.

			In case of (1), truncate will wait for FK operation
			to complete.
			In case of (2), truncate will be rolled forward even
			if it is interrupted. So if the foreign table is
			undergoing a truncate, ignore the FK check. */

			if (foreign_table) {
306 307 308
				if (foreign_table->space
				    && foreign_table->space
				    ->is_being_truncated) {
309 310
					continue;
				}
311 312

				foreign_table->inc_fk_checks();
313 314
			}

osku's avatar
osku committed
315 316
			/* NOTE that if the thread ends up waiting for a lock
			we will release dict_operation_lock temporarily!
317
			But the inc_fk_checks() protects foreign_table from
osku's avatar
osku committed
318
			being dropped while the check is running. */
319

320 321
			err = row_ins_check_foreign_constraint(
				FALSE, foreign, table, entry, thr);
osku's avatar
osku committed
322

323
			if (foreign_table) {
324
				foreign_table->dec_fk_checks();
325
			}
326
			if (ref_table != NULL) {
327
				dict_table_close(ref_table, FALSE, FALSE);
osku's avatar
osku committed
328 329
			}

330
			if (err != DB_SUCCESS) {
331
				goto func_exit;
osku's avatar
osku committed
332 333 334 335
			}
		}
	}

336
	err = DB_SUCCESS;
337

338 339 340 341 342 343
func_exit:
	if (got_s_lock) {
		row_mysql_unfreeze_data_dictionary(trx);
	}

	mem_heap_free(heap);
344

345 346
	DEBUG_SYNC_C("foreign_constraint_check_for_update_done");
	DBUG_RETURN(err);
347
}
348

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
#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;

Sergei Golubchik's avatar
Sergei Golubchik committed
373
	if (table->foreign_set.empty()) {
374 375 376 377 378
		return(DB_SUCCESS);
	}

	trx = thr_get_trx(thr);

379
	/* TODO: make native slave thread bail out here */
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398

	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);
	}

399
	for (dict_foreign_set::iterator it = table->foreign_set.begin();
Sergei Golubchik's avatar
Sergei Golubchik committed
400
	     it != table->foreign_set.end();
401
	     ++it) {
402

403
		foreign = *it;
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
		/* 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);
420
				opened = (foreign->referenced_table) ? TRUE : FALSE;
421 422 423 424 425 426 427 428 429 430 431 432
			}

			/* 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) {
				if (opened == TRUE) {
433
					dict_table_close(foreign->referenced_table, FALSE, FALSE);
434 435 436 437 438 439 440 441 442 443 444
					opened = FALSE;
				}
			}

			if (err != DB_SUCCESS) {
				goto func_exit;
			}
		}
	}

	err = DB_SUCCESS;
445
func_exit:
osku's avatar
osku committed
446 447 448 449 450
	if (got_s_lock) {
		row_mysql_unfreeze_data_dictionary(trx);
	}

	mem_heap_free(heap);
451

452
	return(err);
osku's avatar
osku committed
453
}
454 455 456 457 458

/** Determine if a FOREIGN KEY constraint needs to be processed.
@param[in]	node	query node
@param[in]	trx	transaction
@return	whether the node cannot be ignored */
459 460

inline bool wsrep_must_process_fk(const upd_node_t* node, const trx_t* trx)
461
{
462
	if (!wsrep_on_trx(trx)) {
463 464
		return false;
	}
465 466 467
	return que_node_get_type(node->common.parent) != QUE_NODE_UPDATE
		|| static_cast<upd_node_t*>(node->common.parent)->cascade_node
		!= node;
468
}
469
#endif /* WITH_WSREP */
osku's avatar
osku committed
470

471
/*********************************************************************//**
472
Creates an update node for a query graph.
473
@return own: update node */
osku's avatar
osku committed
474 475 476
upd_node_t*
upd_node_create(
/*============*/
477
	mem_heap_t*	heap)	/*!< in: mem heap where created */
osku's avatar
osku committed
478 479 480
{
	upd_node_t*	node;

481
	node = static_cast<upd_node_t*>(
482
		mem_heap_zalloc(heap, sizeof(upd_node_t)));
483

osku's avatar
osku committed
484 485 486
	node->common.type = QUE_NODE_UPDATE;
	node->state = UPD_NODE_UPDATE_CLUSTERED;
	node->heap = mem_heap_create(128);
487
	node->magic_n = UPD_NODE_MAGIC_N;
osku's avatar
osku committed
488 489 490 491

	return(node);
}

492
/*********************************************************************//**
osku's avatar
osku committed
493 494 495 496 497
Updates the trx id and roll ptr field in a clustered index record in database
recovery. */
void
row_upd_rec_sys_fields_in_recovery(
/*===============================*/
498 499 500 501 502 503
	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 */
osku's avatar
osku committed
504
{
505 506
	ut_ad(rec_offs_validate(rec, NULL, offsets));

507
	if (page_zip) {
508 509
		page_zip_write_trx_id_and_roll_ptr(
			page_zip, rec, offsets, pos, trx_id, roll_ptr);
510 511 512
	} else {
		byte*	field;
		ulint	len;
osku's avatar
osku committed
513

514 515
		field = rec_get_nth_field(rec, offsets, pos, &len);
		ut_ad(len == DATA_TRX_ID_LEN);
516
		compile_time_assert(DATA_TRX_ID + 1 == DATA_ROLL_PTR);
517 518 519
		trx_write_trx_id(field, trx_id);
		trx_write_roll_ptr(field + DATA_TRX_ID_LEN, roll_ptr);
	}
osku's avatar
osku committed
520 521
}

522
/*********************************************************************//**
osku's avatar
osku committed
523 524 525 526
Sets the trx id or roll ptr field of a clustered index entry. */
void
row_upd_index_entry_sys_field(
/*==========================*/
527 528
	dtuple_t*	entry,	/*!< in/out: index entry, where the memory
				buffers for sys fields are already allocated:
osku's avatar
osku committed
529 530
				the function just copies the new values to
				them */
531 532
	dict_index_t*	index,	/*!< in: clustered index */
	ulint		type,	/*!< in: DATA_TRX_ID or DATA_ROLL_PTR */
533
	ib_uint64_t	val)	/*!< in: value to write */
osku's avatar
osku committed
534 535 536 537 538
{
	dfield_t*	dfield;
	byte*		field;
	ulint		pos;

539
	ut_ad(dict_index_is_clust(index));
osku's avatar
osku committed
540 541 542

	pos = dict_index_get_sys_col_pos(index, type);

543
	dfield = dtuple_get_nth_field(entry, pos);
544
	field = static_cast<byte*>(dfield_get_data(dfield));
osku's avatar
osku committed
545 546

	if (type == DATA_TRX_ID) {
547
		ut_ad(val > 0);
548
		trx_write_trx_id(field, val);
osku's avatar
osku committed
549 550
	} else {
		ut_ad(type == DATA_ROLL_PTR);
551
		trx_write_roll_ptr(field, val);
osku's avatar
osku committed
552 553 554
	}
}

555
/***********************************************************//**
osku's avatar
osku committed
556
Returns TRUE if row update changes size of some field in index or if some
557
field to be updated is stored externally in rec or update.
558 559
@return TRUE if the update changes the size of some field in index or
the field is external in rec or update */
osku's avatar
osku committed
560 561 562
ibool
row_upd_changes_field_size_or_external(
/*===================================*/
563 564 565
	dict_index_t*	index,	/*!< in: index */
	const ulint*	offsets,/*!< in: rec_get_offsets(rec, index) */
	const upd_t*	update)	/*!< in: update vector */
osku's avatar
osku committed
566
{
567 568 569 570 571 572
	const upd_field_t*	upd_field;
	const dfield_t*		new_val;
	ulint			old_len;
	ulint			new_len;
	ulint			n_fields;
	ulint			i;
osku's avatar
osku committed
573 574

	ut_ad(rec_offs_validate(NULL, index, offsets));
575
	ut_ad(!index->table->skip_alter_undo);
osku's avatar
osku committed
576 577 578 579 580
	n_fields = upd_get_n_fields(update);

	for (i = 0; i < n_fields; i++) {
		upd_field = upd_get_nth_field(update, i);

581 582 583
		/* We should ignore virtual field if the index is not
		a virtual index */
		if (upd_fld_is_virtual_col(upd_field)
584
		    && !index->has_virtual()) {
585 586 587
			continue;
		}

osku's avatar
osku committed
588
		new_val = &(upd_field->new_val);
589 590 591
		if (dfield_is_ext(new_val)) {
			return(TRUE);
		}
592
		new_len = dfield_get_len(new_val);
593
		ut_ad(new_len != UNIV_SQL_DEFAULT);
osku's avatar
osku committed
594

595
		if (dfield_is_null(new_val) && !rec_offs_comp(offsets)) {
osku's avatar
osku committed
596 597 598 599 600
			/* 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. */

601 602
			new_len = dict_col_get_sql_null_size(
				dict_index_get_nth_col(index,
603 604
						       upd_field->field_no),
				0);
osku's avatar
osku committed
605 606
		}

607 608 609 610 611
		if (rec_offs_nth_default(offsets, upd_field->field_no)) {
			/* This is an instantly added column that is
			at the initial default value. */
			return(TRUE);
		}
osku's avatar
osku committed
612 613

		if (rec_offs_comp(offsets)
614
		    && rec_offs_nth_sql_null(offsets, upd_field->field_no)) {
osku's avatar
osku committed
615 616 617 618 619 620 621 622
			/* 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;
623 624 625
		} else {
			old_len = rec_offs_nth_size(offsets,
						    upd_field->field_no);
osku's avatar
osku committed
626 627
		}

628
		if (old_len != new_len
629
		    || rec_offs_nth_extern(offsets, upd_field->field_no)) {
osku's avatar
osku committed
630 631 632 633 634 635 636

			return(TRUE);
		}
	}

	return(FALSE);
}
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

/***********************************************************//**
Returns true if row update contains disowned external fields.
@return true if the update contains disowned external fields. */
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);
}
osku's avatar
osku committed
677

678
/***********************************************************//**
679 680 681 682 683
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(). */
osku's avatar
osku committed
684 685 686
void
row_upd_rec_in_place(
/*=================*/
687 688 689 690 691
	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
692
				available, or NULL */
osku's avatar
osku committed
693
{
694 695 696 697
	const upd_field_t*	upd_field;
	const dfield_t*		new_val;
	ulint			n_fields;
	ulint			i;
osku's avatar
osku committed
698

marko's avatar
marko committed
699
	ut_ad(rec_offs_validate(rec, index, offsets));
700
	ut_ad(!index->table->skip_alter_undo);
osku's avatar
osku committed
701

marko's avatar
marko committed
702
	if (rec_offs_comp(offsets)) {
703 704 705 706 707 708 709
#ifdef UNIV_DEBUG
		switch (rec_get_status(rec)) {
		case REC_STATUS_ORDINARY:
			break;
		case REC_STATUS_COLUMNS_ADDED:
			ut_ad(index->is_instant());
			break;
710
		case REC_STATUS_NODE_PTR:
711
			if (index->is_dummy
712 713 714 715 716 717 718 719 720
			    && fil_page_get_type(page_align(rec))
			    == FIL_PAGE_RTREE) {
				/* The function rtr_update_mbr_field_in_place()
				is generating MLOG_COMP_REC_UPDATE_IN_PLACE
				and MLOG_REC_UPDATE_IN_PLACE records for
				node pointer pages. */
				break;
			}
			/* fall through */
721 722 723 724 725 726
		case REC_STATUS_INFIMUM:
		case REC_STATUS_SUPREMUM:
			ut_ad(!"wrong record status in update");
		}
#endif /* UNIV_DEBUG */

727
		rec_set_info_bits_new(rec, update->info_bits);
marko's avatar
marko committed
728 729 730
	} else {
		rec_set_info_bits_old(rec, update->info_bits);
	}
osku's avatar
osku committed
731 732 733 734 735

	n_fields = upd_get_n_fields(update);

	for (i = 0; i < n_fields; i++) {
		upd_field = upd_get_nth_field(update, i);
736 737 738 739 740 741 742

		/* No need to update virtual columns for non-virtual index */
		if (upd_fld_is_virtual_col(upd_field)
		    && !dict_index_has_virtual(index)) {
			continue;
		}

osku's avatar
osku committed
743
		new_val = &(upd_field->new_val);
744
		ut_ad(!dfield_is_ext(new_val) ==
745
		      !rec_offs_nth_extern(offsets, upd_field->field_no));
746

osku's avatar
osku committed
747
		rec_set_nth_field(rec, offsets, upd_field->field_no,
748 749
				  dfield_get_data(new_val),
				  dfield_get_len(new_val));
osku's avatar
osku committed
750
	}
751

752
	if (page_zip) {
753
		page_zip_write_rec(page_zip, rec, index, offsets, 0);
754
	}
osku's avatar
osku committed
755 756
}

757
/*********************************************************************//**
osku's avatar
osku committed
758
Writes into the redo log the values of trx id and roll ptr and enough info
759
to determine their positions within a clustered index record.
760
@return new pointer to mlog */
osku's avatar
osku committed
761 762 763
byte*
row_upd_write_sys_vals_to_log(
/*==========================*/
764
	dict_index_t*	index,	/*!< in: clustered index */
765
	trx_id_t	trx_id,	/*!< in: transaction id */
766 767
	roll_ptr_t	roll_ptr,/*!< in: roll ptr of the undo log record */
	byte*		log_ptr,/*!< pointer to a buffer of size > 20 opened
osku's avatar
osku committed
768
				in mlog */
Sergei Golubchik's avatar
Sergei Golubchik committed
769
	mtr_t*		mtr MY_ATTRIBUTE((unused))) /*!< in: mtr */
osku's avatar
osku committed
770
{
771
	ut_ad(dict_index_is_clust(index));
osku's avatar
osku committed
772 773 774
	ut_ad(mtr);

	log_ptr += mach_write_compressed(log_ptr,
775 776
					 dict_index_get_sys_col_pos(
						 index, DATA_TRX_ID));
osku's avatar
osku committed
777

778
	trx_write_roll_ptr(log_ptr, roll_ptr);
779
	log_ptr += DATA_ROLL_PTR_LEN;
osku's avatar
osku committed
780

781
	log_ptr += mach_u64_write_compressed(log_ptr, trx_id);
osku's avatar
osku committed
782 783 784 785

	return(log_ptr);
}

786
/*********************************************************************//**
787
Parses the log data of system field values.
788
@return log data end or NULL */
osku's avatar
osku committed
789 790 791
byte*
row_upd_parse_sys_vals(
/*===================*/
792 793
	const byte*	ptr,	/*!< in: buffer */
	const byte*	end_ptr,/*!< in: buffer end */
794 795 796
	ulint*		pos,	/*!< out: TRX_ID position in record */
	trx_id_t*	trx_id,	/*!< out: trx id */
	roll_ptr_t*	roll_ptr)/*!< out: roll ptr */
osku's avatar
osku committed
797
{
798
	*pos = mach_parse_compressed(&ptr, end_ptr);
osku's avatar
osku committed
799 800 801 802 803 804 805 806 807 808

	if (ptr == NULL) {

		return(NULL);
	}

	if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {

		return(NULL);
	}
809

osku's avatar
osku committed
810
	*roll_ptr = trx_read_roll_ptr(ptr);
811
	ptr += DATA_ROLL_PTR_LEN;
osku's avatar
osku committed
812

813
	*trx_id = mach_u64_parse_compressed(&ptr, end_ptr);
osku's avatar
osku committed
814

815
	return(const_cast<byte*>(ptr));
osku's avatar
osku committed
816 817
}

818
/***********************************************************//**
osku's avatar
osku committed
819 820 821 822
Writes to the redo log the new values of the fields occurring in the index. */
void
row_upd_index_write_log(
/*====================*/
823 824
	const upd_t*	update,	/*!< in: update vector */
	byte*		log_ptr,/*!< in: pointer to mlog buffer: must
825 826 827
				contain at least MLOG_BUF_MARGIN bytes
				of free space; the buffer is closed
				within this function */
828
	mtr_t*		mtr)	/*!< in: mtr into whose log to write */
osku's avatar
osku committed
829
{
830 831 832 833 834 835
	const upd_field_t*	upd_field;
	const dfield_t*		new_val;
	ulint			len;
	ulint			n_fields;
	byte*			buf_end;
	ulint			i;
osku's avatar
osku committed
836 837 838 839

	n_fields = upd_get_n_fields(update);

	buf_end = log_ptr + MLOG_BUF_MARGIN;
840

osku's avatar
osku committed
841 842 843
	mach_write_to_1(log_ptr, update->info_bits);
	log_ptr++;
	log_ptr += mach_write_compressed(log_ptr, n_fields);
844

osku's avatar
osku committed
845
	for (i = 0; i < n_fields; i++) {
846
		compile_time_assert(MLOG_BUF_MARGIN > 30);
osku's avatar
osku committed
847 848 849

		if (log_ptr + 30 > buf_end) {
			mlog_close(mtr, log_ptr);
850

osku's avatar
osku committed
851 852 853 854 855 856 857 858
			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);

859
		len = dfield_get_len(new_val);
osku's avatar
osku committed
860

861 862 863
		/* If this is a virtual column, mark it using special
		field_no */
		ulint	field_no = upd_fld_is_virtual_col(upd_field)
864 865
			? REC_MAX_N_FIELDS + unsigned(upd_field->field_no)
			: unsigned(upd_field->field_no);
866 867

		log_ptr += mach_write_compressed(log_ptr, field_no);
osku's avatar
osku committed
868 869 870 871
		log_ptr += mach_write_compressed(log_ptr, len);

		if (len != UNIV_SQL_NULL) {
			if (log_ptr + len < buf_end) {
872
				memcpy(log_ptr, dfield_get_data(new_val), len);
osku's avatar
osku committed
873 874 875 876

				log_ptr += len;
			} else {
				mlog_close(mtr, log_ptr);
877

878 879 880 881 882
				mlog_catenate_string(
					mtr,
					static_cast<byte*>(
						dfield_get_data(new_val)),
					len);
osku's avatar
osku committed
883 884 885 886 887 888 889 890 891 892

				log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
				buf_end = log_ptr + MLOG_BUF_MARGIN;
			}
		}
	}

	mlog_close(mtr, log_ptr);
}

893
/*********************************************************************//**
894
Parses the log data written by row_upd_index_write_log.
895
@return log data end or NULL */
osku's avatar
osku committed
896 897 898
byte*
row_upd_index_parse(
/*================*/
899 900
	const byte*	ptr,	/*!< in: buffer */
	const byte*	end_ptr,/*!< in: buffer end */
901
	mem_heap_t*	heap,	/*!< in: memory heap where update vector is
osku's avatar
osku committed
902
				built */
903
	upd_t**		update_out)/*!< out: update vector */
osku's avatar
osku committed
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
{
	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++;
920
	n_fields = mach_parse_compressed(&ptr, end_ptr);
osku's avatar
osku committed
921 922 923 924 925 926 927 928 929 930

	if (ptr == NULL) {

		return(NULL);
	}

	update = upd_create(n_fields, heap);
	update->info_bits = info_bits;

	for (i = 0; i < n_fields; i++) {
931
		ulint	field_no;
osku's avatar
osku committed
932 933 934
		upd_field = upd_get_nth_field(update, i);
		new_val = &(upd_field->new_val);

935
		field_no = mach_parse_compressed(&ptr, end_ptr);
936

osku's avatar
osku committed
937 938 939 940 941
		if (ptr == NULL) {

			return(NULL);
		}

942 943 944 945 946 947 948
		/* Check if this is a virtual column, mark the prtype
		if that is the case */
		if (field_no >= REC_MAX_N_FIELDS) {
			new_val->type.prtype |= DATA_VIRTUAL;
			field_no -= REC_MAX_N_FIELDS;
		}

949 950
		upd_field->field_no = field_no;

951
		len = mach_parse_compressed(&ptr, end_ptr);
osku's avatar
osku committed
952 953 954 955 956 957 958 959 960 961 962 963

		if (ptr == NULL) {

			return(NULL);
		}

		if (len != UNIV_SQL_NULL) {

			if (end_ptr < ptr + len) {

				return(NULL);
			}
964 965 966 967 968 969

			dfield_set_data(new_val,
					mem_heap_dup(heap, ptr, len), len);
			ptr += len;
		} else {
			dfield_set_null(new_val);
osku's avatar
osku committed
970 971 972 973 974
		}
	}

	*update_out = update;

975
	return(const_cast<byte*>(ptr));
osku's avatar
osku committed
976 977
}

978
/***************************************************************//**
osku's avatar
osku committed
979 980
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
981
the fields as binary strings!
982
@return own: update vector of differing fields */
osku's avatar
osku committed
983 984 985
upd_t*
row_upd_build_sec_rec_difference_binary(
/*====================================*/
986
	const rec_t*	rec,	/*!< in: secondary index record */
987
	dict_index_t*	index,	/*!< in: index */
988
	const ulint*	offsets,/*!< in: rec_get_offsets(rec, index) */
989 990
	const dtuple_t*	entry,	/*!< in: entry to insert */
	mem_heap_t*	heap)	/*!< in: memory heap from which allocated */
osku's avatar
osku committed
991 992
{
	upd_field_t*	upd_field;
993 994
	const dfield_t*	dfield;
	const byte*	data;
osku's avatar
osku committed
995 996 997 998 999 1000
	ulint		len;
	upd_t*		update;
	ulint		n_diff;
	ulint		i;

	/* This function is used only for a secondary index */
1001
	ut_a(!dict_index_is_clust(index));
1002 1003 1004
	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));
1005
	ut_ad(!rec_offs_any_default(offsets));
1006
	ut_ad(!index->table->skip_alter_undo);
osku's avatar
osku committed
1007 1008 1009 1010 1011 1012 1013

	update = upd_create(dtuple_get_n_fields(entry), heap);

	n_diff = 0;

	for (i = 0; i < dtuple_get_n_fields(entry); i++) {

1014
		data = rec_get_nth_field(rec, offsets, i, &len);
osku's avatar
osku committed
1015 1016 1017 1018 1019 1020 1021 1022 1023

		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
1024
		row0upd.cc line 713 in versions 4.0.14 - 4.0.16. */
osku's avatar
osku committed
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034

		/* 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);

1035
			upd_field_set_field_no(upd_field, i, index);
osku's avatar
osku committed
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045

			n_diff++;
		}
	}

	update->n_fields = n_diff;

	return(update);
}

1046
/** Builds an update vector from those fields, excluding the roll ptr and
osku's avatar
osku committed
1047
trx id fields, which in an index entry differ from a record that has
1048
the equal ordering fields. NOTE: we compare the fields as binary strings!
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
@param[in]	index		clustered index
@param[in]	entry		clustered index entry to insert
@param[in]	rec		clustered index record
@param[in]	offsets		rec_get_offsets(rec,index), or NULL
@param[in]	no_sys		skip the system columns
				DB_TRX_ID and DB_ROLL_PTR
@param[in]	trx		transaction (for diagnostics),
				or NULL
@param[in]	heap		memory heap from which allocated
@param[in]	mysql_table	NULL, or mysql table object when
				user thread invokes dml
1060 1061
@return own: update vector of differing fields, excluding roll ptr and
trx id */
1062
upd_t*
osku's avatar
osku committed
1063
row_upd_build_difference_binary(
1064 1065 1066 1067 1068 1069 1070 1071
	dict_index_t*	index,
	const dtuple_t*	entry,
	const rec_t*	rec,
	const ulint*	offsets,
	bool		no_sys,
	trx_t*		trx,
	mem_heap_t*	heap,
	TABLE*		mysql_table)
osku's avatar
osku committed
1072 1073
{
	upd_field_t*	upd_field;
1074
	dfield_t*	dfield;
1075
	const byte*	data;
osku's avatar
osku committed
1076 1077 1078 1079 1080 1081
	ulint		len;
	upd_t*		update;
	ulint		n_diff;
	ulint		trx_id_pos;
	ulint		i;
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
1082 1083
	ulint		n_fld = dtuple_get_n_fields(entry);
	ulint		n_v_fld = dtuple_get_n_v_fields(entry);
1084
	rec_offs_init(offsets_);
osku's avatar
osku committed
1085 1086

	/* This function is used only for a clustered index */
1087
	ut_a(dict_index_is_clust(index));
1088
	ut_ad(!index->table->skip_alter_undo);
osku's avatar
osku committed
1089

1090
	update = upd_create(n_fld + n_v_fld, heap);
osku's avatar
osku committed
1091 1092 1093 1094

	n_diff = 0;

	trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
1095 1096
	ut_ad(dict_index_get_sys_col_pos(index, DATA_ROLL_PTR)
	      == trx_id_pos + 1);
osku's avatar
osku committed
1097

1098
	if (!offsets) {
1099
		offsets = rec_get_offsets(rec, index, offsets_, true,
1100 1101 1102 1103
					  ULINT_UNDEFINED, &heap);
	} else {
		ut_ad(rec_offs_validate(rec, index, offsets));
	}
osku's avatar
osku committed
1104

1105
	for (i = 0; i < n_fld; i++) {
1106
		data = rec_get_nth_cfield(rec, index, offsets, i, &len);
osku's avatar
osku committed
1107 1108 1109 1110 1111

		dfield = dtuple_get_nth_field(entry, i);

		/* NOTE: we compare the fields as binary strings!
		(No collation) */
1112 1113 1114 1115 1116
		if (no_sys) {
			/* TRX_ID */
			if (i == trx_id_pos) {
				continue;
			}
osku's avatar
osku committed
1117

1118
			/* DB_ROLL_PTR */
1119
			if (i == trx_id_pos + 1) {
1120 1121
				continue;
			}
osku's avatar
osku committed
1122 1123
		}

1124 1125
		if (!dfield_is_ext(dfield)
		    != !rec_offs_nth_extern(offsets, i)
1126
		    || !dfield_data_is_binary_equal(dfield, len, data)) {
osku's avatar
osku committed
1127 1128 1129 1130 1131

			upd_field = upd_get_nth_field(update, n_diff);

			dfield_copy(&(upd_field->new_val), dfield);

1132
			upd_field_set_field_no(upd_field, i, index);
osku's avatar
osku committed
1133 1134 1135 1136 1137

			n_diff++;
		}
	}

1138 1139 1140 1141 1142
	/* Check the virtual columns updates. Even if there is no non-virtual
	column (base columns) change, we will still need to build the
	indexed virtual column value so that undo log would log them (
	for purge/mvcc purpose) */
	if (n_v_fld > 0) {
1143 1144
		row_ext_t*	ext;
		mem_heap_t*	v_heap = NULL;
1145 1146 1147 1148 1149 1150 1151
		THD*		thd;

		if (trx == NULL) {
			thd = current_thd;
		} else {
			thd = trx->mysql_thd;
		}
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171

		ut_ad(!update->old_vrow);

		for (i = 0; i < n_v_fld; i++) {
			const dict_v_col_t*     col
                                = dict_table_get_nth_v_col(index->table, i);

			if (!col->m_col.ord_part) {
				continue;
			}

			if (update->old_vrow == NULL) {
				update->old_vrow = row_build(
					ROW_COPY_POINTERS, index, rec, offsets,
					index->table, NULL, NULL, &ext, heap);
			}

			dfield = dtuple_get_nth_v_field(entry, i);

			dfield_t*	vfield = innobase_get_computed_value(
1172 1173 1174
				update->old_vrow, col, index,
				&v_heap, heap, NULL, thd, mysql_table,
				NULL, NULL, NULL);
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202

			if (!dfield_data_is_binary_equal(
				dfield, vfield->len,
				static_cast<byte*>(vfield->data))) {
				upd_field = upd_get_nth_field(update, n_diff);

				upd_field->old_v_val = static_cast<dfield_t*>(
					mem_heap_alloc(
						heap,
						sizeof *upd_field->old_v_val));

				dfield_copy(upd_field->old_v_val, vfield);

				dfield_copy(&(upd_field->new_val), dfield);

				upd_field_set_v_field_no(
					upd_field, i, index);

				n_diff++;

			}
		}

		if (v_heap) {
			mem_heap_free(v_heap);
		}
	}

osku's avatar
osku committed
1203
	update->n_fields = n_diff;
1204
	ut_ad(update->validate());
osku's avatar
osku committed
1205 1206 1207 1208

	return(update);
}

1209 1210
/** Fetch a prefix of an externally stored column.
This is similar to row_ext_lookup(), but the row_ext_t holds the old values
1211
of the column and must not be poisoned with the new values.
1212 1213 1214 1215 1216 1217 1218 1219
@param[in]	data		'internally' stored part of the field
containing also the reference to the external part
@param[in]	local_len	length of data, in bytes
@param[in]	page_size	BLOB page size
@param[in,out]	len		input - length of prefix to
fetch; output: fetched length of the prefix
@param[in,out]	heap		heap where to allocate
@return BLOB prefix */
1220 1221 1222
static
byte*
row_upd_ext_fetch(
1223 1224 1225 1226 1227
	const byte*		data,
	ulint			local_len,
	const page_size_t&	page_size,
	ulint*			len,
	mem_heap_t*		heap)
1228
{
1229 1230 1231
	byte*	buf = static_cast<byte*>(mem_heap_alloc(heap, *len));

	*len = btr_copy_externally_stored_field_prefix(
1232
		buf, *len, page_size, data, local_len);
1233

1234 1235
	/* We should never update records containing a half-deleted BLOB. */
	ut_a(*len);
1236 1237 1238 1239

	return(buf);
}

1240 1241 1242 1243 1244 1245 1246 1247 1248
/** Replaces the new column value stored in the update vector in
the given index entry field.
@param[in,out]	dfield		data field of the index entry
@param[in]	field		index field
@param[in]	col		field->col
@param[in]	uf		update field
@param[in,out]	heap		memory heap for allocating and copying
the new value
@param[in]	page_size	page size */
1249 1250 1251
static
void
row_upd_index_replace_new_col_val(
1252 1253 1254 1255 1256 1257
	dfield_t*		dfield,
	const dict_field_t*	field,
	const dict_col_t*	col,
	const upd_field_t*	uf,
	mem_heap_t*		heap,
	const page_size_t&	page_size)
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
{
	ulint		len;
	const byte*	data;

	dfield_copy_data(dfield, &uf->new_val);

	if (dfield_is_null(dfield)) {
		return;
	}

	len = dfield_get_len(dfield);
1269
	data = static_cast<const byte*>(dfield_get_data(dfield));
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280

	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;

1281
			data = row_upd_ext_fetch(data, l, page_size,
1282 1283 1284 1285
						 &len, heap);
		}

		len = dtype_get_at_most_n_mbchars(col->prtype,
1286
						  col->mbminlen, col->mbmaxlen,
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319
						  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);
1320 1321
		buf = static_cast<byte*>(mem_heap_alloc(heap, uf->orig_len));

1322 1323
		/* Copy the locally stored prefix. */
		memcpy(buf, data,
1324
		       unsigned(uf->orig_len) - BTR_EXTERN_FIELD_REF_SIZE);
1325

1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
		/* 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;
	}
}

1337 1338 1339 1340 1341 1342 1343
/** Apply an update vector to an index entry.
@param[in,out]	entry	index entry to be updated; the clustered index record
			must be covered by a lock or a page latch to prevent
			deletion (rollback or purge)
@param[in]	index	index of the entry
@param[in]	update	update vector built for the entry
@param[in,out]	heap	memory heap for copying off-page columns */
osku's avatar
osku committed
1344 1345
void
row_upd_index_replace_new_col_vals_index_pos(
1346 1347 1348 1349
	dtuple_t*		entry,
	const dict_index_t*	index,
	const upd_t*		update,
	mem_heap_t*		heap)
osku's avatar
osku committed
1350
{
1351 1352
	ut_ad(!index->table->skip_alter_undo);

1353
	const page_size_t&	page_size = dict_table_page_size(index->table);
osku's avatar
osku committed
1354 1355 1356

	dtuple_set_info_bits(entry, update->info_bits);

1357
	for (unsigned i = index->n_fields; i--; ) {
1358 1359 1360 1361 1362 1363
		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);
1364
		if (col->is_virtual()) {
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
			const dict_v_col_t*	vcol = reinterpret_cast<
							const dict_v_col_t*>(
								col);

			uf = upd_get_field_by_field_no(
				update, vcol->v_pos, true);
		} else {
			uf = upd_get_field_by_field_no(
				update, i, false);
		}
1375 1376 1377 1378

		if (uf) {
			row_upd_index_replace_new_col_val(
				dtuple_get_nth_field(entry, i),
1379
				field, col, uf, heap, page_size);
osku's avatar
osku committed
1380 1381 1382 1383
		}
	}
}

1384
/***********************************************************//**
osku's avatar
osku committed
1385 1386 1387 1388 1389
Replaces the new column values stored in the update vector to the index entry
given. */
void
row_upd_index_replace_new_col_vals(
/*===============================*/
1390
	dtuple_t*	entry,	/*!< in/out: index entry where replaced;
1391 1392 1393
				the clustered index record must be
				covered by a lock or a page latch to
				prevent deletion (rollback or purge) */
1394
	dict_index_t*	index,	/*!< in: index; NOTE that this may also be a
osku's avatar
osku committed
1395
				non-clustered index */
1396
	const upd_t*	update,	/*!< in: an update vector built for the
osku's avatar
osku committed
1397 1398
				CLUSTERED index so that the field number in
				an upd_field is the clustered index position */
1399
	mem_heap_t*	heap)	/*!< in: memory heap for allocating and
1400
				copying the new values */
osku's avatar
osku committed
1401
{
1402 1403 1404
	ulint			i;
	const dict_index_t*	clust_index
		= dict_table_get_first_index(index->table);
1405
	const page_size_t&	page_size = dict_table_page_size(index->table);
1406

1407 1408
	ut_ad(!index->table->skip_alter_undo);

osku's avatar
osku committed
1409 1410
	dtuple_set_info_bits(entry, update->info_bits);

1411 1412 1413 1414
	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;
osku's avatar
osku committed
1415

1416 1417
		field = dict_index_get_nth_field(index, i);
		col = dict_field_get_col(field);
1418
		if (col->is_virtual()) {
1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
			const dict_v_col_t*	vcol = reinterpret_cast<
							const dict_v_col_t*>(
								col);

			uf = upd_get_field_by_field_no(
				update, vcol->v_pos, true);
		} else {
			uf = upd_get_field_by_field_no(
				update,
				dict_col_get_clust_pos(col, clust_index),
				false);
		}
1431

1432 1433 1434
		if (uf) {
			row_upd_index_replace_new_col_val(
				dtuple_get_nth_field(entry, i),
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
				field, col, uf, heap, page_size);
		}
	}
}

/** Replaces the virtual column values stored in the update vector.
@param[in,out]	row	row whose column to be set
@param[in]	field	data to set
@param[in]	len	data length
@param[in]	vcol	virtual column info */
static
void
row_upd_set_vcol_data(
	dtuple_t*		row,
	const byte*             field,
	ulint                   len,
	dict_v_col_t*		vcol)
{
	dfield_t*	dfield = dtuple_get_nth_v_field(row, vcol->v_pos);

	if (dfield_get_type(dfield)->mtype == DATA_MISSING) {
		dict_col_copy_type(&vcol->m_col, dfield_get_type(dfield));

		dfield_set_data(dfield, field, len);
	}
}

/** Replaces the virtual column values stored in a dtuple with that of
a update vector.
@param[in,out]	row	row whose column to be updated
@param[in]	table	table
@param[in]	update	an update vector built for the clustered index
@param[in]	upd_new	update to new or old value
@param[in,out]	undo_row undo row (if needs to be updated)
@param[in]	ptr	remaining part in update undo log */
void
row_upd_replace_vcol(
	dtuple_t*		row,
	const dict_table_t*	table,
	const upd_t*		update,
	bool			upd_new,
	dtuple_t*		undo_row,
	const byte*		ptr)
{
	ulint			col_no;
	ulint			i;
	ulint			n_cols;

1483 1484
	ut_ad(!table->skip_alter_undo);

1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
	n_cols = dtuple_get_n_v_fields(row);
	for (col_no = 0; col_no < n_cols; col_no++) {
		dfield_t*		dfield;

		const dict_v_col_t*	col
			= dict_table_get_nth_v_col(table, col_no);

		/* If there is no index on the column, do not bother for
		value update */
		if (!col->m_col.ord_part) {
1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
			dict_index_t*	clust_index
				= dict_table_get_first_index(table);

			/* Skip the column if there is no online alter
			table in progress or it is not being indexed
			in new table */
			if (!dict_index_is_online_ddl(clust_index)
			    || !row_log_col_is_indexed(clust_index, col_no)) {
				continue;
			}
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
		}

		dfield = dtuple_get_nth_v_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_fld_is_virtual_col(upd_field)
			    || upd_field->field_no != col->v_pos) {
				continue;
			}

			if (upd_new) {
				dfield_copy_data(dfield, &upd_field->new_val);
			} else {
				dfield_copy_data(dfield, upd_field->old_v_val);
			}

1523
			dfield->type = upd_field->new_val.type;
1524 1525 1526 1527 1528
			break;
		}
	}

	bool	first_v_col = true;
1529
	bool	is_undo_log = true;
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550

	/* We will read those unchanged (but indexed) virtual columns in */
	if (ptr != NULL) {
		const byte*	end_ptr;

		end_ptr = ptr + mach_read_from_2(ptr);
		ptr += 2;

		while (ptr != end_ptr) {
			const byte*             field;
			ulint                   field_no;
			ulint                   len;
			ulint                   orig_len;
			bool			is_v;

			field_no = mach_read_next_compressed(&ptr);

			is_v = (field_no >= REC_MAX_N_FIELDS);

			if (is_v) {
				ptr = trx_undo_read_v_idx(
1551 1552
					table, ptr, first_v_col, &is_undo_log,
					&field_no);
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575
				first_v_col = false;
			}

			ptr = trx_undo_rec_get_col_val(
				ptr, &field, &len, &orig_len);

			if (field_no == ULINT_UNDEFINED) {
				ut_ad(is_v);
				continue;
			}

			if (is_v) {
				dict_v_col_t* vcol = dict_table_get_nth_v_col(
							table, field_no);

				row_upd_set_vcol_data(row, field, len, vcol);

				if (undo_row) {
					row_upd_set_vcol_data(
						undo_row, field, len, vcol);
				}
			}
			ut_ad(ptr<= end_ptr);
osku's avatar
osku committed
1576 1577 1578 1579
		}
	}
}

1580
/***********************************************************//**
1581 1582 1583 1584
Replaces the new column values stored in the update vector. */
void
row_upd_replace(
/*============*/
1585
	dtuple_t*		row,	/*!< in/out: row where replaced,
1586 1587 1588 1589
					indexed by col_no;
					the clustered index record must be
					covered by a lock or a page latch to
					prevent deletion (rollback or purge) */
1590
	row_ext_t**		ext,	/*!< out, own: NULL, or externally
1591
					stored column prefixes */
1592 1593
	const dict_index_t*	index,	/*!< in: clustered index */
	const upd_t*		update,	/*!< in: an update vector built for the
1594
					clustered index */
1595
	mem_heap_t*		heap)	/*!< in: memory heap */
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609
{
	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);
1610
	ut_ad(update->validate());
1611 1612 1613 1614 1615

	n_cols = dtuple_get_n_fields(row);
	table = index->table;
	ut_ad(n_cols == dict_table_get_n_cols(table));

1616 1617 1618
	ext_cols = static_cast<ulint*>(
		mem_heap_alloc(heap, n_cols * sizeof *ext_cols));

1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
	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);

1643 1644
			if (upd_field->field_no != clust_pos
			    || upd_fld_is_virtual_col(upd_field)) {
1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658

				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) {
1659 1660
		*ext = row_ext_create(n_ext_cols, ext_cols, table->flags, row,
				      heap);
1661 1662 1663
	} else {
		*ext = NULL;
	}
1664 1665

	row_upd_replace_vcol(row, table, update, true, NULL, NULL);
1666 1667
}

1668
/***********************************************************//**
osku's avatar
osku committed
1669
Checks if an update vector changes an ordering field of an index record.
1670

osku's avatar
osku committed
1671 1672
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.
1673
NOTE: we compare the fields as binary strings!
1674
@return TRUE if update vector changes an ordering field in the index record */
osku's avatar
osku committed
1675
ibool
1676 1677 1678 1679 1680 1681 1682 1683 1684
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 */
1685
	const dtuple_t*	row,	/*!< in: old value of row, or NULL if the
osku's avatar
osku committed
1686 1687 1688
				row and the data values in update are not
				known when this function is called, e.g., at
				compile time */
1689
	const row_ext_t*ext,	/*!< NULL, or prefixes of the externally
1690
				stored columns in the old row */
1691 1692
	ulint		flag)	/*!< in: ROW_BUILD_NORMAL,
				ROW_BUILD_FOR_PURGE or ROW_BUILD_FOR_UNDO */
osku's avatar
osku committed
1693
{
1694 1695 1696
	ulint			n_unique;
	ulint			i;
	const dict_index_t*	clust_index;
1697

1698 1699 1700
	ut_ad(thr);
	ut_ad(thr->graph);
	ut_ad(thr->graph->trx);
1701
	ut_ad(!index->table->skip_alter_undo);
osku's avatar
osku committed
1702 1703 1704

	n_unique = dict_index_get_n_unique(index);

1705 1706
	clust_index = dict_table_get_first_index(index->table);

osku's avatar
osku committed
1707 1708
	for (i = 0; i < n_unique; i++) {

1709 1710 1711
		const dict_field_t*	ind_field;
		const dict_col_t*	col;
		ulint			col_no;
1712 1713 1714
		const upd_field_t*	upd_field;
		const dfield_t*		dfield;
		dfield_t		dfield_ext;
Sergei Golubchik's avatar
Sergei Golubchik committed
1715
		ulint			dfield_len= 0;
1716
		const byte*		buf;
1717 1718
		bool			is_virtual;
		const dict_v_col_t*	vcol = NULL;
1719

osku's avatar
osku committed
1720 1721 1722
		ind_field = dict_index_get_nth_field(index, i);
		col = dict_field_get_col(ind_field);
		col_no = dict_col_get_no(col);
1723
		is_virtual = col->is_virtual();
1724 1725 1726

		if (is_virtual) {
			vcol = reinterpret_cast<const dict_v_col_t*>(col);
osku's avatar
osku committed
1727

1728 1729 1730 1731 1732 1733 1734 1735
			upd_field = upd_get_field_by_field_no(
				update, vcol->v_pos, true);
		} else {
			upd_field = upd_get_field_by_field_no(
				update,
				dict_col_get_clust_pos(col, clust_index),
				false);
		}
osku's avatar
osku committed
1736

1737 1738 1739
		if (upd_field == NULL) {
			continue;
		}
osku's avatar
osku committed
1740

1741 1742 1743 1744
		if (row == NULL) {
			ut_ad(ext == NULL);
			return(TRUE);
		}
osku's avatar
osku committed
1745

1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805
		if (is_virtual) {
			dfield = dtuple_get_nth_v_field(
				row,  vcol->v_pos);
		} else {
			dfield = dtuple_get_nth_field(row, col_no);
		}

		/* For spatial index update, since the different geometry
		data could generate same MBR, so, if the new index entry is
		same as old entry, which means the MBR is not changed, we
		don't need to do anything. */
		if (dict_index_is_spatial(index) && i == 0) {
			double		mbr1[SPDIMS * 2];
			double		mbr2[SPDIMS * 2];
			rtr_mbr_t*	old_mbr;
			rtr_mbr_t*	new_mbr;
			uchar*		dptr = NULL;
			ulint		flen = 0;
			ulint		dlen = 0;
			mem_heap_t*	temp_heap = NULL;
			const dfield_t*	new_field = &upd_field->new_val;

			const page_size_t	page_size
				= (ext != NULL)
				? ext->page_size
				: dict_table_page_size(
					index->table);

			ut_ad(dfield->data != NULL
			      && dfield->len > GEO_DATA_HEADER_SIZE);
			ut_ad(dict_col_get_spatial_status(col) != SPATIAL_NONE);

			/* Get the old mbr. */
			if (dfield_is_ext(dfield)) {
				/* For off-page stored data, we
				need to read the whole field data. */
				flen = dfield_get_len(dfield);
				dptr = static_cast<byte*>(
					dfield_get_data(dfield));
				temp_heap = mem_heap_create(1000);

				dptr = btr_copy_externally_stored_field(
					&dlen, dptr,
					page_size,
					flen,
					temp_heap);
			} else {
				dptr = static_cast<uchar*>(dfield->data);
				dlen = dfield->len;
			}

			rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE,
					   static_cast<uint>(dlen
					   - GEO_DATA_HEADER_SIZE),
					   SPDIMS, mbr1);
			old_mbr = reinterpret_cast<rtr_mbr_t*>(mbr1);

			/* Get the new mbr. */
			if (dfield_is_ext(new_field)) {
				if (flag == ROW_BUILD_FOR_UNDO
1806 1807
				    && dict_table_has_atomic_blobs(
					    index->table)) {
1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851
					/* For undo, and the table is Barrcuda,
					we need to skip the prefix data. */
					flen = BTR_EXTERN_FIELD_REF_SIZE;
					ut_ad(dfield_get_len(new_field) >=
					      BTR_EXTERN_FIELD_REF_SIZE);
					dptr = static_cast<byte*>(
						dfield_get_data(new_field))
						+ dfield_get_len(new_field)
						- BTR_EXTERN_FIELD_REF_SIZE;
				} else {
					flen = dfield_get_len(new_field);
					dptr = static_cast<byte*>(
						dfield_get_data(new_field));
				}

				if (temp_heap == NULL) {
					temp_heap = mem_heap_create(1000);
				}

				dptr = btr_copy_externally_stored_field(
					&dlen, dptr,
					page_size,
					flen,
					temp_heap);
			} else {
				dptr = static_cast<uchar*>(upd_field->new_val.data);
				dlen = upd_field->new_val.len;
			}
			rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE,
					   static_cast<uint>(dlen
					   - GEO_DATA_HEADER_SIZE),
					   SPDIMS, mbr2);
			new_mbr = reinterpret_cast<rtr_mbr_t*>(mbr2);

			if (temp_heap) {
				mem_heap_free(temp_heap);
			}

			if (!MBR_EQUAL_CMP(old_mbr, new_mbr)) {
				return(TRUE);
			} else {
				continue;
			}
		}
1852

1853 1854 1855 1856 1857 1858
		/* 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 */
1859
		} else if (ext) {
1860 1861 1862 1863
			/* Silence a compiler warning without
			silencing a Valgrind error. */
			dfield_len = 0;
			UNIV_MEM_INVALID(&dfield_len, sizeof dfield_len);
1864 1865 1866 1867 1868 1869 1870
			/* 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)) {
1871 1872 1873 1874 1875 1876 1877 1878
					/* 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));
1879 1880 1881 1882
					return(TRUE);
				}

				goto copy_dfield;
osku's avatar
osku committed
1883
			}
1884 1885 1886 1887 1888 1889
		} 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);
1890 1891

			buf = static_cast<byte*>(dfield_get_data(dfield));
1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903
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);
osku's avatar
osku committed
1904 1905 1906 1907 1908 1909
		}
	}

	return(FALSE);
}

1910
/***********************************************************//**
osku's avatar
osku committed
1911
Checks if an update vector changes an ordering field of an index record.
1912
NOTE: we compare the fields as binary strings!
1913 1914
@return TRUE if update vector may change an ordering field in an index
record */
osku's avatar
osku committed
1915 1916 1917
ibool
row_upd_changes_some_index_ord_field_binary(
/*========================================*/
1918 1919
	const dict_table_t*	table,	/*!< in: table */
	const upd_t*		update)	/*!< in: update vector for the row */
osku's avatar
osku committed
1920 1921 1922 1923
{
	upd_field_t*	upd_field;
	dict_index_t*	index;
	ulint		i;
1924

osku's avatar
osku committed
1925
	index = dict_table_get_first_index(table);
1926

osku's avatar
osku committed
1927 1928 1929 1930
	for (i = 0; i < upd_get_n_fields(update); i++) {

		upd_field = upd_get_nth_field(update, i);

1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941
		if (upd_fld_is_virtual_col(upd_field)) {
			if (dict_table_get_nth_v_col(index->table,
						     upd_field->field_no)
			    ->m_col.ord_part) {
				return(TRUE);
			}
		} else {
			if (dict_field_get_col(dict_index_get_nth_field(
				index, upd_field->field_no))->ord_part) {
				return(TRUE);
			}
osku's avatar
osku committed
1942 1943
		}
	}
1944

osku's avatar
osku committed
1945 1946 1947
	return(FALSE);
}

1948 1949
/***********************************************************//**
Checks if an FTS Doc ID column is affected by an UPDATE.
1950 1951
@return whether the Doc ID column is changed */
bool
1952 1953 1954 1955 1956 1957 1958 1959 1960
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;

1961 1962
	ut_ad(!table->skip_alter_undo);

1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
	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 */
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;

1985 1986
	ut_ad(!table->skip_alter_undo);

1987 1988 1989 1990 1991
	if (upd_fld_is_virtual_col(upd_field)) {
		col_no = upd_field->field_no;
		return(dict_table_is_fts_column(fts->indexes, col_no, true));
	} else {
		clust_index = dict_table_get_first_index(table);
1992

1993 1994 1995 1996 1997 1998
		/* 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, false));
	}
1999 2000 2001

}

2002
/***********************************************************//**
osku's avatar
osku committed
2003 2004
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
2005
that index does not contain column prefixes.
2006
@return TRUE if changes */
osku's avatar
osku committed
2007 2008 2009 2010
static
ibool
row_upd_changes_first_fields_binary(
/*================================*/
2011 2012 2013 2014
	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 */
osku's avatar
osku committed
2015 2016 2017
{
	ulint		n_upd_fields;
	ulint		i, j;
2018
	dict_index_t*	clust_index;
2019

2020 2021
	ut_ad(update && index);
	ut_ad(n <= dict_index_get_n_fields(index));
2022

osku's avatar
osku committed
2023
	n_upd_fields = upd_get_n_fields(update);
2024
	clust_index = dict_table_get_first_index(index->table);
osku's avatar
osku committed
2025 2026 2027

	for (i = 0; i < n; i++) {

2028 2029 2030 2031
		const dict_field_t*	ind_field;
		const dict_col_t*	col;
		ulint			col_pos;

osku's avatar
osku committed
2032 2033
		ind_field = dict_index_get_nth_field(index, i);
		col = dict_field_get_col(ind_field);
2034
		col_pos = dict_col_get_clust_pos(col, clust_index);
osku's avatar
osku committed
2035 2036 2037 2038 2039

		ut_a(ind_field->prefix_len == 0);

		for (j = 0; j < n_upd_fields; j++) {

2040 2041
			upd_field_t*	upd_field
				= upd_get_nth_field(update, j);
osku's avatar
osku committed
2042 2043

			if (col_pos == upd_field->field_no
2044 2045
			    && !dfield_datas_are_binary_equal(
				    dtuple_get_nth_field(entry, i),
2046
				    &upd_field->new_val, 0)) {
2047

osku's avatar
osku committed
2048 2049 2050 2051 2052 2053 2054 2055
				return(TRUE);
			}
		}
	}

	return(FALSE);
}

2056
/*********************************************************************//**
osku's avatar
osku committed
2057 2058 2059 2060 2061
Copies the column values from a record. */
UNIV_INLINE
void
row_upd_copy_columns(
/*=================*/
2062 2063
	rec_t*		rec,	/*!< in: record in a clustered index */
	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
2064
	const dict_index_t*	index, /*!< in: index of rec */
2065
	sym_node_t*	column)	/*!< in: first column in a column list, or
osku's avatar
osku committed
2066 2067
				NULL */
{
2068 2069 2070
	ut_ad(dict_index_is_clust(index));

	const byte*	data;
osku's avatar
osku committed
2071 2072 2073
	ulint	len;

	while (column) {
2074 2075 2076
		data = rec_get_nth_cfield(
			rec, index, offsets,
			column->field_nos[SYM_CLUST_FIELD_NO], &len);
osku's avatar
osku committed
2077 2078 2079 2080 2081 2082
		eval_node_copy_and_alloc_val(column, data, len);

		column = UT_LIST_GET_NEXT(col_var_list, column);
	}
}

2083
/*********************************************************************//**
osku's avatar
osku committed
2084 2085 2086 2087 2088 2089
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(
/*==================*/
2090
	upd_t*	update)	/*!< in/out: update vector */
osku's avatar
osku committed
2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109
{
	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));
	}
}

2110
/** Stores to the heap the virtual columns that need for any indexes
2111 2112 2113 2114
@param[in,out]	node		row update node
@param[in]	update		an update vector if it is update
@param[in]	thd		mysql thread handle
@param[in,out]	mysql_table	mysql table object */
Marko Mäkelä's avatar
Marko Mäkelä committed
2115
static
2116 2117 2118
void
row_upd_store_v_row(
	upd_node_t*	node,
2119 2120 2121
	const upd_t*	update,
	THD*		thd,
	TABLE*		mysql_table)
2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157
{
	mem_heap_t*	heap = NULL;
	dict_index_t*	index = dict_table_get_first_index(node->table);

	for (ulint col_no = 0; col_no < dict_table_get_n_v_cols(node->table);
	     col_no++) {

		const dict_v_col_t*     col
			= dict_table_get_nth_v_col(node->table, col_no);

		if (col->m_col.ord_part) {
			dfield_t*	dfield
				= dtuple_get_nth_v_field(node->row, col_no);
			ulint		n_upd
				= update ? upd_get_n_fields(update) : 0;
			ulint		i = 0;

			/* Check if the value is already in update vector */
			for (i = 0; i < n_upd; i++) {
				const upd_field_t*      upd_field
					= upd_get_nth_field(update, i);
				if (!(upd_field->new_val.type.prtype
				      & DATA_VIRTUAL)
				    || upd_field->field_no != col->v_pos) {
					continue;
				}

				dfield_copy_data(dfield, upd_field->old_v_val);
				break;
			}

			/* Not updated */
			if (i >= n_upd) {
				/* If this is an update, then the value
				should be in update->old_vrow */
				if (update) {
2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170
					if (update->old_vrow == NULL) {
						/* This only happens in
						cascade update. And virtual
						column can't be affected,
						so it is Ok to set it to NULL */
						dfield_set_null(dfield);
					} else {
						dfield_t*       vfield
							= dtuple_get_nth_v_field(
								update->old_vrow,
								col_no);
						dfield_copy_data(dfield, vfield);
					}
2171 2172 2173 2174
				} else {
					/* Need to compute, this happens when
					deleting row */
					innobase_get_computed_value(
2175
						node->row, col, index,
2176
						&heap, node->heap, NULL,
2177 2178
						thd, mysql_table, NULL,
						NULL, NULL);
2179 2180 2181 2182 2183 2184 2185 2186 2187 2188
				}
			}
		}
	}

	if (heap) {
		mem_heap_free(heap);
	}
}

2189 2190 2191 2192 2193
/** Stores to the heap the row on which the node->pcur is positioned.
@param[in]	node		row update node
@param[in]	thd		mysql thread handle
@param[in,out]	mysql_table	NULL, or mysql table object when
				user thread invokes dml */
osku's avatar
osku committed
2194 2195
void
row_upd_store_row(
2196 2197 2198
	upd_node_t*	node,
	THD*		thd,
	TABLE*		mysql_table)
osku's avatar
osku committed
2199 2200 2201 2202
{
	dict_index_t*	clust_index;
	rec_t*		rec;
	mem_heap_t*	heap		= NULL;
2203
	row_ext_t**	ext;
osku's avatar
osku committed
2204 2205
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
	const ulint*	offsets;
2206
	rec_offs_init(offsets_);
osku's avatar
osku committed
2207 2208 2209 2210 2211 2212

	ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);

	if (node->row != NULL) {
		mem_heap_empty(node->heap);
	}
2213

osku's avatar
osku committed
2214 2215 2216
	clust_index = dict_table_get_first_index(node->table);

	rec = btr_pcur_get_rec(node->pcur);
2217

2218
	offsets = rec_get_offsets(rec, clust_index, offsets_, true,
2219
				  ULINT_UNDEFINED, &heap);
2220

2221 2222 2223 2224
	if (dict_table_has_atomic_blobs(node->table)) {
		/* There is no prefix of externally stored columns in
		the clustered index record. Build a cache of column
		prefixes. */
2225 2226 2227 2228 2229 2230 2231 2232 2233
		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;
	}

osku's avatar
osku committed
2234
	node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
2235
			      NULL, NULL, NULL, ext, node->heap);
2236 2237

	if (node->table->n_v_cols) {
2238 2239
		row_upd_store_v_row(node, node->is_delete ? NULL : node->update,
				    thd, mysql_table);
2240 2241
	}

2242
	if (node->is_delete == PLAIN_DELETE) {
2243 2244
		node->upd_row = NULL;
		node->upd_ext = NULL;
osku's avatar
osku committed
2245
	} else {
2246 2247 2248
		node->upd_row = dtuple_copy(node->row, node->heap);
		row_upd_replace(node->upd_row, &node->upd_ext,
				clust_index, node->update, node->heap);
2249 2250
	}

osku's avatar
osku committed
2251 2252 2253 2254 2255
	if (UNIV_LIKELY_NULL(heap)) {
		mem_heap_free(heap);
	}
}

2256
/***********************************************************//**
2257
Updates a secondary index entry of a row.
2258 2259
@return DB_SUCCESS if operation successfully completed, else error
code or DB_LOCK_WAIT */
Sergei Golubchik's avatar
Sergei Golubchik committed
2260
static MY_ATTRIBUTE((nonnull, warn_unused_result))
2261
dberr_t
osku's avatar
osku committed
2262 2263
row_upd_sec_index_entry(
/*====================*/
2264 2265
	upd_node_t*	node,	/*!< in: row update node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
2266
{
2267 2268 2269 2270 2271 2272 2273 2274
	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;
2275
	dberr_t			err	= DB_SUCCESS;
2276
	trx_t*			trx	= thr_get_trx(thr);
2277
	ulint			mode;
2278
	ulint			flags;
2279
	enum row_search_result	search_result;
osku's avatar
osku committed
2280

2281
	ut_ad(trx->id != 0);
2282

osku's avatar
osku committed
2283
	index = node->index;
2284

2285
	referenced = row_upd_index_is_referenced(index, trx);
2286
#ifdef WITH_WSREP
2287
	bool foreign = wsrep_row_upd_index_is_foreign(index, trx);
2288
#endif /* WITH_WSREP */
osku's avatar
osku committed
2289 2290 2291 2292

	heap = mem_heap_create(1024);

	/* Build old index entry */
2293
	entry = row_build_index_entry(node->row, node->ext, index, heap);
2294
	ut_a(entry);
osku's avatar
osku committed
2295

2296
	log_free_check();
2297 2298 2299

	DEBUG_SYNC_C_IF_THD(trx->mysql_thd,
			    "before_row_upd_sec_index_entry");
2300

2301
	mtr.start();
2302

2303
	switch (index->table->space->id) {
2304
	case SRV_TMP_SPACE_ID:
2305
		mtr.set_log_mode(MTR_LOG_NO_REDO);
2306 2307 2308
		flags = BTR_NO_LOCKING_FLAG;
		break;
	default:
2309
		index->set_modified(mtr);
2310 2311
		/* fall through */
	case IBUF_SPACE_ID:
2312
		flags = index->table->no_rollback() ? BTR_NO_ROLLBACK : 0;
2313
		break;
2314
	}
2315

2316 2317 2318 2319
	if (!index->is_committed()) {
		/* The index->online_status may change if the index is
		or was being created online, but not committed yet. It
		is protected by index->lock. */
2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347

		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
2348 2349 2350
		are no foreign key constraints referring to the index.
		Change buffering is disabled for temporary tables and
		spatial index. */
2351
		mode = (referenced || index->table->is_temporary()
2352
			|| dict_index_is_spatial(index))
2353 2354
			? BTR_MODIFY_LEAF_ALREADY_S_LATCHED
			: BTR_DELETE_MARK_LEAF_ALREADY_S_LATCHED;
2355 2356
	} else {
		/* For secondary indexes,
2357 2358
		index->online_status==ONLINE_INDEX_COMPLETE if
		index->is_committed(). */
2359 2360 2361
		ut_ad(!dict_index_is_online_ddl(index));

		/* We can only buffer delete-mark operations if there
2362 2363 2364
		are no foreign key constraints referring to the index.
		Change buffering is disabled for temporary tables and
		spatial index. */
2365
		mode = (referenced || index->table->is_temporary()
2366
			|| dict_index_is_spatial(index))
2367
			? BTR_MODIFY_LEAF
2368
			: BTR_DELETE_MARK_LEAF;
2369 2370
	}

2371 2372 2373 2374 2375
	if (dict_index_is_spatial(index)) {
		ut_ad(mode & BTR_MODIFY_LEAF);
		mode |= BTR_RTREE_DELETE_MARK;
	}

2376 2377
	/* Set the query thread, so that ibuf_insert_low() will be
	able to invoke thd_get_trx(). */
2378 2379
	btr_pcur_get_btr_cur(&pcur)->thr = thr;

2380 2381
	search_result = row_search_index_entry(index, entry, mode,
					       &pcur, &mtr);
2382

osku's avatar
osku committed
2383 2384 2385 2386
	btr_cur = btr_pcur_get_btr_cur(&pcur);

	rec = btr_cur_get_rec(btr_cur);

2387
	switch (search_result) {
2388
	case ROW_NOT_DELETED_REF:	/* should only occur for BTR_DELETE */
2389 2390
		ut_error;
		break;
2391 2392 2393 2394
	case ROW_BUFFERED:
		/* Entry was delete marked already. */
		break;

2395
	case ROW_NOT_FOUND:
2396
		if (!index->is_committed()) {
2397 2398 2399 2400 2401 2402
			/* 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
2403 2404 2405 2406 2407 2408 2409 2410
			upgrade at least until this UPDATE returns.
			After that point, set_committed(true) would be
			invoked by commit_inplace_alter_table(). */
			break;
		}

		if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) {
			/* We found the record, but a delete marked */
2411 2412 2413
			break;
		}

2414 2415 2416 2417 2418 2419 2420 2421 2422
		ib::error()
			<< "Record in index " << index->name
			<< " of table " << index->table->name
			<< " was not found on update: " << *entry
			<< " at: " << rec_index_print(rec, index);
#ifdef UNIV_DEBUG
		mtr_commit(&mtr);
		mtr_start(&mtr);
		ut_ad(btr_validate_index(index, 0, false));
2423
		ut_ad(0);
2424
#endif /* UNIV_DEBUG */
2425 2426
		break;
	case ROW_FOUND:
2427 2428
		ut_ad(err == DB_SUCCESS);

2429 2430
		/* Delete mark the old index record; it can already be
		delete marked if we return after a lock wait in
2431
		row_ins_sec_index_entry() below */
2432
		if (!rec_get_deleted_flag(
2433
			    rec, dict_table_is_comp(index->table))) {
2434
			err = btr_cur_del_mark_set_sec_rec(
2435 2436 2437 2438
				flags, btr_cur, TRUE, thr, &mtr);
			if (err != DB_SUCCESS) {
				break;
			}
2439
#ifdef WITH_WSREP
2440 2441 2442
			if (!referenced && foreign
			    && wsrep_must_process_fk(node, trx)
			    && !wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
2443

2444
				ulint*	offsets = rec_get_offsets(
2445 2446
					rec, index, NULL, true,
					ULINT_UNDEFINED, &heap);
2447

2448 2449 2450
				err = wsrep_row_upd_check_foreign_constraints(
					node, &pcur, index->table,
					index, offsets, thr, &mtr);
2451

2452 2453 2454 2455 2456 2457
				switch (err) {
				case DB_SUCCESS:
				case DB_NO_REFERENCED_ROW:
					err = DB_SUCCESS;
					break;
				case DB_DEADLOCK:
2458
					if (wsrep_debug) {
2459
						ib::warn() << "WSREP: sec index FK check fail for deadlock"
2460 2461
							   << " index " << index->name
							   << " table " << index->table->name;
2462
					}
2463 2464
					break;
				default:
2465 2466 2467
					ib::error() << "WSREP: referenced FK check fail: " << ut_strerr(err)
						    << " index " << index->name
						    << " table " << index->table->name;
2468

2469 2470 2471 2472
					break;
				}
			}
#endif /* WITH_WSREP */
2473 2474 2475 2476 2477 2478 2479 2480 2481
		}

		ut_ad(err == DB_SUCCESS);

		if (referenced) {

			ulint*	offsets;

			offsets = rec_get_offsets(
2482
				rec, index, NULL, true, ULINT_UNDEFINED,
2483 2484 2485 2486 2487 2488 2489 2490
				&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);
		}
osku's avatar
osku committed
2491
	}
marko's avatar
marko committed
2492

osku's avatar
osku committed
2493 2494 2495
	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

2496
	if (node->is_delete == PLAIN_DELETE || err != DB_SUCCESS) {
osku's avatar
osku committed
2497

marko's avatar
marko committed
2498
		goto func_exit;
osku's avatar
osku committed
2499 2500
	}

2501 2502
	mem_heap_empty(heap);

osku's avatar
osku committed
2503
	/* Build a new index entry */
2504 2505 2506
	entry = row_build_index_entry(node->upd_row, node->upd_ext,
				      index, heap);
	ut_a(entry);
osku's avatar
osku committed
2507 2508

	/* Insert new index entry */
2509
	err = row_ins_sec_index_entry(index, entry, thr, false);
osku's avatar
osku committed
2510

marko's avatar
marko committed
2511
func_exit:
2512
	mem_heap_free(heap);
osku's avatar
osku committed
2513

2514
	return(err);
osku's avatar
osku committed
2515 2516
}

2517
/***********************************************************//**
2518
Updates the secondary index record if it is changed in the row update or
2519
deletes it if this is a delete.
2520 2521
@return DB_SUCCESS if operation successfully completed, else error
code or DB_LOCK_WAIT */
Sergei Golubchik's avatar
Sergei Golubchik committed
2522
static MY_ATTRIBUTE((nonnull, warn_unused_result))
2523
dberr_t
osku's avatar
osku committed
2524 2525
row_upd_sec_step(
/*=============*/
2526 2527
	upd_node_t*	node,	/*!< in: row update node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
2528 2529
{
	ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
2530
	      || (node->state == UPD_NODE_UPDATE_SOME_SEC));
2531
	ut_ad(!dict_index_is_clust(node->index));
2532

osku's avatar
osku committed
2533
	if (node->state == UPD_NODE_UPDATE_ALL_SEC
2534 2535
	    || row_upd_changes_ord_field_binary(node->index, node->update,
						thr, node->row, node->ext)) {
2536
		return(row_upd_sec_index_entry(node, thr));
osku's avatar
osku committed
2537 2538 2539 2540 2541
	}

	return(DB_SUCCESS);
}

2542 2543 2544 2545 2546
#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)	\
2547
	row_upd_clust_rec_by_insert_inherit_func(rec,entry,update)
2548 2549 2550 2551 2552 2553
#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().
2554 2555 2556
@return whether any columns were inherited */
static
bool
2557 2558 2559
row_upd_clust_rec_by_insert_inherit_func(
/*=====================================*/
	const rec_t*	rec,	/*!< in: old record, or NULL */
2560
#ifdef UNIV_DEBUG
2561 2562 2563 2564 2565 2566
	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 */
{
2567
	bool	inherit	= false;
2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580
	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)
2581
		      || upd_get_field_by_field_no(update, i, false));
2582
		if (!dfield_is_ext(dfield)
2583
		    || upd_get_field_by_field_no(update, i, false)) {
2584 2585 2586 2587 2588
			continue;
		}

#ifdef UNIV_DEBUG
		if (UNIV_LIKELY(rec != NULL)) {
2589
			ut_ad(!rec_offs_nth_default(offsets, i));
2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609
			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);
2610 2611 2612

		data = static_cast<byte*>(dfield_get_data(dfield));

2613
		data += len - BTR_EXTERN_FIELD_REF_SIZE;
2614 2615
		/* The pointer must not be zero. */
		ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE));
2616 2617 2618 2619 2620

		/* The BLOB must be owned, unless we are resuming from
		a lock wait and we already had disowned the BLOB. */
		ut_a(rec == NULL
		     || !(data[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG));
Sergei Golubchik's avatar
Sergei Golubchik committed
2621
		data[BTR_EXTERN_LEN] &= ~BTR_EXTERN_OWNER_FLAG;
2622 2623
		data[BTR_EXTERN_LEN] |= BTR_EXTERN_INHERITED_FLAG;
		/* The BTR_EXTERN_INHERITED_FLAG only matters in
2624
		rollback of a fresh insert. Purge will always free
2625
		the extern fields of a delete-marked row. */
2626

2627
		inherit = true;
2628 2629 2630 2631 2632
	}

	return(inherit);
}

2633
/***********************************************************//**
osku's avatar
osku committed
2634 2635 2636
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
2637
database applications.
2638 2639
@return DB_SUCCESS if operation successfully completed, else error
code or DB_LOCK_WAIT */
Sergei Golubchik's avatar
Sergei Golubchik committed
2640
static MY_ATTRIBUTE((nonnull, warn_unused_result))
2641
dberr_t
osku's avatar
osku committed
2642 2643
row_upd_clust_rec_by_insert(
/*========================*/
2644
	upd_node_t*	node,	/*!< in/out: row update node */
2645 2646 2647
	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
osku's avatar
osku committed
2648
				a foreign key constraint */
Marko Mäkelä's avatar
Marko Mäkelä committed
2649 2650 2651
#ifdef WITH_WSREP
	bool		foreign,/*!< in: whether this is a foreign key */
#endif
2652
	mtr_t*		mtr)	/*!< in/out: mtr; gets committed here */
osku's avatar
osku committed
2653
{
2654
	mem_heap_t*	heap;
osku's avatar
osku committed
2655 2656 2657 2658 2659
	btr_pcur_t*	pcur;
	btr_cur_t*	btr_cur;
	trx_t*		trx;
	dict_table_t*	table;
	dtuple_t*	entry;
2660
	dberr_t		err;
2661 2662
	rec_t*		rec;
	ulint*		offsets			= NULL;
2663

osku's avatar
osku committed
2664
	ut_ad(node);
2665
	ut_ad(dict_index_is_clust(index));
osku's avatar
osku committed
2666 2667 2668 2669 2670

	trx = thr_get_trx(thr);
	table = node->table;
	pcur = node->pcur;
	btr_cur	= btr_pcur_get_btr_cur(pcur);
2671

2672 2673
	heap = mem_heap_create(1000);

2674 2675
	entry = row_build_index_entry_low(node->upd_row, node->upd_ext,
					  index, heap, ROW_BUILD_FOR_INSERT);
2676
	if (index->is_instant()) entry->trim(*index);
2677
	ut_ad(dtuple_get_info_bits(entry) == 0);
osku's avatar
osku committed
2678

2679 2680 2681 2682 2683 2684
	row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);

	switch (node->state) {
	default:
		ut_error;
	case UPD_NODE_INSERT_CLUSTERED:
2685
		/* A lock wait occurred in row_ins_clust_index_entry() in
2686
		the previous invocation of this function. */
2687 2688
		row_upd_clust_rec_by_insert_inherit(
			NULL, NULL, entry, node->update);
2689 2690 2691 2692 2693 2694
		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);
2695
		offsets = rec_get_offsets(rec, index, NULL, true,
2696 2697
					  ULINT_UNDEFINED, &heap);
		ut_ad(page_rec_is_user_rec(rec));
2698

2699 2700 2701 2702 2703
		if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))) {
			/* If the clustered index record is already delete
			marked, then we are here after a DB_LOCK_WAIT.
			Skip delete marking clustered index and disowning
			its blobs. */
2704 2705
			ut_ad(row_get_rec_trx_id(rec, index, offsets)
			      == trx->id);
2706 2707 2708 2709 2710 2711
			ut_ad(!trx_undo_roll_ptr_is_insert(
			              row_get_rec_roll_ptr(rec, index,
							   offsets)));
			goto check_fk;
		}

2712
		err = btr_cur_del_mark_set_clust_rec(
2713
			btr_cur_get_block(btr_cur), rec, index, offsets,
2714
			thr, node->row, mtr);
osku's avatar
osku committed
2715
		if (err != DB_SUCCESS) {
2716
err_exit:
osku's avatar
osku committed
2717
			mtr_commit(mtr);
2718
			mem_heap_free(heap);
osku's avatar
osku committed
2719 2720 2721
			return(err);
		}

2722 2723 2724 2725
		/* 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. */
osku's avatar
osku committed
2726

2727
		if (rec_offs_any_extern(offsets)) {
2728 2729
			if (row_upd_clust_rec_by_insert_inherit(
				    rec, offsets, entry, node->update)) {
Sergei Golubchik's avatar
Sergei Golubchik committed
2730 2731 2732 2733 2734 2735
				/* 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),
2736 2737
					rec, index, offsets, node->update,
					mtr);
2738 2739
			}
		}
2740
check_fk:
2741
		if (referenced) {
osku's avatar
osku committed
2742 2743
			/* NOTE that the following call loses
			the position of pcur ! */
2744

2745
			err = row_upd_check_references_constraints(
2746
				node, pcur, table, index, offsets, thr, mtr);
2747

osku's avatar
osku committed
2748
			if (err != DB_SUCCESS) {
2749
				goto err_exit;
osku's avatar
osku committed
2750
			}
2751
#ifdef WITH_WSREP
2752
		} else if (foreign && wsrep_must_process_fk(node, trx)) {
2753 2754 2755
			err = wsrep_row_upd_check_foreign_constraints(
				node, pcur, table, index, offsets, thr, mtr);

2756 2757 2758 2759 2760 2761
			switch (err) {
			case DB_SUCCESS:
			case DB_NO_REFERENCED_ROW:
				err = DB_SUCCESS;
				break;
			case DB_DEADLOCK:
2762
				if (wsrep_debug) {
2763
					ib::warn() << "WSREP: sec index FK check fail for deadlock"
2764 2765
						   << " index " << index->name
						   << " table " << index->table->name;
2766
				}
Marko Mäkelä's avatar
Marko Mäkelä committed
2767
				goto err_exit;
2768
			default:
2769 2770 2771
				ib::error() << "WSREP: referenced FK check fail: " << ut_strerr(err)
					    << " index " << index->name
					    << " table " << index->table->name;
2772 2773 2774
				goto err_exit;
			}
#endif /* WITH_WSREP */
2775
		}
2776
	}
osku's avatar
osku committed
2777

2778 2779
	mtr_commit(mtr);

2780 2781
	err = row_ins_clust_index_entry(
		index, entry, thr,
2782 2783
		node->upd_ext ? node->upd_ext->n_ext : 0, false);
	node->state = UPD_NODE_INSERT_CLUSTERED;
osku's avatar
osku committed
2784 2785

	mem_heap_free(heap);
2786

osku's avatar
osku committed
2787 2788 2789
	return(err);
}

2790
/***********************************************************//**
osku's avatar
osku committed
2791
Updates a clustered index record of a row when the ordering fields do
2792
not change.
2793 2794
@return DB_SUCCESS if operation successfully completed, else error
code or DB_LOCK_WAIT */
Sergei Golubchik's avatar
Sergei Golubchik committed
2795
static MY_ATTRIBUTE((nonnull, warn_unused_result))
2796
dberr_t
osku's avatar
osku committed
2797 2798
row_upd_clust_rec(
/*==============*/
2799
	ulint		flags,  /*!< in: undo logging and locking flags */
2800 2801
	upd_node_t*	node,	/*!< in: row update node */
	dict_index_t*	index,	/*!< in: clustered index */
2802 2803 2804
	ulint*		offsets,/*!< in: rec_get_offsets() on node->pcur */
	mem_heap_t**	offsets_heap,
				/*!< in/out: memory heap, can be emptied */
2805 2806
	que_thr_t*	thr,	/*!< in: query thread */
	mtr_t*		mtr)	/*!< in: mtr; gets committed here */
osku's avatar
osku committed
2807
{
2808 2809
	mem_heap_t*	heap		= NULL;
	big_rec_t*	big_rec		= NULL;
osku's avatar
osku committed
2810 2811
	btr_pcur_t*	pcur;
	btr_cur_t*	btr_cur;
2812 2813
	dberr_t		err;
	const dtuple_t*	rebuilt_old_pk	= NULL;
2814

osku's avatar
osku committed
2815
	ut_ad(node);
2816
	ut_ad(dict_index_is_clust(index));
2817
	ut_ad(!thr_get_trx(thr)->in_rollback);
2818
	ut_ad(!node->table->skip_alter_undo);
osku's avatar
osku committed
2819 2820 2821 2822

	pcur = node->pcur;
	btr_cur = btr_pcur_get_btr_cur(pcur);

2823 2824
	ut_ad(btr_cur_get_index(btr_cur) == index);
	ut_ad(!rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
2825
				    dict_table_is_comp(index->table)));
2826 2827 2828 2829
	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(
Sergei Golubchik's avatar
Sergei Golubchik committed
2830
			btr_cur_get_rec(btr_cur), index, offsets, NULL, &heap);
2831
	}
2832

osku's avatar
osku committed
2833 2834 2835 2836 2837
	/* 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) {
2838
		err = btr_cur_update_in_place(
2839
			flags | BTR_NO_LOCKING_FLAG, btr_cur,
2840 2841
			offsets, node->update,
			node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
osku's avatar
osku committed
2842
	} else {
2843
		err = btr_cur_optimistic_update(
2844
			flags | BTR_NO_LOCKING_FLAG, btr_cur,
2845 2846 2847 2848
			&offsets, offsets_heap, node->update,
			node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
	}

2849 2850
	if (err == DB_SUCCESS) {
		goto success;
osku's avatar
osku committed
2851 2852 2853
	}

	mtr_commit(mtr);
2854

2855 2856
	if (buf_LRU_buf_pool_running_out()) {

2857 2858
		err = DB_LOCK_TABLE_FULL;
		goto func_exit;
2859
	}
osku's avatar
osku committed
2860 2861 2862
	/* We may have to modify the tree structure: do a pessimistic descent
	down the index tree */

2863
	mtr->start();
2864

2865 2866 2867
	if (index->table->is_temporary()) {
		/* Disable locking, because temporary tables are never
		shared between transactions or connections. */
2868 2869
		flags |= BTR_NO_LOCKING_FLAG;
		mtr->set_log_mode(MTR_LOG_NO_REDO);
2870
	} else {
2871
		index->set_modified(*mtr);
2872
	}
2873

osku's avatar
osku committed
2874 2875 2876 2877 2878 2879 2880 2881 2882
	/* 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(BTR_MODIFY_TREE, pcur, mtr));

	ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
2883
				    dict_table_is_comp(index->table)));
2884

2885 2886 2887 2888
	if (!heap) {
		heap = mem_heap_create(1024);
	}

2889
	err = btr_cur_pessimistic_update(
2890
		flags | BTR_NO_LOCKING_FLAG | BTR_KEEP_POS_FLAG, btr_cur,
2891 2892 2893
		&offsets, offsets_heap, heap, &big_rec,
		node->update, node->cmpl_info,
		thr, thr_get_trx(thr)->id, mtr);
2894 2895
	if (big_rec) {
		ut_a(err == DB_SUCCESS);
osku's avatar
osku committed
2896

2897
		DEBUG_SYNC_C("before_row_upd_extern");
2898
		err = btr_store_big_rec_extern_fields(
2899
			pcur, offsets, big_rec, mtr, BTR_STORE_UPDATE);
2900
		DEBUG_SYNC_C("after_row_upd_extern");
osku's avatar
osku committed
2901 2902
	}

2903 2904 2905 2906 2907
	if (err == DB_SUCCESS) {
success:
		if (dict_index_is_online_ddl(index)) {
			row_log_table_update(
				btr_cur_get_rec(btr_cur),
2908
				index, offsets, rebuilt_old_pk);
2909
		}
2910
	}
2911

2912 2913 2914
	mtr_commit(mtr);
func_exit:
	if (heap) {
2915 2916 2917
		mem_heap_free(heap);
	}

osku's avatar
osku committed
2918 2919 2920
	if (big_rec) {
		dtuple_big_rec_free(big_rec);
	}
2921

osku's avatar
osku committed
2922 2923 2924
	return(err);
}

2925
/***********************************************************//**
2926
Delete marks a clustered index record.
2927
@return DB_SUCCESS if operation successfully completed, else error code */
Sergei Golubchik's avatar
Sergei Golubchik committed
2928
static MY_ATTRIBUTE((nonnull, warn_unused_result))
2929
dberr_t
osku's avatar
osku committed
2930 2931
row_upd_del_mark_clust_rec(
/*=======================*/
2932 2933 2934
	upd_node_t*	node,	/*!< in: row update node */
	dict_index_t*	index,	/*!< in: clustered index */
	ulint*		offsets,/*!< in/out: rec_get_offsets() for the
2935
				record under the cursor */
2936
	que_thr_t*	thr,	/*!< in: query thread */
2937
	ibool		referenced,
2938
				/*!< in: TRUE if index may be referenced in
osku's avatar
osku committed
2939
				a foreign key constraint */
Marko Mäkelä's avatar
Marko Mäkelä committed
2940 2941 2942
#ifdef WITH_WSREP
	bool		foreign,/*!< in: whether this is a foreign key */
#endif
2943
	mtr_t*		mtr)	/*!< in: mtr; gets committed here */
osku's avatar
osku committed
2944 2945 2946
{
	btr_pcur_t*	pcur;
	btr_cur_t*	btr_cur;
2947
	dberr_t		err;
2948
	rec_t*		rec;
2949
	trx_t*		trx = thr_get_trx(thr);
2950

osku's avatar
osku committed
2951
	ut_ad(node);
2952
	ut_ad(dict_index_is_clust(index));
2953
	ut_ad(node->is_delete == PLAIN_DELETE);
osku's avatar
osku committed
2954 2955 2956 2957 2958 2959

	pcur = node->pcur;
	btr_cur = btr_pcur_get_btr_cur(pcur);

	/* Store row because we have to build also the secondary index
	entries */
2960

2961
	row_upd_store_row(node, trx->mysql_thd,
2962 2963
			  thr->prebuilt  && thr->prebuilt->table == node->table
			  ? thr->prebuilt->m_mysql_table : NULL);
osku's avatar
osku committed
2964 2965 2966 2967

	/* 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 */

2968 2969
	rec = btr_cur_get_rec(btr_cur);

2970
	err = btr_cur_del_mark_set_clust_rec(
2971
		btr_cur_get_block(btr_cur), rec,
2972 2973
		index, offsets, thr, node->row, mtr);

2974 2975
	if (err != DB_SUCCESS) {
	} else if (referenced) {
osku's avatar
osku committed
2976 2977
		/* NOTE that the following call loses the position of pcur ! */

2978 2979
		err = row_upd_check_references_constraints(
			node, pcur, index->table, index, offsets, thr, mtr);
2980
#ifdef WITH_WSREP
2981
	} else if (foreign && wsrep_must_process_fk(node, trx)) {
2982 2983
		err = wsrep_row_upd_check_foreign_constraints(
			node, pcur, index->table, index, offsets, thr, mtr);
2984

2985 2986 2987 2988 2989 2990
		switch (err) {
		case DB_SUCCESS:
		case DB_NO_REFERENCED_ROW:
			err = DB_SUCCESS;
			break;
		case DB_DEADLOCK:
2991
			if (wsrep_debug) {
2992
				ib::warn() << "WSREP: sec index FK check fail for deadlock"
2993 2994
					   << " index " << index->name
					   << " table " << index->table->name;
2995
			}
2996 2997
			break;
		default:
2998 2999 3000
			ib::error() << "WSREP: referenced FK check fail: " << ut_strerr(err)
				    << " index " << index->name
				    << " table " << index->table->name;
3001

3002 3003 3004
			break;
		}
#endif /* WITH_WSREP */
3005
	}
osku's avatar
osku committed
3006 3007

	mtr_commit(mtr);
3008

osku's avatar
osku committed
3009 3010 3011
	return(err);
}

3012
/***********************************************************//**
3013
Updates the clustered index record.
3014 3015
@return DB_SUCCESS if operation successfully completed, DB_LOCK_WAIT
in case of a lock wait, else error code */
Sergei Golubchik's avatar
Sergei Golubchik committed
3016
static MY_ATTRIBUTE((nonnull, warn_unused_result))
3017
dberr_t
osku's avatar
osku committed
3018 3019
row_upd_clust_step(
/*===============*/
3020 3021
	upd_node_t*	node,	/*!< in: row update node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
3022 3023 3024 3025
{
	dict_index_t*	index;
	btr_pcur_t*	pcur;
	ibool		success;
3026 3027
	dberr_t		err;
	mtr_t		mtr;
osku's avatar
osku committed
3028
	rec_t*		rec;
3029
	mem_heap_t*	heap	= NULL;
osku's avatar
osku committed
3030
	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
3031
	ulint*		offsets;
3032
	ibool		referenced;
3033
	ulint		flags;
3034
	trx_t*		trx = thr_get_trx(thr);
3035

3036
	rec_offs_init(offsets_);
osku's avatar
osku committed
3037 3038 3039

	index = dict_table_get_first_index(node->table);

3040
	referenced = row_upd_index_is_referenced(index, trx);
3041

3042
#ifdef WITH_WSREP
Marko Mäkelä's avatar
Marko Mäkelä committed
3043
	const bool foreign = wsrep_row_upd_index_is_foreign(index, trx);
3044
#endif
osku's avatar
osku committed
3045 3046 3047 3048 3049

	pcur = node->pcur;

	/* We have to restore the cursor to its position */

3050
	mtr.start();
3051

3052
	if (node->table->is_temporary()) {
3053 3054 3055 3056 3057 3058
		/* Disable locking, because temporary tables are
		private to the connection (no concurrent access). */
		flags = node->table->no_rollback()
			? BTR_NO_ROLLBACK
			: BTR_NO_LOCKING_FLAG;
		/* Redo logging only matters for persistent tables. */
3059
		mtr.set_log_mode(MTR_LOG_NO_REDO);
3060
	} else {
3061
		flags = node->table->no_rollback() ? BTR_NO_ROLLBACK : 0;
3062
		index->set_modified(mtr);
3063
	}
3064

osku's avatar
osku committed
3065 3066 3067 3068 3069 3070 3071
	/* 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. */
3072

osku's avatar
osku committed
3073 3074
	ut_a(pcur->rel_pos == BTR_PCUR_ON);

3075 3076
	ulint	mode;

3077 3078 3079
	DEBUG_SYNC_C_IF_THD(
		thr_get_trx(thr)->mysql_thd,
		"innodb_row_upd_clust_step_enter");
3080 3081 3082 3083 3084 3085 3086 3087 3088 3089

	if (dict_index_is_online_ddl(index)) {
		ut_ad(node->table->id != DICT_INDEXES_ID);
		mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED;
		mtr_s_lock(dict_index_get_lock(index), &mtr);
	} else {
		mode = BTR_MODIFY_LEAF;
	}

	success = btr_pcur_restore_position(mode, pcur, &mtr);
osku's avatar
osku committed
3090 3091 3092 3093

	if (!success) {
		err = DB_RECORD_NOT_FOUND;

3094
		mtr_commit(&mtr);
osku's avatar
osku committed
3095 3096 3097 3098 3099 3100 3101 3102

		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 */

3103 3104
	if (node->is_delete == PLAIN_DELETE
	    && node->table->id == DICT_INDEXES_ID) {
osku's avatar
osku committed
3105

3106
		ut_ad(!dict_index_is_online_ddl(index));
osku's avatar
osku committed
3107

3108 3109
		dict_drop_index_tree(
			btr_pcur_get_rec(pcur), pcur, &mtr);
osku's avatar
osku committed
3110

3111
		mtr.commit();
3112

3113
		mtr.start();
3114
		index->set_modified(mtr);
osku's avatar
osku committed
3115 3116

		success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
3117
						    &mtr);
osku's avatar
osku committed
3118 3119 3120
		if (!success) {
			err = DB_ERROR;

3121
			mtr.commit();
osku's avatar
osku committed
3122 3123 3124

			return(err);
		}
3125
	}
osku's avatar
osku committed
3126 3127

	rec = btr_pcur_get_rec(pcur);
3128
	offsets = rec_get_offsets(rec, index, offsets_, true,
3129
				  ULINT_UNDEFINED, &heap);
osku's avatar
osku committed
3130

Marko Mäkelä's avatar
Marko Mäkelä committed
3131
	if (!flags && !node->has_clust_rec_x_lock) {
3132
		err = lock_clust_rec_modify_check_and_lock(
3133 3134
			0, btr_pcur_get_block(pcur),
			rec, index, offsets, thr);
osku's avatar
osku committed
3135
		if (err != DB_SUCCESS) {
3136
			mtr.commit();
osku's avatar
osku committed
3137 3138 3139 3140
			goto exit_func;
		}
	}

3141 3142 3143 3144
	ut_ad(index->table->no_rollback()
	      || lock_trx_has_rec_x_lock(thr_get_trx(thr), index->table,
					 btr_pcur_get_block(pcur),
					 page_rec_get_heap_no(rec)));
3145

osku's avatar
osku committed
3146 3147
	/* NOTE: the following function calls will also commit mtr */

3148
	if (node->is_delete == PLAIN_DELETE) {
3149
		err = row_upd_del_mark_clust_rec(
Marko Mäkelä's avatar
Marko Mäkelä committed
3150 3151 3152 3153 3154
			node, index, offsets, thr, referenced,
#ifdef WITH_WSREP
			foreign,
#endif
			&mtr);
3155

osku's avatar
osku committed
3156 3157 3158 3159
		if (err == DB_SUCCESS) {
			node->state = UPD_NODE_UPDATE_ALL_SEC;
			node->index = dict_table_get_next_index(index);
		}
3160 3161

		goto exit_func;
osku's avatar
osku committed
3162
	}
3163

osku's avatar
osku committed
3164 3165
	/* If the update is made for MySQL, we already have the update vector
	ready, else we have to do some evaluation: */
3166

3167
	if (UNIV_UNLIKELY(!node->in_mysql_interface)) {
osku's avatar
osku committed
3168 3169
		/* Copy the necessary columns from clust_rec and calculate the
		new values to set */
3170
		row_upd_copy_columns(rec, offsets, index,
3171
				     UT_LIST_GET_FIRST(node->columns));
osku's avatar
osku committed
3172 3173 3174 3175 3176
		row_upd_eval_new_vals(node->update);
	}

	if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {

3177
		err = row_upd_clust_rec(
3178
			flags, node, index, offsets, &heap, thr, &mtr);
3179
		goto exit_func;
osku's avatar
osku committed
3180
	}
3181

3182 3183
	row_upd_store_row(node, trx->mysql_thd,
			  thr->prebuilt ? thr->prebuilt->m_mysql_table : NULL);
osku's avatar
osku committed
3184

3185 3186
	if (row_upd_changes_ord_field_binary(index, node->update, thr,
					     node->row, node->ext)) {
osku's avatar
osku committed
3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198

		/* 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! */

3199
		err = row_upd_clust_rec_by_insert(
Marko Mäkelä's avatar
Marko Mäkelä committed
3200 3201 3202 3203 3204
			node, index, thr, referenced,
#ifdef WITH_WSREP
			foreign,
#endif
			&mtr);
osku's avatar
osku committed
3205 3206
		if (err != DB_SUCCESS) {

3207
			goto exit_func;
osku's avatar
osku committed
3208 3209 3210 3211
		}

		node->state = UPD_NODE_UPDATE_ALL_SEC;
	} else {
3212
		err = row_upd_clust_rec(
3213
			flags, node, index, offsets, &heap, thr, &mtr);
osku's avatar
osku committed
3214 3215 3216

		if (err != DB_SUCCESS) {

3217
			goto exit_func;
osku's avatar
osku committed
3218 3219 3220 3221 3222 3223 3224
		}

		node->state = UPD_NODE_UPDATE_SOME_SEC;
	}

	node->index = dict_table_get_next_index(index);

3225 3226 3227 3228
exit_func:
	if (heap) {
		mem_heap_free(heap);
	}
osku's avatar
osku committed
3229 3230 3231
	return(err);
}

3232
/***********************************************************//**
osku's avatar
osku committed
3233 3234
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
3235
record, and the position of the cursor is stored in the cursor.
3236 3237
@return DB_SUCCESS if operation successfully completed, else error
code or DB_LOCK_WAIT */
3238
static
3239
dberr_t
osku's avatar
osku committed
3240 3241
row_upd(
/*====*/
3242 3243
	upd_node_t*	node,	/*!< in: row update node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
3244
{
3245
	dberr_t		err	= DB_SUCCESS;
3246
	DBUG_ENTER("row_upd");
3247

3248 3249 3250
	ut_ad(!thr_get_trx(thr)->in_rollback);

	DBUG_PRINT("row_upd", ("table: %s", node->table->name.m_name));
3251
	DBUG_PRINT("row_upd", ("info bits in update vector: 0x" ULINTPFx,
3252 3253 3254
			       node->update ? node->update->info_bits: 0));
	DBUG_PRINT("row_upd", ("foreign_id: %s",
			       node->foreign ? node->foreign->id: "NULL"));
osku's avatar
osku committed
3255

3256
	if (UNIV_LIKELY(node->in_mysql_interface)) {
3257

osku's avatar
osku committed
3258 3259
		/* We do not get the cmpl_info value from the MySQL
		interpreter: we must calculate it on the fly: */
3260

3261
		if (node->is_delete == PLAIN_DELETE
3262 3263
		    || row_upd_changes_some_index_ord_field_binary(
			    node->table, node->update)) {
3264
			node->cmpl_info = 0;
osku's avatar
osku committed
3265 3266 3267 3268 3269
		} else {
			node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
		}
	}

3270 3271 3272
	switch (node->state) {
	case UPD_NODE_UPDATE_CLUSTERED:
	case UPD_NODE_INSERT_CLUSTERED:
3273 3274
		log_free_check();

osku's avatar
osku committed
3275
		err = row_upd_clust_step(node, thr);
3276

osku's avatar
osku committed
3277 3278
		if (err != DB_SUCCESS) {

3279
			DBUG_RETURN(err);
osku's avatar
osku committed
3280 3281 3282
		}
	}

3283 3284 3285
	DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd,
			    "after_row_upd_clust");

3286 3287 3288
	if (node->index == NULL
	    || (!node->is_delete
		&& (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE))) {
osku's avatar
osku committed
3289

3290
		DBUG_RETURN(DB_SUCCESS);
osku's avatar
osku committed
3291 3292
	}

3293 3294
	DBUG_EXECUTE_IF("row_upd_skip_sec", node->index = NULL;);

3295
	do {
3296 3297 3298 3299 3300 3301 3302
		/* Skip corrupted index */
		dict_table_skip_corrupt_index(node->index);

		if (!node->index) {
			break;
		}

3303 3304
		if (node->index->type != DICT_FTS) {
			err = row_upd_sec_step(node, thr);
osku's avatar
osku committed
3305

3306
			if (err != DB_SUCCESS) {
osku's avatar
osku committed
3307

3308
				DBUG_RETURN(err);
3309
			}
osku's avatar
osku committed
3310 3311 3312
		}

		node->index = dict_table_get_next_index(node->index);
3313
	} while (node->index != NULL);
osku's avatar
osku committed
3314

3315
	ut_ad(err == DB_SUCCESS);
osku's avatar
osku committed
3316

3317
	/* Do some cleanup */
osku's avatar
osku committed
3318

3319 3320 3321 3322 3323 3324
	if (node->row != NULL) {
		node->row = NULL;
		node->ext = NULL;
		node->upd_row = NULL;
		node->upd_ext = NULL;
		mem_heap_empty(node->heap);
osku's avatar
osku committed
3325 3326
	}

3327 3328
	node->state = UPD_NODE_UPDATE_CLUSTERED;

3329
	DBUG_RETURN(err);
osku's avatar
osku committed
3330 3331
}

3332
/***********************************************************//**
osku's avatar
osku committed
3333
Updates a row in a table. This is a high-level function used in SQL execution
3334
graphs.
3335
@return query thread to run next or NULL */
osku's avatar
osku committed
3336 3337 3338
que_thr_t*
row_upd_step(
/*=========*/
3339
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
3340 3341 3342 3343
{
	upd_node_t*	node;
	sel_node_t*	sel_node;
	que_node_t*	parent;
3344
	dberr_t		err		= DB_SUCCESS;
osku's avatar
osku committed
3345
	trx_t*		trx;
3346
	DBUG_ENTER("row_upd_step");
osku's avatar
osku committed
3347 3348

	ut_ad(thr);
3349

osku's avatar
osku committed
3350 3351
	trx = thr_get_trx(thr);

3352
	node = static_cast<upd_node_t*>(thr->run_node);
3353

osku's avatar
osku committed
3354 3355 3356
	sel_node = node->select;

	parent = que_node_get_parent(node);
3357

osku's avatar
osku committed
3358
	ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
3359

osku's avatar
osku committed
3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376
	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;
			}
		}
3377

osku's avatar
osku committed
3378 3379 3380 3381 3382
		node->state = UPD_NODE_UPDATE_CLUSTERED;

		if (node->searched_update) {
			/* Reset the cursor */
			sel_node->state = SEL_NODE_OPEN;
3383

osku's avatar
osku committed
3384
			/* Fetch a row to update */
3385

osku's avatar
osku committed
3386
			thr->run_node = sel_node;
3387

3388
			DBUG_RETURN(thr);
osku's avatar
osku committed
3389 3390 3391 3392
		}
	}

	/* sel_node is NULL if we are in the MySQL interface */
3393

osku's avatar
osku committed
3394 3395 3396 3397 3398 3399 3400
	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;
3401

osku's avatar
osku committed
3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412
			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;
3413

3414
		DBUG_RETURN(thr);
osku's avatar
osku committed
3415 3416 3417
	}

	/* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
3418

osku's avatar
osku committed
3419 3420 3421
	err = row_upd(node, thr);

error_handling:
3422
	trx->error_state = err;
osku's avatar
osku committed
3423

3424
	if (err != DB_SUCCESS) {
3425
		DBUG_RETURN(NULL);
osku's avatar
osku committed
3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441
	}

	/* 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;

3442 3443
	DBUG_RETURN(thr);
}
3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497

/** Write query start time as SQL field data to a buffer. Needed by InnoDB.
@param	thd	Thread object
@param	buf	Buffer to hold start time data */
void thd_get_query_start_data(THD *thd, char *buf);

/** Appends row_start or row_end field to update vector and sets a
CURRENT_TIMESTAMP/trx->id value to it.
Supposed to be called only by make_versioned_update() and
make_versioned_delete().
@param[in]	trx	transaction
@param[in]	vers_sys_idx	table->row_start or table->row_end */
void upd_node_t::make_versioned_helper(const trx_t* trx, ulint idx)
{
	ut_ad(in_mysql_interface); // otherwise needs to recalculate
				   // node->cmpl_info
	ut_ad(idx == table->vers_start || idx == table->vers_end);

	dict_index_t* clust_index = dict_table_get_first_index(table);

	update->n_fields++;
	upd_field_t* ufield =
		upd_get_nth_field(update, upd_get_n_fields(update) - 1);
	const dict_col_t* col = dict_table_get_nth_col(table, idx);

	upd_field_set_field_no(ufield, dict_col_get_clust_pos(col, clust_index),
			       clust_index);

	char* where = reinterpret_cast<char*>(update->vers_sys_value);
	if (col->vers_native()) {
		mach_write_to_8(where, trx->id);
	} else {
		thd_get_query_start_data(trx->mysql_thd, where);
	}

	dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len);
}

/** Also set row_start = CURRENT_TIMESTAMP/trx->id
@param[in]	trx	transaction */
void upd_node_t::make_versioned_update(const trx_t* trx)
{
	make_versioned_helper(trx, table->vers_start);
}

/** Only set row_end = CURRENT_TIMESTAMP/trx->id.
Do not touch other fields at all.
@param[in]	trx	transaction */
void upd_node_t::make_versioned_delete(const trx_t* trx)
{
	update->n_fields = 0;
	is_delete = VERSIONED_DELETE;
	make_versioned_helper(trx, table->vers_end);
}