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