aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb93
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb103
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/quoting.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb33
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/oid.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb168
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb69
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb32
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb59
26 files changed, 390 insertions, 370 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index ce4721c99d..3f2e86a98d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -353,6 +353,16 @@ module ActiveRecord
@threads_blocking_new_connections = 0
@available = ConnectionLeasingQueue.new self
+
+ @lock_thread = false
+ end
+
+ def lock_thread=(lock_thread)
+ if lock_thread
+ @lock_thread = Thread.current
+ else
+ @lock_thread = nil
+ end
end
# Retrieve the connection associated with the current thread, or call
@@ -361,7 +371,7 @@ module ActiveRecord
# #connection can be called any number of times; the connection is
# held in a cache keyed by a thread.
def connection
- @thread_cached_conns[connection_cache_key(Thread.current)] ||= checkout
+ @thread_cached_conns[connection_cache_key(@lock_thread || Thread.current)] ||= checkout
end
# Returns true if there is an open connection being used for the current thread.
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 7eab7de5d3..e53ba4e666 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -83,7 +83,9 @@ module ActiveRecord
# the same SQL query and repeatedly return the same result each time, silently
# undermining the randomness you were expecting.
def clear_query_cache
- @query_cache.clear
+ @lock.synchronize do
+ @query_cache.clear
+ end
end
def select_all(arel, name = nil, binds = [], preparable: nil)
@@ -99,21 +101,23 @@ module ActiveRecord
private
def cache_sql(sql, name, binds)
- result =
- if @query_cache[sql].key?(binds)
- ActiveSupport::Notifications.instrument(
- "sql.active_record",
- sql: sql,
- binds: binds,
- name: name,
- connection_id: object_id,
- cached: true,
- )
- @query_cache[sql][binds]
- else
- @query_cache[sql][binds] = yield
- end
- result.dup
+ @lock.synchronize do
+ result =
+ if @query_cache[sql].key?(binds)
+ ActiveSupport::Notifications.instrument(
+ "sql.active_record",
+ sql: sql,
+ binds: binds,
+ name: name,
+ connection_id: object_id,
+ cached: true,
+ )
+ @query_cache[sql][binds]
+ else
+ @query_cache[sql][binds] = yield
+ end
+ result.dup
+ end
end
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 9203a66a17..6019e05c4c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -7,8 +7,13 @@ module ActiveRecord
# Quotes the column value to help prevent
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
def quote(value)
- # records are quoted as their primary key
- return value.quoted_id if value.respond_to?(:quoted_id)
+ value = id_value_for_database(value) if value.is_a?(Base)
+
+ if value.respond_to?(:quoted_id)
+ ActiveSupport::Deprecation.warn \
+ "Using #quoted_id is deprecated and will be removed in Rails 5.2."
+ return value.quoted_id
+ end
_quote(value)
end
@@ -17,6 +22,8 @@ module ActiveRecord
# SQLite does not understand dates, so this method will convert a Date
# to a String.
def type_cast(value, column = nil)
+ value = id_value_for_database(value) if value.is_a?(Base)
+
if value.respond_to?(:quoted_id) && value.respond_to?(:id)
return value.id
end
@@ -141,6 +148,10 @@ module ActiveRecord
quoted_date(value).sub(/\A2000-01-01 /, "")
end
+ def quoted_binary(value) # :nodoc:
+ "'#{quote_string(value.to_s)}'"
+ end
+
private
def type_casted_binds(binds)
@@ -151,13 +162,19 @@ module ActiveRecord
end
end
+ def id_value_for_database(value)
+ if primary_key = value.class.primary_key
+ value.instance_variable_get(:@attributes)[primary_key].value_for_database
+ end
+ end
+
def types_which_need_no_typecasting
[nil, Numeric, String]
end
def _quote(value)
case value
- when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
+ when String, ActiveSupport::Multibyte::Chars
"'#{quote_string(value.to_s)}'"
when true then quoted_true
when false then quoted_false
@@ -165,6 +182,7 @@ module ActiveRecord
# BigDecimals need to be put in a non-normalized form and quoted.
when BigDecimal then value.to_s("F")
when Numeric, ActiveSupport::Duration then value.to_s
+ when Type::Binary::Data then quoted_binary(value)
when Type::Time::Value then "'#{quoted_time(value)}'"
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
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 5eb7787226..4682afc188 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -116,16 +116,12 @@ module ActiveRecord
private
- def as_options(value, default = {})
- if value.is_a?(Hash)
- value
- else
- default
- end
+ def as_options(value)
+ value.is_a?(Hash) ? value : {}
end
def polymorphic_options
- as_options(polymorphic, options)
+ as_options(polymorphic)
end
def index_options
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 8de8c05988..1e826ff5ad 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -31,6 +31,8 @@ module ActiveRecord
# Returns the relation names useable to back Active Record models.
# For most adapters this means all #tables and #views.
def data_sources
+ select_values(data_source_sql, "SCHEMA")
+ rescue NotImplementedError
tables | views
end
@@ -39,12 +41,14 @@ module ActiveRecord
# data_source_exists?(:ebooks)
#
def data_source_exists?(name)
+ select_values(data_source_sql(name), "SCHEMA").any? if name.present?
+ rescue NotImplementedError
data_sources.include?(name.to_s)
end
# Returns an array of table names defined in the database.
def tables
- raise NotImplementedError, "#tables is not implemented"
+ select_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
end
# Checks to see if the table +table_name+ exists on the database.
@@ -52,12 +56,14 @@ module ActiveRecord
# table_exists?(:developers)
#
def table_exists?(table_name)
+ select_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
+ rescue NotImplementedError
tables.include?(table_name.to_s)
end
# Returns an array of view names defined in the database.
def views
- raise NotImplementedError, "#views is not implemented"
+ select_values(data_source_sql(type: "VIEW"), "SCHEMA")
end
# Checks to see if the view +view_name+ exists on the database.
@@ -65,6 +71,8 @@ module ActiveRecord
# view_exists?(:ebooks)
#
def view_exists?(view_name)
+ select_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
+ rescue NotImplementedError
views.include?(view_name.to_s)
end
@@ -273,8 +281,8 @@ module ActiveRecord
yield td if block_given?
- if options[:force] && data_source_exists?(table_name)
- drop_table(table_name, options)
+ if options[:force]
+ drop_table(table_name, **options, if_exists: true)
end
result = execute schema_creation.accept td
@@ -334,18 +342,16 @@ module ActiveRecord
# part_id int NOT NULL,
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
#
- def create_join_table(table_1, table_2, options = {})
+ def create_join_table(table_1, table_2, column_options: {}, **options)
join_table_name = find_join_table_name(table_1, table_2, options)
- column_options = options.delete(:column_options) || {}
- column_options.reverse_merge!(null: false)
- type = column_options.delete(:type) || :integer
+ column_options.reverse_merge!(null: false, index: false)
- t1_column, t2_column = [table_1, table_2].map { |t| t.to_s.singularize.foreign_key }
+ t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
create_table(join_table_name, options.merge!(id: false)) do |td|
- td.send type, t1_column, column_options
- td.send type, t2_column, column_options
+ td.references t1_ref, column_options
+ td.references t2_ref, column_options
yield td if block_given?
end
end
@@ -768,16 +774,17 @@ module ActiveRecord
raise ArgumentError, "You must specify the index name"
end
else
- index_name(table_name, column: options)
+ index_name(table_name, index_name_options(options))
end
end
# Verifies the existence of an index with a given name.
- #
- # The default argument is returned if the underlying implementation does not define the indexes method,
- # as there's no way to determine the correct answer in that case.
- def index_name_exists?(table_name, index_name, default)
- return default unless respond_to?(:indexes)
+ def index_name_exists?(table_name, index_name, default = nil)
+ unless default.nil?
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing default to #index_name_exists? is deprecated without replacement.
+ MSG
+ end
index_name = index_name.to_s
indexes(table_name).detect { |i| i.name == index_name }
end
@@ -856,6 +863,7 @@ module ActiveRecord
else
foreign_key_options = { to_table: reference_name }
end
+ foreign_key_options[:column] ||= "#{ref_name}_id"
remove_foreign_key(table_name, **foreign_key_options)
end
@@ -991,12 +999,12 @@ module ActiveRecord
end
def dump_schema_information #:nodoc:
- versions = ActiveRecord::SchemaMigration.order("version").pluck(:version)
+ versions = ActiveRecord::SchemaMigration.all_versions
insert_versions_sql(versions)
end
def insert_versions_sql(versions) # :nodoc:
- sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
+ sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
if versions.is_a?(Array)
sql = "INSERT INTO #{sm_table} (version) VALUES\n"
@@ -1025,12 +1033,11 @@ module ActiveRecord
def assume_migrated_upto_version(version, migrations_paths)
migrations_paths = Array(migrations_paths)
version = version.to_i
- sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
+ sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
- migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
- paths = migrations_paths.map { |p| "#{p}/[0-9]*_*.rb" }
- versions = Dir[*paths].map do |filename|
- filename.split("/").last.split("_").first.to_i
+ migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i)
+ versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
+ ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
end
unless migrated.include?(version)
@@ -1070,7 +1077,7 @@ module ActiveRecord
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
end
- elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
+ elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
if (0..6) === precision
column_type_sql << "(#{precision})"
else
@@ -1122,18 +1129,14 @@ module ActiveRecord
end
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
- if column_name.is_a?(String) && /\W/.match?(column_name)
- column_names = column_name
- else
- column_names = Array(column_name)
- end
+ column_names = index_column_names(column_name)
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
index_type = options[:type].to_s if options.key?(:type)
index_type ||= options[:unique] ? "UNIQUE" : ""
index_name = options[:name].to_s if options.key?(:name)
- index_name ||= index_name(table_name, index_name_options(column_names))
+ index_name ||= index_name(table_name, column_names)
if options.key?(:algorithm)
algorithm = index_algorithms.fetch(options[:algorithm]) {
@@ -1149,7 +1152,7 @@ module ActiveRecord
validate_index_length!(table_name, index_name, options.fetch(:internal, false))
- if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
+ if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
end
index_columns = quoted_columns_for_index(column_names, options).join(", ")
@@ -1213,13 +1216,13 @@ module ActiveRecord
if options.is_a?(Hash)
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
- column_names = Array(options[:column]).map(&:to_s)
+ column_names = index_column_names(options[:column])
else
- column_names = Array(options).map(&:to_s)
+ column_names = index_column_names(options)
end
- if column_names.any?
- checks << lambda { |i| i.columns.join("_and_") == column_names.join("_and_") }
+ if column_names.present?
+ checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
end
raise ArgumentError, "No name or columns specified" if checks.none?
@@ -1266,8 +1269,16 @@ module ActiveRecord
AlterTable.new create_table_definition(name)
end
+ def index_column_names(column_names)
+ if column_names.is_a?(String) && /\W/.match?(column_names)
+ column_names
+ else
+ Array(column_names)
+ end
+ end
+
def index_name_options(column_names)
- if column_names.is_a?(String)
+ if column_names.is_a?(String) && /\W/.match?(column_names)
column_names = column_names.scan(/\w+/).join("_")
end
@@ -1301,6 +1312,14 @@ module ActiveRecord
def can_remove_index_by_name?(options)
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
end
+
+ def data_source_sql(name = nil, type: nil)
+ raise NotImplementedError
+ end
+
+ def quoted_scope(name = nil, type: nil)
+ raise NotImplementedError
+ end
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 ae24ce7236..ef1d9f81a9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -107,6 +107,7 @@ module ActiveRecord
@schema_cache = SchemaCache.new self
@quoted_column_names, @quoted_table_names = {}, {}
@visitor = arel_visitor
+ @lock = Monitor.new
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@@ -153,8 +154,8 @@ module ActiveRecord
Arel::Visitors::ToSql.new(self)
end
- def valid_type?(type)
- false
+ def valid_type?(type) # :nodoc:
+ !native_database_types[type].nil?
end
def schema_creation
@@ -231,16 +232,15 @@ module ActiveRecord
self.class::ADAPTER_NAME
end
- # Does this adapter support migrations?
- def supports_migrations?
- false
+ def supports_migrations? # :nodoc:
+ true
end
+ deprecate :supports_migrations?
- # Can this adapter determine the primary key for tables not attached
- # to an Active Record class, such as join tables?
- def supports_primary_key?
- false
+ 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?
@@ -439,6 +439,9 @@ module ActiveRecord
# 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
reconnect! unless active?
end
@@ -510,6 +513,10 @@ module ActiveRecord
result
end
+ def default_index_type?(index) # :nodoc:
+ index.using.nil?
+ end
+
private
def initialize_type_map(m)
@@ -602,7 +609,11 @@ module ActiveRecord
binds: binds,
type_casted_binds: type_casted_binds,
statement_name: statement_name,
- connection_id: object_id) { yield }
+ connection_id: object_id) do
+ @lock.synchronize do
+ yield
+ end
+ end
rescue => e
raise translate_exception_class(e, sql)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 279c232144..55ec112c17 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -6,6 +6,7 @@ require "active_record/connection_adapters/mysql/quoting"
require "active_record/connection_adapters/mysql/schema_creation"
require "active_record/connection_adapters/mysql/schema_definitions"
require "active_record/connection_adapters/mysql/schema_dumper"
+require "active_record/connection_adapters/mysql/schema_statements"
require "active_record/connection_adapters/mysql/type_metadata"
require "active_support/core_ext/string/strip"
@@ -15,6 +16,7 @@ module ActiveRecord
class AbstractMysqlAdapter < AbstractAdapter
include MySQL::Quoting
include MySQL::ColumnDumper
+ include MySQL::SchemaStatements
def update_table_definition(table_name, base) # :nodoc:
MySQL::Table.new(table_name, base)
@@ -46,6 +48,7 @@ module ActiveRecord
float: { name: "float" },
decimal: { name: "decimal" },
datetime: { name: "datetime" },
+ timestamp: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
binary: { name: "blob", limit: 65535 },
@@ -88,15 +91,6 @@ module ActiveRecord
/mariadb/i.match?(full_version)
end
- # Returns true, since this connection adapter supports migrations.
- def supports_migrations?
- true
- end
-
- def supports_primary_key?
- true
- end
-
def supports_bulk_alter? #:nodoc:
true
end
@@ -318,57 +312,6 @@ module ActiveRecord
show_variable "collation_database"
end
- def tables # :nodoc:
- sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
- sql << " AND table_schema = #{quote(@config[:database])}"
-
- select_values(sql, "SCHEMA")
- end
-
- def views # :nodoc:
- select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", "SCHEMA")
- end
-
- def data_sources # :nodoc:
- sql = "SELECT table_name FROM information_schema.tables "
- sql << "WHERE table_schema = #{quote(@config[:database])}"
-
- select_values(sql, "SCHEMA")
- end
-
- def table_exists?(table_name) # :nodoc:
- return false unless table_name.present?
-
- schema, name = extract_schema_qualified_name(table_name)
-
- sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
- sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
-
- select_values(sql, "SCHEMA").any?
- end
-
- def data_source_exists?(table_name) # :nodoc:
- return false unless table_name.present?
-
- schema, name = extract_schema_qualified_name(table_name)
-
- sql = "SELECT table_name FROM information_schema.tables "
- sql << "WHERE table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
-
- select_values(sql, "SCHEMA").any?
- end
-
- def view_exists?(view_name) # :nodoc:
- return false unless view_name.present?
-
- schema, name = extract_schema_qualified_name(view_name)
-
- sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
- sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
-
- select_values(sql, "SCHEMA").any?
- end
-
def truncate(table_name, name = nil)
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
end
@@ -414,13 +357,13 @@ module ActiveRecord
end
def table_comment(table_name) # :nodoc:
- schema, name = extract_schema_qualified_name(table_name)
+ scope = quoted_scope(table_name)
select_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT table_comment
FROM information_schema.tables
- WHERE table_schema = #{quote(schema)}
- AND table_name = #{quote(name)}
+ WHERE table_schema = #{scope[:schema]}
+ AND table_name = #{scope[:name]}
SQL
end
@@ -520,7 +463,7 @@ module ActiveRecord
def foreign_keys(table_name)
raise ArgumentError unless table_name.present?
- schema, name = extract_schema_qualified_name(table_name)
+ scope = quoted_scope(table_name)
fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
SELECT fk.referenced_table_name AS 'to_table',
@@ -533,9 +476,9 @@ module ActiveRecord
JOIN information_schema.referential_constraints rc
USING (constraint_schema, constraint_name)
WHERE fk.referenced_column_name IS NOT NULL
- AND fk.table_schema = #{quote(schema)}
- AND fk.table_name = #{quote(name)}
- AND rc.table_name = #{quote(name)}
+ AND fk.table_schema = #{scope[:schema]}
+ AND fk.table_name = #{scope[:name]}
+ AND rc.table_name = #{scope[:name]}
SQL
fk_info.map do |row|
@@ -607,14 +550,14 @@ module ActiveRecord
def primary_keys(table_name) # :nodoc:
raise ArgumentError unless table_name.present?
- schema, name = extract_schema_qualified_name(table_name)
+ scope = quoted_scope(table_name)
select_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name = 'PRIMARY'
- AND table_schema = #{quote(schema)}
- AND table_name = #{quote(name)}
+ AND table_schema = #{scope[:schema]}
+ AND table_name = #{scope[:name]}
ORDER BY ordinal_position
SQL
end
@@ -651,8 +594,8 @@ module ActiveRecord
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
end
- def valid_type?(type)
- !native_database_types[type].nil?
+ def default_index_type?(index) # :nodoc:
+ index.using == :btree || super
end
private
@@ -708,7 +651,7 @@ module ActiveRecord
end
def extract_precision(sql_type)
- if /time/.match?(sql_type)
+ if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
super || 0
else
super
@@ -785,11 +728,11 @@ module ActiveRecord
def change_column_sql(table_name, column_name, type, options = {})
column = column_for(table_name, column_name)
- unless options_include_default?(options)
+ unless options.key?(:default)
options[:default] = column.default
end
- unless options.has_key?(:null)
+ unless options.key?(:null)
options[:null] = column.null
end
@@ -869,9 +812,9 @@ module ActiveRecord
variables["sql_auto_is_null"] = 0
# Increase timeout so the server doesn't disconnect us.
- wait_timeout = @config[:wait_timeout]
+ wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
- variables["wait_timeout"] = self.class.type_cast_config_to_integer(wait_timeout)
+ variables["wait_timeout"] = wait_timeout
defaults = [":default", :default].to_set
@@ -948,12 +891,6 @@ module ActiveRecord
)
end
- def extract_schema_qualified_name(string) # :nodoc:
- schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
- schema, name = @config[:database], schema unless name
- [schema, name]
- end
-
def integer_to_sql(limit) # :nodoc:
case limit
when 1; "tinyint"
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index dcf56997db..3e4ea28f63 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -149,9 +149,18 @@ module ActiveRecord
# Expands each key in @configurations hash into fully resolved hash
def resolve_all
config = configurations.dup
+
+ if env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ env_config = config[env] if config[env].is_a?(Hash) && !(config[env].key?("adapter") || config[env].key?("url"))
+ end
+
+ config.reject! { |k, v| v.is_a?(Hash) && !(v.key?("adapter") || v.key?("url")) }
+ config.merge! env_config if env_config
+
config.each do |key, value|
config[key] = resolve(value) if value
end
+
config
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
index 9d11ad28d4..d4f5906b33 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
@@ -36,15 +36,9 @@ module ActiveRecord
end
end
- private
-
- def _quote(value)
- if value.is_a?(Type::Binary::Data)
- "x'#{value.hex}'"
- else
- super
- end
- end
+ def quoted_binary(value)
+ "x'#{value.hex}'"
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
index e8358271ab..083cd6340f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
@@ -25,6 +25,14 @@ module ActiveRecord
end
def add_column_options!(sql, options)
+ # By default, TIMESTAMP columns are NOT NULL, cannot contain NULL values,
+ # and assigning NULL assigns the current timestamp. To permit a TIMESTAMP
+ # column to contain NULL, explicitly declare it with the NULL attribute.
+ # See http://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
+ if /\Atimestamp\b/.match?(options[:column].sql_type) && !options[:primary_key]
+ sql << " NULL" unless options[:null] == false || options_include_default?(options)
+ end
+
if charset = options[:charset]
sql << " CHARACTER SET #{charset}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
index 773bbcef4e..6d88c14d50 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
@@ -75,6 +75,11 @@ module ActiveRecord
super
end
+
+ private
+ def aliased_types(name, fallback)
+ fallback
+ end
end
class Table < ActiveRecord::ConnectionAdapters::Table
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index ad4a069d73..3e0afd9761 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -30,7 +30,10 @@ module ActiveRecord
end
def schema_type(column)
- if column.sql_type == "tinyblob"
+ case column.sql_type
+ when /\Atimestamp\b/
+ :timestamp
+ when "tinyblob"
:blob
else
super
@@ -38,7 +41,7 @@ module ActiveRecord
end
def schema_precision(column)
- super unless /time/.match?(column.sql_type) && column.precision == 0
+ super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
end
def schema_collation(column)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
new file mode 100644
index 0000000000..10c8bd179a
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -0,0 +1,33 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module MySQL
+ module SchemaStatements # :nodoc:
+ private
+ def data_source_sql(name = nil, type: nil)
+ scope = quoted_scope(name, type: type)
+
+ sql = "SELECT table_name FROM information_schema.tables"
+ sql << " WHERE table_schema = #{scope[:schema]}"
+ sql << " AND table_name = #{scope[:name]}" if scope[:name]
+ sql << " AND table_type = #{scope[:type]}" if scope[:type]
+ sql
+ end
+
+ def quoted_scope(name = nil, type: nil)
+ schema, name = extract_schema_qualified_name(name)
+ scope = {}
+ scope[:schema] = schema ? quote(schema) : "database()"
+ scope[:name] = quote(name) if name
+ scope[:type] = quote(type) if type
+ scope
+ end
+
+ def extract_schema_qualified_name(string)
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
+ schema, name = nil, schema unless name
+ [schema, name]
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
index 24dcf852e1..9ad6a6c0d0 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
@@ -2,6 +2,8 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
+ undef to_yaml if method_defined?(:to_yaml)
+
attr_reader :extra
def initialize(type_metadata, extra: "")
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 0e526f6201..4098250f3e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -11,6 +11,7 @@ require "active_record/connection_adapters/postgresql/oid/inet"
require "active_record/connection_adapters/postgresql/oid/json"
require "active_record/connection_adapters/postgresql/oid/jsonb"
require "active_record/connection_adapters/postgresql/oid/money"
+require "active_record/connection_adapters/postgresql/oid/oid"
require "active_record/connection_adapters/postgresql/oid/point"
require "active_record/connection_adapters/postgresql/oid/legacy_point"
require "active_record/connection_adapters/postgresql/oid/range"
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index e1a75f8e5e..a73a8c1726 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -23,7 +23,7 @@ module ActiveRecord
when ::String
type_cast_array(@pg_decoder.decode(value), :deserialize)
when Data
- deserialize(value.values)
+ type_cast_array(value.values, :deserialize)
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/oid.rb
new file mode 100644
index 0000000000..9c2ac08b30
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/oid.rb
@@ -0,0 +1,13 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module PostgreSQL
+ module OID # :nodoc:
+ class Oid < Type::Integer # :nodoc:
+ def type
+ :oid
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb
index 2d2fede4e8..564e82a4ac 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb
@@ -5,8 +5,9 @@ module ActiveRecord
class SpecializedString < Type::String # :nodoc:
attr_reader :type
- def initialize(type)
+ def initialize(type, **options)
@type = type
+ super(options)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 3783925954..6663448a99 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -55,6 +55,10 @@ module ActiveRecord
end
end
+ def quoted_binary(value) # :nodoc:
+ "'#{escape_bytea(value.to_s)}'"
+ end
+
def quote_default_expression(value, column) # :nodoc:
if value.is_a?(Proc)
value.call
@@ -76,8 +80,6 @@ module ActiveRecord
def _quote(value)
case value
- when Type::Binary::Data
- "'#{escape_bytea(value.to_s)}'"
when OID::Xml::Data
"xml '#{quote_string(value.to_s)}'"
when OID::Bit::Data
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
index 7491fb716e..11ea1e5144 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -88,6 +88,10 @@ module ActiveRecord
args.each { |name| column(name, :inet, options) }
end
+ def interval(*args, **options)
+ args.each { |name| column(name, :interval, options) }
+ end
+
def int4range(*args, **options)
args.each { |name| column(name, :int4range, options) }
end
@@ -120,6 +124,10 @@ module ActiveRecord
args.each { |name| column(name, :numrange, options) }
end
+ def oid(*args, **options)
+ args.each { |name| column(name, :oid, options) }
+ end
+
def point(*args, **options)
args.each { |name| column(name, :point, options) }
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index a4b1723fc7..a332375b78 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -54,87 +54,24 @@ module ActiveRecord
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
end
- # Returns the list of all tables in the schema search path.
- def tables
- select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", "SCHEMA")
- end
-
- def data_sources # :nodoc
- select_values(<<-SQL, "SCHEMA")
- SELECT c.relname
- FROM pg_class c
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
- AND n.nspname = ANY (current_schemas(false))
- SQL
- end
-
- def views # :nodoc:
- select_values(<<-SQL, "SCHEMA")
- SELECT c.relname
- FROM pg_class c
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
- AND n.nspname = ANY (current_schemas(false))
- SQL
- end
-
- # Returns true if table exists.
- # If the schema is not specified as part of +name+ then it will only find tables within
- # the current schema search path (regardless of permissions to access tables in other schemas)
- def table_exists?(name)
- name = Utils.extract_schema_qualified_name(name.to_s)
- return false unless name.identifier
-
- select_values(<<-SQL, "SCHEMA").any?
- SELECT tablename
- FROM pg_tables
- WHERE tablename = #{quote(name.identifier)}
- AND schemaname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
- SQL
- end
-
- def data_source_exists?(name) # :nodoc:
- name = Utils.extract_schema_qualified_name(name.to_s)
- return false unless name.identifier
-
- select_values(<<-SQL, "SCHEMA").any?
- SELECT c.relname
- FROM pg_class c
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
- AND c.relname = #{quote(name.identifier)}
- AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
- SQL
- end
-
- def view_exists?(view_name) # :nodoc:
- name = Utils.extract_schema_qualified_name(view_name.to_s)
- return false unless name.identifier
-
- select_values(<<-SQL, "SCHEMA").any?
- SELECT c.relname
- FROM pg_class c
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
- AND c.relname = #{quote(name.identifier)}
- AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
- SQL
- end
-
def drop_table(table_name, options = {}) # :nodoc:
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
# Returns true if schema exists.
def schema_exists?(name)
- select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", "SCHEMA").to_i > 0
+ select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
end
# Verifies existence of an index with a given name.
- def index_name_exists?(table_name, index_name, default)
- table = Utils.extract_schema_qualified_name(table_name.to_s)
- index = Utils.extract_schema_qualified_name(index_name.to_s)
+ def index_name_exists?(table_name, index_name, default = nil)
+ unless default.nil?
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing default to #index_name_exists? is deprecated without replacement.
+ MSG
+ end
+ table = quoted_scope(table_name)
+ index = quoted_scope(index_name)
select_value(<<-SQL, "SCHEMA").to_i > 0
SELECT COUNT(*)
@@ -143,9 +80,9 @@ module ActiveRecord
INNER JOIN pg_class i ON d.indexrelid = i.oid
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
WHERE i.relkind = 'i'
- AND i.relname = '#{index.identifier}'
- AND t.relname = '#{table.identifier}'
- AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'}
+ AND i.relname = #{index[:name]}
+ AND t.relname = #{table[:name]}
+ AND n.nspname = #{index[:schema]}
SQL
end
@@ -157,7 +94,7 @@ module ActiveRecord
MSG
end
- table = Utils.extract_schema_qualified_name(table_name.to_s)
+ scope = quoted_scope(table_name)
result = query(<<-SQL, "SCHEMA")
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
@@ -171,8 +108,8 @@ module ActiveRecord
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
WHERE i.relkind = 'i'
AND d.indisprimary = 'f'
- AND t.relname = '#{table.identifier}'
- AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
+ AND t.relname = #{scope[:name]}
+ AND n.nspname = #{scope[:schema]}
ORDER BY i.relname
SQL
@@ -234,22 +171,22 @@ module ActiveRecord
# Returns a comment stored in database for given table
def table_comment(table_name) # :nodoc:
- name = Utils.extract_schema_qualified_name(table_name.to_s)
- if name.identifier
+ scope = quoted_scope(table_name, type: "BASE TABLE")
+ if scope[:name]
select_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
FROM pg_catalog.pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relname = #{quote(name.identifier)}
- AND c.relkind IN ('r') -- (r)elation/table
- AND n.nspname = #{name.schema ? quote(name.schema) : 'ANY (current_schemas(false))'}
+ WHERE c.relname = #{scope[:name]}
+ AND c.relkind IN (#{scope[:type]})
+ AND n.nspname = #{scope[:schema]}
SQL
end
end
# Returns the current database name.
def current_database
- select_value("select current_database()", "SCHEMA")
+ select_value("SELECT current_database()", "SCHEMA")
end
# Returns the current schema name.
@@ -425,17 +362,16 @@ module ActiveRecord
end
def primary_keys(table_name) # :nodoc:
+ scope = quoted_scope(table_name)
select_values(<<-SQL.strip_heredoc, "SCHEMA")
- WITH pk_constraint AS (
- SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
- WHERE contype = 'p'
- AND conrelid = #{quote(quote_table_name(table_name))}::regclass
- ), cons AS (
- SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
- )
- SELECT attr.attname FROM pg_attribute attr
- INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
- ORDER BY cons.rownum
+ SELECT column_name
+ FROM information_schema.key_column_usage kcu
+ JOIN information_schema.table_constraints tc
+ USING (table_schema, table_name, constraint_name)
+ WHERE constraint_type = 'PRIMARY KEY'
+ AND kcu.table_name = #{scope[:name]}
+ AND kcu.table_schema = #{scope[:schema]}
+ ORDER BY kcu.ordinal_position
SQL
end
@@ -483,7 +419,7 @@ module ActiveRecord
end
execute sql
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
+ change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
end
@@ -573,6 +509,7 @@ module ActiveRecord
end
def foreign_keys(table_name)
+ scope = quoted_scope(table_name)
fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
FROM pg_constraint c
@@ -582,8 +519,8 @@ module ActiveRecord
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
JOIN pg_namespace t3 ON c.connamespace = t3.oid
WHERE c.contype = 'f'
- AND t1.relname = #{quote(table_name)}
- AND t3.nspname = ANY (current_schemas(false))
+ AND t1.relname = #{scope[:name]}
+ AND t3.nspname = #{scope[:schema]}
ORDER BY c.conname
SQL
@@ -609,10 +546,6 @@ module ActiveRecord
end
end
- def index_name_length
- 63
- end
-
# Maps logical Rails types to PostgreSQL-specific data types.
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
sql = \
@@ -671,6 +604,39 @@ module ActiveRecord
)
PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
end
+
+ private
+ def data_source_sql(name = nil, type: nil)
+ scope = quoted_scope(name, type: type)
+ scope[:type] ||= "'r','v','m'" # (r)elation/table, (v)iew, (m)aterialized view
+
+ sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
+ sql << " WHERE n.nspname = #{scope[:schema]}"
+ sql << " AND c.relname = #{scope[:name]}" if scope[:name]
+ sql << " AND c.relkind IN (#{scope[:type]})"
+ sql
+ end
+
+ def quoted_scope(name = nil, type: nil)
+ schema, name = extract_schema_qualified_name(name)
+ type = \
+ case type
+ when "BASE TABLE"
+ "'r'"
+ when "VIEW"
+ "'v','m'"
+ end
+ scope = {}
+ scope[:schema] = schema ? quote(schema) : "ANY (current_schemas(false))"
+ scope[:name] = quote(name) if name
+ scope[:type] = type if type
+ scope
+ end
+
+ def extract_schema_qualified_name(string)
+ name = Utils.extract_schema_qualified_name(string.to_s)
+ [name.schema, name.identifier]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
index 311988625f..f57179ae59 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
@@ -1,6 +1,8 @@
module ActiveRecord
module ConnectionAdapters
class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
+ undef to_yaml if method_defined?(:to_yaml)
+
attr_reader :oid, :fmod, :array
def initialize(type_metadata, oid: nil, fmod: nil)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 6b1e870a31..22c37abb78 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -3,6 +3,7 @@ gem "pg", "~> 0.18"
require "pg"
require "active_record/connection_adapters/abstract_adapter"
+require "active_record/connection_adapters/statement_pool"
require "active_record/connection_adapters/postgresql/column"
require "active_record/connection_adapters/postgresql/database_statements"
require "active_record/connection_adapters/postgresql/explain_pretty_printer"
@@ -15,7 +16,6 @@ require "active_record/connection_adapters/postgresql/schema_dumper"
require "active_record/connection_adapters/postgresql/schema_statements"
require "active_record/connection_adapters/postgresql/type_metadata"
require "active_record/connection_adapters/postgresql/utils"
-require "active_record/connection_adapters/statement_pool"
module ActiveRecord
module ConnectionHandling # :nodoc:
@@ -109,6 +109,8 @@ module ActiveRecord
bit: { name: "bit" },
bit_varying: { name: "bit varying" },
money: { name: "money" },
+ interval: { name: "interval" },
+ oid: { name: "oid" },
}
OID = PostgreSQL::OID #:nodoc:
@@ -213,7 +215,7 @@ module ActiveRecord
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@local_tz = nil
- @table_alias_length = nil
+ @max_identifier_length = nil
connect
add_pg_encoders
@@ -234,7 +236,9 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
- @statements.clear
+ @lock.synchronize do
+ @statements.clear
+ end
end
def truncate(table_name, name = nil)
@@ -277,16 +281,6 @@ module ActiveRecord
NATIVE_DATABASE_TYPES
end
- # Returns true, since this connection adapter supports migrations.
- def supports_migrations?
- true
- end
-
- # Does PostgreSQL support finding primary key on non-Active Record tables?
- def supports_primary_key? #:nodoc:
- true
- end
-
def set_standard_conforming_strings
execute("SET standard_conforming_strings = on", "SCHEMA")
end
@@ -364,8 +358,9 @@ module ActiveRecord
# Returns the configured supported identifier length supported by PostgreSQL
def table_alias_length
- @table_alias_length ||= query("SHOW max_identifier_length", "SCHEMA")[0][0].to_i
+ @max_identifier_length ||= select_value("SHOW max_identifier_length", "SCHEMA").to_i
end
+ alias index_name_length table_alias_length
# Set the authorized user for this session
def session_auth=(user)
@@ -377,10 +372,6 @@ module ActiveRecord
@use_insert_returning
end
- def valid_type?(type)
- !native_database_types[type].nil?
- end
-
def update_table_definition(table_name, base) #:nodoc:
PostgreSQL::Table.new(table_name, base)
end
@@ -405,6 +396,10 @@ module ActiveRecord
@connection.server_version
end
+ def default_index_type?(index) # :nodoc:
+ index.using == :btree || super
+ end
+
private
# See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
@@ -456,7 +451,7 @@ module ActiveRecord
register_class_with_limit m, "int2", Type::Integer
register_class_with_limit m, "int4", Type::Integer
register_class_with_limit m, "int8", Type::Integer
- m.alias_type "oid", "int2"
+ m.register_type "oid", OID::Oid.new
m.register_type "float4", Type::Float.new
m.alias_type "float8", "float4"
m.register_type "text", Type::Text.new
@@ -491,8 +486,10 @@ module ActiveRecord
m.register_type "polygon", OID::SpecializedString.new(:polygon)
m.register_type "circle", OID::SpecializedString.new(:circle)
- # FIXME: why are we keeping these types as strings?
- m.alias_type "interval", "varchar"
+ m.register_type "interval" do |_, _, sql_type|
+ precision = extract_precision(sql_type)
+ OID::SpecializedString.new(:interval, precision: precision)
+ end
register_class_with_precision m, "time", Type::Time
register_class_with_precision m, "timestamp", OID::DateTime
@@ -634,8 +631,10 @@ module ActiveRecord
if in_transaction?
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
else
- # outside of transactions we can simply flush this query and retry
- @statements.delete sql_key(sql)
+ @lock.synchronize do
+ # outside of transactions we can simply flush this query and retry
+ @statements.delete sql_key(sql)
+ end
retry
end
end
@@ -671,19 +670,21 @@ module ActiveRecord
# Prepare the statement if it hasn't been prepared, return
# the statement key.
def prepare_statement(sql)
- sql_key = sql_key(sql)
- unless @statements.key? sql_key
- nextkey = @statements.next_key
- begin
- @connection.prepare nextkey, sql
- rescue => e
- raise translate_exception_class(e, sql)
+ @lock.synchronize do
+ sql_key = sql_key(sql)
+ unless @statements.key? sql_key
+ nextkey = @statements.next_key
+ begin
+ @connection.prepare nextkey, sql
+ rescue => e
+ raise translate_exception_class(e, sql)
+ end
+ # Clear the queue
+ @connection.get_last_result
+ @statements[sql_key] = nextkey
end
- # Clear the queue
- @connection.get_last_result
- @statements[sql_key] = nextkey
+ @statements[sql_key]
end
- @statements[sql_key]
end
# Connects to a PostgreSQL server and sets up the adapter depending on the
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
index f01ed67b0f..7276a65098 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
@@ -18,15 +18,11 @@ module ActiveRecord
quoted_date(value)
end
- private
+ def quoted_binary(value)
+ "x'#{value.hex}'"
+ end
- def _quote(value)
- if value.is_a?(Type::Binary::Data)
- "x'#{value.hex}'"
- else
- super
- end
- end
+ private
def _type_cast(value)
case value
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
new file mode 100644
index 0000000000..4ba245a0d8
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
@@ -0,0 +1,32 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module SQLite3
+ module SchemaStatements # :nodoc:
+ private
+ def data_source_sql(name = nil, type: nil)
+ scope = quoted_scope(name, type: type)
+ scope[:type] ||= "'table','view'"
+
+ sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
+ sql << " AND name = #{scope[:name]}" if scope[:name]
+ sql << " AND type IN (#{scope[:type]})"
+ sql
+ end
+
+ def quoted_scope(name = nil, type: nil)
+ type = \
+ case type
+ when "BASE TABLE"
+ "'table'"
+ when "VIEW"
+ "'view'"
+ end
+ scope = {}
+ scope[:name] = quote(name) if name
+ scope[:type] = type if type
+ scope
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 3d728d74cf..d24bfc0c93 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -5,6 +5,7 @@ require "active_record/connection_adapters/sqlite3/quoting"
require "active_record/connection_adapters/sqlite3/schema_creation"
require "active_record/connection_adapters/sqlite3/schema_definitions"
require "active_record/connection_adapters/sqlite3/schema_dumper"
+require "active_record/connection_adapters/sqlite3/schema_statements"
gem "sqlite3", "~> 1.3.6"
require "sqlite3"
@@ -55,6 +56,7 @@ module ActiveRecord
include SQLite3::Quoting
include SQLite3::ColumnDumper
+ include SQLite3::SchemaStatements
NATIVE_DATABASE_TYPES = {
primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
@@ -117,15 +119,6 @@ module ActiveRecord
true
end
- # Returns true, since this connection adapter supports migrations.
- def supports_migrations? #:nodoc:
- true
- end
-
- def supports_primary_key? #:nodoc:
- true
- end
-
def requires_reloading?
true
end
@@ -167,10 +160,6 @@ module ActiveRecord
true
end
- def valid_type?(type)
- true
- end
-
# Returns 62. SQLite supports index names up to 64
# characters. The rest is used by Rails internally to perform
# temporary rename operations
@@ -278,45 +267,6 @@ module ActiveRecord
# SCHEMA STATEMENTS ========================================
- def tables # :nodoc:
- select_values("SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'", "SCHEMA")
- end
-
- def data_sources # :nodoc:
- select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", "SCHEMA")
- end
-
- def views # :nodoc:
- select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", "SCHEMA")
- end
-
- def table_exists?(table_name) # :nodoc:
- return false unless table_name.present?
-
- sql = "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'"
- sql << " AND name = #{quote(table_name)}"
-
- select_values(sql, "SCHEMA").any?
- end
-
- def data_source_exists?(table_name) # :nodoc:
- return false unless table_name.present?
-
- sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
- sql << " AND name = #{quote(table_name)}"
-
- select_values(sql, "SCHEMA").any?
- end
-
- def view_exists?(view_name) # :nodoc:
- return false unless view_name.present?
-
- sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
- sql << " AND name = #{quote(view_name)}"
-
- select_values(sql, "SCHEMA").any?
- end
-
def new_column_from_field(table_name, field) # :nondoc:
case field["dflt_value"]
when /^null$/i
@@ -424,11 +374,10 @@ module ActiveRecord
def change_column(table_name, column_name, type, options = {}) #:nodoc:
alter_table(table_name) do |definition|
- include_default = options_include_default?(options)
definition[column_name].instance_eval do
self.type = type
self.limit = options[:limit] if options.include?(:limit)
- self.default = options[:default] if include_default
+ self.default = options[:default] if options.include?(:default)
self.null = options[:null] if options.include?(:null)
self.precision = options[:precision] if options.include?(:precision)
self.scale = options[:scale] if options.include?(:scale)
@@ -549,7 +498,7 @@ module ActiveRecord
end
def sqlite_version
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value("select sqlite_version(*)"))
+ @sqlite_version ||= SQLite3Adapter::Version.new(select_value("SELECT sqlite_version(*)"))
end
def translate_exception(exception, message)