diff options
Diffstat (limited to 'activerecord/lib')
14 files changed, 236 insertions, 152 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index b4db1eed18..6f21cea288 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -113,7 +113,7 @@ module ActiveRecord end end - # A cached lookup for table existence + # A cached lookup for table existence. def table_exists?(name) return true if @tables.key? name @@ -135,7 +135,7 @@ module ActiveRecord @tables.clear end - # Clear out internal caches for table with +table_name+ + # Clear out internal caches for table with +table_name+. def clear_table_cache!(table_name) @columns.delete table_name @columns_hash.delete table_name @@ -193,7 +193,7 @@ module ActiveRecord @connections = [] end - # Clears the cache which maps classes + # Clears the cache which maps classes. def clear_reloadable_connections! @reserved_connections.each do |name, conn| checkin conn @@ -365,7 +365,7 @@ module ActiveRecord @connection_pools.each_value {|pool| pool.release_connection } end - # Clears the cache which maps classes + # Clears the cache which maps classes. def clear_reloadable_connections! @connection_pools.each_value {|pool| pool.clear_reloadable_connections! } end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb index 29ac9341ec..30ccb8f0a4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb @@ -2,52 +2,53 @@ module ActiveRecord module ConnectionAdapters # :nodoc: module DatabaseLimits - # the maximum length of a table alias + # Returns the maximum length of a table alias. def table_alias_length 255 end - # the maximum length of a column name + # Returns the maximum length of a column name. def column_name_length 64 end - # the maximum length of a table name + # Returns the maximum length of a table name. def table_name_length 64 end - # the maximum length of an index name + # Returns the maximum length of an index name. def index_name_length 64 end - # the maximum number of columns per table + # Returns the maximum number of columns per table. def columns_per_table 1024 end - # the maximum number of indexes per table + # Returns the maximum number of indexes per table. def indexes_per_table 16 end - # the maximum number of columns in a multicolumn index + # Returns the maximum number of columns in a multicolumn index. def columns_per_multicolumn_index 16 end - # the maximum number of elements in an IN (x,y,z) clause. nil means no limit + # Returns the maximum number of elements in an IN (x,y,z) clause. + # nil means no limit. def in_clause_length nil end - # the maximum length of an SQL query + # Returns the maximum length of an SQL query. def sql_query_length 1048575 end - # maximum number of joins in a single query + # Returns maximum number of joins in a single query. def joins_per_query 256 end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 70da9d5f1e..3045e30407 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -55,13 +55,27 @@ module ActiveRecord def exec_query(sql, name = 'SQL', binds = []) end - # Executes insert +sql+ statement in the context of this connection using + # Executes insert +sql+ statement in the context of this connection using # +binds+ as the bind substitutes. +name+ is the logged along with # the executed +sql+ statement. def exec_insert(sql, name, binds) exec_query(sql, name, binds) end + # Executes delete +sql+ statement in the context of this connection using + # +binds+ as the bind substitutes. +name+ is the logged along with + # the executed +sql+ statement. + def exec_delete(sql, name, binds) + exec_query(sql, name, binds) + end + + # Executes update +sql+ statement in the context of this connection using + # +binds+ as the bind substitutes. +name+ is the logged along with + # the executed +sql+ statement. + def exec_update(sql, name, binds) + exec_query(sql, name, binds) + end + # Returns the last auto-generated ID from the affected table. # # +id_value+ will be returned unless the value is nil, in @@ -77,13 +91,13 @@ module ActiveRecord end # Executes the update statement and returns the number of rows affected. - def update(sql, name = nil) - update_sql(sql, name) + def update(sql, name = nil, binds = []) + exec_update(sql, name, binds) end # Executes the delete statement and returns the number of rows affected. - def delete(sql, name = nil) - delete_sql(sql, name) + def delete(sql, name = nil, binds = []) + exec_delete(sql, name, binds) end # Checks whether there is currently no transaction active. This is done diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 8bae50885f..9f9c2c42cb 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -17,6 +17,10 @@ module ActiveRecord # def tables(name = nil) end + # Checks to see if the table +table_name+ exists on the database. + # + # === Example + # table_exists?(:developers) def table_exists?(table_name) tables.include?(table_name.to_s) end @@ -24,7 +28,7 @@ module ActiveRecord # Returns an array of indexes for the given table. # def indexes(table_name, name = nil) end - # Checks to see if an index exists on a table for a given index definition + # Checks to see if an index exists on a table for a given index definition. # # === Examples # # Check an index exists diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 7ac72acd58..d3a054c29b 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -1,5 +1,6 @@ # encoding: utf-8 +gem 'mysql2', '~> 0.3.0' require 'mysql2' module ActiveRecord @@ -131,6 +132,7 @@ module ActiveRecord ADAPTER_NAME end + # Returns true, since this connection adapter supports migrations. def supports_migrations? true end @@ -139,6 +141,7 @@ module ActiveRecord true end + # Returns true, since this connection adapter supports savepoints. def supports_savepoints? true end @@ -289,6 +292,15 @@ module ActiveRecord execute sql.gsub('?') { quote(*binds.shift.reverse) }, name end + def exec_delete(sql, name, binds) + binds = binds.dup + + # Pretend to support bind parameters + execute sql.gsub('?') { quote(*binds.shift.reverse) }, name + @connection.affected_rows + end + alias :exec_update :exec_delete + def last_inserted_id(result) @connection.last_id end @@ -376,6 +388,10 @@ module ActiveRecord end end + # Drops a MySQL database. + # + # Example: + # drop_database('sebastian_development') def drop_database(name) #:nodoc: execute "DROP DATABASE IF EXISTS `#{name}`" end @@ -406,6 +422,7 @@ module ActiveRecord super(table_name, options) end + # Returns an array of indexes for the given table. def indexes(table_name, name = nil) indexes = [] current_index = nil @@ -423,6 +440,7 @@ module ActiveRecord indexes end + # Returns an array of +Mysql2Column+ objects for the table specified by +table_name+. def columns(table_name, name = nil) sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" columns = [] @@ -437,6 +455,10 @@ module ActiveRecord super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB")) end + # Renames a table. + # + # Example: + # rename_table('octopuses', 'octopi') def rename_table(table_name, new_name) execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}" end @@ -521,6 +543,7 @@ module ActiveRecord variables.first['Value'] unless variables.empty? end + # Returns a table's primary key and belonging sequence. def pk_and_sequence_for(table) keys = [] result = execute("describe #{quote_table_name(table)}") diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 2c05ff21f9..862ce852e6 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -3,15 +3,8 @@ require 'active_support/core_ext/kernel/requires' require 'active_support/core_ext/object/blank' require 'set' -begin - require 'mysql' -rescue LoadError - raise "!!! Missing the mysql gem. Add it to your Gemfile: gem 'mysql'" -end - -unless defined?(Mysql::Result) && Mysql::Result.method_defined?(:each_hash) - raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'. Or use gem 'mysql2'" -end +gem 'mysql', '~> 2.8.1' +require 'mysql' class Mysql class Time @@ -208,13 +201,13 @@ module ActiveRecord true end - # Returns +true+, since this connection adapter supports prepared statement + # Returns true, since this connection adapter supports prepared statement # caching. def supports_statement_cache? true end - # Returns true. + # Returns true, since this connection adapter supports migrations. def supports_migrations? #:nodoc: true end @@ -224,6 +217,7 @@ module ActiveRecord true end + # Returns true, since this connection adapter supports savepoints. def supports_savepoints? #:nodoc: true end @@ -401,34 +395,9 @@ module ActiveRecord def exec_query(sql, name = 'SQL', binds = []) log(sql, name, binds) do - result = nil - - cache = {} - if binds.empty? - stmt = @connection.prepare(sql) - else - cache = @statements[sql] ||= { - :stmt => @connection.prepare(sql) - } - stmt = cache[:stmt] - end - - stmt.execute(*binds.map { |col, val| - type_cast(val, col) - }) - if metadata = stmt.result_metadata - cols = cache[:cols] ||= metadata.fetch_fields.map { |field| - field.name - } - - metadata.free - result = ActiveRecord::Result.new(cols, stmt.to_a) + exec_stmt(sql, name, binds) do |cols, stmt| + ActiveRecord::Result.new(cols, stmt.to_a) if cols end - - stmt.free_result - stmt.close if binds.empty? - - result end end @@ -480,6 +449,15 @@ module ActiveRecord @connection.affected_rows end + def exec_delete(sql, name, binds) + log(sql, name, binds) do + exec_stmt(sql, name, binds) do |cols, stmt| + stmt.affected_rows + end + end + end + alias :exec_update :exec_delete + def begin_db_transaction #:nodoc: exec_without_stmt "BEGIN" rescue Mysql::Error @@ -607,6 +585,7 @@ module ActiveRecord super(table_name, options) end + # Returns an array of indexes for the given table. def indexes(table_name, name = nil)#:nodoc: indexes = [] current_index = nil @@ -625,6 +604,7 @@ module ActiveRecord indexes end + # Returns an array of +MysqlColumn+ objects for the table specified by +table_name+. def columns(table_name, name = nil)#:nodoc: sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" columns = [] @@ -638,6 +618,10 @@ module ActiveRecord super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB")) end + # Renames a table. + # + # Example: + # rename_table('octopuses', 'octopi') def rename_table(table_name, new_name) execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}" end @@ -833,6 +817,46 @@ module ActiveRecord end private + def exec_stmt(sql, name, binds) + cache = {} + if binds.empty? + stmt = @connection.prepare(sql) + else + cache = @statements[sql] ||= { + :stmt => @connection.prepare(sql) + } + stmt = cache[:stmt] + end + + + begin + stmt.execute(*binds.map { |col, val| type_cast(val, col) }) + rescue Mysql::Error => e + # Older versions of MySQL leave the prepared statement in a bad + # place when an error occurs. To support older mysql versions, we + # need to close the statement and delete the statement from the + # cache. + stmt.close + @statements.delete sql + raise e + end + + cols = nil + if metadata = stmt.result_metadata + cols = cache[:cols] ||= metadata.fetch_fields.map { |field| + field.name + } + end + + result = yield [cols, stmt] + + stmt.result_metadata.free if cols + stmt.free_result + stmt.close if binds.empty? + + result + end + def connect encoding = @config[:encoding] if encoding @@ -875,6 +899,7 @@ module ActiveRecord version[0] >= 5 end + # Returns the version of the connected MySQL server. def version @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 0c2afc180b..37db2be7a9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -122,6 +122,14 @@ module ActiveRecord # Extracts the value from a PostgreSQL column default definition. def self.extract_value_from_default(default) case default + # This is a performance optimization for Ruby 1.9.2 in development. + # If the value is nil, we return nil straight away without checking + # the regular expressions. If we check each regular expression, + # Regexp#=== will call NilClass#to_str, which will trigger + # method_missing (defined by whiny nil in ActiveSupport) which + # makes this method very very slow. + when NilClass + nil # Numeric types when /\A\(?(-?\d+(\.\d*)?\)?)\z/ $1 @@ -237,7 +245,6 @@ module ActiveRecord # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil @table_alias_length = nil - @postgresql_version = nil @statements = {} connect @@ -259,28 +266,16 @@ module ActiveRecord # Is this connection alive and ready for queries? def active? - if @connection.respond_to?(:status) - @connection.status == PGconn::CONNECTION_OK - else - # We're asking the driver, not Active Record, so use @connection.query instead of #query - @connection.query 'SELECT 1' - true - end - # postgres-pr raises a NoMethodError when querying if no connection is available. - rescue PGError, NoMethodError + @connection.status == PGconn::CONNECTION_OK + rescue PGError false end # Close then reopen the connection. def reconnect! - if @connection.respond_to?(:reset) - clear_cache! - @connection.reset - configure_connection - else - disconnect! - connect - end + clear_cache! + @connection.reset + configure_connection end def reset! @@ -299,7 +294,7 @@ module ActiveRecord NATIVE_DATABASE_TYPES end - # Does PostgreSQL support migrations? + # Returns true, since this connection adapter supports migrations. def supports_migrations? true end @@ -325,6 +320,7 @@ module ActiveRecord true end + # Returns true, since this connection adapter supports savepoints. def supports_savepoints? true end @@ -433,17 +429,17 @@ module ActiveRecord # REFERENTIAL INTEGRITY ==================================== - def supports_disable_referential_integrity?() #:nodoc: + def supports_disable_referential_integrity? #:nodoc: true end def disable_referential_integrity #:nodoc: - if supports_disable_referential_integrity?() then + if supports_disable_referential_integrity? then execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";")) end yield ensure - if supports_disable_referential_integrity?() then + if supports_disable_referential_integrity? then execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";")) end end @@ -517,12 +513,7 @@ module ActiveRecord # Queries the database and returns the results in an Array-like object def query(sql, name = nil) #:nodoc: log(sql, name) do - if @async - res = @connection.async_exec(sql) - else - res = @connection.exec(sql) - end - return result_as_array(res) + result_as_array @connection.async_exec(sql) end end @@ -530,11 +521,7 @@ module ActiveRecord # or raising a PGError exception otherwise. def execute(sql, name = nil) log(sql, name) do - if @async - @connection.async_exec(sql) - else - @connection.exec(sql) - end + @connection.async_exec(sql) end end @@ -543,30 +530,27 @@ module ActiveRecord end def exec_query(sql, name = 'SQL', binds = []) - return exec_no_cache(sql, name) if binds.empty? - log(sql, name, binds) do - unless @statements.key? sql - nextkey = "a#{@statements.length + 1}" - @connection.prepare nextkey, sql - @statements[sql] = nextkey - end + result = binds.empty? ? exec_no_cache(sql, binds) : + exec_cache(sql, binds) - key = @statements[sql] - - # Clear the queue - @connection.get_last_result - @connection.send_query_prepared(key, binds.map { |col, val| - type_cast(val, col) - }) - @connection.block - result = @connection.get_last_result ret = ActiveRecord::Result.new(result.fields, result_as_array(result)) result.clear return ret end end + def exec_delete(sql, name = 'SQL', binds = []) + log(sql, name, binds) do + result = binds.empty? ? exec_no_cache(sql, binds) : + exec_cache(sql, binds) + affected = result.cmd_tuples + result.clear + affected + end + end + alias :exec_update :exec_delete + def sql_for_insert(sql, pk, id_value, sequence_name, binds) unless pk _, table = extract_schema_and_table(sql.split(" ", 4)[2]) @@ -699,7 +683,7 @@ module ActiveRecord [schema, table] end - # Returns the list of all indexes for a table. + # Returns an array of indexes for the given table. def indexes(table_name, name = nil) schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') result = query(<<-SQL, name) @@ -864,6 +848,9 @@ module ActiveRecord end # Renames a table. + # + # Example: + # rename_table('octopuses', 'octopi') def rename_table(name, new_name) execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}" end @@ -948,24 +935,9 @@ module ActiveRecord end protected - # Returns the version of the connected PostgreSQL version. + # Returns the version of the connected PostgreSQL server. def postgresql_version - @postgresql_version ||= - if @connection.respond_to?(:server_version) - @connection.server_version - else - # Mimic PGconn.server_version behavior - begin - if query('SELECT version()')[0][0] =~ /PostgreSQL ([0-9.]+)/ - major, minor, tiny = $1.split(".") - (major.to_i * 10000) + (minor.to_i * 100) + tiny.to_i - else - 0 - end - rescue - 0 - end - end + @connection.server_version end def translate_exception(exception, message) @@ -980,13 +952,26 @@ module ActiveRecord end private - def exec_no_cache(sql, name) - log(sql, name) do - result = @connection.async_exec(sql) - ret = ActiveRecord::Result.new(result.fields, result_as_array(result)) - result.clear - ret + def exec_no_cache(sql, binds) + @connection.async_exec(sql) + end + + def exec_cache(sql, binds) + unless @statements.key? sql + nextkey = "a#{@statements.length + 1}" + @connection.prepare nextkey, sql + @statements[sql] = nextkey end + + key = @statements[sql] + + # Clear the queue + @connection.get_last_result + @connection.send_query_prepared(key, binds.map { |col, val| + type_cast(val, col) + }) + @connection.block + @connection.get_last_result end # The internal PostgreSQL identifier of the money data type. @@ -998,10 +983,6 @@ module ActiveRecord # connected server's characteristics. def connect @connection = PGconn.connect(*@connection_parameters) - PGconn.translate_results = false if PGconn.respond_to?(:translate_results=) - - # Ignore async_exec and async_query when using postgres-pr. - @async = @connection.respond_to?(:async_exec) # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision @@ -1015,11 +996,7 @@ module ActiveRecord # This is called by #connect and should not be called manually. def configure_connection if @config[:encoding] - if @connection.respond_to?(:set_client_encoding) - @connection.set_client_encoding(@config[:encoding]) - else - execute("SET client_encoding TO '#{@config[:encoding]}'") - end + @connection.set_client_encoding(@config[:encoding]) end self.client_min_messages = @config[:min_messages] if @config[:min_messages] self.schema_search_path = @config[:schema_search_path] || @config[:schema_order] diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index ed5006dcec..d2785b234a 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -58,10 +58,12 @@ module ActiveRecord 'SQLite' end + # Returns true if SQLite version is '2.0.0' or greater, false otherwise. def supports_ddl_transactions? sqlite_version >= '2.0.0' end + # Returns true if SQLite version is '3.6.8' or greater, false otherwise. def supports_savepoints? sqlite_version >= '3.6.8' end @@ -72,7 +74,7 @@ module ActiveRecord true end - # Returns true. + # Returns true, since this connection adapter supports migrations. def supports_migrations? #:nodoc: true end @@ -86,6 +88,7 @@ module ActiveRecord true end + # Returns true if SQLite version is '3.1.6' or greater, false otherwise. def supports_add_column? sqlite_version >= '3.1.6' end @@ -103,10 +106,12 @@ module ActiveRecord @statements.clear end + # Returns true if SQLite version is '3.2.6' or greater, false otherwise. def supports_count_distinct? #:nodoc: sqlite_version >= '3.2.6' end + # Returns true if SQLite version is '3.1.0' or greater, false otherwise. def supports_autoincrement? #:nodoc: sqlite_version >= '3.1.0' end @@ -178,6 +183,12 @@ module ActiveRecord end end + def exec_delete(sql, name = 'SQL', binds = []) + exec_query(sql, name, binds) + @connection.changes + end + alias :exec_update :exec_delete + def last_inserted_id(result) @connection.last_insert_row_id end @@ -244,6 +255,7 @@ module ActiveRecord end end + # Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+. def columns(table_name, name = nil) #:nodoc: table_structure(table_name).map do |field| case field["dflt_value"] @@ -259,6 +271,7 @@ module ActiveRecord end end + # Returns an array of indexes for the given table. def indexes(table_name, name = nil) #:nodoc: exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row| IndexDefinition.new( @@ -282,6 +295,10 @@ module ActiveRecord exec_query "DROP INDEX #{quote_column_name(index_name)}" end + # Renames a table. + # + # Example: + # rename_table('octopuses', 'octopi') def rename_table(name, new_name) exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}" end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 0939ec2626..96fea741e0 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -509,7 +509,9 @@ class Fixtures # FIXME: Apparently JK uses this. connection = block_given? ? yield : ActiveRecord::Base.connection - files_to_read = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) } + files_to_read = table_names.reject { |table_name| + fixture_is_cached?(connection, table_name) + } unless files_to_read.empty? connection.disable_referential_integrity do @@ -521,7 +523,7 @@ class Fixtures fixtures_map[path] = Fixtures.new( connection, table_name, - class_names[table_name.to_sym], + class_names[table_name.to_sym] || table_name.classify, File.join(fixtures_directory, path)) end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 787ac977e0..b4531ed35f 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -77,7 +77,15 @@ module ActiveRecord def destroy if persisted? IdentityMap.remove(self) if IdentityMap.enabled? - self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all + pk = self.class.primary_key + column = self.class.columns_hash[pk] + substitute = connection.substitute_at(column, 0) + + relation = self.class.unscoped.where( + self.class.arel_table[pk].eq(substitute)) + + relation.bind_values = [[column, id]] + relation.delete_all end @destroyed = true diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 0502f51170..5703fac033 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -301,8 +301,8 @@ db_namespace = namespace :db do require 'active_record/fixtures' ActiveRecord::Base.establish_connection(Rails.env) - base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') - fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir + base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten + fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.{yml,csv}"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| Fixtures.create_fixtures(fixtures_dir, fixture_file) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 8e5f66ec1d..ae9afad48a 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -220,7 +220,7 @@ module ActiveRecord stmt.take limit if limit stmt.order(*order) stmt.key = table[primary_key] - @klass.connection.update stmt.to_sql + @klass.connection.update stmt.to_sql, 'SQL', bind_values end end @@ -338,7 +338,9 @@ module ActiveRecord where(conditions).delete_all else statement = arel.compile_delete - affected = @klass.connection.delete statement.to_sql + affected = @klass.connection.delete( + statement.to_sql, 'SQL', bind_values) + reset affected end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 982b3d7e9f..2814771002 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -25,7 +25,18 @@ module ActiveRecord values = value.to_a.map { |x| x.is_a?(ActiveRecord::Base) ? x.id : x } - attribute.in(values) + + if values.include?(nil) + values = values.compact + if values.empty? + attribute.eq nil + else + attribute.in(values.compact).or attribute.eq(nil) + end + else + attribute.in(values) + end + when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index d73fce9fd0..de36dd20b3 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -30,7 +30,7 @@ module ActiveRecord include ActiveModel::Validations module ClassMethods - # Creates an object just like Base.create but calls save! instead of save + # Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+ # so an exception is raised if the record is invalid. def create!(attributes = nil, &block) if attributes.is_a?(Array) @@ -44,13 +44,13 @@ module ActiveRecord end end - # The validation process on save can be skipped by passing :validate => false. The regular Base#save method is + # The validation process on save can be skipped by passing <tt>:validate => false</tt>. The regular Base#save method is # replaced with this when the validations module is mixed in, which it is by default. def save(options={}) perform_validations(options) ? super : false end - # Attempts to save the record just like Base#save but will raise a RecordInvalid exception instead of returning false + # Attempts to save the record just like Base#save but will raise a +RecordInvalid+ exception instead of returning false # if the record is not valid. def save!(options={}) perform_validations(options) ? super : raise(RecordInvalid.new(self)) |