bc3 0.1.2 → 0.2.0
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/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
|