activerecord 3.2.22.5 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'active_support/
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Inheritance
|
@@ -6,14 +6,29 @@ module ActiveRecord
|
|
6
6
|
|
7
7
|
included do
|
8
8
|
# Determine whether to store the full constant name including namespace when using STI
|
9
|
-
class_attribute :store_full_sti_class
|
9
|
+
class_attribute :store_full_sti_class, instance_writer: false
|
10
10
|
self.store_full_sti_class = true
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
+
# Determines if one of the attributes passed in is the inheritance column,
|
15
|
+
# and if the inheritance column is attr accessible, it initializes an
|
16
|
+
# instance of the given subclass instead of the base class
|
17
|
+
def new(*args, &block)
|
18
|
+
if (attrs = args.first).is_a?(Hash)
|
19
|
+
if subclass = subclass_from_attrs(attrs)
|
20
|
+
return subclass.new(*args, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
# Delegate to the original .new
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
14
27
|
# True if this isn't a concrete subclass needing a STI type condition.
|
15
28
|
def descends_from_active_record?
|
16
|
-
if
|
29
|
+
if self == Base
|
30
|
+
false
|
31
|
+
elsif superclass.abstract_class?
|
17
32
|
superclass.descends_from_active_record?
|
18
33
|
else
|
19
34
|
superclass == Base || !columns_hash.include?(inheritance_column)
|
@@ -33,17 +48,41 @@ module ActiveRecord
|
|
33
48
|
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
|
34
49
|
end
|
35
50
|
|
36
|
-
# Returns the
|
37
|
-
#
|
51
|
+
# Returns the class descending directly from ActiveRecord::Base, or
|
52
|
+
# an abstract class, if any, in the inheritance hierarchy.
|
53
|
+
#
|
54
|
+
# If A extends AR::Base, A.base_class will return A. If B descends from A
|
38
55
|
# through some arbitrarily deep hierarchy, B.base_class will return A.
|
39
56
|
#
|
40
57
|
# If B < A and C < B and if A is an abstract_class then both B.base_class
|
41
58
|
# and C.base_class would return B as the answer since A is an abstract_class.
|
42
59
|
def base_class
|
43
|
-
|
60
|
+
unless self < Base
|
61
|
+
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
|
62
|
+
end
|
63
|
+
|
64
|
+
if superclass == Base || superclass.abstract_class?
|
65
|
+
self
|
66
|
+
else
|
67
|
+
superclass.base_class
|
68
|
+
end
|
44
69
|
end
|
45
70
|
|
46
71
|
# Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
|
72
|
+
# If you are using inheritance with ActiveRecord and don't want child classes
|
73
|
+
# to utilize the implied STI table name of the parent class, this will need to be true.
|
74
|
+
# For example, given the following:
|
75
|
+
#
|
76
|
+
# class SuperClass < ActiveRecord::Base
|
77
|
+
# self.abstract_class = true
|
78
|
+
# end
|
79
|
+
# class Child < SuperClass
|
80
|
+
# self.table_name = 'the_table_i_really_want'
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
#
|
84
|
+
# <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
|
85
|
+
#
|
47
86
|
attr_accessor :abstract_class
|
48
87
|
|
49
88
|
# Returns whether this class is an abstract class or not.
|
@@ -55,36 +94,8 @@ module ActiveRecord
|
|
55
94
|
store_full_sti_class ? name : name.demodulize
|
56
95
|
end
|
57
96
|
|
58
|
-
# Finder methods must instantiate through this method to work with the
|
59
|
-
# single-table inheritance model that makes it possible to create
|
60
|
-
# objects of different types from the same table.
|
61
|
-
def instantiate(record)
|
62
|
-
sti_class = find_sti_class(record[inheritance_column])
|
63
|
-
record_id = sti_class.primary_key && record[sti_class.primary_key]
|
64
|
-
|
65
|
-
if ActiveRecord::IdentityMap.enabled? && record_id
|
66
|
-
instance = use_identity_map(sti_class, record_id, record)
|
67
|
-
else
|
68
|
-
instance = sti_class.allocate.init_with('attributes' => record)
|
69
|
-
end
|
70
|
-
|
71
|
-
instance
|
72
|
-
end
|
73
|
-
|
74
97
|
protected
|
75
98
|
|
76
|
-
# Returns the class descending directly from ActiveRecord::Base or an
|
77
|
-
# abstract class, if any, in the inheritance hierarchy.
|
78
|
-
def class_of_active_record_descendant(klass)
|
79
|
-
if klass == Base || klass.superclass == Base || klass.superclass.abstract_class?
|
80
|
-
klass
|
81
|
-
elsif klass.superclass.nil?
|
82
|
-
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
|
83
|
-
else
|
84
|
-
class_of_active_record_descendant(klass.superclass)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
99
|
# Returns the class type of the record using the current module as a prefix. So descendants of
|
89
100
|
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
90
101
|
def compute_type(type_name)
|
@@ -114,39 +125,33 @@ module ActiveRecord
|
|
114
125
|
|
115
126
|
private
|
116
127
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
instance.reinit_with('attributes' => record)
|
128
|
+
# Called by +instantiate+ to decide which class to use for a new
|
129
|
+
# record instance. For single-table inheritance, we check the record
|
130
|
+
# for a +type+ column and return the corresponding class.
|
131
|
+
def discriminate_class_for_record(record)
|
132
|
+
if using_single_table_inheritance?(record)
|
133
|
+
find_sti_class(record[inheritance_column])
|
124
134
|
else
|
125
|
-
|
126
|
-
IdentityMap.add(instance)
|
135
|
+
super
|
127
136
|
end
|
137
|
+
end
|
128
138
|
|
129
|
-
|
139
|
+
def using_single_table_inheritance?(record)
|
140
|
+
record[inheritance_column].present? && columns_hash.include?(inheritance_column)
|
130
141
|
end
|
131
142
|
|
132
143
|
def find_sti_class(type_name)
|
133
|
-
if
|
134
|
-
|
144
|
+
if store_full_sti_class
|
145
|
+
ActiveSupport::Dependencies.constantize(type_name)
|
135
146
|
else
|
136
|
-
|
137
|
-
if store_full_sti_class
|
138
|
-
ActiveSupport::Dependencies.constantize(type_name)
|
139
|
-
else
|
140
|
-
compute_type(type_name)
|
141
|
-
end
|
142
|
-
rescue NameError
|
143
|
-
raise SubclassNotFound,
|
144
|
-
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
|
145
|
-
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
146
|
-
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
147
|
-
"or overwrite #{name}.inheritance_column to use another column for that information."
|
148
|
-
end
|
147
|
+
compute_type(type_name)
|
149
148
|
end
|
149
|
+
rescue NameError
|
150
|
+
raise SubclassNotFound,
|
151
|
+
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
|
152
|
+
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
153
|
+
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
154
|
+
"or overwrite #{name}.inheritance_column to use another column for that information."
|
150
155
|
end
|
151
156
|
|
152
157
|
def type_condition(table = arel_table)
|
@@ -155,6 +160,19 @@ module ActiveRecord
|
|
155
160
|
|
156
161
|
sti_column.in(sti_names)
|
157
162
|
end
|
163
|
+
|
164
|
+
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
|
165
|
+
# is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
|
166
|
+
# If this is a StrongParameters hash, and access to inheritance_column is not permitted,
|
167
|
+
# this will ignore the inheritance column and return nil
|
168
|
+
def subclass_from_attrs(attrs)
|
169
|
+
subclass_name = attrs.with_indifferent_access[inheritance_column]
|
170
|
+
return nil if subclass_name.blank? || subclass_name == self.name
|
171
|
+
unless subclass = subclasses.detect { |sub| sub.name == subclass_name }
|
172
|
+
raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
|
173
|
+
end
|
174
|
+
subclass
|
175
|
+
end
|
158
176
|
end
|
159
177
|
|
160
178
|
private
|
@@ -5,10 +5,12 @@ module ActiveRecord
|
|
5
5
|
included do
|
6
6
|
##
|
7
7
|
# :singleton-method:
|
8
|
-
# Indicates the format used to generate the timestamp
|
9
|
-
#
|
8
|
+
# Indicates the format used to generate the timestamp in the cache key.
|
9
|
+
# Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
|
10
|
+
#
|
11
|
+
# This is +:nsec+, by default.
|
10
12
|
class_attribute :cache_timestamp_format, :instance_writer => false
|
11
|
-
self.cache_timestamp_format = :
|
13
|
+
self.cache_timestamp_format = :nsec
|
12
14
|
end
|
13
15
|
|
14
16
|
# Returns a String, which Action Pack uses for constructing an URL to this
|
@@ -40,8 +42,6 @@ module ActiveRecord
|
|
40
42
|
|
41
43
|
# Returns a cache key that can be used to identify this record.
|
42
44
|
#
|
43
|
-
# ==== Examples
|
44
|
-
#
|
45
45
|
# Product.new.cache_key # => "products/new"
|
46
46
|
# Product.find(5).cache_key # => "products/5" (updated_at not available)
|
47
47
|
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
|
@@ -4,12 +4,19 @@ en:
|
|
4
4
|
#created_at: "Created at"
|
5
5
|
#updated_at: "Updated at"
|
6
6
|
|
7
|
+
# Default error messages
|
8
|
+
errors:
|
9
|
+
messages:
|
10
|
+
taken: "has already been taken"
|
11
|
+
|
7
12
|
# Active Record models configuration
|
8
13
|
activerecord:
|
9
14
|
errors:
|
10
15
|
messages:
|
11
|
-
taken: "has already been taken"
|
12
16
|
record_invalid: "Validation failed: %{errors}"
|
17
|
+
restrict_dependent_destroy:
|
18
|
+
one: "Cannot delete record because a dependent %{record} exists"
|
19
|
+
many: "Cannot delete record because dependent %{record} exist"
|
13
20
|
# Append your own errors here or at the model/attributes scope.
|
14
21
|
|
15
22
|
# You can define own errors for models or model attributes.
|
@@ -40,16 +40,18 @@ module ActiveRecord
|
|
40
40
|
# This locking mechanism will function inside a single Ruby process. To make it work across all
|
41
41
|
# web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
|
42
42
|
#
|
43
|
-
# You must ensure that your database schema defaults the +lock_version+ column to 0.
|
44
|
-
#
|
45
43
|
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
|
46
|
-
# To override the name of the +lock_version+ column,
|
47
|
-
#
|
44
|
+
# To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
|
45
|
+
#
|
46
|
+
# class Person < ActiveRecord::Base
|
47
|
+
# self.locking_column = :lock_person
|
48
|
+
# end
|
49
|
+
#
|
48
50
|
module Optimistic
|
49
51
|
extend ActiveSupport::Concern
|
50
52
|
|
51
53
|
included do
|
52
|
-
|
54
|
+
class_attribute :lock_optimistically, instance_writer: false
|
53
55
|
self.lock_optimistically = true
|
54
56
|
end
|
55
57
|
|
@@ -64,7 +66,7 @@ module ActiveRecord
|
|
64
66
|
send(lock_col + '=', previous_lock_value + 1)
|
65
67
|
end
|
66
68
|
|
67
|
-
def
|
69
|
+
def update_record(attribute_names = @attributes.keys) #:nodoc:
|
68
70
|
return super unless locking_enabled?
|
69
71
|
return 0 if attribute_names.empty?
|
70
72
|
|
@@ -80,9 +82,9 @@ module ActiveRecord
|
|
80
82
|
|
81
83
|
stmt = relation.where(
|
82
84
|
relation.table[self.class.primary_key].eq(id).and(
|
83
|
-
relation.table[lock_col].eq(
|
85
|
+
relation.table[lock_col].eq(self.class.quote_value(previous_lock_value))
|
84
86
|
)
|
85
|
-
).arel.compile_update(
|
87
|
+
).arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
|
86
88
|
|
87
89
|
affected_rows = connection.update stmt
|
88
90
|
|
@@ -99,26 +101,29 @@ module ActiveRecord
|
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
102
|
-
def
|
103
|
-
|
104
|
+
def destroy_row
|
105
|
+
affected_rows = super
|
106
|
+
|
107
|
+
if locking_enabled? && affected_rows != 1
|
108
|
+
raise ActiveRecord::StaleObjectError.new(self, "destroy")
|
109
|
+
end
|
104
110
|
|
105
|
-
|
111
|
+
affected_rows
|
112
|
+
end
|
106
113
|
|
107
|
-
|
108
|
-
|
109
|
-
lock_col = self.class.locking_column
|
110
|
-
predicate = table[self.class.primary_key].eq(id).
|
111
|
-
and(table[lock_col].eq(send(lock_col).to_i))
|
114
|
+
def relation_for_destroy
|
115
|
+
relation = super
|
112
116
|
|
113
|
-
|
117
|
+
if locking_enabled?
|
118
|
+
column_name = self.class.locking_column
|
119
|
+
column = self.class.columns_hash[column_name]
|
120
|
+
substitute = connection.substitute_at(column, relation.bind_values.length)
|
114
121
|
|
115
|
-
|
116
|
-
|
117
|
-
end
|
122
|
+
relation = relation.where(self.class.arel_table[column_name].eq(substitute))
|
123
|
+
relation.bind_values << [column, self[column_name].to_i]
|
118
124
|
end
|
119
125
|
|
120
|
-
|
121
|
-
freeze
|
126
|
+
relation
|
122
127
|
end
|
123
128
|
|
124
129
|
module ClassMethods
|
@@ -131,14 +136,9 @@ module ActiveRecord
|
|
131
136
|
lock_optimistically && columns_hash[locking_column]
|
132
137
|
end
|
133
138
|
|
134
|
-
def locking_column=(value)
|
135
|
-
@original_locking_column = @locking_column if defined?(@locking_column)
|
136
|
-
@locking_column = value.to_s
|
137
|
-
end
|
138
|
-
|
139
139
|
# Set the column to use for optimistic locking. Defaults to +lock_version+.
|
140
|
-
def
|
141
|
-
|
140
|
+
def locking_column=(value)
|
141
|
+
@locking_column = value.to_s
|
142
142
|
end
|
143
143
|
|
144
144
|
# The version column used for optimistic locking. Defaults to +lock_version+.
|
@@ -147,10 +147,6 @@ module ActiveRecord
|
|
147
147
|
@locking_column
|
148
148
|
end
|
149
149
|
|
150
|
-
def original_locking_column #:nodoc:
|
151
|
-
deprecated_original_property_getter :locking_column
|
152
|
-
end
|
153
|
-
|
154
150
|
# Quote the column name used for optimistic locking.
|
155
151
|
def quoted_locking_column
|
156
152
|
connection.quote_column_name(locking_column)
|
@@ -168,16 +164,16 @@ module ActiveRecord
|
|
168
164
|
super
|
169
165
|
end
|
170
166
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
167
|
+
def column_defaults
|
168
|
+
@column_defaults ||= begin
|
169
|
+
defaults = super
|
170
|
+
|
171
|
+
if defaults.key?(locking_column) && lock_optimistically
|
172
|
+
defaults[locking_column] ||= 0
|
173
|
+
end
|
179
174
|
|
180
|
-
|
175
|
+
defaults
|
176
|
+
end
|
181
177
|
end
|
182
178
|
end
|
183
179
|
end
|
@@ -3,12 +3,12 @@ module ActiveRecord
|
|
3
3
|
# Locking::Pessimistic provides support for row-level locking using
|
4
4
|
# SELECT ... FOR UPDATE and other lock types.
|
5
5
|
#
|
6
|
-
# Pass <tt
|
6
|
+
# Pass <tt>lock: true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
|
7
7
|
# lock on the selected rows:
|
8
8
|
# # select * from accounts where id=1 for update
|
9
|
-
# Account.find(1, :
|
9
|
+
# Account.find(1, lock: true)
|
10
10
|
#
|
11
|
-
# Pass <tt
|
11
|
+
# Pass <tt>lock: 'some locking clause'</tt> to give a database-specific locking clause
|
12
12
|
# of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
|
13
13
|
#
|
14
14
|
# Account.transaction do
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
#
|
27
27
|
# Account.transaction do
|
28
28
|
# # select * from accounts where ...
|
29
|
-
# accounts = Account.where(...)
|
29
|
+
# accounts = Account.where(...)
|
30
30
|
# account1 = accounts.detect { |account| ... }
|
31
31
|
# account2 = accounts.detect { |account| ... }
|
32
32
|
# # select * from accounts where id=? for update
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
3
|
+
IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
|
4
|
+
|
3
5
|
def self.runtime=(value)
|
4
|
-
Thread.current[
|
6
|
+
Thread.current[:active_record_sql_runtime] = value
|
5
7
|
end
|
6
8
|
|
7
9
|
def self.runtime
|
8
|
-
Thread.current[
|
10
|
+
Thread.current[:active_record_sql_runtime] ||= 0
|
9
11
|
end
|
10
12
|
|
11
13
|
def self.reset_runtime
|
@@ -18,25 +20,33 @@ module ActiveRecord
|
|
18
20
|
@odd_or_even = false
|
19
21
|
end
|
20
22
|
|
23
|
+
def render_bind(column, value)
|
24
|
+
if column
|
25
|
+
if column.binary?
|
26
|
+
value = "<#{value.bytesize} bytes of binary data>"
|
27
|
+
end
|
28
|
+
|
29
|
+
[column.name, value]
|
30
|
+
else
|
31
|
+
[nil, value]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
21
35
|
def sql(event)
|
22
36
|
self.class.runtime += event.duration
|
23
37
|
return unless logger.debug?
|
24
38
|
|
25
39
|
payload = event.payload
|
26
40
|
|
27
|
-
return if
|
41
|
+
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
28
42
|
|
29
|
-
name =
|
43
|
+
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
30
44
|
sql = payload[:sql].squeeze(' ')
|
31
45
|
binds = nil
|
32
46
|
|
33
47
|
unless (payload[:binds] || []).empty?
|
34
48
|
binds = " " + payload[:binds].map { |col,v|
|
35
|
-
|
36
|
-
[col.name, v]
|
37
|
-
else
|
38
|
-
[nil, v]
|
39
|
-
end
|
49
|
+
render_bind(col, v)
|
40
50
|
}.inspect
|
41
51
|
end
|
42
52
|
|