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