innodb_ruby 0.8.1 → 0.8.5

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.
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  require "innodb/fseg_entry"
3
4
 
4
5
  # A specialized class for handling INDEX pages, which contain a portion of
@@ -335,22 +336,22 @@ class Innodb::Page::Index < Innodb::Page
335
336
 
336
337
  # Return an array indicating which fields are null.
337
338
  def record_header_compact_null_bitmap(cursor)
338
- columns = (record_format[:key] + record_format[:row])
339
+ fields = (record_format[:key] + record_format[:row])
339
340
 
340
341
  # The number of bits in the bitmap is the number of nullable fields.
341
- size = columns.count { |c| c[:field].type.nullable? }
342
+ size = fields.count { |f| f.nullable? }
342
343
 
343
344
  # There is no bitmap if there are no nullable fields.
344
345
  return nil unless size > 0
345
346
 
346
347
  # To simplify later checks, expand bitmap to one for each field.
347
- bitmap = Array.new(columns.last[:field].position + 1, false)
348
+ bitmap = Array.new(fields.last.position + 1, false)
348
349
 
349
350
  null_bit_array = cursor.get_bit_array(size).reverse!
350
351
 
351
352
  # For every nullable field, set whether the field is actually null.
352
- columns.each do |c|
353
- bitmap[c[:field].position] = c[:field].type.nullable? ? (null_bit_array.shift == 1) : false
353
+ fields.each do |f|
354
+ bitmap[f.position] = f.nullable? ? (null_bit_array.shift == 1) : false
354
355
  end
355
356
 
356
357
  return bitmap
@@ -359,23 +360,22 @@ class Innodb::Page::Index < Innodb::Page
359
360
  # Return an array containing an array of the length of each variable-length
360
361
  # field and an array indicating which fields are stored externally.
361
362
  def record_header_compact_variable_lengths_and_externs(cursor, null_bitmap)
362
- columns = (record_format[:key] + record_format[:row])
363
+ fields = (record_format[:key] + record_format[:row])
363
364
 
364
- len_array = Array.new(columns.last[:field].position + 1, 0)
365
- ext_array = Array.new(columns.last[:field].position + 1, false)
365
+ len_array = Array.new(fields.last.position + 1, 0)
366
+ ext_array = Array.new(fields.last.position + 1, false)
366
367
 
367
368
  # For each non-NULL variable-length field, the record header contains
368
369
  # the length in one or two bytes.
369
- columns.each do |c|
370
- f = c[:field]
371
- next if !f.type.variable? or (null_bitmap && null_bitmap[f.position])
370
+ fields.each do |f|
371
+ next if !f.variable? or (null_bitmap && null_bitmap[f.position])
372
372
 
373
373
  len = cursor.get_uint8
374
374
  ext = false
375
375
 
376
376
  # Two bytes are used only if the length exceeds 127 bytes and the
377
377
  # maximum length exceeds 255 bytes (or the field is a BLOB type).
378
- if len > 127 && (f.type.blob? || f.type.length > 255)
378
+ if len > 127 && (f.blob? || f.data_type.width > 255)
379
379
  ext = (0x40 & len) != 0
380
380
  len = ((len & 0x3f) << 8) + cursor.get_uint8
381
381
  end
@@ -429,12 +429,12 @@ class Innodb::Page::Index < Innodb::Page
429
429
  def system_record(offset)
430
430
  cursor(offset).name("record[#{offset}]") do |c|
431
431
  header = c.peek { record_header(c) }
432
- {
432
+ Innodb::Record.new({
433
433
  :offset => offset,
434
434
  :header => header,
435
435
  :next => header[:next],
436
436
  :data => c.name("data") { c.get_bytes(size_mum_record) },
437
- }
437
+ })
438
438
  end
439
439
  end
440
440
 
@@ -456,10 +456,7 @@ class Innodb::Page::Index < Innodb::Page
456
456
  fields = {:type => description[:type], :key => [], :row => []}
457
457
 
458
458
  description[:key].each do |field|
459
- fields[:key] << {
460
- :name => field[:name],
461
- :field => Innodb::Field.new(position, *field[:type]),
462
- }
459
+ fields[:key] << Innodb::Field.new(position, field[:name], *field[:type])
463
460
  position += 1
464
461
  end
465
462
 
@@ -469,10 +466,7 @@ class Innodb::Page::Index < Innodb::Page
469
466
  end
470
467
 
471
468
  description[:row].each do |field|
472
- fields[:row] << {
473
- :name => field[:name],
474
- :field => Innodb::Field.new(position, *field[:type]),
475
- }
469
+ fields[:row] << Innodb::Field.new(position, field[:name], *field[:type])
476
470
  position += 1
477
471
  end
478
472
 
@@ -508,12 +502,13 @@ class Innodb::Page::Index < Innodb::Page
508
502
 
509
503
  # Read the key fields present in all types of pages.
510
504
  this_record[:key] = []
511
- record_format[:key].each do |column|
512
- c.name("key[#{column[:name]}]") do
505
+ record_format[:key].each do |f|
506
+ c.name("key[#{f.name}]") do
513
507
  this_record[:key] << {
514
- :name => column[:name],
515
- :value => column[:field].read(this_record, c),
516
- :extern => column[:field].read_extern(this_record, c),
508
+ :name => f.name,
509
+ :type => f.data_type.name,
510
+ :value => f.value(this_record, c),
511
+ :extern => f.extern(this_record, c),
517
512
  }
518
513
  end
519
514
  end
@@ -543,12 +538,13 @@ class Innodb::Page::Index < Innodb::Page
543
538
  (record_format[:type] == :secondary)
544
539
  # Read the non-key fields.
545
540
  this_record[:row] = []
546
- record_format[:row].each do |column|
547
- c.name("row[#{column[:name]}]") do
541
+ record_format[:row].each do |f|
542
+ c.name("row[#{f.name}]") do
548
543
  this_record[:row] << {
549
- :name => column[:name],
550
- :value => column[:field].read(this_record, c),
551
- :extern => column[:field].read_extern(this_record, c),
544
+ :name => f.name,
545
+ :type => f.data_type.name,
546
+ :value => f.value(this_record, c),
547
+ :extern => f.extern(this_record, c),
552
548
  }
553
549
  end
554
550
  end
@@ -563,7 +559,7 @@ class Innodb::Page::Index < Innodb::Page
563
559
  end
564
560
  end
565
561
 
566
- this_record
562
+ Innodb::Record.new(this_record)
567
563
  end
568
564
  end
569
565
 
@@ -584,13 +580,13 @@ class Innodb::Page::Index < Innodb::Page
584
580
  if record == @page.supremum
585
581
  # We've reached the end of the linked list at supremum.
586
582
  @offset = nil
587
- elsif record[:next] == @offset
583
+ elsif record.next == @offset
588
584
  # The record links to itself; go ahead and return it (once), but set
589
585
  # the next offset to nil to end the loop.
590
586
  @offset = nil
591
587
  record
592
588
  else
593
- @offset = record[:next]
589
+ @offset = record.next
594
590
  record
595
591
  end
596
592
  end
@@ -603,7 +599,7 @@ class Innodb::Page::Index < Innodb::Page
603
599
 
604
600
  # Return the first record on this page.
605
601
  def first_record
606
- first = record(infimum[:next])
602
+ first = record(infimum.next)
607
603
  first if first != supremum
608
604
  end
609
605
 
@@ -613,7 +609,7 @@ class Innodb::Page::Index < Innodb::Page
613
609
  return enum_for(:each_record)
614
610
  end
615
611
 
616
- c = record_cursor(infimum[:next])
612
+ c = record_cursor(infimum.next)
617
613
 
618
614
  while rec = c.record
619
615
  yield rec
@@ -651,7 +647,7 @@ class Innodb::Page::Index < Innodb::Page
651
647
  end
652
648
 
653
649
  each_record do |rec|
654
- yield rec[:child_page_number], rec[:key]
650
+ yield rec.child_page_number, rec.key
655
651
  end
656
652
 
657
653
  nil
@@ -701,20 +697,20 @@ class Innodb::Page::Index < Innodb::Page
701
697
  puts
702
698
 
703
699
  puts "system records:"
704
- pp infimum
705
- pp supremum
700
+ pp infimum.record
701
+ pp supremum.record
706
702
  puts
707
703
 
708
704
  puts "garbage records:"
709
705
  each_garbage_record do |rec|
710
- pp rec
706
+ pp rec.record
711
707
  puts
712
708
  end
713
709
  puts
714
710
 
715
711
  puts "records:"
716
712
  each_record do |rec|
717
- pp rec
713
+ pp rec.record
718
714
  puts
719
715
  end
720
716
  puts
@@ -0,0 +1,42 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Innodb::Page::Index::Compressed < Innodb::Page::Index
4
+ # The number of directory slots in use.
5
+ def directory_slots
6
+ page_header[:n_heap] - 2
7
+ end
8
+
9
+ def directory
10
+ super.map { |n| n & 0x3fff }
11
+ end
12
+
13
+ def uncompressed_columns_size
14
+ if level == 0
15
+ if record_format && record_format[:type] == :clustered
16
+ 6 + 7 # Transaction ID + Roll Pointer
17
+ else
18
+ 0
19
+ end
20
+ else
21
+ 4 # Node pointer for non-leaf pages
22
+ end
23
+ end
24
+
25
+ # Return the amount of free space in the page.
26
+ def free_space
27
+ free_space_start = size - size_fil_trailer - directory_space -
28
+ (uncompressed_columns_size * (page_header[:n_heap] - 2))
29
+ puts "Free space start == %04x" % [offset * size + free_space_start]
30
+ c = cursor(free_space_start).backward
31
+ zero_bytes = 0
32
+ while (b = c.get_uint8) == 0
33
+ zero_bytes += 1
34
+ end
35
+ zero_bytes
36
+ #page_header[:garbage] +
37
+ # (size - size_fil_trailer - directory_space - page_header[:heap_top])
38
+ end
39
+ end
40
+
41
+ Innodb::Page::SPECIALIZED_CLASSES[{:type => :INDEX, :compressed => true}] = Innodb::Page::Index::Compressed
42
+
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  require "innodb/list"
3
4
 
4
5
  # A specialized class for handling INODE pages, which contain index FSEG (file
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  require "innodb/page/sys_rseg_header"
3
4
  require "innodb/page/sys_data_dictionary_header"
4
5
 
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  class Innodb::Page::SysRsegHeader < Innodb::Page
3
4
  # The position of the rollback segment header within the page.
4
5
  def pos_rseg_header
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  # A specialized class for TRX_SYS pages, which contain various information
3
4
  # about the transaction system within InnoDB. Only one TRX_SYS page exists in
4
5
  # any given InnoDB installation, and it is page 5 of the system tablespace
data/lib/innodb/page.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  require "innodb/cursor"
3
4
 
4
5
  # A generic class for any type of page, which handles reading the common
@@ -0,0 +1,49 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Innodb::Record
4
+ attr_accessor :record
5
+
6
+ def initialize(record)
7
+ @record = record
8
+ end
9
+
10
+ def header
11
+ record[:header]
12
+ end
13
+
14
+ def offset
15
+ record[:offset]
16
+ end
17
+
18
+ def next
19
+ record[:next]
20
+ end
21
+
22
+ def key
23
+ record[:key]
24
+ end
25
+
26
+ def row
27
+ record[:row]
28
+ end
29
+
30
+ def child_page_number
31
+ record[:child_page_number]
32
+ end
33
+
34
+ def uncached_fields
35
+ fields_hash = {}
36
+ [:key, :row].each do |group|
37
+ if record[group]
38
+ record[group].each do |column|
39
+ fields_hash[column[:name]] = column[:value]
40
+ end
41
+ end
42
+ end
43
+ fields_hash
44
+ end
45
+
46
+ def fields
47
+ @fields ||= uncached_fields
48
+ end
49
+ end
@@ -110,6 +110,14 @@ class Innodb::RecordDescriber
110
110
  add_field :row, name, type
111
111
  end
112
112
 
113
+ def field_names
114
+ names = []
115
+ [:key, :row].each do |group|
116
+ names += description[group].map { |n| n[:name] }
117
+ end
118
+ names
119
+ end
120
+
113
121
  def generate_class(name="Describer_#{object_id}")
114
122
  str = "class #{name}\n"
115
123
  str << " type %s\n" % [
data/lib/innodb/space.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  # An InnoDB space file, which can be either a multi-table ibdataN file
3
4
  # or a single-table "innodb_file_per_table" .ibd file.
5
+
4
6
  class Innodb::Space
5
7
  # InnoDB's default page size is 16KiB.
6
8
  DEFAULT_PAGE_SIZE = 16384
@@ -175,6 +177,10 @@ class Innodb::Space
175
177
  @fsp ||= page(page_fsp_hdr).fsp_header
176
178
  end
177
179
 
180
+ def space_id
181
+ fsp[:space_id]
182
+ end
183
+
178
184
  # Return the page number for the space's TRX_SYS page.
179
185
  def page_trx_sys
180
186
  5
@@ -0,0 +1,58 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Innodb::System
4
+ attr_reader :config
5
+ attr_accessor :spaces
6
+
7
+ def initialize(system_space_file)
8
+ @config = {
9
+ :datadir => ".",
10
+ }
11
+ @spaces = {}
12
+ @spaces[0] = Innodb::Space.new(system_space_file)
13
+ end
14
+
15
+ def system_space
16
+ @spaces[0]
17
+ end
18
+
19
+ def each_record_from_data_dictionary_index(table, index)
20
+ unless block_given?
21
+ return enum_for(:each_record_from_data_dictionary_index, table, index)
22
+ end
23
+
24
+ index = system_space.data_dictionary.index(table, index)
25
+ index.each_record do |record|
26
+ yield record
27
+ end
28
+ nil
29
+ end
30
+
31
+ def each_table
32
+ unless block_given?
33
+ return enum_for(:each_table)
34
+ end
35
+
36
+ each_record_from_data_dictionary_index(:SYS_TABLES, :PRIMARY) do |record|
37
+ yield record.fields
38
+ end
39
+ end
40
+
41
+ def each_index
42
+ unless block_given?
43
+ return enum_for(:each_index)
44
+ end
45
+
46
+ each_record_from_data_dictionary_index(:SYS_INDEXES, :PRIMARY) do |record|
47
+ yield record.fields
48
+ end
49
+ end
50
+
51
+ def add_space(space)
52
+ @spaces[space.space_id] = space
53
+ end
54
+
55
+ def add_space_file(space_file)
56
+ add_space(Innodb::Space.new(space_file))
57
+ end
58
+ end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
 
2
3
  class Innodb::UndoLog
3
4
  attr_reader :page
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  module Innodb
3
- VERSION = "0.8.1"
4
+ VERSION = "0.8.5"
4
5
  end
data/lib/innodb/xdes.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  # An InnoDB "extent descriptor entry" or "+XDES+". These structures are used
3
4
  # in the +XDES+ entry array contained in +FSP_HDR+ and +XDES+ pages.
4
5
  #
data/lib/innodb.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  # A set of classes for parsing and working with InnoDB data files.
4
+
3
5
  module Innodb; end
4
6
 
5
7
  require "enumerator"
@@ -16,8 +18,10 @@ require "innodb/page/index"
16
18
  require "innodb/page/trx_sys"
17
19
  require "innodb/page/sys"
18
20
  require "innodb/page/undo_log"
21
+ require "innodb/record"
19
22
  require "innodb/field"
20
23
  require "innodb/space"
24
+ require "innodb/system"
21
25
  require "innodb/inode"
22
26
  require "innodb/index"
23
27
  require "innodb/log_block"
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: innodb_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jeremy Cole
9
+ - Davi Arnaut
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-08-21 00:00:00.000000000 Z
13
+ date: 2014-01-30 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: bindata
@@ -35,38 +36,45 @@ executables:
35
36
  extensions: []
36
37
  extra_rdoc_files: []
37
38
  files:
39
+ - LICENSE
40
+ - AUTHORS.md
38
41
  - README.md
39
42
  - lib/innodb.rb
40
43
  - lib/innodb/checksum.rb
41
44
  - lib/innodb/cursor.rb
42
45
  - lib/innodb/data_dictionary.rb
46
+ - lib/innodb/data_type.rb
43
47
  - lib/innodb/field.rb
44
- - lib/innodb/field_type.rb
45
48
  - lib/innodb/fseg_entry.rb
46
49
  - lib/innodb/index.rb
47
50
  - lib/innodb/inode.rb
48
51
  - lib/innodb/list.rb
49
52
  - lib/innodb/log.rb
50
53
  - lib/innodb/log_block.rb
54
+ - lib/innodb/log_group.rb
51
55
  - lib/innodb/page.rb
52
56
  - lib/innodb/page/blob.rb
53
57
  - lib/innodb/page/fsp_hdr_xdes.rb
54
58
  - lib/innodb/page/index.rb
59
+ - lib/innodb/page/index_compressed.rb
55
60
  - lib/innodb/page/inode.rb
56
61
  - lib/innodb/page/sys.rb
57
62
  - lib/innodb/page/sys_data_dictionary_header.rb
58
63
  - lib/innodb/page/sys_rseg_header.rb
59
64
  - lib/innodb/page/trx_sys.rb
60
65
  - lib/innodb/page/undo_log.rb
66
+ - lib/innodb/record.rb
61
67
  - lib/innodb/record_describer.rb
62
68
  - lib/innodb/space.rb
69
+ - lib/innodb/system.rb
63
70
  - lib/innodb/undo_log.rb
64
71
  - lib/innodb/version.rb
65
72
  - lib/innodb/xdes.rb
66
73
  - bin/innodb_log
67
74
  - bin/innodb_space
68
- homepage: http://jcole.us/
69
- licenses: []
75
+ homepage: https://github.com/jeremycole/innodb_ruby
76
+ licenses:
77
+ - New BSD (3-clause)
70
78
  post_install_message:
71
79
  rdoc_options: []
72
80
  require_paths:
@@ -1,124 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- class Innodb::FieldType
3
- class GenericType
4
- attr_reader :type
5
-
6
- def initialize(type)
7
- @type = type
8
- end
9
-
10
- def read(cursor, length)
11
- cursor.get_bytes(length)
12
- end
13
-
14
- def read_extern(cursor)
15
- cursor.name("extern") { get_extern_field(cursor) }
16
- end
17
-
18
- # Return an external reference field. An extern field contains the page
19
- # address and the length of the externally stored part of the record data.
20
- def get_extern_field(cursor)
21
- {
22
- :space_id => cursor.name("space_id") { cursor.get_uint32 },
23
- :page_number => cursor.name("page_number") { cursor.get_uint32 },
24
- :offset => cursor.name("offset") { cursor.get_uint32 },
25
- :length => cursor.name("length") { cursor.get_uint64 & 0x3fffffff }
26
- }
27
- end
28
- end
29
-
30
- class IntegerType < GenericType
31
- def read(cursor, length)
32
- method = type.unsigned? ? :get_uint_by_size : :get_i_sint_by_size
33
- cursor.send(method, type.length)
34
- end
35
- end
36
-
37
- class VariableStringType < GenericType
38
- def read(cursor, length)
39
- # The SQL standard defines that VARCHAR fields should have end-spaces
40
- # stripped off.
41
- super.sub(/[ ]+$/, "")
42
- end
43
- end
44
-
45
- # Maps data type to fixed storage length.
46
- TYPES = {
47
- :TINYINT => { :class => IntegerType, :length => 1 },
48
- :SMALLINT => { :class => IntegerType, :length => 2 },
49
- :MEDIUMINT => { :class => IntegerType, :length => 3 },
50
- :INT => { :class => IntegerType, :length => 4 },
51
- :INT6 => { :class => IntegerType, :length => 6 },
52
- :BIGINT => { :class => IntegerType, :length => 8 },
53
- :CHAR => { :class => GenericType },
54
- :VARCHAR => { :class => VariableStringType },
55
- :BLOB => { :class => GenericType },
56
- }
57
-
58
- def self.parse_base_type_and_modifiers(type_string)
59
- if matches = /^([a-zA-Z0-9]+)(\(([0-9, ]+)\))?$/.match(type_string)
60
- base_type = matches[1].upcase.to_sym
61
- if matches[3]
62
- modifiers = matches[3].sub(/[ ]/, "").split(/,/).map { |s| s.to_i }
63
- else
64
- modifiers = []
65
- end
66
- [base_type, modifiers]
67
- end
68
- end
69
-
70
- attr_reader :base_type
71
- attr_reader :length
72
- attr_reader :reader
73
- def initialize(type_string, properties)
74
- @base_type, modifiers = self.class.parse_base_type_and_modifiers(type_string)
75
- @length = nil
76
- @nullable = !properties.include?(:NOT_NULL)
77
- @unsigned = properties.include?(:UNSIGNED)
78
- @variable = false
79
- @blob = false
80
- case
81
- when TYPES[base_type][:class] == IntegerType
82
- @length = TYPES[base_type][:length]
83
- when base_type == :CHAR
84
- @length = modifiers[0]
85
- when base_type == :VARCHAR
86
- @length = modifiers[0]
87
- @variable = true
88
- when base_type == :BLOB
89
- @blob = true
90
- @variable = true
91
- else
92
- raise "Data type '#{type_string}' is not supported"
93
- end
94
- @reader = TYPES[base_type][:class].new(self)
95
- end
96
-
97
- def unsigned?
98
- @unsigned
99
- end
100
-
101
- def nullable?
102
- @nullable
103
- end
104
-
105
- def variable?
106
- @variable
107
- end
108
-
109
- def blob?
110
- @blob
111
- end
112
-
113
- def name_suffix
114
- if [:CHAR, :VARCHAR].include?(base_type)
115
- "(#{length})"
116
- else
117
- ""
118
- end
119
- end
120
-
121
- def name
122
- "#{base_type}#{name_suffix}"
123
- end
124
- end