aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb45
-rw-r--r--activerecord/lib/active_record/core.rb41
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb5
7 files changed, 87 insertions, 40 deletions
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index eeb88e4b7a..1e92ee3b96 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -329,14 +329,7 @@ module ActiveRecord
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
def attribute_for_inspect(attr_name)
value = read_attribute(attr_name)
-
- if value.is_a?(String) && value.length > 50
- "#{value[0, 50]}...".inspect
- elsif value.is_a?(Date) || value.is_a?(Time)
- %("#{value.to_s(:db)}")
- else
- value.inspect
- end
+ format_for_inspect(value)
end
# Returns +true+ if the specified +attribute+ has been set by the user or by a
@@ -456,6 +449,16 @@ module ActiveRecord
end
end
+ def format_for_inspect(value)
+ if value.is_a?(String) && value.length > 50
+ "#{value[0, 50]}...".inspect
+ elsif value.is_a?(Date) || value.is_a?(Time)
+ %("#{value.to_s(:db)}")
+ else
+ value.inspect
+ end
+ end
+
def readonly_attribute?(name)
self.class.readonly_attributes.include?(name)
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 0f73641369..13c799b64a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -72,6 +72,10 @@ module ActiveRecord
!mariadb? && version >= "8.0.1"
end
+ def supports_expression_index?
+ !mariadb? && version >= "8.0.13"
+ end
+
def supports_transaction_isolation?
true
end
diff --git a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb b/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
index 3dcb916d99..f158946c6d 100644
--- a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
+++ b/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
@@ -10,8 +10,17 @@ module ActiveRecord
super
end
- def visit_Arel_Nodes_In(*)
+ def visit_Arel_Nodes_In(o, collector)
@preparable = false
+
+ if Array === o.right && !o.right.empty?
+ o.right.delete_if do |bind|
+ if Arel::Nodes::BindParam === bind && Relation::QueryAttribute === bind.value
+ !bind.value.boundable?
+ end
+ end
+ end
+
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
index e167c01802..4894fd1c08 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -35,13 +35,39 @@ module ActiveRecord
]
end
- indexes.last[-2] << row[:Column_name]
- indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
- indexes.last[-1][:orders].merge!(row[:Column_name] => :desc) if row[:Collation] == "D"
+ if row[:Expression]
+ expression = row[:Expression]
+ expression = +"(#{expression})" unless expression.start_with?("(")
+ indexes.last[-2] << expression
+ indexes.last[-1][:expressions] ||= {}
+ indexes.last[-1][:expressions][expression] = expression
+ indexes.last[-1][:orders][expression] = :desc if row[:Collation] == "D"
+ else
+ indexes.last[-2] << row[:Column_name]
+ indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
+ indexes.last[-1][:orders][row[:Column_name]] = :desc if row[:Collation] == "D"
+ end
end
end
- indexes.map { |index| IndexDefinition.new(*index) }
+ indexes.map do |index|
+ options = index.last
+
+ if expressions = options.delete(:expressions)
+ orders = options.delete(:orders)
+ lengths = options.delete(:lengths)
+
+ columns = index[-2].map { |name|
+ [ name.to_sym, expressions[name] || +quote_column_name(name) ]
+ }.to_h
+
+ index[-2] = add_options_for_index_columns(
+ columns, order: orders, length: lengths
+ ).values.join(", ")
+ end
+
+ IndexDefinition.new(*index)
+ end
end
def remove_column(table_name, column_name, type = nil, options = {})
@@ -80,10 +106,13 @@ module ActiveRecord
def new_column_from_field(table_name, field)
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
- if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
- default, default_function = nil, field[:Default]
- else
- default, default_function = field[:Default], nil
+ default, default_function = field[:Default], nil
+
+ if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
+ default, default_function = nil, default
+ elsif type_metadata.extra == "DEFAULT_GENERATED"
+ default = +"(#{default})" unless default.start_with?("(")
+ default, default_function = nil, default
end
MySQL::Column.new(
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 0718688863..da3e2549a2 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -2,15 +2,13 @@
require "active_support/core_ext/hash/indifferent_access"
require "active_support/core_ext/string/filters"
+require "active_support/parameter_filter"
require "concurrent/map"
-require "set"
module ActiveRecord
module Core
extend ActiveSupport::Concern
- FILTERED = "[FILTERED]" # :nodoc:
-
included do
##
# :singleton-method:
@@ -239,9 +237,7 @@ module ActiveRecord
end
# Specifies columns which shouldn't be exposed while calling +#inspect+.
- def filter_attributes=(attributes_names)
- @filter_attributes = attributes_names.map(&:to_s).to_set
- end
+ attr_writer :filter_attributes
# Returns a string like 'Post(id:integer, title:string, body:text)'
def inspect # :nodoc:
@@ -502,11 +498,14 @@ module ActiveRecord
inspection = if defined?(@attributes) && @attributes
self.class.attribute_names.collect do |name|
if has_attribute?(name)
- if filter_attribute?(name)
- "#{name}: #{ActiveRecord::Core::FILTERED}"
+ attr = read_attribute(name)
+ value = if attr.nil?
+ attr.inspect
else
- "#{name}: #{attribute_for_inspect(name)}"
+ attr = format_for_inspect(attr)
+ inspection_filter.filter_param(name, attr)
end
+ "#{name}: #{value}"
end
end.compact.join(", ")
else
@@ -522,18 +521,16 @@ module ActiveRecord
return super if custom_inspect_method_defined?
pp.object_address_group(self) do
if defined?(@attributes) && @attributes
- column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
- pp.seplist(column_names, proc { pp.text "," }) do |column_name|
+ attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
+ pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
pp.breakable " "
pp.group(1) do
- pp.text column_name
+ pp.text attr_name
pp.text ":"
pp.breakable
- if filter_attribute?(column_name)
- pp.text ActiveRecord::Core::FILTERED
- else
- pp.pp read_attribute(column_name)
- end
+ value = read_attribute(attr_name)
+ value = inspection_filter.filter_param(attr_name, value) unless value.nil?
+ pp.pp value
end
end
else
@@ -585,8 +582,14 @@ module ActiveRecord
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
end
- def filter_attribute?(attribute_name)
- self.class.filter_attributes.include?(attribute_name) && !read_attribute(attribute_name).nil?
+ def inspection_filter
+ @inspection_filter ||= begin
+ mask = DelegateClass(::String).new(ActiveSupport::ParameterFilter::FILTERED)
+ def mask.pretty_print(pp)
+ pp.text __getobj__
+ end
+ ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 6f420fe6bb..afaa900442 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -363,7 +363,7 @@ module ActiveRecord
case conditions
when Array, Hash
- relation.where!(conditions)
+ relation.where!(conditions) unless conditions.empty?
else
relation.where!(primary_key => conditions) unless conditions == :none
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index fadb3c420d..ee2ece1560 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -22,9 +22,8 @@ module ActiveRecord
when 1 then predicate_builder.build(attribute, values.first)
else
values.map! do |v|
- bind = predicate_builder.build_bind_attribute(attribute.name, v)
- bind if bind.value.boundable?
- end.compact!
+ predicate_builder.build_bind_attribute(attribute.name, v)
+ end
values.empty? ? NullPredicate : attribute.in(values)
end