Exemplo n.º 1
0
/********************************************************************//**
Insert a block in the flush_rbt and returns a pointer to its
predecessor or NULL if no predecessor. The ordering is maintained
on the basis of the <oldest_modification, space, offset> key.
@return pointer to the predecessor or NULL if no predecessor. */
static
buf_page_t*
buf_flush_insert_in_flush_rbt(
/*==========================*/
	buf_page_t*	bpage)		/*!< in: bpage to be inserted. */
{
	buf_page_t*		prev = NULL;
	const ib_rbt_node_t*	c_node;
	const ib_rbt_node_t*	p_node;

	ut_ad(buf_pool_mutex_own());

	/* Insert this buffer into the rbt. */
	c_node = rbt_insert(buf_pool->flush_rbt, &bpage, &bpage);
	ut_a(c_node != NULL);

	/* Get the predecessor. */
	p_node = rbt_prev(buf_pool->flush_rbt, c_node);

	if (p_node != NULL) {
		prev = *rbt_value(buf_page_t*, p_node);
		ut_a(prev != NULL);
	}
Exemplo n.º 2
0
byte*
mach_dulint_parse_compressed(
/*=========================*/
			/* out: pointer to end of the stored field, NULL if
			not complete */
	byte*   ptr,   	/* in: pointer to buffer from where to read */
	byte*	end_ptr,/* in: pointer to end of the buffer */
	dulint*	val)	/* out: read value */ 
{
	ulint	high;
	ulint	low;
	ulint	size;

	ut_ad(ptr && end_ptr && val);

	if (end_ptr < ptr + 5) {

		return(NULL);
	}

	high = mach_read_compressed(ptr);

	size = mach_get_compressed_size(high);

	ptr += size;

	if (end_ptr < ptr + 4) {

		return(NULL);
	}

	low = mach_read_from_4(ptr);

	*val = ut_dulint_create(high, low);

	return(ptr + 4); 
}
Exemplo n.º 3
0
/*********************************************************************//**
Fetches the clustered index record for a secondary index record. The latches
on the secondary index record are preserved.
@return	record or NULL, if no record found */
UNIV_INTERN
rec_t*
row_get_clust_rec(
/*==============*/
	ulint		mode,	/*!< in: BTR_MODIFY_LEAF, ... */
	const rec_t*	rec,	/*!< in: record in a secondary index */
	dict_index_t*	index,	/*!< in: secondary index */
	dict_index_t**	clust_index,/*!< out: clustered index */
	mtr_t*		mtr)	/*!< in: mtr */
{
	mem_heap_t*	heap;
	dtuple_t*	ref;
	dict_table_t*	table;
	btr_pcur_t	pcur;
	ibool		found;
	rec_t*		clust_rec;

	ut_ad(!dict_index_is_clust(index));

	table = index->table;

	heap = mem_heap_create(256);

	ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec, heap);

	found = row_search_on_row_ref(&pcur, mode, table, ref, mtr);

	clust_rec = found ? btr_pcur_get_rec(&pcur) : NULL;

	mem_heap_free(heap);

	btr_pcur_close(&pcur);

	*clust_index = dict_table_get_first_index(table);

	return(clust_rec);
}
Exemplo n.º 4
0
void
row_upd_index_replace_new_col_vals(
/*===============================*/
	dtuple_t*	entry,	/* in/out: index entry where replaced */
	dict_index_t*	index,	/* in: index; NOTE that may also be a
				non-clustered index */
	upd_t*		update)	/* in: update vector */
{
	upd_field_t*	upd_field;
	dfield_t*	dfield;
	dfield_t*	new_val;
	ulint		field_no;
	dict_index_t*	clust_index;
	ulint		i;

	ut_ad(index);

	clust_index = dict_table_get_first_index(index->table);

	dtuple_set_info_bits(entry, update->info_bits);

	for (i = 0; i < upd_get_n_fields(update); i++) {

		upd_field = upd_get_nth_field(update, i);

		field_no = dict_index_get_nth_col_pos(index,
				dict_index_get_nth_col_no(clust_index,
							upd_field->field_no));
		if (field_no != ULINT_UNDEFINED) {
			dfield = dtuple_get_nth_field(entry, field_no);

			new_val = &(upd_field->new_val);

			dfield_set_data(dfield, new_val->data, new_val->len);
		}
	}
}
Exemplo n.º 5
0
byte*
mlog_parse_initial_log_record(
/*==========================*/
			/* out: parsed record end, NULL if not a complete
			record */
	byte*	ptr,	/* in: buffer */
	byte*	end_ptr,/* in: buffer end */
	byte*	type,	/* out: log record type: MLOG_1BYTE, ... */
	ulint*	space,	/* out: space id */
	ulint*	page_no)/* out: page number */
{
	if (end_ptr < ptr + 1) {

		return(NULL);
	}

	*type = (byte)((ulint)*ptr & ~MLOG_SINGLE_REC_FLAG);
	ut_ad(*type <= MLOG_BIGGEST_TYPE);

	ptr++;

	if (end_ptr < ptr + 2) {

		return(NULL);
	}

	ptr = mach_parse_compressed(ptr, end_ptr, space);

	if (ptr == NULL) {

		return(NULL);
	}

	ptr = mach_parse_compressed(ptr, end_ptr, page_no);

	return(ptr);
}
Exemplo n.º 6
0
void
mlog_write_dulint(
/*==============*/
	byte*	ptr,	/* in: pointer where to write */
	dulint	val,	/* in: value to write */
	mtr_t*	mtr)	/* in: mini-transaction handle */
{
	byte*	log_ptr;

	if (ptr < buf_pool->frame_zero || ptr >= buf_pool->high_end) {
		fprintf(stderr,
	"InnoDB: Error: trying to write to a stray memory location %p\n", ptr);
		ut_error;
	}

	ut_ad(ptr && mtr);

	mach_write_to_8(ptr, val);

	log_ptr = mlog_open(mtr, 11 + 2 + 9);
	
	/* If no logging is requested, we may return now */
	if (log_ptr == NULL) {

		return;
	}

	log_ptr = mlog_write_initial_log_record_fast(ptr, MLOG_8BYTES,
							log_ptr, mtr);

	mach_write_to_2(log_ptr, ptr - buf_frame_align(ptr));
	log_ptr += 2;
	
	log_ptr += mach_dulint_write_compressed(log_ptr, val);

	mlog_close(mtr, log_ptr);
}
Exemplo n.º 7
0
/************************************************************************
Gets the buddy of an area, if it exists in pool. */
UNIV_INLINE
mem_area_t*
mem_area_get_buddy(
/*===============*/
				/* out: the buddy, NULL if no buddy in pool */
	mem_area_t*	area,	/* in: memory area */
	ulint		size,	/* in: memory area size */
	mem_pool_t*	pool)	/* in: memory pool */
{
	mem_area_t*	buddy;

	ut_ad(size != 0);

	if (((((byte*)area) - pool->buf) % (2 * size)) == 0) {

		/* The buddy is in a higher address */

		buddy = (mem_area_t*)(((byte*)area) + size);

		if ((((byte*)buddy) - pool->buf) + size > pool->size) {

			/* The buddy is not wholly contained in the pool:
			there is no buddy */

			buddy = NULL;
		}
	} else {
		/* The buddy is in a lower address; NOTE that area cannot
		be at the pool lower end, because then we would end up to
		the upper branch in this if-clause: the remainder would be
		0 */

		buddy = (mem_area_t*)(((byte*)area) - size);
	}

	return(buddy);
}
Exemplo n.º 8
0
void
trx_purge_sys_create(void)
/*======================*/
{
	ut_ad(mutex_own(&kernel_mutex));

	purge_sys = mem_alloc(sizeof(trx_purge_t));

	purge_sys->state = TRX_STOP_PURGE;

	purge_sys->n_pages_handled = 0;

	purge_sys->purge_trx_no = ut_dulint_zero;
	purge_sys->purge_undo_no = ut_dulint_zero;
	purge_sys->next_stored = FALSE;

	rw_lock_create(&purge_sys->latch, SYNC_PURGE_LATCH);

	mutex_create(&purge_sys->mutex, SYNC_PURGE_SYS);

	purge_sys->heap = mem_heap_create(256);

	purge_sys->arr = trx_undo_arr_create();

	purge_sys->sess = sess_open();

	purge_sys->trx = purge_sys->sess->trx;

	purge_sys->trx->is_purge = 1;

	ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));

	purge_sys->query = trx_purge_graph_build();

	purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
							    purge_sys->heap);
}
Exemplo n.º 9
0
/***********************************************************//**
Purges a delete marking of a record. */
static
void
row_purge_del_mark(
    /*===============*/
    purge_node_t*	node)	/*!< in: row purge node */
{
    mem_heap_t*	heap;
    dtuple_t*	entry;
    dict_index_t*	index;

    ut_ad(node);

    heap = mem_heap_create(1024);

    while (node->index != NULL) {
        /* skip corrupted secondary index */
        dict_table_skip_corrupt_index(node->index);

        if (!node->index) {
            break;
        }

        index = node->index;

        /* Build the index entry */
        entry = row_build_index_entry(node->row, NULL, index, heap);
        ut_a(entry);
        row_purge_remove_sec_if_poss(node, index, entry);

        node->index = dict_table_get_next_index(node->index);
    }

    mem_heap_free(heap);

    row_purge_remove_clust_if_poss(node);
}
Exemplo n.º 10
0
/********************************************************************//**
Assigns a read view for a consistent read query. All the consistent reads
within the same transaction will get the same read view, which is created
when this function is first called for a new started transaction.
@return	consistent read view */
UNIV_INTERN
read_view_t*
trx_assign_read_view(
/*=================*/
	trx_t*	trx)	/*!< in: active transaction */
{
	ut_ad(trx->conc_state == TRX_ACTIVE);

	if (trx->read_view) {
		return(trx->read_view);
	}

	mutex_enter(&kernel_mutex);

	if (!trx->read_view) {
		trx->read_view = read_view_open_now(
			trx->id, trx->global_read_view_heap);
		trx->global_read_view = trx->read_view;
	}

	mutex_exit(&kernel_mutex);

	return(trx->read_view);
}
Exemplo n.º 11
0
/******************************************************************//**
Erases an allocated memory field in the debug version. */
UNIV_INTERN
void
mem_field_erase(
/*============*/
	byte*	buf,	/*!< in: memory field */
	ulint	n __attribute__((unused)))
			/*!< in: how many bytes the user requested */
{
	byte*	usr_buf;

	usr_buf = buf + MEM_FIELD_HEADER_SIZE;

	mutex_enter(&mem_hash_mutex);
	mem_current_allocated_memory	-= n;
	mutex_exit(&mem_hash_mutex);

	/* Check that the field lengths agree */
	ut_ad(n == (ulint)mem_field_header_get_len(usr_buf));

	/* In the debug version, set the freed space to a random
	combination of 0xDE and 0xAD */

	mem_erase_buf(buf, MEM_SPACE_NEEDED(n));
}
Exemplo n.º 12
0
/********************************************************************//**
Removes unnecessary history data from rollback segments. NOTE that when this
function is called, the caller must not have any latches on undo log pages! */
static
void
trx_purge_truncate_history(void)
/*============================*/
{
    trx_rseg_t*	rseg;
    trx_id_t	limit_trx_no;
    undo_no_t	limit_undo_no;

    trx_purge_arr_get_biggest(
        purge_sys->arr, &limit_trx_no, &limit_undo_no);

    if (limit_trx_no == 0) {

        limit_trx_no = purge_sys->purge_trx_no;
        limit_undo_no = purge_sys->purge_undo_no;
    }

    /* We play safe and set the truncate limit at most to the purge view
    low_limit number, though this is not necessary */

    if (limit_trx_no >= purge_sys->view->low_limit_no) {
        limit_trx_no = purge_sys->view->low_limit_no;
        limit_undo_no = 0;
    }

    ut_ad(limit_trx_no <= purge_sys->view->low_limit_no);

    for (rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
            rseg != NULL;
            rseg = UT_LIST_GET_NEXT(rseg_list, rseg)) {

        trx_purge_truncate_rseg_history(
            rseg, limit_trx_no, limit_undo_no);
    }
}
Exemplo n.º 13
0
/**********************************************************************//**
Deallocate a buffer frame of UNIV_PAGE_SIZE. */
static
void
buf_buddy_block_free(
/*=================*/
	buf_pool_t*	buf_pool,	/*!< in: buffer pool instance */
	void*		buf)		/*!< in: buffer frame to deallocate */
{
	const ulint	fold	= BUF_POOL_ZIP_FOLD_PTR(buf);
	buf_page_t*	bpage;
	buf_block_t*	block;

	ut_ad(buf_pool_mutex_own(buf_pool));
	ut_ad(!mutex_own(&buf_pool->zip_mutex));
	ut_a(!ut_align_offset(buf, UNIV_PAGE_SIZE));

	HASH_SEARCH(hash, buf_pool->zip_hash, fold, buf_page_t*, bpage,
		    ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY
			  && bpage->in_zip_hash && !bpage->in_page_hash),
		    ((buf_block_t*) bpage)->frame == buf);
	ut_a(bpage);
	ut_a(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY);
	ut_ad(!bpage->in_page_hash);
	ut_ad(bpage->in_zip_hash);
	ut_d(bpage->in_zip_hash = FALSE);
	HASH_DELETE(buf_page_t, hash, buf_pool->zip_hash, fold, bpage);

	ut_d(memset(buf, 0, UNIV_PAGE_SIZE));
	UNIV_MEM_INVALID(buf, UNIV_PAGE_SIZE);

	block = (buf_block_t*) bpage;
	mutex_enter(&block->mutex);
	buf_LRU_block_free_non_file_page(block);
	mutex_exit(&block->mutex);

	ut_ad(buf_pool->buddy_n_frames > 0);
	ut_d(buf_pool->buddy_n_frames--);
}
Exemplo n.º 14
0
/****************************************************************//**
Creates and initializes a transaction object.
@return	own: the transaction */
UNIV_INTERN
trx_t*
trx_create(
/*=======*/
	sess_t*	sess)	/*!< in: session */
{
	trx_t*	trx;

	ut_ad(mutex_own(&kernel_mutex));
	ut_ad(sess);

	trx = mem_alloc(sizeof(trx_t));

	trx->magic_n = TRX_MAGIC_N;

	trx->op_info = "";

	trx->is_purge = 0;
	trx->is_recovered = 0;
	trx->conc_state = TRX_NOT_STARTED;
	trx->start_time = time(NULL);

	trx->isolation_level = TRX_ISO_REPEATABLE_READ;

	trx->id = ut_dulint_zero;
	trx->no = ut_dulint_max;

	trx->support_xa = TRUE;

	trx->check_foreigns = TRUE;
	trx->check_unique_secondary = TRUE;

	trx->flush_log_later = FALSE;
	trx->must_flush_log_later = FALSE;

	trx->dict_operation = TRX_DICT_OP_NONE;
	trx->table_id = ut_dulint_zero;

	trx->mysql_thd = NULL;
	trx->active_trans = 0;
	trx->duplicates = 0;

	trx->n_mysql_tables_in_use = 0;
	trx->mysql_n_tables_locked = 0;

	trx->mysql_log_file_name = NULL;
	trx->mysql_log_offset = 0;

	mutex_create(&trx->undo_mutex, SYNC_TRX_UNDO);

	trx->rseg = NULL;

	trx->undo_no = ut_dulint_zero;
	trx->last_sql_stat_start.least_undo_no = ut_dulint_zero;
	trx->insert_undo = NULL;
	trx->update_undo = NULL;
	trx->undo_no_arr = NULL;

	trx->error_state = DB_SUCCESS;
	trx->error_key_num = 0;
	trx->detailed_error[0] = '\0';

	trx->sess = sess;
	trx->que_state = TRX_QUE_RUNNING;
	trx->n_active_thrs = 0;

	trx->handling_signals = FALSE;

	UT_LIST_INIT(trx->signals);
	UT_LIST_INIT(trx->reply_signals);

	trx->graph = NULL;

	trx->wait_lock = NULL;
	trx->was_chosen_as_deadlock_victim = FALSE;
	UT_LIST_INIT(trx->wait_thrs);

	trx->lock_heap = mem_heap_create_in_buffer(256);
	UT_LIST_INIT(trx->trx_locks);

	UT_LIST_INIT(trx->trx_savepoints);

	trx->dict_operation_lock_mode = 0;
	trx->has_search_latch = FALSE;
	trx->search_latch_timeout = BTR_SEA_TIMEOUT;

	trx->declared_to_be_inside_innodb = FALSE;
	trx->n_tickets_to_enter_innodb = 0;

	trx->global_read_view_heap = mem_heap_create(256);
	trx->global_read_view = NULL;
	trx->read_view = NULL;

	/* Set X/Open XA transaction identification to NULL */
	memset(&trx->xid, 0, sizeof(trx->xid));
	trx->xid.formatID = -1;

	trx->n_autoinc_rows = 0;

	/* Remember to free the vector explicitly. */
	trx->autoinc_locks = ib_vector_create(
		mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 4), 4);

	return(trx);
}
Exemplo n.º 15
0
/****************************************************************//**
Commits a transaction. */
UNIV_INTERN
void
trx_commit_off_kernel(
/*==================*/
	trx_t*	trx)	/*!< in: transaction */
{
	page_t*		update_hdr_page;
	ib_uint64_t	lsn		= 0;
	trx_rseg_t*	rseg;
	trx_undo_t*	undo;
	mtr_t		mtr;

	ut_ad(mutex_own(&kernel_mutex));

	trx->must_flush_log_later = FALSE;

	rseg = trx->rseg;

	if (trx->insert_undo != NULL || trx->update_undo != NULL) {

		mutex_exit(&kernel_mutex);

		mtr_start(&mtr);

		/* Change the undo log segment states from TRX_UNDO_ACTIVE
		to some other state: these modifications to the file data
		structure define the transaction as committed in the file
		based world, at the serialization point of the log sequence
		number lsn obtained below. */

		mutex_enter(&(rseg->mutex));

		if (trx->insert_undo != NULL) {
			trx_undo_set_state_at_finish(
				rseg, trx, trx->insert_undo, &mtr);
		}

		undo = trx->update_undo;

		if (undo) {
			mutex_enter(&kernel_mutex);
			trx->no = trx_sys_get_new_trx_no();

			mutex_exit(&kernel_mutex);

			/* It is not necessary to obtain trx->undo_mutex here
			because only a single OS thread is allowed to do the
			transaction commit for this transaction. */

			update_hdr_page = trx_undo_set_state_at_finish(
				rseg, trx, undo, &mtr);

			/* We have to do the cleanup for the update log while
			holding the rseg mutex because update log headers
			have to be put to the history list in the order of
			the trx number. */

			trx_undo_update_cleanup(trx, update_hdr_page, &mtr);
		}

		mutex_exit(&(rseg->mutex));

		/* Update the latest MySQL binlog name and offset info
		in trx sys header if MySQL binlogging is on or the database
		server is a MySQL replication slave */

		if (trx->mysql_log_file_name
		    && trx->mysql_log_file_name[0] != '\0') {
			trx_sys_update_mysql_binlog_offset(
				trx->mysql_log_file_name,
				trx->mysql_log_offset,
				TRX_SYS_MYSQL_LOG_INFO, &mtr);
			trx->mysql_log_file_name = NULL;
		}

		/* The following call commits the mini-transaction, making the
		whole transaction committed in the file-based world, at this
		log sequence number. The transaction becomes 'durable' when
		we write the log to disk, but in the logical sense the commit
		in the file-based data structures (undo logs etc.) happens
		here.

		NOTE that transaction numbers, which are assigned only to
		transactions with an update undo log, do not necessarily come
		in exactly the same order as commit lsn's, if the transactions
		have different rollback segments. To get exactly the same
		order we should hold the kernel mutex up to this point,
		adding to the contention of the kernel mutex. However, if
		a transaction T2 is able to see modifications made by
		a transaction T1, T2 will always get a bigger transaction
		number and a bigger commit lsn than T1. */

		/*--------------*/
		mtr_commit(&mtr);
		/*--------------*/
		lsn = mtr.end_lsn;

		mutex_enter(&kernel_mutex);
	}

	ut_ad(trx->conc_state == TRX_ACTIVE
	      || trx->conc_state == TRX_PREPARED);
	ut_ad(mutex_own(&kernel_mutex));

	/* The following assignment makes the transaction committed in memory
	and makes its changes to data visible to other transactions.
	NOTE that there is a small discrepancy from the strict formal
	visibility rules here: a human user of the database can see
	modifications made by another transaction T even before the necessary
	log segment has been flushed to the disk. If the database happens to
	crash before the flush, the user has seen modifications from T which
	will never be a committed transaction. However, any transaction T2
	which sees the modifications of the committing transaction T, and
	which also itself makes modifications to the database, will get an lsn
	larger than the committing transaction T. In the case where the log
	flush fails, and T never gets committed, also T2 will never get
	committed. */

	/*--------------------------------------*/
	trx->conc_state = TRX_COMMITTED_IN_MEMORY;
	/*--------------------------------------*/

	/* If we release kernel_mutex below and we are still doing
	recovery i.e.: back ground rollback thread is still active
	then there is a chance that the rollback thread may see
	this trx as COMMITTED_IN_MEMORY and goes adhead to clean it
	up calling trx_cleanup_at_db_startup(). This can happen
	in the case we are committing a trx here that is left in
	PREPARED state during the crash. Note that commit of the
	rollback of a PREPARED trx happens in the recovery thread
	while the rollback of other transactions happen in the
	background thread. To avoid this race we unconditionally
	unset the is_recovered flag from the trx. */

	trx->is_recovered = FALSE;

	lock_release_off_kernel(trx);

	if (trx->global_read_view) {
		read_view_close(trx->global_read_view);
		mem_heap_empty(trx->global_read_view_heap);
		trx->global_read_view = NULL;
	}

	trx->read_view = NULL;

	if (lsn) {

		mutex_exit(&kernel_mutex);

		if (trx->insert_undo != NULL) {

			trx_undo_insert_cleanup(trx);
		}

		/* NOTE that we could possibly make a group commit more
		efficient here: call os_thread_yield here to allow also other
		trxs to come to commit! */

		/*-------------------------------------*/

		/* Depending on the my.cnf options, we may now write the log
		buffer to the log files, making the transaction durable if
		the OS does not crash. We may also flush the log files to
		disk, making the transaction durable also at an OS crash or a
		power outage.

		The idea in InnoDB's group commit is that a group of
		transactions gather behind a trx doing a physical disk write
		to log files, and when that physical write has been completed,
		one of those transactions does a write which commits the whole
		group. Note that this group commit will only bring benefit if
		there are > 2 users in the database. Then at least 2 users can
		gather behind one doing the physical log write to disk.

		If we are calling trx_commit() under prepare_commit_mutex, we
		will delay possible log write and flush to a separate function
		trx_commit_complete_for_mysql(), which is only called when the
		thread has released the mutex. This is to make the
		group commit algorithm to work. Otherwise, the prepare_commit
		mutex would serialize all commits and prevent a group of
		transactions from gathering. */

		if (trx->flush_log_later) {
			/* Do nothing yet */
			trx->must_flush_log_later = TRUE;
		} else if (srv_flush_log_at_trx_commit == 0) {
			/* Do nothing */
		} else if (srv_flush_log_at_trx_commit == 1) {
			if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
				/* Write the log but do not flush it to disk */

				log_write_up_to(lsn, LOG_WAIT_ONE_GROUP,
						FALSE);
			} else {
				/* Write the log to the log files AND flush
				them to disk */

				log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
			}
		} else if (srv_flush_log_at_trx_commit == 2) {

			/* Write the log but do not flush it to disk */

			log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
		} else {
			ut_error;
		}

		trx->commit_lsn = lsn;

		/*-------------------------------------*/

		mutex_enter(&kernel_mutex);
	}

	/* Free all savepoints */
	trx_roll_free_all_savepoints(trx);

	trx->conc_state = TRX_NOT_STARTED;
	trx->rseg = NULL;
	trx->undo_no = ut_dulint_zero;
	trx->last_sql_stat_start.least_undo_no = ut_dulint_zero;

	ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
	ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0);

	UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx);
}
Exemplo n.º 16
0
/****************************************************************//**
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);
	}
}
Exemplo n.º 17
0
/********************************************************************//**
Frees a transaction object. */
UNIV_INTERN
void
trx_free(
/*=====*/
	trx_t*	trx)	/*!< in, own: trx object */
{
	ut_ad(mutex_own(&kernel_mutex));

	if (trx->declared_to_be_inside_innodb) {
		ut_print_timestamp(stderr);
		fputs("  InnoDB: Error: Freeing a trx which is declared"
		      " to be processing\n"
		      "InnoDB: inside InnoDB.\n", stderr);
		trx_print(stderr, trx, 600);
		putc('\n', stderr);

		/* This is an error but not a fatal error. We must keep
		the counters like srv_conc_n_threads accurate. */
		srv_conc_force_exit_innodb(trx);
	}

	if (trx->n_mysql_tables_in_use != 0
	    || trx->mysql_n_tables_locked != 0) {

		ut_print_timestamp(stderr);
		fprintf(stderr,
			"  InnoDB: Error: MySQL is freeing a thd\n"
			"InnoDB: though trx->n_mysql_tables_in_use is %lu\n"
			"InnoDB: and trx->mysql_n_tables_locked is %lu.\n",
			(ulong)trx->n_mysql_tables_in_use,
			(ulong)trx->mysql_n_tables_locked);

		trx_print(stderr, trx, 600);

		ut_print_buf(stderr, trx, sizeof(trx_t));
		putc('\n', stderr);
	}

	ut_a(trx->magic_n == TRX_MAGIC_N);

	trx->magic_n = 11112222;

	ut_a(trx->conc_state == TRX_NOT_STARTED);

	mutex_free(&(trx->undo_mutex));

	ut_a(trx->insert_undo == NULL);
	ut_a(trx->update_undo == NULL);

	if (trx->undo_no_arr) {
		trx_undo_arr_free(trx->undo_no_arr);
	}

	ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
	ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0);

	ut_a(trx->wait_lock == NULL);
	ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0);

	ut_a(!trx->has_search_latch);

	ut_a(trx->dict_operation_lock_mode == 0);

	if (trx->lock_heap) {
		mem_heap_free(trx->lock_heap);
	}

	ut_a(UT_LIST_GET_LEN(trx->trx_locks) == 0);

	if (trx->global_read_view_heap) {
		mem_heap_free(trx->global_read_view_heap);
	}

	trx->global_read_view = NULL;

	ut_a(trx->read_view == NULL);

	ut_a(ib_vector_is_empty(trx->autoinc_locks));
	/* We allocated a dedicated heap for the vector. */
	ib_vector_free(trx->autoinc_locks);

	mem_free(trx);
}
Exemplo n.º 18
0
/********************************************************//**
Opens a buffer for mlog, writes the initial log record and,
if needed, the field lengths of an index.
@return	buffer, NULL if log mode MTR_LOG_NONE */
UNIV_INTERN
byte*
mlog_open_and_write_index(
/*======================*/
	mtr_t*		mtr,	/*!< in: mtr */
	const byte*	rec,	/*!< in: index record or page */
	dict_index_t*	index,	/*!< in: record descriptor */
	byte		type,	/*!< in: log item type */
	ulint		size)	/*!< in: requested buffer size in bytes
				(if 0, calls mlog_close() and returns NULL) */
{
	byte*		log_ptr;
	const byte*	log_start;
	const byte*	log_end;

	ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table));

	if (!page_rec_is_comp(rec)) {
		log_start = log_ptr = mlog_open(mtr, 11 + size);
		if (!log_ptr) {
			return(NULL); /* logging is disabled */
		}
		log_ptr = mlog_write_initial_log_record_fast(rec, type,
							     log_ptr, mtr);
		log_end = log_ptr + 11 + size;
	} else {
		ulint	i;
		ulint	n	= dict_index_get_n_fields(index);
		/* total size needed */
		ulint	total	= 11 + size + (n + 2) * 2;
		ulint	alloc	= total;
		/* allocate at most DYN_ARRAY_DATA_SIZE at a time */
		if (alloc > DYN_ARRAY_DATA_SIZE) {
			alloc = DYN_ARRAY_DATA_SIZE;
		}
		log_start = log_ptr = mlog_open(mtr, alloc);
		if (!log_ptr) {
			return(NULL); /* logging is disabled */
		}
		log_end = log_ptr + alloc;
		log_ptr = mlog_write_initial_log_record_fast(rec, type,
							     log_ptr, mtr);
		mach_write_to_2(log_ptr, n);
		log_ptr += 2;
		mach_write_to_2(log_ptr,
				dict_index_get_n_unique_in_tree(index));
		log_ptr += 2;
		for (i = 0; i < n; i++) {
			dict_field_t*		field;
			const dict_col_t*	col;
			ulint			len;

			field = dict_index_get_nth_field(index, i);
			col = dict_field_get_col(field);
			len = field->fixed_len;
			ut_ad(len < 0x7fff);
			if (len == 0
			    && (col->len > 255 || col->mtype == DATA_BLOB)) {
				/* variable-length field
				with maximum length > 255 */
				len = 0x7fff;
			}
			if (col->prtype & DATA_NOT_NULL) {
				len |= 0x8000;
			}
			if (log_ptr + 2 > log_end) {
				mlog_close(mtr, log_ptr);
				ut_a(total > (ulint) (log_ptr - log_start));
				total -= log_ptr - log_start;
				alloc = total;
				if (alloc > DYN_ARRAY_DATA_SIZE) {
					alloc = DYN_ARRAY_DATA_SIZE;
				}
				log_start = log_ptr = mlog_open(mtr, alloc);
				if (!log_ptr) {
					return(NULL); /* logging is disabled */
				}
				log_end = log_ptr + alloc;
			}
			mach_write_to_2(log_ptr, len);
			log_ptr += 2;
		}
	}
	if (size == 0) {
		mlog_close(mtr, log_ptr);
		log_ptr = NULL;
	} else if (log_ptr + size > log_end) {
		mlog_close(mtr, log_ptr);
		log_ptr = mlog_open(mtr, size);
	}
	return(log_ptr);
}
Exemplo n.º 19
0
/********************************************************************//**
Adds the update undo log as the first log in the history list. Removes the
update undo log segment from the rseg slot if it is too big for reuse. */
UNIV_INTERN
void
trx_purge_add_update_undo_to_history(
    /*=================================*/
    trx_t*	trx,		/*!< in: transaction */
    page_t*	undo_page,	/*!< in: update undo log header page,
				x-latched */
    mtr_t*	mtr)		/*!< in: mtr */
{
    trx_undo_t*	undo;
    trx_rsegf_t*	rseg_header;
    trx_ulogf_t*	undo_header;

    undo = trx->update_undo;

    ut_ad(undo);

    ut_ad(mutex_own(&undo->rseg->mutex));

    rseg_header = trx_rsegf_get(
                      undo->rseg->space, undo->rseg->zip_size, undo->rseg->page_no,
                      mtr);

    undo_header = undo_page + undo->hdr_offset;
    /* Add the log as the first in the history list */

    if (undo->state != TRX_UNDO_CACHED) {
        ulint		hist_size;
#ifdef UNIV_DEBUG
        trx_usegf_t*	seg_header = undo_page + TRX_UNDO_SEG_HDR;
#endif /* UNIV_DEBUG */

        /* The undo log segment will not be reused */

        if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) {
            fprintf(stderr,
                    "InnoDB: Error: undo->id is %lu\n",
                    (ulong) undo->id);
            ut_error;
        }

        trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);

        hist_size = mtr_read_ulint(
                        rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr);

        ut_ad(undo->size == flst_get_len(
                  seg_header + TRX_UNDO_PAGE_LIST, mtr));

        mlog_write_ulint(
            rseg_header + TRX_RSEG_HISTORY_SIZE,
            hist_size + undo->size, MLOG_4BYTES, mtr);
    }

    flst_add_first(
        rseg_header + TRX_RSEG_HISTORY,
        undo_header + TRX_UNDO_HISTORY_NODE, mtr);

    /* Write the trx number to the undo log header */

    mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);

    /* Write information about delete markings to the undo log header */

    if (!undo->del_marks) {
        mlog_write_ulint(
            undo_header + TRX_UNDO_DEL_MARKS, FALSE,
            MLOG_2BYTES, mtr);
    }

    if (undo->rseg->last_page_no == FIL_NULL) {
        undo->rseg->last_trx_no = trx->no;
        undo->rseg->last_offset = undo->hdr_offset;
        undo->rseg->last_page_no = undo->hdr_page_no;
        undo->rseg->last_del_marks = undo->del_marks;

        /* FIXME: Add a bin heap validate function to check that
        the rseg exists. */
    }

    mutex_enter(&kernel_mutex);
    trx_sys->rseg_history_len++;
    mutex_exit(&kernel_mutex);

//	if (!(trx_sys->rseg_history_len % srv_purge_batch_size)) { /*should wake up always*/
    /* Inform the purge thread that there is work to do. */
    srv_wake_purge_thread_if_not_active();
//	}
}
Exemplo n.º 20
0
/********************************************************************//**
Fetches the next undo log record from the history list to purge. It must be
released with the corresponding release function.
@return copy of an undo log record or pointer to trx_purge_dummy_rec,
if the whole undo log can skipped in purge; NULL if none left */
UNIV_INTERN
trx_undo_rec_t*
trx_purge_fetch_next_rec(
    /*=====================*/
    roll_ptr_t*	roll_ptr,/*!< out: roll pointer to undo record */
    trx_undo_inf_t** cell,	/*!< out: storage cell for the record in the
				purge array */
    mem_heap_t*	heap)	/*!< in: memory heap where copied */
{
    trx_undo_rec_t*	undo_rec;


    if (purge_sys->state == TRX_STOP_PURGE) {
        trx_purge_truncate_if_arr_empty();

        return(NULL);
    } else if (!purge_sys->next_stored) {
        trx_purge_choose_next_log();

        if (!purge_sys->next_stored) {
            purge_sys->state = TRX_STOP_PURGE;

            trx_purge_truncate_if_arr_empty();

            if (srv_print_thread_releases) {
                fprintf(stderr,
                        "Purge: No logs left in the"
                        " history list; pages handled %lu\n",
                        (ulong) purge_sys->n_pages_handled);
            }

            return(NULL);
        }
    }

    if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {

        purge_sys->state = TRX_STOP_PURGE;

        trx_purge_truncate_if_arr_empty();

        return(NULL);
    } else if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) {
        purge_sys->state = TRX_STOP_PURGE;

        trx_purge_truncate_if_arr_empty();

        return(NULL);
    }

    /* fprintf(stderr, "Thread %lu purging trx %llu undo record %llu\n",
    os_thread_get_curr_id(),
    (ullint) purge_sys->purge_trx_no,
    (ullint) purge_sys->purge_undo_no); */


    *roll_ptr = trx_undo_build_roll_ptr(
                    FALSE, (purge_sys->rseg)->id, purge_sys->page_no,
                    purge_sys->offset);

    *cell = trx_purge_arr_store_info(
                purge_sys->purge_trx_no, purge_sys->purge_undo_no);

    ut_ad(purge_sys->purge_trx_no < purge_sys->view->low_limit_no);

    /* The following call will advance the stored values of purge_trx_no
    and purge_undo_no, therefore we had to store them first */

    undo_rec = trx_purge_get_next_rec(heap);

    return(undo_rec);
}
Exemplo n.º 21
0
/******************************************************************//**
Creates, or rather, initializes a mutex object in a specified memory
location (which must be appropriately aligned). The mutex is initialized
in the reset state. Explicit freeing of the mutex with mutex_free is
necessary only if the memory block containing it is freed. */
UNIV_INTERN
void
mutex_create_func(
/*==============*/
	mutex_t*	mutex,		/*!< in: pointer to memory */
#ifdef UNIV_DEBUG
	const char*	cmutex_name,	/*!< in: mutex name */
# ifdef UNIV_SYNC_DEBUG
	ulint		level,		/*!< in: level */
# endif /* UNIV_SYNC_DEBUG */
#endif /* UNIV_DEBUG */
	const char*	cfile_name,	/*!< in: file name where created */
	ulint		cline)		/*!< in: file line where created */
{
#if defined(HAVE_ATOMIC_BUILTINS)
	mutex_reset_lock_word(mutex);
#else
	os_fast_mutex_init(&(mutex->os_fast_mutex));
	mutex->lock_word = 0;
#endif
	mutex->event = os_event_create(NULL);
	mutex_set_waiters(mutex, 0);
#ifdef UNIV_DEBUG
	mutex->magic_n = MUTEX_MAGIC_N;
#endif /* UNIV_DEBUG */
#ifdef UNIV_SYNC_DEBUG
	mutex->line = 0;
	mutex->file_name = "not yet reserved";
	mutex->level = level;
#endif /* UNIV_SYNC_DEBUG */
	mutex->cfile_name = cfile_name;
	mutex->cline = cline;
	mutex->count_os_wait = 0;
#ifdef UNIV_DEBUG
	mutex->cmutex_name=	  cmutex_name;
	mutex->count_using=	  0;
	mutex->mutex_type=	  0;
	mutex->lspent_time=	  0;
	mutex->lmax_spent_time=     0;
	mutex->count_spin_loop= 0;
	mutex->count_spin_rounds=   0;
	mutex->count_os_yield=  0;
#endif /* UNIV_DEBUG */

	/* Check that lock_word is aligned; this is important on Intel */
	ut_ad(((ulint)(&(mutex->lock_word))) % 4 == 0);

	/* NOTE! The very first mutexes are not put to the mutex list */

	if ((mutex == &mutex_list_mutex)
#ifdef UNIV_SYNC_DEBUG
	    || (mutex == &sync_thread_mutex)
#endif /* UNIV_SYNC_DEBUG */
	    ) {

		return;
	}

	mutex_enter(&mutex_list_mutex);

	ut_ad(UT_LIST_GET_LEN(mutex_list) == 0
	      || UT_LIST_GET_FIRST(mutex_list)->magic_n == MUTEX_MAGIC_N);

	UT_LIST_ADD_FIRST(list, mutex_list, mutex);

	mutex_exit(&mutex_list_mutex);
}
Exemplo n.º 22
0
/*****************************************************************//**
Checks the compatibility of a new signal with the other signals in the
queue.
@return	TRUE if the signal can be queued */
static
ibool
trx_sig_is_compatible(
/*==================*/
	trx_t*	trx,	/*!< in: trx handle */
	ulint	type,	/*!< in: signal type */
	ulint	sender)	/*!< in: TRX_SIG_SELF or TRX_SIG_OTHER_SESS */
{
	trx_sig_t*	sig;

	ut_ad(mutex_own(&kernel_mutex));

	if (UT_LIST_GET_LEN(trx->signals) == 0) {

		return(TRUE);
	}

	if (sender == TRX_SIG_SELF) {
		if (type == TRX_SIG_ERROR_OCCURRED) {

			return(TRUE);

		} else if (type == TRX_SIG_BREAK_EXECUTION) {

			return(TRUE);
		} else {
			return(FALSE);
		}
	}

	ut_ad(sender == TRX_SIG_OTHER_SESS);

	sig = UT_LIST_GET_FIRST(trx->signals);

	if (type == TRX_SIG_COMMIT) {
		while (sig != NULL) {

			if (sig->type == TRX_SIG_TOTAL_ROLLBACK) {

				return(FALSE);
			}

			sig = UT_LIST_GET_NEXT(signals, sig);
		}

		return(TRUE);

	} else if (type == TRX_SIG_TOTAL_ROLLBACK) {
		while (sig != NULL) {

			if (sig->type == TRX_SIG_COMMIT) {

				return(FALSE);
			}

			sig = UT_LIST_GET_NEXT(signals, sig);
		}

		return(TRUE);

	} else if (type == TRX_SIG_BREAK_EXECUTION) {

		return(TRUE);
	} else {
		ut_error;

		return(FALSE);
	}
}
Exemplo n.º 23
0
/*******************************************************************//**
This function runs a purge batch.
@return	number of undo log pages handled in the batch */
UNIV_INTERN
ulint
trx_purge(
    /*======*/
    ulint	limit)		/*!< in: the maximum number of records to
				purge in one batch */
{
    que_thr_t*	thr;
    ulint		old_pages_handled;

    if (srv_fake_write)
        return(0);

    ut_a(purge_sys->trx->n_active_thrs == 0);

    rw_lock_x_lock(&purge_sys->latch);

    mutex_enter(&kernel_mutex);

    /* Close and free the old purge view */

    read_view_close(purge_sys->view);
    purge_sys->view = NULL;
    mem_heap_empty(purge_sys->heap);

    /* Determine how much data manipulation language (DML) statements
    need to be delayed in order to reduce the lagging of the purge
    thread. */
    srv_dml_needed_delay = 0; /* in microseconds; default: no delay */

    /* If we cannot advance the 'purge view' because of an old
    'consistent read view', then the DML statements cannot be delayed.
    Also, srv_max_purge_lag <= 0 means 'infinity'. */
    if (srv_max_purge_lag > 0) {
        float	ratio = (float) trx_sys->rseg_history_len
                        / srv_max_purge_lag;
        if (ratio > ULINT_MAX / 10000) {
            /* Avoid overflow: maximum delay is 4295 seconds */
            srv_dml_needed_delay = ULINT_MAX;
        } else if (ratio > 1) {
            /* If the history list length exceeds the
            innodb_max_purge_lag, the
            data manipulation statements are delayed
            by at least 5000 microseconds. */
            srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
        }
    }

    purge_sys->view = read_view_oldest_copy_or_open_new(
                          0, purge_sys->heap);

    mutex_exit(&kernel_mutex);

    rw_lock_x_unlock(&(purge_sys->latch));

    purge_sys->state = TRX_PURGE_ON;

    purge_sys->handle_limit = purge_sys->n_pages_handled + limit;

    old_pages_handled = purge_sys->n_pages_handled;


    mutex_enter(&kernel_mutex);

    thr = que_fork_start_command(purge_sys->query);

    ut_ad(thr);

    mutex_exit(&kernel_mutex);

    if (srv_print_thread_releases) {

        fputs("Starting purge\n", stderr);
    }

    que_run_threads(thr);

    if (srv_print_thread_releases) {

        fprintf(stderr,
                "Purge ends; pages handled %lu\n",
                (ulong) purge_sys->n_pages_handled);
    }

    return(purge_sys->n_pages_handled - old_pages_handled);
}
Exemplo n.º 24
0
/****************************************************************//**
Sends a signal to a trx object. */
UNIV_INTERN
void
trx_sig_send(
/*=========*/
	trx_t*		trx,		/*!< in: trx handle */
	ulint		type,		/*!< in: signal type */
	ulint		sender,		/*!< in: TRX_SIG_SELF or
					TRX_SIG_OTHER_SESS */
	que_thr_t*	receiver_thr,	/*!< in: query thread which wants the
					reply, or NULL; if type is
					TRX_SIG_END_WAIT, this must be NULL */
	trx_savept_t*	savept,		/*!< in: possible rollback savepoint, or
					NULL */
	que_thr_t**	next_thr)	/*!< in/out: next query thread to run;
					if the value which is passed in is
					a pointer to a NULL pointer, then the
					calling function can start running
					a new query thread; if the parameter
					is NULL, it is ignored */
{
	trx_sig_t*	sig;
	trx_t*		receiver_trx;

	ut_ad(trx);
	ut_ad(mutex_own(&kernel_mutex));

	if (!trx_sig_is_compatible(trx, type, sender)) {
		/* The signal is not compatible with the other signals in
		the queue: die */

		ut_error;
	}

	/* Queue the signal object */

	if (UT_LIST_GET_LEN(trx->signals) == 0) {

		/* The signal list is empty: the 'sig' slot must be unused
		(we improve performance a bit by avoiding mem_alloc) */
		sig = &(trx->sig);
	} else {
		/* It might be that the 'sig' slot is unused also in this
		case, but we choose the easy way of using mem_alloc */

		sig = mem_alloc(sizeof(trx_sig_t));
	}

	UT_LIST_ADD_LAST(signals, trx->signals, sig);

	sig->type = type;
	sig->sender = sender;
	sig->receiver = receiver_thr;

	if (savept) {
		sig->savept = *savept;
	}

	if (receiver_thr) {
		receiver_trx = thr_get_trx(receiver_thr);

		UT_LIST_ADD_LAST(reply_signals, receiver_trx->reply_signals,
				 sig);
	}

	if (trx->sess->state == SESS_ERROR) {

		trx_sig_reply_wait_to_suspended(trx);
	}

	if ((sender != TRX_SIG_SELF) || (type == TRX_SIG_BREAK_EXECUTION)) {
		ut_error;
	}

	/* If there were no other signals ahead in the queue, try to start
	handling of the signal */

	if (UT_LIST_GET_FIRST(trx->signals) == sig) {

		trx_sig_start_handle(trx, next_thr);
	}
}
Exemplo n.º 25
0
/**********************************************************************//**
Frees an undo log segment which is in the history list. Cuts the end of the
history list at the youngest undo log in this segment. */
static
void
trx_purge_free_segment(
    /*===================*/
    trx_rseg_t*	rseg,		/*!< in: rollback segment */
    fil_addr_t	hdr_addr,	/*!< in: the file address of log_hdr */
    ulint		n_removed_logs)	/*!< in: count of how many undo logs we
					will cut off from the end of the
					history list */
{
    page_t*		undo_page;
    trx_rsegf_t*	rseg_hdr;
    trx_ulogf_t*	log_hdr;
    trx_usegf_t*	seg_hdr;
    ibool		freed;
    ulint		seg_size;
    ulint		hist_size;
    ibool		marked		= FALSE;
    mtr_t		mtr;

    /*	fputs("Freeing an update undo log segment\n", stderr); */

loop:
    mtr_start(&mtr);
    mutex_enter(&(rseg->mutex));

    rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
                             rseg->page_no, &mtr);

    undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
                                  hdr_addr.page, &mtr);
    seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
    log_hdr = undo_page + hdr_addr.boffset;

    /* Mark the last undo log totally purged, so that if the system
    crashes, the tail of the undo log will not get accessed again. The
    list of pages in the undo log tail gets inconsistent during the
    freeing of the segment, and therefore purge should not try to access
    them again. */

    if (!marked) {
        mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
                         MLOG_2BYTES, &mtr);
        marked = TRUE;
    }

    freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
                                      &mtr);
    if (!freed) {
        mutex_exit(&(rseg->mutex));
        mtr_commit(&mtr);

        goto loop;
    }

    /* The page list may now be inconsistent, but the length field
    stored in the list base node tells us how big it was before we
    started the freeing. */

    seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);

    /* We may free the undo log segment header page; it must be freed
    within the same mtr as the undo log header is removed from the
    history list: otherwise, in case of a database crash, the segment
    could become inaccessible garbage in the file space. */

    flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
                 log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);

    mutex_enter(&kernel_mutex);
    ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
    trx_sys->rseg_history_len -= n_removed_logs;
    mutex_exit(&kernel_mutex);

    freed = FALSE;

    while (!freed) {
        /* Here we assume that a file segment with just the header
        page can be freed in a few steps, so that the buffer pool
        is not flooded with bufferfixed pages: see the note in
        fsp0fsp.c. */

        freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
                               &mtr);
    }

    hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
                               MLOG_4BYTES, &mtr);
    ut_ad(hist_size >= seg_size);

    mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
                     hist_size - seg_size, MLOG_4BYTES, &mtr);

    ut_ad(rseg->curr_size >= seg_size);

    rseg->curr_size -= seg_size;

    mutex_exit(&(rseg->mutex));

    mtr_commit(&mtr);
}
Exemplo n.º 26
0
/****************************************************************//**
Starts handling of a trx signal. */
UNIV_INTERN
void
trx_sig_start_handle(
/*=================*/
	trx_t*		trx,		/*!< in: trx handle */
	que_thr_t**	next_thr)	/*!< in/out: next query thread to run;
					if the value which is passed in is
					a pointer to a NULL pointer, then the
					calling function can start running
					a new query thread; if the parameter
					is NULL, it is ignored */
{
	trx_sig_t*	sig;
	ulint		type;
loop:
	/* We loop in this function body as long as there are queued signals
	we can process immediately */

	ut_ad(trx);
	ut_ad(mutex_own(&kernel_mutex));

	if (trx->handling_signals && (UT_LIST_GET_LEN(trx->signals) == 0)) {

		trx_end_signal_handling(trx);

		return;
	}

	if (trx->conc_state == TRX_NOT_STARTED) {

		trx_start_low(trx, ULINT_UNDEFINED);
	}

	/* If the trx is in a lock wait state, moves the waiting query threads
	to the suspended state */

	if (trx->que_state == TRX_QUE_LOCK_WAIT) {

		trx_lock_wait_to_suspended(trx);
	}

	/* If the session is in the error state and this trx has threads
	waiting for reply from signals, moves these threads to the suspended
	state, canceling wait reservations; note that if the transaction has
	sent a commit or rollback signal to itself, and its session is not in
	the error state, then nothing is done here. */

	if (trx->sess->state == SESS_ERROR) {
		trx_sig_reply_wait_to_suspended(trx);
	}

	/* If there are no running query threads, we can start processing of a
	signal, otherwise we have to wait until all query threads of this
	transaction are aware of the arrival of the signal. */

	if (trx->n_active_thrs > 0) {

		return;
	}

	if (trx->handling_signals == FALSE) {
		trx->graph_before_signal_handling = trx->graph;

		trx->handling_signals = TRUE;
	}

	sig = UT_LIST_GET_FIRST(trx->signals);
	type = sig->type;

	if (type == TRX_SIG_COMMIT) {

		trx_handle_commit_sig_off_kernel(trx, next_thr);

	} else if ((type == TRX_SIG_TOTAL_ROLLBACK)
		   || (type == TRX_SIG_ROLLBACK_TO_SAVEPT)) {

		trx_rollback(trx, sig, next_thr);

		/* No further signals can be handled until the rollback
		completes, therefore we return */

		return;

	} else if (type == TRX_SIG_ERROR_OCCURRED) {

		trx_rollback(trx, sig, next_thr);

		/* No further signals can be handled until the rollback
		completes, therefore we return */

		return;

	} else if (type == TRX_SIG_BREAK_EXECUTION) {

		trx_sig_reply(sig, next_thr);
		trx_sig_remove(trx, sig);
	} else {
		ut_error;
	}

	goto loop;
}
Exemplo n.º 27
0
/***********************************************************************//**
Gets the next record to purge and updates the info in the purge system.
@return	copy of an undo log record or pointer to the dummy undo log record */
static
trx_undo_rec_t*
trx_purge_get_next_rec(
    /*===================*/
    mem_heap_t*	heap)	/*!< in: memory heap where copied */
{
    trx_undo_rec_t*	rec;
    trx_undo_rec_t*	rec_copy;
    trx_undo_rec_t*	rec2;
    trx_undo_rec_t*	next_rec;
    page_t*		undo_page;
    page_t*		page;
    ulint		offset;
    ulint		page_no;
    ulint		space;
    ulint		zip_size;
    ulint		type;
    ulint		cmpl_info;
    mtr_t		mtr;

    ut_ad(purge_sys->next_stored);

    space = purge_sys->rseg->space;
    zip_size = purge_sys->rseg->zip_size;
    page_no = purge_sys->page_no;
    offset = purge_sys->offset;

    if (offset == 0) {
        /* It is the dummy undo log record, which means that there is
        no need to purge this undo log */

        trx_purge_rseg_get_next_history_log(purge_sys->rseg);

        /* Look for the next undo log and record to purge */

        trx_purge_choose_next_log();

        return(&trx_purge_dummy_rec);
    }

    mtr_start(&mtr);

    undo_page = trx_undo_page_get_s_latched(space, zip_size, page_no, &mtr);

    rec = undo_page + offset;

    rec2 = rec;

    for (;;) {
        /* Try first to find the next record which requires a purge
        operation from the same page of the same undo log */

        next_rec = trx_undo_page_get_next_rec(
                       rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset);

        if (next_rec == NULL) {
            rec2 = trx_undo_get_next_rec(
                       rec2, purge_sys->hdr_page_no,
                       purge_sys->hdr_offset, &mtr);
            break;
        }

        rec2 = next_rec;

        type = trx_undo_rec_get_type(rec2);

        if (type == TRX_UNDO_DEL_MARK_REC) {

            break;
        }

        cmpl_info = trx_undo_rec_get_cmpl_info(rec2);

        if (trx_undo_rec_get_extern_storage(rec2)) {
            break;
        }

        if ((type == TRX_UNDO_UPD_EXIST_REC)
                && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
            break;
        }
    }

    if (rec2 == NULL) {
        mtr_commit(&mtr);

        trx_purge_rseg_get_next_history_log(purge_sys->rseg);

        /* Look for the next undo log and record to purge */

        trx_purge_choose_next_log();

        mtr_start(&mtr);

        undo_page = trx_undo_page_get_s_latched(space, zip_size,
                                                page_no, &mtr);

        rec = undo_page + offset;
    } else {
        page = page_align(rec2);

        purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
        purge_sys->page_no = page_get_page_no(page);
        purge_sys->offset = rec2 - page;

        if (undo_page != page) {
            /* We advance to a new page of the undo log: */
            purge_sys->n_pages_handled++;
        }
    }

    rec_copy = trx_undo_rec_copy(rec, heap);

    mtr_commit(&mtr);

    return(rec_copy);
}
Exemplo n.º 28
0
/****************************************************************//**
Prepares a transaction. */
UNIV_INTERN
void
trx_prepare_off_kernel(
/*===================*/
	trx_t*	trx)	/*!< in: transaction */
{
	trx_rseg_t*	rseg;
	ib_uint64_t	lsn		= 0;
	mtr_t		mtr;

	ut_ad(mutex_own(&kernel_mutex));

	rseg = trx->rseg;

	if (trx->insert_undo != NULL || trx->update_undo != NULL) {

		mutex_exit(&kernel_mutex);

		mtr_start(&mtr);

		/* Change the undo log segment states from TRX_UNDO_ACTIVE
		to TRX_UNDO_PREPARED: these modifications to the file data
		structure define the transaction as prepared in the
		file-based world, at the serialization point of lsn. */

		mutex_enter(&(rseg->mutex));

		if (trx->insert_undo != NULL) {

			/* It is not necessary to obtain trx->undo_mutex here
			because only a single OS thread is allowed to do the
			transaction prepare for this transaction. */

			trx_undo_set_state_at_prepare(trx, trx->insert_undo,
						      &mtr);
		}

		if (trx->update_undo) {
			trx_undo_set_state_at_prepare(
				trx, trx->update_undo, &mtr);
		}

		mutex_exit(&(rseg->mutex));

		/*--------------*/
		mtr_commit(&mtr);	/* This mtr commit makes the
					transaction prepared in the file-based
					world */
		/*--------------*/
		lsn = mtr.end_lsn;

		mutex_enter(&kernel_mutex);
	}

	ut_ad(mutex_own(&kernel_mutex));

	/*--------------------------------------*/
	trx->conc_state = TRX_PREPARED;
	/*--------------------------------------*/

	if (lsn) {
		/* Depending on the my.cnf options, we may now write the log
		buffer to the log files, making the prepared state of the
		transaction durable if the OS does not crash. We may also
		flush the log files to disk, making the prepared state of the
		transaction durable also at an OS crash or a power outage.

		The idea in InnoDB's group prepare is that a group of
		transactions gather behind a trx doing a physical disk write
		to log files, and when that physical write has been completed,
		one of those transactions does a write which prepares the whole
		group. Note that this group prepare will only bring benefit if
		there are > 2 users in the database. Then at least 2 users can
		gather behind one doing the physical log write to disk.

		TODO: find out if MySQL holds some mutex when calling this.
		That would spoil our group prepare algorithm. */

		mutex_exit(&kernel_mutex);

		if (srv_flush_log_at_trx_commit == 0) {
			/* Do nothing */
		} else if (srv_flush_log_at_trx_commit == 1) {
			if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
				/* Write the log but do not flush it to disk */

				log_write_up_to(lsn, LOG_WAIT_ONE_GROUP,
						FALSE);
			} else {
				/* Write the log to the log files AND flush
				them to disk */

				log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
			}
		} else if (srv_flush_log_at_trx_commit == 2) {

			/* Write the log but do not flush it to disk */

			log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
		} else {
			ut_error;
		}

		mutex_enter(&kernel_mutex);
	}
}
Exemplo n.º 29
0
/********************************************************//**
Parses a log record written by mlog_open_and_write_index.
@return	parsed record end, NULL if not a complete record */
UNIV_INTERN
byte*
mlog_parse_index(
/*=============*/
	byte*		ptr,	/*!< in: buffer */
	const byte*	end_ptr,/*!< in: buffer end */
	ibool		comp,	/*!< in: TRUE=compact record format */
	dict_index_t**	index)	/*!< out, own: dummy index */
{
	ulint		i, n, n_uniq;
	dict_table_t*	table;
	dict_index_t*	ind;

	ut_ad(comp == FALSE || comp == TRUE);

	if (comp) {
		if (end_ptr < ptr + 4) {
			return(NULL);
		}
		n = mach_read_from_2(ptr);
		ptr += 2;
		n_uniq = mach_read_from_2(ptr);
		ptr += 2;
		ut_ad(n_uniq <= n);
		if (end_ptr < ptr + n * 2) {
			return(NULL);
		}
	} else {
		n = n_uniq = 1;
	}
	table = dict_mem_table_create("LOG_DUMMY", DICT_HDR_SPACE, n,
				      comp ? DICT_TF_COMPACT : 0);
	ind = dict_mem_index_create("LOG_DUMMY", "LOG_DUMMY",
				    DICT_HDR_SPACE, 0, n);
	ind->table = table;
	ind->n_uniq = (unsigned int) n_uniq;
	if (n_uniq != n) {
		ut_a(n_uniq + DATA_ROLL_PTR <= n);
		ind->type = DICT_CLUSTERED;
	}
	if (comp) {
		for (i = 0; i < n; i++) {
			ulint	len = mach_read_from_2(ptr);
			ptr += 2;
			/* The high-order bit of len is the NOT NULL flag;
			the rest is 0 or 0x7fff for variable-length fields,
			and 1..0x7ffe for fixed-length fields. */
			dict_mem_table_add_col(
				table, NULL, NULL,
				((len + 1) & 0x7fff) <= 1
				? DATA_BINARY : DATA_FIXBINARY,
				len & 0x8000 ? DATA_NOT_NULL : 0,
				len & 0x7fff);

			dict_index_add_col(ind, table,
					   dict_table_get_nth_col(table, i),
					   0);
		}
		dict_table_add_system_columns(table, table->heap);
		if (n_uniq != n) {
			/* Identify DB_TRX_ID and DB_ROLL_PTR in the index. */
			ut_a(DATA_TRX_ID_LEN
			     == dict_index_get_nth_col(ind, DATA_TRX_ID - 1
						       + n_uniq)->len);
			ut_a(DATA_ROLL_PTR_LEN
			     == dict_index_get_nth_col(ind, DATA_ROLL_PTR - 1
						       + n_uniq)->len);
			ind->fields[DATA_TRX_ID - 1 + n_uniq].col
				= &table->cols[n + DATA_TRX_ID];
			ind->fields[DATA_ROLL_PTR - 1 + n_uniq].col
				= &table->cols[n + DATA_ROLL_PTR];
		}
	}
	/* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */
	ind->cached = TRUE;
	*index = ind;
	return(ptr);
}
Exemplo n.º 30
0
/**********************************************************************//**
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);
}