Permalink
Browse files

Added generic support for all adapters to support the import function…

…ality. All tests pass with Mysql and PostgreSQL support. PostgreSQL support for the import functionality works, but it relies on single INSERT statement creation. By adding single INSERT statement creation and making it compatible with import all adapters should work flawlessly. All tests pass right now.

git-svn-id: svn+ssh://rubyforge.org/var/svn/arext/trunk@41 5128a5ed-121c-0410-8d5c-98af306bc9be
  • Loading branch information...
1 parent b4fff3a commit 141e8721016a6718cc582dd1a29de908cce6805f zachdennis committed Dec 6, 2006
View
@@ -19,6 +19,7 @@ namespace :test do
task adapter.to_sym do |t|
ENV['ARE_DB'] = adapter
Dir[ File.join( DIR, 'tests', 'test_*.rb' ) ].each{ |f| require f }
+ Dir[ File.join( DIR, 'tests', adapter.to_s, 'test_*.rb' ) ].each{ |f| require f }
end
end
@@ -1,5 +1,5 @@
CREATE TABLE topics (
- id integer NOT NULL,
+ id serial NOT NULL,
title character varying(255) default NULL,
author_name character varying(255) default NULL,
author_email_address character varying(255) default NULL,
@@ -9,20 +9,20 @@ CREATE TABLE topics (
content text,
approved bool default TRUE,
replies_count integer default 0,
- parent_id integer default NULL,
+ parent_id serial default NULL,
type character varying(50) default NULL,
PRIMARY KEY (id)
);
CREATE TABLE projects (
- id integer NOT NULL,
+ id serial NOT NULL,
name character varying(100) default NULL,
type character varying(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE developers (
- id integer NOT NULL,
+ id serial NOT NULL,
name character varying(100) default NULL,
salary integer default 70000,
created_at timestamp default NULL,
@@ -31,7 +31,7 @@ CREATE TABLE developers (
);
CREATE TABLE books (
- id integer NOT NULL,
+ id serial NOT NULL,
title character varying(255) NOT NULL,
publisher character varying(255) NOT NULL,
author_name character varying(255) NOT NULL,
View
@@ -2,11 +2,16 @@
dir = File.dirname( __FILE__ )
require File.join( dir, 'lib', 'extensions' )
+
require File.join( dir, 'lib', 'fulltext' )
+require File.join( dir, 'lib', 'fulltext', 'mysql' )
db_adapters_path = File.join( dir, 'lib', 'adapters' )
require File.join( dir, 'lib', 'import' )
+require File.join( dir, 'lib', 'import', 'mysql' )
+require File.join( dir, 'lib', 'import', 'postgresql' )
+
require File.join( dir, 'lib', 'finders' )
require File.join( db_adapters_path, 'abstract_adapter' )
@@ -1,54 +1,34 @@
require 'forwardable'
-module ActiveRecord::Extensions::FullTextSupport
+module ActiveRecord::Extensions::FullTextSearching
+ class FullTextSearchingNotSupported < StandardError ; end
- class MySQLFullTextExtension < ActiveRecord::Extensions::AbstractExtension
- extend Forwardable
-
- class << self
- extend Forwardable
-
- def register( fulltext_key, options )
- @fulltext_registry ||= ActiveRecord::Extensions::Registry.new
- @fulltext_registry.register( fulltext_key, options )
- end
-
- def registry
- @fulltext_registry
- end
-
- def_delegator :@fulltext_registry, :registers?, :registers?
+ module FullTextSupport
+ def supports_full_text_searching?
+ true
end
-
- RGX = /^match_(.+)/
-
- def process( key, val, caller )
- match_data = key.to_s.match( RGX )
- return nil unless match_data
- fulltext_identifier = match_data.captures[0].to_sym
- if self.class.registers?( fulltext_identifier )
- fields = self.class.registry[fulltext_identifier][:fields]
- str = "MATCH ( #{fields.join( ',' )} ) AGAINST (#{caller.connection.quote(val)})"
- return ActiveRecord::Extensions::Result.new( str, nil )
- end
- nil
- end
-
- def_delegator 'ActiveRecord::Extensions::FullTextSupport::MySQLFullTextExtension', :register
end
- ActiveRecord::Extensions.register MySQLFullTextExtension.new, :adapters=>[:mysql]
-
- module ClassMethods
-
+ module ClassMethods
def fulltext( fulltext_key, options )
- ActiveRecord::Extensions::FullTextSupport::MySQLFullTextExtension.register( fulltext_key, options )
+ connection.register_fulltext_extension( fulltext_key, options )
+ rescue NoMethodError
+ # raise FullTextSearchingNotSupported.new
+ # DO NOT RAISE EXCEPTION, PRINT A WARNING AND DO NOTHING
+ ActiveRecord::Base.logger.warn "FullTextSearching is not supported for adapter!"
end
-
end
end
-ActiveRecord::Base.extend( ActiveRecord::Extensions::FullTextSupport::ClassMethods )
+class ActiveRecord::Base
+ def self.supports_full_text_searching?
+ connection.supports_full_text_searching?
+ rescue NoMethodError
+ false
+ end
+end
+
+ActiveRecord::Base.extend( ActiveRecord::Extensions::FullTextSearching::ClassMethods )
@@ -0,0 +1,44 @@
+class ActiveRecord::Extensions::FullTextSearching::MySQLFullTextExtension < ActiveRecord::Extensions::AbstractExtension
+ extend Forwardable
+
+ class << self
+ extend Forwardable
+
+ def register( fulltext_key, options )
+ @fulltext_registry ||= ActiveRecord::Extensions::Registry.new
+ @fulltext_registry.register( fulltext_key, options )
+ end
+
+ def registry
+ @fulltext_registry
+ end
+
+ def_delegator :@fulltext_registry, :registers?, :registers?
+ end
+
+ RGX = /^match_(.+)/
+
+ def process( key, val, caller )
+ match_data = key.to_s.match( RGX )
+ return nil unless match_data
+ fulltext_identifier = match_data.captures[0].to_sym
+ if self.class.registers?( fulltext_identifier )
+ fields = self.class.registry[fulltext_identifier][:fields]
+ str = "MATCH ( #{fields.join( ',' )} ) AGAINST (#{caller.connection.quote(val)})"
+ return ActiveRecord::Extensions::Result.new( str, nil )
+ end
+ nil
+ end
+
+ def_delegator 'ActiveRecord::Extensions::FullTextSupport::MySQLFullTextExtension', :register
+end
+ActiveRecord::Extensions.register ActiveRecord::Extensions::FullTextSearching::MySQLFullTextExtension.new, :adapters=>[:mysql]
+
+
+class ActiveRecord::ConnectionAdapters::MysqlAdapter
+ include ActiveRecord::Extensions::FullTextSearching::FullTextSupport
+
+ def register_fulltext_extension( fulltext_key, options )
+ ActiveRecord::Extensions::FullTextSearching::MySQLFullTextExtension.register( fulltext_key, options )
+ end
+end
View
@@ -1,8 +1,32 @@
module ActiveRecord::Extensions::ConnectionAdapters ; end
-module ActiveRecord::Extensions::Import ; end
+module ActiveRecord::Extensions::Import
+ module ImportSupport
+ def supports_import?
+ true
+ end
+ end
+
+ module OnDuplicateKeyUpdateSupport
+ def supports_on_duplicate_key_update?
+ true
+ end
+ end
+end
module ActiveRecord::Extensions::Import::Base
+ def supports_import?
+ connection.supports_import?
+ rescue NoMethodError
+ false
+ end
+
+ def supports_on_duplicate_key_update?
+ connection.supports_on_duplicate_key_update?
+ rescue NoMethodError
+ false
+ end
+
# Imports a collection of values to the database. This is more efficient than
# using ActiveRecord::Base#create or ActiveRecord::Base#save multiple times. This
# method works well if you want to create more then one record at a time and do not
@@ -123,7 +147,7 @@ def import_with_validations( column_names, array_of_attributes, options={} ) # :
end
end
array_of_attributes.compact!
-
+
if not array_of_attributes.empty?
import_without_validations_or_callbacks( column_names, array_of_attributes )
end
@@ -135,12 +159,29 @@ def import_with_validations( column_names, array_of_attributes, options={} ) # :
# records without validations or callbacks.
def import_without_validations_or_callbacks( column_names, array_of_attributes, options={} )
escaped_column_names = quote_column_names( column_names )
+ columns = []
+ array_of_attributes.first.each_with_index { |arr,i| columns << columns_hash[ column_names[i] ] }
+
+ if not supports_import?
+ columns_sql = "(" + escaped_column_names.join( ',' ) + ")"
+ insert_statements, values = [], []
+ array_of_attributes.each do |arr|
+ my_values = []
+ arr.each_with_index do |val,j|
+ my_values << connection.quote( val, columns[j] )
+# puts columns[j].inspect
+ # exit
+ end
+ insert_statements << "INSERT INTO #{self.table_name} #{columns_sql} VALUES(" + my_values.join( ',' ) + ")"
+ connection.execute( insert_statements.last )
+ end
+ return
+ else
+
# generate the sql
insert_sql = connection.multiple_value_sets_insert_sql( table_name, escaped_column_names, options )
- columns = []
- array_of_attributes.first.each_with_index { |arr,i| columns << columns_hash[ column_names[i] ] }
values_sql = connection.values_sql_for_column_names_and_attributes( columns, array_of_attributes )
post_sql_statements = connection.post_sql_statements( table_name, options )
@@ -149,6 +190,7 @@ def import_without_validations_or_callbacks( column_names, array_of_attributes,
[ insert_sql, post_sql_statements ].flatten,
values_sql,
"#{self.class.name} Create Many Without Validations Or Callbacks" )
+ end
end
# Returns an array of quoted column names
@@ -171,76 +213,3 @@ def validations_array_for_column_names_and_attributes( column_names, array_of_at
ActiveRecord::Base.extend( ActiveRecord::Extensions::Import::Base )
-
-
-module ActiveRecord::Extensions::ConnectionAdapters::MysqlAdapter
-
- # Returns an array of post SQL statements given the passed in options.
- def post_sql_statements( table_name, options )
- post_sql_statements = []
- if options[:on_duplicate_key_update]
- post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update] )
- end
- post_sql_statements
- end
-
- def multiple_value_sets_insert_sql( table_name, column_names, options )
- "INSERT #{options[:ignore]?'IGNORE':''} INTO #{table_name} (#{column_names.join(', ')}) "
- end
-
- # Returns a generated ON DUPLICATE KEY UPDATE statement given the passed
- # in +args+.
- def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
- sql = ' ON DUPLICATE KEY UPDATE '
- arg = args.first
- if arg.is_a?( Array )
- sql << sql_for_on_duplicate_key_update_as_array( table_name, arg )
- elsif arg.is_a?( Hash )
- sql << sql_for_on_duplicate_key_update_as_hash( table_name, arg )
- else
- raise ArgumentError.new( "Expected Array or Hash" )
- end
- sql
- end
-
- def sql_for_on_duplicate_key_update_as_array( table_name, arr ) # :nodoc:
- qt = quote_column_name( table_name )
- results = arr.map do |column|
- qc = quote_column_name( column )
- "#{qt}.#{qc}=VALUES( #{qc} )"
- end
- results.join( ',' )
- end
-
- def sql_for_on_duplicate_key_update_as_hash( table_name, hsh ) # :nodoc:
- sql = ' ON DUPLICATE KEY UPDATE '
- qt = quote_column_name( table_name )
- results = hsh.map do |column1, column2|
- qc1 = quote_column_name( column1 )
- qc2 = quote_column_name( column2 )
- "#{qt}.#{qc1}=VALUES( #{qc2} )"
- end
- results.join( ',')
- end
-
- # Returns SQL the VALUES for an INSERT statement given the passed in +columns+
- # and +array_of_attributes+.
- def values_sql_for_column_names_and_attributes( columns, array_of_attributes ) # :nodoc:
- values = []
- array_of_attributes.each do |arr|
- my_values = []
- arr.each_with_index do |val,j|
- my_values << quote( val, columns[j] )
- end
- values << my_values
- end
- values_arr = values.map{ |arr| '(' + arr.join( ',' ) + ')' }
- values_arr[0] = "VALUES" + values_arr[0]
- values_arr
- end
-
-end
-
-#ActiveRecord::Base.extend( ActiveRecord::Extensions::ConnectionAdapters::MysqlAdapter )
-ActiveRecord::ConnectionAdapters::MysqlAdapter.send( 'include', ActiveRecord::Extensions::ConnectionAdapters::MysqlAdapter )
-
Oops, something went wrong.

0 comments on commit 141e872

Please sign in to comment.