1
/*****************************************************************************
3
Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved.
5
This program is free software; you can redistribute it and/or modify it under
6
the terms of the GNU General Public License as published by the Free Software
7
Foundation; version 2 of the License.
9
This program is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
17
*****************************************************************************/
19
/**************************************************//**
23
Created 12/27/1996 Heikki Tuuri
24
*******************************************************/
32
#include "dict0dict.h"
35
#ifndef UNIV_HOTBACKUP
36
#include "dict0boot.h"
37
#include "dict0crea.h"
38
#include "mach0data.h"
47
#include "lock0lock.h"
50
#include "eval0eval.h"
54
/* What kind of latch and lock can we assume when the control comes to
55
-------------------------------------------------------------------
58
Efficiency of massive updates would require keeping an x-latch on a
59
clustered index page through many updates, and not setting an explicit
60
x-lock on clustered index records, as they anyway will get an implicit
61
x-lock when they are updated. A problem is that the read nodes in the
62
graph should know that they must keep the latch when passing the control
63
up to the update node, and not set any record lock on the record which
64
will be updated. Another problem occurs if the execution is stopped,
65
as the kernel switches to another query thread, or the transaction must
66
wait for a lock. Then we should be able to release the latch and, maybe,
67
acquire an explicit x-lock on the record.
68
Because this seems too complicated, we conclude that the less
69
efficient solution of releasing all the latches when the control is
70
transferred to another node, and acquiring explicit x-locks, is better. */
72
/* How is a delete performed? If there is a delete without an
73
explicit cursor, i.e., a searched delete, there are at least
74
two different situations:
75
the implicit select cursor may run on (1) the clustered index or
76
on (2) a secondary index. The delete is performed by setting
77
the delete bit in the record and substituting the id of the
78
deleting transaction for the original trx id, and substituting a
79
new roll ptr for previous roll ptr. The old trx id and roll ptr
80
are saved in the undo log record. Thus, no physical changes occur
81
in the index tree structure at the time of the delete. Only
82
when the undo log is purged, the index records will be physically
83
deleted from the index trees.
85
The query graph executing a searched delete would consist of
86
a delete node which has as a subtree a select subgraph.
87
The select subgraph should return a (persistent) cursor
88
in the clustered index, placed on page which is x-latched.
89
The delete node should look for all secondary index records for
90
this clustered index entry and mark them as deleted. When is
91
the x-latch freed? The most efficient way for performing a
92
searched delete is obviously to keep the x-latch for several
93
steps of query graph execution. */
95
/*************************************************************************
96
IMPORTANT NOTE: Any operation that generates redo MUST check that there
97
is enough space in the redo log before for that operation. This is
98
done by calling log_free_check(). The reason for checking the
99
availability of the redo log space before the start of the operation is
100
that we MUST not hold any synchonization objects when performing the
102
If you make a change in this module make sure that no codepath is
103
introduced where a call to log_free_check() is bypassed. */
105
/*************************************************************************
106
IMPORTANT NOTE: Any operation that generates redo MUST check that there
107
is enough space in the redo log before for that operation. This is
108
done by calling log_free_check(). The reason for checking the
109
availability of the redo log space before the start of the operation is
110
that we MUST not hold any synchonization objects when performing the
112
If you make a change in this module make sure that no codepath is
113
introduced where a call to log_free_check() is bypassed. */
115
/***********************************************************//**
116
Checks if an update vector changes some of the first ordering fields of an
117
index record. This is only used in foreign key checks and we can assume
118
that index does not contain column prefixes.
119
@return TRUE if changes */
122
row_upd_changes_first_fields_binary(
123
/*================================*/
124
dtuple_t* entry, /*!< in: old value of index entry */
125
dict_index_t* index, /*!< in: index of entry */
126
const upd_t* update, /*!< in: update vector for the row */
127
ulint n); /*!< in: how many first fields to check */
130
/*********************************************************************//**
131
Checks if index currently is mentioned as a referenced index in a foreign
134
NOTE that since we do not hold dict_operation_lock when leaving the
135
function, it may be that the referencing table has been dropped when
136
we leave this function: this function is only for heuristic use!
138
@return TRUE if referenced */
141
row_upd_index_is_referenced(
142
/*========================*/
143
dict_index_t* index, /*!< in: index */
144
trx_t* trx) /*!< in: transaction */
146
dict_table_t* table = index->table;
147
dict_foreign_t* foreign;
148
ibool froze_data_dict = FALSE;
149
ibool is_referenced = FALSE;
151
if (!UT_LIST_GET_FIRST(table->referenced_list)) {
156
if (trx->dict_operation_lock_mode == 0) {
157
row_mysql_freeze_data_dictionary(trx);
158
froze_data_dict = TRUE;
161
foreign = UT_LIST_GET_FIRST(table->referenced_list);
164
if (foreign->referenced_index == index) {
166
is_referenced = TRUE;
170
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
174
if (froze_data_dict) {
175
row_mysql_unfreeze_data_dictionary(trx);
178
return(is_referenced);
181
/*********************************************************************//**
182
Checks if possible foreign key constraints hold after a delete of the record
185
NOTE that this function will temporarily commit mtr and lose the
188
@return DB_SUCCESS or an error code */
191
row_upd_check_references_constraints(
192
/*=================================*/
193
upd_node_t* node, /*!< in: row update node */
194
btr_pcur_t* pcur, /*!< in: cursor positioned on a record; NOTE: the
195
cursor position is lost in this function! */
196
dict_table_t* table, /*!< in: table in question */
197
dict_index_t* index, /*!< in: index of the cursor */
198
ulint* offsets,/*!< in/out: rec_get_offsets(pcur.rec, index) */
199
que_thr_t* thr, /*!< in: query thread */
200
mtr_t* mtr) /*!< in: mtr */
202
dict_foreign_t* foreign;
209
ibool got_s_lock = FALSE;
211
if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) {
216
trx = thr_get_trx(thr);
218
rec = btr_pcur_get_rec(pcur);
219
ut_ad(rec_offs_validate(rec, index, offsets));
221
heap = mem_heap_create(500);
223
entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index, offsets,
230
if (trx->dict_operation_lock_mode == 0) {
233
row_mysql_freeze_data_dictionary(trx);
236
foreign = UT_LIST_GET_FIRST(table->referenced_list);
239
/* Note that we may have an update which updates the index
240
record, but does NOT update the first fields which are
241
referenced in a foreign key constraint. Then the update does
242
NOT break the constraint. */
244
if (foreign->referenced_index == index
246
|| row_upd_changes_first_fields_binary(
247
entry, index, node->update,
248
foreign->n_fields))) {
250
if (foreign->foreign_table == NULL) {
251
dict_table_get(foreign->foreign_table_name,
255
if (foreign->foreign_table) {
256
mutex_enter(&(dict_sys->mutex));
258
(foreign->foreign_table
259
->n_foreign_key_checks_running)++;
261
mutex_exit(&(dict_sys->mutex));
264
/* NOTE that if the thread ends up waiting for a lock
265
we will release dict_operation_lock temporarily!
266
But the counter on the table protects 'foreign' from
267
being dropped while the check is running. */
269
err = row_ins_check_foreign_constraint(
270
FALSE, foreign, table, entry, thr);
272
if (foreign->foreign_table) {
273
mutex_enter(&(dict_sys->mutex));
275
ut_a(foreign->foreign_table
276
->n_foreign_key_checks_running > 0);
278
(foreign->foreign_table
279
->n_foreign_key_checks_running)--;
281
mutex_exit(&(dict_sys->mutex));
284
if (err != DB_SUCCESS) {
290
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
297
row_mysql_unfreeze_data_dictionary(trx);
305
/*********************************************************************//**
306
Creates an update node for a query graph.
307
@return own: update node */
312
mem_heap_t* heap) /*!< in: mem heap where created */
316
node = mem_heap_alloc(heap, sizeof(upd_node_t));
317
node->common.type = QUE_NODE_UPDATE;
319
node->state = UPD_NODE_UPDATE_CLUSTERED;
320
node->in_mysql_interface = FALSE;
324
node->upd_row = NULL;
325
node->upd_ext = NULL;
329
node->foreign = NULL;
330
node->cascade_heap = NULL;
331
node->cascade_node = NULL;
335
node->heap = mem_heap_create(128);
336
node->magic_n = UPD_NODE_MAGIC_N;
342
#endif /* !UNIV_HOTBACKUP */
344
/*********************************************************************//**
345
Updates the trx id and roll ptr field in a clustered index record in database
349
row_upd_rec_sys_fields_in_recovery(
350
/*===============================*/
351
rec_t* rec, /*!< in/out: record */
352
page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
353
const ulint* offsets,/*!< in: array returned by rec_get_offsets() */
354
ulint pos, /*!< in: TRX_ID position in rec */
355
trx_id_t trx_id, /*!< in: transaction id */
356
roll_ptr_t roll_ptr)/*!< in: roll ptr of the undo log record */
358
ut_ad(rec_offs_validate(rec, NULL, offsets));
360
if (UNIV_LIKELY_NULL(page_zip)) {
361
page_zip_write_trx_id_and_roll_ptr(
362
page_zip, rec, offsets, pos, trx_id, roll_ptr);
367
field = rec_get_nth_field(rec, offsets, pos, &len);
368
ut_ad(len == DATA_TRX_ID_LEN);
369
#if DATA_TRX_ID + 1 != DATA_ROLL_PTR
370
# error "DATA_TRX_ID + 1 != DATA_ROLL_PTR"
372
trx_write_trx_id(field, trx_id);
373
trx_write_roll_ptr(field + DATA_TRX_ID_LEN, roll_ptr);
377
#ifndef UNIV_HOTBACKUP
378
/*********************************************************************//**
379
Sets the trx id or roll ptr field of a clustered index entry. */
382
row_upd_index_entry_sys_field(
383
/*==========================*/
384
const dtuple_t* entry, /*!< in: index entry, where the memory buffers
385
for sys fields are already allocated:
386
the function just copies the new values to
388
dict_index_t* index, /*!< in: clustered index */
389
ulint type, /*!< in: DATA_TRX_ID or DATA_ROLL_PTR */
390
ib_uint64_t val) /*!< in: value to write */
396
ut_ad(dict_index_is_clust(index));
398
pos = dict_index_get_sys_col_pos(index, type);
400
dfield = dtuple_get_nth_field(entry, pos);
401
field = dfield_get_data(dfield);
403
if (type == DATA_TRX_ID) {
404
trx_write_trx_id(field, val);
406
ut_ad(type == DATA_ROLL_PTR);
407
trx_write_roll_ptr(field, val);
411
/***********************************************************//**
412
Returns TRUE if row update changes size of some field in index or if some
413
field to be updated is stored externally in rec or update.
414
@return TRUE if the update changes the size of some field in index or
415
the field is external in rec or update */
418
row_upd_changes_field_size_or_external(
419
/*===================================*/
420
dict_index_t* index, /*!< in: index */
421
const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
422
const upd_t* update) /*!< in: update vector */
424
const upd_field_t* upd_field;
425
const dfield_t* new_val;
431
ut_ad(rec_offs_validate(NULL, index, offsets));
432
n_fields = upd_get_n_fields(update);
434
for (i = 0; i < n_fields; i++) {
435
upd_field = upd_get_nth_field(update, i);
437
new_val = &(upd_field->new_val);
438
new_len = dfield_get_len(new_val);
440
if (dfield_is_null(new_val) && !rec_offs_comp(offsets)) {
441
/* A bug fixed on Dec 31st, 2004: we looked at the
442
SQL NULL size from the wrong field! We may backport
443
this fix also to 4.0. The merge to 5.0 will be made
444
manually immediately after we commit this to 4.1. */
446
new_len = dict_col_get_sql_null_size(
447
dict_index_get_nth_col(index,
448
upd_field->field_no),
452
old_len = rec_offs_nth_size(offsets, upd_field->field_no);
454
if (rec_offs_comp(offsets)
455
&& rec_offs_nth_sql_null(offsets,
456
upd_field->field_no)) {
457
/* Note that in the compact table format, for a
458
variable length field, an SQL NULL will use zero
459
bytes in the offset array at the start of the physical
460
record, but a zero-length value (empty string) will
461
use one byte! Thus, we cannot use update-in-place
462
if we update an SQL NULL varchar to an empty string! */
464
old_len = UNIV_SQL_NULL;
467
if (dfield_is_ext(new_val) || old_len != new_len
468
|| rec_offs_nth_extern(offsets, upd_field->field_no)) {
476
#endif /* !UNIV_HOTBACKUP */
478
/***********************************************************//**
479
Replaces the new column values stored in the update vector to the record
480
given. No field size changes are allowed. */
483
row_upd_rec_in_place(
484
/*=================*/
485
rec_t* rec, /*!< in/out: record where replaced */
486
dict_index_t* index, /*!< in: the index the record belongs to */
487
const ulint* offsets,/*!< in: array returned by rec_get_offsets() */
488
const upd_t* update, /*!< in: update vector */
489
page_zip_des_t* page_zip)/*!< in: compressed page with enough space
490
available, or NULL */
492
const upd_field_t* upd_field;
493
const dfield_t* new_val;
497
ut_ad(rec_offs_validate(rec, index, offsets));
499
if (rec_offs_comp(offsets)) {
500
rec_set_info_bits_new(rec, update->info_bits);
502
rec_set_info_bits_old(rec, update->info_bits);
505
n_fields = upd_get_n_fields(update);
507
for (i = 0; i < n_fields; i++) {
508
upd_field = upd_get_nth_field(update, i);
509
new_val = &(upd_field->new_val);
510
ut_ad(!dfield_is_ext(new_val) ==
511
!rec_offs_nth_extern(offsets, upd_field->field_no));
513
rec_set_nth_field(rec, offsets, upd_field->field_no,
514
dfield_get_data(new_val),
515
dfield_get_len(new_val));
518
if (UNIV_LIKELY_NULL(page_zip)) {
519
page_zip_write_rec(page_zip, rec, index, offsets, 0);
523
#ifndef UNIV_HOTBACKUP
524
/*********************************************************************//**
525
Writes into the redo log the values of trx id and roll ptr and enough info
526
to determine their positions within a clustered index record.
527
@return new pointer to mlog */
530
row_upd_write_sys_vals_to_log(
531
/*==========================*/
532
dict_index_t* index, /*!< in: clustered index */
533
trx_t* trx, /*!< in: transaction */
534
roll_ptr_t roll_ptr,/*!< in: roll ptr of the undo log record */
535
byte* log_ptr,/*!< pointer to a buffer of size > 20 opened
537
mtr_t* mtr __attribute__((unused))) /*!< in: mtr */
539
ut_ad(dict_index_is_clust(index));
542
log_ptr += mach_write_compressed(log_ptr,
543
dict_index_get_sys_col_pos(
544
index, DATA_TRX_ID));
546
trx_write_roll_ptr(log_ptr, roll_ptr);
547
log_ptr += DATA_ROLL_PTR_LEN;
549
log_ptr += mach_ull_write_compressed(log_ptr, trx->id);
553
#endif /* !UNIV_HOTBACKUP */
555
/*********************************************************************//**
556
Parses the log data of system field values.
557
@return log data end or NULL */
560
row_upd_parse_sys_vals(
561
/*===================*/
562
byte* ptr, /*!< in: buffer */
563
byte* end_ptr,/*!< in: buffer end */
564
ulint* pos, /*!< out: TRX_ID position in record */
565
trx_id_t* trx_id, /*!< out: trx id */
566
roll_ptr_t* roll_ptr)/*!< out: roll ptr */
568
ptr = mach_parse_compressed(ptr, end_ptr, pos);
575
if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {
580
*roll_ptr = trx_read_roll_ptr(ptr);
581
ptr += DATA_ROLL_PTR_LEN;
583
ptr = mach_ull_parse_compressed(ptr, end_ptr, trx_id);
588
#ifndef UNIV_HOTBACKUP
589
/***********************************************************//**
590
Writes to the redo log the new values of the fields occurring in the index. */
593
row_upd_index_write_log(
594
/*====================*/
595
const upd_t* update, /*!< in: update vector */
596
byte* log_ptr,/*!< in: pointer to mlog buffer: must
597
contain at least MLOG_BUF_MARGIN bytes
598
of free space; the buffer is closed
599
within this function */
600
mtr_t* mtr) /*!< in: mtr into whose log to write */
602
const upd_field_t* upd_field;
603
const dfield_t* new_val;
609
n_fields = upd_get_n_fields(update);
611
buf_end = log_ptr + MLOG_BUF_MARGIN;
613
mach_write_to_1(log_ptr, update->info_bits);
615
log_ptr += mach_write_compressed(log_ptr, n_fields);
617
for (i = 0; i < n_fields; i++) {
619
#if MLOG_BUF_MARGIN <= 30
620
# error "MLOG_BUF_MARGIN <= 30"
623
if (log_ptr + 30 > buf_end) {
624
mlog_close(mtr, log_ptr);
626
log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
627
buf_end = log_ptr + MLOG_BUF_MARGIN;
630
upd_field = upd_get_nth_field(update, i);
632
new_val = &(upd_field->new_val);
634
len = dfield_get_len(new_val);
636
log_ptr += mach_write_compressed(log_ptr, upd_field->field_no);
637
log_ptr += mach_write_compressed(log_ptr, len);
639
if (len != UNIV_SQL_NULL) {
640
if (log_ptr + len < buf_end) {
641
memcpy(log_ptr, dfield_get_data(new_val), len);
645
mlog_close(mtr, log_ptr);
647
mlog_catenate_string(mtr,
648
dfield_get_data(new_val),
651
log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
652
buf_end = log_ptr + MLOG_BUF_MARGIN;
657
mlog_close(mtr, log_ptr);
659
#endif /* !UNIV_HOTBACKUP */
661
/*********************************************************************//**
662
Parses the log data written by row_upd_index_write_log.
663
@return log data end or NULL */
668
byte* ptr, /*!< in: buffer */
669
byte* end_ptr,/*!< in: buffer end */
670
mem_heap_t* heap, /*!< in: memory heap where update vector is
672
upd_t** update_out)/*!< out: update vector */
675
upd_field_t* upd_field;
682
if (end_ptr < ptr + 1) {
687
info_bits = mach_read_from_1(ptr);
689
ptr = mach_parse_compressed(ptr, end_ptr, &n_fields);
696
update = upd_create(n_fields, heap);
697
update->info_bits = info_bits;
699
for (i = 0; i < n_fields; i++) {
701
upd_field = upd_get_nth_field(update, i);
702
new_val = &(upd_field->new_val);
704
ptr = mach_parse_compressed(ptr, end_ptr, &field_no);
711
upd_field->field_no = field_no;
713
ptr = mach_parse_compressed(ptr, end_ptr, &len);
720
if (len != UNIV_SQL_NULL) {
722
if (end_ptr < ptr + len) {
727
dfield_set_data(new_val,
728
mem_heap_dup(heap, ptr, len), len);
731
dfield_set_null(new_val);
735
*update_out = update;
740
#ifndef UNIV_HOTBACKUP
741
/***************************************************************//**
742
Builds an update vector from those fields which in a secondary index entry
743
differ from a record that has the equal ordering fields. NOTE: we compare
744
the fields as binary strings!
745
@return own: update vector of differing fields */
748
row_upd_build_sec_rec_difference_binary(
749
/*====================================*/
750
dict_index_t* index, /*!< in: index */
751
const dtuple_t* entry, /*!< in: entry to insert */
752
const rec_t* rec, /*!< in: secondary index record */
753
trx_t* trx, /*!< in: transaction */
754
mem_heap_t* heap) /*!< in: memory heap from which allocated */
756
upd_field_t* upd_field;
757
const dfield_t* dfield;
763
ulint offsets_[REC_OFFS_SMALL_SIZE];
764
const ulint* offsets;
765
rec_offs_init(offsets_);
767
/* This function is used only for a secondary index */
768
ut_a(!dict_index_is_clust(index));
770
update = upd_create(dtuple_get_n_fields(entry), heap);
773
offsets = rec_get_offsets(rec, index, offsets_,
774
ULINT_UNDEFINED, &heap);
776
for (i = 0; i < dtuple_get_n_fields(entry); i++) {
778
data = rec_get_nth_field(rec, offsets, i, &len);
780
dfield = dtuple_get_nth_field(entry, i);
782
/* NOTE that it may be that len != dfield_get_len(dfield) if we
783
are updating in a character set and collation where strings of
784
different length can be equal in an alphabetical comparison,
785
and also in the case where we have a column prefix index
786
and the last characters in the index field are spaces; the
787
latter case probably caused the assertion failures reported at
788
row0upd.c line 713 in versions 4.0.14 - 4.0.16. */
790
/* NOTE: we compare the fields as binary strings!
793
if (!dfield_data_is_binary_equal(dfield, len, data)) {
795
upd_field = upd_get_nth_field(update, n_diff);
797
dfield_copy(&(upd_field->new_val), dfield);
799
upd_field_set_field_no(upd_field, i, index, trx);
805
update->n_fields = n_diff;
810
/***************************************************************//**
811
Builds an update vector from those fields, excluding the roll ptr and
812
trx id fields, which in an index entry differ from a record that has
813
the equal ordering fields. NOTE: we compare the fields as binary strings!
814
@return own: update vector of differing fields, excluding roll ptr and
818
row_upd_build_difference_binary(
819
/*============================*/
820
dict_index_t* index, /*!< in: clustered index */
821
const dtuple_t* entry, /*!< in: entry to insert */
822
const rec_t* rec, /*!< in: clustered index record */
823
trx_t* trx, /*!< in: transaction */
824
mem_heap_t* heap) /*!< in: memory heap from which allocated */
826
upd_field_t* upd_field;
827
const dfield_t* dfield;
835
ulint offsets_[REC_OFFS_NORMAL_SIZE];
836
const ulint* offsets;
837
rec_offs_init(offsets_);
839
/* This function is used only for a clustered index */
840
ut_a(dict_index_is_clust(index));
842
update = upd_create(dtuple_get_n_fields(entry), heap);
846
roll_ptr_pos = dict_index_get_sys_col_pos(index, DATA_ROLL_PTR);
847
trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
849
offsets = rec_get_offsets(rec, index, offsets_,
850
ULINT_UNDEFINED, &heap);
852
for (i = 0; i < dtuple_get_n_fields(entry); i++) {
854
data = rec_get_nth_field(rec, offsets, i, &len);
856
dfield = dtuple_get_nth_field(entry, i);
858
/* NOTE: we compare the fields as binary strings!
861
if (i == trx_id_pos || i == roll_ptr_pos) {
866
if (UNIV_UNLIKELY(!dfield_is_ext(dfield)
867
!= !rec_offs_nth_extern(offsets, i))
868
|| !dfield_data_is_binary_equal(dfield, len, data)) {
870
upd_field = upd_get_nth_field(update, n_diff);
872
dfield_copy(&(upd_field->new_val), dfield);
874
upd_field_set_field_no(upd_field, i, index, trx);
882
update->n_fields = n_diff;
887
/***********************************************************//**
888
Fetch a prefix of an externally stored column. This is similar
889
to row_ext_lookup(), but the row_ext_t holds the old values
890
of the column and must not be poisoned with the new values.
891
@return BLOB prefix */
896
const byte* data, /*!< in: 'internally' stored part of the
897
field containing also the reference to
899
ulint local_len, /*!< in: length of data, in bytes */
900
ulint zip_size, /*!< in: nonzero=compressed BLOB
901
page size, zero for uncompressed
903
ulint* len, /*!< in: length of prefix to fetch;
904
out: fetched length of the prefix */
905
mem_heap_t* heap) /*!< in: heap where to allocate */
907
byte* buf = mem_heap_alloc(heap, *len);
909
*len = btr_copy_externally_stored_field_prefix(buf, *len,
912
/* We should never update records containing a half-deleted BLOB. */
918
/***********************************************************//**
919
Replaces the new column value stored in the update vector in
920
the given index entry field. */
923
row_upd_index_replace_new_col_val(
924
/*==============================*/
925
dfield_t* dfield, /*!< in/out: data field
926
of the index entry */
927
const dict_field_t* field, /*!< in: index field */
928
const dict_col_t* col, /*!< in: field->col */
929
const upd_field_t* uf, /*!< in: update field */
930
mem_heap_t* heap, /*!< in: memory heap for allocating
931
and copying the new value */
932
ulint zip_size)/*!< in: compressed page
933
size of the table, or 0 */
938
dfield_copy_data(dfield, &uf->new_val);
940
if (dfield_is_null(dfield)) {
944
len = dfield_get_len(dfield);
945
data = dfield_get_data(dfield);
947
if (field->prefix_len > 0) {
948
ibool fetch_ext = dfield_is_ext(dfield)
949
&& len < (ulint) field->prefix_len
950
+ BTR_EXTERN_FIELD_REF_SIZE;
955
len = field->prefix_len;
957
data = row_upd_ext_fetch(data, l, zip_size,
961
len = dtype_get_at_most_n_mbchars(col->prtype,
963
field->prefix_len, len,
966
dfield_set_data(dfield, data, len);
969
dfield_dup(dfield, heap);
975
switch (uf->orig_len) {
977
case BTR_EXTERN_FIELD_REF_SIZE:
978
/* Restore the original locally stored
979
part of the column. In the undo log,
980
InnoDB writes a longer prefix of externally
981
stored columns, so that column prefixes
982
in secondary indexes can be reconstructed. */
983
dfield_set_data(dfield,
984
data + len - BTR_EXTERN_FIELD_REF_SIZE,
985
BTR_EXTERN_FIELD_REF_SIZE);
986
dfield_set_ext(dfield);
989
dfield_dup(dfield, heap);
992
/* Reconstruct the original locally
993
stored part of the column. The data
994
will have to be copied. */
995
ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
996
buf = mem_heap_alloc(heap, uf->orig_len);
997
/* Copy the locally stored prefix. */
999
uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE);
1000
/* Copy the BLOB pointer. */
1001
memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE,
1002
data + len - BTR_EXTERN_FIELD_REF_SIZE,
1003
BTR_EXTERN_FIELD_REF_SIZE);
1005
dfield_set_data(dfield, buf, uf->orig_len);
1006
dfield_set_ext(dfield);
1011
/***********************************************************//**
1012
Replaces the new column values stored in the update vector to the index entry
1016
row_upd_index_replace_new_col_vals_index_pos(
1017
/*=========================================*/
1018
dtuple_t* entry, /*!< in/out: index entry where replaced;
1019
the clustered index record must be
1020
covered by a lock or a page latch to
1021
prevent deletion (rollback or purge) */
1022
dict_index_t* index, /*!< in: index; NOTE that this may also be a
1023
non-clustered index */
1024
const upd_t* update, /*!< in: an update vector built for the index so
1025
that the field number in an upd_field is the
1028
/*!< in: if TRUE, limit the replacement to
1029
ordering fields of index; note that this
1030
does not work for non-clustered indexes. */
1031
mem_heap_t* heap) /*!< in: memory heap for allocating and
1032
copying the new values */
1036
const ulint zip_size = dict_table_zip_size(index->table);
1040
dtuple_set_info_bits(entry, update->info_bits);
1043
n_fields = dict_index_get_n_unique(index);
1045
n_fields = dict_index_get_n_fields(index);
1048
for (i = 0; i < n_fields; i++) {
1049
const dict_field_t* field;
1050
const dict_col_t* col;
1051
const upd_field_t* uf;
1053
field = dict_index_get_nth_field(index, i);
1054
col = dict_field_get_col(field);
1055
uf = upd_get_field_by_field_no(update, i);
1058
row_upd_index_replace_new_col_val(
1059
dtuple_get_nth_field(entry, i),
1060
field, col, uf, heap, zip_size);
1065
/***********************************************************//**
1066
Replaces the new column values stored in the update vector to the index entry
1070
row_upd_index_replace_new_col_vals(
1071
/*===============================*/
1072
dtuple_t* entry, /*!< in/out: index entry where replaced;
1073
the clustered index record must be
1074
covered by a lock or a page latch to
1075
prevent deletion (rollback or purge) */
1076
dict_index_t* index, /*!< in: index; NOTE that this may also be a
1077
non-clustered index */
1078
const upd_t* update, /*!< in: an update vector built for the
1079
CLUSTERED index so that the field number in
1080
an upd_field is the clustered index position */
1081
mem_heap_t* heap) /*!< in: memory heap for allocating and
1082
copying the new values */
1085
const dict_index_t* clust_index
1086
= dict_table_get_first_index(index->table);
1087
const ulint zip_size
1088
= dict_table_zip_size(index->table);
1090
dtuple_set_info_bits(entry, update->info_bits);
1092
for (i = 0; i < dict_index_get_n_fields(index); i++) {
1093
const dict_field_t* field;
1094
const dict_col_t* col;
1095
const upd_field_t* uf;
1097
field = dict_index_get_nth_field(index, i);
1098
col = dict_field_get_col(field);
1099
uf = upd_get_field_by_field_no(
1100
update, dict_col_get_clust_pos(col, clust_index));
1103
row_upd_index_replace_new_col_val(
1104
dtuple_get_nth_field(entry, i),
1105
field, col, uf, heap, zip_size);
1110
/***********************************************************//**
1111
Replaces the new column values stored in the update vector. */
1116
dtuple_t* row, /*!< in/out: row where replaced,
1118
the clustered index record must be
1119
covered by a lock or a page latch to
1120
prevent deletion (rollback or purge) */
1121
row_ext_t** ext, /*!< out, own: NULL, or externally
1122
stored column prefixes */
1123
const dict_index_t* index, /*!< in: clustered index */
1124
const upd_t* update, /*!< in: an update vector built for the
1126
mem_heap_t* heap) /*!< in: memory heap */
1133
const dict_table_t* table;
1138
ut_ad(dict_index_is_clust(index));
1142
n_cols = dtuple_get_n_fields(row);
1143
table = index->table;
1144
ut_ad(n_cols == dict_table_get_n_cols(table));
1146
ext_cols = mem_heap_alloc(heap, n_cols * sizeof *ext_cols);
1149
dtuple_set_info_bits(row, update->info_bits);
1151
for (col_no = 0; col_no < n_cols; col_no++) {
1153
const dict_col_t* col
1154
= dict_table_get_nth_col(table, col_no);
1155
const ulint clust_pos
1156
= dict_col_get_clust_pos(col, index);
1159
if (UNIV_UNLIKELY(clust_pos == ULINT_UNDEFINED)) {
1164
dfield = dtuple_get_nth_field(row, col_no);
1166
for (i = 0; i < upd_get_n_fields(update); i++) {
1168
const upd_field_t* upd_field
1169
= upd_get_nth_field(update, i);
1171
if (upd_field->field_no != clust_pos) {
1176
dfield_copy_data(dfield, &upd_field->new_val);
1180
if (dfield_is_ext(dfield) && col->ord_part) {
1181
ext_cols[n_ext_cols++] = col_no;
1186
*ext = row_ext_create(n_ext_cols, ext_cols, row,
1187
dict_table_zip_size(table), heap);
1193
/***********************************************************//**
1194
Checks if an update vector changes an ordering field of an index record.
1196
This function is fast if the update vector is short or the number of ordering
1197
fields in the index is small. Otherwise, this can be quadratic.
1198
NOTE: we compare the fields as binary strings!
1199
@return TRUE if update vector changes an ordering field in the index record */
1202
row_upd_changes_ord_field_binary(
1203
/*=============================*/
1204
const dtuple_t* row, /*!< in: old value of row, or NULL if the
1205
row and the data values in update are not
1206
known when this function is called, e.g., at
1208
dict_index_t* index, /*!< in: index of the record */
1209
const upd_t* update) /*!< in: update vector for the row; NOTE: the
1210
field numbers in this MUST be clustered index
1216
dict_index_t* clust_index;
1218
ut_ad(update && index);
1220
n_unique = dict_index_get_n_unique(index);
1221
n_upd_fields = upd_get_n_fields(update);
1223
clust_index = dict_table_get_first_index(index->table);
1225
for (i = 0; i < n_unique; i++) {
1227
const dict_field_t* ind_field;
1228
const dict_col_t* col;
1232
ind_field = dict_index_get_nth_field(index, i);
1233
col = dict_field_get_col(ind_field);
1234
col_pos = dict_col_get_clust_pos(col, clust_index);
1235
col_no = dict_col_get_no(col);
1237
for (j = 0; j < n_upd_fields; j++) {
1239
const upd_field_t* upd_field
1240
= upd_get_nth_field(update, j);
1242
/* Note that if the index field is a column prefix
1243
then it may be that row does not contain an externally
1244
stored part of the column value, and we cannot compare
1247
if (col_pos == upd_field->field_no
1249
|| ind_field->prefix_len > 0
1250
|| !dfield_datas_are_binary_equal(
1251
dtuple_get_nth_field(row, col_no),
1252
&(upd_field->new_val)))) {
1262
/***********************************************************//**
1263
Checks if an update vector changes an ordering field of an index record.
1264
NOTE: we compare the fields as binary strings!
1265
@return TRUE if update vector may change an ordering field in an index
1269
row_upd_changes_some_index_ord_field_binary(
1270
/*========================================*/
1271
const dict_table_t* table, /*!< in: table */
1272
const upd_t* update) /*!< in: update vector for the row */
1274
upd_field_t* upd_field;
1275
dict_index_t* index;
1278
index = dict_table_get_first_index(table);
1280
for (i = 0; i < upd_get_n_fields(update); i++) {
1282
upd_field = upd_get_nth_field(update, i);
1284
if (dict_field_get_col(dict_index_get_nth_field(
1285
index, upd_field->field_no))
1295
/***********************************************************//**
1296
Checks if an update vector changes some of the first ordering fields of an
1297
index record. This is only used in foreign key checks and we can assume
1298
that index does not contain column prefixes.
1299
@return TRUE if changes */
1302
row_upd_changes_first_fields_binary(
1303
/*================================*/
1304
dtuple_t* entry, /*!< in: index entry */
1305
dict_index_t* index, /*!< in: index of entry */
1306
const upd_t* update, /*!< in: update vector for the row */
1307
ulint n) /*!< in: how many first fields to check */
1311
dict_index_t* clust_index;
1313
ut_ad(update && index);
1314
ut_ad(n <= dict_index_get_n_fields(index));
1316
n_upd_fields = upd_get_n_fields(update);
1317
clust_index = dict_table_get_first_index(index->table);
1319
for (i = 0; i < n; i++) {
1321
const dict_field_t* ind_field;
1322
const dict_col_t* col;
1325
ind_field = dict_index_get_nth_field(index, i);
1326
col = dict_field_get_col(ind_field);
1327
col_pos = dict_col_get_clust_pos(col, clust_index);
1329
ut_a(ind_field->prefix_len == 0);
1331
for (j = 0; j < n_upd_fields; j++) {
1333
upd_field_t* upd_field
1334
= upd_get_nth_field(update, j);
1336
if (col_pos == upd_field->field_no
1337
&& !dfield_datas_are_binary_equal(
1338
dtuple_get_nth_field(entry, i),
1339
&(upd_field->new_val))) {
1349
/*********************************************************************//**
1350
Copies the column values from a record. */
1353
row_upd_copy_columns(
1354
/*=================*/
1355
rec_t* rec, /*!< in: record in a clustered index */
1356
const ulint* offsets,/*!< in: array returned by rec_get_offsets() */
1357
sym_node_t* column) /*!< in: first column in a column list, or
1364
data = rec_get_nth_field(rec, offsets,
1365
column->field_nos[SYM_CLUST_FIELD_NO],
1367
eval_node_copy_and_alloc_val(column, data, len);
1369
column = UT_LIST_GET_NEXT(col_var_list, column);
1373
/*********************************************************************//**
1374
Calculates the new values for fields to update. Note that row_upd_copy_columns
1375
must have been called first. */
1378
row_upd_eval_new_vals(
1379
/*==================*/
1380
upd_t* update) /*!< in/out: update vector */
1383
upd_field_t* upd_field;
1387
n_fields = upd_get_n_fields(update);
1389
for (i = 0; i < n_fields; i++) {
1390
upd_field = upd_get_nth_field(update, i);
1392
exp = upd_field->exp;
1396
dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
1400
/***********************************************************//**
1401
Stores to the heap the row on which the node->pcur is positioned. */
1406
upd_node_t* node) /*!< in: row update node */
1408
dict_index_t* clust_index;
1410
mem_heap_t* heap = NULL;
1412
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1413
const ulint* offsets;
1414
rec_offs_init(offsets_);
1416
ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
1418
if (node->row != NULL) {
1419
mem_heap_empty(node->heap);
1422
clust_index = dict_table_get_first_index(node->table);
1424
rec = btr_pcur_get_rec(node->pcur);
1426
offsets = rec_get_offsets(rec, clust_index, offsets_,
1427
ULINT_UNDEFINED, &heap);
1429
if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) {
1430
/* In DYNAMIC or COMPRESSED format, there is no prefix
1431
of externally stored columns in the clustered index
1432
record. Build a cache of column prefixes. */
1435
/* REDUNDANT and COMPACT formats store a local
1436
768-byte prefix of each externally stored column.
1437
No cache is needed. */
1442
node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
1443
NULL, ext, node->heap);
1444
if (node->is_delete) {
1445
node->upd_row = NULL;
1446
node->upd_ext = NULL;
1448
node->upd_row = dtuple_copy(node->row, node->heap);
1449
row_upd_replace(node->upd_row, &node->upd_ext,
1450
clust_index, node->update, node->heap);
1453
if (UNIV_LIKELY_NULL(heap)) {
1454
mem_heap_free(heap);
1458
/***********************************************************//**
1459
Updates a secondary index entry of a row.
1460
@return DB_SUCCESS if operation successfully completed, else error
1461
code or DB_LOCK_WAIT */
1464
row_upd_sec_index_entry(
1465
/*====================*/
1466
upd_node_t* node, /*!< in: row update node */
1467
que_thr_t* thr) /*!< in: query thread */
1474
dict_index_t* index;
1477
ulint err = DB_SUCCESS;
1478
trx_t* trx = thr_get_trx(thr);
1479
ulint mode = BTR_MODIFY_LEAF;
1480
enum row_search_result search_result;
1482
index = node->index;
1484
referenced = row_upd_index_is_referenced(index, trx);
1486
heap = mem_heap_create(1024);
1488
/* Build old index entry */
1489
entry = row_build_index_entry(node->row, node->ext, index, heap);
1494
/* Set the query thread, so that ibuf_insert_low() will be
1495
able to invoke thd_get_trx(). */
1496
btr_pcur_get_btr_cur(&pcur)->thr = thr;
1498
/* We can only try to use the insert/delete buffer to buffer
1499
delete-mark operations if the index we're modifying has no foreign
1500
key constraints referring to it. */
1502
mode |= BTR_DELETE_MARK;
1505
search_result = row_search_index_entry(index, entry, mode,
1508
btr_cur = btr_pcur_get_btr_cur(&pcur);
1510
rec = btr_cur_get_rec(btr_cur);
1512
switch (search_result) {
1513
case ROW_NOT_DELETED_REF: /* should only occur for BTR_DELETE */
1517
/* Entry was delete marked already. */
1521
fputs("InnoDB: error in sec index entry update in\n"
1522
"InnoDB: ", stderr);
1523
dict_index_name_print(stderr, trx, index);
1525
"InnoDB: tuple ", stderr);
1526
dtuple_print(stderr, entry);
1528
"InnoDB: record ", stderr);
1529
rec_print(stderr, rec, index);
1532
trx_print(stderr, trx, 0);
1535
"InnoDB: Submit a detailed bug report"
1536
" to http://bugs.mysql.com\n", stderr);
1539
/* Delete mark the old index record; it can already be
1540
delete marked if we return after a lock wait in
1541
row_ins_index_entry below */
1543
if (!rec_get_deleted_flag(
1544
rec, dict_table_is_comp(index->table))) {
1546
err = btr_cur_del_mark_set_sec_rec(
1547
0, btr_cur, TRUE, thr, &mtr);
1549
if (err == DB_SUCCESS && referenced) {
1553
offsets = rec_get_offsets(
1554
rec, index, NULL, ULINT_UNDEFINED,
1557
/* NOTE that the following call loses
1558
the position of pcur ! */
1559
err = row_upd_check_references_constraints(
1560
node, &pcur, index->table,
1561
index, offsets, thr, &mtr);
1567
btr_pcur_close(&pcur);
1570
if (node->is_delete || err != DB_SUCCESS) {
1575
/* Build a new index entry */
1576
entry = row_build_index_entry(node->upd_row, node->upd_ext,
1580
/* Insert new index entry */
1581
err = row_ins_index_entry(index, entry, 0, TRUE, thr);
1584
mem_heap_free(heap);
1589
/***********************************************************//**
1590
Updates the secondary index record if it is changed in the row update or
1591
deletes it if this is a delete.
1592
@return DB_SUCCESS if operation successfully completed, else error
1593
code or DB_LOCK_WAIT */
1598
upd_node_t* node, /*!< in: row update node */
1599
que_thr_t* thr) /*!< in: query thread */
1601
ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
1602
|| (node->state == UPD_NODE_UPDATE_SOME_SEC));
1603
ut_ad(!dict_index_is_clust(node->index));
1605
if (node->state == UPD_NODE_UPDATE_ALL_SEC
1606
|| row_upd_changes_ord_field_binary(node->row, node->index,
1608
return(row_upd_sec_index_entry(node, thr));
1614
/***********************************************************//**
1615
Marks the clustered index record deleted and inserts the updated version
1616
of the record to the index. This function should be used when the ordering
1617
fields of the clustered index record change. This should be quite rare in
1618
database applications.
1619
@return DB_SUCCESS if operation successfully completed, else error
1620
code or DB_LOCK_WAIT */
1623
row_upd_clust_rec_by_insert(
1624
/*========================*/
1625
upd_node_t* node, /*!< in: row update node */
1626
dict_index_t* index, /*!< in: clustered index of the record */
1627
que_thr_t* thr, /*!< in: query thread */
1628
ibool referenced,/*!< in: TRUE if index may be referenced in
1629
a foreign key constraint */
1630
mtr_t* mtr) /*!< in: mtr; gets committed here */
1632
mem_heap_t* heap = NULL;
1636
dict_table_t* table;
1639
ibool change_ownership = FALSE;
1642
ut_ad(dict_index_is_clust(index));
1644
trx = thr_get_trx(thr);
1645
table = node->table;
1647
btr_cur = btr_pcur_get_btr_cur(pcur);
1649
if (node->state != UPD_NODE_INSERT_CLUSTERED) {
1651
dict_index_t* index;
1652
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1654
rec_offs_init(offsets_);
1656
err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
1657
btr_cur, TRUE, thr, mtr);
1658
if (err != DB_SUCCESS) {
1663
/* Mark as not-owned the externally stored fields which the new
1664
row inherits from the delete marked record: purge should not
1665
free those externally stored fields even if the delete marked
1666
record is removed from the index tree, or updated. */
1668
rec = btr_cur_get_rec(btr_cur);
1669
index = dict_table_get_first_index(table);
1670
offsets = rec_get_offsets(rec, index, offsets_,
1671
ULINT_UNDEFINED, &heap);
1672
change_ownership = btr_cur_mark_extern_inherited_fields(
1673
btr_cur_get_page_zip(btr_cur), rec, index, offsets,
1676
/* NOTE that the following call loses
1677
the position of pcur ! */
1679
err = row_upd_check_references_constraints(
1680
node, pcur, table, index, offsets, thr, mtr);
1682
if (err != DB_SUCCESS) {
1686
if (UNIV_LIKELY_NULL(heap)) {
1687
mem_heap_free(heap);
1698
heap = mem_heap_create(500);
1700
node->state = UPD_NODE_INSERT_CLUSTERED;
1702
entry = row_build_index_entry(node->upd_row, node->upd_ext,
1706
row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
1708
if (change_ownership) {
1709
/* If we return from a lock wait, for example, we may have
1710
extern fields marked as not-owned in entry (marked in the
1711
if-branch above). We must unmark them, take the ownership
1714
btr_cur_unmark_dtuple_extern_fields(entry);
1716
/* We must mark non-updated extern fields in entry as
1717
inherited, so that a possible rollback will not free them. */
1719
btr_cur_mark_dtuple_inherited_extern(entry, node->update);
1722
err = row_ins_index_entry(index, entry,
1723
node->upd_ext ? node->upd_ext->n_ext : 0,
1725
mem_heap_free(heap);
1730
/***********************************************************//**
1731
Updates a clustered index record of a row when the ordering fields do
1733
@return DB_SUCCESS if operation successfully completed, else error
1734
code or DB_LOCK_WAIT */
1739
upd_node_t* node, /*!< in: row update node */
1740
dict_index_t* index, /*!< in: clustered index */
1741
que_thr_t* thr, /*!< in: query thread */
1742
mtr_t* mtr) /*!< in: mtr; gets committed here */
1744
mem_heap_t* heap = NULL;
1745
big_rec_t* big_rec = NULL;
1751
ut_ad(dict_index_is_clust(index));
1754
btr_cur = btr_pcur_get_btr_cur(pcur);
1756
ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
1757
dict_table_is_comp(index->table)));
1759
/* Try optimistic updating of the record, keeping changes within
1760
the page; we do not check locks because we assume the x-lock on the
1763
if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
1764
err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG,
1765
btr_cur, node->update,
1766
node->cmpl_info, thr, mtr);
1768
err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG,
1769
btr_cur, node->update,
1770
node->cmpl_info, thr, mtr);
1775
if (UNIV_LIKELY(err == DB_SUCCESS)) {
1780
if (buf_LRU_buf_pool_running_out()) {
1782
return(DB_LOCK_TABLE_FULL);
1784
/* We may have to modify the tree structure: do a pessimistic descent
1785
down the index tree */
1789
/* NOTE: this transaction has an s-lock or x-lock on the record and
1790
therefore other transactions cannot modify the record when we have no
1791
latch on the page. In addition, we assume that other query threads of
1792
the same transaction do not modify the record in the meantime.
1793
Therefore we can assert that the restoration of the cursor succeeds. */
1795
ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
1797
ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
1798
dict_table_is_comp(index->table)));
1800
err = btr_cur_pessimistic_update(BTR_NO_LOCKING_FLAG, btr_cur,
1801
&heap, &big_rec, node->update,
1802
node->cmpl_info, thr, mtr);
1805
if (err == DB_SUCCESS && big_rec) {
1806
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1808
rec_offs_init(offsets_);
1812
ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
1813
rec = btr_cur_get_rec(btr_cur);
1814
err = btr_store_big_rec_extern_fields(
1815
index, btr_cur_get_block(btr_cur), rec,
1816
rec_get_offsets(rec, index, offsets_,
1817
ULINT_UNDEFINED, &heap),
1822
if (UNIV_LIKELY_NULL(heap)) {
1823
mem_heap_free(heap);
1827
dtuple_big_rec_free(big_rec);
1833
/***********************************************************//**
1834
Delete marks a clustered index record.
1835
@return DB_SUCCESS if operation successfully completed, else error code */
1838
row_upd_del_mark_clust_rec(
1839
/*=======================*/
1840
upd_node_t* node, /*!< in: row update node */
1841
dict_index_t* index, /*!< in: clustered index */
1842
ulint* offsets,/*!< in/out: rec_get_offsets() for the
1843
record under the cursor */
1844
que_thr_t* thr, /*!< in: query thread */
1846
/*!< in: TRUE if index may be referenced in
1847
a foreign key constraint */
1848
mtr_t* mtr) /*!< in: mtr; gets committed here */
1855
ut_ad(dict_index_is_clust(index));
1856
ut_ad(node->is_delete);
1859
btr_cur = btr_pcur_get_btr_cur(pcur);
1861
/* Store row because we have to build also the secondary index
1864
row_upd_store_row(node);
1866
/* Mark the clustered index record deleted; we do not have to check
1867
locks, because we assume that we have an x-lock on the record */
1869
err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
1870
btr_cur, TRUE, thr, mtr);
1871
if (err == DB_SUCCESS && referenced) {
1872
/* NOTE that the following call loses the position of pcur ! */
1874
err = row_upd_check_references_constraints(
1875
node, pcur, index->table, index, offsets, thr, mtr);
1883
/***********************************************************//**
1884
Updates the clustered index record.
1885
@return DB_SUCCESS if operation successfully completed, DB_LOCK_WAIT
1886
in case of a lock wait, else error code */
1891
upd_node_t* node, /*!< in: row update node */
1892
que_thr_t* thr) /*!< in: query thread */
1894
dict_index_t* index;
1901
mem_heap_t* heap = NULL;
1902
ulint offsets_[REC_OFFS_NORMAL_SIZE];
1905
rec_offs_init(offsets_);
1907
index = dict_table_get_first_index(node->table);
1909
referenced = row_upd_index_is_referenced(index, thr_get_trx(thr));
1913
/* We have to restore the cursor to its position */
1918
/* If the restoration does not succeed, then the same
1919
transaction has deleted the record on which the cursor was,
1920
and that is an SQL error. If the restoration succeeds, it may
1921
still be that the same transaction has successively deleted
1922
and inserted a record with the same ordering fields, but in
1923
that case we know that the transaction has at least an
1924
implicit x-lock on the record. */
1926
ut_a(pcur->rel_pos == BTR_PCUR_ON);
1928
success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr);
1931
err = DB_RECORD_NOT_FOUND;
1938
/* If this is a row in SYS_INDEXES table of the data dictionary,
1939
then we have to free the file segments of the index tree associated
1942
if (node->is_delete && node->table->id == DICT_INDEXES_ID) {
1944
dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr);
1950
success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
1961
rec = btr_pcur_get_rec(pcur);
1962
offsets = rec_get_offsets(rec, index, offsets_,
1963
ULINT_UNDEFINED, &heap);
1965
if (!node->has_clust_rec_x_lock) {
1966
err = lock_clust_rec_modify_check_and_lock(
1967
0, btr_pcur_get_block(pcur),
1968
rec, index, offsets, thr);
1969
if (err != DB_SUCCESS) {
1975
/* NOTE: the following function calls will also commit mtr */
1977
if (node->is_delete) {
1978
err = row_upd_del_mark_clust_rec(
1979
node, index, offsets, thr, referenced, mtr);
1981
if (err == DB_SUCCESS) {
1982
node->state = UPD_NODE_UPDATE_ALL_SEC;
1983
node->index = dict_table_get_next_index(index);
1986
if (UNIV_LIKELY_NULL(heap)) {
1987
mem_heap_free(heap);
1992
/* If the update is made for MySQL, we already have the update vector
1993
ready, else we have to do some evaluation: */
1995
if (UNIV_UNLIKELY(!node->in_mysql_interface)) {
1996
/* Copy the necessary columns from clust_rec and calculate the
1997
new values to set */
1998
row_upd_copy_columns(rec, offsets,
1999
UT_LIST_GET_FIRST(node->columns));
2000
row_upd_eval_new_vals(node->update);
2003
if (UNIV_LIKELY_NULL(heap)) {
2004
mem_heap_free(heap);
2007
if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
2009
err = row_upd_clust_rec(node, index, thr, mtr);
2013
row_upd_store_row(node);
2015
if (row_upd_changes_ord_field_binary(node->row, index, node->update)) {
2017
/* Update causes an ordering field (ordering fields within
2018
the B-tree) of the clustered index record to change: perform
2019
the update by delete marking and inserting.
2021
TODO! What to do to the 'Halloween problem', where an update
2022
moves the record forward in index so that it is again
2023
updated when the cursor arrives there? Solution: the
2024
read operation must check the undo record undo number when
2025
choosing records to update. MySQL solves now the problem
2028
err = row_upd_clust_rec_by_insert(
2029
node, index, thr, referenced, mtr);
2031
if (err != DB_SUCCESS) {
2036
node->state = UPD_NODE_UPDATE_ALL_SEC;
2038
err = row_upd_clust_rec(node, index, thr, mtr);
2040
if (err != DB_SUCCESS) {
2045
node->state = UPD_NODE_UPDATE_SOME_SEC;
2048
node->index = dict_table_get_next_index(index);
2053
/***********************************************************//**
2054
Updates the affected index records of a row. When the control is transferred
2055
to this node, we assume that we have a persistent cursor which was on a
2056
record, and the position of the cursor is stored in the cursor.
2057
@return DB_SUCCESS if operation successfully completed, else error
2058
code or DB_LOCK_WAIT */
2063
upd_node_t* node, /*!< in: row update node */
2064
que_thr_t* thr) /*!< in: query thread */
2066
ulint err = DB_SUCCESS;
2070
if (UNIV_LIKELY(node->in_mysql_interface)) {
2072
/* We do not get the cmpl_info value from the MySQL
2073
interpreter: we must calculate it on the fly: */
2076
|| row_upd_changes_some_index_ord_field_binary(
2077
node->table, node->update)) {
2078
node->cmpl_info = 0;
2080
node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
2084
if (node->state == UPD_NODE_UPDATE_CLUSTERED
2085
|| node->state == UPD_NODE_INSERT_CLUSTERED) {
2088
err = row_upd_clust_step(node, thr);
2090
if (err != DB_SUCCESS) {
2096
if (!node->is_delete && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
2101
while (node->index != NULL) {
2104
err = row_upd_sec_step(node, thr);
2106
if (err != DB_SUCCESS) {
2111
node->index = dict_table_get_next_index(node->index);
2115
if (err == DB_SUCCESS) {
2116
/* Do some cleanup */
2118
if (node->row != NULL) {
2121
node->upd_row = NULL;
2122
node->upd_ext = NULL;
2123
mem_heap_empty(node->heap);
2126
node->state = UPD_NODE_UPDATE_CLUSTERED;
2132
/***********************************************************//**
2133
Updates a row in a table. This is a high-level function used in SQL execution
2135
@return query thread to run next or NULL */
2140
que_thr_t* thr) /*!< in: query thread */
2143
sel_node_t* sel_node;
2145
ulint err = DB_SUCCESS;
2150
trx = thr_get_trx(thr);
2152
trx_start_if_not_started(trx);
2154
node = thr->run_node;
2156
sel_node = node->select;
2158
parent = que_node_get_parent(node);
2160
ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
2162
if (thr->prev_node == parent) {
2163
node->state = UPD_NODE_SET_IX_LOCK;
2166
if (node->state == UPD_NODE_SET_IX_LOCK) {
2168
if (!node->has_clust_rec_x_lock) {
2169
/* It may be that the current session has not yet
2170
started its transaction, or it has been committed: */
2172
err = lock_table(0, node->table, LOCK_IX, thr);
2174
if (err != DB_SUCCESS) {
2176
goto error_handling;
2180
node->state = UPD_NODE_UPDATE_CLUSTERED;
2182
if (node->searched_update) {
2183
/* Reset the cursor */
2184
sel_node->state = SEL_NODE_OPEN;
2186
/* Fetch a row to update */
2188
thr->run_node = sel_node;
2194
/* sel_node is NULL if we are in the MySQL interface */
2196
if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
2198
if (!node->searched_update) {
2199
/* An explicit cursor should be positioned on a row
2206
goto error_handling;
2209
ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
2211
/* No more rows to update, or the select node performed the
2212
updates directly in-place */
2214
thr->run_node = parent;
2219
/* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
2221
err = row_upd(node, thr);
2224
trx->error_state = err;
2226
if (err != DB_SUCCESS) {
2230
/* DO THE TRIGGER ACTIONS HERE */
2232
if (node->searched_update) {
2233
/* Fetch next row to update */
2235
thr->run_node = sel_node;
2237
/* It was an explicit cursor update */
2239
thr->run_node = parent;
2242
node->state = UPD_NODE_UPDATE_CLUSTERED;
2246
#endif /* !UNIV_HOTBACKUP */