~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/innobase/row/row0umod.cc

  • Committer: Monty Taylor
  • Date: 2008-10-16 06:32:59 UTC
  • mfrom: (518 drizzle)
  • mto: (511.1.5 codestyle)
  • mto: This revision was merged to the branch mainline in revision 521.
  • Revision ID: monty@inaugust.com-20081016063259-fwbqogq7lnezct0l
Merged with trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*****************************************************************************
2
 
 
3
 
Copyright (C) 1997, 2010, Innobase Oy. All Rights Reserved.
4
 
 
5
 
This program is free software; you can redistribute it and/or modify it under
6
 
the terms of the GNU General Public License as published by the Free Software
7
 
Foundation; version 2 of the License.
8
 
 
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.
12
 
 
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
16
 
 
17
 
*****************************************************************************/
18
 
 
19
 
/**************************************************//**
20
 
@file row/row0umod.c
21
 
Undo modify of a row
22
 
 
23
 
Created 2/27/1997 Heikki Tuuri
24
 
*******************************************************/
25
 
 
26
 
#include "row0umod.h"
27
 
 
28
 
#ifdef UNIV_NONINL
29
 
#include "row0umod.ic"
30
 
#endif
31
 
 
32
 
#include "dict0dict.h"
33
 
#include "dict0boot.h"
34
 
#include "trx0undo.h"
35
 
#include "trx0roll.h"
36
 
#include "btr0btr.h"
37
 
#include "mach0data.h"
38
 
#include "row0undo.h"
39
 
#include "row0vers.h"
40
 
#include "trx0trx.h"
41
 
#include "trx0rec.h"
42
 
#include "row0row.h"
43
 
#include "row0upd.h"
44
 
#include "que0que.h"
45
 
#include "log0log.h"
46
 
 
47
 
/* Considerations on undoing a modify operation.
48
 
(1) Undoing a delete marking: all index records should be found. Some of
49
 
them may have delete mark already FALSE, if the delete mark operation was
50
 
stopped underway, or if the undo operation ended prematurely because of a
51
 
system crash.
52
 
(2) Undoing an update of a delete unmarked record: the newer version of
53
 
an updated secondary index entry should be removed if no prior version
54
 
of the clustered index record requires its existence. Otherwise, it should
55
 
be delete marked.
56
 
(3) Undoing an update of a delete marked record. In this kind of update a
57
 
delete marked clustered index record was delete unmarked and possibly also
58
 
some of its fields were changed. Now, it is possible that the delete marked
59
 
version has become obsolete at the time the undo is started. */
60
 
 
61
 
/*************************************************************************
62
 
IMPORTANT NOTE: Any operation that generates redo MUST check that there
63
 
is enough space in the redo log before for that operation. This is
64
 
done by calling log_free_check(). The reason for checking the
65
 
availability of the redo log space before the start of the operation is
66
 
that we MUST not hold any synchonization objects when performing the
67
 
check.
68
 
If you make a change in this module make sure that no codepath is
69
 
introduced where a call to log_free_check() is bypassed. */
70
 
 
71
 
/***********************************************************//**
72
 
Checks if also the previous version of the clustered index record was
73
 
modified or inserted by the same transaction, and its undo number is such
74
 
that it should be undone in the same rollback.
75
 
@return TRUE if also previous modify or insert of this row should be undone */
76
 
static
77
 
ibool
78
 
row_undo_mod_undo_also_prev_vers(
79
 
/*=============================*/
80
 
        undo_node_t*    node,   /*!< in: row undo node */
81
 
        undo_no_t*      undo_no)/*!< out: the undo number */
82
 
{
83
 
        trx_undo_rec_t* undo_rec;
84
 
        trx_t*          trx;
85
 
 
86
 
        trx = node->trx;
87
 
 
88
 
        if (node->new_trx_id != trx->id) {
89
 
 
90
 
                *undo_no = 0;
91
 
                return(FALSE);
92
 
        }
93
 
 
94
 
        undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
95
 
 
96
 
        *undo_no = trx_undo_rec_get_undo_no(undo_rec);
97
 
 
98
 
        return(trx->roll_limit <= *undo_no);
99
 
}
100
 
 
101
 
/***********************************************************//**
102
 
Undoes a modify in a clustered index record.
103
 
@return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
104
 
static
105
 
ulint
106
 
row_undo_mod_clust_low(
107
 
/*===================*/
108
 
        undo_node_t*    node,   /*!< in: row undo node */
109
 
        que_thr_t*      thr,    /*!< in: query thread */
110
 
        mtr_t*          mtr,    /*!< in: mtr; must be committed before
111
 
                                latching any further pages */
112
 
        ulint           mode)   /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
113
 
{
114
 
        btr_pcur_t*     pcur;
115
 
        btr_cur_t*      btr_cur;
116
 
        ulint           err;
117
 
#ifdef UNIV_DEBUG
118
 
        ibool           success;
119
 
#endif /* UNIV_DEBUG */
120
 
 
121
 
        pcur = &(node->pcur);
122
 
        btr_cur = btr_pcur_get_btr_cur(pcur);
123
 
 
124
 
#ifdef UNIV_DEBUG
125
 
        success =
126
 
#endif /* UNIV_DEBUG */
127
 
        btr_pcur_restore_position(mode, pcur, mtr);
128
 
 
129
 
        ut_ad(success);
130
 
 
131
 
        if (mode == BTR_MODIFY_LEAF) {
132
 
 
133
 
                err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
134
 
                                                | BTR_NO_UNDO_LOG_FLAG
135
 
                                                | BTR_KEEP_SYS_FLAG,
136
 
                                                btr_cur, node->update,
137
 
                                                node->cmpl_info, thr, mtr);
138
 
        } else {
139
 
                mem_heap_t*     heap            = NULL;
140
 
                big_rec_t*      dummy_big_rec;
141
 
 
142
 
                ut_ad(mode == BTR_MODIFY_TREE);
143
 
 
144
 
                err = btr_cur_pessimistic_update(
145
 
                        BTR_NO_LOCKING_FLAG
146
 
                        | BTR_NO_UNDO_LOG_FLAG
147
 
                        | BTR_KEEP_SYS_FLAG,
148
 
                        btr_cur, &heap, &dummy_big_rec, node->update,
149
 
                        node->cmpl_info, thr, mtr);
150
 
 
151
 
                ut_a(!dummy_big_rec);
152
 
                if (UNIV_LIKELY_NULL(heap)) {
153
 
                        mem_heap_free(heap);
154
 
                }
155
 
        }
156
 
 
157
 
        return(err);
158
 
}
159
 
 
160
 
/***********************************************************//**
161
 
Removes a clustered index record after undo if possible.
162
 
This is attempted when the record was inserted by updating a
163
 
delete-marked record and there no longer exist transactions
164
 
that would see the delete-marked record.  In other words, we
165
 
roll back the insert by purging the record.
166
 
@return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
167
 
static
168
 
ulint
169
 
row_undo_mod_remove_clust_low(
170
 
/*==========================*/
171
 
        undo_node_t*    node,   /*!< in: row undo node */
172
 
        que_thr_t*      thr,    /*!< in: query thread */
173
 
        mtr_t*          mtr,    /*!< in: mtr */
174
 
        ulint           mode)   /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
175
 
{
176
 
        btr_pcur_t*     pcur;
177
 
        btr_cur_t*      btr_cur;
178
 
        ulint           err;
179
 
        ibool           success;
180
 
 
181
 
        ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
182
 
        pcur = &(node->pcur);
183
 
        btr_cur = btr_pcur_get_btr_cur(pcur);
184
 
 
185
 
        success = btr_pcur_restore_position(mode, pcur, mtr);
186
 
 
187
 
        if (!success) {
188
 
 
189
 
                return(DB_SUCCESS);
190
 
        }
191
 
 
192
 
        /* Find out if we can remove the whole clustered index record */
193
 
 
194
 
        if (node->rec_type == TRX_UNDO_UPD_DEL_REC
195
 
            && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
196
 
 
197
 
                /* Ok, we can remove */
198
 
        } else {
199
 
                return(DB_SUCCESS);
200
 
        }
201
 
 
202
 
        if (mode == BTR_MODIFY_LEAF) {
203
 
                success = btr_cur_optimistic_delete(btr_cur, mtr);
204
 
 
205
 
                if (success) {
206
 
                        err = DB_SUCCESS;
207
 
                } else {
208
 
                        err = DB_FAIL;
209
 
                }
210
 
        } else {
211
 
                ut_ad(mode == BTR_MODIFY_TREE);
212
 
 
213
 
                /* This operation is analogous to purge, we can free also
214
 
                inherited externally stored fields */
215
 
 
216
 
                btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
217
 
                                           thr_is_recv(thr)
218
 
                                           ? RB_RECOVERY_PURGE_REC
219
 
                                           : RB_NONE, mtr);
220
 
 
221
 
                /* The delete operation may fail if we have little
222
 
                file space left: TODO: easiest to crash the database
223
 
                and restart with more file space */
224
 
        }
225
 
 
226
 
        return(err);
227
 
}
228
 
 
229
 
/***********************************************************//**
230
 
Undoes a modify in a clustered index record. Sets also the node state for the
231
 
next round of undo.
232
 
@return DB_SUCCESS or error code: we may run out of file space */
233
 
static
234
 
ulint
235
 
row_undo_mod_clust(
236
 
/*===============*/
237
 
        undo_node_t*    node,   /*!< in: row undo node */
238
 
        que_thr_t*      thr)    /*!< in: query thread */
239
 
{
240
 
        btr_pcur_t*     pcur;
241
 
        mtr_t           mtr;
242
 
        ulint           err;
243
 
        ibool           success;
244
 
        ibool           more_vers;
245
 
        undo_no_t       new_undo_no;
246
 
 
247
 
        ut_ad(node && thr);
248
 
 
249
 
        log_free_check();
250
 
 
251
 
        /* Check if also the previous version of the clustered index record
252
 
        should be undone in this same rollback operation */
253
 
 
254
 
        more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
255
 
 
256
 
        pcur = &(node->pcur);
257
 
 
258
 
        mtr_start(&mtr);
259
 
 
260
 
        /* Try optimistic processing of the record, keeping changes within
261
 
        the index page */
262
 
 
263
 
        err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
264
 
 
265
 
        if (err != DB_SUCCESS) {
266
 
                btr_pcur_commit_specify_mtr(pcur, &mtr);
267
 
 
268
 
                /* We may have to modify tree structure: do a pessimistic
269
 
                descent down the index tree */
270
 
 
271
 
                mtr_start(&mtr);
272
 
 
273
 
                err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
274
 
        }
275
 
 
276
 
        btr_pcur_commit_specify_mtr(pcur, &mtr);
277
 
 
278
 
        if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
279
 
 
280
 
                mtr_start(&mtr);
281
 
 
282
 
                err = row_undo_mod_remove_clust_low(node, thr, &mtr,
283
 
                                                    BTR_MODIFY_LEAF);
284
 
                if (err != DB_SUCCESS) {
285
 
                        btr_pcur_commit_specify_mtr(pcur, &mtr);
286
 
 
287
 
                        /* We may have to modify tree structure: do a
288
 
                        pessimistic descent down the index tree */
289
 
 
290
 
                        mtr_start(&mtr);
291
 
 
292
 
                        err = row_undo_mod_remove_clust_low(node, thr, &mtr,
293
 
                                                            BTR_MODIFY_TREE);
294
 
                }
295
 
 
296
 
                btr_pcur_commit_specify_mtr(pcur, &mtr);
297
 
        }
298
 
 
299
 
        node->state = UNDO_NODE_FETCH_NEXT;
300
 
 
301
 
        trx_undo_rec_release(node->trx, node->undo_no);
302
 
 
303
 
        if (more_vers && err == DB_SUCCESS) {
304
 
 
305
 
                /* Reserve the undo log record to the prior version after
306
 
                committing &mtr: this is necessary to comply with the latching
307
 
                order, as &mtr may contain the fsp latch which is lower in
308
 
                the latch hierarchy than trx->undo_mutex. */
309
 
 
310
 
                success = trx_undo_rec_reserve(node->trx, new_undo_no);
311
 
 
312
 
                if (success) {
313
 
                        node->state = UNDO_NODE_PREV_VERS;
314
 
                }
315
 
        }
316
 
 
317
 
        return(err);
318
 
}
319
 
 
320
 
/***********************************************************//**
321
 
Delete marks or removes a secondary index entry if found.
322
 
@return DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
323
 
static
324
 
ulint
325
 
row_undo_mod_del_mark_or_remove_sec_low(
326
 
/*====================================*/
327
 
        undo_node_t*    node,   /*!< in: row undo node */
328
 
        que_thr_t*      thr,    /*!< in: query thread */
329
 
        dict_index_t*   index,  /*!< in: index */
330
 
        dtuple_t*       entry,  /*!< in: index entry */
331
 
        ulint           mode)   /*!< in: latch mode BTR_MODIFY_LEAF or
332
 
                                BTR_MODIFY_TREE */
333
 
{
334
 
        btr_pcur_t              pcur;
335
 
        btr_cur_t*              btr_cur;
336
 
        ibool                   success;
337
 
        ibool                   old_has;
338
 
        ulint                   err;
339
 
        mtr_t                   mtr;
340
 
        mtr_t                   mtr_vers;
341
 
        enum row_search_result  search_result;
342
 
 
343
 
        log_free_check();
344
 
        mtr_start(&mtr);
345
 
 
346
 
        btr_cur = btr_pcur_get_btr_cur(&pcur);
347
 
 
348
 
        ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
349
 
 
350
 
        search_result = row_search_index_entry(index, entry, mode,
351
 
                                               &pcur, &mtr);
352
 
 
353
 
        switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
354
 
        case ROW_NOT_FOUND:
355
 
                /* In crash recovery, the secondary index record may
356
 
                be missing if the UPDATE did not have time to insert
357
 
                the secondary index records before the crash.  When we
358
 
                are undoing that UPDATE in crash recovery, the record
359
 
                may be missing.
360
 
 
361
 
                In normal processing, if an update ends in a deadlock
362
 
                before it has inserted all updated secondary index
363
 
                records, then the undo will not find those records. */
364
 
 
365
 
                err = DB_SUCCESS;
366
 
                goto func_exit;
367
 
        case ROW_FOUND:
368
 
                break;
369
 
        case ROW_BUFFERED:
370
 
        case ROW_NOT_DELETED_REF:
371
 
                /* These are invalid outcomes, because the mode passed
372
 
                to row_search_index_entry() did not include any of the
373
 
                flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
374
 
                ut_error;
375
 
        }
376
 
 
377
 
        /* We should remove the index record if no prior version of the row,
378
 
        which cannot be purged yet, requires its existence. If some requires,
379
 
        we should delete mark the record. */
380
 
 
381
 
        mtr_start(&mtr_vers);
382
 
 
383
 
        success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
384
 
                                            &mtr_vers);
385
 
        ut_a(success);
386
 
 
387
 
        old_has = row_vers_old_has_index_entry(FALSE,
388
 
                                               btr_pcur_get_rec(&(node->pcur)),
389
 
                                               &mtr_vers, index, entry);
390
 
        if (old_has) {
391
 
                err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
392
 
                                                   btr_cur, TRUE, thr, &mtr);
393
 
                ut_ad(err == DB_SUCCESS);
394
 
        } else {
395
 
                /* Remove the index record */
396
 
 
397
 
                if (mode == BTR_MODIFY_LEAF) {
398
 
                        success = btr_cur_optimistic_delete(btr_cur, &mtr);
399
 
                        if (success) {
400
 
                                err = DB_SUCCESS;
401
 
                        } else {
402
 
                                err = DB_FAIL;
403
 
                        }
404
 
                } else {
405
 
                        ut_ad(mode == BTR_MODIFY_TREE);
406
 
 
407
 
                        /* No need to distinguish RB_RECOVERY_PURGE here,
408
 
                        because we are deleting a secondary index record:
409
 
                        the distinction between RB_NORMAL and
410
 
                        RB_RECOVERY_PURGE only matters when deleting a
411
 
                        record that contains externally stored
412
 
                        columns. */
413
 
                        ut_ad(!dict_index_is_clust(index));
414
 
                        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
415
 
                                                   RB_NORMAL, &mtr);
416
 
 
417
 
                        /* The delete operation may fail if we have little
418
 
                        file space left: TODO: easiest to crash the database
419
 
                        and restart with more file space */
420
 
                }
421
 
        }
422
 
 
423
 
        btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
424
 
 
425
 
func_exit:
426
 
        btr_pcur_close(&pcur);
427
 
        mtr_commit(&mtr);
428
 
 
429
 
        return(err);
430
 
}
431
 
 
432
 
/***********************************************************//**
433
 
Delete marks or removes a secondary index entry if found.
434
 
NOTE that if we updated the fields of a delete-marked secondary index record
435
 
so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
436
 
return to the original values because we do not know them. But this should
437
 
not cause problems because in row0sel.c, in queries we always retrieve the
438
 
clustered index record or an earlier version of it, if the secondary index
439
 
record through which we do the search is delete-marked.
440
 
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
441
 
static
442
 
ulint
443
 
row_undo_mod_del_mark_or_remove_sec(
444
 
/*================================*/
445
 
        undo_node_t*    node,   /*!< in: row undo node */
446
 
        que_thr_t*      thr,    /*!< in: query thread */
447
 
        dict_index_t*   index,  /*!< in: index */
448
 
        dtuple_t*       entry)  /*!< in: index entry */
449
 
{
450
 
        ulint   err;
451
 
 
452
 
        err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
453
 
                                                      entry, BTR_MODIFY_LEAF);
454
 
        if (err == DB_SUCCESS) {
455
 
 
456
 
                return(err);
457
 
        }
458
 
 
459
 
        err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
460
 
                                                      entry, BTR_MODIFY_TREE);
461
 
        return(err);
462
 
}
463
 
 
464
 
/***********************************************************//**
465
 
Delete unmarks a secondary index entry which must be found. It might not be
466
 
delete-marked at the moment, but it does not harm to unmark it anyway. We also
467
 
need to update the fields of the secondary index record if we updated its
468
 
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'.
469
 
@return DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
470
 
static
471
 
ulint
472
 
row_undo_mod_del_unmark_sec_and_undo_update(
473
 
/*========================================*/
474
 
        ulint           mode,   /*!< in: search mode: BTR_MODIFY_LEAF or
475
 
                                BTR_MODIFY_TREE */
476
 
        que_thr_t*      thr,    /*!< in: query thread */
477
 
        dict_index_t*   index,  /*!< in: index */
478
 
        const dtuple_t* entry)  /*!< in: index entry */
479
 
{
480
 
        mem_heap_t*             heap;
481
 
        btr_pcur_t              pcur;
482
 
        btr_cur_t*              btr_cur;
483
 
        upd_t*                  update;
484
 
        ulint                   err             = DB_SUCCESS;
485
 
        big_rec_t*              dummy_big_rec;
486
 
        mtr_t                   mtr;
487
 
        trx_t*                  trx             = thr_get_trx(thr);
488
 
        enum row_search_result  search_result;
489
 
 
490
 
        /* Ignore indexes that are being created. */
491
 
        if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
492
 
 
493
 
                return(DB_SUCCESS);
494
 
        }
495
 
 
496
 
        log_free_check();
497
 
        mtr_start(&mtr);
498
 
 
499
 
        ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
500
 
 
501
 
        search_result = row_search_index_entry(index, entry, mode,
502
 
                                               &pcur, &mtr);
503
 
 
504
 
        switch (search_result) {
505
 
        case ROW_BUFFERED:
506
 
        case ROW_NOT_DELETED_REF:
507
 
                /* These are invalid outcomes, because the mode passed
508
 
                to row_search_index_entry() did not include any of the
509
 
                flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
510
 
                ut_error;
511
 
        case ROW_NOT_FOUND:
512
 
                fputs("InnoDB: error in sec index entry del undo in\n"
513
 
                      "InnoDB: ", stderr);
514
 
                dict_index_name_print(stderr, trx, index);
515
 
                fputs("\n"
516
 
                      "InnoDB: tuple ", stderr);
517
 
                dtuple_print(stderr, entry);
518
 
                fputs("\n"
519
 
                      "InnoDB: record ", stderr);
520
 
                rec_print(stderr, btr_pcur_get_rec(&pcur), index);
521
 
                putc('\n', stderr);
522
 
                trx_print(stderr, trx, 0);
523
 
                fputs("\n"
524
 
                      "InnoDB: Submit a detailed bug report"
525
 
                      " to http://bugs.mysql.com\n", stderr);
526
 
                break;
527
 
        case ROW_FOUND:
528
 
                btr_cur = btr_pcur_get_btr_cur(&pcur);
529
 
                err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
530
 
                                                   btr_cur, FALSE, thr, &mtr);
531
 
                ut_a(err == DB_SUCCESS);
532
 
                heap = mem_heap_create(100);
533
 
 
534
 
                update = row_upd_build_sec_rec_difference_binary(
535
 
                        index, entry, btr_cur_get_rec(btr_cur), trx, heap);
536
 
                if (upd_get_n_fields(update) == 0) {
537
 
 
538
 
                        /* Do nothing */
539
 
 
540
 
                } else if (mode == BTR_MODIFY_LEAF) {
541
 
                        /* Try an optimistic updating of the record, keeping
542
 
                        changes within the page */
543
 
 
544
 
                        err = btr_cur_optimistic_update(
545
 
                                BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
546
 
                                btr_cur, update, 0, thr, &mtr);
547
 
                        switch (err) {
548
 
                        case DB_OVERFLOW:
549
 
                        case DB_UNDERFLOW:
550
 
                        case DB_ZIP_OVERFLOW:
551
 
                                err = DB_FAIL;
552
 
                        }
553
 
                } else {
554
 
                        ut_a(mode == BTR_MODIFY_TREE);
555
 
                        err = btr_cur_pessimistic_update(
556
 
                                BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
557
 
                                btr_cur, &heap, &dummy_big_rec,
558
 
                                update, 0, thr, &mtr);
559
 
                        ut_a(!dummy_big_rec);
560
 
                }
561
 
 
562
 
                mem_heap_free(heap);
563
 
        }
564
 
 
565
 
        btr_pcur_close(&pcur);
566
 
        mtr_commit(&mtr);
567
 
 
568
 
        return(err);
569
 
}
570
 
 
571
 
/***********************************************************//**
572
 
Undoes a modify in secondary indexes when undo record type is UPD_DEL.
573
 
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
574
 
static
575
 
ulint
576
 
row_undo_mod_upd_del_sec(
577
 
/*=====================*/
578
 
        undo_node_t*    node,   /*!< in: row undo node */
579
 
        que_thr_t*      thr)    /*!< in: query thread */
580
 
{
581
 
        mem_heap_t*     heap;
582
 
        dtuple_t*       entry;
583
 
        dict_index_t*   index;
584
 
        ulint           err     = DB_SUCCESS;
585
 
 
586
 
        ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
587
 
        heap = mem_heap_create(1024);
588
 
 
589
 
        while (node->index != NULL) {
590
 
                index = node->index;
591
 
 
592
 
                entry = row_build_index_entry(node->row, node->ext,
593
 
                                              index, heap);
594
 
                if (UNIV_UNLIKELY(!entry)) {
595
 
                        /* The database must have crashed after
596
 
                        inserting a clustered index record but before
597
 
                        writing all the externally stored columns of
598
 
                        that record.  Because secondary index entries
599
 
                        are inserted after the clustered index record,
600
 
                        we may assume that the secondary index record
601
 
                        does not exist.  However, this situation may
602
 
                        only occur during the rollback of incomplete
603
 
                        transactions. */
604
 
                        ut_a(thr_is_recv(thr));
605
 
                } else {
606
 
                        err = row_undo_mod_del_mark_or_remove_sec(
607
 
                                node, thr, index, entry);
608
 
 
609
 
                        if (err != DB_SUCCESS) {
610
 
 
611
 
                                break;
612
 
                        }
613
 
                }
614
 
 
615
 
                mem_heap_empty(heap);
616
 
 
617
 
                node->index = dict_table_get_next_index(node->index);
618
 
        }
619
 
 
620
 
        mem_heap_free(heap);
621
 
 
622
 
        return(err);
623
 
}
624
 
 
625
 
/***********************************************************//**
626
 
Undoes a modify in secondary indexes when undo record type is DEL_MARK.
627
 
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
628
 
static
629
 
ulint
630
 
row_undo_mod_del_mark_sec(
631
 
/*======================*/
632
 
        undo_node_t*    node,   /*!< in: row undo node */
633
 
        que_thr_t*      thr)    /*!< in: query thread */
634
 
{
635
 
        mem_heap_t*     heap;
636
 
        dtuple_t*       entry;
637
 
        dict_index_t*   index;
638
 
        ulint           err;
639
 
 
640
 
        heap = mem_heap_create(1024);
641
 
 
642
 
        while (node->index != NULL) {
643
 
                index = node->index;
644
 
 
645
 
                entry = row_build_index_entry(node->row, node->ext,
646
 
                                              index, heap);
647
 
                ut_a(entry);
648
 
                err = row_undo_mod_del_unmark_sec_and_undo_update(
649
 
                        BTR_MODIFY_LEAF, thr, index, entry);
650
 
                if (err == DB_FAIL) {
651
 
                        err = row_undo_mod_del_unmark_sec_and_undo_update(
652
 
                                BTR_MODIFY_TREE, thr, index, entry);
653
 
                }
654
 
 
655
 
                if (err != DB_SUCCESS) {
656
 
 
657
 
                        mem_heap_free(heap);
658
 
 
659
 
                        return(err);
660
 
                }
661
 
 
662
 
                node->index = dict_table_get_next_index(node->index);
663
 
        }
664
 
 
665
 
        mem_heap_free(heap);
666
 
 
667
 
        return(DB_SUCCESS);
668
 
}
669
 
 
670
 
/***********************************************************//**
671
 
Undoes a modify in secondary indexes when undo record type is UPD_EXIST.
672
 
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
673
 
static
674
 
ulint
675
 
row_undo_mod_upd_exist_sec(
676
 
/*=======================*/
677
 
        undo_node_t*    node,   /*!< in: row undo node */
678
 
        que_thr_t*      thr)    /*!< in: query thread */
679
 
{
680
 
        mem_heap_t*     heap;
681
 
        dtuple_t*       entry;
682
 
        dict_index_t*   index;
683
 
        ulint           err;
684
 
 
685
 
        if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
686
 
                /* No change in secondary indexes */
687
 
 
688
 
                return(DB_SUCCESS);
689
 
        }
690
 
 
691
 
        heap = mem_heap_create(1024);
692
 
 
693
 
        while (node->index != NULL) {
694
 
                index = node->index;
695
 
 
696
 
                if (row_upd_changes_ord_field_binary(node->row, node->index,
697
 
                                                     node->update)) {
698
 
 
699
 
                        /* Build the newest version of the index entry */
700
 
                        entry = row_build_index_entry(node->row, node->ext,
701
 
                                                      index, heap);
702
 
                        if (UNIV_UNLIKELY(!entry)) {
703
 
                                /* The server must have crashed in
704
 
                                row_upd_clust_rec_by_insert(), in
705
 
                                row_ins_index_entry_low() before
706
 
                                btr_store_big_rec_extern_fields()
707
 
                                has written the externally stored columns
708
 
                                (BLOBs) of the new clustered index entry. */
709
 
 
710
 
                                /* The table must be in DYNAMIC or COMPRESSED
711
 
                                format.  REDUNDANT and COMPACT formats
712
 
                                store a local 768-byte prefix of each
713
 
                                externally stored column. */
714
 
                                ut_a(dict_table_get_format(index->table)
715
 
                                     >= DICT_TF_FORMAT_ZIP);
716
 
 
717
 
                                /* This is only legitimate when
718
 
                                rolling back an incomplete transaction
719
 
                                after crash recovery. */
720
 
                                ut_a(thr_get_trx(thr)->is_recovered);
721
 
 
722
 
                                /* The server must have crashed before
723
 
                                completing the insert of the new
724
 
                                clustered index entry and before
725
 
                                inserting to the secondary indexes.
726
 
                                Because node->row was not yet written
727
 
                                to this index, we can ignore it.  But
728
 
                                we must restore node->undo_row. */
729
 
                        } else {
730
 
                                /* NOTE that if we updated the fields of a
731
 
                                delete-marked secondary index record so that
732
 
                                alphabetically they stayed the same, e.g.,
733
 
                                'abc' -> 'aBc', we cannot return to the
734
 
                                original values because we do not know them.
735
 
                                But this should not cause problems because
736
 
                                in row0sel.c, in queries we always retrieve
737
 
                                the clustered index record or an earlier
738
 
                                version of it, if the secondary index record
739
 
                                through which we do the search is
740
 
                                delete-marked. */
741
 
 
742
 
                                err = row_undo_mod_del_mark_or_remove_sec(
743
 
                                        node, thr, index, entry);
744
 
                                if (err != DB_SUCCESS) {
745
 
                                        mem_heap_free(heap);
746
 
 
747
 
                                        return(err);
748
 
                                }
749
 
 
750
 
                                mem_heap_empty(heap);
751
 
                        }
752
 
 
753
 
                        /* We may have to update the delete mark in the
754
 
                        secondary index record of the previous version of
755
 
                        the row. We also need to update the fields of
756
 
                        the secondary index record if we updated its fields
757
 
                        but alphabetically they stayed the same, e.g.,
758
 
                        'abc' -> 'aBc'. */
759
 
                        entry = row_build_index_entry(node->undo_row,
760
 
                                                      node->undo_ext,
761
 
                                                      index, heap);
762
 
                        ut_a(entry);
763
 
 
764
 
                        err = row_undo_mod_del_unmark_sec_and_undo_update(
765
 
                                BTR_MODIFY_LEAF, thr, index, entry);
766
 
                        if (err == DB_FAIL) {
767
 
                                err = row_undo_mod_del_unmark_sec_and_undo_update(
768
 
                                        BTR_MODIFY_TREE, thr, index, entry);
769
 
                        }
770
 
 
771
 
                        if (err != DB_SUCCESS) {
772
 
                                mem_heap_free(heap);
773
 
 
774
 
                                return(err);
775
 
                        }
776
 
                }
777
 
 
778
 
                node->index = dict_table_get_next_index(node->index);
779
 
        }
780
 
 
781
 
        mem_heap_free(heap);
782
 
 
783
 
        return(DB_SUCCESS);
784
 
}
785
 
 
786
 
/***********************************************************//**
787
 
Parses the row reference and other info in a modify undo log record. */
788
 
static
789
 
void
790
 
row_undo_mod_parse_undo_rec(
791
 
/*========================*/
792
 
        undo_node_t*    node,   /*!< in: row undo node */
793
 
        que_thr_t*      thr)    /*!< in: query thread */
794
 
{
795
 
        dict_index_t*   clust_index;
796
 
        byte*           ptr;
797
 
        undo_no_t       undo_no;
798
 
        table_id_t      table_id;
799
 
        trx_id_t        trx_id;
800
 
        roll_ptr_t      roll_ptr;
801
 
        ulint           info_bits;
802
 
        ulint           type;
803
 
        ulint           cmpl_info;
804
 
        ibool           dummy_extern;
805
 
        trx_t*          trx;
806
 
 
807
 
        ut_ad(node && thr);
808
 
        trx = thr_get_trx(thr);
809
 
        ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
810
 
                                    &dummy_extern, &undo_no, &table_id);
811
 
        node->rec_type = type;
812
 
 
813
 
        node->table = dict_table_get_on_id(table_id, trx);
814
 
 
815
 
        /* TODO: other fixes associated with DROP TABLE + rollback in the
816
 
        same table by another user */
817
 
 
818
 
        if (node->table == NULL) {
819
 
                /* Table was dropped */
820
 
                return;
821
 
        }
822
 
 
823
 
        if (node->table->ibd_file_missing) {
824
 
                /* We skip undo operations to missing .ibd files */
825
 
                node->table = NULL;
826
 
 
827
 
                return;
828
 
        }
829
 
 
830
 
        clust_index = dict_table_get_first_index(node->table);
831
 
 
832
 
        ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
833
 
                                               &info_bits);
834
 
 
835
 
        ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
836
 
                                       node->heap);
837
 
 
838
 
        trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
839
 
                                       roll_ptr, info_bits, trx,
840
 
                                       node->heap, &(node->update));
841
 
        node->new_roll_ptr = roll_ptr;
842
 
        node->new_trx_id = trx_id;
843
 
        node->cmpl_info = cmpl_info;
844
 
}
845
 
 
846
 
/***********************************************************//**
847
 
Undoes a modify operation on a row of a table.
848
 
@return DB_SUCCESS or error code */
849
 
UNIV_INTERN
850
 
ulint
851
 
row_undo_mod(
852
 
/*=========*/
853
 
        undo_node_t*    node,   /*!< in: row undo node */
854
 
        que_thr_t*      thr)    /*!< in: query thread */
855
 
{
856
 
        ulint   err;
857
 
 
858
 
        ut_ad(node && thr);
859
 
        ut_ad(node->state == UNDO_NODE_MODIFY);
860
 
 
861
 
        row_undo_mod_parse_undo_rec(node, thr);
862
 
 
863
 
        if (!node->table || !row_undo_search_clust_to_pcur(node)) {
864
 
                /* It is already undone, or will be undone by another query
865
 
                thread, or table was dropped */
866
 
 
867
 
                trx_undo_rec_release(node->trx, node->undo_no);
868
 
                node->state = UNDO_NODE_FETCH_NEXT;
869
 
 
870
 
                return(DB_SUCCESS);
871
 
        }
872
 
 
873
 
        node->index = dict_table_get_next_index(
874
 
                dict_table_get_first_index(node->table));
875
 
 
876
 
        if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
877
 
 
878
 
                err = row_undo_mod_upd_exist_sec(node, thr);
879
 
 
880
 
        } else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
881
 
 
882
 
                err = row_undo_mod_del_mark_sec(node, thr);
883
 
        } else {
884
 
                ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
885
 
                err = row_undo_mod_upd_del_sec(node, thr);
886
 
        }
887
 
 
888
 
        if (err != DB_SUCCESS) {
889
 
 
890
 
                return(err);
891
 
        }
892
 
 
893
 
        err = row_undo_mod_clust(node, thr);
894
 
 
895
 
        return(err);
896
 
}