Class: Innodb::Page
- Inherits:
-
Object
- Object
- Innodb::Page
- Defined in:
- lib/innodb/page.rb
Overview
A generic class for any type of page, which handles reading the common FIL header and trailer, and can handle (via #parse) dispatching to a more specialized class depending on page type (which comes from the FIL header). A page being handled by Innodb::Page indicates that its type is not currently handled by any more specialized class.
Direct Known Subclasses
Blob, FspHdrXdes, IbufBitmap, Index, Inode, Sys, SysDataDictionaryHeader, SysIbufHeader, SysRsegHeader, TrxSys, UndoLog
Defined Under Namespace
Classes: Blob, FspHdrXdes, IbufBitmap, Index, Inode, Sys, SysDataDictionaryHeader, SysIbufHeader, SysRsegHeader, TrxSys, UndoLog
Constant Summary collapse
- SPECIALIZED_CLASSES =
A hash of page types to specialized classes to handle them. Normally subclasses will register themselves in this list.
{}
- PAGE_TYPE =
InnoDB Page Type constants from include/fil0fil.h.
{ :ALLOCATED => { :value => 0, :description => "Freshly allocated", :usage => "page type field has not been initialized", }, :UNDO_LOG => { :value => 2, :description => "Undo log", :usage => "stores previous values of modified records", }, :INODE => { :value => 3, :description => "File segment inode", :usage => "bookkeeping for file segments", }, :IBUF_FREE_LIST => { :value => 4, :description => "Insert buffer free list", :usage => "bookkeeping for insert buffer free space management", }, :IBUF_BITMAP => { :value => 5, :description => "Insert buffer bitmap", :usage => "bookkeeping for insert buffer writes to be merged", }, :SYS => { :value => 6, :description => "System internal", :usage => "used for various purposes in the system tablespace", }, :TRX_SYS => { :value => 7, :description => "Transaction system header", :usage => "bookkeeping for the transaction system in system tablespace", }, :FSP_HDR => { :value => 8, :description => "File space header", :usage => "header page (page 0) for each tablespace file", }, :XDES => { :value => 9, :description => "Extent descriptor", :usage => "header page for subsequent blocks of 16,384 pages", }, :BLOB => { :value => 10, :description => "Uncompressed BLOB", :usage => "externally-stored uncompressed BLOB column data", }, :ZBLOB => { :value => 11, :description => "First compressed BLOB", :usage => "externally-stored compressed BLOB column data, first page", }, :ZBLOB2 => { :value => 12, :description => "Subsequent compressed BLOB", :usage => "externally-stored compressed BLOB column data, subsequent page", }, :INDEX => { :value => 17855, :description => "B+Tree index", :usage => "table and index data stored in B+Tree structure", }, }
- PAGE_TYPE_BY_VALUE =
PAGE_TYPE.inject({}) { |h, (k, v)| h[v[:value]] = k; h }
Instance Attribute Summary collapse
-
#space ⇒ Object
readonly
Returns the value of attribute space.
Class Method Summary collapse
-
.handle(page, space, buffer) ⇒ Object
Allow the specialized class to do something that isn’t ‘new’ with this page.
-
.maybe_undefined(value) ⇒ Object
A helper to convert “undefined” values stored in previous and next pointers in the page header to nil.
-
.parse(space, buffer) ⇒ 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.
Instance Method Summary collapse
-
#calculate_checksum ⇒ Object
Calculate the checksum of the page using InnoDB’s algorithm.
-
#checksum ⇒ Object
A helper function to return the checksum from the “fil” header, for easier access.
-
#corrupt? ⇒ Boolean
Is the page corrupt? Calculate the checksum of the page and compare to the stored checksum; return true or false.
-
#cursor(buffer_offset) ⇒ Object
If no block is passed, return an BufferCursor object positioned at a specific offset.
-
#dump ⇒ Object
Dump the contents of a page for debugging purposes.
- #each_region {|{ :offset => pos_fil_header, :length => size_fil_header, :name => :fil_header, :info => "FIL Header", }| ... } ⇒ Object
-
#fil_header ⇒ Object
Return the “fil” header from the page, which is common for all page types.
-
#initialize(space, buffer) ⇒ 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.
-
#lsn ⇒ Object
A helper function to return the LSN, for easier access.
-
#name ⇒ Object
Return a simple string to uniquely identify this page within the space.
-
#next ⇒ Object
A helper function to return the page number of the logical next page (from the doubly-linked list from page to page) from the “fil” header, for easier access.
-
#offset ⇒ Object
A helper function to return the page offset from the “fil” header, for easier access.
-
#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.
-
#prev ⇒ Object
A helper function to return the page number of the logical previous page (from the doubly-linked list from page to page) from the “fil” header, for easier access.
-
#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.
-
#type ⇒ Object
A helper function to return the page type from the “fil” header, for easier access.
Constructor Details
#initialize(space, buffer) ⇒ Page
Initialize a page by passing in a buffer containing the raw page contents. The buffer size should match the space’s page size.
42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/innodb/page.rb', line 42 def initialize(space, buffer) unless space && buffer raise "Page can't be initialized from nil space or buffer (space: #{space}, buffer: #{buffer})" end unless space.page_size == buffer.size raise "Buffer size #{buffer.size} is different than space page size" end @space = space @buffer = buffer end |
Instance Attribute Details
#space ⇒ Object (readonly)
Returns the value of attribute space.
55 56 57 |
# File 'lib/innodb/page.rb', line 55 def space @space end |
Class Method Details
.handle(page, space, buffer) ⇒ Object
Allow the specialized class to do something that isn’t ‘new’ with this page.
36 37 38 |
# File 'lib/innodb/page.rb', line 36 def self.handle(page, space, buffer) self.new(space, buffer) end |
.maybe_undefined(value) ⇒ Object
A helper to convert “undefined” values stored in previous and next pointers in the page header to nil.
193 194 195 |
# File 'lib/innodb/page.rb', line 193 def self.maybe_undefined(value) value == 4294967295 ? nil : value end |
.parse(space, buffer) ⇒ 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.
22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/innodb/page.rb', line 22 def self.parse(space, buffer) # Create a page object as a generic page. page = Innodb::Page.new(space, buffer) # 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) end page end |
Instance Method Details
#calculate_checksum ⇒ Object
Calculate the checksum of the page using InnoDB’s algorithm. Two sections of the page are checksummed separately, and then added together to produce the final checksum.
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/innodb/page.rb', line 257 def calculate_checksum unless size == 16384 raise "Checksum calculation is only supported for 16 KiB pages" end # Calculate the checksum of the FIL header, except for the following: # :checksum (offset 4, size 4) # :flush_lsn (offset 26, size 8) # :space_id (offset 34, size 4) c_partial_header = Innodb::Checksum.fold_enumerator( cursor(pos_fil_header + 4).each_byte_as_uint8( size_fil_header - 4 - 8 - 4 ) ) # Calculate the checksum of the page body, except for the FIL header and # the FIL trailer. c_page_body = Innodb::Checksum.fold_enumerator( cursor(pos_page_body).each_byte_as_uint8( size - size_fil_trailer - size_fil_header ) ) # Add the two checksums together, and mask the result back to 32 bits. (c_partial_header + c_page_body) & Innodb::Checksum::MAX end |
#checksum ⇒ Object
A helper function to return the checksum from the “fil” header, for easier access.
219 220 221 |
# File 'lib/innodb/page.rb', line 219 def checksum fil_header[:checksum] end |
#corrupt? ⇒ Boolean
Is the page corrupt? Calculate the checksum of the page and compare to the stored checksum; return true or false.
288 289 290 |
# File 'lib/innodb/page.rb', line 288 def corrupt? checksum != calculate_checksum 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.
78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/innodb/page.rb', line 78 def cursor(buffer_offset) new_cursor = BufferCursor.new(@buffer, buffer_offset) new_cursor.push_name("space[#{space.name}]") 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 |
#dump ⇒ Object
Dump the contents of a page for debugging purposes.
333 334 335 336 337 338 339 340 |
# File 'lib/innodb/page.rb', line 333 def dump puts "#{self}:" puts puts "fil header:" pp fil_header puts end |
#each_region {|{ :offset => pos_fil_header, :length => size_fil_header, :name => :fil_header, :info => "FIL Header", }| ... } ⇒ Object
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/innodb/page.rb', line 292 def each_region unless block_given? return enum_for(:each_region) end yield({ :offset => pos_fil_header, :length => size_fil_header, :name => :fil_header, :info => "FIL Header", }) yield({ :offset => pos_fil_trailer, :length => size_fil_trailer, :name => :fil_trailer, :info => "FIL Trailer", }) nil end |
#fil_header ⇒ Object
Return the “fil” header from the page, which is common for all page types.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/innodb/page.rb', line 198 def fil_header @fil_header ||= cursor(pos_fil_header).name("fil") do |c| { :checksum => c.name("checksum") { c.get_uint32 }, :offset => c.name("offset") { c.get_uint32 }, :prev => c.name("prev") { Innodb::Page.maybe_undefined(c.get_uint32) }, :next => c.name("next") { Innodb::Page.maybe_undefined(c.get_uint32) }, :lsn => c.name("lsn") { c.get_uint64 }, :type => c.name("type") { PAGE_TYPE_BY_VALUE[c.get_uint16] }, :flush_lsn => c.name("flush_lsn") { c.get_uint64 }, :space_id => c.name("space_id") { c.get_uint32 }, } end 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.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/innodb/page.rb', line 316 def inspect if fil_header "#<%s: size=%i, space_id=%i, offset=%i, type=%s, prev=%s, next=%s>" % [ self.class, size, fil_header[:space_id], fil_header[:offset], fil_header[:type], fil_header[:prev] || "nil", fil_header[:next] || "nil", ] else "#<#{self.class}>" end end |
#lsn ⇒ Object
A helper function to return the LSN, for easier access.
244 245 246 |
# File 'lib/innodb/page.rb', line 244 def lsn fil_header[:lsn] 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.
65 66 67 68 69 70 71 72 |
# File 'lib/innodb/page.rb', line 65 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 |
#next ⇒ Object
A helper function to return the page number of the logical next page (from the doubly-linked list from page to page) from the “fil” header, for easier access.
239 240 241 |
# File 'lib/innodb/page.rb', line 239 def next fil_header[:next] end |
#offset ⇒ Object
A helper function to return the page offset from the “fil” header, for easier access.
225 226 227 |
# File 'lib/innodb/page.rb', line 225 def offset fil_header[:offset] 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.
94 95 96 |
# File 'lib/innodb/page.rb', line 94 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.
105 106 107 |
# File 'lib/innodb/page.rb', line 105 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.
116 117 118 |
# File 'lib/innodb/page.rb', line 116 def pos_page_body pos_fil_header + size_fil_header end |
#prev ⇒ Object
A helper function to return the page number of the logical previous page (from the doubly-linked list from page to page) from the “fil” header, for easier access.
232 233 234 |
# File 'lib/innodb/page.rb', line 232 def prev fil_header[:prev] end |
#size ⇒ Object
Return the page size, to eventually be able to deal with non-16kB pages.
58 59 60 |
# File 'lib/innodb/page.rb', line 58 def size @size ||= @buffer.size end |
#size_fil_header ⇒ Object
Return the size of the “fil” header, in bytes.
99 100 101 |
# File 'lib/innodb/page.rb', line 99 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.
110 111 112 |
# File 'lib/innodb/page.rb', line 110 def size_fil_trailer 4 + 4 end |
#type ⇒ Object
A helper function to return the page type from the “fil” header, for easier access.
250 251 252 |
# File 'lib/innodb/page.rb', line 250 def type fil_header[:type] end |