1 /*****************************************************************************
2 
3 Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2018, 2020, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /**************************************************//**
21 @file lock/lock0prdt.cc
22 The transaction lock system
23 
24 Created 9/7/2013 Jimmy Yang
25 *******************************************************/
26 
27 #define LOCK_MODULE_IMPLEMENTATION
28 
29 #include "lock0lock.h"
30 #include "lock0priv.h"
31 #include "lock0prdt.h"
32 #include "dict0mem.h"
33 #include "que0que.h"
34 
35 /*********************************************************************//**
36 Get a minimum bounding box from a Predicate
37 @return	the minimum bounding box */
38 UNIV_INLINE
39 rtr_mbr_t*
prdt_get_mbr_from_prdt(const lock_prdt_t * prdt)40 prdt_get_mbr_from_prdt(
41 /*===================*/
42 	const lock_prdt_t*	prdt)	/*!< in: the lock predicate */
43 {
44 	rtr_mbr_t*	mbr_loc = reinterpret_cast<rtr_mbr_t*>(prdt->data);
45 
46 	return(mbr_loc);
47 }
48 
49 /*********************************************************************//**
50 Get a predicate from a lock
51 @return	the predicate */
52 lock_prdt_t*
lock_get_prdt_from_lock(const lock_t * lock)53 lock_get_prdt_from_lock(
54 /*====================*/
55 	const lock_t*	lock)	/*!< in: the lock */
56 {
57 	lock_prdt_t*	prdt = reinterpret_cast<lock_prdt_t*>(
58 				&((reinterpret_cast<byte*>(
59 					const_cast<lock_t*>(&lock[1])))[
60 						UNIV_WORD_SIZE]));
61 
62 	return(prdt);
63 }
64 
65 /*********************************************************************//**
66 Get a minimum bounding box directly from a lock
67 @return	the minimum bounding box*/
68 UNIV_INLINE
69 rtr_mbr_t*
lock_prdt_get_mbr_from_lock(const lock_t * lock)70 lock_prdt_get_mbr_from_lock(
71 /*========================*/
72 	const lock_t*	lock)	/*!< in: the lock */
73 {
74 	ut_ad(lock->type_mode & LOCK_PREDICATE);
75 
76 	lock_prdt_t*	prdt = lock_get_prdt_from_lock(lock);
77 
78 	rtr_mbr_t*	mbr_loc = prdt_get_mbr_from_prdt(prdt);
79 
80 	return(mbr_loc);
81 }
82 
83 /*********************************************************************//**
84 Append a predicate to the lock */
85 void
lock_prdt_set_prdt(lock_t * lock,const lock_prdt_t * prdt)86 lock_prdt_set_prdt(
87 /*===============*/
88 	lock_t*			lock,	/*!< in: lock */
89 	const lock_prdt_t*	prdt)	/*!< in: Predicate */
90 {
91 	ut_ad(lock->type_mode & LOCK_PREDICATE);
92 
93 	memcpy(&(((byte*) &lock[1])[UNIV_WORD_SIZE]), prdt, sizeof *prdt);
94 }
95 
96 
97 /** Check whether two predicate locks are compatible with each other
98 @param[in]	prdt1	first predicate lock
99 @param[in]	prdt2	second predicate lock
100 @param[in]	op	predicate comparison operator
101 @return	true if consistent */
102 static
103 bool
lock_prdt_consistent(lock_prdt_t * prdt1,lock_prdt_t * prdt2,ulint op)104 lock_prdt_consistent(
105 	lock_prdt_t*	prdt1,
106 	lock_prdt_t*	prdt2,
107 	ulint		op)
108 {
109 	bool		ret = false;
110 	rtr_mbr_t*	mbr1 = prdt_get_mbr_from_prdt(prdt1);
111 	rtr_mbr_t*	mbr2 = prdt_get_mbr_from_prdt(prdt2);
112 	ulint		action;
113 
114 	if (op) {
115 		action = op;
116 	} else {
117 		if (prdt2->op != 0 && (prdt1->op != prdt2->op)) {
118 			return(false);
119 		}
120 
121 		action = prdt1->op;
122 	}
123 
124 	switch (action) {
125 	case PAGE_CUR_CONTAIN:
126 		ret = MBR_CONTAIN_CMP(mbr1, mbr2);
127 		break;
128 	case PAGE_CUR_DISJOINT:
129 		ret = MBR_DISJOINT_CMP(mbr1, mbr2);
130 		break;
131 	case PAGE_CUR_MBR_EQUAL:
132 		ret = MBR_EQUAL_CMP(mbr1, mbr2);
133 		break;
134 	case PAGE_CUR_INTERSECT:
135 		ret = MBR_INTERSECT_CMP(mbr1, mbr2);
136 		break;
137 	case PAGE_CUR_WITHIN:
138 		ret = MBR_WITHIN_CMP(mbr1, mbr2);
139 		break;
140 	default:
141 		ib::error() << "invalid operator " << action;
142 		ut_error;
143 	}
144 
145 	return(ret);
146 }
147 
148 /*********************************************************************//**
149 Checks if a predicate lock request for a new lock has to wait for
150 another lock.
151 @return	true if new lock has to wait for lock2 to be released */
152 bool
lock_prdt_has_to_wait(const trx_t * trx,unsigned type_mode,lock_prdt_t * prdt,const lock_t * lock2)153 lock_prdt_has_to_wait(
154 /*==================*/
155 	const trx_t*	trx,	/*!< in: trx of new lock */
156 	unsigned	type_mode,/*!< in: precise mode of the new lock
157 				to set: LOCK_S or LOCK_X, possibly
158 				ORed to LOCK_PREDICATE or LOCK_PRDT_PAGE,
159 				LOCK_INSERT_INTENTION */
160 	lock_prdt_t*	prdt,	/*!< in: lock predicate to check */
161 	const lock_t*	lock2)	/*!< in: another record lock; NOTE that
162 				it is assumed that this has a lock bit
163 				set on the same record as in the new
164 				lock we are setting */
165 {
166 	lock_prdt_t*	cur_prdt = lock_get_prdt_from_lock(lock2);
167 
168 	ut_ad(trx && lock2);
169 	ut_ad((lock2->type_mode & LOCK_PREDICATE && type_mode & LOCK_PREDICATE)
170 	      || (lock2->type_mode & LOCK_PRDT_PAGE
171 		  && type_mode & LOCK_PRDT_PAGE));
172 
173 	ut_ad(type_mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE));
174 
175 	if (trx != lock2->trx
176 	    && !lock_mode_compatible(static_cast<lock_mode>(
177 			             LOCK_MODE_MASK & type_mode),
178 				     lock_get_mode(lock2))) {
179 
180 		/* If it is a page lock, then return true (conflict) */
181 		if (type_mode & LOCK_PRDT_PAGE) {
182 			ut_ad(lock2->type_mode & LOCK_PRDT_PAGE);
183 
184 			return(true);
185 		}
186 
187 		/* Predicate lock does not conflicts with non-predicate lock */
188 		if (!(lock2->type_mode & LOCK_PREDICATE)) {
189 			return(FALSE);
190 		}
191 
192 		ut_ad(lock2->type_mode & LOCK_PREDICATE);
193 
194 		if (!(type_mode & LOCK_INSERT_INTENTION)) {
195 			/* PREDICATE locks without LOCK_INSERT_INTENTION flag
196 			do not need to wait for anything. This is because
197 			different users can have conflicting lock types
198 			on predicates. */
199 
200 			return(FALSE);
201 		}
202 
203 		if (lock2->type_mode & LOCK_INSERT_INTENTION) {
204 
205 			/* No lock request needs to wait for an insert
206 			intention lock to be removed. This makes it similar
207 			to GAP lock, that allows conflicting insert intention
208 			locks */
209 			return(FALSE);
210 		}
211 
212 		if (!lock_prdt_consistent(cur_prdt, prdt, 0)) {
213 			return(false);
214 		}
215 
216 		return(TRUE);
217 	}
218 
219 	return(FALSE);
220 }
221 
222 /*********************************************************************//**
223 Checks if a transaction has a GRANTED stronger or equal predicate lock
224 on the page
225 @return	lock or NULL */
226 UNIV_INLINE
227 lock_t*
lock_prdt_has_lock(ulint precise_mode,unsigned type_mode,const buf_block_t * block,lock_prdt_t * prdt,const trx_t * trx)228 lock_prdt_has_lock(
229 /*===============*/
230 	ulint			precise_mode,	/*!< in: LOCK_S or LOCK_X */
231 	unsigned		type_mode,	/*!< in: LOCK_PREDICATE etc. */
232 	const buf_block_t*	block,		/*!< in: buffer block
233 						containing the record */
234 	lock_prdt_t*		prdt,		/*!< in: The predicate to be
235 						attached to the new lock */
236 	const trx_t*		trx)		/*!< in: transaction */
237 {
238 	lock_t*		lock;
239 
240 	ut_ad(lock_mutex_own());
241 	ut_ad((precise_mode & LOCK_MODE_MASK) == LOCK_S
242 	      || (precise_mode & LOCK_MODE_MASK) == LOCK_X);
243 	ut_ad(!(precise_mode & LOCK_INSERT_INTENTION));
244 
245 	for (lock = lock_rec_get_first(
246 		lock_hash_get(type_mode), block->page.id(), PRDT_HEAPNO);
247 	     lock != NULL;
248 	     lock = lock_rec_get_next(PRDT_HEAPNO, lock)) {
249 		ut_ad(lock->type_mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE));
250 
251 		if (lock->trx == trx
252 		    && !(lock->type_mode & LOCK_INSERT_INTENTION)
253 		    && !lock_get_wait(lock)
254 		    && lock_mode_stronger_or_eq(
255 			    lock_get_mode(lock),
256 			    static_cast<lock_mode>(
257 				    precise_mode & LOCK_MODE_MASK))) {
258 			if (lock->type_mode & LOCK_PRDT_PAGE) {
259 				return(lock);
260 			}
261 
262 			ut_ad(lock->type_mode & LOCK_PREDICATE);
263 			lock_prdt_t*	cur_prdt = lock_get_prdt_from_lock(
264 							lock);
265 
266 			/* if the lock predicate operator is the same
267 			as the one to look, and prdicate test is successful,
268 			then we find a lock */
269 			if (cur_prdt->op == prdt->op
270 			    && lock_prdt_consistent(cur_prdt, prdt, 0)) {
271 
272 				return(lock);
273 			}
274 		}
275 	}
276 
277 	return(NULL);
278 }
279 
280 /*********************************************************************//**
281 Checks if some other transaction has a conflicting predicate
282 lock request in the queue, so that we have to wait.
283 @return	lock or NULL */
284 static
285 lock_t*
lock_prdt_other_has_conflicting(unsigned mode,const buf_block_t * block,lock_prdt_t * prdt,const trx_t * trx)286 lock_prdt_other_has_conflicting(
287 /*============================*/
288 	unsigned		mode,	/*!< in: LOCK_S or LOCK_X,
289 					possibly ORed to LOCK_PREDICATE or
290 					LOCK_PRDT_PAGE, LOCK_INSERT_INTENTION */
291 	const buf_block_t*	block,	/*!< in: buffer block containing
292 					the record */
293 	lock_prdt_t*		prdt,    /*!< in: Predicates (currently)
294 					the Minimum Bounding Rectangle)
295 					the new lock will be on */
296 	const trx_t*		trx)	/*!< in: our transaction */
297 {
298 	ut_ad(lock_mutex_own());
299 
300 	for (lock_t* lock = lock_rec_get_first(
301 		lock_hash_get(mode), block->page.id(), PRDT_HEAPNO);
302 	     lock != NULL;
303 	     lock = lock_rec_get_next(PRDT_HEAPNO, lock)) {
304 
305 		if (lock->trx == trx) {
306 			continue;
307 		}
308 
309 		if (lock_prdt_has_to_wait(trx, mode, prdt, lock)) {
310 			return(lock);
311 		}
312 	}
313 
314 	return(NULL);
315 }
316 
317 /*********************************************************************//**
318 Reset the Minimum Bounding Rectangle (to a large area) */
319 static
320 void
lock_prdt_enlarge_mbr(const lock_t * lock,rtr_mbr_t * mbr)321 lock_prdt_enlarge_mbr(
322 /*==================*/
323 	const lock_t*	lock,	/*!< in/out: lock to modify */
324 	rtr_mbr_t*	mbr)    /*!< in: Minimum Bounding Rectangle */
325 {
326 	rtr_mbr_t*	cur_mbr = lock_prdt_get_mbr_from_lock(lock);
327 
328 	if (cur_mbr->xmin > mbr->xmin) {
329 		cur_mbr->xmin = mbr->xmin;
330 	}
331 
332 	if (cur_mbr->ymin > mbr->ymin) {
333 		cur_mbr->ymin = mbr->ymin;
334 	}
335 
336 	if (cur_mbr->xmax < mbr->xmax) {
337 		cur_mbr->xmax = mbr->xmax;
338 	}
339 
340 	if (cur_mbr->ymax < mbr->ymax) {
341 		cur_mbr->ymax = mbr->ymax;
342 	}
343 }
344 
345 /*********************************************************************//**
346 Reset the predicates to a "covering" (larger) predicates */
347 static
348 void
lock_prdt_enlarge_prdt(lock_t * lock,lock_prdt_t * prdt)349 lock_prdt_enlarge_prdt(
350 /*===================*/
351 	lock_t*		lock,	/*!< in/out: lock to modify */
352 	lock_prdt_t*	prdt)	/*!< in: predicate */
353 {
354 	rtr_mbr_t*	mbr = prdt_get_mbr_from_prdt(prdt);
355 
356 	lock_prdt_enlarge_mbr(lock, mbr);
357 }
358 
359 /*********************************************************************//**
360 Check two predicates' MBRs are the same
361 @return	true if they are the same */
362 static
363 bool
lock_prdt_is_same(lock_prdt_t * prdt1,lock_prdt_t * prdt2)364 lock_prdt_is_same(
365 /*==============*/
366 	lock_prdt_t*	prdt1,		/*!< in: MBR with the lock */
367 	lock_prdt_t*	prdt2)		/*!< in: MBR with the lock */
368 {
369 	rtr_mbr_t*	mbr1 = prdt_get_mbr_from_prdt(prdt1);
370 	rtr_mbr_t*	mbr2 = prdt_get_mbr_from_prdt(prdt2);
371 
372 	if (prdt1->op == prdt2->op && MBR_EQUAL_CMP(mbr1, mbr2)) {
373 		return(true);
374 	}
375 
376 	return(false);
377 }
378 
379 /*********************************************************************//**
380 Looks for a similar predicate lock struct by the same trx on the same page.
381 This can be used to save space when a new record lock should be set on a page:
382 no new struct is needed, if a suitable old one is found.
383 @return	lock or NULL */
384 static
385 lock_t*
lock_prdt_find_on_page(unsigned type_mode,const buf_block_t * block,lock_prdt_t * prdt,const trx_t * trx)386 lock_prdt_find_on_page(
387 /*===================*/
388 	unsigned		type_mode,	/*!< in: lock type_mode field */
389 	const buf_block_t*	block,		/*!< in: buffer block */
390 	lock_prdt_t*		prdt,		/*!< in: MBR with the lock */
391 	const trx_t*		trx)		/*!< in: transaction */
392 {
393 	lock_t*	lock;
394 
395 	ut_ad(lock_mutex_own());
396 
397 	for (lock = lock_sys.get_first(*lock_hash_get(type_mode),
398 				       block->page.id());
399 	     lock != NULL;
400 	     lock = lock_rec_get_next_on_page(lock)) {
401 
402 		if (lock->trx == trx
403 		    && lock->type_mode == type_mode) {
404 			if (lock->type_mode & LOCK_PRDT_PAGE) {
405 				return(lock);
406 			}
407 
408 			ut_ad(lock->type_mode & LOCK_PREDICATE);
409 
410 			if (lock_prdt_is_same(lock_get_prdt_from_lock(lock),
411 					      prdt)) {
412 				return(lock);
413 			}
414 		}
415 	}
416 
417 	return(NULL);
418 }
419 
420 /*********************************************************************//**
421 Adds a predicate lock request in the predicate lock queue.
422 @return	lock where the bit was set */
423 static
424 lock_t*
lock_prdt_add_to_queue(unsigned type_mode,const buf_block_t * block,dict_index_t * index,trx_t * trx,lock_prdt_t * prdt,bool caller_owns_trx_mutex)425 lock_prdt_add_to_queue(
426 /*===================*/
427 	unsigned		type_mode,/*!< in: lock mode, wait, predicate
428 					etc. flags; type is ignored
429 					and replaced by LOCK_REC */
430 	const buf_block_t*	block,	/*!< in: buffer block containing
431 					the record */
432 	dict_index_t*		index,	/*!< in: index of record */
433 	trx_t*			trx,	/*!< in/out: transaction */
434 	lock_prdt_t*		prdt,	/*!< in: Minimum Bounding Rectangle
435 					the new lock will be on */
436 	bool			caller_owns_trx_mutex)
437 					/*!< in: TRUE if caller owns the
438 					transaction mutex */
439 {
440 	ut_ad(lock_mutex_own());
441 	ut_ad(caller_owns_trx_mutex == trx_mutex_own(trx));
442 	ut_ad(!dict_index_is_clust(index) && !dict_index_is_online_ddl(index));
443 	ut_ad(type_mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE));
444 
445 #ifdef UNIV_DEBUG
446 	switch (type_mode & LOCK_MODE_MASK) {
447 	case LOCK_X:
448 	case LOCK_S:
449 		break;
450 	default:
451 		ut_error;
452 	}
453 #endif /* UNIV_DEBUG */
454 
455 	type_mode |= LOCK_REC;
456 
457 	/* Look for a waiting lock request on the same record or on a gap */
458 
459 	lock_t*		lock;
460 
461 	for (lock = lock_sys.get_first(*lock_hash_get(type_mode),
462 				       block->page.id());
463 	     lock != NULL;
464 	     lock = lock_rec_get_next_on_page(lock)) {
465 
466 		if (lock_get_wait(lock)
467 		    && lock_rec_get_nth_bit(lock, PRDT_HEAPNO)
468 		    && lock->type_mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE)) {
469 
470 			break;
471 		}
472 	}
473 
474 	if (lock == NULL && !(type_mode & LOCK_WAIT)) {
475 
476 		/* Look for a similar record lock on the same page:
477 		if one is found and there are no waiting lock requests,
478 		we can just set the bit */
479 
480 		lock = lock_prdt_find_on_page(type_mode, block, prdt, trx);
481 
482 		if (lock != NULL) {
483 
484 			if (lock->type_mode & LOCK_PREDICATE) {
485 				lock_prdt_enlarge_prdt(lock, prdt);
486 			}
487 
488 			return(lock);
489 		}
490 	}
491 
492 	/* Note: We will not pass any conflicting lock to lock_rec_create(),
493 	because we should be moving an existing waiting lock request. */
494 	ut_ad(!(type_mode & LOCK_WAIT) || trx->lock.wait_trx);
495 
496 	lock = lock_rec_create(NULL,
497 #ifdef WITH_WSREP
498 		NULL, /* FIXME: replicate SPATIAL INDEX locks */
499 #endif
500 		type_mode, block, PRDT_HEAPNO, index, trx,
501 		caller_owns_trx_mutex);
502 
503 	if (lock->type_mode & LOCK_PREDICATE) {
504 		lock_prdt_set_prdt(lock, prdt);
505 	}
506 
507 	return lock;
508 }
509 
510 /*********************************************************************//**
511 Checks if locks of other transactions prevent an immediate insert of
512 a predicate record.
513 @return	DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
514 dberr_t
lock_prdt_insert_check_and_lock(ulint flags,const rec_t * rec,buf_block_t * block,dict_index_t * index,que_thr_t * thr,mtr_t * mtr,lock_prdt_t * prdt)515 lock_prdt_insert_check_and_lock(
516 /*============================*/
517 	ulint		flags,	/*!< in: if BTR_NO_LOCKING_FLAG bit is
518 				set, does nothing */
519 	const rec_t*	rec,	/*!< in: record after which to insert */
520 	buf_block_t*	block,	/*!< in/out: buffer block of rec */
521 	dict_index_t*	index,	/*!< in: index */
522 	que_thr_t*	thr,	/*!< in: query thread */
523 	mtr_t*		mtr,	/*!< in/out: mini-transaction */
524 	lock_prdt_t*	prdt)	/*!< in: Predicates with Minimum Bound
525 				Rectangle */
526 {
527 	ut_ad(block->frame == page_align(rec));
528 
529 	if (flags & BTR_NO_LOCKING_FLAG) {
530 
531 		return(DB_SUCCESS);
532 	}
533 
534 	ut_ad(!index->table->is_temporary());
535 	ut_ad(!dict_index_is_clust(index));
536 
537 	trx_t*	trx = thr_get_trx(thr);
538 
539 	lock_mutex_enter();
540 
541 	/* Because this code is invoked for a running transaction by
542 	the thread that is serving the transaction, it is not necessary
543 	to hold trx->mutex here. */
544 
545 	ut_ad(lock_table_has(trx, index->table, LOCK_IX));
546 
547 	lock_t*		lock;
548 
549 	/* Only need to check locks on prdt_hash */
550 	lock = lock_rec_get_first(&lock_sys.prdt_hash, block->page.id(),
551 	    PRDT_HEAPNO);
552 
553 	if (lock == NULL) {
554 		lock_mutex_exit();
555 
556 		/* Update the page max trx id field */
557 		page_update_max_trx_id(block, buf_block_get_page_zip(block),
558 				       trx->id, mtr);
559 
560 		return(DB_SUCCESS);
561 	}
562 
563 	ut_ad(lock->type_mode & LOCK_PREDICATE);
564 
565 	dberr_t		err;
566 
567 	/* If another transaction has an explicit lock request which locks
568 	the predicate, waiting or granted, on the successor, the insert
569 	has to wait.
570 
571 	Similar to GAP lock, we do not consider lock from inserts conflicts
572 	with each other */
573 
574 	const ulint	mode = LOCK_X | LOCK_PREDICATE | LOCK_INSERT_INTENTION;
575 
576 	const lock_t*	wait_for = lock_prdt_other_has_conflicting(
577 		mode, block, prdt, trx);
578 
579 	if (wait_for != NULL) {
580 		rtr_mbr_t*	mbr = prdt_get_mbr_from_prdt(prdt);
581 
582 		/* Allocate MBR on the lock heap */
583 		lock_init_prdt_from_mbr(prdt, mbr, 0, trx->lock.lock_heap);
584 
585 		/* Note that we may get DB_SUCCESS also here! */
586 		trx_mutex_enter(trx);
587 
588 		err = lock_rec_enqueue_waiting(
589 			NULL, /* FIXME: replicate SPATIAL INDEX locks */
590 			LOCK_X | LOCK_PREDICATE | LOCK_INSERT_INTENTION,
591 			block, PRDT_HEAPNO, index, thr, prdt);
592 
593 		trx_mutex_exit(trx);
594 	} else {
595 		err = DB_SUCCESS;
596 	}
597 
598 	lock_mutex_exit();
599 
600 	switch (err) {
601 	case DB_SUCCESS_LOCKED_REC:
602 		err = DB_SUCCESS;
603 		/* fall through */
604 	case DB_SUCCESS:
605 		/* Update the page max trx id field */
606 		page_update_max_trx_id(block,
607 				       buf_block_get_page_zip(block),
608 				       trx->id, mtr);
609 	default:
610 		/* We only care about the two return values. */
611 		break;
612 	}
613 
614 	return(err);
615 }
616 
617 /**************************************************************//**
618 Check whether any predicate lock in parent needs to propagate to
619 child page after split. */
620 void
lock_prdt_update_parent(buf_block_t * left_block,buf_block_t * right_block,lock_prdt_t * left_prdt,lock_prdt_t * right_prdt,const page_id_t page_id)621 lock_prdt_update_parent(
622 /*====================*/
623         buf_block_t*    left_block,	/*!< in/out: page to be split */
624         buf_block_t*    right_block,	/*!< in/out: the new half page */
625         lock_prdt_t*	left_prdt,	/*!< in: MBR on the old page */
626         lock_prdt_t*	right_prdt,	/*!< in: MBR on the new page */
627 	const page_id_t	page_id)	/*!< in: parent page */
628 {
629 	lock_mutex_enter();
630 
631 	/* Get all locks in parent */
632 	for (lock_t *lock = lock_sys.get_first_prdt(page_id);
633 	     lock;
634 	     lock = lock_rec_get_next_on_page(lock)) {
635 		lock_prdt_t*	lock_prdt;
636 		ulint		op = PAGE_CUR_DISJOINT;
637 
638 		ut_ad(lock);
639 
640 		if (!(lock->type_mode & LOCK_PREDICATE)
641 		    || (lock->type_mode & LOCK_MODE_MASK) == LOCK_X) {
642 			continue;
643 		}
644 
645 		lock_prdt = lock_get_prdt_from_lock(lock);
646 
647 		/* Check each lock in parent to see if it intersects with
648 		left or right child */
649 		if (!lock_prdt_consistent(lock_prdt, left_prdt, op)
650 		    && !lock_prdt_find_on_page(lock->type_mode, left_block,
651 					       lock_prdt, lock->trx)) {
652 			lock_prdt_add_to_queue(lock->type_mode,
653 					       left_block, lock->index,
654 					       lock->trx, lock_prdt,
655 					       FALSE);
656 		}
657 
658 		if (!lock_prdt_consistent(lock_prdt, right_prdt, op)
659 		    && !lock_prdt_find_on_page(lock->type_mode, right_block,
660 					       lock_prdt, lock->trx)) {
661 			lock_prdt_add_to_queue(lock->type_mode, right_block,
662 					       lock->index, lock->trx,
663 					       lock_prdt, FALSE);
664 		}
665 	}
666 
667 	lock_mutex_exit();
668 }
669 
670 /**************************************************************//**
671 Update predicate lock when page splits */
672 static
673 void
lock_prdt_update_split_low(buf_block_t * new_block,lock_prdt_t * prdt,lock_prdt_t * new_prdt,const page_id_t page_id,unsigned type_mode)674 lock_prdt_update_split_low(
675 /*=======================*/
676 	buf_block_t*	new_block,	/*!< in/out: the new half page */
677 	lock_prdt_t*	prdt,		/*!< in: MBR on the old page */
678 	lock_prdt_t*	new_prdt,	/*!< in: MBR on the new page */
679 	const page_id_t	page_id,	/*!< in: page number */
680 	unsigned	type_mode)	/*!< in: LOCK_PREDICATE or
681 					LOCK_PRDT_PAGE */
682 {
683 	lock_t*		lock;
684 
685 	for (lock = lock_sys.get_first(*lock_hash_get(type_mode), page_id);
686 	     lock;
687 	     lock = lock_rec_get_next_on_page(lock)) {
688 		/* First dealing with Page Lock */
689 		if (lock->type_mode & LOCK_PRDT_PAGE) {
690 			/* Duplicate the lock to new page */
691 			trx_mutex_enter(lock->trx);
692 			lock_prdt_add_to_queue(lock->type_mode,
693 					       new_block,
694 					       lock->index,
695 					       lock->trx, NULL, TRUE);
696 
697 			trx_mutex_exit(lock->trx);
698 			continue;
699 		}
700 
701 		/* Now dealing with Predicate Lock */
702 		lock_prdt_t*	lock_prdt;
703 		ulint		op = PAGE_CUR_DISJOINT;
704 
705 		ut_ad(lock->type_mode & LOCK_PREDICATE);
706 
707 		/* No need to duplicate waiting X locks */
708 		if ((lock->type_mode & LOCK_MODE_MASK) == LOCK_X) {
709 			continue;
710 		}
711 
712 		lock_prdt = lock_get_prdt_from_lock(lock);
713 
714 		if (lock_prdt_consistent(lock_prdt, prdt, op)) {
715 
716 			if (!lock_prdt_consistent(lock_prdt, new_prdt, op)) {
717 				/* Move the lock to new page */
718 				trx_mutex_enter(lock->trx);
719 				lock_prdt_add_to_queue(lock->type_mode,
720 						       new_block,
721 						       lock->index,
722 						       lock->trx, lock_prdt,
723 						       TRUE);
724 				trx_mutex_exit(lock->trx);
725 			}
726 		} else if (!lock_prdt_consistent(lock_prdt, new_prdt, op)) {
727 			/* Duplicate the lock to new page */
728 			trx_mutex_enter(lock->trx);
729 			lock_prdt_add_to_queue(lock->type_mode,
730 					       new_block,
731 					       lock->index,
732 					       lock->trx, lock_prdt, TRUE);
733 
734 			trx_mutex_exit(lock->trx);
735 		}
736 	}
737 }
738 
739 /**************************************************************//**
740 Update predicate lock when page splits */
741 void
lock_prdt_update_split(buf_block_t * new_block,lock_prdt_t * prdt,lock_prdt_t * new_prdt,const page_id_t page_id)742 lock_prdt_update_split(
743 /*===================*/
744 	buf_block_t*	new_block,	/*!< in/out: the new half page */
745 	lock_prdt_t*	prdt,		/*!< in: MBR on the old page */
746 	lock_prdt_t*	new_prdt,	/*!< in: MBR on the new page */
747 	const page_id_t	page_id)	/*!< in: page number */
748 {
749 	lock_mutex_enter();
750 
751 	lock_prdt_update_split_low(new_block, prdt, new_prdt,
752 				   page_id, LOCK_PREDICATE);
753 
754 	lock_prdt_update_split_low(new_block, NULL, NULL,
755 				   page_id, LOCK_PRDT_PAGE);
756 
757 	lock_mutex_exit();
758 }
759 
760 /*********************************************************************//**
761 Initiate a Predicate Lock from a MBR */
762 void
lock_init_prdt_from_mbr(lock_prdt_t * prdt,rtr_mbr_t * mbr,ulint mode,mem_heap_t * heap)763 lock_init_prdt_from_mbr(
764 /*====================*/
765 	lock_prdt_t*	prdt,	/*!< in/out: predicate to initialized */
766 	rtr_mbr_t*	mbr,	/*!< in: Minimum Bounding Rectangle */
767 	ulint		mode,	/*!< in: Search mode */
768 	mem_heap_t*	heap)	/*!< in: heap for allocating memory */
769 {
770 	memset(prdt, 0, sizeof(*prdt));
771 
772 	if (heap != NULL) {
773 		prdt->data = mem_heap_alloc(heap, sizeof(*mbr));
774 		memcpy(prdt->data, mbr, sizeof(*mbr));
775 	} else {
776 		prdt->data = static_cast<void*>(mbr);
777 	}
778 
779 	prdt->op = static_cast<uint16>(mode);
780 }
781 
782 /*********************************************************************//**
783 Acquire a predicate lock on a block
784 @return	DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
785 dberr_t
lock_prdt_lock(buf_block_t * block,lock_prdt_t * prdt,dict_index_t * index,lock_mode mode,unsigned type_mode,que_thr_t * thr)786 lock_prdt_lock(
787 /*===========*/
788 	buf_block_t*	block,	/*!< in/out: buffer block of rec */
789 	lock_prdt_t*	prdt,	/*!< in: Predicate for the lock */
790 	dict_index_t*	index,	/*!< in: secondary index */
791 	lock_mode	mode,	/*!< in: mode of the lock which
792 				the read cursor should set on
793 				records: LOCK_S or LOCK_X; the
794 				latter is possible in
795 				SELECT FOR UPDATE */
796 	unsigned	type_mode,
797 				/*!< in: LOCK_PREDICATE or LOCK_PRDT_PAGE */
798 	que_thr_t*	thr)	/*!< in: query thread
799 				(can be NULL if BTR_NO_LOCKING_FLAG) */
800 {
801 	trx_t*		trx = thr_get_trx(thr);
802 	dberr_t		err = DB_SUCCESS;
803 	lock_rec_req_status	status = LOCK_REC_SUCCESS;
804 
805 	if (trx->read_only || index->table->is_temporary()) {
806 		return(DB_SUCCESS);
807 	}
808 
809 	ut_ad(!dict_index_is_clust(index));
810 	ut_ad(!dict_index_is_online_ddl(index));
811 	ut_ad(type_mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE));
812 
813 	const hash_table_t& hash = type_mode == LOCK_PREDICATE
814 		? lock_sys.prdt_hash
815 		: lock_sys.prdt_page_hash;
816 
817 	/* Another transaction cannot have an implicit lock on the record,
818 	because when we come here, we already have modified the clustered
819 	index record, and this would not have been possible if another active
820 	transaction had modified this secondary index record. */
821 
822 	lock_mutex_enter();
823 
824 	const unsigned	prdt_mode = type_mode | mode;
825 	lock_t*		lock = lock_sys.get_first(hash, block->page.id());
826 
827 	if (lock == NULL) {
828 		lock = lock_rec_create(NULL,
829 #ifdef WITH_WSREP
830 			NULL, /* FIXME: replicate SPATIAL INDEX locks */
831 #endif
832 			prdt_mode, block, PRDT_HEAPNO,
833 			index, trx, FALSE);
834 
835 		status = LOCK_REC_SUCCESS_CREATED;
836 	} else {
837 		trx_mutex_enter(trx);
838 
839 		if (lock_rec_get_next_on_page(lock)
840 		    || lock->trx != trx
841 		    || lock->type_mode != (LOCK_REC | prdt_mode)
842 		    || lock_rec_get_n_bits(lock) == 0
843 		    || ((type_mode & LOCK_PREDICATE)
844 		        && (!lock_prdt_consistent(
845 				lock_get_prdt_from_lock(lock), prdt, 0)))) {
846 
847 			lock = lock_prdt_has_lock(
848 				mode, type_mode, block, prdt, trx);
849 
850 			if (lock == NULL) {
851 
852 				lock_t*	wait_for;
853 
854 				wait_for = lock_prdt_other_has_conflicting(
855 					prdt_mode, block, prdt, trx);
856 
857 				if (wait_for != NULL) {
858 
859 					err = lock_rec_enqueue_waiting(
860 						NULL, /* FIXME: replicate
861 						      SPATIAL INDEX locks */
862 						prdt_mode,
863 						block, PRDT_HEAPNO,
864 						index, thr, prdt);
865 				} else {
866 
867 					lock_prdt_add_to_queue(
868 						prdt_mode, block, index, trx,
869 						prdt, true);
870 
871 					status = LOCK_REC_SUCCESS;
872 				}
873 			}
874 
875 			trx_mutex_exit(trx);
876 
877 		} else {
878 			trx_mutex_exit(trx);
879 
880 			if (!lock_rec_get_nth_bit(lock, PRDT_HEAPNO)) {
881 				lock_rec_set_nth_bit(lock, PRDT_HEAPNO);
882 				status = LOCK_REC_SUCCESS_CREATED;
883 			}
884 		}
885 	}
886 
887 	lock_mutex_exit();
888 
889 	if (status == LOCK_REC_SUCCESS_CREATED && type_mode == LOCK_PREDICATE) {
890 		/* Append the predicate in the lock record */
891 		lock_prdt_set_prdt(lock, prdt);
892 	}
893 
894 	return(err);
895 }
896 
897 /*********************************************************************//**
898 Acquire a "Page" lock on a block
899 @return	DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
900 dberr_t
lock_place_prdt_page_lock(const page_id_t page_id,dict_index_t * index,que_thr_t * thr)901 lock_place_prdt_page_lock(
902 	const page_id_t	page_id,	/*!< in: page identifier */
903 	dict_index_t*	index,		/*!< in: secondary index */
904 	que_thr_t*	thr)		/*!< in: query thread */
905 {
906 	ut_ad(thr != NULL);
907 	ut_ad(!srv_read_only_mode);
908 
909 	ut_ad(!dict_index_is_clust(index));
910 	ut_ad(!dict_index_is_online_ddl(index));
911 
912 	/* Another transaction cannot have an implicit lock on the record,
913 	because when we come here, we already have modified the clustered
914 	index record, and this would not have been possible if another active
915 	transaction had modified this secondary index record. */
916 
917 	lock_mutex_enter();
918 
919 	const lock_t*	lock = lock_sys.get_first_prdt_page(page_id);
920 	const ulint	mode = LOCK_S | LOCK_PRDT_PAGE;
921 	trx_t*		trx = thr_get_trx(thr);
922 
923 	if (lock != NULL) {
924 
925 		trx_mutex_enter(trx);
926 
927 		/* Find a matching record lock owned by this transaction. */
928 
929 		while (lock != NULL && lock->trx != trx) {
930 
931 			lock = lock_rec_get_next_on_page_const(lock);
932 		}
933 
934 		ut_ad(lock == NULL || lock->type_mode == (mode | LOCK_REC));
935 		ut_ad(lock == NULL || lock_rec_get_n_bits(lock) != 0);
936 
937 		trx_mutex_exit(trx);
938 	}
939 
940 	if (lock == NULL) {
941 		lock = lock_rec_create_low(NULL,
942 #ifdef WITH_WSREP
943 			NULL, /* FIXME: replicate SPATIAL INDEX locks */
944 #endif
945 			mode, page_id, NULL, PRDT_HEAPNO,
946 			index, trx, FALSE);
947 
948 #ifdef PRDT_DIAG
949 		printf("GIS_DIAGNOSTIC: page lock %d\n", (int) page_no);
950 #endif /* PRDT_DIAG */
951 	}
952 
953 	lock_mutex_exit();
954 
955 	return(DB_SUCCESS);
956 }
957 
958 /** Check whether there are R-tree Page lock on a page
959 @param[in]	trx	trx to test the lock
960 @param[in]	page_id	page identifier
961 @return	true if there is none */
lock_test_prdt_page_lock(const trx_t * trx,const page_id_t page_id)962 bool lock_test_prdt_page_lock(const trx_t *trx, const page_id_t page_id)
963 {
964 	lock_t*		lock;
965 
966 	lock_mutex_enter();
967 
968 	lock = lock_sys.get_first_prdt_page(page_id);
969 
970 	lock_mutex_exit();
971 
972 	return(!lock || trx == lock->trx);
973 }
974 
975 /*************************************************************//**
976 Moves the locks of a page to another page and resets the lock bits of
977 the donating records. */
978 void
lock_prdt_rec_move(const buf_block_t * receiver,const buf_block_t * donator)979 lock_prdt_rec_move(
980 /*===============*/
981 	const buf_block_t*	receiver,	/*!< in: buffer block containing
982 						the receiving record */
983 	const buf_block_t*	donator)	/*!< in: buffer block containing
984 						the donating record */
985 {
986 	lock_mutex_enter();
987 
988 	for (lock_t *lock = lock_rec_get_first(&lock_sys.prdt_hash,
989 					       donator->page.id(), PRDT_HEAPNO);
990 	     lock != NULL;
991 	     lock = lock_rec_get_next(PRDT_HEAPNO, lock)) {
992 
993 		const auto type_mode = lock->type_mode;
994 		lock_prdt_t*	lock_prdt = lock_get_prdt_from_lock(lock);
995 
996 		lock_rec_reset_nth_bit(lock, PRDT_HEAPNO);
997 		lock_reset_lock_and_trx_wait(lock);
998 
999 		lock_prdt_add_to_queue(
1000 			type_mode, receiver, lock->index, lock->trx,
1001 			lock_prdt, FALSE);
1002 	}
1003 
1004 	lock_mutex_exit();
1005 }
1006 
1007 /** Removes predicate lock objects set on an index page which is discarded.
1008 @param[in]	block		page to be discarded
1009 @param[in]	lock_hash	lock hash */
1010 void
lock_prdt_page_free_from_discard(const buf_block_t * block,hash_table_t * lock_hash)1011 lock_prdt_page_free_from_discard(
1012 	const buf_block_t*      block,
1013 	hash_table_t*		lock_hash)
1014 {
1015 	lock_t*	lock;
1016 	lock_t*	next_lock;
1017 
1018 	ut_ad(lock_mutex_own());
1019 
1020 	lock = lock_sys.get_first(*lock_hash, block->page.id());
1021 
1022 	while (lock != NULL) {
1023 		next_lock = lock_rec_get_next_on_page(lock);
1024 
1025 		lock_rec_discard(lock);
1026 
1027 		lock = next_lock;
1028 	}
1029 }
1030