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[in]	prebuilt	compress_heap must be taken from here
889 @param[out]	error		error number in case of failure
890 @return own: update vector of differing fields, excluding roll ptr and
891 trx id,if error is not equal to DB_SUCCESS, return NULL */
892 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,row_prebuilt_t * prebuilt,dberr_t * error)893 row_upd_build_difference_binary(
894 	dict_index_t*	index,
895 	const dtuple_t*	entry,
896 	const rec_t*	rec,
897 	const ulint*	offsets,
898 	bool		no_sys,
899 	trx_t*		trx,
900 	mem_heap_t*	heap,
901 	TABLE*		mysql_table,
902 	row_prebuilt_t*	prebuilt,
903 	dberr_t*	error)
904 {
905 	upd_field_t*	upd_field;
906 	dfield_t*	dfield;
907 	const byte*	data;
908 	ulint		len;
909 	upd_t*		update;
910 	ulint		n_diff;
911 	ulint		trx_id_pos;
912 	ulint		i;
913 	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
914 	ulint		n_fld = dtuple_get_n_fields(entry);
915 	ulint		n_v_fld = dtuple_get_n_v_fields(entry);
916 	rec_offs_init(offsets_);
917 
918 	/* This function is used only for a clustered index */
919 	ut_a(dict_index_is_clust(index));
920 
921 	update = upd_create(n_fld + n_v_fld, heap);
922 
923 	n_diff = 0;
924 
925 	trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
926 	ut_ad(dict_table_is_intrinsic(index->table)
927 	      || (dict_index_get_sys_col_pos(index, DATA_ROLL_PTR)
928 			== trx_id_pos + 1));
929 
930 	if (!offsets) {
931 		offsets = rec_get_offsets(rec, index, offsets_,
932 					  ULINT_UNDEFINED, &heap);
933 	} else {
934 		ut_ad(rec_offs_validate(rec, index, offsets));
935 	}
936 
937 	for (i = 0; i < n_fld; i++) {
938 
939 		data = rec_get_nth_field(rec, offsets, i, &len);
940 
941 		dfield = dtuple_get_nth_field(entry, i);
942 
943 		/* NOTE: we compare the fields as binary strings!
944 		(No collation) */
945 		if (no_sys) {
946 			/* TRX_ID */
947 			if (i == trx_id_pos) {
948 				continue;
949 			}
950 
951 			/* DB_ROLL_PTR */
952 			if (i == trx_id_pos + 1
953 			    && !dict_table_is_intrinsic(index->table)) {
954 				continue;
955 			}
956 		}
957 
958 		if (!dfield_is_ext(dfield)
959 		    != !rec_offs_nth_extern(offsets, i)
960 		    || !dfield_data_is_binary_equal(dfield, len, data)) {
961 
962 			upd_field = upd_get_nth_field(update, n_diff);
963 
964 			dfield_copy(&(upd_field->new_val), dfield);
965 
966 			upd_field_set_field_no(upd_field, i, index, trx);
967 
968 			n_diff++;
969 		}
970 	}
971 
972 	/* Check the virtual columns updates. Even if there is no non-virtual
973 	column (base columns) change, we will still need to build the
974 	indexed virtual column value so that undo log would log them (
975 	for purge/mvcc purpose) */
976 	if (n_v_fld > 0) {
977 		row_ext_t*	ext;
978 		mem_heap_t*	v_heap = NULL;
979 		THD*		thd;
980 
981 		if (trx == NULL) {
982 			thd = current_thd;
983 		} else {
984 			thd = trx->mysql_thd;
985 		}
986 
987 		ut_ad(!update->old_vrow);
988 
989 		for (i = 0; i < n_v_fld; i++) {
990 			const dict_v_col_t*     col
991                                 = dict_table_get_nth_v_col(index->table, i);
992 
993 			if (!col->m_col.ord_part) {
994 				continue;
995 			}
996 
997 			if (update->old_vrow == NULL) {
998 				update->old_vrow = row_build(
999 					ROW_COPY_POINTERS, index, rec, offsets,
1000 					index->table, NULL, NULL, &ext, heap);
1001 			}
1002 
1003 			dfield = dtuple_get_nth_v_field(entry, i);
1004 
1005 			dfield_t*	vfield = innobase_get_computed_value(
1006 				update->old_vrow, col, index,
1007 				&v_heap, heap, NULL, thd, mysql_table,
1008 				NULL, NULL, NULL, prebuilt);
1009 			if (vfield == NULL) {
1010 				*error = DB_COMPUTE_VALUE_FAILED;
1011 				return(NULL);
1012 			}
1013 
1014 			if (!dfield_data_is_binary_equal(
1015 				dfield, vfield->len,
1016 				static_cast<byte*>(vfield->data))) {
1017 				upd_field = upd_get_nth_field(update, n_diff);
1018 
1019 				upd_field->old_v_val = static_cast<dfield_t*>(
1020 					mem_heap_alloc(
1021 						heap,
1022 						sizeof *upd_field->old_v_val));
1023 
1024 				dfield_copy(upd_field->old_v_val, vfield);
1025 
1026 				dfield_copy(&(upd_field->new_val), dfield);
1027 
1028 				upd_field_set_v_field_no(
1029 					upd_field, i, index);
1030 
1031 				n_diff++;
1032 
1033 			}
1034 		}
1035 
1036 		if (v_heap) {
1037 			mem_heap_free(v_heap);
1038 		}
1039 	}
1040 
1041 	update->n_fields = n_diff;
1042 	ut_ad(update->validate());
1043 
1044 	return(update);
1045 }
1046 
1047 /** Fetch a prefix of an externally stored column.
1048 This is similar to row_ext_lookup(), but the row_ext_t holds the old values
1049 of the column and must not be poisoned with the new values.
1050 @param[in]	data		'internally' stored part of the field
1051 containing also the reference to the external part
1052 @param[in]	local_len	length of data, in bytes
1053 @param[in]	page_size	BLOB page size
1054 @param[in,out]	len		input - length of prefix to
1055 fetch; output: fetched length of the prefix
1056 @param[in,out]	heap		heap where to allocate
1057 @return BLOB prefix */
1058 static
1059 byte*
row_upd_ext_fetch(const byte * data,ulint local_len,const page_size_t & page_size,ulint * len,mem_heap_t * heap)1060 row_upd_ext_fetch(
1061 	const byte*		data,
1062 	ulint			local_len,
1063 	const page_size_t&	page_size,
1064 	ulint*			len,
1065 	mem_heap_t*		heap)
1066 {
1067 	byte*	buf = static_cast<byte*>(mem_heap_alloc(heap, *len));
1068 
1069 	*len = btr_copy_externally_stored_field_prefix(
1070 		buf, *len, page_size, data, local_len);
1071 
1072 	/* We should never update records containing a half-deleted BLOB. */
1073 	ut_a(*len);
1074 
1075 	return(buf);
1076 }
1077 
1078 /** Replaces the new column value stored in the update vector in
1079 the given index entry field.
1080 @param[in,out]	dfield		data field of the index entry
1081 @param[in]	field		index field
1082 @param[in]	col		field->col
1083 @param[in]	uf		update field
1084 @param[in,out]	heap		memory heap for allocating and copying
1085 the new value
1086 @param[in]	page_size	page size */
1087 static
1088 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)1089 row_upd_index_replace_new_col_val(
1090 	dfield_t*		dfield,
1091 	const dict_field_t*	field,
1092 	const dict_col_t*	col,
1093 	const upd_field_t*	uf,
1094 	mem_heap_t*		heap,
1095 	const page_size_t&	page_size)
1096 {
1097 	ulint		len;
1098 	const byte*	data;
1099 
1100 	dfield_copy_data(dfield, &uf->new_val);
1101 
1102 	if (dfield_is_null(dfield)) {
1103 		return;
1104 	}
1105 
1106 	len = dfield_get_len(dfield);
1107 	data = static_cast<const byte*>(dfield_get_data(dfield));
1108 
1109 	if (field->prefix_len > 0) {
1110 		ibool		fetch_ext = dfield_is_ext(dfield)
1111 			&& len < (ulint) field->prefix_len
1112 			+ BTR_EXTERN_FIELD_REF_SIZE;
1113 
1114 		if (fetch_ext) {
1115 			ulint	l = len;
1116 
1117 			len = field->prefix_len;
1118 
1119 			data = row_upd_ext_fetch(data, l, page_size,
1120 						 &len, heap);
1121 		}
1122 
1123 		len = dtype_get_at_most_n_mbchars(col->prtype,
1124 						  col->mbminmaxlen,
1125 						  field->prefix_len, len,
1126 						  (const char*) data);
1127 
1128 		dfield_set_data(dfield, data, len);
1129 
1130 		if (!fetch_ext) {
1131 			dfield_dup(dfield, heap);
1132 		}
1133 
1134 		return;
1135 	}
1136 
1137 	switch (uf->orig_len) {
1138 		byte*	buf;
1139 	case BTR_EXTERN_FIELD_REF_SIZE:
1140 		/* Restore the original locally stored
1141 		part of the column.  In the undo log,
1142 		InnoDB writes a longer prefix of externally
1143 		stored columns, so that column prefixes
1144 		in secondary indexes can be reconstructed. */
1145 		dfield_set_data(dfield,
1146 				data + len - BTR_EXTERN_FIELD_REF_SIZE,
1147 				BTR_EXTERN_FIELD_REF_SIZE);
1148 		dfield_set_ext(dfield);
1149 		/* fall through */
1150 	case 0:
1151 		dfield_dup(dfield, heap);
1152 		break;
1153 	default:
1154 		/* Reconstruct the original locally
1155 		stored part of the column.  The data
1156 		will have to be copied. */
1157 		ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
1158 		buf = static_cast<byte*>(mem_heap_alloc(heap, uf->orig_len));
1159 
1160 		/* Copy the locally stored prefix. */
1161 		memcpy(buf, data,
1162 		       uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE);
1163 
1164 		/* Copy the BLOB pointer. */
1165 		memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE,
1166 		       data + len - BTR_EXTERN_FIELD_REF_SIZE,
1167 		       BTR_EXTERN_FIELD_REF_SIZE);
1168 
1169 		dfield_set_data(dfield, buf, uf->orig_len);
1170 		dfield_set_ext(dfield);
1171 		break;
1172 	}
1173 }
1174 
1175 /***********************************************************//**
1176 Replaces the new column values stored in the update vector to the index entry
1177 given. */
1178 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)1179 row_upd_index_replace_new_col_vals_index_pos(
1180 /*=========================================*/
1181 	dtuple_t*	entry,	/*!< in/out: index entry where replaced;
1182 				the clustered index record must be
1183 				covered by a lock or a page latch to
1184 				prevent deletion (rollback or purge) */
1185 	dict_index_t*	index,	/*!< in: index; NOTE that this may also be a
1186 				non-clustered index */
1187 	const upd_t*	update,	/*!< in: an update vector built for the index so
1188 				that the field number in an upd_field is the
1189 				index position */
1190 	ibool		order_only,
1191 				/*!< in: if TRUE, limit the replacement to
1192 				ordering fields of index; note that this
1193 				does not work for non-clustered indexes. */
1194 	mem_heap_t*	heap)	/*!< in: memory heap for allocating and
1195 				copying the new values */
1196 {
1197 	ulint		i;
1198 	ulint		n_fields;
1199 	const page_size_t&	page_size = dict_table_page_size(index->table);
1200 
1201 	ut_ad(index);
1202 
1203 	dtuple_set_info_bits(entry, update->info_bits);
1204 
1205 	if (order_only) {
1206 		n_fields = dict_index_get_n_unique(index);
1207 	} else {
1208 		n_fields = dict_index_get_n_fields(index);
1209 	}
1210 
1211 	for (i = 0; i < n_fields; i++) {
1212 		const dict_field_t*	field;
1213 		const dict_col_t*	col;
1214 		const upd_field_t*	uf;
1215 
1216 		field = dict_index_get_nth_field(index, i);
1217 		col = dict_field_get_col(field);
1218 		if (dict_col_is_virtual(col)) {
1219 			const dict_v_col_t*	vcol = reinterpret_cast<
1220 							const dict_v_col_t*>(
1221 								col);
1222 
1223 			uf = upd_get_field_by_field_no(
1224 				update, vcol->v_pos, true);
1225 		} else {
1226 			uf = upd_get_field_by_field_no(
1227 				update, i, false);
1228 		}
1229 
1230 		if (uf) {
1231 			row_upd_index_replace_new_col_val(
1232 				dtuple_get_nth_field(entry, i),
1233 				field, col, uf, heap, page_size);
1234 		}
1235 	}
1236 }
1237 
1238 /***********************************************************//**
1239 Replaces the new column values stored in the update vector to the index entry
1240 given. */
1241 void
row_upd_index_replace_new_col_vals(dtuple_t * entry,dict_index_t * index,const upd_t * update,mem_heap_t * heap)1242 row_upd_index_replace_new_col_vals(
1243 /*===============================*/
1244 	dtuple_t*	entry,	/*!< in/out: index entry where replaced;
1245 				the clustered index record must be
1246 				covered by a lock or a page latch to
1247 				prevent deletion (rollback or purge) */
1248 	dict_index_t*	index,	/*!< in: index; NOTE that this may also be a
1249 				non-clustered index */
1250 	const upd_t*	update,	/*!< in: an update vector built for the
1251 				CLUSTERED index so that the field number in
1252 				an upd_field is the clustered index position */
1253 	mem_heap_t*	heap)	/*!< in: memory heap for allocating and
1254 				copying the new values */
1255 {
1256 	ulint			i;
1257 	const dict_index_t*	clust_index
1258 		= dict_table_get_first_index(index->table);
1259 	const page_size_t&	page_size = dict_table_page_size(index->table);
1260 
1261 	dtuple_set_info_bits(entry, update->info_bits);
1262 
1263 	for (i = 0; i < dict_index_get_n_fields(index); i++) {
1264 		const dict_field_t*	field;
1265 		const dict_col_t*	col;
1266 		const upd_field_t*	uf;
1267 
1268 		field = dict_index_get_nth_field(index, i);
1269 		col = dict_field_get_col(field);
1270 		if (dict_col_is_virtual(col)) {
1271 			const dict_v_col_t*	vcol = reinterpret_cast<
1272 							const dict_v_col_t*>(
1273 								col);
1274 
1275 			uf = upd_get_field_by_field_no(
1276 				update, vcol->v_pos, true);
1277 		} else {
1278 			uf = upd_get_field_by_field_no(
1279 				update,
1280 				dict_col_get_clust_pos(col, clust_index),
1281 				false);
1282 		}
1283 
1284 		if (uf) {
1285 			row_upd_index_replace_new_col_val(
1286 				dtuple_get_nth_field(entry, i),
1287 				field, col, uf, heap, page_size);
1288 		}
1289 	}
1290 }
1291 
1292 /** Replaces the virtual column values stored in the update vector.
1293 @param[in,out]	row	row whose column to be set
1294 @param[in]	field	data to set
1295 @param[in]	len	data length
1296 @param[in]	vcol	virtual column info */
1297 static
1298 void
row_upd_set_vcol_data(dtuple_t * row,const byte * field,ulint len,dict_v_col_t * vcol)1299 row_upd_set_vcol_data(
1300 	dtuple_t*		row,
1301 	const byte*             field,
1302 	ulint                   len,
1303 	dict_v_col_t*		vcol)
1304 {
1305 	dfield_t*	dfield = dtuple_get_nth_v_field(row, vcol->v_pos);
1306 
1307 	if (dfield_get_type(dfield)->mtype == DATA_MISSING) {
1308 		dict_col_copy_type(&vcol->m_col, dfield_get_type(dfield));
1309 
1310 		dfield_set_data(dfield, field, len);
1311 	}
1312 }
1313 
1314 /** Replaces the virtual column values stored in a dtuple with that of
1315 a update vector.
1316 @param[in,out]	row	row whose column to be updated
1317 @param[in]	table	table
1318 @param[in]	update	an update vector built for the clustered index
1319 @param[in]	upd_new	update to new or old value
1320 @param[in,out]	undo_row undo row (if needs to be updated)
1321 @param[in]	ptr	remaining part in update undo log */
1322 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)1323 row_upd_replace_vcol(
1324 	dtuple_t*		row,
1325 	const dict_table_t*	table,
1326 	const upd_t*		update,
1327 	bool			upd_new,
1328 	dtuple_t*		undo_row,
1329 	const byte*		ptr)
1330 {
1331 	ulint			col_no;
1332 	ulint			i;
1333 	ulint			n_cols;
1334 
1335 	n_cols = dtuple_get_n_v_fields(row);
1336 	for (col_no = 0; col_no < n_cols; col_no++) {
1337 		dfield_t*		dfield;
1338 
1339 		const dict_v_col_t*	col
1340 			= dict_table_get_nth_v_col(table, col_no);
1341 
1342 		/* If there is no index on the column, do not bother for
1343 		value update */
1344 		if (!col->m_col.ord_part) {
1345 			dict_index_t*	clust_index
1346 				= dict_table_get_first_index(table);
1347 
1348 			/* Skip the column if there is no online alter
1349 			table in progress or it is not being indexed
1350 			in new table */
1351 			if (!dict_index_is_online_ddl(clust_index)
1352 			    || !row_log_col_is_indexed(clust_index, col_no)) {
1353 				continue;
1354 			}
1355 		}
1356 
1357 		dfield = dtuple_get_nth_v_field(row, col_no);
1358 
1359 		for (i = 0; i < upd_get_n_fields(update); i++) {
1360 			const upd_field_t*	upd_field
1361 				= upd_get_nth_field(update, i);
1362 			if (!upd_fld_is_virtual_col(upd_field)
1363 			    || upd_field->field_no != col->v_pos) {
1364 				continue;
1365 			}
1366 
1367 			if (upd_new) {
1368 				dfield_copy_data(dfield, &upd_field->new_val);
1369 			} else {
1370 				dfield_copy_data(dfield, upd_field->old_v_val);
1371 			}
1372 
1373 			dfield_get_type(dfield)->mtype =
1374 				upd_field->new_val.type.mtype;
1375 			dfield_get_type(dfield)->prtype =
1376 				upd_field->new_val.type.prtype;
1377 			dfield_get_type(dfield)->mbminmaxlen =
1378 				upd_field->new_val.type.mbminmaxlen;
1379 			break;
1380 		}
1381 	}
1382 
1383 	bool	first_v_col = true;
1384 	bool	is_undo_log = true;
1385 
1386 	/* We will read those unchanged (but indexed) virtual columns in */
1387 	if (ptr != NULL) {
1388 		const byte*	end_ptr;
1389 
1390 		end_ptr = ptr + mach_read_from_2(ptr);
1391 		ptr += 2;
1392 
1393 		while (ptr != end_ptr) {
1394 			const byte*             field;
1395 			ulint                   field_no;
1396 			ulint                   len;
1397 			ulint                   orig_len;
1398 			bool			is_v;
1399 
1400 			field_no = mach_read_next_compressed(&ptr);
1401 
1402 			is_v = (field_no >= REC_MAX_N_FIELDS);
1403 
1404 			if (is_v) {
1405 				ptr = trx_undo_read_v_idx(
1406 					table, ptr, first_v_col, &is_undo_log,
1407 					&field_no);
1408 				first_v_col = false;
1409 			}
1410 
1411 			ptr = trx_undo_rec_get_col_val(
1412 				ptr, &field, &len, &orig_len);
1413 
1414 			if (field_no == ULINT_UNDEFINED) {
1415 				ut_ad(is_v);
1416 				continue;
1417 			}
1418 
1419 			if (is_v) {
1420 				dict_v_col_t* vcol = dict_table_get_nth_v_col(
1421 							table, field_no);
1422 
1423 				row_upd_set_vcol_data(row, field, len, vcol);
1424 
1425 				if (undo_row) {
1426 					row_upd_set_vcol_data(
1427 						undo_row, field, len, vcol);
1428 				}
1429 			}
1430 			ut_ad(ptr<= end_ptr);
1431 		}
1432 	}
1433 }
1434 
1435 /***********************************************************//**
1436 Replaces the new column values stored in the update vector. */
1437 void
row_upd_replace(dtuple_t * row,row_ext_t ** ext,const dict_index_t * index,const upd_t * update,mem_heap_t * heap)1438 row_upd_replace(
1439 /*============*/
1440 	dtuple_t*		row,	/*!< in/out: row where replaced,
1441 					indexed by col_no;
1442 					the clustered index record must be
1443 					covered by a lock or a page latch to
1444 					prevent deletion (rollback or purge) */
1445 	row_ext_t**		ext,	/*!< out, own: NULL, or externally
1446 					stored column prefixes */
1447 	const dict_index_t*	index,	/*!< in: clustered index */
1448 	const upd_t*		update,	/*!< in: an update vector built for the
1449 					clustered index */
1450 	mem_heap_t*		heap)	/*!< in: memory heap */
1451 {
1452 	ulint			col_no;
1453 	ulint			i;
1454 	ulint			n_cols;
1455 	ulint			n_ext_cols;
1456 	ulint*			ext_cols;
1457 	const dict_table_t*	table;
1458 
1459 	ut_ad(row);
1460 	ut_ad(ext);
1461 	ut_ad(index);
1462 	ut_ad(dict_index_is_clust(index));
1463 	ut_ad(update);
1464 	ut_ad(heap);
1465 	ut_ad(update->validate());
1466 
1467 	n_cols = dtuple_get_n_fields(row);
1468 	table = index->table;
1469 	ut_ad(n_cols == dict_table_get_n_cols(table));
1470 
1471 	ext_cols = static_cast<ulint*>(
1472 		mem_heap_alloc(heap, n_cols * sizeof *ext_cols));
1473 
1474 	n_ext_cols = 0;
1475 
1476 	dtuple_set_info_bits(row, update->info_bits);
1477 
1478 	for (col_no = 0; col_no < n_cols; col_no++) {
1479 
1480 		const dict_col_t*	col
1481 			= dict_table_get_nth_col(table, col_no);
1482 		const ulint		clust_pos
1483 			= dict_col_get_clust_pos(col, index);
1484 		dfield_t*		dfield;
1485 
1486 		if (UNIV_UNLIKELY(clust_pos == ULINT_UNDEFINED)) {
1487 
1488 			continue;
1489 		}
1490 
1491 		dfield = dtuple_get_nth_field(row, col_no);
1492 
1493 		for (i = 0; i < upd_get_n_fields(update); i++) {
1494 
1495 			const upd_field_t*	upd_field
1496 				= upd_get_nth_field(update, i);
1497 
1498 			if (upd_field->field_no != clust_pos
1499 			    || upd_fld_is_virtual_col(upd_field)) {
1500 
1501 				continue;
1502 			}
1503 
1504 			dfield_copy_data(dfield, &upd_field->new_val);
1505 			break;
1506 		}
1507 
1508 		if (dfield_is_ext(dfield) && col->ord_part) {
1509 			ext_cols[n_ext_cols++] = col_no;
1510 		}
1511 	}
1512 
1513 	if (n_ext_cols) {
1514 		*ext = row_ext_create(n_ext_cols, ext_cols, table->flags, row,
1515 				      heap);
1516 	} else {
1517 		*ext = NULL;
1518 	}
1519 
1520 	row_upd_replace_vcol(row, table, update, true, NULL, NULL);
1521 }
1522 
1523 /***********************************************************//**
1524 Checks if an update vector changes an ordering field of an index record.
1525 
1526 This function is fast if the update vector is short or the number of ordering
1527 fields in the index is small. Otherwise, this can be quadratic.
1528 NOTE: we compare the fields as binary strings!
1529 @return TRUE if update vector changes an ordering field in the index record */
1530 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)1531 row_upd_changes_ord_field_binary_func(
1532 /*==================================*/
1533 	dict_index_t*	index,	/*!< in: index of the record */
1534 	const upd_t*	update,	/*!< in: update vector for the row; NOTE: the
1535 				field numbers in this MUST be clustered index
1536 				positions! */
1537 #ifdef UNIV_DEBUG
1538 	const que_thr_t*thr,	/*!< in: query thread */
1539 #endif /* UNIV_DEBUG */
1540 	const dtuple_t*	row,	/*!< in: old value of row, or NULL if the
1541 				row and the data values in update are not
1542 				known when this function is called, e.g., at
1543 				compile time */
1544 	const row_ext_t*ext,	/*!< NULL, or prefixes of the externally
1545 				stored columns in the old row */
1546 	ulint		flag)	/*!< in: ROW_BUILD_NORMAL,
1547 				ROW_BUILD_FOR_PURGE or ROW_BUILD_FOR_UNDO */
1548 {
1549 	ulint			n_unique;
1550 	ulint			i;
1551 	const dict_index_t*	clust_index;
1552 
1553 	ut_ad(index);
1554 	ut_ad(update);
1555 	ut_ad(thr);
1556 	ut_ad(thr->graph);
1557 	ut_ad(thr->graph->trx);
1558 
1559 	n_unique = dict_index_get_n_unique(index);
1560 
1561 	clust_index = dict_table_get_first_index(index->table);
1562 
1563 	for (i = 0; i < n_unique; i++) {
1564 
1565 		const dict_field_t*	ind_field;
1566 		const dict_col_t*	col;
1567 		ulint			col_no;
1568 		const upd_field_t*	upd_field;
1569 		const dfield_t*		dfield;
1570 		dfield_t		dfield_ext;
1571 		ulint			dfield_len;
1572 		const byte*		buf;
1573 		bool			is_virtual;
1574 		const dict_v_col_t*	vcol = NULL;
1575 
1576 		ind_field = dict_index_get_nth_field(index, i);
1577 		col = dict_field_get_col(ind_field);
1578 		col_no = dict_col_get_no(col);
1579 		is_virtual = dict_col_is_virtual(col);
1580 
1581 		if (is_virtual) {
1582 			vcol = reinterpret_cast<const dict_v_col_t*>(col);
1583 
1584 			upd_field = upd_get_field_by_field_no(
1585 				update, vcol->v_pos, true);
1586 		} else {
1587 			upd_field = upd_get_field_by_field_no(
1588 				update,
1589 				dict_col_get_clust_pos(col, clust_index),
1590 				false);
1591 		}
1592 
1593 		if (upd_field == NULL) {
1594 			continue;
1595 		}
1596 
1597 		if (row == NULL) {
1598 			ut_ad(ext == NULL);
1599 			return(TRUE);
1600 		}
1601 
1602 		if (is_virtual) {
1603 			dfield = dtuple_get_nth_v_field(
1604 				row,  vcol->v_pos);
1605 		} else {
1606 			dfield = dtuple_get_nth_field(row, col_no);
1607 		}
1608 
1609 		/* For spatial index update, since the different geometry
1610 		data could generate same MBR, so, if the new index entry is
1611 		same as old entry, which means the MBR is not changed, we
1612 		don't need to do anything. */
1613 		if (dict_index_is_spatial(index) && i == 0) {
1614 			double		mbr1[SPDIMS * 2];
1615 			double		mbr2[SPDIMS * 2];
1616 			rtr_mbr_t*	old_mbr;
1617 			rtr_mbr_t*	new_mbr;
1618 			uchar*		dptr = NULL;
1619 			ulint		flen = 0;
1620 			ulint		dlen = 0;
1621 			mem_heap_t*	temp_heap = NULL;
1622 			const dfield_t*	new_field = &upd_field->new_val;
1623 
1624 			const page_size_t	page_size
1625 				= (ext != NULL)
1626 				? ext->page_size
1627 				: dict_table_page_size(
1628 					index->table);
1629 
1630 			ut_ad(dfield->data != NULL
1631 			      && dfield->len > GEO_DATA_HEADER_SIZE);
1632 			ut_ad(dict_col_get_spatial_status(col) != SPATIAL_NONE);
1633 
1634 			/* Get the old mbr. */
1635 			if (dfield_is_ext(dfield)) {
1636 				/* For off-page stored data, we
1637 				need to read the whole field data. */
1638 				flen = dfield_get_len(dfield);
1639 				dptr = static_cast<byte*>(
1640 					dfield_get_data(dfield));
1641 				temp_heap = mem_heap_create(1000);
1642 
1643 				dptr = btr_copy_externally_stored_field(
1644 					&dlen, dptr,
1645 					page_size,
1646 					flen,
1647 					temp_heap);
1648 			} else {
1649 				dptr = static_cast<uchar*>(dfield->data);
1650 				dlen = dfield->len;
1651 			}
1652 
1653 			rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE,
1654 					   static_cast<uint>(dlen
1655 					   - GEO_DATA_HEADER_SIZE),
1656 					   SPDIMS, mbr1);
1657 			old_mbr = reinterpret_cast<rtr_mbr_t*>(mbr1);
1658 
1659 			/* Get the new mbr. */
1660 			if (dfield_is_ext(new_field)) {
1661 				if (flag == ROW_BUILD_FOR_UNDO
1662 				    && dict_table_get_format(index->table)
1663 					>= UNIV_FORMAT_B) {
1664 					/* For undo, and the table is Barrcuda,
1665 					we need to skip the prefix data. */
1666 					flen = BTR_EXTERN_FIELD_REF_SIZE;
1667 					ut_ad(dfield_get_len(new_field) >=
1668 					      BTR_EXTERN_FIELD_REF_SIZE);
1669 					dptr = static_cast<byte*>(
1670 						dfield_get_data(new_field))
1671 						+ dfield_get_len(new_field)
1672 						- BTR_EXTERN_FIELD_REF_SIZE;
1673 				} else {
1674 					flen = dfield_get_len(new_field);
1675 					dptr = static_cast<byte*>(
1676 						dfield_get_data(new_field));
1677 				}
1678 
1679 				if (temp_heap == NULL) {
1680 					temp_heap = mem_heap_create(1000);
1681 				}
1682 
1683 				dptr = btr_copy_externally_stored_field(
1684 					&dlen, dptr,
1685 					page_size,
1686 					flen,
1687 					temp_heap);
1688 			} else {
1689 				dptr = static_cast<uchar*>(upd_field->new_val.data);
1690 				dlen = upd_field->new_val.len;
1691 			}
1692 			rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE,
1693 					   static_cast<uint>(dlen
1694 					   - GEO_DATA_HEADER_SIZE),
1695 					   SPDIMS, mbr2);
1696 			new_mbr = reinterpret_cast<rtr_mbr_t*>(mbr2);
1697 
1698 			if (temp_heap) {
1699 				mem_heap_free(temp_heap);
1700 			}
1701 
1702 			if (!MBR_EQUAL_CMP(old_mbr, new_mbr)) {
1703 				return(TRUE);
1704 			} else {
1705 				continue;
1706 			}
1707 		}
1708 
1709 		/* This treatment of column prefix indexes is loosely
1710 		based on row_build_index_entry(). */
1711 
1712 		if (UNIV_LIKELY(ind_field->prefix_len == 0)
1713 		    || dfield_is_null(dfield)) {
1714 			/* do nothing special */
1715 		} else if (ext) {
1716 			/* Silence a compiler warning without
1717 			silencing a Valgrind error. */
1718 			dfield_len = 0;
1719 			UNIV_MEM_INVALID(&dfield_len, sizeof dfield_len);
1720 			/* See if the column is stored externally. */
1721 			buf = row_ext_lookup(ext, col_no, &dfield_len);
1722 
1723 			ut_ad(col->ord_part);
1724 
1725 			if (UNIV_LIKELY_NULL(buf)) {
1726 				if (UNIV_UNLIKELY(buf == field_ref_zero)) {
1727 					/* The externally stored field
1728 					was not written yet. This
1729 					record should only be seen by
1730 					recv_recovery_rollback_active(),
1731 					when the server had crashed before
1732 					storing the field. */
1733 					ut_ad(thr->graph->trx->is_recovered);
1734 					ut_ad(trx_is_recv(thr->graph->trx));
1735 					return(TRUE);
1736 				}
1737 
1738 				goto copy_dfield;
1739 			}
1740 		} else if (dfield_is_ext(dfield)) {
1741 			dfield_len = dfield_get_len(dfield);
1742 			ut_a(dfield_len > BTR_EXTERN_FIELD_REF_SIZE);
1743 			dfield_len -= BTR_EXTERN_FIELD_REF_SIZE;
1744 			ut_a(dict_index_is_clust(index)
1745 			     || ind_field->prefix_len <= dfield_len);
1746 
1747 			buf = static_cast<byte*>(dfield_get_data(dfield));
1748 copy_dfield:
1749 			ut_a(dfield_len > 0);
1750 			dfield_copy(&dfield_ext, dfield);
1751 			dfield_set_data(&dfield_ext, buf, dfield_len);
1752 			dfield = &dfield_ext;
1753 		}
1754 
1755 		if (!dfield_datas_are_binary_equal(
1756 			    dfield, &upd_field->new_val,
1757 			    ind_field->prefix_len)) {
1758 
1759 			return(TRUE);
1760 		}
1761 	}
1762 
1763 	return(FALSE);
1764 }
1765 
1766 /***********************************************************//**
1767 Checks if an update vector changes an ordering field of an index record.
1768 NOTE: we compare the fields as binary strings!
1769 @return TRUE if update vector may change an ordering field in an index
1770 record */
1771 ibool
row_upd_changes_some_index_ord_field_binary(const dict_table_t * table,const upd_t * update)1772 row_upd_changes_some_index_ord_field_binary(
1773 /*========================================*/
1774 	const dict_table_t*	table,	/*!< in: table */
1775 	const upd_t*		update)	/*!< in: update vector for the row */
1776 {
1777 	upd_field_t*	upd_field;
1778 	dict_index_t*	index;
1779 	ulint		i;
1780 
1781 	index = dict_table_get_first_index(table);
1782 
1783 	for (i = 0; i < upd_get_n_fields(update); i++) {
1784 
1785 		upd_field = upd_get_nth_field(update, i);
1786 
1787 		if (upd_fld_is_virtual_col(upd_field)) {
1788 			if (dict_table_get_nth_v_col(index->table,
1789 						     upd_field->field_no)
1790 			    ->m_col.ord_part) {
1791 				return(TRUE);
1792 			}
1793 		} else {
1794 			if (dict_field_get_col(dict_index_get_nth_field(
1795 				index, upd_field->field_no))->ord_part) {
1796 				return(TRUE);
1797 			}
1798 		}
1799 	}
1800 
1801 	return(FALSE);
1802 }
1803 
1804 /***********************************************************//**
1805 Checks if an FTS Doc ID column is affected by an UPDATE.
1806 @return whether the Doc ID column is changed */
1807 bool
row_upd_changes_doc_id(dict_table_t * table,upd_field_t * upd_field)1808 row_upd_changes_doc_id(
1809 /*===================*/
1810 	dict_table_t*	table,		/*!< in: table */
1811 	upd_field_t*	upd_field)	/*!< in: field to check */
1812 {
1813 	ulint		col_no;
1814 	dict_index_t*	clust_index;
1815 	fts_t*		fts = table->fts;
1816 
1817 	clust_index = dict_table_get_first_index(table);
1818 
1819 	/* Convert from index-specific column number to table-global
1820 	column number. */
1821 	col_no = dict_index_get_nth_col_no(clust_index, upd_field->field_no);
1822 
1823 	return(col_no == fts->doc_col);
1824 }
1825 /***********************************************************//**
1826 Checks if an FTS indexed column is affected by an UPDATE.
1827 @return offset within fts_t::indexes if FTS indexed column updated else
1828 ULINT_UNDEFINED */
1829 ulint
row_upd_changes_fts_column(dict_table_t * table,upd_field_t * upd_field)1830 row_upd_changes_fts_column(
1831 /*=======================*/
1832 	dict_table_t*	table,		/*!< in: table */
1833 	upd_field_t*	upd_field)	/*!< in: field to check */
1834 {
1835 	ulint		col_no;
1836 	dict_index_t*	clust_index;
1837 	fts_t*		fts = table->fts;
1838 
1839 	if (upd_fld_is_virtual_col(upd_field)) {
1840 		col_no = upd_field->field_no;
1841 		return(dict_table_is_fts_column(fts->indexes, col_no, true));
1842 	} else {
1843 		clust_index = dict_table_get_first_index(table);
1844 
1845 		/* Convert from index-specific column number to table-global
1846 		column number. */
1847 		col_no = dict_index_get_nth_col_no(clust_index,
1848 						   upd_field->field_no);
1849 		return(dict_table_is_fts_column(fts->indexes, col_no, false));
1850 	}
1851 
1852 }
1853 
1854 /***********************************************************//**
1855 Checks if an update vector changes some of the first ordering fields of an
1856 index record. This is only used in foreign key checks and we can assume
1857 that index does not contain column prefixes.
1858 @return TRUE if changes */
1859 static
1860 ibool
row_upd_changes_first_fields_binary(dtuple_t * entry,dict_index_t * index,const upd_t * update,ulint n)1861 row_upd_changes_first_fields_binary(
1862 /*================================*/
1863 	dtuple_t*	entry,	/*!< in: index entry */
1864 	dict_index_t*	index,	/*!< in: index of entry */
1865 	const upd_t*	update,	/*!< in: update vector for the row */
1866 	ulint		n)	/*!< in: how many first fields to check */
1867 {
1868 	ulint		n_upd_fields;
1869 	ulint		i, j;
1870 	dict_index_t*	clust_index;
1871 
1872 	ut_ad(update && index);
1873 	ut_ad(n <= dict_index_get_n_fields(index));
1874 
1875 	n_upd_fields = upd_get_n_fields(update);
1876 	clust_index = dict_table_get_first_index(index->table);
1877 
1878 	for (i = 0; i < n; i++) {
1879 
1880 		const dict_field_t*	ind_field;
1881 		const dict_col_t*	col;
1882 		ulint			col_pos;
1883 
1884 		ind_field = dict_index_get_nth_field(index, i);
1885 		col = dict_field_get_col(ind_field);
1886 		col_pos = dict_col_get_clust_pos(col, clust_index);
1887 
1888 		ut_a(ind_field->prefix_len == 0);
1889 
1890 		for (j = 0; j < n_upd_fields; j++) {
1891 
1892 			upd_field_t*	upd_field
1893 				= upd_get_nth_field(update, j);
1894 
1895 			if (col_pos == upd_field->field_no
1896 			    && !dfield_datas_are_binary_equal(
1897 				    dtuple_get_nth_field(entry, i),
1898 				    &upd_field->new_val, 0)) {
1899 
1900 				return(TRUE);
1901 			}
1902 		}
1903 	}
1904 
1905 	return(FALSE);
1906 }
1907 
1908 /*********************************************************************//**
1909 Copies the column values from a record. */
1910 UNIV_INLINE
1911 void
row_upd_copy_columns(rec_t * rec,const ulint * offsets,sym_node_t * column)1912 row_upd_copy_columns(
1913 /*=================*/
1914 	rec_t*		rec,	/*!< in: record in a clustered index */
1915 	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
1916 	sym_node_t*	column)	/*!< in: first column in a column list, or
1917 				NULL */
1918 {
1919 	byte*	data;
1920 	ulint	len;
1921 
1922 	while (column) {
1923 		data = rec_get_nth_field(rec, offsets,
1924 					 column->field_nos[SYM_CLUST_FIELD_NO],
1925 					 &len);
1926 		eval_node_copy_and_alloc_val(column, data, len);
1927 
1928 		column = UT_LIST_GET_NEXT(col_var_list, column);
1929 	}
1930 }
1931 
1932 /*********************************************************************//**
1933 Calculates the new values for fields to update. Note that row_upd_copy_columns
1934 must have been called first. */
1935 UNIV_INLINE
1936 void
row_upd_eval_new_vals(upd_t * update)1937 row_upd_eval_new_vals(
1938 /*==================*/
1939 	upd_t*	update)	/*!< in/out: update vector */
1940 {
1941 	que_node_t*	exp;
1942 	upd_field_t*	upd_field;
1943 	ulint		n_fields;
1944 	ulint		i;
1945 
1946 	n_fields = upd_get_n_fields(update);
1947 
1948 	for (i = 0; i < n_fields; i++) {
1949 		upd_field = upd_get_nth_field(update, i);
1950 
1951 		exp = upd_field->exp;
1952 
1953 		eval_exp(exp);
1954 
1955 		dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
1956 	}
1957 }
1958 
1959 /** Stores to the heap the virtual columns that need for any indexes
1960 @param[in,out]	node		row update node
1961 @param[in]	update		an update vector if it is update
1962 @param[in]	thd		mysql thread handle
1963 @param[in,out]	prebuilt	NULL, or a prebuilt object: used to extract
1964 				mysql table object when user thread invokes
1965 				dml and for compress heap */
1966 static
1967 void
row_upd_store_v_row(upd_node_t * node,const upd_t * update,THD * thd,row_prebuilt_t * prebuilt)1968 row_upd_store_v_row(
1969 	upd_node_t*	node,
1970 	const upd_t*	update,
1971 	THD*		thd,
1972 	row_prebuilt_t*	prebuilt)
1973 {
1974 	mem_heap_t*	heap = NULL;
1975 	dict_index_t*	index = dict_table_get_first_index(node->table);
1976 	TABLE*		mysql_table =
1977 		prebuilt ? prebuilt->m_mysql_table : NULL;
1978 
1979 	for (ulint col_no = 0; col_no < dict_table_get_n_v_cols(node->table);
1980 	     col_no++) {
1981 
1982 		const dict_v_col_t*     col
1983 			= dict_table_get_nth_v_col(node->table, col_no);
1984 
1985 		if (col->m_col.ord_part) {
1986 			dfield_t*	dfield
1987 				= dtuple_get_nth_v_field(node->row, col_no);
1988 			ulint		n_upd
1989 				= update ? upd_get_n_fields(update) : 0;
1990 			ulint		i = 0;
1991 
1992 			/* Check if the value is already in update vector */
1993 			for (i = 0; i < n_upd; i++) {
1994 				const upd_field_t*      upd_field
1995 					= upd_get_nth_field(update, i);
1996 				if (!(upd_field->new_val.type.prtype
1997 				      & DATA_VIRTUAL)
1998 				    || upd_field->field_no != col->v_pos) {
1999 					continue;
2000 				}
2001 
2002 				dfield_copy_data(dfield, upd_field->old_v_val);
2003 				dfield_dup(dfield, node->heap);
2004 				break;
2005 			}
2006 
2007 			/* Not updated */
2008 			if (i >= n_upd) {
2009 				/* If this is an update, then the value
2010 				should be in update->old_vrow */
2011 				if (update) {
2012 					if (update->old_vrow == NULL) {
2013 						/* This only happens in
2014 						cascade update. And virtual
2015 						column can't be affected,
2016 						so it is Ok to set it to NULL */
2017 						dfield_set_null(dfield);
2018 					} else {
2019 						dfield_t*       vfield
2020 							= dtuple_get_nth_v_field(
2021 								update->old_vrow,
2022 								col_no);
2023 						dfield_copy_data(dfield, vfield);
2024 						dfield_dup(dfield, node->heap);
2025 						if (dfield_is_null(dfield)) {
2026 						  innobase_get_computed_value(
2027 							node->row, col, index,
2028 							&heap, node->heap, NULL,
2029 							thd, mysql_table, NULL,
2030 							NULL, NULL, prebuilt);
2031 						}
2032 					}
2033 				} else {
2034 					/* Need to compute, this happens when
2035 					deleting row */
2036 					innobase_get_computed_value(
2037 						node->row, col, index,
2038 						&heap, node->heap, NULL,
2039 						thd, mysql_table, NULL,
2040 						NULL, NULL, prebuilt);
2041 				}
2042 			}
2043 		}
2044 	}
2045 
2046 	if (heap) {
2047 		mem_heap_free(heap);
2048 	}
2049 }
2050 
2051 /** Stores to the heap the row on which the node->pcur is positioned.
2052 @param[in]	node		row update node
2053 @param[in]	thd		mysql thread handle
2054 @param[in,out]	prebuilt	NULL, or a prebuilt object: used to extract
2055 				mysql table object when user thread invokes
2056 				dml and for compress heap */
2057 void
row_upd_store_row(upd_node_t * node,THD * thd,row_prebuilt_t * prebuilt)2058 row_upd_store_row(
2059 	upd_node_t*	node,
2060 	THD*		thd,
2061 	row_prebuilt_t*	prebuilt)
2062 {
2063 	dict_index_t*	clust_index;
2064 	rec_t*		rec;
2065 	mem_heap_t*	heap		= NULL;
2066 	row_ext_t**	ext;
2067 	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
2068 	const ulint*	offsets;
2069 	rec_offs_init(offsets_);
2070 
2071 	ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
2072 
2073 	if (node->row != NULL) {
2074 		mem_heap_empty(node->heap);
2075 	}
2076 
2077 	clust_index = dict_table_get_first_index(node->table);
2078 
2079 	rec = btr_pcur_get_rec(node->pcur);
2080 
2081 	offsets = rec_get_offsets(rec, clust_index, offsets_,
2082 				  ULINT_UNDEFINED, &heap);
2083 
2084 	if (dict_table_get_format(node->table) >= UNIV_FORMAT_B) {
2085 		/* In DYNAMIC or COMPRESSED format, there is no prefix
2086 		of externally stored columns in the clustered index
2087 		record. Build a cache of column prefixes. */
2088 		ext = &node->ext;
2089 	} else {
2090 		/* REDUNDANT and COMPACT formats store a local
2091 		768-byte prefix of each externally stored column.
2092 		No cache is needed. */
2093 		ext = NULL;
2094 		node->ext = NULL;
2095 	}
2096 
2097 	node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
2098 			      NULL, NULL, NULL, ext, node->heap);
2099 
2100 	if (node->table->n_v_cols) {
2101 		row_upd_store_v_row(node, node->is_delete ? NULL : node->update,
2102 				    thd, prebuilt);
2103 	}
2104 
2105 	if (node->is_delete) {
2106 		node->upd_row = NULL;
2107 		node->upd_ext = NULL;
2108 	} else {
2109 		node->upd_row = dtuple_copy(node->row, node->heap);
2110 		row_upd_replace(node->upd_row, &node->upd_ext,
2111 				clust_index, node->update, node->heap);
2112 	}
2113 
2114 	if (UNIV_LIKELY_NULL(heap)) {
2115 		mem_heap_free(heap);
2116 	}
2117 }
2118 
2119 /***********************************************************//**
2120 Print a MBR data from disk */
2121 static
2122 void
srv_mbr_print(const byte * data)2123 srv_mbr_print(const byte* data)
2124 {
2125         double a, b, c, d;
2126         a = mach_double_read(data);
2127         data += sizeof(double);
2128         b = mach_double_read(data);
2129         data += sizeof(double);
2130         c = mach_double_read(data);
2131         data += sizeof(double);
2132         d = mach_double_read(data);
2133 
2134         ib::info() << "GIS MBR INFO: " << a << " and " << b << ", " << c
2135 		<< ", " << d << "\n";
2136 }
2137 
2138 
2139 /***********************************************************//**
2140 Updates a secondary index entry of a row.
2141 @return DB_SUCCESS if operation successfully completed, else error
2142 code or DB_LOCK_WAIT */
2143 static MY_ATTRIBUTE((warn_unused_result))
2144 dberr_t
row_upd_sec_index_entry(upd_node_t * node,que_thr_t * thr)2145 row_upd_sec_index_entry(
2146 /*====================*/
2147 	upd_node_t*	node,	/*!< in: row update node */
2148 	que_thr_t*	thr)	/*!< in: query thread */
2149 {
2150 	mtr_t			mtr;
2151 	const rec_t*		rec;
2152 	btr_pcur_t		pcur;
2153 	mem_heap_t*		heap;
2154 	dtuple_t*		entry;
2155 	dict_index_t*		index;
2156 	btr_cur_t*		btr_cur;
2157 	ibool			referenced;
2158 	dberr_t			err	= DB_SUCCESS;
2159 	trx_t*			trx	= thr_get_trx(thr);
2160 	ulint			mode;
2161 	ulint			flags = 0;
2162 	enum row_search_result	search_result;
2163 
2164 	ut_ad(trx->id != 0);
2165 
2166 	index = node->index;
2167 
2168 	referenced = row_upd_index_is_referenced(index, trx);
2169 
2170 	heap = mem_heap_create(1024);
2171 
2172 	/* Build old index entry */
2173 	entry = row_build_index_entry(node->row, node->ext, index, heap);
2174 	ut_a(entry);
2175 
2176 	if (!dict_table_is_intrinsic(index->table)) {
2177 		log_free_check();
2178 	}
2179 
2180 	DEBUG_SYNC_C_IF_THD(trx->mysql_thd,
2181 			    "before_row_upd_sec_index_entry");
2182 
2183 	mtr_start(&mtr);
2184 	mtr.set_named_space(index->space);
2185 
2186 	/* Disable REDO logging as lifetime of temp-tables is limited to
2187 	server or connection lifetime and so REDO information is not needed
2188 	on restart for recovery.
2189 	Disable locking as temp-tables are not shared across connection. */
2190 	if (dict_table_is_temporary(index->table)) {
2191 		flags |= BTR_NO_LOCKING_FLAG;
2192 		mtr.set_log_mode(MTR_LOG_NO_REDO);
2193 
2194 		if (dict_table_is_intrinsic(index->table)) {
2195 			flags |= BTR_NO_UNDO_LOG_FLAG;
2196 		}
2197 	}
2198 
2199 	if (!index->is_committed()) {
2200 		/* The index->online_status may change if the index is
2201 		or was being created online, but not committed yet. It
2202 		is protected by index->lock. */
2203 
2204 		mtr_s_lock(dict_index_get_lock(index), &mtr);
2205 
2206 		switch (dict_index_get_online_status(index)) {
2207 		case ONLINE_INDEX_COMPLETE:
2208 			/* This is a normal index. Do not log anything.
2209 			Perform the update on the index tree directly. */
2210 			break;
2211 		case ONLINE_INDEX_CREATION:
2212 			/* Log a DELETE and optionally INSERT. */
2213 			row_log_online_op(index, entry, 0);
2214 
2215 			if (!node->is_delete) {
2216 				mem_heap_empty(heap);
2217 				entry = row_build_index_entry(
2218 					node->upd_row, node->upd_ext,
2219 					index, heap);
2220 				ut_a(entry);
2221 				row_log_online_op(index, entry, trx->id);
2222 			}
2223 			/* fall through */
2224 		case ONLINE_INDEX_ABORTED:
2225 		case ONLINE_INDEX_ABORTED_DROPPED:
2226 			mtr_commit(&mtr);
2227 			goto func_exit;
2228 		}
2229 
2230 		/* We can only buffer delete-mark operations if there
2231 		are no foreign key constraints referring to the index.
2232 		Change buffering is disabled for temporary tables and
2233 		spatial index. */
2234 		mode = (referenced || dict_table_is_temporary(index->table)
2235 			|| dict_index_is_spatial(index))
2236 			? BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
2237 			: BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
2238 			| BTR_DELETE_MARK;
2239 	} else {
2240 		/* For secondary indexes,
2241 		index->online_status==ONLINE_INDEX_COMPLETE if
2242 		index->is_committed(). */
2243 		ut_ad(!dict_index_is_online_ddl(index));
2244 
2245 		/* We can only buffer delete-mark operations if there
2246 		are no foreign key constraints referring to the index.
2247 		Change buffering is disabled for temporary tables and
2248 		spatial index. */
2249 		mode = (referenced || dict_table_is_temporary(index->table)
2250 			|| dict_index_is_spatial(index))
2251 			? BTR_MODIFY_LEAF
2252 			: BTR_MODIFY_LEAF | BTR_DELETE_MARK;
2253 	}
2254 
2255 	if (dict_index_is_spatial(index)) {
2256 		ut_ad(mode & BTR_MODIFY_LEAF);
2257 		mode |= BTR_RTREE_DELETE_MARK;
2258 	}
2259 
2260 	/* Set the query thread, so that ibuf_insert_low() will be
2261 	able to invoke thd_get_trx(). */
2262 	btr_pcur_get_btr_cur(&pcur)->thr = thr;
2263 
2264 	search_result = row_search_index_entry(index, entry, mode,
2265 					       &pcur, &mtr);
2266 
2267 	btr_cur = btr_pcur_get_btr_cur(&pcur);
2268 
2269 	rec = btr_cur_get_rec(btr_cur);
2270 
2271 	switch (search_result) {
2272 	case ROW_NOT_DELETED_REF:	/* should only occur for BTR_DELETE */
2273 		ut_error;
2274 		break;
2275 	case ROW_BUFFERED:
2276 		/* Entry was delete marked already. */
2277 		break;
2278 
2279 	case ROW_NOT_FOUND:
2280 		if (!index->is_committed()) {
2281 			/* When online CREATE INDEX copied the update
2282 			that we already made to the clustered index,
2283 			and completed the secondary index creation
2284 			before we got here, the old secondary index
2285 			record would not exist. The CREATE INDEX
2286 			should be waiting for a MySQL meta-data lock
2287 			upgrade at least until this UPDATE returns.
2288 			After that point, set_committed(true) would be
2289 			invoked by commit_inplace_alter_table(). */
2290 			break;
2291 		}
2292 
2293 		if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) {
2294 			/* We found the record, but a delete marked */
2295 			break;
2296 		}
2297 
2298 		ib::error()
2299 			<< "Record in index " << index->name
2300 			<< " of table " << index->table->name
2301 			<< " was not found on update: " << *entry
2302 			<< " at: " << rec_index_print(rec, index);
2303 		srv_mbr_print((unsigned char*)entry->fields[0].data);
2304 #ifdef UNIV_DEBUG
2305 		mtr_commit(&mtr);
2306 		mtr_start(&mtr);
2307 		ut_ad(btr_validate_index(index, 0, false));
2308 		ut_ad(0);
2309 #endif /* UNIV_DEBUG */
2310 		break;
2311 	case ROW_FOUND:
2312 		ut_ad(err == DB_SUCCESS);
2313 
2314 		/* Delete mark the old index record; it can already be
2315 		delete marked if we return after a lock wait in
2316 		row_ins_sec_index_entry() below */
2317 		if (!rec_get_deleted_flag(
2318 			    rec, dict_table_is_comp(index->table))) {
2319 			err = btr_cur_del_mark_set_sec_rec(
2320 				flags, btr_cur, TRUE, thr, &mtr);
2321 			if (err != DB_SUCCESS) {
2322 				break;
2323 			}
2324 		}
2325 
2326 		ut_ad(err == DB_SUCCESS);
2327 
2328 		if (referenced) {
2329 
2330 			ulint*	offsets;
2331 
2332 			offsets = rec_get_offsets(
2333 				rec, index, NULL, ULINT_UNDEFINED,
2334 				&heap);
2335 
2336 			/* NOTE that the following call loses
2337 			the position of pcur ! */
2338 			err = row_upd_check_references_constraints(
2339 				node, &pcur, index->table,
2340 				index, offsets, thr, &mtr);
2341 		}
2342 		break;
2343 	}
2344 
2345 	btr_pcur_close(&pcur);
2346 	mtr_commit(&mtr);
2347 
2348 	if (node->is_delete || err != DB_SUCCESS) {
2349 
2350 		goto func_exit;
2351 	}
2352 
2353 	mem_heap_empty(heap);
2354 
2355 	/* Build a new index entry */
2356 	entry = row_build_index_entry(node->upd_row, node->upd_ext,
2357 				      index, heap);
2358 	ut_a(entry);
2359 
2360 	/* Insert new index entry */
2361 	err = row_ins_sec_index_entry(index, entry, thr, false);
2362 
2363 func_exit:
2364 	mem_heap_free(heap);
2365 
2366 	return(err);
2367 }
2368 
2369 /***********************************************************//**
2370 Updates the secondary index record if it is changed in the row update or
2371 deletes it if this is a delete.
2372 @return DB_SUCCESS if operation successfully completed, else error
2373 code or DB_LOCK_WAIT */
2374 static MY_ATTRIBUTE((warn_unused_result))
2375 dberr_t
row_upd_sec_step(upd_node_t * node,que_thr_t * thr)2376 row_upd_sec_step(
2377 /*=============*/
2378 	upd_node_t*	node,	/*!< in: row update node */
2379 	que_thr_t*	thr)	/*!< in: query thread */
2380 {
2381 	ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
2382 	      || (node->state == UPD_NODE_UPDATE_SOME_SEC));
2383 	ut_ad(!dict_index_is_clust(node->index));
2384 
2385 	if (node->state == UPD_NODE_UPDATE_ALL_SEC
2386 	    || row_upd_changes_ord_field_binary(node->index, node->update,
2387 						thr, node->row, node->ext)) {
2388 		return(row_upd_sec_index_entry(node, thr));
2389 	}
2390 
2391 	return(DB_SUCCESS);
2392 }
2393 
2394 #ifdef UNIV_DEBUG
2395 # define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update)	\
2396 	row_upd_clust_rec_by_insert_inherit_func(rec,offsets,entry,update)
2397 #else /* UNIV_DEBUG */
2398 # define row_upd_clust_rec_by_insert_inherit(rec,offsets,entry,update)	\
2399 	row_upd_clust_rec_by_insert_inherit_func(rec,entry,update)
2400 #endif /* UNIV_DEBUG */
2401 /*******************************************************************//**
2402 Mark non-updated off-page columns inherited when the primary key is
2403 updated. We must mark them as inherited in entry, so that they are not
2404 freed in a rollback. A limited version of this function used to be
2405 called btr_cur_mark_dtuple_inherited_extern().
2406 @return whether any columns were inherited */
2407 static
2408 bool
row_upd_clust_rec_by_insert_inherit_func(const rec_t * rec,const ulint * offsets,dtuple_t * entry,const upd_t * update)2409 row_upd_clust_rec_by_insert_inherit_func(
2410 /*=====================================*/
2411 	const rec_t*	rec,	/*!< in: old record, or NULL */
2412 #ifdef UNIV_DEBUG
2413 	const ulint*	offsets,/*!< in: rec_get_offsets(rec), or NULL */
2414 #endif /* UNIV_DEBUG */
2415 	dtuple_t*	entry,	/*!< in/out: updated entry to be
2416 				inserted into the clustered index */
2417 	const upd_t*	update)	/*!< in: update vector */
2418 {
2419 	bool	inherit	= false;
2420 	ulint	i;
2421 
2422 	ut_ad(!rec == !offsets);
2423 	ut_ad(!rec || rec_offs_any_extern(offsets));
2424 
2425 	for (i = 0; i < dtuple_get_n_fields(entry); i++) {
2426 		dfield_t*	dfield	= dtuple_get_nth_field(entry, i);
2427 		byte*		data;
2428 		ulint		len;
2429 
2430 		ut_ad(!offsets
2431 		      || !rec_offs_nth_extern(offsets, i)
2432 		      == !dfield_is_ext(dfield)
2433 		      || upd_get_field_by_field_no(update, i, false));
2434 		if (!dfield_is_ext(dfield)
2435 		    || upd_get_field_by_field_no(update, i, false)) {
2436 			continue;
2437 		}
2438 
2439 #ifdef UNIV_DEBUG
2440 		if (UNIV_LIKELY(rec != NULL)) {
2441 			const byte* rec_data
2442 				= rec_get_nth_field(rec, offsets, i, &len);
2443 			ut_ad(len == dfield_get_len(dfield));
2444 			ut_ad(len != UNIV_SQL_NULL);
2445 			ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE);
2446 
2447 			rec_data += len - BTR_EXTERN_FIELD_REF_SIZE;
2448 
2449 			/* The pointer must not be zero. */
2450 			ut_ad(memcmp(rec_data, field_ref_zero,
2451 				     BTR_EXTERN_FIELD_REF_SIZE));
2452 			/* The BLOB must be owned. */
2453 			ut_ad(!(rec_data[BTR_EXTERN_LEN]
2454 				& BTR_EXTERN_OWNER_FLAG));
2455 		}
2456 #endif /* UNIV_DEBUG */
2457 
2458 		len = dfield_get_len(dfield);
2459 		ut_a(len != UNIV_SQL_NULL);
2460 		ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
2461 
2462 		data = static_cast<byte*>(dfield_get_data(dfield));
2463 
2464 		data += len - BTR_EXTERN_FIELD_REF_SIZE;
2465 		/* The pointer must not be zero. */
2466 		ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE));
2467 
2468 		/* The BLOB must be owned, unless we are resuming from
2469 		a lock wait and we already had disowned the BLOB. */
2470 		ut_a(rec == NULL
2471 		     || !(data[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG));
2472 		data[BTR_EXTERN_LEN] &= ~BTR_EXTERN_OWNER_FLAG;
2473 		data[BTR_EXTERN_LEN] |= BTR_EXTERN_INHERITED_FLAG;
2474 		/* The BTR_EXTERN_INHERITED_FLAG only matters in
2475 		rollback of a fresh insert (insert_undo log).
2476 		Purge (operating on update_undo log) will always free
2477 		the extern fields of a delete-marked row. */
2478 
2479 		inherit = true;
2480 	}
2481 
2482 	return(inherit);
2483 }
2484 
2485 /***********************************************************//**
2486 Marks the clustered index record deleted and inserts the updated version
2487 of the record to the index. This function should be used when the ordering
2488 fields of the clustered index record change. This should be quite rare in
2489 database applications.
2490 @return DB_SUCCESS if operation successfully completed, else error
2491 code or DB_LOCK_WAIT */
2492 static MY_ATTRIBUTE((warn_unused_result))
2493 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)2494 row_upd_clust_rec_by_insert(
2495 /*========================*/
2496 	ulint		flags,  /*!< in: undo logging and locking flags */
2497 	upd_node_t*	node,	/*!< in/out: row update node */
2498 	dict_index_t*	index,	/*!< in: clustered index of the record */
2499 	que_thr_t*	thr,	/*!< in: query thread */
2500 	ibool		referenced,/*!< in: TRUE if index may be referenced in
2501 				a foreign key constraint */
2502 	mtr_t*		mtr)	/*!< in/out: mtr; gets committed here */
2503 {
2504 	mem_heap_t*	heap;
2505 	btr_pcur_t*	pcur;
2506 	btr_cur_t*	btr_cur;
2507 	trx_t*		trx;
2508 	dict_table_t*	table;
2509 	dtuple_t*	entry;
2510 	dberr_t		err;
2511 	rec_t*		rec;
2512 	ulint*		offsets			= NULL;
2513 
2514 	ut_ad(node);
2515 	ut_ad(dict_index_is_clust(index));
2516 
2517 	trx = thr_get_trx(thr);
2518 	table = node->table;
2519 	pcur = node->pcur;
2520 	btr_cur	= btr_pcur_get_btr_cur(pcur);
2521 
2522 	heap = mem_heap_create(1000);
2523 
2524 	entry = row_build_index_entry_low(node->upd_row, node->upd_ext,
2525 					  index, heap, ROW_BUILD_FOR_INSERT);
2526 	ut_ad(dtuple_get_info_bits(entry) == 0);
2527 
2528 	row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
2529 
2530 	switch (node->state) {
2531 	default:
2532 		ut_error;
2533 	case UPD_NODE_INSERT_CLUSTERED:
2534 		/* A lock wait occurred in row_ins_clust_index_entry() in
2535 		the previous invocation of this function. */
2536 		row_upd_clust_rec_by_insert_inherit(
2537 			NULL, NULL, entry, node->update);
2538 		break;
2539 	case UPD_NODE_UPDATE_CLUSTERED:
2540 		/* This is the first invocation of the function where
2541 		we update the primary key.  Delete-mark the old record
2542 		in the clustered index and prepare to insert a new entry. */
2543 		rec = btr_cur_get_rec(btr_cur);
2544 		offsets = rec_get_offsets(rec, index, NULL,
2545 					  ULINT_UNDEFINED, &heap);
2546 		ut_ad(page_rec_is_user_rec(rec));
2547 
2548 		if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))) {
2549 			/* If the clustered index record is already delete
2550 			marked, then we are here after a DB_LOCK_WAIT.
2551 			Skip delete marking clustered index and disowning
2552 			its blobs. */
2553 			ut_ad(rec_get_trx_id(rec, index) == trx->id);
2554 			ut_ad(!trx_undo_roll_ptr_is_insert(
2555 			              row_get_rec_roll_ptr(rec, index,
2556 							   offsets)));
2557 			goto check_fk;
2558 		}
2559 
2560 		err = btr_cur_del_mark_set_clust_rec(
2561 			flags, btr_cur_get_block(btr_cur), rec, index, offsets,
2562 			thr, node->row, mtr);
2563 		if (err != DB_SUCCESS) {
2564 err_exit:
2565 			mtr_commit(mtr);
2566 			mem_heap_free(heap);
2567 			return(err);
2568 		}
2569 
2570 		/* If the the new row inherits externally stored
2571 		fields (off-page columns a.k.a. BLOBs) from the
2572 		delete-marked old record, mark them disowned by the
2573 		old record and owned by the new entry. */
2574 
2575 		if (rec_offs_any_extern(offsets)) {
2576 			if (row_upd_clust_rec_by_insert_inherit(
2577 				    rec, offsets, entry, node->update)) {
2578 				/* The blobs are disowned here, expecting the
2579 				insert down below to inherit them.  But if the
2580 				insert fails, then this disown will be undone
2581 				when the operation is rolled back. */
2582 				btr_cur_disown_inherited_fields(
2583 					btr_cur_get_page_zip(btr_cur),
2584 					rec, index, offsets, node->update,
2585 					mtr);
2586 			}
2587 		}
2588 check_fk:
2589 		if (referenced) {
2590 			/* NOTE that the following call loses
2591 			the position of pcur ! */
2592 
2593 			err = row_upd_check_references_constraints(
2594 				node, pcur, table, index, offsets, thr, mtr);
2595 
2596 			if (err != DB_SUCCESS) {
2597 				goto err_exit;
2598 			}
2599 		}
2600 	}
2601 
2602 	mtr_commit(mtr);
2603 
2604 	err = row_ins_clust_index_entry(
2605 		index, entry, thr,
2606 		entry->get_n_ext(), false);
2607 	node->state = UPD_NODE_INSERT_CLUSTERED;
2608 
2609 	mem_heap_free(heap);
2610 
2611 	return(err);
2612 }
2613 
2614 /***********************************************************//**
2615 Updates a clustered index record of a row when the ordering fields do
2616 not change.
2617 @return DB_SUCCESS if operation successfully completed, else error
2618 code or DB_LOCK_WAIT */
2619 static MY_ATTRIBUTE((warn_unused_result))
2620 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)2621 row_upd_clust_rec(
2622 /*==============*/
2623 	ulint		flags,  /*!< in: undo logging and locking flags */
2624 	upd_node_t*	node,	/*!< in: row update node */
2625 	dict_index_t*	index,	/*!< in: clustered index */
2626 	ulint*		offsets,/*!< in: rec_get_offsets() on node->pcur */
2627 	mem_heap_t**	offsets_heap,
2628 				/*!< in/out: memory heap, can be emptied */
2629 	que_thr_t*	thr,	/*!< in: query thread */
2630 	mtr_t*		mtr)	/*!< in: mtr; gets committed here */
2631 {
2632 	mem_heap_t*	heap		= NULL;
2633 	big_rec_t*	big_rec		= NULL;
2634 	btr_pcur_t*	pcur;
2635 	btr_cur_t*	btr_cur;
2636 	dberr_t		err;
2637 	const dtuple_t*	rebuilt_old_pk	= NULL;
2638 
2639 	ut_ad(node);
2640 	ut_ad(dict_index_is_clust(index));
2641 	ut_ad(!thr_get_trx(thr)->in_rollback);
2642 
2643 	pcur = node->pcur;
2644 	btr_cur = btr_pcur_get_btr_cur(pcur);
2645 
2646 	ut_ad(btr_cur_get_index(btr_cur) == index);
2647 	ut_ad(!rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
2648 				    dict_table_is_comp(index->table)));
2649 	ut_ad(rec_offs_validate(btr_cur_get_rec(btr_cur), index, offsets));
2650 
2651 	if (dict_index_is_online_ddl(index)) {
2652 		rebuilt_old_pk = row_log_table_get_pk(
2653 			btr_cur_get_rec(btr_cur), index, offsets, NULL, &heap);
2654 	}
2655 
2656 	/* Try optimistic updating of the record, keeping changes within
2657 	the page; we do not check locks because we assume the x-lock on the
2658 	record to update */
2659 
2660 	if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
2661 		err = btr_cur_update_in_place(
2662 			flags | BTR_NO_LOCKING_FLAG, btr_cur,
2663 			offsets, node->update,
2664 			node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
2665 	} else {
2666 		err = btr_cur_optimistic_update(
2667 			flags | BTR_NO_LOCKING_FLAG, btr_cur,
2668 			&offsets, offsets_heap, node->update,
2669 			node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
2670 	}
2671 
2672 	if (err == DB_SUCCESS) {
2673 		goto success;
2674 	}
2675 
2676 	mtr_commit(mtr);
2677 
2678 	if (buf_LRU_buf_pool_running_out()) {
2679 
2680 		err = DB_LOCK_TABLE_FULL;
2681 		goto func_exit;
2682 	}
2683 	/* We may have to modify the tree structure: do a pessimistic descent
2684 	down the index tree */
2685 
2686 	mtr_start(mtr);
2687 	mtr->set_named_space(index->space);
2688 
2689 	/* Disable REDO logging as lifetime of temp-tables is limited to
2690 	server or connection lifetime and so REDO information is not needed
2691 	on restart for recovery.
2692 	Disable locking as temp-tables are not shared across connection. */
2693 	if (dict_table_is_temporary(index->table)) {
2694 		flags |= BTR_NO_LOCKING_FLAG;
2695 		mtr->set_log_mode(MTR_LOG_NO_REDO);
2696 
2697 		if (dict_table_is_intrinsic(index->table)) {
2698 			flags |= BTR_NO_UNDO_LOG_FLAG;
2699 		}
2700 	}
2701 
2702 	/* NOTE: this transaction has an s-lock or x-lock on the record and
2703 	therefore other transactions cannot modify the record when we have no
2704 	latch on the page. In addition, we assume that other query threads of
2705 	the same transaction do not modify the record in the meantime.
2706 	Therefore we can assert that the restoration of the cursor succeeds. */
2707 
2708 	ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
2709 
2710 	ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
2711 				    dict_table_is_comp(index->table)));
2712 
2713 	if (!heap) {
2714 		heap = mem_heap_create(1024);
2715 	}
2716 
2717 	err = btr_cur_pessimistic_update(
2718 		flags | BTR_NO_LOCKING_FLAG | BTR_KEEP_POS_FLAG, btr_cur,
2719 		&offsets, offsets_heap, heap, &big_rec,
2720 		node->update, node->cmpl_info,
2721 		thr, thr_get_trx(thr)->id, mtr);
2722 	if (big_rec) {
2723 		ut_a(err == DB_SUCCESS);
2724 
2725 		DEBUG_SYNC_C("before_row_upd_extern");
2726 		err = btr_store_big_rec_extern_fields(
2727 			pcur, node->update, offsets, big_rec, mtr,
2728 			BTR_STORE_UPDATE);
2729 		DEBUG_SYNC_C("after_row_upd_extern");
2730 	}
2731 
2732 	if (err == DB_SUCCESS) {
2733 success:
2734 		if (dict_index_is_online_ddl(index)) {
2735 			dtuple_t*	new_v_row = NULL;
2736 			dtuple_t*	old_v_row = NULL;
2737 
2738 			if (!(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
2739 				new_v_row = node->upd_row;
2740 				old_v_row = node->update->old_vrow;
2741 			}
2742 
2743 			row_log_table_update(
2744 				btr_cur_get_rec(btr_cur),
2745 				index, offsets, rebuilt_old_pk, new_v_row,
2746 				old_v_row);
2747 		}
2748 	}
2749 
2750 	mtr_commit(mtr);
2751 func_exit:
2752 	if (heap) {
2753 		mem_heap_free(heap);
2754 	}
2755 
2756 	if (big_rec) {
2757 		dtuple_big_rec_free(big_rec);
2758 	}
2759 
2760 	return(err);
2761 }
2762 
2763 /***********************************************************//**
2764 Delete marks a clustered index record.
2765 @return DB_SUCCESS if operation successfully completed, else error code */
2766 static MY_ATTRIBUTE((warn_unused_result))
2767 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)2768 row_upd_del_mark_clust_rec(
2769 /*=======================*/
2770 	ulint		flags,  /*!< in: undo logging and locking flags */
2771 	upd_node_t*	node,	/*!< in: row update node */
2772 	dict_index_t*	index,	/*!< in: clustered index */
2773 	ulint*		offsets,/*!< in/out: rec_get_offsets() for the
2774 				record under the cursor */
2775 	que_thr_t*	thr,	/*!< in: query thread */
2776 	ibool		referenced,
2777 				/*!< in: TRUE if index may be referenced in
2778 				a foreign key constraint */
2779 	mtr_t*		mtr)	/*!< in: mtr; gets committed here */
2780 {
2781 	btr_pcur_t*	pcur;
2782 	btr_cur_t*	btr_cur;
2783 	dberr_t		err;
2784 
2785 	ut_ad(node);
2786 	ut_ad(dict_index_is_clust(index));
2787 	ut_ad(node->is_delete);
2788 
2789 	pcur = node->pcur;
2790 	btr_cur = btr_pcur_get_btr_cur(pcur);
2791 
2792 	/* Store row because we have to build also the secondary index
2793 	entries */
2794 
2795 	row_upd_store_row(node, thr_get_trx(thr)->mysql_thd, thr->prebuilt);
2796 
2797 	/* Mark the clustered index record deleted; we do not have to check
2798 	locks, because we assume that we have an x-lock on the record */
2799 
2800 	err = btr_cur_del_mark_set_clust_rec(
2801 		flags, btr_cur_get_block(btr_cur), btr_cur_get_rec(btr_cur),
2802 		index, offsets, thr, node->row, mtr);
2803 	if (err == DB_SUCCESS && referenced) {
2804 		/* NOTE that the following call loses the position of pcur ! */
2805 
2806 		err = row_upd_check_references_constraints(
2807 			node, pcur, index->table, index, offsets, thr, mtr);
2808 	}
2809 
2810 	mtr_commit(mtr);
2811 
2812 	return(err);
2813 }
2814 
2815 /***********************************************************//**
2816 Updates the clustered index record.
2817 @return DB_SUCCESS if operation successfully completed, DB_LOCK_WAIT
2818 in case of a lock wait, else error code */
2819 static MY_ATTRIBUTE((warn_unused_result))
2820 dberr_t
row_upd_clust_step(upd_node_t * node,que_thr_t * thr)2821 row_upd_clust_step(
2822 /*===============*/
2823 	upd_node_t*	node,	/*!< in: row update node */
2824 	que_thr_t*	thr)	/*!< in: query thread */
2825 {
2826 	dict_index_t*	index;
2827 	btr_pcur_t*	pcur;
2828 	ibool		success;
2829 	dberr_t		err;
2830 	mtr_t		mtr;
2831 	rec_t*		rec;
2832 	mem_heap_t*	heap	= NULL;
2833 	ulint		offsets_[REC_OFFS_NORMAL_SIZE];
2834 	ulint*		offsets;
2835 	ibool		referenced;
2836 	ulint		flags	= 0;
2837 	trx_t*		trx = thr_get_trx(thr);
2838 	rec_offs_init(offsets_);
2839 
2840 	index = dict_table_get_first_index(node->table);
2841 
2842 	referenced = row_upd_index_is_referenced(index, trx);
2843 
2844 	pcur = node->pcur;
2845 
2846 	/* We have to restore the cursor to its position */
2847 
2848 	mtr_start(&mtr);
2849 	mtr.set_named_space(index->space);
2850 
2851 	/* Disable REDO logging as lifetime of temp-tables is limited to
2852 	server or connection lifetime and so REDO information is not needed
2853 	on restart for recovery.
2854 	Disable locking as temp-tables are not shared across connection. */
2855 	if (dict_table_is_temporary(index->table)) {
2856 		flags |= BTR_NO_LOCKING_FLAG;
2857 		mtr.set_log_mode(MTR_LOG_NO_REDO);
2858 
2859 		if (dict_table_is_intrinsic(index->table)) {
2860 			flags |= BTR_NO_UNDO_LOG_FLAG;
2861 		}
2862 	}
2863 
2864 	/* If the restoration does not succeed, then the same
2865 	transaction has deleted the record on which the cursor was,
2866 	and that is an SQL error. If the restoration succeeds, it may
2867 	still be that the same transaction has successively deleted
2868 	and inserted a record with the same ordering fields, but in
2869 	that case we know that the transaction has at least an
2870 	implicit x-lock on the record. */
2871 
2872 	ut_a(pcur->rel_pos == BTR_PCUR_ON);
2873 
2874 	ulint	mode;
2875 
2876 	DEBUG_SYNC_C_IF_THD(
2877 		thr_get_trx(thr)->mysql_thd,
2878 		"innodb_row_upd_clust_step_enter");
2879 
2880 	if (dict_index_is_online_ddl(index)) {
2881 		ut_ad(node->table->id != DICT_INDEXES_ID);
2882 		mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED;
2883 		mtr_s_lock(dict_index_get_lock(index), &mtr);
2884 	} else {
2885 		mode = BTR_MODIFY_LEAF;
2886 	}
2887 
2888 	success = btr_pcur_restore_position(mode, pcur, &mtr);
2889 
2890 	if (!success) {
2891 		err = DB_RECORD_NOT_FOUND;
2892 
2893 		mtr_commit(&mtr);
2894 
2895 		return(err);
2896 	}
2897 
2898 	/* If this is a row in SYS_INDEXES table of the data dictionary,
2899 	then we have to free the file segments of the index tree associated
2900 	with the index */
2901 
2902 	if (node->is_delete && node->table->id == DICT_INDEXES_ID) {
2903 
2904 		ut_ad(!dict_index_is_online_ddl(index));
2905 
2906 		dict_drop_index_tree(
2907 			btr_pcur_get_rec(pcur), pcur, &mtr);
2908 
2909 		mtr_commit(&mtr);
2910 
2911 		mtr_start(&mtr);
2912 		mtr.set_named_space(index->space);
2913 
2914 		success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
2915 						    &mtr);
2916 		if (!success) {
2917 			err = DB_ERROR;
2918 
2919 			mtr_commit(&mtr);
2920 
2921 			return(err);
2922 		}
2923 	}
2924 
2925 	rec = btr_pcur_get_rec(pcur);
2926 	offsets = rec_get_offsets(rec, index, offsets_,
2927 				  ULINT_UNDEFINED, &heap);
2928 
2929 	if (!node->has_clust_rec_x_lock) {
2930 		err = lock_clust_rec_modify_check_and_lock(
2931 			flags, btr_pcur_get_block(pcur),
2932 			rec, index, offsets, thr);
2933 		if (err != DB_SUCCESS) {
2934 			mtr_commit(&mtr);
2935 			goto exit_func;
2936 		}
2937 	}
2938 
2939 	ut_ad(lock_trx_has_rec_x_lock(thr_get_trx(thr), index->table,
2940 				      btr_pcur_get_block(pcur),
2941 				      page_rec_get_heap_no(rec)));
2942 
2943 	/* NOTE: the following function calls will also commit mtr */
2944 
2945 	if (node->is_delete) {
2946 		err = row_upd_del_mark_clust_rec(
2947 			flags, node, index, offsets, thr, referenced, &mtr);
2948 
2949 		if (err == DB_SUCCESS) {
2950 			node->state = UPD_NODE_UPDATE_ALL_SEC;
2951 			node->index = dict_table_get_next_index(index);
2952 		}
2953 
2954 		goto exit_func;
2955 	}
2956 
2957 	/* If the update is made for MySQL, we already have the update vector
2958 	ready, else we have to do some evaluation: */
2959 
2960 	if (UNIV_UNLIKELY(!node->in_mysql_interface)) {
2961 		/* Copy the necessary columns from clust_rec and calculate the
2962 		new values to set */
2963 		row_upd_copy_columns(rec, offsets,
2964 				     UT_LIST_GET_FIRST(node->columns));
2965 		row_upd_eval_new_vals(node->update);
2966 	}
2967 
2968 	if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
2969 
2970 		err = row_upd_clust_rec(
2971 			flags, node, index, offsets, &heap, thr, &mtr);
2972 		goto exit_func;
2973 	}
2974 
2975 	row_upd_store_row(node, trx->mysql_thd, thr->prebuilt);
2976 
2977 	if (row_upd_changes_ord_field_binary(index, node->update, thr,
2978 					     node->row, node->ext)) {
2979 
2980 		/* Update causes an ordering field (ordering fields within
2981 		the B-tree) of the clustered index record to change: perform
2982 		the update by delete marking and inserting.
2983 
2984 		TODO! What to do to the 'Halloween problem', where an update
2985 		moves the record forward in index so that it is again
2986 		updated when the cursor arrives there? Solution: the
2987 		read operation must check the undo record undo number when
2988 		choosing records to update. MySQL solves now the problem
2989 		externally! */
2990 
2991 		err = row_upd_clust_rec_by_insert(
2992 			flags, node, index, thr, referenced, &mtr);
2993 
2994 		if (err != DB_SUCCESS) {
2995 
2996 			goto exit_func;
2997 		}
2998 
2999 		node->state = UPD_NODE_UPDATE_ALL_SEC;
3000 	} else {
3001 		err = row_upd_clust_rec(
3002 			flags, node, index, offsets, &heap, thr, &mtr);
3003 
3004 		if (err != DB_SUCCESS) {
3005 
3006 			goto exit_func;
3007 		}
3008 
3009 		node->state = UPD_NODE_UPDATE_SOME_SEC;
3010 	}
3011 
3012 	node->index = dict_table_get_next_index(index);
3013 
3014 exit_func:
3015 	if (heap) {
3016 		mem_heap_free(heap);
3017 	}
3018 	return(err);
3019 }
3020 
3021 /***********************************************************//**
3022 Updates the affected index records of a row. When the control is transferred
3023 to this node, we assume that we have a persistent cursor which was on a
3024 record, and the position of the cursor is stored in the cursor.
3025 @return DB_SUCCESS if operation successfully completed, else error
3026 code or DB_LOCK_WAIT */
3027 dberr_t
row_upd(upd_node_t * node,que_thr_t * thr)3028 row_upd(
3029 /*====*/
3030 	upd_node_t*	node,	/*!< in: row update node */
3031 	que_thr_t*	thr)	/*!< in: query thread */
3032 {
3033 	dberr_t		err	= DB_SUCCESS;
3034 	DBUG_ENTER("row_upd");
3035 
3036 	ut_ad(node != NULL);
3037 	ut_ad(thr != NULL);
3038 	ut_ad(!thr_get_trx(thr)->in_rollback);
3039 
3040 	DBUG_PRINT("row_upd", ("table: %s", node->table->name.m_name));
3041 	DBUG_PRINT("row_upd", ("info bits in update vector: 0x%lx",
3042 			       node->update ? node->update->info_bits: 0));
3043 	DBUG_PRINT("row_upd", ("foreign_id: %s",
3044 			       node->foreign ? node->foreign->id: "NULL"));
3045 
3046 	if (UNIV_LIKELY(node->in_mysql_interface)) {
3047 
3048 		/* We do not get the cmpl_info value from the MySQL
3049 		interpreter: we must calculate it on the fly: */
3050 
3051 		if (node->is_delete
3052 		    || row_upd_changes_some_index_ord_field_binary(
3053 			    node->table, node->update)) {
3054 			node->cmpl_info = 0;
3055 		} else {
3056 			node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
3057 		}
3058 	}
3059 
3060 	switch (node->state) {
3061 	case UPD_NODE_UPDATE_CLUSTERED:
3062 	case UPD_NODE_INSERT_CLUSTERED:
3063 		if (!dict_table_is_intrinsic(node->table)) {
3064 			log_free_check();
3065 		}
3066 		err = row_upd_clust_step(node, thr);
3067 
3068 		if (err != DB_SUCCESS) {
3069 
3070 			DBUG_RETURN(err);
3071 		}
3072 	}
3073 
3074 	DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd,
3075 			    "after_row_upd_clust");
3076 
3077 	if (node->index == NULL
3078 	    || (!node->is_delete
3079 		&& (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE))) {
3080 
3081 		DBUG_RETURN(DB_SUCCESS);
3082 	}
3083 
3084 	DBUG_EXECUTE_IF("row_upd_skip_sec", node->index = NULL;);
3085 
3086 	do {
3087 		/* Skip corrupted index */
3088 		dict_table_skip_corrupt_index(node->index);
3089 
3090 		if (!node->index) {
3091 			break;
3092 		}
3093 
3094 		if (node->index->type != DICT_FTS) {
3095 			err = row_upd_sec_step(node, thr);
3096 
3097 			if (err != DB_SUCCESS) {
3098 
3099 				DBUG_RETURN(err);
3100 			}
3101 		}
3102 
3103 		node->index = dict_table_get_next_index(node->index);
3104 	} while (node->index != NULL);
3105 
3106 	ut_ad(err == DB_SUCCESS);
3107 
3108 	/* Do some cleanup */
3109 
3110 	if (node->row != NULL) {
3111 		node->row = NULL;
3112 		node->ext = NULL;
3113 		node->upd_row = NULL;
3114 		node->upd_ext = NULL;
3115 		mem_heap_empty(node->heap);
3116 	}
3117 
3118 	node->state = UPD_NODE_UPDATE_CLUSTERED;
3119 
3120 	DBUG_RETURN(err);
3121 }
3122 
3123 /***********************************************************//**
3124 Updates a row in a table. This is a high-level function used in SQL execution
3125 graphs.
3126 @return query thread to run next or NULL */
3127 que_thr_t*
row_upd_step(que_thr_t * thr)3128 row_upd_step(
3129 /*=========*/
3130 	que_thr_t*	thr)	/*!< in: query thread */
3131 {
3132 	upd_node_t*	node;
3133 	sel_node_t*	sel_node;
3134 	que_node_t*	parent;
3135 	dberr_t		err		= DB_SUCCESS;
3136 	trx_t*		trx;
3137 	DBUG_ENTER("row_upd_step");
3138 
3139 	ut_ad(thr);
3140 
3141 	trx = thr_get_trx(thr);
3142 
3143 	trx_start_if_not_started_xa(trx, true);
3144 
3145 	node = static_cast<upd_node_t*>(thr->run_node);
3146 
3147 	sel_node = node->select;
3148 
3149 	parent = que_node_get_parent(node);
3150 
3151 	ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
3152 
3153 	if (thr->prev_node == parent) {
3154 		node->state = UPD_NODE_SET_IX_LOCK;
3155 	}
3156 
3157 	if (node->state == UPD_NODE_SET_IX_LOCK) {
3158 
3159 		if (!node->has_clust_rec_x_lock) {
3160 			/* It may be that the current session has not yet
3161 			started its transaction, or it has been committed: */
3162 
3163 			err = lock_table(0, node->table, LOCK_IX, thr);
3164 
3165 			if (err != DB_SUCCESS) {
3166 
3167 				goto error_handling;
3168 			}
3169 		}
3170 
3171 		node->state = UPD_NODE_UPDATE_CLUSTERED;
3172 
3173 		if (node->searched_update) {
3174 			/* Reset the cursor */
3175 			sel_node->state = SEL_NODE_OPEN;
3176 
3177 			/* Fetch a row to update */
3178 
3179 			thr->run_node = sel_node;
3180 
3181 			DBUG_RETURN(thr);
3182 		}
3183 	}
3184 
3185 	/* sel_node is NULL if we are in the MySQL interface */
3186 
3187 	if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
3188 
3189 		if (!node->searched_update) {
3190 			/* An explicit cursor should be positioned on a row
3191 			to update */
3192 
3193 			ut_error;
3194 
3195 			err = DB_ERROR;
3196 
3197 			goto error_handling;
3198 		}
3199 
3200 		ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
3201 
3202 		/* No more rows to update, or the select node performed the
3203 		updates directly in-place */
3204 
3205 		thr->run_node = parent;
3206 
3207 		DBUG_RETURN(thr);
3208 	}
3209 
3210 	/* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
3211 
3212 	err = row_upd(node, thr);
3213 
3214 error_handling:
3215 	trx->error_state = err;
3216 
3217 	if (err != DB_SUCCESS) {
3218 		DBUG_RETURN(NULL);
3219 	}
3220 
3221 	/* DO THE TRIGGER ACTIONS HERE */
3222 
3223 	if (node->searched_update) {
3224 		/* Fetch next row to update */
3225 
3226 		thr->run_node = sel_node;
3227 	} else {
3228 		/* It was an explicit cursor update */
3229 
3230 		thr->run_node = parent;
3231 	}
3232 
3233 	node->state = UPD_NODE_UPDATE_CLUSTERED;
3234 
3235 	DBUG_RETURN(thr);
3236 }
3237 
3238 #endif /* !UNIV_HOTBACKUP */
3239