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.
- data/AUTHORS.md +12 -0
- data/LICENSE +29 -0
- data/bin/innodb_log +64 -9
- data/bin/innodb_space +95 -42
- data/lib/innodb/checksum.rb +1 -0
- data/lib/innodb/cursor.rb +8 -63
- data/lib/innodb/data_dictionary.rb +2 -0
- data/lib/innodb/data_type.rb +360 -0
- data/lib/innodb/field.rb +65 -10
- data/lib/innodb/fseg_entry.rb +2 -0
- data/lib/innodb/index.rb +5 -0
- data/lib/innodb/inode.rb +1 -0
- data/lib/innodb/list.rb +2 -0
- data/lib/innodb/log.rb +2 -0
- data/lib/innodb/log_block.rb +1 -0
- data/lib/innodb/log_group.rb +79 -0
- data/lib/innodb/page/fsp_hdr_xdes.rb +1 -0
- data/lib/innodb/page/index.rb +38 -42
- data/lib/innodb/page/index_compressed.rb +42 -0
- data/lib/innodb/page/inode.rb +1 -0
- data/lib/innodb/page/sys.rb +1 -0
- data/lib/innodb/page/sys_rseg_header.rb +1 -0
- data/lib/innodb/page/trx_sys.rb +1 -0
- data/lib/innodb/page.rb +1 -0
- data/lib/innodb/record.rb +49 -0
- data/lib/innodb/record_describer.rb +8 -0
- data/lib/innodb/space.rb +6 -0
- data/lib/innodb/system.rb +58 -0
- data/lib/innodb/undo_log.rb +1 -0
- data/lib/innodb/version.rb +2 -1
- data/lib/innodb/xdes.rb +1 -0
- data/lib/innodb.rb +4 -0
- metadata +13 -5
- data/lib/innodb/field_type.rb +0 -124
data/lib/innodb/page/index.rb
CHANGED
@@ -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
|
-
|
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 =
|
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(
|
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
|
-
|
353
|
-
bitmap[
|
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
|
-
|
363
|
+
fields = (record_format[:key] + record_format[:row])
|
363
364
|
|
364
|
-
len_array = Array.new(
|
365
|
-
ext_array = Array.new(
|
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
|
-
|
370
|
-
f
|
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.
|
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 |
|
512
|
-
c.name("key[#{
|
505
|
+
record_format[:key].each do |f|
|
506
|
+
c.name("key[#{f.name}]") do
|
513
507
|
this_record[:key] << {
|
514
|
-
:name =>
|
515
|
-
:
|
516
|
-
:
|
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 |
|
547
|
-
c.name("row[#{
|
541
|
+
record_format[:row].each do |f|
|
542
|
+
c.name("row[#{f.name}]") do
|
548
543
|
this_record[:row] << {
|
549
|
-
:name =>
|
550
|
-
:
|
551
|
-
:
|
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
|
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
|
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
|
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
|
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
|
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
|
+
|
data/lib/innodb/page/inode.rb
CHANGED
data/lib/innodb/page/sys.rb
CHANGED
data/lib/innodb/page/trx_sys.rb
CHANGED
data/lib/innodb/page.rb
CHANGED
@@ -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
|
data/lib/innodb/undo_log.rb
CHANGED
data/lib/innodb/version.rb
CHANGED
data/lib/innodb/xdes.rb
CHANGED
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.
|
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:
|
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:
|
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:
|
data/lib/innodb/field_type.rb
DELETED
@@ -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
|