Empact-ar-extensions 0.9.2

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 (45) hide show
  1. data/ChangeLog +145 -0
  2. data/README +167 -0
  3. data/Rakefile +61 -0
  4. data/config/database.yml +7 -0
  5. data/config/database.yml.template +7 -0
  6. data/config/mysql.schema +72 -0
  7. data/config/postgresql.schema +39 -0
  8. data/db/migrate/generic_schema.rb +97 -0
  9. data/db/migrate/mysql_schema.rb +32 -0
  10. data/db/migrate/oracle_schema.rb +5 -0
  11. data/db/migrate/version.rb +4 -0
  12. data/init.rb +31 -0
  13. data/lib/ar-extensions.rb +5 -0
  14. data/lib/ar-extensions/adapters/abstract_adapter.rb +146 -0
  15. data/lib/ar-extensions/adapters/mysql.rb +10 -0
  16. data/lib/ar-extensions/adapters/oracle.rb +14 -0
  17. data/lib/ar-extensions/adapters/postgresql.rb +9 -0
  18. data/lib/ar-extensions/adapters/sqlite.rb +7 -0
  19. data/lib/ar-extensions/create_and_update.rb +508 -0
  20. data/lib/ar-extensions/create_and_update/mysql.rb +7 -0
  21. data/lib/ar-extensions/csv.rb +309 -0
  22. data/lib/ar-extensions/delete.rb +143 -0
  23. data/lib/ar-extensions/delete/mysql.rb +3 -0
  24. data/lib/ar-extensions/extensions.rb +509 -0
  25. data/lib/ar-extensions/finder_options.rb +275 -0
  26. data/lib/ar-extensions/finder_options/mysql.rb +6 -0
  27. data/lib/ar-extensions/finders.rb +96 -0
  28. data/lib/ar-extensions/foreign_keys.rb +70 -0
  29. data/lib/ar-extensions/fulltext.rb +62 -0
  30. data/lib/ar-extensions/fulltext/mysql.rb +44 -0
  31. data/lib/ar-extensions/import.rb +354 -0
  32. data/lib/ar-extensions/import/mysql.rb +50 -0
  33. data/lib/ar-extensions/import/postgresql.rb +0 -0
  34. data/lib/ar-extensions/import/sqlite.rb +22 -0
  35. data/lib/ar-extensions/insert_select.rb +178 -0
  36. data/lib/ar-extensions/insert_select/mysql.rb +7 -0
  37. data/lib/ar-extensions/synchronize.rb +30 -0
  38. data/lib/ar-extensions/temporary_table.rb +131 -0
  39. data/lib/ar-extensions/temporary_table/mysql.rb +3 -0
  40. data/lib/ar-extensions/union.rb +204 -0
  41. data/lib/ar-extensions/union/mysql.rb +6 -0
  42. data/lib/ar-extensions/util/sql_generation.rb +27 -0
  43. data/lib/ar-extensions/util/support_methods.rb +32 -0
  44. data/lib/ar-extensions/version.rb +9 -0
  45. metadata +128 -0
@@ -0,0 +1,3 @@
1
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
2
+ include ActiveRecord::Extensions::Delete::DeleteSupport
3
+ end
@@ -0,0 +1,509 @@
1
+ require 'forwardable'
2
+
3
+ # ActiveRecord::Extensions provides additional functionality to the ActiveRecord
4
+ # ORM library created by DHH for Rails.
5
+ #
6
+ # It's main features include:
7
+ # * better finder support using a :conditions Hash for ActiveRecord::Base#find
8
+ # * better finder support using any object that responds to the to_sql method
9
+ # * mass data import functionality
10
+ # * a more modular design to extending ActiveRecord
11
+ #
12
+ #
13
+ # == Using Better Finder Hash Support
14
+ # Here are a few examples, please refer to the class documentation for each
15
+ # extensions:
16
+ #
17
+ # class Post < ActiveRecord::Base ; end
18
+ #
19
+ # Post.find( :all, :conditions=>{
20
+ # :title => "Title", # title='Title'
21
+ # :author_contains => "Zach", # author like '%Zach%'
22
+ # :author_starts_with => "Zach", # author like 'Zach%'
23
+ # :author_ends_with => "Dennis", # author like '%Zach'
24
+ # :published_at => (Date.now-30 .. Date.now), # published_at BETWEEN xxx AND xxx
25
+ # :rating => [ 4, 5, 6 ], # rating IN ( 4, 5, 6 )
26
+ # :rating_not_in => [ 7, 8, 9 ] # rating NOT IN( 4, 5, 6 )
27
+ # :rating_ne => 4, # rating != 4
28
+ # :rating_gt => 4, # rating > 4
29
+ # :rating_lt => 4, # rating < 4
30
+ # :content => /(a|b|c)/ # REGEXP '(a|b|c)'
31
+ # )
32
+ #
33
+ #
34
+ # == Create Your Own Finder Extension Example
35
+ # The following example shows you how-to create a robust and reliable
36
+ # finder extension which allows you to use Ranges in your :conditions Hash. This
37
+ # is the actual implementation in ActiveRecord::Extensions.
38
+ #
39
+ # class RangeExt
40
+ # NOT_IN_RGX = /^(.+)_(ne|not|not_in|not_between)$/
41
+ #
42
+ # def self.process( key, val, caller )
43
+ # return nil unless val.is_a?( Range )
44
+ # match_data = key.to_s.match( NOT_IN_RGX )
45
+ # key = match_data.captures[0] if match_data
46
+ # fieldname = caller.connection.quote_column_name( key )
47
+ # min = caller.connection.quote( val.first, caller.columns_hash[ key ] )
48
+ # max = caller.connection.quote( val.last, caller.columns_hash[ key ] )
49
+ # str = "#{caller.quoted_table_name}.#{fieldname} #{match_data ? 'NOT ' : '' } BETWEEN #{min} AND #{max}"
50
+ # Result.new( str, nil )
51
+ # end
52
+ #
53
+ #
54
+ # == Using to_sql Ducks In Your Find Methods!
55
+ # The below example shows you how-to utilize objects that respond_to the method +to_sql+ in
56
+ # your finds:
57
+ #
58
+ # class InsuranceClaim < ActiveRecord::Base ; end
59
+ #
60
+ # class InsuranceClaimAgeAndTypeQuery
61
+ # def to_sql
62
+ # "age_in_days BETWEEN 1 AND 60 AND claim_type IN( 'typea', 'typeb' )"
63
+ # end
64
+ # end
65
+ #
66
+ # claims = InsuranceClaim.find( :all, InsuranceClaimAgeAndTypeQuery.new )
67
+ #
68
+ # claims = InsuranceClaim.find( :all, :conditions=>{
69
+ # :claim_amount_gt => 30000,
70
+ # :age_and_type => InsuranceClaimAgeAndTypeQuery.new }
71
+ # )
72
+ #
73
+ # == Importing Lots of Data
74
+ #
75
+ # ActiveRecord executes a single INSERT statement for every call to 'create'
76
+ # and for every call to 'save' on a new model object. When you have only
77
+ # a handful of records to create or save this is not a big deal, but when
78
+ # you have hundreds, thousands or hundreds of thousands of records
79
+ # you need to have better performance.
80
+ #
81
+ # Below is an example of how to import the least amount of INSERT statements
82
+ # using mechanisms provided by your database vendor:
83
+ #
84
+ # class Student < ActiveRecord::Base ; end
85
+ #
86
+ # column_names = Student.columns.map{ |column| column.name }
87
+ # value_sets = some_method_to_load_data_from_csv_file( 'students.csv' )
88
+ # options = { :valudate => true }
89
+ #
90
+ # Student.import( column_names, value_sets, options )
91
+ #
92
+ # The +import+ functionality can be used even if there is not specific
93
+ # support for you vendor. This happens when a particular database vendor
94
+ # specific enhancement hasn't been added to ActiveRecord::Extensions.
95
+ # You can still use +import+ though because the +import+ functionality has
96
+ # been created with backwards compatibility. You may still get better
97
+ # performance using +import+, but you will definitely get no worse then
98
+ # ActiveRecord's create or save methods.
99
+ #
100
+ # See ActiveRecord::Base.import for more information and other ways to use
101
+ # this functionality.
102
+ #
103
+ # == Developers
104
+ # * Zach Dennis
105
+ # * Mark Van Holsytn
106
+ #
107
+ # == Homepage
108
+ # * Project Site: http://www.continuousthinking.com/tags/arext
109
+ # * Rubyforge Project: http://rubyforge.org/projects/arext
110
+ # * Anonymous SVN: svn checkout svn://rubyforge.org/var/svn/arext
111
+ #
112
+ module ActiveRecord::Extensions
113
+
114
+ Result = Struct.new( :sql, :value )
115
+
116
+ # ActiveRecored::Extensions::Registry is used to register finder extensions.
117
+ # Extensions are processed in last in first out order, like a stack.
118
+ class Registry # :nodoc:
119
+
120
+ def options( extension )
121
+ extension_arr = @registry.detect{ |arr| arr.first == extension }
122
+ return unless extension_arr
123
+ extension_arr.last
124
+ end
125
+
126
+ def registers?( extension ) # :nodoc:
127
+ @registry.detect{ |arr| arr.first == extension }
128
+ end
129
+
130
+ def register( extension, options ) # :nodoc:
131
+ @registry << [ extension, options ]
132
+ end
133
+
134
+ def initialize # :nodoc:
135
+ @registry = []
136
+ end
137
+
138
+ def process( field, value, caller ) # :nodoc:
139
+ current_adapter = caller.connection.adapter_name.downcase
140
+ @registry.reverse.each do |(extension,options)|
141
+ adapters = options[:adapters]
142
+ adapters.map!{ |e| e.to_s } unless adapters == :all
143
+ next if options[:adapters] != :all and adapters.grep( /#{current_adapter}/ ).empty?
144
+ if result=extension.process( field, value, caller )
145
+ return result
146
+ end
147
+ end
148
+ nil
149
+ end
150
+
151
+ end
152
+
153
+ class << self
154
+ extend Forwardable
155
+
156
+ def register( extension, options ) # :nodoc:
157
+ @registry ||= Registry.new
158
+ @registry.register( extension, options )
159
+ end
160
+
161
+ def_delegator :@registry, :process, :process
162
+ end
163
+
164
+
165
+ # ActiveRecord::Extension to translate an Array of values
166
+ # into the approriate IN( ... ) or NOT IN( ... ) SQL.
167
+ #
168
+ # == Examples
169
+ # Model.find :all, :conditions=>{ :id => [ 1,2,3 ] }
170
+ #
171
+ # # the following three calls are equivalent
172
+ # Model.find :all, :conditions=>{ :id_ne => [ 4,5,6 ] }
173
+ # Model.find :all, :conditions=>{ :id_not => [ 4,5,6 ] }
174
+ # Model.find :all, :conditions=>{ :id_not_in => [ 4,5,6 ] }
175
+ class ArrayExt
176
+
177
+ NOT_EQUAL_RGX = /(.+)_(ne|not|not_in)/
178
+
179
+ def self.process( key, val, caller )
180
+ if val.is_a?( Array )
181
+ match_data = key.to_s.match( NOT_EQUAL_RGX )
182
+ key = match_data.captures[0] if match_data
183
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )} " +
184
+ (match_data ? 'NOT ' : '') + "IN( ? )"
185
+ return Result.new( str, [val] )
186
+ end
187
+ nil
188
+ end
189
+
190
+ end
191
+
192
+
193
+ # ActiveRecord::Extension to translate Hash keys which end in
194
+ # +_lt+, +_lte+, +_gt+, or +_gte+ with the approriate <, <=, >,
195
+ # or >= symbols.
196
+ # * +_lt+ - denotes less than
197
+ # * +_gt+ - denotes greater than
198
+ # * +_lte+ - denotes less than or equal to
199
+ # * +_gte+ - denotes greater than or equal to
200
+ #
201
+ # == Examples
202
+ # Model.find :all, :conditions=>{ 'number_gt'=>100 }
203
+ # Model.find :all, :conditions=>{ 'number_lt'=>100 }
204
+ # Model.find :all, :conditions=>{ 'number_gte'=>100 }
205
+ # Model.find :all, :conditions=>{ 'number_lte'=>100 }
206
+ class Comparison
207
+
208
+ SUFFIX_MAP = { 'eq'=>'=', 'lt'=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>=', 'ne'=>'!=', 'not'=>'!=' }
209
+ ACCEPTABLE_COMPARISONS = [ String, Numeric, Time, DateTime, Date ]
210
+
211
+ def self.process( key, val, caller )
212
+ process_without_suffix( key, val, caller ) || process_with_suffix( key, val, caller )
213
+ end
214
+
215
+ def self.process_without_suffix( key, val, caller )
216
+ return nil unless caller.columns_hash.has_key?( key )
217
+ if val.nil?
218
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )} is ?"
219
+ else
220
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )}=?"
221
+ end
222
+ Result.new( str, val )
223
+ end
224
+
225
+ def self.process_with_suffix( key, val, caller )
226
+ return nil unless ACCEPTABLE_COMPARISONS.find{ |klass| val.is_a?(klass) }
227
+ SUFFIX_MAP.each_pair do |k,v|
228
+ match_data = key.to_s.match( /(.+)_#{k}$/ )
229
+ if match_data
230
+ fieldname = match_data.captures[0]
231
+ return nil unless caller.columns_hash.has_key?( fieldname )
232
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( fieldname )} " +
233
+ "#{v} #{caller.connection.quote( val, caller.columns_hash[ fieldname ] )} "
234
+ return Result.new( str, nil )
235
+ end
236
+ end
237
+ nil
238
+ end
239
+
240
+ end
241
+
242
+
243
+ # ActiveRecord::Extension to translate Hash keys which end in
244
+ # +_like+ or +_contains+ with the approriate LIKE keyword
245
+ # used in SQL.
246
+ #
247
+ # == Examples
248
+ # # the below two examples are equivalent
249
+ # Model.find :all, :conditions=>{ :name_like => 'John' }
250
+ # Model.find :all, :conditions=>{ :name_contains => 'John' }
251
+ #
252
+ # Model.find :all, :conditions=>{ :name_starts_with => 'J' }
253
+ # Model.find :all, :conditions=>{ :name_ends_with => 'n' }
254
+ class Like
255
+ LIKE_RGX = /(.+)_(like|contains)$/
256
+ STARTS_WITH_RGX = /(.+)_starts_with$/
257
+ ENDS_WITH_RGX = /(.+)_ends_with$/
258
+ def self.process( key, val, caller )
259
+ values = case val
260
+ when NilClass
261
+ []
262
+ when Array
263
+ val
264
+ else
265
+ [val]
266
+ end
267
+ result_values = []
268
+ case key.to_s
269
+ when LIKE_RGX
270
+ str = values.collect do |v|
271
+ result_values << "%%#{v}%%"
272
+ "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( $1 )} LIKE ?"
273
+ end
274
+ when STARTS_WITH_RGX
275
+ str = values.collect do |v|
276
+ result_values << "#{v}%%"
277
+ "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( $1 )} LIKE ?"
278
+ end
279
+ when ENDS_WITH_RGX
280
+ str = values.collect do |v|
281
+ result_values << "%%#{v}"
282
+ "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( $1 )} LIKE ?"
283
+ end
284
+ else
285
+ return nil
286
+ end
287
+
288
+ str = str.join(' OR ')
289
+ return Result.new(str, result_values)
290
+ end
291
+ end
292
+
293
+
294
+ # ActiveRecord::Extension to translate a ruby Range object into SQL's BETWEEN ... AND ...
295
+ # or NOT BETWEEN ... AND ... . This works on Ranges of Numbers, Dates, Times, etc.
296
+ #
297
+ # == Examples
298
+ # # the following two statements are identical because of how Ranges treat .. and ...
299
+ # Model.find :all, :conditions=>{ :id => ( 1 .. 2 ) }
300
+ # Model.find :all, :conditions=>{ :id => ( 1 ... 2 ) }
301
+ #
302
+ # # the following four statements are identical, this finds NOT BETWEEN matches
303
+ # Model.find :all, :conditions=>{ :id_ne => ( 4 .. 6 ) }
304
+ # Model.find :all, :conditions=>{ :id_not => ( 4 .. 6 ) }
305
+ # Model.find :all, :conditions=>{ :id_not_in => ( 4 ..6 ) }
306
+ # Model.find :all, :conditions=>{ :id_not_between => ( 4 .. 6 ) }
307
+ #
308
+ # # a little more creative, working with date ranges
309
+ # Model.find :all, :conditions=>{ :created_on => (Date.now-30 .. Date.now) }
310
+ class RangeExt
311
+ NOT_IN_RGX = /^(.+)_(ne|not|not_in|not_between)$/
312
+
313
+ def self.process( key, val, caller )
314
+ if val.is_a?( Range )
315
+ match_data = key.to_s.match( NOT_IN_RGX )
316
+ key = match_data.captures[0] if match_data
317
+ fieldname = caller.connection.quote_column_name( key )
318
+ min = caller.connection.quote( val.first, caller.columns_hash[ key ] )
319
+ max = caller.connection.quote( val.last, caller.columns_hash[ key ] )
320
+ str = if val.exclude_end?
321
+ "#{match_data ? 'NOT ' : '' }(#{caller.quoted_table_name}.#{fieldname} >= #{min} AND #{caller.quoted_table_name}.#{fieldname} < #{max})"
322
+ else
323
+ "#{caller.quoted_table_name}.#{fieldname} #{match_data ? 'NOT ' : '' } BETWEEN #{min} AND #{max}"
324
+ end
325
+
326
+ return Result.new( str, nil )
327
+ end
328
+ nil
329
+ end
330
+
331
+ end
332
+
333
+ # A base class for database vendor specific Regexp implementations. This is meant to be
334
+ # subclassed only because of the helper method(s) it provides.
335
+ class RegexpBase
336
+ NOT_EQUAL_RGX = /^(.+)_(ne|not|does_not_match)$/
337
+ EQUAL_RGX = /^(.+)_(eq|is|matches)$/
338
+
339
+ # A result class which provides an easy interface.
340
+ class RegexpResult
341
+ attr_reader :fieldname, :negate
342
+
343
+ def initialize( fieldname, negate=false )
344
+ @fieldname, @negate = fieldname, negate
345
+ end
346
+
347
+ def negate?
348
+ negate ? true : false
349
+ end
350
+ end
351
+
352
+ # Given the passed in +str+ and +caller+ this will return a RegexpResult object
353
+ # which gives the database quoted fieldname/column and can tell you whether or not
354
+ # the original +str+ is indicating a negated regular expression.
355
+ #
356
+ # == Examples
357
+ # r = RegexpBase.field_result( 'id' )
358
+ # r.fieldname => # 'id'
359
+ # r.negate? => # false
360
+ #
361
+ # r = RegexpBase.field_result( 'id_ne' )
362
+ # r.fieldname => # 'id'
363
+ # r.negate? => # true
364
+ #
365
+ # r = RegexpBase.field_result( 'id_not' )
366
+ # r.fieldname => # 'id'
367
+ # r.negate? => # true
368
+ #
369
+ # r = RegexpBase.field_result( 'id_does_not_match' )
370
+ # r.fieldname => # 'id'
371
+ # r.negate? => # true
372
+ def self.field_result( str, caller )
373
+ negate = false
374
+ if match_data=str.to_s.match( NOT_EQUAL_RGX )
375
+ negate = true
376
+ str = match_data.captures[0]
377
+ end
378
+ if match_data=str.to_s.match( EQUAL_RGX )
379
+ str = match_data.captures[0]
380
+ end
381
+ fieldname = caller.connection.quote_column_name( str )
382
+ RegexpResult.new( fieldname, negate )
383
+ end
384
+
385
+ end
386
+
387
+
388
+ # ActiveRecord::Extension for implementing Regexp implementation for MySQL.
389
+ # See documention for RegexpBase.
390
+ class MySQLRegexp < RegexpBase
391
+
392
+ def self.process( key, val, caller )
393
+ return nil unless val.is_a?( Regexp )
394
+ r = field_result( key, caller )
395
+ Result.new( "#{caller.quoted_table_name}.#{r.fieldname} #{r.negate? ? 'NOT ':''} REGEXP ?", val )
396
+ end
397
+
398
+ end
399
+
400
+
401
+ # ActiveRecord::Extension for implementing Regexp implementation for PostgreSQL.
402
+ # See documention for RegexpBase.
403
+ #
404
+ # Note: this doesn't support case insensitive matches.
405
+ class PostgreSQLRegexp < RegexpBase
406
+
407
+ def self.process( key, val, caller )
408
+ return nil unless val.is_a?( Regexp )
409
+ r = field_result( key, caller )
410
+ return Result.new((r.negate? \
411
+ ? "#{caller.quoted_table_name}.#{r.fieldname} !~ ? OR #{caller.quoted_table_name}.#{r.fieldname} IS NULL" \
412
+ : "#{caller.quoted_table_name}.#{r.fieldname} ~ ?"), val )
413
+ end
414
+
415
+ end
416
+
417
+ # ActiveRecord::Extension for implementing Regexp implementation for Oracle.
418
+ # See documention for RegexpBase.
419
+ #
420
+ class OracleRegexp < RegexpBase
421
+
422
+ def self.process( key, val, caller )
423
+ return nil unless val.is_a?( Regexp )
424
+ r = field_result( key, caller )
425
+ return Result.new( "#{r.negate? ? ' NOT ':''} REGEXP_LIKE(#{caller.quoted_table_name}.#{r.fieldname} , ?)", val )
426
+ end
427
+
428
+ end
429
+
430
+
431
+ # ActiveRecord::Extension for implementing Regexp implementation for MySQL.
432
+ # See documention for RegexpBase.
433
+ class SqliteRegexp < RegexpBase
434
+ class_inheritable_accessor :connections
435
+ self.connections = []
436
+
437
+ def self.add_rlike_function( connection )
438
+ self.connections << connection
439
+ unless connection.respond_to?( 'sqlite_regexp_support?' )
440
+ class << connection
441
+ def sqlite_regexp_support? ; true ; end
442
+ end
443
+ connection.instance_eval( '@connection' ).create_function( 'rlike', 3 ) do |func, a, b, negate|
444
+ func.set_result 1 if negate.to_s == 'true' ? a.to_s !~ /#{b}/ : a.to_s =~ /#{b}/
445
+ end
446
+ end
447
+ end
448
+
449
+ def self.process( key, val, caller )
450
+ return nil unless val.is_a?( Regexp )
451
+ r = field_result( key, caller )
452
+ unless self.connections.include?( caller.connection )
453
+ add_rlike_function( caller.connection )
454
+ end
455
+ Result.new( "rlike( #{r.fieldname}, ?, '#{r.negate?}' )", val )
456
+ end
457
+
458
+ end
459
+
460
+ class DatetimeSupport
461
+ SUFFIX_MAP = { 'eq'=>'=', 'lt'=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>=', 'ne'=>'!=', 'not'=>'!=' }
462
+
463
+ def self.process( key, val, caller )
464
+ return unless val.is_a?( Time )
465
+ process_without_suffix( key, val, caller ) || process_with_suffix( key, val, caller )
466
+ end
467
+
468
+ def self.process_without_suffix( key, val, caller )
469
+ return nil unless caller.columns_hash.has_key?( key )
470
+ if val.nil?
471
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )} IS NULL"
472
+ else
473
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )}=" +
474
+ "#{caller.connection.quote( val.to_s(:db), caller.columns_hash[ key ] )} "
475
+ end
476
+ Result.new( str, nil )
477
+ end
478
+
479
+ def self.process_with_suffix( key, val, caller )
480
+ SUFFIX_MAP.each_pair do |k,v|
481
+ match_data = key.to_s.match( /(.+)_#{k}$/ )
482
+ if match_data
483
+ fieldname = match_data.captures[0]
484
+ return nil unless caller.columns_hash.has_key?( fieldname )
485
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( fieldname )} " +
486
+ "#{v} #{caller.connection.quote( val.to_s(:db), caller.columns_hash[ fieldname ] )} "
487
+ return Result.new( str, nil )
488
+ end
489
+ end
490
+ nil
491
+ end
492
+
493
+
494
+ end
495
+
496
+
497
+ register Comparison, :adapters=>:all
498
+ register ArrayExt, :adapters=>:all
499
+ register Like, :adapters=>:all
500
+ register RangeExt, :adapters=>:all
501
+ register MySQLRegexp, :adapters=>[ :mysql ]
502
+ register PostgreSQLRegexp, :adapters=>[ :postgresql ]
503
+ register SqliteRegexp, :adapters =>[ :sqlite ]
504
+ register OracleRegexp, :adapters =>[ :oracle ]
505
+ register DatetimeSupport, :adapters =>[ :mysql, :sqlite, :oracle ]
506
+ end
507
+
508
+
509
+