1/*****************************************************************************
2
3Copyright (c) 2007, 2014, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file include/lock0priv.ic
22Lock module internal inline methods.
23
24Created July 16, 2007 Vasil Dimov
25*******************************************************/
26
27/* This file contains only methods which are used in
28lock/lock0* files, other than lock/lock0lock.cc.
29I.e. lock/lock0lock.cc contains more internal inline
30methods but they are used only in that file. */
31
32#ifndef LOCK_MODULE_IMPLEMENTATION
33#error Do not include lock0priv.ic outside of the lock/ module
34#endif
35
36#include "row0row.h"
37
38/*********************************************************************//**
39Gets the type of a lock.
40@return LOCK_TABLE or LOCK_REC */
41UNIV_INLINE
42ulint
43lock_get_type_low(
44/*==============*/
45	const lock_t*	lock)	/*!< in: lock */
46{
47	ut_ad(lock);
48
49	return(lock->type_mode & LOCK_TYPE_MASK);
50}
51
52/*********************************************************************//**
53Checks if some transaction has an implicit x-lock on a record in a clustered
54index.
55@return transaction id of the transaction which has the x-lock, or 0 */
56UNIV_INLINE
57trx_id_t
58lock_clust_rec_some_has_impl(
59/*=========================*/
60	const rec_t*		rec,	/*!< in: user record */
61	const dict_index_t*	index,	/*!< in: clustered index */
62	const rec_offs*		offsets)/*!< in: rec_get_offsets(rec, index) */
63{
64	ut_ad(dict_index_is_clust(index));
65	ut_ad(page_rec_is_user_rec(rec));
66
67	return(row_get_rec_trx_id(rec, index, offsets));
68}
69
70/*********************************************************************//**
71Gets the number of bits in a record lock bitmap.
72@return	number of bits */
73UNIV_INLINE
74ulint
75lock_rec_get_n_bits(
76/*================*/
77	const lock_t*	lock)	/*!< in: record lock */
78{
79	return(lock->un_member.rec_lock.n_bits);
80}
81
82/**********************************************************************//**
83Sets the nth bit of a record lock to TRUE. */
84UNIV_INLINE
85void
86lock_rec_set_nth_bit(
87/*=================*/
88	lock_t*	lock,	/*!< in: record lock */
89	ulint	i)	/*!< in: index of the bit */
90{
91	ulint	byte_index;
92	ulint	bit_index;
93
94	ut_ad(lock);
95	ut_ad(lock_get_type_low(lock) == LOCK_REC);
96	ut_ad(i < lock->un_member.rec_lock.n_bits);
97
98	byte_index = i / 8;
99	bit_index = i % 8;
100
101	((byte*) &lock[1])[byte_index] |= 1 << bit_index;
102
103	++lock->trx->lock.n_rec_locks;
104}
105
106/*********************************************************************//**
107Gets the first or next record lock on a page.
108@return	next lock, NULL if none exists */
109UNIV_INLINE
110lock_t*
111lock_rec_get_next_on_page(
112/*======================*/
113	lock_t*	lock)	/*!< in: a record lock */
114{
115	return((lock_t*) lock_rec_get_next_on_page_const(lock));
116}
117
118/*********************************************************************//**
119Gets the first record lock on a page, where the page is identified by its
120file address.
121@return	first lock, NULL if none exists */
122UNIV_INLINE
123lock_t*
124lock_rec_get_first_on_page_addr(
125/*============================*/
126	hash_table_t*	lock_hash,	/* Lock hash table */
127	ulint		space,		/*!< in: space */
128	ulint		page_no)	/*!< in: page number */
129{
130	ut_ad(lock_mutex_own());
131
132	for (lock_t* lock = static_cast<lock_t*>(
133			HASH_GET_FIRST(lock_hash,
134				       lock_rec_hash(space, page_no)));
135	     lock != NULL;
136	     lock = static_cast<lock_t*>(HASH_GET_NEXT(hash, lock))) {
137
138		if (lock->un_member.rec_lock.space == space
139		    && lock->un_member.rec_lock.page_no == page_no) {
140
141			return(lock);
142		}
143	}
144
145	return(NULL);
146}
147
148/*********************************************************************//**
149Gets the first record lock on a page, where the page is identified by a
150pointer to it.
151@return	first lock, NULL if none exists */
152UNIV_INLINE
153lock_t*
154lock_rec_get_first_on_page(
155/*=======================*/
156	hash_table_t*		lock_hash,	/*!< in: lock hash table */
157	const buf_block_t*	block)		/*!< in: buffer block */
158{
159	ut_ad(lock_mutex_own());
160
161	ulint	space	= block->page.id.space();
162	ulint	page_no	= block->page.id.page_no();
163	ulint	hash = buf_block_get_lock_hash_val(block);
164
165	for (lock_t* lock = static_cast<lock_t*>(
166			HASH_GET_FIRST(lock_hash, hash));
167	     lock != NULL;
168	     lock = static_cast<lock_t*>(HASH_GET_NEXT(hash, lock))) {
169
170		if (lock->un_member.rec_lock.space == space
171		    && lock->un_member.rec_lock.page_no == page_no) {
172
173			return(lock);
174		}
175	}
176
177	return(NULL);
178}
179
180/*********************************************************************//**
181Gets the next explicit lock request on a record.
182@return	next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */
183UNIV_INLINE
184lock_t*
185lock_rec_get_next(
186/*==============*/
187	ulint	heap_no,/*!< in: heap number of the record */
188	lock_t*	lock)	/*!< in: lock */
189{
190	ut_ad(lock_mutex_own());
191
192	do {
193		ut_ad(lock_get_type_low(lock) == LOCK_REC);
194		lock = lock_rec_get_next_on_page(lock);
195	} while (lock && !lock_rec_get_nth_bit(lock, heap_no));
196
197	return(lock);
198}
199
200/*********************************************************************//**
201Gets the next explicit lock request on a record.
202@return	next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */
203UNIV_INLINE
204const lock_t*
205lock_rec_get_next_const(
206/*====================*/
207	ulint		heap_no,/*!< in: heap number of the record */
208	const lock_t*	lock)	/*!< in: lock */
209{
210	return(lock_rec_get_next(heap_no, (lock_t*) lock));
211}
212
213/*********************************************************************//**
214Gets the first explicit lock request on a record.
215@return	first lock, NULL if none exists */
216UNIV_INLINE
217lock_t*
218lock_rec_get_first(
219/*===============*/
220	hash_table_t*		hash,	/*!< in: hash chain the lock on */
221	const buf_block_t*	block,	/*!< in: block containing the record */
222	ulint			heap_no)/*!< in: heap number of the record */
223{
224	ut_ad(lock_mutex_own());
225
226	for (lock_t* lock = lock_rec_get_first_on_page(hash, block); lock;
227	     lock = lock_rec_get_next_on_page(lock)) {
228		if (lock_rec_get_nth_bit(lock, heap_no)) {
229			return(lock);
230		}
231	}
232
233	return(NULL);
234}
235
236/*********************************************************************//**
237Gets the nth bit of a record lock.
238@return TRUE if bit set also if i == ULINT_UNDEFINED return FALSE*/
239UNIV_INLINE
240ibool
241lock_rec_get_nth_bit(
242/*=================*/
243	const lock_t*	lock,	/*!< in: record lock */
244	ulint		i)	/*!< in: index of the bit */
245{
246	const byte*     b;
247
248	ut_ad(lock);
249	ut_ad(lock_get_type_low(lock) == LOCK_REC);
250
251	if (i >= lock->un_member.rec_lock.n_bits) {
252
253		return(FALSE);
254	}
255
256	b = ((const byte*) &lock[1]) + (i / 8);
257
258	return(1 & *b >> (i % 8));
259}
260
261/*********************************************************************//**
262Gets the first or next record lock on a page.
263@return next lock, NULL if none exists */
264UNIV_INLINE
265const lock_t*
266lock_rec_get_next_on_page_const(
267/*============================*/
268	const lock_t*	lock)	/*!< in: a record lock */
269{
270	ut_ad(lock_mutex_own());
271	ut_ad(lock_get_type_low(lock) == LOCK_REC);
272
273	ulint	space = lock->un_member.rec_lock.space;
274	ulint	page_no = lock->un_member.rec_lock.page_no;
275
276	while ((lock = static_cast<const lock_t*>(HASH_GET_NEXT(hash, lock)))
277	       != NULL) {
278
279		if (lock->un_member.rec_lock.space == space
280		    && lock->un_member.rec_lock.page_no == page_no) {
281
282			return(lock);
283		}
284	}
285
286	return(NULL);
287}
288
289/*********************************************************************//**
290Gets the mode of a lock.
291@return mode */
292UNIV_INLINE
293enum lock_mode
294lock_get_mode(
295/*==========*/
296	const lock_t*	lock)   /*!< in: lock */
297{
298	ut_ad(lock);
299
300	return(static_cast<enum lock_mode>(lock->type_mode & LOCK_MODE_MASK));
301}
302
303/*********************************************************************//**
304Calculates if lock mode 1 is compatible with lock mode 2.
305@return nonzero if mode1 compatible with mode2 */
306UNIV_INLINE
307ulint
308lock_mode_compatible(
309/*=================*/
310	enum lock_mode	mode1,	/*!< in: lock mode */
311	enum lock_mode	mode2)	/*!< in: lock mode */
312{
313	ut_ad((ulint) mode1 < lock_types);
314	ut_ad((ulint) mode2 < lock_types);
315
316	return(lock_compatibility_matrix[mode1][mode2]);
317}
318
319/*********************************************************************//**
320Calculates if lock mode 1 is stronger or equal to lock mode 2.
321@return nonzero if mode1 stronger or equal to mode2 */
322UNIV_INLINE
323ulint
324lock_mode_stronger_or_eq(
325/*=====================*/
326	enum lock_mode	mode1,	/*!< in: lock mode */
327	enum lock_mode	mode2)	/*!< in: lock mode */
328{
329	ut_ad((ulint) mode1 < lock_types);
330	ut_ad((ulint) mode2 < lock_types);
331
332	return(lock_strength_matrix[mode1][mode2]);
333}
334
335/*********************************************************************//**
336Gets the wait flag of a lock.
337@return LOCK_WAIT if waiting, 0 if not */
338UNIV_INLINE
339ulint
340lock_get_wait(
341/*==========*/
342	const lock_t*	lock)	/*!< in: lock */
343{
344	ut_ad(lock);
345
346	return(lock->type_mode & LOCK_WAIT);
347}
348
349/*********************************************************************//**
350Looks for a suitable type record lock struct by the same trx on the same page.
351This can be used to save space when a new record lock should be set on a page:
352no new struct is needed, if a suitable old is found.
353@return lock or NULL */
354UNIV_INLINE
355lock_t*
356lock_rec_find_similar_on_page(
357/*==========================*/
358	ulint           type_mode,      /*!< in: lock type_mode field */
359	ulint           heap_no,        /*!< in: heap number of the record */
360	lock_t*         lock,           /*!< in: lock_rec_get_first_on_page() */
361	const trx_t*    trx)            /*!< in: transaction */
362{
363	ut_ad(lock_mutex_own());
364
365	for (/* No op */;
366	     lock != NULL;
367	     lock = lock_rec_get_next_on_page(lock)) {
368
369		if (lock->trx == trx
370		    && lock->type_mode == type_mode
371		    && lock_rec_get_n_bits(lock) > heap_no) {
372
373			return(lock);
374		}
375	}
376
377	return(NULL);
378}
379
380/*********************************************************************//**
381Checks if a transaction has the specified table lock, or stronger. This
382function should only be called by the thread that owns the transaction.
383@return lock or NULL */
384UNIV_INLINE
385const lock_t*
386lock_table_has(
387/*===========*/
388	const trx_t*		trx,	/*!< in: transaction */
389	const dict_table_t*	table,	/*!< in: table */
390	lock_mode		in_mode)/*!< in: lock mode */
391{
392	/* Look for stronger locks the same trx already has on the table */
393
394	for (lock_list::const_iterator it = trx->lock.table_locks.begin(),
395             end = trx->lock.table_locks.end(); it != end; ++it) {
396
397		const lock_t*	lock = *it;
398
399		if (lock == NULL) {
400			continue;
401		}
402
403		lock_mode	mode = lock_get_mode(lock);
404
405		ut_ad(trx == lock->trx);
406		ut_ad(lock_get_type_low(lock) & LOCK_TABLE);
407		ut_ad(lock->un_member.tab_lock.table != NULL);
408
409		if (table == lock->un_member.tab_lock.table
410		    && lock_mode_stronger_or_eq(mode, in_mode)) {
411
412			ut_ad(!lock_get_wait(lock));
413
414			return(lock);
415		}
416	}
417
418	return(NULL);
419}
420
421/* vim: set filetype=c: */
422