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