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