activerecord 3.2.22.4 → 4.0.13
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 +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- 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 +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- 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 +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- 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/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- 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 +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -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/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- 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
@@ -17,6 +17,15 @@ module ActiveRecord
|
|
17
17
|
64
|
18
18
|
end
|
19
19
|
|
20
|
+
# Returns the maximum allowed length for an index name. This
|
21
|
+
# limit is enforced by rails and Is less than or equal to
|
22
|
+
# <tt>index_name_length</tt>. The gap between
|
23
|
+
# <tt>index_name_length</tt> is to allow internal rails
|
24
|
+
# operations to use prefixes in temporary operations.
|
25
|
+
def allowed_index_name_length
|
26
|
+
index_name_length
|
27
|
+
end
|
28
|
+
|
20
29
|
# Returns the maximum length of an index name.
|
21
30
|
def index_name_length
|
22
31
|
64
|
@@ -1,9 +1,15 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters # :nodoc:
|
3
3
|
module DatabaseStatements
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
reset_transaction
|
7
|
+
end
|
8
|
+
|
4
9
|
# Converts an arel AST to SQL
|
5
10
|
def to_sql(arel, binds = [])
|
6
11
|
if arel.respond_to?(:ast)
|
12
|
+
binds = binds.dup
|
7
13
|
visitor.accept(arel.ast) do
|
8
14
|
quote(*binds.shift.reverse)
|
9
15
|
end
|
@@ -15,19 +21,20 @@ module ActiveRecord
|
|
15
21
|
# Returns an array of record hashes with the column names as keys and
|
16
22
|
# column values as values.
|
17
23
|
def select_all(arel, name = nil, binds = [])
|
24
|
+
arel, binds = binds_from_relation arel, binds
|
18
25
|
select(to_sql(arel, binds), name, binds)
|
19
26
|
end
|
20
27
|
|
21
28
|
# Returns a record hash with the column names as keys and column values
|
22
29
|
# as values.
|
23
|
-
def select_one(arel, name = nil)
|
24
|
-
result = select_all(arel, name)
|
30
|
+
def select_one(arel, name = nil, binds = [])
|
31
|
+
result = select_all(arel, name, binds)
|
25
32
|
result.first if result
|
26
33
|
end
|
27
34
|
|
28
35
|
# Returns a single value from a record
|
29
|
-
def select_value(arel, name = nil)
|
30
|
-
if result = select_one(arel, name)
|
36
|
+
def select_value(arel, name = nil, binds = [])
|
37
|
+
if result = select_one(arel, name, binds)
|
31
38
|
result.values.first
|
32
39
|
end
|
33
40
|
end
|
@@ -35,13 +42,13 @@ module ActiveRecord
|
|
35
42
|
# Returns an array of the values of the first column in a select:
|
36
43
|
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
37
44
|
def select_values(arel, name = nil)
|
38
|
-
|
39
|
-
|
45
|
+
arel, binds = binds_from_relation arel, []
|
46
|
+
select_rows(to_sql(arel, binds), name, binds).map(&:first)
|
40
47
|
end
|
41
48
|
|
42
49
|
# Returns an array of arrays containing the field values.
|
43
50
|
# Order is the same as that returned by +columns+.
|
44
|
-
def select_rows(sql, name = nil)
|
51
|
+
def select_rows(sql, name = nil, binds = [])
|
45
52
|
end
|
46
53
|
undef_method :select_rows
|
47
54
|
|
@@ -57,21 +64,21 @@ module ActiveRecord
|
|
57
64
|
end
|
58
65
|
|
59
66
|
# Executes insert +sql+ statement in the context of this connection using
|
60
|
-
# +binds+ as the bind substitutes. +name+ is
|
67
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
61
68
|
# the executed +sql+ statement.
|
62
|
-
def exec_insert(sql, name, binds)
|
69
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
63
70
|
exec_query(sql, name, binds)
|
64
71
|
end
|
65
72
|
|
66
73
|
# Executes delete +sql+ statement in the context of this connection using
|
67
|
-
# +binds+ as the bind substitutes. +name+ is
|
74
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
68
75
|
# the executed +sql+ statement.
|
69
76
|
def exec_delete(sql, name, binds)
|
70
77
|
exec_query(sql, name, binds)
|
71
78
|
end
|
72
79
|
|
73
80
|
# Executes update +sql+ statement in the context of this connection using
|
74
|
-
# +binds+ as the bind substitutes. +name+ is
|
81
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
75
82
|
# the executed +sql+ statement.
|
76
83
|
def exec_update(sql, name, binds)
|
77
84
|
exec_query(sql, name, binds)
|
@@ -87,7 +94,7 @@ module ActiveRecord
|
|
87
94
|
# passed in as +id_value+.
|
88
95
|
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
89
96
|
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
90
|
-
value = exec_insert(sql, name, binds)
|
97
|
+
value = exec_insert(sql, name, binds, pk, sequence_name)
|
91
98
|
id_value || last_inserted_id(value)
|
92
99
|
end
|
93
100
|
|
@@ -101,20 +108,6 @@ module ActiveRecord
|
|
101
108
|
exec_delete(to_sql(arel, binds), name, binds)
|
102
109
|
end
|
103
110
|
|
104
|
-
# Checks whether there is currently no transaction active. This is done
|
105
|
-
# by querying the database driver, and does not use the transaction
|
106
|
-
# house-keeping information recorded by #increment_open_transactions and
|
107
|
-
# friends.
|
108
|
-
#
|
109
|
-
# Returns true if there is no transaction active, false if there is a
|
110
|
-
# transaction active, and nil if this information is unknown.
|
111
|
-
#
|
112
|
-
# Not all adapters supports transaction state introspection. Currently,
|
113
|
-
# only the PostgreSQL adapter supports this.
|
114
|
-
def outside_transaction?
|
115
|
-
nil
|
116
|
-
end
|
117
|
-
|
118
111
|
# Returns +true+ when the connection adapter supports prepared statement
|
119
112
|
# caching, otherwise returns +false+
|
120
113
|
def supports_statement_cache?
|
@@ -133,7 +126,8 @@ module ActiveRecord
|
|
133
126
|
# In order to get around this problem, #transaction will emulate the effect
|
134
127
|
# of nested transactions, by using savepoints:
|
135
128
|
# http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
|
136
|
-
# Savepoints are supported by MySQL and PostgreSQL
|
129
|
+
# Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
|
130
|
+
# supports savepoints.
|
137
131
|
#
|
138
132
|
# It is safe to call this method if a database transaction is already open,
|
139
133
|
# i.e. if #transaction is called within another #transaction block. In case
|
@@ -158,95 +152,124 @@ module ActiveRecord
|
|
158
152
|
# already-automatically-released savepoints:
|
159
153
|
#
|
160
154
|
# Model.connection.transaction do # BEGIN
|
161
|
-
# Model.connection.transaction(:
|
155
|
+
# Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
162
156
|
# Model.connection.create_table(...)
|
163
157
|
# # active_record_1 now automatically released
|
164
158
|
# end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
|
165
159
|
# end
|
160
|
+
#
|
161
|
+
# == Transaction isolation
|
162
|
+
#
|
163
|
+
# If your database supports setting the isolation level for a transaction, you can set
|
164
|
+
# it like so:
|
165
|
+
#
|
166
|
+
# Post.transaction(isolation: :serializable) do
|
167
|
+
# # ...
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# Valid isolation levels are:
|
171
|
+
#
|
172
|
+
# * <tt>:read_uncommitted</tt>
|
173
|
+
# * <tt>:read_committed</tt>
|
174
|
+
# * <tt>:repeatable_read</tt>
|
175
|
+
# * <tt>:serializable</tt>
|
176
|
+
#
|
177
|
+
# You should consult the documentation for your database to understand the
|
178
|
+
# semantics of these different levels:
|
179
|
+
#
|
180
|
+
# * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
|
181
|
+
# * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
|
182
|
+
#
|
183
|
+
# An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
|
184
|
+
#
|
185
|
+
# * The adapter does not support setting the isolation level
|
186
|
+
# * You are joining an existing open transaction
|
187
|
+
# * You are creating a nested (savepoint) transaction
|
188
|
+
#
|
189
|
+
# The mysql, mysql2 and postgresql adapters support setting the transaction
|
190
|
+
# isolation level. However, support is disabled for mysql versions below 5,
|
191
|
+
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
|
192
|
+
# which means the isolation level gets persisted outside the transaction.
|
166
193
|
def transaction(options = {})
|
167
|
-
options.assert_valid_keys :requires_new, :joinable
|
194
|
+
options.assert_valid_keys :requires_new, :joinable, :isolation
|
195
|
+
|
196
|
+
if !options[:requires_new] && current_transaction.joinable?
|
197
|
+
if options[:isolation]
|
198
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
199
|
+
end
|
168
200
|
|
169
|
-
|
170
|
-
if options.has_key?(:joinable)
|
171
|
-
@transaction_joinable = options[:joinable]
|
201
|
+
yield
|
172
202
|
else
|
173
|
-
|
203
|
+
within_new_transaction(options) { yield }
|
174
204
|
end
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
@_current_transaction_records ||= []
|
205
|
+
rescue ActiveRecord::Rollback
|
206
|
+
# rollbacks are silently swallowed
|
207
|
+
end
|
179
208
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
create_savepoint
|
187
|
-
end
|
188
|
-
increment_open_transactions
|
189
|
-
transaction_open = true
|
190
|
-
@_current_transaction_records.push([])
|
191
|
-
end
|
192
|
-
yield
|
193
|
-
end
|
194
|
-
rescue Exception => database_transaction_rollback
|
195
|
-
if transaction_open && !outside_transaction?
|
196
|
-
transaction_open = false
|
197
|
-
decrement_open_transactions
|
198
|
-
if open_transactions == 0
|
199
|
-
rollback_db_transaction
|
200
|
-
rollback_transaction_records(true)
|
201
|
-
else
|
202
|
-
rollback_to_savepoint
|
203
|
-
rollback_transaction_records(false)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
|
207
|
-
end
|
209
|
+
def within_new_transaction(options = {}) #:nodoc:
|
210
|
+
transaction = begin_transaction(options)
|
211
|
+
yield
|
212
|
+
rescue Exception => error
|
213
|
+
rollback_transaction if transaction
|
214
|
+
raise
|
208
215
|
ensure
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
decrement_open_transactions
|
215
|
-
begin
|
216
|
-
if open_transactions == 0
|
217
|
-
commit_db_transaction
|
218
|
-
commit_transaction_records
|
219
|
-
else
|
220
|
-
release_savepoint
|
221
|
-
save_point_records = @_current_transaction_records.pop
|
222
|
-
unless save_point_records.blank?
|
223
|
-
@_current_transaction_records.push([]) if @_current_transaction_records.empty?
|
224
|
-
@_current_transaction_records.last.concat(save_point_records)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
rescue Exception => database_transaction_rollback
|
228
|
-
if open_transactions == 0
|
229
|
-
rollback_db_transaction
|
230
|
-
rollback_transaction_records(true)
|
231
|
-
else
|
232
|
-
rollback_to_savepoint
|
233
|
-
rollback_transaction_records(false)
|
234
|
-
end
|
235
|
-
raise
|
236
|
-
end
|
216
|
+
begin
|
217
|
+
commit_transaction unless error
|
218
|
+
rescue Exception
|
219
|
+
rollback_transaction
|
220
|
+
raise
|
237
221
|
end
|
238
222
|
end
|
239
223
|
|
224
|
+
def current_transaction #:nodoc:
|
225
|
+
@transaction
|
226
|
+
end
|
227
|
+
|
228
|
+
def transaction_open?
|
229
|
+
@transaction.open?
|
230
|
+
end
|
231
|
+
|
232
|
+
def begin_transaction(options = {}) #:nodoc:
|
233
|
+
@transaction = @transaction.begin(options)
|
234
|
+
end
|
235
|
+
|
236
|
+
def commit_transaction #:nodoc:
|
237
|
+
@transaction = @transaction.commit
|
238
|
+
end
|
239
|
+
|
240
|
+
def rollback_transaction #:nodoc:
|
241
|
+
@transaction = @transaction.rollback
|
242
|
+
end
|
243
|
+
|
244
|
+
def reset_transaction #:nodoc:
|
245
|
+
@transaction = ClosedTransaction.new(self)
|
246
|
+
end
|
247
|
+
|
240
248
|
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
241
249
|
# can be called.
|
242
250
|
def add_transaction_record(record)
|
243
|
-
|
244
|
-
last_batch << record if last_batch
|
251
|
+
@transaction.add_record(record)
|
245
252
|
end
|
246
253
|
|
247
254
|
# Begins the transaction (and turns off auto-committing).
|
248
255
|
def begin_db_transaction() end
|
249
256
|
|
257
|
+
def transaction_isolation_levels
|
258
|
+
{
|
259
|
+
read_uncommitted: "READ UNCOMMITTED",
|
260
|
+
read_committed: "READ COMMITTED",
|
261
|
+
repeatable_read: "REPEATABLE READ",
|
262
|
+
serializable: "SERIALIZABLE"
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
# Begins the transaction with the isolation level set. Raises an error by
|
267
|
+
# default; adapters that support setting the isolation level should implement
|
268
|
+
# this method.
|
269
|
+
def begin_isolated_db_transaction(isolation)
|
270
|
+
raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
|
271
|
+
end
|
272
|
+
|
250
273
|
# Commits the transaction (and turns on auto-committing).
|
251
274
|
def commit_db_transaction() end
|
252
275
|
|
@@ -278,7 +301,7 @@ module ActiveRecord
|
|
278
301
|
end
|
279
302
|
|
280
303
|
def empty_insert_statement_value
|
281
|
-
"VALUES
|
304
|
+
"DEFAULT VALUES"
|
282
305
|
end
|
283
306
|
|
284
307
|
def case_sensitive_equality_operator
|
@@ -312,13 +335,27 @@ module ActiveRecord
|
|
312
335
|
# on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
|
313
336
|
# an UPDATE statement, so in the mysql adapters we redefine this to do that.
|
314
337
|
def join_to_update(update, select) #:nodoc:
|
315
|
-
|
316
|
-
subselect
|
338
|
+
key = update.key
|
339
|
+
subselect = subquery_for(key, select)
|
340
|
+
|
341
|
+
update.where key.in(subselect)
|
342
|
+
end
|
317
343
|
|
318
|
-
|
344
|
+
def join_to_delete(delete, select, key) #:nodoc:
|
345
|
+
subselect = subquery_for(key, select)
|
346
|
+
|
347
|
+
delete.where key.in(subselect)
|
319
348
|
end
|
320
349
|
|
321
350
|
protected
|
351
|
+
|
352
|
+
# Return a subquery for the given key using the join information.
|
353
|
+
def subquery_for(key, select)
|
354
|
+
subselect = select.clone
|
355
|
+
subselect.projections = [key]
|
356
|
+
subselect
|
357
|
+
end
|
358
|
+
|
322
359
|
# Returns an array of record hashes with the column names as keys and
|
323
360
|
# column values as values.
|
324
361
|
def select(sql, name = nil, binds = [])
|
@@ -341,50 +378,21 @@ module ActiveRecord
|
|
341
378
|
update_sql(sql, name)
|
342
379
|
end
|
343
380
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
if rollback
|
348
|
-
records = @_current_transaction_records.flatten
|
349
|
-
@_current_transaction_records.clear
|
350
|
-
else
|
351
|
-
records = @_current_transaction_records.pop
|
352
|
-
end
|
381
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
382
|
+
[sql, binds]
|
383
|
+
end
|
353
384
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
record.rolledback!(rollback)
|
358
|
-
rescue Exception => e
|
359
|
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|
385
|
+
def last_inserted_id(result)
|
386
|
+
row = result.rows.first
|
387
|
+
row && row.first
|
363
388
|
end
|
364
389
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
@_current_transaction_records.clear
|
369
|
-
unless records.blank?
|
370
|
-
records.uniq.each do |record|
|
371
|
-
begin
|
372
|
-
record.committed!
|
373
|
-
rescue Exception => e
|
374
|
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
|
375
|
-
end
|
376
|
-
end
|
390
|
+
def binds_from_relation(relation, binds)
|
391
|
+
if relation.is_a?(Relation) && binds.blank?
|
392
|
+
relation, binds = relation.arel, relation.bind_values
|
377
393
|
end
|
394
|
+
[relation, binds]
|
378
395
|
end
|
379
|
-
|
380
|
-
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
381
|
-
[sql, binds]
|
382
|
-
end
|
383
|
-
|
384
|
-
def last_inserted_id(result)
|
385
|
-
row = result.rows.first
|
386
|
-
row && row.first
|
387
|
-
end
|
388
396
|
end
|
389
397
|
end
|
390
398
|
end
|
@@ -2,16 +2,16 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters # :nodoc:
|
3
3
|
module QueryCache
|
4
4
|
class << self
|
5
|
-
def included(base)
|
5
|
+
def included(base) #:nodoc:
|
6
6
|
dirties_query_cache base, :insert, :update, :delete
|
7
7
|
end
|
8
8
|
|
9
9
|
def dirties_query_cache(base, *method_names)
|
10
10
|
method_names.each do |method_name|
|
11
11
|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
|
12
|
-
def #{method_name}(*) # def update_with_query_dirty(*
|
12
|
+
def #{method_name}(*) # def update_with_query_dirty(*)
|
13
13
|
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
|
14
|
-
super #
|
14
|
+
super # super
|
15
15
|
end # end
|
16
16
|
end_code
|
17
17
|
end
|
@@ -57,6 +57,7 @@ module ActiveRecord
|
|
57
57
|
|
58
58
|
def select_all(arel, name = nil, binds = [])
|
59
59
|
if @query_cache_enabled && !locked?(arel)
|
60
|
+
arel, binds = binds_from_relation arel, binds
|
60
61
|
sql = to_sql(arel, binds)
|
61
62
|
cache_sql(sql, binds) { super(sql, name, binds) }
|
62
63
|
else
|
@@ -65,26 +66,31 @@ module ActiveRecord
|
|
65
66
|
end
|
66
67
|
|
67
68
|
private
|
68
|
-
def cache_sql(sql, binds)
|
69
|
-
result =
|
70
|
-
if @query_cache[sql].key?(binds)
|
71
|
-
ActiveSupport::Notifications.instrument("sql.active_record",
|
72
|
-
:sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
|
73
|
-
@query_cache[sql][binds]
|
74
|
-
else
|
75
|
-
@query_cache[sql][binds] = yield
|
76
|
-
end
|
77
69
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
70
|
+
def cache_sql(sql, binds)
|
71
|
+
result =
|
72
|
+
if @query_cache[sql].key?(binds)
|
73
|
+
ActiveSupport::Notifications.instrument("sql.active_record",
|
74
|
+
:sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
|
75
|
+
@query_cache[sql][binds]
|
84
76
|
else
|
85
|
-
|
77
|
+
@query_cache[sql][binds] = yield
|
86
78
|
end
|
79
|
+
|
80
|
+
# FIXME: we should guarantee that all cached items are Result
|
81
|
+
# objects. Then we can avoid this conditional
|
82
|
+
if ActiveRecord::Result === result
|
83
|
+
result.dup
|
84
|
+
else
|
85
|
+
result.collect { |row| row.dup }
|
87
86
|
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
|
90
|
+
# queries should not be cached.
|
91
|
+
def locked?(arel)
|
92
|
+
arel.respond_to?(:locked) && arel.locked
|
93
|
+
end
|
88
94
|
end
|
89
95
|
end
|
90
96
|
end
|
@@ -31,9 +31,10 @@ module ActiveRecord
|
|
31
31
|
# BigDecimals need to be put in a non-normalized form and quoted.
|
32
32
|
when nil then "NULL"
|
33
33
|
when BigDecimal then value.to_s('F')
|
34
|
-
when Numeric
|
34
|
+
when Numeric, ActiveSupport::Duration then value.to_s
|
35
35
|
when Date, Time then "'#{quoted_date(value)}'"
|
36
36
|
when Symbol then "'#{quote_string(value.to_s)}'"
|
37
|
+
when Class then "'#{value.to_s}'"
|
37
38
|
else
|
38
39
|
"'#{quote_string(YAML.dump(value))}'"
|
39
40
|
end
|
@@ -43,7 +44,9 @@ module ActiveRecord
|
|
43
44
|
# SQLite does not understand dates, so this method will convert a Date
|
44
45
|
# to a String.
|
45
46
|
def type_cast(value, column)
|
46
|
-
|
47
|
+
if value.respond_to?(:quoted_id) && value.respond_to?(:id)
|
48
|
+
return value.id
|
49
|
+
end
|
47
50
|
|
48
51
|
case value
|
49
52
|
when String, ActiveSupport::Multibyte::Chars
|
@@ -71,7 +74,8 @@ module ActiveRecord
|
|
71
74
|
when Date, Time then quoted_date(value)
|
72
75
|
when Symbol then value.to_s
|
73
76
|
else
|
74
|
-
|
77
|
+
to_type = column ? " to #{column.type}" : ""
|
78
|
+
raise TypeError, "can't cast #{value.class}#{to_type}"
|
75
79
|
end
|
76
80
|
end
|
77
81
|
|
@@ -91,6 +95,18 @@ module ActiveRecord
|
|
91
95
|
quote_column_name(table_name)
|
92
96
|
end
|
93
97
|
|
98
|
+
# Override to return the quoted table name for assignment. Defaults to
|
99
|
+
# table quoting.
|
100
|
+
#
|
101
|
+
# This works for mysql and mysql2 where table.column can be used to
|
102
|
+
# resolve ambiguity.
|
103
|
+
#
|
104
|
+
# We override this in the sqlite and postgresql adapters to use only
|
105
|
+
# the column name (as per syntax requirements).
|
106
|
+
def quote_table_name_for_assignment(table, attr)
|
107
|
+
quote_table_name("#{table}.#{attr}")
|
108
|
+
end
|
109
|
+
|
94
110
|
def quoted_true
|
95
111
|
"'t'"
|
96
112
|
end
|