andyjeffries-rubyrep 1.2.1

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