trx0roll.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:22k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /******************************************************
  2. Transaction rollback
  3. (c) 1996 Innobase Oy
  4. Created 3/26/1996 Heikki Tuuri
  5. *******************************************************/
  6. #include "trx0roll.h"
  7. #ifdef UNIV_NONINL
  8. #include "trx0roll.ic"
  9. #endif
  10. #include "fsp0fsp.h"
  11. #include "mach0data.h"
  12. #include "trx0rseg.h"
  13. #include "trx0trx.h"
  14. #include "trx0undo.h"
  15. #include "trx0rec.h"
  16. #include "que0que.h"
  17. #include "usr0sess.h"
  18. #include "srv0que.h"
  19. #include "row0undo.h"
  20. #include "row0mysql.h"
  21. #include "lock0lock.h"
  22. #include "pars0pars.h"
  23. /* This many pages must be undone before a truncate is tried within rollback */
  24. #define TRX_ROLL_TRUNC_THRESHOLD 1
  25. /***********************************************************************
  26. Rollback a transaction used in MySQL. */
  27. int
  28. trx_general_rollback_for_mysql(
  29. /*===========================*/
  30. /* out: error code or DB_SUCCESS */
  31. trx_t* trx, /* in: transaction handle */
  32. ibool partial,/* in: TRUE if partial rollback requested */
  33. trx_savept_t* savept) /* in: pointer to savepoint undo number, if
  34. partial rollback requested */
  35. {
  36. mem_heap_t* heap;
  37. que_thr_t* thr;
  38. roll_node_t* roll_node;
  39. heap = mem_heap_create(512);
  40. roll_node = roll_node_create(heap);
  41. roll_node->partial = partial;
  42. if (partial) {
  43. roll_node->savept = *savept;
  44. }
  45. trx->error_state = DB_SUCCESS;
  46. thr = pars_complete_graph_for_exec(roll_node, trx, heap);
  47. ut_a(thr == que_fork_start_command(que_node_get_parent(thr),
  48. SESS_COMM_EXECUTE, 0));
  49. que_run_threads(thr);
  50. mutex_enter(&kernel_mutex);
  51. while (trx->que_state != TRX_QUE_RUNNING) {
  52. mutex_exit(&kernel_mutex);
  53. os_thread_sleep(100000);
  54. mutex_enter(&kernel_mutex);
  55. }
  56. mutex_exit(&kernel_mutex);
  57.   mem_heap_free(heap);
  58.   ut_a(trx->error_state == DB_SUCCESS);
  59. return((int) trx->error_state);
  60. }
  61. /***********************************************************************
  62. Rollback a transaction used in MySQL. */
  63. int
  64. trx_rollback_for_mysql(
  65. /*===================*/
  66. /* out: error code or DB_SUCCESS */
  67. trx_t* trx) /* in: transaction handle */
  68. {
  69. int err;
  70. if (trx->conc_state == TRX_NOT_STARTED) {
  71. return(DB_SUCCESS);
  72. }
  73. /* Tell Innobase server that there might be work for
  74. utility threads: */
  75. srv_active_wake_master_thread();
  76. err = trx_general_rollback_for_mysql(trx, FALSE, NULL);
  77. /* Tell Innobase server that there might be work for
  78. utility threads: */
  79. srv_active_wake_master_thread();
  80. return(err);
  81. }
  82. /***********************************************************************
  83. Rollback the latest SQL statement for MySQL. */
  84. int
  85. trx_rollback_last_sql_stat_for_mysql(
  86. /*=================================*/
  87. /* out: error code or DB_SUCCESS */
  88. trx_t* trx) /* in: transaction handle */
  89. {
  90. int err;
  91. if (trx->conc_state == TRX_NOT_STARTED) {
  92. return(DB_SUCCESS);
  93. }
  94. /* Tell Innobase server that there might be work for
  95. utility threads: */
  96. srv_active_wake_master_thread();
  97. err = trx_general_rollback_for_mysql(trx, TRUE,
  98. &(trx->last_sql_stat_start));
  99. trx_mark_sql_stat_end(trx);
  100. /* Tell Innobase server that there might be work for
  101. utility threads: */
  102. srv_active_wake_master_thread();
  103. return(err);
  104. }
  105. /***********************************************************************
  106. Rollback uncommitted transactions which have no user session. */
  107. void
  108. trx_rollback_all_without_sess(void)
  109. /*===============================*/
  110. {
  111. mem_heap_t* heap;
  112. que_fork_t* fork;
  113. que_thr_t* thr;
  114. roll_node_t* roll_node;
  115. trx_t* trx;
  116. dict_table_t* table;
  117. int err;
  118. mutex_enter(&kernel_mutex);
  119. /* Open a dummy session */
  120. if (!trx_dummy_sess) {
  121. trx_dummy_sess = sess_open(NULL, (byte*)"Dummy sess",
  122. ut_strlen("Dummy sess"));
  123. }
  124. mutex_exit(&kernel_mutex);
  125. if (UT_LIST_GET_FIRST(trx_sys->trx_list)) {
  126. fprintf(stderr,
  127. "Innobase: Starting rollback of uncommitted transactionsn");
  128. } else {
  129. return;
  130. }
  131. loop:
  132. heap = mem_heap_create(512);
  133. mutex_enter(&kernel_mutex);
  134. trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
  135. while (trx && (trx->sess || (trx->conc_state == TRX_NOT_STARTED))) {
  136. trx = UT_LIST_GET_NEXT(trx_list, trx);
  137. }
  138. mutex_exit(&kernel_mutex);
  139. if (trx == NULL) {
  140. fprintf(stderr,
  141. "Innobase: Rollback of uncommitted transactions completedn");
  142.   mem_heap_free(heap);
  143. return;
  144. }
  145. trx->sess = trx_dummy_sess;
  146. fork = que_fork_create(NULL, NULL, QUE_FORK_RECOVERY, heap);
  147. fork->trx = trx;
  148. thr = que_thr_create(fork, heap);
  149. roll_node = roll_node_create(heap);
  150. thr->child = roll_node;
  151. roll_node->common.parent = thr;
  152. mutex_enter(&kernel_mutex);
  153. trx->graph = fork;
  154. ut_a(thr == que_fork_start_command(fork, SESS_COMM_EXECUTE, 0));
  155. fprintf(stderr, "Innobase: Rolling back trx no %lun",
  156. ut_dulint_get_low(trx->id));
  157. mutex_exit(&kernel_mutex);
  158. if (trx->dict_operation) {
  159. mutex_enter(&(dict_sys->mutex));
  160. }
  161. que_run_threads(thr);
  162. mutex_enter(&kernel_mutex);
  163. while (trx->que_state != TRX_QUE_RUNNING) {
  164. mutex_exit(&kernel_mutex);
  165. fprintf(stderr,
  166. "Innobase: Waiting rollback of trx no %lu to endn",
  167. ut_dulint_get_low(trx->id));
  168. os_thread_sleep(100000);
  169. mutex_enter(&kernel_mutex);
  170. }
  171. mutex_exit(&kernel_mutex);
  172. if (trx->dict_operation) {
  173. /* If the transaction was for a dictionary operation, we
  174. drop the relevant table, if it still exists */
  175. table = dict_table_get_on_id_low(trx->table_id, trx);
  176. if (table) {
  177. err = row_drop_table_for_mysql(table->name, trx,
  178. TRUE);
  179. ut_a(err == (int) DB_SUCCESS);
  180. }
  181. }
  182. if (trx->dict_operation) {
  183. mutex_exit(&(dict_sys->mutex));
  184. }
  185. fprintf(stderr, "Innobase: Rolling back of trx no %lu completedn",
  186. ut_dulint_get_low(trx->id));
  187. mem_heap_free(heap);
  188. goto loop;
  189. }
  190. /***********************************************************************
  191. Returns a transaction savepoint taken at this point in time. */
  192. trx_savept_t
  193. trx_savept_take(
  194. /*============*/
  195. /* out: savepoint */
  196. trx_t* trx) /* in: transaction */
  197. {
  198. trx_savept_t savept;
  199. savept.least_undo_no = trx->undo_no;
  200. return(savept);
  201. }
  202. /***********************************************************************
  203. Creates an undo number array. */
  204. trx_undo_arr_t*
  205. trx_undo_arr_create(void)
  206. /*=====================*/
  207. {
  208. trx_undo_arr_t* arr;
  209. mem_heap_t* heap;
  210. ulint i;
  211. heap = mem_heap_create(1024);
  212. arr = mem_heap_alloc(heap, sizeof(trx_undo_arr_t));
  213. arr->infos = mem_heap_alloc(heap, sizeof(trx_undo_inf_t)
  214. * UNIV_MAX_PARALLELISM);
  215. arr->n_cells = UNIV_MAX_PARALLELISM;
  216. arr->n_used = 0;
  217. arr->heap = heap;
  218. for (i = 0; i < UNIV_MAX_PARALLELISM; i++) {
  219. (trx_undo_arr_get_nth_info(arr, i))->in_use = FALSE;
  220. }
  221. return(arr);
  222. }
  223. /***********************************************************************
  224. Frees an undo number array. */
  225. void
  226. trx_undo_arr_free(
  227. /*==============*/
  228. trx_undo_arr_t* arr) /* in: undo number array */
  229. {
  230. ut_ad(arr->n_used == 0);
  231. mem_heap_free(arr->heap);
  232. }
  233. /***********************************************************************
  234. Stores info of an undo log record to the array if it is not stored yet. */
  235. static
  236. ibool
  237. trx_undo_arr_store_info(
  238. /*====================*/
  239. /* out: FALSE if the record already existed in the
  240. array */
  241. trx_t* trx, /* in: transaction */
  242. dulint undo_no)/* in: undo number */
  243. {
  244. trx_undo_inf_t* cell;
  245. trx_undo_inf_t* stored_here;
  246. trx_undo_arr_t* arr;
  247. ulint n_used;
  248. ulint n;
  249. ulint i;
  250. n = 0;
  251. arr = trx->undo_no_arr;
  252. n_used = arr->n_used;
  253. stored_here = NULL;
  254. for (i = 0;; i++) {
  255. cell = trx_undo_arr_get_nth_info(arr, i);
  256. if (!cell->in_use) {
  257. if (!stored_here) {
  258. /* Not in use, we may store here */
  259. cell->undo_no = undo_no;
  260. cell->in_use = TRUE;
  261. arr->n_used++;
  262. stored_here = cell;
  263. }
  264. } else {
  265. n++;
  266. if (0 == ut_dulint_cmp(cell->undo_no, undo_no)) {
  267. if (stored_here) {
  268. stored_here->in_use = FALSE;
  269. ut_ad(arr->n_used > 0);
  270. arr->n_used--;
  271. }
  272. ut_ad(arr->n_used == n_used);
  273. return(FALSE);
  274. }
  275. }
  276. if (n == n_used && stored_here) {
  277. ut_ad(arr->n_used == 1 + n_used);
  278. return(TRUE);
  279. }
  280. }
  281. }
  282. /***********************************************************************
  283. Removes an undo number from the array. */
  284. static
  285. void
  286. trx_undo_arr_remove_info(
  287. /*=====================*/
  288. trx_undo_arr_t* arr, /* in: undo number array */
  289. dulint undo_no)/* in: undo number */
  290. {
  291. trx_undo_inf_t* cell;
  292. ulint n_used;
  293. ulint n;
  294. ulint i;
  295. n_used = arr->n_used;
  296. n = 0;
  297. for (i = 0;; i++) {
  298. cell = trx_undo_arr_get_nth_info(arr, i);
  299. if (cell->in_use
  300.      && 0 == ut_dulint_cmp(cell->undo_no, undo_no)) {
  301. cell->in_use = FALSE;
  302. ut_ad(arr->n_used > 0);
  303. arr->n_used--;
  304. return;
  305. }
  306. }
  307. }
  308. /***********************************************************************
  309. Gets the biggest undo number in an array. */
  310. static
  311. dulint
  312. trx_undo_arr_get_biggest(
  313. /*=====================*/
  314. /* out: biggest value, ut_dulint_zero if
  315. the array is empty */
  316. trx_undo_arr_t* arr) /* in: undo number array */
  317. {
  318. trx_undo_inf_t* cell;
  319. ulint n_used;
  320. dulint biggest;
  321. ulint n;
  322. ulint i;
  323. n = 0;
  324. n_used = arr->n_used;
  325. biggest = ut_dulint_zero;
  326. for (i = 0;; i++) {
  327. cell = trx_undo_arr_get_nth_info(arr, i);
  328. if (cell->in_use) {
  329. n++;
  330. if (ut_dulint_cmp(cell->undo_no, biggest) > 0) {
  331. biggest = cell->undo_no;
  332. }
  333. }
  334. if (n == n_used) {
  335. return(biggest);
  336. }
  337. }
  338. }
  339. /***************************************************************************
  340. Tries truncate the undo logs. */
  341. void
  342. trx_roll_try_truncate(
  343. /*==================*/
  344. trx_t* trx) /* in: transaction */
  345. {
  346. trx_undo_arr_t* arr;
  347. dulint limit;
  348. dulint biggest;
  349. ut_ad(mutex_own(&(trx->undo_mutex)));
  350. ut_ad(mutex_own(&((trx->rseg)->mutex)));
  351. trx->pages_undone = 0;
  352. arr = trx->undo_no_arr;
  353. limit = trx->undo_no;
  354. if (arr->n_used > 0) {
  355. biggest = trx_undo_arr_get_biggest(arr);
  356.      if (ut_dulint_cmp(biggest, limit) >= 0) {
  357.      limit = ut_dulint_add(biggest, 1);
  358.      }
  359. }
  360. if (trx->insert_undo) {
  361. trx_undo_truncate_end(trx, trx->insert_undo, limit);
  362. }
  363. if (trx->update_undo) {
  364. trx_undo_truncate_end(trx, trx->update_undo, limit);
  365. }
  366. }
  367. /***************************************************************************
  368. Pops the topmost undo log record in a single undo log and updates the info
  369. about the topmost record in the undo log memory struct. */
  370. static
  371. trx_undo_rec_t*
  372. trx_roll_pop_top_rec(
  373. /*=================*/
  374. /* out: undo log record, the page s-latched */
  375. trx_t* trx, /* in: transaction */
  376. trx_undo_t* undo, /* in: undo log */
  377. mtr_t* mtr) /* in: mtr */
  378. {
  379. page_t*  undo_page;
  380. ulint offset;
  381. trx_undo_rec_t* prev_rec;
  382. page_t* prev_rec_page;
  383. ut_ad(mutex_own(&(trx->undo_mutex)));
  384. undo_page = trx_undo_page_get_s_latched(undo->space,
  385. undo->top_page_no, mtr);
  386. offset = undo->top_offset;
  387. /* printf("Thread %lu undoing trx %lu undo record %lun",
  388. os_thread_get_curr_id(), ut_dulint_get_low(trx->id),
  389. ut_dulint_get_low(undo->top_undo_no)); */
  390. prev_rec = trx_undo_get_prev_rec(undo_page + offset,
  391. undo->hdr_page_no, undo->hdr_offset,
  392. mtr);
  393. if (prev_rec == NULL) {
  394. undo->empty = TRUE;
  395. } else {
  396. prev_rec_page = buf_frame_align(prev_rec);
  397. if (prev_rec_page != undo_page) {
  398. trx->pages_undone++;
  399. }
  400. undo->top_page_no = buf_frame_get_page_no(prev_rec_page);
  401. undo->top_offset  = prev_rec - prev_rec_page;
  402. undo->top_undo_no = trx_undo_rec_get_undo_no(prev_rec);
  403. }
  404. return(undo_page + offset);
  405. }
  406. /************************************************************************
  407. Pops the topmost record when the two undo logs of a transaction are seen
  408. as a single stack of records ordered by their undo numbers. Inserts the
  409. undo number of the popped undo record to the array of currently processed
  410. undo numbers in the transaction. When the query thread finishes processing
  411. of this undo record, it must be released with trx_undo_rec_release. */
  412. trx_undo_rec_t*
  413. trx_roll_pop_top_rec_of_trx(
  414. /*========================*/
  415. /* out: undo log record copied to heap, NULL
  416. if none left, or if the undo number of the
  417. top record would be less than the limit */
  418. trx_t* trx, /* in: transaction */
  419. dulint limit, /* in: least undo number we need */
  420. dulint* roll_ptr,/* out: roll pointer to undo record */
  421. mem_heap_t* heap) /* in: memory heap where copied */
  422. {
  423. trx_undo_t* undo;
  424. trx_undo_t* ins_undo;
  425. trx_undo_t* upd_undo;
  426. trx_undo_rec_t* undo_rec;
  427. trx_undo_rec_t* undo_rec_copy;
  428. dulint undo_no;
  429. ibool is_insert;
  430. trx_rseg_t* rseg;
  431. mtr_t mtr;
  432. rseg = trx->rseg;
  433. try_again:
  434. mutex_enter(&(trx->undo_mutex));
  435. if (trx->pages_undone >= TRX_ROLL_TRUNC_THRESHOLD) {
  436. mutex_enter(&(rseg->mutex));
  437. trx_roll_try_truncate(trx);
  438. mutex_exit(&(rseg->mutex));
  439. }
  440. ins_undo = trx->insert_undo;
  441. upd_undo = trx->update_undo;
  442. if (!ins_undo || ins_undo->empty) {
  443. undo = upd_undo;
  444. } else if (!upd_undo || upd_undo->empty) {
  445. undo = ins_undo;
  446. } else if (ut_dulint_cmp(upd_undo->top_undo_no,
  447.  ins_undo->top_undo_no) > 0) {
  448. undo = upd_undo;
  449. } else {
  450. undo = ins_undo;
  451. }
  452. if (!undo || undo->empty
  453. || (ut_dulint_cmp(limit, undo->top_undo_no) > 0)) {
  454.      if ((trx->undo_no_arr)->n_used == 0) {
  455. /* Rollback is ending */
  456. mutex_enter(&(rseg->mutex));
  457. trx_roll_try_truncate(trx);
  458. mutex_exit(&(rseg->mutex));
  459. }
  460. mutex_exit(&(trx->undo_mutex));
  461. return(NULL);
  462. }
  463. if (undo == ins_undo) {
  464. is_insert = TRUE;
  465. } else {
  466. is_insert = FALSE;
  467. }
  468. *roll_ptr = trx_undo_build_roll_ptr(is_insert, (undo->rseg)->id,
  469. undo->top_page_no, undo->top_offset);
  470. mtr_start(&mtr);
  471. undo_rec = trx_roll_pop_top_rec(trx, undo, &mtr);
  472. undo_no = trx_undo_rec_get_undo_no(undo_rec);
  473. ut_ad(ut_dulint_cmp(ut_dulint_add(undo_no, 1), trx->undo_no) == 0);
  474. trx->undo_no = undo_no;
  475. if (!trx_undo_arr_store_info(trx, undo_no)) {
  476. /* A query thread is already processing this undo log record */
  477. mutex_exit(&(trx->undo_mutex));
  478. mtr_commit(&mtr);
  479. goto try_again;
  480. }
  481. undo_rec_copy = trx_undo_rec_copy(undo_rec, heap);
  482. mutex_exit(&(trx->undo_mutex));
  483. mtr_commit(&mtr);
  484. return(undo_rec_copy);
  485. }
  486. /************************************************************************
  487. Reserves an undo log record for a query thread to undo. This should be
  488. called if the query thread gets the undo log record not using the pop
  489. function above. */
  490. ibool
  491. trx_undo_rec_reserve(
  492. /*=================*/
  493. /* out: TRUE if succeeded */
  494. trx_t* trx, /* in: transaction */
  495. dulint undo_no)/* in: undo number of the record */
  496. {
  497. ibool ret;
  498. mutex_enter(&(trx->undo_mutex));
  499. ret = trx_undo_arr_store_info(trx, undo_no);
  500. mutex_exit(&(trx->undo_mutex));
  501. return(ret);
  502. }
  503. /***********************************************************************
  504. Releases a reserved undo record. */
  505. void
  506. trx_undo_rec_release(
  507. /*=================*/
  508. trx_t* trx, /* in: transaction */
  509. dulint undo_no)/* in: undo number */
  510. {
  511. trx_undo_arr_t* arr;
  512. mutex_enter(&(trx->undo_mutex));
  513. arr = trx->undo_no_arr;
  514. trx_undo_arr_remove_info(arr, undo_no);
  515. mutex_exit(&(trx->undo_mutex));
  516. }
  517. /*************************************************************************
  518. Starts a rollback operation. */
  519. void
  520. trx_rollback(
  521. /*=========*/
  522. trx_t* trx, /* in: transaction */
  523. trx_sig_t* sig, /* in: signal starting the rollback */
  524. que_thr_t** next_thr)/* in/out: next query thread to run;
  525. if the value which is passed in is
  526. a pointer to a NULL pointer, then the
  527. calling function can start running
  528. a new query thread; if the passed value is
  529. NULL, the parameter is ignored */
  530. {
  531. que_t* roll_graph;
  532. que_thr_t* thr;
  533. /* que_thr_t* thr2; */
  534. ut_ad(mutex_own(&kernel_mutex));
  535. ut_ad((trx->undo_no_arr == NULL) || ((trx->undo_no_arr)->n_used == 0));
  536. /* Initialize the rollback field in the transaction */
  537. if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {
  538. trx->roll_limit = ut_dulint_zero;
  539. } else if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) {
  540. trx->roll_limit = (sig->savept).least_undo_no;
  541. } else if (sig->type == TRX_SIG_ERROR_OCCURRED) {
  542. trx->roll_limit = trx->last_sql_stat_start.least_undo_no;
  543. } else {
  544. ut_error;
  545. }
  546. ut_a(ut_dulint_cmp(trx->roll_limit, trx->undo_no) <= 0);
  547. trx->pages_undone = 0;
  548. if (trx->undo_no_arr == NULL) {
  549. trx->undo_no_arr = trx_undo_arr_create();
  550. }
  551. /* Build a 'query' graph which will perform the undo operations */
  552. roll_graph = trx_roll_graph_build(trx);
  553. trx->graph = roll_graph;
  554. trx->que_state = TRX_QUE_ROLLING_BACK;
  555. thr = que_fork_start_command(roll_graph, SESS_COMM_EXECUTE, 0);
  556. ut_ad(thr);
  557. /* thr2 = que_fork_start_command(roll_graph, SESS_COMM_EXECUTE, 0);
  558. ut_ad(thr2); */
  559. if (next_thr && (*next_thr == NULL)) {
  560. *next_thr = thr;
  561. /* srv_que_task_enqueue_low(thr2); */
  562. } else {
  563. srv_que_task_enqueue_low(thr);
  564. /* srv_que_task_enqueue_low(thr2); */
  565. }
  566. }
  567. /********************************************************************
  568. Builds an undo 'query' graph for a transaction. The actual rollback is
  569. performed by executing this query graph like a query subprocedure call.
  570. The reply about the completion of the rollback will be sent by this
  571. graph. */
  572. que_t*
  573. trx_roll_graph_build(
  574. /*=================*/
  575. /* out, own: the query graph */
  576. trx_t* trx) /* in: trx handle */
  577. {
  578. mem_heap_t* heap;
  579. que_fork_t* fork;
  580. que_thr_t* thr;
  581. /* que_thr_t* thr2; */
  582. ut_ad(mutex_own(&kernel_mutex));
  583. heap = mem_heap_create(512);
  584. fork = que_fork_create(NULL, NULL, QUE_FORK_ROLLBACK, heap);
  585. fork->trx = trx;
  586. thr = que_thr_create(fork, heap);
  587. /* thr2 = que_thr_create(fork, heap); */
  588. thr->child = row_undo_node_create(trx, thr, heap);  
  589. /* thr2->child = row_undo_node_create(trx, thr2, heap); */
  590. return(fork);
  591. }
  592. /*************************************************************************
  593. Finishes error processing after the necessary partial rollback has been
  594. done. */
  595. static
  596. void
  597. trx_finish_error_processing(
  598. /*========================*/
  599. trx_t* trx) /* in: transaction */
  600. {
  601. trx_sig_t* sig;
  602. trx_sig_t* next_sig;
  603. ut_ad(mutex_own(&kernel_mutex));
  604. sig = UT_LIST_GET_FIRST(trx->signals);
  605. while (sig != NULL) {
  606. next_sig = UT_LIST_GET_NEXT(signals, sig);
  607. if (sig->type == TRX_SIG_ERROR_OCCURRED) {
  608. trx_sig_remove(trx, sig);
  609. }
  610. sig = next_sig;
  611. }
  612. trx->que_state = TRX_QUE_RUNNING;
  613. }
  614. /*************************************************************************
  615. Finishes a partial rollback operation. */
  616. static
  617. void
  618. trx_finish_partial_rollback_off_kernel(
  619. /*===================================*/
  620. trx_t* trx, /* in: transaction */
  621. que_thr_t** next_thr)/* in/out: next query thread to run;
  622. if the value which is passed in is a pointer
  623. to a NULL pointer, then the calling function
  624. can start running a new query thread; if this
  625. parameter is NULL, it is ignored */
  626. {
  627. trx_sig_t* sig;
  628. ut_ad(mutex_own(&kernel_mutex));
  629. sig = UT_LIST_GET_FIRST(trx->signals);
  630. /* Remove the signal from the signal queue and send reply message
  631. to it */
  632. trx_sig_reply(trx, sig, next_thr);
  633. trx_sig_remove(trx, sig);
  634. trx->que_state = TRX_QUE_RUNNING;
  635. }
  636. /********************************************************************
  637. Finishes a transaction rollback. */
  638. void
  639. trx_finish_rollback_off_kernel(
  640. /*===========================*/
  641. que_t* graph, /* in: undo graph which can now be freed */
  642. trx_t* trx, /* in: transaction */
  643. que_thr_t** next_thr)/* in/out: next query thread to run;
  644. if the value which is passed in is
  645. a pointer to a NULL pointer, then the
  646.     calling function can start running
  647. a new query thread; if this parameter is
  648. NULL, it is ignored */
  649. {
  650. trx_sig_t* sig;
  651. trx_sig_t* next_sig;
  652. ut_ad(mutex_own(&kernel_mutex));
  653. ut_a(trx->undo_no_arr == NULL || trx->undo_no_arr->n_used == 0);
  654. /* Free the memory reserved by the undo graph */
  655. que_graph_free(graph);
  656. sig = UT_LIST_GET_FIRST(trx->signals);
  657. if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) {
  658. trx_finish_partial_rollback_off_kernel(trx, next_thr);
  659. return;
  660. } else if (sig->type == TRX_SIG_ERROR_OCCURRED) {
  661. trx_finish_error_processing(trx);
  662. return;
  663. }
  664. if (lock_print_waits) {
  665. printf("Trx %lu rollback finishedn",
  666. ut_dulint_get_low(trx->id));
  667. }
  668. trx_commit_off_kernel(trx);
  669. /* Remove all TRX_SIG_TOTAL_ROLLBACK signals from the signal queue and
  670. send reply messages to them */
  671. trx->que_state = TRX_QUE_RUNNING;
  672. while (sig != NULL) {
  673. next_sig = UT_LIST_GET_NEXT(signals, sig);
  674. if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {
  675. trx_sig_reply(trx, sig, next_thr);
  676. trx_sig_remove(trx, sig);
  677. }
  678. sig = next_sig;
  679. }
  680. }
  681. /*************************************************************************
  682. Creates a rollback command node struct. */
  683. roll_node_t*
  684. roll_node_create(
  685. /*=============*/
  686. /* out, own: rollback node struct */
  687. mem_heap_t* heap) /* in: mem heap where created */
  688. {
  689. roll_node_t* node;
  690. node = mem_heap_alloc(heap, sizeof(roll_node_t));
  691. node->common.type = QUE_NODE_ROLLBACK;
  692. node->state = ROLL_NODE_SEND;
  693. node->partial = FALSE;
  694. return(node);
  695. }
  696. /***************************************************************
  697. Performs an execution step for a rollback command node in a query graph. */
  698. que_thr_t*
  699. trx_rollback_step(
  700. /*==============*/
  701. /* out: query thread to run next, or NULL */
  702. que_thr_t* thr) /* in: query thread */
  703. {
  704. roll_node_t* node;
  705. ibool success;
  706. ulint sig_no;
  707. trx_savept_t* savept;
  708. node = thr->run_node;
  709. ut_ad(que_node_get_type(node) == QUE_NODE_ROLLBACK);
  710. if (thr->prev_node == que_node_get_parent(node)) {
  711. node->state = ROLL_NODE_SEND;
  712. }
  713. if (node->state == ROLL_NODE_SEND) {
  714. mutex_enter(&kernel_mutex);
  715. node->state = ROLL_NODE_WAIT;
  716. if (node->partial) {
  717. sig_no = TRX_SIG_ROLLBACK_TO_SAVEPT;
  718. savept = &(node->savept);
  719. } else {
  720. sig_no = TRX_SIG_TOTAL_ROLLBACK;
  721. savept = NULL;
  722. }
  723. /* Send a rollback signal to the transaction */
  724. success = trx_sig_send(thr_get_trx(thr),
  725. sig_no, TRX_SIG_SELF,
  726. TRUE, thr, savept, NULL);
  727. thr->state = QUE_THR_SIG_REPLY_WAIT;
  728. mutex_exit(&kernel_mutex);
  729. if (!success) {
  730. /* Error in delivering the rollback signal */
  731. que_thr_handle_error(thr, DB_ERROR, NULL, 0);
  732. }
  733. return(NULL);
  734. }
  735. ut_ad(node->state == ROLL_NODE_WAIT);
  736. thr->run_node = que_node_get_parent(node);
  737. return(thr);
  738. }