aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb8
-rw-r--r--activerecord/lib/active_record/insert_all.rb8
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb24
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb7
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb10
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb12
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb10
14 files changed, 76 insertions, 51 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 3c872d6c1b..bfd1c8402c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -144,7 +144,11 @@ module ActiveRecord
# Executes the truncate statement.
def truncate(table_name, name = nil)
- raise NotImplementedError
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
+ end
+
+ def truncate_tables(*table_names) # :nodoc:
+ table_names.each { |table_name| truncate(table_name) }
end
# Executes update +sql+ statement in the context of this connection using
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 202187a047..7aad306d50 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -384,6 +384,11 @@ module ActiveRecord
false
end
+ # Does this adapter support optimizer hints?
+ def supports_optimizer_hints?
+ false
+ end
+
def supports_lazy_transactions?
false
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 a518b897a0..5ceddf449c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -103,6 +103,11 @@ module ActiveRecord
mariadb? || version >= "5.7.5"
end
+ # See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
+ def supports_optimizer_hints?
+ !mariadb? && version >= "5.7.7"
+ end
+
def supports_advisory_locks?
true
end
@@ -269,10 +274,6 @@ module ActiveRecord
show_variable "collation_database"
end
- def truncate(table_name, name = nil)
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
- end
-
def table_comment(table_name) # :nodoc:
scope = quoted_scope(table_name)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 41633872e2..208934385f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -143,6 +143,12 @@ module ActiveRecord
end
end
+ def truncate_tables(*table_names) # :nodoc:
+ unless table_names.empty?
+ execute "TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"
+ end
+ end
+
# Begins a transaction.
def begin_db_transaction
execute "BEGIN"
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 672f11cfcb..29f764e8f4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -259,10 +259,6 @@ module ActiveRecord
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end
- def truncate(table_name, name = nil)
- exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
- end
-
# Is this connection alive and ready for queries?
def active?
@lock.synchronize do
@@ -351,6 +347,13 @@ module ActiveRecord
postgresql_version >= 90400
end
+ def supports_optimizer_hints?
+ unless defined?(@has_pg_hint_plan)
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
+ end
+ @has_pg_hint_plan
+ end
+
def supports_lazy_transactions?
true
end
@@ -381,9 +384,12 @@ module ActiveRecord
}
end
+ def extension_available?(name)
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
+ end
+
def extension_enabled?(name)
- res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
- res.cast_values.first
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
end
def extensions
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 3004caf82d..8ee7e4c763 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -155,10 +155,6 @@ module ActiveRecord
@connection.close rescue nil
end
- def truncate(table_name, name = nil)
- execute "DELETE FROM #{quote_table_name(table_name)}", name
- end
-
def supports_index_sort_order?
true
end
@@ -279,6 +275,10 @@ module ActiveRecord
end
end
+ def truncate(table_name, name = nil) # :nodoc:
+ execute "DELETE FROM #{quote_table_name(table_name)}", name
+ end
+
def begin_db_transaction #:nodoc:
log("begin transaction", nil) { @connection.transaction }
end
diff --git a/activerecord/lib/active_record/insert_all.rb b/activerecord/lib/active_record/insert_all.rb
index 5baaea344b..3833cb2fcf 100644
--- a/activerecord/lib/active_record/insert_all.rb
+++ b/activerecord/lib/active_record/insert_all.rb
@@ -92,7 +92,15 @@ module ActiveRecord
def values_list
columns = connection.schema_cache.columns_hash(model.table_name)
+
+ column_names = columns.keys.to_set
keys = insert_all.keys.to_set
+ unknown_columns = keys - column_names
+
+ unless unknown_columns.empty?
+ raise UnknownAttributeError.new(model.new, unknown_columns.first)
+ end
+
types = keys.map { |key| [ key, connection.lookup_cast_type_from_column(columns[key]) ] }.to_h
values_list = insert_all.inserts.map do |attributes|
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 7a8d0cb663..86021f0a80 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -14,7 +14,7 @@ module ActiveRecord
:find_each, :find_in_batches, :in_batches,
:select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending, :or,
- :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
+ :having, :create_with, :distinct, :references, :none, :unscope, :optimizer_hints, :merge, :except, :only,
:count, :average, :minimum, :maximum, :sum, :calculate,
:pluck, :pick, :ids
].freeze # :nodoc:
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ac37f73b76..37179774fa 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -5,7 +5,7 @@ module ActiveRecord
class Relation
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
:order, :joins, :left_outer_joins, :references,
- :extending, :unscope]
+ :extending, :unscope, :optimizer_hints]
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
:reverse_order, :distinct, :create_with, :skip_query_cache]
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 0f2cff4c9c..2603fac8b2 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -901,6 +901,29 @@ module ActiveRecord
self
end
+ # Specify optimizer hints to be used in the SELECT statement.
+ #
+ # Example (for MySQL):
+ #
+ # Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
+ # # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
+ #
+ # Example (for PostgreSQL with pg_hint_plan):
+ #
+ # Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
+ # # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
+ def optimizer_hints(*args)
+ check_if_method_has_arguments!(:optimizer_hints, args)
+ spawn.optimizer_hints!(*args)
+ end
+
+ def optimizer_hints!(*args) # :nodoc:
+ args.flatten!
+
+ self.optimizer_hints_values += args
+ self
+ end
+
# Reverse the existing order clause on the relation.
#
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
@@ -977,6 +1000,7 @@ module ActiveRecord
build_select(arel)
+ arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
arel.distinct(distinct_value)
arel.from(build_from) unless from_clause.empty?
arel.lock(lock_value) if lock_value
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index a8433fa0db..5a46003732 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -185,14 +185,17 @@ module ActiveRecord
def truncate_tables(configuration)
ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
table_names = ActiveRecord::Base.connection.tables
- internal_table_names = [
+ table_names -= [
ActiveRecord::Base.schema_migrations_table_name,
ActiveRecord::Base.internal_metadata_table_name
]
- class_for_adapter(configuration["adapter"]).new(configuration).truncate_tables(*table_names.without(*internal_table_names))
+ ActiveRecord::Base.connection.disable_referential_integrity do
+ ActiveRecord::Base.connection.truncate_tables(*table_names)
+ end unless table_names.empty?
end
end
+ private :truncate_tables
def truncate_all(environment = env)
ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index f2b4ead98d..1c1b29b5e1 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -31,16 +31,6 @@ module ActiveRecord
connection.recreate_database configuration["database"], creation_options
end
- def truncate_tables(*table_names)
- return if table_names.empty?
-
- ActiveRecord::Base.connection.disable_referential_integrity do
- table_names.each do |table_name|
- ActiveRecord::Base.connection.truncate(table_name)
- end
- end
- end
-
def charset
connection.charset
end
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index dc368eb97d..8acb11f75f 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -48,18 +48,6 @@ module ActiveRecord
create true
end
- def truncate_tables(*table_names)
- return if table_names.empty?
-
- ActiveRecord::Base.connection.disable_referential_integrity do
- quoted_table_names = table_names.map do |table_name|
- ActiveRecord::Base.connection.quote_table_name(table_name)
- end
-
- ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{quoted_table_names.join(", ")}"
- end
- end
-
def structure_dump(filename, extra_flags)
set_psql_env
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index cedbae6b7f..a82cea80ca 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -33,16 +33,6 @@ module ActiveRecord
create
end
- def truncate_tables(*table_names)
- return if table_names.empty?
-
- ActiveRecord::Base.connection.disable_referential_integrity do
- table_names.each do |table_name|
- ActiveRecord::Base.connection.truncate(table_name)
- end
- end
- end
-
def charset
connection.encoding
end