innodb_ruby 0.7.12 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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