aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/abstract_adapter.rb')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb190
1 files changed, 87 insertions, 103 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 24007db8f0..56a5ea153b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -1,11 +1,15 @@
-require "active_record/type"
+# frozen_string_literal: true
+
require "active_record/connection_adapters/determine_if_preparable_visitor"
require "active_record/connection_adapters/schema_cache"
require "active_record/connection_adapters/sql_type_metadata"
require "active_record/connection_adapters/abstract/schema_dumper"
require "active_record/connection_adapters/abstract/schema_creation"
+require "active_support/concurrency/load_interlock_aware_monitor"
require "arel/collectors/bind"
+require "arel/collectors/composite"
require "arel/collectors/sql_string"
+require "arel/collectors/substitute_binds"
module ActiveRecord
module ConnectionAdapters # :nodoc:
@@ -68,13 +72,12 @@ module ActiveRecord
include Quoting, DatabaseStatements, SchemaStatements
include DatabaseLimits
include QueryCache
- include ColumnDumper
include Savepoints
SIMPLE_INT = /\A\d+\z/
attr_accessor :visitor, :pool
- attr_reader :schema_cache, :owner, :logger
+ attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock
alias :in_use? :owner
def self.type_cast_config_to_integer(config)
@@ -93,8 +96,6 @@ module ActiveRecord
end
end
- attr_reader :prepared_statements
-
def initialize(connection, logger = nil, config = {}) # :nodoc:
super()
@@ -104,10 +105,11 @@ module ActiveRecord
@logger = logger
@config = config
@pool = nil
+ @idle_since = Concurrent.monotonic_time
@schema_cache = SchemaCache.new self
@quoted_column_names, @quoted_table_names = {}, {}
@visitor = arel_visitor
- @lock = Monitor.new
+ @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@@ -117,6 +119,14 @@ module ActiveRecord
end
end
+ def migrations_paths
+ @config[:migrations_paths] || Migrator.migrations_paths
+ end
+
+ def migration_context
+ MigrationContext.new(migrations_paths)
+ end
+
class Version
include Comparable
@@ -129,51 +139,14 @@ module ActiveRecord
end
end
- class BindCollector < Arel::Collectors::Bind
- def compile(bvs, conn)
- casted_binds = bvs.map(&:value_for_database)
- super(casted_binds.map { |value| conn.quote(value) })
- end
- end
-
- class SQLString < Arel::Collectors::SQLString
- def compile(bvs, conn)
- super(bvs)
- end
- end
-
- def collector
- if prepared_statements
- SQLString.new
- else
- BindCollector.new
- end
- end
-
- def arel_visitor # :nodoc:
- Arel::Visitors::ToSql.new(self)
- end
-
- def valid_type?(type)
- false
- end
-
- def schema_creation
- SchemaCreation.new self
- end
-
- # Returns an array of +Column+ objects for the table specified by +table_name+.
- def columns(table_name) # :nodoc:
- table_name = table_name.to_s
- column_definitions(table_name).map do |field|
- new_column_from_field(table_name, field)
- end
+ def valid_type?(type) # :nodoc:
+ !native_database_types[type].nil?
end
# this method must only be called while holding connection pool's mutex
def lease
if in_use?
- msg = "Cannot lease connection, "
+ msg = "Cannot lease connection, ".dup
if @owner == Thread.current
msg << "it is already leased by the current thread."
else
@@ -200,6 +173,7 @@ module ActiveRecord
"Current thread: #{Thread.current}."
end
+ @idle_since = Concurrent.monotonic_time
@owner = nil
else
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
@@ -219,6 +193,12 @@ module ActiveRecord
end
end
+ # Seconds since this connection was returned to the pool
+ def seconds_idle # :nodoc:
+ return 0 if in_use?
+ Concurrent.monotonic_time - @idle_since
+ end
+
def unprepared_statement
old_prepared_statements, @prepared_statements = @prepared_statements, false
yield
@@ -232,16 +212,6 @@ module ActiveRecord
self.class::ADAPTER_NAME
end
- # Does this adapter support migrations?
- def supports_migrations?
- false
- end
-
- def supports_primary_key? # :nodoc:
- true
- end
- deprecate :supports_primary_key?
-
# Does this adapter support DDL rollbacks in transactions? That is, would
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
def supports_ddl_transactions?
@@ -310,6 +280,11 @@ module ActiveRecord
false
end
+ # Does this adapter support creating invalid constraints?
+ def supports_validate_constraints?
+ false
+ end
+
# Does this adapter support creating foreign key constraints
# in the same statement as creating the table?
def supports_foreign_keys_in_create?
@@ -351,6 +326,11 @@ module ActiveRecord
false
end
+ # Does this adapter support foreign/external tables?
+ def supports_foreign_tables?
+ false
+ end
+
# This is meant to be implemented by the adapters that support extensions
def disable_extension(name)
end
@@ -413,6 +393,19 @@ module ActiveRecord
reset_transaction
end
+ # Immediately forget this connection ever existed. Unlike disconnect!,
+ # this will not communicate with the server.
+ #
+ # After calling this method, the behavior of all other methods becomes
+ # undefined. This is called internally just before a forked process gets
+ # rid of a connection that belonged to its parent.
+ def discard!
+ # This should be overridden by concrete adapters.
+ #
+ # Prevent @connection's finalizer from touching the socket, or
+ # otherwise communicating with its server, when it is collected.
+ end
+
# Reset the state of this connection, directing the DBMS to clear
# transactions and other connection-related server-side state. Usually a
# database-dependent operation.
@@ -438,16 +431,13 @@ module ActiveRecord
# Checks whether the connection to the database is still active (i.e. not stale).
# This is done under the hood by calling #active?. If the connection
# is no longer active, then this method will reconnect to the database.
- def verify!(*ignored)
- if ignored.size > 0
- ActiveSupport::Deprecation.warn("Passing arguments to #verify method of the connection has no effect and has been deprecated. Please remove all arguments from the #verify method call.")
- end
+ def verify!
reconnect! unless active?
end
# Provides access to the underlying database driver for this adapter. For
# example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
- # and a PGconn object in case of PostgreSQLAdapter.
+ # and a PG::Connection object in case of PostgreSQLAdapter.
#
# This is useful for when you need to call a proprietary method such as
# PostgreSQL's lo_* methods.
@@ -477,40 +467,12 @@ module ActiveRecord
pool.checkin self
end
- def type_map # :nodoc:
- @type_map ||= Type::TypeMap.new.tap do |mapping|
- initialize_type_map(mapping)
- end
- end
-
- def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil) # :nodoc:
- Column.new(name, default, sql_type_metadata, null, table_name, default_function, collation)
+ def column_name_for_operation(operation, node) # :nodoc:
+ column_name_from_arel_node(node)
end
- def lookup_cast_type(sql_type) # :nodoc:
- type_map.lookup(sql_type)
- end
-
- def column_name_for_operation(operation, node) # :nodoc:
- visitor.accept(node, collector).value
- end
-
- def combine_bind_parameters(
- from_clause: [],
- join_clause: [],
- where_clause: [],
- having_clause: [],
- limit: nil,
- offset: nil
- ) # :nodoc:
- result = from_clause + join_clause + where_clause + having_clause
- if limit
- result << limit
- end
- if offset
- result << offset
- end
- result
+ def column_name_from_arel_node(node) # :nodoc:
+ visitor.accept(node, Arel::Collectors::SQLString.new).value
end
def default_index_type?(index) # :nodoc:
@@ -518,8 +480,13 @@ module ActiveRecord
end
private
+ def type_map
+ @type_map ||= Type::TypeMap.new.tap do |mapping|
+ initialize_type_map(mapping)
+ end
+ end
- def initialize_type_map(m)
+ def initialize_type_map(m = type_map)
register_class_with_limit m, %r(boolean)i, Type::Boolean
register_class_with_limit m, %r(char)i, Type::String
register_class_with_limit m, %r(binary)i, Type::Binary
@@ -537,6 +504,8 @@ module ActiveRecord
m.alias_type %r(number)i, "decimal"
m.alias_type %r(double)i, "float"
+ m.register_type %r(^json)i, Type::Json.new
+
m.register_type(%r(decimal)i) do |sql_type|
scale = extract_scale(sql_type)
precision = extract_precision(sql_type)
@@ -552,7 +521,7 @@ module ActiveRecord
def reload_type_map
type_map.clear
- initialize_type_map(type_map)
+ initialize_type_map
end
def register_class_with_limit(mapping, key, klass)
@@ -581,12 +550,7 @@ module ActiveRecord
end
def extract_limit(sql_type)
- case sql_type
- when /^bigint/i
- 8
- when /\((.*)\)/
- $1.to_i
- end
+ $1.to_i if sql_type =~ /\((.*)\)/
end
def translate_exception_class(e, sql)
@@ -610,12 +574,14 @@ module ActiveRecord
type_casted_binds: type_casted_binds,
statement_name: statement_name,
connection_id: object_id) do
+ begin
@lock.synchronize do
yield
end
+ rescue => e
+ raise translate_exception_class(e, sql)
end
- rescue => e
- raise translate_exception_class(e, sql)
+ end
end
def translate_exception(exception, message)
@@ -637,6 +603,24 @@ module ActiveRecord
columns(table_name).detect { |c| c.name == column_name } ||
raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
end
+
+ def collector
+ if prepared_statements
+ Arel::Collectors::Composite.new(
+ Arel::Collectors::SQLString.new,
+ Arel::Collectors::Bind.new,
+ )
+ else
+ Arel::Collectors::SubstituteBinds.new(
+ self,
+ Arel::Collectors::SQLString.new,
+ )
+ end
+ end
+
+ def arel_visitor
+ Arel::Visitors::ToSql.new(self)
+ end
end
end
end