~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/trx/trx0roll.c

Tags: innodb-plugin-1.0.1
Imported 1.0.1 with clean - with no changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Transaction rollback
 
3
 
 
4
(c) 1996 Innobase Oy
 
5
 
 
6
Created 3/26/1996 Heikki Tuuri
 
7
*******************************************************/
 
8
 
 
9
#include "trx0roll.h"
 
10
 
 
11
#ifdef UNIV_NONINL
 
12
#include "trx0roll.ic"
 
13
#endif
 
14
 
 
15
#include "fsp0fsp.h"
 
16
#include "mach0data.h"
 
17
#include "trx0rseg.h"
 
18
#include "trx0trx.h"
 
19
#include "trx0undo.h"
 
20
#include "trx0rec.h"
 
21
#include "que0que.h"
 
22
#include "usr0sess.h"
 
23
#include "srv0que.h"
 
24
#include "srv0start.h"
 
25
#include "row0undo.h"
 
26
#include "row0mysql.h"
 
27
#include "lock0lock.h"
 
28
#include "pars0pars.h"
 
29
 
 
30
/* This many pages must be undone before a truncate is tried within rollback */
 
31
#define TRX_ROLL_TRUNC_THRESHOLD        1
 
32
 
 
33
/* In crash recovery, the current trx to be rolled back */
 
34
static trx_t*           trx_roll_crash_recv_trx = NULL;
 
35
 
 
36
/* In crash recovery we set this to the undo n:o of the current trx to be
 
37
rolled back. Then we can print how many % the rollback has progressed. */
 
38
static ib_int64_t       trx_roll_max_undo_no;
 
39
 
 
40
/* Auxiliary variable which tells the previous progress % we printed */
 
41
static ulint            trx_roll_progress_printed_pct;
 
42
 
 
43
/***********************************************************************
 
44
Rollback a transaction used in MySQL. */
 
45
UNIV_INTERN
 
46
int
 
47
trx_general_rollback_for_mysql(
 
48
/*===========================*/
 
49
                                /* out: error code or DB_SUCCESS */
 
50
        trx_t*          trx,    /* in: transaction handle */
 
51
        ibool           partial,/* in: TRUE if partial rollback requested */
 
52
        trx_savept_t*   savept) /* in: pointer to savepoint undo number, if
 
53
                                partial rollback requested */
 
54
{
 
55
#ifndef UNIV_HOTBACKUP
 
56
        mem_heap_t*     heap;
 
57
        que_thr_t*      thr;
 
58
        roll_node_t*    roll_node;
 
59
 
 
60
        /* Tell Innobase server that there might be work for
 
61
        utility threads: */
 
62
 
 
63
        srv_active_wake_master_thread();
 
64
 
 
65
        trx_start_if_not_started(trx);
 
66
 
 
67
        heap = mem_heap_create(512);
 
68
 
 
69
        roll_node = roll_node_create(heap);
 
70
 
 
71
        roll_node->partial = partial;
 
72
 
 
73
        if (partial) {
 
74
                roll_node->savept = *savept;
 
75
        }
 
76
 
 
77
        trx->error_state = DB_SUCCESS;
 
78
 
 
79
        thr = pars_complete_graph_for_exec(roll_node, trx, heap);
 
80
 
 
81
        ut_a(thr == que_fork_start_command(que_node_get_parent(thr)));
 
82
        que_run_threads(thr);
 
83
 
 
84
        mutex_enter(&kernel_mutex);
 
85
 
 
86
        while (trx->que_state != TRX_QUE_RUNNING) {
 
87
 
 
88
                mutex_exit(&kernel_mutex);
 
89
 
 
90
                os_thread_sleep(100000);
 
91
 
 
92
                mutex_enter(&kernel_mutex);
 
93
        }
 
94
 
 
95
        mutex_exit(&kernel_mutex);
 
96
 
 
97
        mem_heap_free(heap);
 
98
 
 
99
        ut_a(trx->error_state == DB_SUCCESS);
 
100
 
 
101
        /* Tell Innobase server that there might be work for
 
102
        utility threads: */
 
103
 
 
104
        srv_active_wake_master_thread();
 
105
 
 
106
        return((int) trx->error_state);
 
107
#else /* UNIV_HOTBACKUP */
 
108
        /* This function depends on MySQL code that is not included in
 
109
        InnoDB Hot Backup builds.  Besides, this function should never
 
110
        be called in InnoDB Hot Backup. */
 
111
        ut_error;
 
112
        return(DB_FAIL);
 
113
#endif /* UNIV_HOTBACKUP */
 
114
}
 
115
 
 
116
/***********************************************************************
 
117
Rollback a transaction used in MySQL. */
 
118
UNIV_INTERN
 
119
int
 
120
trx_rollback_for_mysql(
 
121
/*===================*/
 
122
                        /* out: error code or DB_SUCCESS */
 
123
        trx_t*  trx)    /* in: transaction handle */
 
124
{
 
125
        int     err;
 
126
 
 
127
        if (trx->conc_state == TRX_NOT_STARTED) {
 
128
 
 
129
                return(DB_SUCCESS);
 
130
        }
 
131
 
 
132
        trx->op_info = "rollback";
 
133
 
 
134
        /* If we are doing the XA recovery of prepared transactions, then
 
135
        the transaction object does not have an InnoDB session object, and we
 
136
        set a dummy session that we use for all MySQL transactions. */
 
137
 
 
138
        err = trx_general_rollback_for_mysql(trx, FALSE, NULL);
 
139
 
 
140
        trx->op_info = "";
 
141
 
 
142
        return(err);
 
143
}
 
144
 
 
145
/***********************************************************************
 
146
Rollback the latest SQL statement for MySQL. */
 
147
UNIV_INTERN
 
148
int
 
149
trx_rollback_last_sql_stat_for_mysql(
 
150
/*=================================*/
 
151
                        /* out: error code or DB_SUCCESS */
 
152
        trx_t*  trx)    /* in: transaction handle */
 
153
{
 
154
        int     err;
 
155
 
 
156
        if (trx->conc_state == TRX_NOT_STARTED) {
 
157
 
 
158
                return(DB_SUCCESS);
 
159
        }
 
160
 
 
161
        trx->op_info = "rollback of SQL statement";
 
162
 
 
163
        err = trx_general_rollback_for_mysql(trx, TRUE,
 
164
                                             &(trx->last_sql_stat_start));
 
165
        /* The following call should not be needed, but we play safe: */
 
166
        trx_mark_sql_stat_end(trx);
 
167
 
 
168
        trx->op_info = "";
 
169
 
 
170
        return(err);
 
171
}
 
172
 
 
173
/***********************************************************************
 
174
Frees savepoint structs. */
 
175
UNIV_INTERN
 
176
void
 
177
trx_roll_savepoints_free(
 
178
/*=====================*/
 
179
        trx_t*                  trx,    /* in: transaction handle */
 
180
        trx_named_savept_t*     savep)  /* in: free all savepoints > this one;
 
181
                                        if this is NULL, free all savepoints
 
182
                                        of trx */
 
183
{
 
184
        trx_named_savept_t*     next_savep;
 
185
 
 
186
        if (savep == NULL) {
 
187
                savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
 
188
        } else {
 
189
                savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
 
190
        }
 
191
 
 
192
        while (savep != NULL) {
 
193
                next_savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
 
194
 
 
195
                UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep);
 
196
                mem_free(savep->name);
 
197
                mem_free(savep);
 
198
 
 
199
                savep = next_savep;
 
200
        }
 
201
}
 
202
 
 
203
/***********************************************************************
 
204
Rolls back a transaction back to a named savepoint. Modifications after the
 
205
savepoint are undone but InnoDB does NOT release the corresponding locks
 
206
which are stored in memory. If a lock is 'implicit', that is, a new inserted
 
207
row holds a lock where the lock information is carried by the trx id stored in
 
208
the row, these locks are naturally released in the rollback. Savepoints which
 
209
were set after this savepoint are deleted. */
 
210
UNIV_INTERN
 
211
ulint
 
212
trx_rollback_to_savepoint_for_mysql(
 
213
/*================================*/
 
214
                                                /* out: if no savepoint
 
215
                                                of the name found then
 
216
                                                DB_NO_SAVEPOINT,
 
217
                                                otherwise DB_SUCCESS */
 
218
        trx_t*          trx,                    /* in: transaction handle */
 
219
        const char*     savepoint_name,         /* in: savepoint name */
 
220
        ib_int64_t*     mysql_binlog_cache_pos) /* out: the MySQL binlog cache
 
221
                                                position corresponding to this
 
222
                                                savepoint; MySQL needs this
 
223
                                                information to remove the
 
224
                                                binlog entries of the queries
 
225
                                                executed after the savepoint */
 
226
{
 
227
        trx_named_savept_t*     savep;
 
228
        ulint                   err;
 
229
 
 
230
        savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
 
231
 
 
232
        while (savep != NULL) {
 
233
                if (0 == ut_strcmp(savep->name, savepoint_name)) {
 
234
                        /* Found */
 
235
                        break;
 
236
                }
 
237
                savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
 
238
        }
 
239
 
 
240
        if (savep == NULL) {
 
241
 
 
242
                return(DB_NO_SAVEPOINT);
 
243
        }
 
244
 
 
245
        if (trx->conc_state == TRX_NOT_STARTED) {
 
246
                ut_print_timestamp(stderr);
 
247
                fputs("  InnoDB: Error: transaction has a savepoint ", stderr);
 
248
                ut_print_name(stderr, trx, FALSE, savep->name);
 
249
                fputs(" though it is not started\n", stderr);
 
250
                return(DB_ERROR);
 
251
        }
 
252
 
 
253
        /* We can now free all savepoints strictly later than this one */
 
254
 
 
255
        trx_roll_savepoints_free(trx, savep);
 
256
 
 
257
        *mysql_binlog_cache_pos = savep->mysql_binlog_cache_pos;
 
258
 
 
259
        trx->op_info = "rollback to a savepoint";
 
260
 
 
261
        err = trx_general_rollback_for_mysql(trx, TRUE, &(savep->savept));
 
262
 
 
263
        /* Store the current undo_no of the transaction so that we know where
 
264
        to roll back if we have to roll back the next SQL statement: */
 
265
 
 
266
        trx_mark_sql_stat_end(trx);
 
267
 
 
268
        trx->op_info = "";
 
269
 
 
270
        return(err);
 
271
}
 
272
 
 
273
/***********************************************************************
 
274
Creates a named savepoint. If the transaction is not yet started, starts it.
 
275
If there is already a savepoint of the same name, this call erases that old
 
276
savepoint and replaces it with a new. Savepoints are deleted in a transaction
 
277
commit or rollback. */
 
278
UNIV_INTERN
 
279
ulint
 
280
trx_savepoint_for_mysql(
 
281
/*====================*/
 
282
                                                /* out: always DB_SUCCESS */
 
283
        trx_t*          trx,                    /* in: transaction handle */
 
284
        const char*     savepoint_name,         /* in: savepoint name */
 
285
        ib_int64_t      binlog_cache_pos)       /* in: MySQL binlog cache
 
286
                                                position corresponding to this
 
287
                                                connection at the time of the
 
288
                                                savepoint */
 
289
{
 
290
        trx_named_savept_t*     savep;
 
291
 
 
292
        ut_a(trx);
 
293
        ut_a(savepoint_name);
 
294
 
 
295
        trx_start_if_not_started(trx);
 
296
 
 
297
        savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
 
298
 
 
299
        while (savep != NULL) {
 
300
                if (0 == ut_strcmp(savep->name, savepoint_name)) {
 
301
                        /* Found */
 
302
                        break;
 
303
                }
 
304
                savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
 
305
        }
 
306
 
 
307
        if (savep) {
 
308
                /* There is a savepoint with the same name: free that */
 
309
 
 
310
                UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep);
 
311
 
 
312
                mem_free(savep->name);
 
313
                mem_free(savep);
 
314
        }
 
315
 
 
316
        /* Create a new savepoint and add it as the last in the list */
 
317
 
 
318
        savep = mem_alloc(sizeof(trx_named_savept_t));
 
319
 
 
320
        savep->name = mem_strdup(savepoint_name);
 
321
 
 
322
        savep->savept = trx_savept_take(trx);
 
323
 
 
324
        savep->mysql_binlog_cache_pos = binlog_cache_pos;
 
325
 
 
326
        UT_LIST_ADD_LAST(trx_savepoints, trx->trx_savepoints, savep);
 
327
 
 
328
        return(DB_SUCCESS);
 
329
}
 
330
 
 
331
/***********************************************************************
 
332
Releases a named savepoint. Savepoints which
 
333
were set after this savepoint are deleted. */
 
334
UNIV_INTERN
 
335
ulint
 
336
trx_release_savepoint_for_mysql(
 
337
/*============================*/
 
338
                                                /* out: if no savepoint
 
339
                                                of the name found then
 
340
                                                DB_NO_SAVEPOINT,
 
341
                                                otherwise DB_SUCCESS */
 
342
        trx_t*          trx,                    /* in: transaction handle */
 
343
        const char*     savepoint_name)         /* in: savepoint name */
 
344
{
 
345
        trx_named_savept_t*     savep;
 
346
 
 
347
        savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
 
348
 
 
349
        while (savep != NULL) {
 
350
                if (0 == ut_strcmp(savep->name, savepoint_name)) {
 
351
                        /* Found */
 
352
                        break;
 
353
                }
 
354
                savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
 
355
        }
 
356
 
 
357
        if (savep == NULL) {
 
358
 
 
359
                return(DB_NO_SAVEPOINT);
 
360
        }
 
361
 
 
362
        /* We can now free all savepoints strictly later than this one */
 
363
 
 
364
        trx_roll_savepoints_free(trx, savep);
 
365
 
 
366
        /* Now we can free this savepoint too */
 
367
 
 
368
        UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep);
 
369
 
 
370
        mem_free(savep->name);
 
371
        mem_free(savep);
 
372
 
 
373
        return(DB_SUCCESS);
 
374
}
 
375
 
 
376
/***********************************************************************
 
377
Returns a transaction savepoint taken at this point in time. */
 
378
UNIV_INTERN
 
379
trx_savept_t
 
380
trx_savept_take(
 
381
/*============*/
 
382
                        /* out: savepoint */
 
383
        trx_t*  trx)    /* in: transaction */
 
384
{
 
385
        trx_savept_t    savept;
 
386
 
 
387
        savept.least_undo_no = trx->undo_no;
 
388
 
 
389
        return(savept);
 
390
}
 
391
 
 
392
/***********************************************************************
 
393
Roll back an active transaction. */
 
394
static
 
395
void
 
396
trx_rollback_active(
 
397
/*================*/
 
398
        trx_t*  trx)    /* in/out: transaction */
 
399
{
 
400
        mem_heap_t*     heap;
 
401
        que_fork_t*     fork;
 
402
        que_thr_t*      thr;
 
403
        roll_node_t*    roll_node;
 
404
        dict_table_t*   table;
 
405
        ib_int64_t      rows_to_undo;
 
406
        const char*     unit            = "";
 
407
        ibool           dictionary_locked = FALSE;
 
408
 
 
409
        heap = mem_heap_create(512);
 
410
 
 
411
        fork = que_fork_create(NULL, NULL, QUE_FORK_RECOVERY, heap);
 
412
        fork->trx = trx;
 
413
 
 
414
        thr = que_thr_create(fork, heap);
 
415
 
 
416
        roll_node = roll_node_create(heap);
 
417
 
 
418
        thr->child = roll_node;
 
419
        roll_node->common.parent = thr;
 
420
 
 
421
        mutex_enter(&kernel_mutex);
 
422
 
 
423
        trx->graph = fork;
 
424
 
 
425
        ut_a(thr == que_fork_start_command(fork));
 
426
 
 
427
        trx_roll_crash_recv_trx = trx;
 
428
        trx_roll_max_undo_no = ut_conv_dulint_to_longlong(trx->undo_no);
 
429
        trx_roll_progress_printed_pct = 0;
 
430
        rows_to_undo = trx_roll_max_undo_no;
 
431
 
 
432
        if (rows_to_undo > 1000000000) {
 
433
                rows_to_undo = rows_to_undo / 1000000;
 
434
                unit = "M";
 
435
        }
 
436
 
 
437
        ut_print_timestamp(stderr);
 
438
        fprintf(stderr,
 
439
                "  InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s"
 
440
                " rows to undo\n",
 
441
                TRX_ID_PREP_PRINTF(trx->id),
 
442
                (ulong) rows_to_undo, unit);
 
443
        mutex_exit(&kernel_mutex);
 
444
 
 
445
        trx->mysql_thread_id = os_thread_get_curr_id();
 
446
 
 
447
        trx->mysql_process_no = os_proc_get_number();
 
448
 
 
449
        if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
 
450
                row_mysql_lock_data_dictionary(trx);
 
451
                dictionary_locked = TRUE;
 
452
        }
 
453
 
 
454
        que_run_threads(thr);
 
455
 
 
456
        mutex_enter(&kernel_mutex);
 
457
 
 
458
        while (trx->que_state != TRX_QUE_RUNNING) {
 
459
 
 
460
                mutex_exit(&kernel_mutex);
 
461
 
 
462
                fprintf(stderr,
 
463
                        "InnoDB: Waiting for rollback of trx id %lu to end\n",
 
464
                        (ulong) ut_dulint_get_low(trx->id));
 
465
                os_thread_sleep(100000);
 
466
 
 
467
                mutex_enter(&kernel_mutex);
 
468
        }
 
469
 
 
470
        mutex_exit(&kernel_mutex);
 
471
 
 
472
        if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE
 
473
            && !ut_dulint_is_zero(trx->table_id)) {
 
474
 
 
475
                /* If the transaction was for a dictionary operation, we
 
476
                drop the relevant table, if it still exists */
 
477
 
 
478
                fprintf(stderr,
 
479
                        "InnoDB: Dropping table with id %lu %lu"
 
480
                        " in recovery if it exists\n",
 
481
                        (ulong) ut_dulint_get_high(trx->table_id),
 
482
                        (ulong) ut_dulint_get_low(trx->table_id));
 
483
 
 
484
                table = dict_table_get_on_id_low(trx->table_id);
 
485
 
 
486
                if (table) {
 
487
                        ulint   err;
 
488
 
 
489
                        fputs("InnoDB: Table found: dropping table ", stderr);
 
490
                        ut_print_name(stderr, trx, TRUE, table->name);
 
491
                        fputs(" in recovery\n", stderr);
 
492
 
 
493
                        err = row_drop_table_for_mysql(table->name, trx, TRUE);
 
494
 
 
495
                        ut_a(err == (int) DB_SUCCESS);
 
496
                }
 
497
        }
 
498
 
 
499
        if (dictionary_locked) {
 
500
                row_mysql_unlock_data_dictionary(trx);
 
501
        }
 
502
 
 
503
        fprintf(stderr, "\nInnoDB: Rolling back of trx id " TRX_ID_FMT
 
504
                " completed\n",
 
505
                TRX_ID_PREP_PRINTF(trx->id));
 
506
        mem_heap_free(heap);
 
507
 
 
508
        trx_roll_crash_recv_trx = NULL;
 
509
}
 
510
 
 
511
/***********************************************************************
 
512
Rollback or clean up any incomplete transactions which were
 
513
encountered in crash recovery.  If the transaction already was
 
514
committed, then we clean up a possible insert undo log. If the
 
515
transaction was not yet committed, then we roll it back.
 
516
Note: this is done in a background thread. */
 
517
UNIV_INTERN
 
518
os_thread_ret_t
 
519
trx_rollback_or_clean_all_recovered(
 
520
/*================================*/
 
521
                        /* out: a dummy parameter */
 
522
        void*   arg __attribute__((unused)))
 
523
                        /* in: a dummy parameter required by
 
524
                        os_thread_create */
 
525
{
 
526
        trx_t*  trx;
 
527
 
 
528
        mutex_enter(&kernel_mutex);
 
529
 
 
530
        if (UT_LIST_GET_FIRST(trx_sys->trx_list)) {
 
531
 
 
532
                fprintf(stderr,
 
533
                        "InnoDB: Starting in background the rollback"
 
534
                        " of uncommitted transactions\n");
 
535
        } else {
 
536
                goto leave_function;
 
537
        }
 
538
 
 
539
        mutex_exit(&kernel_mutex);
 
540
 
 
541
loop:
 
542
        mutex_enter(&kernel_mutex);
 
543
 
 
544
        for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx;
 
545
             trx = UT_LIST_GET_NEXT(trx_list, trx)) {
 
546
                if (!trx->is_recovered) {
 
547
                        continue;
 
548
                }
 
549
 
 
550
                switch (trx->conc_state) {
 
551
                case TRX_NOT_STARTED:
 
552
                case TRX_PREPARED:
 
553
                        continue;
 
554
 
 
555
                case TRX_COMMITTED_IN_MEMORY:
 
556
                        mutex_exit(&kernel_mutex);
 
557
                        fprintf(stderr,
 
558
                                "InnoDB: Cleaning up trx with id "
 
559
                                TRX_ID_FMT "\n",
 
560
                                TRX_ID_PREP_PRINTF(trx->id));
 
561
                        trx_cleanup_at_db_startup(trx);
 
562
                        goto loop;
 
563
 
 
564
                case TRX_ACTIVE:
 
565
                        mutex_exit(&kernel_mutex);
 
566
                        trx_rollback_active(trx);
 
567
                        goto loop;
 
568
                }
 
569
        }
 
570
 
 
571
        ut_print_timestamp(stderr);
 
572
        fprintf(stderr,
 
573
                "  InnoDB: Rollback of non-prepared transactions completed\n");
 
574
 
 
575
leave_function:
 
576
        mutex_exit(&kernel_mutex);
 
577
 
 
578
        /* We count the number of threads in os_thread_exit(). A created
 
579
        thread should always use that to exit and not use return() to exit. */
 
580
 
 
581
        os_thread_exit(NULL);
 
582
 
 
583
        OS_THREAD_DUMMY_RETURN;
 
584
}
 
585
 
 
586
/***********************************************************************
 
587
Creates an undo number array. */
 
588
UNIV_INTERN
 
589
trx_undo_arr_t*
 
590
trx_undo_arr_create(void)
 
591
/*=====================*/
 
592
{
 
593
        trx_undo_arr_t* arr;
 
594
        mem_heap_t*     heap;
 
595
        ulint           i;
 
596
 
 
597
        heap = mem_heap_create(1024);
 
598
 
 
599
        arr = mem_heap_alloc(heap, sizeof(trx_undo_arr_t));
 
600
 
 
601
        arr->infos = mem_heap_alloc(heap, sizeof(trx_undo_inf_t)
 
602
                                    * UNIV_MAX_PARALLELISM);
 
603
        arr->n_cells = UNIV_MAX_PARALLELISM;
 
604
        arr->n_used = 0;
 
605
 
 
606
        arr->heap = heap;
 
607
 
 
608
        for (i = 0; i < UNIV_MAX_PARALLELISM; i++) {
 
609
 
 
610
                (trx_undo_arr_get_nth_info(arr, i))->in_use = FALSE;
 
611
        }
 
612
 
 
613
        return(arr);
 
614
}
 
615
 
 
616
/***********************************************************************
 
617
Frees an undo number array. */
 
618
UNIV_INTERN
 
619
void
 
620
trx_undo_arr_free(
 
621
/*==============*/
 
622
        trx_undo_arr_t* arr)    /* in: undo number array */
 
623
{
 
624
        ut_ad(arr->n_used == 0);
 
625
 
 
626
        mem_heap_free(arr->heap);
 
627
}
 
628
 
 
629
/***********************************************************************
 
630
Stores info of an undo log record to the array if it is not stored yet. */
 
631
static
 
632
ibool
 
633
trx_undo_arr_store_info(
 
634
/*====================*/
 
635
                        /* out: FALSE if the record already existed in the
 
636
                        array */
 
637
        trx_t*  trx,    /* in: transaction */
 
638
        dulint  undo_no)/* in: undo number */
 
639
{
 
640
        trx_undo_inf_t* cell;
 
641
        trx_undo_inf_t* stored_here;
 
642
        trx_undo_arr_t* arr;
 
643
        ulint           n_used;
 
644
        ulint           n;
 
645
        ulint           i;
 
646
 
 
647
        n = 0;
 
648
        arr = trx->undo_no_arr;
 
649
        n_used = arr->n_used;
 
650
        stored_here = NULL;
 
651
 
 
652
        for (i = 0;; i++) {
 
653
                cell = trx_undo_arr_get_nth_info(arr, i);
 
654
 
 
655
                if (!cell->in_use) {
 
656
                        if (!stored_here) {
 
657
                                /* Not in use, we may store here */
 
658
                                cell->undo_no = undo_no;
 
659
                                cell->in_use = TRUE;
 
660
 
 
661
                                arr->n_used++;
 
662
 
 
663
                                stored_here = cell;
 
664
                        }
 
665
                } else {
 
666
                        n++;
 
667
 
 
668
                        if (0 == ut_dulint_cmp(cell->undo_no, undo_no)) {
 
669
 
 
670
                                if (stored_here) {
 
671
                                        stored_here->in_use = FALSE;
 
672
                                        ut_ad(arr->n_used > 0);
 
673
                                        arr->n_used--;
 
674
                                }
 
675
 
 
676
                                ut_ad(arr->n_used == n_used);
 
677
 
 
678
                                return(FALSE);
 
679
                        }
 
680
                }
 
681
 
 
682
                if (n == n_used && stored_here) {
 
683
 
 
684
                        ut_ad(arr->n_used == 1 + n_used);
 
685
 
 
686
                        return(TRUE);
 
687
                }
 
688
        }
 
689
}
 
690
 
 
691
/***********************************************************************
 
692
Removes an undo number from the array. */
 
693
static
 
694
void
 
695
trx_undo_arr_remove_info(
 
696
/*=====================*/
 
697
        trx_undo_arr_t* arr,    /* in: undo number array */
 
698
        dulint          undo_no)/* in: undo number */
 
699
{
 
700
        trx_undo_inf_t* cell;
 
701
        ulint           n_used;
 
702
        ulint           n;
 
703
        ulint           i;
 
704
 
 
705
        n_used = arr->n_used;
 
706
        n = 0;
 
707
 
 
708
        for (i = 0;; i++) {
 
709
                cell = trx_undo_arr_get_nth_info(arr, i);
 
710
 
 
711
                if (cell->in_use
 
712
                    && 0 == ut_dulint_cmp(cell->undo_no, undo_no)) {
 
713
 
 
714
                        cell->in_use = FALSE;
 
715
 
 
716
                        ut_ad(arr->n_used > 0);
 
717
 
 
718
                        arr->n_used--;
 
719
 
 
720
                        return;
 
721
                }
 
722
        }
 
723
}
 
724
 
 
725
/***********************************************************************
 
726
Gets the biggest undo number in an array. */
 
727
static
 
728
dulint
 
729
trx_undo_arr_get_biggest(
 
730
/*=====================*/
 
731
                                /* out: biggest value, ut_dulint_zero if
 
732
                                the array is empty */
 
733
        trx_undo_arr_t* arr)    /* in: undo number array */
 
734
{
 
735
        trx_undo_inf_t* cell;
 
736
        ulint           n_used;
 
737
        dulint          biggest;
 
738
        ulint           n;
 
739
        ulint           i;
 
740
 
 
741
        n = 0;
 
742
        n_used = arr->n_used;
 
743
        biggest = ut_dulint_zero;
 
744
 
 
745
        for (i = 0;; i++) {
 
746
                cell = trx_undo_arr_get_nth_info(arr, i);
 
747
 
 
748
                if (cell->in_use) {
 
749
                        n++;
 
750
                        if (ut_dulint_cmp(cell->undo_no, biggest) > 0) {
 
751
 
 
752
                                biggest = cell->undo_no;
 
753
                        }
 
754
                }
 
755
 
 
756
                if (n == n_used) {
 
757
                        return(biggest);
 
758
                }
 
759
        }
 
760
}
 
761
 
 
762
/***************************************************************************
 
763
Tries truncate the undo logs. */
 
764
UNIV_INTERN
 
765
void
 
766
trx_roll_try_truncate(
 
767
/*==================*/
 
768
        trx_t*  trx)    /* in: transaction */
 
769
{
 
770
        trx_undo_arr_t* arr;
 
771
        dulint          limit;
 
772
        dulint          biggest;
 
773
 
 
774
        ut_ad(mutex_own(&(trx->undo_mutex)));
 
775
        ut_ad(mutex_own(&((trx->rseg)->mutex)));
 
776
 
 
777
        trx->pages_undone = 0;
 
778
 
 
779
        arr = trx->undo_no_arr;
 
780
 
 
781
        limit = trx->undo_no;
 
782
 
 
783
        if (arr->n_used > 0) {
 
784
                biggest = trx_undo_arr_get_biggest(arr);
 
785
 
 
786
                if (ut_dulint_cmp(biggest, limit) >= 0) {
 
787
 
 
788
                        limit = ut_dulint_add(biggest, 1);
 
789
                }
 
790
        }
 
791
 
 
792
        if (trx->insert_undo) {
 
793
                trx_undo_truncate_end(trx, trx->insert_undo, limit);
 
794
        }
 
795
 
 
796
        if (trx->update_undo) {
 
797
                trx_undo_truncate_end(trx, trx->update_undo, limit);
 
798
        }
 
799
}
 
800
 
 
801
/***************************************************************************
 
802
Pops the topmost undo log record in a single undo log and updates the info
 
803
about the topmost record in the undo log memory struct. */
 
804
static
 
805
trx_undo_rec_t*
 
806
trx_roll_pop_top_rec(
 
807
/*=================*/
 
808
                                /* out: undo log record, the page s-latched */
 
809
        trx_t*          trx,    /* in: transaction */
 
810
        trx_undo_t*     undo,   /* in: undo log */
 
811
        mtr_t*          mtr)    /* in: mtr */
 
812
{
 
813
        page_t*         undo_page;
 
814
        ulint           offset;
 
815
        trx_undo_rec_t* prev_rec;
 
816
        page_t*         prev_rec_page;
 
817
 
 
818
        ut_ad(mutex_own(&(trx->undo_mutex)));
 
819
 
 
820
        undo_page = trx_undo_page_get_s_latched(undo->space, undo->zip_size,
 
821
                                                undo->top_page_no, mtr);
 
822
        offset = undo->top_offset;
 
823
 
 
824
        /*      fprintf(stderr, "Thread %lu undoing trx %lu undo record %lu\n",
 
825
        os_thread_get_curr_id(), ut_dulint_get_low(trx->id),
 
826
        ut_dulint_get_low(undo->top_undo_no)); */
 
827
 
 
828
        prev_rec = trx_undo_get_prev_rec(undo_page + offset,
 
829
                                         undo->hdr_page_no, undo->hdr_offset,
 
830
                                         mtr);
 
831
        if (prev_rec == NULL) {
 
832
 
 
833
                undo->empty = TRUE;
 
834
        } else {
 
835
                prev_rec_page = page_align(prev_rec);
 
836
 
 
837
                if (prev_rec_page != undo_page) {
 
838
 
 
839
                        trx->pages_undone++;
 
840
                }
 
841
 
 
842
                undo->top_page_no = page_get_page_no(prev_rec_page);
 
843
                undo->top_offset  = prev_rec - prev_rec_page;
 
844
                undo->top_undo_no = trx_undo_rec_get_undo_no(prev_rec);
 
845
        }
 
846
 
 
847
        return(undo_page + offset);
 
848
}
 
849
 
 
850
/************************************************************************
 
851
Pops the topmost record when the two undo logs of a transaction are seen
 
852
as a single stack of records ordered by their undo numbers. Inserts the
 
853
undo number of the popped undo record to the array of currently processed
 
854
undo numbers in the transaction. When the query thread finishes processing
 
855
of this undo record, it must be released with trx_undo_rec_release. */
 
856
UNIV_INTERN
 
857
trx_undo_rec_t*
 
858
trx_roll_pop_top_rec_of_trx(
 
859
/*========================*/
 
860
                                /* out: undo log record copied to heap, NULL
 
861
                                if none left, or if the undo number of the
 
862
                                top record would be less than the limit */
 
863
        trx_t*          trx,    /* in: transaction */
 
864
        dulint          limit,  /* in: least undo number we need */
 
865
        dulint*         roll_ptr,/* out: roll pointer to undo record */
 
866
        mem_heap_t*     heap)   /* in: memory heap where copied */
 
867
{
 
868
        trx_undo_t*     undo;
 
869
        trx_undo_t*     ins_undo;
 
870
        trx_undo_t*     upd_undo;
 
871
        trx_undo_rec_t* undo_rec;
 
872
        trx_undo_rec_t* undo_rec_copy;
 
873
        dulint          undo_no;
 
874
        ibool           is_insert;
 
875
        trx_rseg_t*     rseg;
 
876
        ulint           progress_pct;
 
877
        mtr_t           mtr;
 
878
 
 
879
        rseg = trx->rseg;
 
880
try_again:
 
881
        mutex_enter(&(trx->undo_mutex));
 
882
 
 
883
        if (trx->pages_undone >= TRX_ROLL_TRUNC_THRESHOLD) {
 
884
                mutex_enter(&(rseg->mutex));
 
885
 
 
886
                trx_roll_try_truncate(trx);
 
887
 
 
888
                mutex_exit(&(rseg->mutex));
 
889
        }
 
890
 
 
891
        ins_undo = trx->insert_undo;
 
892
        upd_undo = trx->update_undo;
 
893
 
 
894
        if (!ins_undo || ins_undo->empty) {
 
895
                undo = upd_undo;
 
896
        } else if (!upd_undo || upd_undo->empty) {
 
897
                undo = ins_undo;
 
898
        } else if (ut_dulint_cmp(upd_undo->top_undo_no,
 
899
                                 ins_undo->top_undo_no) > 0) {
 
900
                undo = upd_undo;
 
901
        } else {
 
902
                undo = ins_undo;
 
903
        }
 
904
 
 
905
        if (!undo || undo->empty
 
906
            || (ut_dulint_cmp(limit, undo->top_undo_no) > 0)) {
 
907
 
 
908
                if ((trx->undo_no_arr)->n_used == 0) {
 
909
                        /* Rollback is ending */
 
910
 
 
911
                        mutex_enter(&(rseg->mutex));
 
912
 
 
913
                        trx_roll_try_truncate(trx);
 
914
 
 
915
                        mutex_exit(&(rseg->mutex));
 
916
                }
 
917
 
 
918
                mutex_exit(&(trx->undo_mutex));
 
919
 
 
920
                return(NULL);
 
921
        }
 
922
 
 
923
        if (undo == ins_undo) {
 
924
                is_insert = TRUE;
 
925
        } else {
 
926
                is_insert = FALSE;
 
927
        }
 
928
 
 
929
        *roll_ptr = trx_undo_build_roll_ptr(is_insert, (undo->rseg)->id,
 
930
                                            undo->top_page_no,
 
931
                                            undo->top_offset);
 
932
        mtr_start(&mtr);
 
933
 
 
934
        undo_rec = trx_roll_pop_top_rec(trx, undo, &mtr);
 
935
 
 
936
        undo_no = trx_undo_rec_get_undo_no(undo_rec);
 
937
 
 
938
        ut_ad(ut_dulint_cmp(ut_dulint_add(undo_no, 1), trx->undo_no) == 0);
 
939
 
 
940
        /* We print rollback progress info if we are in a crash recovery
 
941
        and the transaction has at least 1000 row operations to undo. */
 
942
 
 
943
        if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) {
 
944
 
 
945
                progress_pct = 100 - (ulint)
 
946
                        ((ut_conv_dulint_to_longlong(undo_no) * 100)
 
947
                         / trx_roll_max_undo_no);
 
948
                if (progress_pct != trx_roll_progress_printed_pct) {
 
949
                        if (trx_roll_progress_printed_pct == 0) {
 
950
                                fprintf(stderr,
 
951
                                        "\nInnoDB: Progress in percents:"
 
952
                                        " %lu", (ulong) progress_pct);
 
953
                        } else {
 
954
                                fprintf(stderr,
 
955
                                        " %lu", (ulong) progress_pct);
 
956
                        }
 
957
                        fflush(stderr);
 
958
                        trx_roll_progress_printed_pct = progress_pct;
 
959
                }
 
960
        }
 
961
 
 
962
        trx->undo_no = undo_no;
 
963
 
 
964
        if (!trx_undo_arr_store_info(trx, undo_no)) {
 
965
                /* A query thread is already processing this undo log record */
 
966
 
 
967
                mutex_exit(&(trx->undo_mutex));
 
968
 
 
969
                mtr_commit(&mtr);
 
970
 
 
971
                goto try_again;
 
972
        }
 
973
 
 
974
        undo_rec_copy = trx_undo_rec_copy(undo_rec, heap);
 
975
 
 
976
        mutex_exit(&(trx->undo_mutex));
 
977
 
 
978
        mtr_commit(&mtr);
 
979
 
 
980
        return(undo_rec_copy);
 
981
}
 
982
 
 
983
/************************************************************************
 
984
Reserves an undo log record for a query thread to undo. This should be
 
985
called if the query thread gets the undo log record not using the pop
 
986
function above. */
 
987
UNIV_INTERN
 
988
ibool
 
989
trx_undo_rec_reserve(
 
990
/*=================*/
 
991
                        /* out: TRUE if succeeded */
 
992
        trx_t*  trx,    /* in: transaction */
 
993
        dulint  undo_no)/* in: undo number of the record */
 
994
{
 
995
        ibool   ret;
 
996
 
 
997
        mutex_enter(&(trx->undo_mutex));
 
998
 
 
999
        ret = trx_undo_arr_store_info(trx, undo_no);
 
1000
 
 
1001
        mutex_exit(&(trx->undo_mutex));
 
1002
 
 
1003
        return(ret);
 
1004
}
 
1005
 
 
1006
/***********************************************************************
 
1007
Releases a reserved undo record. */
 
1008
UNIV_INTERN
 
1009
void
 
1010
trx_undo_rec_release(
 
1011
/*=================*/
 
1012
        trx_t*  trx,    /* in: transaction */
 
1013
        dulint  undo_no)/* in: undo number */
 
1014
{
 
1015
        trx_undo_arr_t* arr;
 
1016
 
 
1017
        mutex_enter(&(trx->undo_mutex));
 
1018
 
 
1019
        arr = trx->undo_no_arr;
 
1020
 
 
1021
        trx_undo_arr_remove_info(arr, undo_no);
 
1022
 
 
1023
        mutex_exit(&(trx->undo_mutex));
 
1024
}
 
1025
 
 
1026
/*************************************************************************
 
1027
Starts a rollback operation. */
 
1028
UNIV_INTERN
 
1029
void
 
1030
trx_rollback(
 
1031
/*=========*/
 
1032
        trx_t*          trx,    /* in: transaction */
 
1033
        trx_sig_t*      sig,    /* in: signal starting the rollback */
 
1034
        que_thr_t**     next_thr)/* in/out: next query thread to run;
 
1035
                                if the value which is passed in is
 
1036
                                a pointer to a NULL pointer, then the
 
1037
                                calling function can start running
 
1038
                                a new query thread; if the passed value is
 
1039
                                NULL, the parameter is ignored */
 
1040
{
 
1041
        que_t*          roll_graph;
 
1042
        que_thr_t*      thr;
 
1043
        /*      que_thr_t*      thr2; */
 
1044
 
 
1045
        ut_ad(mutex_own(&kernel_mutex));
 
1046
        ut_ad((trx->undo_no_arr == NULL) || ((trx->undo_no_arr)->n_used == 0));
 
1047
 
 
1048
        /* Initialize the rollback field in the transaction */
 
1049
 
 
1050
        if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {
 
1051
 
 
1052
                trx->roll_limit = ut_dulint_zero;
 
1053
 
 
1054
        } else if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) {
 
1055
 
 
1056
                trx->roll_limit = (sig->savept).least_undo_no;
 
1057
 
 
1058
        } else if (sig->type == TRX_SIG_ERROR_OCCURRED) {
 
1059
 
 
1060
                trx->roll_limit = trx->last_sql_stat_start.least_undo_no;
 
1061
        } else {
 
1062
                ut_error;
 
1063
        }
 
1064
 
 
1065
        ut_a(ut_dulint_cmp(trx->roll_limit, trx->undo_no) <= 0);
 
1066
 
 
1067
        trx->pages_undone = 0;
 
1068
 
 
1069
        if (trx->undo_no_arr == NULL) {
 
1070
                trx->undo_no_arr = trx_undo_arr_create();
 
1071
        }
 
1072
 
 
1073
        /* Build a 'query' graph which will perform the undo operations */
 
1074
 
 
1075
        roll_graph = trx_roll_graph_build(trx);
 
1076
 
 
1077
        trx->graph = roll_graph;
 
1078
        trx->que_state = TRX_QUE_ROLLING_BACK;
 
1079
 
 
1080
        thr = que_fork_start_command(roll_graph);
 
1081
 
 
1082
        ut_ad(thr);
 
1083
 
 
1084
        /*      thr2 = que_fork_start_command(roll_graph);
 
1085
 
 
1086
        ut_ad(thr2); */
 
1087
 
 
1088
        if (next_thr && (*next_thr == NULL)) {
 
1089
                *next_thr = thr;
 
1090
                /*              srv_que_task_enqueue_low(thr2); */
 
1091
        } else {
 
1092
                srv_que_task_enqueue_low(thr);
 
1093
                /*              srv_que_task_enqueue_low(thr2); */
 
1094
        }
 
1095
}
 
1096
 
 
1097
/********************************************************************
 
1098
Builds an undo 'query' graph for a transaction. The actual rollback is
 
1099
performed by executing this query graph like a query subprocedure call.
 
1100
The reply about the completion of the rollback will be sent by this
 
1101
graph. */
 
1102
UNIV_INTERN
 
1103
que_t*
 
1104
trx_roll_graph_build(
 
1105
/*=================*/
 
1106
                        /* out, own: the query graph */
 
1107
        trx_t*  trx)    /* in: trx handle */
 
1108
{
 
1109
        mem_heap_t*     heap;
 
1110
        que_fork_t*     fork;
 
1111
        que_thr_t*      thr;
 
1112
        /*      que_thr_t*      thr2; */
 
1113
 
 
1114
        ut_ad(mutex_own(&kernel_mutex));
 
1115
 
 
1116
        heap = mem_heap_create(512);
 
1117
        fork = que_fork_create(NULL, NULL, QUE_FORK_ROLLBACK, heap);
 
1118
        fork->trx = trx;
 
1119
 
 
1120
        thr = que_thr_create(fork, heap);
 
1121
        /*      thr2 = que_thr_create(fork, heap); */
 
1122
 
 
1123
        thr->child = row_undo_node_create(trx, thr, heap);
 
1124
        /*      thr2->child = row_undo_node_create(trx, thr2, heap); */
 
1125
 
 
1126
        return(fork);
 
1127
}
 
1128
 
 
1129
/*************************************************************************
 
1130
Finishes error processing after the necessary partial rollback has been
 
1131
done. */
 
1132
static
 
1133
void
 
1134
trx_finish_error_processing(
 
1135
/*========================*/
 
1136
        trx_t*  trx)    /* in: transaction */
 
1137
{
 
1138
        trx_sig_t*      sig;
 
1139
        trx_sig_t*      next_sig;
 
1140
 
 
1141
        ut_ad(mutex_own(&kernel_mutex));
 
1142
 
 
1143
        sig = UT_LIST_GET_FIRST(trx->signals);
 
1144
 
 
1145
        while (sig != NULL) {
 
1146
                next_sig = UT_LIST_GET_NEXT(signals, sig);
 
1147
 
 
1148
                if (sig->type == TRX_SIG_ERROR_OCCURRED) {
 
1149
 
 
1150
                        trx_sig_remove(trx, sig);
 
1151
                }
 
1152
 
 
1153
                sig = next_sig;
 
1154
        }
 
1155
 
 
1156
        trx->que_state = TRX_QUE_RUNNING;
 
1157
}
 
1158
 
 
1159
/*************************************************************************
 
1160
Finishes a partial rollback operation. */
 
1161
static
 
1162
void
 
1163
trx_finish_partial_rollback_off_kernel(
 
1164
/*===================================*/
 
1165
        trx_t*          trx,    /* in: transaction */
 
1166
        que_thr_t**     next_thr)/* in/out: next query thread to run;
 
1167
                                if the value which is passed in is a pointer
 
1168
                                to a NULL pointer, then the calling function
 
1169
                                can start running a new query thread; if this
 
1170
                                parameter is NULL, it is ignored */
 
1171
{
 
1172
        trx_sig_t*      sig;
 
1173
 
 
1174
        ut_ad(mutex_own(&kernel_mutex));
 
1175
 
 
1176
        sig = UT_LIST_GET_FIRST(trx->signals);
 
1177
 
 
1178
        /* Remove the signal from the signal queue and send reply message
 
1179
        to it */
 
1180
 
 
1181
        trx_sig_reply(sig, next_thr);
 
1182
        trx_sig_remove(trx, sig);
 
1183
 
 
1184
        trx->que_state = TRX_QUE_RUNNING;
 
1185
}
 
1186
 
 
1187
/********************************************************************
 
1188
Finishes a transaction rollback. */
 
1189
UNIV_INTERN
 
1190
void
 
1191
trx_finish_rollback_off_kernel(
 
1192
/*===========================*/
 
1193
        que_t*          graph,  /* in: undo graph which can now be freed */
 
1194
        trx_t*          trx,    /* in: transaction */
 
1195
        que_thr_t**     next_thr)/* in/out: next query thread to run;
 
1196
                                if the value which is passed in is
 
1197
                                a pointer to a NULL pointer, then the
 
1198
                                calling function can start running
 
1199
                                a new query thread; if this parameter is
 
1200
                                NULL, it is ignored */
 
1201
{
 
1202
        trx_sig_t*      sig;
 
1203
        trx_sig_t*      next_sig;
 
1204
 
 
1205
        ut_ad(mutex_own(&kernel_mutex));
 
1206
 
 
1207
        ut_a(trx->undo_no_arr == NULL || trx->undo_no_arr->n_used == 0);
 
1208
 
 
1209
        /* Free the memory reserved by the undo graph */
 
1210
        que_graph_free(graph);
 
1211
 
 
1212
        sig = UT_LIST_GET_FIRST(trx->signals);
 
1213
 
 
1214
        if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) {
 
1215
 
 
1216
                trx_finish_partial_rollback_off_kernel(trx, next_thr);
 
1217
 
 
1218
                return;
 
1219
 
 
1220
        } else if (sig->type == TRX_SIG_ERROR_OCCURRED) {
 
1221
 
 
1222
                trx_finish_error_processing(trx);
 
1223
 
 
1224
                return;
 
1225
        }
 
1226
 
 
1227
#ifdef UNIV_DEBUG
 
1228
        if (lock_print_waits) {
 
1229
                fprintf(stderr, "Trx %lu rollback finished\n",
 
1230
                        (ulong) ut_dulint_get_low(trx->id));
 
1231
        }
 
1232
#endif /* UNIV_DEBUG */
 
1233
 
 
1234
        trx_commit_off_kernel(trx);
 
1235
 
 
1236
        /* Remove all TRX_SIG_TOTAL_ROLLBACK signals from the signal queue and
 
1237
        send reply messages to them */
 
1238
 
 
1239
        trx->que_state = TRX_QUE_RUNNING;
 
1240
 
 
1241
        while (sig != NULL) {
 
1242
                next_sig = UT_LIST_GET_NEXT(signals, sig);
 
1243
 
 
1244
                if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {
 
1245
 
 
1246
                        trx_sig_reply(sig, next_thr);
 
1247
 
 
1248
                        trx_sig_remove(trx, sig);
 
1249
                }
 
1250
 
 
1251
                sig = next_sig;
 
1252
        }
 
1253
}
 
1254
 
 
1255
/*************************************************************************
 
1256
Creates a rollback command node struct. */
 
1257
UNIV_INTERN
 
1258
roll_node_t*
 
1259
roll_node_create(
 
1260
/*=============*/
 
1261
                                /* out, own: rollback node struct */
 
1262
        mem_heap_t*     heap)   /* in: mem heap where created */
 
1263
{
 
1264
        roll_node_t*    node;
 
1265
 
 
1266
        node = mem_heap_alloc(heap, sizeof(roll_node_t));
 
1267
        node->common.type = QUE_NODE_ROLLBACK;
 
1268
        node->state = ROLL_NODE_SEND;
 
1269
 
 
1270
        node->partial = FALSE;
 
1271
 
 
1272
        return(node);
 
1273
}
 
1274
 
 
1275
/***************************************************************
 
1276
Performs an execution step for a rollback command node in a query graph. */
 
1277
UNIV_INTERN
 
1278
que_thr_t*
 
1279
trx_rollback_step(
 
1280
/*==============*/
 
1281
                                /* out: query thread to run next, or NULL */
 
1282
        que_thr_t*      thr)    /* in: query thread */
 
1283
{
 
1284
        roll_node_t*    node;
 
1285
        ulint           sig_no;
 
1286
        trx_savept_t*   savept;
 
1287
 
 
1288
        node = thr->run_node;
 
1289
 
 
1290
        ut_ad(que_node_get_type(node) == QUE_NODE_ROLLBACK);
 
1291
 
 
1292
        if (thr->prev_node == que_node_get_parent(node)) {
 
1293
                node->state = ROLL_NODE_SEND;
 
1294
        }
 
1295
 
 
1296
        if (node->state == ROLL_NODE_SEND) {
 
1297
                mutex_enter(&kernel_mutex);
 
1298
 
 
1299
                node->state = ROLL_NODE_WAIT;
 
1300
 
 
1301
                if (node->partial) {
 
1302
                        sig_no = TRX_SIG_ROLLBACK_TO_SAVEPT;
 
1303
                        savept = &(node->savept);
 
1304
                } else {
 
1305
                        sig_no = TRX_SIG_TOTAL_ROLLBACK;
 
1306
                        savept = NULL;
 
1307
                }
 
1308
 
 
1309
                /* Send a rollback signal to the transaction */
 
1310
 
 
1311
                trx_sig_send(thr_get_trx(thr), sig_no, TRX_SIG_SELF, thr,
 
1312
                             savept, NULL);
 
1313
 
 
1314
                thr->state = QUE_THR_SIG_REPLY_WAIT;
 
1315
 
 
1316
                mutex_exit(&kernel_mutex);
 
1317
 
 
1318
                return(NULL);
 
1319
        }
 
1320
 
 
1321
        ut_ad(node->state == ROLL_NODE_WAIT);
 
1322
 
 
1323
        thr->run_node = que_node_get_parent(node);
 
1324
 
 
1325
        return(thr);
 
1326
}