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