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