~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/row/row0purge.c

  • Committer: Brian Aker
  • Date: 2009-02-12 22:45:08 UTC
  • Revision ID: brian@tangent.org-20090212224508-mrd9jwgn1zjdpqdk
Minor refactoring (we will need to disconnect the code from the include
file).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Purge obsolete records
 
3
 
 
4
(c) 1997 Innobase Oy
 
5
 
 
6
Created 3/14/1997 Heikki Tuuri
 
7
*******************************************************/
 
8
 
 
9
#include "row0purge.h"
 
10
 
 
11
#ifdef UNIV_NONINL
 
12
#include "row0purge.ic"
 
13
#endif
 
14
 
 
15
#include "fsp0fsp.h"
 
16
#include "mach0data.h"
 
17
#include "trx0rseg.h"
 
18
#include "trx0trx.h"
 
19
#include "trx0roll.h"
 
20
#include "trx0undo.h"
 
21
#include "trx0purge.h"
 
22
#include "trx0rec.h"
 
23
#include "que0que.h"
 
24
#include "row0row.h"
 
25
#include "row0upd.h"
 
26
#include "row0vers.h"
 
27
#include "row0mysql.h"
 
28
#include "log0log.h"
 
29
 
 
30
/************************************************************************
 
31
Creates a purge node to a query graph. */
 
32
UNIV_INTERN
 
33
purge_node_t*
 
34
row_purge_node_create(
 
35
/*==================*/
 
36
                                /* out, own: purge node */
 
37
        que_thr_t*      parent, /* in: parent node, i.e., a thr node */
 
38
        mem_heap_t*     heap)   /* in: memory heap where created */
 
39
{
 
40
        purge_node_t*   node;
 
41
 
 
42
        ut_ad(parent && heap);
 
43
 
 
44
        node = mem_heap_alloc(heap, sizeof(purge_node_t));
 
45
 
 
46
        node->common.type = QUE_NODE_PURGE;
 
47
        node->common.parent = parent;
 
48
 
 
49
        node->heap = mem_heap_create(256);
 
50
 
 
51
        return(node);
 
52
}
 
53
 
 
54
/***************************************************************
 
55
Repositions the pcur in the purge node on the clustered index record,
 
56
if found. */
 
57
static
 
58
ibool
 
59
row_purge_reposition_pcur(
 
60
/*======================*/
 
61
                                /* out: TRUE if the record was found */
 
62
        ulint           mode,   /* in: latching mode */
 
63
        purge_node_t*   node,   /* in: row purge node */
 
64
        mtr_t*          mtr)    /* in: mtr */
 
65
{
 
66
        ibool   found;
 
67
 
 
68
        if (node->found_clust) {
 
69
                found = btr_pcur_restore_position(mode, &(node->pcur), mtr);
 
70
 
 
71
                return(found);
 
72
        }
 
73
 
 
74
        found = row_search_on_row_ref(&(node->pcur), mode, node->table,
 
75
                                      node->ref, mtr);
 
76
        node->found_clust = found;
 
77
 
 
78
        if (found) {
 
79
                btr_pcur_store_position(&(node->pcur), mtr);
 
80
        }
 
81
 
 
82
        return(found);
 
83
}
 
84
 
 
85
/***************************************************************
 
86
Removes a delete marked clustered index record if possible. */
 
87
static
 
88
ibool
 
89
row_purge_remove_clust_if_poss_low(
 
90
/*===============================*/
 
91
                                /* out: TRUE if success, or if not found, or
 
92
                                if modified after the delete marking */
 
93
        purge_node_t*   node,   /* in: row purge node */
 
94
        ulint           mode)   /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
 
95
{
 
96
        dict_index_t*   index;
 
97
        btr_pcur_t*     pcur;
 
98
        btr_cur_t*      btr_cur;
 
99
        ibool           success;
 
100
        ulint           err;
 
101
        mtr_t           mtr;
 
102
        rec_t*          rec;
 
103
        mem_heap_t*     heap            = NULL;
 
104
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
105
        rec_offs_init(offsets_);
 
106
 
 
107
        index = dict_table_get_first_index(node->table);
 
108
 
 
109
        pcur = &(node->pcur);
 
110
        btr_cur = btr_pcur_get_btr_cur(pcur);
 
111
 
 
112
        mtr_start(&mtr);
 
113
 
 
114
        success = row_purge_reposition_pcur(mode, node, &mtr);
 
115
 
 
116
        if (!success) {
 
117
                /* The record is already removed */
 
118
 
 
119
                btr_pcur_commit_specify_mtr(pcur, &mtr);
 
120
 
 
121
                return(TRUE);
 
122
        }
 
123
 
 
124
        rec = btr_pcur_get_rec(pcur);
 
125
 
 
126
        if (0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(
 
127
                                       rec, index, rec_get_offsets(
 
128
                                               rec, index, offsets_,
 
129
                                               ULINT_UNDEFINED, &heap)))) {
 
130
                if (UNIV_LIKELY_NULL(heap)) {
 
131
                        mem_heap_free(heap);
 
132
                }
 
133
                /* Someone else has modified the record later: do not remove */
 
134
                btr_pcur_commit_specify_mtr(pcur, &mtr);
 
135
 
 
136
                return(TRUE);
 
137
        }
 
138
 
 
139
        if (UNIV_LIKELY_NULL(heap)) {
 
140
                mem_heap_free(heap);
 
141
        }
 
142
 
 
143
        if (mode == BTR_MODIFY_LEAF) {
 
144
                success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
145
        } else {
 
146
                ut_ad(mode == BTR_MODIFY_TREE);
 
147
                btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
 
148
                                           RB_NONE, &mtr);
 
149
 
 
150
                if (err == DB_SUCCESS) {
 
151
                        success = TRUE;
 
152
                } else if (err == DB_OUT_OF_FILE_SPACE) {
 
153
                        success = FALSE;
 
154
                } else {
 
155
                        ut_error;
 
156
                }
 
157
        }
 
158
 
 
159
        btr_pcur_commit_specify_mtr(pcur, &mtr);
 
160
 
 
161
        return(success);
 
162
}
 
163
 
 
164
/***************************************************************
 
165
Removes a clustered index record if it has not been modified after the delete
 
166
marking. */
 
167
static
 
168
void
 
169
row_purge_remove_clust_if_poss(
 
170
/*===========================*/
 
171
        purge_node_t*   node)   /* in: row purge node */
 
172
{
 
173
        ibool   success;
 
174
        ulint   n_tries = 0;
 
175
 
 
176
        /*      fputs("Purge: Removing clustered record\n", stderr); */
 
177
 
 
178
        success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF);
 
179
        if (success) {
 
180
 
 
181
                return;
 
182
        }
 
183
retry:
 
184
        success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_TREE);
 
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
        if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
 
190
                n_tries++;
 
191
 
 
192
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
 
193
 
 
194
                goto retry;
 
195
        }
 
196
 
 
197
        ut_a(success);
 
198
}
 
199
 
 
200
/***************************************************************
 
201
Removes a secondary index entry if possible. */
 
202
static
 
203
ibool
 
204
row_purge_remove_sec_if_poss_low(
 
205
/*=============================*/
 
206
                                /* out: TRUE if success or if not found */
 
207
        purge_node_t*   node,   /* in: row purge node */
 
208
        dict_index_t*   index,  /* in: index */
 
209
        const dtuple_t* entry,  /* in: index entry */
 
210
        ulint           mode)   /* in: latch mode BTR_MODIFY_LEAF or
 
211
                                BTR_MODIFY_TREE */
 
212
{
 
213
        btr_pcur_t      pcur;
 
214
        btr_cur_t*      btr_cur;
 
215
        ibool           success;
 
216
        ibool           old_has = 0; /* remove warning */
 
217
        ibool           found;
 
218
        ulint           err;
 
219
        mtr_t           mtr;
 
220
        mtr_t*          mtr_vers;
 
221
 
 
222
        log_free_check();
 
223
        mtr_start(&mtr);
 
224
 
 
225
        found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
 
226
 
 
227
        if (!found) {
 
228
                /* Not found */
 
229
 
 
230
                /* fputs("PURGE:........sec entry not found\n", stderr); */
 
231
                /* dtuple_print(stderr, entry); */
 
232
 
 
233
                btr_pcur_close(&pcur);
 
234
                mtr_commit(&mtr);
 
235
 
 
236
                return(TRUE);
 
237
        }
 
238
 
 
239
        btr_cur = btr_pcur_get_btr_cur(&pcur);
 
240
 
 
241
        /* We should remove the index record if no later version of the row,
 
242
        which cannot be purged yet, requires its existence. If some requires,
 
243
        we should do nothing. */
 
244
 
 
245
        mtr_vers = mem_alloc(sizeof(mtr_t));
 
246
 
 
247
        mtr_start(mtr_vers);
 
248
 
 
249
        success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, mtr_vers);
 
250
 
 
251
        if (success) {
 
252
                old_has = row_vers_old_has_index_entry(
 
253
                        TRUE, btr_pcur_get_rec(&(node->pcur)),
 
254
                        mtr_vers, index, entry);
 
255
        }
 
256
 
 
257
        btr_pcur_commit_specify_mtr(&(node->pcur), mtr_vers);
 
258
 
 
259
        mem_free(mtr_vers);
 
260
 
 
261
        if (!success || !old_has) {
 
262
                /* Remove the index record */
 
263
 
 
264
                if (mode == BTR_MODIFY_LEAF) {
 
265
                        success = btr_cur_optimistic_delete(btr_cur, &mtr);
 
266
                } else {
 
267
                        ut_ad(mode == BTR_MODIFY_TREE);
 
268
                        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
 
269
                                                   RB_NONE, &mtr);
 
270
                        success = err == DB_SUCCESS;
 
271
                        ut_a(success || err == DB_OUT_OF_FILE_SPACE);
 
272
                }
 
273
        }
 
274
 
 
275
        btr_pcur_close(&pcur);
 
276
        mtr_commit(&mtr);
 
277
 
 
278
        return(success);
 
279
}
 
280
 
 
281
/***************************************************************
 
282
Removes a secondary index entry if possible. */
 
283
UNIV_INLINE
 
284
void
 
285
row_purge_remove_sec_if_poss(
 
286
/*=========================*/
 
287
        purge_node_t*   node,   /* in: row purge node */
 
288
        dict_index_t*   index,  /* in: index */
 
289
        dtuple_t*       entry)  /* in: index entry */
 
290
{
 
291
        ibool   success;
 
292
        ulint   n_tries         = 0;
 
293
 
 
294
        /*      fputs("Purge: Removing secondary record\n", stderr); */
 
295
 
 
296
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
297
                                                   BTR_MODIFY_LEAF);
 
298
        if (success) {
 
299
 
 
300
                return;
 
301
        }
 
302
retry:
 
303
        success = row_purge_remove_sec_if_poss_low(node, index, entry,
 
304
                                                   BTR_MODIFY_TREE);
 
305
        /* The delete operation may fail if we have little
 
306
        file space left: TODO: easiest to crash the database
 
307
        and restart with more file space */
 
308
 
 
309
        if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
 
310
 
 
311
                n_tries++;
 
312
 
 
313
                os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
 
314
 
 
315
                goto retry;
 
316
        }
 
317
 
 
318
        ut_a(success);
 
319
}
 
320
 
 
321
/***************************************************************
 
322
Purges a delete marking of a record. */
 
323
static
 
324
void
 
325
row_purge_del_mark(
 
326
/*===============*/
 
327
        purge_node_t*   node)   /* in: row purge node */
 
328
{
 
329
        mem_heap_t*     heap;
 
330
        dtuple_t*       entry;
 
331
        dict_index_t*   index;
 
332
 
 
333
        ut_ad(node);
 
334
 
 
335
        heap = mem_heap_create(1024);
 
336
 
 
337
        while (node->index != NULL) {
 
338
                index = node->index;
 
339
 
 
340
                /* Build the index entry */
 
341
                entry = row_build_index_entry(node->row, NULL, index, heap);
 
342
                ut_a(entry);
 
343
                row_purge_remove_sec_if_poss(node, index, entry);
 
344
 
 
345
                node->index = dict_table_get_next_index(node->index);
 
346
        }
 
347
 
 
348
        mem_heap_free(heap);
 
349
 
 
350
        row_purge_remove_clust_if_poss(node);
 
351
}
 
352
 
 
353
/***************************************************************
 
354
Purges an update of an existing record. Also purges an update of a delete
 
355
marked record if that record contained an externally stored field. */
 
356
static
 
357
void
 
358
row_purge_upd_exist_or_extern(
 
359
/*==========================*/
 
360
        purge_node_t*   node)   /* in: row purge node */
 
361
{
 
362
        mem_heap_t*     heap;
 
363
        dtuple_t*       entry;
 
364
        dict_index_t*   index;
 
365
        ibool           is_insert;
 
366
        ulint           rseg_id;
 
367
        ulint           page_no;
 
368
        ulint           offset;
 
369
        ulint           i;
 
370
        mtr_t           mtr;
 
371
 
 
372
        ut_ad(node);
 
373
 
 
374
        if (node->rec_type == TRX_UNDO_UPD_DEL_REC) {
 
375
 
 
376
                goto skip_secondaries;
 
377
        }
 
378
 
 
379
        heap = mem_heap_create(1024);
 
380
 
 
381
        while (node->index != NULL) {
 
382
                index = node->index;
 
383
 
 
384
                if (row_upd_changes_ord_field_binary(NULL, node->index,
 
385
                                                     node->update)) {
 
386
                        /* Build the older version of the index entry */
 
387
                        entry = row_build_index_entry(node->row, NULL,
 
388
                                                      index, heap);
 
389
                        ut_a(entry);
 
390
                        row_purge_remove_sec_if_poss(node, index, entry);
 
391
                }
 
392
 
 
393
                node->index = dict_table_get_next_index(node->index);
 
394
        }
 
395
 
 
396
        mem_heap_free(heap);
 
397
 
 
398
skip_secondaries:
 
399
        /* Free possible externally stored fields */
 
400
        for (i = 0; i < upd_get_n_fields(node->update); i++) {
 
401
 
 
402
                const upd_field_t*      ufield
 
403
                        = upd_get_nth_field(node->update, i);
 
404
 
 
405
                if (dfield_is_ext(&ufield->new_val)) {
 
406
                        buf_block_t*    block;
 
407
                        ulint           internal_offset;
 
408
                        byte*           data_field;
 
409
 
 
410
                        /* We use the fact that new_val points to
 
411
                        node->undo_rec and get thus the offset of
 
412
                        dfield data inside the undo record. Then we
 
413
                        can calculate from node->roll_ptr the file
 
414
                        address of the new_val data */
 
415
 
 
416
                        internal_offset
 
417
                                = ((const byte*)
 
418
                                   dfield_get_data(&ufield->new_val))
 
419
                                - node->undo_rec;
 
420
 
 
421
                        ut_a(internal_offset < UNIV_PAGE_SIZE);
 
422
 
 
423
                        trx_undo_decode_roll_ptr(node->roll_ptr,
 
424
                                                 &is_insert, &rseg_id,
 
425
                                                 &page_no, &offset);
 
426
                        mtr_start(&mtr);
 
427
 
 
428
                        /* We have to acquire an X-latch to the clustered
 
429
                        index tree */
 
430
 
 
431
                        index = dict_table_get_first_index(node->table);
 
432
 
 
433
                        mtr_x_lock(dict_index_get_lock(index), &mtr);
 
434
 
 
435
                        /* NOTE: we must also acquire an X-latch to the
 
436
                        root page of the tree. We will need it when we
 
437
                        free pages from the tree. If the tree is of height 1,
 
438
                        the tree X-latch does NOT protect the root page,
 
439
                        because it is also a leaf page. Since we will have a
 
440
                        latch on an undo log page, we would break the
 
441
                        latching order if we would only later latch the
 
442
                        root page of such a tree! */
 
443
 
 
444
                        btr_root_get(index, &mtr);
 
445
 
 
446
                        /* We assume in purge of externally stored fields
 
447
                        that the space id of the undo log record is 0! */
 
448
 
 
449
                        block = buf_page_get(0, 0, page_no, RW_X_LATCH, &mtr);
 
450
                        buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
 
451
 
 
452
                        data_field = buf_block_get_frame(block)
 
453
                                + offset + internal_offset;
 
454
 
 
455
                        ut_a(dfield_get_len(&ufield->new_val)
 
456
                             >= BTR_EXTERN_FIELD_REF_SIZE);
 
457
                        btr_free_externally_stored_field(
 
458
                                index,
 
459
                                data_field + dfield_get_len(&ufield->new_val)
 
460
                                - BTR_EXTERN_FIELD_REF_SIZE,
 
461
                                NULL, NULL, NULL, 0, RB_NONE, &mtr);
 
462
                        mtr_commit(&mtr);
 
463
                }
 
464
        }
 
465
}
 
466
 
 
467
/***************************************************************
 
468
Parses the row reference and other info in a modify undo log record. */
 
469
static
 
470
ibool
 
471
row_purge_parse_undo_rec(
 
472
/*=====================*/
 
473
                                /* out: TRUE if purge operation required:
 
474
                                NOTE that then the CALLER must unfreeze
 
475
                                data dictionary! */
 
476
        purge_node_t*   node,   /* in: row undo node */
 
477
        ibool*          updated_extern,
 
478
                                /* out: TRUE if an externally stored field
 
479
                                was updated */
 
480
        que_thr_t*      thr)    /* in: query thread */
 
481
{
 
482
        dict_index_t*   clust_index;
 
483
        byte*           ptr;
 
484
        trx_t*          trx;
 
485
        dulint          undo_no;
 
486
        dulint          table_id;
 
487
        dulint          trx_id;
 
488
        dulint          roll_ptr;
 
489
        ulint           info_bits;
 
490
        ulint           type;
 
491
        ulint           cmpl_info;
 
492
 
 
493
        ut_ad(node && thr);
 
494
 
 
495
        trx = thr_get_trx(thr);
 
496
 
 
497
        ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
 
498
                                    updated_extern, &undo_no, &table_id);
 
499
        node->rec_type = type;
 
500
 
 
501
        if (type == TRX_UNDO_UPD_DEL_REC && !(*updated_extern)) {
 
502
 
 
503
                return(FALSE);
 
504
        }
 
505
 
 
506
        ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
 
507
                                               &info_bits);
 
508
        node->table = NULL;
 
509
 
 
510
        if (type == TRX_UNDO_UPD_EXIST_REC
 
511
            && cmpl_info & UPD_NODE_NO_ORD_CHANGE && !(*updated_extern)) {
 
512
 
 
513
                /* Purge requires no changes to indexes: we may return */
 
514
 
 
515
                return(FALSE);
 
516
        }
 
517
 
 
518
        /* Prevent DROP TABLE etc. from running when we are doing the purge
 
519
        for this row */
 
520
 
 
521
        row_mysql_freeze_data_dictionary(trx);
 
522
 
 
523
        mutex_enter(&(dict_sys->mutex));
 
524
 
 
525
        node->table = dict_table_get_on_id_low(table_id);
 
526
 
 
527
        mutex_exit(&(dict_sys->mutex));
 
528
 
 
529
        if (node->table == NULL) {
 
530
                /* The table has been dropped: no need to do purge */
 
531
err_exit:
 
532
                row_mysql_unfreeze_data_dictionary(trx);
 
533
                return(FALSE);
 
534
        }
 
535
 
 
536
        if (node->table->ibd_file_missing) {
 
537
                /* We skip purge of missing .ibd files */
 
538
 
 
539
                node->table = NULL;
 
540
 
 
541
                goto err_exit;
 
542
        }
 
543
 
 
544
        clust_index = dict_table_get_first_index(node->table);
 
545
 
 
546
        if (clust_index == NULL) {
 
547
                /* The table was corrupt in the data dictionary */
 
548
 
 
549
                goto err_exit;
 
550
        }
 
551
 
 
552
        ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
 
553
                                       node->heap);
 
554
 
 
555
        ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
 
556
                                             roll_ptr, info_bits, trx,
 
557
                                             node->heap, &(node->update));
 
558
 
 
559
        /* Read to the partial row the fields that occur in indexes */
 
560
 
 
561
        if (!(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
 
562
                ptr = trx_undo_rec_get_partial_row(
 
563
                        ptr, clust_index, &node->row,
 
564
                        type == TRX_UNDO_UPD_DEL_REC,
 
565
                        node->heap);
 
566
        }
 
567
 
 
568
        return(TRUE);
 
569
}
 
570
 
 
571
/***************************************************************
 
572
Fetches an undo log record and does the purge for the recorded operation.
 
573
If none left, or the current purge completed, returns the control to the
 
574
parent node, which is always a query thread node. */
 
575
static
 
576
ulint
 
577
row_purge(
 
578
/*======*/
 
579
                                /* out: DB_SUCCESS if operation successfully
 
580
                                completed, else error code */
 
581
        purge_node_t*   node,   /* in: row purge node */
 
582
        que_thr_t*      thr)    /* in: query thread */
 
583
{
 
584
        dulint  roll_ptr;
 
585
        ibool   purge_needed;
 
586
        ibool   updated_extern;
 
587
        trx_t*  trx;
 
588
 
 
589
        ut_ad(node && thr);
 
590
 
 
591
        trx = thr_get_trx(thr);
 
592
 
 
593
        node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr,
 
594
                                                  &(node->reservation),
 
595
                                                  node->heap);
 
596
        if (!node->undo_rec) {
 
597
                /* Purge completed for this query thread */
 
598
 
 
599
                thr->run_node = que_node_get_parent(node);
 
600
 
 
601
                return(DB_SUCCESS);
 
602
        }
 
603
 
 
604
        node->roll_ptr = roll_ptr;
 
605
 
 
606
        if (node->undo_rec == &trx_purge_dummy_rec) {
 
607
                purge_needed = FALSE;
 
608
        } else {
 
609
                purge_needed = row_purge_parse_undo_rec(node, &updated_extern,
 
610
                                                        thr);
 
611
                /* If purge_needed == TRUE, we must also remember to unfreeze
 
612
                data dictionary! */
 
613
        }
 
614
 
 
615
        if (purge_needed) {
 
616
                node->found_clust = FALSE;
 
617
 
 
618
                node->index = dict_table_get_next_index(
 
619
                        dict_table_get_first_index(node->table));
 
620
 
 
621
                if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
 
622
                        row_purge_del_mark(node);
 
623
 
 
624
                } else if (updated_extern
 
625
                           || node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
 
626
 
 
627
                        row_purge_upd_exist_or_extern(node);
 
628
                }
 
629
 
 
630
                if (node->found_clust) {
 
631
                        btr_pcur_close(&(node->pcur));
 
632
                }
 
633
 
 
634
                row_mysql_unfreeze_data_dictionary(trx);
 
635
        }
 
636
 
 
637
        /* Do some cleanup */
 
638
        trx_purge_rec_release(node->reservation);
 
639
        mem_heap_empty(node->heap);
 
640
 
 
641
        thr->run_node = node;
 
642
 
 
643
        return(DB_SUCCESS);
 
644
}
 
645
 
 
646
/***************************************************************
 
647
Does the purge operation for a single undo log record. This is a high-level
 
648
function used in an SQL execution graph. */
 
649
UNIV_INTERN
 
650
que_thr_t*
 
651
row_purge_step(
 
652
/*===========*/
 
653
                                /* out: query thread to run next or NULL */
 
654
        que_thr_t*      thr)    /* in: query thread */
 
655
{
 
656
        purge_node_t*   node;
 
657
        ulint           err;
 
658
 
 
659
        ut_ad(thr);
 
660
 
 
661
        node = thr->run_node;
 
662
 
 
663
        ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
 
664
 
 
665
        err = row_purge(node, thr);
 
666
 
 
667
        ut_ad(err == DB_SUCCESS);
 
668
 
 
669
        return(thr);
 
670
}