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 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 not tables.include? table
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
- SELECT a.attname as name, format_type(a.atttypid, a.atttypmod) as type, d.adsrc as default, a.attnotnull as notnull
85
- FROM pg_attribute a LEFT JOIN pg_attrdef d
86
- ON a.attrelid = d.adrelid AND a.attnum = d.adnum
87
- WHERE a.attrelid = '#{table_name}'::regclass
88
- AND a.attnum > 0 AND NOT a.attisdropped
89
- ORDER BY a.attnum
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 #{params[:trigger_name]};
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 #{params[:trigger_name]}(change_key varchar(2000), change_new_key varchar(2000), change_type varchar(1))
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 #{params[:trigger_name]}_#{action};
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 #{params[:trigger_name]}(#{key_clause('OLD', params)}, #{key_clause('NEW', params)}, '#{action[0,1].upcase}');"
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 #{params[:trigger_name]}(#{key_clause(trigger_var, params)}, null, '#{action[0,1].upcase}');"
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 #{params[:trigger_name]}_#{action}
82
- AFTER #{action} ON #{params[:table]} FOR EACH ROW BEGIN
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 #{trigger_name}_#{action};"
105
+ execute "DROP TRIGGER `#{trigger_name}_#{action}`;"
106
106
  end
107
- execute "DROP PROCEDURE #{trigger_name};"
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 #{table_name} where extra = 'auto_increment'
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(#{column_name}) as current_max from #{table_name}
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 #outdated_sequence_values for the left database
180
+ # hash as returned by #sequence_values for the left database
181
181
  # * +right_sequence_values+:
182
- # hash as returned by #outdated_sequence_values for the right database
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 #{trigger_name};
210
+ DROP TRIGGER IF EXISTS `#{trigger_name}`;
211
211
  end_sql
212
212
 
213
213
  execute(<<-end_sql)
214
- CREATE TRIGGER #{trigger_name}
215
- BEFORE INSERT ON #{table_name} FOR EACH ROW BEGIN
216
- IF NEW.#{column_name} = 0 THEN
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.#{column_name} = LAST_INSERT_ID();
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 #{trigger_name}"
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 #outdated_sequence_values for the left database
136
+ # hash as returned by #sequence_values for the left database
137
137
  # * +right_sequence_values+:
138
- # hash as returned by #outdated_sequence_values for the right database
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 = details == nil ? nil : details[0...ReplicationInitializer::LONG_DESCRIPTION_SIZE]
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 => outcome.to_s,
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 of the repliation log column long_description
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.send(database).select_one(
24
- "select id from #{session.configuration.options[:rep_prefix]}_pending_changes"
25
- ) != nil
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
- def replicate_difference(diff, remaining_attempts = MAX_REPLICATION_ATTEMPTS)
288
- raise Exception, "max replication attempts exceeded" if remaining_attempts == 0
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
@@ -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 = details == nil ? nil : details[0...ReplicationInitializer::LONG_DESCRIPTION_SIZE]
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 => outcome.to_s,
95
+ :description => sync_outcome,
94
96
  :long_description => sync_details,
95
97
  :event_time => Time.now,
96
98
  :diff_dump => nil
@@ -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)
@@ -2,7 +2,7 @@ module RR #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
4
  MINOR = 0
5
- TINY = 2
5
+ TINY = 3
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/lib/rubyrep.rb CHANGED
@@ -34,6 +34,7 @@ require 'base_runner'
34
34
  require 'scan_runner'
35
35
  require 'committers/committers'
36
36
  require 'committers/buffered_committer'
37
+ require 'log_helper'
37
38
  require 'sync_helper'
38
39
  require 'table_sorter'
39
40
  require 'table_sync'
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 == 'ignore'
105
- row['long_description'].should == 'ignored'
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
@@ -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 == 'my_outcome'
54
- row['long_description'].should == 'my_long_description'
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
- connection = RR::ConnectionExtenders.db_connect(config)
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 rescue nil
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 rescue nil
436
- drop_sample_schema RR::Initializer.configuration.right rescue nil
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
@@ -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.2
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-13 00:00:00 +09:00
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