andyjeffries-rubyrep 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/History.txt +83 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +151 -0
  4. data/README.txt +37 -0
  5. data/bin/rubyrep +8 -0
  6. data/lib/rubyrep.rb +72 -0
  7. data/lib/rubyrep/base_runner.rb +195 -0
  8. data/lib/rubyrep/command_runner.rb +144 -0
  9. data/lib/rubyrep/committers/buffered_committer.rb +151 -0
  10. data/lib/rubyrep/committers/committers.rb +152 -0
  11. data/lib/rubyrep/configuration.rb +275 -0
  12. data/lib/rubyrep/connection_extenders/connection_extenders.rb +165 -0
  13. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +65 -0
  14. data/lib/rubyrep/connection_extenders/mysql_extender.rb +59 -0
  15. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +277 -0
  16. data/lib/rubyrep/database_proxy.rb +52 -0
  17. data/lib/rubyrep/direct_table_scan.rb +75 -0
  18. data/lib/rubyrep/generate_runner.rb +105 -0
  19. data/lib/rubyrep/initializer.rb +39 -0
  20. data/lib/rubyrep/log_helper.rb +30 -0
  21. data/lib/rubyrep/logged_change.rb +160 -0
  22. data/lib/rubyrep/logged_change_loader.rb +197 -0
  23. data/lib/rubyrep/noisy_connection.rb +80 -0
  24. data/lib/rubyrep/proxied_table_scan.rb +171 -0
  25. data/lib/rubyrep/proxy_block_cursor.rb +145 -0
  26. data/lib/rubyrep/proxy_connection.rb +431 -0
  27. data/lib/rubyrep/proxy_cursor.rb +44 -0
  28. data/lib/rubyrep/proxy_row_cursor.rb +43 -0
  29. data/lib/rubyrep/proxy_runner.rb +89 -0
  30. data/lib/rubyrep/replication_difference.rb +100 -0
  31. data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
  32. data/lib/rubyrep/replication_extenders/postgresql_replication.rb +236 -0
  33. data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
  34. data/lib/rubyrep/replication_helper.rb +142 -0
  35. data/lib/rubyrep/replication_initializer.rb +327 -0
  36. data/lib/rubyrep/replication_run.rb +142 -0
  37. data/lib/rubyrep/replication_runner.rb +166 -0
  38. data/lib/rubyrep/replicators/replicators.rb +42 -0
  39. data/lib/rubyrep/replicators/two_way_replicator.rb +361 -0
  40. data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
  41. data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
  42. data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
  43. data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
  44. data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
  45. data/lib/rubyrep/scan_runner.rb +25 -0
  46. data/lib/rubyrep/session.rb +230 -0
  47. data/lib/rubyrep/sync_helper.rb +121 -0
  48. data/lib/rubyrep/sync_runner.rb +31 -0
  49. data/lib/rubyrep/syncers/syncers.rb +112 -0
  50. data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
  51. data/lib/rubyrep/table_scan.rb +54 -0
  52. data/lib/rubyrep/table_scan_helper.rb +46 -0
  53. data/lib/rubyrep/table_sorter.rb +70 -0
  54. data/lib/rubyrep/table_spec_resolver.rb +142 -0
  55. data/lib/rubyrep/table_sync.rb +90 -0
  56. data/lib/rubyrep/task_sweeper.rb +77 -0
  57. data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
  58. data/lib/rubyrep/type_casting_cursor.rb +31 -0
  59. data/lib/rubyrep/uninstall_runner.rb +93 -0
  60. data/lib/rubyrep/version.rb +9 -0
  61. data/rubyrep +8 -0
  62. data/rubyrep.bat +4 -0
  63. data/setup.rb +1585 -0
  64. data/spec/base_runner_spec.rb +218 -0
  65. data/spec/buffered_committer_spec.rb +274 -0
  66. data/spec/command_runner_spec.rb +145 -0
  67. data/spec/committers_spec.rb +178 -0
  68. data/spec/configuration_spec.rb +203 -0
  69. data/spec/connection_extender_interface_spec.rb +141 -0
  70. data/spec/connection_extenders_registration_spec.rb +164 -0
  71. data/spec/database_proxy_spec.rb +48 -0
  72. data/spec/database_rake_spec.rb +40 -0
  73. data/spec/db_specific_connection_extenders_spec.rb +34 -0
  74. data/spec/db_specific_replication_extenders_spec.rb +38 -0
  75. data/spec/direct_table_scan_spec.rb +61 -0
  76. data/spec/dolphins.jpg +0 -0
  77. data/spec/generate_runner_spec.rb +84 -0
  78. data/spec/initializer_spec.rb +46 -0
  79. data/spec/log_helper_spec.rb +39 -0
  80. data/spec/logged_change_loader_spec.rb +68 -0
  81. data/spec/logged_change_spec.rb +470 -0
  82. data/spec/noisy_connection_spec.rb +78 -0
  83. data/spec/postgresql_replication_spec.rb +48 -0
  84. data/spec/postgresql_schema_support_spec.rb +212 -0
  85. data/spec/postgresql_support_spec.rb +63 -0
  86. data/spec/progress_bar_spec.rb +77 -0
  87. data/spec/proxied_table_scan_spec.rb +151 -0
  88. data/spec/proxy_block_cursor_spec.rb +197 -0
  89. data/spec/proxy_connection_spec.rb +423 -0
  90. data/spec/proxy_cursor_spec.rb +56 -0
  91. data/spec/proxy_row_cursor_spec.rb +66 -0
  92. data/spec/proxy_runner_spec.rb +70 -0
  93. data/spec/replication_difference_spec.rb +161 -0
  94. data/spec/replication_extender_interface_spec.rb +367 -0
  95. data/spec/replication_extenders_spec.rb +32 -0
  96. data/spec/replication_helper_spec.rb +178 -0
  97. data/spec/replication_initializer_spec.rb +509 -0
  98. data/spec/replication_run_spec.rb +443 -0
  99. data/spec/replication_runner_spec.rb +254 -0
  100. data/spec/replicators_spec.rb +36 -0
  101. data/spec/rubyrep_spec.rb +8 -0
  102. data/spec/scan_detail_reporter_spec.rb +119 -0
  103. data/spec/scan_progress_printers_spec.rb +68 -0
  104. data/spec/scan_report_printers_spec.rb +67 -0
  105. data/spec/scan_runner_spec.rb +50 -0
  106. data/spec/scan_summary_reporter_spec.rb +61 -0
  107. data/spec/session_spec.rb +253 -0
  108. data/spec/spec.opts +1 -0
  109. data/spec/spec_helper.rb +305 -0
  110. data/spec/strange_name_support_spec.rb +135 -0
  111. data/spec/sync_helper_spec.rb +169 -0
  112. data/spec/sync_runner_spec.rb +78 -0
  113. data/spec/syncers_spec.rb +171 -0
  114. data/spec/table_scan_helper_spec.rb +36 -0
  115. data/spec/table_scan_spec.rb +49 -0
  116. data/spec/table_sorter_spec.rb +30 -0
  117. data/spec/table_spec_resolver_spec.rb +111 -0
  118. data/spec/table_sync_spec.rb +140 -0
  119. data/spec/task_sweeper_spec.rb +47 -0
  120. data/spec/trigger_mode_switcher_spec.rb +83 -0
  121. data/spec/two_way_replicator_spec.rb +721 -0
  122. data/spec/two_way_syncer_spec.rb +256 -0
  123. data/spec/type_casting_cursor_spec.rb +50 -0
  124. data/spec/uninstall_runner_spec.rb +93 -0
  125. metadata +190 -0
@@ -0,0 +1,431 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/..'
2
+
3
+ require 'drb'
4
+
5
+ require 'rubyrep'
6
+ require 'forwardable'
7
+
8
+ require 'active_record/connection_adapters/abstract_adapter'
9
+
10
+ module RR
11
+
12
+ # Enables the fetching of (potential large) result sets in chunks.
13
+ class ResultFetcher
14
+
15
+ # The current database ProxyConnection
16
+ attr_accessor :connection
17
+
18
+ # hash of select options as described under ProxyConnection#select_cursor
19
+ attr_accessor :options
20
+
21
+ # column_name => value hash of the last returned row
22
+ attr_accessor :last_row
23
+
24
+ # The current row set: an array of column_name => value hashes
25
+ attr_accessor :rows
26
+
27
+ # Index to the current row in rows
28
+ attr_accessor :current_row_index
29
+
30
+ # Creates a new fetcher.
31
+ # * +connection+: the current ProxyConnection
32
+ # * +options+: hash of select options as described under ProxyConnection#select_cursor
33
+ def initialize(connection, options)
34
+ self.connection = connection
35
+ self.options = options.clone
36
+ end
37
+
38
+ # Returns +true+ if there are more rows to read.
39
+ def next?
40
+ unless self.rows
41
+ # Try to load some records
42
+
43
+ if options[:query] and last_row != nil
44
+ # A query was directly specified and all it's rows were returned
45
+ # ==> Finished.
46
+ return false
47
+ end
48
+
49
+ if options[:query]
50
+ # If a query has been directly specified, just directly execute it
51
+ query = options[:query]
52
+ else
53
+ # Otherwise build the query
54
+ if last_row
55
+ # There was a previous batch.
56
+ # Next batch will start after the last returned row
57
+ options.merge! :from => last_row, :exclude_starting_row => true
58
+ end
59
+
60
+ query = connection.table_select_query(options[:table], options)
61
+
62
+ if options[:row_buffer_size]
63
+ # Set the batch size
64
+ query += " limit #{options[:row_buffer_size]}"
65
+ end
66
+ end
67
+
68
+ self.rows = connection.select_all query
69
+ self.current_row_index = 0
70
+ end
71
+ self.current_row_index < self.rows.size
72
+ end
73
+
74
+ # Returns the row as a column => value hash and moves the cursor to the next row.
75
+ def next_row
76
+ raise("no more rows available") unless next?
77
+ self.last_row = self.rows[self.current_row_index]
78
+ self.current_row_index += 1
79
+
80
+ if self.current_row_index == self.rows.size
81
+ self.rows = nil
82
+ end
83
+
84
+ self.last_row
85
+ end
86
+
87
+ # Frees up all ressources
88
+ def clear
89
+ self.rows = nil
90
+ end
91
+ end
92
+
93
+ # This class represents a remote activerecord database connection.
94
+ # Normally created by DatabaseProxy
95
+ class ProxyConnection
96
+ # Ensure that the proxy object always stays on server side and only remote
97
+ # references are returned to the client.
98
+ include DRbUndumped
99
+
100
+ extend Forwardable
101
+
102
+ # The database connection
103
+ attr_accessor :connection
104
+
105
+ # A hash as described by ActiveRecord::Base#establish_connection
106
+ attr_accessor :config
107
+
108
+ # Forward certain methods to the proxied database connection
109
+ def_delegators :connection,
110
+ :columns, :quote_column_name,
111
+ :quote_table_name, :execute,
112
+ :select_one, :select_all, :tables, :update, :delete,
113
+ :begin_db_transaction, :rollback_db_transaction, :commit_db_transaction,
114
+ :referenced_tables,
115
+ :create_or_replace_replication_trigger_function,
116
+ :create_replication_trigger, :drop_replication_trigger, :replication_trigger_exists?,
117
+ :sequence_values, :update_sequences, :clear_sequence_setup,
118
+ :drop_table, :add_big_primary_key, :add_column, :remove_column
119
+
120
+ # Caching the primary keys. This is a hash with
121
+ # * key: table name
122
+ # * value: array of primary key names
123
+ attr_accessor :primary_key_names_cache
124
+
125
+ # Hash to register cursors.
126
+ # Purpose:
127
+ # Objects only referenced remotely via DRb can be garbage collected.
128
+ # We register them in this hash to protect them from unintended garbage collection.
129
+ attr_accessor :cursors
130
+
131
+ # 2-level Hash of table_name => column_name => Column objects.
132
+ attr_accessor :table_columns
133
+
134
+ # Hash of table_name => array of column names pairs.
135
+ attr_accessor :table_column_names
136
+
137
+ # A hash of manually overwritten primary keys:
138
+ # * key: table_name
139
+ # * value: array of primary key names
140
+ attr_accessor :manual_primary_keys
141
+
142
+ # Returns an array of primary key names for the given +table_name+.
143
+ # Caches the result for future calls. Allows manual overwrites through
144
+ # the Configuration options +:primary_key_names+ or :+primary_key_only_limit+.
145
+ #
146
+ # Parameters:
147
+ # * +table_name+: name of the table
148
+ # * +options+: An option hash with the following valid options:
149
+ # * :+raw+: if +true+, than don't use manual overwrites and don't cache
150
+ def primary_key_names(table_name, options = {})
151
+ return connection.primary_key_names(table_name) if options[:raw]
152
+
153
+ self.primary_key_names_cache ||= {}
154
+ result = primary_key_names_cache[table_name]
155
+ unless result
156
+ result = manual_primary_keys[table_name] || connection.primary_key_names(table_name)
157
+ primary_key_names_cache[table_name] = result
158
+ end
159
+ result
160
+ end
161
+
162
+ # Creates a table
163
+ # Call forwarded to ActiveRecord::ConnectionAdapters::SchemaStatements#create_table
164
+ # Provides an empty block (to prevent DRB from calling back the client)
165
+ def create_table(*params)
166
+ connection.create_table(*params) {}
167
+ end
168
+
169
+ # Returns a Hash of currently registerred cursors
170
+ def cursors
171
+ @cursors ||= {}
172
+ end
173
+
174
+ # Store a cursor in the register to protect it from the garbage collector.
175
+ def save_cursor(cursor)
176
+ cursors[cursor] = cursor
177
+ end
178
+
179
+ # Returns a cusor as produced by the #select_cursor method of the connection
180
+ # extenders.
181
+ #
182
+ # Two modes of operation: Either
183
+ # * execute the specified query (takes precedense) OR
184
+ # * first build the query based on options forwarded to #table_select_query
185
+ # +options+ is a hash with
186
+ # * :+query+: executes the given query
187
+ # * :+type_cast+:
188
+ # Unless explicitely disabled with +false+, build type casting cursor
189
+ # around result.
190
+ # * :+table+:
191
+ # Name of the table from which to read data.
192
+ # Required unless type casting is disabled.
193
+ # * further options as taken by #table_select_query to build the query
194
+ # * :+row_buffer_size+:
195
+ # Integer controlling how many rows a read into memory at one time.
196
+ def select_cursor(options)
197
+ cursor = ResultFetcher.new(self, options)
198
+ unless options[:type_cast] == false
199
+ cursor = TypeCastingCursor.new(self, options[:table], cursor)
200
+ end
201
+ cursor
202
+ end
203
+
204
+ # Reads the designated record from the database.
205
+ # Refer to #select_cursor for details parameter description.
206
+ # Returns the first matching row (column_name => value hash or +nil+).
207
+ def select_record(options)
208
+ cursor = select_cursor({:row_buffer_size => 1}.merge(options))
209
+ row = cursor.next? ? cursor.next_row : nil
210
+ cursor.clear
211
+ row
212
+ end
213
+
214
+ # Reads the designated records from the database.
215
+ # Refer to #select_cursor for details parameter description.
216
+ # Returns an array of matching rows (column_name => value hashes).
217
+ def select_records(options)
218
+ cursor = select_cursor(options)
219
+ rows = []
220
+ while cursor.next?
221
+ rows << cursor.next_row
222
+ end
223
+ cursor.clear
224
+ rows
225
+ end
226
+
227
+ # Create a session on the proxy side according to provided configuration hash.
228
+ # +config+ is a hash as described by ActiveRecord::Base#establish_connection
229
+ def initialize(config)
230
+ self.connection = ConnectionExtenders.db_connect config
231
+ self.config = config
232
+ self.manual_primary_keys = {}
233
+ end
234
+
235
+ # Destroys the session
236
+ def destroy
237
+ cursors.each_key do |cursor|
238
+ cursor.destroy
239
+ end
240
+ cursors.clear
241
+
242
+ if connection.log_subscriber
243
+ ActiveSupport::Notifications.notifier.unsubscribe connection.log_subscriber
244
+ connection.log_subscriber = nil
245
+ end
246
+
247
+ self.connection.disconnect!
248
+ end
249
+
250
+ # Quotes the given value. It is assumed that the value belongs to the specified column name and table name.
251
+ # Caches the column objects for higher speed.
252
+ def quote_value(table, column, value)
253
+ self.table_columns ||= {}
254
+ unless table_columns.include? table
255
+ table_columns[table] = {}
256
+ columns(table).each {|c| table_columns[table][c.name] = c}
257
+ end
258
+ connection.quote value, table_columns[table][column]
259
+ end
260
+
261
+ # Create a cursor for the given table.
262
+ # * +cursor_class+: should specify the Cursor class (e. g. ProxyBlockCursor or ProxyRowCursor).
263
+ # * +table+: name of the table
264
+ # * +options+: An option hash that is used to construct the SQL query. See ProxyCursor#construct_query for details.
265
+ def create_cursor(cursor_class, table, options = {})
266
+ cursor = cursor_class.new self, table
267
+ cursor.prepare_fetch options
268
+ save_cursor cursor
269
+ cursor
270
+ end
271
+
272
+ # Destroys the provided cursor and removes it from the register
273
+ def destroy_cursor(cursor)
274
+ cursor.destroy
275
+ cursors.delete cursor
276
+ end
277
+
278
+ # Returns an array of column names of the given table name.
279
+ # The array is ordered in the sequence as returned by the database.
280
+ # The result is cached for higher speed.
281
+ def column_names(table)
282
+ self.table_column_names ||= {}
283
+ unless table_column_names.include? table
284
+ table_column_names[table] = columns(table).map {|c| c.name}
285
+ end
286
+ table_column_names[table]
287
+ end
288
+
289
+ # Returns a list of quoted column names for the given +table+ as comma
290
+ # separated string.
291
+ def quote_column_list(table)
292
+ column_names(table).map do |column_name|
293
+ quote_column_name(column_name)
294
+ end.join(', ')
295
+ end
296
+ private :quote_column_list
297
+
298
+ # Returns a list of quoted primary key names for the given +table+ as comma
299
+ # separated string.
300
+ def quote_key_list(table)
301
+ primary_key_names(table).map do |column_name|
302
+ quote_column_name(column_name)
303
+ end.join(', ')
304
+ end
305
+ private :quote_key_list
306
+
307
+
308
+ # Generates an sql condition string for the given +table+ based on
309
+ # * +row+: a hash of primary key => value pairs designating the target row
310
+ # * +condition+: the type of sql condition (something like '>=' or '=', etc.)
311
+ def row_condition(table, row, condition)
312
+ query_part = ""
313
+ query_part << ' (' << quote_key_list(table) << ') ' << condition
314
+ query_part << ' (' << primary_key_names(table).map do |key|
315
+ quote_value(table, key, row[key])
316
+ end.join(', ') << ')'
317
+ query_part
318
+ end
319
+ private :row_condition
320
+
321
+ # Returns an SQL query string for the given +table+ based on the provided +options+.
322
+ # +options+ is a hash that can contain any of the following:
323
+ # * :+from+: nil OR the hash of primary key => value pairs designating the start of the selection
324
+ # * :+exclude_starting_row+: if true, do not include the row specified by :+from+
325
+ # * :+to+: nil OR the hash of primary key => value pairs designating the end of the selection
326
+ # * :+row_keys+: an array of primary key => value hashes specify the target rows.
327
+ def table_select_query(table, options = {})
328
+ query = "select #{quote_column_list(table)}"
329
+ query << " from #{quote_table_name(table)}"
330
+ query << " where" if [:from, :to, :row_keys].any? {|key| options.include? key}
331
+ first_condition = true
332
+ if options[:from]
333
+ first_condition = false
334
+ matching_condition = options[:exclude_starting_row] ? '>' : '>='
335
+ query << row_condition(table, options[:from], matching_condition)
336
+ end
337
+ if options[:to]
338
+ query << ' and' unless first_condition
339
+ first_condition = false
340
+ query << row_condition(table, options[:to], '<=')
341
+ end
342
+ if options[:row_keys]
343
+ query << ' and' unless first_condition
344
+ if options[:row_keys].empty?
345
+ query << ' false'
346
+ else
347
+ query << ' (' << quote_key_list(table) << ') in ('
348
+ first_key = true
349
+ options[:row_keys].each do |row|
350
+ query << ', ' unless first_key
351
+ first_key = false
352
+ query << '(' << primary_key_names(table).map do |key|
353
+ quote_value(table, key, row[key])
354
+ end.join(', ') << ')'
355
+ end
356
+ query << ')'
357
+ end
358
+ end
359
+ query << " order by #{quote_key_list(table)}"
360
+
361
+ query
362
+ end
363
+
364
+ # Returns an SQL insert query for the given +table+ and +values+.
365
+ # +values+ is a hash of column_name => value pairs.
366
+ def table_insert_query(table, values)
367
+ query = "insert into #{quote_table_name(table)}"
368
+ query << '(' << values.keys.map do |column_name|
369
+ quote_column_name(column_name)
370
+ end.join(', ') << ') '
371
+ query << 'values(' << values.map do |column_name, value|
372
+ quote_value(table, column_name, value)
373
+ end.join(', ') << ')'
374
+ query
375
+ end
376
+
377
+ # Inserts the specified records into the named +table+.
378
+ # +values+ is a hash of column_name => value pairs.
379
+ def insert_record(table, values)
380
+ execute table_insert_query(table, values)
381
+ end
382
+
383
+ # Returns an SQL update query.
384
+ # * +table+: name of the target table
385
+ # * +values+: a hash of column_name => value pairs
386
+ # * +org_key+:
387
+ # A hash of column_name => value pairs. If +nil+, use the key specified by
388
+ # +values+ instead.
389
+ def table_update_query(table, values, org_key = nil)
390
+ org_key ||= values
391
+ query = "update #{quote_table_name(table)} set "
392
+ query << values.map do |column_name, value|
393
+ "#{quote_column_name(column_name)} = #{quote_value(table, column_name, value)}"
394
+ end.join(', ')
395
+ query << " where (" << quote_key_list(table) << ") = ("
396
+ query << primary_key_names(table).map do |key|
397
+ quote_value(table, key, org_key[key])
398
+ end.join(', ') << ")"
399
+ end
400
+
401
+ # Updates the specified records of the specified table.
402
+ # * +table+: name of the target table
403
+ # * +values+: a hash of column_name => value pairs.
404
+ # * +org_key+:
405
+ # A hash of column_name => value pairs. If +nil+, use the key specified by
406
+ # +values+ instead.
407
+ # Returns the number of modified records.
408
+ def update_record(table, values, org_key = nil)
409
+ update table_update_query(table, values, org_key)
410
+ end
411
+
412
+ # Returns an SQL delete query for the given +table+ and +values+
413
+ # +values+ is a hash of column_name => value pairs. (Only the primary key
414
+ # values will be used and must be included in the hash.)
415
+ def table_delete_query(table, values)
416
+ query = "delete from #{quote_table_name(table)}"
417
+ query << " where (" << quote_key_list(table) << ") = ("
418
+ query << primary_key_names(table).map do |key|
419
+ quote_value(table, key, values[key])
420
+ end.join(', ') << ")"
421
+ end
422
+
423
+ # Deletes the specified record from the named +table+.
424
+ # +values+ is a hash of column_name => value pairs. (Only the primary key
425
+ # values will be used and must be included in the hash.)
426
+ # Returns the number of deleted records.
427
+ def delete_record(table, values)
428
+ delete table_delete_query(table, values)
429
+ end
430
+ end
431
+ end
@@ -0,0 +1,44 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/..'
2
+
3
+ require 'rubyrep'
4
+
5
+ module RR
6
+ # Provides shared functionality for ProxyRowCursor and ProxyBlockCursor
7
+ class ProxyCursor
8
+
9
+ # The current ProxyConnection.
10
+ attr_accessor :connection
11
+
12
+ # The name of the current table.
13
+ attr_accessor :table
14
+
15
+ # Array of primary key names for current table.
16
+ attr_accessor :primary_key_names
17
+
18
+ # The current cursor.
19
+ attr_accessor :cursor
20
+
21
+ # Shared initializations
22
+ # * connection: the current proxy connection
23
+ # * table: table_name
24
+ def initialize(connection, table)
25
+ self.connection = connection
26
+ self.table = table
27
+ self.primary_key_names = connection.primary_key_names table
28
+ end
29
+
30
+ # Initiate a query for the specified row range.
31
+ # +options+: An option hash that is used to construct the SQL query. See ProxyCursor#construct_query for details.
32
+ def prepare_fetch(options = {})
33
+ self.cursor = connection.select_cursor(
34
+ options.merge(:table => table, :type_cast => true)
35
+ )
36
+ end
37
+
38
+ # Releases all ressources
39
+ def destroy
40
+ self.cursor.clear if self.cursor
41
+ self.cursor = nil
42
+ end
43
+ end
44
+ end