ghazel-ar-extensions 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/ChangeLog +145 -0
  2. data/README +169 -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/adapters/abstract_adapter.rb +146 -0
  14. data/lib/ar-extensions/adapters/mysql.rb +10 -0
  15. data/lib/ar-extensions/adapters/oracle.rb +14 -0
  16. data/lib/ar-extensions/adapters/postgresql.rb +9 -0
  17. data/lib/ar-extensions/adapters/sqlite.rb +7 -0
  18. data/lib/ar-extensions/create_and_update/mysql.rb +7 -0
  19. data/lib/ar-extensions/create_and_update.rb +509 -0
  20. data/lib/ar-extensions/csv.rb +309 -0
  21. data/lib/ar-extensions/delete/mysql.rb +3 -0
  22. data/lib/ar-extensions/delete.rb +143 -0
  23. data/lib/ar-extensions/extensions.rb +513 -0
  24. data/lib/ar-extensions/finder_options/mysql.rb +6 -0
  25. data/lib/ar-extensions/finder_options.rb +275 -0
  26. data/lib/ar-extensions/finders.rb +94 -0
  27. data/lib/ar-extensions/foreign_keys.rb +70 -0
  28. data/lib/ar-extensions/fulltext/mysql.rb +44 -0
  29. data/lib/ar-extensions/fulltext.rb +62 -0
  30. data/lib/ar-extensions/import/mysql.rb +50 -0
  31. data/lib/ar-extensions/import/postgresql.rb +0 -0
  32. data/lib/ar-extensions/import/sqlite.rb +22 -0
  33. data/lib/ar-extensions/import.rb +348 -0
  34. data/lib/ar-extensions/insert_select/mysql.rb +7 -0
  35. data/lib/ar-extensions/insert_select.rb +178 -0
  36. data/lib/ar-extensions/synchronize.rb +30 -0
  37. data/lib/ar-extensions/temporary_table/mysql.rb +3 -0
  38. data/lib/ar-extensions/temporary_table.rb +131 -0
  39. data/lib/ar-extensions/union/mysql.rb +6 -0
  40. data/lib/ar-extensions/union.rb +204 -0
  41. data/lib/ar-extensions/util/sql_generation.rb +27 -0
  42. data/lib/ar-extensions/util/support_methods.rb +32 -0
  43. data/lib/ar-extensions/version.rb +9 -0
  44. data/lib/ar-extensions.rb +5 -0
  45. metadata +110 -0
@@ -0,0 +1,275 @@
1
+ # ActiveRecord::Extensions::FinderOptions provides additional functionality to the ActiveRecord
2
+ # ORM library created by DHH for Rails.
3
+ #
4
+ # == Using finder_sql_to_string
5
+ # Expose the finder sql to a string. The options are identical to those accepted by <tt>find(:all, options)</tt>
6
+ # the find method takes.
7
+ # === Example:
8
+ # sql = Contact.finder_sql_to_string(:include => :primary_email_address)
9
+ # Contact.find_by_sql(sql + 'USE_INDEX(blah)')
10
+ #
11
+ # == Enhanced Finder Options
12
+ # Add index hints, keywords, and pre and post SQL to the query without writing direct SQL
13
+ # === Parameter options:
14
+ # * <tt>:pre_sql</tt> appends SQL after the SELECT and before the selected columns
15
+ #
16
+ # sql = Contact.find :first, :pre_sql => "HIGH_PRIORITY", :select => 'contacts.name', :conditions => 'id = 5'
17
+ # SQL> SELECT HIGH_PRIORITY contacts.name FROM `contacts` WHERE id = 5
18
+ #
19
+ # * <tt>:post_sql</tt> appends additional SQL to the end of the statement
20
+ # Contact.find :first, :post_sql => 'FOR UPDATE', :select => 'contacts.name', :conditions => 'id = 5'
21
+ # SQL> SELECT contacts.name FROM `contacts` where id == 5 FOR UPDATE
22
+ #
23
+ # Book.find :all, :post_sql => 'USE_INDEX(blah)'
24
+ # SQL> SELECT books.* FROM `books` USE_INDEX(blah)
25
+ #
26
+ # * <tt>:override_select</tt> is used to override the <tt>SELECT</tt> clause of eager loaded associations
27
+ # The <tt>:select</tt> option is ignored by the vanilla ActiveRecord when using eager loading with associations (when <tt>:include</tt> is used)
28
+ # (refer to http://dev.rubyonrails.org/ticket/5371)
29
+ # The <tt>:override_select</tt> options allows us to directly specify a <tt>SELECT</tt> clause without affecting the operations of legacy code (ignore <tt>:select</tt>)
30
+ # of the current code. Several plugins are available that enable select with eager loading
31
+ # Several plugins exist to force <tt>:select</tt> to work with eager loading.
32
+ # <tt>script/plugin install git://github.com/blythedunham/eload-select.git </tt>
33
+ #
34
+ # * <tt>:having</tt> only works when <tt>:group</tt> option is specified
35
+ # Book.find(:all, :select => 'count(*) as count_all, topic_id', :group => :topic_id, :having => 'count(*) > 1')
36
+ # SQL>SELECT count(*) as count_all, topic_id FROM `books` GROUP BY topic_id HAVING count(*) > 1
37
+ #
38
+ # == Developers
39
+ # * Blythe Dunham http://blythedunham.com
40
+ #
41
+ # == Homepage
42
+ # * Project Site: http://www.continuousthinking.com/tags/arext
43
+ # * Rubyforge Project: http://rubyforge.org/projects/arext
44
+ # * Anonymous SVN: svn checkout svn://rubyforge.org/var/svn/arext
45
+ #
46
+ require 'active_record/version'
47
+ module ActiveRecord::Extensions::FinderOptions
48
+ def self.included(base)
49
+
50
+ #alias and include only if not yet defined
51
+ unless base.respond_to?(:construct_finder_sql_ext)
52
+ base.extend ClassMethods
53
+ base.extend ActiveRecord::Extensions::SqlGeneration
54
+ base.extend HavingOptionBackCompatibility
55
+ base.extend ConstructSqlCompatibility
56
+
57
+ base.class_eval do
58
+ class << self
59
+ VALID_FIND_OPTIONS.concat([:pre_sql, :post_sql, :keywords, :ignore, :rollup, :override_select, :having, :index_hint])
60
+ alias_method :construct_finder_sql, :construct_finder_sql_ext
61
+ alias_method_chain :construct_finder_sql_with_included_associations, :ext
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ module ClassMethods
68
+ # Return a string containing the SQL used with the find(:all)
69
+ # The options are the same as those with find(:all)
70
+ #
71
+ # Additional parameter of
72
+ # <tt>:force_eager_load</tt> forces eager loading even if the
73
+ # column is not referenced.
74
+ #
75
+ # sql = Contact.finder_sql_to_string(:include => :primary_email_address)
76
+ # Contact.find_by_sql(sql + 'USE_INDEX(blah)')
77
+ def finder_sql_to_string(options)
78
+ select_sql = self.send(
79
+ (use_eager_loading_sql?(options) ? :finder_sql_with_included_associations : :construct_finder_sql),
80
+ options.reject{|k,v| k == :force_eager_load}).strip
81
+ end
82
+
83
+ protected
84
+
85
+ # use eager loading sql (join associations) if inclu
86
+ def use_eager_loading_sql?(options)# :nodoc:
87
+ include_associations = merge_includes(scope(:find, :include), options[:include])
88
+ return ((include_associations.any?) &&
89
+ (options[:force_eager_load].is_a?(TrueClass) ||
90
+ references_eager_loaded_tables?(options)))
91
+ end
92
+
93
+ # construct_finder_sql is called when not using eager loading (:include option is NOT specified)
94
+ def construct_finder_sql_ext(options) # :nodoc:
95
+
96
+ #add piggy back option if plugin is installed
97
+ add_piggy_back!(options) if self.respond_to? :add_piggy_back!
98
+
99
+ scope = scope(:find)
100
+ sql = pre_sql_statements(options)
101
+ add_select_column_sql!(sql, options, scope)
102
+ add_from!(sql, options, scope)
103
+
104
+ sql << "#{options[:index_hint]} " if options[:index_hint]
105
+
106
+ add_joins!(sql, options[:joins], scope)
107
+ add_conditions!(sql, options[:conditions], scope)
108
+ add_group_with_having!(sql, options[:group], options[:having], scope)
109
+
110
+ add_order!(sql, options[:order], scope)
111
+ add_limit!(sql, options, scope)
112
+ add_lock!(sql, options, scope)
113
+
114
+ sql << post_sql_statements(options)
115
+ sql
116
+ end
117
+
118
+ #override the constructor for use with associations (:include option)
119
+ #directly use eager select if that plugin is loaded instead of this one
120
+ def construct_finder_sql_with_included_associations_with_ext(options, join_dependency)#:nodoc
121
+ scope = scope(:find)
122
+ sql = pre_sql_statements(options)
123
+
124
+ add_eager_selected_column_sql!(sql, options, scope, join_dependency)
125
+ add_from!(sql, options, scope)
126
+
127
+ sql << "#{options[:index_hint]} " if options[:index_hint]
128
+ sql << join_dependency.join_associations.collect{|join| join.association_join }.join
129
+
130
+ add_joins!(sql, options[:joins], scope)
131
+ add_conditions!(sql, options[:conditions], scope)
132
+
133
+ add_limited_ids_condition!(sql, options_with_group(options), join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
134
+
135
+ add_group_with_having!(sql, options[:group], options[:having], scope)
136
+ add_order!(sql, options[:order], scope)
137
+ add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
138
+ add_lock!(sql, options, scope)
139
+
140
+ sql << post_sql_statements(options)
141
+
142
+ return sanitize_sql(sql)
143
+ end
144
+
145
+ #generate the finder sql for use with associations (:include => :something)
146
+ def finder_sql_with_included_associations(options = {})#:nodoc
147
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
148
+ sql = construct_finder_sql_with_included_associations_with_ext(options, join_dependency)
149
+ end
150
+
151
+ #first use :override_select
152
+ #next use :construct_eload_select_sql if eload-select is loaded
153
+ #finally use normal column aliases
154
+ def add_eager_selected_column_sql!(sql, options, scope, join_dependency)#:nodoc:
155
+ if options[:override_select]
156
+ sql << options[:override_select]
157
+ elsif respond_to? :construct_eload_select_sql
158
+ sql << construct_eload_select_sql((scope && scope[:select]) || options[:select], join_dependency)
159
+ else
160
+ sql << column_aliases(join_dependency)
161
+ end
162
+ end
163
+
164
+ #simple select sql
165
+ def add_select_column_sql!(sql, options, scope = :auto)#:nodoc:
166
+ sql << "#{options[:select] || options[:override_select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))}"
167
+ end
168
+
169
+ #from sql
170
+ def add_from!(sql, options, scope = :auto)#:nodoc:
171
+ sql << " FROM #{options[:from] || (scope && scope[:from]) || quoted_table_name} "
172
+ end
173
+
174
+ def options_with_group(options)#:nodoc:
175
+ options
176
+ end
177
+ end
178
+
179
+ #In Rails 2.0.0 add_joins! signature changed
180
+ # Pre Rails 2.0.0: add_joins!(sql, options, scope)
181
+ # After 2.0.0: add_joins!(sql, options[:joins], scope)
182
+ module ConstructSqlCompatibility
183
+ def self.extended(base)
184
+ if ActiveRecord::VERSION::STRING < '2.0.0'
185
+ base.extend ClassMethods
186
+ base.class_eval do
187
+ class << self
188
+ alias_method_chain :add_joins!, :compatibility
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ module ClassMethods
195
+ def add_joins_with_compatibility!(sql, options, scope = :auto)#:nodoc:
196
+ join_param = options.is_a?(Hash) ? options : { :joins => options }
197
+ add_joins_without_compatibility!(sql, join_param, scope)
198
+ end
199
+
200
+ #aliasing threw errors
201
+ def quoted_table_name#:nodoc:
202
+ self.table_name
203
+ end
204
+
205
+ #pre Rails 2.0.0 the order of the scope and options was different
206
+ def add_from!(sql, options, scope = :auto)#:nodoc:
207
+ sql << " FROM #{(scope && scope[:from]) || options[:from] || table_name} "
208
+ end
209
+
210
+ def add_select_column_sql!(sql, options, scope = :auto)#:nodoc:
211
+ sql << "#{options[:override_select] || (scope && scope[:select]) || options[:select] || '*'}"
212
+ end
213
+
214
+ end
215
+ end
216
+
217
+ # Before Version 2.3.0 there was no :having option
218
+ # Add this option to previous versions by overriding add_group!
219
+ # to accept a hash with keys :group and :having instead of just group
220
+ # this avoids having to completely rewrite dependent functions like
221
+ # construct_finder_sql_for_association_limiting
222
+
223
+ module HavingOptionBackCompatibility#:nodoc:
224
+ def self.extended(base)
225
+
226
+ #for previous versions define having
227
+ if ActiveRecord::VERSION::STRING < '2.3.0'
228
+ base.extend ClassMethods
229
+
230
+ #for 2.3.0+ alias our method to :add_group!
231
+ else
232
+ base.class_eval do
233
+ class << self
234
+ alias_method :add_group_with_having!, :add_group!
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ module ClassMethods#:nodoc:
241
+ #add_group! in version 2.3 adds having already
242
+ #copy that implementation
243
+ def add_group_with_having!(sql, group, having, scope =:auto)#:nodoc:
244
+ if group
245
+ sql << " GROUP BY #{group}"
246
+ sql << " HAVING #{sanitize_sql(having)}" if having
247
+ else
248
+ scope = scope(:find) if :auto == scope
249
+ if scope && (scoped_group = scope[:group])
250
+ sql << " GROUP BY #{scoped_group}"
251
+ sql << " HAVING #{sanitize_sql(scope[:having])}" if scope[:having]
252
+ end
253
+ end
254
+ end
255
+
256
+ def add_group!(sql, group_options, scope = :auto)#:nodoc:
257
+ group, having = if group_options.is_a?(Hash) && group_options.has_key?(:group)
258
+ [group_options[:group] , group_options[:having]]
259
+ else
260
+ [group_options, nil]
261
+ end
262
+ add_group_with_having!(sql, group, having, scope)
263
+ end
264
+
265
+ def options_with_group(options)#:nodoc:
266
+ if options[:group]
267
+ options.merge(:group => {:group => options[:group], :having => options[:having]})
268
+ else
269
+ options
270
+ end
271
+ end
272
+ end
273
+
274
+ end
275
+ end
@@ -0,0 +1,94 @@
1
+ require 'active_record/version'
2
+
3
+ module ActiveRecord::ConnectionAdapters::Quoting
4
+ alias :quote_before_arext :quote
5
+ def quote( value, column=nil ) # :nodoc:
6
+ if value.is_a?( Regexp )
7
+ "'#{value.inspect[1...-1]}'"
8
+ else
9
+ quote_before_arext( value, column )
10
+ end
11
+ end
12
+ end
13
+
14
+ unless ActiveRecord::VERSION::STRING < '2.0.2'
15
+ class ActiveRecord::Base
16
+
17
+ class << self
18
+
19
+ private
20
+
21
+ alias :sanitize_sql_orig :sanitize_sql
22
+ def sanitize_sql(arg, table_name = quoted_table_name) # :nodoc:
23
+ return if arg.blank? # don't process arguments like [], {}, "" or nil
24
+ if arg.respond_to?( :to_sql )
25
+ arg = sanitize_sql_by_way_of_duck_typing(arg)
26
+ elsif arg.is_a?(Hash)
27
+ arg = sanitize_sql_from_hash(arg, table_name)
28
+ elsif arg.is_a?( Array ) and arg.size == 2 and arg.first.is_a?( String ) and arg.last.is_a?( Hash )
29
+ arg = sanitize_sql_from_string_and_hash(arg, table_name)
30
+ end
31
+ sanitize_sql_orig(arg)
32
+ end
33
+
34
+ def sanitize_sql_by_way_of_duck_typing(arg) #: nodoc:
35
+ arg.to_sql( caller )
36
+ end
37
+
38
+ def sanitize_sql_from_string_and_hash(arr, table_name = quoted_table_name) # :nodoc:
39
+ return arr if arr.first =~ /\:[\w]+/
40
+ return arr if arr.last.empty? # skip empty hash conditions, ie: :conditions => ["", {}]
41
+ arr2 = sanitize_sql_from_hash( arr.last, table_name )
42
+ if arr2.empty?
43
+ conditions = arr.first
44
+ else
45
+ conditions = [ arr.first << " AND (#{arr2.first})" ]
46
+ conditions.push( *arr2[1..-1] )
47
+ end
48
+ conditions
49
+ end
50
+
51
+ def sanitize_sql_from_hash(hsh, table_name = quoted_table_name) #:nodoc:
52
+ conditions, values = [], []
53
+ hsh = expand_hash_conditions_for_aggregates(hsh) # introduced in Rails 2.0.2
54
+
55
+ hsh.each_pair do |key,val|
56
+ if val.respond_to?( :to_sql )
57
+ conditions << sanitize_sql_by_way_of_duck_typing( val )
58
+ next
59
+ elsif val.is_a?(Hash) # don't mess with ActiveRecord hash nested hash functionality
60
+ conditions << sanitize_sql_hash_for_conditions({key => val}, table_name)
61
+ else
62
+ sql = nil
63
+ result = ActiveRecord::Extensions.process( key, val, self )
64
+ if result
65
+ conditions << result.sql
66
+ values.push( result.value ) unless result.value.nil?
67
+ else
68
+ # Extract table name from qualified attribute names.
69
+ attr = key.to_s
70
+ if attr.include?('.')
71
+ table_name, attr = attr.split('.', 2)
72
+ table_name = connection.quote_table_name(table_name)
73
+ end
74
+ # ActiveRecord in 2.3.1 changed the method signature for
75
+ # the method attribute_condition
76
+ if ActiveRecord::VERSION::STRING < '2.3.1'
77
+ conditions << "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition( val )} "
78
+ else
79
+ conditions << attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", val)
80
+ end
81
+ values << val
82
+ end
83
+ end
84
+ end
85
+
86
+ conditions = conditions.join( ' AND ' )
87
+ return [] if conditions.size == 1 and conditions.first.empty?
88
+ [ conditions, *values ]
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,70 @@
1
+ # Enables support for enabling and disabling foreign keys
2
+ # for the underlyig database connection for ActiveRecord.
3
+ #
4
+ # This can be used with or without block form. This also
5
+ # uses the connection attached to the model.
6
+ #
7
+ # ==== Example 1, without block form
8
+ # Project.foreign_keys.disable
9
+ # Project.foreign_keys.enable
10
+ #
11
+ # If you use this form you have to manually re-enable the foreign
12
+ # keys.
13
+ #
14
+ # ==== Example 2, with block form
15
+ # Project.foreign_keys.disable do
16
+ # # ...
17
+ # end
18
+ #
19
+ # Project.foreign_keys.enable do
20
+ # # ...
21
+ # end
22
+ #
23
+ # If you use the block form the foreign keys are automatically
24
+ # enabled or disabled when the block exits. This currently
25
+ # does not restore the state of foreign keys to the state before
26
+ # the block was entered.
27
+ #
28
+ # Note: If you use the disable block foreign keys
29
+ # will be enabled after the block exits. If you use the enable block foreign keys
30
+ # will be disabled after the block exits.
31
+ #
32
+ # TODO: check the external state and restore that state when using block form.
33
+ module ActiveRecord::Extensions::ForeignKeys
34
+
35
+ class ForeignKeyController # :nodoc:
36
+ attr_reader :clazz
37
+
38
+ def initialize( clazz )
39
+ @clazz = clazz
40
+ end
41
+
42
+ def disable # :nodoc:
43
+ if block_given?
44
+ disable
45
+ yield
46
+ enable
47
+ else
48
+ clazz.connection.execute "set foreign_key_checks = 0"
49
+ end
50
+ end
51
+
52
+ def enable #:nodoc:
53
+ if block_given?
54
+ enable
55
+ yield
56
+ disable
57
+ else
58
+ clazz.connection.execute "set foreign_key_checks = 1"
59
+ end
60
+ end
61
+
62
+ end #end ForeignKeyController
63
+
64
+ def foreign_keys # :nodoc:
65
+ ForeignKeyController.new( self )
66
+ end
67
+
68
+ end
69
+
70
+ ActiveRecord::Base.extend( ActiveRecord::Extensions::ForeignKeys )
@@ -0,0 +1,44 @@
1
+ # This adds FullText searching functionality for the MySQLAdapter.
2
+ class ActiveRecord::Extensions::FullTextSearching::MySQLFullTextExtension
3
+ extend Forwardable
4
+
5
+ class << self
6
+ extend Forwardable
7
+
8
+ def register( fulltext_key, options ) # :nodoc:
9
+ @fulltext_registry ||= ActiveRecord::Extensions::Registry.new
10
+ @fulltext_registry.register( fulltext_key, options )
11
+ end
12
+
13
+ def registry # :nodoc:
14
+ @fulltext_registry
15
+ end
16
+
17
+ def_delegator :@fulltext_registry, :registers?, :registers?
18
+ end
19
+
20
+ RGX = /^match_(.+)/
21
+
22
+ def process( key, val, caller ) # :nodoc:
23
+ match_data = key.to_s.match( RGX )
24
+ return nil unless match_data
25
+ fulltext_identifier = match_data.captures[0].to_sym
26
+ if self.class.registers?( fulltext_identifier )
27
+ fields = self.class.registry.options( fulltext_identifier )[:fields]
28
+ str = "MATCH ( #{fields.join( ',' )} ) AGAINST (#{caller.connection.quote(val)})"
29
+ return ActiveRecord::Extensions::Result.new( str, nil )
30
+ end
31
+ nil
32
+ end
33
+
34
+ def_delegator 'ActiveRecord::Extensions::FullTextSupport::MySQLFullTextExtension', :register
35
+ end
36
+ ActiveRecord::Extensions.register ActiveRecord::Extensions::FullTextSearching::MySQLFullTextExtension.new, :adapters=>[:mysql]
37
+
38
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
39
+ include ActiveRecord::Extensions::FullTextSearching::FullTextSupport
40
+
41
+ def register_fulltext_extension( fulltext_key, options ) # :nodoc:
42
+ ActiveRecord::Extensions::FullTextSearching::MySQLFullTextExtension.register( fulltext_key, options )
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ require 'forwardable'
2
+
3
+ # FullTextSearching provides fulltext searching capabilities
4
+ # if the underlying database adapter supports it. Currently
5
+ # only MySQL is supported.
6
+ module ActiveRecord::Extensions::FullTextSearching
7
+
8
+ module FullTextSupport # :nodoc:
9
+ def supports_full_text_searching? #:nodoc:
10
+ true
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ class ActiveRecord::Base
17
+ class FullTextSearchingNotSupported < StandardError ; end
18
+
19
+ class << self
20
+
21
+ # Adds fulltext searching capabilities to the current model
22
+ # for the given fulltext key and option hash.
23
+ #
24
+ # == Parameters
25
+ # * +fulltext_key+ - the key/attribute to be used to as the fulltext index
26
+ # * +options+ - the options hash.
27
+ #
28
+ # ==== Options
29
+ # * +fields+ - an array of field names to be used in the fulltext search
30
+ #
31
+ # == Example
32
+ #
33
+ # class Book < ActiveRecord::Base
34
+ # fulltext :title, :fields=>%W( title publisher author_name )
35
+ # end
36
+ #
37
+ # # To use the fulltext index
38
+ # Book.find :all, :conditions=>{ :match_title => 'Zach' }
39
+ #
40
+ def fulltext( fulltext_key, options )
41
+ connection.register_fulltext_extension( fulltext_key, options )
42
+ rescue NoMethodError
43
+ logger.warn "FullTextSearching is not supported for adapter!"
44
+ raise FullTextSearchingNotSupported.new
45
+ end
46
+
47
+ # Returns true if the current connection adapter supports full
48
+ # text searching, otherwise returns false.
49
+ def supports_full_text_searching?
50
+ connection.supports_full_text_searching?
51
+ rescue NoMethodError
52
+ false
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+
59
+
60
+
61
+
62
+
@@ -0,0 +1,50 @@
1
+ module ActiveRecord::Extensions::ConnectionAdapters::MysqlAdapter # :nodoc:
2
+
3
+ include ActiveRecord::Extensions::Import::ImportSupport
4
+ include ActiveRecord::Extensions::Import::OnDuplicateKeyUpdateSupport
5
+
6
+ # Returns a generated ON DUPLICATE KEY UPDATE statement given the passed
7
+ # in +args+.
8
+ def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
9
+ sql = ' ON DUPLICATE KEY UPDATE '
10
+ arg = args.first
11
+ if arg.is_a?( Array )
12
+ sql << sql_for_on_duplicate_key_update_as_array( table_name, arg )
13
+ elsif arg.is_a?( Hash )
14
+ sql << sql_for_on_duplicate_key_update_as_hash( table_name, arg )
15
+ elsif arg.is_a?( String )
16
+ sql << arg
17
+ else
18
+ raise ArgumentError.new( "Expected Array or Hash" )
19
+ end
20
+ sql
21
+ end
22
+
23
+ def sql_for_on_duplicate_key_update_as_array( table_name, arr ) # :nodoc:
24
+ results = arr.map do |column|
25
+ qc = quote_column_name( column )
26
+ "#{table_name}.#{qc}=VALUES(#{qc})"
27
+ end
28
+ results.join( ',' )
29
+ end
30
+
31
+ def sql_for_on_duplicate_key_update_as_hash( table_name, hsh ) # :nodoc:
32
+ sql = ' ON DUPLICATE KEY UPDATE '
33
+ results = hsh.map do |column1, column2|
34
+ qc1 = quote_column_name( column1 )
35
+ qc2 = quote_column_name( column2 )
36
+ "#{table_name}.#{qc1}=VALUES( #{qc2} )"
37
+ end
38
+ results.join( ',')
39
+ end
40
+
41
+ #return true if the statement is a duplicate key record error
42
+ def duplicate_key_update_error?(exception)# :nodoc:
43
+ exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry')
44
+ end
45
+
46
+ end
47
+
48
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
49
+ include ActiveRecord::Extensions::ConnectionAdapters::MysqlAdapter
50
+ end
File without changes
@@ -0,0 +1,22 @@
1
+ module ActiveRecord::Extensions::ConnectionAdapters::SQLiteAdapter # :nodoc:
2
+ include ActiveRecord::Extensions::Import::ImportSupport
3
+
4
+ def post_sql_statements( table_name, options )
5
+ []
6
+ end
7
+
8
+ def insert_many( sql, values, *args ) # :nodoc:
9
+ sql2insert = []
10
+ values.each do |value|
11
+ sql2insert << "#{sql} #{value};"
12
+ end
13
+
14
+ raw_connection.transaction { |db| db.execute_batch(sql2insert.join("\n")) }
15
+ number_of_rows_inserted = sql2insert.size
16
+ end
17
+
18
+ end
19
+
20
+ ActiveRecord::ConnectionAdapters::SQLiteAdapter.class_eval do
21
+ include ActiveRecord::Extensions::ConnectionAdapters::SQLiteAdapter
22
+ end