| 1 | /***************************************************************************** |
| 2 | |
| 3 | Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. |
| 4 | Copyright (c) 2015, 2018, 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, Suite 500, Boston, MA 02110-1335 USA |
| 17 | |
| 18 | *****************************************************************************/ |
| 19 | |
| 20 | /**************************************************//** |
| 21 | @file include/lock0priv.h |
| 22 | Lock module internal structures and methods. |
| 23 | |
| 24 | Created July 12, 2007 Vasil Dimov |
| 25 | *******************************************************/ |
| 26 | |
| 27 | #ifndef lock0priv_h |
| 28 | #define lock0priv_h |
| 29 | |
| 30 | #ifndef LOCK_MODULE_IMPLEMENTATION |
| 31 | /* If you need to access members of the structures defined in this |
| 32 | file, please write appropriate functions that retrieve them and put |
| 33 | those functions in lock/ */ |
| 34 | #error Do not include lock0priv.h outside of the lock/ module |
| 35 | #endif |
| 36 | |
| 37 | #include "univ.i" |
| 38 | #include "hash0hash.h" |
| 39 | #include "trx0trx.h" |
| 40 | |
| 41 | #ifndef UINT32_MAX |
| 42 | #define UINT32_MAX (4294967295U) |
| 43 | #endif |
| 44 | |
| 45 | /** A table lock */ |
| 46 | struct lock_table_t { |
| 47 | dict_table_t* table; /*!< database table in dictionary |
| 48 | cache */ |
| 49 | UT_LIST_NODE_T(lock_t) |
| 50 | locks; /*!< list of locks on the same |
| 51 | table */ |
| 52 | /** Print the table lock into the given output stream |
| 53 | @param[in,out] out the output stream |
| 54 | @return the given output stream. */ |
| 55 | std::ostream& print(std::ostream& out) const; |
| 56 | }; |
| 57 | |
| 58 | /** Print the table lock into the given output stream |
| 59 | @param[in,out] out the output stream |
| 60 | @return the given output stream. */ |
| 61 | inline |
| 62 | std::ostream& lock_table_t::print(std::ostream& out) const |
| 63 | { |
| 64 | out << "[lock_table_t: name=" << table->name << "]" ; |
| 65 | return(out); |
| 66 | } |
| 67 | |
| 68 | /** The global output operator is overloaded to conveniently |
| 69 | print the lock_table_t object into the given output stream. |
| 70 | @param[in,out] out the output stream |
| 71 | @param[in] lock the table lock |
| 72 | @return the given output stream */ |
| 73 | inline |
| 74 | std::ostream& |
| 75 | operator<<(std::ostream& out, const lock_table_t& lock) |
| 76 | { |
| 77 | return(lock.print(out)); |
| 78 | } |
| 79 | |
| 80 | /** Record lock for a page */ |
| 81 | struct lock_rec_t { |
| 82 | ib_uint32_t space; /*!< space id */ |
| 83 | ib_uint32_t page_no; /*!< page number */ |
| 84 | ib_uint32_t n_bits; /*!< number of bits in the lock |
| 85 | bitmap; NOTE: the lock bitmap is |
| 86 | placed immediately after the |
| 87 | lock struct */ |
| 88 | |
| 89 | /** Print the record lock into the given output stream |
| 90 | @param[in,out] out the output stream |
| 91 | @return the given output stream. */ |
| 92 | std::ostream& print(std::ostream& out) const; |
| 93 | }; |
| 94 | |
| 95 | /** Print the record lock into the given output stream |
| 96 | @param[in,out] out the output stream |
| 97 | @return the given output stream. */ |
| 98 | inline |
| 99 | std::ostream& lock_rec_t::print(std::ostream& out) const |
| 100 | { |
| 101 | out << "[lock_rec_t: space=" << space << ", page_no=" << page_no |
| 102 | << ", n_bits=" << n_bits << "]" ; |
| 103 | return(out); |
| 104 | } |
| 105 | |
| 106 | inline |
| 107 | std::ostream& |
| 108 | operator<<(std::ostream& out, const lock_rec_t& lock) |
| 109 | { |
| 110 | return(lock.print(out)); |
| 111 | } |
| 112 | |
| 113 | /** Lock struct; protected by lock_sys.mutex */ |
| 114 | struct lock_t { |
| 115 | trx_t* trx; /*!< transaction owning the |
| 116 | lock */ |
| 117 | UT_LIST_NODE_T(lock_t) |
| 118 | trx_locks; /*!< list of the locks of the |
| 119 | transaction */ |
| 120 | |
| 121 | dict_index_t* index; /*!< index for a record lock */ |
| 122 | |
| 123 | lock_t* hash; /*!< hash chain node for a record |
| 124 | lock. The link node in a singly linked |
| 125 | list, used during hashing. */ |
| 126 | |
| 127 | /* Statistics for how long lock has been held and time |
| 128 | how long this lock had to be waited before it was granted */ |
| 129 | time_t requested_time; /*!< Lock request time */ |
| 130 | ulint wait_time; /*!< Time waited this lock or 0 */ |
| 131 | |
| 132 | union { |
| 133 | lock_table_t tab_lock;/*!< table lock */ |
| 134 | lock_rec_t rec_lock;/*!< record lock */ |
| 135 | } un_member; /*!< lock details */ |
| 136 | |
| 137 | ib_uint32_t type_mode; /*!< lock type, mode, LOCK_GAP or |
| 138 | LOCK_REC_NOT_GAP, |
| 139 | LOCK_INSERT_INTENTION, |
| 140 | wait flag, ORed */ |
| 141 | |
| 142 | /** Determine if the lock object is a record lock. |
| 143 | @return true if record lock, false otherwise. */ |
| 144 | bool is_record_lock() const |
| 145 | { |
| 146 | return(type() == LOCK_REC); |
| 147 | } |
| 148 | |
| 149 | bool is_waiting() const |
| 150 | { |
| 151 | return(type_mode & LOCK_WAIT); |
| 152 | } |
| 153 | |
| 154 | bool is_gap() const |
| 155 | { |
| 156 | return(type_mode & LOCK_GAP); |
| 157 | } |
| 158 | |
| 159 | bool is_record_not_gap() const |
| 160 | { |
| 161 | return(type_mode & LOCK_REC_NOT_GAP); |
| 162 | } |
| 163 | |
| 164 | bool is_insert_intention() const |
| 165 | { |
| 166 | return(type_mode & LOCK_INSERT_INTENTION); |
| 167 | } |
| 168 | |
| 169 | ulint type() const { |
| 170 | return(type_mode & LOCK_TYPE_MASK); |
| 171 | } |
| 172 | |
| 173 | enum lock_mode mode() const |
| 174 | { |
| 175 | return(static_cast<enum lock_mode>(type_mode & LOCK_MODE_MASK)); |
| 176 | } |
| 177 | |
| 178 | /** Print the lock object into the given output stream. |
| 179 | @param[in,out] out the output stream |
| 180 | @return the given output stream. */ |
| 181 | std::ostream& print(std::ostream& out) const; |
| 182 | |
| 183 | /** Convert the member 'type_mode' into a human readable string. |
| 184 | @return human readable string */ |
| 185 | std::string type_mode_string() const; |
| 186 | |
| 187 | const char* type_string() const |
| 188 | { |
| 189 | switch (type_mode & LOCK_TYPE_MASK) { |
| 190 | case LOCK_REC: |
| 191 | return("LOCK_REC" ); |
| 192 | case LOCK_TABLE: |
| 193 | return("LOCK_TABLE" ); |
| 194 | default: |
| 195 | ut_error; |
| 196 | } |
| 197 | } |
| 198 | }; |
| 199 | |
| 200 | /** Convert the member 'type_mode' into a human readable string. |
| 201 | @return human readable string */ |
| 202 | inline |
| 203 | std::string |
| 204 | lock_t::type_mode_string() const |
| 205 | { |
| 206 | std::ostringstream sout; |
| 207 | sout << type_string(); |
| 208 | sout << " | " << lock_mode_string(mode()); |
| 209 | |
| 210 | if (is_record_not_gap()) { |
| 211 | sout << " | LOCK_REC_NOT_GAP" ; |
| 212 | } |
| 213 | |
| 214 | if (is_waiting()) { |
| 215 | sout << " | LOCK_WAIT" ; |
| 216 | } |
| 217 | |
| 218 | if (is_gap()) { |
| 219 | sout << " | LOCK_GAP" ; |
| 220 | } |
| 221 | |
| 222 | if (is_insert_intention()) { |
| 223 | sout << " | LOCK_INSERT_INTENTION" ; |
| 224 | } |
| 225 | return(sout.str()); |
| 226 | } |
| 227 | |
| 228 | inline |
| 229 | std::ostream& |
| 230 | lock_t::print(std::ostream& out) const |
| 231 | { |
| 232 | out << "[lock_t: type_mode=" << type_mode << "(" |
| 233 | << type_mode_string() << ")" ; |
| 234 | |
| 235 | if (is_record_lock()) { |
| 236 | out << un_member.rec_lock; |
| 237 | } else { |
| 238 | out << un_member.tab_lock; |
| 239 | } |
| 240 | |
| 241 | out << "]" ; |
| 242 | return(out); |
| 243 | } |
| 244 | |
| 245 | inline |
| 246 | std::ostream& |
| 247 | operator<<(std::ostream& out, const lock_t& lock) |
| 248 | { |
| 249 | return(lock.print(out)); |
| 250 | } |
| 251 | |
| 252 | #ifdef UNIV_DEBUG |
| 253 | extern ibool lock_print_waits; |
| 254 | #endif /* UNIV_DEBUG */ |
| 255 | |
| 256 | /** Restricts the length of search we will do in the waits-for |
| 257 | graph of transactions */ |
| 258 | static const ulint LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK = 1000000; |
| 259 | |
| 260 | /** Restricts the search depth we will do in the waits-for graph of |
| 261 | transactions */ |
| 262 | static const ulint LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK = 200; |
| 263 | |
| 264 | /** When releasing transaction locks, this specifies how often we release |
| 265 | the lock mutex for a moment to give also others access to it */ |
| 266 | static const ulint LOCK_RELEASE_INTERVAL = 1000; |
| 267 | |
| 268 | /* Safety margin when creating a new record lock: this many extra records |
| 269 | can be inserted to the page without need to create a lock with a bigger |
| 270 | bitmap */ |
| 271 | |
| 272 | static const ulint LOCK_PAGE_BITMAP_MARGIN = 64; |
| 273 | |
| 274 | /* An explicit record lock affects both the record and the gap before it. |
| 275 | An implicit x-lock does not affect the gap, it only locks the index |
| 276 | record from read or update. |
| 277 | |
| 278 | If a transaction has modified or inserted an index record, then |
| 279 | it owns an implicit x-lock on the record. On a secondary index record, |
| 280 | a transaction has an implicit x-lock also if it has modified the |
| 281 | clustered index record, the max trx id of the page where the secondary |
| 282 | index record resides is >= trx id of the transaction (or database recovery |
| 283 | is running), and there are no explicit non-gap lock requests on the |
| 284 | secondary index record. |
| 285 | |
| 286 | This complicated definition for a secondary index comes from the |
| 287 | implementation: we want to be able to determine if a secondary index |
| 288 | record has an implicit x-lock, just by looking at the present clustered |
| 289 | index record, not at the historical versions of the record. The |
| 290 | complicated definition can be explained to the user so that there is |
| 291 | nondeterminism in the access path when a query is answered: we may, |
| 292 | or may not, access the clustered index record and thus may, or may not, |
| 293 | bump into an x-lock set there. |
| 294 | |
| 295 | Different transaction can have conflicting locks set on the gap at the |
| 296 | same time. The locks on the gap are purely inhibitive: an insert cannot |
| 297 | be made, or a select cursor may have to wait if a different transaction |
| 298 | has a conflicting lock on the gap. An x-lock on the gap does not give |
| 299 | the right to insert into the gap. |
| 300 | |
| 301 | An explicit lock can be placed on a user record or the supremum record of |
| 302 | a page. The locks on the supremum record are always thought to be of the gap |
| 303 | type, though the gap bit is not set. When we perform an update of a record |
| 304 | where the size of the record changes, we may temporarily store its explicit |
| 305 | locks on the infimum record of the page, though the infimum otherwise never |
| 306 | carries locks. |
| 307 | |
| 308 | A waiting record lock can also be of the gap type. A waiting lock request |
| 309 | can be granted when there is no conflicting mode lock request by another |
| 310 | transaction ahead of it in the explicit lock queue. |
| 311 | |
| 312 | In version 4.0.5 we added yet another explicit lock type: LOCK_REC_NOT_GAP. |
| 313 | It only locks the record it is placed on, not the gap before the record. |
| 314 | This lock type is necessary to emulate an Oracle-like READ COMMITTED isolation |
| 315 | level. |
| 316 | |
| 317 | ------------------------------------------------------------------------- |
| 318 | RULE 1: If there is an implicit x-lock on a record, and there are non-gap |
| 319 | ------- |
| 320 | lock requests waiting in the queue, then the transaction holding the implicit |
| 321 | x-lock also has an explicit non-gap record x-lock. Therefore, as locks are |
| 322 | released, we can grant locks to waiting lock requests purely by looking at |
| 323 | the explicit lock requests in the queue. |
| 324 | |
| 325 | RULE 3: Different transactions cannot have conflicting granted non-gap locks |
| 326 | ------- |
| 327 | on a record at the same time. However, they can have conflicting granted gap |
| 328 | locks. |
| 329 | RULE 4: If a there is a waiting lock request in a queue, no lock request, |
| 330 | ------- |
| 331 | gap or not, can be inserted ahead of it in the queue. In record deletes |
| 332 | and page splits new gap type locks can be created by the database manager |
| 333 | for a transaction, and without rule 4, the waits-for graph of transactions |
| 334 | might become cyclic without the database noticing it, as the deadlock check |
| 335 | is only performed when a transaction itself requests a lock! |
| 336 | ------------------------------------------------------------------------- |
| 337 | |
| 338 | An insert is allowed to a gap if there are no explicit lock requests by |
| 339 | other transactions on the next record. It does not matter if these lock |
| 340 | requests are granted or waiting, gap bit set or not, with the exception |
| 341 | that a gap type request set by another transaction to wait for |
| 342 | its turn to do an insert is ignored. On the other hand, an |
| 343 | implicit x-lock by another transaction does not prevent an insert, which |
| 344 | allows for more concurrency when using an Oracle-style sequence number |
| 345 | generator for the primary key with many transactions doing inserts |
| 346 | concurrently. |
| 347 | |
| 348 | A modify of a record is allowed if the transaction has an x-lock on the |
| 349 | record, or if other transactions do not have any non-gap lock requests on the |
| 350 | record. |
| 351 | |
| 352 | A read of a single user record with a cursor is allowed if the transaction |
| 353 | has a non-gap explicit, or an implicit lock on the record, or if the other |
| 354 | transactions have no x-lock requests on the record. At a page supremum a |
| 355 | read is always allowed. |
| 356 | |
| 357 | In summary, an implicit lock is seen as a granted x-lock only on the |
| 358 | record, not on the gap. An explicit lock with no gap bit set is a lock |
| 359 | both on the record and the gap. If the gap bit is set, the lock is only |
| 360 | on the gap. Different transaction cannot own conflicting locks on the |
| 361 | record at the same time, but they may own conflicting locks on the gap. |
| 362 | Granted locks on a record give an access right to the record, but gap type |
| 363 | locks just inhibit operations. |
| 364 | |
| 365 | NOTE: Finding out if some transaction has an implicit x-lock on a secondary |
| 366 | index record can be cumbersome. We may have to look at previous versions of |
| 367 | the corresponding clustered index record to find out if a delete marked |
| 368 | secondary index record was delete marked by an active transaction, not by |
| 369 | a committed one. |
| 370 | |
| 371 | FACT A: If a transaction has inserted a row, it can delete it any time |
| 372 | without need to wait for locks. |
| 373 | |
| 374 | PROOF: The transaction has an implicit x-lock on every index record inserted |
| 375 | for the row, and can thus modify each record without the need to wait. Q.E.D. |
| 376 | |
| 377 | FACT B: If a transaction has read some result set with a cursor, it can read |
| 378 | it again, and retrieves the same result set, if it has not modified the |
| 379 | result set in the meantime. Hence, there is no phantom problem. If the |
| 380 | biggest record, in the alphabetical order, touched by the cursor is removed, |
| 381 | a lock wait may occur, otherwise not. |
| 382 | |
| 383 | PROOF: When a read cursor proceeds, it sets an s-lock on each user record |
| 384 | it passes, and a gap type s-lock on each page supremum. The cursor must |
| 385 | wait until it has these locks granted. Then no other transaction can |
| 386 | have a granted x-lock on any of the user records, and therefore cannot |
| 387 | modify the user records. Neither can any other transaction insert into |
| 388 | the gaps which were passed over by the cursor. Page splits and merges, |
| 389 | and removal of obsolete versions of records do not affect this, because |
| 390 | when a user record or a page supremum is removed, the next record inherits |
| 391 | its locks as gap type locks, and therefore blocks inserts to the same gap. |
| 392 | Also, if a page supremum is inserted, it inherits its locks from the successor |
| 393 | record. When the cursor is positioned again at the start of the result set, |
| 394 | the records it will touch on its course are either records it touched |
| 395 | during the last pass or new inserted page supremums. It can immediately |
| 396 | access all these records, and when it arrives at the biggest record, it |
| 397 | notices that the result set is complete. If the biggest record was removed, |
| 398 | lock wait can occur because the next record only inherits a gap type lock, |
| 399 | and a wait may be needed. Q.E.D. */ |
| 400 | |
| 401 | /* If an index record should be changed or a new inserted, we must check |
| 402 | the lock on the record or the next. When a read cursor starts reading, |
| 403 | we will set a record level s-lock on each record it passes, except on the |
| 404 | initial record on which the cursor is positioned before we start to fetch |
| 405 | records. Our index tree search has the convention that the B-tree |
| 406 | cursor is positioned BEFORE the first possibly matching record in |
| 407 | the search. Optimizations are possible here: if the record is searched |
| 408 | on an equality condition to a unique key, we could actually set a special |
| 409 | lock on the record, a lock which would not prevent any insert before |
| 410 | this record. In the next key locking an x-lock set on a record also |
| 411 | prevents inserts just before that record. |
| 412 | There are special infimum and supremum records on each page. |
| 413 | A supremum record can be locked by a read cursor. This records cannot be |
| 414 | updated but the lock prevents insert of a user record to the end of |
| 415 | the page. |
| 416 | Next key locks will prevent the phantom problem where new rows |
| 417 | could appear to SELECT result sets after the select operation has been |
| 418 | performed. Prevention of phantoms ensures the serilizability of |
| 419 | transactions. |
| 420 | What should we check if an insert of a new record is wanted? |
| 421 | Only the lock on the next record on the same page, because also the |
| 422 | supremum record can carry a lock. An s-lock prevents insertion, but |
| 423 | what about an x-lock? If it was set by a searched update, then there |
| 424 | is implicitly an s-lock, too, and the insert should be prevented. |
| 425 | What if our transaction owns an x-lock to the next record, but there is |
| 426 | a waiting s-lock request on the next record? If this s-lock was placed |
| 427 | by a read cursor moving in the ascending order in the index, we cannot |
| 428 | do the insert immediately, because when we finally commit our transaction, |
| 429 | the read cursor should see also the new inserted record. So we should |
| 430 | move the read cursor backward from the next record for it to pass over |
| 431 | the new inserted record. This move backward may be too cumbersome to |
| 432 | implement. If we in this situation just enqueue a second x-lock request |
| 433 | for our transaction on the next record, then the deadlock mechanism |
| 434 | notices a deadlock between our transaction and the s-lock request |
| 435 | transaction. This seems to be an ok solution. |
| 436 | We could have the convention that granted explicit record locks, |
| 437 | lock the corresponding records from changing, and also lock the gaps |
| 438 | before them from inserting. A waiting explicit lock request locks the gap |
| 439 | before from inserting. Implicit record x-locks, which we derive from the |
| 440 | transaction id in the clustered index record, only lock the record itself |
| 441 | from modification, not the gap before it from inserting. |
| 442 | How should we store update locks? If the search is done by a unique |
| 443 | key, we could just modify the record trx id. Otherwise, we could put a record |
| 444 | x-lock on the record. If the update changes ordering fields of the |
| 445 | clustered index record, the inserted new record needs no record lock in |
| 446 | lock table, the trx id is enough. The same holds for a secondary index |
| 447 | record. Searched delete is similar to update. |
| 448 | |
| 449 | PROBLEM: |
| 450 | What about waiting lock requests? If a transaction is waiting to make an |
| 451 | update to a record which another modified, how does the other transaction |
| 452 | know to send the end-lock-wait signal to the waiting transaction? If we have |
| 453 | the convention that a transaction may wait for just one lock at a time, how |
| 454 | do we preserve it if lock wait ends? |
| 455 | |
| 456 | PROBLEM: |
| 457 | Checking the trx id label of a secondary index record. In the case of a |
| 458 | modification, not an insert, is this necessary? A secondary index record |
| 459 | is modified only by setting or resetting its deleted flag. A secondary index |
| 460 | record contains fields to uniquely determine the corresponding clustered |
| 461 | index record. A secondary index record is therefore only modified if we |
| 462 | also modify the clustered index record, and the trx id checking is done |
| 463 | on the clustered index record, before we come to modify the secondary index |
| 464 | record. So, in the case of delete marking or unmarking a secondary index |
| 465 | record, we do not have to care about trx ids, only the locks in the lock |
| 466 | table must be checked. In the case of a select from a secondary index, the |
| 467 | trx id is relevant, and in this case we may have to search the clustered |
| 468 | index record. |
| 469 | |
| 470 | PROBLEM: How to update record locks when page is split or merged, or |
| 471 | -------------------------------------------------------------------- |
| 472 | a record is deleted or updated? |
| 473 | If the size of fields in a record changes, we perform the update by |
| 474 | a delete followed by an insert. How can we retain the locks set or |
| 475 | waiting on the record? Because a record lock is indexed in the bitmap |
| 476 | by the heap number of the record, when we remove the record from the |
| 477 | record list, it is possible still to keep the lock bits. If the page |
| 478 | is reorganized, we could make a table of old and new heap numbers, |
| 479 | and permute the bitmaps in the locks accordingly. We can add to the |
| 480 | table a row telling where the updated record ended. If the update does |
| 481 | not require a reorganization of the page, we can simply move the lock |
| 482 | bits for the updated record to the position determined by its new heap |
| 483 | number (we may have to allocate a new lock, if we run out of the bitmap |
| 484 | in the old one). |
| 485 | A more complicated case is the one where the reinsertion of the |
| 486 | updated record is done pessimistically, because the structure of the |
| 487 | tree may change. |
| 488 | |
| 489 | PROBLEM: If a supremum record is removed in a page merge, or a record |
| 490 | --------------------------------------------------------------------- |
| 491 | removed in a purge, what to do to the waiting lock requests? In a split to |
| 492 | the right, we just move the lock requests to the new supremum. If a record |
| 493 | is removed, we could move the waiting lock request to its inheritor, the |
| 494 | next record in the index. But, the next record may already have lock |
| 495 | requests on its own queue. A new deadlock check should be made then. Maybe |
| 496 | it is easier just to release the waiting transactions. They can then enqueue |
| 497 | new lock requests on appropriate records. |
| 498 | |
| 499 | PROBLEM: When a record is inserted, what locks should it inherit from the |
| 500 | ------------------------------------------------------------------------- |
| 501 | upper neighbor? An insert of a new supremum record in a page split is |
| 502 | always possible, but an insert of a new user record requires that the upper |
| 503 | neighbor does not have any lock requests by other transactions, granted or |
| 504 | waiting, in its lock queue. Solution: We can copy the locks as gap type |
| 505 | locks, so that also the waiting locks are transformed to granted gap type |
| 506 | locks on the inserted record. */ |
| 507 | |
| 508 | /* LOCK COMPATIBILITY MATRIX |
| 509 | * IS IX S X AI |
| 510 | * IS + + + - + |
| 511 | * IX + + - - + |
| 512 | * S + - + - - |
| 513 | * X - - - - - |
| 514 | * AI + + - - - |
| 515 | * |
| 516 | * Note that for rows, InnoDB only acquires S or X locks. |
| 517 | * For tables, InnoDB normally acquires IS or IX locks. |
| 518 | * S or X table locks are only acquired for LOCK TABLES. |
| 519 | * Auto-increment (AI) locks are needed because of |
| 520 | * statement-level MySQL binlog. |
| 521 | * See also lock_mode_compatible(). |
| 522 | */ |
| 523 | static const byte lock_compatibility_matrix[5][5] = { |
| 524 | /** IS IX S X AI */ |
| 525 | /* IS */ { TRUE, TRUE, TRUE, FALSE, TRUE}, |
| 526 | /* IX */ { TRUE, TRUE, FALSE, FALSE, TRUE}, |
| 527 | /* S */ { TRUE, FALSE, TRUE, FALSE, FALSE}, |
| 528 | /* X */ { FALSE, FALSE, FALSE, FALSE, FALSE}, |
| 529 | /* AI */ { TRUE, TRUE, FALSE, FALSE, FALSE} |
| 530 | }; |
| 531 | |
| 532 | /* STRONGER-OR-EQUAL RELATION (mode1=row, mode2=column) |
| 533 | * IS IX S X AI |
| 534 | * IS + - - - - |
| 535 | * IX + + - - - |
| 536 | * S + - + - - |
| 537 | * X + + + + + |
| 538 | * AI - - - - + |
| 539 | * See lock_mode_stronger_or_eq(). |
| 540 | */ |
| 541 | static const byte lock_strength_matrix[5][5] = { |
| 542 | /** IS IX S X AI */ |
| 543 | /* IS */ { TRUE, FALSE, FALSE, FALSE, FALSE}, |
| 544 | /* IX */ { TRUE, TRUE, FALSE, FALSE, FALSE}, |
| 545 | /* S */ { TRUE, FALSE, TRUE, FALSE, FALSE}, |
| 546 | /* X */ { TRUE, TRUE, TRUE, TRUE, TRUE}, |
| 547 | /* AI */ { FALSE, FALSE, FALSE, FALSE, TRUE} |
| 548 | }; |
| 549 | |
| 550 | /** Maximum depth of the DFS stack. */ |
| 551 | static const ulint MAX_STACK_SIZE = 4096; |
| 552 | |
| 553 | #define PRDT_HEAPNO PAGE_HEAP_NO_INFIMUM |
| 554 | /** Record locking request status */ |
| 555 | enum lock_rec_req_status { |
| 556 | /** Failed to acquire a lock */ |
| 557 | LOCK_REC_FAIL, |
| 558 | /** Succeeded in acquiring a lock (implicit or already acquired) */ |
| 559 | LOCK_REC_SUCCESS, |
| 560 | /** Explicitly created a new lock */ |
| 561 | LOCK_REC_SUCCESS_CREATED |
| 562 | }; |
| 563 | |
| 564 | #ifdef UNIV_DEBUG |
| 565 | /** The count of the types of locks. */ |
| 566 | static const ulint lock_types = UT_ARR_SIZE(lock_compatibility_matrix); |
| 567 | #endif /* UNIV_DEBUG */ |
| 568 | |
| 569 | /*********************************************************************//** |
| 570 | Gets the type of a lock. |
| 571 | @return LOCK_TABLE or LOCK_REC */ |
| 572 | UNIV_INLINE |
| 573 | ulint |
| 574 | lock_get_type_low( |
| 575 | /*==============*/ |
| 576 | const lock_t* lock); /*!< in: lock */ |
| 577 | |
| 578 | /*********************************************************************//** |
| 579 | Gets the previous record lock set on a record. |
| 580 | @return previous lock on the same record, NULL if none exists */ |
| 581 | const lock_t* |
| 582 | lock_rec_get_prev( |
| 583 | /*==============*/ |
| 584 | const lock_t* in_lock,/*!< in: record lock */ |
| 585 | ulint heap_no);/*!< in: heap number of the record */ |
| 586 | |
| 587 | /*********************************************************************//** |
| 588 | Cancels a waiting lock request and releases possible other transactions |
| 589 | waiting behind it. */ |
| 590 | void |
| 591 | lock_cancel_waiting_and_release( |
| 592 | /*============================*/ |
| 593 | lock_t* lock); /*!< in/out: waiting lock request */ |
| 594 | |
| 595 | /*********************************************************************//** |
| 596 | Checks if some transaction has an implicit x-lock on a record in a clustered |
| 597 | index. |
| 598 | @return transaction id of the transaction which has the x-lock, or 0 */ |
| 599 | UNIV_INLINE |
| 600 | trx_id_t |
| 601 | lock_clust_rec_some_has_impl( |
| 602 | /*=========================*/ |
| 603 | const rec_t* rec, /*!< in: user record */ |
| 604 | const dict_index_t* index, /*!< in: clustered index */ |
| 605 | const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ |
| 606 | MY_ATTRIBUTE((warn_unused_result)); |
| 607 | |
| 608 | /*********************************************************************//** |
| 609 | Gets the first or next record lock on a page. |
| 610 | @return next lock, NULL if none exists */ |
| 611 | UNIV_INLINE |
| 612 | const lock_t* |
| 613 | lock_rec_get_next_on_page_const( |
| 614 | /*============================*/ |
| 615 | const lock_t* lock); /*!< in: a record lock */ |
| 616 | |
| 617 | /*********************************************************************//** |
| 618 | Gets the nth bit of a record lock. |
| 619 | @return TRUE if bit set also if i == ULINT_UNDEFINED return FALSE*/ |
| 620 | UNIV_INLINE |
| 621 | ibool |
| 622 | lock_rec_get_nth_bit( |
| 623 | /*=================*/ |
| 624 | const lock_t* lock, /*!< in: record lock */ |
| 625 | ulint i); /*!< in: index of the bit */ |
| 626 | |
| 627 | /*********************************************************************//** |
| 628 | Gets the number of bits in a record lock bitmap. |
| 629 | @return number of bits */ |
| 630 | UNIV_INLINE |
| 631 | ulint |
| 632 | lock_rec_get_n_bits( |
| 633 | /*================*/ |
| 634 | const lock_t* lock); /*!< in: record lock */ |
| 635 | |
| 636 | /**********************************************************************//** |
| 637 | Sets the nth bit of a record lock to TRUE. */ |
| 638 | UNIV_INLINE |
| 639 | void |
| 640 | lock_rec_set_nth_bit( |
| 641 | /*=================*/ |
| 642 | lock_t* lock, /*!< in: record lock */ |
| 643 | ulint i); /*!< in: index of the bit */ |
| 644 | |
| 645 | /** Reset the nth bit of a record lock. |
| 646 | @param[in,out] lock record lock |
| 647 | @param[in] i index of the bit that will be reset |
| 648 | @return previous value of the bit */ |
| 649 | inline byte lock_rec_reset_nth_bit(lock_t* lock, ulint i) |
| 650 | { |
| 651 | ut_ad(lock_get_type_low(lock) == LOCK_REC); |
| 652 | ut_ad(i < lock->un_member.rec_lock.n_bits); |
| 653 | |
| 654 | byte* b = reinterpret_cast<byte*>(&lock[1]) + (i >> 3); |
| 655 | byte mask = byte(1U << (i & 7)); |
| 656 | byte bit = *b & mask; |
| 657 | *b &= ~mask; |
| 658 | |
| 659 | if (bit != 0) { |
| 660 | ut_ad(lock->trx->lock.n_rec_locks > 0); |
| 661 | --lock->trx->lock.n_rec_locks; |
| 662 | } |
| 663 | |
| 664 | return(bit); |
| 665 | } |
| 666 | |
| 667 | /*********************************************************************//** |
| 668 | Gets the first or next record lock on a page. |
| 669 | @return next lock, NULL if none exists */ |
| 670 | UNIV_INLINE |
| 671 | lock_t* |
| 672 | lock_rec_get_next_on_page( |
| 673 | /*======================*/ |
| 674 | lock_t* lock); /*!< in: a record lock */ |
| 675 | /*********************************************************************//** |
| 676 | Gets the first record lock on a page, where the page is identified by its |
| 677 | file address. |
| 678 | @return first lock, NULL if none exists */ |
| 679 | UNIV_INLINE |
| 680 | lock_t* |
| 681 | lock_rec_get_first_on_page_addr( |
| 682 | /*============================*/ |
| 683 | hash_table_t* lock_hash, /* Lock hash table */ |
| 684 | ulint space, /*!< in: space */ |
| 685 | ulint page_no); /*!< in: page number */ |
| 686 | |
| 687 | /*********************************************************************//** |
| 688 | Gets the first record lock on a page, where the page is identified by a |
| 689 | pointer to it. |
| 690 | @return first lock, NULL if none exists */ |
| 691 | UNIV_INLINE |
| 692 | lock_t* |
| 693 | lock_rec_get_first_on_page( |
| 694 | /*=======================*/ |
| 695 | hash_table_t* lock_hash, /*!< in: lock hash table */ |
| 696 | const buf_block_t* block); /*!< in: buffer block */ |
| 697 | |
| 698 | |
| 699 | /*********************************************************************//** |
| 700 | Gets the next explicit lock request on a record. |
| 701 | @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */ |
| 702 | UNIV_INLINE |
| 703 | lock_t* |
| 704 | lock_rec_get_next( |
| 705 | /*==============*/ |
| 706 | ulint heap_no,/*!< in: heap number of the record */ |
| 707 | lock_t* lock); /*!< in: lock */ |
| 708 | |
| 709 | /*********************************************************************//** |
| 710 | Gets the next explicit lock request on a record. |
| 711 | @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */ |
| 712 | UNIV_INLINE |
| 713 | const lock_t* |
| 714 | lock_rec_get_next_const( |
| 715 | /*====================*/ |
| 716 | ulint heap_no,/*!< in: heap number of the record */ |
| 717 | const lock_t* lock); /*!< in: lock */ |
| 718 | |
| 719 | /*********************************************************************//** |
| 720 | Gets the first explicit lock request on a record. |
| 721 | @return first lock, NULL if none exists */ |
| 722 | UNIV_INLINE |
| 723 | lock_t* |
| 724 | lock_rec_get_first( |
| 725 | /*===============*/ |
| 726 | hash_table_t* hash, /*!< in: hash chain the lock on */ |
| 727 | const buf_block_t* block, /*!< in: block containing the record */ |
| 728 | ulint heap_no);/*!< in: heap number of the record */ |
| 729 | |
| 730 | /*********************************************************************//** |
| 731 | Gets the mode of a lock. |
| 732 | @return mode */ |
| 733 | UNIV_INLINE |
| 734 | enum lock_mode |
| 735 | lock_get_mode( |
| 736 | /*==========*/ |
| 737 | const lock_t* lock); /*!< in: lock */ |
| 738 | |
| 739 | /*********************************************************************//** |
| 740 | Calculates if lock mode 1 is compatible with lock mode 2. |
| 741 | @return nonzero if mode1 compatible with mode2 */ |
| 742 | UNIV_INLINE |
| 743 | ulint |
| 744 | lock_mode_compatible( |
| 745 | /*=================*/ |
| 746 | enum lock_mode mode1, /*!< in: lock mode */ |
| 747 | enum lock_mode mode2); /*!< in: lock mode */ |
| 748 | |
| 749 | /*********************************************************************//** |
| 750 | Calculates if lock mode 1 is stronger or equal to lock mode 2. |
| 751 | @return nonzero if mode1 stronger or equal to mode2 */ |
| 752 | UNIV_INLINE |
| 753 | ulint |
| 754 | lock_mode_stronger_or_eq( |
| 755 | /*=====================*/ |
| 756 | enum lock_mode mode1, /*!< in: lock mode */ |
| 757 | enum lock_mode mode2); /*!< in: lock mode */ |
| 758 | |
| 759 | /*********************************************************************//** |
| 760 | Gets the wait flag of a lock. |
| 761 | @return LOCK_WAIT if waiting, 0 if not */ |
| 762 | UNIV_INLINE |
| 763 | ulint |
| 764 | lock_get_wait( |
| 765 | /*==========*/ |
| 766 | const lock_t* lock); /*!< in: lock */ |
| 767 | |
| 768 | /*********************************************************************//** |
| 769 | Looks for a suitable type record lock struct by the same trx on the same page. |
| 770 | This can be used to save space when a new record lock should be set on a page: |
| 771 | no new struct is needed, if a suitable old is found. |
| 772 | @return lock or NULL */ |
| 773 | UNIV_INLINE |
| 774 | lock_t* |
| 775 | lock_rec_find_similar_on_page( |
| 776 | /*==========================*/ |
| 777 | ulint type_mode, /*!< in: lock type_mode field */ |
| 778 | ulint heap_no, /*!< in: heap number of the record */ |
| 779 | lock_t* lock, /*!< in: lock_rec_get_first_on_page() */ |
| 780 | const trx_t* trx); /*!< in: transaction */ |
| 781 | |
| 782 | /*********************************************************************//** |
| 783 | Checks if a transaction has the specified table lock, or stronger. This |
| 784 | function should only be called by the thread that owns the transaction. |
| 785 | @return lock or NULL */ |
| 786 | UNIV_INLINE |
| 787 | const lock_t* |
| 788 | lock_table_has( |
| 789 | /*===========*/ |
| 790 | const trx_t* trx, /*!< in: transaction */ |
| 791 | const dict_table_t* table, /*!< in: table */ |
| 792 | enum lock_mode mode); /*!< in: lock mode */ |
| 793 | |
| 794 | /** Set the wait status of a lock. |
| 795 | @param[in,out] lock lock that will be waited for |
| 796 | @param[in,out] trx transaction that will wait for the lock */ |
| 797 | inline void lock_set_lock_and_trx_wait(lock_t* lock, trx_t* trx) |
| 798 | { |
| 799 | ut_ad(lock); |
| 800 | ut_ad(lock->trx == trx); |
| 801 | ut_ad(trx->lock.wait_lock == NULL); |
| 802 | ut_ad(lock_mutex_own()); |
| 803 | ut_ad(trx_mutex_own(trx)); |
| 804 | |
| 805 | trx->lock.wait_lock = lock; |
| 806 | lock->type_mode |= LOCK_WAIT; |
| 807 | } |
| 808 | |
| 809 | /** Reset the wait status of a lock. |
| 810 | @param[in,out] lock lock that was possibly being waited for */ |
| 811 | inline void lock_reset_lock_and_trx_wait(lock_t* lock) |
| 812 | { |
| 813 | ut_ad(lock_get_wait(lock)); |
| 814 | ut_ad(lock_mutex_own()); |
| 815 | ut_ad(lock->trx->lock.wait_lock == NULL |
| 816 | || lock->trx->lock.wait_lock == lock); |
| 817 | lock->trx->lock.wait_lock = NULL; |
| 818 | lock->type_mode &= ~LOCK_WAIT; |
| 819 | } |
| 820 | |
| 821 | #include "lock0priv.ic" |
| 822 | |
| 823 | #endif /* lock0priv_h */ |
| 824 | |