mySQL 5.0.11 sources for tomato
[tomato.git] / release / src / router / mysql / storage / innobase / row / row0upd.c
blobd3ed71089a835353cf98aeb845907f65ef516332
1 /******************************************************
2 Update of a row
4 (c) 1996 Innobase Oy
6 Created 12/27/1996 Heikki Tuuri
7 *******************************************************/
9 #include "my_global.h" /* HAVE_* */
10 #include "m_string.h" /* for my_sys.h */
11 #include "my_sys.h" /* DEBUG_SYNC_C */
12 #include "row0upd.h"
14 #ifdef UNIV_NONINL
15 #include "row0upd.ic"
16 #endif
18 #include "dict0dict.h"
19 #include "dict0boot.h"
20 #include "dict0crea.h"
21 #include "mach0data.h"
22 #include "trx0undo.h"
23 #include "btr0btr.h"
24 #include "btr0cur.h"
25 #include "que0que.h"
26 #include "row0ins.h"
27 #include "row0sel.h"
28 #include "row0row.h"
29 #include "rem0cmp.h"
30 #include "lock0lock.h"
31 #include "log0log.h"
32 #include "pars0sym.h"
33 #include "eval0eval.h"
34 #include "buf0lru.h"
37 /* What kind of latch and lock can we assume when the control comes to
38 -------------------------------------------------------------------
39 an update node?
40 --------------
41 Efficiency of massive updates would require keeping an x-latch on a
42 clustered index page through many updates, and not setting an explicit
43 x-lock on clustered index records, as they anyway will get an implicit
44 x-lock when they are updated. A problem is that the read nodes in the
45 graph should know that they must keep the latch when passing the control
46 up to the update node, and not set any record lock on the record which
47 will be updated. Another problem occurs if the execution is stopped,
48 as the kernel switches to another query thread, or the transaction must
49 wait for a lock. Then we should be able to release the latch and, maybe,
50 acquire an explicit x-lock on the record.
51 Because this seems too complicated, we conclude that the less
52 efficient solution of releasing all the latches when the control is
53 transferred to another node, and acquiring explicit x-locks, is better. */
55 /* How is a delete performed? If there is a delete without an
56 explicit cursor, i.e., a searched delete, there are at least
57 two different situations:
58 the implicit select cursor may run on (1) the clustered index or
59 on (2) a secondary index. The delete is performed by setting
60 the delete bit in the record and substituting the id of the
61 deleting transaction for the original trx id, and substituting a
62 new roll ptr for previous roll ptr. The old trx id and roll ptr
63 are saved in the undo log record. Thus, no physical changes occur
64 in the index tree structure at the time of the delete. Only
65 when the undo log is purged, the index records will be physically
66 deleted from the index trees.
68 The query graph executing a searched delete would consist of
69 a delete node which has as a subtree a select subgraph.
70 The select subgraph should return a (persistent) cursor
71 in the clustered index, placed on page which is x-latched.
72 The delete node should look for all secondary index records for
73 this clustered index entry and mark them as deleted. When is
74 the x-latch freed? The most efficient way for performing a
75 searched delete is obviously to keep the x-latch for several
76 steps of query graph execution. */
78 /***************************************************************
79 Checks if an update vector changes some of the first ordering fields of an
80 index record. This is only used in foreign key checks and we can assume
81 that index does not contain column prefixes. */
82 static
83 ibool
84 row_upd_changes_first_fields_binary(
85 /*================================*/
86 /* out: TRUE if changes */
87 dtuple_t* entry, /* in: old value of index entry */
88 dict_index_t* index, /* in: index of entry */
89 upd_t* update, /* in: update vector for the row */
90 ulint n); /* in: how many first fields to check */
93 /*************************************************************************
94 Checks if index currently is mentioned as a referenced index in a foreign
95 key constraint. */
96 static
97 ibool
98 row_upd_index_is_referenced(
99 /*========================*/
100 /* out: TRUE if referenced; NOTE that since
101 we do not hold dict_operation_lock
102 when leaving the function, it may be that
103 the referencing table has been dropped when
104 we leave this function: this function is only
105 for heuristic use! */
106 dict_index_t* index, /* in: index */
107 trx_t* trx) /* in: transaction */
109 dict_table_t* table = index->table;
110 dict_foreign_t* foreign;
111 ibool froze_data_dict = FALSE;
113 if (!UT_LIST_GET_FIRST(table->referenced_list)) {
115 return(FALSE);
118 if (trx->dict_operation_lock_mode == 0) {
119 row_mysql_freeze_data_dictionary(trx);
120 froze_data_dict = TRUE;
123 foreign = UT_LIST_GET_FIRST(table->referenced_list);
125 while (foreign) {
126 if (foreign->referenced_index == index) {
128 if (froze_data_dict) {
129 row_mysql_unfreeze_data_dictionary(trx);
132 return(TRUE);
135 foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
138 if (froze_data_dict) {
139 row_mysql_unfreeze_data_dictionary(trx);
142 return(FALSE);
145 /*************************************************************************
146 Checks if possible foreign key constraints hold after a delete of the record
147 under pcur. NOTE that this function will temporarily commit mtr and lose the
148 pcur position! */
149 static
150 ulint
151 row_upd_check_references_constraints(
152 /*=================================*/
153 /* out: DB_SUCCESS or an error code */
154 upd_node_t* node, /* in: row update node */
155 btr_pcur_t* pcur, /* in: cursor positioned on a record; NOTE: the
156 cursor position is lost in this function! */
157 dict_table_t* table, /* in: table in question */
158 dict_index_t* index, /* in: index of the cursor */
159 que_thr_t* thr, /* in: query thread */
160 mtr_t* mtr) /* in: mtr */
162 dict_foreign_t* foreign;
163 mem_heap_t* heap;
164 dtuple_t* entry;
165 trx_t* trx;
166 rec_t* rec;
167 ulint err;
168 ibool got_s_lock = FALSE;
170 if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) {
172 return(DB_SUCCESS);
175 trx = thr_get_trx(thr);
177 rec = btr_pcur_get_rec(pcur);
179 heap = mem_heap_create(500);
181 entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
183 mtr_commit(mtr);
185 mtr_start(mtr);
187 if (trx->dict_operation_lock_mode == 0) {
188 got_s_lock = TRUE;
190 row_mysql_freeze_data_dictionary(trx);
193 foreign = UT_LIST_GET_FIRST(table->referenced_list);
195 while (foreign) {
196 /* Note that we may have an update which updates the index
197 record, but does NOT update the first fields which are
198 referenced in a foreign key constraint. Then the update does
199 NOT break the constraint. */
201 if (foreign->referenced_index == index
202 && (node->is_delete
203 || row_upd_changes_first_fields_binary(
204 entry, index, node->update,
205 foreign->n_fields))) {
207 if (foreign->foreign_table == NULL) {
208 dict_table_get(foreign->foreign_table_name,
209 FALSE);
212 if (foreign->foreign_table) {
213 mutex_enter(&(dict_sys->mutex));
215 (foreign->foreign_table
216 ->n_foreign_key_checks_running)++;
218 mutex_exit(&(dict_sys->mutex));
221 /* NOTE that if the thread ends up waiting for a lock
222 we will release dict_operation_lock temporarily!
223 But the counter on the table protects 'foreign' from
224 being dropped while the check is running. */
226 err = row_ins_check_foreign_constraint(
227 FALSE, foreign, table, entry, thr);
229 if (foreign->foreign_table) {
230 mutex_enter(&(dict_sys->mutex));
232 ut_a(foreign->foreign_table
233 ->n_foreign_key_checks_running > 0);
235 (foreign->foreign_table
236 ->n_foreign_key_checks_running)--;
238 mutex_exit(&(dict_sys->mutex));
241 if (err != DB_SUCCESS) {
242 if (got_s_lock) {
243 row_mysql_unfreeze_data_dictionary(
244 trx);
247 mem_heap_free(heap);
249 return(err);
253 foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
256 if (got_s_lock) {
257 row_mysql_unfreeze_data_dictionary(trx);
260 mem_heap_free(heap);
262 return(DB_SUCCESS);
265 /*************************************************************************
266 Creates an update node for a query graph. */
268 upd_node_t*
269 upd_node_create(
270 /*============*/
271 /* out, own: update node */
272 mem_heap_t* heap) /* in: mem heap where created */
274 upd_node_t* node;
276 node = mem_heap_alloc(heap, sizeof(upd_node_t));
277 node->common.type = QUE_NODE_UPDATE;
279 node->state = UPD_NODE_UPDATE_CLUSTERED;
280 node->select_will_do_update = FALSE;
281 node->in_mysql_interface = FALSE;
283 node->row = NULL;
284 node->ext_vec = NULL;
285 node->index = NULL;
286 node->update = NULL;
288 node->foreign = NULL;
289 node->cascade_heap = NULL;
290 node->cascade_node = NULL;
292 node->select = NULL;
294 node->heap = mem_heap_create(128);
295 node->magic_n = UPD_NODE_MAGIC_N;
297 node->cmpl_info = 0;
299 return(node);
302 /*************************************************************************
303 Updates the trx id and roll ptr field in a clustered index record in database
304 recovery. */
306 void
307 row_upd_rec_sys_fields_in_recovery(
308 /*===============================*/
309 rec_t* rec, /* in: record */
310 const ulint* offsets,/* in: array returned by rec_get_offsets() */
311 ulint pos, /* in: TRX_ID position in rec */
312 dulint trx_id, /* in: transaction id */
313 dulint roll_ptr)/* in: roll ptr of the undo log record */
315 byte* field;
316 ulint len;
318 field = rec_get_nth_field(rec, offsets, pos, &len);
319 ut_ad(len == DATA_TRX_ID_LEN);
320 trx_write_trx_id(field, trx_id);
322 field = rec_get_nth_field(rec, offsets, pos + 1, &len);
323 ut_ad(len == DATA_ROLL_PTR_LEN);
324 trx_write_roll_ptr(field, roll_ptr);
327 /*************************************************************************
328 Sets the trx id or roll ptr field of a clustered index entry. */
330 void
331 row_upd_index_entry_sys_field(
332 /*==========================*/
333 dtuple_t* entry, /* in: index entry, where the memory buffers
334 for sys fields are already allocated:
335 the function just copies the new values to
336 them */
337 dict_index_t* index, /* in: clustered index */
338 ulint type, /* in: DATA_TRX_ID or DATA_ROLL_PTR */
339 dulint val) /* in: value to write */
341 dfield_t* dfield;
342 byte* field;
343 ulint pos;
345 ut_ad(index->type & DICT_CLUSTERED);
347 pos = dict_index_get_sys_col_pos(index, type);
349 dfield = dtuple_get_nth_field(entry, pos);
350 field = dfield_get_data(dfield);
352 if (type == DATA_TRX_ID) {
353 trx_write_trx_id(field, val);
354 } else {
355 ut_ad(type == DATA_ROLL_PTR);
356 trx_write_roll_ptr(field, val);
360 /***************************************************************
361 Returns TRUE if row update changes size of some field in index or if some
362 field to be updated is stored externally in rec or update. */
364 ibool
365 row_upd_changes_field_size_or_external(
366 /*===================================*/
367 /* out: TRUE if the update changes the size of
368 some field in index or the field is external
369 in rec or update */
370 dict_index_t* index, /* in: index */
371 const ulint* offsets,/* in: rec_get_offsets(rec, index) */
372 upd_t* update) /* in: update vector */
374 upd_field_t* upd_field;
375 dfield_t* new_val;
376 ulint old_len;
377 ulint new_len;
378 ulint n_fields;
379 ulint i;
381 ut_ad(rec_offs_validate(NULL, index, offsets));
382 n_fields = upd_get_n_fields(update);
384 for (i = 0; i < n_fields; i++) {
385 upd_field = upd_get_nth_field(update, i);
387 new_val = &(upd_field->new_val);
388 new_len = new_val->len;
390 if (new_len == UNIV_SQL_NULL && !rec_offs_comp(offsets)) {
391 /* A bug fixed on Dec 31st, 2004: we looked at the
392 SQL NULL size from the wrong field! We may backport
393 this fix also to 4.0. The merge to 5.0 will be made
394 manually immediately after we commit this to 4.1. */
396 new_len = dict_col_get_sql_null_size(
397 dict_index_get_nth_col(index,
398 upd_field->field_no));
401 old_len = rec_offs_nth_size(offsets, upd_field->field_no);
403 if (rec_offs_comp(offsets)
404 && rec_offs_nth_sql_null(offsets,
405 upd_field->field_no)) {
406 /* Note that in the compact table format, for a
407 variable length field, an SQL NULL will use zero
408 bytes in the offset array at the start of the physical
409 record, but a zero-length value (empty string) will
410 use one byte! Thus, we cannot use update-in-place
411 if we update an SQL NULL varchar to an empty string! */
413 old_len = UNIV_SQL_NULL;
416 if (old_len != new_len) {
418 return(TRUE);
421 if (rec_offs_nth_extern(offsets, upd_field->field_no)) {
423 return(TRUE);
426 if (upd_field->extern_storage) {
428 return(TRUE);
432 return(FALSE);
435 /***************************************************************
436 Replaces the new column values stored in the update vector to the
437 record given. No field size changes are allowed. This function is
438 usually invoked on a clustered index. The only use case for a
439 secondary index is row_ins_sec_index_entry_by_modify() or its
440 counterpart in ibuf_insert_to_index_page(). */
442 void
443 row_upd_rec_in_place(
444 /*=================*/
445 rec_t* rec, /* in/out: record where replaced */
446 const ulint* offsets,/* in: array returned by rec_get_offsets() */
447 upd_t* update) /* in: update vector */
449 upd_field_t* upd_field;
450 dfield_t* new_val;
451 ulint n_fields;
452 ulint i;
454 ut_ad(rec_offs_validate(rec, NULL, offsets));
456 rec_set_info_bits(rec, rec_offs_comp(offsets), update->info_bits);
458 n_fields = upd_get_n_fields(update);
460 for (i = 0; i < n_fields; i++) {
461 upd_field = upd_get_nth_field(update, i);
462 new_val = &(upd_field->new_val);
464 rec_set_nth_field(rec, offsets, upd_field->field_no,
465 dfield_get_data(new_val),
466 dfield_get_len(new_val));
470 /*************************************************************************
471 Writes into the redo log the values of trx id and roll ptr and enough info
472 to determine their positions within a clustered index record. */
474 byte*
475 row_upd_write_sys_vals_to_log(
476 /*==========================*/
477 /* out: new pointer to mlog */
478 dict_index_t* index, /* in: clustered index */
479 trx_t* trx, /* in: transaction */
480 dulint roll_ptr,/* in: roll ptr of the undo log record */
481 byte* log_ptr,/* pointer to a buffer of size > 20 opened
482 in mlog */
483 mtr_t* mtr __attribute__((unused))) /* in: mtr */
485 ut_ad(index->type & DICT_CLUSTERED);
486 ut_ad(mtr);
488 log_ptr += mach_write_compressed(log_ptr,
489 dict_index_get_sys_col_pos(
490 index, DATA_TRX_ID));
492 trx_write_roll_ptr(log_ptr, roll_ptr);
493 log_ptr += DATA_ROLL_PTR_LEN;
495 log_ptr += mach_dulint_write_compressed(log_ptr, trx->id);
497 return(log_ptr);
500 /*************************************************************************
501 Parses the log data of system field values. */
503 byte*
504 row_upd_parse_sys_vals(
505 /*===================*/
506 /* out: log data end or NULL */
507 byte* ptr, /* in: buffer */
508 byte* end_ptr,/* in: buffer end */
509 ulint* pos, /* out: TRX_ID position in record */
510 dulint* trx_id, /* out: trx id */
511 dulint* roll_ptr)/* out: roll ptr */
513 ptr = mach_parse_compressed(ptr, end_ptr, pos);
515 if (ptr == NULL) {
517 return(NULL);
520 if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {
522 return(NULL);
525 *roll_ptr = trx_read_roll_ptr(ptr);
526 ptr += DATA_ROLL_PTR_LEN;
528 ptr = mach_dulint_parse_compressed(ptr, end_ptr, trx_id);
530 return(ptr);
533 /***************************************************************
534 Writes to the redo log the new values of the fields occurring in the index. */
536 void
537 row_upd_index_write_log(
538 /*====================*/
539 upd_t* update, /* in: update vector */
540 byte* log_ptr,/* in: pointer to mlog buffer: must contain at least
541 MLOG_BUF_MARGIN bytes of free space; the buffer is
542 closed within this function */
543 mtr_t* mtr) /* in: mtr into whose log to write */
545 upd_field_t* upd_field;
546 dfield_t* new_val;
547 ulint len;
548 ulint n_fields;
549 byte* buf_end;
550 ulint i;
552 n_fields = upd_get_n_fields(update);
554 buf_end = log_ptr + MLOG_BUF_MARGIN;
556 mach_write_to_1(log_ptr, update->info_bits);
557 log_ptr++;
558 log_ptr += mach_write_compressed(log_ptr, n_fields);
560 for (i = 0; i < n_fields; i++) {
562 #if MLOG_BUF_MARGIN <= 30
563 # error "MLOG_BUF_MARGIN <= 30"
564 #endif
566 if (log_ptr + 30 > buf_end) {
567 mlog_close(mtr, log_ptr);
569 log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
570 buf_end = log_ptr + MLOG_BUF_MARGIN;
573 upd_field = upd_get_nth_field(update, i);
575 new_val = &(upd_field->new_val);
577 len = new_val->len;
579 log_ptr += mach_write_compressed(log_ptr, upd_field->field_no);
580 log_ptr += mach_write_compressed(log_ptr, len);
582 if (len != UNIV_SQL_NULL) {
583 if (log_ptr + len < buf_end) {
584 ut_memcpy(log_ptr, new_val->data, len);
586 log_ptr += len;
587 } else {
588 mlog_close(mtr, log_ptr);
590 mlog_catenate_string(mtr, new_val->data, len);
592 log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
593 buf_end = log_ptr + MLOG_BUF_MARGIN;
598 mlog_close(mtr, log_ptr);
601 /*************************************************************************
602 Parses the log data written by row_upd_index_write_log. */
604 byte*
605 row_upd_index_parse(
606 /*================*/
607 /* out: log data end or NULL */
608 byte* ptr, /* in: buffer */
609 byte* end_ptr,/* in: buffer end */
610 mem_heap_t* heap, /* in: memory heap where update vector is
611 built */
612 upd_t** update_out)/* out: update vector */
614 upd_t* update;
615 upd_field_t* upd_field;
616 dfield_t* new_val;
617 ulint len;
618 ulint n_fields;
619 byte* buf;
620 ulint info_bits;
621 ulint i;
623 if (end_ptr < ptr + 1) {
625 return(NULL);
628 info_bits = mach_read_from_1(ptr);
629 ptr++;
630 ptr = mach_parse_compressed(ptr, end_ptr, &n_fields);
632 if (ptr == NULL) {
634 return(NULL);
637 update = upd_create(n_fields, heap);
638 update->info_bits = info_bits;
640 for (i = 0; i < n_fields; i++) {
641 upd_field = upd_get_nth_field(update, i);
642 new_val = &(upd_field->new_val);
644 ptr = mach_parse_compressed(ptr, end_ptr,
645 &(upd_field->field_no));
646 if (ptr == NULL) {
648 return(NULL);
651 ptr = mach_parse_compressed(ptr, end_ptr, &len);
653 if (ptr == NULL) {
655 return(NULL);
658 new_val->len = len;
660 if (len != UNIV_SQL_NULL) {
662 if (end_ptr < ptr + len) {
664 return(NULL);
665 } else {
666 buf = mem_heap_alloc(heap, len);
667 ut_memcpy(buf, ptr, len);
669 ptr += len;
671 new_val->data = buf;
676 *update_out = update;
678 return(ptr);
681 /*******************************************************************
682 Returns TRUE if ext_vec contains i. */
683 static
684 ibool
685 upd_ext_vec_contains(
686 /*=================*/
687 /* out: TRUE if i is in ext_vec */
688 ulint* ext_vec, /* in: array of indexes or NULL */
689 ulint n_ext_vec, /* in: number of numbers in ext_vec */
690 ulint i) /* in: a number */
692 ulint j;
694 if (ext_vec == NULL) {
696 return(FALSE);
699 for (j = 0; j < n_ext_vec; j++) {
700 if (ext_vec[j] == i) {
702 return(TRUE);
706 return(FALSE);
709 /*******************************************************************
710 Builds an update vector from those fields which in a secondary index entry
711 differ from a record that has the equal ordering fields. NOTE: we compare
712 the fields as binary strings! */
714 upd_t*
715 row_upd_build_sec_rec_difference_binary(
716 /*====================================*/
717 /* out, own: update vector of differing
718 fields */
719 dict_index_t* index, /* in: index */
720 dtuple_t* entry, /* in: entry to insert */
721 rec_t* rec, /* in: secondary index record */
722 trx_t* trx, /* in: transaction */
723 mem_heap_t* heap) /* in: memory heap from which allocated */
725 upd_field_t* upd_field;
726 dfield_t* dfield;
727 byte* data;
728 ulint len;
729 upd_t* update;
730 ulint n_diff;
731 ulint i;
732 ulint offsets_[REC_OFFS_SMALL_SIZE];
733 const ulint* offsets;
734 *offsets_ = (sizeof offsets_) / sizeof *offsets_;
736 /* This function is used only for a secondary index */
737 ut_a(0 == (index->type & DICT_CLUSTERED));
739 update = upd_create(dtuple_get_n_fields(entry), heap);
741 n_diff = 0;
742 offsets = rec_get_offsets(rec, index, offsets_,
743 ULINT_UNDEFINED, &heap);
745 for (i = 0; i < dtuple_get_n_fields(entry); i++) {
747 data = rec_get_nth_field(rec, offsets, i, &len);
749 dfield = dtuple_get_nth_field(entry, i);
751 /* NOTE that it may be that len != dfield_get_len(dfield) if we
752 are updating in a character set and collation where strings of
753 different length can be equal in an alphabetical comparison,
754 and also in the case where we have a column prefix index
755 and the last characters in the index field are spaces; the
756 latter case probably caused the assertion failures reported at
757 row0upd.c line 713 in versions 4.0.14 - 4.0.16. */
759 /* NOTE: we compare the fields as binary strings!
760 (No collation) */
762 if (!dfield_data_is_binary_equal(dfield, len, data)) {
764 upd_field = upd_get_nth_field(update, n_diff);
766 dfield_copy(&(upd_field->new_val), dfield);
768 upd_field_set_field_no(upd_field, i, index, trx);
770 upd_field->extern_storage = FALSE;
772 n_diff++;
776 update->n_fields = n_diff;
778 return(update);
781 /*******************************************************************
782 Builds an update vector from those fields, excluding the roll ptr and
783 trx id fields, which in an index entry differ from a record that has
784 the equal ordering fields. NOTE: we compare the fields as binary strings! */
786 upd_t*
787 row_upd_build_difference_binary(
788 /*============================*/
789 /* out, own: update vector of differing
790 fields, excluding roll ptr and trx id */
791 dict_index_t* index, /* in: clustered index */
792 dtuple_t* entry, /* in: entry to insert */
793 ulint* ext_vec,/* in: array containing field numbers of
794 externally stored fields in entry, or NULL */
795 ulint n_ext_vec,/* in: number of fields in ext_vec */
796 rec_t* rec, /* in: clustered index record */
797 trx_t* trx, /* in: transaction */
798 mem_heap_t* heap) /* in: memory heap from which allocated */
800 upd_field_t* upd_field;
801 dfield_t* dfield;
802 byte* data;
803 ulint len;
804 upd_t* update;
805 ulint n_diff;
806 ulint roll_ptr_pos;
807 ulint trx_id_pos;
808 ibool extern_bit;
809 ulint i;
810 ulint offsets_[REC_OFFS_NORMAL_SIZE];
811 const ulint* offsets;
812 *offsets_ = (sizeof offsets_) / sizeof *offsets_;
814 /* This function is used only for a clustered index */
815 ut_a(index->type & DICT_CLUSTERED);
817 update = upd_create(dtuple_get_n_fields(entry), heap);
819 n_diff = 0;
821 roll_ptr_pos = dict_index_get_sys_col_pos(index, DATA_ROLL_PTR);
822 trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
824 offsets = rec_get_offsets(rec, index, offsets_,
825 ULINT_UNDEFINED, &heap);
827 for (i = 0; i < dtuple_get_n_fields(entry); i++) {
829 data = rec_get_nth_field(rec, offsets, i, &len);
831 dfield = dtuple_get_nth_field(entry, i);
833 /* NOTE: we compare the fields as binary strings!
834 (No collation) */
836 if (i == trx_id_pos || i == roll_ptr_pos) {
838 goto skip_compare;
841 extern_bit = upd_ext_vec_contains(ext_vec, n_ext_vec, i);
843 if (UNIV_UNLIKELY(extern_bit
844 == (ibool)!rec_offs_nth_extern(offsets, i))
845 || !dfield_data_is_binary_equal(dfield, len, data)) {
847 upd_field = upd_get_nth_field(update, n_diff);
849 dfield_copy(&(upd_field->new_val), dfield);
851 upd_field_set_field_no(upd_field, i, index, trx);
853 upd_field->extern_storage = extern_bit;
855 n_diff++;
857 skip_compare:
861 update->n_fields = n_diff;
863 return(update);
866 /***************************************************************
867 Replaces the new column values stored in the update vector to the index entry
868 given. */
870 void
871 row_upd_index_replace_new_col_vals_index_pos(
872 /*=========================================*/
873 dtuple_t* entry, /* in/out: index entry where replaced */
874 dict_index_t* index, /* in: index; NOTE that this may also be a
875 non-clustered index */
876 upd_t* update, /* in: an update vector built for the index so
877 that the field number in an upd_field is the
878 index position */
879 ibool order_only,
880 /* in: if TRUE, limit the replacement to
881 ordering fields of index; note that this
882 does not work for non-clustered indexes. */
883 mem_heap_t* heap) /* in: memory heap to which we allocate and
884 copy the new values, set this as NULL if you
885 do not want allocation */
887 dict_field_t* field;
888 upd_field_t* upd_field;
889 dfield_t* dfield;
890 dfield_t* new_val;
891 ulint j;
892 ulint i;
893 ulint n_fields;
895 ut_ad(index);
897 dtuple_set_info_bits(entry, update->info_bits);
899 if (order_only) {
900 n_fields = dict_index_get_n_unique(index);
901 } else {
902 n_fields = dict_index_get_n_fields(index);
905 for (j = 0; j < n_fields; j++) {
907 field = dict_index_get_nth_field(index, j);
909 for (i = 0; i < upd_get_n_fields(update); i++) {
911 upd_field = upd_get_nth_field(update, i);
913 if (upd_field->field_no == j) {
915 dfield = dtuple_get_nth_field(entry, j);
917 new_val = &(upd_field->new_val);
919 dfield_set_data(dfield, new_val->data,
920 new_val->len);
921 if (heap && new_val->len != UNIV_SQL_NULL) {
922 dfield->data = mem_heap_alloc(
923 heap, new_val->len);
924 ut_memcpy(dfield->data, new_val->data,
925 new_val->len);
928 if (field->prefix_len > 0
929 && new_val->len != UNIV_SQL_NULL) {
931 const dict_col_t* col
932 = dict_field_get_col(field);
934 dfield->len
935 = dtype_get_at_most_n_mbchars(
936 col->prtype,
937 col->mbminlen,
938 col->mbmaxlen,
939 field->prefix_len,
940 new_val->len,
941 new_val->data);
948 /***************************************************************
949 Replaces the new column values stored in the update vector to the index entry
950 given. */
952 void
953 row_upd_index_replace_new_col_vals(
954 /*===============================*/
955 dtuple_t* entry, /* in/out: index entry where replaced */
956 dict_index_t* index, /* in: index; NOTE that this may also be a
957 non-clustered index */
958 upd_t* update, /* in: an update vector built for the
959 CLUSTERED index so that the field number in
960 an upd_field is the clustered index position */
961 mem_heap_t* heap) /* in: memory heap to which we allocate and
962 copy the new values, set this as NULL if you
963 do not want allocation */
965 upd_field_t* upd_field;
966 dfield_t* dfield;
967 dfield_t* new_val;
968 ulint j;
969 ulint i;
970 dict_index_t* clust_index;
972 ut_ad(index);
974 clust_index = dict_table_get_first_index(index->table);
976 dtuple_set_info_bits(entry, update->info_bits);
978 for (j = 0; j < dict_index_get_n_fields(index); j++) {
980 ulint clust_pos;
981 dict_field_t* field = dict_index_get_nth_field(index, j);
983 clust_pos = dict_col_get_clust_pos(field->col, clust_index);
985 for (i = 0; i < upd_get_n_fields(update); i++) {
987 upd_field = upd_get_nth_field(update, i);
989 if (upd_field->field_no == clust_pos) {
991 dfield = dtuple_get_nth_field(entry, j);
993 new_val = &(upd_field->new_val);
995 dfield_set_data(dfield, new_val->data,
996 new_val->len);
997 if (heap && new_val->len != UNIV_SQL_NULL) {
998 dfield->data = mem_heap_alloc(
999 heap, new_val->len);
1000 ut_memcpy(dfield->data, new_val->data,
1001 new_val->len);
1004 if (field->prefix_len > 0
1005 && new_val->len != UNIV_SQL_NULL) {
1007 const dict_col_t* col
1008 = dict_field_get_col(field);
1010 dfield->len
1011 = dtype_get_at_most_n_mbchars(
1012 col->prtype,
1013 col->mbminlen,
1014 col->mbmaxlen,
1015 field->prefix_len,
1016 new_val->len,
1017 new_val->data);
1024 /***************************************************************
1025 Checks if an update vector changes an ordering field of an index record.
1026 This function is fast if the update vector is short or the number of ordering
1027 fields in the index is small. Otherwise, this can be quadratic.
1028 NOTE: we compare the fields as binary strings! */
1030 ibool
1031 row_upd_changes_ord_field_binary(
1032 /*=============================*/
1033 /* out: TRUE if update vector changes
1034 an ordering field in the index record;
1035 NOTE: the fields are compared as binary
1036 strings */
1037 dtuple_t* row, /* in: old value of row, or NULL if the
1038 row and the data values in update are not
1039 known when this function is called, e.g., at
1040 compile time */
1041 dict_index_t* index, /* in: index of the record */
1042 upd_t* update) /* in: update vector for the row; NOTE: the
1043 field numbers in this MUST be clustered index
1044 positions! */
1046 ulint n_unique;
1047 ulint n_upd_fields;
1048 ulint i, j;
1049 dict_index_t* clust_index;
1051 ut_ad(update && index);
1053 n_unique = dict_index_get_n_unique(index);
1054 n_upd_fields = upd_get_n_fields(update);
1056 clust_index = dict_table_get_first_index(index->table);
1058 for (i = 0; i < n_unique; i++) {
1060 const dict_field_t* ind_field;
1061 const dict_col_t* col;
1062 ulint col_pos;
1063 ulint col_no;
1065 ind_field = dict_index_get_nth_field(index, i);
1066 col = dict_field_get_col(ind_field);
1067 col_pos = dict_col_get_clust_pos(col, clust_index);
1068 col_no = dict_col_get_no(col);
1070 for (j = 0; j < n_upd_fields; j++) {
1072 upd_field_t* upd_field
1073 = upd_get_nth_field(update, j);
1075 /* Note that if the index field is a column prefix
1076 then it may be that row does not contain an externally
1077 stored part of the column value, and we cannot compare
1078 the datas */
1080 if (col_pos == upd_field->field_no
1081 && (row == NULL
1082 || ind_field->prefix_len > 0
1083 || !dfield_datas_are_binary_equal(
1084 dtuple_get_nth_field(row, col_no),
1085 &(upd_field->new_val)))) {
1087 return(TRUE);
1092 return(FALSE);
1095 /***************************************************************
1096 Checks if an update vector changes an ordering field of an index record.
1097 NOTE: we compare the fields as binary strings! */
1099 ibool
1100 row_upd_changes_some_index_ord_field_binary(
1101 /*========================================*/
1102 /* out: TRUE if update vector may change
1103 an ordering field in an index record */
1104 dict_table_t* table, /* in: table */
1105 upd_t* update) /* in: update vector for the row */
1107 upd_field_t* upd_field;
1108 dict_index_t* index;
1109 ulint i;
1111 index = dict_table_get_first_index(table);
1113 for (i = 0; i < upd_get_n_fields(update); i++) {
1115 upd_field = upd_get_nth_field(update, i);
1117 if (dict_field_get_col(dict_index_get_nth_field(
1118 index, upd_field->field_no))
1119 ->ord_part) {
1121 return(TRUE);
1125 return(FALSE);
1128 /***************************************************************
1129 Checks if an update vector changes some of the first ordering fields of an
1130 index record. This is only used in foreign key checks and we can assume
1131 that index does not contain column prefixes. */
1132 static
1133 ibool
1134 row_upd_changes_first_fields_binary(
1135 /*================================*/
1136 /* out: TRUE if changes */
1137 dtuple_t* entry, /* in: index entry */
1138 dict_index_t* index, /* in: index of entry */
1139 upd_t* update, /* in: update vector for the row */
1140 ulint n) /* in: how many first fields to check */
1142 ulint n_upd_fields;
1143 ulint i, j;
1144 dict_index_t* clust_index;
1146 ut_ad(update && index);
1147 ut_ad(n <= dict_index_get_n_fields(index));
1149 n_upd_fields = upd_get_n_fields(update);
1150 clust_index = dict_table_get_first_index(index->table);
1152 for (i = 0; i < n; i++) {
1154 const dict_field_t* ind_field;
1155 const dict_col_t* col;
1156 ulint col_pos;
1158 ind_field = dict_index_get_nth_field(index, i);
1159 col = dict_field_get_col(ind_field);
1160 col_pos = dict_col_get_clust_pos(col, clust_index);
1162 ut_a(ind_field->prefix_len == 0);
1164 for (j = 0; j < n_upd_fields; j++) {
1166 upd_field_t* upd_field
1167 = upd_get_nth_field(update, j);
1169 if (col_pos == upd_field->field_no
1170 && !dfield_datas_are_binary_equal(
1171 dtuple_get_nth_field(entry, i),
1172 &(upd_field->new_val))) {
1174 return(TRUE);
1179 return(FALSE);
1182 /*************************************************************************
1183 Copies the column values from a record. */
1184 UNIV_INLINE
1185 void
1186 row_upd_copy_columns(
1187 /*=================*/
1188 rec_t* rec, /* in: record in a clustered index */
1189 const ulint* offsets,/* in: array returned by rec_get_offsets() */
1190 sym_node_t* column) /* in: first column in a column list, or
1191 NULL */
1193 byte* data;
1194 ulint len;
1196 while (column) {
1197 data = rec_get_nth_field(rec, offsets,
1198 column->field_nos[SYM_CLUST_FIELD_NO],
1199 &len);
1200 eval_node_copy_and_alloc_val(column, data, len);
1202 column = UT_LIST_GET_NEXT(col_var_list, column);
1206 /*************************************************************************
1207 Calculates the new values for fields to update. Note that row_upd_copy_columns
1208 must have been called first. */
1209 UNIV_INLINE
1210 void
1211 row_upd_eval_new_vals(
1212 /*==================*/
1213 upd_t* update) /* in: update vector */
1215 que_node_t* exp;
1216 upd_field_t* upd_field;
1217 ulint n_fields;
1218 ulint i;
1220 n_fields = upd_get_n_fields(update);
1222 for (i = 0; i < n_fields; i++) {
1223 upd_field = upd_get_nth_field(update, i);
1225 exp = upd_field->exp;
1227 eval_exp(exp);
1229 dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
1233 /***************************************************************
1234 Stores to the heap the row on which the node->pcur is positioned. */
1235 static
1236 void
1237 row_upd_store_row(
1238 /*==============*/
1239 upd_node_t* node) /* in: row update node */
1241 dict_index_t* clust_index;
1242 upd_t* update;
1243 rec_t* rec;
1244 mem_heap_t* heap = NULL;
1245 ulint offsets_[REC_OFFS_NORMAL_SIZE];
1246 const ulint* offsets;
1247 *offsets_ = (sizeof offsets_) / sizeof *offsets_;
1249 ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
1251 if (node->row != NULL) {
1252 mem_heap_empty(node->heap);
1253 node->row = NULL;
1256 clust_index = dict_table_get_first_index(node->table);
1258 rec = btr_pcur_get_rec(node->pcur);
1260 offsets = rec_get_offsets(rec, clust_index, offsets_,
1261 ULINT_UNDEFINED, &heap);
1262 node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
1263 node->heap);
1264 node->ext_vec = mem_heap_alloc(node->heap, sizeof(ulint)
1265 * rec_offs_n_fields(offsets));
1266 if (node->is_delete) {
1267 update = NULL;
1268 } else {
1269 update = node->update;
1272 node->n_ext_vec = btr_push_update_extern_fields(node->ext_vec,
1273 offsets, update);
1274 if (UNIV_LIKELY_NULL(heap)) {
1275 mem_heap_free(heap);
1279 /***************************************************************
1280 Updates a secondary index entry of a row. */
1281 static
1282 ulint
1283 row_upd_sec_index_entry(
1284 /*====================*/
1285 /* out: DB_SUCCESS if operation successfully
1286 completed, else error code or DB_LOCK_WAIT */
1287 upd_node_t* node, /* in: row update node */
1288 que_thr_t* thr) /* in: query thread */
1290 ibool check_ref;
1291 ibool found;
1292 dict_index_t* index;
1293 dtuple_t* entry;
1294 btr_pcur_t pcur;
1295 btr_cur_t* btr_cur;
1296 mem_heap_t* heap;
1297 rec_t* rec;
1298 ulint err = DB_SUCCESS;
1299 mtr_t mtr;
1300 trx_t* trx = thr_get_trx(thr);
1302 index = node->index;
1304 check_ref = row_upd_index_is_referenced(index, trx);
1306 heap = mem_heap_create(1024);
1308 /* Build old index entry */
1309 entry = row_build_index_entry(node->row, index, heap);
1311 log_free_check();
1312 mtr_start(&mtr);
1314 found = row_search_index_entry(index, entry, BTR_MODIFY_LEAF, &pcur,
1315 &mtr);
1316 btr_cur = btr_pcur_get_btr_cur(&pcur);
1318 rec = btr_cur_get_rec(btr_cur);
1320 if (UNIV_UNLIKELY(!found)) {
1321 fputs("InnoDB: error in sec index entry update in\n"
1322 "InnoDB: ", stderr);
1323 dict_index_name_print(stderr, trx, index);
1324 fputs("\n"
1325 "InnoDB: tuple ", stderr);
1326 dtuple_print(stderr, entry);
1327 fputs("\n"
1328 "InnoDB: record ", stderr);
1329 rec_print(stderr, rec, index);
1330 putc('\n', stderr);
1332 trx_print(stderr, trx, 0);
1334 fputs("\n"
1335 "InnoDB: Submit a detailed bug report"
1336 " to http://bugs.mysql.com\n", stderr);
1337 } else {
1338 /* Delete mark the old index record; it can already be
1339 delete marked if we return after a lock wait in
1340 row_ins_index_entry below */
1342 if (!rec_get_deleted_flag(rec,
1343 dict_table_is_comp(index->table))) {
1344 err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE,
1345 thr, &mtr);
1346 if (err == DB_SUCCESS && check_ref) {
1348 /* NOTE that the following call loses
1349 the position of pcur ! */
1350 err = row_upd_check_references_constraints(
1351 node, &pcur, index->table,
1352 index, thr, &mtr);
1353 if (err != DB_SUCCESS) {
1355 goto close_cur;
1361 close_cur:
1362 btr_pcur_close(&pcur);
1363 mtr_commit(&mtr);
1365 if (node->is_delete || err != DB_SUCCESS) {
1367 mem_heap_free(heap);
1369 return(err);
1372 /* Build a new index entry */
1373 row_upd_index_replace_new_col_vals(entry, index, node->update, NULL);
1375 /* Insert new index entry */
1376 err = row_ins_index_entry(index, entry, NULL, 0, thr);
1378 mem_heap_free(heap);
1380 return(err);
1383 /***************************************************************
1384 Updates the secondary index record if it is changed in the row update or
1385 deletes it if this is a delete. */
1386 UNIV_INLINE
1387 ulint
1388 row_upd_sec_step(
1389 /*=============*/
1390 /* out: DB_SUCCESS if operation successfully
1391 completed, else error code or DB_LOCK_WAIT */
1392 upd_node_t* node, /* in: row update node */
1393 que_thr_t* thr) /* in: query thread */
1395 ulint err;
1397 ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
1398 || (node->state == UPD_NODE_UPDATE_SOME_SEC));
1399 ut_ad(!(node->index->type & DICT_CLUSTERED));
1401 if (node->state == UPD_NODE_UPDATE_ALL_SEC
1402 || row_upd_changes_ord_field_binary(node->row, node->index,
1403 node->update)) {
1404 err = row_upd_sec_index_entry(node, thr);
1406 return(err);
1409 return(DB_SUCCESS);
1412 /***************************************************************
1413 Marks the clustered index record deleted and inserts the updated version
1414 of the record to the index. This function should be used when the ordering
1415 fields of the clustered index record change. This should be quite rare in
1416 database applications. */
1417 static
1418 ulint
1419 row_upd_clust_rec_by_insert(
1420 /*========================*/
1421 /* out: DB_SUCCESS if operation successfully
1422 completed, else error code or DB_LOCK_WAIT */
1423 upd_node_t* node, /* in: row update node */
1424 dict_index_t* index, /* in: clustered index of the record */
1425 que_thr_t* thr, /* in: query thread */
1426 ibool check_ref,/* in: TRUE if index may be referenced in
1427 a foreign key constraint */
1428 mtr_t* mtr) /* in: mtr; gets committed here */
1430 mem_heap_t* heap = NULL;
1431 btr_pcur_t* pcur;
1432 btr_cur_t* btr_cur;
1433 trx_t* trx;
1434 dict_table_t* table;
1435 dtuple_t* entry;
1436 ulint err;
1438 ut_ad(node);
1439 ut_ad(index->type & DICT_CLUSTERED);
1441 trx = thr_get_trx(thr);
1442 table = node->table;
1443 pcur = node->pcur;
1444 btr_cur = btr_pcur_get_btr_cur(pcur);
1446 if (node->state != UPD_NODE_INSERT_CLUSTERED) {
1447 ulint offsets_[REC_OFFS_NORMAL_SIZE];
1448 *offsets_ = (sizeof offsets_) / sizeof *offsets_;
1450 err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
1451 btr_cur, TRUE, thr, mtr);
1452 if (err != DB_SUCCESS) {
1453 mtr_commit(mtr);
1454 return(err);
1457 /* Mark as not-owned the externally stored fields which the new
1458 row inherits from the delete marked record: purge should not
1459 free those externally stored fields even if the delete marked
1460 record is removed from the index tree, or updated. */
1462 btr_cur_mark_extern_inherited_fields(
1463 btr_cur_get_rec(btr_cur),
1464 rec_get_offsets(btr_cur_get_rec(btr_cur),
1465 dict_table_get_first_index(table),
1466 offsets_, ULINT_UNDEFINED, &heap),
1467 node->update, mtr);
1468 if (check_ref) {
1469 /* NOTE that the following call loses
1470 the position of pcur ! */
1471 err = row_upd_check_references_constraints(
1472 node, pcur, table, index, thr, mtr);
1473 if (err != DB_SUCCESS) {
1474 mtr_commit(mtr);
1475 if (UNIV_LIKELY_NULL(heap)) {
1476 mem_heap_free(heap);
1478 return(err);
1484 mtr_commit(mtr);
1486 if (!heap) {
1487 heap = mem_heap_create(500);
1489 node->state = UPD_NODE_INSERT_CLUSTERED;
1491 entry = row_build_index_entry(node->row, index, heap);
1493 row_upd_index_replace_new_col_vals(entry, index, node->update, NULL);
1495 row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
1497 /* If we return from a lock wait, for example, we may have
1498 extern fields marked as not-owned in entry (marked in the
1499 if-branch above). We must unmark them. */
1501 btr_cur_unmark_dtuple_extern_fields(entry, node->ext_vec,
1502 node->n_ext_vec);
1503 /* We must mark non-updated extern fields in entry as inherited,
1504 so that a possible rollback will not free them */
1506 btr_cur_mark_dtuple_inherited_extern(entry, node->ext_vec,
1507 node->n_ext_vec,
1508 node->update);
1510 err = row_ins_index_entry(index, entry, node->ext_vec,
1511 node->n_ext_vec, thr);
1512 mem_heap_free(heap);
1514 return(err);
1517 /***************************************************************
1518 Updates a clustered index record of a row when the ordering fields do
1519 not change. */
1520 static
1521 ulint
1522 row_upd_clust_rec(
1523 /*==============*/
1524 /* out: DB_SUCCESS if operation successfully
1525 completed, else error code or DB_LOCK_WAIT */
1526 upd_node_t* node, /* in: row update node */
1527 dict_index_t* index, /* in: clustered index */
1528 que_thr_t* thr, /* in: query thread */
1529 mtr_t* mtr) /* in: mtr; gets committed here */
1531 big_rec_t* big_rec = NULL;
1532 btr_pcur_t* pcur;
1533 btr_cur_t* btr_cur;
1534 ulint err;
1536 ut_ad(node);
1537 ut_ad(index->type & DICT_CLUSTERED);
1539 pcur = node->pcur;
1540 btr_cur = btr_pcur_get_btr_cur(pcur);
1542 ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
1543 dict_table_is_comp(index->table)));
1545 /* Try optimistic updating of the record, keeping changes within
1546 the page; we do not check locks because we assume the x-lock on the
1547 record to update */
1549 if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
1550 err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG,
1551 btr_cur, node->update,
1552 node->cmpl_info, thr, mtr);
1553 } else {
1554 err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG,
1555 btr_cur, node->update,
1556 node->cmpl_info, thr, mtr);
1559 mtr_commit(mtr);
1561 if (err == DB_SUCCESS) {
1563 return(err);
1566 if (buf_LRU_buf_pool_running_out()) {
1568 return(DB_LOCK_TABLE_FULL);
1570 /* We may have to modify the tree structure: do a pessimistic descent
1571 down the index tree */
1573 mtr_start(mtr);
1575 /* NOTE: this transaction has an s-lock or x-lock on the record and
1576 therefore other transactions cannot modify the record when we have no
1577 latch on the page. In addition, we assume that other query threads of
1578 the same transaction do not modify the record in the meantime.
1579 Therefore we can assert that the restoration of the cursor succeeds. */
1581 ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
1583 ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
1584 dict_table_is_comp(index->table)));
1586 err = btr_cur_pessimistic_update(BTR_NO_LOCKING_FLAG, btr_cur,
1587 &big_rec, node->update,
1588 node->cmpl_info, thr, mtr);
1589 mtr_commit(mtr);
1591 if (err == DB_SUCCESS && big_rec) {
1592 mem_heap_t* heap = NULL;
1593 ulint offsets_[REC_OFFS_NORMAL_SIZE];
1594 rec_t* rec;
1595 *offsets_ = (sizeof offsets_) / sizeof *offsets_;
1597 DBUG_EXECUTE_IF(
1598 "row_upd_extern_checkpoint",
1599 log_make_checkpoint_at(ut_dulint_max, TRUE););
1601 mtr_start(mtr);
1602 ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
1603 rec = btr_cur_get_rec(btr_cur);
1604 DEBUG_SYNC_C("before_row_upd_extern");
1605 err = btr_store_big_rec_extern_fields(
1606 index, rec,
1607 rec_get_offsets(rec, index, offsets_,
1608 ULINT_UNDEFINED, &heap),
1609 big_rec, mtr);
1610 DEBUG_SYNC_C("after_row_upd_extern");
1611 if (UNIV_LIKELY_NULL(heap)) {
1612 mem_heap_free(heap);
1614 mtr_commit(mtr);
1617 if (big_rec) {
1618 dtuple_big_rec_free(big_rec);
1621 return(err);
1624 /***************************************************************
1625 Delete marks a clustered index record. */
1626 static
1627 ulint
1628 row_upd_del_mark_clust_rec(
1629 /*=======================*/
1630 /* out: DB_SUCCESS if operation successfully
1631 completed, else error code */
1632 upd_node_t* node, /* in: row update node */
1633 dict_index_t* index, /* in: clustered index */
1634 que_thr_t* thr, /* in: query thread */
1635 ibool check_ref,/* in: TRUE if index may be referenced in
1636 a foreign key constraint */
1637 mtr_t* mtr) /* in: mtr; gets committed here */
1639 btr_pcur_t* pcur;
1640 btr_cur_t* btr_cur;
1641 ulint err;
1643 ut_ad(node);
1644 ut_ad(index->type & DICT_CLUSTERED);
1645 ut_ad(node->is_delete);
1647 pcur = node->pcur;
1648 btr_cur = btr_pcur_get_btr_cur(pcur);
1650 /* Store row because we have to build also the secondary index
1651 entries */
1653 row_upd_store_row(node);
1655 /* Mark the clustered index record deleted; we do not have to check
1656 locks, because we assume that we have an x-lock on the record */
1658 err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
1659 btr_cur, TRUE, thr, mtr);
1660 if (err == DB_SUCCESS && check_ref) {
1661 /* NOTE that the following call loses the position of pcur ! */
1663 err = row_upd_check_references_constraints(node,
1664 pcur, index->table,
1665 index, thr, mtr);
1666 if (err != DB_SUCCESS) {
1667 mtr_commit(mtr);
1669 return(err);
1673 mtr_commit(mtr);
1675 return(err);
1678 /***************************************************************
1679 Updates the clustered index record. */
1680 static
1681 ulint
1682 row_upd_clust_step(
1683 /*===============*/
1684 /* out: DB_SUCCESS if operation successfully
1685 completed, DB_LOCK_WAIT in case of a lock wait,
1686 else error code */
1687 upd_node_t* node, /* in: row update node */
1688 que_thr_t* thr) /* in: query thread */
1690 dict_index_t* index;
1691 btr_pcur_t* pcur;
1692 ibool success;
1693 ibool check_ref;
1694 ulint err;
1695 mtr_t* mtr;
1696 mtr_t mtr_buf;
1697 rec_t* rec;
1698 mem_heap_t* heap = NULL;
1699 ulint offsets_[REC_OFFS_NORMAL_SIZE];
1700 const ulint* offsets;
1701 *offsets_ = (sizeof offsets_) / sizeof *offsets_;
1703 index = dict_table_get_first_index(node->table);
1705 check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr));
1707 pcur = node->pcur;
1709 /* We have to restore the cursor to its position */
1710 mtr = &mtr_buf;
1712 mtr_start(mtr);
1714 /* If the restoration does not succeed, then the same
1715 transaction has deleted the record on which the cursor was,
1716 and that is an SQL error. If the restoration succeeds, it may
1717 still be that the same transaction has successively deleted
1718 and inserted a record with the same ordering fields, but in
1719 that case we know that the transaction has at least an
1720 implicit x-lock on the record. */
1722 ut_a(pcur->rel_pos == BTR_PCUR_ON);
1724 success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr);
1726 if (!success) {
1727 err = DB_RECORD_NOT_FOUND;
1729 mtr_commit(mtr);
1731 return(err);
1734 /* If this is a row in SYS_INDEXES table of the data dictionary,
1735 then we have to free the file segments of the index tree associated
1736 with the index */
1738 if (node->is_delete
1739 && ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) {
1741 dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr);
1743 mtr_commit(mtr);
1745 mtr_start(mtr);
1747 success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
1748 mtr);
1749 if (!success) {
1750 err = DB_ERROR;
1752 mtr_commit(mtr);
1754 return(err);
1758 rec = btr_pcur_get_rec(pcur);
1759 offsets = rec_get_offsets(rec, index, offsets_,
1760 ULINT_UNDEFINED, &heap);
1762 if (!node->has_clust_rec_x_lock) {
1763 err = lock_clust_rec_modify_check_and_lock(
1764 0, rec, index, offsets, thr);
1765 if (err != DB_SUCCESS) {
1766 mtr_commit(mtr);
1767 goto exit_func;
1771 /* NOTE: the following function calls will also commit mtr */
1773 if (node->is_delete) {
1774 err = row_upd_del_mark_clust_rec(node, index, thr, check_ref,
1775 mtr);
1776 if (err == DB_SUCCESS) {
1777 node->state = UPD_NODE_UPDATE_ALL_SEC;
1778 node->index = dict_table_get_next_index(index);
1780 exit_func:
1781 if (UNIV_LIKELY_NULL(heap)) {
1782 mem_heap_free(heap);
1784 return(err);
1787 /* If the update is made for MySQL, we already have the update vector
1788 ready, else we have to do some evaluation: */
1790 if (!node->in_mysql_interface) {
1791 /* Copy the necessary columns from clust_rec and calculate the
1792 new values to set */
1793 row_upd_copy_columns(rec, offsets,
1794 UT_LIST_GET_FIRST(node->columns));
1795 row_upd_eval_new_vals(node->update);
1798 if (UNIV_LIKELY_NULL(heap)) {
1799 mem_heap_free(heap);
1802 if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
1804 err = row_upd_clust_rec(node, index, thr, mtr);
1805 return(err);
1808 row_upd_store_row(node);
1810 if (row_upd_changes_ord_field_binary(node->row, index, node->update)) {
1812 /* Update causes an ordering field (ordering fields within
1813 the B-tree) of the clustered index record to change: perform
1814 the update by delete marking and inserting.
1816 TODO! What to do to the 'Halloween problem', where an update
1817 moves the record forward in index so that it is again
1818 updated when the cursor arrives there? Solution: the
1819 read operation must check the undo record undo number when
1820 choosing records to update. MySQL solves now the problem
1821 externally! */
1823 err = row_upd_clust_rec_by_insert(node, index, thr, check_ref,
1824 mtr);
1825 if (err != DB_SUCCESS) {
1827 return(err);
1830 node->state = UPD_NODE_UPDATE_ALL_SEC;
1831 } else {
1832 err = row_upd_clust_rec(node, index, thr, mtr);
1834 if (err != DB_SUCCESS) {
1836 return(err);
1839 node->state = UPD_NODE_UPDATE_SOME_SEC;
1842 node->index = dict_table_get_next_index(index);
1844 return(err);
1847 /***************************************************************
1848 Updates the affected index records of a row. When the control is transferred
1849 to this node, we assume that we have a persistent cursor which was on a
1850 record, and the position of the cursor is stored in the cursor. */
1851 static
1852 ulint
1853 row_upd(
1854 /*====*/
1855 /* out: DB_SUCCESS if operation successfully
1856 completed, else error code or DB_LOCK_WAIT */
1857 upd_node_t* node, /* in: row update node */
1858 que_thr_t* thr) /* in: query thread */
1860 ulint err = DB_SUCCESS;
1862 ut_ad(node && thr);
1864 if (UNIV_LIKELY(node->in_mysql_interface)) {
1866 /* We do not get the cmpl_info value from the MySQL
1867 interpreter: we must calculate it on the fly: */
1869 if (node->is_delete
1870 || row_upd_changes_some_index_ord_field_binary(
1871 node->table, node->update)) {
1872 node->cmpl_info = 0;
1873 } else {
1874 node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
1878 if (node->state == UPD_NODE_UPDATE_CLUSTERED
1879 || node->state == UPD_NODE_INSERT_CLUSTERED) {
1881 err = row_upd_clust_step(node, thr);
1883 if (err != DB_SUCCESS) {
1885 goto function_exit;
1889 if (!node->is_delete && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
1891 goto function_exit;
1894 while (node->index != NULL) {
1895 err = row_upd_sec_step(node, thr);
1897 if (err != DB_SUCCESS) {
1899 goto function_exit;
1902 node->index = dict_table_get_next_index(node->index);
1905 function_exit:
1906 if (err == DB_SUCCESS) {
1907 /* Do some cleanup */
1909 if (node->row != NULL) {
1910 node->row = NULL;
1911 node->n_ext_vec = 0;
1912 mem_heap_empty(node->heap);
1915 node->state = UPD_NODE_UPDATE_CLUSTERED;
1918 return(err);
1921 /***************************************************************
1922 Updates a row in a table. This is a high-level function used in SQL execution
1923 graphs. */
1925 que_thr_t*
1926 row_upd_step(
1927 /*=========*/
1928 /* out: query thread to run next or NULL */
1929 que_thr_t* thr) /* in: query thread */
1931 upd_node_t* node;
1932 sel_node_t* sel_node;
1933 que_node_t* parent;
1934 ulint err = DB_SUCCESS;
1935 trx_t* trx;
1937 ut_ad(thr);
1939 trx = thr_get_trx(thr);
1941 trx_start_if_not_started(trx);
1943 node = thr->run_node;
1945 sel_node = node->select;
1947 parent = que_node_get_parent(node);
1949 ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
1951 if (thr->prev_node == parent) {
1952 node->state = UPD_NODE_SET_IX_LOCK;
1955 if (node->state == UPD_NODE_SET_IX_LOCK) {
1957 if (!node->has_clust_rec_x_lock) {
1958 /* It may be that the current session has not yet
1959 started its transaction, or it has been committed: */
1961 err = lock_table(0, node->table, LOCK_IX, thr);
1963 if (err != DB_SUCCESS) {
1965 goto error_handling;
1969 node->state = UPD_NODE_UPDATE_CLUSTERED;
1971 if (node->searched_update) {
1972 /* Reset the cursor */
1973 sel_node->state = SEL_NODE_OPEN;
1975 /* Fetch a row to update */
1977 thr->run_node = sel_node;
1979 return(thr);
1983 /* sel_node is NULL if we are in the MySQL interface */
1985 if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
1987 if (!node->searched_update) {
1988 /* An explicit cursor should be positioned on a row
1989 to update */
1991 ut_error;
1993 err = DB_ERROR;
1995 goto error_handling;
1998 ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
2000 /* No more rows to update, or the select node performed the
2001 updates directly in-place */
2003 thr->run_node = parent;
2005 return(thr);
2008 /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
2010 err = row_upd(node, thr);
2012 error_handling:
2013 trx->error_state = err;
2015 if (err != DB_SUCCESS) {
2016 return(NULL);
2019 /* DO THE TRIGGER ACTIONS HERE */
2021 if (node->searched_update) {
2022 /* Fetch next row to update */
2024 thr->run_node = sel_node;
2025 } else {
2026 /* It was an explicit cursor update */
2028 thr->run_node = parent;
2031 node->state = UPD_NODE_UPDATE_CLUSTERED;
2033 return(thr);
2036 /*************************************************************************
2037 Performs an in-place update for the current clustered index record in
2038 select. */
2040 void
2041 row_upd_in_place_in_select(
2042 /*=======================*/
2043 sel_node_t* sel_node, /* in: select node */
2044 que_thr_t* thr, /* in: query thread */
2045 mtr_t* mtr) /* in: mtr */
2047 upd_node_t* node;
2048 btr_pcur_t* pcur;
2049 btr_cur_t* btr_cur;
2050 #ifdef UNIV_DEBUG
2051 ulint err;
2052 #endif /* UNIV_DEBUG */
2053 mem_heap_t* heap = NULL;
2054 ulint offsets_[REC_OFFS_NORMAL_SIZE];
2055 *offsets_ = (sizeof offsets_) / sizeof *offsets_;
2057 ut_ad(sel_node->select_will_do_update);
2058 ut_ad(sel_node->latch_mode == BTR_MODIFY_LEAF);
2059 ut_ad(sel_node->asc);
2061 node = que_node_get_parent(sel_node);
2063 ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
2065 pcur = node->pcur;
2066 btr_cur = btr_pcur_get_btr_cur(pcur);
2068 /* Copy the necessary columns from clust_rec and calculate the new
2069 values to set */
2071 row_upd_copy_columns(btr_pcur_get_rec(pcur),
2072 rec_get_offsets(btr_pcur_get_rec(pcur),
2073 btr_cur->index, offsets_,
2074 ULINT_UNDEFINED, &heap),
2075 UT_LIST_GET_FIRST(node->columns));
2076 if (UNIV_LIKELY_NULL(heap)) {
2077 mem_heap_free(heap);
2079 row_upd_eval_new_vals(node->update);
2081 ut_ad(!rec_get_deleted_flag(
2082 btr_pcur_get_rec(pcur),
2083 dict_table_is_comp(btr_cur->index->table)));
2085 ut_ad(node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE);
2086 ut_ad(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE);
2087 ut_ad(node->select_will_do_update);
2089 #ifdef UNIV_DEBUG
2090 err =
2091 #endif /* UNIV_DEBUG */
2092 btr_cur_update_in_place(BTR_NO_LOCKING_FLAG, btr_cur,
2093 node->update, node->cmpl_info,
2094 thr, mtr);
2095 ut_ad(err == DB_SUCCESS);