1
/******************************************************
6
Created 12/27/1996 Heikki Tuuri
7
*******************************************************/
15
#include "dict0dict.h"
16
#include "dict0boot.h"
17
#include "dict0crea.h"
18
#include "mach0data.h"
27
#include "lock0lock.h"
30
#include "eval0eval.h"
34
/* What kind of latch and lock can we assume when the control comes to
35
-------------------------------------------------------------------
38
Efficiency of massive updates would require keeping an x-latch on a
39
clustered index page through many updates, and not setting an explicit
40
x-lock on clustered index records, as they anyway will get an implicit
41
x-lock when they are updated. A problem is that the read nodes in the
42
graph should know that they must keep the latch when passing the control
43
up to the update node, and not set any record lock on the record which
44
will be updated. Another problem occurs if the execution is stopped,
45
as the kernel switches to another query thread, or the transaction must
46
wait for a lock. Then we should be able to release the latch and, maybe,
47
acquire an explicit x-lock on the record.
48
Because this seems too complicated, we conclude that the less
49
efficient solution of releasing all the latches when the control is
50
transferred to another node, and acquiring explicit x-locks, is better. */
52
/* How is a delete performed? If there is a delete without an
53
explicit cursor, i.e., a searched delete, there are at least
54
two different situations:
55
the implicit select cursor may run on (1) the clustered index or
56
on (2) a secondary index. The delete is performed by setting
57
the delete bit in the record and substituting the id of the
58
deleting transaction for the original trx id, and substituting a
59
new roll ptr for previous roll ptr. The old trx id and roll ptr
60
are saved in the undo log record. Thus, no physical changes occur
61
in the index tree structure at the time of the delete. Only
62
when the undo log is purged, the index records will be physically
63
deleted from the index trees.
65
The query graph executing a searched delete would consist of
66
a delete node which has as a subtree a select subgraph.
67
The select subgraph should return a (persistent) cursor
68
in the clustered index, placed on page which is x-latched.
69
The delete node should look for all secondary index records for
70
this clustered index entry and mark them as deleted. When is
71
the x-latch freed? The most efficient way for performing a
72
searched delete is obviously to keep the x-latch for several
73
steps of query graph execution. */
75
/***************************************************************
76
Checks if an update vector changes some of the first ordering fields of an
77
index record. This is only used in foreign key checks and we can assume
78
that index does not contain column prefixes. */
81
row_upd_changes_first_fields_binary(
82
/*================================*/
83
/* out: TRUE if changes */
84
dtuple_t* entry, /* in: old value of index entry */
85
dict_index_t* index, /* in: index of entry */
86
upd_t* update, /* in: update vector for the row */
87
ulint n); /* in: how many first fields to check */
90
/*************************************************************************
91
Checks if index currently is mentioned as a referenced index in a foreign
95
row_upd_index_is_referenced(
96
/*========================*/
97
/* out: TRUE if referenced; NOTE that since
98
we do not hold dict_operation_lock
99
when leaving the function, it may be that
100
the referencing table has been dropped when
101
we leave this function: this function is only
102
for heuristic use! */
103
dict_index_t* index, /* in: index */
104
trx_t* trx) /* in: transaction */
106
dict_table_t* table = index->table;
107
dict_foreign_t* foreign;
108
ibool froze_data_dict = FALSE;
110
if (!UT_LIST_GET_FIRST(table->referenced_list)) {
115
if (trx->dict_operation_lock_mode == 0) {
116
row_mysql_freeze_data_dictionary(trx);
117
froze_data_dict = TRUE;
120
foreign = UT_LIST_GET_FIRST(table->referenced_list);
123
if (foreign->referenced_index == index) {
125
if (froze_data_dict) {
126
row_mysql_unfreeze_data_dictionary(trx);
132
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
135
if (froze_data_dict) {
136
row_mysql_unfreeze_data_dictionary(trx);
142
/*************************************************************************
143
Checks if possible foreign key constraints hold after a delete of the record
144
under pcur. NOTE that this function will temporarily commit mtr and lose the
148
row_upd_check_references_constraints(
149
/*=================================*/
150
/* out: DB_SUCCESS or an error code */
151
upd_node_t* node, /* in: row update node */
152
btr_pcur_t* pcur, /* in: cursor positioned on a record; NOTE: the
153
cursor position is lost in this function! */
154
dict_table_t* table, /* in: table in question */
155
dict_index_t* index, /* in: index of the cursor */
156
que_thr_t* thr, /* in: query thread */
157
mtr_t* mtr) /* in: mtr */
159
dict_foreign_t* foreign;
165
ibool got_s_lock = FALSE;
167
if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) {
172
trx = thr_get_trx(thr);
174
rec = btr_pcur_get_rec(pcur);
176
heap = mem_heap_create(500);
178
entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
184
if (trx->dict_operation_lock_mode == 0) {
187
row_mysql_freeze_data_dictionary(trx);
190
foreign = UT_LIST_GET_FIRST(table->referenced_list);
193
/* Note that we may have an update which updates the index
194
record, but does NOT update the first fields which are
195
referenced in a foreign key constraint. Then the update does
196
NOT break the constraint. */
198
if (foreign->referenced_index == index
200
|| row_upd_changes_first_fields_binary(
201
entry, index, node->update,
202
foreign->n_fields))) {
204
if (foreign->foreign_table == NULL) {
205
dict_table_get(foreign->foreign_table_name,
209
if (foreign->foreign_table) {
210
mutex_enter(&(dict_sys->mutex));
212
(foreign->foreign_table
213
->n_foreign_key_checks_running)++;
215
mutex_exit(&(dict_sys->mutex));
218
/* NOTE that if the thread ends up waiting for a lock
219
we will release dict_operation_lock temporarily!
220
But the counter on the table protects 'foreign' from
221
being dropped while the check is running. */
223
err = row_ins_check_foreign_constraint(
224
FALSE, foreign, table, entry, thr);
226
if (foreign->foreign_table) {
227
mutex_enter(&(dict_sys->mutex));
229
ut_a(foreign->foreign_table
230
->n_foreign_key_checks_running > 0);
232
(foreign->foreign_table
233
->n_foreign_key_checks_running)--;
235
mutex_exit(&(dict_sys->mutex));
238
if (err != DB_SUCCESS) {
240
row_mysql_unfreeze_data_dictionary(
250
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
254
row_mysql_unfreeze_data_dictionary(trx);
262
/*************************************************************************
263
Creates an update node for a query graph. */
268
/* out, own: update node */
269
mem_heap_t* heap) /* in: mem heap where created */
273
node = mem_heap_alloc(heap, sizeof(upd_node_t));
274
node->common.type = QUE_NODE_UPDATE;
276
node->state = UPD_NODE_UPDATE_CLUSTERED;
277
node->select_will_do_update = FALSE;
278
node->in_mysql_interface = FALSE;
281
node->ext_vec = NULL;
285
node->foreign = NULL;
286
node->cascade_heap = NULL;
287
node->cascade_node = NULL;
291
node->heap = mem_heap_create(128);
292
node->magic_n = UPD_NODE_MAGIC_N;
299
/*************************************************************************
300
Updates the trx id and roll ptr field in a clustered index record in database
304
row_upd_rec_sys_fields_in_recovery(
305
/*===============================*/
306
rec_t* rec, /* in: record */
307
const ulint* offsets,/* in: array returned by rec_get_offsets() */
308
ulint pos, /* in: TRX_ID position in rec */
309
dulint trx_id, /* in: transaction id */
310
dulint roll_ptr)/* in: roll ptr of the undo log record */
315
field = rec_get_nth_field(rec, offsets, pos, &len);
316
ut_ad(len == DATA_TRX_ID_LEN);
317
trx_write_trx_id(field, trx_id);
319
field = rec_get_nth_field(rec, offsets, pos + 1, &len);
320
ut_ad(len == DATA_ROLL_PTR_LEN);
321
trx_write_roll_ptr(field, roll_ptr);
324
/*************************************************************************
325
Sets the trx id or roll ptr field of a clustered index entry. */
328
row_upd_index_entry_sys_field(
329
/*==========================*/
330
dtuple_t* entry, /* in: index entry, where the memory buffers
331
for sys fields are already allocated:
332
the function just copies the new values to
334
dict_index_t* index, /* in: clustered index */
335
ulint type, /* in: DATA_TRX_ID or DATA_ROLL_PTR */
336
dulint val) /* in: value to write */
342
ut_ad(index->type & DICT_CLUSTERED);
344
pos = dict_index_get_sys_col_pos(index, type);
346
dfield = dtuple_get_nth_field(entry, pos);
347
field = dfield_get_data(dfield);
349
if (type == DATA_TRX_ID) {
350
trx_write_trx_id(field, val);
352
ut_ad(type == DATA_ROLL_PTR);
353
trx_write_roll_ptr(field, val);
357
/***************************************************************
358
Returns TRUE if row update changes size of some field in index or if some
359
field to be updated is stored externally in rec or update. */
362
row_upd_changes_field_size_or_external(
363
/*===================================*/
364
/* out: TRUE if the update changes the size of
365
some field in index or the field is external
367
dict_index_t* index, /* in: index */
368
const ulint* offsets,/* in: rec_get_offsets(rec, index) */
369
upd_t* update) /* in: update vector */
371
upd_field_t* upd_field;
378
ut_ad(rec_offs_validate(NULL, index, offsets));
379
n_fields = upd_get_n_fields(update);
381
for (i = 0; i < n_fields; i++) {
382
upd_field = upd_get_nth_field(update, i);
384
new_val = &(upd_field->new_val);
385
new_len = new_val->len;
387
if (new_len == UNIV_SQL_NULL && !rec_offs_comp(offsets)) {
388
/* A bug fixed on Dec 31st, 2004: we looked at the
389
SQL NULL size from the wrong field! We may backport
390
this fix also to 4.0. The merge to 5.0 will be made
391
manually immediately after we commit this to 4.1. */
393
new_len = dict_col_get_sql_null_size(
394
dict_index_get_nth_col(index,
395
upd_field->field_no));
398
old_len = rec_offs_nth_size(offsets, upd_field->field_no);
400
if (rec_offs_comp(offsets)
401
&& rec_offs_nth_sql_null(offsets,
402
upd_field->field_no)) {
403
/* Note that in the compact table format, for a
404
variable length field, an SQL NULL will use zero
405
bytes in the offset array at the start of the physical
406
record, but a zero-length value (empty string) will
407
use one byte! Thus, we cannot use update-in-place
408
if we update an SQL NULL varchar to an empty string! */
410
old_len = UNIV_SQL_NULL;
413
if (old_len != new_len) {
418
if (rec_offs_nth_extern(offsets, upd_field->field_no)) {
423
if (upd_field->extern_storage) {
432
/***************************************************************
433
Replaces the new column values stored in the update vector to the record
434
given. No field size changes are allowed. This function is used only for
438
row_upd_rec_in_place(
439
/*=================*/
440
rec_t* rec, /* in/out: record where replaced */
441
const ulint* offsets,/* in: array returned by rec_get_offsets() */
442
upd_t* update) /* in: update vector */
444
upd_field_t* upd_field;
449
ut_ad(rec_offs_validate(rec, NULL, offsets));
451
rec_set_info_bits(rec, rec_offs_comp(offsets), update->info_bits);
453
n_fields = upd_get_n_fields(update);
455
for (i = 0; i < n_fields; i++) {
456
upd_field = upd_get_nth_field(update, i);
457
new_val = &(upd_field->new_val);
459
rec_set_nth_field(rec, offsets, upd_field->field_no,
460
dfield_get_data(new_val),
461
dfield_get_len(new_val));
465
/*************************************************************************
466
Writes into the redo log the values of trx id and roll ptr and enough info
467
to determine their positions within a clustered index record. */
470
row_upd_write_sys_vals_to_log(
471
/*==========================*/
472
/* out: new pointer to mlog */
473
dict_index_t* index, /* in: clustered index */
474
trx_t* trx, /* in: transaction */
475
dulint roll_ptr,/* in: roll ptr of the undo log record */
476
byte* log_ptr,/* pointer to a buffer of size > 20 opened
478
mtr_t* mtr __attribute__((unused))) /* in: mtr */
480
ut_ad(index->type & DICT_CLUSTERED);
483
log_ptr += mach_write_compressed(log_ptr,
484
dict_index_get_sys_col_pos(
485
index, DATA_TRX_ID));
487
trx_write_roll_ptr(log_ptr, roll_ptr);
488
log_ptr += DATA_ROLL_PTR_LEN;
490
log_ptr += mach_dulint_write_compressed(log_ptr, trx->id);
495
/*************************************************************************
496
Parses the log data of system field values. */
499
row_upd_parse_sys_vals(
500
/*===================*/
501
/* out: log data end or NULL */
502
byte* ptr, /* in: buffer */
503
byte* end_ptr,/* in: buffer end */
504
ulint* pos, /* out: TRX_ID position in record */
505
dulint* trx_id, /* out: trx id */
506
dulint* roll_ptr)/* out: roll ptr */
508
ptr = mach_parse_compressed(ptr, end_ptr, pos);
515
if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {
520
*roll_ptr = trx_read_roll_ptr(ptr);
521
ptr += DATA_ROLL_PTR_LEN;
523
ptr = mach_dulint_parse_compressed(ptr, end_ptr, trx_id);
528
/***************************************************************
529
Writes to the redo log the new values of the fields occurring in the index. */
532
row_upd_index_write_log(
533
/*====================*/
534
upd_t* update, /* in: update vector */
535
byte* log_ptr,/* in: pointer to mlog buffer: must contain at least
536
MLOG_BUF_MARGIN bytes of free space; the buffer is
537
closed within this function */
538
mtr_t* mtr) /* in: mtr into whose log to write */
540
upd_field_t* upd_field;
547
n_fields = upd_get_n_fields(update);
549
buf_end = log_ptr + MLOG_BUF_MARGIN;
551
mach_write_to_1(log_ptr, update->info_bits);
553
log_ptr += mach_write_compressed(log_ptr, n_fields);
555
for (i = 0; i < n_fields; i++) {
557
#if MLOG_BUF_MARGIN <= 30
558
# error "MLOG_BUF_MARGIN <= 30"
561
if (log_ptr + 30 > buf_end) {
562
mlog_close(mtr, log_ptr);
564
log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
565
buf_end = log_ptr + MLOG_BUF_MARGIN;
568
upd_field = upd_get_nth_field(update, i);
570
new_val = &(upd_field->new_val);
574
log_ptr += mach_write_compressed(log_ptr, upd_field->field_no);
575
log_ptr += mach_write_compressed(log_ptr, len);
577
if (len != UNIV_SQL_NULL) {
578
if (log_ptr + len < buf_end) {
579
ut_memcpy(log_ptr, new_val->data, len);
583
mlog_close(mtr, log_ptr);
585
mlog_catenate_string(mtr, new_val->data, len);
587
log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
588
buf_end = log_ptr + MLOG_BUF_MARGIN;
593
mlog_close(mtr, log_ptr);
596
/*************************************************************************
597
Parses the log data written by row_upd_index_write_log. */
602
/* out: log data end or NULL */
603
byte* ptr, /* in: buffer */
604
byte* end_ptr,/* in: buffer end */
605
mem_heap_t* heap, /* in: memory heap where update vector is
607
upd_t** update_out)/* out: update vector */
610
upd_field_t* upd_field;
618
if (end_ptr < ptr + 1) {
623
info_bits = mach_read_from_1(ptr);
625
ptr = mach_parse_compressed(ptr, end_ptr, &n_fields);
632
update = upd_create(n_fields, heap);
633
update->info_bits = info_bits;
635
for (i = 0; i < n_fields; i++) {
636
upd_field = upd_get_nth_field(update, i);
637
new_val = &(upd_field->new_val);
639
ptr = mach_parse_compressed(ptr, end_ptr,
640
&(upd_field->field_no));
646
ptr = mach_parse_compressed(ptr, end_ptr, &len);
655
if (len != UNIV_SQL_NULL) {
657
if (end_ptr < ptr + len) {
661
buf = mem_heap_alloc(heap, len);
662
ut_memcpy(buf, ptr, len);
671
*update_out = update;
676
/*******************************************************************
677
Returns TRUE if ext_vec contains i. */
680
upd_ext_vec_contains(
681
/*=================*/
682
/* out: TRUE if i is in ext_vec */
683
ulint* ext_vec, /* in: array of indexes or NULL */
684
ulint n_ext_vec, /* in: number of numbers in ext_vec */
685
ulint i) /* in: a number */
689
if (ext_vec == NULL) {
694
for (j = 0; j < n_ext_vec; j++) {
695
if (ext_vec[j] == i) {
704
/*******************************************************************
705
Builds an update vector from those fields which in a secondary index entry
706
differ from a record that has the equal ordering fields. NOTE: we compare
707
the fields as binary strings! */
710
row_upd_build_sec_rec_difference_binary(
711
/*====================================*/
712
/* out, own: update vector of differing
714
dict_index_t* index, /* in: index */
715
dtuple_t* entry, /* in: entry to insert */
716
rec_t* rec, /* in: secondary index record */
717
trx_t* trx, /* in: transaction */
718
mem_heap_t* heap) /* in: memory heap from which allocated */
720
upd_field_t* upd_field;
727
ulint offsets_[REC_OFFS_SMALL_SIZE];
728
const ulint* offsets;
729
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
731
/* This function is used only for a secondary index */
732
ut_a(0 == (index->type & DICT_CLUSTERED));
734
update = upd_create(dtuple_get_n_fields(entry), heap);
737
offsets = rec_get_offsets(rec, index, offsets_,
738
ULINT_UNDEFINED, &heap);
740
for (i = 0; i < dtuple_get_n_fields(entry); i++) {
742
data = rec_get_nth_field(rec, offsets, i, &len);
744
dfield = dtuple_get_nth_field(entry, i);
746
/* NOTE that it may be that len != dfield_get_len(dfield) if we
747
are updating in a character set and collation where strings of
748
different length can be equal in an alphabetical comparison,
749
and also in the case where we have a column prefix index
750
and the last characters in the index field are spaces; the
751
latter case probably caused the assertion failures reported at
752
row0upd.c line 713 in versions 4.0.14 - 4.0.16. */
754
/* NOTE: we compare the fields as binary strings!
757
if (!dfield_data_is_binary_equal(dfield, len, data)) {
759
upd_field = upd_get_nth_field(update, n_diff);
761
dfield_copy(&(upd_field->new_val), dfield);
763
upd_field_set_field_no(upd_field, i, index, trx);
765
upd_field->extern_storage = FALSE;
771
update->n_fields = n_diff;
776
/*******************************************************************
777
Builds an update vector from those fields, excluding the roll ptr and
778
trx id fields, which in an index entry differ from a record that has
779
the equal ordering fields. NOTE: we compare the fields as binary strings! */
782
row_upd_build_difference_binary(
783
/*============================*/
784
/* out, own: update vector of differing
785
fields, excluding roll ptr and trx id */
786
dict_index_t* index, /* in: clustered index */
787
dtuple_t* entry, /* in: entry to insert */
788
ulint* ext_vec,/* in: array containing field numbers of
789
externally stored fields in entry, or NULL */
790
ulint n_ext_vec,/* in: number of fields in ext_vec */
791
rec_t* rec, /* in: clustered index record */
792
trx_t* trx, /* in: transaction */
793
mem_heap_t* heap) /* in: memory heap from which allocated */
795
upd_field_t* upd_field;
805
ulint offsets_[REC_OFFS_NORMAL_SIZE];
806
const ulint* offsets;
807
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
809
/* This function is used only for a clustered index */
810
ut_a(index->type & DICT_CLUSTERED);
812
update = upd_create(dtuple_get_n_fields(entry), heap);
816
roll_ptr_pos = dict_index_get_sys_col_pos(index, DATA_ROLL_PTR);
817
trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
819
offsets = rec_get_offsets(rec, index, offsets_,
820
ULINT_UNDEFINED, &heap);
822
for (i = 0; i < dtuple_get_n_fields(entry); i++) {
824
data = rec_get_nth_field(rec, offsets, i, &len);
826
dfield = dtuple_get_nth_field(entry, i);
828
/* NOTE: we compare the fields as binary strings!
831
if (i == trx_id_pos || i == roll_ptr_pos) {
836
extern_bit = upd_ext_vec_contains(ext_vec, n_ext_vec, i);
838
if (UNIV_UNLIKELY(extern_bit
839
== (ibool)!rec_offs_nth_extern(offsets, i))
840
|| !dfield_data_is_binary_equal(dfield, len, data)) {
842
upd_field = upd_get_nth_field(update, n_diff);
844
dfield_copy(&(upd_field->new_val), dfield);
846
upd_field_set_field_no(upd_field, i, index, trx);
848
upd_field->extern_storage = extern_bit;
856
update->n_fields = n_diff;
861
/***************************************************************
862
Replaces the new column values stored in the update vector to the index entry
866
row_upd_index_replace_new_col_vals_index_pos(
867
/*=========================================*/
868
dtuple_t* entry, /* in/out: index entry where replaced */
869
dict_index_t* index, /* in: index; NOTE that this may also be a
870
non-clustered index */
871
upd_t* update, /* in: an update vector built for the index so
872
that the field number in an upd_field is the
875
/* in: if TRUE, limit the replacement to
876
ordering fields of index; note that this
877
does not work for non-clustered indexes. */
878
mem_heap_t* heap) /* in: memory heap to which we allocate and
879
copy the new values, set this as NULL if you
880
do not want allocation */
883
upd_field_t* upd_field;
892
dtuple_set_info_bits(entry, update->info_bits);
895
n_fields = dict_index_get_n_unique(index);
897
n_fields = dict_index_get_n_fields(index);
900
for (j = 0; j < n_fields; j++) {
902
field = dict_index_get_nth_field(index, j);
904
for (i = 0; i < upd_get_n_fields(update); i++) {
906
upd_field = upd_get_nth_field(update, i);
908
if (upd_field->field_no == j) {
910
dfield = dtuple_get_nth_field(entry, j);
912
new_val = &(upd_field->new_val);
914
dfield_set_data(dfield, new_val->data,
916
if (heap && new_val->len != UNIV_SQL_NULL) {
917
dfield->data = mem_heap_alloc(
919
ut_memcpy(dfield->data, new_val->data,
923
if (field->prefix_len > 0
924
&& new_val->len != UNIV_SQL_NULL) {
926
const dict_col_t* col
927
= dict_field_get_col(field);
930
= dtype_get_at_most_n_mbchars(
943
/***************************************************************
944
Replaces the new column values stored in the update vector to the index entry
948
row_upd_index_replace_new_col_vals(
949
/*===============================*/
950
dtuple_t* entry, /* in/out: index entry where replaced */
951
dict_index_t* index, /* in: index; NOTE that this may also be a
952
non-clustered index */
953
upd_t* update, /* in: an update vector built for the
954
CLUSTERED index so that the field number in
955
an upd_field is the clustered index position */
956
mem_heap_t* heap) /* in: memory heap to which we allocate and
957
copy the new values, set this as NULL if you
958
do not want allocation */
960
upd_field_t* upd_field;
965
dict_index_t* clust_index;
969
clust_index = dict_table_get_first_index(index->table);
971
dtuple_set_info_bits(entry, update->info_bits);
973
for (j = 0; j < dict_index_get_n_fields(index); j++) {
976
dict_field_t* field = dict_index_get_nth_field(index, j);
978
clust_pos = dict_col_get_clust_pos(field->col, clust_index);
980
for (i = 0; i < upd_get_n_fields(update); i++) {
982
upd_field = upd_get_nth_field(update, i);
984
if (upd_field->field_no == clust_pos) {
986
dfield = dtuple_get_nth_field(entry, j);
988
new_val = &(upd_field->new_val);
990
dfield_set_data(dfield, new_val->data,
992
if (heap && new_val->len != UNIV_SQL_NULL) {
993
dfield->data = mem_heap_alloc(
995
ut_memcpy(dfield->data, new_val->data,
999
if (field->prefix_len > 0
1000
&& new_val->len != UNIV_SQL_NULL) {
1002
const dict_col_t* col
1003
= dict_field_get_col(field);
1006
= dtype_get_at_most_n_mbchars(
1019
/***************************************************************
1020
Checks if an update vector changes an ordering field of an index record.
1021
This function is fast if the update vector is short or the number of ordering
1022
fields in the index is small. Otherwise, this can be quadratic.
1023
NOTE: we compare the fields as binary strings! */
1026
row_upd_changes_ord_field_binary(
1027
/*=============================*/
1028
/* out: TRUE if update vector changes
1029
an ordering field in the index record;
1030
NOTE: the fields are compared as binary
1032
dtuple_t* row, /* in: old value of row, or NULL if the
1033
row and the data values in update are not
1034
known when this function is called, e.g., at
1036
dict_index_t* index, /* in: index of the record */
1037
upd_t* update) /* in: update vector for the row; NOTE: the
1038
field numbers in this MUST be clustered index
1044
dict_index_t* clust_index;
1046
ut_ad(update && index);
1048
n_unique = dict_index_get_n_unique(index);
1049
n_upd_fields = upd_get_n_fields(update);
1051
clust_index = dict_table_get_first_index(index->table);
1053
for (i = 0; i < n_unique; i++) {
1055
const dict_field_t* ind_field;
1056
const dict_col_t* col;
1060
ind_field = dict_index_get_nth_field(index, i);
1061
col = dict_field_get_col(ind_field);
1062
col_pos = dict_col_get_clust_pos(col, clust_index);
1063
col_no = dict_col_get_no(col);
1065
for (j = 0; j < n_upd_fields; j++) {
1067
upd_field_t* upd_field
1068
= upd_get_nth_field(update, j);
1070
/* Note that if the index field is a column prefix
1071
then it may be that row does not contain an externally
1072
stored part of the column value, and we cannot compare
1075
if (col_pos == upd_field->field_no
1077
|| ind_field->prefix_len > 0
1078
|| !dfield_datas_are_binary_equal(
1079
dtuple_get_nth_field(row, col_no),
1080
&(upd_field->new_val)))) {
1090
/***************************************************************
1091
Checks if an update vector changes an ordering field of an index record.
1092
NOTE: we compare the fields as binary strings! */
1095
row_upd_changes_some_index_ord_field_binary(
1096
/*========================================*/
1097
/* out: TRUE if update vector may change
1098
an ordering field in an index record */
1099
dict_table_t* table, /* in: table */
1100
upd_t* update) /* in: update vector for the row */
1102
upd_field_t* upd_field;
1103
dict_index_t* index;
1106
index = dict_table_get_first_index(table);
1108
for (i = 0; i < upd_get_n_fields(update); i++) {
1110
upd_field = upd_get_nth_field(update, i);
1112
if (dict_field_get_col(dict_index_get_nth_field(
1113
index, upd_field->field_no))
1123
/***************************************************************
1124
Checks if an update vector changes some of the first ordering fields of an
1125
index record. This is only used in foreign key checks and we can assume
1126
that index does not contain column prefixes. */
1129
row_upd_changes_first_fields_binary(
1130
/*================================*/
1131
/* out: TRUE if changes */
1132
dtuple_t* entry, /* in: index entry */
1133
dict_index_t* index, /* in: index of entry */
1134
upd_t* update, /* in: update vector for the row */
1135
ulint n) /* in: how many first fields to check */
1139
dict_index_t* clust_index;
1141
ut_ad(update && index);
1142
ut_ad(n <= dict_index_get_n_fields(index));
1144
n_upd_fields = upd_get_n_fields(update);
1145
clust_index = dict_table_get_first_index(index->table);
1147
for (i = 0; i < n; i++) {
1149
const dict_field_t* ind_field;
1150
const dict_col_t* col;
1153
ind_field = dict_index_get_nth_field(index, i);
1154
col = dict_field_get_col(ind_field);
1155
col_pos = dict_col_get_clust_pos(col, clust_index);
1157
ut_a(ind_field->prefix_len == 0);
1159
for (j = 0; j < n_upd_fields; j++) {
1161
upd_field_t* upd_field
1162
= upd_get_nth_field(update, j);
1164
if (col_pos == upd_field->field_no
1165
&& !dfield_datas_are_binary_equal(
1166
dtuple_get_nth_field(entry, i),
1167
&(upd_field->new_val))) {
1177
/*************************************************************************
1178
Copies the column values from a record. */
1181
row_upd_copy_columns(
1182
/*=================*/
1183
rec_t* rec, /* in: record in a clustered index */
1184
const ulint* offsets,/* in: array returned by rec_get_offsets() */
1185
sym_node_t* column) /* in: first column in a column list, or
1192
data = rec_get_nth_field(rec, offsets,
1193
column->field_nos[SYM_CLUST_FIELD_NO],
1195
eval_node_copy_and_alloc_val(column, data, len);
1197
column = UT_LIST_GET_NEXT(col_var_list, column);
1201
/*************************************************************************
1202
Calculates the new values for fields to update. Note that row_upd_copy_columns
1203
must have been called first. */
1206
row_upd_eval_new_vals(
1207
/*==================*/
1208
upd_t* update) /* in: update vector */
1211
upd_field_t* upd_field;
1215
n_fields = upd_get_n_fields(update);
1217
for (i = 0; i < n_fields; i++) {
1218
upd_field = upd_get_nth_field(update, i);
1220
exp = upd_field->exp;
1224
dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
1228
/***************************************************************
1229
Stores to the heap the row on which the node->pcur is positioned. */
1234
upd_node_t* node) /* in: row update node */
1236
dict_index_t* clust_index;
1239
mem_heap_t* heap = NULL;
1240
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1241
const ulint* offsets;
1242
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1244
ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
1246
if (node->row != NULL) {
1247
mem_heap_empty(node->heap);
1251
clust_index = dict_table_get_first_index(node->table);
1253
rec = btr_pcur_get_rec(node->pcur);
1255
offsets = rec_get_offsets(rec, clust_index, offsets_,
1256
ULINT_UNDEFINED, &heap);
1257
node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
1259
node->ext_vec = mem_heap_alloc(node->heap, sizeof(ulint)
1260
* rec_offs_n_fields(offsets));
1261
if (node->is_delete) {
1264
update = node->update;
1267
node->n_ext_vec = btr_push_update_extern_fields(node->ext_vec,
1269
if (UNIV_LIKELY_NULL(heap)) {
1270
mem_heap_free(heap);
1274
/***************************************************************
1275
Updates a secondary index entry of a row. */
1278
row_upd_sec_index_entry(
1279
/*====================*/
1280
/* out: DB_SUCCESS if operation successfully
1281
completed, else error code or DB_LOCK_WAIT */
1282
upd_node_t* node, /* in: row update node */
1283
que_thr_t* thr) /* in: query thread */
1287
dict_index_t* index;
1293
ulint err = DB_SUCCESS;
1295
trx_t* trx = thr_get_trx(thr);
1297
index = node->index;
1299
check_ref = row_upd_index_is_referenced(index, trx);
1301
heap = mem_heap_create(1024);
1303
/* Build old index entry */
1304
entry = row_build_index_entry(node->row, index, heap);
1309
found = row_search_index_entry(index, entry, BTR_MODIFY_LEAF, &pcur,
1311
btr_cur = btr_pcur_get_btr_cur(&pcur);
1313
rec = btr_cur_get_rec(btr_cur);
1315
if (UNIV_UNLIKELY(!found)) {
1316
fputs("InnoDB: error in sec index entry update in\n"
1317
"InnoDB: ", stderr);
1318
dict_index_name_print(stderr, trx, index);
1320
"InnoDB: tuple ", stderr);
1321
dtuple_print(stderr, entry);
1323
"InnoDB: record ", stderr);
1324
rec_print(stderr, rec, index);
1327
trx_print(stderr, trx, 0);
1330
"InnoDB: Submit a detailed bug report"
1331
" to http://bugs.mysql.com\n", stderr);
1333
/* Delete mark the old index record; it can already be
1334
delete marked if we return after a lock wait in
1335
row_ins_index_entry below */
1337
if (!rec_get_deleted_flag(rec,
1338
dict_table_is_comp(index->table))) {
1339
err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE,
1341
if (err == DB_SUCCESS && check_ref) {
1343
/* NOTE that the following call loses
1344
the position of pcur ! */
1345
err = row_upd_check_references_constraints(
1346
node, &pcur, index->table,
1348
if (err != DB_SUCCESS) {
1357
btr_pcur_close(&pcur);
1360
if (node->is_delete || err != DB_SUCCESS) {
1362
mem_heap_free(heap);
1367
/* Build a new index entry */
1368
row_upd_index_replace_new_col_vals(entry, index, node->update, NULL);
1370
/* Insert new index entry */
1371
err = row_ins_index_entry(index, entry, NULL, 0, thr);
1373
mem_heap_free(heap);
1378
/***************************************************************
1379
Updates the secondary index record if it is changed in the row update or
1380
deletes it if this is a delete. */
1385
/* out: DB_SUCCESS if operation successfully
1386
completed, else error code or DB_LOCK_WAIT */
1387
upd_node_t* node, /* in: row update node */
1388
que_thr_t* thr) /* in: query thread */
1392
ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
1393
|| (node->state == UPD_NODE_UPDATE_SOME_SEC));
1394
ut_ad(!(node->index->type & DICT_CLUSTERED));
1396
if (node->state == UPD_NODE_UPDATE_ALL_SEC
1397
|| row_upd_changes_ord_field_binary(node->row, node->index,
1399
err = row_upd_sec_index_entry(node, thr);
1407
/***************************************************************
1408
Marks the clustered index record deleted and inserts the updated version
1409
of the record to the index. This function should be used when the ordering
1410
fields of the clustered index record change. This should be quite rare in
1411
database applications. */
1414
row_upd_clust_rec_by_insert(
1415
/*========================*/
1416
/* out: DB_SUCCESS if operation successfully
1417
completed, else error code or DB_LOCK_WAIT */
1418
upd_node_t* node, /* in: row update node */
1419
dict_index_t* index, /* in: clustered index of the record */
1420
que_thr_t* thr, /* in: query thread */
1421
ibool check_ref,/* in: TRUE if index may be referenced in
1422
a foreign key constraint */
1423
mtr_t* mtr) /* in: mtr; gets committed here */
1425
mem_heap_t* heap = NULL;
1429
dict_table_t* table;
1434
ut_ad(index->type & DICT_CLUSTERED);
1436
trx = thr_get_trx(thr);
1437
table = node->table;
1439
btr_cur = btr_pcur_get_btr_cur(pcur);
1441
if (node->state != UPD_NODE_INSERT_CLUSTERED) {
1442
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1443
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1445
err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
1446
btr_cur, TRUE, thr, mtr);
1447
if (err != DB_SUCCESS) {
1452
/* Mark as not-owned the externally stored fields which the new
1453
row inherits from the delete marked record: purge should not
1454
free those externally stored fields even if the delete marked
1455
record is removed from the index tree, or updated. */
1457
btr_cur_mark_extern_inherited_fields(
1458
btr_cur_get_rec(btr_cur),
1459
rec_get_offsets(btr_cur_get_rec(btr_cur),
1460
dict_table_get_first_index(table),
1461
offsets_, ULINT_UNDEFINED, &heap),
1464
/* NOTE that the following call loses
1465
the position of pcur ! */
1466
err = row_upd_check_references_constraints(
1467
node, pcur, table, index, thr, mtr);
1468
if (err != DB_SUCCESS) {
1470
if (UNIV_LIKELY_NULL(heap)) {
1471
mem_heap_free(heap);
1482
heap = mem_heap_create(500);
1484
node->state = UPD_NODE_INSERT_CLUSTERED;
1486
entry = row_build_index_entry(node->row, index, heap);
1488
row_upd_index_replace_new_col_vals(entry, index, node->update, NULL);
1490
row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
1492
/* If we return from a lock wait, for example, we may have
1493
extern fields marked as not-owned in entry (marked in the
1494
if-branch above). We must unmark them. */
1496
btr_cur_unmark_dtuple_extern_fields(entry, node->ext_vec,
1498
/* We must mark non-updated extern fields in entry as inherited,
1499
so that a possible rollback will not free them */
1501
btr_cur_mark_dtuple_inherited_extern(entry, node->ext_vec,
1505
err = row_ins_index_entry(index, entry, node->ext_vec,
1506
node->n_ext_vec, thr);
1507
mem_heap_free(heap);
1512
/***************************************************************
1513
Updates a clustered index record of a row when the ordering fields do
1519
/* out: DB_SUCCESS if operation successfully
1520
completed, else error code or DB_LOCK_WAIT */
1521
upd_node_t* node, /* in: row update node */
1522
dict_index_t* index, /* in: clustered index */
1523
que_thr_t* thr, /* in: query thread */
1524
mtr_t* mtr) /* in: mtr; gets committed here */
1526
big_rec_t* big_rec = NULL;
1532
ut_ad(index->type & DICT_CLUSTERED);
1535
btr_cur = btr_pcur_get_btr_cur(pcur);
1537
ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
1538
dict_table_is_comp(index->table)));
1540
/* Try optimistic updating of the record, keeping changes within
1541
the page; we do not check locks because we assume the x-lock on the
1544
if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
1545
err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG,
1546
btr_cur, node->update,
1547
node->cmpl_info, thr, mtr);
1549
err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG,
1550
btr_cur, node->update,
1551
node->cmpl_info, thr, mtr);
1556
if (err == DB_SUCCESS) {
1561
if (buf_LRU_buf_pool_running_out()) {
1563
return(DB_LOCK_TABLE_FULL);
1565
/* We may have to modify the tree structure: do a pessimistic descent
1566
down the index tree */
1570
/* NOTE: this transaction has an s-lock or x-lock on the record and
1571
therefore other transactions cannot modify the record when we have no
1572
latch on the page. In addition, we assume that other query threads of
1573
the same transaction do not modify the record in the meantime.
1574
Therefore we can assert that the restoration of the cursor succeeds. */
1576
ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
1578
ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
1579
dict_table_is_comp(index->table)));
1581
err = btr_cur_pessimistic_update(BTR_NO_LOCKING_FLAG, btr_cur,
1582
&big_rec, node->update,
1583
node->cmpl_info, thr, mtr);
1586
if (err == DB_SUCCESS && big_rec) {
1587
mem_heap_t* heap = NULL;
1588
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1590
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1594
ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
1595
rec = btr_cur_get_rec(btr_cur);
1596
err = btr_store_big_rec_extern_fields(
1598
rec_get_offsets(rec, index, offsets_,
1599
ULINT_UNDEFINED, &heap),
1601
if (UNIV_LIKELY_NULL(heap)) {
1602
mem_heap_free(heap);
1608
dtuple_big_rec_free(big_rec);
1614
/***************************************************************
1615
Delete marks a clustered index record. */
1618
row_upd_del_mark_clust_rec(
1619
/*=======================*/
1620
/* out: DB_SUCCESS if operation successfully
1621
completed, else error code */
1622
upd_node_t* node, /* in: row update node */
1623
dict_index_t* index, /* in: clustered index */
1624
que_thr_t* thr, /* in: query thread */
1625
ibool check_ref,/* in: TRUE if index may be referenced in
1626
a foreign key constraint */
1627
mtr_t* mtr) /* in: mtr; gets committed here */
1634
ut_ad(index->type & DICT_CLUSTERED);
1635
ut_ad(node->is_delete);
1638
btr_cur = btr_pcur_get_btr_cur(pcur);
1640
/* Store row because we have to build also the secondary index
1643
row_upd_store_row(node);
1645
/* Mark the clustered index record deleted; we do not have to check
1646
locks, because we assume that we have an x-lock on the record */
1648
err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
1649
btr_cur, TRUE, thr, mtr);
1650
if (err == DB_SUCCESS && check_ref) {
1651
/* NOTE that the following call loses the position of pcur ! */
1653
err = row_upd_check_references_constraints(node,
1656
if (err != DB_SUCCESS) {
1668
/***************************************************************
1669
Updates the clustered index record. */
1674
/* out: DB_SUCCESS if operation successfully
1675
completed, DB_LOCK_WAIT in case of a lock wait,
1677
upd_node_t* node, /* in: row update node */
1678
que_thr_t* thr) /* in: query thread */
1680
dict_index_t* index;
1688
mem_heap_t* heap = NULL;
1689
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1690
const ulint* offsets;
1691
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
1693
index = dict_table_get_first_index(node->table);
1695
check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr));
1699
/* We have to restore the cursor to its position */
1704
/* If the restoration does not succeed, then the same
1705
transaction has deleted the record on which the cursor was,
1706
and that is an SQL error. If the restoration succeeds, it may
1707
still be that the same transaction has successively deleted
1708
and inserted a record with the same ordering fields, but in
1709
that case we know that the transaction has at least an
1710
implicit x-lock on the record. */
1712
ut_a(pcur->rel_pos == BTR_PCUR_ON);
1714
success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr);
1717
err = DB_RECORD_NOT_FOUND;
1724
/* If this is a row in SYS_INDEXES table of the data dictionary,
1725
then we have to free the file segments of the index tree associated
1729
&& ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) {
1731
dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr);
1737
success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
1748
rec = btr_pcur_get_rec(pcur);
1749
offsets = rec_get_offsets(rec, index, offsets_,
1750
ULINT_UNDEFINED, &heap);
1752
if (!node->has_clust_rec_x_lock) {
1753
err = lock_clust_rec_modify_check_and_lock(
1754
0, rec, index, offsets, thr);
1755
if (err != DB_SUCCESS) {
1761
/* NOTE: the following function calls will also commit mtr */
1763
if (node->is_delete) {
1764
err = row_upd_del_mark_clust_rec(node, index, thr, check_ref,
1766
if (err == DB_SUCCESS) {
1767
node->state = UPD_NODE_UPDATE_ALL_SEC;
1768
node->index = dict_table_get_next_index(index);
1771
if (UNIV_LIKELY_NULL(heap)) {
1772
mem_heap_free(heap);
1777
/* If the update is made for MySQL, we already have the update vector
1778
ready, else we have to do some evaluation: */
1780
if (!node->in_mysql_interface) {
1781
/* Copy the necessary columns from clust_rec and calculate the
1782
new values to set */
1783
row_upd_copy_columns(rec, offsets,
1784
UT_LIST_GET_FIRST(node->columns));
1785
row_upd_eval_new_vals(node->update);
1788
if (UNIV_LIKELY_NULL(heap)) {
1789
mem_heap_free(heap);
1792
if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
1794
err = row_upd_clust_rec(node, index, thr, mtr);
1798
row_upd_store_row(node);
1800
if (row_upd_changes_ord_field_binary(node->row, index, node->update)) {
1802
/* Update causes an ordering field (ordering fields within
1803
the B-tree) of the clustered index record to change: perform
1804
the update by delete marking and inserting.
1806
TODO! What to do to the 'Halloween problem', where an update
1807
moves the record forward in index so that it is again
1808
updated when the cursor arrives there? Solution: the
1809
read operation must check the undo record undo number when
1810
choosing records to update. MySQL solves now the problem
1813
err = row_upd_clust_rec_by_insert(node, index, thr, check_ref,
1815
if (err != DB_SUCCESS) {
1820
node->state = UPD_NODE_UPDATE_ALL_SEC;
1822
err = row_upd_clust_rec(node, index, thr, mtr);
1824
if (err != DB_SUCCESS) {
1829
node->state = UPD_NODE_UPDATE_SOME_SEC;
1832
node->index = dict_table_get_next_index(index);
1837
/***************************************************************
1838
Updates the affected index records of a row. When the control is transferred
1839
to this node, we assume that we have a persistent cursor which was on a
1840
record, and the position of the cursor is stored in the cursor. */
1845
/* out: DB_SUCCESS if operation successfully
1846
completed, else error code or DB_LOCK_WAIT */
1847
upd_node_t* node, /* in: row update node */
1848
que_thr_t* thr) /* in: query thread */
1850
ulint err = DB_SUCCESS;
1854
if (UNIV_LIKELY(node->in_mysql_interface)) {
1856
/* We do not get the cmpl_info value from the MySQL
1857
interpreter: we must calculate it on the fly: */
1860
|| row_upd_changes_some_index_ord_field_binary(
1861
node->table, node->update)) {
1862
node->cmpl_info = 0;
1864
node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
1868
if (node->state == UPD_NODE_UPDATE_CLUSTERED
1869
|| node->state == UPD_NODE_INSERT_CLUSTERED) {
1871
err = row_upd_clust_step(node, thr);
1873
if (err != DB_SUCCESS) {
1879
if (!node->is_delete && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
1884
while (node->index != NULL) {
1885
err = row_upd_sec_step(node, thr);
1887
if (err != DB_SUCCESS) {
1892
node->index = dict_table_get_next_index(node->index);
1896
if (err == DB_SUCCESS) {
1897
/* Do some cleanup */
1899
if (node->row != NULL) {
1901
node->n_ext_vec = 0;
1902
mem_heap_empty(node->heap);
1905
node->state = UPD_NODE_UPDATE_CLUSTERED;
1911
/***************************************************************
1912
Updates a row in a table. This is a high-level function used in SQL execution
1918
/* out: query thread to run next or NULL */
1919
que_thr_t* thr) /* in: query thread */
1922
sel_node_t* sel_node;
1924
ulint err = DB_SUCCESS;
1929
trx = thr_get_trx(thr);
1931
trx_start_if_not_started(trx);
1933
node = thr->run_node;
1935
sel_node = node->select;
1937
parent = que_node_get_parent(node);
1939
ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
1941
if (thr->prev_node == parent) {
1942
node->state = UPD_NODE_SET_IX_LOCK;
1945
if (node->state == UPD_NODE_SET_IX_LOCK) {
1947
if (!node->has_clust_rec_x_lock) {
1948
/* It may be that the current session has not yet
1949
started its transaction, or it has been committed: */
1951
err = lock_table(0, node->table, LOCK_IX, thr);
1953
if (err != DB_SUCCESS) {
1955
goto error_handling;
1959
node->state = UPD_NODE_UPDATE_CLUSTERED;
1961
if (node->searched_update) {
1962
/* Reset the cursor */
1963
sel_node->state = SEL_NODE_OPEN;
1965
/* Fetch a row to update */
1967
thr->run_node = sel_node;
1973
/* sel_node is NULL if we are in the MySQL interface */
1975
if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
1977
if (!node->searched_update) {
1978
/* An explicit cursor should be positioned on a row
1985
goto error_handling;
1988
ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
1990
/* No more rows to update, or the select node performed the
1991
updates directly in-place */
1993
thr->run_node = parent;
1998
/* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
2000
err = row_upd(node, thr);
2003
trx->error_state = err;
2005
if (err != DB_SUCCESS) {
2009
/* DO THE TRIGGER ACTIONS HERE */
2011
if (node->searched_update) {
2012
/* Fetch next row to update */
2014
thr->run_node = sel_node;
2016
/* It was an explicit cursor update */
2018
thr->run_node = parent;
2021
node->state = UPD_NODE_UPDATE_CLUSTERED;
2026
/*************************************************************************
2027
Performs an in-place update for the current clustered index record in
2031
row_upd_in_place_in_select(
2032
/*=======================*/
2033
sel_node_t* sel_node, /* in: select node */
2034
que_thr_t* thr, /* in: query thread */
2035
mtr_t* mtr) /* in: mtr */
2041
mem_heap_t* heap = NULL;
2042
ulint offsets_[REC_OFFS_NORMAL_SIZE];
2043
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
2045
ut_ad(sel_node->select_will_do_update);
2046
ut_ad(sel_node->latch_mode == BTR_MODIFY_LEAF);
2047
ut_ad(sel_node->asc);
2049
node = que_node_get_parent(sel_node);
2051
ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
2054
btr_cur = btr_pcur_get_btr_cur(pcur);
2056
/* Copy the necessary columns from clust_rec and calculate the new
2059
row_upd_copy_columns(btr_pcur_get_rec(pcur),
2060
rec_get_offsets(btr_pcur_get_rec(pcur),
2061
btr_cur->index, offsets_,
2062
ULINT_UNDEFINED, &heap),
2063
UT_LIST_GET_FIRST(node->columns));
2064
if (UNIV_LIKELY_NULL(heap)) {
2065
mem_heap_free(heap);
2067
row_upd_eval_new_vals(node->update);
2069
ut_ad(!rec_get_deleted_flag(
2070
btr_pcur_get_rec(pcur),
2071
dict_table_is_comp(btr_cur->index->table)));
2073
ut_ad(node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE);
2074
ut_ad(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE);
2075
ut_ad(node->select_will_do_update);
2077
err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG, btr_cur,
2078
node->update, node->cmpl_info,
2080
ut_ad(err == DB_SUCCESS);