bc3 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/bc3_db_compare.rb +473 -0
- data/changes.rd +3 -0
- data/examples_db_connect/BCSettings_db4bc3.bcpkg +0 -0
- data/examples_db_connect/sqlite_test_1.db4bc3 +6 -0
- data/examples_db_connect/sqlite_test_2.db4bc3 +8 -0
- data/examples_db_connect/sqlite_test_sql.db4bc3 +11 -0
- data/examples_db_connect/test.db +0 -0
- data/lib/bc3.rb +1 -1
- data/lib/bc3/time.rb +1 -1
- data/unittest/unittest_bc3_db_compare.rb +310 -0
- metadata +43 -4
@@ -0,0 +1,473 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
=begin rdoc
|
3
|
+
==What's for
|
4
|
+
|
5
|
+
BC3 offers no genuine method to compare database content.
|
6
|
+
|
7
|
+
This script is part of a solution to compare two tables in two databases
|
8
|
+
(or different selections in one database...)
|
9
|
+
|
10
|
+
==How to use
|
11
|
+
Call
|
12
|
+
bc3_db-compare <configfile> <targetfile> [<logfile>]
|
13
|
+
|
14
|
+
The configfile will be used to prepare a database connection and
|
15
|
+
a DB-selection. The result is written to targetfile.
|
16
|
+
|
17
|
+
The optional logfile will get some logging data
|
18
|
+
(can be used to analyse errors).
|
19
|
+
|
20
|
+
==How to use in BC3
|
21
|
+
Import the file formats from file examples/BCSettings_db4bc3.
|
22
|
+
to Beyond Compare 3 (BC3)
|
23
|
+
|
24
|
+
Save your two configuration files with extension bcpkg
|
25
|
+
|
26
|
+
Compare your two configuration files.
|
27
|
+
|
28
|
+
|
29
|
+
==How it works
|
30
|
+
You can define file formats with conversion process.
|
31
|
+
This script implement such a conversion for
|
32
|
+
database access.
|
33
|
+
|
34
|
+
The file to compare contains a
|
35
|
+
configuration to make a data base selection and
|
36
|
+
build a result file.
|
37
|
+
|
38
|
+
Two of this configuration files will be compared with BC3.
|
39
|
+
BC3 calls this script as conversion tool,
|
40
|
+
the data base selection is executed and a text file is the result.
|
41
|
+
|
42
|
+
The configuration file defines the configuration in yaml-syntax.
|
43
|
+
Details see DBconverter#check_configuration_file.
|
44
|
+
|
45
|
+
The file formats can be imported from file examples/BCSettings_db4bc3.bcpkg
|
46
|
+
|
47
|
+
This script based on a question in
|
48
|
+
http://www.scootersoftware.com/vbulletin/showthread.php?t=7360
|
49
|
+
=end
|
50
|
+
|
51
|
+
=begin
|
52
|
+
==Beyond Compare
|
53
|
+
Beyond Compare is limited to comparison of plain text files. To compare binary files, or to format files before comparison, Beyond Compare can call an external program to generate a temporary text file for comparison. The following details the conversion process.
|
54
|
+
|
55
|
+
To access settings for external conversion in the File Viewer, select Tools | Edit Current Rules. Go to the Conversion tab.
|
56
|
+
|
57
|
+
Variables for external conversion
|
58
|
+
%s - source file and path
|
59
|
+
%t - target file and path
|
60
|
+
%n - source filename
|
61
|
+
%x - extension of the source file
|
62
|
+
=end
|
63
|
+
require 'yaml'
|
64
|
+
require 'sequel'
|
65
|
+
require 'log4r'
|
66
|
+
$log = Log4r::Logger.new('log')
|
67
|
+
|
68
|
+
module BC3
|
69
|
+
=begin rdoc
|
70
|
+
Class to support comparison of database content.
|
71
|
+
|
72
|
+
Define a converter for Beyond Compare 3.
|
73
|
+
=end
|
74
|
+
class DBconverter
|
75
|
+
VERSION = '0.2.0'
|
76
|
+
#Exitcodes
|
77
|
+
EXITCODES = {
|
78
|
+
# => 1 normal ruby errors (syntax error)
|
79
|
+
:sourcefile_missing => 2,
|
80
|
+
:configuration_error => 3,
|
81
|
+
:configuration_incomplete => 4,
|
82
|
+
|
83
|
+
:db_connection_error => 10,
|
84
|
+
:adapter_unknown => 11,
|
85
|
+
:sequel_error => 12,
|
86
|
+
:db_table_missing => 20,
|
87
|
+
|
88
|
+
:dataset_error => 30,
|
89
|
+
|
90
|
+
:argv_wrong => 90,
|
91
|
+
} # File.open(__FILE__){|f| puts f.read.scan(/EXITCODES\[(.*)\]/) }; exit
|
92
|
+
|
93
|
+
|
94
|
+
OBLIGATORY_KEYS = %w{adapter db tab}
|
95
|
+
VALID_KEYS = OBLIGATORY_KEYS + %w{
|
96
|
+
server password username
|
97
|
+
header schema
|
98
|
+
data headline
|
99
|
+
select filter order
|
100
|
+
sql
|
101
|
+
show_sql
|
102
|
+
}
|
103
|
+
|
104
|
+
=begin rdoc
|
105
|
+
Expect a configuration file and a filename to save the result.
|
106
|
+
|
107
|
+
Without target file name, an empty object is created.
|
108
|
+
=end
|
109
|
+
def initialize( source_filename = nil, target_filename = nil, log_filename = nil )
|
110
|
+
if target_filename
|
111
|
+
|
112
|
+
if log_filename
|
113
|
+
$log.outputters << Log4r::FileOutputter.new('log', :filename => log_filename )
|
114
|
+
end
|
115
|
+
|
116
|
+
@header = true #Default
|
117
|
+
|
118
|
+
$log.info("Start conversion (%s)%s => %s" % [DBconverter::VERSION, source_filename, target_filename]) if $log.info?
|
119
|
+
#set attributes
|
120
|
+
$log.debug("Open %s" % source_filename) if $log.debug?
|
121
|
+
check_configuration_file(source_filename)
|
122
|
+
analyse_configuration_file(File.read(source_filename), File.dirname(source_filename))
|
123
|
+
|
124
|
+
prepare_dataset()
|
125
|
+
save_target(target_filename)
|
126
|
+
end
|
127
|
+
end # initialize
|
128
|
+
|
129
|
+
=begin rdoc
|
130
|
+
Leave the script after an error.
|
131
|
+
|
132
|
+
For unit test, this may be redefined to throw an exception or similar.
|
133
|
+
=end
|
134
|
+
def bc3_exit( exitcode, description)
|
135
|
+
$log.error(description) if $log.error?
|
136
|
+
exit EXITCODES[exitcode]
|
137
|
+
end
|
138
|
+
=begin rdoc
|
139
|
+
Check existence of configuration file.
|
140
|
+
=end
|
141
|
+
def check_configuration_file(filename)
|
142
|
+
if ! filename or ! File.exist?(filename)
|
143
|
+
bc3_exit(:sourcefile_missing, "Open %s" % filename.inspect)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
=begin rdoc
|
147
|
+
Open and analyse configuration file
|
148
|
+
|
149
|
+
==Examples for configuration files
|
150
|
+
Compare data of table 'languages' in sqlite data base file 'test.db' in a
|
151
|
+
csv-file.
|
152
|
+
|
153
|
+
adapter: sqlite
|
154
|
+
db: test.db
|
155
|
+
tab: languages
|
156
|
+
data: csv_tab
|
157
|
+
|
158
|
+
|
159
|
+
|
160
|
+
==Obligatory configuration settings
|
161
|
+
===adapter
|
162
|
+
Defines the type of DB-connection.
|
163
|
+
|
164
|
+
Supported:
|
165
|
+
* ado (server required, username and password optional)
|
166
|
+
* sqlite
|
167
|
+
|
168
|
+
===db
|
169
|
+
Name of the @db.
|
170
|
+
|
171
|
+
* ado: name of the database.
|
172
|
+
* sqlite: filename
|
173
|
+
|
174
|
+
Warning! BC3 calls the script from bc3-folder.
|
175
|
+
The filename should be an absolute path.
|
176
|
+
|
177
|
+
===tab
|
178
|
+
Table name for comparison.
|
179
|
+
|
180
|
+
==Optional configuration settings
|
181
|
+
===schema
|
182
|
+
if true, the table schema (meta data of table) is added on top.
|
183
|
+
|
184
|
+
==Adapt the selection
|
185
|
+
|
186
|
+
===select
|
187
|
+
Restrict columns.
|
188
|
+
See Sequel::@dataset#select
|
189
|
+
http://sequel.rubyforge.org/rdoc/classes/Sequel/@dataset.html#M000439
|
190
|
+
|
191
|
+
===filter
|
192
|
+
Where-clause. Restrict rows.
|
193
|
+
See Sequel::@dataset#filter
|
194
|
+
* http://sequel.rubyforge.org/rdoc/classes/Sequel/@dataset.html#M000412
|
195
|
+
|
196
|
+
===order
|
197
|
+
See Sequel::@dataset#order
|
198
|
+
http://sequel.rubyforge.org/rdoc/classes/Sequel/@dataset.html#M000429
|
199
|
+
|
200
|
+
List of column names.
|
201
|
+
|
202
|
+
===sql
|
203
|
+
You can define a sql-command.
|
204
|
+
|
205
|
+
This sql-command will ignore all other selection commands (select, filter, order)
|
206
|
+
|
207
|
+
=end
|
208
|
+
def analyse_configuration_file(configuration, dirname = nil)
|
209
|
+
|
210
|
+
$log.debug("Analyse Configuration") if $log.debug?
|
211
|
+
options = {}
|
212
|
+
begin
|
213
|
+
options = YAML.load(configuration)
|
214
|
+
rescue ArgumentError => err
|
215
|
+
bc3_exit(:configuration_error, "Configuration file has error #{err}")
|
216
|
+
end
|
217
|
+
# check obligatory parameters
|
218
|
+
OBLIGATORY_KEYS.each{|opt|
|
219
|
+
if ! options[opt]
|
220
|
+
bc3_exit(:configuration_incomplete,"Option %s missing" % opt)
|
221
|
+
end
|
222
|
+
}
|
223
|
+
options.each{|key, value|
|
224
|
+
if ! VALID_KEYS.include?(key)
|
225
|
+
$log.warn("Undefined option %s" % key) if $log.warn?
|
226
|
+
end
|
227
|
+
}
|
228
|
+
|
229
|
+
@dbname = options['db']
|
230
|
+
case @adapter = options['adapter']
|
231
|
+
when 'ado'
|
232
|
+
if ! @server = options['server']
|
233
|
+
bc3_exit(:db_connection_error, "No Server given" % @dbname)
|
234
|
+
end
|
235
|
+
if ! @dbname
|
236
|
+
bc3_exit(:db_connection_error, "No @db given" % @dbname)
|
237
|
+
end
|
238
|
+
@username = options['username'] #optional, not needed for all logins (single sign on...)
|
239
|
+
@password = options['password'] #optional, not needed for all logins (single sign on...)
|
240
|
+
when 'sqlite'
|
241
|
+
#set the path relative to configuration file
|
242
|
+
@dbname = File.join(dirname, @dbname) if dirname and ! File.exist?(@dbname)
|
243
|
+
if ! File.exist?(@dbname)
|
244
|
+
bc3_exit(:db_connection_error, "DB-File %s not found in %s" % [@dbname, Dir.pwd])
|
245
|
+
end
|
246
|
+
else
|
247
|
+
bc3_exit(:adapter_unknown, "Adapter %s not defined " % @adapter.inspect)
|
248
|
+
end
|
249
|
+
@tabsym = options['tab'].downcase.to_sym
|
250
|
+
|
251
|
+
@select = [options['select']].flatten if options['select']
|
252
|
+
@filter = options['filter']
|
253
|
+
@order = options['order']
|
254
|
+
@sql = options['sql']
|
255
|
+
|
256
|
+
@header = options['header'] if options.has_key?('header') #add header (information about DB, table,,,)
|
257
|
+
@show_schema = options['schema']
|
258
|
+
@show_sql = options['show_sql']
|
259
|
+
@headline = options['headline'] #add headline for csv
|
260
|
+
|
261
|
+
case @show_data = options['data']
|
262
|
+
when nil #no data required
|
263
|
+
when 'inspect', 'csv_tab'
|
264
|
+
$log.info("Define data conversion routine #{@show_data}") if $log.info?
|
265
|
+
else
|
266
|
+
$log.error("Data conversion routine #{@show_data} not found") if $log.error?
|
267
|
+
end
|
268
|
+
end #analyse_configuration_file
|
269
|
+
|
270
|
+
#add header information
|
271
|
+
attr_accessor :header
|
272
|
+
#add heading data to csv
|
273
|
+
attr_accessor :headline
|
274
|
+
#(re)-define select-clause (what fields are needed)
|
275
|
+
attr_accessor :select
|
276
|
+
#(re)-define where-clause
|
277
|
+
attr_accessor :filter
|
278
|
+
#(re)-define order-clause
|
279
|
+
attr_accessor :order
|
280
|
+
#(re)-define raw sql -clause.
|
281
|
+
#This inactivates #select, #filter and #order
|
282
|
+
attr_writer :sql
|
283
|
+
|
284
|
+
=begin rdoc
|
285
|
+
Connect DB
|
286
|
+
=end
|
287
|
+
def connect()
|
288
|
+
$log.debug("Connect DB %s" % @dbname) if $log.debug?
|
289
|
+
begin
|
290
|
+
case @adapter
|
291
|
+
when 'ado'
|
292
|
+
@db = Sequel.connect(
|
293
|
+
:adapter=>'ado',
|
294
|
+
:host =>@server,
|
295
|
+
:database=>@dbname,
|
296
|
+
:user => @username,
|
297
|
+
:password=>@password
|
298
|
+
)
|
299
|
+
when 'sqlite'
|
300
|
+
@db = Sequel.sqlite(@dbname)
|
301
|
+
else
|
302
|
+
bc3_exit(:adapter_unknown, "Adapter %s not defined " % @adapter.inspect)
|
303
|
+
end #@adapter
|
304
|
+
#Test connection
|
305
|
+
if @db.test_connection
|
306
|
+
$log.info("Connected to DB %s" % @dbname) if $log.info?
|
307
|
+
else
|
308
|
+
bc3_exit(:db_connection_error, "DB-Connection error")
|
309
|
+
end
|
310
|
+
rescue Sequel::DatabaseError, Sequel::DatabaseConnectionError => err
|
311
|
+
bc3_exit(:sequel_error, "DB-Connection error: %s" % err.inspect)
|
312
|
+
end
|
313
|
+
@db
|
314
|
+
end #connect
|
315
|
+
=begin rdoc
|
316
|
+
Prepare @dataset.
|
317
|
+
|
318
|
+
Defines only the accessor.
|
319
|
+
There is no check, if the data are correct.
|
320
|
+
=end
|
321
|
+
def prepare_dataset(tabsym = @tabsym, select = @select, filter = @filter, order = @order )
|
322
|
+
@db = connect
|
323
|
+
|
324
|
+
if ! tabsym
|
325
|
+
$log.error("No table name available") if $log.error?
|
326
|
+
return {}
|
327
|
+
elsif ! @db.table_exists?(tabsym)
|
328
|
+
bc3_exit(:db_table_missing, "DB error: Table #{tabsym} not defined")
|
329
|
+
end
|
330
|
+
|
331
|
+
if @sql
|
332
|
+
$log.info("Use raw sql for dataset (#{@sql.inspect})") if $log.info?
|
333
|
+
return @dataset = @db.execute(@sql)
|
334
|
+
end
|
335
|
+
|
336
|
+
@dataset = @db[tabsym]
|
337
|
+
if select
|
338
|
+
$log.debug("Restrict rows to #{select.inspect}") if $log.debug?
|
339
|
+
@select.each{|row|
|
340
|
+
$log.warn("Rows definition for filter is no symbol: #{row.inspect}") unless row.is_a?(Symbol)
|
341
|
+
}
|
342
|
+
@dataset = @dataset.select(*select)
|
343
|
+
end
|
344
|
+
if filter
|
345
|
+
$log.debug("Restrict columns to #{filter.inspect}") if $log.debug?
|
346
|
+
@dataset = @dataset.filter(filter)
|
347
|
+
end
|
348
|
+
if order
|
349
|
+
$log.debug("Order columns by #{order.inspect}") if $log.debug?
|
350
|
+
@dataset = @dataset.order(order)
|
351
|
+
end
|
352
|
+
@dataset
|
353
|
+
end #prepare_dataset()
|
354
|
+
=begin rdoc
|
355
|
+
Build target file
|
356
|
+
=end
|
357
|
+
def save_target(filename = @target_filename)
|
358
|
+
$log.info("Save %s" % [filename]) if $log.info?
|
359
|
+
File.open(filename, 'w'){|f|
|
360
|
+
f << build_target.join("\n")
|
361
|
+
}
|
362
|
+
end #save_target
|
363
|
+
=begin rdoc
|
364
|
+
Build content for target file
|
365
|
+
=end
|
366
|
+
def build_target()
|
367
|
+
|
368
|
+
bc3_exit(:db_connection_error, "Database missing (forgot #prepare_dataset ?)") unless @db
|
369
|
+
|
370
|
+
txt = []
|
371
|
+
if @header
|
372
|
+
txt << "Check table %s.%s" % [@dbname, @tabsym]
|
373
|
+
end
|
374
|
+
|
375
|
+
|
376
|
+
if ! @db.table_exists?(@tabsym)
|
377
|
+
txt << "Table #{@tabsym} not found"
|
378
|
+
bc3_exit(:db_table_missing, "Table #{@tabsym} not found")
|
379
|
+
end
|
380
|
+
|
381
|
+
=begin
|
382
|
+
Add schema of table.
|
383
|
+
=end
|
384
|
+
if @show_schema
|
385
|
+
$log.debug("Get table schema #{@tabsym}") if $log.debug?
|
386
|
+
txt << "Table schema #{@tabsym}" if @header
|
387
|
+
txt << @db.schema(@tabsym).to_yaml
|
388
|
+
end
|
389
|
+
|
390
|
+
=begin
|
391
|
+
Add SQL
|
392
|
+
=end
|
393
|
+
if @show_sql
|
394
|
+
$log.debug("Write SQL-command (%s)" % (@sql ? 'raw sql' : 'dataset')) if $log.debug?
|
395
|
+
|
396
|
+
txt << "SQL for selection:" if @header
|
397
|
+
txt << ( @sql ? @sql : @dataset.sql )
|
398
|
+
end
|
399
|
+
|
400
|
+
=begin
|
401
|
+
Check table content/get content.
|
402
|
+
|
403
|
+
Only, when options['data'] is set.
|
404
|
+
=end
|
405
|
+
if @show_data
|
406
|
+
$log.debug("Get data from #{@tabsym}") if $log.debug?
|
407
|
+
txt << "Data content of table #{@tabsym}:" if @header
|
408
|
+
|
409
|
+
first_line = true
|
410
|
+
@dataset.each{|line|
|
411
|
+
case @show_data
|
412
|
+
when 'inspect'
|
413
|
+
txt << line.inspect
|
414
|
+
when 'csv_tab'
|
415
|
+
case line
|
416
|
+
when Array #@sql defined
|
417
|
+
txt << line.join("\t")
|
418
|
+
when Hash #Sequel::Dataset
|
419
|
+
if first_line and @headline
|
420
|
+
txt << @dataset.columns.map{|x|x.to_s}.join("\t")
|
421
|
+
end
|
422
|
+
txt << build_csv_line(line, "\t")
|
423
|
+
else
|
424
|
+
bc3_exit(:dataset_error , "Type of dataset is undefined #{line.inspect}")
|
425
|
+
end #case line
|
426
|
+
else
|
427
|
+
$log.error("Data conversion routine #{@show_data} not found") if $log.error?
|
428
|
+
txt << "Data conversion routine #{@show_data} not found"
|
429
|
+
end
|
430
|
+
first_line = false
|
431
|
+
}
|
432
|
+
else#ok, maybe you only want to compare the scheme.
|
433
|
+
$log.info("No data output") if $log.debug?
|
434
|
+
end
|
435
|
+
txt
|
436
|
+
end #build_target
|
437
|
+
=begin rdoc
|
438
|
+
Build one character separated value-line.
|
439
|
+
=end
|
440
|
+
def build_csv_line(line, sep, dataset = @dataset)
|
441
|
+
vals = []
|
442
|
+
dataset.columns.each{|col|
|
443
|
+
if line[col].respond_to?(:include?) and line[col].include?(sep)
|
444
|
+
$log.warn("Data contain separator #{sep} (#{line[col]})") if $log.warn?
|
445
|
+
vals << "\"#{line[col]}\""
|
446
|
+
else
|
447
|
+
vals << line[col].to_s
|
448
|
+
end
|
449
|
+
|
450
|
+
}
|
451
|
+
vals.join(sep)
|
452
|
+
end
|
453
|
+
end #class DBconverter
|
454
|
+
end #module BC3
|
455
|
+
|
456
|
+
|
457
|
+
$log.outputters = Log4r::StdoutOutputter.new('log', :level => Log4r::WARN)
|
458
|
+
|
459
|
+
$log.info("Start %s V%s" % [__FILE__, BC3::DBconverter::VERSION]) if $log.info?
|
460
|
+
if $log.debug?
|
461
|
+
$log.debug(" Parameters: %s" % ARGV.inspect)
|
462
|
+
$log.debug(" We are in: %s" % Dir.pwd)
|
463
|
+
end
|
464
|
+
|
465
|
+
|
466
|
+
if ARGV.empty?
|
467
|
+
$log.error("Testcase ARGV empty")
|
468
|
+
#~ $log.outputters = Log4r::StdoutOutputter.new('log', :level => Log4r::DEBUG)
|
469
|
+
#~ conv = DBconverter.new('../unittest/test_sql.db4bc3', 'xx')
|
470
|
+
exit BC3::DBconverter::EXITCODES[:argv_wrong]
|
471
|
+
else
|
472
|
+
conv = BC3::DBconverter.new(*ARGV)
|
473
|
+
end unless defined? SKIP_AUTOSTART
|
data/changes.rd
CHANGED
@@ -8,12 +8,15 @@
|
|
8
8
|
* Changed BC3::Snapshot.newd -> BC3::Snapshot.new_filesystem (newd as alias)
|
9
9
|
* BC3::Snapshot.save saves yaml or bcss file
|
10
10
|
* bin/bc_merge.rb can handle yaml-files
|
11
|
+
|
11
12
|
==0.1.2 2011-02-20
|
12
13
|
* BC3::SnapshotParse: Support UTF for linux files
|
13
14
|
* BC3::Snapshot#utf: Added flag. Allows creation of Linux-UTF-snapshots.
|
14
15
|
* BC3::Snapshot: Support UTF for linux files (Snapshot uses UTF-8).
|
15
16
|
* BC3::Snapshot.uncompress: Added
|
16
17
|
|
18
|
+
==0.2.0 2011-04-08
|
19
|
+
* Add bc3_db_compare.rb
|
17
20
|
|
18
21
|
|
19
22
|
=Known problems/Todos
|
Binary file
|
Binary file
|
data/lib/bc3.rb
CHANGED
data/lib/bc3/time.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
=begin rdoc
|
2
1
|
bignum too big to convert into `unsigned long' (RangeError)
|
3
2
|
AD_EPOCH = 116_444_736_000_000_000
|
4
3
|
AD_MULTIPLIER = 10_000_000
|
5
4
|
# convert a Time object to AD's epoch
|
6
5
|
def time2ad()
|
7
6
|
(self.to_f * AD_MULTIPLIER) + AD_EPOCH
|
8
7
|
end
|
9
8
|
# convert from AD's time string to a Time object
|
10
9
|
def self.ad2time(time)
|
11
10
|
Time.at((time.to_f - AD_EPOCH) / AD_MULTIPLIER)
|
12
11
|
end
|
12
|
+
=begin rdoc
|
13
13
|
bignum too big to convert into `unsigned long' (RangeError)
|
14
14
|
AD_EPOCH = 116_444_736_000_000_000
|
15
15
|
AD_MULTIPLIER = 10_000_000
|
16
16
|
# convert a Time object to AD's epoch
|
17
17
|
def time2ad()
|
18
18
|
(self.to_f * AD_MULTIPLIER) + AD_EPOCH
|
19
19
|
end
|
20
20
|
# convert from AD's time string to a Time object
|
21
21
|
def self.ad2time(time)
|
22
22
|
Time.at((time.to_f - AD_EPOCH) / AD_MULTIPLIER)
|
23
23
|
end
|
@@ -0,0 +1,310 @@
|
|
1
|
+
=begin
|
2
|
+
This tests need sqlite3.
|
3
|
+
|
4
|
+
Details see
|
5
|
+
* http://www.sqlite.org/
|
6
|
+
* http://rubygems.org/gems/sqlite3
|
7
|
+
|
8
|
+
=end
|
9
|
+
gem 'test-unit'
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
$:.unshift('../bin')
|
13
|
+
SKIP_AUTOSTART = true #avoid ARGV-interpretation
|
14
|
+
require 'bc3_db_compare.rb'
|
15
|
+
|
16
|
+
#~ $log.level = Log4r::FATAL
|
17
|
+
TESTDB = 'test.db'
|
18
|
+
TESTDATA = [
|
19
|
+
{:lc2=>"af", :lc3=>"afr", :name_en=>"Afrikaans", :iso8859=>"ISO8859_1"},
|
20
|
+
{:lc2=>"ar", :lc3=>"ara", :name_en=>"Arabic", :iso8859=>nil},
|
21
|
+
{:lc2=>"bg", :lc3=>"bul", :name_en=>"Bulgarian", :iso8859=>"ISO8859_5"},
|
22
|
+
{:lc2=>"br", :lc3=>"por", :name_en=>"Portuguese braz.", :iso8859=>"ISO8859_1"},
|
23
|
+
{:lc2=>"de", :lc3=>"deu", :name_en=>"German", :iso8859=>"ISO8859_1"},
|
24
|
+
{:lc2=>"en", :lc3=>"eng", :name_en=>"English", :iso8859=>"ISO8859_1"},
|
25
|
+
{:lc2=>"es", :lc3=>"spa", :name_en=>"Spanish", :iso8859=>"ISO8859_1"},
|
26
|
+
{:lc2=>"et", :lc3=>"est", :name_en=>"Estonian", :iso8859=>"ISO8859_4"},
|
27
|
+
{:lc2=>"fi", :lc3=>"fin", :name_en=>"Finnish", :iso8859=>"ISO8859_1"},
|
28
|
+
{:lc2=>"pt", :lc3=>"por", :name_en=>"Portuguese", :iso8859=>"ISO8859_1"},
|
29
|
+
]
|
30
|
+
def build_testdata()
|
31
|
+
if ! File.exist?(TESTDB)
|
32
|
+
db = Sequel.sqlite(TESTDB)
|
33
|
+
db.create_table :languages do
|
34
|
+
primary_key :key
|
35
|
+
column :lc2, :string, :max_chars => 2
|
36
|
+
column :lc3, :string
|
37
|
+
column :name_en, :string
|
38
|
+
column :iso8859, :string
|
39
|
+
end
|
40
|
+
TESTDATA.each{|ds| db[:languages].insert(ds)}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
class BC3::DBconverter
|
46
|
+
#Build excetions as replacement for exit
|
47
|
+
EXCEPTIONS = {}
|
48
|
+
EXITCODES.each{|key, value|
|
49
|
+
eval <<-xx
|
50
|
+
EXCEPTIONS[#{key.inspect}] = Error_#{key} = Class.new(SystemExit)
|
51
|
+
xx
|
52
|
+
}
|
53
|
+
=begin rdoc
|
54
|
+
For unit test, this must be redefined,
|
55
|
+
else the test is finished.
|
56
|
+
=end
|
57
|
+
def bc3_exit( exitcode, description)
|
58
|
+
#~ $log.error(description) if $log.error?
|
59
|
+
#~ exit EXITCODES[exitcode]
|
60
|
+
raise EXCEPTIONS[exitcode], description
|
61
|
+
end
|
62
|
+
end #class BC3::DBconverter
|
63
|
+
|
64
|
+
class Test_configuration < Test::Unit::TestCase
|
65
|
+
=begin
|
66
|
+
Build some test data with sqlite
|
67
|
+
=end
|
68
|
+
def self.startup
|
69
|
+
build_testdata
|
70
|
+
end #self.startup
|
71
|
+
def setup()
|
72
|
+
@bc3 = BC3::DBconverter.new()
|
73
|
+
end
|
74
|
+
def test_configuration()
|
75
|
+
#Invalid yaml-data
|
76
|
+
assert_raise(BC3::DBconverter::Error_configuration_error){
|
77
|
+
@bc3.analyse_configuration_file("- 1\na")
|
78
|
+
}
|
79
|
+
|
80
|
+
assert_raise(BC3::DBconverter::Error_configuration_incomplete){
|
81
|
+
@bc3.analyse_configuration_file('xx')
|
82
|
+
}
|
83
|
+
#adapter, tab and db must be defined.
|
84
|
+
assert_raise(BC3::DBconverter::Error_configuration_incomplete){
|
85
|
+
@bc3.analyse_configuration_file(<<-config
|
86
|
+
adapter: undefined_adapter
|
87
|
+
db: undefined_db
|
88
|
+
config
|
89
|
+
)
|
90
|
+
}
|
91
|
+
assert_raise(BC3::DBconverter::Error_adapter_unknown){
|
92
|
+
@bc3.analyse_configuration_file(<<-config
|
93
|
+
adapter: undefined_adapter
|
94
|
+
db: undefined_db
|
95
|
+
tab: xx
|
96
|
+
config
|
97
|
+
)
|
98
|
+
}
|
99
|
+
end #test_configuration()
|
100
|
+
def test_configuration_ado()
|
101
|
+
assert_nothing_raised(){
|
102
|
+
@bc3.analyse_configuration_file(<<-config
|
103
|
+
adapter: ado
|
104
|
+
db: undefined_db
|
105
|
+
tab: xx
|
106
|
+
server: xx
|
107
|
+
config
|
108
|
+
)
|
109
|
+
}
|
110
|
+
assert_raise(BC3::DBconverter::Error_db_connection_error, 'Server missing'){
|
111
|
+
@bc3.analyse_configuration_file(<<-config
|
112
|
+
adapter: ado
|
113
|
+
db: undefined_db
|
114
|
+
tab: xx
|
115
|
+
config
|
116
|
+
)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_configuration_sqlite()
|
121
|
+
assert_nothing_raised(){
|
122
|
+
@bc3.analyse_configuration_file(<<-config
|
123
|
+
adapter: sqlite
|
124
|
+
db: #{TESTDB}
|
125
|
+
tab: languages
|
126
|
+
config
|
127
|
+
)
|
128
|
+
}
|
129
|
+
assert_raise(BC3::DBconverter::Error_configuration_incomplete, 'filename missing'){
|
130
|
+
@bc3.analyse_configuration_file(<<-config
|
131
|
+
adapter: sqlite
|
132
|
+
tab: xx
|
133
|
+
config
|
134
|
+
)
|
135
|
+
}
|
136
|
+
assert_raise(BC3::DBconverter::Error_db_connection_error, 'filename wrong'){
|
137
|
+
@bc3.analyse_configuration_file(<<-config
|
138
|
+
adapter: sqlite
|
139
|
+
db: undefined_db
|
140
|
+
tab: xx
|
141
|
+
config
|
142
|
+
)
|
143
|
+
}
|
144
|
+
assert_nothing_raised(){
|
145
|
+
@bc3.analyse_configuration_file(<<-config
|
146
|
+
adapter: sqlite
|
147
|
+
db: #{TESTDB}
|
148
|
+
tab: xx_languages
|
149
|
+
config
|
150
|
+
)
|
151
|
+
}
|
152
|
+
#Test table xx_languages
|
153
|
+
assert_raise(BC3::DBconverter::Error_db_table_missing, 'table not defined'){
|
154
|
+
@bc3.prepare_dataset()
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class Test_testdata_sqlite < Test::Unit::TestCase
|
160
|
+
def self.startup
|
161
|
+
build_testdata
|
162
|
+
end #self.startup
|
163
|
+
def setup()
|
164
|
+
@bc3 = BC3::DBconverter.new()
|
165
|
+
@bc3.analyse_configuration_file(<<-config
|
166
|
+
adapter: sqlite
|
167
|
+
db: #{TESTDB}
|
168
|
+
tab: languages
|
169
|
+
config
|
170
|
+
)
|
171
|
+
end
|
172
|
+
def test_dataset()
|
173
|
+
assert_instance_of(Sequel::SQLite::Dataset, @bc3.prepare_dataset())
|
174
|
+
assert_equal("SELECT * FROM `languages`", @bc3.prepare_dataset.sql)
|
175
|
+
assert_equal([], @bc3.build_target())
|
176
|
+
@bc3.header = true
|
177
|
+
assert_equal(["Check table test.db.languages"], @bc3.build_target())
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Test_testdata_sqlite_inspect < Test::Unit::TestCase
|
182
|
+
def self.startup
|
183
|
+
build_testdata
|
184
|
+
end #self.startup
|
185
|
+
def setup()
|
186
|
+
@bc3 = BC3::DBconverter.new()
|
187
|
+
@bc3.analyse_configuration_file(<<-config
|
188
|
+
adapter: sqlite
|
189
|
+
db: #{TESTDB}
|
190
|
+
tab: languages
|
191
|
+
data: inspect
|
192
|
+
config
|
193
|
+
)
|
194
|
+
end
|
195
|
+
def test_dataset()
|
196
|
+
assert_instance_of(Sequel::SQLite::Dataset, @bc3.prepare_dataset())
|
197
|
+
assert_equal("SELECT * FROM `languages`", @bc3.prepare_dataset.sql)
|
198
|
+
assert_equal("{:key=>1, :lc2=>\"af\", :lc3=>\"afr\", :name_en=>\"Afrikaans\", :iso8859=>\"ISO8859_1\"}\n{:key=>2, :lc2=>\"ar\", :lc3=>\"ara\", :name_en=>\"Arabic\", :iso8859=>nil}\n{:key=>3, :lc2=>\"bg\", :lc3=>\"bul\", :name_en=>\"Bulgarian\", :iso8859=>\"ISO8859_5\"}\n{:key=>4, :lc2=>\"br\", :lc3=>\"por\", :name_en=>\"Portuguese braz.\", :iso8859=>\"ISO8859_1\"}\n{:key=>5, :lc2=>\"de\", :lc3=>\"deu\", :name_en=>\"German\", :iso8859=>\"ISO8859_1\"}\n{:key=>6, :lc2=>\"en\", :lc3=>\"eng\", :name_en=>\"English\", :iso8859=>\"ISO8859_1\"}\n{:key=>7, :lc2=>\"es\", :lc3=>\"spa\", :name_en=>\"Spanish\", :iso8859=>\"ISO8859_1\"}\n{:key=>8, :lc2=>\"et\", :lc3=>\"est\", :name_en=>\"Estonian\", :iso8859=>\"ISO8859_4\"}\n{:key=>9, :lc2=>\"fi\", :lc3=>\"fin\", :name_en=>\"Finnish\", :iso8859=>\"ISO8859_1\"}\n{:key=>10, :lc2=>\"pt\", :lc3=>\"por\", :name_en=>\"Portuguese\", :iso8859=>\"ISO8859_1\"}",
|
199
|
+
@bc3.build_target.join("\n")
|
200
|
+
)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class Test_testdata_sqlite_csv_tab < Test::Unit::TestCase
|
205
|
+
def self.startup
|
206
|
+
build_testdata
|
207
|
+
end #self.startup
|
208
|
+
def setup()
|
209
|
+
@bc3 = BC3::DBconverter.new()
|
210
|
+
@bc3.analyse_configuration_file(<<-config
|
211
|
+
adapter: sqlite
|
212
|
+
db: #{TESTDB}
|
213
|
+
tab: languages
|
214
|
+
data: csv_tab
|
215
|
+
config
|
216
|
+
)
|
217
|
+
end
|
218
|
+
def test_dataset()
|
219
|
+
assert_instance_of(Sequel::SQLite::Dataset, @bc3.prepare_dataset())
|
220
|
+
assert_equal("SELECT * FROM `languages`", @bc3.prepare_dataset.sql)
|
221
|
+
assert_equal(%{1 af afr Afrikaans ISO8859_1
|
222
|
+
2 ar ara Arabic
|
223
|
+
3 bg bul Bulgarian ISO8859_5
|
224
|
+
4 br por Portuguese braz. ISO8859_1
|
225
|
+
5 de deu German ISO8859_1
|
226
|
+
6 en eng English ISO8859_1
|
227
|
+
7 es spa Spanish ISO8859_1
|
228
|
+
8 et est Estonian ISO8859_4
|
229
|
+
9 fi fin Finnish ISO8859_1
|
230
|
+
10 pt por Portuguese ISO8859_1},
|
231
|
+
@bc3.build_target.join("\n")
|
232
|
+
)
|
233
|
+
end
|
234
|
+
def test_dataset_headline()
|
235
|
+
@bc3.headline = true
|
236
|
+
assert_equal("SELECT * FROM `languages`", @bc3.prepare_dataset.sql)
|
237
|
+
assert_equal(%{key lc2 lc3 name_en iso8859
|
238
|
+
1 af afr Afrikaans ISO8859_1
|
239
|
+
2 ar ara Arabic
|
240
|
+
3 bg bul Bulgarian ISO8859_5
|
241
|
+
4 br por Portuguese braz. ISO8859_1
|
242
|
+
5 de deu German ISO8859_1
|
243
|
+
6 en eng English ISO8859_1
|
244
|
+
7 es spa Spanish ISO8859_1
|
245
|
+
8 et est Estonian ISO8859_4
|
246
|
+
9 fi fin Finnish ISO8859_1
|
247
|
+
10 pt por Portuguese ISO8859_1},
|
248
|
+
@bc3.build_target.join("\n")
|
249
|
+
)
|
250
|
+
end
|
251
|
+
def test_dataset_filter()
|
252
|
+
@bc3.filter = {:key => [1,2] }
|
253
|
+
assert_equal("SELECT * FROM `languages` WHERE (`key` IN (1, 2))", @bc3.prepare_dataset.sql)
|
254
|
+
assert_equal(%{1 af afr Afrikaans ISO8859_1
|
255
|
+
2 ar ara Arabic\t},
|
256
|
+
@bc3.build_target.join("\n")
|
257
|
+
)
|
258
|
+
@bc3.filter = nil #reset filter
|
259
|
+
end
|
260
|
+
def test_dataset_select()
|
261
|
+
@bc3.select = [:lc2, :lc3]
|
262
|
+
assert_equal("SELECT `lc2`, `lc3` FROM `languages`", @bc3.prepare_dataset.sql)
|
263
|
+
assert_equal(%{af afr
|
264
|
+
ar ara
|
265
|
+
bg bul
|
266
|
+
br por
|
267
|
+
de deu
|
268
|
+
en eng
|
269
|
+
es spa
|
270
|
+
et est
|
271
|
+
fi fin
|
272
|
+
pt por},
|
273
|
+
@bc3.build_target.join("\n")
|
274
|
+
)
|
275
|
+
@bc3.select = nil #reset filter
|
276
|
+
end
|
277
|
+
def test_dataset_order()
|
278
|
+
@bc3.order = [:name_en]
|
279
|
+
assert_equal("SELECT * FROM `languages` ORDER BY (`name_en`)", @bc3.prepare_dataset.sql)
|
280
|
+
#~ puts @bc3.build_target.join("\n")
|
281
|
+
assert_equal(%{1 af afr Afrikaans ISO8859_1
|
282
|
+
2 ar ara Arabic
|
283
|
+
3 bg bul Bulgarian ISO8859_5
|
284
|
+
6 en eng English ISO8859_1
|
285
|
+
8 et est Estonian ISO8859_4
|
286
|
+
9 fi fin Finnish ISO8859_1
|
287
|
+
5 de deu German ISO8859_1
|
288
|
+
10 pt por Portuguese ISO8859_1
|
289
|
+
4 br por Portuguese braz. ISO8859_1
|
290
|
+
7 es spa Spanish ISO8859_1},
|
291
|
+
@bc3.build_target.join("\n")
|
292
|
+
)
|
293
|
+
@bc3.order = nil #reset filter
|
294
|
+
end
|
295
|
+
def test_dataset_sql()
|
296
|
+
@bc3.sql = "SELECT * FROM `languages` WHERE (`key` IN (1, 2))"
|
297
|
+
#exception before @bc3.prepare_dataset
|
298
|
+
assert_raise(BC3::DBconverter::Error_db_connection_error){@bc3.build_target}
|
299
|
+
assert_instance_of(SQLite3::ResultSet, @bc3.prepare_dataset)
|
300
|
+
assert_equal(%w{key lc2 lc3 name_en iso8859}, @bc3.prepare_dataset.columns)
|
301
|
+
|
302
|
+
assert_equal(%{1 af afr Afrikaans ISO8859_1
|
303
|
+
2 ar ara Arabic\t},
|
304
|
+
@bc3.build_target.join("\n")
|
305
|
+
)
|
306
|
+
@bc3.sql = nil #reset sql
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
__END__
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bc3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 1
|
9
8
|
- 2
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Knut Lickert
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-04-08 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -32,6 +32,34 @@ dependencies:
|
|
32
32
|
version: "0"
|
33
33
|
type: :runtime
|
34
34
|
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: sequel
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: sqlite3
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
35
63
|
description: |
|
36
64
|
Build and analyse Beyond Compare (BC3) Snapshot-Files.
|
37
65
|
This gem allows flexible definition of BC3-snapshots and
|
@@ -43,6 +71,7 @@ email: knut@lickert.net
|
|
43
71
|
executables:
|
44
72
|
- bc3_merge.rb
|
45
73
|
- bc3_search.rb
|
74
|
+
- bc3_db_compare.rb
|
46
75
|
extensions: []
|
47
76
|
|
48
77
|
extra_rdoc_files:
|
@@ -50,11 +79,13 @@ extra_rdoc_files:
|
|
50
79
|
- changes.rd
|
51
80
|
- bin/bc3_merge.rb
|
52
81
|
- bin/bc3_search.rb
|
82
|
+
- bin/bc3_db_compare.rb
|
53
83
|
files:
|
54
84
|
- changes.rd
|
55
85
|
- BCSS_Binary_Format.txt
|
56
86
|
- bin/bc3_merge.rb
|
57
87
|
- bin/bc3_search.rb
|
88
|
+
- bin/bc3_db_compare.rb
|
58
89
|
- lib/bc3.rb
|
59
90
|
- lib/bc3/file.rb
|
60
91
|
- lib/bc3/folder.rb
|
@@ -77,6 +108,11 @@ files:
|
|
77
108
|
- examples/scootersoftware/UnicodePath(Linux).bcss
|
78
109
|
- examples/scootersoftware/UnicodeFilenames(Win32).bcss
|
79
110
|
- examples/scootersoftware/UnicodePath(Win32).bcss
|
111
|
+
- examples_db_connect/BCSettings_db4bc3.bcpkg
|
112
|
+
- examples_db_connect/sqlite_test_1.db4bc3
|
113
|
+
- examples_db_connect/sqlite_test_2.db4bc3
|
114
|
+
- examples_db_connect/sqlite_test_sql.db4bc3
|
115
|
+
- examples_db_connect/test.db
|
80
116
|
- unittest/unittest_bc3_helper.rb
|
81
117
|
- unittest/unittest_bc3_time.rb
|
82
118
|
- unittest/unittest_bc3_file.rb
|
@@ -84,6 +120,7 @@ files:
|
|
84
120
|
- unittest/unittest_bc3_snapshot.rb
|
85
121
|
- unittest/unittest_bc3_merge.rb
|
86
122
|
- unittest/unittest_bc3_search.rb
|
123
|
+
- unittest/unittest_bc3_db_compare.rb
|
87
124
|
has_rdoc: true
|
88
125
|
homepage: http://gems.rubypla.net/bc3
|
89
126
|
licenses: []
|
@@ -114,6 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
151
|
version: "0"
|
115
152
|
requirements:
|
116
153
|
- Beyond Compare, see http://www.scootersoftware.com
|
154
|
+
- sequel for bc3_db_compare.rb
|
117
155
|
rubyforge_project:
|
118
156
|
rubygems_version: 1.3.7
|
119
157
|
signing_key:
|
@@ -127,3 +165,4 @@ test_files:
|
|
127
165
|
- unittest/unittest_bc3_snapshot.rb
|
128
166
|
- unittest/unittest_bc3_merge.rb
|
129
167
|
- unittest/unittest_bc3_search.rb
|
168
|
+
- unittest/unittest_bc3_db_compare.rb
|