innodb_ruby 0.7.12 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/innodb_space CHANGED
@@ -262,7 +262,12 @@ def page_account(space, page_number)
262
262
  end
263
263
 
264
264
  page = space.page(page_number)
265
- puts " Page type is #{page.type}."
265
+ page_type = Innodb::Page::PAGE_TYPE[page.type]
266
+ puts " Page type is %s (%s, %s)." % [
267
+ page.type,
268
+ page_type[:description],
269
+ page_type[:usage],
270
+ ]
266
271
 
267
272
  xdes = space.xdes_for_page(page_number)
268
273
  puts " Extent descriptor for pages %d-%d is at page %d, offset %d." % [
@@ -354,7 +359,7 @@ def page_directory_summary(page)
354
359
  record = page.record(offset)
355
360
  key = if [:conventional, :node_pointer].include? record[:header][:type]
356
361
  if record[:key]
357
- "(%s)" % record[:key].join(", ")
362
+ "(%s)" % record[:key].map { |r| "%s=%s" % [r[:name], r[:value]] }.join(", ")
358
363
  end
359
364
  end
360
365
  puts "%-8i%-8i%-14s%-8i%s" % [
@@ -409,8 +414,8 @@ def index_recurse(index)
409
414
  page.each_record do |record|
410
415
  puts "%sRECORD: (%s) -> (%s)" % [
411
416
  " " * (depth+1),
412
- record[:key].join(", "),
413
- record[:row].join(", "),
417
+ record[:key].map { |r| "%s=%s" % [r[:name], r[:value]] }.join(", "),
418
+ record[:row].map { |r| "%s=%s" % [r[:name], r[:value]] }.join(", "),
414
419
  ]
415
420
  end
416
421
  end
@@ -418,7 +423,7 @@ def index_recurse(index)
418
423
  lambda do |parent_page, child_page, child_min_key, depth|
419
424
  puts "%sNODE POINTER RECORD >= (%s) -> #%i" % [
420
425
  " " * depth,
421
- child_min_key.join(", "),
426
+ child_min_key.map { |r| "%s=%s" % [r[:name], r[:value]] }.join(", "),
422
427
  child_page.offset,
423
428
  ]
424
429
  end
@@ -479,7 +484,7 @@ def index_level_summary(index, levels)
479
484
  page.record_space,
480
485
  page.free_space,
481
486
  page.records,
482
- page.first_record[:key].join("|"),
487
+ page.first_record[:key].map { |r| r[:value] }.join("|"),
483
488
  ]
484
489
  end
485
490
  end
@@ -667,9 +672,11 @@ end
667
672
  space = Innodb::Space.new(@options.file, @options.page_size)
668
673
 
669
674
  if @options.describer
670
- unless space.record_describer = eval(@options.describer)
671
- space.record_describer = Innodb::RecordDescriber.const_get(@options.describer)
675
+ describer = eval(@options.describer)
676
+ unless describer
677
+ describer = Innodb::RecordDescriber.const_get(@options.describer)
672
678
  end
679
+ space.record_describer = describer.new
673
680
  end
674
681
 
675
682
  if ARGV.empty?
data/lib/innodb.rb CHANGED
@@ -6,6 +6,8 @@ require "enumerator"
6
6
 
7
7
  require "innodb/version"
8
8
  require "innodb/checksum"
9
+ require "innodb/record_describer"
10
+ require "innodb/data_dictionary"
9
11
  require "innodb/page"
10
12
  require "innodb/page/blob"
11
13
  require "innodb/page/fsp_hdr_xdes"
@@ -14,7 +16,6 @@ require "innodb/page/index"
14
16
  require "innodb/page/trx_sys"
15
17
  require "innodb/page/sys"
16
18
  require "innodb/page/undo_log"
17
- require "innodb/record_describer"
18
19
  require "innodb/field"
19
20
  require "innodb/space"
20
21
  require "innodb/inode"
@@ -0,0 +1,53 @@
1
+ class Innodb::DataDictionary
2
+ # A record describer for SYS_TABLES clustered records.
3
+ class SYS_TABLES_PRIMARY < Innodb::RecordDescriber
4
+ type :clustered
5
+ key "NAME", "VARCHAR(100)", :NOT_NULL
6
+ row "ID", :BIGINT, :UNSIGNED, :NOT_NULL
7
+ row "N_COLS", :INT, :UNSIGNED, :NOT_NULL
8
+ row "TYPE", :INT, :UNSIGNED, :NOT_NULL
9
+ row "MIX_ID", :BIGINT, :UNSIGNED, :NOT_NULL
10
+ row "MIX_LEN", :INT, :UNSIGNED, :NOT_NULL
11
+ row "CLUSTER_NAME", "VARCHAR(100)", :NOT_NULL
12
+ row "SPACE", :INT, :UNSIGNED, :NOT_NULL
13
+ end
14
+
15
+ # A record describer for SYS_TABLES secondary key on ID.
16
+ class SYS_TABLES_ID < Innodb::RecordDescriber
17
+ type :secondary
18
+ key "ID", :BIGINT, :UNSIGNED, :NOT_NULL
19
+ row "NAME", "VARCHAR(100)", :NOT_NULL
20
+ end
21
+
22
+ # A record describer for SYS_COLUMNS clustered records.
23
+ class SYS_COLUMNS_PRIMARY < Innodb::RecordDescriber
24
+ type :clustered
25
+ key "TABLE_ID", :BIGINT, :UNSIGNED, :NOT_NULL
26
+ key "POS", :INT, :UNSIGNED, :NOT_NULL
27
+ row "NAME", "VARCHAR(100)", :NOT_NULL
28
+ row "MTYPE", :INT, :UNSIGNED, :NOT_NULL
29
+ row "PRTYPE", :INT, :UNSIGNED, :NOT_NULL
30
+ row "LEN", :INT, :UNSIGNED, :NOT_NULL
31
+ row "PREC", :INT, :UNSIGNED, :NOT_NULL
32
+ end
33
+
34
+ # A record describer for SYS_INDEXES clustered records.
35
+ class SYS_INDEXES_PRIMARY < Innodb::RecordDescriber
36
+ type :clustered
37
+ key "TABLE_ID", :BIGINT, :UNSIGNED, :NOT_NULL
38
+ key "ID", :BIGINT, :UNSIGNED, :NOT_NULL
39
+ row "NAME", "VARCHAR(100)", :NOT_NULL
40
+ row "N_FIELDS", :INT, :UNSIGNED, :NOT_NULL
41
+ row "TYPE", :INT, :UNSIGNED, :NOT_NULL
42
+ row "SPACE", :INT, :UNSIGNED, :NOT_NULL
43
+ row "PAGE_NO", :INT, :UNSIGNED, :NOT_NULL
44
+ end
45
+
46
+ # A record describer for SYS_FIELDS clustered records.
47
+ class SYS_FIELDS_PRIMARY < Innodb::RecordDescriber
48
+ type :clustered
49
+ key "INDEX_ID", :BIGINT, :UNSIGNED, :NOT_NULL
50
+ key "POS", :INT, :UNSIGNED, :NOT_NULL
51
+ row "COL_NAME", "VARCHAR(100)", :NOT_NULL
52
+ end
53
+ end
data/lib/innodb/index.rb CHANGED
@@ -201,7 +201,7 @@ class Innodb::Index
201
201
  puts "linear_search_from_cursor: page=%i, level=%i, start=(%s)" % [
202
202
  page.offset,
203
203
  page.level,
204
- this_rec && this_rec[:key].join(", "),
204
+ this_rec && this_rec[:key].map { |r| r[:value] }.join(", "),
205
205
  ]
206
206
  end
207
207
 
@@ -214,19 +214,19 @@ class Innodb::Index
214
214
  puts "linear_search_from_cursor: page=%i, level=%i, current=(%s)" % [
215
215
  page.offset,
216
216
  page.level,
217
- this_rec && this_rec[:key].join(", "),
217
+ this_rec && this_rec[:key].map { |r| r[:value] }.join(", "),
218
218
  ]
219
219
  end
220
220
 
221
221
  # If we reach supremum, return the last non-system record we got.
222
222
  return this_rec if next_rec[:header][:type] == :supremum
223
223
 
224
- if compare_key(key, this_rec[:key]) < 0
224
+ if compare_key(key, this_rec[:key][:value]) < 0
225
225
  return this_rec
226
226
  end
227
227
 
228
- if (compare_key(key, this_rec[:key]) >= 0) &&
229
- (compare_key(key, next_rec[:key]) < 0)
228
+ if (compare_key(key, this_rec[:key][:value]) >= 0) &&
229
+ (compare_key(key, next_rec[:key][:value]) < 0)
230
230
  # The desired key is either an exact match for this_rec or is greater
231
231
  # than it but less than next_rec. If this is a non-leaf page, that
232
232
  # will mean that the record will fall on the leaf page this node
@@ -263,7 +263,7 @@ class Innodb::Index
263
263
  page.level,
264
264
  dir.size,
265
265
  mid,
266
- rec[:key] && rec[:key].join(", "),
266
+ rec[:key] && rec[:key].map { |r| r[:value] }.join(", "),
267
267
  ]
268
268
  end
269
269
 
@@ -276,7 +276,7 @@ class Innodb::Index
276
276
  end
277
277
 
278
278
  # Compare the desired key to the mid-point record's key.
279
- case compare_key(key, rec[:key])
279
+ case compare_key(key, rec[:key][:value])
280
280
  when 0
281
281
  # An exact match for the key was found. Return the record.
282
282
  @stats[:binary_search_by_directory_exact_match] += 1
@@ -292,7 +292,7 @@ class Innodb::Index
292
292
  binary_search_by_directory(page, dir[mid...dir.size], key)
293
293
  else
294
294
  next_rec = page.record(dir[mid+1])
295
- next_key = next_rec && compare_key(key, next_rec[:key])
295
+ next_key = next_rec && compare_key(key, next_rec[:key][:value])
296
296
  if dir.size == 1 || next_key == -1 || next_key == 0
297
297
  # This is the last entry remaining from the directory, or our key is
298
298
  # greater than rec and less than rec+1's key. Use linear search to
@@ -350,7 +350,7 @@ class Innodb::Index
350
350
  # We're on a leaf page, so return the page and record if there is a
351
351
  # match. If there is no match, break the loop and cause nil to be
352
352
  # returned.
353
- return page, rec if compare_key(key, rec[:key]) == 0
353
+ return page, rec if compare_key(key, rec[:key][:value]) == 0
354
354
  break
355
355
  end
356
356
  end
@@ -383,7 +383,7 @@ class Innodb::Index
383
383
  # We're on a leaf page, so return the page and record if there is a
384
384
  # match. If there is no match, break the loop and cause nil to be
385
385
  # returned.
386
- return page, rec if compare_key(key, rec[:key]) == 0
386
+ return page, rec if compare_key(key, rec[:key][:value]) == 0
387
387
  break
388
388
  end
389
389
  end
data/lib/innodb/page.rb CHANGED
@@ -112,21 +112,75 @@ class Innodb::Page
112
112
 
113
113
  # InnoDB Page Type constants from include/fil0fil.h.
114
114
  PAGE_TYPE = {
115
- 0 => :ALLOCATED, # Freshly allocated page
116
- 2 => :UNDO_LOG, # Undo log page
117
- 3 => :INODE, # Index node
118
- 4 => :IBUF_FREE_LIST, # Insert buffer free list
119
- 5 => :IBUF_BITMAP, # Insert buffer bitmap
120
- 6 => :SYS, # System page
121
- 7 => :TRX_SYS, # Transaction system data
122
- 8 => :FSP_HDR, # File space header
123
- 9 => :XDES, # Extent descriptor page
124
- 10 => :BLOB, # Uncompressed BLOB page
125
- 11 => :ZBLOB, # First compressed BLOB page
126
- 12 => :ZBLOB2, # Subsequent compressed BLOB page
127
- 17855 => :INDEX, # B-tree node
115
+ :ALLOCATED => {
116
+ :value => 0,
117
+ :description => "Freshly allocated",
118
+ :usage => "page type field has not been initialized",
119
+ },
120
+ :UNDO_LOG => {
121
+ :value => 2,
122
+ :description => "Undo log",
123
+ :usage => "stores previous values of modified records",
124
+ },
125
+ :INODE => {
126
+ :value => 3,
127
+ :description => "File segment inode",
128
+ :usage => "bookkeeping for file segments",
129
+ },
130
+ :IBUF_FREE_LIST => {
131
+ :value => 4,
132
+ :description => "Insert buffer free list",
133
+ :usage => "bookkeeping for insert buffer free space management",
134
+ },
135
+ :IBUF_BITMAP => {
136
+ :value => 5,
137
+ :description => "Insert buffer bitmap",
138
+ :usage => "bookkeeping for insert buffer writes to be merged",
139
+ },
140
+ :SYS => {
141
+ :value => 6,
142
+ :description => "System internal",
143
+ :usage => "used for various purposes in the system tablespace",
144
+ },
145
+ :TRX_SYS => {
146
+ :value => 7,
147
+ :description => "Transaction system header",
148
+ :usage => "bookkeeping for the transaction system in system tablespace",
149
+ },
150
+ :FSP_HDR => {
151
+ :value => 8,
152
+ :description => "File space header",
153
+ :usage => "header page (page 0) for each tablespace file",
154
+ },
155
+ :XDES => {
156
+ :value => 9,
157
+ :description => "Extent descriptor",
158
+ :usage => "header page for subsequent blocks of 16,384 pages",
159
+ },
160
+ :BLOB => {
161
+ :value => 10,
162
+ :description => "Uncompressed BLOB",
163
+ :usage => "externally-stored uncompressed BLOB column data",
164
+ },
165
+ :ZBLOB => {
166
+ :value => 11,
167
+ :description => "First compressed BLOB",
168
+ :usage => "externally-stored compressed BLOB column data, first page",
169
+ },
170
+ :ZBLOB2 => {
171
+ :value => 12,
172
+ :description => "Subsequent compressed BLOB",
173
+ :usage => "externally-stored compressed BLOB column data, subsequent page",
174
+ },
175
+ :INDEX => {
176
+ :value => 17855,
177
+ :description => "B+Tree index",
178
+ :usage => "table and index data stored in B+Tree structure",
179
+ },
128
180
  }
129
181
 
182
+ PAGE_TYPE_BY_VALUE = PAGE_TYPE.inject({}) { |h, (k, v)| h[v[:value]] = k; h }
183
+
130
184
  # A helper to convert "undefined" values stored in previous and next pointers
131
185
  # in the page header to nil.
132
186
  def self.maybe_undefined(value)
@@ -146,7 +200,7 @@ class Innodb::Page
146
200
  Innodb::Page.maybe_undefined(c.get_uint32)
147
201
  },
148
202
  :lsn => c.name("lsn") { c.get_uint64 },
149
- :type => c.name("type") { PAGE_TYPE[c.get_uint16] },
203
+ :type => c.name("type") { PAGE_TYPE_BY_VALUE[c.get_uint16] },
150
204
  :flush_lsn => c.name("flush_lsn") { c.get_uint64 },
151
205
  :space_id => c.name("space_id") { c.get_uint32 },
152
206
  }
@@ -335,22 +335,22 @@ class Innodb::Page::Index < Innodb::Page
335
335
 
336
336
  # Return an array indicating which fields are null.
337
337
  def record_header_compact_null_bitmap(cursor)
338
- fields = (record_format[:key] + record_format[:row])
338
+ columns = (record_format[:key] + record_format[:row])
339
339
 
340
340
  # The number of bits in the bitmap is the number of nullable fields.
341
- size = fields.count { |f| f.type.nullable? }
341
+ size = columns.count { |c| c[:field].type.nullable? }
342
342
 
343
343
  # There is no bitmap if there are no nullable fields.
344
344
  return nil unless size > 0
345
345
 
346
346
  # To simplify later checks, expand bitmap to one for each field.
347
- bitmap = Array.new(fields.last.position + 1, false)
347
+ bitmap = Array.new(columns.last[:field].position + 1, false)
348
348
 
349
349
  null_bit_array = cursor.get_bit_array(size).reverse!
350
350
 
351
351
  # For every nullable field, set whether the field is actually null.
352
- fields.each do |f|
353
- bitmap[f.position] = f.type.nullable? ? (null_bit_array.shift == 1) : false
352
+ columns.each do |c|
353
+ bitmap[c[:field].position] = c[:field].type.nullable? ? (null_bit_array.shift == 1) : false
354
354
  end
355
355
 
356
356
  return bitmap
@@ -359,14 +359,15 @@ class Innodb::Page::Index < Innodb::Page
359
359
  # Return an array containing an array of the length of each variable-length
360
360
  # field and an array indicating which fields are stored externally.
361
361
  def record_header_compact_variable_lengths_and_externs(cursor, null_bitmap)
362
- fields = (record_format[:key] + record_format[:row])
362
+ columns = (record_format[:key] + record_format[:row])
363
363
 
364
- len_array = Array.new(fields.last.position + 1, 0)
365
- ext_array = Array.new(fields.last.position + 1, false)
364
+ len_array = Array.new(columns.last[:field].position + 1, 0)
365
+ ext_array = Array.new(columns.last[:field].position + 1, false)
366
366
 
367
367
  # For each non-NULL variable-length field, the record header contains
368
368
  # the length in one or two bytes.
369
- fields.each do |f|
369
+ columns.each do |c|
370
+ f = c[:field]
370
371
  next if !f.type.variable? or (null_bitmap && null_bitmap[f.position])
371
372
 
372
373
  len = cursor.get_uint8
@@ -449,21 +450,29 @@ class Innodb::Page::Index < Innodb::Page
449
450
 
450
451
  # Return a set of field objects that describe the record.
451
452
  def make_record_description
452
- description = record_describer.cursor_sendable_description(self)
453
+ description = record_describer.description
453
454
 
454
455
  position = 0
455
456
  fields = {:type => description[:type], :key => [], :row => []}
456
457
 
457
- description[:key].each do |d|
458
- fields[:key] << Innodb::Field.new(position, *d)
458
+ description[:key].each do |field|
459
+ fields[:key] << {
460
+ :name => field[:name],
461
+ :field => Innodb::Field.new(position, *field[:type]),
462
+ }
459
463
  position += 1
460
464
  end
461
465
 
462
- # Account for TRX_ID and ROLL_PTR.
463
- position += 2
466
+ if description[:type] == :clustered
467
+ # Account for TRX_ID and ROLL_PTR.
468
+ position += 2
469
+ end
464
470
 
465
- description[:row].each do |d|
466
- fields[:row] << Innodb::Field.new(position, *d)
471
+ description[:row].each do |field|
472
+ fields[:row] << {
473
+ :name => field[:name],
474
+ :field => Innodb::Field.new(position, *field[:type]),
475
+ }
467
476
  position += 1
468
477
  end
469
478
 
@@ -499,11 +508,13 @@ class Innodb::Page::Index < Innodb::Page
499
508
 
500
509
  # Read the key fields present in all types of pages.
501
510
  this_record[:key] = []
502
- this_record[:key_ext] = []
503
- c.name("key") do
504
- record_format[:key].each do |f|
505
- this_record[:key].push f.read(this_record, c)
506
- this_record[:key_ext].push f.read_extern(this_record, c)
511
+ record_format[:key].each do |column|
512
+ c.name("key[#{column[:name]}]") do
513
+ this_record[:key] << {
514
+ :name => column[:name],
515
+ :value => column[:field].read(this_record, c),
516
+ :extern => column[:field].read_extern(this_record, c),
517
+ }
507
518
  end
508
519
  end
509
520
 
@@ -532,11 +543,13 @@ class Innodb::Page::Index < Innodb::Page
532
543
  (record_format[:type] == :secondary)
533
544
  # Read the non-key fields.
534
545
  this_record[:row] = []
535
- this_record[:row_ext] = []
536
- c.name("row") do
537
- record_format[:row].each do |f|
538
- this_record[:row].push f.read(this_record, c)
539
- this_record[:row_ext].push f.read_extern(this_record, c)
546
+ record_format[:row].each do |column|
547
+ c.name("row[#{column[:name]}]") do
548
+ this_record[:row] << {
549
+ :name => column[:name],
550
+ :value => column[:field].read(this_record, c),
551
+ :extern => column[:field].read_extern(this_record, c),
552
+ }
540
553
  end
541
554
  end
542
555
  end
@@ -1,5 +1,18 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
3
+ require "innodb/data_dictionary"
4
+
2
5
  class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
6
+ RECORD_DESCRIBERS = {
7
+ :SYS_TABLES => {
8
+ :PRIMARY => Innodb::DataDictionary::SYS_TABLES_PRIMARY,
9
+ :ID => Innodb::DataDictionary::SYS_TABLES_ID
10
+ },
11
+ :SYS_COLUMNS => { :PRIMARY => Innodb::DataDictionary::SYS_COLUMNS_PRIMARY },
12
+ :SYS_INDEXES => { :PRIMARY => Innodb::DataDictionary::SYS_INDEXES_PRIMARY },
13
+ :SYS_FIELDS => { :PRIMARY => Innodb::DataDictionary::SYS_FIELDS_PRIMARY },
14
+ }
15
+
3
16
  # The position of the data dictionary header within the page.
4
17
  def pos_data_dictionary_header
5
18
  pos_fil_header + size_fil_header
@@ -40,6 +53,22 @@ class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
40
53
  end
41
54
  end
42
55
 
56
+ def index(table_name, index_name)
57
+ unless table_entry = data_dictionary_header[:indexes][table_name]
58
+ raise "Unknown data dictionary table #{table_name}"
59
+ end
60
+
61
+ unless index_root_page = table_entry[index_name]
62
+ raise "Unknown data dictionary index #{table_name}.#{index_name}"
63
+ end
64
+
65
+ # If we have a record describer for this index, load it.
66
+ record_describer = RECORD_DESCRIBERS[table_name] &&
67
+ RECORD_DESCRIBERS[table_name][index_name]
68
+
69
+ @space.index(index_root_page, record_describer.new)
70
+ end
71
+
43
72
  # Iterate through all indexes in the data dictionary, yielding the table
44
73
  # name, index name, and the index itself as an Innodb::Index.
45
74
  def each_index
@@ -49,7 +78,7 @@ class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
49
78
 
50
79
  data_dictionary_header[:indexes].each do |table_name, indexes|
51
80
  indexes.each do |index_name, root_page_number|
52
- yield table_name, index_name, @space.index(root_page_number)
81
+ yield table_name, index_name, index(table_name, index_name)
53
82
  end
54
83
  end
55
84
  end
@@ -61,32 +90,4 @@ class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
61
90
  puts "data_dictionary header:"
62
91
  pp data_dictionary_header
63
92
  end
64
-
65
- # A record describer for SYS_TABLES clustered records.
66
- class SYS_TABLES_PRIMARY
67
- def self.cursor_sendable_description(page)
68
- {
69
- :type => :clustered,
70
- :key => [
71
- ["VARCHAR(100)", :NOT_NULL], # NAME
72
- ],
73
- :row => [
74
- [:BIGINT, :UNSIGNED, :NOT_NULL], # ID
75
- [:INT, :UNSIGNED, :NOT_NULL], # N_COLS
76
- [:INT, :UNSIGNED, :NOT_NULL], # TYPE
77
- [:BIGINT, :UNSIGNED, :NOT_NULL], # MIX_ID
78
- [:INT, :UNSIGNED, :NOT_NULL], # MIX_LEN
79
- ["VARCHAR(100)"], # CLUSTER_NAME
80
- [:INT, :UNSIGNED, :NOT_NULL], # SPACE
81
- ]
82
- }
83
- end
84
- end
85
-
86
- RECORD_DESCRIBERS = {
87
- :SYS_TABLES => { :PRIMARY => SYS_TABLES_PRIMARY, :ID => nil },
88
- :SYS_COLUMNS => { :PRIMARY => nil },
89
- :SYS_INDEXES => { :PRIMARY => nil },
90
- :SYS_FIELDS => { :PRIMARY => nil },
91
- }
92
93
  end
@@ -1,3 +1,112 @@
1
1
  # -*- encoding : utf-8 -*-
2
- # An empty class in order to establish the Innodb::RecordDescriber namespace.
3
- class Innodb::RecordDescriber; end
2
+
3
+ #
4
+ # A class to describe record layouts for InnoDB indexes. Designed to be usable
5
+ # in two different ways: statically built and dynamically built. Note that in
6
+ # both cases, the order that statements are encountered is critical. Columns
7
+ # must be added to the key and row structures in the correct order.
8
+ #
9
+ # STATIC USAGE
10
+ #
11
+ # Static building is useful for building a custom describer for any index
12
+ # and looks like the following:
13
+ #
14
+ # To describe the SQL syntax:
15
+ #
16
+ # CREATE TABLE my_table (
17
+ # id BIGINT NOT NULL,
18
+ # name VARCHAR(100) NOT NULL,
19
+ # age INT UNSIGNED,
20
+ # PRIMARY KEY (id)
21
+ # );
22
+ #
23
+ # The clustered key would require a class like:
24
+ #
25
+ # class MyTableClusteredDescriber < Innodb::RecordDescriber
26
+ # type :clustered
27
+ # key "id", :BIGINT, :UNSIGNED, :NOT_NULL
28
+ # row "name", "VARCHAR(100)", :NOT_NULL
29
+ # row "age", :INT, :UNSIGNED
30
+ # end
31
+ #
32
+ # It can then be instantiated as usual:
33
+ #
34
+ # my_table_clustered = MyTableClusteredDescriber.new
35
+ #
36
+ # All statically-defined type, key, and row information will be copied into
37
+ # the instance when it is initialized. Once initialized, the instance can
38
+ # be additionally used dynamically, as per below. (A dynamic class is just
39
+ # the same as a static class that is empty.)
40
+ #
41
+ # Note that since InnoDB works in terms of *indexes* individually, a new class
42
+ # must be created for each index.
43
+ #
44
+ # DYNAMIC USAGE
45
+ #
46
+ # If a record describer needs to be built based on runtime information, such
47
+ # as index descriptions from a live data dictionary, instances can be built
48
+ # dynamically. For the same table above, this would require:
49
+ #
50
+ # my_table_clustered = Innodb::RecordDescriber.new
51
+ # my_table_clustered.type = :clustered
52
+ # my_table_clustered.key "id", :BIGINT, :UNSIGNED, :NOT_NULL
53
+ # my_table_clustered.row "name", "VARCHAR(100)", :NOT_NULL
54
+ # my_table_clustered.row "age", :INT, :UNSIGNED
55
+ #
56
+
57
+ class Innodb::RecordDescriber
58
+ # Internal method to initialize the class's instance variable on access.
59
+ def self.static_description
60
+ @static_description ||= {
61
+ :type => nil,
62
+ :key => [],
63
+ :row => []
64
+ }
65
+ end
66
+
67
+ # A 'type' method to be used from the DSL.
68
+ def self.type(type)
69
+ static_description[:type] = type
70
+ end
71
+
72
+ # An internal method wrapped with 'key' and 'row' helpers.
73
+ def self.add_static_field(group, name, type)
74
+ static_description[group] << { :name => name, :type => type }
75
+ end
76
+
77
+ # A 'key' method to be used from the DSL.
78
+ def self.key(name, *type)
79
+ add_static_field :key, name, type
80
+ end
81
+
82
+ # A 'row' method to be used from the DSL.
83
+ def self.row(name, *type)
84
+ add_static_field :row, name, type
85
+ end
86
+
87
+ attr_accessor :description
88
+
89
+ def initialize
90
+ @description = self.class.static_description.dup
91
+ end
92
+
93
+ # Set the type of this record (:clustered or :secondary).
94
+ def type(type)
95
+ description[:type] = type
96
+ end
97
+
98
+ # An internal method wrapped with 'key' and 'row' helpers.
99
+ def add_field(group, name, type)
100
+ description[group] << { :name => name, :type => type }
101
+ end
102
+
103
+ # Add a key column to the record description.
104
+ def key(name, *type)
105
+ add_field :key, name, type
106
+ end
107
+
108
+ # Add a row (non-key) column to the record description.
109
+ def row(name, *type)
110
+ add_field :row, name, type
111
+ end
112
+ end
data/lib/innodb/space.rb CHANGED
@@ -57,7 +57,7 @@ class Innodb::Space
57
57
  # to offset 0 and page type :FSP_HDR (8).
58
58
  page_offset = BinData::Uint32be.read(read_at_offset(4, 4))
59
59
  page_type = BinData::Uint16be.read(read_at_offset(24, 2))
60
- unless page_offset == 0 && Innodb::Page::PAGE_TYPE[page_type] == :FSP_HDR
60
+ unless page_offset == 0 && Innodb::Page::PAGE_TYPE_BY_VALUE[page_type] == :FSP_HDR
61
61
  raise "Something is very wrong; Page 0 does not seem to be type FSP_HDR"
62
62
  end
63
63
 
@@ -165,14 +165,24 @@ class Innodb::Space
165
165
  true
166
166
  end
167
167
 
168
+ # Return the page number for the space's FSP_HDR page.
169
+ def page_fsp_hdr
170
+ 0
171
+ end
172
+
168
173
  # Get (and cache) the FSP header from the FSP_HDR page.
169
174
  def fsp
170
- @fsp ||= page(0).fsp_header
175
+ @fsp ||= page(page_fsp_hdr).fsp_header
176
+ end
177
+
178
+ # Return the page number for the space's TRX_SYS page.
179
+ def page_trx_sys
180
+ 5
171
181
  end
172
182
 
173
183
  # Get the Innodb::Page::TrxSys page for a system space.
174
184
  def trx_sys
175
- page(5) if system_space?
185
+ page(page_trx_sys) if system_space?
176
186
  end
177
187
 
178
188
  def rseg_page?(page_number)
@@ -184,9 +194,14 @@ class Innodb::Space
184
194
  end
185
195
  end
186
196
 
197
+ # Return the page number for the space's SYS data dictionary header.
198
+ def page_sys_data_dictionary
199
+ 7
200
+ end
201
+
187
202
  # Get the Innodb::Page::SysDataDictionaryHeader page for a system space.
188
203
  def data_dictionary
189
- page(7) if system_space?
204
+ page(page_sys_data_dictionary) if system_space?
190
205
  end
191
206
 
192
207
  # Get an Innodb::List object for a specific list by list name.
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Innodb
3
- VERSION = "0.7.12"
3
+ VERSION = "0.8.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: innodb_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.12
4
+ version: 0.8.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-13 00:00:00.000000000 Z
12
+ date: 2013-08-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bindata
@@ -39,6 +39,7 @@ files:
39
39
  - lib/innodb.rb
40
40
  - lib/innodb/checksum.rb
41
41
  - lib/innodb/cursor.rb
42
+ - lib/innodb/data_dictionary.rb
42
43
  - lib/innodb/field.rb
43
44
  - lib/innodb/field_type.rb
44
45
  - lib/innodb/fseg_entry.rb