~ubuntu-branches/ubuntu/saucy/drizzle/saucy-proposed

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2011-01-04 09:31:58 UTC
  • mfrom: (1.2.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20110104093158-smhgvkfdi2y9au3i
Tags: 2011.01.07-0ubuntu1
New upstream release.

Show diffs side-by-side

added added

removed removed

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