innodb_ruby 0.8.1 → 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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