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