~percona-dev/percona-innodb-plugin/percona-innodb-1.0

« back to all changes in this revision

Viewing changes to row/row0umod.c

  • Committer: Vadim Tkachenko
  • Date: 2008-12-01 02:05:57 UTC
  • Revision ID: vadim@percona.com-20081201020557-p7k2m94mjtdg1a83
New rw-locks

Show diffs side-by-side

added added

removed removed

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