rubyrep 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -0
- data/Manifest.txt +3 -0
- data/lib/rubyrep/configuration.rb +3 -0
- data/lib/rubyrep/connection_extenders/jdbc_extender.rb +8 -7
- data/lib/rubyrep/connection_extenders/postgresql_extender.rb +16 -0
- data/lib/rubyrep/replication_extenders/mysql_replication.rb +19 -19
- data/lib/rubyrep/replication_extenders/postgresql_replication.rb +10 -10
- data/lib/rubyrep/replication_helper.rb +4 -2
- data/lib/rubyrep/replication_initializer.rb +5 -2
- data/lib/rubyrep/replication_run.rb +7 -3
- data/lib/rubyrep/replicators/two_way_replicator.rb +8 -6
- data/lib/rubyrep/sync_helper.rb +4 -2
- data/lib/rubyrep/table_scan.rb +2 -2
- data/lib/rubyrep/version.rb +1 -1
- data/lib/rubyrep.rb +1 -0
- data/spec/dolphins.jpg +0 -0
- data/spec/replication_helper_spec.rb +7 -2
- data/spec/replication_run_spec.rb +3 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/strange_name_support_spec.rb +90 -0
- data/spec/sync_helper_spec.rb +7 -2
- data/tasks/database.rake +20 -6
- data/tasks/website.rake +19 -0
- data.tar.gz.sig +0 -0
- metadata +5 -2
- metadata.gz.sig +0 -0
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 1.0.3 ???
|
2
|
+
|
3
|
+
* Minor enhancements:
|
4
|
+
* Added support (and according tests) for non-standard table / column names
|
5
|
+
* Verified compatibility with jruby 1.3.0
|
6
|
+
|
7
|
+
* Bug fixes:
|
8
|
+
* Also big exception messages should be accommodated in event log table.
|
9
|
+
|
1
10
|
== 1.0.2 2009-06-13
|
2
11
|
|
3
12
|
* 3 minor enhancements:
|
data/Manifest.txt
CHANGED
@@ -84,6 +84,7 @@ spec/database_rake_spec.rb
|
|
84
84
|
spec/db_specific_connection_extenders_spec.rb
|
85
85
|
spec/db_specific_replication_extenders_spec.rb
|
86
86
|
spec/direct_table_scan_spec.rb
|
87
|
+
spec/dolphins.jpg
|
87
88
|
spec/generate_runner_spec.rb
|
88
89
|
spec/initializer_spec.rb
|
89
90
|
spec/logged_change_spec.rb
|
@@ -113,6 +114,7 @@ spec/scan_summary_reporter_spec.rb
|
|
113
114
|
spec/session_spec.rb
|
114
115
|
spec/spec.opts
|
115
116
|
spec/spec_helper.rb
|
117
|
+
spec/strange_name_support_spec.rb
|
116
118
|
spec/sync_helper_spec.rb
|
117
119
|
spec/sync_runner_spec.rb
|
118
120
|
spec/syncers_spec.rb
|
@@ -135,3 +137,4 @@ tasks/rspec.rake
|
|
135
137
|
tasks/rubyrep.tailor
|
136
138
|
tasks/stats.rake
|
137
139
|
tasks/task_helper.rb
|
140
|
+
tasks/website.rake
|
@@ -37,6 +37,7 @@ module RR
|
|
37
37
|
:right_sequence_offset => 1,
|
38
38
|
:replication_interval => 1,
|
39
39
|
:auto_key_limit => 0,
|
40
|
+
:database_connection_timeout => 5,
|
40
41
|
|
41
42
|
:rep_prefix => 'rr',
|
42
43
|
:key_sep => '|',
|
@@ -109,6 +110,8 @@ module RR
|
|
109
110
|
# E. g. with a +sequence_increment+ of 2, an offset of 0 will produce even,
|
110
111
|
# an offset of 1 will produce odd numbers.
|
111
112
|
# * :+replication_interval+: time in seconds between replication runs
|
113
|
+
# * :+database_connection_timeout+:
|
114
|
+
# Time in seconds after which database connections time out.
|
112
115
|
attr_reader :options
|
113
116
|
|
114
117
|
# Merges the specified +options+ hash into the existing options
|
@@ -17,7 +17,8 @@ module RR
|
|
17
17
|
|
18
18
|
# Returns an ordered list of primary key column names of the given table
|
19
19
|
def primary_key_names(table)
|
20
|
-
if
|
20
|
+
if tables.grep(/^#{table}$/i).empty?
|
21
|
+
# Note: Cannot use tables.include? as returned tables are made lowercase under JRuby MySQL
|
21
22
|
raise "table '#{table}' does not exist"
|
22
23
|
end
|
23
24
|
columns = []
|
@@ -81,12 +82,12 @@ module RR
|
|
81
82
|
# - ::regclass is a function that gives the id for a table name
|
82
83
|
def column_definitions(table_name) #:nodoc:
|
83
84
|
rows = select_all(<<-end_sql)
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
SELECT a.attname as name, format_type(a.atttypid, a.atttypmod) as type, d.adsrc as default, a.attnotnull as notnull
|
86
|
+
FROM pg_attribute a LEFT JOIN pg_attrdef d
|
87
|
+
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
88
|
+
WHERE a.attrelid = (SELECT oid FROM pg_class WHERE relname = '#{table_name}')
|
89
|
+
AND a.attnum > 0 AND NOT a.attisdropped
|
90
|
+
ORDER BY a.attnum
|
90
91
|
end_sql
|
91
92
|
|
92
93
|
rows.map do |row|
|
@@ -153,6 +153,22 @@ module RR
|
|
153
153
|
end
|
154
154
|
result
|
155
155
|
end
|
156
|
+
|
157
|
+
# *** Monkey patch***
|
158
|
+
# Returns the list of a table's column names, data types, and default values.
|
159
|
+
# This overwrites the according ActiveRecord::PostgreSQLAdapter method
|
160
|
+
# to work with tables containing a dot (".").
|
161
|
+
def column_definitions(table_name) #:nodoc:
|
162
|
+
query <<-end_sql
|
163
|
+
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
|
164
|
+
FROM pg_attribute a LEFT JOIN pg_attrdef d
|
165
|
+
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
166
|
+
WHERE a.attrelid = (SELECT oid FROM pg_class WHERE relname = '#{table_name}')
|
167
|
+
AND a.attnum > 0 AND NOT a.attisdropped
|
168
|
+
ORDER BY a.attnum
|
169
|
+
end_sql
|
170
|
+
end
|
171
|
+
|
156
172
|
end
|
157
173
|
end
|
158
174
|
end
|
@@ -9,7 +9,7 @@ module RR
|
|
9
9
|
# See #create_replication_trigger for a descriptions of the +params+ hash.
|
10
10
|
def create_or_replace_replication_trigger_function(params)
|
11
11
|
execute(<<-end_sql)
|
12
|
-
DROP PROCEDURE IF EXISTS
|
12
|
+
DROP PROCEDURE IF EXISTS `#{params[:trigger_name]}`;
|
13
13
|
end_sql
|
14
14
|
|
15
15
|
activity_check = ""
|
@@ -24,7 +24,7 @@ module RR
|
|
24
24
|
end
|
25
25
|
|
26
26
|
execute(<<-end_sql)
|
27
|
-
CREATE PROCEDURE
|
27
|
+
CREATE PROCEDURE `#{params[:trigger_name]}`(change_key varchar(2000), change_new_key varchar(2000), change_type varchar(1))
|
28
28
|
p: BEGIN
|
29
29
|
#{activity_check}
|
30
30
|
INSERT INTO #{params[:log_table]}(change_table, change_key, change_new_key, change_type, change_time)
|
@@ -59,7 +59,7 @@ module RR
|
|
59
59
|
|
60
60
|
%w(insert update delete).each do |action|
|
61
61
|
execute(<<-end_sql)
|
62
|
-
DROP TRIGGER IF EXISTS
|
62
|
+
DROP TRIGGER IF EXISTS `#{params[:trigger_name]}_#{action}`;
|
63
63
|
end_sql
|
64
64
|
|
65
65
|
# The created triggers can handle the case where the trigger procedure
|
@@ -73,13 +73,13 @@ module RR
|
|
73
73
|
|
74
74
|
trigger_var = action == 'delete' ? 'OLD' : 'NEW'
|
75
75
|
if action == 'update'
|
76
|
-
call_statement = "CALL
|
76
|
+
call_statement = "CALL `#{params[:trigger_name]}`(#{key_clause('OLD', params)}, #{key_clause('NEW', params)}, '#{action[0,1].upcase}');"
|
77
77
|
else
|
78
|
-
call_statement = "CALL
|
78
|
+
call_statement = "CALL `#{params[:trigger_name]}`(#{key_clause(trigger_var, params)}, null, '#{action[0,1].upcase}');"
|
79
79
|
end
|
80
80
|
execute(<<-end_sql)
|
81
|
-
CREATE TRIGGER
|
82
|
-
AFTER #{action} ON
|
81
|
+
CREATE TRIGGER `#{params[:trigger_name]}_#{action}`
|
82
|
+
AFTER #{action} ON `#{params[:table]}` FOR EACH ROW BEGIN
|
83
83
|
DECLARE number_attempts INT DEFAULT 0;
|
84
84
|
DECLARE failed INT;
|
85
85
|
DECLARE CONTINUE HANDLER FOR 1305 BEGIN
|
@@ -102,9 +102,9 @@ module RR
|
|
102
102
|
# * +table_name+: name of the table for which the trigger exists
|
103
103
|
def drop_replication_trigger(trigger_name, table_name)
|
104
104
|
%w(insert update delete).each do |action|
|
105
|
-
execute "DROP TRIGGER
|
105
|
+
execute "DROP TRIGGER `#{trigger_name}_#{action}`;"
|
106
106
|
end
|
107
|
-
execute "DROP PROCEDURE
|
107
|
+
execute "DROP PROCEDURE `#{trigger_name}`;"
|
108
108
|
end
|
109
109
|
|
110
110
|
# Returns +true+ if the named trigger exists for the named table.
|
@@ -129,7 +129,7 @@ module RR
|
|
129
129
|
def sequence_values(rep_prefix, table_name)
|
130
130
|
# check if the table has an auto_increment column, return if not
|
131
131
|
sequence_row = select_one(<<-end_sql)
|
132
|
-
show columns from
|
132
|
+
show columns from `#{table_name}` where extra = 'auto_increment'
|
133
133
|
end_sql
|
134
134
|
return {} unless sequence_row
|
135
135
|
column_name = sequence_row['Field']
|
@@ -154,7 +154,7 @@ module RR
|
|
154
154
|
sequence_row = select_one("select current_value, increment, offset from #{sequence_table_name} where name = '#{table_name}'")
|
155
155
|
if sequence_row == nil
|
156
156
|
current_max = select_one(<<-end_sql)['current_max'].to_i
|
157
|
-
select max(
|
157
|
+
select max(`#{column_name}`) as current_max from `#{table_name}`
|
158
158
|
end_sql
|
159
159
|
return {column_name => {
|
160
160
|
:increment => 1,
|
@@ -177,9 +177,9 @@ module RR
|
|
177
177
|
# * +increment+: increment of the sequence
|
178
178
|
# * +offset+: offset
|
179
179
|
# * +left_sequence_values+:
|
180
|
-
# hash as returned by #
|
180
|
+
# hash as returned by #sequence_values for the left database
|
181
181
|
# * +right_sequence_values+:
|
182
|
-
# hash as returned by #
|
182
|
+
# hash as returned by #sequence_values for the right database
|
183
183
|
# * +adjustment_buffer+:
|
184
184
|
# the "gap" that is created during sequence update to avoid concurrency problems
|
185
185
|
# E. g. an increment of 2 and offset of 1 will lead to generation of odd
|
@@ -207,17 +207,17 @@ module RR
|
|
207
207
|
end_sql
|
208
208
|
trigger_name = "#{rep_prefix}_#{table_name}_sequence"
|
209
209
|
execute(<<-end_sql)
|
210
|
-
DROP TRIGGER IF EXISTS
|
210
|
+
DROP TRIGGER IF EXISTS `#{trigger_name}`;
|
211
211
|
end_sql
|
212
212
|
|
213
213
|
execute(<<-end_sql)
|
214
|
-
CREATE TRIGGER
|
215
|
-
BEFORE INSERT ON
|
216
|
-
IF NEW
|
214
|
+
CREATE TRIGGER `#{trigger_name}`
|
215
|
+
BEFORE INSERT ON `#{table_name}` FOR EACH ROW BEGIN
|
216
|
+
IF NEW.`#{column_name}` = 0 THEN
|
217
217
|
UPDATE #{sequence_table_name}
|
218
218
|
SET current_value = LAST_INSERT_ID(current_value + increment)
|
219
219
|
WHERE name = '#{table_name}';
|
220
|
-
SET NEW
|
220
|
+
SET NEW.`#{column_name}` = LAST_INSERT_ID();
|
221
221
|
END IF;
|
222
222
|
END;
|
223
223
|
end_sql
|
@@ -256,7 +256,7 @@ module RR
|
|
256
256
|
and trigger_name = '#{trigger_name}'
|
257
257
|
end_sql
|
258
258
|
if trigger_row
|
259
|
-
execute "DROP TRIGGER
|
259
|
+
execute "DROP TRIGGER `#{trigger_name}`"
|
260
260
|
execute "delete from #{sequence_table_name} where name = '#{table_name}'"
|
261
261
|
unless select_one("select * from #{sequence_table_name}")
|
262
262
|
# no more sequences left --> delete sequence table
|
@@ -35,7 +35,7 @@ module RR
|
|
35
35
|
|
36
36
|
# now create the trigger
|
37
37
|
execute(<<-end_sql)
|
38
|
-
CREATE OR REPLACE FUNCTION #{params[:trigger_name]}() RETURNS TRIGGER AS $change_trigger$
|
38
|
+
CREATE OR REPLACE FUNCTION "#{params[:trigger_name]}"() RETURNS TRIGGER AS $change_trigger$
|
39
39
|
BEGIN
|
40
40
|
#{activity_check}
|
41
41
|
IF (TG_OP = 'DELETE') THEN
|
@@ -69,9 +69,9 @@ module RR
|
|
69
69
|
create_or_replace_replication_trigger_function params
|
70
70
|
|
71
71
|
execute(<<-end_sql)
|
72
|
-
CREATE TRIGGER #{params[:trigger_name]}
|
73
|
-
AFTER INSERT OR UPDATE OR DELETE ON #{params[:table]}
|
74
|
-
FOR EACH ROW EXECUTE PROCEDURE #{params[:trigger_name]}();
|
72
|
+
CREATE TRIGGER "#{params[:trigger_name]}"
|
73
|
+
AFTER INSERT OR UPDATE OR DELETE ON "#{params[:table]}"
|
74
|
+
FOR EACH ROW EXECUTE PROCEDURE "#{params[:trigger_name]}"();
|
75
75
|
end_sql
|
76
76
|
end
|
77
77
|
|
@@ -79,8 +79,8 @@ module RR
|
|
79
79
|
# * +trigger_name+: name of the trigger
|
80
80
|
# * +table_name+: name of the table for which the trigger exists
|
81
81
|
def drop_replication_trigger(trigger_name, table_name)
|
82
|
-
execute "DROP TRIGGER #{trigger_name} ON #{table_name};"
|
83
|
-
execute "DROP FUNCTION #{trigger_name}();"
|
82
|
+
execute "DROP TRIGGER \"#{trigger_name}\" ON \"#{table_name}\";"
|
83
|
+
execute "DROP FUNCTION \"#{trigger_name}\"();"
|
84
84
|
end
|
85
85
|
|
86
86
|
# Returns +true+ if the named trigger exists for the named table.
|
@@ -117,7 +117,7 @@ module RR
|
|
117
117
|
and t.relname = '#{table_name}'
|
118
118
|
end_sql
|
119
119
|
sequence_names.each do |sequence_name|
|
120
|
-
row = select_one("select last_value, increment_by from #{sequence_name}")
|
120
|
+
row = select_one("select last_value, increment_by from \"#{sequence_name}\"")
|
121
121
|
result[sequence_name] = {
|
122
122
|
:increment => row['increment_by'].to_i,
|
123
123
|
:value => row['last_value'].to_i
|
@@ -133,9 +133,9 @@ module RR
|
|
133
133
|
# * +increment+: increment of the sequence
|
134
134
|
# * +offset+: offset
|
135
135
|
# * +left_sequence_values+:
|
136
|
-
# hash as returned by #
|
136
|
+
# hash as returned by #sequence_values for the left database
|
137
137
|
# * +right_sequence_values+:
|
138
|
-
# hash as returned by #
|
138
|
+
# hash as returned by #sequence_values for the right database
|
139
139
|
# * +adjustment_buffer+:
|
140
140
|
# the "gap" that is created during sequence update to avoid concurrency problems
|
141
141
|
# E. g. an increment of 2 and offset of 1 will lead to generation of odd
|
@@ -144,7 +144,7 @@ module RR
|
|
144
144
|
rep_prefix, table_name, increment, offset,
|
145
145
|
left_sequence_values, right_sequence_values, adjustment_buffer)
|
146
146
|
left_sequence_values.each do |sequence_name, left_current_value|
|
147
|
-
row = select_one("select last_value, increment_by from #{sequence_name}")
|
147
|
+
row = select_one("select last_value, increment_by from \"#{sequence_name}\"")
|
148
148
|
current_increment = row['increment_by'].to_i
|
149
149
|
current_value = row['last_value'].to_i
|
150
150
|
unless current_increment == increment and current_value % increment == offset
|
@@ -5,6 +5,8 @@ module RR
|
|
5
5
|
# for third party replicators.
|
6
6
|
class ReplicationHelper
|
7
7
|
|
8
|
+
include LogHelper
|
9
|
+
|
8
10
|
# The current +ReplicationRun+ instance
|
9
11
|
attr_accessor :replication_run
|
10
12
|
|
@@ -74,7 +76,7 @@ module RR
|
|
74
76
|
end
|
75
77
|
key = key_parts.join(', ')
|
76
78
|
end
|
77
|
-
rep_details =
|
79
|
+
rep_outcome, rep_details = fit_description_columns(outcome, details)
|
78
80
|
diff_dump = diff.to_yaml[0...ReplicationInitializer::DIFF_DUMP_SIZE]
|
79
81
|
|
80
82
|
session.left.insert_record "#{options[:rep_prefix]}_logged_events", {
|
@@ -84,7 +86,7 @@ module RR
|
|
84
86
|
:change_key => key,
|
85
87
|
:left_change_type => (diff.changes[:left] ? diff.changes[:left].type.to_s : nil),
|
86
88
|
:right_change_type => (diff.changes[:right] ? diff.changes[:right].type.to_s : nil),
|
87
|
-
:description =>
|
89
|
+
:description => rep_outcome,
|
88
90
|
:long_description => rep_details,
|
89
91
|
:event_time => Time.now,
|
90
92
|
:diff_dump => diff_dump
|
@@ -124,7 +124,10 @@ module RR
|
|
124
124
|
# Size of the replication log column diff_dump
|
125
125
|
DIFF_DUMP_SIZE = 2000
|
126
126
|
|
127
|
-
# Size
|
127
|
+
# Size fo the event log column 'description'
|
128
|
+
DESCRIPTION_SIZE = 255
|
129
|
+
|
130
|
+
# Size of the event log column 'long_description'
|
128
131
|
LONG_DESCRIPTION_SIZE = 1000
|
129
132
|
|
130
133
|
# Ensures that create_table and related statements don't print notices to
|
@@ -153,7 +156,7 @@ module RR
|
|
153
156
|
t.column :change_key, :string
|
154
157
|
t.column :left_change_type, :string
|
155
158
|
t.column :right_change_type, :string
|
156
|
-
t.column :description, :string
|
159
|
+
t.column :description, :string, :limit => DESCRIPTION_SIZE
|
157
160
|
t.column :long_description, :string, :limit => LONG_DESCRIPTION_SIZE
|
158
161
|
t.column :event_time, :timestamp
|
159
162
|
t.column :diff_dump, :string, :limit => DIFF_DUMP_SIZE
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
1
3
|
module RR
|
2
4
|
|
3
5
|
# Executes a single replication run
|
@@ -20,9 +22,11 @@ module RR
|
|
20
22
|
# Executes the replication run.
|
21
23
|
def run
|
22
24
|
return unless [:left, :right].any? do |database|
|
23
|
-
session.
|
24
|
-
|
25
|
-
|
25
|
+
Timeout::timeout(session.configuration.options[:database_connection_timeout]) do
|
26
|
+
session.send(database).select_one(
|
27
|
+
"select id from #{session.configuration.options[:rep_prefix]}_pending_changes"
|
28
|
+
) != nil
|
29
|
+
end
|
26
30
|
end
|
27
31
|
begin
|
28
32
|
success = false
|
@@ -225,7 +225,7 @@ module RR
|
|
225
225
|
values = rep_helper.load_record source_db, source_table, source_key
|
226
226
|
if values == nil
|
227
227
|
diff.amend
|
228
|
-
replicate_difference diff, remaining_attempts - 1
|
228
|
+
replicate_difference diff, remaining_attempts - 1, "source record for insert vanished"
|
229
229
|
else
|
230
230
|
begin
|
231
231
|
# note: savepoints have to be used for postgresql (as a failed SQL
|
@@ -238,7 +238,8 @@ module RR
|
|
238
238
|
row = rep_helper.load_record target_db, target_table, source_key
|
239
239
|
raise unless row # problem is not the existence of the record in the target db
|
240
240
|
diff.amend
|
241
|
-
replicate_difference diff, remaining_attempts - 1
|
241
|
+
replicate_difference diff, remaining_attempts - 1,
|
242
|
+
"insert failed with #{e.message}"
|
242
243
|
end
|
243
244
|
end
|
244
245
|
end
|
@@ -260,14 +261,14 @@ module RR
|
|
260
261
|
values = rep_helper.load_record source_db, source_table, source_key
|
261
262
|
if values == nil
|
262
263
|
diff.amend
|
263
|
-
replicate_difference diff, remaining_attempts - 1
|
264
|
+
replicate_difference diff, remaining_attempts - 1, "source record for update vanished"
|
264
265
|
else
|
265
266
|
log_replication_outcome source_db, diff
|
266
267
|
rep_helper.update_record target_db, target_table, values, target_key
|
267
268
|
end
|
268
269
|
end
|
269
270
|
|
270
|
-
# Attempts delete the source record from the target database.
|
271
|
+
# Attempts to delete the source record from the target database.
|
271
272
|
# E. g. if +source_db is :+left+, then the record is deleted in database
|
272
273
|
# :+right+.
|
273
274
|
# * +source_db+: either :+left+ or :+right+ - source database of replication
|
@@ -284,8 +285,9 @@ module RR
|
|
284
285
|
# Called to replicate the specified difference.
|
285
286
|
# * :+diff+: ReplicationDifference instance
|
286
287
|
# * :+remaining_attempts+: how many more times a replication will be attempted
|
287
|
-
|
288
|
-
|
288
|
+
# * :+previous_failure_description+: why the previous replication attempt failed
|
289
|
+
def replicate_difference(diff, remaining_attempts = MAX_REPLICATION_ATTEMPTS, previous_failure_description = nil)
|
290
|
+
raise Exception, previous_failure_description || "max replication attempts exceeded" if remaining_attempts == 0
|
289
291
|
options = options_for_table(diff.changes[:left].table)
|
290
292
|
if diff.type == :left or diff.type == :right
|
291
293
|
key = diff.type == :left ? :left_change_handling : :right_change_handling
|
data/lib/rubyrep/sync_helper.rb
CHANGED
@@ -5,6 +5,8 @@ module RR
|
|
5
5
|
# for third party syncers.
|
6
6
|
class SyncHelper
|
7
7
|
|
8
|
+
include LogHelper
|
9
|
+
|
8
10
|
# The current +TableSync+ instance
|
9
11
|
attr_accessor :table_sync
|
10
12
|
|
@@ -81,7 +83,7 @@ module RR
|
|
81
83
|
end
|
82
84
|
key = key_parts.join(', ')
|
83
85
|
end
|
84
|
-
sync_details =
|
86
|
+
sync_outcome, sync_details = fit_description_columns(outcome, details)
|
85
87
|
|
86
88
|
session.left.insert_record "#{sync_options[:rep_prefix]}_logged_events", {
|
87
89
|
:activity => 'sync',
|
@@ -90,7 +92,7 @@ module RR
|
|
90
92
|
:change_key => key,
|
91
93
|
:left_change_type => nil,
|
92
94
|
:right_change_type => nil,
|
93
|
-
:description =>
|
95
|
+
:description => sync_outcome,
|
94
96
|
:long_description => sync_details,
|
95
97
|
:event_time => Time.now,
|
96
98
|
:diff_dump => nil
|
data/lib/rubyrep/table_scan.rb
CHANGED
@@ -30,8 +30,8 @@ module RR
|
|
30
30
|
return unless progress_printer
|
31
31
|
unless @progress_printer_instance
|
32
32
|
total_records =
|
33
|
-
session.left.select_one("select count(*) as n from #{left_table}")['n'].to_i +
|
34
|
-
session.right.select_one("select count(*) as n from #{right_table}")['n'].to_i
|
33
|
+
session.left.select_one("select count(*) as n from #{session.left.quote_table_name(left_table)}")['n'].to_i +
|
34
|
+
session.right.select_one("select count(*) as n from #{session.right.quote_table_name(right_table)}")['n'].to_i
|
35
35
|
@progress_printer_instance = progress_printer.new(total_records, session, left_table, right_table)
|
36
36
|
end
|
37
37
|
@progress_printer_instance.step(steps)
|
data/lib/rubyrep/version.rb
CHANGED
data/lib/rubyrep.rb
CHANGED
data/spec/dolphins.jpg
ADDED
Binary file
|
@@ -92,6 +92,11 @@ describe ReplicationHelper do
|
|
92
92
|
left_change.table = right_change.table = 'extender_combined_key'
|
93
93
|
left_change.key = right_change.key = {'first_id' => 1, 'second_id' => 2}
|
94
94
|
|
95
|
+
# Verify that the log information are made fitting
|
96
|
+
helper.should_receive(:fit_description_columns).
|
97
|
+
with('ignore', 'ignored').
|
98
|
+
and_return(['ignoreX', 'ignoredY'])
|
99
|
+
|
95
100
|
helper.log_replication_outcome diff, 'ignore', 'ignored'
|
96
101
|
|
97
102
|
row = session.left.select_one("select * from rr_logged_events order by id desc")
|
@@ -101,8 +106,8 @@ describe ReplicationHelper do
|
|
101
106
|
row['change_key'].should == '"first_id"=>"1", "second_id"=>"2"'
|
102
107
|
row['left_change_type'].should == 'update'
|
103
108
|
row['right_change_type'].should == 'delete'
|
104
|
-
row['description'].should == '
|
105
|
-
row['long_description'].should == '
|
109
|
+
row['description'].should == 'ignoreX'
|
110
|
+
row['long_description'].should == 'ignoredY'
|
106
111
|
Time.parse(row['event_time']).should >= 10.seconds.ago
|
107
112
|
row['diff_dump'].should == diff.to_yaml
|
108
113
|
ensure
|
@@ -105,7 +105,8 @@ describe ReplicationRun do
|
|
105
105
|
session.left.begin_db_transaction
|
106
106
|
session.right.begin_db_transaction
|
107
107
|
begin
|
108
|
-
|
108
|
+
session.left.execute "delete from rr_pending_changes"
|
109
|
+
session.left.execute "delete from rr_logged_events"
|
109
110
|
session.left.insert_record 'rr_pending_changes', {
|
110
111
|
'change_table' => 'extender_no_record',
|
111
112
|
'change_key' => 'id|1',
|
@@ -153,6 +154,7 @@ describe ReplicationRun do
|
|
153
154
|
config.options[:logged_replication_events] = [:all_changes]
|
154
155
|
|
155
156
|
session = Session.new(config)
|
157
|
+
session.left.execute "delete from rr_logged_events"
|
156
158
|
initializer = ReplicationInitializer.new(session)
|
157
159
|
initializer.create_trigger :left, 'extender_no_record'
|
158
160
|
|
data/spec/spec_helper.rb
CHANGED
@@ -15,6 +15,15 @@ $LOAD_PATH.unshift File.dirname(__FILE__)
|
|
15
15
|
require 'rubyrep'
|
16
16
|
require 'connection_extender_interface_spec'
|
17
17
|
|
18
|
+
unless self.class.const_defined?('STRANGE_TABLE')
|
19
|
+
if ENV['RR_TEST_DB'] == 'postgres' || ENV['RR_TEST_DB'] == nil
|
20
|
+
STRANGE_TABLE = 'table_with.stränge Name山'
|
21
|
+
else
|
22
|
+
STRANGE_TABLE = 'table_with_stränge Name山'
|
23
|
+
end
|
24
|
+
STRANGE_COLUMN = 'stränge. Column山'
|
25
|
+
end
|
26
|
+
|
18
27
|
class Module
|
19
28
|
# Used to verify that an instance of the class / module receives a call of the
|
20
29
|
# specified method.
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
include RR
|
4
|
+
|
5
|
+
describe "Unusual table and column name support" do
|
6
|
+
before(:each) do
|
7
|
+
Initializer.configuration = standard_config
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be able to insert, update and delete records" do
|
11
|
+
session = Session.new
|
12
|
+
session.left.begin_db_transaction
|
13
|
+
begin
|
14
|
+
session.left.insert_record STRANGE_TABLE, {
|
15
|
+
'id' => 1,
|
16
|
+
STRANGE_COLUMN => 'bla'
|
17
|
+
}
|
18
|
+
ensure
|
19
|
+
session.left.rollback_db_transaction
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be able to identify primary keys" do
|
24
|
+
session = Session.new
|
25
|
+
session.left.primary_key_names(STRANGE_TABLE).should == ['id']
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be able to identify referenced tables" do
|
29
|
+
session = Session.new
|
30
|
+
referenced_tables = session.left.referenced_tables([STRANGE_TABLE])
|
31
|
+
referenced_tables.size.should == 1
|
32
|
+
referenced_tables[STRANGE_TABLE].sort.
|
33
|
+
should == ["referenced_table"]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should support sequence operations" do
|
37
|
+
session = Session.new
|
38
|
+
begin
|
39
|
+
left_sequence_values = session.left.sequence_values('rr', STRANGE_TABLE)
|
40
|
+
right_sequence_values = session.right.sequence_values('rr', STRANGE_TABLE)
|
41
|
+
session.left.update_sequences(
|
42
|
+
'rr', STRANGE_TABLE, 10, 7, left_sequence_values, right_sequence_values, 1)
|
43
|
+
|
44
|
+
sequence_props = session.left.sequence_values('rr', STRANGE_TABLE).values.first
|
45
|
+
sequence_props[:increment].should == 10
|
46
|
+
(sequence_props[:value] % 10).should == 7
|
47
|
+
|
48
|
+
session.left.clear_sequence_setup 'rr', STRANGE_TABLE
|
49
|
+
|
50
|
+
session.left.sequence_values('rr', STRANGE_TABLE).values.first[:increment].should == 1
|
51
|
+
ensure
|
52
|
+
session.left.clear_sequence_setup 'rr', STRANGE_TABLE
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should support trigger operations" do
|
57
|
+
trigger_name = 'rr_' + STRANGE_TABLE
|
58
|
+
session = Session.new
|
59
|
+
begin
|
60
|
+
session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE).should be_false
|
61
|
+
session.left.create_replication_trigger({
|
62
|
+
:trigger_name => trigger_name,
|
63
|
+
:table => STRANGE_TABLE,
|
64
|
+
:keys => ['id'],
|
65
|
+
:log_table => 'rr_pending_changes',
|
66
|
+
:activity_table => 'rr_running_flags',
|
67
|
+
:key_sep => '|',
|
68
|
+
:exclude_rr_activity => true
|
69
|
+
})
|
70
|
+
session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE).should be_true
|
71
|
+
session.left.insert_record STRANGE_TABLE, {
|
72
|
+
'id' => 11,
|
73
|
+
STRANGE_COLUMN => 'blub'
|
74
|
+
}
|
75
|
+
log_record = session.left.select_one(
|
76
|
+
"select * from rr_pending_changes where change_table = '#{STRANGE_TABLE}'")
|
77
|
+
log_record['change_key'].should == 'id|11'
|
78
|
+
log_record['change_type'].should == 'I'
|
79
|
+
|
80
|
+
session.left.drop_replication_trigger trigger_name, STRANGE_TABLE
|
81
|
+
session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE).should be_false
|
82
|
+
ensure
|
83
|
+
if session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE)
|
84
|
+
session.left.drop_replication_trigger trigger_name, STRANGE_TABLE
|
85
|
+
end
|
86
|
+
session.left.execute "delete from #{session.left.quote_table_name(STRANGE_TABLE)}"
|
87
|
+
session.left.execute "delete from rr_pending_changes"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/spec/sync_helper_spec.rb
CHANGED
@@ -36,6 +36,11 @@ describe SyncHelper do
|
|
36
36
|
sync = TableSync.new(Session.new, 'scanner_records')
|
37
37
|
helper = SyncHelper.new(sync)
|
38
38
|
|
39
|
+
# Verify that the log information are made fitting
|
40
|
+
helper.should_receive(:fit_description_columns).
|
41
|
+
with('my_outcome', 'my_long_description').
|
42
|
+
and_return(['my_outcomeX', 'my_long_descriptionY'])
|
43
|
+
|
39
44
|
helper.log_sync_outcome(
|
40
45
|
{'bla' => 'blub', 'id' => 1},
|
41
46
|
'my_sync_type',
|
@@ -50,8 +55,8 @@ describe SyncHelper do
|
|
50
55
|
row['change_key'].should == '1'
|
51
56
|
row['left_change_type'].should be_nil
|
52
57
|
row['right_change_type'].should be_nil
|
53
|
-
row['description'].should == '
|
54
|
-
row['long_description'].should == '
|
58
|
+
row['description'].should == 'my_outcomeX'
|
59
|
+
row['long_description'].should == 'my_long_descriptionY'
|
55
60
|
Time.parse(row['event_time']).should >= 10.seconds.ago
|
56
61
|
row['diff_dump'].should == nil
|
57
62
|
ensure
|
data/tasks/database.rake
CHANGED
@@ -4,6 +4,7 @@ require 'rubyrep'
|
|
4
4
|
|
5
5
|
require File.dirname(__FILE__) + '/task_helper.rb'
|
6
6
|
require File.dirname(__FILE__) + '/../config/test_config'
|
7
|
+
require File.dirname(__FILE__) + '/../spec/spec_helper'
|
7
8
|
|
8
9
|
# Creates the databases for the given configuration hash
|
9
10
|
def create_database(config)
|
@@ -97,7 +98,7 @@ def drop_postgres_schema(config)
|
|
97
98
|
return unless config[:adapter] == 'postgresql'
|
98
99
|
ActiveRecord::Base.establish_connection config
|
99
100
|
ActiveRecord::Schema.define do
|
100
|
-
execute "drop schema rr cascade"
|
101
|
+
execute "drop schema rr cascade" rescue nil
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
@@ -252,6 +253,18 @@ def create_sample_schema(database, config)
|
|
252
253
|
t.column :name, :string
|
253
254
|
end
|
254
255
|
|
256
|
+
create_table STRANGE_TABLE do |t|
|
257
|
+
t.column :first_fk, :integer
|
258
|
+
t.column :second_fk, :integer
|
259
|
+
t.column STRANGE_COLUMN, :string
|
260
|
+
end
|
261
|
+
|
262
|
+
ActiveRecord::Base.connection.execute(<<-end_sql)
|
263
|
+
ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(STRANGE_TABLE)} ADD CONSTRAINT strange_table_fkey
|
264
|
+
FOREIGN KEY (first_fk, second_fk)
|
265
|
+
REFERENCES referenced_table(first_id, second_id)
|
266
|
+
end_sql
|
267
|
+
|
255
268
|
create_table :left_table do |t|
|
256
269
|
t.column :name, :string
|
257
270
|
end if database == :left
|
@@ -267,10 +280,10 @@ end
|
|
267
280
|
def drop_sample_schema(config)
|
268
281
|
drop_postgres_schema config
|
269
282
|
|
270
|
-
|
271
|
-
ActiveRecord::Base.connection = connection
|
283
|
+
ActiveRecord::Base.establish_connection config
|
272
284
|
|
273
285
|
ActiveRecord::Schema.define do
|
286
|
+
drop_table STRANGE_TABLE rescue nil
|
274
287
|
drop_table :extender_type_check rescue nil
|
275
288
|
drop_table :extender_no_record rescue nil
|
276
289
|
drop_table :extender_one_record rescue nil
|
@@ -285,6 +298,7 @@ def drop_sample_schema(config)
|
|
285
298
|
drop_table :referenced_table2 rescue nil
|
286
299
|
drop_table :table_with_manual_key
|
287
300
|
drop_table :rr_pending_changes rescue nil
|
301
|
+
drop_table :rr_logged_events rescue nil
|
288
302
|
drop_table :rr_running_flags rescue nil
|
289
303
|
drop_table :trigger_test rescue nil
|
290
304
|
drop_table :sequence_test rescue nil
|
@@ -412,7 +426,7 @@ namespace :db do
|
|
412
426
|
|
413
427
|
desc "Create the sample schemas"
|
414
428
|
task :create_schema do
|
415
|
-
create_sample_schema :left, RR::Initializer.configuration
|
429
|
+
create_sample_schema :left, RR::Initializer.configuration
|
416
430
|
create_sample_schema :right, RR::Initializer.configuration rescue nil
|
417
431
|
end
|
418
432
|
|
@@ -432,8 +446,8 @@ namespace :db do
|
|
432
446
|
if pid
|
433
447
|
Process.wait pid
|
434
448
|
else
|
435
|
-
drop_sample_schema RR::Initializer.configuration.left
|
436
|
-
drop_sample_schema RR::Initializer.configuration.right
|
449
|
+
drop_sample_schema RR::Initializer.configuration.left
|
450
|
+
drop_sample_schema RR::Initializer.configuration.right
|
437
451
|
Kernel.exit!
|
438
452
|
end
|
439
453
|
end
|
data/tasks/website.rake
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
namespace :website do
|
2
|
+
desc 'Generate website files'
|
3
|
+
task :generate => :ruby_env do
|
4
|
+
(Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt|
|
5
|
+
sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'Upload website files to rubyforge'
|
10
|
+
task :upload do
|
11
|
+
host = "#{rubyforge_username}@rubyforge.org"
|
12
|
+
remote_dir = "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/"
|
13
|
+
local_dir = 'website'
|
14
|
+
sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Generate and upload website files'
|
18
|
+
task :build => [:website_generate, :website_upload, :publish_docs]
|
19
|
+
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyrep
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arndt Lehmann
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
NwT26VZnE2nr8g==
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2009-06-
|
33
|
+
date: 2009-06-20 00:00:00 +09:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -161,6 +161,7 @@ files:
|
|
161
161
|
- spec/db_specific_connection_extenders_spec.rb
|
162
162
|
- spec/db_specific_replication_extenders_spec.rb
|
163
163
|
- spec/direct_table_scan_spec.rb
|
164
|
+
- spec/dolphins.jpg
|
164
165
|
- spec/generate_runner_spec.rb
|
165
166
|
- spec/initializer_spec.rb
|
166
167
|
- spec/logged_change_spec.rb
|
@@ -190,6 +191,7 @@ files:
|
|
190
191
|
- spec/session_spec.rb
|
191
192
|
- spec/spec.opts
|
192
193
|
- spec/spec_helper.rb
|
194
|
+
- spec/strange_name_support_spec.rb
|
193
195
|
- spec/sync_helper_spec.rb
|
194
196
|
- spec/sync_runner_spec.rb
|
195
197
|
- spec/syncers_spec.rb
|
@@ -212,6 +214,7 @@ files:
|
|
212
214
|
- tasks/rubyrep.tailor
|
213
215
|
- tasks/stats.rake
|
214
216
|
- tasks/task_helper.rb
|
217
|
+
- tasks/website.rake
|
215
218
|
has_rdoc: true
|
216
219
|
homepage: http://rubyrep.rubyforge.org
|
217
220
|
post_install_message:
|
metadata.gz.sig
CHANGED
Binary file
|