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