aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2010-10-28 18:06:01 +0100
committerJon Leighton <j@jonathanleighton.com>2010-10-28 18:06:01 +0100
commitfc276e5635821e65c04b8961170cc6bd3c11b923 (patch)
tree9bd4ed018858611248e9b6aa7392dced92bc814a /activerecord
parent8e53e058acea471eab7a1609dc150aa9fdbfa833 (diff)
parent6a3d6b7f1352efd3e7b931533740252b04850e27 (diff)
downloadrails-fc276e5635821e65c04b8961170cc6bd3c11b923.tar.gz
rails-fc276e5635821e65c04b8961170cc6bd3c11b923.tar.bz2
rails-fc276e5635821e65c04b8961170cc6bd3c11b923.zip
Merge branch 'master' into nested_has_many_through
Conflicts: activerecord/CHANGELOG activerecord/lib/active_record/association_preload.rb activerecord/lib/active_record/associations.rb activerecord/test/schema/schema.rb
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG3
-rw-r--r--activerecord/lib/active_record/association_preload.rb2
-rw-r--r--activerecord/lib/active_record/associations.rb32
-rw-r--r--activerecord/lib/active_record/autosave_association.rb11
-rw-r--r--activerecord/lib/active_record/base.rb49
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb16
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb135
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb72
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb61
-rw-r--r--activerecord/lib/active_record/named_scope.rb6
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/lib/active_record/relation.rb4
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb7
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb17
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb18
-rw-r--r--activerecord/lib/active_record/result.rb30
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb58
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb51
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb32
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb62
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb4
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb6
-rw-r--r--activerecord/test/cases/autosave_association_test.rb20
-rw-r--r--activerecord/test/cases/base_test.rb7
-rw-r--r--activerecord/test/cases/helper.rb8
-rw-r--r--activerecord/test/cases/migration_test.rb8
-rw-r--r--activerecord/test/cases/named_scope_test.rb6
-rw-r--r--activerecord/test/cases/query_cache_test.rb8
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb46
-rw-r--r--activerecord/test/cases/relations_test.rb16
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb4
-rw-r--r--activerecord/test/cases/transactions_test.rb2
-rw-r--r--activerecord/test/connections/native_oracle/connection.rb4
-rw-r--r--activerecord/test/models/eye.rb37
-rw-r--r--activerecord/test/models/post.rb6
-rw-r--r--activerecord/test/models/topic.rb7
-rw-r--r--activerecord/test/schema/schema.rb113
39 files changed, 811 insertions, 209 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 191890aefc..06c1428217 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -2,6 +2,9 @@
* Associations with a :through option can now use *any* association as the through or source association, including other associations which have a :through option and has_and_belongs_to_many associations #1812 [Jon Leighton]
+* has_one maintains the association with separate after_create/after_update instead
+ of a single after_save. [fxn]
+
* The following code:
Model.limit(10).scoping { Model.count }
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 664b0a7d59..bafd59fc02 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -254,8 +254,6 @@ module ActiveRecord
alias_method :preload_has_many_association, :preload_has_one_or_has_many_association
def preload_through_records(records, reflection, through_association)
- through_reflection = reflections[through_association]
-
# If the same through record is loaded twice, we want to return exactly the same
# object in the result, rather than two separate instances representing the same
# record. This is so that we can preload the source association for each record,
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 25be9a52ff..ee2e2765bc 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -2043,7 +2043,7 @@ module ActiveRecord
association_proxy = record.send("set_#{join_part.reflection.name}_target", association)
association_proxy.__send__(:set_inverse_instance, association, record)
end
-
+
# A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited
# by JoinBase and JoinAssociation. A JoinBase represents the Active Record which
# everything else is being joined onto. A JoinAssociation represents an association which
@@ -2055,38 +2055,38 @@ module ActiveRecord
# this is the actual base model, for a JoinAssociation this is the target model of the
# association.
attr_reader :active_record
-
+
delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record
-
+
def initialize(active_record)
@active_record = active_record
@cached_record = {}
end
-
+
def ==(other)
raise NotImplementedError
end
-
+
# An Arel::Table for the active_record
def table
raise NotImplementedError
end
-
+
# The prefix to be used when aliasing columns in the active_record's table
def aliased_prefix
raise NotImplementedError
end
-
+
# The alias for the active_record's table
def aliased_table_name
raise NotImplementedError
end
-
+
# The alias for the primary key of the active_record's table
def aliased_primary_key
"#{aliased_prefix}_r0"
end
-
+
# An array of [column_name, alias] pairs for the table
def column_names_with_alias
unless defined?(@column_names_with_alias)
@@ -2116,7 +2116,7 @@ module ActiveRecord
class JoinBase < JoinPart # :nodoc:
# Extra joins provided when the JoinDependency was created
attr_reader :table_joins
-
+
def initialize(active_record, joins = nil)
super(active_record)
@table_joins = joins
@@ -2144,16 +2144,16 @@ module ActiveRecord
class JoinAssociation < JoinPart # :nodoc:
# The reflection of the association represented
attr_reader :reflection
-
+
# The JoinDependency object which this JoinAssociation exists within. This is mainly
# relevant for generating aliases which do not conflict with other joins which are
# part of the query.
attr_reader :join_dependency
-
+
# A JoinBase instance representing the active record we are joining onto.
# (So in Author.has_many :posts, the Author would be that base record.)
attr_reader :parent
-
+
# What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
attr_accessor :join_type
@@ -2165,7 +2165,7 @@ module ActiveRecord
def initialize(reflection, join_dependency, parent = nil)
reflection.check_validity!
-
+
if reflection.options[:polymorphic]
raise EagerLoadPolymorphicError.new(reflection)
end
@@ -2192,7 +2192,7 @@ module ActiveRecord
self.parent == join_part
end
end
-
+
def join_to(relation)
# The chain starts with the target table, but we want to end with it here (makes
# more sense in this context)
@@ -2282,7 +2282,7 @@ module ActiveRecord
self.join_type = Arel::OuterJoin
joining_relation.joins(self)
end
-
+
def table
if @tables.last.is_a?(Array)
@tables.last.first
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index f3f89fe7c3..0b89a49896 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -167,7 +167,16 @@ module ActiveRecord
else
if reflection.macro == :has_one
define_method(save_method) { save_has_one_association(reflection) }
- after_save save_method
+ # Configures two callbacks instead of a single after_save so that
+ # the model may rely on their execution order relative to its
+ # own callbacks.
+ #
+ # For example, given that after_creates run before after_saves, if
+ # we configured instead an after_save there would be no way to fire
+ # a custom after_create callback after the child association gets
+ # created.
+ after_create save_method
+ after_update save_method
else
define_method(save_method) { save_belongs_to_association(reflection) }
before_save save_method
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 78b3507dd9..06a388cd21 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -448,8 +448,8 @@ module ActiveRecord #:nodoc:
# # You can use the same string replacement techniques as you can with ActiveRecord#find
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
# > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...]
- def find_by_sql(sql)
- connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
+ def find_by_sql(sql, binds = [])
+ connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
end
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -718,6 +718,7 @@ module ActiveRecord #:nodoc:
# end
# end
def reset_column_information
+ connection.clear_cache!
undefine_attribute_methods
@column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
@arel_engine = @relation = @arel_table = nil
@@ -972,14 +973,11 @@ module ActiveRecord #:nodoc:
super unless all_attributes_exists?(attribute_names)
if match.scope?
self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
- def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
- options = args.extract_options! # options = args.extract_options!
- attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
- [:#{attribute_names.join(',:')}], args # [:user_name, :password], args
- ) # )
- #
- scoped(:conditions => attributes) # scoped(:conditions => attributes)
- end # end
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
+ attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
+ #
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
+ end # end
METHOD
send(method_id, *arguments)
end
@@ -988,31 +986,22 @@ module ActiveRecord #:nodoc:
end
end
- def construct_attributes_from_arguments(attribute_names, arguments)
- attributes = {}
- attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
- attributes
- end
-
# Similar in purpose to +expand_hash_conditions_for_aggregates+.
def expand_attribute_names_for_aggregates(attribute_names)
- expanded_attribute_names = []
- attribute_names.each do |attribute_name|
+ attribute_names.map { |attribute_name|
unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
- aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
- expanded_attribute_names << field_attr
+ aggregate_mapping(aggregation).map do |field_attr, _|
+ field_attr.to_sym
end
else
- expanded_attribute_names << attribute_name
+ attribute_name.to_sym
end
- end
- expanded_attribute_names
+ }.flatten
end
def all_attributes_exists?(attribute_names)
- expand_attribute_names_for_aggregates(attribute_names).all? { |name|
- column_methods_hash.include?(name.to_sym)
- }
+ (expand_attribute_names_for_aggregates(attribute_names) -
+ column_methods_hash.keys).empty?
end
protected
@@ -1139,11 +1128,17 @@ MSG
# Article.new.published # => true
# Article.create.published # => true
def default_scope(options = {})
+ reset_scoped_methods
self.default_scoping << construct_finder_arel(options, default_scoping.pop)
end
def current_scoped_methods #:nodoc:
- scoped_methods.last
+ method = scoped_methods.last
+ if method.respond_to?(:call)
+ relation.scoping { method.call }
+ else
+ method
+ end
end
def reset_scoped_methods #:nodoc:
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 646a78622c..6178130c00 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -3,8 +3,16 @@ module ActiveRecord
module DatabaseStatements
# Returns an array of record hashes with the column names as keys and
# column values as values.
- def select_all(sql, name = nil)
- select(sql, name)
+ def select_all(sql, name = nil, binds = [])
+ if supports_statement_cache?
+ select(sql, name, binds)
+ else
+ return select(sql, name) if binds.empty?
+ binds = binds.dup
+ select sql.gsub('?') {
+ quote(*binds.shift.reverse)
+ }, name
+ end
end
# Returns a record hash with the column names as keys and column values
@@ -39,6 +47,12 @@ module ActiveRecord
end
undef_method :execute
+ # Executes +sql+ statement in the context of this connection using
+ # +binds+ as the bind substitutes. +name+ is logged along with
+ # the executed +sql+ statement.
+ def exec(sql, name = 'SQL', binds = [])
+ end
+
# Returns the last auto-generated ID from the affected table.
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
insert_sql(sql, name, pk, id_value, sequence_name)
@@ -68,6 +82,12 @@ module ActiveRecord
nil
end
+ # Returns +true+ when the connection adapter supports prepared statement
+ # caching, otherwise returns +false+
+ def supports_statement_cache?
+ false
+ end
+
# Runs the given block in a database transaction, and returns the result
# of the block.
#
@@ -254,7 +274,7 @@ module ActiveRecord
protected
# Returns an array of record hashes with the column names as keys and
# column values as values.
- def select(sql, name = nil)
+ def select(sql, name = nil, binds = [])
end
undef_method :select
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 0ee61d0b6f..d555308485 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/duplicable'
-
module ActiveRecord
module ConnectionAdapters # :nodoc:
module QueryCache
@@ -49,32 +47,26 @@ module ActiveRecord
@query_cache.clear
end
- def select_all(*args)
+ def select_all(sql, name = nil, binds = [])
if @query_cache_enabled
- cache_sql(args.first) { super }
+ cache_sql(sql, binds) { super }
else
super
end
end
private
- def cache_sql(sql)
+ def cache_sql(sql, binds)
result =
- if @query_cache.has_key?(sql)
+ if @query_cache[sql].key?(binds)
ActiveSupport::Notifications.instrument("sql.active_record",
:sql => sql, :name => "CACHE", :connection_id => self.object_id)
- @query_cache[sql]
+ @query_cache[sql][binds]
else
- @query_cache[sql] = yield
+ @query_cache[sql][binds] = yield
end
- if Array === result
- result.collect { |row| row.dup }
- else
- result.duplicable? ? result.dup : result
- end
- rescue TypeError
- result
+ result.collect { |row| row.dup }
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index d8c92d0ad3..f3fba9a3a9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -12,6 +12,7 @@ require 'active_record/connection_adapters/abstract/connection_pool'
require 'active_record/connection_adapters/abstract/connection_specification'
require 'active_record/connection_adapters/abstract/query_cache'
require 'active_record/connection_adapters/abstract/database_limits'
+require 'active_record/result'
module ActiveRecord
module ConnectionAdapters # :nodoc:
@@ -40,7 +41,7 @@ module ActiveRecord
@active = nil
@connection, @logger = connection, logger
@query_cache_enabled = false
- @query_cache = {}
+ @query_cache = Hash.new { |h,sql| h[sql] = {} }
@instrumenter = ActiveSupport::Notifications.instrumenter
end
@@ -97,6 +98,12 @@ module ActiveRecord
quote_column_name(name)
end
+ # Returns a bind substitution value given a +column+ and list of current
+ # +binds+
+ def substitute_for(column, binds)
+ Arel.sql '?'
+ end
+
# REFERENTIAL INTEGRITY ====================================
# Override to turn off referential integrity while executing <tt>&block</tt>.
@@ -135,6 +142,13 @@ module ActiveRecord
# this should be overridden by concrete adapters
end
+ ###
+ # Clear any caching the database adapter may be doing, for example
+ # clearing the prepared statement cache. This is database specific.
+ def clear_cache!
+ # this should be overridden by concrete adapters
+ end
+
# Returns true if its required to reload the connection between requests for development mode.
# This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
def requires_reloading?
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index a4b336dfaf..7d47d06ae1 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -3,6 +3,28 @@ 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
+
+class Mysql
+ class Time
+ ###
+ # This monkey patch is for test_additional_columns_from_join_table
+ def to_date
+ Date.new(year, month, day)
+ end
+ end
+ class Stmt; include Enumerable end
+ class Result; include Enumerable end
+end
+
module ActiveRecord
class Base
# Establishes a connection to the database that's used by all Active Record objects.
@@ -15,18 +37,6 @@ module ActiveRecord
password = config[:password].to_s
database = config[:database]
- unless defined? Mysql
- begin
- require 'mysql'
- rescue LoadError
- raise "!!! Missing the mysql2 gem. Add it to your Gemfile: gem 'mysql2'"
- 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
- end
-
mysql = Mysql.init
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
@@ -39,6 +49,30 @@ module ActiveRecord
module ConnectionAdapters
class MysqlColumn < Column #:nodoc:
+ class << self
+ def string_to_time(value)
+ return super unless Mysql::Time === value
+ new_time(
+ value.year,
+ value.month,
+ value.day,
+ value.hour,
+ value.minute,
+ value.second,
+ value.second_part)
+ end
+
+ def string_to_dummy_time(v)
+ return super unless Mysql::Time === v
+ new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
+ end
+
+ def string_to_date(v)
+ return super unless Mysql::Time === v
+ new_date(v.year, v.month, v.day)
+ end
+ end
+
def extract_default(default)
if sql_type =~ /blob/i || type == :text
if default.blank?
@@ -161,6 +195,7 @@ module ActiveRecord
super(connection, logger)
@connection_options, @config = connection_options, config
@quoted_column_names, @quoted_table_names = {}, {}
+ @statements = {}
connect
end
@@ -168,6 +203,12 @@ module ActiveRecord
ADAPTER_NAME
end
+ # Returns +true+ when the connection adapter supports prepared statement
+ # caching, otherwise returns +false+
+ def supports_statement_cache?
+ true
+ end
+
def supports_migrations? #:nodoc:
true
end
@@ -252,6 +293,7 @@ module ActiveRecord
def reconnect!
disconnect!
+ clear_cache!
connect
end
@@ -272,14 +314,63 @@ module ActiveRecord
def select_rows(sql, name = nil)
@connection.query_with_result = true
- result = execute(sql, name)
- rows = []
- result.each { |row| rows << row }
- result.free
+ rows = exec_without_stmt(sql, name).rows
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
+ def clear_cache!
+ @statements.values.each do |cache|
+ cache[:stmt].close
+ end
+ @statements.clear
+ end
+
+ def exec(sql, name = 'SQL', binds = [])
+ log(sql, name) 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|
+ col ? col.type_cast(val) : val
+ })
+ 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)
+ end
+
+ stmt.free_result
+ stmt.close if binds.empty?
+
+ result
+ end
+ end
+
+ def exec_without_stmt(sql, name = 'SQL') # :nodoc:
+ # Some queries, like SHOW CREATE TABLE don't work through the prepared
+ # statement API. For those queries, we need to use this method. :'(
+ log(sql, name) do
+ result = @connection.query(sql)
+ cols = result.fetch_fields.map { |field| field.name }
+ rows = result.to_a
+ result.free
+ ActiveRecord::Result.new(cols, rows)
+ end
+ end
+
# Executes an SQL query and returns a MySQL::Result object. Note that you have to free
# the Result object after you're done using it.
def execute(sql, name = nil) #:nodoc:
@@ -308,7 +399,7 @@ module ActiveRecord
end
def begin_db_transaction #:nodoc:
- execute "BEGIN"
+ exec_without_stmt "BEGIN"
rescue Exception
# Transactions aren't supported
end
@@ -360,7 +451,8 @@ module ActiveRecord
select_all(sql).map do |table|
table.delete('Table_type')
- select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
+ sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
+ exec_without_stmt(sql).first['Create Table'] + ";\n\n"
end.join("")
end
@@ -614,12 +706,9 @@ module ActiveRecord
execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
end
- def select(sql, name = nil)
+ def select(sql, name = nil, binds = [])
@connection.query_with_result = true
- result = execute(sql, name)
- rows = []
- result.each_hash { |row| rows << row }
- result.free
+ rows = exec(sql, name, binds).to_a
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 5949985e4d..58c0f85dc2 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -211,6 +211,12 @@ module ActiveRecord
ADAPTER_NAME
end
+ # Returns +true+ when the connection adapter supports prepared statement
+ # caching, otherwise returns +false+
+ def supports_statement_cache?
+ true
+ end
+
# Initializes and connects a PostgreSQL adapter.
def initialize(connection, logger, connection_parameters, config)
super(connection, logger)
@@ -220,11 +226,19 @@ module ActiveRecord
@local_tz = nil
@table_alias_length = nil
@postgresql_version = nil
+ @statements = {}
connect
@local_tz = execute('SHOW TIME ZONE').first["TimeZone"]
end
+ def clear_cache!
+ @statements.each_value do |value|
+ @connection.query "DEALLOCATE #{value}"
+ end
+ @statements.clear
+ end
+
# Is this connection alive and ready for queries?
def active?
if @connection.respond_to?(:status)
@@ -242,6 +256,7 @@ module ActiveRecord
# Close then reopen the connection.
def reconnect!
if @connection.respond_to?(:reset)
+ clear_cache!
@connection.reset
configure_connection
else
@@ -250,8 +265,14 @@ module ActiveRecord
end
end
+ def reset!
+ clear_cache!
+ super
+ end
+
# Close the connection.
def disconnect!
+ clear_cache!
@connection.close rescue nil
end
@@ -374,6 +395,12 @@ module ActiveRecord
end
end
+ # Set the authorized user for this session
+ def session_auth=(user)
+ clear_cache!
+ exec "SET SESSION AUTHORIZATION #{user}"
+ end
+
# REFERENTIAL INTEGRITY ====================================
def supports_disable_referential_integrity?() #:nodoc:
@@ -501,6 +528,35 @@ module ActiveRecord
end
end
+ def substitute_for(column, current_values)
+ Arel.sql("$#{current_values.length + 1}")
+ end
+
+ def exec(sql, name = 'SQL', binds = [])
+ return exec_no_cache(sql, name) if binds.empty?
+
+ log(sql, name) do
+ 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|
+ col ? col.type_cast(val) : val
+ })
+ @connection.block
+ result = @connection.get_last_result
+ ret = ActiveRecord::Result.new(result.fields, result_as_array(result))
+ result.clear
+ return ret
+ end
+ end
+
# Executes an UPDATE query and returns the number of affected tuples.
def update_sql(sql, name = nil)
super.cmd_tuples
@@ -920,6 +976,15 @@ 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
+ end
+ end
+
# The internal PostgreSQL identifier of the money data type.
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
# The internal PostgreSQL identifier of the BYTEA data type.
@@ -974,11 +1039,8 @@ module ActiveRecord
# Executes a SELECT query and returns the results, performing any data type
# conversions that are required to be performed here instead of in PostgreSQLColumn.
- def select(sql, name = nil)
- fields, rows = select_raw(sql, name)
- rows.map do |row|
- Hash[fields.zip(row)]
- end
+ def select(sql, name = nil, binds = [])
+ exec(sql, name, binds).to_a
end
def select_raw(sql, name = nil)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index c0cc7ba20d..a4d7d12298 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -50,6 +50,7 @@ module ActiveRecord
def initialize(connection, logger, config)
super(connection, logger)
+ @statements = {}
@config = config
end
@@ -61,6 +62,12 @@ module ActiveRecord
sqlite_version >= '2.0.0'
end
+ # Returns +true+ when the connection adapter supports prepared statement
+ # caching, otherwise returns +false+
+ def supports_statement_cache?
+ true
+ end
+
def supports_migrations? #:nodoc:
true
end
@@ -79,9 +86,14 @@ module ActiveRecord
def disconnect!
super
+ clear_cache!
@connection.close rescue nil
end
+ def clear_cache!
+ @statements.clear
+ end
+
def supports_count_distinct? #:nodoc:
sqlite_version >= '3.2.6'
end
@@ -121,7 +133,7 @@ module ActiveRecord
# Quote date/time values for use in SQL input. Includes microseconds
# if the value is a Time responding to usec.
def quoted_date(value) #:nodoc:
- if value.acts_like?(:time) && value.respond_to?(:usec)
+ if value.respond_to?(:usec)
"#{super}.#{sprintf("%06d", value.usec)}"
else
super
@@ -131,6 +143,29 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
+ def exec(sql, name = nil, binds = [])
+ log(sql, name) do
+
+ # Don't cache statements without bind values
+ if binds.empty?
+ stmt = @connection.prepare(sql)
+ cols = stmt.columns
+ else
+ cache = @statements[sql] ||= {
+ :stmt => @connection.prepare(sql)
+ }
+ stmt = cache[:stmt]
+ cols = cache[:cols] ||= stmt.columns
+ stmt.reset!
+ stmt.bind_params binds.map { |col, val|
+ col ? col.type_cast(val) : val
+ }
+ end
+
+ ActiveRecord::Result.new(cols, stmt.to_a)
+ end
+ end
+
def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.execute(sql) }
end
@@ -151,9 +186,7 @@ module ActiveRecord
alias :create :insert_sql
def select_rows(sql, name = nil)
- execute(sql, name).map do |row|
- (0...(row.size / 2)).map { |i| row[i] }
- end
+ exec(sql, name).rows
end
def begin_db_transaction #:nodoc:
@@ -177,7 +210,7 @@ module ActiveRecord
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
SQL
- execute(sql, name).map do |row|
+ exec(sql, name).map do |row|
row['name']
end
end
@@ -189,12 +222,12 @@ module ActiveRecord
end
def indexes(table_name, name = nil) #:nodoc:
- execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
+ exec("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
IndexDefinition.new(
table_name,
row['name'],
row['unique'].to_i != 0,
- execute("PRAGMA index_info('#{row['name']}')").map { |col|
+ exec("PRAGMA index_info('#{row['name']}')").map { |col|
col['name']
})
end
@@ -208,11 +241,11 @@ module ActiveRecord
end
def remove_index!(table_name, index_name) #:nodoc:
- execute "DROP INDEX #{quote_column_name(index_name)}"
+ exec "DROP INDEX #{quote_column_name(index_name)}"
end
def rename_table(name, new_name)
- execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
+ exec "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
end
# See: http://www.sqlite.org/lang_altertable.html
@@ -249,7 +282,7 @@ module ActiveRecord
def change_column_null(table_name, column_name, null, default = nil)
unless null || default.nil?
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
+ exec("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end
alter_table(table_name) do |definition|
definition[column_name].null = null
@@ -280,8 +313,8 @@ module ActiveRecord
end
protected
- def select(sql, name = nil) #:nodoc:
- execute(sql, name).map do |row|
+ def select(sql, name = nil, binds = []) #:nodoc:
+ exec(sql, name, binds).map do |row|
record = {}
row.each do |key, value|
record[key.sub(/^"?\w+"?\./, '')] = value if key.is_a?(String)
@@ -367,11 +400,11 @@ module ActiveRecord
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
quoted_to = quote_table_name(to)
- @connection.execute "SELECT * FROM #{quote_table_name(from)}" do |row|
+ exec("SELECT * FROM #{quote_table_name(from)}").each do |row|
sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
sql << ')'
- @connection.execute sql
+ exec sql
end
end
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 6ab84df25b..0b92ba5caa 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -97,14 +97,14 @@ module ActiveRecord
#
# Article.published.new.published # => true
# Article.published.create.published # => true
- def scope(name, scope_options = {}, &block)
+ def scope(name, scope_options = {})
name = name.to_sym
valid_scope_name?(name)
- extension = Module.new(&block) if block_given?
+ extension = Module.new(&Proc.new) if block_given?
scopes[name] = lambda do |*args|
- options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options
+ options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options
relation = if options.is_a?(Hash)
scoped.apply_finder_options(options)
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 824674ee1d..6eb2057f66 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -36,7 +36,7 @@ module ActiveRecord
# Returns an array of AggregateReflection objects for all the aggregations in the class.
def reflect_on_all_aggregations
- reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
+ reflections.values.grep(AggregateReflection)
end
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
@@ -58,7 +58,7 @@ module ActiveRecord
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
#
def reflect_on_all_associations(macro = nil)
- association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) }
+ association_reflections = reflections.values.grep(AssociationReflection)
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index f129b54f9a..58f7b74198 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -5,7 +5,7 @@ module ActiveRecord
class Relation
JoinOperation = Struct.new(:relation, :join_class, :on)
ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
- MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having]
+ MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from]
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
@@ -61,7 +61,7 @@ module ActiveRecord
def to_a
return @records if loaded?
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql)
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
preload = @preload_values
preload += @includes_values unless eager_loading?
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index b763e22ec6..fe1ef2e2e3 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -288,7 +288,12 @@ module ActiveRecord
def find_one(id)
id = id.id if ActiveRecord::Base === id
- record = where(primary_key.eq(id)).first
+ column = primary_key.column
+
+ substitute = connection.substitute_for(column, @bind_values)
+ relation = where(primary_key.eq(substitute))
+ relation.bind_values = [[column, id]]
+ record = relation.first
unless record
conditions = arel.where_sql
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 59ce76ea42..9c399d3333 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -6,7 +6,8 @@ module ActiveRecord
extend ActiveSupport::Concern
attr_accessor :includes_values, :eager_load_values, :preload_values,
- :select_values, :group_values, :order_values, :joins_values, :where_values, :having_values,
+ :select_values, :group_values, :order_values, :joins_values,
+ :where_values, :having_values, :bind_values,
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value
def includes(*args)
@@ -62,6 +63,12 @@ module ActiveRecord
relation
end
+ def bind(value)
+ relation = clone
+ relation.bind_values += [value]
+ relation
+ end
+
def where(opts, *rest)
relation = clone
relation.where_values += build_where(opts, rest) unless opts.blank?
@@ -117,8 +124,10 @@ module ActiveRecord
relation
end
- def extending(*modules, &block)
- modules << Module.new(&block) if block_given?
+ def extending(*modules)
+ modules << Module.new(&Proc.new) if block_given?
+
+ return self if modules.empty?
relation = clone
relation.send(:apply_modules, modules.flatten)
@@ -233,7 +242,7 @@ module ActiveRecord
@implicit_readonly = false
arel.project(*selects)
else
- arel.project(Arel::SqlLiteral.new(@klass.quoted_table_name + '.*'))
+ arel.project(Arel.sql(@klass.quoted_table_name + '.*'))
end
end
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index dcddc3dac4..648a02f1cc 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -26,19 +26,23 @@ module ActiveRecord
merged_relation = merged_relation.joins(r.joins_values)
- merged_wheres = @where_values
+ merged_wheres = @where_values + r.where_values
- r.where_values.each do |w|
+ # Remove duplicates, last one wins.
+ seen = {}
+ merged_wheres = merged_wheres.reverse.reject { |w|
+ nuke = false
if w.respond_to?(:operator) && w.operator == :==
- merged_wheres = merged_wheres.reject {|p| p.respond_to?(:operator) && p.operator == :== && p.operand1.name == w.operand1.name }
+ name = w.left.name
+ nuke = seen[name]
+ seen[name] = true
end
-
- merged_wheres += [w]
- end
+ nuke
+ }.reverse
merged_relation.where_values = merged_wheres
- Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:lock]).each do |method|
value = r.send(:"#{method}_value")
merged_relation.send(:"#{method}_value=", value) unless value.nil?
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
new file mode 100644
index 0000000000..8deff1478f
--- /dev/null
+++ b/activerecord/lib/active_record/result.rb
@@ -0,0 +1,30 @@
+module ActiveRecord
+ ###
+ # This class encapsulates a Result returned from calling +exec+ on any
+ # database connection adapter. For example:
+ #
+ # x = ActiveRecord::Base.connection.exec('SELECT * FROM foo')
+ # x # => #<ActiveRecord::Result:0xdeadbeef>
+ class Result
+ include Enumerable
+
+ attr_reader :columns, :rows
+
+ def initialize(columns, rows)
+ @columns = columns
+ @rows = rows
+ @hash_rows = nil
+ end
+
+ def each
+ hash_rows.each { |row| yield row }
+ end
+
+ private
+ def hash_rows
+ @hash_rows ||= @rows.map { |row|
+ ActiveSupport::OrderedHash[@columns.zip(row)]
+ }
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index f76a23a8ad..67bd8ec7e0 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -43,6 +43,64 @@ class MysqlConnectionTest < ActiveRecord::TestCase
assert @connection.active?
end
+ def test_bind_value_substitute
+ bind_param = @connection.substitute_for('foo', [])
+ assert_equal Arel.sql('?'), bind_param
+ end
+
+ def test_exec_no_binds
+ @connection.exec('drop table if exists ex')
+ @connection.exec(<<-eosql)
+ CREATE TABLE `ex` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY,
+ `data` varchar(255))
+ eosql
+ result = @connection.exec('SELECT id, data FROM ex')
+ assert_equal 0, result.rows.length
+ assert_equal 2, result.columns.length
+ assert_equal %w{ id data }, result.columns
+
+ @connection.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ result = @connection.exec('SELECT id, data FROM ex')
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
+ end
+
+ def test_exec_with_binds
+ @connection.exec('drop table if exists ex')
+ @connection.exec(<<-eosql)
+ CREATE TABLE `ex` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY,
+ `data` varchar(255))
+ eosql
+ @connection.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ result = @connection.exec(
+ 'SELECT id, data FROM ex WHERE id = ?', nil, [[nil, 1]])
+
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
+ end
+
+ def test_exec_typecasts_bind_vals
+ @connection.exec('drop table if exists ex')
+ @connection.exec(<<-eosql)
+ CREATE TABLE `ex` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY,
+ `data` varchar(255))
+ eosql
+ @connection.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ column = @connection.columns('ex').find { |col| col.name == 'id' }
+
+ result = @connection.exec(
+ 'SELECT id, data FROM ex WHERE id = ?', nil, [[column, '1-fuu']])
+
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
+ end
+
# Test that MySQL allows multiple results for stored procedures
if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
def test_multi_results
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 7b72151b57..b0fd2273df 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -5,6 +5,8 @@ module ActiveRecord
class PostgreSQLAdapterTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
+ @connection.exec('drop table if exists ex')
+ @connection.exec('create table ex(id serial primary key, data character varying(255))')
end
def test_table_alias_length
@@ -12,6 +14,55 @@ module ActiveRecord
@connection.table_alias_length
end
end
+
+ def test_exec_no_binds
+ result = @connection.exec('SELECT id, data FROM ex')
+ assert_equal 0, result.rows.length
+ assert_equal 2, result.columns.length
+ assert_equal %w{ id data }, result.columns
+
+ string = @connection.quote('foo')
+ @connection.exec("INSERT INTO ex (id, data) VALUES (1, #{string})")
+ result = @connection.exec('SELECT id, data FROM ex')
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [['1', 'foo']], result.rows
+ end
+
+ def test_exec_with_binds
+ string = @connection.quote('foo')
+ @connection.exec("INSERT INTO ex (id, data) VALUES (1, #{string})")
+ result = @connection.exec(
+ 'SELECT id, data FROM ex WHERE id = $1', nil, [[nil, 1]])
+
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [['1', 'foo']], result.rows
+ end
+
+ def test_exec_typecasts_bind_vals
+ string = @connection.quote('foo')
+ @connection.exec("INSERT INTO ex (id, data) VALUES (1, #{string})")
+
+ column = @connection.columns('ex').find { |col| col.name == 'id' }
+ result = @connection.exec(
+ 'SELECT id, data FROM ex WHERE id = $1', nil, [[column, '1-fuu']])
+
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [['1', 'foo']], result.rows
+ end
+
+ def test_substitute_for
+ bind = @connection.substitute_for(nil, [])
+ assert_equal Arel.sql('$1'), bind
+
+ bind = @connection.substitute_for(nil, [nil])
+ assert_equal Arel.sql('$2'), bind
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index 6f372edc38..881631fb19 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -43,6 +43,36 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase
end
end
+ def test_session_auth=
+ assert_raise(ActiveRecord::StatementInvalid) do
+ @connection.session_auth = 'DEFAULT'
+ @connection.execute "SELECT * FROM #{TABLE_NAME}"
+ end
+ end
+
+ def test_setting_auth_clears_stmt_cache
+ assert_nothing_raised do
+ set_session_auth
+ USERS.each do |u|
+ set_session_auth u
+ assert_equal u, @connection.exec("SELECT name FROM #{TABLE_NAME} WHERE id = $1", 'SQL', [[nil, 1]]).first['name']
+ set_session_auth
+ end
+ end
+ end
+
+ def test_auth_with_bind
+ assert_nothing_raised do
+ set_session_auth
+ USERS.each do |u|
+ @connection.clear_cache!
+ set_session_auth u
+ assert_equal u, @connection.exec("SELECT name FROM #{TABLE_NAME} WHERE id = $1", 'SQL', [[nil, 1]]).first['name']
+ set_session_auth
+ end
+ end
+ end
+
def test_schema_uniqueness
assert_nothing_raised do
set_session_auth
@@ -78,7 +108,7 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase
private
def set_session_auth auth = nil
- @connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}"
+ @connection.session_auth = auth || 'default'
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 934cf72f72..973c51f3d7 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -3,6 +3,12 @@ require "cases/helper"
module ActiveRecord
module ConnectionAdapters
class SQLite3AdapterTest < ActiveRecord::TestCase
+ def setup
+ @conn = Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => 100
+ end
+
def test_connection_no_db
assert_raises(ArgumentError) do
Base.sqlite3_connection {}
@@ -38,18 +44,58 @@ module ActiveRecord
end
def test_connect
- conn = Base.sqlite3_connection :database => ':memory:',
- :adapter => 'sqlite3',
- :timeout => 100
- assert conn, 'should have connection'
+ assert @conn, 'should have connection'
end
# sqlite3 defaults to UTF-8 encoding
def test_encoding
- conn = Base.sqlite3_connection :database => ':memory:',
- :adapter => 'sqlite3',
- :timeout => 100
- assert_equal 'UTF-8', conn.encoding
+ assert_equal 'UTF-8', @conn.encoding
+ end
+
+ def test_bind_value_substitute
+ bind_param = @conn.substitute_for('foo', [])
+ assert_equal Arel.sql('?'), bind_param
+ end
+
+ def test_exec_no_binds
+ @conn.exec('create table ex(id int, data string)')
+ result = @conn.exec('SELECT id, data FROM ex')
+ assert_equal 0, result.rows.length
+ assert_equal 2, result.columns.length
+ assert_equal %w{ id data }, result.columns
+
+ @conn.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ result = @conn.exec('SELECT id, data FROM ex')
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
+ end
+
+ def test_exec_with_binds
+ @conn.exec('create table ex(id int, data string)')
+ @conn.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ result = @conn.exec(
+ 'SELECT id, data FROM ex WHERE id = ?', nil, [[nil, 1]])
+
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
+ end
+
+ def test_exec_typecasts_bind_vals
+ @conn.exec('create table ex(id int, data string)')
+ @conn.exec('INSERT INTO ex (id, data) VALUES (1, "foo")')
+ column = @conn.columns('ex').find { |col| col.name == 'id' }
+
+ result = @conn.exec(
+ 'SELECT id, data FROM ex WHERE id = ?', nil, [[column, '1-fuu']])
+
+ assert_equal 1, result.rows.length
+ assert_equal 2, result.columns.length
+
+ assert_equal [[1, 'foo']], result.rows
end
end
end
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index 15537d6940..6a30e2905b 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -72,7 +72,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
def test_has_many_callbacks_for_save_on_parent
jack = Author.new :name => "Jack"
- post = jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep"
+ jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep"
callback_log = ["before_adding<new>", "after_adding#{jack.posts_with_callbacks.first.id}"]
assert_equal callback_log, jack.post_log
@@ -149,7 +149,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
assert !@david.unchangable_posts.include?(@authorless)
begin
@david.unchangable_posts << @authorless
- rescue Exception => e
+ rescue Exception
end
assert @david.post_log.empty?
assert !@david.unchangable_posts.include?(@authorless)
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 1750bf004a..ab9a65944f 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -103,7 +103,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
if current_adapter?(:MysqlAdapter)
def test_read_attributes_before_type_cast_on_boolean
bool = Boolean.create({ "value" => false })
- assert_equal "0", bool.reload.attributes_before_type_cast["value"]
+ assert_equal 0, bool.reload.attributes_before_type_cast["value"]
end
end
@@ -112,7 +112,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
developer = Developer.find(:first)
# Oracle adapter returns Time before type cast
unless current_adapter?(:OracleAdapter)
- assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
+ assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s
else
assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db)
@@ -121,7 +121,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal developer.created_at, nil
developer.created_at = "2010-03-21 21:23:32"
- assert_equal developer.created_at_before_type_cast, "2010-03-21 21:23:32"
+ assert_equal developer.created_at_before_type_cast.to_s, "2010-03-21 21:23:32"
assert_equal developer.created_at, Time.parse("2010-03-21 21:23:32")
end
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 52382f5afc..89be94c81f 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -17,6 +17,7 @@ require 'models/tag'
require 'models/tagging'
require 'models/treasure'
require 'models/company'
+require 'models/eye'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_should_be_a_valid_option_for_has_one
@@ -170,6 +171,25 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
firm.account = Account.find(:first).clone
assert_queries(2) { firm.save! }
end
+
+ def test_callbacks_firing_order_on_create
+ eye = Eye.create(:iris_attributes => {:color => 'honey'})
+ assert_equal [true, false], eye.after_create_callbacks_stack
+ end
+
+ def test_callbacks_firing_order_on_update
+ eye = Eye.create(:iris_attributes => {:color => 'honey'})
+ eye.update_attributes(:iris_attributes => {:color => 'green'})
+ assert_equal [true, false], eye.after_update_callbacks_stack
+ end
+
+ def test_callbacks_firing_order_on_save
+ eye = Eye.create(:iris_attributes => {:color => 'honey'})
+ assert_equal [false, false], eye.after_save_callbacks_stack
+
+ eye.update_attributes(:iris_attributes => {:color => 'blue'})
+ assert_equal [false, false, false, false], eye.after_save_callbacks_stack
+ end
end
class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 8acee9ac71..e63e1fbe09 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1150,6 +1150,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal 3, scoped_developers.size
end
+ def test_no_limit_offset
+ assert_nothing_raised do
+ Developer.find(:all, :offset => 2)
+ end
+ end
+
def test_scoped_find_limit_offset
scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
Developer.find(:all, :order => 'id')
@@ -1456,6 +1462,7 @@ class BasicsTest < ActiveRecord::TestCase
UnloadablePost.class_eval do
default_scope order('posts.comments_count ASC')
end
+ UnloadablePost.scoped_methods # make Thread.current[:UnloadablePost_scoped_methods] not nil
UnloadablePost.unloadable
assert_not_nil Thread.current[:UnloadablePost_scoped_methods]
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 2d3047c875..f6ef155d66 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -55,6 +55,14 @@ ActiveRecord::Base.connection.class.class_eval do
end
alias_method_chain :execute, :query_record
+
+ def exec_with_query_record(sql, name = nil, binds = [], &block)
+ $queries_executed ||= []
+ $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
+ exec_without_query_record(sql, name, binds, &block)
+ end
+
+ alias_method_chain :exec, :query_record
end
ActiveRecord::Base.connection.class.class_eval {
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index ef949300b0..e6eef805cf 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -422,7 +422,7 @@ if ActiveRecord::Base.connection.supports_migrations?
# Sybase, and SQLite3 will not allow you to add a NOT NULL
# column to a table without a default value.
- unless current_adapter?(:SybaseAdapter, :SQLiteAdapter)
+ unless current_adapter?(:SybaseAdapter, :SQLite3Adapter)
def test_add_column_not_null_without_default
Person.connection.create_table :testings do |t|
t.column :foo, :string
@@ -821,7 +821,7 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
- if current_adapter?(:SQLiteAdapter)
+ if current_adapter?(:SQLite3Adapter)
def test_rename_table_for_sqlite_should_work_with_reserved_words
begin
assert_nothing_raised do
@@ -1131,7 +1131,7 @@ if ActiveRecord::Base.connection.supports_migrations?
# so this happens there too
assert_kind_of BigDecimal, b.value_of_e
assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
- elsif current_adapter?(:SQLiteAdapter)
+ elsif current_adapter?(:SQLite3Adapter)
# - SQLite3 stores a float, in violation of SQL
assert_kind_of BigDecimal, b.value_of_e
assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
@@ -1588,7 +1588,7 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
- if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
+ if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
def test_xml_creates_xml_column
type = current_adapter?(:PostgreSQLAdapter) ? 'xml' : :text
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index cc4438395e..fb24c65fff 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -125,6 +125,12 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal posts_with_authors_at_address_titles, Post.with_authors_at_address(address).find(:all, :select => 'title')
end
+ def test_scope_with_object
+ objects = Topic.with_object
+ assert_operator objects.length, :>, 0
+ assert objects.all?(&:approved?), 'all objects should be approved'
+ end
+
def test_extensions
assert_equal 1, Topic.anonymous_extension.one
assert_equal 2, Topic.named_extension.two
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 594db1d0ab..5bb21a54bd 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -22,6 +22,12 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
+ def test_find_queries_with_cache_multi_record
+ Task.cache do
+ assert_queries(2) { Task.find(1); Task.find(1); Task.find(2) }
+ end
+ end
+
def test_count_queries_with_cache
Task.cache do
assert_queries(1) { Task.count; Task.count }
@@ -57,7 +63,7 @@ class QueryCacheTest < ActiveRecord::TestCase
# Oracle adapter returns count() as Fixnum or Float
if current_adapter?(:OracleAdapter)
assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- elsif current_adapter?(:SQLite3Adapter) && SQLite3::Version::VERSION > '1.2.5' or current_adapter?(:Mysql2Adapter)
+ elsif current_adapter?(:SQLite3Adapter) && SQLite3::Version::VERSION > '1.2.5' || current_adapter?(:Mysql2Adapter) || current_adapter?(:MysqlAdapter)
# Future versions of the sqlite3 adapter will return numeric
assert_instance_of Fixnum,
Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index 64365c1d75..bf41199b77 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -311,6 +311,35 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
+ def test_default_scope_with_lambda
+ expected = Post.find_all_by_author_id(2)
+ PostForAuthor.selected_author = 2
+ received = PostForAuthor.all
+ assert_equal expected, received
+ expected = Post.find_all_by_author_id(1)
+ PostForAuthor.selected_author = 1
+ received = PostForAuthor.all
+ assert_equal expected, received
+ end
+
+ def test_default_scope_with_thing_that_responds_to_call
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'posts'
+ end
+
+ klass.class_eval do
+ default_scope Class.new(Struct.new(:klass)) {
+ def call
+ klass.where(:author_id => 2)
+ end
+ }.new(self)
+ end
+
+ records = klass.all
+ assert_equal 3, records.length
+ assert_equal 2, records.first.author_id
+ end
+
def test_default_scope_is_unscoped_on_find
assert_equal 1, DeveloperCalledDavid.count
assert_equal 11, DeveloperCalledDavid.unscoped.count
@@ -364,6 +393,23 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal 100000, klass.first.salary
end
+ def test_default_scope_called_twice_in_different_place_merges_where_clause
+ Developer.destroy_all
+ Developer.create!(:name => "David", :salary => 80000)
+ Developer.create!(:name => "David", :salary => 100000)
+ Developer.create!(:name => "Brian", :salary => 100000)
+
+ klass = Class.new(Developer)
+ klass.class_eval do
+ default_scope where("name = 'David'")
+ default_scope where("salary = 100000")
+ end
+
+ assert_equal 1, klass.count
+ assert_equal "David", klass.first.name
+ assert_equal 100000, klass.first.salary
+ end
+
def test_method_scope
expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary }
received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary }
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 0b143224be..b47ef7592d 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -19,19 +19,21 @@ class RelationTest < ActiveRecord::TestCase
fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
:taggings, :cars
+ def test_bind_values
+ relation = Post.scoped
+ assert_equal [], relation.bind_values
+
+ relation2 = relation.bind 'foo'
+ assert_equal %w{ foo }, relation2.bind_values
+ assert_equal [], relation.bind_values
+ end
+
def test_two_named_scopes_with_includes_should_not_drop_any_include
car = Car.incl_engines.incl_tyres.first
assert_no_queries { car.tyres.length }
assert_no_queries { car.engines.length }
end
- def test_apply_relation_as_where_id
- posts = Post.arel_table
- post_authors = posts.where(posts[:author_id].eq(1)).project(posts[:id])
- assert_equal 5, post_authors.to_a.size
- assert_equal 5, Post.where(:id => post_authors).size
- end
-
def test_dynamic_finder
x = Post.where('author_id = ?', 1)
assert x.klass.respond_to?(:find_by_id), '@klass should handle dynamic finders'
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 66446b6b7e..9b2c7c00df 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -100,7 +100,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{c_int_4.*}, output
assert_no_match %r{c_int_4.*:limit}, output
- elsif current_adapter?(:SQLiteAdapter)
+ elsif current_adapter?(:SQLite3Adapter)
assert_match %r{c_int_1.*:limit => 1}, output
assert_match %r{c_int_2.*:limit => 2}, output
assert_match %r{c_int_3.*:limit => 3}, output
@@ -109,7 +109,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{c_int_without_limit.*}, output
assert_no_match %r{c_int_without_limit.*:limit}, output
- if current_adapter?(:SQLiteAdapter)
+ if current_adapter?(:SQLite3Adapter)
assert_match %r{c_int_5.*:limit => 5}, output
assert_match %r{c_int_6.*:limit => 6}, output
assert_match %r{c_int_7.*:limit => 7}, output
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 44af54b143..0fbcef4091 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -420,7 +420,7 @@ class TransactionTest < ActiveRecord::TestCase
end
def test_sqlite_add_column_in_transaction
- return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
+ return true unless current_adapter?(:SQLite3Adapter)
# Test first if column creation/deletion works correctly when no
# transaction is in place.
diff --git a/activerecord/test/connections/native_oracle/connection.rb b/activerecord/test/connections/native_oracle/connection.rb
index 9a717018b2..c942036128 100644
--- a/activerecord/test/connections/native_oracle/connection.rb
+++ b/activerecord/test/connections/native_oracle/connection.rb
@@ -37,10 +37,10 @@ Course.establish_connection 'arunit2'
ActiveRecord::Base.connection.class.class_eval do
IGNORED_SELECT_SQL = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from ((all|user)_tab_columns|(all|user)_triggers|(all|user)_constraints)/im]
- def select_with_query_record(sql, name = nil, return_column_names = false)
+ def select_with_query_record(sql, name = nil)
$queries_executed ||= []
$queries_executed << sql unless IGNORED_SELECT_SQL.any? { |r| sql =~ r }
- select_without_query_record(sql, name, return_column_names)
+ select_without_query_record(sql, name)
end
alias_method_chain :select, :query_record
diff --git a/activerecord/test/models/eye.rb b/activerecord/test/models/eye.rb
new file mode 100644
index 0000000000..77f17b578e
--- /dev/null
+++ b/activerecord/test/models/eye.rb
@@ -0,0 +1,37 @@
+class Eye < ActiveRecord::Base
+ attr_reader :after_create_callbacks_stack
+ attr_reader :after_update_callbacks_stack
+ attr_reader :after_save_callbacks_stack
+
+ # Callbacks configured before the ones has_one sets up.
+ after_create :trace_after_create
+ after_update :trace_after_update
+ after_save :trace_after_save
+
+ has_one :iris
+ accepts_nested_attributes_for :iris
+
+ # Callbacks configured after the ones has_one sets up.
+ after_create :trace_after_create2
+ after_update :trace_after_update2
+ after_save :trace_after_save2
+
+ def trace_after_create
+ (@after_create_callbacks_stack ||= []) << iris.new_record?
+ end
+ alias trace_after_create2 trace_after_create
+
+ def trace_after_update
+ (@after_update_callbacks_stack ||= []) << iris.changed?
+ end
+ alias trace_after_update2 trace_after_update
+
+ def trace_after_save
+ (@after_save_callbacks_stack ||= []) << iris.changed?
+ end
+ alias trace_after_save2 trace_after_save
+end
+
+class Iris < ActiveRecord::Base
+ belongs_to :eye
+end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 7c919a55eb..68d2b79a3b 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -121,3 +121,9 @@ class PostWithComment < ActiveRecord::Base
self.table_name = 'posts'
default_scope where("posts.comments_count > 0").order("posts.comments_count ASC")
end
+
+class PostForAuthor < ActiveRecord::Base
+ self.table_name = 'posts'
+ cattr_accessor :selected_author
+ default_scope lambda { where(:author_id => PostForAuthor.selected_author) }
+end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index ba2fe1987b..82d4b5997f 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -18,6 +18,13 @@ class Topic < ActiveRecord::Base
1
end
end
+
+ scope :with_object, Class.new(Struct.new(:klass)) {
+ def call
+ klass.where(:approved => true)
+ end
+ }.new(self)
+
module NamedExtension
def two
2
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index c77651a782..7ad08869f9 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -18,8 +18,13 @@ ActiveRecord::Schema.define do
end
- # Please keep these create table statements in alphabetical order
- # unless the ordering matters. In which case, define them below
+ # ------------------------------------------------------------------- #
+ # #
+ # Please keep these create table statements in alphabetical order #
+ # unless the ordering matters. In which case, define them below. #
+ # #
+ # ------------------------------------------------------------------- #
+
create_table :accounts, :force => true do |t|
t.integer :firm_id
t.string :firm_name
@@ -56,7 +61,6 @@ ActiveRecord::Schema.define do
t.column :favorite_author_id, :integer
end
-
create_table :auto_id_tests, :force => true, :id => false do |t|
t.primary_key :auto_id
t.integer :value
@@ -81,6 +85,11 @@ ActiveRecord::Schema.define do
t.boolean :value
end
+ create_table :bulbs, :force => true do |t|
+ t.integer :car_id
+ t.string :name
+ end
+
create_table "CamelCase", :force => true do |t|
t.string :name
end
@@ -198,15 +207,6 @@ ActiveRecord::Schema.define do
t.integer :car_id
end
- create_table :tyres, :force => true do |t|
- t.integer :car_id
- end
-
- create_table :bulbs, :force => true do |t|
- t.integer :car_id
- t.string :name
- end
-
create_table :entrants, :force => true do |t|
t.string :name, :null => false
t.integer :course_id, :null => false
@@ -224,6 +224,9 @@ ActiveRecord::Schema.define do
t.string :title, :limit => 5
end
+ create_table :eyes, :force => true do |t|
+ end
+
create_table :funny_jokes, :force => true do |t|
t.string :name
end
@@ -233,13 +236,8 @@ ActiveRecord::Schema.define do
t.string :info
end
- create_table :invoices, :force => true do |t|
- t.integer :balance
- t.datetime :updated_at
- end
-
- create_table :items, :force => true do |t|
- t.column :name, :string
+ create_table :guids, :force => true do |t|
+ t.column :key, :string
end
create_table :inept_wizards, :force => true do |t|
@@ -248,6 +246,26 @@ ActiveRecord::Schema.define do
t.column :type, :string
end
+ create_table :integer_limits, :force => true do |t|
+ t.integer :"c_int_without_limit"
+ (1..8).each do |i|
+ t.integer :"c_int_#{i}", :limit => i
+ end
+ end
+
+ create_table :invoices, :force => true do |t|
+ t.integer :balance
+ t.datetime :updated_at
+ end
+
+ create_table :iris, :force => true do |t|
+ t.references :eye
+ t.string :color
+ end
+
+ create_table :items, :force => true do |t|
+ t.column :name, :string
+ end
create_table :jobs, :force => true do |t|
t.integer :ideal_reference_id
@@ -304,13 +322,6 @@ ActiveRecord::Schema.define do
t.string :name
end
- create_table :references, :force => true do |t|
- t.integer :person_id
- t.integer :job_id
- t.boolean :favourite
- t.integer :lock_version, :default => 0
- end
-
create_table :minivans, :force => true, :id => false do |t|
t.string :minivan_id
t.string :name
@@ -374,7 +385,6 @@ ActiveRecord::Schema.define do
t.string :essay_id
end
-
create_table :paint_colors, :force => true do |t|
t.integer :non_poly_one_id
end
@@ -466,6 +476,13 @@ ActiveRecord::Schema.define do
t.boolean :skimmer, :default => false
end
+ create_table :references, :force => true do |t|
+ t.integer :person_id
+ t.integer :job_id
+ t.boolean :favourite
+ t.integer :lock_version, :default => 0
+ end
+
create_table :shape_expressions, :force => true do |t|
t.string :paint_type
t.integer :paint_id
@@ -510,6 +527,19 @@ ActiveRecord::Schema.define do
t.integer :book_id
end
+ create_table :tags, :force => true do |t|
+ t.column :name, :string
+ t.column :taggings_count, :integer, :default => 0
+ end
+
+ create_table :taggings, :force => true do |t|
+ t.column :tag_id, :integer
+ t.column :super_tag_id, :integer
+ t.column :taggable_type, :string
+ t.column :taggable_id, :integer
+ t.string :comment
+ end
+
create_table :tasks, :force => true do |t|
t.datetime :starting
t.datetime :ending
@@ -537,19 +567,6 @@ ActiveRecord::Schema.define do
t.string :group
end
- create_table :taggings, :force => true do |t|
- t.column :tag_id, :integer
- t.column :super_tag_id, :integer
- t.column :taggable_type, :string
- t.column :taggable_id, :integer
- t.string :comment
- end
-
- create_table :tags, :force => true do |t|
- t.column :name, :string
- t.column :taggings_count, :integer, :default => 0
- end
-
create_table :toys, :primary_key => :toy_id ,:force => true do |t|
t.string :name
t.integer :pet_id, :integer
@@ -569,6 +586,10 @@ ActiveRecord::Schema.define do
t.column :looter_type, :string
end
+ create_table :tyres, :force => true do |t|
+ t.integer :car_id
+ end
+
create_table :variants, :force => true do |t|
t.references :product
t.string :name
@@ -586,17 +607,6 @@ ActiveRecord::Schema.define do
create_table(t, :force => true) { }
end
- create_table :guids, :force => true do |t|
- t.column :key, :string
- end
-
- create_table :integer_limits, :force => true do |t|
- t.integer :"c_int_without_limit"
- (1..8).each do |i|
- t.integer :"c_int_#{i}", :limit => i
- end
- end
-
# NOTE - the following 4 tables are used by models that have :inverse_of options on the associations
create_table :men, :force => true do |t|
t.string :name
@@ -652,7 +662,6 @@ ActiveRecord::Schema.define do
t.string :name
end
-
except 'SQLite' do
# fk_test_has_fk should be before fk_test_has_pk
create_table :fk_test_has_fk, :force => true do |t|