rubyrep 1.0.2 → 1.0.3
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/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
|