1/*****************************************************************************
2
3Copyright (c) 1996, 2018, Oracle and/or its affiliates. All Rights Reserved.
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License, version 2.0,
7as published by the Free Software Foundation.
8
9This program is also distributed with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation.  The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have included with MySQL.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License, version 2.0, for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24
25*****************************************************************************/
26
27/**************************************************//**
28@file include/trx0trx.ic
29The transaction
30
31Created 3/26/1996 Heikki Tuuri
32*******************************************************/
33
34#include "read0read.h"
35
36/**********************************************************************//**
37Determines if a transaction is in the given state.
38The caller must hold trx_sys->mutex, or it must be the thread
39that is serving a running transaction.
40A running RW transaction must be in trx_sys->rw_trx_list.
41@return TRUE if trx->state == state */
42UNIV_INLINE
43bool
44trx_state_eq(
45/*=========*/
46	const trx_t*	trx,	/*!< in: transaction */
47	trx_state_t	state)	/*!< in: state */
48{
49#ifdef UNIV_DEBUG
50	switch (trx->state) {
51	case TRX_STATE_PREPARED:
52
53		ut_ad(!trx_is_autocommit_non_locking(trx));
54		return(trx->state == state);
55
56	case TRX_STATE_ACTIVE:
57
58		assert_trx_nonlocking_or_in_list(trx);
59		return(state == trx->state);
60
61	case TRX_STATE_COMMITTED_IN_MEMORY:
62
63		check_trx_state(trx);
64		return(state == trx->state);
65
66	case TRX_STATE_NOT_STARTED:
67	case TRX_STATE_FORCED_ROLLBACK:
68
69		/* These states are not allowed for running transactions. */
70		ut_a(state == TRX_STATE_NOT_STARTED
71		     || state == TRX_STATE_FORCED_ROLLBACK);
72
73		ut_ad(!trx->in_rw_trx_list);
74
75		return(true);
76	}
77	ut_error;
78#endif /* UNIV_DEBUG */
79	return(trx->state == state);
80}
81
82/****************************************************************//**
83Retrieves the index causing the error from a trx.
84@return the error info */
85UNIV_INLINE
86const dict_index_t*
87trx_get_error_index(
88/*===============*/
89	const trx_t*	trx)	/*!< in: trx object */
90{
91	return(trx->error_index);
92}
93
94/*******************************************************************//**
95Retrieves transaction's que state in a human readable string. The string
96should not be free()'d or modified.
97@return string in the data segment */
98UNIV_INLINE
99const char*
100trx_get_que_state_str(
101/*==================*/
102	const trx_t*	trx)	/*!< in: transaction */
103{
104	/* be sure to adjust TRX_QUE_STATE_STR_MAX_LEN if you change this */
105	switch (trx->lock.que_state) {
106	case TRX_QUE_RUNNING:
107		return("RUNNING");
108	case TRX_QUE_LOCK_WAIT:
109		return("LOCK WAIT");
110	case TRX_QUE_ROLLING_BACK:
111		return("ROLLING BACK");
112	case TRX_QUE_COMMITTING:
113		return("COMMITTING");
114	default:
115		return("UNKNOWN");
116	}
117}
118
119/** Retreieves the transaction ID.
120In a given point in time it is guaranteed that IDs of the running
121transactions are unique. The values returned by this function for readonly
122transactions may be reused, so a subsequent RO transaction may get the same ID
123as a RO transaction that existed in the past. The values returned by this
124function should be used for printing purposes only.
125@param[in]	trx	transaction whose id to retrieve
126@return transaction id */
127UNIV_INLINE
128trx_id_t
129trx_get_id_for_print(
130	const trx_t*	trx)
131{
132	/* Readonly and transactions whose intentions are unknown (whether
133	they will eventually do a WRITE) don't have trx_t::id assigned (it is
134	0 for those transactions). Transaction IDs in
135	innodb_trx.trx_id,
136	innodb_locks.lock_id,
137	innodb_locks.lock_trx_id,
138	innodb_lock_waits.requesting_trx_id,
139	innodb_lock_waits.blocking_trx_id should match because those tables
140	could be used in an SQL JOIN on those columns. Also trx_t::id is
141	printed by SHOW ENGINE INNODB STATUS, and in logs, so we must have the
142	same value printed everywhere consistently. */
143
144	/* DATA_TRX_ID_LEN is the storage size in bytes. */
145	static const trx_id_t	max_trx_id
146		= (1ULL << (DATA_TRX_ID_LEN * CHAR_BIT)) - 1;
147
148	ut_ad(trx->id <= max_trx_id);
149
150	return(trx->id != 0
151	       ? trx->id
152	       : reinterpret_cast<trx_id_t>(trx) | (max_trx_id + 1));
153}
154
155/**********************************************************************//**
156Determine if a transaction is a dictionary operation.
157@return dictionary operation mode */
158UNIV_INLINE
159enum trx_dict_op_t
160trx_get_dict_operation(
161/*===================*/
162	const trx_t*	trx)	/*!< in: transaction */
163{
164	trx_dict_op_t op = static_cast<trx_dict_op_t>(trx->dict_operation);
165
166#ifdef UNIV_DEBUG
167	switch (op) {
168	case TRX_DICT_OP_NONE:
169	case TRX_DICT_OP_TABLE:
170	case TRX_DICT_OP_INDEX:
171		return(op);
172	}
173	ut_error;
174#endif /* UNIV_DEBUG */
175	return(op);
176}
177/**********************************************************************//**
178Flag a transaction a dictionary operation. */
179UNIV_INLINE
180void
181trx_set_dict_operation(
182/*===================*/
183	trx_t*			trx,	/*!< in/out: transaction */
184	enum trx_dict_op_t	op)	/*!< in: operation, not
185					TRX_DICT_OP_NONE */
186{
187#ifdef UNIV_DEBUG
188	enum trx_dict_op_t	old_op = trx_get_dict_operation(trx);
189
190	switch (op) {
191	case TRX_DICT_OP_NONE:
192		ut_error;
193		break;
194	case TRX_DICT_OP_TABLE:
195		switch (old_op) {
196		case TRX_DICT_OP_NONE:
197		case TRX_DICT_OP_INDEX:
198		case TRX_DICT_OP_TABLE:
199			goto ok;
200		}
201		ut_error;
202		break;
203	case TRX_DICT_OP_INDEX:
204		ut_ad(old_op == TRX_DICT_OP_NONE);
205		break;
206	}
207ok:
208#endif /* UNIV_DEBUG */
209
210	trx->ddl = true;
211	trx->dict_operation = op;
212}
213
214/**
215Releases the search latch if trx has reserved it.
216@param[in,out] trx		Transaction that may own the AHI latch */
217UNIV_INLINE
218void
219trx_search_latch_release_if_reserved(trx_t* trx)
220{
221	ut_a(!trx->has_search_latch);
222}
223
224/********************************************************************//**
225Check if redo rseg is modified for insert/update. */
226UNIV_INLINE
227bool
228trx_is_redo_rseg_updated(
229/*=====================*/
230	const trx_t*	   trx) /*!< in: transaction */
231{
232	return(trx->rsegs.m_redo.insert_undo != 0
233	       || trx->rsegs.m_redo.update_undo != 0);
234}
235
236/********************************************************************//**
237Check if noredo rseg is modified for insert/update. */
238UNIV_INLINE
239bool
240trx_is_noredo_rseg_updated(
241/*=======================*/
242	const trx_t*	   trx) /*!< in: transaction */
243{
244	return(trx->rsegs.m_noredo.insert_undo != 0
245	       || trx->rsegs.m_noredo.update_undo != 0);
246}
247
248/********************************************************************//**
249Check if redo/noredo rseg is modified for insert/update. */
250UNIV_INLINE
251bool
252trx_is_rseg_updated(
253/*================*/
254	const trx_t*	   trx) /*!< in: transaction */
255{
256	return(trx_is_redo_rseg_updated(trx)
257	       || trx_is_noredo_rseg_updated(trx));
258}
259
260/********************************************************************//**
261Check if redo/nonredo rseg is valid. */
262UNIV_INLINE
263bool
264trx_is_rseg_assigned(
265/*=================*/
266	const trx_t*	   trx) /*!< in: transaction */
267{
268	return(trx->rsegs.m_redo.rseg != NULL
269	       || trx->rsegs.m_noredo.rseg != NULL);
270}
271
272/**
273Increase the reference count. If the transaction is in state
274TRX_STATE_COMMITTED_IN_MEMORY then the transaction is considered
275committed and the reference count is not incremented.
276@param trx Transaction that is being referenced
277@param do_ref_count Increment the reference iff this is true
278@return transaction instance if it is not committed */
279UNIV_INLINE
280trx_t*
281trx_reference(
282	trx_t*		trx,
283	bool		do_ref_count)
284{
285	trx_mutex_enter(trx);
286
287	if (trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)) {
288		trx_mutex_exit(trx);
289		trx = NULL;
290	} else if (do_ref_count) {
291		ut_ad(trx->n_ref >= 0);
292		++trx->n_ref;
293		trx_mutex_exit(trx);
294	} else {
295		trx_mutex_exit(trx);
296	}
297
298	return(trx);
299}
300
301/**
302Release the transaction. Decrease the reference count.
303@param trx Transaction that is being released */
304UNIV_INLINE
305void
306trx_release_reference(
307	trx_t*		trx)
308{
309	trx_mutex_enter(trx);
310
311	ut_ad(trx->n_ref > 0);
312	--trx->n_ref;
313
314	trx_mutex_exit(trx);
315}
316
317
318/**
319@param trx		Get the active view for this transaction, if one exists
320@return the transaction's read view or NULL if one not assigned. */
321UNIV_INLINE
322ReadView*
323trx_get_read_view(
324	trx_t*		trx)
325{
326	return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view);
327}
328
329/**
330@param trx		Get the active view for this transaction, if one exists
331@return the transaction's read view or NULL if one not assigned. */
332UNIV_INLINE
333const ReadView*
334trx_get_read_view(
335	const trx_t*	trx)
336{
337	return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view);
338}
339
340/**
341@param[in] trx		Transaction to check
342@return true if the transaction is a high priority transaction.*/
343UNIV_INLINE
344bool
345trx_is_high_priority(const trx_t* trx)
346{
347	if (trx->mysql_thd == NULL) {
348		return(false);
349	}
350
351	return(thd_trx_priority(trx->mysql_thd) > 0);
352}
353
354/**
355@param[in] requestor	Transaction requesting the lock
356@param[in] holder	Transaction holding the lock
357@return the transaction that will be rolled back, null don't care */
358UNIV_INLINE
359const trx_t*
360trx_arbitrate(const trx_t* requestor, const trx_t* holder)
361{
362	ut_ad(!trx_is_autocommit_non_locking(holder));
363	ut_ad(!trx_is_autocommit_non_locking(requestor));
364
365	/* Note: Background stats collection transactions also acquire
366	locks on user tables. They don't have an associated MySQL session
367	instance. */
368
369	if (requestor->mysql_thd == NULL) {
370
371		ut_ad(!trx_is_high_priority(requestor));
372
373		if (trx_is_high_priority(holder)) {
374			return(requestor);
375		} else {
376			return(NULL);
377		}
378
379	} else if (holder->mysql_thd == NULL) {
380
381		ut_ad(!trx_is_high_priority(holder));
382
383		if (trx_is_high_priority(requestor)) {
384			return(holder);
385		}
386
387		return(NULL);
388	}
389
390	const THD*	victim = thd_trx_arbitrate(
391		requestor->mysql_thd, holder->mysql_thd);
392
393	ut_ad(victim == NULL
394	      || victim == requestor->mysql_thd
395	      || victim == holder->mysql_thd);
396
397	if (victim != NULL) {
398		return(victim == requestor->mysql_thd ? requestor : holder);
399	}
400
401	return(NULL);
402}
403