aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/abstract
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/abstract')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb59
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb66
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb10
7 files changed, 96 insertions, 77 deletions
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 848aeb821c..aa5ae15285 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -27,10 +27,10 @@ module ActiveRecord
end
# Returns an ActiveRecord::Result instance.
- def select_all(arel, name = nil, binds = [])
+ def select_all(arel, name = nil, binds = [], preparable: nil)
arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
- if arel.is_a?(String)
+ if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
preparable = false
else
preparable = visitor.preparable
@@ -58,8 +58,8 @@ module ActiveRecord
# Returns an array of the values of the first column in a select:
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
- def select_values(arel, name = nil)
- arel, binds = binds_from_relation arel, []
+ def select_values(arel, name = nil, binds = [])
+ arel, binds = binds_from_relation arel, binds
select_rows(to_sql(arel, binds), name, binds).map(&:first)
end
@@ -69,7 +69,11 @@ module ActiveRecord
end
undef_method :select_rows
- # Executes the SQL statement in the context of this connection.
+ # Executes the SQL statement in the context of this connection and returns
+ # the raw result from the connection adapter.
+ # Note: depending on your database connector, the result returned by this
+ # method may be manually memory managed. Consider using the exec_query
+ # wrapper instead.
def execute(sql, name = nil)
end
undef_method :execute
@@ -106,7 +110,7 @@ module ActiveRecord
exec_query(sql, name, binds)
end
- # Returns the last auto-generated ID from the affected table.
+ # Executes an INSERT query and returns the new record's ID
#
# +id_value+ will be returned unless the value is nil, in
# which case the database will attempt to calculate the last inserted
@@ -115,20 +119,24 @@ module ActiveRecord
# If the next id was calculated in advance (as in Oracle), it should be
# passed in as +id_value+.
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
- sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
- value = exec_insert(sql, name, binds, pk, sequence_name)
+ sql, binds, pk, sequence_name = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
+ value = exec_insert(sql, name, binds, pk, sequence_name)
id_value || last_inserted_id(value)
end
+ alias create insert
+ alias insert_sql insert
# Executes the update statement and returns the number of rows affected.
def update(arel, name = nil, binds = [])
exec_update(to_sql(arel, binds), name, binds)
end
+ alias update_sql update
# Executes the delete statement and returns the number of rows affected.
def delete(arel, name = nil, binds = [])
exec_delete(to_sql(arel, binds), name, binds)
end
+ alias delete_sql delete
# Returns +true+ when the connection adapter supports prepared statement
# caching, otherwise returns +false+
@@ -208,7 +216,7 @@ module ActiveRecord
# * You are joining an existing open transaction
# * You are creating a nested (savepoint) transaction
#
- # The mysql, mysql2 and postgresql adapters support setting the transaction
+ # The mysql2 and postgresql adapters support setting the transaction
# isolation level. However, support is disabled for MySQL versions below 5,
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
# which means the isolation level gets persisted outside the transaction.
@@ -296,14 +304,15 @@ module ActiveRecord
# Inserts the given fixture into the table. Overridden in adapters that require
# something beyond a simple insert (eg. Oracle).
def insert_fixture(fixture, table_name)
- columns = schema_cache.columns_hash(table_name)
+ fixture = fixture.stringify_keys
+ columns = schema_cache.columns_hash(table_name)
binds = fixture.map do |name, value|
if column = columns[name]
type = lookup_cast_type_from_column(column)
Relation::QueryAttribute.new(name, value, type)
else
- raise Fixture::FixtureError, %(table "#{table_name}" has no column named "#{name}".)
+ raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
end
end
key_list = fixture.keys.map { |name| quote_column_name(name) }
@@ -344,18 +353,12 @@ module ActiveRecord
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
# on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
# an UPDATE statement, so in the MySQL adapters we redefine this to do that.
- def join_to_update(update, select) #:nodoc:
- key = update.key
+ def join_to_update(update, select, key) # :nodoc:
subselect = subquery_for(key, select)
update.where key.in(subselect)
end
-
- def join_to_delete(delete, select, key) #:nodoc:
- subselect = subquery_for(key, select)
-
- delete.where key.in(subselect)
- end
+ alias join_to_delete join_to_update
protected
@@ -375,24 +378,8 @@ module ActiveRecord
exec_query(sql, name, binds, prepare: true)
end
- # Returns the last auto-generated ID from the affected table.
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- execute(sql, name)
- id_value
- end
-
- # Executes the update statement and returns the number of rows affected.
- def update_sql(sql, name = nil)
- execute(sql, name)
- end
-
- # Executes the delete statement and returns the number of rows affected.
- def delete_sql(sql, name = nil)
- update_sql(sql, name)
- end
-
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
- [sql, binds]
+ [sql, binds, pk, sequence_name]
end
def last_inserted_id(result)
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 5e27cfe507..33dbab41cb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -61,11 +61,11 @@ module ActiveRecord
@query_cache.clear
end
- def select_all(arel, name = nil, binds = [])
+ def select_all(arel, name = nil, binds = [], preparable: nil)
if @query_cache_enabled && !locked?(arel)
arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
- cache_sql(sql, binds) { super(sql, name, binds) }
+ cache_sql(sql, binds) { super(sql, name, binds, preparable: visitor.preparable) }
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 9ec0a67c8f..7e3760d34b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -93,7 +93,7 @@ module ActiveRecord
# Override to return the quoted table name for assignment. Defaults to
# table quoting.
#
- # This works for mysql and mysql2 where table.column can be used to
+ # This works for mysql2 where table.column can be used to
# resolve ambiguity.
#
# We override this in the sqlite3 and postgresql adapters to use only
@@ -102,9 +102,13 @@ module ActiveRecord
quote_table_name("#{table}.#{attr}")
end
- def quote_default_expression(value, column) #:nodoc:
- value = lookup_cast_type(column.sql_type).serialize(value)
- quote(value)
+ def quote_default_expression(value, column) # :nodoc:
+ if value.is_a?(Proc)
+ value.call
+ else
+ value = lookup_cast_type(column.sql_type).serialize(value)
+ quote(value)
+ end
end
def quoted_true
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 1cda23dc1d..4f97c7c065 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -69,7 +69,7 @@ module ActiveRecord
def initialize(
name,
polymorphic: false,
- index: false,
+ index: true,
foreign_key: false,
type: :integer,
**options
@@ -182,6 +182,7 @@ module ActiveRecord
end
CODE
end
+ alias_method :numeric, :decimal
end
# Represents the schema of an SQL table in an abstract way. This class
@@ -211,7 +212,7 @@ module ActiveRecord
def initialize(name, temporary, options, as = nil)
@columns_hash = {}
@indexes = {}
- @foreign_keys = {}
+ @foreign_keys = []
@primary_keys = nil
@temporary = temporary
@options = options
@@ -329,7 +330,7 @@ module ActiveRecord
end
def foreign_key(table_name, options = {}) # :nodoc:
- foreign_keys[table_name] = options
+ foreign_keys.push([table_name, options])
end
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -436,6 +437,7 @@ module ActiveRecord
# t.bigint
# t.float
# t.decimal
+ # t.numeric
# t.datetime
# t.timestamp
# t.time
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 797662d07c..b1b6044e72 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -14,7 +14,7 @@ module ActiveRecord
def column_spec_for_primary_key(column)
return if column.type == :integer
- spec = { id: column.type.inspect }
+ spec = { id: schema_type(column).inspect }
spec.merge!(prepare_column_options(column).delete_if { |key, _| [:name, :type].include?(key) })
end
@@ -24,7 +24,7 @@ module ActiveRecord
def prepare_column_options(column)
spec = {}
spec[:name] = column.name.inspect
- spec[:type] = schema_type(column)
+ spec[:type] = schema_type(column).to_s
spec[:null] = 'false' unless column.null
if limit = schema_limit(column)
@@ -57,7 +57,7 @@ module ActiveRecord
private
def schema_type(column)
- column.type.to_s
+ column.type
end
def schema_limit(column)
@@ -76,11 +76,17 @@ module ActiveRecord
def schema_default(column)
type = lookup_cast_type_from_column(column)
default = type.deserialize(column.default)
- unless default.nil?
+ if default.nil?
+ schema_expression(column)
+ else
type.type_cast_for_schema(default)
end
end
+ def schema_expression(column)
+ "-> { #{column.default_function.inspect} }" if column.default_function
+ end
+
def schema_collation(column)
column.collation.inspect if column.collation
end
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 7bf548fcba..f0f855963a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -92,7 +92,9 @@ module ActiveRecord
# Returns an array of Column objects for the table specified by +table_name+.
# See the concrete implementation for details on the expected parameter values.
- def columns(table_name) end
+ def columns(table_name)
+ raise NotImplementedError, "#columns is not implemented"
+ end
# Checks to see if a column exists in a given table.
#
@@ -110,18 +112,25 @@ module ActiveRecord
#
def column_exists?(table_name, column_name, type = nil, options = {})
column_name = column_name.to_s
- columns(table_name).any?{ |c| c.name == column_name &&
- (!type || c.type == type) &&
- (!options.key?(:limit) || c.limit == options[:limit]) &&
- (!options.key?(:precision) || c.precision == options[:precision]) &&
- (!options.key?(:scale) || c.scale == options[:scale]) &&
- (!options.key?(:default) || c.default == options[:default]) &&
- (!options.key?(:null) || c.null == options[:null]) }
+ checks = []
+ checks << lambda { |c| c.name == column_name }
+ checks << lambda { |c| c.type == type } if type
+ (migration_keys - [:name]).each do |attr|
+ checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
+ end
+
+ columns(table_name).any? { |c| checks.all? { |check| check[c] } }
end
# Returns just a table's primary key
def primary_key(table_name)
pks = primary_keys(table_name)
+ warn <<-WARNING.strip_heredoc if pks.count > 1
+ WARNING: Rails does not support composite primary key.
+
+ #{table_name} has composite primary key. Composite primary key is ignored.
+ WARNING
+
pks.first if pks.one?
end
@@ -450,7 +459,7 @@ module ActiveRecord
# The +type+ parameter is normally one of the migrations native types,
# which is one of the following:
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
- # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
+ # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
# <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
# <tt>:binary</tt>, <tt>:boolean</tt>.
#
@@ -468,9 +477,9 @@ module ActiveRecord
# Allows or disallows +NULL+ values in the column. This option could
# have been named <tt>:null_allowed</tt>.
# * <tt>:precision</tt> -
- # Specifies the precision for a <tt>:decimal</tt> column.
+ # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
# * <tt>:scale</tt> -
- # Specifies the scale for a <tt>:decimal</tt> column.
+ # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
#
# Note: The precision is the total number of significant digits
# and the scale is the number of digits that can be stored following
@@ -487,8 +496,6 @@ module ActiveRecord
# Default is (10,0).
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
# <tt>:scale</tt> [0..infinity]. No default.
- # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
- # Internal storage as strings. No default.
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
# but the maximum supported <tt>:precision</tt> is 16. No default.
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
@@ -691,7 +698,7 @@ module ActiveRecord
#
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
#
- # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
+ # Note: only supported by MySQL.
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -957,9 +964,9 @@ module ActiveRecord
def dump_schema_information #:nodoc:
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- ActiveRecord::SchemaMigration.order('version').map { |sm|
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
- }.join "\n\n"
+ sql = "INSERT INTO #{sm_table} (version) VALUES "
+ sql << ActiveRecord::SchemaMigration.order('version').pluck(:version).map {|v| "('#{v}')" }.join(', ')
+ sql << ";\n\n"
end
# Should not be called normally, but this operation is non-destructive.
@@ -968,6 +975,14 @@ module ActiveRecord
ActiveRecord::SchemaMigration.create_table
end
+ def initialize_internal_metadata_table
+ ActiveRecord::InternalMetadata.create_table
+ end
+
+ def internal_string_options_for_primary_key # :nodoc:
+ { primary_key: true }
+ end
+
def assume_migrated_upto_version(version, migrations_paths)
migrations_paths = Array(migrations_paths)
version = version.to_i
@@ -983,14 +998,12 @@ module ActiveRecord
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
end
- inserted = Set.new
- (versions - migrated).each do |v|
- if inserted.include?(v)
- raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
- elsif v < version
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
- inserted << v
+ inserting = (versions - migrated).select {|v| v < version}
+ if inserting.any?
+ if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
+ raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
end
+ execute "INSERT INTO #{sm_table} (version) VALUES #{inserting.map {|v| "('#{v}')"}.join(', ') }"
end
end
@@ -1028,11 +1041,12 @@ module ActiveRecord
end
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
- # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
+ # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
# require the order columns appear in the SELECT.
#
# columns_for_distinct("posts.id", ["posts.created_at desc"])
- def columns_for_distinct(columns, orders) #:nodoc:
+ #
+ def columns_for_distinct(columns, orders) # :nodoc:
columns
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 295a7bed87..6ecdab6eb0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -33,6 +33,7 @@ module ActiveRecord
class NullTransaction #:nodoc:
def initialize; end
+ def state; end
def closed?; true; end
def open?; false; end
def joinable?; false; end
@@ -166,8 +167,13 @@ module ActiveRecord
def commit_transaction
transaction = @stack.last
- transaction.before_commit_records
- @stack.pop
+
+ begin
+ transaction.before_commit_records
+ ensure
+ @stack.pop
+ end
+
transaction.commit
transaction.commit_records
end