You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2635 lines
69 KiB

Applying InnoDB Plugin 1.0.5 snapshot, part 2 From r5639 to r5685 Detailed revision comments: r5639 | marko | 2009-08-06 05:39:34 -0500 (Thu, 06 Aug 2009) | 3 lines branches/zip: mem_heap_block_free(): If innodb_use_sys_malloc is set, do not tell Valgrind that the memory is free, to avoid a bogus warning in Valgrind's built-in free() hook. r5642 | calvin | 2009-08-06 18:04:03 -0500 (Thu, 06 Aug 2009) | 2 lines branches/zip: remove duplicate "the" in comments. r5662 | marko | 2009-08-11 04:54:16 -0500 (Tue, 11 Aug 2009) | 1 line branches/zip: Bump the version number to 1.0.5 after releasing 1.0.4. r5663 | marko | 2009-08-11 06:42:37 -0500 (Tue, 11 Aug 2009) | 2 lines branches/zip: trx_general_rollback_for_mysql(): Remove the redundant parameter partial. If savept==NULL, partial==FALSE. r5670 | marko | 2009-08-12 08:16:37 -0500 (Wed, 12 Aug 2009) | 2 lines branches/zip: trx_undo_rec_copy(): Add const qualifier to undo_rec. This is a non-functional change. r5671 | marko | 2009-08-13 03:46:33 -0500 (Thu, 13 Aug 2009) | 5 lines branches/zip: ha_innobase::add_index(): Fix Bug #46557: after a successful operation, read innodb_table->flags from the newly created table object, not from the old one that was just freed. Approved by Sunny. r5681 | sunny | 2009-08-14 01:16:24 -0500 (Fri, 14 Aug 2009) | 3 lines branches/zip: When building HotBackup srv_use_sys_malloc is #ifdef out. We move access to the this variable within a !UNIV_HOTBACKUP block. r5684 | sunny | 2009-08-20 03:05:30 -0500 (Thu, 20 Aug 2009) | 10 lines branches/zip: Fix bug# 46650: Innodb assertion autoinc_lock == lock in lock_table_remove_low on INSERT SELECT We only store the autoinc locks that are granted in the transaction's autoinc lock vector. A transacton, that has been rolled back due to a deadlock because of an AUTOINC lock attempt, will not have added that lock to the vector. We need to check for that when we remove that lock. rb://145 Approved by Marko. r5685 | sunny | 2009-08-20 03:18:29 -0500 (Thu, 20 Aug 2009) | 2 lines branches/zip: Update the ChangeLog with r5684 change.
16 years ago
Implement UNIV_BLOB_DEBUG. An early version of this caught Bug #55284. This option is known to be broken when tablespaces contain off-page columns after crash recovery. It has only been tested when creating the data files from the scratch. btr_blob_dbg_t: A map from page_no:heap_no:field_no to first_blob_page_no. This map is instantiated for every clustered index in index->blobs. It is protected by index->blobs_mutex. btr_blob_dbg_msg_issue(): Issue a diagnostic message. Invoked when btr_blob_dbg_msg is set. btr_blob_dbg_rbt_insert(): Insert a btr_blob_dbg_t into index->blobs. btr_blob_dbg_rbt_delete(): Remove a btr_blob_dbg_t from index->blobs. btr_blob_dbg_cmp(): Comparator for btr_blob_dbg_t. btr_blob_dbg_add_blob(): Add a BLOB reference to the map. btr_blob_dbg_add_rec(): Add all BLOB references from a record to the map. btr_blob_dbg_print(): Display the map of BLOB references in an index. btr_blob_dbg_remove_rec(): Remove all BLOB references of a record from the map. btr_blob_dbg_is_empty(): Check that no BLOB references exist to or from a page. Disowned references from delete-marked records are tolerated. btr_blob_dbg_op(): Perform an operation on all BLOB references on a B-tree page. btr_blob_dbg_add(): Add all BLOB references from a B-tree page to the map. btr_blob_dbg_remove(): Remove all BLOB references from a B-tree page from the map. btr_blob_dbg_restore(): Restore the BLOB references after a failed page reorganize. btr_blob_dbg_set_deleted_flag(): Modify the 'deleted' flag in the BLOB references of a record. btr_blob_dbg_owner(): Own or disown a BLOB reference. btr_page_create(), btr_page_free_low(): Assert that no BLOB references exist. btr_create(): Create index->blobs for clustered indexes. btr_page_reorganize_low(): Invoke btr_blob_dbg_remove() before copying the records. Invoke btr_blob_dbg_restore() if the operation fails. btr_page_empty(), btr_lift_page_up(), btr_compress(), btr_discard_page(): Invoke btr_blob_dbg_remove(). btr_cur_del_mark_set_clust_rec(): Invoke btr_blob_dbg_set_deleted_flag(). Other cases of modifying the delete mark are either in the secondary index or during crash recovery, which we do not promise to support. btr_cur_set_ownership_of_extern_field(): Invoke btr_blob_dbg_owner(). btr_store_big_rec_extern_fields(): Invoke btr_blob_dbg_add_blob(). btr_free_externally_stored_field(): Invoke btr_blob_dbg_assert_empty() on the first BLOB page. page_cur_insert_rec_low(), page_cur_insert_rec_zip(), page_copy_rec_list_end_to_created_page(): Invoke btr_blob_dbg_add_rec(). page_cur_insert_rec_zip_reorg(), page_copy_rec_list_end(), page_copy_rec_list_start(): After failure, invoke btr_blob_dbg_remove() and btr_blob_dbg_add(). page_cur_delete_rec(): Invoke btr_blob_dbg_remove_rec(). page_delete_rec_list_end(): Invoke btr_blob_dbg_op(btr_blob_dbg_remove_rec). page_zip_reorganize(): Invoke btr_blob_dbg_remove() before copying the records. page_zip_copy_recs(): Invoke btr_blob_dbg_add(). row_upd_rec_in_place(): Invoke btr_blob_dbg_rbt_delete() and btr_blob_dbg_rbt_insert(). innobase_start_or_create_for_mysql(): Warn when UNIV_BLOB_DEBUG is enabled. rb://550 approved by Jimmy Yang
15 years ago
Applying InnoDB Plugin 1.0.5 snapshot ,part 12 From r5995 to r6043 Detailed revision comments: r5995 | marko | 2009-09-28 03:52:25 -0500 (Mon, 28 Sep 2009) | 17 lines branches/zip: Do not write to PAGE_INDEX_ID after page creation, not even when restoring an uncompressed page after a compression failure. btr_page_reorganize_low(): On compression failure, do not restore those page header fields that should not be affected by the reorganization. Instead, compare the fields. page_zip_decompress(): Add the parameter ibool all, for copying all page header fields. Pass the parameter all=TRUE on block read completion, redo log application, and page_zip_validate(); pass all=FALSE in all other cases. page_zip_reorganize(): Do not restore the uncompressed page on failure. It will be restored (to pre-modification state) by the caller anyway. rb://167, Issue #346 r5996 | marko | 2009-09-28 07:46:02 -0500 (Mon, 28 Sep 2009) | 4 lines branches/zip: Address Issue #350 in comments. lock_rec_queue_validate(), lock_rec_queue_validate(): Note that this debug code may violate the latching order and cause deadlocks. r5997 | marko | 2009-09-28 08:03:58 -0500 (Mon, 28 Sep 2009) | 12 lines branches/zip: Remove an assertion failure when the InnoDB data dictionary is inconsistent with the MySQL .frm file. ha_innobase::index_read(): When the index cannot be found, return an error. ha_innobase::change_active_index(): When prebuilt->index == NULL, set also prebuilt->index_usable = FALSE. This is not needed for correctness, because prebuilt->index_usable is only checked by row_search_for_mysql(), which requires prebuilt->index != NULL. This addresses Issue #349. Approved by Heikki Tuuri over IM. r6005 | vasil | 2009-09-29 03:09:52 -0500 (Tue, 29 Sep 2009) | 4 lines branches/zip: ChangeLog: wrap around 78th column, not earlier. r6006 | vasil | 2009-09-29 05:15:25 -0500 (Tue, 29 Sep 2009) | 4 lines branches/zip: Add ChangeLog entry for the release of 1.0.4. r6007 | vasil | 2009-09-29 08:19:59 -0500 (Tue, 29 Sep 2009) | 6 lines branches/zip: Fix the year, should be 2009. Pointed by: Calvin r6026 | marko | 2009-09-30 02:18:24 -0500 (Wed, 30 Sep 2009) | 1 line branches/zip: Add some debug assertions for checking FSEG_MAGIC_N. r6028 | marko | 2009-09-30 08:55:23 -0500 (Wed, 30 Sep 2009) | 3 lines branches/zip: recv_no_log_write: New debug flag for tracking down Mantis Issue #347. No modifications should be made to the database while recv_apply_hashed_log_recs() is about to complete. r6029 | calvin | 2009-09-30 15:32:02 -0500 (Wed, 30 Sep 2009) | 4 lines branches/zip: non-functional changes Fix typo. r6031 | marko | 2009-10-01 06:24:33 -0500 (Thu, 01 Oct 2009) | 49 lines branches/zip: Clean up after a crash during DROP INDEX. When InnoDB crashes while dropping an index, ensure that the index will be completely dropped during crash recovery. row_merge_drop_index(): Before dropping an index, rename the index to start with TEMP_INDEX_PREFIX_STR and commit the change, so that row_merge_drop_temp_indexes() will drop the index after crash recovery if the server crashes while dropping the index. fseg_inode_try_get(): New function, forked from fseg_inode_get(). Return NULL if the file segment index node is free. fseg_inode_get(): Assert that the file segment index node is not free. fseg_free_step(): If the file segment index node is already free, print a diagnostic message and return TRUE. fsp_free_seg_inode(): Write a nonzero number to FSEG_MAGIC_N, so that allocated-and-freed file segment index nodes can be better distinguished from uninitialized ones. This is rb://174, addressing Issue #348. Tested by restarting mysqld upon the completion of the added log_write_up_to() invocation below, during DROP INDEX. The index was dropped after crash recovery, and re-issuing the DROP INDEX did not crash the server. Index: btr/btr0btr.c =================================================================== --- btr/btr0btr.c (revision 6026) +++ btr/btr0btr.c (working copy) @@ -42,6 +42,7 @@ Created 6/2/1994 Heikki Tuuri #include "ibuf0ibuf.h" #include "trx0trx.h" +#include "log0log.h" /* Latching strategy of the InnoDB B-tree -------------------------------------- @@ -873,6 +874,8 @@ leaf_loop: goto leaf_loop; } + + log_write_up_to(mtr.end_lsn, LOG_WAIT_ALL_GROUPS, TRUE); top_loop: mtr_start(&mtr); r6033 | calvin | 2009-10-01 15:19:46 -0500 (Thu, 01 Oct 2009) | 4 lines branches/zip: fix a typo in error message Reported as bug#47763. r6043 | inaam | 2009-10-05 09:45:35 -0500 (Mon, 05 Oct 2009) | 12 lines branches/zip rb://176 Do not invalidate buffer pool while an LRU batch is active. Added code to buf_pool_invalidate() to wait for the running batches to finish. This patch also resets the state of buf_pool struct at invalidation. This addresses the concern where buf_pool->freed_page_clock becomes non-zero because we read in a system tablespace page for file format info at startup. Approved by: Marko
16 years ago
Implement UNIV_BLOB_DEBUG. An early version of this caught Bug #55284. This option is known to be broken when tablespaces contain off-page columns after crash recovery. It has only been tested when creating the data files from the scratch. btr_blob_dbg_t: A map from page_no:heap_no:field_no to first_blob_page_no. This map is instantiated for every clustered index in index->blobs. It is protected by index->blobs_mutex. btr_blob_dbg_msg_issue(): Issue a diagnostic message. Invoked when btr_blob_dbg_msg is set. btr_blob_dbg_rbt_insert(): Insert a btr_blob_dbg_t into index->blobs. btr_blob_dbg_rbt_delete(): Remove a btr_blob_dbg_t from index->blobs. btr_blob_dbg_cmp(): Comparator for btr_blob_dbg_t. btr_blob_dbg_add_blob(): Add a BLOB reference to the map. btr_blob_dbg_add_rec(): Add all BLOB references from a record to the map. btr_blob_dbg_print(): Display the map of BLOB references in an index. btr_blob_dbg_remove_rec(): Remove all BLOB references of a record from the map. btr_blob_dbg_is_empty(): Check that no BLOB references exist to or from a page. Disowned references from delete-marked records are tolerated. btr_blob_dbg_op(): Perform an operation on all BLOB references on a B-tree page. btr_blob_dbg_add(): Add all BLOB references from a B-tree page to the map. btr_blob_dbg_remove(): Remove all BLOB references from a B-tree page from the map. btr_blob_dbg_restore(): Restore the BLOB references after a failed page reorganize. btr_blob_dbg_set_deleted_flag(): Modify the 'deleted' flag in the BLOB references of a record. btr_blob_dbg_owner(): Own or disown a BLOB reference. btr_page_create(), btr_page_free_low(): Assert that no BLOB references exist. btr_create(): Create index->blobs for clustered indexes. btr_page_reorganize_low(): Invoke btr_blob_dbg_remove() before copying the records. Invoke btr_blob_dbg_restore() if the operation fails. btr_page_empty(), btr_lift_page_up(), btr_compress(), btr_discard_page(): Invoke btr_blob_dbg_remove(). btr_cur_del_mark_set_clust_rec(): Invoke btr_blob_dbg_set_deleted_flag(). Other cases of modifying the delete mark are either in the secondary index or during crash recovery, which we do not promise to support. btr_cur_set_ownership_of_extern_field(): Invoke btr_blob_dbg_owner(). btr_store_big_rec_extern_fields(): Invoke btr_blob_dbg_add_blob(). btr_free_externally_stored_field(): Invoke btr_blob_dbg_assert_empty() on the first BLOB page. page_cur_insert_rec_low(), page_cur_insert_rec_zip(), page_copy_rec_list_end_to_created_page(): Invoke btr_blob_dbg_add_rec(). page_cur_insert_rec_zip_reorg(), page_copy_rec_list_end(), page_copy_rec_list_start(): After failure, invoke btr_blob_dbg_remove() and btr_blob_dbg_add(). page_cur_delete_rec(): Invoke btr_blob_dbg_remove_rec(). page_delete_rec_list_end(): Invoke btr_blob_dbg_op(btr_blob_dbg_remove_rec). page_zip_reorganize(): Invoke btr_blob_dbg_remove() before copying the records. page_zip_copy_recs(): Invoke btr_blob_dbg_add(). row_upd_rec_in_place(): Invoke btr_blob_dbg_rbt_delete() and btr_blob_dbg_rbt_insert(). innobase_start_or_create_for_mysql(): Warn when UNIV_BLOB_DEBUG is enabled. rb://550 approved by Jimmy Yang
15 years ago
Implement UNIV_BLOB_DEBUG. An early version of this caught Bug #55284. This option is known to be broken when tablespaces contain off-page columns after crash recovery. It has only been tested when creating the data files from the scratch. btr_blob_dbg_t: A map from page_no:heap_no:field_no to first_blob_page_no. This map is instantiated for every clustered index in index->blobs. It is protected by index->blobs_mutex. btr_blob_dbg_msg_issue(): Issue a diagnostic message. Invoked when btr_blob_dbg_msg is set. btr_blob_dbg_rbt_insert(): Insert a btr_blob_dbg_t into index->blobs. btr_blob_dbg_rbt_delete(): Remove a btr_blob_dbg_t from index->blobs. btr_blob_dbg_cmp(): Comparator for btr_blob_dbg_t. btr_blob_dbg_add_blob(): Add a BLOB reference to the map. btr_blob_dbg_add_rec(): Add all BLOB references from a record to the map. btr_blob_dbg_print(): Display the map of BLOB references in an index. btr_blob_dbg_remove_rec(): Remove all BLOB references of a record from the map. btr_blob_dbg_is_empty(): Check that no BLOB references exist to or from a page. Disowned references from delete-marked records are tolerated. btr_blob_dbg_op(): Perform an operation on all BLOB references on a B-tree page. btr_blob_dbg_add(): Add all BLOB references from a B-tree page to the map. btr_blob_dbg_remove(): Remove all BLOB references from a B-tree page from the map. btr_blob_dbg_restore(): Restore the BLOB references after a failed page reorganize. btr_blob_dbg_set_deleted_flag(): Modify the 'deleted' flag in the BLOB references of a record. btr_blob_dbg_owner(): Own or disown a BLOB reference. btr_page_create(), btr_page_free_low(): Assert that no BLOB references exist. btr_create(): Create index->blobs for clustered indexes. btr_page_reorganize_low(): Invoke btr_blob_dbg_remove() before copying the records. Invoke btr_blob_dbg_restore() if the operation fails. btr_page_empty(), btr_lift_page_up(), btr_compress(), btr_discard_page(): Invoke btr_blob_dbg_remove(). btr_cur_del_mark_set_clust_rec(): Invoke btr_blob_dbg_set_deleted_flag(). Other cases of modifying the delete mark are either in the secondary index or during crash recovery, which we do not promise to support. btr_cur_set_ownership_of_extern_field(): Invoke btr_blob_dbg_owner(). btr_store_big_rec_extern_fields(): Invoke btr_blob_dbg_add_blob(). btr_free_externally_stored_field(): Invoke btr_blob_dbg_assert_empty() on the first BLOB page. page_cur_insert_rec_low(), page_cur_insert_rec_zip(), page_copy_rec_list_end_to_created_page(): Invoke btr_blob_dbg_add_rec(). page_cur_insert_rec_zip_reorg(), page_copy_rec_list_end(), page_copy_rec_list_start(): After failure, invoke btr_blob_dbg_remove() and btr_blob_dbg_add(). page_cur_delete_rec(): Invoke btr_blob_dbg_remove_rec(). page_delete_rec_list_end(): Invoke btr_blob_dbg_op(btr_blob_dbg_remove_rec). page_zip_reorganize(): Invoke btr_blob_dbg_remove() before copying the records. page_zip_copy_recs(): Invoke btr_blob_dbg_add(). row_upd_rec_in_place(): Invoke btr_blob_dbg_rbt_delete() and btr_blob_dbg_rbt_insert(). innobase_start_or_create_for_mysql(): Warn when UNIV_BLOB_DEBUG is enabled. rb://550 approved by Jimmy Yang
15 years ago
Applying InnoDB Plugin 1.0.5 snapshot ,part 12 From r5995 to r6043 Detailed revision comments: r5995 | marko | 2009-09-28 03:52:25 -0500 (Mon, 28 Sep 2009) | 17 lines branches/zip: Do not write to PAGE_INDEX_ID after page creation, not even when restoring an uncompressed page after a compression failure. btr_page_reorganize_low(): On compression failure, do not restore those page header fields that should not be affected by the reorganization. Instead, compare the fields. page_zip_decompress(): Add the parameter ibool all, for copying all page header fields. Pass the parameter all=TRUE on block read completion, redo log application, and page_zip_validate(); pass all=FALSE in all other cases. page_zip_reorganize(): Do not restore the uncompressed page on failure. It will be restored (to pre-modification state) by the caller anyway. rb://167, Issue #346 r5996 | marko | 2009-09-28 07:46:02 -0500 (Mon, 28 Sep 2009) | 4 lines branches/zip: Address Issue #350 in comments. lock_rec_queue_validate(), lock_rec_queue_validate(): Note that this debug code may violate the latching order and cause deadlocks. r5997 | marko | 2009-09-28 08:03:58 -0500 (Mon, 28 Sep 2009) | 12 lines branches/zip: Remove an assertion failure when the InnoDB data dictionary is inconsistent with the MySQL .frm file. ha_innobase::index_read(): When the index cannot be found, return an error. ha_innobase::change_active_index(): When prebuilt->index == NULL, set also prebuilt->index_usable = FALSE. This is not needed for correctness, because prebuilt->index_usable is only checked by row_search_for_mysql(), which requires prebuilt->index != NULL. This addresses Issue #349. Approved by Heikki Tuuri over IM. r6005 | vasil | 2009-09-29 03:09:52 -0500 (Tue, 29 Sep 2009) | 4 lines branches/zip: ChangeLog: wrap around 78th column, not earlier. r6006 | vasil | 2009-09-29 05:15:25 -0500 (Tue, 29 Sep 2009) | 4 lines branches/zip: Add ChangeLog entry for the release of 1.0.4. r6007 | vasil | 2009-09-29 08:19:59 -0500 (Tue, 29 Sep 2009) | 6 lines branches/zip: Fix the year, should be 2009. Pointed by: Calvin r6026 | marko | 2009-09-30 02:18:24 -0500 (Wed, 30 Sep 2009) | 1 line branches/zip: Add some debug assertions for checking FSEG_MAGIC_N. r6028 | marko | 2009-09-30 08:55:23 -0500 (Wed, 30 Sep 2009) | 3 lines branches/zip: recv_no_log_write: New debug flag for tracking down Mantis Issue #347. No modifications should be made to the database while recv_apply_hashed_log_recs() is about to complete. r6029 | calvin | 2009-09-30 15:32:02 -0500 (Wed, 30 Sep 2009) | 4 lines branches/zip: non-functional changes Fix typo. r6031 | marko | 2009-10-01 06:24:33 -0500 (Thu, 01 Oct 2009) | 49 lines branches/zip: Clean up after a crash during DROP INDEX. When InnoDB crashes while dropping an index, ensure that the index will be completely dropped during crash recovery. row_merge_drop_index(): Before dropping an index, rename the index to start with TEMP_INDEX_PREFIX_STR and commit the change, so that row_merge_drop_temp_indexes() will drop the index after crash recovery if the server crashes while dropping the index. fseg_inode_try_get(): New function, forked from fseg_inode_get(). Return NULL if the file segment index node is free. fseg_inode_get(): Assert that the file segment index node is not free. fseg_free_step(): If the file segment index node is already free, print a diagnostic message and return TRUE. fsp_free_seg_inode(): Write a nonzero number to FSEG_MAGIC_N, so that allocated-and-freed file segment index nodes can be better distinguished from uninitialized ones. This is rb://174, addressing Issue #348. Tested by restarting mysqld upon the completion of the added log_write_up_to() invocation below, during DROP INDEX. The index was dropped after crash recovery, and re-issuing the DROP INDEX did not crash the server. Index: btr/btr0btr.c =================================================================== --- btr/btr0btr.c (revision 6026) +++ btr/btr0btr.c (working copy) @@ -42,6 +42,7 @@ Created 6/2/1994 Heikki Tuuri #include "ibuf0ibuf.h" #include "trx0trx.h" +#include "log0log.h" /* Latching strategy of the InnoDB B-tree -------------------------------------- @@ -873,6 +874,8 @@ leaf_loop: goto leaf_loop; } + + log_write_up_to(mtr.end_lsn, LOG_WAIT_ALL_GROUPS, TRUE); top_loop: mtr_start(&mtr); r6033 | calvin | 2009-10-01 15:19:46 -0500 (Thu, 01 Oct 2009) | 4 lines branches/zip: fix a typo in error message Reported as bug#47763. r6043 | inaam | 2009-10-05 09:45:35 -0500 (Mon, 05 Oct 2009) | 12 lines branches/zip rb://176 Do not invalidate buffer pool while an LRU batch is active. Added code to buf_pool_invalidate() to wait for the running batches to finish. This patch also resets the state of buf_pool struct at invalidation. This addresses the concern where buf_pool->freed_page_clock becomes non-zero because we read in a system tablespace page for file format info at startup. Approved by: Marko
16 years ago
Implement UNIV_BLOB_DEBUG. An early version of this caught Bug #55284. This option is known to be broken when tablespaces contain off-page columns after crash recovery. It has only been tested when creating the data files from the scratch. btr_blob_dbg_t: A map from page_no:heap_no:field_no to first_blob_page_no. This map is instantiated for every clustered index in index->blobs. It is protected by index->blobs_mutex. btr_blob_dbg_msg_issue(): Issue a diagnostic message. Invoked when btr_blob_dbg_msg is set. btr_blob_dbg_rbt_insert(): Insert a btr_blob_dbg_t into index->blobs. btr_blob_dbg_rbt_delete(): Remove a btr_blob_dbg_t from index->blobs. btr_blob_dbg_cmp(): Comparator for btr_blob_dbg_t. btr_blob_dbg_add_blob(): Add a BLOB reference to the map. btr_blob_dbg_add_rec(): Add all BLOB references from a record to the map. btr_blob_dbg_print(): Display the map of BLOB references in an index. btr_blob_dbg_remove_rec(): Remove all BLOB references of a record from the map. btr_blob_dbg_is_empty(): Check that no BLOB references exist to or from a page. Disowned references from delete-marked records are tolerated. btr_blob_dbg_op(): Perform an operation on all BLOB references on a B-tree page. btr_blob_dbg_add(): Add all BLOB references from a B-tree page to the map. btr_blob_dbg_remove(): Remove all BLOB references from a B-tree page from the map. btr_blob_dbg_restore(): Restore the BLOB references after a failed page reorganize. btr_blob_dbg_set_deleted_flag(): Modify the 'deleted' flag in the BLOB references of a record. btr_blob_dbg_owner(): Own or disown a BLOB reference. btr_page_create(), btr_page_free_low(): Assert that no BLOB references exist. btr_create(): Create index->blobs for clustered indexes. btr_page_reorganize_low(): Invoke btr_blob_dbg_remove() before copying the records. Invoke btr_blob_dbg_restore() if the operation fails. btr_page_empty(), btr_lift_page_up(), btr_compress(), btr_discard_page(): Invoke btr_blob_dbg_remove(). btr_cur_del_mark_set_clust_rec(): Invoke btr_blob_dbg_set_deleted_flag(). Other cases of modifying the delete mark are either in the secondary index or during crash recovery, which we do not promise to support. btr_cur_set_ownership_of_extern_field(): Invoke btr_blob_dbg_owner(). btr_store_big_rec_extern_fields(): Invoke btr_blob_dbg_add_blob(). btr_free_externally_stored_field(): Invoke btr_blob_dbg_assert_empty() on the first BLOB page. page_cur_insert_rec_low(), page_cur_insert_rec_zip(), page_copy_rec_list_end_to_created_page(): Invoke btr_blob_dbg_add_rec(). page_cur_insert_rec_zip_reorg(), page_copy_rec_list_end(), page_copy_rec_list_start(): After failure, invoke btr_blob_dbg_remove() and btr_blob_dbg_add(). page_cur_delete_rec(): Invoke btr_blob_dbg_remove_rec(). page_delete_rec_list_end(): Invoke btr_blob_dbg_op(btr_blob_dbg_remove_rec). page_zip_reorganize(): Invoke btr_blob_dbg_remove() before copying the records. page_zip_copy_recs(): Invoke btr_blob_dbg_add(). row_upd_rec_in_place(): Invoke btr_blob_dbg_rbt_delete() and btr_blob_dbg_rbt_insert(). innobase_start_or_create_for_mysql(): Warn when UNIV_BLOB_DEBUG is enabled. rb://550 approved by Jimmy Yang
15 years ago
Implement UNIV_BLOB_DEBUG. An early version of this caught Bug #55284. This option is known to be broken when tablespaces contain off-page columns after crash recovery. It has only been tested when creating the data files from the scratch. btr_blob_dbg_t: A map from page_no:heap_no:field_no to first_blob_page_no. This map is instantiated for every clustered index in index->blobs. It is protected by index->blobs_mutex. btr_blob_dbg_msg_issue(): Issue a diagnostic message. Invoked when btr_blob_dbg_msg is set. btr_blob_dbg_rbt_insert(): Insert a btr_blob_dbg_t into index->blobs. btr_blob_dbg_rbt_delete(): Remove a btr_blob_dbg_t from index->blobs. btr_blob_dbg_cmp(): Comparator for btr_blob_dbg_t. btr_blob_dbg_add_blob(): Add a BLOB reference to the map. btr_blob_dbg_add_rec(): Add all BLOB references from a record to the map. btr_blob_dbg_print(): Display the map of BLOB references in an index. btr_blob_dbg_remove_rec(): Remove all BLOB references of a record from the map. btr_blob_dbg_is_empty(): Check that no BLOB references exist to or from a page. Disowned references from delete-marked records are tolerated. btr_blob_dbg_op(): Perform an operation on all BLOB references on a B-tree page. btr_blob_dbg_add(): Add all BLOB references from a B-tree page to the map. btr_blob_dbg_remove(): Remove all BLOB references from a B-tree page from the map. btr_blob_dbg_restore(): Restore the BLOB references after a failed page reorganize. btr_blob_dbg_set_deleted_flag(): Modify the 'deleted' flag in the BLOB references of a record. btr_blob_dbg_owner(): Own or disown a BLOB reference. btr_page_create(), btr_page_free_low(): Assert that no BLOB references exist. btr_create(): Create index->blobs for clustered indexes. btr_page_reorganize_low(): Invoke btr_blob_dbg_remove() before copying the records. Invoke btr_blob_dbg_restore() if the operation fails. btr_page_empty(), btr_lift_page_up(), btr_compress(), btr_discard_page(): Invoke btr_blob_dbg_remove(). btr_cur_del_mark_set_clust_rec(): Invoke btr_blob_dbg_set_deleted_flag(). Other cases of modifying the delete mark are either in the secondary index or during crash recovery, which we do not promise to support. btr_cur_set_ownership_of_extern_field(): Invoke btr_blob_dbg_owner(). btr_store_big_rec_extern_fields(): Invoke btr_blob_dbg_add_blob(). btr_free_externally_stored_field(): Invoke btr_blob_dbg_assert_empty() on the first BLOB page. page_cur_insert_rec_low(), page_cur_insert_rec_zip(), page_copy_rec_list_end_to_created_page(): Invoke btr_blob_dbg_add_rec(). page_cur_insert_rec_zip_reorg(), page_copy_rec_list_end(), page_copy_rec_list_start(): After failure, invoke btr_blob_dbg_remove() and btr_blob_dbg_add(). page_cur_delete_rec(): Invoke btr_blob_dbg_remove_rec(). page_delete_rec_list_end(): Invoke btr_blob_dbg_op(btr_blob_dbg_remove_rec). page_zip_reorganize(): Invoke btr_blob_dbg_remove() before copying the records. page_zip_copy_recs(): Invoke btr_blob_dbg_add(). row_upd_rec_in_place(): Invoke btr_blob_dbg_rbt_delete() and btr_blob_dbg_rbt_insert(). innobase_start_or_create_for_mysql(): Warn when UNIV_BLOB_DEBUG is enabled. rb://550 approved by Jimmy Yang
15 years ago
  1. /*****************************************************************************
  2. Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved.
  3. This program is free software; you can redistribute it and/or modify it under
  4. the terms of the GNU General Public License as published by the Free Software
  5. Foundation; version 2 of the License.
  6. This program is distributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along with
  10. this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  11. Place, Suite 330, Boston, MA 02111-1307 USA
  12. *****************************************************************************/
  13. /**************************************************//**
  14. @file page/page0page.c
  15. Index page routines
  16. Created 2/2/1994 Heikki Tuuri
  17. *******************************************************/
  18. #define THIS_MODULE
  19. #include "page0page.h"
  20. #ifdef UNIV_NONINL
  21. #include "page0page.ic"
  22. #endif
  23. #undef THIS_MODULE
  24. #include "page0cur.h"
  25. #include "page0zip.h"
  26. #include "buf0buf.h"
  27. #include "btr0btr.h"
  28. #ifndef UNIV_HOTBACKUP
  29. # include "srv0srv.h"
  30. # include "lock0lock.h"
  31. # include "fut0lst.h"
  32. # include "btr0sea.h"
  33. #endif /* !UNIV_HOTBACKUP */
  34. /* THE INDEX PAGE
  35. ==============
  36. The index page consists of a page header which contains the page's
  37. id and other information. On top of it are the index records
  38. in a heap linked into a one way linear list according to alphabetic order.
  39. Just below page end is an array of pointers which we call page directory,
  40. to about every sixth record in the list. The pointers are placed in
  41. the directory in the alphabetical order of the records pointed to,
  42. enabling us to make binary search using the array. Each slot n:o I
  43. in the directory points to a record, where a 4-bit field contains a count
  44. of those records which are in the linear list between pointer I and
  45. the pointer I - 1 in the directory, including the record
  46. pointed to by pointer I and not including the record pointed to by I - 1.
  47. We say that the record pointed to by slot I, or that slot I, owns
  48. these records. The count is always kept in the range 4 to 8, with
  49. the exception that it is 1 for the first slot, and 1--8 for the second slot.
  50. An essentially binary search can be performed in the list of index
  51. records, like we could do if we had pointer to every record in the
  52. page directory. The data structure is, however, more efficient when
  53. we are doing inserts, because most inserts are just pushed on a heap.
  54. Only every 8th insert requires block move in the directory pointer
  55. table, which itself is quite small. A record is deleted from the page
  56. by just taking it off the linear list and updating the number of owned
  57. records-field of the record which owns it, and updating the page directory,
  58. if necessary. A special case is the one when the record owns itself.
  59. Because the overhead of inserts is so small, we may also increase the
  60. page size from the projected default of 8 kB to 64 kB without too
  61. much loss of efficiency in inserts. Bigger page becomes actual
  62. when the disk transfer rate compared to seek and latency time rises.
  63. On the present system, the page size is set so that the page transfer
  64. time (3 ms) is 20 % of the disk random access time (15 ms).
  65. When the page is split, merged, or becomes full but contains deleted
  66. records, we have to reorganize the page.
  67. Assuming a page size of 8 kB, a typical index page of a secondary
  68. index contains 300 index entries, and the size of the page directory
  69. is 50 x 4 bytes = 200 bytes. */
  70. /***************************************************************//**
  71. Looks for the directory slot which owns the given record.
  72. @return the directory slot number */
  73. UNIV_INTERN
  74. ulint
  75. page_dir_find_owner_slot(
  76. /*=====================*/
  77. const rec_t* rec) /*!< in: the physical record */
  78. {
  79. const page_t* page;
  80. register uint16 rec_offs_bytes;
  81. register const page_dir_slot_t* slot;
  82. register const page_dir_slot_t* first_slot;
  83. register const rec_t* r = rec;
  84. ut_ad(page_rec_check(rec));
  85. page = page_align(rec);
  86. first_slot = page_dir_get_nth_slot(page, 0);
  87. slot = page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1);
  88. if (page_is_comp(page)) {
  89. while (rec_get_n_owned_new(r) == 0) {
  90. r = rec_get_next_ptr_const(r, TRUE);
  91. ut_ad(r >= page + PAGE_NEW_SUPREMUM);
  92. ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
  93. }
  94. } else {
  95. while (rec_get_n_owned_old(r) == 0) {
  96. r = rec_get_next_ptr_const(r, FALSE);
  97. ut_ad(r >= page + PAGE_OLD_SUPREMUM);
  98. ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR));
  99. }
  100. }
  101. rec_offs_bytes = mach_encode_2(r - page);
  102. while (UNIV_LIKELY(*(uint16*) slot != rec_offs_bytes)) {
  103. if (UNIV_UNLIKELY(slot == first_slot)) {
  104. fprintf(stderr,
  105. "InnoDB: Probable data corruption on"
  106. " page %lu\n"
  107. "InnoDB: Original record ",
  108. (ulong) page_get_page_no(page));
  109. if (page_is_comp(page)) {
  110. fputs("(compact record)", stderr);
  111. } else {
  112. rec_print_old(stderr, rec);
  113. }
  114. fputs("\n"
  115. "InnoDB: on that page.\n"
  116. "InnoDB: Cannot find the dir slot for record ",
  117. stderr);
  118. if (page_is_comp(page)) {
  119. fputs("(compact record)", stderr);
  120. } else {
  121. rec_print_old(stderr, page
  122. + mach_decode_2(rec_offs_bytes));
  123. }
  124. fputs("\n"
  125. "InnoDB: on that page!\n", stderr);
  126. buf_page_print(page, 0);
  127. ut_error;
  128. }
  129. slot += PAGE_DIR_SLOT_SIZE;
  130. }
  131. return(((ulint) (first_slot - slot)) / PAGE_DIR_SLOT_SIZE);
  132. }
  133. /**************************************************************//**
  134. Used to check the consistency of a directory slot.
  135. @return TRUE if succeed */
  136. static
  137. ibool
  138. page_dir_slot_check(
  139. /*================*/
  140. page_dir_slot_t* slot) /*!< in: slot */
  141. {
  142. page_t* page;
  143. ulint n_slots;
  144. ulint n_owned;
  145. ut_a(slot);
  146. page = page_align(slot);
  147. n_slots = page_dir_get_n_slots(page);
  148. ut_a(slot <= page_dir_get_nth_slot(page, 0));
  149. ut_a(slot >= page_dir_get_nth_slot(page, n_slots - 1));
  150. ut_a(page_rec_check(page_dir_slot_get_rec(slot)));
  151. if (page_is_comp(page)) {
  152. n_owned = rec_get_n_owned_new(page_dir_slot_get_rec(slot));
  153. } else {
  154. n_owned = rec_get_n_owned_old(page_dir_slot_get_rec(slot));
  155. }
  156. if (slot == page_dir_get_nth_slot(page, 0)) {
  157. ut_a(n_owned == 1);
  158. } else if (slot == page_dir_get_nth_slot(page, n_slots - 1)) {
  159. ut_a(n_owned >= 1);
  160. ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
  161. } else {
  162. ut_a(n_owned >= PAGE_DIR_SLOT_MIN_N_OWNED);
  163. ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED);
  164. }
  165. return(TRUE);
  166. }
  167. /*************************************************************//**
  168. Sets the max trx id field value. */
  169. UNIV_INTERN
  170. void
  171. page_set_max_trx_id(
  172. /*================*/
  173. buf_block_t* block, /*!< in/out: page */
  174. page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
  175. trx_id_t trx_id, /*!< in: transaction id */
  176. mtr_t* mtr) /*!< in/out: mini-transaction, or NULL */
  177. {
  178. page_t* page = buf_block_get_frame(block);
  179. #ifndef UNIV_HOTBACKUP
  180. const ibool is_hashed = block->is_hashed;
  181. if (is_hashed) {
  182. rw_lock_x_lock(&btr_search_latch);
  183. }
  184. ut_ad(!mtr || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
  185. #endif /* !UNIV_HOTBACKUP */
  186. /* It is not necessary to write this change to the redo log, as
  187. during a database recovery we assume that the max trx id of every
  188. page is the maximum trx id assigned before the crash. */
  189. if (UNIV_LIKELY_NULL(page_zip)) {
  190. mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
  191. page_zip_write_header(page_zip,
  192. page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
  193. 8, mtr);
  194. #ifndef UNIV_HOTBACKUP
  195. } else if (mtr) {
  196. mlog_write_dulint(page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
  197. trx_id, mtr);
  198. #endif /* !UNIV_HOTBACKUP */
  199. } else {
  200. mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id);
  201. }
  202. #ifndef UNIV_HOTBACKUP
  203. if (is_hashed) {
  204. rw_lock_x_unlock(&btr_search_latch);
  205. }
  206. #endif /* !UNIV_HOTBACKUP */
  207. }
  208. /************************************************************//**
  209. Allocates a block of memory from the heap of an index page.
  210. @return pointer to start of allocated buffer, or NULL if allocation fails */
  211. UNIV_INTERN
  212. byte*
  213. page_mem_alloc_heap(
  214. /*================*/
  215. page_t* page, /*!< in/out: index page */
  216. page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
  217. space available for inserting the record,
  218. or NULL */
  219. ulint need, /*!< in: total number of bytes needed */
  220. ulint* heap_no)/*!< out: this contains the heap number
  221. of the allocated record
  222. if allocation succeeds */
  223. {
  224. byte* block;
  225. ulint avl_space;
  226. ut_ad(page && heap_no);
  227. avl_space = page_get_max_insert_size(page, 1);
  228. if (avl_space >= need) {
  229. block = page_header_get_ptr(page, PAGE_HEAP_TOP);
  230. page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP,
  231. block + need);
  232. *heap_no = page_dir_get_n_heap(page);
  233. page_dir_set_n_heap(page, page_zip, 1 + *heap_no);
  234. return(block);
  235. }
  236. return(NULL);
  237. }
  238. #ifndef UNIV_HOTBACKUP
  239. /**********************************************************//**
  240. Writes a log record of page creation. */
  241. UNIV_INLINE
  242. void
  243. page_create_write_log(
  244. /*==================*/
  245. buf_frame_t* frame, /*!< in: a buffer frame where the page is
  246. created */
  247. mtr_t* mtr, /*!< in: mini-transaction handle */
  248. ibool comp) /*!< in: TRUE=compact page format */
  249. {
  250. mlog_write_initial_log_record(frame, comp
  251. ? MLOG_COMP_PAGE_CREATE
  252. : MLOG_PAGE_CREATE, mtr);
  253. }
  254. #else /* !UNIV_HOTBACKUP */
  255. # define page_create_write_log(frame,mtr,comp) ((void) 0)
  256. #endif /* !UNIV_HOTBACKUP */
  257. /***********************************************************//**
  258. Parses a redo log record of creating a page.
  259. @return end of log record or NULL */
  260. UNIV_INTERN
  261. byte*
  262. page_parse_create(
  263. /*==============*/
  264. byte* ptr, /*!< in: buffer */
  265. byte* end_ptr __attribute__((unused)), /*!< in: buffer end */
  266. ulint comp, /*!< in: nonzero=compact page format */
  267. buf_block_t* block, /*!< in: block or NULL */
  268. mtr_t* mtr) /*!< in: mtr or NULL */
  269. {
  270. ut_ad(ptr && end_ptr);
  271. /* The record is empty, except for the record initial part */
  272. if (block) {
  273. page_create(block, mtr, comp);
  274. }
  275. return(ptr);
  276. }
  277. /**********************************************************//**
  278. The index page creation function.
  279. @return pointer to the page */
  280. static
  281. page_t*
  282. page_create_low(
  283. /*============*/
  284. buf_block_t* block, /*!< in: a buffer block where the
  285. page is created */
  286. ulint comp) /*!< in: nonzero=compact page format */
  287. {
  288. page_dir_slot_t* slot;
  289. mem_heap_t* heap;
  290. dtuple_t* tuple;
  291. dfield_t* field;
  292. byte* heap_top;
  293. rec_t* infimum_rec;
  294. rec_t* supremum_rec;
  295. page_t* page;
  296. dict_index_t* index;
  297. ulint* offsets;
  298. ut_ad(block);
  299. #if PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA
  300. # error "PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA"
  301. #endif
  302. #if PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA
  303. # error "PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA"
  304. #endif
  305. /* The infimum and supremum records use a dummy index. */
  306. if (UNIV_LIKELY(comp)) {
  307. index = dict_ind_compact;
  308. } else {
  309. index = dict_ind_redundant;
  310. }
  311. /* 1. INCREMENT MODIFY CLOCK */
  312. buf_block_modify_clock_inc(block);
  313. page = buf_block_get_frame(block);
  314. fil_page_set_type(page, FIL_PAGE_INDEX);
  315. heap = mem_heap_create(200);
  316. /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */
  317. /* Create first a data tuple for infimum record */
  318. tuple = dtuple_create(heap, 1);
  319. dtuple_set_info_bits(tuple, REC_STATUS_INFIMUM);
  320. field = dtuple_get_nth_field(tuple, 0);
  321. dfield_set_data(field, "infimum", 8);
  322. dtype_set(dfield_get_type(field),
  323. DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, 8);
  324. /* Set the corresponding physical record to its place in the page
  325. record heap */
  326. heap_top = page + PAGE_DATA;
  327. infimum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
  328. if (UNIV_LIKELY(comp)) {
  329. ut_a(infimum_rec == page + PAGE_NEW_INFIMUM);
  330. rec_set_n_owned_new(infimum_rec, NULL, 1);
  331. rec_set_heap_no_new(infimum_rec, 0);
  332. } else {
  333. ut_a(infimum_rec == page + PAGE_OLD_INFIMUM);
  334. rec_set_n_owned_old(infimum_rec, 1);
  335. rec_set_heap_no_old(infimum_rec, 0);
  336. }
  337. offsets = rec_get_offsets(infimum_rec, index, NULL,
  338. ULINT_UNDEFINED, &heap);
  339. heap_top = rec_get_end(infimum_rec, offsets);
  340. /* Create then a tuple for supremum */
  341. tuple = dtuple_create(heap, 1);
  342. dtuple_set_info_bits(tuple, REC_STATUS_SUPREMUM);
  343. field = dtuple_get_nth_field(tuple, 0);
  344. dfield_set_data(field, "supremum", comp ? 8 : 9);
  345. dtype_set(dfield_get_type(field),
  346. DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, comp ? 8 : 9);
  347. supremum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0);
  348. if (UNIV_LIKELY(comp)) {
  349. ut_a(supremum_rec == page + PAGE_NEW_SUPREMUM);
  350. rec_set_n_owned_new(supremum_rec, NULL, 1);
  351. rec_set_heap_no_new(supremum_rec, 1);
  352. } else {
  353. ut_a(supremum_rec == page + PAGE_OLD_SUPREMUM);
  354. rec_set_n_owned_old(supremum_rec, 1);
  355. rec_set_heap_no_old(supremum_rec, 1);
  356. }
  357. offsets = rec_get_offsets(supremum_rec, index, offsets,
  358. ULINT_UNDEFINED, &heap);
  359. heap_top = rec_get_end(supremum_rec, offsets);
  360. ut_ad(heap_top == page
  361. + (comp ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END));
  362. mem_heap_free(heap);
  363. /* 4. INITIALIZE THE PAGE */
  364. page_header_set_field(page, NULL, PAGE_N_DIR_SLOTS, 2);
  365. page_header_set_ptr(page, NULL, PAGE_HEAP_TOP, heap_top);
  366. page_header_set_field(page, NULL, PAGE_N_HEAP, comp
  367. ? 0x8000 | PAGE_HEAP_NO_USER_LOW
  368. : PAGE_HEAP_NO_USER_LOW);
  369. page_header_set_ptr(page, NULL, PAGE_FREE, NULL);
  370. page_header_set_field(page, NULL, PAGE_GARBAGE, 0);
  371. page_header_set_ptr(page, NULL, PAGE_LAST_INSERT, NULL);
  372. page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION);
  373. page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0);
  374. page_header_set_field(page, NULL, PAGE_N_RECS, 0);
  375. page_set_max_trx_id(block, NULL, ut_dulint_zero, NULL);
  376. memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START
  377. - page_offset(heap_top));
  378. /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
  379. /* Set the slots to point to infimum and supremum. */
  380. slot = page_dir_get_nth_slot(page, 0);
  381. page_dir_slot_set_rec(slot, infimum_rec);
  382. slot = page_dir_get_nth_slot(page, 1);
  383. page_dir_slot_set_rec(slot, supremum_rec);
  384. /* Set the next pointers in infimum and supremum */
  385. if (UNIV_LIKELY(comp)) {
  386. rec_set_next_offs_new(infimum_rec, PAGE_NEW_SUPREMUM);
  387. rec_set_next_offs_new(supremum_rec, 0);
  388. } else {
  389. rec_set_next_offs_old(infimum_rec, PAGE_OLD_SUPREMUM);
  390. rec_set_next_offs_old(supremum_rec, 0);
  391. }
  392. return(page);
  393. }
  394. /**********************************************************//**
  395. Create an uncompressed B-tree index page.
  396. @return pointer to the page */
  397. UNIV_INTERN
  398. page_t*
  399. page_create(
  400. /*========*/
  401. buf_block_t* block, /*!< in: a buffer block where the
  402. page is created */
  403. mtr_t* mtr, /*!< in: mini-transaction handle */
  404. ulint comp) /*!< in: nonzero=compact page format */
  405. {
  406. page_create_write_log(buf_block_get_frame(block), mtr, comp);
  407. return(page_create_low(block, comp));
  408. }
  409. /**********************************************************//**
  410. Create a compressed B-tree index page.
  411. @return pointer to the page */
  412. UNIV_INTERN
  413. page_t*
  414. page_create_zip(
  415. /*============*/
  416. buf_block_t* block, /*!< in/out: a buffer frame where the
  417. page is created */
  418. dict_index_t* index, /*!< in: the index of the page */
  419. ulint level, /*!< in: the B-tree level of the page */
  420. mtr_t* mtr) /*!< in: mini-transaction handle */
  421. {
  422. page_t* page;
  423. page_zip_des_t* page_zip = buf_block_get_page_zip(block);
  424. ut_ad(block);
  425. ut_ad(page_zip);
  426. ut_ad(index);
  427. ut_ad(dict_table_is_comp(index->table));
  428. page = page_create_low(block, TRUE);
  429. mach_write_to_2(page + PAGE_HEADER + PAGE_LEVEL, level);
  430. if (UNIV_UNLIKELY(!page_zip_compress(page_zip, page, index, mtr))) {
  431. /* The compression of a newly created page
  432. should always succeed. */
  433. ut_error;
  434. }
  435. return(page);
  436. }
  437. /*************************************************************//**
  438. Differs from page_copy_rec_list_end, because this function does not
  439. touch the lock table and max trx id on page or compress the page. */
  440. UNIV_INTERN
  441. void
  442. page_copy_rec_list_end_no_locks(
  443. /*============================*/
  444. buf_block_t* new_block, /*!< in: index page to copy to */
  445. buf_block_t* block, /*!< in: index page of rec */
  446. rec_t* rec, /*!< in: record on page */
  447. dict_index_t* index, /*!< in: record descriptor */
  448. mtr_t* mtr) /*!< in: mtr */
  449. {
  450. page_t* new_page = buf_block_get_frame(new_block);
  451. page_cur_t cur1;
  452. rec_t* cur2;
  453. mem_heap_t* heap = NULL;
  454. ulint offsets_[REC_OFFS_NORMAL_SIZE];
  455. ulint* offsets = offsets_;
  456. rec_offs_init(offsets_);
  457. page_cur_position(rec, block, &cur1);
  458. if (page_cur_is_before_first(&cur1)) {
  459. page_cur_move_to_next(&cur1);
  460. }
  461. ut_a((ibool)!!page_is_comp(new_page)
  462. == dict_table_is_comp(index->table));
  463. ut_a(page_is_comp(new_page) == page_rec_is_comp(rec));
  464. ut_a(mach_read_from_2(new_page + UNIV_PAGE_SIZE - 10) == (ulint)
  465. (page_is_comp(new_page) ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM));
  466. cur2 = page_get_infimum_rec(buf_block_get_frame(new_block));
  467. /* Copy records from the original page to the new page */
  468. while (!page_cur_is_after_last(&cur1)) {
  469. rec_t* cur1_rec = page_cur_get_rec(&cur1);
  470. rec_t* ins_rec;
  471. offsets = rec_get_offsets(cur1_rec, index, offsets,
  472. ULINT_UNDEFINED, &heap);
  473. ins_rec = page_cur_insert_rec_low(cur2, index,
  474. cur1_rec, offsets, mtr);
  475. if (UNIV_UNLIKELY(!ins_rec)) {
  476. /* Track an assertion failure reported on the mailing
  477. list on June 18th, 2003 */
  478. buf_page_print(new_page, 0);
  479. buf_page_print(page_align(rec), 0);
  480. ut_print_timestamp(stderr);
  481. fprintf(stderr,
  482. "InnoDB: rec offset %lu, cur1 offset %lu,"
  483. " cur2 offset %lu\n",
  484. (ulong) page_offset(rec),
  485. (ulong) page_offset(page_cur_get_rec(&cur1)),
  486. (ulong) page_offset(cur2));
  487. ut_error;
  488. }
  489. page_cur_move_to_next(&cur1);
  490. cur2 = ins_rec;
  491. }
  492. if (UNIV_LIKELY_NULL(heap)) {
  493. mem_heap_free(heap);
  494. }
  495. }
  496. #ifndef UNIV_HOTBACKUP
  497. /*************************************************************//**
  498. Copies records from page to new_page, from a given record onward,
  499. including that record. Infimum and supremum records are not copied.
  500. The records are copied to the start of the record list on new_page.
  501. @return pointer to the original successor of the infimum record on
  502. new_page, or NULL on zip overflow (new_block will be decompressed) */
  503. UNIV_INTERN
  504. rec_t*
  505. page_copy_rec_list_end(
  506. /*===================*/
  507. buf_block_t* new_block, /*!< in/out: index page to copy to */
  508. buf_block_t* block, /*!< in: index page containing rec */
  509. rec_t* rec, /*!< in: record on page */
  510. dict_index_t* index, /*!< in: record descriptor */
  511. mtr_t* mtr) /*!< in: mtr */
  512. {
  513. page_t* new_page = buf_block_get_frame(new_block);
  514. page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block);
  515. page_t* page = page_align(rec);
  516. rec_t* ret = page_rec_get_next(
  517. page_get_infimum_rec(new_page));
  518. ulint log_mode = 0; /* remove warning */
  519. #ifdef UNIV_ZIP_DEBUG
  520. if (new_page_zip) {
  521. page_zip_des_t* page_zip = buf_block_get_page_zip(block);
  522. ut_a(page_zip);
  523. /* Strict page_zip_validate() may fail here.
  524. Furthermore, btr_compress() may set FIL_PAGE_PREV to
  525. FIL_NULL on new_page while leaving it intact on
  526. new_page_zip. So, we cannot validate new_page_zip. */
  527. ut_a(page_zip_validate_low(page_zip, page, TRUE));
  528. }
  529. #endif /* UNIV_ZIP_DEBUG */
  530. ut_ad(buf_block_get_frame(block) == page);
  531. ut_ad(page_is_leaf(page) == page_is_leaf(new_page));
  532. ut_ad(page_is_comp(page) == page_is_comp(new_page));
  533. /* Here, "ret" may be pointing to a user record or the
  534. predefined supremum record. */
  535. if (UNIV_LIKELY_NULL(new_page_zip)) {
  536. log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
  537. }
  538. if (page_dir_get_n_heap(new_page) == PAGE_HEAP_NO_USER_LOW) {
  539. page_copy_rec_list_end_to_created_page(new_page, rec,
  540. index, mtr);
  541. } else {
  542. page_copy_rec_list_end_no_locks(new_block, block, rec,
  543. index, mtr);
  544. }
  545. /* Update PAGE_MAX_TRX_ID on the uncompressed page.
  546. Modifications will be redo logged and copied to the compressed
  547. page in page_zip_compress() or page_zip_reorganize() below. */
  548. if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) {
  549. page_update_max_trx_id(new_block, NULL,
  550. page_get_max_trx_id(page), mtr);
  551. }
  552. if (UNIV_LIKELY_NULL(new_page_zip)) {
  553. mtr_set_log_mode(mtr, log_mode);
  554. if (UNIV_UNLIKELY
  555. (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
  556. /* Before trying to reorganize the page,
  557. store the number of preceding records on the page. */
  558. ulint ret_pos
  559. = page_rec_get_n_recs_before(ret);
  560. /* Before copying, "ret" was the successor of
  561. the predefined infimum record. It must still
  562. have at least one predecessor (the predefined
  563. infimum record, or a freshly copied record
  564. that is smaller than "ret"). */
  565. ut_a(ret_pos > 0);
  566. if (UNIV_UNLIKELY
  567. (!page_zip_reorganize(new_block, index, mtr))) {
  568. btr_blob_dbg_remove(new_page, index,
  569. "copy_end_reorg_fail");
  570. if (UNIV_UNLIKELY
  571. (!page_zip_decompress(new_page_zip,
  572. new_page, FALSE))) {
  573. ut_error;
  574. }
  575. ut_ad(page_validate(new_page, index));
  576. btr_blob_dbg_add(new_page, index,
  577. "copy_end_reorg_fail");
  578. return(NULL);
  579. } else {
  580. /* The page was reorganized:
  581. Seek to ret_pos. */
  582. ret = new_page + PAGE_NEW_INFIMUM;
  583. do {
  584. ret = rec_get_next_ptr(ret, TRUE);
  585. } while (--ret_pos);
  586. }
  587. }
  588. }
  589. /* Update the lock table and possible hash index */
  590. lock_move_rec_list_end(new_block, block, rec);
  591. btr_search_move_or_delete_hash_entries(new_block, block, index);
  592. return(ret);
  593. }
  594. /*************************************************************//**
  595. Copies records from page to new_page, up to the given record,
  596. NOT including that record. Infimum and supremum records are not copied.
  597. The records are copied to the end of the record list on new_page.
  598. @return pointer to the original predecessor of the supremum record on
  599. new_page, or NULL on zip overflow (new_block will be decompressed) */
  600. UNIV_INTERN
  601. rec_t*
  602. page_copy_rec_list_start(
  603. /*=====================*/
  604. buf_block_t* new_block, /*!< in/out: index page to copy to */
  605. buf_block_t* block, /*!< in: index page containing rec */
  606. rec_t* rec, /*!< in: record on page */
  607. dict_index_t* index, /*!< in: record descriptor */
  608. mtr_t* mtr) /*!< in: mtr */
  609. {
  610. page_t* new_page = buf_block_get_frame(new_block);
  611. page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block);
  612. page_cur_t cur1;
  613. rec_t* cur2;
  614. ulint log_mode = 0 /* remove warning */;
  615. mem_heap_t* heap = NULL;
  616. rec_t* ret
  617. = page_rec_get_prev(page_get_supremum_rec(new_page));
  618. ulint offsets_[REC_OFFS_NORMAL_SIZE];
  619. ulint* offsets = offsets_;
  620. rec_offs_init(offsets_);
  621. /* Here, "ret" may be pointing to a user record or the
  622. predefined infimum record. */
  623. if (page_rec_is_infimum(rec)) {
  624. return(ret);
  625. }
  626. if (UNIV_LIKELY_NULL(new_page_zip)) {
  627. log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
  628. }
  629. page_cur_set_before_first(block, &cur1);
  630. page_cur_move_to_next(&cur1);
  631. cur2 = ret;
  632. /* Copy records from the original page to the new page */
  633. while (page_cur_get_rec(&cur1) != rec) {
  634. rec_t* cur1_rec = page_cur_get_rec(&cur1);
  635. offsets = rec_get_offsets(cur1_rec, index, offsets,
  636. ULINT_UNDEFINED, &heap);
  637. cur2 = page_cur_insert_rec_low(cur2, index,
  638. cur1_rec, offsets, mtr);
  639. ut_a(cur2);
  640. page_cur_move_to_next(&cur1);
  641. }
  642. if (UNIV_LIKELY_NULL(heap)) {
  643. mem_heap_free(heap);
  644. }
  645. /* Update PAGE_MAX_TRX_ID on the uncompressed page.
  646. Modifications will be redo logged and copied to the compressed
  647. page in page_zip_compress() or page_zip_reorganize() below. */
  648. if (dict_index_is_sec_or_ibuf(index)
  649. && page_is_leaf(page_align(rec))) {
  650. page_update_max_trx_id(new_block, NULL,
  651. page_get_max_trx_id(page_align(rec)),
  652. mtr);
  653. }
  654. if (UNIV_LIKELY_NULL(new_page_zip)) {
  655. mtr_set_log_mode(mtr, log_mode);
  656. if (UNIV_UNLIKELY
  657. (!page_zip_compress(new_page_zip, new_page, index, mtr))) {
  658. /* Before trying to reorganize the page,
  659. store the number of preceding records on the page. */
  660. ulint ret_pos
  661. = page_rec_get_n_recs_before(ret);
  662. /* Before copying, "ret" was the predecessor
  663. of the predefined supremum record. If it was
  664. the predefined infimum record, then it would
  665. still be the infimum. Thus, the assertion
  666. ut_a(ret_pos > 0) would fail here. */
  667. if (UNIV_UNLIKELY
  668. (!page_zip_reorganize(new_block, index, mtr))) {
  669. btr_blob_dbg_remove(new_page, index,
  670. "copy_start_reorg_fail");
  671. if (UNIV_UNLIKELY
  672. (!page_zip_decompress(new_page_zip,
  673. new_page, FALSE))) {
  674. ut_error;
  675. }
  676. ut_ad(page_validate(new_page, index));
  677. btr_blob_dbg_add(new_page, index,
  678. "copy_start_reorg_fail");
  679. return(NULL);
  680. } else {
  681. /* The page was reorganized:
  682. Seek to ret_pos. */
  683. ret = new_page + PAGE_NEW_INFIMUM;
  684. do {
  685. ret = rec_get_next_ptr(ret, TRUE);
  686. } while (--ret_pos);
  687. }
  688. }
  689. }
  690. /* Update the lock table and possible hash index */
  691. lock_move_rec_list_start(new_block, block, rec, ret);
  692. btr_search_move_or_delete_hash_entries(new_block, block, index);
  693. return(ret);
  694. }
  695. /**********************************************************//**
  696. Writes a log record of a record list end or start deletion. */
  697. UNIV_INLINE
  698. void
  699. page_delete_rec_list_write_log(
  700. /*===========================*/
  701. rec_t* rec, /*!< in: record on page */
  702. dict_index_t* index, /*!< in: record descriptor */
  703. byte type, /*!< in: operation type:
  704. MLOG_LIST_END_DELETE, ... */
  705. mtr_t* mtr) /*!< in: mtr */
  706. {
  707. byte* log_ptr;
  708. ut_ad(type == MLOG_LIST_END_DELETE
  709. || type == MLOG_LIST_START_DELETE
  710. || type == MLOG_COMP_LIST_END_DELETE
  711. || type == MLOG_COMP_LIST_START_DELETE);
  712. log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2);
  713. if (log_ptr) {
  714. /* Write the parameter as a 2-byte ulint */
  715. mach_write_to_2(log_ptr, page_offset(rec));
  716. mlog_close(mtr, log_ptr + 2);
  717. }
  718. }
  719. #else /* !UNIV_HOTBACKUP */
  720. # define page_delete_rec_list_write_log(rec,index,type,mtr) ((void) 0)
  721. #endif /* !UNIV_HOTBACKUP */
  722. /**********************************************************//**
  723. Parses a log record of a record list end or start deletion.
  724. @return end of log record or NULL */
  725. UNIV_INTERN
  726. byte*
  727. page_parse_delete_rec_list(
  728. /*=======================*/
  729. byte type, /*!< in: MLOG_LIST_END_DELETE,
  730. MLOG_LIST_START_DELETE,
  731. MLOG_COMP_LIST_END_DELETE or
  732. MLOG_COMP_LIST_START_DELETE */
  733. byte* ptr, /*!< in: buffer */
  734. byte* end_ptr,/*!< in: buffer end */
  735. buf_block_t* block, /*!< in/out: buffer block or NULL */
  736. dict_index_t* index, /*!< in: record descriptor */
  737. mtr_t* mtr) /*!< in: mtr or NULL */
  738. {
  739. page_t* page;
  740. ulint offset;
  741. ut_ad(type == MLOG_LIST_END_DELETE
  742. || type == MLOG_LIST_START_DELETE
  743. || type == MLOG_COMP_LIST_END_DELETE
  744. || type == MLOG_COMP_LIST_START_DELETE);
  745. /* Read the record offset as a 2-byte ulint */
  746. if (end_ptr < ptr + 2) {
  747. return(NULL);
  748. }
  749. offset = mach_read_from_2(ptr);
  750. ptr += 2;
  751. if (!block) {
  752. return(ptr);
  753. }
  754. page = buf_block_get_frame(block);
  755. ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
  756. if (type == MLOG_LIST_END_DELETE
  757. || type == MLOG_COMP_LIST_END_DELETE) {
  758. page_delete_rec_list_end(page + offset, block, index,
  759. ULINT_UNDEFINED, ULINT_UNDEFINED,
  760. mtr);
  761. } else {
  762. page_delete_rec_list_start(page + offset, block, index, mtr);
  763. }
  764. return(ptr);
  765. }
  766. /*************************************************************//**
  767. Deletes records from a page from a given record onward, including that record.
  768. The infimum and supremum records are not deleted. */
  769. UNIV_INTERN
  770. void
  771. page_delete_rec_list_end(
  772. /*=====================*/
  773. rec_t* rec, /*!< in: pointer to record on page */
  774. buf_block_t* block, /*!< in: buffer block of the page */
  775. dict_index_t* index, /*!< in: record descriptor */
  776. ulint n_recs, /*!< in: number of records to delete,
  777. or ULINT_UNDEFINED if not known */
  778. ulint size, /*!< in: the sum of the sizes of the
  779. records in the end of the chain to
  780. delete, or ULINT_UNDEFINED if not known */
  781. mtr_t* mtr) /*!< in: mtr */
  782. {
  783. page_dir_slot_t*slot;
  784. ulint slot_index;
  785. rec_t* last_rec;
  786. rec_t* prev_rec;
  787. ulint n_owned;
  788. page_zip_des_t* page_zip = buf_block_get_page_zip(block);
  789. page_t* page = page_align(rec);
  790. mem_heap_t* heap = NULL;
  791. ulint offsets_[REC_OFFS_NORMAL_SIZE];
  792. ulint* offsets = offsets_;
  793. rec_offs_init(offsets_);
  794. ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE);
  795. ut_ad(!page_zip || page_rec_is_comp(rec));
  796. #ifdef UNIV_ZIP_DEBUG
  797. ut_a(!page_zip || page_zip_validate(page_zip, page));
  798. #endif /* UNIV_ZIP_DEBUG */
  799. if (page_rec_is_infimum(rec)) {
  800. rec = page_rec_get_next(rec);
  801. }
  802. if (page_rec_is_supremum(rec)) {
  803. return;
  804. }
  805. /* Reset the last insert info in the page header and increment
  806. the modify clock for the frame */
  807. page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL);
  808. /* The page gets invalid for optimistic searches: increment the
  809. frame modify clock */
  810. buf_block_modify_clock_inc(block);
  811. page_delete_rec_list_write_log(rec, index, page_is_comp(page)
  812. ? MLOG_COMP_LIST_END_DELETE
  813. : MLOG_LIST_END_DELETE, mtr);
  814. if (UNIV_LIKELY_NULL(page_zip)) {
  815. ulint log_mode;
  816. ut_a(page_is_comp(page));
  817. /* Individual deletes are not logged */
  818. log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
  819. do {
  820. page_cur_t cur;
  821. page_cur_position(rec, block, &cur);
  822. offsets = rec_get_offsets(rec, index, offsets,
  823. ULINT_UNDEFINED, &heap);
  824. rec = rec_get_next_ptr(rec, TRUE);
  825. #ifdef UNIV_ZIP_DEBUG
  826. ut_a(page_zip_validate(page_zip, page));
  827. #endif /* UNIV_ZIP_DEBUG */
  828. page_cur_delete_rec(&cur, index, offsets, mtr);
  829. } while (page_offset(rec) != PAGE_NEW_SUPREMUM);
  830. if (UNIV_LIKELY_NULL(heap)) {
  831. mem_heap_free(heap);
  832. }
  833. /* Restore log mode */
  834. mtr_set_log_mode(mtr, log_mode);
  835. return;
  836. }
  837. prev_rec = page_rec_get_prev(rec);
  838. last_rec = page_rec_get_prev(page_get_supremum_rec(page));
  839. if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) {
  840. rec_t* rec2 = rec;
  841. /* Calculate the sum of sizes and the number of records */
  842. size = 0;
  843. n_recs = 0;
  844. do {
  845. ulint s;
  846. offsets = rec_get_offsets(rec2, index, offsets,
  847. ULINT_UNDEFINED, &heap);
  848. s = rec_offs_size(offsets);
  849. ut_ad(rec2 - page + s - rec_offs_extra_size(offsets)
  850. < UNIV_PAGE_SIZE);
  851. ut_ad(size + s < UNIV_PAGE_SIZE);
  852. size += s;
  853. n_recs++;
  854. rec2 = page_rec_get_next(rec2);
  855. } while (!page_rec_is_supremum(rec2));
  856. if (UNIV_LIKELY_NULL(heap)) {
  857. mem_heap_free(heap);
  858. }
  859. }
  860. ut_ad(size < UNIV_PAGE_SIZE);
  861. /* Update the page directory; there is no need to balance the number
  862. of the records owned by the supremum record, as it is allowed to be
  863. less than PAGE_DIR_SLOT_MIN_N_OWNED */
  864. if (page_is_comp(page)) {
  865. rec_t* rec2 = rec;
  866. ulint count = 0;
  867. while (rec_get_n_owned_new(rec2) == 0) {
  868. count++;
  869. rec2 = rec_get_next_ptr(rec2, TRUE);
  870. }
  871. ut_ad(rec_get_n_owned_new(rec2) > count);
  872. n_owned = rec_get_n_owned_new(rec2) - count;
  873. slot_index = page_dir_find_owner_slot(rec2);
  874. slot = page_dir_get_nth_slot(page, slot_index);
  875. } else {
  876. rec_t* rec2 = rec;
  877. ulint count = 0;
  878. while (rec_get_n_owned_old(rec2) == 0) {
  879. count++;
  880. rec2 = rec_get_next_ptr(rec2, FALSE);
  881. }
  882. ut_ad(rec_get_n_owned_old(rec2) > count);
  883. n_owned = rec_get_n_owned_old(rec2) - count;
  884. slot_index = page_dir_find_owner_slot(rec2);
  885. slot = page_dir_get_nth_slot(page, slot_index);
  886. }
  887. page_dir_slot_set_rec(slot, page_get_supremum_rec(page));
  888. page_dir_slot_set_n_owned(slot, NULL, n_owned);
  889. page_dir_set_n_slots(page, NULL, slot_index + 1);
  890. /* Remove the record chain segment from the record chain */
  891. page_rec_set_next(prev_rec, page_get_supremum_rec(page));
  892. btr_blob_dbg_op(page, rec, index, "delete_end",
  893. btr_blob_dbg_remove_rec);
  894. /* Catenate the deleted chain segment to the page free list */
  895. page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE));
  896. page_header_set_ptr(page, NULL, PAGE_FREE, rec);
  897. page_header_set_field(page, NULL, PAGE_GARBAGE, size
  898. + page_header_get_field(page, PAGE_GARBAGE));
  899. page_header_set_field(page, NULL, PAGE_N_RECS,
  900. (ulint)(page_get_n_recs(page) - n_recs));
  901. }
  902. /*************************************************************//**
  903. Deletes records from page, up to the given record, NOT including
  904. that record. Infimum and supremum records are not deleted. */
  905. UNIV_INTERN
  906. void
  907. page_delete_rec_list_start(
  908. /*=======================*/
  909. rec_t* rec, /*!< in: record on page */
  910. buf_block_t* block, /*!< in: buffer block of the page */
  911. dict_index_t* index, /*!< in: record descriptor */
  912. mtr_t* mtr) /*!< in: mtr */
  913. {
  914. page_cur_t cur1;
  915. ulint log_mode;
  916. ulint offsets_[REC_OFFS_NORMAL_SIZE];
  917. ulint* offsets = offsets_;
  918. mem_heap_t* heap = NULL;
  919. byte type;
  920. rec_offs_init(offsets_);
  921. ut_ad((ibool) !!page_rec_is_comp(rec)
  922. == dict_table_is_comp(index->table));
  923. #ifdef UNIV_ZIP_DEBUG
  924. {
  925. page_zip_des_t* page_zip= buf_block_get_page_zip(block);
  926. page_t* page = buf_block_get_frame(block);
  927. /* page_zip_validate() would detect a min_rec_mark mismatch
  928. in btr_page_split_and_insert()
  929. between btr_attach_half_pages() and insert_page = ...
  930. when btr_page_get_split_rec_to_left() holds
  931. (direction == FSP_DOWN). */
  932. ut_a(!page_zip || page_zip_validate_low(page_zip, page, TRUE));
  933. }
  934. #endif /* UNIV_ZIP_DEBUG */
  935. if (page_rec_is_infimum(rec)) {
  936. return;
  937. }
  938. if (page_rec_is_comp(rec)) {
  939. type = MLOG_COMP_LIST_START_DELETE;
  940. } else {
  941. type = MLOG_LIST_START_DELETE;
  942. }
  943. page_delete_rec_list_write_log(rec, index, type, mtr);
  944. page_cur_set_before_first(block, &cur1);
  945. page_cur_move_to_next(&cur1);
  946. /* Individual deletes are not logged */
  947. log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
  948. while (page_cur_get_rec(&cur1) != rec) {
  949. offsets = rec_get_offsets(page_cur_get_rec(&cur1), index,
  950. offsets, ULINT_UNDEFINED, &heap);
  951. page_cur_delete_rec(&cur1, index, offsets, mtr);
  952. }
  953. if (UNIV_LIKELY_NULL(heap)) {
  954. mem_heap_free(heap);
  955. }
  956. /* Restore log mode */
  957. mtr_set_log_mode(mtr, log_mode);
  958. }
  959. #ifndef UNIV_HOTBACKUP
  960. /*************************************************************//**
  961. Moves record list end to another page. Moved records include
  962. split_rec.
  963. @return TRUE on success; FALSE on compression failure (new_block will
  964. be decompressed) */
  965. UNIV_INTERN
  966. ibool
  967. page_move_rec_list_end(
  968. /*===================*/
  969. buf_block_t* new_block, /*!< in/out: index page where to move */
  970. buf_block_t* block, /*!< in: index page from where to move */
  971. rec_t* split_rec, /*!< in: first record to move */
  972. dict_index_t* index, /*!< in: record descriptor */
  973. mtr_t* mtr) /*!< in: mtr */
  974. {
  975. page_t* new_page = buf_block_get_frame(new_block);
  976. ulint old_data_size;
  977. ulint new_data_size;
  978. ulint old_n_recs;
  979. ulint new_n_recs;
  980. old_data_size = page_get_data_size(new_page);
  981. old_n_recs = page_get_n_recs(new_page);
  982. #ifdef UNIV_ZIP_DEBUG
  983. {
  984. page_zip_des_t* new_page_zip
  985. = buf_block_get_page_zip(new_block);
  986. page_zip_des_t* page_zip
  987. = buf_block_get_page_zip(block);
  988. ut_a(!new_page_zip == !page_zip);
  989. ut_a(!new_page_zip
  990. || page_zip_validate(new_page_zip, new_page));
  991. ut_a(!page_zip
  992. || page_zip_validate(page_zip, page_align(split_rec)));
  993. }
  994. #endif /* UNIV_ZIP_DEBUG */
  995. if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_block, block,
  996. split_rec, index, mtr))) {
  997. return(FALSE);
  998. }
  999. new_data_size = page_get_data_size(new_page);
  1000. new_n_recs = page_get_n_recs(new_page);
  1001. ut_ad(new_data_size >= old_data_size);
  1002. page_delete_rec_list_end(split_rec, block, index,
  1003. new_n_recs - old_n_recs,
  1004. new_data_size - old_data_size, mtr);
  1005. return(TRUE);
  1006. }
  1007. /*************************************************************//**
  1008. Moves record list start to another page. Moved records do not include
  1009. split_rec.
  1010. @return TRUE on success; FALSE on compression failure */
  1011. UNIV_INTERN
  1012. ibool
  1013. page_move_rec_list_start(
  1014. /*=====================*/
  1015. buf_block_t* new_block, /*!< in/out: index page where to move */
  1016. buf_block_t* block, /*!< in/out: page containing split_rec */
  1017. rec_t* split_rec, /*!< in: first record not to move */
  1018. dict_index_t* index, /*!< in: record descriptor */
  1019. mtr_t* mtr) /*!< in: mtr */
  1020. {
  1021. if (UNIV_UNLIKELY(!page_copy_rec_list_start(new_block, block,
  1022. split_rec, index, mtr))) {
  1023. return(FALSE);
  1024. }
  1025. page_delete_rec_list_start(split_rec, block, index, mtr);
  1026. return(TRUE);
  1027. }
  1028. /***********************************************************************//**
  1029. This is a low-level operation which is used in a database index creation
  1030. to update the page number of a created B-tree to a data dictionary record. */
  1031. UNIV_INTERN
  1032. void
  1033. page_rec_write_index_page_no(
  1034. /*=========================*/
  1035. rec_t* rec, /*!< in: record to update */
  1036. ulint i, /*!< in: index of the field to update */
  1037. ulint page_no,/*!< in: value to write */
  1038. mtr_t* mtr) /*!< in: mtr */
  1039. {
  1040. byte* data;
  1041. ulint len;
  1042. data = rec_get_nth_field_old(rec, i, &len);
  1043. ut_ad(len == 4);
  1044. mlog_write_ulint(data, page_no, MLOG_4BYTES, mtr);
  1045. }
  1046. #endif /* !UNIV_HOTBACKUP */
  1047. /**************************************************************//**
  1048. Used to delete n slots from the directory. This function updates
  1049. also n_owned fields in the records, so that the first slot after
  1050. the deleted ones inherits the records of the deleted slots. */
  1051. UNIV_INLINE
  1052. void
  1053. page_dir_delete_slot(
  1054. /*=================*/
  1055. page_t* page, /*!< in/out: the index page */
  1056. page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
  1057. ulint slot_no)/*!< in: slot to be deleted */
  1058. {
  1059. page_dir_slot_t* slot;
  1060. ulint n_owned;
  1061. ulint i;
  1062. ulint n_slots;
  1063. ut_ad(!page_zip || page_is_comp(page));
  1064. ut_ad(slot_no > 0);
  1065. ut_ad(slot_no + 1 < page_dir_get_n_slots(page));
  1066. n_slots = page_dir_get_n_slots(page);
  1067. /* 1. Reset the n_owned fields of the slots to be
  1068. deleted */
  1069. slot = page_dir_get_nth_slot(page, slot_no);
  1070. n_owned = page_dir_slot_get_n_owned(slot);
  1071. page_dir_slot_set_n_owned(slot, page_zip, 0);
  1072. /* 2. Update the n_owned value of the first non-deleted slot */
  1073. slot = page_dir_get_nth_slot(page, slot_no + 1);
  1074. page_dir_slot_set_n_owned(slot, page_zip,
  1075. n_owned + page_dir_slot_get_n_owned(slot));
  1076. /* 3. Destroy the slot by copying slots */
  1077. for (i = slot_no + 1; i < n_slots; i++) {
  1078. rec_t* rec = (rec_t*)
  1079. page_dir_slot_get_rec(page_dir_get_nth_slot(page, i));
  1080. page_dir_slot_set_rec(page_dir_get_nth_slot(page, i - 1), rec);
  1081. }
  1082. /* 4. Zero out the last slot, which will be removed */
  1083. mach_write_to_2(page_dir_get_nth_slot(page, n_slots - 1), 0);
  1084. /* 5. Update the page header */
  1085. page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots - 1);
  1086. }
  1087. /**************************************************************//**
  1088. Used to add n slots to the directory. Does not set the record pointers
  1089. in the added slots or update n_owned values: this is the responsibility
  1090. of the caller. */
  1091. UNIV_INLINE
  1092. void
  1093. page_dir_add_slot(
  1094. /*==============*/
  1095. page_t* page, /*!< in/out: the index page */
  1096. page_zip_des_t* page_zip,/*!< in/out: comprssed page, or NULL */
  1097. ulint start) /*!< in: the slot above which the new slots
  1098. are added */
  1099. {
  1100. page_dir_slot_t* slot;
  1101. ulint n_slots;
  1102. n_slots = page_dir_get_n_slots(page);
  1103. ut_ad(start < n_slots - 1);
  1104. /* Update the page header */
  1105. page_dir_set_n_slots(page, page_zip, n_slots + 1);
  1106. /* Move slots up */
  1107. slot = page_dir_get_nth_slot(page, n_slots);
  1108. memmove(slot, slot + PAGE_DIR_SLOT_SIZE,
  1109. (n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE);
  1110. }
  1111. /****************************************************************//**
  1112. Splits a directory slot which owns too many records. */
  1113. UNIV_INTERN
  1114. void
  1115. page_dir_split_slot(
  1116. /*================*/
  1117. page_t* page, /*!< in/out: index page */
  1118. page_zip_des_t* page_zip,/*!< in/out: compressed page whose
  1119. uncompressed part will be written, or NULL */
  1120. ulint slot_no)/*!< in: the directory slot */
  1121. {
  1122. rec_t* rec;
  1123. page_dir_slot_t* new_slot;
  1124. page_dir_slot_t* prev_slot;
  1125. page_dir_slot_t* slot;
  1126. ulint i;
  1127. ulint n_owned;
  1128. ut_ad(page);
  1129. ut_ad(!page_zip || page_is_comp(page));
  1130. ut_ad(slot_no > 0);
  1131. slot = page_dir_get_nth_slot(page, slot_no);
  1132. n_owned = page_dir_slot_get_n_owned(slot);
  1133. ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1);
  1134. /* 1. We loop to find a record approximately in the middle of the
  1135. records owned by the slot. */
  1136. prev_slot = page_dir_get_nth_slot(page, slot_no - 1);
  1137. rec = (rec_t*) page_dir_slot_get_rec(prev_slot);
  1138. for (i = 0; i < n_owned / 2; i++) {
  1139. rec = page_rec_get_next(rec);
  1140. }
  1141. ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED);
  1142. /* 2. We add one directory slot immediately below the slot to be
  1143. split. */
  1144. page_dir_add_slot(page, page_zip, slot_no - 1);
  1145. /* The added slot is now number slot_no, and the old slot is
  1146. now number slot_no + 1 */
  1147. new_slot = page_dir_get_nth_slot(page, slot_no);
  1148. slot = page_dir_get_nth_slot(page, slot_no + 1);
  1149. /* 3. We store the appropriate values to the new slot. */
  1150. page_dir_slot_set_rec(new_slot, rec);
  1151. page_dir_slot_set_n_owned(new_slot, page_zip, n_owned / 2);
  1152. /* 4. Finally, we update the number of records field of the
  1153. original slot */
  1154. page_dir_slot_set_n_owned(slot, page_zip, n_owned - (n_owned / 2));
  1155. }
  1156. /*************************************************************//**
  1157. Tries to balance the given directory slot with too few records with the upper
  1158. neighbor, so that there are at least the minimum number of records owned by
  1159. the slot; this may result in the merging of two slots. */
  1160. UNIV_INTERN
  1161. void
  1162. page_dir_balance_slot(
  1163. /*==================*/
  1164. page_t* page, /*!< in/out: index page */
  1165. page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
  1166. ulint slot_no)/*!< in: the directory slot */
  1167. {
  1168. page_dir_slot_t* slot;
  1169. page_dir_slot_t* up_slot;
  1170. ulint n_owned;
  1171. ulint up_n_owned;
  1172. rec_t* old_rec;
  1173. rec_t* new_rec;
  1174. ut_ad(page);
  1175. ut_ad(!page_zip || page_is_comp(page));
  1176. ut_ad(slot_no > 0);
  1177. slot = page_dir_get_nth_slot(page, slot_no);
  1178. /* The last directory slot cannot be balanced with the upper
  1179. neighbor, as there is none. */
  1180. if (UNIV_UNLIKELY(slot_no == page_dir_get_n_slots(page) - 1)) {
  1181. return;
  1182. }
  1183. up_slot = page_dir_get_nth_slot(page, slot_no + 1);
  1184. n_owned = page_dir_slot_get_n_owned(slot);
  1185. up_n_owned = page_dir_slot_get_n_owned(up_slot);
  1186. ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1);
  1187. /* If the upper slot has the minimum value of n_owned, we will merge
  1188. the two slots, therefore we assert: */
  1189. ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED);
  1190. if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) {
  1191. /* In this case we can just transfer one record owned
  1192. by the upper slot to the property of the lower slot */
  1193. old_rec = (rec_t*) page_dir_slot_get_rec(slot);
  1194. if (page_is_comp(page)) {
  1195. new_rec = rec_get_next_ptr(old_rec, TRUE);
  1196. rec_set_n_owned_new(old_rec, page_zip, 0);
  1197. rec_set_n_owned_new(new_rec, page_zip, n_owned + 1);
  1198. } else {
  1199. new_rec = rec_get_next_ptr(old_rec, FALSE);
  1200. rec_set_n_owned_old(old_rec, 0);
  1201. rec_set_n_owned_old(new_rec, n_owned + 1);
  1202. }
  1203. page_dir_slot_set_rec(slot, new_rec);
  1204. page_dir_slot_set_n_owned(up_slot, page_zip, up_n_owned -1);
  1205. } else {
  1206. /* In this case we may merge the two slots */
  1207. page_dir_delete_slot(page, page_zip, slot_no);
  1208. }
  1209. }
  1210. #ifndef UNIV_HOTBACKUP
  1211. /************************************************************//**
  1212. Returns the middle record of the record list. If there are an even number
  1213. of records in the list, returns the first record of the upper half-list.
  1214. @return middle record */
  1215. UNIV_INTERN
  1216. rec_t*
  1217. page_get_middle_rec(
  1218. /*================*/
  1219. page_t* page) /*!< in: page */
  1220. {
  1221. page_dir_slot_t* slot;
  1222. ulint middle;
  1223. ulint i;
  1224. ulint n_owned;
  1225. ulint count;
  1226. rec_t* rec;
  1227. /* This many records we must leave behind */
  1228. middle = (page_get_n_recs(page) + PAGE_HEAP_NO_USER_LOW) / 2;
  1229. count = 0;
  1230. for (i = 0;; i++) {
  1231. slot = page_dir_get_nth_slot(page, i);
  1232. n_owned = page_dir_slot_get_n_owned(slot);
  1233. if (count + n_owned > middle) {
  1234. break;
  1235. } else {
  1236. count += n_owned;
  1237. }
  1238. }
  1239. ut_ad(i > 0);
  1240. slot = page_dir_get_nth_slot(page, i - 1);
  1241. rec = (rec_t*) page_dir_slot_get_rec(slot);
  1242. rec = page_rec_get_next(rec);
  1243. /* There are now count records behind rec */
  1244. for (i = 0; i < middle - count; i++) {
  1245. rec = page_rec_get_next(rec);
  1246. }
  1247. return(rec);
  1248. }
  1249. #endif /* !UNIV_HOTBACKUP */
  1250. /***************************************************************//**
  1251. Returns the number of records before the given record in chain.
  1252. The number includes infimum and supremum records.
  1253. @return number of records */
  1254. UNIV_INTERN
  1255. ulint
  1256. page_rec_get_n_recs_before(
  1257. /*=======================*/
  1258. const rec_t* rec) /*!< in: the physical record */
  1259. {
  1260. const page_dir_slot_t* slot;
  1261. const rec_t* slot_rec;
  1262. const page_t* page;
  1263. ulint i;
  1264. lint n = 0;
  1265. ut_ad(page_rec_check(rec));
  1266. page = page_align(rec);
  1267. if (page_is_comp(page)) {
  1268. while (rec_get_n_owned_new(rec) == 0) {
  1269. rec = rec_get_next_ptr_const(rec, TRUE);
  1270. n--;
  1271. }
  1272. for (i = 0; ; i++) {
  1273. slot = page_dir_get_nth_slot(page, i);
  1274. slot_rec = page_dir_slot_get_rec(slot);
  1275. n += rec_get_n_owned_new(slot_rec);
  1276. if (rec == slot_rec) {
  1277. break;
  1278. }
  1279. }
  1280. } else {
  1281. while (rec_get_n_owned_old(rec) == 0) {
  1282. rec = rec_get_next_ptr_const(rec, FALSE);
  1283. n--;
  1284. }
  1285. for (i = 0; ; i++) {
  1286. slot = page_dir_get_nth_slot(page, i);
  1287. slot_rec = page_dir_slot_get_rec(slot);
  1288. n += rec_get_n_owned_old(slot_rec);
  1289. if (rec == slot_rec) {
  1290. break;
  1291. }
  1292. }
  1293. }
  1294. n--;
  1295. ut_ad(n >= 0);
  1296. return((ulint) n);
  1297. }
  1298. #ifndef UNIV_HOTBACKUP
  1299. /************************************************************//**
  1300. Prints record contents including the data relevant only in
  1301. the index page context. */
  1302. UNIV_INTERN
  1303. void
  1304. page_rec_print(
  1305. /*===========*/
  1306. const rec_t* rec, /*!< in: physical record */
  1307. const ulint* offsets)/*!< in: record descriptor */
  1308. {
  1309. ut_a(!page_rec_is_comp(rec) == !rec_offs_comp(offsets));
  1310. rec_print_new(stderr, rec, offsets);
  1311. if (page_rec_is_comp(rec)) {
  1312. fprintf(stderr,
  1313. " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
  1314. (ulong) rec_get_n_owned_new(rec),
  1315. (ulong) rec_get_heap_no_new(rec),
  1316. (ulong) rec_get_next_offs(rec, TRUE));
  1317. } else {
  1318. fprintf(stderr,
  1319. " n_owned: %lu; heap_no: %lu; next rec: %lu\n",
  1320. (ulong) rec_get_n_owned_old(rec),
  1321. (ulong) rec_get_heap_no_old(rec),
  1322. (ulong) rec_get_next_offs(rec, TRUE));
  1323. }
  1324. page_rec_check(rec);
  1325. rec_validate(rec, offsets);
  1326. }
  1327. /***************************************************************//**
  1328. This is used to print the contents of the directory for
  1329. debugging purposes. */
  1330. UNIV_INTERN
  1331. void
  1332. page_dir_print(
  1333. /*===========*/
  1334. page_t* page, /*!< in: index page */
  1335. ulint pr_n) /*!< in: print n first and n last entries */
  1336. {
  1337. ulint n;
  1338. ulint i;
  1339. page_dir_slot_t* slot;
  1340. n = page_dir_get_n_slots(page);
  1341. fprintf(stderr, "--------------------------------\n"
  1342. "PAGE DIRECTORY\n"
  1343. "Page address %p\n"
  1344. "Directory stack top at offs: %lu; number of slots: %lu\n",
  1345. page, (ulong) page_offset(page_dir_get_nth_slot(page, n - 1)),
  1346. (ulong) n);
  1347. for (i = 0; i < n; i++) {
  1348. slot = page_dir_get_nth_slot(page, i);
  1349. if ((i == pr_n) && (i < n - pr_n)) {
  1350. fputs(" ... \n", stderr);
  1351. }
  1352. if ((i < pr_n) || (i >= n - pr_n)) {
  1353. fprintf(stderr,
  1354. "Contents of slot: %lu: n_owned: %lu,"
  1355. " rec offs: %lu\n",
  1356. (ulong) i,
  1357. (ulong) page_dir_slot_get_n_owned(slot),
  1358. (ulong)
  1359. page_offset(page_dir_slot_get_rec(slot)));
  1360. }
  1361. }
  1362. fprintf(stderr, "Total of %lu records\n"
  1363. "--------------------------------\n",
  1364. (ulong) (PAGE_HEAP_NO_USER_LOW + page_get_n_recs(page)));
  1365. }
  1366. /***************************************************************//**
  1367. This is used to print the contents of the page record list for
  1368. debugging purposes. */
  1369. UNIV_INTERN
  1370. void
  1371. page_print_list(
  1372. /*============*/
  1373. buf_block_t* block, /*!< in: index page */
  1374. dict_index_t* index, /*!< in: dictionary index of the page */
  1375. ulint pr_n) /*!< in: print n first and n last entries */
  1376. {
  1377. page_t* page = block->frame;
  1378. page_cur_t cur;
  1379. ulint count;
  1380. ulint n_recs;
  1381. mem_heap_t* heap = NULL;
  1382. ulint offsets_[REC_OFFS_NORMAL_SIZE];
  1383. ulint* offsets = offsets_;
  1384. rec_offs_init(offsets_);
  1385. ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table));
  1386. fprintf(stderr,
  1387. "--------------------------------\n"
  1388. "PAGE RECORD LIST\n"
  1389. "Page address %p\n", page);
  1390. n_recs = page_get_n_recs(page);
  1391. page_cur_set_before_first(block, &cur);
  1392. count = 0;
  1393. for (;;) {
  1394. offsets = rec_get_offsets(cur.rec, index, offsets,
  1395. ULINT_UNDEFINED, &heap);
  1396. page_rec_print(cur.rec, offsets);
  1397. if (count == pr_n) {
  1398. break;
  1399. }
  1400. if (page_cur_is_after_last(&cur)) {
  1401. break;
  1402. }
  1403. page_cur_move_to_next(&cur);
  1404. count++;
  1405. }
  1406. if (n_recs > 2 * pr_n) {
  1407. fputs(" ... \n", stderr);
  1408. }
  1409. while (!page_cur_is_after_last(&cur)) {
  1410. page_cur_move_to_next(&cur);
  1411. if (count + pr_n >= n_recs) {
  1412. offsets = rec_get_offsets(cur.rec, index, offsets,
  1413. ULINT_UNDEFINED, &heap);
  1414. page_rec_print(cur.rec, offsets);
  1415. }
  1416. count++;
  1417. }
  1418. fprintf(stderr,
  1419. "Total of %lu records \n"
  1420. "--------------------------------\n",
  1421. (ulong) (count + 1));
  1422. if (UNIV_LIKELY_NULL(heap)) {
  1423. mem_heap_free(heap);
  1424. }
  1425. }
  1426. /***************************************************************//**
  1427. Prints the info in a page header. */
  1428. UNIV_INTERN
  1429. void
  1430. page_header_print(
  1431. /*==============*/
  1432. const page_t* page)
  1433. {
  1434. fprintf(stderr,
  1435. "--------------------------------\n"
  1436. "PAGE HEADER INFO\n"
  1437. "Page address %p, n records %lu (%s)\n"
  1438. "n dir slots %lu, heap top %lu\n"
  1439. "Page n heap %lu, free %lu, garbage %lu\n"
  1440. "Page last insert %lu, direction %lu, n direction %lu\n",
  1441. page, (ulong) page_header_get_field(page, PAGE_N_RECS),
  1442. page_is_comp(page) ? "compact format" : "original format",
  1443. (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS),
  1444. (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
  1445. (ulong) page_dir_get_n_heap(page),
  1446. (ulong) page_header_get_field(page, PAGE_FREE),
  1447. (ulong) page_header_get_field(page, PAGE_GARBAGE),
  1448. (ulong) page_header_get_field(page, PAGE_LAST_INSERT),
  1449. (ulong) page_header_get_field(page, PAGE_DIRECTION),
  1450. (ulong) page_header_get_field(page, PAGE_N_DIRECTION));
  1451. }
  1452. /***************************************************************//**
  1453. This is used to print the contents of the page for
  1454. debugging purposes. */
  1455. UNIV_INTERN
  1456. void
  1457. page_print(
  1458. /*=======*/
  1459. buf_block_t* block, /*!< in: index page */
  1460. dict_index_t* index, /*!< in: dictionary index of the page */
  1461. ulint dn, /*!< in: print dn first and last entries
  1462. in directory */
  1463. ulint rn) /*!< in: print rn first and last records
  1464. in directory */
  1465. {
  1466. page_t* page = block->frame;
  1467. page_header_print(page);
  1468. page_dir_print(page, dn);
  1469. page_print_list(block, index, rn);
  1470. }
  1471. #endif /* !UNIV_HOTBACKUP */
  1472. /***************************************************************//**
  1473. The following is used to validate a record on a page. This function
  1474. differs from rec_validate as it can also check the n_owned field and
  1475. the heap_no field.
  1476. @return TRUE if ok */
  1477. UNIV_INTERN
  1478. ibool
  1479. page_rec_validate(
  1480. /*==============*/
  1481. rec_t* rec, /*!< in: physical record */
  1482. const ulint* offsets)/*!< in: array returned by rec_get_offsets() */
  1483. {
  1484. ulint n_owned;
  1485. ulint heap_no;
  1486. page_t* page;
  1487. page = page_align(rec);
  1488. ut_a(!page_is_comp(page) == !rec_offs_comp(offsets));
  1489. page_rec_check(rec);
  1490. rec_validate(rec, offsets);
  1491. if (page_rec_is_comp(rec)) {
  1492. n_owned = rec_get_n_owned_new(rec);
  1493. heap_no = rec_get_heap_no_new(rec);
  1494. } else {
  1495. n_owned = rec_get_n_owned_old(rec);
  1496. heap_no = rec_get_heap_no_old(rec);
  1497. }
  1498. if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) {
  1499. fprintf(stderr,
  1500. "InnoDB: Dir slot of rec %lu, n owned too big %lu\n",
  1501. (ulong) page_offset(rec), (ulong) n_owned);
  1502. return(FALSE);
  1503. }
  1504. if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) {
  1505. fprintf(stderr,
  1506. "InnoDB: Heap no of rec %lu too big %lu %lu\n",
  1507. (ulong) page_offset(rec), (ulong) heap_no,
  1508. (ulong) page_dir_get_n_heap(page));
  1509. return(FALSE);
  1510. }
  1511. return(TRUE);
  1512. }
  1513. #ifndef UNIV_HOTBACKUP
  1514. /***************************************************************//**
  1515. Checks that the first directory slot points to the infimum record and
  1516. the last to the supremum. This function is intended to track if the
  1517. bug fixed in 4.0.14 has caused corruption to users' databases. */
  1518. UNIV_INTERN
  1519. void
  1520. page_check_dir(
  1521. /*===========*/
  1522. const page_t* page) /*!< in: index page */
  1523. {
  1524. ulint n_slots;
  1525. ulint infimum_offs;
  1526. ulint supremum_offs;
  1527. n_slots = page_dir_get_n_slots(page);
  1528. infimum_offs = mach_read_from_2(page_dir_get_nth_slot(page, 0));
  1529. supremum_offs = mach_read_from_2(page_dir_get_nth_slot(page,
  1530. n_slots - 1));
  1531. if (UNIV_UNLIKELY(!page_rec_is_infimum_low(infimum_offs))) {
  1532. fprintf(stderr,
  1533. "InnoDB: Page directory corruption:"
  1534. " infimum not pointed to\n");
  1535. buf_page_print(page, 0);
  1536. }
  1537. if (UNIV_UNLIKELY(!page_rec_is_supremum_low(supremum_offs))) {
  1538. fprintf(stderr,
  1539. "InnoDB: Page directory corruption:"
  1540. " supremum not pointed to\n");
  1541. buf_page_print(page, 0);
  1542. }
  1543. }
  1544. #endif /* !UNIV_HOTBACKUP */
  1545. /***************************************************************//**
  1546. This function checks the consistency of an index page when we do not
  1547. know the index. This is also resilient so that this should never crash
  1548. even if the page is total garbage.
  1549. @return TRUE if ok */
  1550. UNIV_INTERN
  1551. ibool
  1552. page_simple_validate_old(
  1553. /*=====================*/
  1554. page_t* page) /*!< in: old-style index page */
  1555. {
  1556. page_dir_slot_t* slot;
  1557. ulint slot_no;
  1558. ulint n_slots;
  1559. rec_t* rec;
  1560. byte* rec_heap_top;
  1561. ulint count;
  1562. ulint own_count;
  1563. ibool ret = FALSE;
  1564. ut_a(!page_is_comp(page));
  1565. /* Check first that the record heap and the directory do not
  1566. overlap. */
  1567. n_slots = page_dir_get_n_slots(page);
  1568. if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
  1569. fprintf(stderr,
  1570. "InnoDB: Nonsensical number %lu of page dir slots\n",
  1571. (ulong) n_slots);
  1572. goto func_exit;
  1573. }
  1574. rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
  1575. if (UNIV_UNLIKELY(rec_heap_top
  1576. > page_dir_get_nth_slot(page, n_slots - 1))) {
  1577. fprintf(stderr,
  1578. "InnoDB: Record heap and dir overlap on a page,"
  1579. " heap top %lu, dir %lu\n",
  1580. (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
  1581. (ulong)
  1582. page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
  1583. goto func_exit;
  1584. }
  1585. /* Validate the record list in a loop checking also that it is
  1586. consistent with the page record directory. */
  1587. count = 0;
  1588. own_count = 1;
  1589. slot_no = 0;
  1590. slot = page_dir_get_nth_slot(page, slot_no);
  1591. rec = page_get_infimum_rec(page);
  1592. for (;;) {
  1593. if (UNIV_UNLIKELY(rec > rec_heap_top)) {
  1594. fprintf(stderr,
  1595. "InnoDB: Record %lu is above"
  1596. " rec heap top %lu\n",
  1597. (ulong)(rec - page),
  1598. (ulong)(rec_heap_top - page));
  1599. goto func_exit;
  1600. }
  1601. if (UNIV_UNLIKELY(rec_get_n_owned_old(rec))) {
  1602. /* This is a record pointed to by a dir slot */
  1603. if (UNIV_UNLIKELY(rec_get_n_owned_old(rec)
  1604. != own_count)) {
  1605. fprintf(stderr,
  1606. "InnoDB: Wrong owned count %lu, %lu,"
  1607. " rec %lu\n",
  1608. (ulong) rec_get_n_owned_old(rec),
  1609. (ulong) own_count,
  1610. (ulong)(rec - page));
  1611. goto func_exit;
  1612. }
  1613. if (UNIV_UNLIKELY
  1614. (page_dir_slot_get_rec(slot) != rec)) {
  1615. fprintf(stderr,
  1616. "InnoDB: Dir slot does not point"
  1617. " to right rec %lu\n",
  1618. (ulong)(rec - page));
  1619. goto func_exit;
  1620. }
  1621. own_count = 0;
  1622. if (!page_rec_is_supremum(rec)) {
  1623. slot_no++;
  1624. slot = page_dir_get_nth_slot(page, slot_no);
  1625. }
  1626. }
  1627. if (page_rec_is_supremum(rec)) {
  1628. break;
  1629. }
  1630. if (UNIV_UNLIKELY
  1631. (rec_get_next_offs(rec, FALSE) < FIL_PAGE_DATA
  1632. || rec_get_next_offs(rec, FALSE) >= UNIV_PAGE_SIZE)) {
  1633. fprintf(stderr,
  1634. "InnoDB: Next record offset"
  1635. " nonsensical %lu for rec %lu\n",
  1636. (ulong) rec_get_next_offs(rec, FALSE),
  1637. (ulong) (rec - page));
  1638. goto func_exit;
  1639. }
  1640. count++;
  1641. if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
  1642. fprintf(stderr,
  1643. "InnoDB: Page record list appears"
  1644. " to be circular %lu\n",
  1645. (ulong) count);
  1646. goto func_exit;
  1647. }
  1648. rec = page_rec_get_next(rec);
  1649. own_count++;
  1650. }
  1651. if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
  1652. fprintf(stderr, "InnoDB: n owned is zero in a supremum rec\n");
  1653. goto func_exit;
  1654. }
  1655. if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
  1656. fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
  1657. (ulong) slot_no, (ulong) (n_slots - 1));
  1658. goto func_exit;
  1659. }
  1660. if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
  1661. + PAGE_HEAP_NO_USER_LOW
  1662. != count + 1)) {
  1663. fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
  1664. (ulong) page_header_get_field(page, PAGE_N_RECS)
  1665. + PAGE_HEAP_NO_USER_LOW,
  1666. (ulong) (count + 1));
  1667. goto func_exit;
  1668. }
  1669. /* Check then the free list */
  1670. rec = page_header_get_ptr(page, PAGE_FREE);
  1671. while (rec != NULL) {
  1672. if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
  1673. || rec >= page + UNIV_PAGE_SIZE)) {
  1674. fprintf(stderr,
  1675. "InnoDB: Free list record has"
  1676. " a nonsensical offset %lu\n",
  1677. (ulong) (rec - page));
  1678. goto func_exit;
  1679. }
  1680. if (UNIV_UNLIKELY(rec > rec_heap_top)) {
  1681. fprintf(stderr,
  1682. "InnoDB: Free list record %lu"
  1683. " is above rec heap top %lu\n",
  1684. (ulong) (rec - page),
  1685. (ulong) (rec_heap_top - page));
  1686. goto func_exit;
  1687. }
  1688. count++;
  1689. if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
  1690. fprintf(stderr,
  1691. "InnoDB: Page free list appears"
  1692. " to be circular %lu\n",
  1693. (ulong) count);
  1694. goto func_exit;
  1695. }
  1696. rec = page_rec_get_next(rec);
  1697. }
  1698. if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
  1699. fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
  1700. (ulong) page_dir_get_n_heap(page),
  1701. (ulong) (count + 1));
  1702. goto func_exit;
  1703. }
  1704. ret = TRUE;
  1705. func_exit:
  1706. return(ret);
  1707. }
  1708. /***************************************************************//**
  1709. This function checks the consistency of an index page when we do not
  1710. know the index. This is also resilient so that this should never crash
  1711. even if the page is total garbage.
  1712. @return TRUE if ok */
  1713. UNIV_INTERN
  1714. ibool
  1715. page_simple_validate_new(
  1716. /*=====================*/
  1717. page_t* page) /*!< in: new-style index page */
  1718. {
  1719. page_dir_slot_t* slot;
  1720. ulint slot_no;
  1721. ulint n_slots;
  1722. rec_t* rec;
  1723. byte* rec_heap_top;
  1724. ulint count;
  1725. ulint own_count;
  1726. ibool ret = FALSE;
  1727. ut_a(page_is_comp(page));
  1728. /* Check first that the record heap and the directory do not
  1729. overlap. */
  1730. n_slots = page_dir_get_n_slots(page);
  1731. if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) {
  1732. fprintf(stderr,
  1733. "InnoDB: Nonsensical number %lu"
  1734. " of page dir slots\n", (ulong) n_slots);
  1735. goto func_exit;
  1736. }
  1737. rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP);
  1738. if (UNIV_UNLIKELY(rec_heap_top
  1739. > page_dir_get_nth_slot(page, n_slots - 1))) {
  1740. fprintf(stderr,
  1741. "InnoDB: Record heap and dir overlap on a page,"
  1742. " heap top %lu, dir %lu\n",
  1743. (ulong) page_header_get_field(page, PAGE_HEAP_TOP),
  1744. (ulong)
  1745. page_offset(page_dir_get_nth_slot(page, n_slots - 1)));
  1746. goto func_exit;
  1747. }
  1748. /* Validate the record list in a loop checking also that it is
  1749. consistent with the page record directory. */
  1750. count = 0;
  1751. own_count = 1;
  1752. slot_no = 0;
  1753. slot = page_dir_get_nth_slot(page, slot_no);
  1754. rec = page_get_infimum_rec(page);
  1755. for (;;) {
  1756. if (UNIV_UNLIKELY(rec > rec_heap_top)) {
  1757. fprintf(stderr,
  1758. "InnoDB: Record %lu is above rec"
  1759. " heap top %lu\n",
  1760. (ulong) page_offset(rec),
  1761. (ulong) page_offset(rec_heap_top));
  1762. goto func_exit;
  1763. }
  1764. if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) {
  1765. /* This is a record pointed to by a dir slot */
  1766. if (UNIV_UNLIKELY(rec_get_n_owned_new(rec)
  1767. != own_count)) {
  1768. fprintf(stderr,
  1769. "InnoDB: Wrong owned count %lu, %lu,"
  1770. " rec %lu\n",
  1771. (ulong) rec_get_n_owned_new(rec),
  1772. (ulong) own_count,
  1773. (ulong) page_offset(rec));
  1774. goto func_exit;
  1775. }
  1776. if (UNIV_UNLIKELY
  1777. (page_dir_slot_get_rec(slot) != rec)) {
  1778. fprintf(stderr,
  1779. "InnoDB: Dir slot does not point"
  1780. " to right rec %lu\n",
  1781. (ulong) page_offset(rec));
  1782. goto func_exit;
  1783. }
  1784. own_count = 0;
  1785. if (!page_rec_is_supremum(rec)) {
  1786. slot_no++;
  1787. slot = page_dir_get_nth_slot(page, slot_no);
  1788. }
  1789. }
  1790. if (page_rec_is_supremum(rec)) {
  1791. break;
  1792. }
  1793. if (UNIV_UNLIKELY
  1794. (rec_get_next_offs(rec, TRUE) < FIL_PAGE_DATA
  1795. || rec_get_next_offs(rec, TRUE) >= UNIV_PAGE_SIZE)) {
  1796. fprintf(stderr,
  1797. "InnoDB: Next record offset nonsensical %lu"
  1798. " for rec %lu\n",
  1799. (ulong) rec_get_next_offs(rec, TRUE),
  1800. (ulong) page_offset(rec));
  1801. goto func_exit;
  1802. }
  1803. count++;
  1804. if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
  1805. fprintf(stderr,
  1806. "InnoDB: Page record list appears"
  1807. " to be circular %lu\n",
  1808. (ulong) count);
  1809. goto func_exit;
  1810. }
  1811. rec = page_rec_get_next(rec);
  1812. own_count++;
  1813. }
  1814. if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
  1815. fprintf(stderr, "InnoDB: n owned is zero"
  1816. " in a supremum rec\n");
  1817. goto func_exit;
  1818. }
  1819. if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
  1820. fprintf(stderr, "InnoDB: n slots wrong %lu, %lu\n",
  1821. (ulong) slot_no, (ulong) (n_slots - 1));
  1822. goto func_exit;
  1823. }
  1824. if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
  1825. + PAGE_HEAP_NO_USER_LOW
  1826. != count + 1)) {
  1827. fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
  1828. (ulong) page_header_get_field(page, PAGE_N_RECS)
  1829. + PAGE_HEAP_NO_USER_LOW,
  1830. (ulong) (count + 1));
  1831. goto func_exit;
  1832. }
  1833. /* Check then the free list */
  1834. rec = page_header_get_ptr(page, PAGE_FREE);
  1835. while (rec != NULL) {
  1836. if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA
  1837. || rec >= page + UNIV_PAGE_SIZE)) {
  1838. fprintf(stderr,
  1839. "InnoDB: Free list record has"
  1840. " a nonsensical offset %lu\n",
  1841. (ulong) page_offset(rec));
  1842. goto func_exit;
  1843. }
  1844. if (UNIV_UNLIKELY(rec > rec_heap_top)) {
  1845. fprintf(stderr,
  1846. "InnoDB: Free list record %lu"
  1847. " is above rec heap top %lu\n",
  1848. (ulong) page_offset(rec),
  1849. (ulong) page_offset(rec_heap_top));
  1850. goto func_exit;
  1851. }
  1852. count++;
  1853. if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) {
  1854. fprintf(stderr,
  1855. "InnoDB: Page free list appears"
  1856. " to be circular %lu\n",
  1857. (ulong) count);
  1858. goto func_exit;
  1859. }
  1860. rec = page_rec_get_next(rec);
  1861. }
  1862. if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
  1863. fprintf(stderr, "InnoDB: N heap is wrong %lu, %lu\n",
  1864. (ulong) page_dir_get_n_heap(page),
  1865. (ulong) (count + 1));
  1866. goto func_exit;
  1867. }
  1868. ret = TRUE;
  1869. func_exit:
  1870. return(ret);
  1871. }
  1872. /***************************************************************//**
  1873. This function checks the consistency of an index page.
  1874. @return TRUE if ok */
  1875. UNIV_INTERN
  1876. ibool
  1877. page_validate(
  1878. /*==========*/
  1879. page_t* page, /*!< in: index page */
  1880. dict_index_t* index) /*!< in: data dictionary index containing
  1881. the page record type definition */
  1882. {
  1883. page_dir_slot_t*slot;
  1884. mem_heap_t* heap;
  1885. byte* buf;
  1886. ulint count;
  1887. ulint own_count;
  1888. ulint rec_own_count;
  1889. ulint slot_no;
  1890. ulint data_size;
  1891. rec_t* rec;
  1892. rec_t* old_rec = NULL;
  1893. ulint offs;
  1894. ulint n_slots;
  1895. ibool ret = FALSE;
  1896. ulint i;
  1897. ulint* offsets = NULL;
  1898. ulint* old_offsets = NULL;
  1899. if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
  1900. != dict_table_is_comp(index->table))) {
  1901. fputs("InnoDB: 'compact format' flag mismatch\n", stderr);
  1902. goto func_exit2;
  1903. }
  1904. if (page_is_comp(page)) {
  1905. if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
  1906. goto func_exit2;
  1907. }
  1908. } else {
  1909. if (UNIV_UNLIKELY(!page_simple_validate_old(page))) {
  1910. goto func_exit2;
  1911. }
  1912. }
  1913. heap = mem_heap_create(UNIV_PAGE_SIZE + 200);
  1914. /* The following buffer is used to check that the
  1915. records in the page record heap do not overlap */
  1916. buf = mem_heap_zalloc(heap, UNIV_PAGE_SIZE);
  1917. /* Check first that the record heap and the directory do not
  1918. overlap. */
  1919. n_slots = page_dir_get_n_slots(page);
  1920. if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
  1921. <= page_dir_get_nth_slot(page, n_slots - 1)))) {
  1922. fprintf(stderr,
  1923. "InnoDB: Record heap and dir overlap"
  1924. " on space %lu page %lu index %s, %p, %p\n",
  1925. (ulong) page_get_space_id(page),
  1926. (ulong) page_get_page_no(page), index->name,
  1927. page_header_get_ptr(page, PAGE_HEAP_TOP),
  1928. page_dir_get_nth_slot(page, n_slots - 1));
  1929. goto func_exit;
  1930. }
  1931. /* Validate the record list in a loop checking also that
  1932. it is consistent with the directory. */
  1933. count = 0;
  1934. data_size = 0;
  1935. own_count = 1;
  1936. slot_no = 0;
  1937. slot = page_dir_get_nth_slot(page, slot_no);
  1938. rec = page_get_infimum_rec(page);
  1939. for (;;) {
  1940. offsets = rec_get_offsets(rec, index, offsets,
  1941. ULINT_UNDEFINED, &heap);
  1942. if (page_is_comp(page) && page_rec_is_user_rec(rec)
  1943. && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
  1944. == page_is_leaf(page))) {
  1945. fputs("InnoDB: node_ptr flag mismatch\n", stderr);
  1946. goto func_exit;
  1947. }
  1948. if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
  1949. goto func_exit;
  1950. }
  1951. #ifndef UNIV_HOTBACKUP
  1952. /* Check that the records are in the ascending order */
  1953. if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW)
  1954. && !page_rec_is_supremum(rec)) {
  1955. if (UNIV_UNLIKELY
  1956. (1 != cmp_rec_rec(rec, old_rec,
  1957. offsets, old_offsets, index))) {
  1958. fprintf(stderr,
  1959. "InnoDB: Records in wrong order"
  1960. " on space %lu page %lu index %s\n",
  1961. (ulong) page_get_space_id(page),
  1962. (ulong) page_get_page_no(page),
  1963. index->name);
  1964. fputs("\nInnoDB: previous record ", stderr);
  1965. rec_print_new(stderr, old_rec, old_offsets);
  1966. fputs("\nInnoDB: record ", stderr);
  1967. rec_print_new(stderr, rec, offsets);
  1968. putc('\n', stderr);
  1969. goto func_exit;
  1970. }
  1971. }
  1972. #endif /* !UNIV_HOTBACKUP */
  1973. if (page_rec_is_user_rec(rec)) {
  1974. data_size += rec_offs_size(offsets);
  1975. }
  1976. offs = page_offset(rec_get_start(rec, offsets));
  1977. i = rec_offs_size(offsets);
  1978. if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
  1979. fputs("InnoDB: record offset out of bounds\n", stderr);
  1980. goto func_exit;
  1981. }
  1982. while (i--) {
  1983. if (UNIV_UNLIKELY(buf[offs + i])) {
  1984. /* No other record may overlap this */
  1985. fputs("InnoDB: Record overlaps another\n",
  1986. stderr);
  1987. goto func_exit;
  1988. }
  1989. buf[offs + i] = 1;
  1990. }
  1991. if (page_is_comp(page)) {
  1992. rec_own_count = rec_get_n_owned_new(rec);
  1993. } else {
  1994. rec_own_count = rec_get_n_owned_old(rec);
  1995. }
  1996. if (UNIV_UNLIKELY(rec_own_count)) {
  1997. /* This is a record pointed to by a dir slot */
  1998. if (UNIV_UNLIKELY(rec_own_count != own_count)) {
  1999. fprintf(stderr,
  2000. "InnoDB: Wrong owned count %lu, %lu\n",
  2001. (ulong) rec_own_count,
  2002. (ulong) own_count);
  2003. goto func_exit;
  2004. }
  2005. if (page_dir_slot_get_rec(slot) != rec) {
  2006. fputs("InnoDB: Dir slot does not"
  2007. " point to right rec\n",
  2008. stderr);
  2009. goto func_exit;
  2010. }
  2011. page_dir_slot_check(slot);
  2012. own_count = 0;
  2013. if (!page_rec_is_supremum(rec)) {
  2014. slot_no++;
  2015. slot = page_dir_get_nth_slot(page, slot_no);
  2016. }
  2017. }
  2018. if (page_rec_is_supremum(rec)) {
  2019. break;
  2020. }
  2021. count++;
  2022. own_count++;
  2023. old_rec = rec;
  2024. rec = page_rec_get_next(rec);
  2025. /* set old_offsets to offsets; recycle offsets */
  2026. {
  2027. ulint* offs = old_offsets;
  2028. old_offsets = offsets;
  2029. offsets = offs;
  2030. }
  2031. }
  2032. if (page_is_comp(page)) {
  2033. if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) {
  2034. goto n_owned_zero;
  2035. }
  2036. } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
  2037. n_owned_zero:
  2038. fputs("InnoDB: n owned is zero\n", stderr);
  2039. goto func_exit;
  2040. }
  2041. if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
  2042. fprintf(stderr, "InnoDB: n slots wrong %lu %lu\n",
  2043. (ulong) slot_no, (ulong) (n_slots - 1));
  2044. goto func_exit;
  2045. }
  2046. if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS)
  2047. + PAGE_HEAP_NO_USER_LOW
  2048. != count + 1)) {
  2049. fprintf(stderr, "InnoDB: n recs wrong %lu %lu\n",
  2050. (ulong) page_header_get_field(page, PAGE_N_RECS)
  2051. + PAGE_HEAP_NO_USER_LOW,
  2052. (ulong) (count + 1));
  2053. goto func_exit;
  2054. }
  2055. if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
  2056. fprintf(stderr,
  2057. "InnoDB: Summed data size %lu, returned by func %lu\n",
  2058. (ulong) data_size, (ulong) page_get_data_size(page));
  2059. goto func_exit;
  2060. }
  2061. /* Check then the free list */
  2062. rec = page_header_get_ptr(page, PAGE_FREE);
  2063. while (rec != NULL) {
  2064. offsets = rec_get_offsets(rec, index, offsets,
  2065. ULINT_UNDEFINED, &heap);
  2066. if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
  2067. goto func_exit;
  2068. }
  2069. count++;
  2070. offs = page_offset(rec_get_start(rec, offsets));
  2071. i = rec_offs_size(offsets);
  2072. if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) {
  2073. fputs("InnoDB: record offset out of bounds\n", stderr);
  2074. goto func_exit;
  2075. }
  2076. while (i--) {
  2077. if (UNIV_UNLIKELY(buf[offs + i])) {
  2078. fputs("InnoDB: Record overlaps another"
  2079. " in free list\n", stderr);
  2080. goto func_exit;
  2081. }
  2082. buf[offs + i] = 1;
  2083. }
  2084. rec = page_rec_get_next(rec);
  2085. }
  2086. if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
  2087. fprintf(stderr, "InnoDB: N heap is wrong %lu %lu\n",
  2088. (ulong) page_dir_get_n_heap(page),
  2089. (ulong) count + 1);
  2090. goto func_exit;
  2091. }
  2092. ret = TRUE;
  2093. func_exit:
  2094. mem_heap_free(heap);
  2095. if (UNIV_UNLIKELY(ret == FALSE)) {
  2096. func_exit2:
  2097. fprintf(stderr,
  2098. "InnoDB: Apparent corruption"
  2099. " in space %lu page %lu index %s\n",
  2100. (ulong) page_get_space_id(page),
  2101. (ulong) page_get_page_no(page),
  2102. index->name);
  2103. buf_page_print(page, 0);
  2104. }
  2105. return(ret);
  2106. }
  2107. #ifndef UNIV_HOTBACKUP
  2108. /***************************************************************//**
  2109. Looks in the page record list for a record with the given heap number.
  2110. @return record, NULL if not found */
  2111. UNIV_INTERN
  2112. const rec_t*
  2113. page_find_rec_with_heap_no(
  2114. /*=======================*/
  2115. const page_t* page, /*!< in: index page */
  2116. ulint heap_no)/*!< in: heap number */
  2117. {
  2118. const rec_t* rec;
  2119. if (page_is_comp(page)) {
  2120. rec = page + PAGE_NEW_INFIMUM;
  2121. for(;;) {
  2122. ulint rec_heap_no = rec_get_heap_no_new(rec);
  2123. if (rec_heap_no == heap_no) {
  2124. return(rec);
  2125. } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
  2126. return(NULL);
  2127. }
  2128. rec = page + rec_get_next_offs(rec, TRUE);
  2129. }
  2130. } else {
  2131. rec = page + PAGE_OLD_INFIMUM;
  2132. for (;;) {
  2133. ulint rec_heap_no = rec_get_heap_no_old(rec);
  2134. if (rec_heap_no == heap_no) {
  2135. return(rec);
  2136. } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) {
  2137. return(NULL);
  2138. }
  2139. rec = page + rec_get_next_offs(rec, FALSE);
  2140. }
  2141. }
  2142. }
  2143. #endif /* !UNIV_HOTBACKUP */