/******************************************************************//** Prints information of the purge system to stderr. */ UNIV_INTERN void trx_purge_sys_print(void) /*=====================*/ { fprintf(stderr, "InnoDB: Purge system view:\n"); read_view_print(purge_sys->view); fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT ", undo n:o " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no), TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no)); fprintf(stderr, "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n" "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n", (ulong) purge_sys->next_stored, (ulong) purge_sys->page_no, (ulong) purge_sys->offset, (ulong) purge_sys->hdr_page_no, (ulong) purge_sys->hdr_offset); }
/****************************************************************//** Creates trx objects for transactions and initializes the trx list of trx_sys at database start. Rollback segment and undo log lists must already exist when this function is called, because the lists of transactions to be rolled back or cleaned up are built based on the undo log lists. */ UNIV_INTERN void trx_lists_init_at_db_start(void) /*============================*/ { trx_rseg_t* rseg; trx_undo_t* undo; trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); UT_LIST_INIT(trx_sys->trx_list); /* Look from the rollback segments if there exist undo logs for transactions */ rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); while (rseg != NULL) { undo = UT_LIST_GET_FIRST(rseg->insert_undo_list); while (undo != NULL) { trx = trx_create(trx_dummy_sess); trx->is_recovered = TRUE; trx->id = undo->trx_id; trx->xid = undo->xid; trx->insert_undo = undo; trx->rseg = rseg; if (undo->state != TRX_UNDO_ACTIVE) { /* Prepared transactions are left in the prepared state waiting for a commit or abort decision from MySQL */ if (undo->state == TRX_UNDO_PREPARED) { fprintf(stderr, "InnoDB: Transaction " TRX_ID_FMT " was in the" " XA prepared state.\n", TRX_ID_PREP_PRINTF(trx->id)); if (srv_force_recovery == 0) { trx->conc_state = TRX_PREPARED; } else { fprintf(stderr, "InnoDB: Since" " innodb_force_recovery" " > 0, we will" " rollback it" " anyway.\n"); trx->conc_state = TRX_ACTIVE; } } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; } /* We give a dummy value for the trx no; this should have no relevance since purge is not interested in committed transaction numbers, unless they are in the history list, in which case it looks the number from the disk based undo log structure */ trx->no = trx->id; } else { trx->conc_state = TRX_ACTIVE; /* A running transaction always has the number field inited to ut_dulint_max */ trx->no = ut_dulint_max; } if (undo->dict_operation) { trx_set_dict_operation( trx, TRX_DICT_OP_TABLE); trx->table_id = undo->table_id; } if (!undo->empty) { trx->undo_no = ut_dulint_add(undo->top_undo_no, 1); } trx_list_insert_ordered(trx); undo = UT_LIST_GET_NEXT(undo_list, undo); } undo = UT_LIST_GET_FIRST(rseg->update_undo_list); while (undo != NULL) { trx = trx_get_on_id(undo->trx_id); if (NULL == trx) { trx = trx_create(trx_dummy_sess); trx->is_recovered = TRUE; trx->id = undo->trx_id; trx->xid = undo->xid; if (undo->state != TRX_UNDO_ACTIVE) { /* Prepared transactions are left in the prepared state waiting for a commit or abort decision from MySQL */ if (undo->state == TRX_UNDO_PREPARED) { fprintf(stderr, "InnoDB: Transaction " TRX_ID_FMT " was in the" " XA prepared state.\n", TRX_ID_PREP_PRINTF( trx->id)); if (srv_force_recovery == 0) { trx->conc_state = TRX_PREPARED; } else { fprintf(stderr, "InnoDB: Since" " innodb_force_recovery" " > 0, we will" " rollback it" " anyway.\n"); trx->conc_state = TRX_ACTIVE; } } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; } /* We give a dummy value for the trx number */ trx->no = trx->id; } else { trx->conc_state = TRX_ACTIVE; /* A running transaction always has the number field inited to ut_dulint_max */ trx->no = ut_dulint_max; } trx->rseg = rseg; trx_list_insert_ordered(trx); if (undo->dict_operation) { trx_set_dict_operation( trx, TRX_DICT_OP_TABLE); trx->table_id = undo->table_id; } } trx->update_undo = undo; if ((!undo->empty) && (ut_dulint_cmp(undo->top_undo_no, trx->undo_no) >= 0)) { trx->undo_no = ut_dulint_add(undo->top_undo_no, 1); } undo = UT_LIST_GET_NEXT(undo_list, undo); } rseg = UT_LIST_GET_NEXT(rseg_list, rseg); } }
/**********************************************************************//** This function is used to find number of prepared transactions and their transaction objects for a recovery. @return number of prepared transactions stored in xid_list */ UNIV_INTERN int trx_recover_for_mysql( /*==================*/ XID* xid_list, /*!< in/out: prepared transactions */ ulint len) /*!< in: number of slots in xid_list */ { trx_t* trx; ulint count = 0; ut_ad(xid_list); ut_ad(len); /* We should set those transactions which are in the prepared state to the xid_list */ mutex_enter(&kernel_mutex); trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx) { if (trx->conc_state == TRX_PREPARED) { xid_list[count] = trx->xid; if (count == 0) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Starting recovery for" " XA transactions...\n"); } ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Transaction " TRX_ID_FMT " in" " prepared state after recovery\n", TRX_ID_PREP_PRINTF(trx->id)); ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Transaction contains changes" " to %lu rows\n", (ulong) ut_conv_dulint_to_longlong( trx->undo_no)); count++; if (count == len) { break; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } mutex_exit(&kernel_mutex); if (count > 0){ ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: %lu transactions in prepared state" " after recovery\n", (ulong) count); } return ((int) count); }
/**********************************************************************//** Prints info about a transaction to the given file. The caller must own the kernel mutex. */ UNIV_INTERN void trx_print( /*======*/ FILE* f, /*!< in: output stream */ trx_t* trx, /*!< in: transaction */ ulint max_query_len) /*!< in: max query length to print, or 0 to use the default max length */ { ibool newline; fprintf(f, "TRANSACTION " TRX_ID_FMT, TRX_ID_PREP_PRINTF(trx->id)); switch (trx->conc_state) { case TRX_NOT_STARTED: fputs(", not started", f); break; case TRX_ACTIVE: fprintf(f, ", ACTIVE %lu sec", (ulong)difftime(time(NULL), trx->start_time)); break; case TRX_PREPARED: fprintf(f, ", ACTIVE (PREPARED) %lu sec", (ulong)difftime(time(NULL), trx->start_time)); break; case TRX_COMMITTED_IN_MEMORY: fputs(", COMMITTED IN MEMORY", f); break; default: fprintf(f, " state %lu", (ulong) trx->conc_state); } #ifdef UNIV_LINUX fprintf(f, ", process no %lu", trx->mysql_process_no); #endif fprintf(f, ", OS thread id %lu", (ulong) os_thread_pf(trx->mysql_thread_id)); if (*trx->op_info) { putc(' ', f); fputs(trx->op_info, f); } if (trx->is_recovered) { fputs(" recovered trx", f); } if (trx->is_purge) { fputs(" purge trx", f); } if (trx->declared_to_be_inside_innodb) { fprintf(f, ", thread declared inside InnoDB %lu", (ulong) trx->n_tickets_to_enter_innodb); } putc('\n', f); if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) { fprintf(f, "mysql tables in use %lu, locked %lu\n", (ulong) trx->n_mysql_tables_in_use, (ulong) trx->mysql_n_tables_locked); } newline = TRUE; switch (trx->que_state) { case TRX_QUE_RUNNING: newline = FALSE; break; case TRX_QUE_LOCK_WAIT: fputs("LOCK WAIT ", f); break; case TRX_QUE_ROLLING_BACK: fputs("ROLLING BACK ", f); break; case TRX_QUE_COMMITTING: fputs("COMMITTING ", f); break; default: fprintf(f, "que state %lu ", (ulong) trx->que_state); } if (0 < UT_LIST_GET_LEN(trx->trx_locks) || mem_heap_get_size(trx->lock_heap) > 400) { newline = TRUE; fprintf(f, "%lu lock struct(s), heap size %lu," " %lu row lock(s)", (ulong) UT_LIST_GET_LEN(trx->trx_locks), (ulong) mem_heap_get_size(trx->lock_heap), (ulong) lock_number_of_rows_locked(trx)); } if (trx->has_search_latch) { newline = TRUE; fputs(", holds adaptive hash latch", f); } if (!ut_dulint_is_zero(trx->undo_no)) { newline = TRUE; fprintf(f, ", undo log entries %lu", (ulong) ut_dulint_get_low(trx->undo_no)); } if (newline) { putc('\n', f); } if (trx->mysql_thd != NULL) { innobase_mysql_print_thd(f, trx->mysql_thd, max_query_len); } }
/*************************************************************//** Pretty prints a dfield value according to its data type. Also the hex string is printed if a string contains non-printable characters. */ UNIV_INTERN void dfield_print_also_hex( /*==================*/ const dfield_t* dfield) /*!< in: dfield */ { const byte* data; ulint len; ulint prtype; ulint i; ibool print_also_hex; len = dfield_get_len(dfield); data = dfield_get_data(dfield); if (dfield_is_null(dfield)) { fputs("NULL", stderr); return; } prtype = dtype_get_prtype(dfield_get_type(dfield)); switch (dtype_get_mtype(dfield_get_type(dfield))) { dulint id; case DATA_INT: switch (len) { ulint val; case 1: val = mach_read_from_1(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x80; fprintf(stderr, "%ld", (long) val); } else { fprintf(stderr, "%lu", (ulong) val); } break; case 2: val = mach_read_from_2(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x8000; fprintf(stderr, "%ld", (long) val); } else { fprintf(stderr, "%lu", (ulong) val); } break; case 3: val = mach_read_from_3(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x800000; fprintf(stderr, "%ld", (long) val); } else { fprintf(stderr, "%lu", (ulong) val); } break; case 4: val = mach_read_from_4(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x80000000; fprintf(stderr, "%ld", (long) val); } else { fprintf(stderr, "%lu", (ulong) val); } break; case 6: id = mach_read_from_6(data); fprintf(stderr, "{%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; case 7: id = mach_read_from_7(data); fprintf(stderr, "{%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; case 8: id = mach_read_from_8(data); fprintf(stderr, "{%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; default: goto print_hex; } break; case DATA_SYS: switch (prtype & DATA_SYS_PRTYPE_MASK) { case DATA_TRX_ID: id = mach_read_from_6(data); fprintf(stderr, "trx_id " TRX_ID_FMT, TRX_ID_PREP_PRINTF(id)); break; case DATA_ROLL_PTR: id = mach_read_from_7(data); fprintf(stderr, "roll_ptr {%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; case DATA_ROW_ID: id = mach_read_from_6(data); fprintf(stderr, "row_id {%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; default: id = mach_dulint_read_compressed(data); fprintf(stderr, "mix_id {%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); } break; case DATA_CHAR: case DATA_VARCHAR: print_also_hex = FALSE; for (i = 0; i < len; i++) { int c = *data++; if (!isprint(c)) { print_also_hex = TRUE; fprintf(stderr, "\\x%02x", (unsigned char) c); } else { putc(c, stderr); } } if (dfield_is_ext(dfield)) { fputs("(external)", stderr); } if (!print_also_hex) { break; } data = dfield_get_data(dfield); /* fall through */ case DATA_BINARY: default: print_hex: fputs(" Hex: ",stderr); for (i = 0; i < len; i++) { fprintf(stderr, "%02lx", (ulint) *data++); } if (dfield_is_ext(dfield)) { fputs("(external)", stderr); } } }