ghazel-ar-extensions 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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