1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2015, 2021, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /**************************************************//**
21 @file row/row0upd.cc
22 Update of a row
23 
24 Created 12/27/1996 Heikki Tuuri
25 *******************************************************/
26 
27 #include "row0upd.h"
28 #include "dict0dict.h"
29 #include "dict0mem.h"
30 #include "trx0undo.h"
31 #include "rem0rec.h"
32 #include "dict0boot.h"
33 #include "dict0crea.h"
34 #include "mach0data.h"
35 #include "btr0btr.h"
36 #include "btr0cur.h"
37 #include "que0que.h"
38 #include "row0ext.h"
39 #include "row0ins.h"
40 #include "row0log.h"
41 #include "row0row.h"
42 #include "row0sel.h"
43 #include "rem0cmp.h"
44 #include "lock0lock.h"
45 #include "log0log.h"
46 #include "pars0sym.h"
47 #include "eval0eval.h"
48 #include "buf0lru.h"
49 #include "trx0rec.h"
50 #include "fts0fts.h"
51 #include "fts0types.h"
52 #include <algorithm>
53 #include <mysql/plugin.h>
54 #include <mysql/service_wsrep.h>
55 #ifdef WITH_WSREP
56 #include "log.h"
57 #include "wsrep.h"
58 #endif /* WITH_WSREP */
59 
60 
61 /* What kind of latch and lock can we assume when the control comes to
62    -------------------------------------------------------------------
63 an update node?
64 --------------
65 Efficiency of massive updates would require keeping an x-latch on a
66 clustered index page through many updates, and not setting an explicit
67 x-lock on clustered index records, as they anyway will get an implicit
68 x-lock when they are updated. A problem is that the read nodes in the
69 graph should know that they must keep the latch when passing the control
70 up to the update node, and not set any record lock on the record which
71 will be updated. Another problem occurs if the execution is stopped,
72 as the kernel switches to another query thread, or the transaction must
73 wait for a lock. Then we should be able to release the latch and, maybe,
74 acquire an explicit x-lock on the record.
75 	Because this seems too complicated, we conclude that the less
76 efficient solution of releasing all the latches when the control is
77 transferred to another node, and acquiring explicit x-locks, is better. */
78 
79 /* How is a delete performed? If there is a delete without an
80 explicit cursor, i.e., a searched delete, there are at least
81 two different situations:
82 the implicit select cursor may run on (1) the clustered index or
83 on (2) a secondary index. The delete is performed by setting
84 the delete bit in the record and substituting the id of the
85 deleting transaction for the original trx id, and substituting a
86 new roll ptr for previous roll ptr. The old trx id and roll ptr
87 are saved in the undo log record. Thus, no physical changes occur
88 in the index tree structure at the time of the delete. Only
89 when the undo log is purged, the index records will be physically
90 deleted from the index trees.
91 
92 The query graph executing a searched delete would consist of
93 a delete node which has as a subtree a select subgraph.
94 The select subgraph should return a (persistent) cursor
95 in the clustered index, placed on page which is x-latched.
96 The delete node should look for all secondary index records for
97 this clustered index entry and mark them as deleted. When is
98 the x-latch freed? The most efficient way for performing a
99 searched delete is obviously to keep the x-latch for several
100 steps of query graph execution. */
101 
102 /*************************************************************************
103 IMPORTANT NOTE: Any operation that generates redo MUST check that there
104 is enough space in the redo log before for that operation. This is
105 done by calling log_free_check(). The reason for checking the
106 availability of the redo log space before the start of the operation is
107 that we MUST not hold any synchonization objects when performing the
108 check.
109 If you make a change in this module make sure that no codepath is
110 introduced where a call to log_free_check() is bypassed. */
111 
112 /***********************************************************//**
113 Checks if an update vector changes some of the first ordering fields of an
114 index record. This is only used in foreign key checks and we can assume
115 that index does not contain column prefixes.
116 @return TRUE if changes */
117 static
118 ibool
119 row_upd_changes_first_fields_binary(
120 /*================================*/
121 	dtuple_t*	entry,	/*!< in: old value of index entry */
122 	dict_index_t*	index,	/*!< in: index of entry */
123 	const upd_t*	update,	/*!< in: update vector for the row */
124 	ulint		n);	/*!< in: how many first fields to check */
125 
126 /*********************************************************************//**
127 Checks if index currently is mentioned as a referenced index in a foreign
128 key constraint.
129 
130 NOTE that since we do not hold dict_operation_lock when leaving the
131 function, it may be that the referencing table has been dropped when
132 we leave this function: this function is only for heuristic use!
133 
134 @return true if referenced */
135 static
136 bool
row_upd_index_is_referenced(dict_index_t * index,trx_t * trx)137 row_upd_index_is_referenced(
138 /*========================*/
139 	dict_index_t*	index,	/*!< in: index */
140 	trx_t*		trx)	/*!< in: transaction */
141 {
142 	dict_table_t*	table		= index->table;
143 
144 	if (table->referenced_set.empty()) {
145 		return false;
146 	}
147 
148 	const bool froze_data_dict = !trx->dict_operation_lock_mode;
149 	if (froze_data_dict) {
150 		row_mysql_freeze_data_dictionary(trx);
151 	}
152 
153 	dict_foreign_set::iterator	it
154 		= std::find_if(table->referenced_set.begin(),
155 			       table->referenced_set.end(),
156 			       dict_foreign_with_index(index));
157 
158 	const bool is_referenced = (it != table->referenced_set.end());
159 
160 	if (froze_data_dict) {
161 		row_mysql_unfreeze_data_dictionary(trx);
162 	}
163 
164 	return is_referenced;
165 }
166 
167 #ifdef WITH_WSREP
168 static
169 ibool
wsrep_row_upd_index_is_foreign(dict_index_t * index,trx_t * trx)170 wsrep_row_upd_index_is_foreign(
171 /*========================*/
172 	dict_index_t*	index,	/*!< in: index */
173 	trx_t*		trx)	/*!< in: transaction */
174 {
175 	dict_table_t*	table		= index->table;
176 	ibool		froze_data_dict	= FALSE;
177 	ibool		is_referenced	= FALSE;
178 
179 	if (table->foreign_set.empty()) {
180 		return(FALSE);
181 	}
182 
183 	if (trx->dict_operation_lock_mode == 0) {
184 		row_mysql_freeze_data_dictionary(trx);
185 		froze_data_dict = TRUE;
186 	}
187 
188 	dict_foreign_set::iterator	it
189 		= std::find_if(table->foreign_set.begin(),
190 			       table->foreign_set.end(),
191 			       dict_foreign_with_foreign_index(index));
192 
193 	is_referenced = (it != table->foreign_set.end());
194 
195 	if (froze_data_dict) {
196 		row_mysql_unfreeze_data_dictionary(trx);
197 	}
198 
199 	return(is_referenced);
200 }
201 #endif /* WITH_WSREP */
202 
203 /*********************************************************************//**
204 Checks if possible foreign key constraints hold after a delete of the record
205 under pcur.
206 
207 NOTE that this function will temporarily commit mtr and lose the
208 pcur position!
209 
210 @return DB_SUCCESS or an error code */
211 static MY_ATTRIBUTE((nonnull, warn_unused_result))
212 dberr_t
row_upd_check_references_constraints(upd_node_t * node,btr_pcur_t * pcur,dict_table_t * table,dict_index_t * index,rec_offs * offsets,que_thr_t * thr,mtr_t * mtr)213 row_upd_check_references_constraints(
214 /*=================================*/
215 	upd_node_t*	node,	/*!< in: row update node */
216 	btr_pcur_t*	pcur,	/*!< in: cursor positioned on a record; NOTE: the
217 				cursor position is lost in this function! */
218 	dict_table_t*	table,	/*!< in: table in question */
219 	dict_index_t*	index,	/*!< in: index of the cursor */
220 	rec_offs*	offsets,/*!< in/out: rec_get_offsets(pcur.rec, index) */
221 	que_thr_t*	thr,	/*!< in: query thread */
222 	mtr_t*		mtr)	/*!< in: mtr */
223 {
224 	dict_foreign_t*	foreign;
225 	mem_heap_t*	heap;
226 	dtuple_t*	entry;
227 	trx_t*		trx;
228 	const rec_t*	rec;
229 	dberr_t		err;
230 	ibool		got_s_lock	= FALSE;
231 
232 	DBUG_ENTER("row_upd_check_references_constraints");
233 
234 	if (table->referenced_set.empty()) {
235 		DBUG_RETURN(DB_SUCCESS);
236 	}
237 
238 	trx = thr_get_trx(thr);
239 
240 	rec = btr_pcur_get_rec(pcur);
241 	ut_ad(rec_offs_validate(rec, index, offsets));
242 
243 	heap = mem_heap_create(500);
244 
245 	entry = row_rec_to_index_entry(rec, index, offsets, heap);
246 
247 	mtr_commit(mtr);
248 
249 	DEBUG_SYNC_C("foreign_constraint_check_for_update");
250 
251 	mtr->start();
252 
253 	if (trx->dict_operation_lock_mode == 0) {
254 		got_s_lock = TRUE;
255 
256 		row_mysql_freeze_data_dictionary(trx);
257 	}
258 
259 	DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd,
260 			    "foreign_constraint_check_for_insert");
261 
262 	for (dict_foreign_set::iterator it = table->referenced_set.begin();
263 	     it != table->referenced_set.end();
264 	     ++it) {
265 
266 		foreign = *it;
267 
268 		/* Note that we may have an update which updates the index
269 		record, but does NOT update the first fields which are
270 		referenced in a foreign key constraint. Then the update does
271 		NOT break the constraint. */
272 
273 		if (foreign->referenced_index == index
274 		    && (node->is_delete
275 			|| row_upd_changes_first_fields_binary(
276 				entry, index, node->update,
277 				foreign->n_fields))) {
278 			dict_table_t*	foreign_table = foreign->foreign_table;
279 
280 			dict_table_t*	ref_table = NULL;
281 
282 			if (foreign_table == NULL) {
283 
284 				ref_table = dict_table_open_on_name(
285 					foreign->foreign_table_name_lookup,
286 					FALSE, FALSE, DICT_ERR_IGNORE_NONE);
287 			}
288 
289 			if (foreign_table) {
290 				foreign_table->inc_fk_checks();
291 			}
292 
293 			/* NOTE that if the thread ends up waiting for a lock
294 			we will release dict_operation_lock temporarily!
295 			But the inc_fk_checks() protects foreign_table from
296 			being dropped while the check is running. */
297 
298 			err = row_ins_check_foreign_constraint(
299 				FALSE, foreign, table, entry, thr);
300 
301 			if (foreign_table) {
302 				foreign_table->dec_fk_checks();
303 			}
304 			if (ref_table != NULL) {
305 				dict_table_close(ref_table, FALSE, FALSE);
306 			}
307 
308 			if (err != DB_SUCCESS) {
309 				goto func_exit;
310 			}
311 		}
312 	}
313 
314 	err = DB_SUCCESS;
315 
316 func_exit:
317 	if (got_s_lock) {
318 		row_mysql_unfreeze_data_dictionary(trx);
319 	}
320 
321 	mem_heap_free(heap);
322 
323 	DEBUG_SYNC_C("foreign_constraint_check_for_update_done");
324 	DBUG_RETURN(err);
325 }
326 
327 #ifdef WITH_WSREP
328 static
329 dberr_t
wsrep_row_upd_check_foreign_constraints(upd_node_t * node,btr_pcur_t * pcur,dict_table_t * table,dict_index_t * index,rec_offs * offsets,que_thr_t * thr,mtr_t * mtr)330 wsrep_row_upd_check_foreign_constraints(
331 /*=================================*/
332 	upd_node_t*	node,	/*!< in: row update node */
333 	btr_pcur_t*	pcur,	/*!< in: cursor positioned on a record; NOTE: the
334 				cursor position is lost in this function! */
335 	dict_table_t*	table,	/*!< in: table in question */
336 	dict_index_t*	index,	/*!< in: index of the cursor */
337 	rec_offs*	offsets,/*!< in/out: rec_get_offsets(pcur.rec, index) */
338 	que_thr_t*	thr,	/*!< in: query thread */
339 	mtr_t*		mtr)	/*!< in: mtr */
340 {
341 	dict_foreign_t*	foreign;
342 	mem_heap_t*	heap;
343 	dtuple_t*	entry;
344 	trx_t*		trx;
345 	const rec_t*	rec;
346 	dberr_t		err;
347 	ibool		got_s_lock	= FALSE;
348 	ibool		opened     	= FALSE;
349 
350 	if (table->foreign_set.empty()) {
351 		return(DB_SUCCESS);
352 	}
353 
354 	trx = thr_get_trx(thr);
355 
356 	/* TODO: make native slave thread bail out here */
357 
358 	rec = btr_pcur_get_rec(pcur);
359 	ut_ad(rec_offs_validate(rec, index, offsets));
360 
361 	heap = mem_heap_create(500);
362 
363 	entry = row_rec_to_index_entry(rec, index, offsets, heap);
364 
365 	mtr_commit(mtr);
366 
367 	mtr_start(mtr);
368 
369 	if (trx->dict_operation_lock_mode == 0) {
370 		got_s_lock = TRUE;
371 
372 		row_mysql_freeze_data_dictionary(trx);
373 	}
374 
375 	for (dict_foreign_set::iterator it = table->foreign_set.begin();
376 	     it != table->foreign_set.end();
377 	     ++it) {
378 
379 		foreign = *it;
380 		/* Note that we may have an update which updates the index
381 		record, but does NOT update the first fields which are
382 		referenced in a foreign key constraint. Then the update does
383 		NOT break the constraint. */
384 
385 		if (foreign->foreign_index == index
386 		    && (node->is_delete
387 			|| row_upd_changes_first_fields_binary(
388 				entry, index, node->update,
389 				foreign->n_fields))) {
390 
391 			if (foreign->referenced_table == NULL) {
392 				foreign->referenced_table =
393 					dict_table_open_on_name(
394 					  foreign->referenced_table_name_lookup,
395 					  FALSE, FALSE, DICT_ERR_IGNORE_NONE);
396 				opened = (foreign->referenced_table) ? TRUE : FALSE;
397 			}
398 
399 			/* NOTE that if the thread ends up waiting for a lock
400 			we will release dict_operation_lock temporarily!
401 			But the counter on the table protects 'foreign' from
402 			being dropped while the check is running. */
403 
404 			err = row_ins_check_foreign_constraint(
405 				TRUE, foreign, table, entry, thr);
406 
407 			if (foreign->referenced_table) {
408 				if (opened == TRUE) {
409 					dict_table_close(foreign->referenced_table, FALSE, FALSE);
410 					opened = FALSE;
411 				}
412 			}
413 
414 			if (err != DB_SUCCESS) {
415 				goto func_exit;
416 			}
417 		}
418 	}
419 
420 	err = DB_SUCCESS;
421 func_exit:
422 	if (got_s_lock) {
423 		row_mysql_unfreeze_data_dictionary(trx);
424 	}
425 
426 	mem_heap_free(heap);
427 
428 	return(err);
429 }
430 
431 /** Determine if a FOREIGN KEY constraint needs to be processed.
432 @param[in]	node	query node
433 @param[in]	trx	transaction
434 @return	whether the node cannot be ignored */
435 
wsrep_must_process_fk(const upd_node_t * node,const trx_t * trx)436 inline bool wsrep_must_process_fk(const upd_node_t* node, const trx_t* trx)
437 {
438 	if (!trx->is_wsrep()) {
439 		return false;
440 	}
441 	return que_node_get_type(node->common.parent) != QUE_NODE_UPDATE
442 		|| static_cast<upd_node_t*>(node->common.parent)->cascade_node
443 		!= node;
444 }
445 #endif /* WITH_WSREP */
446 
447 /*********************************************************************//**
448 Creates an update node for a query graph.
449 @return own: update node */
450 upd_node_t*
upd_node_create(mem_heap_t * heap)451 upd_node_create(
452 /*============*/
453 	mem_heap_t*	heap)	/*!< in: mem heap where created */
454 {
455 	upd_node_t*	node;
456 
457 	node = static_cast<upd_node_t*>(
458 		mem_heap_zalloc(heap, sizeof(upd_node_t)));
459 
460 	node->common.type = QUE_NODE_UPDATE;
461 	node->state = UPD_NODE_UPDATE_CLUSTERED;
462 	node->heap = mem_heap_create(128);
463 	node->magic_n = UPD_NODE_MAGIC_N;
464 
465 	return(node);
466 }
467 
468 /*********************************************************************//**
469 Updates the trx id and roll ptr field in a clustered index record in database
470 recovery. */
471 void
row_upd_rec_sys_fields_in_recovery(rec_t * rec,page_zip_des_t * page_zip,const rec_offs * offsets,ulint pos,trx_id_t trx_id,roll_ptr_t roll_ptr)472 row_upd_rec_sys_fields_in_recovery(
473 /*===============================*/
474 	rec_t*		rec,	/*!< in/out: record */
475 	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
476 	const rec_offs*	offsets,/*!< in: array returned by rec_get_offsets() */
477 	ulint		pos,	/*!< in: TRX_ID position in rec */
478 	trx_id_t	trx_id,	/*!< in: transaction id */
479 	roll_ptr_t	roll_ptr)/*!< in: roll ptr of the undo log record */
480 {
481 	ut_ad(rec_offs_validate(rec, NULL, offsets));
482 
483 	if (page_zip) {
484 		page_zip_write_trx_id_and_roll_ptr(
485 			page_zip, rec, offsets, pos, trx_id, roll_ptr);
486 	} else {
487 		byte*	field;
488 		ulint	len;
489 
490 		field = rec_get_nth_field(rec, offsets, pos, &len);
491 		ut_ad(len == DATA_TRX_ID_LEN);
492 		compile_time_assert(DATA_TRX_ID + 1 == DATA_ROLL_PTR);
493 		trx_write_trx_id(field, trx_id);
494 		trx_write_roll_ptr(field + DATA_TRX_ID_LEN, roll_ptr);
495 	}
496 }
497 
498 /*********************************************************************//**
499 Sets the trx id or roll ptr field of a clustered index entry. */
500 void
row_upd_index_entry_sys_field(dtuple_t * entry,dict_index_t * index,ulint type,ib_uint64_t val)501 row_upd_index_entry_sys_field(
502 /*==========================*/
503 	dtuple_t*	entry,	/*!< in/out: index entry, where the memory
504 				buffers for sys fields are already allocated:
505 				the function just copies the new values to
506 				them */
507 	dict_index_t*	index,	/*!< in: clustered index */
508 	ulint		type,	/*!< in: DATA_TRX_ID or DATA_ROLL_PTR */
509 	ib_uint64_t	val)	/*!< in: value to write */
510 {
511 	dfield_t*	dfield;
512 	byte*		field;
513 	ulint		pos;
514 
515 	ut_ad(dict_index_is_clust(index));
516 
517 	pos = dict_index_get_sys_col_pos(index, type);
518 
519 	dfield = dtuple_get_nth_field(entry, pos);
520 	field = static_cast<byte*>(dfield_get_data(dfield));
521 
522 	if (type == DATA_TRX_ID) {
523 		ut_ad(val > 0 || index->table->is_temporary());
524 		trx_write_trx_id(field, val);
525 	} else {
526 		ut_ad(type == DATA_ROLL_PTR);
527 		trx_write_roll_ptr(field, val);
528 	}
529 }
530 
531 /***********************************************************//**
532 Returns TRUE if row update changes size of some field in index or if some
533 field to be updated is stored externally in rec or update.
534 @return TRUE if the update changes the size of some field in index or
535 the field is external in rec or update */
536 ibool
row_upd_changes_field_size_or_external(dict_index_t * index,const rec_offs * offsets,const upd_t * update)537 row_upd_changes_field_size_or_external(
538 /*===================================*/
539 	dict_index_t*	index,	/*!< in: index */
540 	const rec_offs*	offsets,/*!< in: rec_get_offsets(rec, index) */
541 	const upd_t*	update)	/*!< in: update vector */
542 {
543 	const upd_field_t*	upd_field;
544 	const dfield_t*		new_val;
545 	ulint			old_len;
546 	ulint			new_len;
547 	ulint			n_fields;
548 	ulint			i;
549 
550 	ut_ad(rec_offs_validate(NULL, index, offsets));
551 	ut_ad(!index->table->skip_alter_undo);
552 	n_fields = upd_get_n_fields(update);
553 
554 	for (i = 0; i < n_fields; i++) {
555 		upd_field = upd_get_nth_field(update, i);
556 
557 		/* We should ignore virtual field if the index is not
558 		a virtual index */
559 		if (upd_fld_is_virtual_col(upd_field)
560 		    && !index->has_virtual()) {
561 			continue;
562 		}
563 
564 		new_val = &(upd_field->new_val);
565 		if (dfield_is_ext(new_val)) {
566 			return(TRUE);
567 		}
568 		new_len = dfield_get_len(new_val);
569 		ut_ad(new_len != UNIV_SQL_DEFAULT);
570 
571 		if (dfield_is_null(new_val) && !rec_offs_comp(offsets)) {
572 			/* A bug fixed on Dec 31st, 2004: we looked at the
573 			SQL NULL size from the wrong field! We may backport
574 			this fix also to 4.0. The merge to 5.0 will be made
575 			manually immediately after we commit this to 4.1. */
576 
577 			new_len = dict_col_get_sql_null_size(
578 				dict_index_get_nth_col(index,
579 						       upd_field->field_no),
580 				0);
581 		}
582 
583 		if (rec_offs_nth_default(offsets, upd_field->field_no)) {
584 			/* This is an instantly added column that is
585 			at the initial default value. */
586 			return(TRUE);
587 		}
588 
589 		if (rec_offs_comp(offsets)
590 		    && rec_offs_nth_sql_null(offsets, upd_field->field_no)) {
591 			/* Note that in the compact table format, for a
592 			variable length field, an SQL NULL will use zero
593 			bytes in the offset array at the start of the physical
594 			record, but a zero-length value (empty string) will
595 			use one byte! Thus, we cannot use update-in-place
596 			if we update an SQL NULL varchar to an empty string! */
597 
598 			old_len = UNIV_SQL_NULL;
599 		} else {
600 			old_len = rec_offs_nth_size(offsets,
601 						    upd_field->field_no);
602 		}
603 
604 		if (old_len != new_len
605 		    || rec_offs_nth_extern(offsets, upd_field->field_no)) {
606 
607 			return(TRUE);
608 		}
609 	}
610 
611 	return(FALSE);
612 }
613 
614 /***********************************************************//**
615 Returns true if row update contains disowned external fields.
616 @return true if the update contains disowned external fields. */
617 bool
row_upd_changes_disowned_external(const upd_t * update)618 row_upd_changes_disowned_external(
619 /*==============================*/
620 	const upd_t*	update)	/*!< in: update vector */
621 {
622 	const upd_field_t*	upd_field;
623 	const dfield_t*		new_val;
624 	ulint			new_len;
625 	ulint                   n_fields;
626 	ulint			i;
627 
628 	n_fields = upd_get_n_fields(update);
629 
630 	for (i = 0; i < n_fields; i++) {
631 		const byte*	field_ref;
632 
633 		upd_field = upd_get_nth_field(update, i);
634 		new_val = &(upd_field->new_val);
635 		new_len = dfield_get_len(new_val);
636 
637 		if (!dfield_is_ext(new_val)) {
638 			continue;
639 		}
640 
641 		ut_ad(new_len >= BTR_EXTERN_FIELD_REF_SIZE);
642 
643 		field_ref = static_cast<const byte*>(dfield_get_data(new_val))
644 			    + new_len - BTR_EXTERN_FIELD_REF_SIZE;
645 
646 		if (field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG) {
647 			return(true);
648 		}
649 	}
650 
651 	return(false);
652 }
653 
654 /***********************************************************//**
655 Replaces the new column values stored in the update vector to the
656 record given. No field size changes are allowed. This function is
657 usually invoked on a clustered index. The only use case for a
658 secondary index is row_ins_sec_index_entry_by_modify() or its
659 counterpart in ibuf_insert_to_index_page(). */
660 void
row_upd_rec_in_place(rec_t * rec,dict_index_t * index,const rec_offs * offsets,const upd_t * update,page_zip_des_t * page_zip)661 row_upd_rec_in_place(
662 /*=================*/
663 	rec_t*		rec,	/*!< in/out: record where replaced */
664 	dict_index_t*	index,	/*!< in: the index the record belongs to */
665 	const rec_offs*	offsets,/*!< in: array returned by rec_get_offsets() */
666 	const upd_t*	update,	/*!< in: update vector */
667 	page_zip_des_t*	page_zip)/*!< in: compressed page with enough space
668 				available, or NULL */
669 {
670 	const upd_field_t*	upd_field;
671 	const dfield_t*		new_val;
672 	ulint			n_fields;
673 	ulint			i;
674 
675 	ut_ad(rec_offs_validate(rec, index, offsets));
676 	ut_ad(!index->table->skip_alter_undo);
677 
678 	if (rec_offs_comp(offsets)) {
679 #ifdef UNIV_DEBUG
680 		switch (rec_get_status(rec)) {
681 		case REC_STATUS_ORDINARY:
682 			break;
683 		case REC_STATUS_COLUMNS_ADDED:
684 			ut_ad(index->is_instant());
685 			break;
686 		case REC_STATUS_NODE_PTR:
687 			if (index->is_dummy
688 			    && fil_page_get_type(page_align(rec))
689 			    == FIL_PAGE_RTREE) {
690 				/* The function rtr_update_mbr_field_in_place()
691 				is generating MLOG_COMP_REC_UPDATE_IN_PLACE
692 				and MLOG_REC_UPDATE_IN_PLACE records for
693 				node pointer pages. */
694 				break;
695 			}
696 			/* fall through */
697 		case REC_STATUS_INFIMUM:
698 		case REC_STATUS_SUPREMUM:
699 			ut_ad(!"wrong record status in update");
700 		}
701 #endif /* UNIV_DEBUG */
702 
703 		rec_set_info_bits_new(rec, update->info_bits);
704 	} else {
705 		rec_set_info_bits_old(rec, update->info_bits);
706 	}
707 
708 	n_fields = upd_get_n_fields(update);
709 
710 	for (i = 0; i < n_fields; i++) {
711 		upd_field = upd_get_nth_field(update, i);
712 
713 		/* No need to update virtual columns for non-virtual index */
714 		if (upd_fld_is_virtual_col(upd_field)
715 		    && !dict_index_has_virtual(index)) {
716 			continue;
717 		}
718 
719 		new_val = &(upd_field->new_val);
720 		ut_ad(!dfield_is_ext(new_val) ==
721 		      !rec_offs_nth_extern(offsets, upd_field->field_no));
722 
723 		rec_set_nth_field(rec, offsets, upd_field->field_no,
724 				  dfield_get_data(new_val),
725 				  dfield_get_len(new_val));
726 	}
727 
728 	if (page_zip) {
729 		page_zip_write_rec(page_zip, rec, index, offsets, 0);
730 	}
731 }
732 
733 /*********************************************************************//**
734 Writes into the redo log the values of trx id and roll ptr and enough info
735 to determine their positions within a clustered index record.
736 @return new pointer to mlog */
737 byte*
row_upd_write_sys_vals_to_log(dict_index_t * index,trx_id_t trx_id,roll_ptr_t roll_ptr,byte * log_ptr,mtr_t * mtr MY_ATTRIBUTE ((unused)))738 row_upd_write_sys_vals_to_log(
739 /*==========================*/
740 	dict_index_t*	index,	/*!< in: clustered index */
741 	trx_id_t	trx_id,	/*!< in: transaction id */
742 	roll_ptr_t	roll_ptr,/*!< in: roll ptr of the undo log record */
743 	byte*		log_ptr,/*!< pointer to a buffer of size > 20 opened
744 				in mlog */
745 	mtr_t*		mtr MY_ATTRIBUTE((unused))) /*!< in: mtr */
746 {
747 	ut_ad(dict_index_is_clust(index));
748 	ut_ad(mtr);
749 
750 	log_ptr += mach_write_compressed(log_ptr,
751 					 dict_index_get_sys_col_pos(
752 						 index, DATA_TRX_ID));
753 
754 	trx_write_roll_ptr(log_ptr, roll_ptr);
755 	log_ptr += DATA_ROLL_PTR_LEN;
756 
757 	log_ptr += mach_u64_write_compressed(log_ptr, trx_id);
758 
759 	return(log_ptr);
760 }
761 
762 /*********************************************************************//**
763 Parses the log data of system field values.
764 @return log data end or NULL */
765 byte*
row_upd_parse_sys_vals(const byte * ptr,const byte * end_ptr,ulint * pos,trx_id_t * trx_id,roll_ptr_t * roll_ptr)766 row_upd_parse_sys_vals(
767 /*===================*/
768 	const byte*	ptr,	/*!< in: buffer */
769 	const byte*	end_ptr,/*!< in: buffer end */
770 	ulint*		pos,	/*!< out: TRX_ID position in record */
771 	trx_id_t*	trx_id,	/*!< out: trx id */
772 	roll_ptr_t*	roll_ptr)/*!< out: roll ptr */
773 {
774 	*pos = mach_parse_compressed(&ptr, end_ptr);
775 
776 	if (ptr == NULL) {
777 
778 		return(NULL);
779 	}
780 
781 	if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {
782 
783 		return(NULL);
784 	}
785 
786 	*roll_ptr = trx_read_roll_ptr(ptr);
787 	ptr += DATA_ROLL_PTR_LEN;
788 
789 	*trx_id = mach_u64_parse_compressed(&ptr, end_ptr);
790 
791 	return(const_cast<byte*>(ptr));
792 }
793 
794 /***********************************************************//**
795 Writes to the redo log the new values of the fields occurring in the index. */
796 void
row_upd_index_write_log(const upd_t * update,byte * log_ptr,mtr_t * mtr)797 row_upd_index_write_log(
798 /*====================*/
799 	const upd_t*	update,	/*!< in: update vector */
800 	byte*		log_ptr,/*!< in: pointer to mlog buffer: must
801 				contain at least MLOG_BUF_MARGIN bytes
802 				of free space; the buffer is closed
803 				within this function */
804 	mtr_t*		mtr)	/*!< in: mtr into whose log to write */
805 {
806 	const upd_field_t*	upd_field;
807 	const dfield_t*		new_val;
808 	ulint			len;
809 	ulint			n_fields;
810 	byte*			buf_end;
811 	ulint			i;
812 
813 	n_fields = upd_get_n_fields(update);
814 
815 	buf_end = log_ptr + MLOG_BUF_MARGIN;
816 
817 	mach_write_to_1(log_ptr, update->info_bits);
818 	log_ptr++;
819 	log_ptr += mach_write_compressed(log_ptr, n_fields);
820 
821 	for (i = 0; i < n_fields; i++) {
822 		compile_time_assert(MLOG_BUF_MARGIN > 30);
823 
824 		if (log_ptr + 30 > buf_end) {
825 			mlog_close(mtr, log_ptr);
826 
827 			log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
828 			buf_end = log_ptr + MLOG_BUF_MARGIN;
829 		}
830 
831 		upd_field = upd_get_nth_field(update, i);
832 
833 		new_val = &(upd_field->new_val);
834 
835 		len = dfield_get_len(new_val);
836 
837 		/* If this is a virtual column, mark it using special
838 		field_no */
839 		ulint	field_no = upd_fld_is_virtual_col(upd_field)
840 			? REC_MAX_N_FIELDS + unsigned(upd_field->field_no)
841 			: unsigned(upd_field->field_no);
842 
843 		log_ptr += mach_write_compressed(log_ptr, field_no);
844 		log_ptr += mach_write_compressed(log_ptr, len);
845 
846 		if (len != UNIV_SQL_NULL) {
847 			if (log_ptr + len < buf_end) {
848 				memcpy(log_ptr, dfield_get_data(new_val), len);
849 
850 				log_ptr += len;
851 			} else {
852 				mlog_close(mtr, log_ptr);
853 
854 				mlog_catenate_string(
855 					mtr,
856 					static_cast<const byte*>(
857 						dfield_get_data(new_val)),
858 					len);
859 
860 				log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
861 				buf_end = log_ptr + MLOG_BUF_MARGIN;
862 			}
863 		}
864 	}
865 
866 	mlog_close(mtr, log_ptr);
867 }
868 
869 /*********************************************************************//**
870 Parses the log data written by row_upd_index_write_log.
871 @return log data end or NULL */
872 byte*
row_upd_index_parse(const byte * ptr,const byte * end_ptr,mem_heap_t * heap,upd_t ** update_out)873 row_upd_index_parse(
874 /*================*/
875 	const byte*	ptr,	/*!< in: buffer */
876 	const byte*	end_ptr,/*!< in: buffer end */
877 	mem_heap_t*	heap,	/*!< in: memory heap where update vector is
878 				built */
879 	upd_t**		update_out)/*!< out: update vector */
880 {
881 	upd_t*		update;
882 	upd_field_t*	upd_field;
883 	dfield_t*	new_val;
884 	ulint		len;
885 	ulint		n_fields;
886 	ulint		info_bits;
887 	ulint		i;
888 
889 	if (end_ptr < ptr + 1) {
890 
891 		return(NULL);
892 	}
893 
894 	info_bits = mach_read_from_1(ptr);
895 	ptr++;
896 	n_fields = mach_parse_compressed(&ptr, end_ptr);
897 
898 	if (ptr == NULL) {
899 
900 		return(NULL);
901 	}
902 
903 	update = upd_create(n_fields, heap);
904 	update->info_bits = info_bits;
905 
906 	for (i = 0; i < n_fields; i++) {
907 		ulint	field_no;
908 		upd_field = upd_get_nth_field(update, i);
909 		new_val = &(upd_field->new_val);
910 
911 		field_no = mach_parse_compressed(&ptr, end_ptr);
912 
913 		if (ptr == NULL) {
914 
915 			return(NULL);
916 		}
917 
918 		/* Check if this is a virtual column, mark the prtype
919 		if that is the case */
920 		if (field_no >= REC_MAX_N_FIELDS) {
921 			new_val->type.prtype |= DATA_VIRTUAL;
922 			field_no -= REC_MAX_N_FIELDS;
923 		}
924 
925 		upd_field->field_no = field_no;
926 
927 		len = mach_parse_compressed(&ptr, end_ptr);
928 
929 		if (ptr == NULL) {
930 
931 			return(NULL);
932 		}
933 
934 		if (len != UNIV_SQL_NULL) {
935 
936 			if (end_ptr < ptr + len) {
937 
938 				return(NULL);
939 			}
940 
941 			dfield_set_data(new_val,
942 					mem_heap_dup(heap, ptr, len), len);
943 			ptr += len;
944 		} else {
945 			dfield_set_null(new_val);
946 		}
947 	}
948 
949 	*update_out = update;
950 
951 	return(const_cast<byte*>(ptr));
952 }
953 
954 /***************************************************************//**
955 Builds an update vector from those fields which in a secondary index entry
956 differ from a record that has the equal ordering fields. NOTE: we compare
957 the fields as binary strings!
958 @return own: update vector of differing fields */
959 upd_t*
row_upd_build_sec_rec_difference_binary(const rec_t * rec,dict_index_t * index,const rec_offs * offsets,const dtuple_t * entry,mem_heap_t * heap)960 row_upd_build_sec_rec_difference_binary(
961 /*====================================*/
962 	const rec_t*	rec,	/*!< in: secondary index record */
963 	dict_index_t*	index,	/*!< in: index */
964 	const rec_offs*	offsets,/*!< in: rec_get_offsets(rec, index) */
965 	const dtuple_t*	entry,	/*!< in: entry to insert */
966 	mem_heap_t*	heap)	/*!< in: memory heap from which allocated */
967 {
968 	upd_field_t*	upd_field;
969 	const dfield_t*	dfield;
970 	const byte*	data;
971 	ulint		len;
972 	upd_t*		update;
973 	ulint		n_diff;
974 	ulint		i;
975 
976 	/* This function is used only for a secondary index */
977 	ut_a(!dict_index_is_clust(index));
978 	ut_ad(rec_offs_validate(rec, index, offsets));
979 	ut_ad(rec_offs_n_fields(offsets) == dtuple_get_n_fields(entry));
980 	ut_ad(!rec_offs_any_extern(offsets));
981 	ut_ad(!rec_offs_any_default(offsets));
982 	ut_ad(!index->table->skip_alter_undo);
983 
984 	update = upd_create(dtuple_get_n_fields(entry), heap);
985 
986 	n_diff = 0;
987 
988 	for (i = 0; i < dtuple_get_n_fields(entry); i++) {
989 
990 		data = rec_get_nth_field(rec, offsets, i, &len);
991 
992 		dfield = dtuple_get_nth_field(entry, i);
993 
994 		/* NOTE that it may be that len != dfield_get_len(dfield) if we
995 		are updating in a character set and collation where strings of
996 		different length can be equal in an alphabetical comparison,
997 		and also in the case where we have a column prefix index
998 		and the last characters in the index field are spaces; the
999 		latter case probably caused the assertion failures reported at
1000 		row0upd.cc line 713 in versions 4.0.14 - 4.0.16. */
1001 
1002 		/* NOTE: we compare the fields as binary strings!
1003 		(No collation) */
1004 
1005 		if (!dfield_data_is_binary_equal(dfield, len, data)) {
1006 
1007 			upd_field = upd_get_nth_field(update, n_diff);
1008 
1009 			dfield_copy(&(upd_field->new_val), dfield);
1010 
1011 			upd_field_set_field_no(upd_field, i, index);
1012 
1013 			n_diff++;
1014 		}
1015 	}
1016 
1017 	update->n_fields = n_diff;
1018 
1019 	return(update);
1020 }
1021 
1022 
1023 /** Builds an update vector from those fields, excluding the roll ptr and
1024 trx id fields, which in an index entry differ from a record that has
1025 the equal ordering fields. NOTE: we compare the fields as binary strings!
1026 @param[in]	index		clustered index
1027 @param[in]	entry		clustered index entry to insert
1028 @param[in]	rec		clustered index record
1029 @param[in]	offsets		rec_get_offsets(rec,index), or NULL
1030 @param[in]	no_sys		skip the system columns
1031 				DB_TRX_ID and DB_ROLL_PTR
1032 @param[in]	trx		transaction (for diagnostics),
1033 				or NULL
1034 @param[in]	heap		memory heap from which allocated
1035 @param[in]	mysql_table	NULL, or mysql table object when
1036 				user thread invokes dml
1037 @param[out]	error		error number in case of failure
1038 @return own: update vector of differing fields, excluding roll ptr and
1039 trx id,if error is not equal to DB_SUCCESS, return NULL */
1040 upd_t*
row_upd_build_difference_binary(dict_index_t * index,const dtuple_t * entry,const rec_t * rec,const rec_offs * offsets,bool no_sys,trx_t * trx,mem_heap_t * heap,TABLE * mysql_table,dberr_t * error)1041 row_upd_build_difference_binary(
1042 	dict_index_t*	index,
1043 	const dtuple_t*	entry,
1044 	const rec_t*	rec,
1045 	const rec_offs*	offsets,
1046 	bool		no_sys,
1047 	trx_t*		trx,
1048 	mem_heap_t*	heap,
1049 	TABLE*		mysql_table,
1050 	dberr_t*	error)
1051 {
1052 	ulint		len;
1053 	upd_t*		update;
1054 	ulint		n_diff;
1055 	ulint		trx_id_pos;
1056 	rec_offs	offsets_[REC_OFFS_NORMAL_SIZE];
1057 	const ulint	n_v_fld = dtuple_get_n_v_fields(entry);
1058 	rec_offs_init(offsets_);
1059 
1060 	/* This function is used only for a clustered index */
1061 	ut_a(dict_index_is_clust(index));
1062 	ut_ad(!index->table->skip_alter_undo);
1063 	ut_ad(entry->n_fields <= index->n_fields);
1064 	ut_ad(entry->n_fields >= index->n_core_fields);
1065 
1066 	update = upd_create(index->n_fields + n_v_fld, heap);
1067 
1068 	n_diff = 0;
1069 
1070 	trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
1071 	ut_ad(dict_index_get_sys_col_pos(index, DATA_ROLL_PTR)
1072 	      == trx_id_pos + 1);
1073 
1074 	if (!offsets) {
1075 		offsets = rec_get_offsets(rec, index, offsets_,
1076 					  index->n_core_fields,
1077 					  ULINT_UNDEFINED, &heap);
1078 	} else {
1079 		ut_ad(rec_offs_validate(rec, index, offsets));
1080 	}
1081 
1082 	for (ulint i = 0; i < entry->n_fields; i++) {
1083 		const byte* data = rec_get_nth_cfield(rec, index, offsets, i,
1084 						      &len);
1085 		const dfield_t* dfield = dtuple_get_nth_field(entry, i);
1086 
1087 		/* NOTE: we compare the fields as binary strings!
1088 		(No collation) */
1089 		if (no_sys) {
1090 			/* TRX_ID */
1091 			if (i == trx_id_pos) {
1092 				continue;
1093 			}
1094 
1095 			/* DB_ROLL_PTR */
1096 			if (i == trx_id_pos + 1) {
1097 				continue;
1098 			}
1099 		}
1100 
1101 		if (!dfield_is_ext(dfield)
1102 		    != !rec_offs_nth_extern(offsets, i)
1103 		    || !dfield_data_is_binary_equal(dfield, len, data)) {
1104 			upd_field_t* uf = upd_get_nth_field(update, n_diff++);
1105 			dfield_copy(&uf->new_val, dfield);
1106 			upd_field_set_field_no(uf, i, index);
1107 		}
1108 	}
1109 
1110 	for (ulint i = entry->n_fields; i < index->n_fields; i++) {
1111 		upd_field_t* uf = upd_get_nth_field(update, n_diff++);
1112 		const dict_col_t* col = dict_index_get_nth_col(index, i);
1113 		/* upd_create() zero-initialized uf */
1114 		uf->new_val.data = const_cast<byte*>(col->instant_value(&len));
1115 		uf->new_val.len = static_cast<unsigned>(len);
1116 		dict_col_copy_type(col, &uf->new_val.type);
1117 		upd_field_set_field_no(uf, i, index);
1118 	}
1119 
1120 	/* Check the virtual columns updates. Even if there is no non-virtual
1121 	column (base columns) change, we will still need to build the
1122 	indexed virtual column value so that undo log would log them (
1123 	for purge/mvcc purpose) */
1124 	if (n_v_fld > 0) {
1125 		row_ext_t*	ext;
1126 		THD*		thd;
1127 
1128 		if (trx == NULL) {
1129 			thd = current_thd;
1130 		} else {
1131 			thd = trx->mysql_thd;
1132 		}
1133 
1134 		ut_ad(!update->old_vrow);
1135 
1136 		ib_vcol_row vc(NULL);
1137 		uchar *record = vc.record(thd, index, &mysql_table);
1138 
1139 		for (ulint i = 0; i < n_v_fld; i++) {
1140 			const dict_v_col_t*     col
1141                                 = dict_table_get_nth_v_col(index->table, i);
1142 
1143 			if (!col->m_col.ord_part) {
1144 				continue;
1145 			}
1146 
1147 			if (update->old_vrow == NULL) {
1148 				update->old_vrow = row_build(
1149 					ROW_COPY_POINTERS, index, rec, offsets,
1150 					index->table, NULL, NULL, &ext, heap);
1151 			}
1152 
1153 			dfield_t*	vfield = innobase_get_computed_value(
1154 				update->old_vrow, col, index,
1155 				&vc.heap, heap, NULL, thd, mysql_table, record,
1156 				NULL, NULL);
1157 			if (vfield == NULL) {
1158 				*error = DB_COMPUTE_VALUE_FAILED;
1159 				return(NULL);
1160 			}
1161 
1162 			const dfield_t* dfield = dtuple_get_nth_v_field(
1163 				entry, i);
1164 
1165 			if (!dfield_data_is_binary_equal(
1166 				    dfield, vfield->len,
1167 				    static_cast<byte*>(vfield->data))) {
1168 				upd_field_t* uf = upd_get_nth_field(update,
1169 								    n_diff++);
1170 				uf->old_v_val = static_cast<dfield_t*>(
1171 					mem_heap_alloc(heap,
1172 						       sizeof *uf->old_v_val));
1173 				dfield_copy(uf->old_v_val, vfield);
1174 				dfield_copy(&uf->new_val, dfield);
1175 				upd_field_set_v_field_no(uf, i, index);
1176 			}
1177 		}
1178 	}
1179 
1180 	update->n_fields = n_diff;
1181 	ut_ad(update->validate());
1182 
1183 	return(update);
1184 }
1185 
1186 /** Fetch a prefix of an externally stored column.
1187 This is similar to row_ext_lookup(), but the row_ext_t holds the old values
1188 of the column and must not be poisoned with the new values.
1189 @param[in]	data		'internally' stored part of the field
1190 containing also the reference to the external part
1191 @param[in]	local_len	length of data, in bytes
1192 @param[in]	page_size	BLOB page size
1193 @param[in,out]	len		input - length of prefix to
1194 fetch; output: fetched length of the prefix
1195 @param[in,out]	heap		heap where to allocate
1196 @return BLOB prefix
1197 @retval NULL if the record is incomplete (should only happen
1198 in row_vers_vc_matches_cluster() executed concurrently with another purge) */
1199 static
1200 byte*
row_upd_ext_fetch(const byte * data,ulint local_len,const page_size_t & page_size,ulint * len,mem_heap_t * heap)1201 row_upd_ext_fetch(
1202 	const byte*		data,
1203 	ulint			local_len,
1204 	const page_size_t&	page_size,
1205 	ulint*			len,
1206 	mem_heap_t*		heap)
1207 {
1208 	byte*	buf = static_cast<byte*>(mem_heap_alloc(heap, *len));
1209 
1210 	*len = btr_copy_externally_stored_field_prefix(
1211 		buf, *len, page_size, data, local_len);
1212 
1213 	return *len ? buf : NULL;
1214 }
1215 
1216 /** Replaces the new column value stored in the update vector in
1217 the given index entry field.
1218 @param[in,out]	dfield		data field of the index entry
1219 @param[in]	field		index field
1220 @param[in]	col		field->col
1221 @param[in]	uf		update field
1222 @param[in,out]	heap		memory heap for allocating and copying
1223 the new value
1224 @param[in]	page_size	page size
1225 @return whether the previous version was built successfully */
1226 MY_ATTRIBUTE((nonnull, warn_unused_result))
1227 static
1228 bool
row_upd_index_replace_new_col_val(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)1229 row_upd_index_replace_new_col_val(
1230 	dfield_t*		dfield,
1231 	const dict_field_t*	field,
1232 	const dict_col_t*	col,
1233 	const upd_field_t*	uf,
1234 	mem_heap_t*		heap,
1235 	const page_size_t&	page_size)
1236 {
1237 	ulint		len;
1238 	const byte*	data;
1239 
1240 	dfield_copy_data(dfield, &uf->new_val);
1241 
1242 	if (dfield_is_null(dfield)) {
1243 		return true;
1244 	}
1245 
1246 	len = dfield_get_len(dfield);
1247 	data = static_cast<const byte*>(dfield_get_data(dfield));
1248 
1249 	if (field->prefix_len > 0) {
1250 		ibool		fetch_ext = dfield_is_ext(dfield)
1251 			&& len < (ulint) field->prefix_len
1252 			+ BTR_EXTERN_FIELD_REF_SIZE;
1253 
1254 		if (fetch_ext) {
1255 			ulint	l = len;
1256 
1257 			len = field->prefix_len;
1258 
1259 			data = row_upd_ext_fetch(data, l, page_size,
1260 						 &len, heap);
1261 			if (UNIV_UNLIKELY(!data)) {
1262 				return false;
1263 			}
1264 		}
1265 
1266 		len = dtype_get_at_most_n_mbchars(col->prtype,
1267 						  col->mbminlen, col->mbmaxlen,
1268 						  field->prefix_len, len,
1269 						  (const char*) data);
1270 
1271 		dfield_set_data(dfield, data, len);
1272 
1273 		if (!fetch_ext) {
1274 			dfield_dup(dfield, heap);
1275 		}
1276 
1277 		return true;
1278 	}
1279 
1280 	switch (uf->orig_len) {
1281 		byte*	buf;
1282 	case BTR_EXTERN_FIELD_REF_SIZE:
1283 		/* Restore the original locally stored
1284 		part of the column.  In the undo log,
1285 		InnoDB writes a longer prefix of externally
1286 		stored columns, so that column prefixes
1287 		in secondary indexes can be reconstructed. */
1288 		dfield_set_data(dfield,
1289 				data + len - BTR_EXTERN_FIELD_REF_SIZE,
1290 				BTR_EXTERN_FIELD_REF_SIZE);
1291 		dfield_set_ext(dfield);
1292 		/* fall through */
1293 	case 0:
1294 		dfield_dup(dfield, heap);
1295 		break;
1296 	default:
1297 		/* Reconstruct the original locally
1298 		stored part of the column.  The data
1299 		will have to be copied. */
1300 		ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
1301 		buf = static_cast<byte*>(mem_heap_alloc(heap, uf->orig_len));
1302 
1303 		/* Copy the locally stored prefix. */
1304 		memcpy(buf, data,
1305 		       unsigned(uf->orig_len) - BTR_EXTERN_FIELD_REF_SIZE);
1306 
1307 		/* Copy the BLOB pointer. */
1308 		memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE,
1309 		       data + len - BTR_EXTERN_FIELD_REF_SIZE,
1310 		       BTR_EXTERN_FIELD_REF_SIZE);
1311 
1312 		dfield_set_data(dfield, buf, uf->orig_len);
1313 		dfield_set_ext(dfield);
1314 		break;
1315 	}
1316 
1317 	return true;
1318 }
1319 
1320 /** Apply an update vector to an index entry.
1321 @param[in,out]	entry	index entry to be updated; the clustered index record
1322 			must be covered by a lock or a page latch to prevent
1323 			deletion (rollback or purge)
1324 @param[in]	index	index of the entry
1325 @param[in]	update	update vector built for the entry
1326 @param[in,out]	heap	memory heap for copying off-page columns */
1327 void
row_upd_index_replace_new_col_vals_index_pos(dtuple_t * entry,const dict_index_t * index,const upd_t * update,mem_heap_t * heap)1328 row_upd_index_replace_new_col_vals_index_pos(
1329 	dtuple_t*		entry,
1330 	const dict_index_t*	index,
1331 	const upd_t*		update,
1332 	mem_heap_t*		heap)
1333 {
1334 	ut_ad(!index->table->skip_alter_undo);
1335 
1336 	const page_size_t&	page_size = dict_table_page_size(index->table);
1337 
1338 	dtuple_set_info_bits(entry, update->info_bits);
1339 
1340 	for (unsigned i = index->n_fields; i--; ) {
1341 		const dict_field_t*	field;
1342 		const dict_col_t*	col;
1343 		const upd_field_t*	uf;
1344 
1345 		field = dict_index_get_nth_field(index, i);
1346 		col = dict_field_get_col(field);
1347 		if (col->is_virtual()) {
1348 			const dict_v_col_t*	vcol = reinterpret_cast<
1349 							const dict_v_col_t*>(
1350 								col);
1351 
1352 			uf = upd_get_field_by_field_no(
1353 				update, vcol->v_pos, true);
1354 		} else {
1355 			uf = upd_get_field_by_field_no(
1356 				update, i, false);
1357 		}
1358 
1359 		if (uf && UNIV_UNLIKELY(!row_upd_index_replace_new_col_val(
1360 						dtuple_get_nth_field(entry, i),
1361 						field, col, uf, heap,
1362 						page_size))) {
1363 			ut_error;
1364 		}
1365 	}
1366 }
1367 
1368 /** Replace the new column values stored in the update vector,
1369 during trx_undo_prev_version_build().
1370 @param entry   clustered index tuple where the values are replaced
1371                (the clustered index leaf page latch must be held)
1372 @param index   clustered index
1373 @param update  update vector for the clustered index
1374 @param heap    memory heap for allocating and copying values
1375 @return whether the previous version was built successfully */
1376 bool
row_upd_index_replace_new_col_vals(dtuple_t * entry,const dict_index_t & index,const upd_t * update,mem_heap_t * heap)1377 row_upd_index_replace_new_col_vals(dtuple_t *entry, const dict_index_t &index,
1378                                    const upd_t *update, mem_heap_t *heap)
1379 {
1380   ut_ad(index.is_primary());
1381   const page_size_t& page_size= dict_table_page_size(index.table);
1382 
1383   ut_ad(!index.table->skip_alter_undo);
1384   dtuple_set_info_bits(entry, update->info_bits);
1385 
1386   for (ulint i= 0; i < index.n_fields; i++)
1387   {
1388    const dict_field_t *field= &index.fields[i];
1389    const dict_col_t* col= dict_field_get_col(field);
1390    const upd_field_t *uf;
1391 
1392    if (col->is_virtual())
1393    {
1394      const dict_v_col_t *vcol= reinterpret_cast<const dict_v_col_t*>(col);
1395      uf= upd_get_field_by_field_no(update, vcol->v_pos, true);
1396    }
1397    else
1398      uf= upd_get_field_by_field_no(update, dict_col_get_clust_pos(col, &index),
1399                                    false);
1400 
1401    if (!uf)
1402      continue;
1403 
1404    if (!row_upd_index_replace_new_col_val(dtuple_get_nth_field(entry, i),
1405                                           field, col, uf, heap, page_size))
1406      return false;
1407   }
1408 
1409   return true;
1410 }
1411 
1412 /** Replaces the virtual column values stored in the update vector.
1413 @param[in,out]	row	row whose column to be set
1414 @param[in]	field	data to set
1415 @param[in]	len	data length
1416 @param[in]	vcol	virtual column info */
1417 static
1418 void
row_upd_set_vcol_data(dtuple_t * row,const byte * field,ulint len,dict_v_col_t * vcol)1419 row_upd_set_vcol_data(
1420 	dtuple_t*		row,
1421 	const byte*             field,
1422 	ulint                   len,
1423 	dict_v_col_t*		vcol)
1424 {
1425 	dfield_t*	dfield = dtuple_get_nth_v_field(row, vcol->v_pos);
1426 
1427 	if (dfield_get_type(dfield)->mtype == DATA_MISSING) {
1428 		dict_col_copy_type(&vcol->m_col, dfield_get_type(dfield));
1429 
1430 		dfield_set_data(dfield, field, len);
1431 	}
1432 }
1433 
1434 /** Replaces the virtual column values stored in a dtuple with that of
1435 a update vector.
1436 @param[in,out]	row	row whose column to be updated
1437 @param[in]	table	table
1438 @param[in]	update	an update vector built for the clustered index
1439 @param[in]	upd_new	update to new or old value
1440 @param[in,out]	undo_row undo row (if needs to be updated)
1441 @param[in]	ptr	remaining part in update undo log */
1442 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)1443 row_upd_replace_vcol(
1444 	dtuple_t*		row,
1445 	const dict_table_t*	table,
1446 	const upd_t*		update,
1447 	bool			upd_new,
1448 	dtuple_t*		undo_row,
1449 	const byte*		ptr)
1450 {
1451 	ulint			col_no;
1452 	ulint			i;
1453 	ulint			n_cols;
1454 
1455 	ut_ad(!table->skip_alter_undo);
1456 
1457 	n_cols = dtuple_get_n_v_fields(row);
1458 	for (col_no = 0; col_no < n_cols; col_no++) {
1459 		dfield_t*		dfield;
1460 
1461 		const dict_v_col_t*	col
1462 			= dict_table_get_nth_v_col(table, col_no);
1463 
1464 		/* If there is no index on the column, do not bother for
1465 		value update */
1466 		if (!col->m_col.ord_part) {
1467 			dict_index_t*	clust_index
1468 				= dict_table_get_first_index(table);
1469 
1470 			/* Skip the column if there is no online alter
1471 			table in progress or it is not being indexed
1472 			in new table */
1473 			if (!dict_index_is_online_ddl(clust_index)
1474 			    || !row_log_col_is_indexed(clust_index, col_no)) {
1475 				continue;
1476 			}
1477 		}
1478 
1479 		dfield = dtuple_get_nth_v_field(row, col_no);
1480 
1481 		for (i = 0; i < upd_get_n_fields(update); i++) {
1482 			const upd_field_t*	upd_field
1483 				= upd_get_nth_field(update, i);
1484 			if (!upd_fld_is_virtual_col(upd_field)
1485 			    || upd_field->field_no != col->v_pos) {
1486 				continue;
1487 			}
1488 
1489 			if (upd_new) {
1490 				dfield_copy_data(dfield, &upd_field->new_val);
1491 			} else {
1492 				dfield_copy_data(dfield, upd_field->old_v_val);
1493 			}
1494 
1495 			dfield->type = upd_field->new_val.type;
1496 			break;
1497 		}
1498 	}
1499 
1500 	bool	first_v_col = true;
1501 	bool	is_undo_log = true;
1502 
1503 	/* We will read those unchanged (but indexed) virtual columns in */
1504 	if (ptr != NULL) {
1505 		const byte*	end_ptr;
1506 
1507 		end_ptr = ptr + mach_read_from_2(ptr);
1508 		ptr += 2;
1509 
1510 		while (ptr != end_ptr) {
1511 			const byte*             field;
1512 			ulint                   field_no;
1513 			ulint                   len;
1514 			ulint                   orig_len;
1515 			bool			is_v;
1516 
1517 			field_no = mach_read_next_compressed(&ptr);
1518 
1519 			is_v = (field_no >= REC_MAX_N_FIELDS);
1520 
1521 			if (is_v) {
1522 				ptr = trx_undo_read_v_idx(
1523 					table, ptr, first_v_col, &is_undo_log,
1524 					&field_no);
1525 				first_v_col = false;
1526 			}
1527 
1528 			ptr = trx_undo_rec_get_col_val(
1529 				ptr, &field, &len, &orig_len);
1530 
1531 			if (field_no == ULINT_UNDEFINED) {
1532 				ut_ad(is_v);
1533 				continue;
1534 			}
1535 
1536 			if (is_v) {
1537 				dict_v_col_t* vcol = dict_table_get_nth_v_col(
1538 							table, field_no);
1539 
1540 				row_upd_set_vcol_data(row, field, len, vcol);
1541 
1542 				if (undo_row) {
1543 					row_upd_set_vcol_data(
1544 						undo_row, field, len, vcol);
1545 				}
1546 			}
1547 			ut_ad(ptr<= end_ptr);
1548 		}
1549 	}
1550 }
1551 
1552 /***********************************************************//**
1553 Replaces the new column values stored in the update vector. */
1554 void
row_upd_replace(dtuple_t * row,row_ext_t ** ext,const dict_index_t * index,const upd_t * update,mem_heap_t * heap)1555 row_upd_replace(
1556 /*============*/
1557 	dtuple_t*		row,	/*!< in/out: row where replaced,
1558 					indexed by col_no;
1559 					the clustered index record must be
1560 					covered by a lock or a page latch to
1561 					prevent deletion (rollback or purge) */
1562 	row_ext_t**		ext,	/*!< out, own: NULL, or externally
1563 					stored column prefixes */
1564 	const dict_index_t*	index,	/*!< in: clustered index */
1565 	const upd_t*		update,	/*!< in: an update vector built for the
1566 					clustered index */
1567 	mem_heap_t*		heap)	/*!< in: memory heap */
1568 {
1569 	ulint			col_no;
1570 	ulint			i;
1571 	ulint			n_cols;
1572 	ulint			n_ext_cols;
1573 	ulint*			ext_cols;
1574 	const dict_table_t*	table;
1575 
1576 	ut_ad(row);
1577 	ut_ad(ext);
1578 	ut_ad(index);
1579 	ut_ad(dict_index_is_clust(index));
1580 	ut_ad(update);
1581 	ut_ad(heap);
1582 	ut_ad(update->validate());
1583 
1584 	n_cols = dtuple_get_n_fields(row);
1585 	table = index->table;
1586 	ut_ad(n_cols == dict_table_get_n_cols(table));
1587 
1588 	ext_cols = static_cast<ulint*>(
1589 		mem_heap_alloc(heap, n_cols * sizeof *ext_cols));
1590 
1591 	n_ext_cols = 0;
1592 
1593 	dtuple_set_info_bits(row, update->info_bits);
1594 
1595 	for (col_no = 0; col_no < n_cols; col_no++) {
1596 
1597 		const dict_col_t*	col
1598 			= dict_table_get_nth_col(table, col_no);
1599 		const ulint		clust_pos
1600 			= dict_col_get_clust_pos(col, index);
1601 		dfield_t*		dfield;
1602 
1603 		if (UNIV_UNLIKELY(clust_pos == ULINT_UNDEFINED)) {
1604 
1605 			continue;
1606 		}
1607 
1608 		dfield = dtuple_get_nth_field(row, col_no);
1609 
1610 		for (i = 0; i < upd_get_n_fields(update); i++) {
1611 
1612 			const upd_field_t*	upd_field
1613 				= upd_get_nth_field(update, i);
1614 
1615 			if (upd_field->field_no != clust_pos
1616 			    || upd_fld_is_virtual_col(upd_field)) {
1617 
1618 				continue;
1619 			}
1620 
1621 			dfield_copy_data(dfield, &upd_field->new_val);
1622 			break;
1623 		}
1624 
1625 		if (dfield_is_ext(dfield) && col->ord_part) {
1626 			ext_cols[n_ext_cols++] = col_no;
1627 		}
1628 	}
1629 
1630 	if (n_ext_cols) {
1631 		*ext = row_ext_create(n_ext_cols, ext_cols, table->flags, row,
1632 				      heap);
1633 	} else {
1634 		*ext = NULL;
1635 	}
1636 
1637 	row_upd_replace_vcol(row, table, update, true, NULL, NULL);
1638 }
1639 
1640 /***********************************************************//**
1641 Checks if an update vector changes an ordering field of an index record.
1642 
1643 This function is fast if the update vector is short or the number of ordering
1644 fields in the index is small. Otherwise, this can be quadratic.
1645 NOTE: we compare the fields as binary strings!
1646 @return TRUE if update vector changes an ordering field in the index record */
1647 ibool
row_upd_changes_ord_field_binary_func(dict_index_t * index,const upd_t * update,const que_thr_t * thr,const dtuple_t * row,const row_ext_t * ext,ulint flag)1648 row_upd_changes_ord_field_binary_func(
1649 /*==================================*/
1650 	dict_index_t*	index,	/*!< in: index of the record */
1651 	const upd_t*	update,	/*!< in: update vector for the row; NOTE: the
1652 				field numbers in this MUST be clustered index
1653 				positions! */
1654 #ifdef UNIV_DEBUG
1655 	const que_thr_t*thr,	/*!< in: query thread */
1656 #endif /* UNIV_DEBUG */
1657 	const dtuple_t*	row,	/*!< in: old value of row, or NULL if the
1658 				row and the data values in update are not
1659 				known when this function is called, e.g., at
1660 				compile time */
1661 	const row_ext_t*ext,	/*!< NULL, or prefixes of the externally
1662 				stored columns in the old row */
1663 	ulint		flag)	/*!< in: ROW_BUILD_NORMAL,
1664 				ROW_BUILD_FOR_PURGE or ROW_BUILD_FOR_UNDO */
1665 {
1666 	ulint			n_unique;
1667 	ulint			i;
1668 	const dict_index_t*	clust_index;
1669 
1670 	ut_ad(thr);
1671 	ut_ad(thr->graph);
1672 	ut_ad(thr->graph->trx);
1673 	ut_ad(!index->table->skip_alter_undo);
1674 
1675 	n_unique = dict_index_get_n_unique(index);
1676 
1677 	clust_index = dict_table_get_first_index(index->table);
1678 
1679 	for (i = 0; i < n_unique; i++) {
1680 
1681 		const dict_field_t*	ind_field;
1682 		const dict_col_t*	col;
1683 		ulint			col_no;
1684 		const upd_field_t*	upd_field;
1685 		const dfield_t*		dfield;
1686 		dfield_t		dfield_ext;
1687 		ulint			dfield_len= 0;
1688 		const byte*		buf;
1689 		bool			is_virtual;
1690 		const dict_v_col_t*	vcol = NULL;
1691 
1692 		ind_field = dict_index_get_nth_field(index, i);
1693 		col = dict_field_get_col(ind_field);
1694 		col_no = dict_col_get_no(col);
1695 		is_virtual = col->is_virtual();
1696 
1697 		if (is_virtual) {
1698 			vcol = reinterpret_cast<const dict_v_col_t*>(col);
1699 
1700 			upd_field = upd_get_field_by_field_no(
1701 				update, vcol->v_pos, true);
1702 		} else {
1703 			upd_field = upd_get_field_by_field_no(
1704 				update,
1705 				dict_col_get_clust_pos(col, clust_index),
1706 				false);
1707 		}
1708 
1709 		if (upd_field == NULL) {
1710 			continue;
1711 		}
1712 
1713 		if (row == NULL) {
1714 			ut_ad(ext == NULL);
1715 			return(TRUE);
1716 		}
1717 
1718 		if (is_virtual) {
1719 			dfield = dtuple_get_nth_v_field(
1720 				row,  vcol->v_pos);
1721 		} else {
1722 			dfield = dtuple_get_nth_field(row, col_no);
1723 		}
1724 
1725 		/* For spatial index update, since the different geometry
1726 		data could generate same MBR, so, if the new index entry is
1727 		same as old entry, which means the MBR is not changed, we
1728 		don't need to do anything. */
1729 		if (dict_index_is_spatial(index) && i == 0) {
1730 			double		mbr1[SPDIMS * 2];
1731 			double		mbr2[SPDIMS * 2];
1732 			rtr_mbr_t*	old_mbr;
1733 			rtr_mbr_t*	new_mbr;
1734 			const uchar*	dptr = NULL;
1735 			ulint		flen = 0;
1736 			ulint		dlen = 0;
1737 			mem_heap_t*	temp_heap = NULL;
1738 			const dfield_t*	new_field = &upd_field->new_val;
1739 
1740 			const page_size_t	page_size
1741 				= (ext != NULL)
1742 				? ext->page_size
1743 				: dict_table_page_size(
1744 					index->table);
1745 
1746 			ut_ad(dfield->data != NULL
1747 			      && dfield->len > GEO_DATA_HEADER_SIZE);
1748 			ut_ad(dict_col_get_spatial_status(col) != SPATIAL_NONE);
1749 
1750 			/* Get the old mbr. */
1751 			if (dfield_is_ext(dfield)) {
1752 				/* For off-page stored data, we
1753 				need to read the whole field data. */
1754 				flen = dfield_get_len(dfield);
1755 				dptr = static_cast<const byte*>(
1756 					dfield_get_data(dfield));
1757 				temp_heap = mem_heap_create(1000);
1758 
1759 				dptr = btr_copy_externally_stored_field(
1760 					&dlen, dptr,
1761 					page_size,
1762 					flen,
1763 					temp_heap);
1764 			} else {
1765 				dptr = static_cast<const uchar*>(dfield->data);
1766 				dlen = dfield->len;
1767 			}
1768 
1769 			rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE,
1770 					   static_cast<uint>(dlen
1771 					   - GEO_DATA_HEADER_SIZE),
1772 					   SPDIMS, mbr1);
1773 			old_mbr = reinterpret_cast<rtr_mbr_t*>(mbr1);
1774 
1775 			/* Get the new mbr. */
1776 			if (dfield_is_ext(new_field)) {
1777 				if (flag == ROW_BUILD_FOR_UNDO
1778 				    && dict_table_has_atomic_blobs(
1779 					    index->table)) {
1780 					/* For ROW_FORMAT=DYNAMIC
1781 					or COMPRESSED, a prefix of
1782 					off-page records is stored
1783 					in the undo log record
1784 					(for any column prefix indexes).
1785 					For SPATIAL INDEX, we must
1786 					ignore this prefix. The
1787 					full column value is stored in
1788 					the BLOB.
1789 					For non-spatial index, we
1790 					would have already fetched a
1791 					necessary prefix of the BLOB,
1792 					available in the "ext" parameter.
1793 
1794 					Here, for SPATIAL INDEX, we are
1795 					fetching the full column, which is
1796 					potentially wasting a lot of I/O,
1797 					memory, and possibly involving a
1798 					concurrency problem, similar to ones
1799 					that existed before the introduction
1800 					of row_ext_t.
1801 
1802 					MDEV-11657 FIXME: write the MBR
1803 					directly to the undo log record,
1804 					and avoid recomputing it here! */
1805 					flen = BTR_EXTERN_FIELD_REF_SIZE;
1806 					ut_ad(dfield_get_len(new_field) >=
1807 					      BTR_EXTERN_FIELD_REF_SIZE);
1808 					dptr = static_cast<const byte*>(
1809 						dfield_get_data(new_field))
1810 						+ dfield_get_len(new_field)
1811 						- BTR_EXTERN_FIELD_REF_SIZE;
1812 				} else {
1813 					flen = dfield_get_len(new_field);
1814 					dptr = static_cast<const byte*>(
1815 						dfield_get_data(new_field));
1816 				}
1817 
1818 				if (temp_heap == NULL) {
1819 					temp_heap = mem_heap_create(1000);
1820 				}
1821 
1822 				dptr = btr_copy_externally_stored_field(
1823 					&dlen, dptr,
1824 					page_size,
1825 					flen,
1826 					temp_heap);
1827 			} else {
1828 				dptr = static_cast<const byte*>(
1829 					upd_field->new_val.data);
1830 				dlen = upd_field->new_val.len;
1831 			}
1832 			rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE,
1833 					   static_cast<uint>(dlen
1834 					   - GEO_DATA_HEADER_SIZE),
1835 					   SPDIMS, mbr2);
1836 			new_mbr = reinterpret_cast<rtr_mbr_t*>(mbr2);
1837 
1838 			if (temp_heap) {
1839 				mem_heap_free(temp_heap);
1840 			}
1841 
1842 			if (!MBR_EQUAL_CMP(old_mbr, new_mbr)) {
1843 				return(TRUE);
1844 			} else {
1845 				continue;
1846 			}
1847 		}
1848 
1849 		/* This treatment of column prefix indexes is loosely
1850 		based on row_build_index_entry(). */
1851 
1852 		if (UNIV_LIKELY(ind_field->prefix_len == 0)
1853 		    || dfield_is_null(dfield)) {
1854 			/* do nothing special */
1855 		} else if (ext) {
1856 			/* Silence a compiler warning without
1857 			silencing a Valgrind error. */
1858 			dfield_len = 0;
1859 			MEM_UNDEFINED(&dfield_len, sizeof dfield_len);
1860 			/* See if the column is stored externally. */
1861 			buf = row_ext_lookup(ext, col_no, &dfield_len);
1862 
1863 			ut_ad(col->ord_part);
1864 
1865 			if (UNIV_LIKELY_NULL(buf)) {
1866 				if (UNIV_UNLIKELY(buf == field_ref_zero)) {
1867 					/* The externally stored field
1868 					was not written yet. This
1869 					record should only be seen by
1870 					recv_recovery_rollback_active(),
1871 					when the server had crashed before
1872 					storing the field. */
1873 					ut_ad(thr->graph->trx->is_recovered);
1874 					ut_ad(thr->graph->trx
1875 					      == trx_roll_crash_recv_trx);
1876 					return(TRUE);
1877 				}
1878 
1879 				goto copy_dfield;
1880 			}
1881 		} else if (dfield_is_ext(dfield)) {
1882 			dfield_len = dfield_get_len(dfield);
1883 			ut_a(dfield_len > BTR_EXTERN_FIELD_REF_SIZE);
1884 			dfield_len -= BTR_EXTERN_FIELD_REF_SIZE;
1885 			ut_a(dict_index_is_clust(index)
1886 			     || ind_field->prefix_len <= dfield_len);
1887 
1888 			buf= static_cast<const byte*>(dfield_get_data(dfield));
1889 copy_dfield:
1890 			ut_a(dfield_len > 0);
1891 			dfield_copy(&dfield_ext, dfield);
1892 			dfield_set_data(&dfield_ext, buf, dfield_len);
1893 			dfield = &dfield_ext;
1894 		}
1895 
1896 		if (!dfield_datas_are_binary_equal(
1897 			    dfield, &upd_field->new_val,
1898 			    ind_field->prefix_len)) {
1899 
1900 			return(TRUE);
1901 		}
1902 	}
1903 
1904 	return(FALSE);
1905 }
1906 
1907 /***********************************************************//**
1908 Checks if an update vector changes an ordering field of an index record.
1909 NOTE: we compare the fields as binary strings!
1910 @return TRUE if update vector may change an ordering field in an index
1911 record */
1912 ibool
row_upd_changes_some_index_ord_field_binary(const dict_table_t * table,const upd_t * update)1913 row_upd_changes_some_index_ord_field_binary(
1914 /*========================================*/
1915 	const dict_table_t*	table,	/*!< in: table */
1916 	const upd_t*		update)	/*!< in: update vector for the row */
1917 {
1918 	upd_field_t*	upd_field;
1919 	dict_index_t*	index;
1920 	ulint		i;
1921 
1922 	index = dict_table_get_first_index(table);
1923 
1924 	for (i = 0; i < upd_get_n_fields(update); i++) {
1925 
1926 		upd_field = upd_get_nth_field(update, i);
1927 
1928 		if (upd_fld_is_virtual_col(upd_field)) {
1929 			if (dict_table_get_nth_v_col(index->table,
1930 						     upd_field->field_no)
1931 			    ->m_col.ord_part) {
1932 				return(TRUE);
1933 			}
1934 		} else {
1935 			if (dict_field_get_col(dict_index_get_nth_field(
1936 				index, upd_field->field_no))->ord_part) {
1937 				return(TRUE);
1938 			}
1939 		}
1940 	}
1941 
1942 	return(FALSE);
1943 }
1944 
1945 /***********************************************************//**
1946 Checks if an FTS Doc ID column is affected by an UPDATE.
1947 @return whether the Doc ID column is changed */
1948 bool
row_upd_changes_doc_id(dict_table_t * table,upd_field_t * upd_field)1949 row_upd_changes_doc_id(
1950 /*===================*/
1951 	dict_table_t*	table,		/*!< in: table */
1952 	upd_field_t*	upd_field)	/*!< in: field to check */
1953 {
1954 	ulint		col_no;
1955 	dict_index_t*	clust_index;
1956 	fts_t*		fts = table->fts;
1957 
1958 	ut_ad(!table->skip_alter_undo);
1959 
1960 	clust_index = dict_table_get_first_index(table);
1961 
1962 	/* Convert from index-specific column number to table-global
1963 	column number. */
1964 	col_no = dict_index_get_nth_col_no(clust_index, upd_field->field_no);
1965 
1966 	return(col_no == fts->doc_col);
1967 }
1968 /***********************************************************//**
1969 Checks if an FTS indexed column is affected by an UPDATE.
1970 @return offset within fts_t::indexes if FTS indexed column updated else
1971 ULINT_UNDEFINED */
1972 ulint
row_upd_changes_fts_column(dict_table_t * table,upd_field_t * upd_field)1973 row_upd_changes_fts_column(
1974 /*=======================*/
1975 	dict_table_t*	table,		/*!< in: table */
1976 	upd_field_t*	upd_field)	/*!< in: field to check */
1977 {
1978 	ulint		col_no;
1979 	dict_index_t*	clust_index;
1980 	fts_t*		fts = table->fts;
1981 
1982 	ut_ad(!table->skip_alter_undo);
1983 
1984 	if (upd_fld_is_virtual_col(upd_field)) {
1985 		col_no = upd_field->field_no;
1986 		return(dict_table_is_fts_column(fts->indexes, col_no, true));
1987 	} else {
1988 		clust_index = dict_table_get_first_index(table);
1989 
1990 		/* Convert from index-specific column number to table-global
1991 		column number. */
1992 		col_no = dict_index_get_nth_col_no(clust_index,
1993 						   upd_field->field_no);
1994 		return(dict_table_is_fts_column(fts->indexes, col_no, false));
1995 	}
1996 
1997 }
1998 
1999 /***********************************************************//**
2000 Checks if an update vector changes some of the first ordering fields of an
2001 index record. This is only used in foreign key checks and we can assume
2002 that index does not contain column prefixes.
2003 @return TRUE if changes */
2004 static
2005 ibool
row_upd_changes_first_fields_binary(dtuple_t * entry,dict_index_t * index,const upd_t * update,ulint n)2006 row_upd_changes_first_fields_binary(
2007 /*================================*/
2008 	dtuple_t*	entry,	/*!< in: index entry */
2009 	dict_index_t*	index,	/*!< in: index of entry */
2010 	const upd_t*	update,	/*!< in: update vector for the row */
2011 	ulint		n)	/*!< in: how many first fields to check */
2012 {
2013 	ulint		n_upd_fields;
2014 	ulint		i, j;
2015 	dict_index_t*	clust_index;
2016 
2017 	ut_ad(update && index);
2018 	ut_ad(n <= dict_index_get_n_fields(index));
2019 
2020 	n_upd_fields = upd_get_n_fields(update);
2021 	clust_index = dict_table_get_first_index(index->table);
2022 
2023 	for (i = 0; i < n; i++) {
2024 
2025 		const dict_field_t*	ind_field;
2026 		const dict_col_t*	col;
2027 		ulint			col_pos;
2028 
2029 		ind_field = dict_index_get_nth_field(index, i);
2030 		col = dict_field_get_col(ind_field);
2031 		col_pos = dict_col_get_clust_pos(col, clust_index);
2032 
2033 		ut_a(ind_field->prefix_len == 0);
2034 
2035 		for (j = 0; j < n_upd_fields; j++) {
2036 
2037 			upd_field_t*	upd_field
2038 				= upd_get_nth_field(update, j);
2039 
2040 			if (col_pos == upd_field->field_no
2041 			    && !dfield_datas_are_binary_equal(
2042 				    dtuple_get_nth_field(entry, i),
2043 				    &upd_field->new_val, 0)) {
2044 
2045 				return(TRUE);
2046 			}
2047 		}
2048 	}
2049 
2050 	return(FALSE);
2051 }
2052 
2053 /*********************************************************************//**
2054 Copies the column values from a record. */
2055 UNIV_INLINE
2056 void
row_upd_copy_columns(rec_t * rec,const rec_offs * offsets,const dict_index_t * index,sym_node_t * column)2057 row_upd_copy_columns(
2058 /*=================*/
2059 	rec_t*		rec,	/*!< in: record in a clustered index */
2060 	const rec_offs*	offsets,/*!< in: array returned by rec_get_offsets() */
2061 	const dict_index_t*	index, /*!< in: index of rec */
2062 	sym_node_t*	column)	/*!< in: first column in a column list, or
2063 				NULL */
2064 {
2065 	ut_ad(dict_index_is_clust(index));
2066 
2067 	const byte*	data;
2068 	ulint	len;
2069 
2070 	while (column) {
2071 		data = rec_get_nth_cfield(
2072 			rec, index, offsets,
2073 			column->field_nos[SYM_CLUST_FIELD_NO], &len);
2074 		eval_node_copy_and_alloc_val(column, data, len);
2075 
2076 		column = UT_LIST_GET_NEXT(col_var_list, column);
2077 	}
2078 }
2079 
2080 /*********************************************************************//**
2081 Calculates the new values for fields to update. Note that row_upd_copy_columns
2082 must have been called first. */
2083 UNIV_INLINE
2084 void
row_upd_eval_new_vals(upd_t * update)2085 row_upd_eval_new_vals(
2086 /*==================*/
2087 	upd_t*	update)	/*!< in/out: update vector */
2088 {
2089 	que_node_t*	exp;
2090 	upd_field_t*	upd_field;
2091 	ulint		n_fields;
2092 	ulint		i;
2093 
2094 	n_fields = upd_get_n_fields(update);
2095 
2096 	for (i = 0; i < n_fields; i++) {
2097 		upd_field = upd_get_nth_field(update, i);
2098 
2099 		exp = upd_field->exp;
2100 
2101 		eval_exp(exp);
2102 
2103 		dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
2104 	}
2105 }
2106 
2107 /** Stores to the heap the virtual columns that need for any indexes
2108 @param[in,out]	node		row update node
2109 @param[in]	update		an update vector if it is update
2110 @param[in]	thd		mysql thread handle
2111 @param[in,out]	mysql_table	mysql table object
2112 @return true if success
2113 	false if virtual column value computation fails. */
2114 static
2115 bool
row_upd_store_v_row(upd_node_t * node,const upd_t * update,THD * thd,TABLE * mysql_table)2116 row_upd_store_v_row(
2117 	upd_node_t*	node,
2118 	const upd_t*	update,
2119 	THD*		thd,
2120 	TABLE*		mysql_table)
2121 {
2122 	dict_index_t*	index = dict_table_get_first_index(node->table);
2123 	ib_vcol_row	vc(NULL);
2124 
2125 	for (ulint col_no = 0; col_no < dict_table_get_n_v_cols(node->table);
2126 	     col_no++) {
2127 
2128 		const dict_v_col_t*     col
2129 			= dict_table_get_nth_v_col(node->table, col_no);
2130 
2131 		if (col->m_col.ord_part) {
2132 			dfield_t*	dfield
2133 				= dtuple_get_nth_v_field(node->row, col_no);
2134 			ulint		n_upd
2135 				= update ? upd_get_n_fields(update) : 0;
2136 			ulint		i = 0;
2137 
2138 			/* Check if the value is already in update vector */
2139 			for (i = 0; i < n_upd; i++) {
2140 				const upd_field_t*      upd_field
2141 					= upd_get_nth_field(update, i);
2142 				if (!(upd_field->new_val.type.prtype
2143 				      & DATA_VIRTUAL)
2144 				    || upd_field->field_no != col->v_pos) {
2145 					continue;
2146 				}
2147 
2148 				dfield_copy_data(dfield, upd_field->old_v_val);
2149 				dfield_dup(dfield, node->heap);
2150 				break;
2151 			}
2152 
2153 			/* Not updated */
2154 			if (i >= n_upd) {
2155 				/* If this is an update, then the value
2156 				should be in update->old_vrow */
2157 				if (update) {
2158 					if (update->old_vrow == NULL) {
2159 						/* This only happens in
2160 						cascade update. And virtual
2161 						column can't be affected,
2162 						so it is Ok to set it to NULL */
2163 						dfield_set_null(dfield);
2164 					} else {
2165 						dfield_t*       vfield
2166 							= dtuple_get_nth_v_field(
2167 								update->old_vrow,
2168 								col_no);
2169 						dfield_copy_data(dfield, vfield);
2170 						dfield_dup(dfield, node->heap);
2171 					}
2172 				} else {
2173 					uchar *record = vc.record(thd, index,
2174 								  &mysql_table);
2175 					/* Need to compute, this happens when
2176 					deleting row */
2177 					dfield_t* vfield =
2178 						innobase_get_computed_value(
2179 							node->row, col, index,
2180 							&vc.heap, node->heap,
2181 							NULL, thd, mysql_table,
2182 							record, NULL, NULL);
2183 					if (vfield == NULL) {
2184 						return false;
2185 					}
2186 				}
2187 			}
2188 		}
2189 	}
2190 
2191 	return true;
2192 }
2193 
2194 /** Stores to the heap the row on which the node->pcur is positioned.
2195 @param[in]	node		row update node
2196 @param[in]	thd		mysql thread handle
2197 @param[in,out]	mysql_table	NULL, or mysql table object when
2198 				user thread invokes dml
2199 @return false if virtual column value computation fails
2200 	true otherwise. */
2201 static
2202 bool
row_upd_store_row(upd_node_t * node,THD * thd,TABLE * mysql_table)2203 row_upd_store_row(
2204 	upd_node_t*	node,
2205 	THD*		thd,
2206 	TABLE*		mysql_table)
2207 {
2208 	dict_index_t*	clust_index;
2209 	rec_t*		rec;
2210 	mem_heap_t*	heap		= NULL;
2211 	row_ext_t**	ext;
2212 	rec_offs	offsets_[REC_OFFS_NORMAL_SIZE];
2213 	const rec_offs*	offsets;
2214 	rec_offs_init(offsets_);
2215 
2216 	ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
2217 
2218 	if (node->row != NULL) {
2219 		mem_heap_empty(node->heap);
2220 	}
2221 
2222 	clust_index = dict_table_get_first_index(node->table);
2223 
2224 	rec = btr_pcur_get_rec(node->pcur);
2225 
2226 	offsets = rec_get_offsets(rec, clust_index, offsets_,
2227 				  clust_index->n_core_fields,
2228 				  ULINT_UNDEFINED, &heap);
2229 
2230 	if (dict_table_has_atomic_blobs(node->table)) {
2231 		/* There is no prefix of externally stored columns in
2232 		the clustered index record. Build a cache of column
2233 		prefixes. */
2234 		ext = &node->ext;
2235 	} else {
2236 		/* REDUNDANT and COMPACT formats store a local
2237 		768-byte prefix of each externally stored column.
2238 		No cache is needed. */
2239 		ext = NULL;
2240 		node->ext = NULL;
2241 	}
2242 
2243 	node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
2244 			      NULL, NULL, NULL, ext, node->heap);
2245 
2246 	if (node->table->n_v_cols) {
2247 		bool ok = row_upd_store_v_row(node,
2248 				    node->is_delete ? NULL : node->update,
2249 				    thd, mysql_table);
2250 		if (!ok) {
2251 			return false;
2252 		}
2253 	}
2254 
2255 	if (node->is_delete == PLAIN_DELETE) {
2256 		node->upd_row = NULL;
2257 		node->upd_ext = NULL;
2258 	} else {
2259 		node->upd_row = dtuple_copy(node->row, node->heap);
2260 		row_upd_replace(node->upd_row, &node->upd_ext,
2261 				clust_index, node->update, node->heap);
2262 	}
2263 
2264 	if (UNIV_LIKELY_NULL(heap)) {
2265 		mem_heap_free(heap);
2266 	}
2267 	return true;
2268 }
2269 
2270 /***********************************************************//**
2271 Updates a secondary index entry of a row.
2272 @return DB_SUCCESS if operation successfully completed, else error
2273 code or DB_LOCK_WAIT */
2274 static MY_ATTRIBUTE((nonnull, warn_unused_result))
2275 dberr_t
row_upd_sec_index_entry(upd_node_t * node,que_thr_t * thr)2276 row_upd_sec_index_entry(
2277 /*====================*/
2278 	upd_node_t*	node,	/*!< in: row update node */
2279 	que_thr_t*	thr)	/*!< in: query thread */
2280 {
2281 	mtr_t			mtr;
2282 	const rec_t*		rec;
2283 	btr_pcur_t		pcur;
2284 	mem_heap_t*		heap;
2285 	dtuple_t*		entry;
2286 	dict_index_t*		index;
2287 	btr_cur_t*		btr_cur;
2288 	dberr_t			err	= DB_SUCCESS;
2289 	trx_t*			trx	= thr_get_trx(thr);
2290 	ulint			mode;
2291 	ulint			flags;
2292 	enum row_search_result	search_result;
2293 
2294 	ut_ad(trx->id != 0);
2295 
2296 	index = node->index;
2297 
2298 	const bool referenced = row_upd_index_is_referenced(index, trx);
2299 #ifdef WITH_WSREP
2300 	bool foreign = wsrep_row_upd_index_is_foreign(index, trx);
2301 #endif /* WITH_WSREP */
2302 
2303 	heap = mem_heap_create(1024);
2304 
2305 	/* Build old index entry */
2306 	entry = row_build_index_entry(node->row, node->ext, index, heap);
2307 	ut_a(entry);
2308 
2309 	log_free_check();
2310 
2311 	DEBUG_SYNC_C_IF_THD(trx->mysql_thd,
2312 			    "before_row_upd_sec_index_entry");
2313 
2314 	mtr.start();
2315 
2316 	switch (index->table->space_id) {
2317 	case SRV_TMP_SPACE_ID:
2318 		mtr.set_log_mode(MTR_LOG_NO_REDO);
2319 		flags = BTR_NO_LOCKING_FLAG;
2320 		break;
2321 	default:
2322 		index->set_modified(mtr);
2323 		/* fall through */
2324 	case IBUF_SPACE_ID:
2325 		flags = index->table->no_rollback() ? BTR_NO_ROLLBACK : 0;
2326 		break;
2327 	}
2328 
2329 	bool uncommitted = !index->is_committed();
2330 
2331 	if (uncommitted) {
2332 		/* The index->online_status may change if the index is
2333 		or was being created online, but not committed yet. It
2334 		is protected by index->lock. */
2335 
2336 		mtr_s_lock_index(index, &mtr);
2337 
2338 		switch (dict_index_get_online_status(index)) {
2339 		case ONLINE_INDEX_COMPLETE:
2340 			/* This is a normal index. Do not log anything.
2341 			Perform the update on the index tree directly. */
2342 			break;
2343 		case ONLINE_INDEX_CREATION:
2344 			/* Log a DELETE and optionally INSERT. */
2345 			row_log_online_op(index, entry, 0);
2346 
2347 			if (!node->is_delete) {
2348 				mem_heap_empty(heap);
2349 				entry = row_build_index_entry(
2350 					node->upd_row, node->upd_ext,
2351 					index, heap);
2352 				ut_a(entry);
2353 				row_log_online_op(index, entry, trx->id);
2354 			}
2355 			/* fall through */
2356 		case ONLINE_INDEX_ABORTED:
2357 		case ONLINE_INDEX_ABORTED_DROPPED:
2358 			mtr_commit(&mtr);
2359 			goto func_exit;
2360 		}
2361 
2362 		/* We can only buffer delete-mark operations if there
2363 		are no foreign key constraints referring to the index.
2364 		Change buffering is disabled for temporary tables and
2365 		spatial index. */
2366 		mode = (referenced || index->table->is_temporary()
2367 			|| dict_index_is_spatial(index))
2368 			? BTR_MODIFY_LEAF_ALREADY_S_LATCHED
2369 			: BTR_DELETE_MARK_LEAF_ALREADY_S_LATCHED;
2370 	} else {
2371 		/* For secondary indexes,
2372 		index->online_status==ONLINE_INDEX_COMPLETE if
2373 		index->is_committed(). */
2374 		ut_ad(!dict_index_is_online_ddl(index));
2375 
2376 		/* We can only buffer delete-mark operations if there
2377 		are no foreign key constraints referring to the index.
2378 		Change buffering is disabled for temporary tables and
2379 		spatial index. */
2380 		mode = (referenced || index->table->is_temporary()
2381 			|| dict_index_is_spatial(index))
2382 			? BTR_MODIFY_LEAF
2383 			: BTR_DELETE_MARK_LEAF;
2384 	}
2385 
2386 	if (dict_index_is_spatial(index)) {
2387 		ut_ad(mode & BTR_MODIFY_LEAF);
2388 		mode |= BTR_RTREE_DELETE_MARK;
2389 	}
2390 
2391 	/* Set the query thread, so that ibuf_insert_low() will be
2392 	able to invoke thd_get_trx(). */
2393 	btr_pcur_get_btr_cur(&pcur)->thr = thr;
2394 
2395 	search_result = row_search_index_entry(index, entry, mode,
2396 					       &pcur, &mtr);
2397 
2398 	btr_cur = btr_pcur_get_btr_cur(&pcur);
2399 
2400 	rec = btr_cur_get_rec(btr_cur);
2401 
2402 	switch (search_result) {
2403 	case ROW_NOT_DELETED_REF:	/* should only occur for BTR_DELETE */
2404 		ut_error;
2405 		break;
2406 	case ROW_BUFFERED:
2407 		/* Entry was delete marked already. */
2408 		break;
2409 
2410 	case ROW_NOT_FOUND:
2411 		if (!index->is_committed()) {
2412 			/* When online CREATE INDEX copied the update
2413 			that we already made to the clustered index,
2414 			and completed the secondary index creation
2415 			before we got here, the old secondary index
2416 			record would not exist. The CREATE INDEX
2417 			should be waiting for a MySQL meta-data lock
2418 			upgrade at least until this UPDATE returns.
2419 			After that point, set_committed(true) would be
2420 			invoked by commit_inplace_alter_table(). */
2421 			break;
2422 		}
2423 
2424 		if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) {
2425 			/* We found the record, but a delete marked */
2426 			break;
2427 		}
2428 
2429 		ib::error()
2430 			<< "Record in index " << index->name
2431 			<< " of table " << index->table->name
2432 			<< " was not found on update: " << *entry
2433 			<< " at: " << rec_index_print(rec, index);
2434 #ifdef UNIV_DEBUG
2435 		mtr_commit(&mtr);
2436 		mtr_start(&mtr);
2437 		ut_ad(btr_validate_index(index, 0, false) == DB_SUCCESS);
2438 		ut_ad(0);
2439 #endif /* UNIV_DEBUG */
2440 		break;
2441 	case ROW_FOUND:
2442 		ut_ad(err == DB_SUCCESS);
2443 
2444 		/* Delete mark the old index record; it can already be
2445 		delete marked if we return after a lock wait in
2446 		row_ins_sec_index_entry() below */
2447 		if (!rec_get_deleted_flag(
2448 			    rec, dict_table_is_comp(index->table))) {
2449 			err = btr_cur_del_mark_set_sec_rec(
2450 				flags, btr_cur, TRUE, thr, &mtr);
2451 			if (err != DB_SUCCESS) {
2452 				break;
2453 			}
2454 #ifdef WITH_WSREP
2455 			if (!referenced && foreign
2456 			    && wsrep_must_process_fk(node, trx)
2457 			    && !wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
2458 
2459 				rec_offs* offsets = rec_get_offsets(
2460 					rec, index, NULL, index->n_core_fields,
2461 					ULINT_UNDEFINED, &heap);
2462 
2463 				err = wsrep_row_upd_check_foreign_constraints(
2464 					node, &pcur, index->table,
2465 					index, offsets, thr, &mtr);
2466 
2467 				switch (err) {
2468 				case DB_SUCCESS:
2469 				case DB_NO_REFERENCED_ROW:
2470 					err = DB_SUCCESS;
2471 					break;
2472 				case DB_LOCK_WAIT:
2473 				case DB_DEADLOCK:
2474 				case DB_LOCK_WAIT_TIMEOUT:
2475 					WSREP_DEBUG("Foreign key check fail: "
2476 						"%s on table %s index %s query %s",
2477 						ut_strerr(err), index->name(), index->table->name.m_name,
2478 						wsrep_thd_query(trx->mysql_thd));
2479 					break;
2480 				default:
2481 					WSREP_ERROR("Foreign key check fail: "
2482 						"%s on table %s index %s query %s",
2483 						ut_strerr(err), index->name(), index->table->name.m_name,
2484 						wsrep_thd_query(trx->mysql_thd));
2485 					break;
2486 				}
2487 			}
2488 #endif /* WITH_WSREP */
2489 		}
2490 
2491 #ifdef WITH_WSREP
2492 		ut_ad(err == DB_SUCCESS || err == DB_LOCK_WAIT
2493 		      || err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT);
2494 #else
2495 		ut_ad(err == DB_SUCCESS);
2496 #endif
2497 
2498 		if (referenced) {
2499 			rec_offs* offsets = rec_get_offsets(
2500 				rec, index, NULL, index->n_core_fields,
2501 				ULINT_UNDEFINED, &heap);
2502 
2503 			/* NOTE that the following call loses
2504 			the position of pcur ! */
2505 			err = row_upd_check_references_constraints(
2506 				node, &pcur, index->table,
2507 				index, offsets, thr, &mtr);
2508 		}
2509 	}
2510 
2511 	btr_pcur_close(&pcur);
2512 	mtr_commit(&mtr);
2513 
2514 	if (node->is_delete == PLAIN_DELETE || err != DB_SUCCESS) {
2515 
2516 		goto func_exit;
2517 	}
2518 
2519 	mem_heap_empty(heap);
2520 
2521 	DEBUG_SYNC_C_IF_THD(trx->mysql_thd,
2522 			    "before_row_upd_sec_new_index_entry");
2523 
2524 	uncommitted = !index->is_committed();
2525 	if (uncommitted) {
2526 		mtr.start();
2527 		/* The index->online_status may change if the index is
2528 		being rollbacked. It is protected by index->lock. */
2529 
2530 		mtr_s_lock_index(index, &mtr);
2531 
2532 		switch (dict_index_get_online_status(index)) {
2533 		case ONLINE_INDEX_COMPLETE:
2534 		case ONLINE_INDEX_CREATION:
2535 		       break;
2536 		case ONLINE_INDEX_ABORTED:
2537 		case ONLINE_INDEX_ABORTED_DROPPED:
2538 		       mtr_commit(&mtr);
2539 		       goto func_exit;
2540 		}
2541 
2542 	}
2543 
2544 	/* Build a new index entry */
2545 	entry = row_build_index_entry(node->upd_row, node->upd_ext,
2546 				      index, heap);
2547 	ut_a(entry);
2548 
2549 	if (uncommitted) {
2550 		mtr_commit(&mtr);
2551 	}
2552 
2553 	/* Insert new index entry */
2554 	err = row_ins_sec_index_entry(index, entry, thr, !node->is_delete);
2555 
2556 func_exit:
2557 	mem_heap_free(heap);
2558 
2559 	return(err);
2560 }
2561 
2562 /***********************************************************//**
2563 Updates the secondary index record if it is changed in the row update or
2564 deletes it if this is a delete.
2565 @return DB_SUCCESS if operation successfully completed, else error
2566 code or DB_LOCK_WAIT */
2567 static MY_ATTRIBUTE((nonnull, warn_unused_result))
2568 dberr_t
row_upd_sec_step(upd_node_t * node,que_thr_t * thr)2569 row_upd_sec_step(
2570 /*=============*/
2571 	upd_node_t*	node,	/*!< in: row update node */
2572 	que_thr_t*	thr)	/*!< in: query thread */
2573 {
2574 	ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
2575 	      || (node->state == UPD_NODE_UPDATE_SOME_SEC));
2576 	ut_ad(!dict_index_is_clust(node->index));
2577 
2578 	if (node->state == UPD_NODE_UPDATE_ALL_SEC
2579 	    || row_upd_changes_ord_field_binary(node->index, node->update,
2580 						thr, node->row, node->ext)) {
2581 		return(row_upd_sec_index_entry(node, thr));
2582 	}
2583 
2584 	return(DB_SUCCESS);
2585 }
2586 
2587 #ifdef UNIV_DEBUG
2588 # define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update)	\
2589 	row_upd_clust_rec_by_insert_inherit_func(rec,offsets,entry,update)
2590 #else /* UNIV_DEBUG */
2591 # define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update)	\
2592 	row_upd_clust_rec_by_insert_inherit_func(rec,entry,update)
2593 #endif /* UNIV_DEBUG */
2594 /*******************************************************************//**
2595 Mark non-updated off-page columns inherited when the primary key is
2596 updated. We must mark them as inherited in entry, so that they are not
2597 freed in a rollback. A limited version of this function used to be
2598 called btr_cur_mark_dtuple_inherited_extern().
2599 @return whether any columns were inherited */
2600 static
2601 bool
row_upd_clust_rec_by_insert_inherit_func(const rec_t * rec,const rec_offs * offsets,dtuple_t * entry,const upd_t * update)2602 row_upd_clust_rec_by_insert_inherit_func(
2603 /*=====================================*/
2604 	const rec_t*	rec,	/*!< in: old record, or NULL */
2605 #ifdef UNIV_DEBUG
2606 	const rec_offs*	offsets,/*!< in: rec_get_offsets(rec), or NULL */
2607 #endif /* UNIV_DEBUG */
2608 	dtuple_t*	entry,	/*!< in/out: updated entry to be
2609 				inserted into the clustered index */
2610 	const upd_t*	update)	/*!< in: update vector */
2611 {
2612 	bool	inherit	= false;
2613 	ulint	i;
2614 
2615 	ut_ad(!rec == !offsets);
2616 	ut_ad(!rec || rec_offs_any_extern(offsets));
2617 
2618 	for (i = 0; i < dtuple_get_n_fields(entry); i++) {
2619 		dfield_t*	dfield	= dtuple_get_nth_field(entry, i);
2620 		byte*		data;
2621 		ulint		len;
2622 
2623 		ut_ad(!offsets
2624 		      || !rec_offs_nth_extern(offsets, i)
2625 		      == !dfield_is_ext(dfield)
2626 		      || upd_get_field_by_field_no(update, i, false));
2627 		if (!dfield_is_ext(dfield)
2628 		    || upd_get_field_by_field_no(update, i, false)) {
2629 			continue;
2630 		}
2631 
2632 #ifdef UNIV_DEBUG
2633 		if (UNIV_LIKELY(rec != NULL)) {
2634 			ut_ad(!rec_offs_nth_default(offsets, i));
2635 			const byte* rec_data
2636 				= rec_get_nth_field(rec, offsets, i, &len);
2637 			ut_ad(len == dfield_get_len(dfield));
2638 			ut_ad(len != UNIV_SQL_NULL);
2639 			ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE);
2640 
2641 			rec_data += len - BTR_EXTERN_FIELD_REF_SIZE;
2642 
2643 			/* The pointer must not be zero. */
2644 			ut_ad(memcmp(rec_data, field_ref_zero,
2645 				     BTR_EXTERN_FIELD_REF_SIZE));
2646 			/* The BLOB must be owned. */
2647 			ut_ad(!(rec_data[BTR_EXTERN_LEN]
2648 				& BTR_EXTERN_OWNER_FLAG));
2649 		}
2650 #endif /* UNIV_DEBUG */
2651 
2652 		len = dfield_get_len(dfield);
2653 		ut_a(len != UNIV_SQL_NULL);
2654 		ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
2655 
2656 		data = static_cast<byte*>(dfield_get_data(dfield));
2657 
2658 		data += len - BTR_EXTERN_FIELD_REF_SIZE;
2659 		/* The pointer must not be zero. */
2660 		ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE));
2661 
2662 		/* The BLOB must be owned, unless we are resuming from
2663 		a lock wait and we already had disowned the BLOB. */
2664 		ut_a(rec == NULL
2665 		     || !(data[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG));
2666 		data[BTR_EXTERN_LEN] &= ~BTR_EXTERN_OWNER_FLAG;
2667 		data[BTR_EXTERN_LEN] |= BTR_EXTERN_INHERITED_FLAG;
2668 		/* The BTR_EXTERN_INHERITED_FLAG only matters in
2669 		rollback of a fresh insert. Purge will always free
2670 		the extern fields of a delete-marked row. */
2671 
2672 		inherit = true;
2673 	}
2674 
2675 	return(inherit);
2676 }
2677 
2678 /***********************************************************//**
2679 Marks the clustered index record deleted and inserts the updated version
2680 of the record to the index. This function should be used when the ordering
2681 fields of the clustered index record change. This should be quite rare in
2682 database applications.
2683 @return DB_SUCCESS if operation successfully completed, else error
2684 code or DB_LOCK_WAIT */
2685 static MY_ATTRIBUTE((nonnull, warn_unused_result))
2686 dberr_t
row_upd_clust_rec_by_insert(upd_node_t * node,dict_index_t * index,que_thr_t * thr,bool referenced,bool foreign,mtr_t * mtr)2687 row_upd_clust_rec_by_insert(
2688 /*========================*/
2689 	upd_node_t*	node,	/*!< in/out: row update node */
2690 	dict_index_t*	index,	/*!< in: clustered index of the record */
2691 	que_thr_t*	thr,	/*!< in: query thread */
2692 	bool		referenced,/*!< in: whether index may be referenced in
2693 				a foreign key constraint */
2694 #ifdef WITH_WSREP
2695 	bool		foreign,/*!< in: whether this is a foreign key */
2696 #endif
2697 	mtr_t*		mtr)	/*!< in/out: mini-transaction,
2698 				may be committed and restarted */
2699 {
2700 	mem_heap_t*	heap;
2701 	btr_pcur_t*	pcur;
2702 	btr_cur_t*	btr_cur;
2703 	trx_t*		trx;
2704 	dict_table_t*	table;
2705 	dtuple_t*	entry;
2706 	dberr_t		err;
2707 	rec_t*		rec;
2708 	rec_offs	offsets_[REC_OFFS_NORMAL_SIZE];
2709 	rec_offs*	offsets			= offsets_;
2710 
2711 	ut_ad(dict_index_is_clust(index));
2712 
2713 	rec_offs_init(offsets_);
2714 
2715 	trx = thr_get_trx(thr);
2716 	table = node->table;
2717 	pcur = node->pcur;
2718 	btr_cur	= btr_pcur_get_btr_cur(pcur);
2719 
2720 	heap = mem_heap_create(1000);
2721 
2722 	entry = row_build_index_entry_low(node->upd_row, node->upd_ext,
2723 					  index, heap, ROW_BUILD_FOR_INSERT);
2724 	if (index->is_instant()) entry->trim(*index);
2725 	ut_ad(dtuple_get_info_bits(entry) == 0);
2726 
2727 	row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
2728 
2729 	switch (node->state) {
2730 	default:
2731 		ut_error;
2732 	case UPD_NODE_INSERT_CLUSTERED:
2733 		/* A lock wait occurred in row_ins_clust_index_entry() in
2734 		the previous invocation of this function. */
2735 		row_upd_clust_rec_by_insert_inherit(
2736 			NULL, NULL, entry, node->update);
2737 		break;
2738 	case UPD_NODE_UPDATE_CLUSTERED:
2739 		/* This is the first invocation of the function where
2740 		we update the primary key.  Delete-mark the old record
2741 		in the clustered index and prepare to insert a new entry. */
2742 		rec = btr_cur_get_rec(btr_cur);
2743 		offsets = rec_get_offsets(rec, index, offsets,
2744 					  index->n_core_fields,
2745 					  ULINT_UNDEFINED, &heap);
2746 		ut_ad(page_rec_is_user_rec(rec));
2747 
2748 		if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))) {
2749 			/* If the clustered index record is already delete
2750 			marked, then we are here after a DB_LOCK_WAIT.
2751 			Skip delete marking clustered index and disowning
2752 			its blobs. */
2753 			ut_ad(row_get_rec_trx_id(rec, index, offsets)
2754 			      == trx->id);
2755 			ut_ad(!trx_undo_roll_ptr_is_insert(
2756 			              row_get_rec_roll_ptr(rec, index,
2757 							   offsets)));
2758 			goto check_fk;
2759 		}
2760 
2761 		err = btr_cur_del_mark_set_clust_rec(
2762 			btr_cur_get_block(btr_cur), rec, index, offsets,
2763 			thr, node->row, mtr);
2764 		if (err != DB_SUCCESS) {
2765 			goto err_exit;
2766 		}
2767 
2768 		/* If the the new row inherits externally stored
2769 		fields (off-page columns a.k.a. BLOBs) from the
2770 		delete-marked old record, mark them disowned by the
2771 		old record and owned by the new entry. */
2772 
2773 		if (rec_offs_any_extern(offsets)) {
2774 			if (row_upd_clust_rec_by_insert_inherit(
2775 				    rec, offsets, entry, node->update)) {
2776 				/* The blobs are disowned here, expecting the
2777 				insert down below to inherit them.  But if the
2778 				insert fails, then this disown will be undone
2779 				when the operation is rolled back. */
2780 				btr_cur_disown_inherited_fields(
2781 					btr_cur_get_page_zip(btr_cur),
2782 					rec, index, offsets, node->update,
2783 					mtr);
2784 			}
2785 		}
2786 check_fk:
2787 		if (referenced) {
2788 			/* NOTE that the following call loses
2789 			the position of pcur ! */
2790 
2791 			err = row_upd_check_references_constraints(
2792 				node, pcur, table, index, offsets, thr, mtr);
2793 
2794 			if (err != DB_SUCCESS) {
2795 				goto err_exit;
2796 			}
2797 #ifdef WITH_WSREP
2798 		} else if (foreign && wsrep_must_process_fk(node, trx)) {
2799 			err = wsrep_row_upd_check_foreign_constraints(
2800 				node, pcur, table, index, offsets, thr, mtr);
2801 
2802 			switch (err) {
2803 			case DB_SUCCESS:
2804 			case DB_NO_REFERENCED_ROW:
2805 				err = DB_SUCCESS;
2806 				break;
2807 			case DB_LOCK_WAIT:
2808 			case DB_DEADLOCK:
2809 			case DB_LOCK_WAIT_TIMEOUT:
2810 				WSREP_DEBUG("Foreign key check fail: "
2811 					    "%s on table %s index %s query %s",
2812 					    ut_strerr(err), index->name(), index->table->name.m_name,
2813 					    wsrep_thd_query(trx->mysql_thd));
2814 
2815 				goto err_exit;
2816 			default:
2817 				WSREP_ERROR("Foreign key check fail: "
2818 					    "%s on table %s index %s query %s",
2819 					    ut_strerr(err), index->name(), index->table->name.m_name,
2820 					    wsrep_thd_query(trx->mysql_thd));
2821 
2822 				goto err_exit;
2823 			}
2824 #endif /* WITH_WSREP */
2825 		}
2826 	}
2827 
2828 	mtr->commit();
2829 	mtr->start();
2830 
2831 	node->state = UPD_NODE_INSERT_CLUSTERED;
2832 	err = row_ins_clust_index_entry(index, entry, thr,
2833 					dtuple_get_n_ext(entry));
2834 err_exit:
2835 	mem_heap_free(heap);
2836 	return(err);
2837 }
2838 
2839 /***********************************************************//**
2840 Updates a clustered index record of a row when the ordering fields do
2841 not change.
2842 @return DB_SUCCESS if operation successfully completed, else error
2843 code or DB_LOCK_WAIT */
2844 static MY_ATTRIBUTE((nonnull, warn_unused_result))
2845 dberr_t
row_upd_clust_rec(ulint flags,upd_node_t * node,dict_index_t * index,rec_offs * offsets,mem_heap_t ** offsets_heap,que_thr_t * thr,mtr_t * mtr)2846 row_upd_clust_rec(
2847 /*==============*/
2848 	ulint		flags,  /*!< in: undo logging and locking flags */
2849 	upd_node_t*	node,	/*!< in: row update node */
2850 	dict_index_t*	index,	/*!< in: clustered index */
2851 	rec_offs*	offsets,/*!< in: rec_get_offsets() on node->pcur */
2852 	mem_heap_t**	offsets_heap,
2853 				/*!< in/out: memory heap, can be emptied */
2854 	que_thr_t*	thr,	/*!< in: query thread */
2855 	mtr_t*		mtr)	/*!< in,out: mini-transaction; may be
2856 				committed and restarted here */
2857 {
2858 	mem_heap_t*	heap		= NULL;
2859 	big_rec_t*	big_rec		= NULL;
2860 	btr_pcur_t*	pcur;
2861 	btr_cur_t*	btr_cur;
2862 	dberr_t		err;
2863 	const dtuple_t*	rebuilt_old_pk	= NULL;
2864 
2865 	ut_ad(dict_index_is_clust(index));
2866 	ut_ad(!thr_get_trx(thr)->in_rollback);
2867 	ut_ad(!node->table->skip_alter_undo);
2868 
2869 	pcur = node->pcur;
2870 	btr_cur = btr_pcur_get_btr_cur(pcur);
2871 
2872 	ut_ad(btr_cur_get_index(btr_cur) == index);
2873 	ut_ad(!rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
2874 				    dict_table_is_comp(index->table)));
2875 	ut_ad(rec_offs_validate(btr_cur_get_rec(btr_cur), index, offsets));
2876 
2877 	if (dict_index_is_online_ddl(index)) {
2878 		rebuilt_old_pk = row_log_table_get_pk(
2879 			btr_cur_get_rec(btr_cur), index, offsets, NULL, &heap);
2880 	}
2881 
2882 	/* Try optimistic updating of the record, keeping changes within
2883 	the page; we do not check locks because we assume the x-lock on the
2884 	record to update */
2885 
2886 	if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
2887 		err = btr_cur_update_in_place(
2888 			flags | BTR_NO_LOCKING_FLAG, btr_cur,
2889 			offsets, node->update,
2890 			node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
2891 	} else {
2892 		err = btr_cur_optimistic_update(
2893 			flags | BTR_NO_LOCKING_FLAG, btr_cur,
2894 			&offsets, offsets_heap, node->update,
2895 			node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
2896 	}
2897 
2898 	if (err == DB_SUCCESS) {
2899 		goto success;
2900 	}
2901 
2902 	if (buf_LRU_buf_pool_running_out()) {
2903 		err = DB_LOCK_TABLE_FULL;
2904 		goto func_exit;
2905 	}
2906 
2907 	/* We may have to modify the tree structure: do a pessimistic descent
2908 	down the index tree */
2909 
2910 	mtr->commit();
2911 	mtr->start();
2912 
2913 	if (index->table->is_temporary()) {
2914 		/* Disable locking, because temporary tables are never
2915 		shared between transactions or connections. */
2916 		flags |= BTR_NO_LOCKING_FLAG;
2917 		mtr->set_log_mode(MTR_LOG_NO_REDO);
2918 	} else {
2919 		index->set_modified(*mtr);
2920 	}
2921 
2922 	/* NOTE: this transaction has an s-lock or x-lock on the record and
2923 	therefore other transactions cannot modify the record when we have no
2924 	latch on the page. In addition, we assume that other query threads of
2925 	the same transaction do not modify the record in the meantime.
2926 	Therefore we can assert that the restoration of the cursor succeeds. */
2927 
2928 	ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
2929 
2930 	ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
2931 				    dict_table_is_comp(index->table)));
2932 
2933 	if (!heap) {
2934 		heap = mem_heap_create(1024);
2935 	}
2936 
2937 	err = btr_cur_pessimistic_update(
2938 		flags | BTR_NO_LOCKING_FLAG | BTR_KEEP_POS_FLAG, btr_cur,
2939 		&offsets, offsets_heap, heap, &big_rec,
2940 		node->update, node->cmpl_info,
2941 		thr, thr_get_trx(thr)->id, mtr);
2942 	if (big_rec) {
2943 		ut_a(err == DB_SUCCESS);
2944 
2945 		DEBUG_SYNC_C("before_row_upd_extern");
2946 		err = btr_store_big_rec_extern_fields(
2947 			pcur, offsets, big_rec, mtr, BTR_STORE_UPDATE);
2948 		DEBUG_SYNC_C("after_row_upd_extern");
2949 	}
2950 
2951 	if (err == DB_SUCCESS) {
2952 success:
2953 		if (dict_index_is_online_ddl(index)) {
2954 			row_log_table_update(
2955 				btr_cur_get_rec(btr_cur),
2956 				index, offsets, rebuilt_old_pk);
2957 		}
2958 	}
2959 
2960 func_exit:
2961 	if (heap) {
2962 		mem_heap_free(heap);
2963 	}
2964 
2965 	if (big_rec) {
2966 		dtuple_big_rec_free(big_rec);
2967 	}
2968 
2969 	return(err);
2970 }
2971 
2972 /***********************************************************//**
2973 Delete marks a clustered index record.
2974 @return DB_SUCCESS if operation successfully completed, else error code */
2975 static MY_ATTRIBUTE((nonnull, warn_unused_result))
2976 dberr_t
row_upd_del_mark_clust_rec(upd_node_t * node,dict_index_t * index,rec_offs * offsets,que_thr_t * thr,bool referenced,bool foreign,mtr_t * mtr)2977 row_upd_del_mark_clust_rec(
2978 /*=======================*/
2979 	upd_node_t*	node,	/*!< in: row update node */
2980 	dict_index_t*	index,	/*!< in: clustered index */
2981 	rec_offs*	offsets,/*!< in/out: rec_get_offsets() for the
2982 				record under the cursor */
2983 	que_thr_t*	thr,	/*!< in: query thread */
2984 	bool		referenced,
2985 				/*!< in: whether index may be referenced in
2986 				a foreign key constraint */
2987 #ifdef WITH_WSREP
2988 	bool		foreign,/*!< in: whether this is a foreign key */
2989 #endif
2990 	mtr_t*		mtr)	/*!< in,out: mini-transaction;
2991 				will be committed and restarted */
2992 {
2993 	btr_pcur_t*	pcur;
2994 	btr_cur_t*	btr_cur;
2995 	rec_t*		rec;
2996 	trx_t*		trx = thr_get_trx(thr);
2997 
2998 	ut_ad(dict_index_is_clust(index));
2999 	ut_ad(node->is_delete == PLAIN_DELETE);
3000 
3001 	pcur = node->pcur;
3002 	btr_cur = btr_pcur_get_btr_cur(pcur);
3003 
3004 	/* Store row because we have to build also the secondary index
3005 	entries */
3006 
3007 	if (!row_upd_store_row(node, trx->mysql_thd,
3008 			  thr->prebuilt  && thr->prebuilt->table == node->table
3009 			  ? thr->prebuilt->m_mysql_table : NULL)) {
3010 		return DB_COMPUTE_VALUE_FAILED;
3011 	}
3012 
3013 	/* Mark the clustered index record deleted; we do not have to check
3014 	locks, because we assume that we have an x-lock on the record */
3015 
3016 	rec = btr_cur_get_rec(btr_cur);
3017 
3018 	dberr_t err = btr_cur_del_mark_set_clust_rec(
3019 		btr_cur_get_block(btr_cur), rec,
3020 		index, offsets, thr, node->row, mtr);
3021 
3022 	if (err != DB_SUCCESS) {
3023 	} else if (referenced) {
3024 		/* NOTE that the following call loses the position of pcur ! */
3025 
3026 		err = row_upd_check_references_constraints(
3027 			node, pcur, index->table, index, offsets, thr, mtr);
3028 #ifdef WITH_WSREP
3029 	} else if (foreign && wsrep_must_process_fk(node, trx)) {
3030 		err = wsrep_row_upd_check_foreign_constraints(
3031 			node, pcur, index->table, index, offsets, thr, mtr);
3032 
3033 		switch (err) {
3034 		case DB_SUCCESS:
3035 		case DB_NO_REFERENCED_ROW:
3036 			err = DB_SUCCESS;
3037 			break;
3038 		case DB_LOCK_WAIT:
3039 		case DB_DEADLOCK:
3040 		case DB_LOCK_WAIT_TIMEOUT:
3041 			WSREP_DEBUG("Foreign key check fail: "
3042 				    "%d on table %s index %s query %s",
3043 				    err, index->name(), index->table->name.m_name,
3044 				    wsrep_thd_query(trx->mysql_thd));
3045 			break;
3046 		default:
3047 			WSREP_ERROR("Foreign key check fail: "
3048 				    "%d on table %s index %s query %s",
3049 				    err, index->name(), index->table->name.m_name,
3050 				    wsrep_thd_query(trx->mysql_thd));
3051 			break;
3052 		}
3053 #endif /* WITH_WSREP */
3054 	}
3055 
3056 	return(err);
3057 }
3058 
3059 /***********************************************************//**
3060 Updates the clustered index record.
3061 @return DB_SUCCESS if operation successfully completed, DB_LOCK_WAIT
3062 in case of a lock wait, else error code */
3063 static MY_ATTRIBUTE((nonnull, warn_unused_result))
3064 dberr_t
row_upd_clust_step(upd_node_t * node,que_thr_t * thr)3065 row_upd_clust_step(
3066 /*===============*/
3067 	upd_node_t*	node,	/*!< in: row update node */
3068 	que_thr_t*	thr)	/*!< in: query thread */
3069 {
3070 	dict_index_t*	index;
3071 	btr_pcur_t*	pcur;
3072 	dberr_t		err;
3073 	mtr_t		mtr;
3074 	rec_t*		rec;
3075 	mem_heap_t*	heap	= NULL;
3076 	rec_offs	offsets_[REC_OFFS_NORMAL_SIZE];
3077 	rec_offs*	offsets;
3078 	ulint		flags;
3079 	trx_t*		trx = thr_get_trx(thr);
3080 
3081 	rec_offs_init(offsets_);
3082 
3083 	index = dict_table_get_first_index(node->table);
3084 
3085 	const bool referenced = row_upd_index_is_referenced(index, trx);
3086 #ifdef WITH_WSREP
3087 	const bool foreign = wsrep_row_upd_index_is_foreign(index, trx);
3088 #endif
3089 
3090 	pcur = node->pcur;
3091 
3092 	/* We have to restore the cursor to its position */
3093 
3094 	mtr.start();
3095 
3096 	if (node->table->is_temporary()) {
3097 		/* Disable locking, because temporary tables are
3098 		private to the connection (no concurrent access). */
3099 		flags = node->table->no_rollback()
3100 			? BTR_NO_ROLLBACK
3101 			: BTR_NO_LOCKING_FLAG;
3102 		/* Redo logging only matters for persistent tables. */
3103 		mtr.set_log_mode(MTR_LOG_NO_REDO);
3104 	} else {
3105 		flags = node->table->no_rollback() ? BTR_NO_ROLLBACK : 0;
3106 		index->set_modified(mtr);
3107 	}
3108 
3109 	/* If the restoration does not succeed, then the same
3110 	transaction has deleted the record on which the cursor was,
3111 	and that is an SQL error. If the restoration succeeds, it may
3112 	still be that the same transaction has successively deleted
3113 	and inserted a record with the same ordering fields, but in
3114 	that case we know that the transaction has at least an
3115 	implicit x-lock on the record. */
3116 
3117 	ut_a(pcur->rel_pos == BTR_PCUR_ON);
3118 
3119 	ulint	mode;
3120 
3121 	DEBUG_SYNC_C_IF_THD(trx->mysql_thd, "innodb_row_upd_clust_step_enter");
3122 
3123 	if (dict_index_is_online_ddl(index)) {
3124 		ut_ad(node->table->id != DICT_INDEXES_ID);
3125 		mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED;
3126 		mtr_s_lock_index(index, &mtr);
3127 	} else {
3128 		mode = BTR_MODIFY_LEAF;
3129 	}
3130 
3131 	if (!btr_pcur_restore_position(mode, pcur, &mtr)) {
3132 		err = DB_RECORD_NOT_FOUND;
3133 		goto exit_func;
3134 	}
3135 
3136 	/* If this is a row in SYS_INDEXES table of the data dictionary,
3137 	then we have to free the file segments of the index tree associated
3138 	with the index */
3139 
3140 	if (node->is_delete == PLAIN_DELETE
3141 	    && node->table->id == DICT_INDEXES_ID) {
3142 
3143 		ut_ad(!dict_index_is_online_ddl(index));
3144 
3145 		dict_drop_index_tree(
3146 			btr_pcur_get_rec(pcur), pcur, trx, &mtr);
3147 
3148 		mtr.commit();
3149 
3150 		mtr.start();
3151 		index->set_modified(mtr);
3152 
3153 		if (!btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, &mtr)) {
3154 			err = DB_ERROR;
3155 			goto exit_func;
3156 		}
3157 	}
3158 
3159 	rec = btr_pcur_get_rec(pcur);
3160 	offsets = rec_get_offsets(rec, index, offsets_, index->n_core_fields,
3161 				  ULINT_UNDEFINED, &heap);
3162 
3163 	if (!flags && !node->has_clust_rec_x_lock) {
3164 		err = lock_clust_rec_modify_check_and_lock(
3165 			0, btr_pcur_get_block(pcur),
3166 			rec, index, offsets, thr);
3167 		if (err != DB_SUCCESS) {
3168 			goto exit_func;
3169 		}
3170 	}
3171 
3172 	ut_ad(index->table->no_rollback() || index->table->is_temporary()
3173 	      || row_get_rec_trx_id(rec, index, offsets) == trx->id
3174 	      || lock_trx_has_expl_x_lock(trx, index->table,
3175 					  btr_pcur_get_block(pcur),
3176 					  page_rec_get_heap_no(rec)));
3177 
3178 	if (node->is_delete == PLAIN_DELETE) {
3179 		err = row_upd_del_mark_clust_rec(
3180 			node, index, offsets, thr, referenced,
3181 #ifdef WITH_WSREP
3182 			foreign,
3183 #endif
3184 			&mtr);
3185 		goto all_done;
3186 	}
3187 
3188 	/* If the update is made for MySQL, we already have the update vector
3189 	ready, else we have to do some evaluation: */
3190 
3191 	if (UNIV_UNLIKELY(!node->in_mysql_interface)) {
3192 		/* Copy the necessary columns from clust_rec and calculate the
3193 		new values to set */
3194 		row_upd_copy_columns(rec, offsets, index,
3195 				     UT_LIST_GET_FIRST(node->columns));
3196 		row_upd_eval_new_vals(node->update);
3197 	}
3198 
3199 	if (!node->is_delete && node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
3200 		err = row_upd_clust_rec(
3201 			flags, node, index, offsets, &heap, thr, &mtr);
3202 		goto exit_func;
3203 	}
3204 
3205 	if (!row_upd_store_row(node, trx->mysql_thd, thr->prebuilt
3206 			       ? thr->prebuilt->m_mysql_table : NULL)) {
3207 		err = DB_COMPUTE_VALUE_FAILED;
3208 		goto exit_func;
3209 	}
3210 
3211 	if (row_upd_changes_ord_field_binary(index, node->update, thr,
3212 					     node->row, node->ext)) {
3213 
3214 		/* Update causes an ordering field (ordering fields within
3215 		the B-tree) of the clustered index record to change: perform
3216 		the update by delete marking and inserting.
3217 
3218 		TODO! What to do to the 'Halloween problem', where an update
3219 		moves the record forward in index so that it is again
3220 		updated when the cursor arrives there? Solution: the
3221 		read operation must check the undo record undo number when
3222 		choosing records to update. MySQL solves now the problem
3223 		externally! */
3224 
3225 		err = row_upd_clust_rec_by_insert(
3226 			node, index, thr, referenced,
3227 #ifdef WITH_WSREP
3228 			foreign,
3229 #endif
3230 			&mtr);
3231 all_done:
3232 		if (err == DB_SUCCESS) {
3233 			node->state = UPD_NODE_UPDATE_ALL_SEC;
3234 success:
3235 			node->index = dict_table_get_next_index(index);
3236 		}
3237 	} else {
3238 		err = row_upd_clust_rec(
3239 			flags, node, index, offsets, &heap, thr, &mtr);
3240 
3241 		if (err == DB_SUCCESS) {
3242 			ut_ad(node->is_delete != PLAIN_DELETE);
3243 			node->state = node->is_delete
3244 				? UPD_NODE_UPDATE_ALL_SEC
3245 				: UPD_NODE_UPDATE_SOME_SEC;
3246 			goto success;
3247 		}
3248 	}
3249 
3250 exit_func:
3251 	mtr.commit();
3252 	if (UNIV_LIKELY_NULL(heap)) {
3253 		mem_heap_free(heap);
3254 	}
3255 	return err;
3256 }
3257 
3258 /***********************************************************//**
3259 Updates the affected index records of a row. When the control is transferred
3260 to this node, we assume that we have a persistent cursor which was on a
3261 record, and the position of the cursor is stored in the cursor.
3262 @return DB_SUCCESS if operation successfully completed, else error
3263 code or DB_LOCK_WAIT */
3264 static
3265 dberr_t
row_upd(upd_node_t * node,que_thr_t * thr)3266 row_upd(
3267 /*====*/
3268 	upd_node_t*	node,	/*!< in: row update node */
3269 	que_thr_t*	thr)	/*!< in: query thread */
3270 {
3271 	dberr_t		err	= DB_SUCCESS;
3272 	DBUG_ENTER("row_upd");
3273 
3274 	ut_ad(!thr_get_trx(thr)->in_rollback);
3275 
3276 	DBUG_PRINT("row_upd", ("table: %s", node->table->name.m_name));
3277 	DBUG_PRINT("row_upd", ("info bits in update vector: 0x" ULINTPFx,
3278 			       node->update ? node->update->info_bits: 0));
3279 	DBUG_PRINT("row_upd", ("foreign_id: %s",
3280 			       node->foreign ? node->foreign->id: "NULL"));
3281 
3282 	if (UNIV_LIKELY(node->in_mysql_interface)) {
3283 
3284 		/* We do not get the cmpl_info value from the MySQL
3285 		interpreter: we must calculate it on the fly: */
3286 
3287 		if (node->is_delete == PLAIN_DELETE
3288 		    || row_upd_changes_some_index_ord_field_binary(
3289 			    node->table, node->update)) {
3290 			node->cmpl_info = 0;
3291 		} else {
3292 			node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
3293 		}
3294 	}
3295 
3296 	switch (node->state) {
3297 	case UPD_NODE_UPDATE_CLUSTERED:
3298 	case UPD_NODE_INSERT_CLUSTERED:
3299 		log_free_check();
3300 
3301 		err = row_upd_clust_step(node, thr);
3302 
3303 		if (err != DB_SUCCESS) {
3304 
3305 			DBUG_RETURN(err);
3306 		}
3307 	}
3308 
3309 	DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd,
3310 			    "after_row_upd_clust");
3311 
3312 	if (node->index == NULL
3313 	    || (!node->is_delete
3314 		&& (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE))) {
3315 
3316 		DBUG_RETURN(DB_SUCCESS);
3317 	}
3318 
3319 	DBUG_EXECUTE_IF("row_upd_skip_sec", node->index = NULL;);
3320 
3321 	do {
3322 		/* Skip corrupted index */
3323 		dict_table_skip_corrupt_index(node->index);
3324 
3325 		if (!node->index) {
3326 			break;
3327 		}
3328 
3329 		if (node->index->type != DICT_FTS) {
3330 			err = row_upd_sec_step(node, thr);
3331 
3332 			if (err != DB_SUCCESS) {
3333 
3334 				DBUG_RETURN(err);
3335 			}
3336 		}
3337 
3338 		node->index = dict_table_get_next_index(node->index);
3339 	} while (node->index != NULL);
3340 
3341 	ut_ad(err == DB_SUCCESS);
3342 
3343 	/* Do some cleanup */
3344 
3345 	if (node->row != NULL) {
3346 		node->row = NULL;
3347 		node->ext = NULL;
3348 		node->upd_row = NULL;
3349 		node->upd_ext = NULL;
3350 		mem_heap_empty(node->heap);
3351 	}
3352 
3353 	node->state = UPD_NODE_UPDATE_CLUSTERED;
3354 
3355 	DBUG_RETURN(err);
3356 }
3357 
3358 /***********************************************************//**
3359 Updates a row in a table. This is a high-level function used in SQL execution
3360 graphs.
3361 @return query thread to run next or NULL */
3362 que_thr_t*
row_upd_step(que_thr_t * thr)3363 row_upd_step(
3364 /*=========*/
3365 	que_thr_t*	thr)	/*!< in: query thread */
3366 {
3367 	upd_node_t*	node;
3368 	sel_node_t*	sel_node;
3369 	que_node_t*	parent;
3370 	dberr_t		err		= DB_SUCCESS;
3371 	trx_t*		trx;
3372 	DBUG_ENTER("row_upd_step");
3373 
3374 	ut_ad(thr);
3375 
3376 	trx = thr_get_trx(thr);
3377 
3378 	node = static_cast<upd_node_t*>(thr->run_node);
3379 
3380 	sel_node = node->select;
3381 
3382 	parent = que_node_get_parent(node);
3383 
3384 	ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
3385 
3386 	if (thr->prev_node == parent) {
3387 		node->state = UPD_NODE_SET_IX_LOCK;
3388 	}
3389 
3390 	if (node->state == UPD_NODE_SET_IX_LOCK) {
3391 
3392 		if (!node->has_clust_rec_x_lock) {
3393 			/* It may be that the current session has not yet
3394 			started its transaction, or it has been committed: */
3395 
3396 			err = lock_table(0, node->table, LOCK_IX, thr);
3397 
3398 			if (err != DB_SUCCESS) {
3399 
3400 				goto error_handling;
3401 			}
3402 		}
3403 
3404 		node->state = UPD_NODE_UPDATE_CLUSTERED;
3405 
3406 		if (node->searched_update) {
3407 			/* Reset the cursor */
3408 			sel_node->state = SEL_NODE_OPEN;
3409 
3410 			/* Fetch a row to update */
3411 
3412 			thr->run_node = sel_node;
3413 
3414 			DBUG_RETURN(thr);
3415 		}
3416 	}
3417 
3418 	/* sel_node is NULL if we are in the MySQL interface */
3419 
3420 	if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
3421 
3422 		if (!node->searched_update) {
3423 			/* An explicit cursor should be positioned on a row
3424 			to update */
3425 
3426 			ut_error;
3427 
3428 			err = DB_ERROR;
3429 
3430 			goto error_handling;
3431 		}
3432 
3433 		ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
3434 
3435 		/* No more rows to update, or the select node performed the
3436 		updates directly in-place */
3437 
3438 		thr->run_node = parent;
3439 
3440 		DBUG_RETURN(thr);
3441 	}
3442 
3443 	/* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
3444 
3445 	err = row_upd(node, thr);
3446 
3447 error_handling:
3448 	trx->error_state = err;
3449 
3450 	if (err != DB_SUCCESS) {
3451 		DBUG_RETURN(NULL);
3452 	}
3453 
3454 	/* DO THE TRIGGER ACTIONS HERE */
3455 
3456 	if (node->searched_update) {
3457 		/* Fetch next row to update */
3458 
3459 		thr->run_node = sel_node;
3460 	} else {
3461 		/* It was an explicit cursor update */
3462 
3463 		thr->run_node = parent;
3464 	}
3465 
3466 	node->state = UPD_NODE_UPDATE_CLUSTERED;
3467 
3468 	DBUG_RETURN(thr);
3469 }
3470 
3471 /** Write query start time as SQL field data to a buffer. Needed by InnoDB.
3472 @param	thd	Thread object
3473 @param	buf	Buffer to hold start time data */
3474 void thd_get_query_start_data(THD *thd, char *buf);
3475 
3476 /** Appends row_start or row_end field to update vector and sets a
3477 CURRENT_TIMESTAMP/trx->id value to it.
3478 Supposed to be called only by make_versioned_update() and
3479 make_versioned_delete().
3480 @param[in]	trx	transaction
3481 @param[in]	vers_sys_idx	table->row_start or table->row_end */
vers_update_fields(const trx_t * trx,ulint idx)3482 void upd_node_t::vers_update_fields(const trx_t *trx, ulint idx)
3483 {
3484 	ut_ad(in_mysql_interface); // otherwise needs to recalculate
3485 				   // node->cmpl_info
3486 	ut_ad(idx == table->vers_start || idx == table->vers_end);
3487 
3488 	dict_index_t* clust_index = dict_table_get_first_index(table);
3489         const dict_col_t *col= dict_table_get_nth_col(table, idx);
3490         ulint field_no= dict_col_get_clust_pos(col, clust_index);
3491         upd_field_t *ufield;
3492 
3493         for (ulint i= 0; i < update->n_fields; ++i)
3494         {
3495           if (update->fields[i].field_no == field_no)
3496           {
3497             ufield= &update->fields[i];
3498             goto skip_append;
3499           }
3500         }
3501 
3502         /* row_create_update_node_for_mysql() pre-allocated this much.
3503 	   At least one PK column always remains unchanged. */
3504 	ut_ad(update->n_fields < ulint(table->n_cols + table->n_v_cols));
3505 
3506 	update->n_fields++;
3507         ufield= upd_get_nth_field(update, update->n_fields - 1);
3508         upd_field_set_field_no(ufield, field_no, clust_index);
3509 
3510 skip_append:
3511   char *where= reinterpret_cast<char *>(update->vers_sys_value);
3512   if (col->vers_native())
3513   {
3514     mach_write_to_8(where, trx->id);
3515   }
3516   else
3517   {
3518     thd_get_query_start_data(trx->mysql_thd, where);
3519   }
3520 
3521   dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len);
3522 
3523   for (ulint col_no= 0; col_no < dict_table_get_n_v_cols(table); col_no++)
3524   {
3525 
3526     const dict_v_col_t *v_col= dict_table_get_nth_v_col(table, col_no);
3527     if (!v_col->m_col.ord_part)
3528       continue;
3529     for (ulint i= 0; i < unsigned(v_col->num_base); i++)
3530     {
3531       dict_col_t *base_col= v_col->base_col[i];
3532       if (base_col->ind == col->ind)
3533       {
3534         /* Virtual column depends on system field value
3535         which we updated above. Remove it from update
3536         vector, so it is recalculated in
3537         row_upd_store_v_row() (see !update branch). */
3538         update->remove(v_col->v_pos);
3539         break;
3540       }
3541     }
3542   }
3543 }
3544