Class: Innodb::Page
- Inherits:
-
Object
- Object
- Innodb::Page
- Extended by:
- Forwardable
- Defined in:
- lib/innodb/page.rb,
lib/innodb/page/sys.rb,
lib/innodb/page/blob.rb,
lib/innodb/page/index.rb,
lib/innodb/page/inode.rb,
lib/innodb/page/trx_sys.rb,
lib/innodb/page/undo_log.rb,
lib/innodb/page/ibuf_bitmap.rb,
lib/innodb/page/fsp_hdr_xdes.rb,
lib/innodb/page/sys_ibuf_header.rb,
lib/innodb/page/sys_rseg_header.rb,
lib/innodb/page/index_compressed.rb,
lib/innodb/page/sys_data_dictionary_header.rb
Direct Known Subclasses
Blob, FspHdrXdes, IbufBitmap, Index, Inode, Sys, SysDataDictionaryHeader, SysIbufHeader, SysRsegHeader, TrxSys, UndoLog
Defined Under Namespace
Classes: Address, Blob, FilHeader, FilTrailer, FspHdrXdes, IbufBitmap, Index, IndexCompressed, Inode, Region, Sys, SysDataDictionaryHeader, SysIbufHeader, SysRsegHeader, TrxSys, UndoLog
Constant Summary collapse
- PAGE_TYPE =
InnoDB Page Type constants from include/fil0fil.h.
{ ALLOCATED: { value: 0, description: "Freshly allocated", }, UNDO_LOG: { value: 2, description: "Undo log", }, INODE: { value: 3, description: "File segment inode", }, IBUF_FREE_LIST: { value: 4, description: "Insert buffer free list", }, IBUF_BITMAP: { value: 5, description: "Insert buffer bitmap", }, SYS: { value: 6, description: "System internal", }, TRX_SYS: { value: 7, description: "Transaction system header", }, FSP_HDR: { value: 8, description: "File space header", }, XDES: { value: 9, description: "Extent descriptor", }, BLOB: { value: 10, description: "Uncompressed BLOB", }, ZBLOB: { value: 11, description: "First compressed BLOB", }, ZBLOB2: { value: 12, description: "Subsequent compressed BLOB", }, UNKNOWN: { value: 13, description: "Unknown", }, COMPRESSED: { value: 14, description: "Compressed", }, ENCRYPTED: { value: 15, description: "Encrypted", }, COMPRESSED_AND_ENCRYPTED: { value: 16, description: "Compressed and Encrypted", }, ENCRYPTED_RTREE: { value: 17, description: "Encrypted R-tree", }, SDI_BLOB: { value: 18, description: "Uncompressed SDI BLOB", }, SDI_ZBLOB: { value: 19, description: "Compressed SDI BLOB", }, LEGACY_DBLWR: { value: 20, description: "Legacy doublewrite buffer", }, RSEG_ARRAY: { value: 21, description: "Rollback Segment Array", }, LOB_INDEX: { value: 22, description: "Index of uncompressed LOB", }, LOB_DATA: { value: 23, description: "Data of uncompressed LOB", }, LOB_FIRST: { value: 24, description: "First page of an uncompressed LOB", }, ZLOB_FIRST: { value: 25, description: "First page of a compressed LOB", }, ZLOB_DATA: { value: 26, description: "Data of compressed LOB", }, ZLOB_INDEX: { value: 27, description: "Index of compressed LOB", }, ZLOB_FRAG: { value: 28, description: "Fragment of compressed LOB", }, ZLOB_FRAG_ENTRY: { value: 29, description: "Index of fragment for compressed LOB", }, SDI: { value: 17_853, description: "Serialized Dictionary Information", }, RTREE: { value: 17_854, description: "R-tree index", }, INDEX: { value: 17_855, description: "B+Tree index", }, }.freeze
- PAGE_TYPE_BY_VALUE =
PAGE_TYPE.each_with_object({}) { |(k, v), h| h[v[:value]] = k }
- UNDEFINED_PAGE_NUMBER =
A page number representing “undefined” values, (4294967295).
(2**32) - 1
Class Attribute Summary collapse
-
.specialized_classes ⇒ Object
readonly
Returns the value of attribute specialized_classes.
Instance Attribute Summary collapse
-
#space ⇒ Object
readonly
Returns the value of attribute space.
Class Method Summary collapse
-
.handle(_page, space, buffer, page_number = nil) ⇒ Object
Allow the specialized class to do something that isn’t ‘new’ with this page.
-
.maybe_undefined(page_number) ⇒ Object
A helper to convert “undefined” values stored in previous and next pointers in the page header to nil.
- .page_type_by_value(value) ⇒ Object
-
.parse(space, buffer, page_number = nil) ⇒ Object
Load a page as a generic page in order to make the “fil” header accessible, and then attempt to hand off the page to a specialized class to be re-parsed if possible.
- .register_specialization(page_type, specialized_class) ⇒ Object
- .specialization_for(page_type) ⇒ Object
- .specialization_for?(page_type) ⇒ Boolean
-
.undefined?(page_number) ⇒ Boolean
A helper to check if a page number is the undefined page number.
Instance Method Summary collapse
-
#checksum_crc32 ⇒ Object
Calculate the checksum of the page using the CRC32c algorithm.
- #checksum_crc32? ⇒ Boolean
-
#checksum_innodb ⇒ Object
Calculate the checksum of the page using InnoDB’s algorithm.
- #checksum_innodb? ⇒ Boolean
-
#checksum_invalid? ⇒ Boolean
Is the page checksum incorrect?.
- #checksum_type ⇒ Object
-
#checksum_valid? ⇒ Boolean
Is the page checksum correct?.
-
#corrupt? ⇒ Boolean
Is the page corrupt, either due to data corruption, tearing, or in the wrong place?.
-
#cursor(buffer_offset) ⇒ Object
If no block is passed, return an BufferCursor object positioned at a specific offset.
- #default_page_size? ⇒ Boolean
-
#dump ⇒ Object
Dump the contents of a page for debugging purposes.
-
#each_page_body_byte_as_uint8(&block) ⇒ Object
Iterate each byte of the page body, except for the FIL header and the FIL trailer.
-
#each_page_header_byte_as_uint8(&block) ⇒ Object
Iterate each byte of the FIL header.
- #each_region {|Region.new( offset: pos_fil_header, length: size_fil_header, name: :fil_header, info: "FIL Header" )| ... } ⇒ Object
-
#extent_descriptor? ⇒ Boolean
Is this an extent descriptor page (either FSP_HDR or XDES)?.
-
#fil_header ⇒ Object
Return the “fil” header from the page, which is common for all page types.
-
#fil_trailer ⇒ Object
Return the “fil” trailer from the page, which is common for all page types.
-
#in_doublewrite_buffer? ⇒ Boolean
Is the page in the doublewrite buffer?.
-
#initialize(space, buffer, page_number = nil) ⇒ Page
constructor
Initialize a page by passing in a buffer containing the raw page contents.
-
#inspect ⇒ Object
Implement a custom inspect method to avoid irb printing the contents of the page buffer, since it’s very large and mostly not interesting.
- #inspect_header_fields ⇒ Object
-
#misplaced? ⇒ Boolean
Is the page misplaced in the wrong file or by offset in the file?.
-
#misplaced_offset? ⇒ Boolean
Is the page number stored in the header different from the page number which was supposed to be read?.
-
#misplaced_space? ⇒ Boolean
Is the space ID stored in the header different from that of the space provided when initializing this page?.
-
#name ⇒ Object
Return a simple string to uniquely identify this page within the space.
-
#pos_fil_header ⇒ Object
Return the byte offset of the start of the “fil” header, which is at the beginning of the page.
-
#pos_fil_trailer ⇒ Object
Return the byte offset of the start of the “fil” trailer, which is at the end of the page.
-
#pos_page_body ⇒ Object
Return the position of the “body” of the page, which starts after the FIL header.
-
#pos_partial_page_header ⇒ Object
The start of the checksummed portion of the file header.
-
#size ⇒ Object
Return the page size, to eventually be able to deal with non-16kB pages.
-
#size_fil_header ⇒ Object
Return the size of the “fil” header, in bytes.
-
#size_fil_trailer ⇒ Object
Return the size of the “fil” trailer, in bytes.
-
#size_page_body ⇒ Object
Return the size of the page body, excluding the header and trailer.
-
#size_partial_page_header ⇒ Object
The size of the portion of the fil header that is included in the checksum.
-
#torn? ⇒ Boolean
Is the LSN stored in the header different from the one stored in the trailer?.
Constructor Details
#initialize(space, buffer, page_number = nil) ⇒ Page
Initialize a page by passing in a buffer containing the raw page contents. The buffer size should match the space’s page size.
103 104 105 106 107 108 109 110 |
# File 'lib/innodb/page.rb', line 103 def initialize(space, buffer, page_number = nil) raise "Page can't be initialized from nil space or buffer (space: #{space}, buffer: #{buffer})" unless buffer raise "Buffer size #{buffer.size} is different than space page size" if space && space.page_size != buffer.size @space = space @buffer = buffer @page_number = page_number end |
Class Attribute Details
.specialized_classes ⇒ Object (readonly)
Returns the value of attribute specialized_classes.
57 58 59 |
# File 'lib/innodb/page.rb', line 57 def specialized_classes @specialized_classes end |
Instance Attribute Details
#space ⇒ Object (readonly)
Returns the value of attribute space.
112 113 114 |
# File 'lib/innodb/page.rb', line 112 def space @space end |
Class Method Details
.handle(_page, space, buffer, page_number = nil) ⇒ Object
Allow the specialized class to do something that isn’t ‘new’ with this page.
97 98 99 |
# File 'lib/innodb/page.rb', line 97 def self.handle(_page, space, buffer, page_number = nil) new(space, buffer, page_number) end |
.maybe_undefined(page_number) ⇒ Object
A helper to convert “undefined” values stored in previous and next pointers in the page header to nil.
344 345 346 |
# File 'lib/innodb/page.rb', line 344 def self.maybe_undefined(page_number) page_number unless undefined?(page_number) end |
.page_type_by_value(value) ⇒ Object
348 349 350 |
# File 'lib/innodb/page.rb', line 348 def self.page_type_by_value(value) PAGE_TYPE_BY_VALUE[value] || value end |
.parse(space, buffer, page_number = nil) ⇒ Object
Load a page as a generic page in order to make the “fil” header accessible, and then attempt to hand off the page to a specialized class to be re-parsed if possible. If there is no specialized class for this type of page, return the generic object.
This could be optimized to reach into the page buffer and efficiently extract the page type in order to avoid throwing away a generic Innodb::Page object when parsing every specialized page, but this is a bit cleaner, and we’re not particularly performance sensitive.
83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/innodb/page.rb', line 83 def self.parse(space, buffer, page_number = nil) # Create a page object as a generic page. page = Innodb::Page.new(space, buffer, page_number) # If there is a specialized class available for this page type, re-create # the page object using that specialized class. if (specialized_class = specialized_classes[page.type]) page = specialized_class.handle(page, space, buffer, page_number) end page end |
.register_specialization(page_type, specialized_class) ⇒ Object
60 61 62 |
# File 'lib/innodb/page.rb', line 60 def self.register_specialization(page_type, specialized_class) @specialized_classes[page_type] = specialized_class end |
.specialization_for(page_type) ⇒ Object
64 65 66 67 68 |
# File 'lib/innodb/page.rb', line 64 def self.specialization_for(page_type) # This needs to intentionally use Innodb::Page because we need to register # in the class instance variable in *that* class. Innodb::Page.register_specialization(page_type, self) end |
.specialization_for?(page_type) ⇒ Boolean
70 71 72 |
# File 'lib/innodb/page.rb', line 70 def self.specialization_for?(page_type) Innodb::Page.specialized_classes.include?(page_type) end |
.undefined?(page_number) ⇒ Boolean
A helper to check if a page number is the undefined page number.
338 339 340 |
# File 'lib/innodb/page.rb', line 338 def self.undefined?(page_number) page_number == UNDEFINED_PAGE_NUMBER end |
Instance Method Details
#checksum_crc32 ⇒ Object
Calculate the checksum of the page using the CRC32c algorithm.
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
# File 'lib/innodb/page.rb', line 422 def checksum_crc32 raise "Checksum calculation is only supported for 16 KiB pages" unless default_page_size? @checksum_crc32 ||= begin # Calculate the CRC32c of the page header. crc_partial_header = Digest::CRC32c.new each_page_header_byte_as_uint8 do |byte| crc_partial_header << byte.chr end # Calculate the CRC32c of the page body. crc_page_body = Digest::CRC32c.new each_page_body_byte_as_uint8 do |byte| crc_page_body << byte.chr end # Bitwise XOR the two checksums together. crc_partial_header.checksum ^ crc_page_body.checksum end end |
#checksum_crc32? ⇒ Boolean
443 444 445 |
# File 'lib/innodb/page.rb', line 443 def checksum_crc32? checksum == checksum_crc32 end |
#checksum_innodb ⇒ Object
Calculate the checksum of the page using InnoDB’s algorithm.
402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
# File 'lib/innodb/page.rb', line 402 def checksum_innodb raise "Checksum calculation is only supported for 16 KiB pages" unless default_page_size? @checksum_innodb ||= begin # Calculate the InnoDB checksum of the page header. c_partial_header = Innodb::Checksum.fold_enumerator(each_page_header_byte_as_uint8) # Calculate the InnoDB checksum of the page body. c_page_body = Innodb::Checksum.fold_enumerator(each_page_body_byte_as_uint8) # Add the two checksums together, and mask the result back to 32 bits. (c_partial_header + c_page_body) & Innodb::Checksum::MAX end end |
#checksum_innodb? ⇒ Boolean
417 418 419 |
# File 'lib/innodb/page.rb', line 417 def checksum_innodb? checksum == checksum_innodb end |
#checksum_invalid? ⇒ Boolean
Is the page checksum incorrect?
453 454 455 |
# File 'lib/innodb/page.rb', line 453 def checksum_invalid? !checksum_valid? end |
#checksum_type ⇒ Object
457 458 459 460 461 462 |
# File 'lib/innodb/page.rb', line 457 def checksum_type return :crc32 if checksum_crc32? return :innodb if checksum_innodb? nil end |
#checksum_valid? ⇒ Boolean
Is the page checksum correct?
448 449 450 |
# File 'lib/innodb/page.rb', line 448 def checksum_valid? checksum_crc32? || checksum_innodb? end |
#corrupt? ⇒ Boolean
Is the page corrupt, either due to data corruption, tearing, or in the wrong place?
494 495 496 |
# File 'lib/innodb/page.rb', line 494 def corrupt? checksum_invalid? || torn? || misplaced? end |
#cursor(buffer_offset) ⇒ Object
If no block is passed, return an BufferCursor object positioned at a specific offset. If a block is passed, create a cursor at the provided offset and yield it to the provided block one time, and then return the return value of the block.
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/innodb/page.rb', line 139 def cursor(buffer_offset) new_cursor = BufferCursor.new(@buffer, buffer_offset) new_cursor.push_name("space[#{space&.name || 'unknown'}]") new_cursor.push_name("page[#{name}]") if block_given? # Call the block once and return its return value. yield new_cursor else # Return the cursor itself. new_cursor end end |
#default_page_size? ⇒ Boolean
119 120 121 |
# File 'lib/innodb/page.rb', line 119 def default_page_size? size == Innodb::Space::DEFAULT_PAGE_SIZE end |
#dump ⇒ Object
Dump the contents of a page for debugging purposes.
547 548 549 550 551 552 553 554 555 556 557 558 |
# File 'lib/innodb/page.rb', line 547 def dump puts "#{self}:" puts puts "fil header:" pp fil_header puts puts "fil trailer:" pp fil_trailer puts end |
#each_page_body_byte_as_uint8(&block) ⇒ Object
Iterate each byte of the page body, except for the FIL header and the FIL trailer.
395 396 397 398 399 |
# File 'lib/innodb/page.rb', line 395 def each_page_body_byte_as_uint8(&block) return enum_for(:each_page_body_byte_as_uint8) unless block_given? cursor(pos_page_body).each_byte_as_uint8(size_page_body, &block) end |
#each_page_header_byte_as_uint8(&block) ⇒ Object
Iterate each byte of the FIL header.
387 388 389 390 391 |
# File 'lib/innodb/page.rb', line 387 def each_page_header_byte_as_uint8(&block) return enum_for(:each_page_header_byte_as_uint8) unless block_given? cursor(pos_partial_page_header).each_byte_as_uint8(size_partial_page_header, &block) end |
#each_region {|Region.new( offset: pos_fil_header, length: size_fil_header, name: :fil_header, info: "FIL Header" )| ... } ⇒ Object
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
# File 'lib/innodb/page.rb', line 503 def each_region return enum_for(:each_region) unless block_given? yield Region.new( offset: pos_fil_header, length: size_fil_header, name: :fil_header, info: "FIL Header" ) yield Region.new( offset: pos_fil_trailer, length: size_fil_trailer, name: :fil_trailer, info: "FIL Trailer" ) nil end |
#extent_descriptor? ⇒ Boolean
Is this an extent descriptor page (either FSP_HDR or XDES)?
499 500 501 |
# File 'lib/innodb/page.rb', line 499 def extent_descriptor? type == :FSP_HDR || type == :XDES end |
#fil_header ⇒ Object
Return the “fil” header from the page, which is common for all page types.
353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
# File 'lib/innodb/page.rb', line 353 def fil_header @fil_header ||= cursor(pos_fil_header).name("fil_header") do |c| FilHeader.new( checksum: c.name("checksum") { c.read_uint32 }, offset: c.name("offset") { c.read_uint32 }, prev: c.name("prev") { Innodb::Page.maybe_undefined(c.read_uint32) }, next: c.name("next") { Innodb::Page.maybe_undefined(c.read_uint32) }, lsn: c.name("lsn") { c.read_uint64 }, type: c.name("type") { Innodb::Page.page_type_by_value(c.read_uint16) }, flush_lsn: c.name("flush_lsn") { c.read_uint64 }, space_id: c.name("space_id") { c.read_uint32 } ) end end |
#fil_trailer ⇒ Object
Return the “fil” trailer from the page, which is common for all page types.
369 370 371 372 373 374 375 376 |
# File 'lib/innodb/page.rb', line 369 def fil_trailer @fil_trailer ||= cursor(pos_fil_trailer).name("fil_trailer") do |c| FilTrailer.new( checksum: c.name("checksum") { c.read_uint32 }, lsn_low32: c.name("lsn_low32") { c.read_uint32 } ) end end |
#in_doublewrite_buffer? ⇒ Boolean
Is the page in the doublewrite buffer?
471 472 473 |
# File 'lib/innodb/page.rb', line 471 def in_doublewrite_buffer? space&.system_space? && space.doublewrite_page?(offset) end |
#inspect ⇒ Object
Implement a custom inspect method to avoid irb printing the contents of the page buffer, since it’s very large and mostly not interesting.
542 543 544 |
# File 'lib/innodb/page.rb', line 542 def inspect "#<#{self.class} #{inspect_header_fields || '(page header unavailable)'}>" end |
#inspect_header_fields ⇒ Object
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
# File 'lib/innodb/page.rb', line 523 def inspect_header_fields return nil unless fil_header %i[ size space_id offset type prev next checksum_valid? checksum_type torn? misplaced? ].map { |m| "#{m}=#{send(m).inspect}" }.join(", ") end |
#misplaced? ⇒ Boolean
Is the page misplaced in the wrong file or by offset in the file?
488 489 490 |
# File 'lib/innodb/page.rb', line 488 def misplaced? !in_doublewrite_buffer? && (misplaced_space? || misplaced_offset?) end |
#misplaced_offset? ⇒ Boolean
Is the page number stored in the header different from the page number which was supposed to be read?
483 484 485 |
# File 'lib/innodb/page.rb', line 483 def misplaced_offset? offset != @page_number end |
#misplaced_space? ⇒ Boolean
Is the space ID stored in the header different from that of the space provided when initializing this page?
477 478 479 |
# File 'lib/innodb/page.rb', line 477 def misplaced_space? space && (space_id != space.space_id) end |
#name ⇒ Object
Return a simple string to uniquely identify this page within the space. Be careful not to call anything which would instantiate a BufferCursor so that we can use this method in cursor initialization.
126 127 128 129 130 131 132 133 |
# File 'lib/innodb/page.rb', line 126 def name page_offset = BinData::Uint32be.read(@buffer.slice(4, 4)) page_type = BinData::Uint16be.read(@buffer.slice(24, 2)) "%i,%s" % [ page_offset, PAGE_TYPE_BY_VALUE[page_type], ] end |
#pos_fil_header ⇒ Object
Return the byte offset of the start of the “fil” header, which is at the beginning of the page. Included here primarily for completeness.
155 156 157 |
# File 'lib/innodb/page.rb', line 155 def pos_fil_header 0 end |
#pos_fil_trailer ⇒ Object
Return the byte offset of the start of the “fil” trailer, which is at the end of the page.
180 181 182 |
# File 'lib/innodb/page.rb', line 180 def pos_fil_trailer size - size_fil_trailer end |
#pos_page_body ⇒ Object
Return the position of the “body” of the page, which starts after the FIL header.
191 192 193 |
# File 'lib/innodb/page.rb', line 191 def pos_page_body pos_fil_header + size_fil_header end |
#pos_partial_page_header ⇒ Object
The start of the checksummed portion of the file header.
165 166 167 |
# File 'lib/innodb/page.rb', line 165 def pos_partial_page_header pos_fil_header + 4 end |
#size ⇒ Object
Return the page size, to eventually be able to deal with non-16kB pages.
115 116 117 |
# File 'lib/innodb/page.rb', line 115 def size @size ||= @buffer.size end |
#size_fil_header ⇒ Object
Return the size of the “fil” header, in bytes.
160 161 162 |
# File 'lib/innodb/page.rb', line 160 def size_fil_header 4 + 4 + 4 + 4 + 8 + 2 + 8 + 4 end |
#size_fil_trailer ⇒ Object
Return the size of the “fil” trailer, in bytes.
185 186 187 |
# File 'lib/innodb/page.rb', line 185 def size_fil_trailer 4 + 4 end |
#size_page_body ⇒ Object
Return the size of the page body, excluding the header and trailer.
196 197 198 |
# File 'lib/innodb/page.rb', line 196 def size_page_body size - size_fil_trailer - size_fil_header end |
#size_partial_page_header ⇒ Object
The size of the portion of the fil header that is included in the checksum. Exclude the following:
:checksum (offset 4, size 4)
:flush_lsn (offset 26, size 8)
:space_id (offset 34, size 4)
174 175 176 |
# File 'lib/innodb/page.rb', line 174 def size_partial_page_header size_fil_header - 4 - 8 - 4 end |
#torn? ⇒ Boolean
Is the LSN stored in the header different from the one stored in the trailer?
466 467 468 |
# File 'lib/innodb/page.rb', line 466 def torn? fil_header.lsn_low32 != fil_trailer.lsn_low32 end |