blackwinter-flattendb 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +676 -0
- data/ChangeLog +5 -0
- data/README +42 -0
- data/Rakefile +21 -0
- data/bin/flattendb +195 -0
- data/bin/flattendb.mdb +55 -0
- data/bin/flattendb.mysql +91 -0
- data/example/mysql-sample.flat.xml +120 -0
- data/example/mysql-sample.sql +0 -0
- data/example/mysql-sample.xml +162 -0
- data/example/mysql-sample2flat.yaml +18 -0
- data/lib/flattendb.rb +40 -0
- data/lib/flattendb/base.rb +139 -0
- data/lib/flattendb/cli.rb +79 -0
- data/lib/flattendb/types/mdb.rb +53 -0
- data/lib/flattendb/types/mysql.rb +176 -0
- data/lib/flattendb/version.rb +55 -0
- metadata +116 -0
data/ChangeLog
ADDED
data/README
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
= flattendb - Flatten relational databases
|
2
|
+
|
3
|
+
== VERSION
|
4
|
+
|
5
|
+
This documentation refers to flattendb version 0.0.4
|
6
|
+
|
7
|
+
|
8
|
+
== DESCRIPTION
|
9
|
+
|
10
|
+
TODO: well, the description... ;-)
|
11
|
+
|
12
|
+
|
13
|
+
== LINKS
|
14
|
+
|
15
|
+
<b></b>
|
16
|
+
Documentation:: <http://prometheus.rubyforge.org/flattendb>
|
17
|
+
Source code (old):: <http://prometheus.rubyforge.org/svn/scratch/flattendb>
|
18
|
+
Source code:: <http://github.com/blackwinter/flattendb>
|
19
|
+
Rubyforge project:: <http://rubyforge.org/projects/prometheus>
|
20
|
+
|
21
|
+
|
22
|
+
== AUTHORS
|
23
|
+
|
24
|
+
* Jens Wille <mailto:jens.wille@uni-koeln.de>
|
25
|
+
|
26
|
+
|
27
|
+
== LICENSE AND COPYRIGHT
|
28
|
+
|
29
|
+
Copyright (C) 2007 University of Cologne,
|
30
|
+
Albertus-Magnus-Platz, 50932 Cologne, Germany
|
31
|
+
|
32
|
+
flattendb is free software: you can redistribute it and/or modify it under the
|
33
|
+
terms of the GNU General Public License as published by the Free Software
|
34
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
35
|
+
version.
|
36
|
+
|
37
|
+
flattendb is distributed in the hope that it will be useful, but WITHOUT ANY
|
38
|
+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
39
|
+
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
40
|
+
|
41
|
+
You should have received a copy of the GNU General Public License along with
|
42
|
+
flattendb. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'hen'
|
3
|
+
rescue LoadError
|
4
|
+
abort "Please install the 'hen' gem first."
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'lib/flattendb/version'
|
8
|
+
|
9
|
+
Hen.lay! {{
|
10
|
+
:rubyforge => {
|
11
|
+
:package => 'flattendb'
|
12
|
+
},
|
13
|
+
|
14
|
+
:gem => {
|
15
|
+
:version => FlattenDB::VERSION,
|
16
|
+
:summary => 'Flatten relational databases.',
|
17
|
+
:files => FileList['lib/**/*.rb', 'bin/*'].to_a,
|
18
|
+
:extra_files => FileList['[A-Z]*', 'example/*'].to_a,
|
19
|
+
:dependencies => %w[highline libxml-ruby builder ruby-nuggets]
|
20
|
+
}
|
21
|
+
}}
|
data/bin/flattendb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# flattendb -- Flatten relational databases #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2007 University of Cologne, #
|
9
|
+
# Albertus-Magnus-Platz, #
|
10
|
+
# 50932 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 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 General Public License for #
|
23
|
+
# more details. #
|
24
|
+
# #
|
25
|
+
# You should have received a copy of the GNU General Public License along #
|
26
|
+
# with flattendb. If not, see <http://www.gnu.org/licenses/>. #
|
27
|
+
# #
|
28
|
+
###############################################################################
|
29
|
+
#++
|
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
|
+
require 'flattendb/cli'
|
41
|
+
|
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
|
+
begin
|
186
|
+
load File.join(File.dirname(__FILE__), "flattendb.#{$type}")
|
187
|
+
rescue LoadError
|
188
|
+
# silently ignore
|
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
|
195
|
+
end
|
data/bin/flattendb.mdb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
# vi:ft=ruby
|
4
|
+
|
5
|
+
#--
|
6
|
+
###############################################################################
|
7
|
+
# #
|
8
|
+
# flattendb -- Flatten relational databases #
|
9
|
+
# #
|
10
|
+
# Copyright (C) 2007 University of Cologne, #
|
11
|
+
# Albertus-Magnus-Platz, #
|
12
|
+
# 50932 Cologne, Germany #
|
13
|
+
# #
|
14
|
+
# Authors: #
|
15
|
+
# Jens Wille <jens.wille@uni-koeln.de> #
|
16
|
+
# #
|
17
|
+
# flattendb is free software; you can redistribute it and/or modify it under #
|
18
|
+
# the terms of the GNU General Public License as published by the Free #
|
19
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
20
|
+
# any later version. #
|
21
|
+
# #
|
22
|
+
# flattendb is distributed in the hope that it will be useful, but WITHOUT #
|
23
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
24
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
25
|
+
# more details. #
|
26
|
+
# #
|
27
|
+
# You should have received a copy of the GNU General Public License along #
|
28
|
+
# with flattendb. If not, see <http://www.gnu.org/licenses/>. #
|
29
|
+
# #
|
30
|
+
###############################################################################
|
31
|
+
#++
|
32
|
+
|
33
|
+
unless $type
|
34
|
+
$type = :mdb
|
35
|
+
load File.join(File.dirname(__FILE__), 'flattendb')
|
36
|
+
else
|
37
|
+
case $options[:intype]
|
38
|
+
when :xml
|
39
|
+
$infiles.each { |infile|
|
40
|
+
unless IO.read(infile, 6) == '<?xml '
|
41
|
+
abort "Input file doesn't seem to be a valid XML file, XML declaration missing: #{infile}"
|
42
|
+
end
|
43
|
+
}
|
44
|
+
when :mdb
|
45
|
+
tables_cmd = 'mdb-tables'
|
46
|
+
export_cmd = 'mdb-export'
|
47
|
+
|
48
|
+
require_commands(tables_cmd, export_cmd, :pkg => 'mdbtools')
|
49
|
+
require_libraries('fastercsv')
|
50
|
+
|
51
|
+
$infiles.map! { |infile|
|
52
|
+
$dump_file
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
data/bin/flattendb.mysql
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
# vi:ft=ruby
|
4
|
+
|
5
|
+
#--
|
6
|
+
###############################################################################
|
7
|
+
# #
|
8
|
+
# flattendb -- Flatten relational databases #
|
9
|
+
# #
|
10
|
+
# Copyright (C) 2007 University of Cologne, #
|
11
|
+
# Albertus-Magnus-Platz, #
|
12
|
+
# 50932 Cologne, Germany #
|
13
|
+
# #
|
14
|
+
# Authors: #
|
15
|
+
# Jens Wille <jens.wille@uni-koeln.de> #
|
16
|
+
# #
|
17
|
+
# flattendb is free software; you can redistribute it and/or modify it under #
|
18
|
+
# the terms of the GNU General Public License as published by the Free #
|
19
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
20
|
+
# any later version. #
|
21
|
+
# #
|
22
|
+
# flattendb is distributed in the hope that it will be useful, but WITHOUT #
|
23
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
24
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
25
|
+
# more details. #
|
26
|
+
# #
|
27
|
+
# You should have received a copy of the GNU General Public License along #
|
28
|
+
# with flattendb. If not, see <http://www.gnu.org/licenses/>. #
|
29
|
+
# #
|
30
|
+
###############################################################################
|
31
|
+
#++
|
32
|
+
|
33
|
+
unless $type
|
34
|
+
$type = :mysql
|
35
|
+
load File.join(File.dirname(__FILE__), 'flattendb')
|
36
|
+
else
|
37
|
+
$infile = $infiles.first
|
38
|
+
|
39
|
+
case $options[:intype]
|
40
|
+
when :xml
|
41
|
+
unless IO.read($infile, 6) == '<?xml '
|
42
|
+
abort "Input file doesn't seem to be a valid XML file, XML declaration missing"
|
43
|
+
end
|
44
|
+
when :sql
|
45
|
+
$dump_file = $infile.sub(/\.(?:sql|dump)$/i, '') << '.xml'
|
46
|
+
abort "Dump file and output file are the same: #{$dump_file} = #{$outfile}" \
|
47
|
+
if File.expand_path($dump_file) == File.expand_path($outfile)
|
48
|
+
|
49
|
+
mysql_cmd = 'mysql'
|
50
|
+
dump_cmd = 'mysqldump'
|
51
|
+
|
52
|
+
require_commands(mysql_cmd, dump_cmd, :pkg => 'a suitable MySQL client')
|
53
|
+
require_libraries('mysql')
|
54
|
+
|
55
|
+
mysql_user = ask('Please enter the MySQL user name: ') \
|
56
|
+
{ |q| q.default = ENV['USER'] }
|
57
|
+
mysql_pass = ask("Please enter the MySQL password for that user: ") \
|
58
|
+
{ |q| q.echo = false }
|
59
|
+
|
60
|
+
# according to <http://www.adamspiers.org/computing/mysqldiff/#how> MySQL
|
61
|
+
# default permissions allow anyone to create databases beginning with the
|
62
|
+
# prefix 'test_'
|
63
|
+
temp_db = 'flattendb_temp_%d_%d' % [Time.now, $$]
|
64
|
+
temp_user = 'flattendb_user'
|
65
|
+
temp_pass = 'flattendb_pass'
|
66
|
+
|
67
|
+
mysql_args = "--one-database -u#{temp_user} -p#{temp_pass} #{temp_db} < #{$infile}"
|
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}'")
|
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'") \
|
82
|
+
if mysql_user == 'root'
|
83
|
+
|
84
|
+
mysql.query("DROP DATABASE IF EXISTS #{temp_db}")
|
85
|
+
mysql.close
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
$infile = $dump_file
|
90
|
+
end
|
91
|
+
end
|
@@ -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>
|