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