flattendb 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/Rakefile +1 -1
- data/bin/flattendb +3 -161
- data/bin/flattendb.mdb +3 -51
- data/bin/flattendb.mysql +3 -88
- data/example/mysql-sample.flat-sql.xml +120 -0
- data/example/mysql-sample.sql +137 -0
- data/lib/flattendb/base.rb +9 -33
- data/lib/flattendb/cli.rb +190 -30
- data/lib/flattendb/types/mdb.rb +16 -4
- data/lib/flattendb/types/mysql.rb +51 -14
- data/lib/flattendb/version.rb +2 -2
- data/lib/flattendb.rb +4 -1
- metadata +14 -11
data/README
CHANGED
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ begin
|
|
14
14
|
:summary => %q{Flatten relational databases.},
|
15
15
|
:author => %q{Jens Wille},
|
16
16
|
:email => %q{jens.wille@uni-koeln.de},
|
17
|
-
:dependencies => %w[
|
17
|
+
:dependencies => %w[libxml-ruby builder ruby-nuggets] << ['athena', '>= 0.1.5']
|
18
18
|
}
|
19
19
|
}}
|
20
20
|
rescue LoadError => err
|
data/bin/flattendb
CHANGED
@@ -28,168 +28,10 @@
|
|
28
28
|
###############################################################################
|
29
29
|
#++
|
30
30
|
|
31
|
-
require 'optparse'
|
32
|
-
|
33
|
-
require 'rubygems'
|
34
|
-
require 'highline/import'
|
35
|
-
require 'nuggets/array/flatten_once'
|
36
|
-
|
37
|
-
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
38
|
-
|
39
|
-
require 'flattendb'
|
40
31
|
require 'flattendb/cli'
|
41
32
|
|
42
|
-
include FlattenDB::CLI
|
43
|
-
|
44
|
-
USAGE = "Usage: #{$0} [-h|--help] [options]"
|
45
|
-
abort USAGE if ARGV.empty?
|
46
|
-
|
47
|
-
$global_options = {
|
48
|
-
:type => :mysql,
|
49
|
-
:infiles => [],
|
50
|
-
:outfile => nil,
|
51
|
-
:confile => nil,
|
52
|
-
:keep => false,
|
53
|
-
:mysql => {
|
54
|
-
:intype => :xml
|
55
|
-
},
|
56
|
-
:mdb => {
|
57
|
-
:intype => :mdb
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
$opts_by_type = {
|
62
|
-
:mysql => {
|
63
|
-
:title => 'MySQL',
|
64
|
-
:opts => lambda { |opts|
|
65
|
-
opts.on('-x', '--mysql-xml', "Input file is of type XML [This is the default]") {
|
66
|
-
$global_options[:mysql][:intype] = :xml
|
67
|
-
}
|
68
|
-
opts.on('-s', '--sql', "Input file is of type SQL") {
|
69
|
-
$global_options[:mysql][:intype] = :sql
|
70
|
-
}
|
71
|
-
}
|
72
|
-
},
|
73
|
-
:mdb => {
|
74
|
-
:title => 'MS Access',
|
75
|
-
:opts => lambda { |opts|
|
76
|
-
opts.on('-m', '--mdb', "Input file is of type MDB [This is the default]") {
|
77
|
-
$global_options[:mdb][:intype] = :mdb
|
78
|
-
}
|
79
|
-
opts.on('-y', '--mdb-xml', "Input file is of type XML") {
|
80
|
-
$global_options[:mdb][:intype] = :xml
|
81
|
-
}
|
82
|
-
}
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
|
-
def type_options(type, opts, with_heading = true)
|
87
|
-
if type == :all
|
88
|
-
$opts_by_type.keys.sort_by { |t| t.to_s }.each { |t|
|
89
|
-
type_options(t, opts, with_heading)
|
90
|
-
}
|
91
|
-
else
|
92
|
-
opt = $opts_by_type[type.to_sym]
|
93
|
-
|
94
|
-
if with_heading
|
95
|
-
opts.separator ''
|
96
|
-
opts.separator " - [#{type}] #{opt[:title]}"
|
97
|
-
end
|
98
|
-
|
99
|
-
opt[:opts][opts]
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
OptionParser.new { |opts|
|
104
|
-
opts.banner = USAGE
|
105
|
-
|
106
|
-
opts.separator ''
|
107
|
-
opts.separator 'Options:'
|
108
|
-
|
109
|
-
if $type
|
110
|
-
opts.separator " [-t, --type] TYPE OVERRIDE IN EFFECT (#{$type})"
|
111
|
-
else
|
112
|
-
opts.on('-t', '--type TYPE', "Type of database at hand [Default: #{$global_options[:type]}]") { |t|
|
113
|
-
$global_options[:type] = t.downcase.to_sym
|
114
|
-
}
|
115
|
-
end
|
116
|
-
|
117
|
-
opts.separator ''
|
118
|
-
|
119
|
-
opts.on('-i', '--input-file FILE', "Input file; depending on the database type, this option", "may be given multiple times [REQUIRED]") { |f|
|
120
|
-
$global_options[:infiles] << f
|
121
|
-
}
|
122
|
-
|
123
|
-
opts.on('-o', '--output-file FILE', "Output file (flat XML) [REQUIRED]") { |f|
|
124
|
-
$global_options[:outfile] = f
|
125
|
-
}
|
126
|
-
|
127
|
-
opts.on('-c', '--config-file FILE', "Configuration file (YAML) [REQUIRED]") { |f|
|
128
|
-
$global_options[:confile] = f
|
129
|
-
}
|
130
|
-
|
131
|
-
opts.separator ''
|
132
|
-
|
133
|
-
opts.on('-k', '--keep', "Keep any intermediate files (generated XML dumps, etc.)") {
|
134
|
-
$global_options[:keep] = true
|
135
|
-
}
|
136
|
-
|
137
|
-
opts.separator ''
|
138
|
-
opts.separator 'Database-specific Options:'
|
139
|
-
|
140
|
-
if $type
|
141
|
-
type_options($type, opts, false)
|
142
|
-
else
|
143
|
-
type_options(:all, opts)
|
144
|
-
end
|
145
|
-
|
146
|
-
opts.separator ''
|
147
|
-
opts.separator 'Generic options:'
|
148
|
-
|
149
|
-
opts.on('-h', '--help', 'Print this help message and exit') {
|
150
|
-
abort opts.to_s
|
151
|
-
}
|
152
|
-
|
153
|
-
opts.on('--version', 'Print program version and exit') {
|
154
|
-
abort "#{File.basename($0)} v#{FlattenDB::VERSION}"
|
155
|
-
}
|
156
|
-
}.parse!
|
157
|
-
|
158
|
-
$type ||= $global_options[:type]
|
159
|
-
$options = $global_options[$type] || {}
|
160
|
-
|
161
|
-
# Load corresponding module
|
162
|
-
begin
|
163
|
-
require "flattendb/types/#{$type}"
|
164
|
-
rescue LoadError
|
165
|
-
abort "Database type '#{$type}' is not supported at the moment."
|
166
|
-
end
|
167
|
-
|
168
|
-
abort "No output file specified" unless $global_options[:outfile]
|
169
|
-
|
170
|
-
if confile = $global_options[:confile]
|
171
|
-
abort "Configuration file not found: #{confile}" unless File.readable?(confile)
|
172
|
-
else
|
173
|
-
abort "No configuration file specified"
|
174
|
-
end
|
175
|
-
|
176
|
-
$global_options[:infiles].each { |infile|
|
177
|
-
abort "Input file not found: #{infile}" unless File.readable?(infile)
|
178
|
-
}
|
179
|
-
|
180
|
-
$infiles = $global_options[:infiles]
|
181
|
-
$outfile = $global_options[:outfile]
|
182
|
-
$confile = $global_options[:confile]
|
183
|
-
|
184
|
-
# Load type-specific script
|
185
33
|
begin
|
186
|
-
|
187
|
-
rescue
|
188
|
-
|
189
|
-
end
|
190
|
-
|
191
|
-
FlattenDB[$type].new($infile || $infiles, $outfile, $confile).flatten!.to_xml
|
192
|
-
|
193
|
-
unless $global_options[:keep]
|
194
|
-
File.delete($dump_file) if $dump_file
|
34
|
+
FlattenDB::CLI.execute($flattendb_type, ARGV, STDIN, STDOUT, STDERR)
|
35
|
+
rescue ArgumentError => err
|
36
|
+
abort err
|
195
37
|
end
|
data/bin/flattendb.mdb
CHANGED
@@ -1,55 +1,7 @@
|
|
1
1
|
#! /usr/bin/ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# flattendb -- Flatten relational databases #
|
7
|
-
# #
|
8
|
-
# Copyright (C) 2007-2011 University of Cologne, #
|
9
|
-
# Albertus-Magnus-Platz, #
|
10
|
-
# 50923 Cologne, Germany #
|
11
|
-
# #
|
12
|
-
# Authors: #
|
13
|
-
# Jens Wille <jens.wille@uni-koeln.de> #
|
14
|
-
# #
|
15
|
-
# flattendb is free software; you can redistribute it and/or modify it under #
|
16
|
-
# the terms of the GNU Affero General Public License as published by the Free #
|
17
|
-
# Software Foundation; either version 3 of the License, or (at your option) #
|
18
|
-
# any later version. #
|
19
|
-
# #
|
20
|
-
# flattendb is distributed in the hope that it will be useful, but WITHOUT #
|
21
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
22
|
-
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License #
|
23
|
-
# for more details. #
|
24
|
-
# #
|
25
|
-
# You should have received a copy of the GNU Affero General Public License #
|
26
|
-
# along with flattendb. If not, see <http://www.gnu.org/licenses/>. #
|
27
|
-
# #
|
28
|
-
###############################################################################
|
29
|
-
#++
|
30
|
-
|
31
|
-
unless $type
|
32
|
-
$type = :mdb
|
33
|
-
load File.join(File.dirname(__FILE__), 'flattendb')
|
34
|
-
else
|
35
|
-
case $options[:intype]
|
36
|
-
when :xml
|
37
|
-
$infiles.each { |infile|
|
38
|
-
unless IO.read(infile, 6) == '<?xml '
|
39
|
-
abort "Input file doesn't seem to be a valid XML file, XML declaration missing: #{infile}"
|
40
|
-
end
|
41
|
-
}
|
42
|
-
when :mdb
|
43
|
-
tables_cmd = 'mdb-tables'
|
44
|
-
export_cmd = 'mdb-export'
|
45
|
-
|
46
|
-
require_commands(tables_cmd, export_cmd, :pkg => 'mdbtools')
|
47
|
-
require_libraries('fastercsv')
|
48
|
-
|
49
|
-
$infiles.map! { |infile|
|
50
|
-
$dump_file
|
51
|
-
}
|
52
|
-
end
|
53
|
-
end
|
3
|
+
ext = File.extname(__FILE__)
|
4
|
+
$flattendb_type = ext.sub(/\A\./, '')
|
5
|
+
load __FILE__.sub(/#{Regexp.escape(ext)}\z/, '')
|
54
6
|
|
55
7
|
# vim:ft=ruby
|
data/bin/flattendb.mysql
CHANGED
@@ -1,92 +1,7 @@
|
|
1
1
|
#! /usr/bin/ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# flattendb -- Flatten relational databases #
|
7
|
-
# #
|
8
|
-
# Copyright (C) 2007-2011 University of Cologne, #
|
9
|
-
# Albertus-Magnus-Platz, #
|
10
|
-
# 50923 Cologne, Germany #
|
11
|
-
# #
|
12
|
-
# Authors: #
|
13
|
-
# Jens Wille <jens.wille@uni-koeln.de> #
|
14
|
-
# #
|
15
|
-
# flattendb is free software; you can redistribute it and/or modify it under #
|
16
|
-
# the terms of the GNU Affero General Public License as published by the Free #
|
17
|
-
# Software Foundation; either version 3 of the License, or (at your option) #
|
18
|
-
# any later version. #
|
19
|
-
# #
|
20
|
-
# flattendb is distributed in the hope that it will be useful, but WITHOUT #
|
21
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
22
|
-
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License #
|
23
|
-
# for more details. #
|
24
|
-
# #
|
25
|
-
# You should have received a copy of the GNU Affero General Public License #
|
26
|
-
# along with flattendb. If not, see <http://www.gnu.org/licenses/>. #
|
27
|
-
# #
|
28
|
-
###############################################################################
|
29
|
-
#++
|
30
|
-
|
31
|
-
unless $type
|
32
|
-
$type = :mysql
|
33
|
-
load File.join(File.dirname(__FILE__), 'flattendb')
|
34
|
-
else
|
35
|
-
$infile = $infiles.first
|
36
|
-
|
37
|
-
case $options[:intype]
|
38
|
-
when :xml
|
39
|
-
unless IO.read($infile, 6) == '<?xml '
|
40
|
-
abort "Input file doesn't seem to be a valid XML file, XML declaration missing"
|
41
|
-
end
|
42
|
-
when :sql
|
43
|
-
$dump_file = $infile.sub(/\.(?:sql|dump)(?:\.gz)?\z/i, '') << '.xml'
|
44
|
-
abort "Dump file and output file are the same: #{$dump_file} = #{$outfile}" \
|
45
|
-
if File.expand_path($dump_file) == File.expand_path($outfile)
|
46
|
-
|
47
|
-
mysql_cmd = 'mysql'
|
48
|
-
dump_cmd = 'mysqldump'
|
49
|
-
|
50
|
-
require_commands(mysql_cmd, dump_cmd, :pkg => 'a suitable MySQL client')
|
51
|
-
require_libraries('mysql')
|
52
|
-
|
53
|
-
mysql_user = ask('Please enter the MySQL user name: ') \
|
54
|
-
{ |q| q.default = ENV['USER'] }
|
55
|
-
mysql_pass = ask('Please enter the MySQL password for that user: ') \
|
56
|
-
{ |q| q.echo = false }
|
57
|
-
|
58
|
-
is_root = mysql_user == 'root'
|
59
|
-
|
60
|
-
temp_db = ENV['FLATTEN_DB'] || 'flattendb_temp'
|
61
|
-
temp_db = "#{temp_db}_%d_%d" % [Time.now, $$] if is_root
|
62
|
-
|
63
|
-
temp_user = ENV['FLATTEN_USER'] || 'flattendb_user'
|
64
|
-
temp_pass = ENV['FLATTEN_PASS'] || 'flattendb_pass'
|
65
|
-
|
66
|
-
input = $infile =~ /\.gz\z/i ? "<(zcat #{$infile})" : "< #{$infile}"
|
67
|
-
mysql_args = "--one-database -u#{temp_user} -p#{temp_pass} #{temp_db} #{input}"
|
68
|
-
dump_args = "--xml -u#{temp_user} -p#{temp_pass} #{temp_db} > #{$dump_file}"
|
69
|
-
|
70
|
-
begin
|
71
|
-
mysql = Mysql.real_connect('localhost', mysql_user, mysql_pass)
|
72
|
-
|
73
|
-
mysql.query("CREATE DATABASE #{temp_db}")
|
74
|
-
mysql.query("GRANT ALL ON #{temp_db}.* TO '#{temp_user}'@'localhost' IDENTIFIED BY '#{temp_pass}'") if is_root
|
75
|
-
|
76
|
-
system("#{mysql_cmd} #{mysql_args} && #{dump_cmd} #{dump_args}")
|
77
|
-
rescue Mysql::Error => err
|
78
|
-
abort "ERROR #{err.errno} (#{err.sqlstate}): #{err.error}"
|
79
|
-
ensure
|
80
|
-
if mysql
|
81
|
-
mysql.query("REVOKE ALL ON #{temp_db}.* FROM '#{temp_user}'@'localhost'") if is_root
|
82
|
-
mysql.query("DROP DATABASE IF EXISTS #{temp_db}")
|
83
|
-
|
84
|
-
mysql.close
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
$infile = $dump_file
|
89
|
-
end
|
90
|
-
end
|
3
|
+
ext = File.extname(__FILE__)
|
4
|
+
$flattendb_type = ext.sub(/\A\./, '')
|
5
|
+
load __FILE__.sub(/#{Regexp.escape(ext)}\z/, '')
|
91
6
|
|
92
7
|
# vim:ft=ruby
|
@@ -0,0 +1,120 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ndr>
|
3
|
+
<row>
|
4
|
+
<Bla>12</Bla>
|
5
|
+
<Blub></Blub>
|
6
|
+
<ObjID>1</ObjID>
|
7
|
+
<attr1>
|
8
|
+
<ObjID>1</ObjID>
|
9
|
+
<Val>3</Val>
|
10
|
+
<attr1ID>1</attr1ID>
|
11
|
+
</attr1>
|
12
|
+
<attr2>
|
13
|
+
<ObjID>1</ObjID>
|
14
|
+
<Val>4</Val>
|
15
|
+
<attr2ID>3</attr2ID>
|
16
|
+
</attr2>
|
17
|
+
<barobject>
|
18
|
+
<Bar>1002</Bar>
|
19
|
+
<ObjID>1</ObjID>
|
20
|
+
<attr4>
|
21
|
+
<Val>0</Val>
|
22
|
+
<attr4ID>2</attr4ID>
|
23
|
+
</attr4>
|
24
|
+
<attr4ID>2</attr4ID>
|
25
|
+
</barobject>
|
26
|
+
<fooobject>
|
27
|
+
<Foo>112</Foo>
|
28
|
+
<ObjID>1</ObjID>
|
29
|
+
<attr1>
|
30
|
+
<ObjID>1</ObjID>
|
31
|
+
<Val>3</Val>
|
32
|
+
<attr1ID>1</attr1ID>
|
33
|
+
</attr1>
|
34
|
+
<attr1ID>1</attr1ID>
|
35
|
+
<attr3>
|
36
|
+
<Val>8</Val>
|
37
|
+
<attr3ID>2</attr3ID>
|
38
|
+
</attr3>
|
39
|
+
<attr3ID>2</attr3ID>
|
40
|
+
</fooobject>
|
41
|
+
</row>
|
42
|
+
<row>
|
43
|
+
<Bla>12</Bla>
|
44
|
+
<Blub>hi</Blub>
|
45
|
+
<ObjID>2</ObjID>
|
46
|
+
<attr1>
|
47
|
+
<ObjID>2</ObjID>
|
48
|
+
<Val>2</Val>
|
49
|
+
<attr1ID>2</attr1ID>
|
50
|
+
</attr1>
|
51
|
+
<attr2>
|
52
|
+
<ObjID>2</ObjID>
|
53
|
+
<Val>5</Val>
|
54
|
+
<attr2ID>2</attr2ID>
|
55
|
+
</attr2>
|
56
|
+
<barobject>
|
57
|
+
<Bar>1200</Bar>
|
58
|
+
<ObjID>2</ObjID>
|
59
|
+
<attr4>
|
60
|
+
<Val>0</Val>
|
61
|
+
<attr4ID>1</attr4ID>
|
62
|
+
</attr4>
|
63
|
+
<attr4ID>1</attr4ID>
|
64
|
+
</barobject>
|
65
|
+
<fooobject>
|
66
|
+
<Foo>122</Foo>
|
67
|
+
<ObjID>2</ObjID>
|
68
|
+
<attr1>
|
69
|
+
<ObjID>2</ObjID>
|
70
|
+
<Val>2</Val>
|
71
|
+
<attr1ID>2</attr1ID>
|
72
|
+
</attr1>
|
73
|
+
<attr1ID>2</attr1ID>
|
74
|
+
<attr3>
|
75
|
+
<Val>0</Val>
|
76
|
+
<attr3ID>1</attr3ID>
|
77
|
+
</attr3>
|
78
|
+
<attr3ID>1</attr3ID>
|
79
|
+
</fooobject>
|
80
|
+
</row>
|
81
|
+
<row>
|
82
|
+
<Bla>1</Bla>
|
83
|
+
<Blub>ho</Blub>
|
84
|
+
<ObjID>3</ObjID>
|
85
|
+
<attr1>
|
86
|
+
<ObjID>3</ObjID>
|
87
|
+
<Val>1</Val>
|
88
|
+
<attr1ID>3</attr1ID>
|
89
|
+
</attr1>
|
90
|
+
<attr2>
|
91
|
+
<ObjID>3</ObjID>
|
92
|
+
<Val>6</Val>
|
93
|
+
<attr2ID>1</attr2ID>
|
94
|
+
</attr2>
|
95
|
+
<barobject>
|
96
|
+
<Bar>1000</Bar>
|
97
|
+
<ObjID>3</ObjID>
|
98
|
+
<attr4>
|
99
|
+
<Val>9</Val>
|
100
|
+
<attr4ID>3</attr4ID>
|
101
|
+
</attr4>
|
102
|
+
<attr4ID>3</attr4ID>
|
103
|
+
</barobject>
|
104
|
+
<fooobject>
|
105
|
+
<Foo>111</Foo>
|
106
|
+
<ObjID>3</ObjID>
|
107
|
+
<attr1>
|
108
|
+
<ObjID>3</ObjID>
|
109
|
+
<Val>1</Val>
|
110
|
+
<attr1ID>3</attr1ID>
|
111
|
+
</attr1>
|
112
|
+
<attr1ID>3</attr1ID>
|
113
|
+
<attr3>
|
114
|
+
<Val>7</Val>
|
115
|
+
<attr3ID>3</attr3ID>
|
116
|
+
</attr3>
|
117
|
+
<attr3ID>3</attr3ID>
|
118
|
+
</fooobject>
|
119
|
+
</row>
|
120
|
+
</ndr>
|
data/example/mysql-sample.sql
CHANGED
@@ -0,0 +1,137 @@
|
|
1
|
+
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
2
|
+
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
3
|
+
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
4
|
+
/*!40101 SET NAMES utf8 */;
|
5
|
+
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
6
|
+
/*!40103 SET TIME_ZONE='+00:00' */;
|
7
|
+
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
8
|
+
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
9
|
+
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
10
|
+
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
11
|
+
|
12
|
+
DROP TABLE IF EXISTS `object`;
|
13
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
14
|
+
/*!40101 SET character_set_client = utf8 */;
|
15
|
+
CREATE TABLE `object` (
|
16
|
+
`ObjID` int(11) NOT NULL,
|
17
|
+
`Bla` int(11) NOT NULL,
|
18
|
+
`Blub` varchar(50) DEFAULT NULL,
|
19
|
+
PRIMARY KEY (`ObjID`)
|
20
|
+
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
21
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
22
|
+
|
23
|
+
LOCK TABLES `object` WRITE;
|
24
|
+
/*!40000 ALTER TABLE `object` DISABLE KEYS */;
|
25
|
+
INSERT INTO `object` VALUES (1,12,NULL),(2,12,'hi'),(3,1,'ho');
|
26
|
+
/*!40000 ALTER TABLE `object` ENABLE KEYS */;
|
27
|
+
UNLOCK TABLES;
|
28
|
+
|
29
|
+
DROP TABLE IF EXISTS `fooobject`;
|
30
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
31
|
+
/*!40101 SET character_set_client = utf8 */;
|
32
|
+
CREATE TABLE `fooobject` (
|
33
|
+
`ObjID` int(11) NOT NULL,
|
34
|
+
`attr1ID` int(11) NOT NULL,
|
35
|
+
`attr3ID` int(11) NOT NULL,
|
36
|
+
`Foo` int(11) NOT NULL,
|
37
|
+
PRIMARY KEY (`ObjID`)
|
38
|
+
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
39
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
40
|
+
|
41
|
+
LOCK TABLES `fooobject` WRITE;
|
42
|
+
/*!40000 ALTER TABLE `fooobject` DISABLE KEYS */;
|
43
|
+
INSERT INTO `fooobject` VALUES (1,1,2,112),(2,2,1,122),(3,3,3,111);
|
44
|
+
/*!40000 ALTER TABLE `fooobject` ENABLE KEYS */;
|
45
|
+
UNLOCK TABLES;
|
46
|
+
|
47
|
+
DROP TABLE IF EXISTS `barobject`;
|
48
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
49
|
+
/*!40101 SET character_set_client = utf8 */;
|
50
|
+
CREATE TABLE `barobject` (
|
51
|
+
`ObjID` int(11) NOT NULL,
|
52
|
+
`attr4ID` int(11) NOT NULL,
|
53
|
+
`Bar` int(11) NOT NULL,
|
54
|
+
PRIMARY KEY (`ObjID`)
|
55
|
+
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
56
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
57
|
+
|
58
|
+
LOCK TABLES `barobject` WRITE;
|
59
|
+
/*!40000 ALTER TABLE `barobject` DISABLE KEYS */;
|
60
|
+
INSERT INTO `barobject` VALUES (1,2,1002),(2,1,1200),(3,3,1000);
|
61
|
+
/*!40000 ALTER TABLE `barobject` ENABLE KEYS */;
|
62
|
+
UNLOCK TABLES;
|
63
|
+
|
64
|
+
DROP TABLE IF EXISTS `attr1`;
|
65
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
66
|
+
/*!40101 SET character_set_client = utf8 */;
|
67
|
+
CREATE TABLE `attr1` (
|
68
|
+
`ObjID` int(11) NOT NULL,
|
69
|
+
`attr1ID` int(11) NOT NULL,
|
70
|
+
`Val` int(11) NOT NULL,
|
71
|
+
PRIMARY KEY (`attr1ID`)
|
72
|
+
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
73
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
74
|
+
|
75
|
+
LOCK TABLES `attr1` WRITE;
|
76
|
+
/*!40000 ALTER TABLE `attr1` DISABLE KEYS */;
|
77
|
+
INSERT INTO `attr1` VALUES (1,1,3),(2,2,2),(3,3,1);
|
78
|
+
/*!40000 ALTER TABLE `attr1` ENABLE KEYS */;
|
79
|
+
UNLOCK TABLES;
|
80
|
+
|
81
|
+
DROP TABLE IF EXISTS `attr2`;
|
82
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
83
|
+
/*!40101 SET character_set_client = utf8 */;
|
84
|
+
CREATE TABLE `attr2` (
|
85
|
+
`ObjID` int(11) NOT NULL,
|
86
|
+
`attr2ID` int(11) NOT NULL,
|
87
|
+
`Val` int(11) NOT NULL,
|
88
|
+
PRIMARY KEY (`attr2ID`)
|
89
|
+
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
90
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
91
|
+
|
92
|
+
LOCK TABLES `attr2` WRITE;
|
93
|
+
/*!40000 ALTER TABLE `attr2` DISABLE KEYS */;
|
94
|
+
INSERT INTO `attr2` VALUES (1,3,4),(2,2,5),(3,1,6);
|
95
|
+
/*!40000 ALTER TABLE `attr2` ENABLE KEYS */;
|
96
|
+
UNLOCK TABLES;
|
97
|
+
|
98
|
+
DROP TABLE IF EXISTS `attr3`;
|
99
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
100
|
+
/*!40101 SET character_set_client = utf8 */;
|
101
|
+
CREATE TABLE `attr3` (
|
102
|
+
`attr3ID` int(11) NOT NULL,
|
103
|
+
`Val` int(11) NOT NULL,
|
104
|
+
PRIMARY KEY (`attr3ID`)
|
105
|
+
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
106
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
107
|
+
|
108
|
+
LOCK TABLES `attr3` WRITE;
|
109
|
+
/*!40000 ALTER TABLE `attr3` DISABLE KEYS */;
|
110
|
+
INSERT INTO `attr3` VALUES (1,0),(2,8),(3,7);
|
111
|
+
/*!40000 ALTER TABLE `attr3` ENABLE KEYS */;
|
112
|
+
UNLOCK TABLES;
|
113
|
+
|
114
|
+
DROP TABLE IF EXISTS `attr4`;
|
115
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
116
|
+
/*!40101 SET character_set_client = utf8 */;
|
117
|
+
CREATE TABLE `attr4` (
|
118
|
+
`attr4ID` int(11) NOT NULL,
|
119
|
+
`Val` int(11) NOT NULL,
|
120
|
+
PRIMARY KEY (`attr4ID`)
|
121
|
+
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
122
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
123
|
+
|
124
|
+
LOCK TABLES `attr4` WRITE;
|
125
|
+
/*!40000 ALTER TABLE `attr4` DISABLE KEYS */;
|
126
|
+
INSERT INTO `attr4` VALUES (1,0),(2,0),(3,9);
|
127
|
+
/*!40000 ALTER TABLE `attr4` ENABLE KEYS */;
|
128
|
+
UNLOCK TABLES;
|
129
|
+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
130
|
+
|
131
|
+
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
132
|
+
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
133
|
+
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
134
|
+
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
135
|
+
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
136
|
+
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
137
|
+
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
data/lib/flattendb/base.rb
CHANGED
@@ -27,9 +27,8 @@
|
|
27
27
|
#++
|
28
28
|
|
29
29
|
require 'yaml'
|
30
|
-
|
31
|
-
require 'rubygems'
|
32
30
|
require 'builder'
|
31
|
+
require 'flattendb'
|
33
32
|
|
34
33
|
module FlattenDB
|
35
34
|
|
@@ -49,8 +48,12 @@ module FlattenDB
|
|
49
48
|
|
50
49
|
class << self
|
51
50
|
|
51
|
+
def to_flat!(*args)
|
52
|
+
new(*args).flatten!.to_xml
|
53
|
+
end
|
54
|
+
|
52
55
|
def types
|
53
|
-
Base.instance_variable_get
|
56
|
+
Base.instance_variable_get(:@types)
|
54
57
|
end
|
55
58
|
|
56
59
|
def [](type)
|
@@ -67,40 +70,13 @@ module FlattenDB
|
|
67
70
|
|
68
71
|
attr_reader :root, :config, :input, :output
|
69
72
|
|
70
|
-
def initialize(
|
71
|
-
config =
|
72
|
-
when Hash
|
73
|
-
config
|
74
|
-
when String
|
75
|
-
# assume file name
|
76
|
-
YAML.load_file(config)
|
77
|
-
else
|
78
|
-
raise ArgumentError, "invalid config argument of type '#{config.class}'"
|
79
|
-
end
|
73
|
+
def initialize(options)
|
74
|
+
config = options.select { |k, _| k.is_a?(String) }
|
80
75
|
raise ArgumentError, "can't have more than one primary (root) table" if config.size > 1
|
81
76
|
|
82
77
|
(@root, @config), _ = *config # get "first" (and only) hash element
|
83
78
|
|
84
|
-
@input =
|
85
|
-
case infile
|
86
|
-
when String
|
87
|
-
infile
|
88
|
-
when File
|
89
|
-
infile.path
|
90
|
-
else
|
91
|
-
raise ArgumentError, "invalid infile argument of type '#{infile.class}'"
|
92
|
-
end
|
93
|
-
}
|
94
|
-
|
95
|
-
@output = case outfile
|
96
|
-
when IO
|
97
|
-
outfile
|
98
|
-
when String
|
99
|
-
# assume file name
|
100
|
-
File.open(outfile, 'w')
|
101
|
-
else
|
102
|
-
raise ArgumentError, "invalid outfile argument of type '#{outfile.class}'"
|
103
|
-
end
|
79
|
+
@input, @output = options.values_at(:input, :output)
|
104
80
|
end
|
105
81
|
|
106
82
|
def flatten!(*args)
|
data/lib/flattendb/cli.rb
CHANGED
@@ -26,52 +26,212 @@
|
|
26
26
|
###############################################################################
|
27
27
|
#++
|
28
28
|
|
29
|
+
require 'optparse'
|
30
|
+
require 'yaml'
|
31
|
+
require 'zlib'
|
32
|
+
require 'flattendb'
|
33
|
+
|
29
34
|
module FlattenDB
|
30
35
|
|
31
|
-
|
36
|
+
class CLI
|
32
37
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
USAGE = "Usage: #{$0} [-h|--help] [options]"
|
39
|
+
|
40
|
+
DEFAULTS = {
|
41
|
+
:input => '-',
|
42
|
+
:inputs => [],
|
43
|
+
:output => '-',
|
44
|
+
:config => 'config.yaml'
|
45
|
+
}
|
46
|
+
|
47
|
+
TYPES = {
|
48
|
+
:mysql => {
|
49
|
+
:title => 'MySQL',
|
50
|
+
:opts => lambda { |opts, options|
|
51
|
+
opts.on('-x', '--xml', 'Input file is of type XML [This is the default]') {
|
52
|
+
options[:type] = :xml
|
53
|
+
}
|
54
|
+
opts.on('-s', '--sql', 'Input file is of type SQL') {
|
55
|
+
options[:type] = :sql
|
56
|
+
}
|
57
|
+
}
|
58
|
+
},
|
59
|
+
:mdb => {
|
60
|
+
:title => 'MS Access',
|
61
|
+
:opts => lambda { |opts, options|
|
62
|
+
opts.separator(" NOTE: Repeat '-i' for each .mdb file")
|
63
|
+
}
|
40
64
|
}
|
65
|
+
}
|
66
|
+
|
67
|
+
def self.execute(type = nil, *args)
|
68
|
+
new(type).execute(*args)
|
41
69
|
end
|
42
70
|
|
43
|
-
|
44
|
-
|
45
|
-
catch :cmd_found do
|
46
|
-
ENV['PATH'].split(':').each { |path|
|
47
|
-
throw :cmd_found if File.executable?(File.join(path, cmd))
|
48
|
-
}
|
71
|
+
attr_reader :type, :options, :config, :defaults
|
72
|
+
attr_reader :stdin, :stdout, :stderr
|
49
73
|
|
50
|
-
|
51
|
-
|
52
|
-
|
74
|
+
def initialize(type = nil, defaults = DEFAULTS)
|
75
|
+
@defaults = defaults
|
76
|
+
|
77
|
+
reset(type)
|
78
|
+
|
79
|
+
# prevent backtrace on ^C
|
80
|
+
trap(:INT) { exit 130 }
|
53
81
|
end
|
54
82
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
83
|
+
def execute(arguments = [], *inouterr)
|
84
|
+
reset(type, *inouterr)
|
85
|
+
|
86
|
+
parse_options(arguments, defaults)
|
58
87
|
|
59
|
-
|
88
|
+
if type
|
89
|
+
require "flattendb/types/#{type}"
|
90
|
+
else
|
91
|
+
abort 'Database type is required!'
|
92
|
+
end
|
93
|
+
|
94
|
+
options[:input] = if type == :mdb
|
95
|
+
if options[:inputs].empty?
|
96
|
+
if arguments.empty?
|
97
|
+
options[:inputs] << options[:input]
|
98
|
+
else
|
99
|
+
options[:inputs].concat(arguments)
|
100
|
+
arguments.clear
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
options[:inputs].map! { |file| open_file_or_std(file) }
|
105
|
+
else
|
106
|
+
open_file_or_std(
|
107
|
+
options[:inputs].last || arguments.shift || options[:input]
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
abort USAGE unless arguments.empty?
|
112
|
+
|
113
|
+
options[:output] = open_file_or_std(options[:output], true)
|
114
|
+
|
115
|
+
FlattenDB[type].to_flat!(options)
|
116
|
+
ensure
|
117
|
+
options[:output].close if options[:output].is_a?(Zlib::GzipWriter)
|
118
|
+
end
|
119
|
+
|
120
|
+
def reset(type = nil, stdin = STDIN, stdout = STDOUT, stderr = STDERR)
|
121
|
+
@stdin, @stdout, @stderr = stdin, stdout, stderr
|
122
|
+
self.type, @options, @config = type, {}, {}
|
60
123
|
end
|
61
124
|
|
62
125
|
private
|
63
126
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
127
|
+
def type=(type)
|
128
|
+
if type
|
129
|
+
@type = type.to_s.downcase.to_sym
|
130
|
+
abort "Database type not supported: #{type}" unless TYPES.has_key?(@type)
|
131
|
+
else
|
132
|
+
@type = nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def open_file_or_std(file, write = false)
|
137
|
+
if file == '-'
|
138
|
+
write ? stdout : stdin
|
139
|
+
else
|
140
|
+
gz = file =~ /\.gz\z/i
|
141
|
+
|
142
|
+
if write
|
143
|
+
gz ? Zlib::GzipWriter.open(file) : File.open(file, 'w')
|
144
|
+
else
|
145
|
+
abort "No such file: #{file}" unless File.readable?(file)
|
146
|
+
(gz ? Zlib::GzipReader : File).open(file)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def warn(msg, output = stderr)
|
152
|
+
output.puts(msg)
|
153
|
+
end
|
154
|
+
|
155
|
+
def abort(msg = nil, status = 1, output = stderr)
|
156
|
+
warn(msg, output) if msg
|
157
|
+
exit(status)
|
158
|
+
end
|
159
|
+
|
160
|
+
def parse_options(arguments, defaults)
|
161
|
+
option_parser(defaults).parse!(arguments)
|
162
|
+
|
163
|
+
config_file = options[:config] || defaults[:config]
|
164
|
+
@config = YAML.load_file(config_file) if File.readable?(config_file)
|
165
|
+
|
166
|
+
[config, defaults].each { |hash| hash.each { |key, value| options[key] ||= value } }
|
167
|
+
end
|
168
|
+
|
169
|
+
def option_parser(defaults)
|
170
|
+
sorted_types = TYPES.keys.sort_by { |t| t.to_s }
|
171
|
+
|
172
|
+
OptionParser.new { |opts|
|
173
|
+
opts.banner = USAGE
|
174
|
+
|
175
|
+
if type
|
176
|
+
opts.separator ''
|
177
|
+
opts.separator "TYPE = #{type} (#{TYPES[type][:title]})"
|
178
|
+
end
|
179
|
+
|
180
|
+
opts.separator ''
|
181
|
+
opts.separator 'Options:'
|
182
|
+
|
183
|
+
unless type
|
184
|
+
opts.on('-t', '--type TYPE', 'Type of database [REQUIRED]') { |type|
|
185
|
+
self.type = type
|
186
|
+
}
|
187
|
+
|
188
|
+
opts.separator ''
|
189
|
+
end
|
190
|
+
|
191
|
+
opts.on('-i', '--input FILE', 'Input file(s) [Default: STDIN]') { |input|
|
192
|
+
(options[:inputs] ||= []) << input
|
193
|
+
}
|
194
|
+
|
195
|
+
opts.on('-o', '--output FILE', 'Output file (flat XML) [Default: STDOUT]') { |output|
|
196
|
+
options[:output] = output
|
197
|
+
}
|
198
|
+
|
199
|
+
opts.on('-c', '--config FILE', "Configuration file (YAML) [Default: #{defaults[:config]}#{' (currently not present)' unless File.readable?(defaults[:config])}]") { |config|
|
200
|
+
options[:config] = config
|
201
|
+
}
|
202
|
+
|
203
|
+
opts.separator ''
|
204
|
+
opts.separator 'Database-specific options:'
|
205
|
+
|
206
|
+
type ? type_options(opts) : sorted_types.each { |t| type_options(opts, true, t) }
|
67
207
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
208
|
+
opts.separator ''
|
209
|
+
opts.separator 'Generic options:'
|
210
|
+
|
211
|
+
opts.on('-h', '--help', 'Print this help message and exit') {
|
212
|
+
abort opts.to_s
|
73
213
|
}
|
74
|
-
|
214
|
+
|
215
|
+
opts.on('--version', 'Print program version and exit') {
|
216
|
+
abort "#{File.basename($0)} v#{FlattenDB::VERSION}"
|
217
|
+
}
|
218
|
+
|
219
|
+
unless type
|
220
|
+
opts.separator ''
|
221
|
+
opts.separator "Supported database types: #{sorted_types.map { |t| "#{t} (#{TYPES[t][:title]})" }.join(', ')}."
|
222
|
+
end
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
def type_options(opts, heading = false, type = type)
|
227
|
+
cfg = TYPES[type]
|
228
|
+
|
229
|
+
if heading
|
230
|
+
opts.separator ''
|
231
|
+
opts.separator " - [#{type}] #{cfg[:title]}"
|
232
|
+
end
|
233
|
+
|
234
|
+
cfg[:opts][opts, options]
|
75
235
|
end
|
76
236
|
|
77
237
|
end
|
data/lib/flattendb/types/mdb.rb
CHANGED
@@ -26,16 +26,28 @@
|
|
26
26
|
###############################################################################
|
27
27
|
#++
|
28
28
|
|
29
|
-
require '
|
29
|
+
require 'fastercsv'
|
30
|
+
require 'nuggets/file/which'
|
31
|
+
require 'flattendb'
|
30
32
|
|
31
33
|
module FlattenDB
|
32
34
|
|
33
35
|
class MDB < Base
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
def initialize(infiles, outfile, config)
|
37
|
+
def initialize(options)
|
38
38
|
super
|
39
|
+
parse
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse
|
43
|
+
tables_cmd, export_cmd = 'mdb-tables', 'mdb-export'
|
44
|
+
|
45
|
+
[tables_cmd, export_cmd].each { |cmd|
|
46
|
+
next if File.which(cmd)
|
47
|
+
abort "Command not found: #{cmd}! Please install `mdbtools' first."
|
48
|
+
}
|
49
|
+
|
50
|
+
# ...
|
39
51
|
end
|
40
52
|
|
41
53
|
def flatten!(options = {}, builder_options = {})
|
@@ -27,8 +27,8 @@
|
|
27
27
|
#++
|
28
28
|
|
29
29
|
require 'libxml'
|
30
|
-
|
31
|
-
require 'flattendb
|
30
|
+
require 'athena'
|
31
|
+
require 'flattendb'
|
32
32
|
|
33
33
|
module FlattenDB
|
34
34
|
|
@@ -36,22 +36,21 @@ module FlattenDB
|
|
36
36
|
|
37
37
|
JOIN_KEY = '@key'
|
38
38
|
|
39
|
-
attr_reader :
|
39
|
+
attr_reader :type, :name, :tables, :builder
|
40
40
|
|
41
|
-
def initialize(
|
41
|
+
def initialize(options)
|
42
42
|
super
|
43
43
|
|
44
|
-
@
|
45
|
-
@
|
46
|
-
|
47
|
-
@tables = {}
|
44
|
+
@type = options[:type] || :xml
|
45
|
+
@name, @tables = parse
|
46
|
+
end
|
48
47
|
|
49
|
-
|
48
|
+
def parse(tables = {})
|
49
|
+
[send("parse_#{type}", tables) || 'root', tables]
|
50
50
|
end
|
51
51
|
|
52
52
|
def flatten!(options = {}, builder_options = {})
|
53
53
|
flatten_tables!(tables, root, config)
|
54
|
-
|
55
54
|
self
|
56
55
|
end
|
57
56
|
|
@@ -76,9 +75,12 @@ module FlattenDB
|
|
76
75
|
|
77
76
|
private
|
78
77
|
|
79
|
-
def
|
78
|
+
def parse_xml(tables)
|
79
|
+
document = LibXML::XML::Document.io(input)
|
80
|
+
database = document.root.find_first('database[@name]')
|
81
|
+
|
80
82
|
database.find('table_data[@name]').each { |table|
|
81
|
-
rows = []
|
83
|
+
rows = tables[table[:name]] ||= []
|
82
84
|
|
83
85
|
table.find('row').each { |row|
|
84
86
|
fields = {}
|
@@ -89,9 +91,44 @@ module FlattenDB
|
|
89
91
|
|
90
92
|
rows << fields
|
91
93
|
}
|
94
|
+
}
|
92
95
|
|
93
|
-
|
96
|
+
database[:name]
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_sql(tables)
|
100
|
+
columns, table, name = Hash.new { |h, k| h[k] = [] }, nil, nil
|
101
|
+
parser = Athena::Formats::MySQL::SQLParser.new
|
102
|
+
|
103
|
+
input.each { |line|
|
104
|
+
case line
|
105
|
+
when /\AUSE\s+`(.+?)`/i
|
106
|
+
raise 'dump file contains more than one database' if name
|
107
|
+
name = $1
|
108
|
+
when /\ACREATE\s+TABLE\s+`(.+?)`/i
|
109
|
+
table = $1
|
110
|
+
when /\A\s+`(.+?)`/i
|
111
|
+
columns[table] << $1 if table
|
112
|
+
when /\A\).*;\Z/
|
113
|
+
table = nil
|
114
|
+
when /\AINSERT\s+INTO\s+`(.+?)`\s+VALUES\s*(.*);\Z/i
|
115
|
+
_columns = columns[_table = $1]
|
116
|
+
next if _columns.empty?
|
117
|
+
|
118
|
+
parser.parse($2) { |row|
|
119
|
+
fields = {}
|
120
|
+
|
121
|
+
row.each_with_index { |value, index|
|
122
|
+
column = _columns[index] or next
|
123
|
+
fields[column] = value.to_s
|
124
|
+
}
|
125
|
+
|
126
|
+
(tables[_table] ||= []) << fields
|
127
|
+
}
|
128
|
+
end
|
94
129
|
}
|
130
|
+
|
131
|
+
name
|
95
132
|
end
|
96
133
|
|
97
134
|
def flatten_tables!(tables, primary_table, config)
|
@@ -144,7 +181,7 @@ module FlattenDB
|
|
144
181
|
builder.tag!(table) {
|
145
182
|
rows.each { |row|
|
146
183
|
row_to_xml('row', row, builder)
|
147
|
-
}
|
184
|
+
} if rows
|
148
185
|
}
|
149
186
|
end
|
150
187
|
|
data/lib/flattendb/version.rb
CHANGED
data/lib/flattendb.rb
CHANGED
@@ -27,10 +27,13 @@
|
|
27
27
|
#++
|
28
28
|
|
29
29
|
require 'flattendb/version'
|
30
|
-
require 'flattendb/base'
|
31
30
|
|
32
31
|
module FlattenDB
|
33
32
|
|
33
|
+
autoload :Base, 'flattendb/base'
|
34
|
+
autoload :MDB, 'flattendb/types/mdb'
|
35
|
+
autoload :MySQL, 'flattendb/types/mysql'
|
36
|
+
|
34
37
|
extend self
|
35
38
|
|
36
39
|
def [](type)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flattendb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.8
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jens Wille
|
@@ -15,10 +15,10 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-07-
|
18
|
+
date: 2011-07-12 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: libxml-ruby
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
35
|
+
name: builder
|
36
36
|
prerelease: false
|
37
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
38
|
none: false
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
type: :runtime
|
47
47
|
version_requirements: *id002
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
49
|
+
name: ruby-nuggets
|
50
50
|
prerelease: false
|
51
51
|
requirement: &id003 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
@@ -60,17 +60,19 @@ dependencies:
|
|
60
60
|
type: :runtime
|
61
61
|
version_requirements: *id003
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: athena
|
64
64
|
prerelease: false
|
65
65
|
requirement: &id004 !ruby/object:Gem::Requirement
|
66
66
|
none: false
|
67
67
|
requirements:
|
68
68
|
- - ">="
|
69
69
|
- !ruby/object:Gem::Version
|
70
|
-
hash:
|
70
|
+
hash: 17
|
71
71
|
segments:
|
72
72
|
- 0
|
73
|
-
|
73
|
+
- 1
|
74
|
+
- 5
|
75
|
+
version: 0.1.5
|
74
76
|
type: :runtime
|
75
77
|
version_requirements: *id004
|
76
78
|
description: Flatten relational databases.
|
@@ -100,6 +102,7 @@ files:
|
|
100
102
|
- Rakefile
|
101
103
|
- COPYING
|
102
104
|
- example/mysql-sample.flat.xml
|
105
|
+
- example/mysql-sample.flat-sql.xml
|
103
106
|
- example/mysql-sample2flat.yaml
|
104
107
|
- example/mysql-sample.xml
|
105
108
|
- example/mysql-sample.sql
|
@@ -111,7 +114,7 @@ rdoc_options:
|
|
111
114
|
- --charset
|
112
115
|
- UTF-8
|
113
116
|
- --title
|
114
|
-
- flattendb Application documentation (v0.0
|
117
|
+
- flattendb Application documentation (v0.1.0)
|
115
118
|
- --main
|
116
119
|
- README
|
117
120
|
- --line-numbers
|