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 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