activerecord 3.1.10 → 4.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +6 -6
- data/CHANGELOG.md +1837 -338
- data/MIT-LICENSE +1 -1
- data/README.rdoc +39 -43
- data/examples/performance.rb +51 -20
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +57 -43
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -39
- data/lib/active_record/associations/association.rb +71 -85
- data/lib/active_record/associations/association_scope.rb +138 -89
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
- data/lib/active_record/associations/builder/association.rb +125 -29
- data/lib/active_record/associations/builder/belongs_to.rb +91 -60
- data/lib/active_record/associations/builder/collection_association.rb +69 -49
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +12 -52
- data/lib/active_record/associations/builder/singular_association.rb +22 -29
- data/lib/active_record/associations/collection_association.rb +294 -187
- data/lib/active_record/associations/collection_proxy.rb +961 -94
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +118 -23
- data/lib/active_record/associations/has_many_through_association.rb +115 -45
- data/lib/active_record/associations/has_one_association.rb +57 -24
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +230 -156
- data/lib/active_record/associations/preloader/association.rb +96 -55
- data/lib/active_record/associations/preloader/collection_association.rb +3 -3
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +61 -32
- data/lib/active_record/associations/preloader.rb +113 -87
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +37 -19
- data/lib/active_record/associations.rb +505 -371
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +141 -51
- data/lib/active_record/attribute_methods/primary_key.rb +87 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +74 -117
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
- data/lib/active_record/attribute_methods/write.rb +60 -21
- data/lib/active_record/attribute_methods.rb +409 -48
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +279 -232
- data/lib/active_record/base.rb +84 -1969
- data/lib/active_record/callbacks.rb +66 -28
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
- data/lib/active_record/connection_adapters/column.rb +33 -221
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
- data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +159 -102
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +102 -34
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +318 -260
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +80 -52
- data/lib/active_record/locking/pessimistic.rb +27 -5
- data/lib/active_record/log_subscriber.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +130 -38
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +532 -201
- data/lib/active_record/model_schema.rb +342 -0
- data/lib/active_record/nested_attributes.rb +229 -139
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +304 -99
- data/lib/active_record/query_cache.rb +25 -43
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +86 -45
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +7 -4
- data/lib/active_record/railties/databases.rake +198 -377
- data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +516 -165
- data/lib/active_record/relation/batches.rb +96 -45
- data/lib/active_record/relation/calculations.rb +221 -144
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +362 -243
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +135 -41
- data/lib/active_record/relation/query_methods.rb +982 -155
- data/lib/active_record/relation/spawn_methods.rb +50 -110
- data/lib/active_record/relation.rb +371 -180
- data/lib/active_record/result.rb +109 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +111 -61
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +135 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/serialization.rb +7 -45
- data/lib/active_record/serializers/xml_serializer.rb +14 -65
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +299 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +35 -14
- data/lib/active_record/transactions.rb +141 -74
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +27 -18
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +125 -66
- data/lib/active_record/validations.rb +37 -30
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +80 -25
- data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +132 -53
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
- data/lib/active_record/dynamic_finder_match.rb +0 -56
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/identity_map.rb +0 -163
- data/lib/active_record/named_scope.rb +0 -200
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -69
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
- 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 -16
@@ -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,35 +1,46 @@
|
|
1
|
-
require 'active_support/core_ext/module/deprecation'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module ConnectionAdapters # :nodoc:
|
5
3
|
module DatabaseStatements
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
reset_transaction
|
7
|
+
end
|
8
|
+
|
6
9
|
# Converts an arel AST to SQL
|
7
10
|
def to_sql(arel, binds = [])
|
8
11
|
if arel.respond_to?(:ast)
|
9
|
-
visitor.accept(arel.ast)
|
10
|
-
|
11
|
-
end
|
12
|
+
collected = visitor.accept(arel.ast, collector)
|
13
|
+
collected.compile(binds.dup, self)
|
12
14
|
else
|
13
15
|
arel
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
|
-
#
|
18
|
-
#
|
19
|
+
# This is used in the StatementCache object. It returns an object that
|
20
|
+
# can be used to query the database repeatedly.
|
21
|
+
def cacheable_query(arel) # :nodoc:
|
22
|
+
if prepared_statements
|
23
|
+
ActiveRecord::StatementCache.query visitor, arel.ast
|
24
|
+
else
|
25
|
+
ActiveRecord::StatementCache.partial_query visitor, arel.ast, collector
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns an ActiveRecord::Result instance.
|
19
30
|
def select_all(arel, name = nil, binds = [])
|
31
|
+
arel, binds = binds_from_relation arel, binds
|
20
32
|
select(to_sql(arel, binds), name, binds)
|
21
33
|
end
|
22
34
|
|
23
35
|
# Returns a record hash with the column names as keys and column values
|
24
36
|
# as values.
|
25
|
-
def select_one(arel, name = nil)
|
26
|
-
|
27
|
-
result.first if result
|
37
|
+
def select_one(arel, name = nil, binds = [])
|
38
|
+
select_all(arel, name, binds).first
|
28
39
|
end
|
29
40
|
|
30
41
|
# Returns a single value from a record
|
31
|
-
def select_value(arel, name = nil)
|
32
|
-
if result = select_one(arel, name)
|
42
|
+
def select_value(arel, name = nil, binds = [])
|
43
|
+
if result = select_one(arel, name, binds)
|
33
44
|
result.values.first
|
34
45
|
end
|
35
46
|
end
|
@@ -37,13 +48,13 @@ module ActiveRecord
|
|
37
48
|
# Returns an array of the values of the first column in a select:
|
38
49
|
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
39
50
|
def select_values(arel, name = nil)
|
40
|
-
|
41
|
-
|
51
|
+
arel, binds = binds_from_relation arel, []
|
52
|
+
select_rows(to_sql(arel, binds), name, binds).map(&:first)
|
42
53
|
end
|
43
54
|
|
44
55
|
# Returns an array of arrays containing the field values.
|
45
56
|
# Order is the same as that returned by +columns+.
|
46
|
-
def select_rows(sql, name = nil)
|
57
|
+
def select_rows(sql, name = nil, binds = [])
|
47
58
|
end
|
48
59
|
undef_method :select_rows
|
49
60
|
|
@@ -53,27 +64,32 @@ module ActiveRecord
|
|
53
64
|
undef_method :execute
|
54
65
|
|
55
66
|
# Executes +sql+ statement in the context of this connection using
|
56
|
-
# +binds+ as the bind substitutes.
|
67
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
57
68
|
# the executed +sql+ statement.
|
58
69
|
def exec_query(sql, name = 'SQL', binds = [])
|
59
70
|
end
|
60
71
|
|
61
72
|
# Executes insert +sql+ statement in the context of this connection using
|
62
|
-
# +binds+ as the bind substitutes. +name+ is
|
73
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
63
74
|
# the executed +sql+ statement.
|
64
|
-
def exec_insert(sql, name, binds)
|
75
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
65
76
|
exec_query(sql, name, binds)
|
66
77
|
end
|
67
78
|
|
68
79
|
# Executes delete +sql+ statement in the context of this connection using
|
69
|
-
# +binds+ as the bind substitutes. +name+ is
|
80
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
70
81
|
# the executed +sql+ statement.
|
71
82
|
def exec_delete(sql, name, binds)
|
72
83
|
exec_query(sql, name, binds)
|
73
84
|
end
|
74
85
|
|
86
|
+
# Executes the truncate statement.
|
87
|
+
def truncate(table_name, name = nil)
|
88
|
+
raise NotImplementedError
|
89
|
+
end
|
90
|
+
|
75
91
|
# Executes update +sql+ statement in the context of this connection using
|
76
|
-
# +binds+ as the bind substitutes. +name+ is
|
92
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
77
93
|
# the executed +sql+ statement.
|
78
94
|
def exec_update(sql, name, binds)
|
79
95
|
exec_query(sql, name, binds)
|
@@ -89,7 +105,7 @@ module ActiveRecord
|
|
89
105
|
# passed in as +id_value+.
|
90
106
|
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
91
107
|
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
92
|
-
value = exec_insert(sql, name, binds)
|
108
|
+
value = exec_insert(sql, name, binds, pk, sequence_name)
|
93
109
|
id_value || last_inserted_id(value)
|
94
110
|
end
|
95
111
|
|
@@ -103,20 +119,6 @@ module ActiveRecord
|
|
103
119
|
exec_delete(to_sql(arel, binds), name, binds)
|
104
120
|
end
|
105
121
|
|
106
|
-
# Checks whether there is currently no transaction active. This is done
|
107
|
-
# by querying the database driver, and does not use the transaction
|
108
|
-
# house-keeping information recorded by #increment_open_transactions and
|
109
|
-
# friends.
|
110
|
-
#
|
111
|
-
# Returns true if there is no transaction active, false if there is a
|
112
|
-
# transaction active, and nil if this information is unknown.
|
113
|
-
#
|
114
|
-
# Not all adapters supports transaction state introspection. Currently,
|
115
|
-
# only the PostgreSQL adapter supports this.
|
116
|
-
def outside_transaction?
|
117
|
-
nil
|
118
|
-
end
|
119
|
-
|
120
122
|
# Returns +true+ when the connection adapter supports prepared statement
|
121
123
|
# caching, otherwise returns +false+
|
122
124
|
def supports_statement_cache?
|
@@ -135,7 +137,8 @@ module ActiveRecord
|
|
135
137
|
# In order to get around this problem, #transaction will emulate the effect
|
136
138
|
# of nested transactions, by using savepoints:
|
137
139
|
# http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
|
138
|
-
# Savepoints are supported by MySQL and PostgreSQL
|
140
|
+
# Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
|
141
|
+
# supports savepoints.
|
139
142
|
#
|
140
143
|
# It is safe to call this method if a database transaction is already open,
|
141
144
|
# i.e. if #transaction is called within another #transaction block. In case
|
@@ -160,126 +163,117 @@ module ActiveRecord
|
|
160
163
|
# already-automatically-released savepoints:
|
161
164
|
#
|
162
165
|
# Model.connection.transaction do # BEGIN
|
163
|
-
# Model.connection.transaction(:
|
166
|
+
# Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
164
167
|
# Model.connection.create_table(...)
|
165
168
|
# # active_record_1 now automatically released
|
166
169
|
# end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
|
167
170
|
# end
|
171
|
+
#
|
172
|
+
# == Transaction isolation
|
173
|
+
#
|
174
|
+
# If your database supports setting the isolation level for a transaction, you can set
|
175
|
+
# it like so:
|
176
|
+
#
|
177
|
+
# Post.transaction(isolation: :serializable) do
|
178
|
+
# # ...
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# Valid isolation levels are:
|
182
|
+
#
|
183
|
+
# * <tt>:read_uncommitted</tt>
|
184
|
+
# * <tt>:read_committed</tt>
|
185
|
+
# * <tt>:repeatable_read</tt>
|
186
|
+
# * <tt>:serializable</tt>
|
187
|
+
#
|
188
|
+
# You should consult the documentation for your database to understand the
|
189
|
+
# semantics of these different levels:
|
190
|
+
#
|
191
|
+
# * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
|
192
|
+
# * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
|
193
|
+
#
|
194
|
+
# An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
|
195
|
+
#
|
196
|
+
# * The adapter does not support setting the isolation level
|
197
|
+
# * You are joining an existing open transaction
|
198
|
+
# * You are creating a nested (savepoint) transaction
|
199
|
+
#
|
200
|
+
# The mysql, mysql2 and postgresql adapters support setting the transaction
|
201
|
+
# isolation level. However, support is disabled for MySQL versions below 5,
|
202
|
+
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
|
203
|
+
# which means the isolation level gets persisted outside the transaction.
|
168
204
|
def transaction(options = {})
|
169
|
-
options.assert_valid_keys :requires_new, :joinable
|
205
|
+
options.assert_valid_keys :requires_new, :joinable, :isolation
|
170
206
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
else
|
175
|
-
@transaction_joinable = true
|
176
|
-
end
|
177
|
-
requires_new = options[:requires_new] || !last_transaction_joinable
|
178
|
-
|
179
|
-
transaction_open = false
|
180
|
-
@_current_transaction_records ||= []
|
181
|
-
|
182
|
-
begin
|
183
|
-
if block_given?
|
184
|
-
if requires_new || open_transactions == 0
|
185
|
-
if open_transactions == 0
|
186
|
-
begin_db_transaction
|
187
|
-
elsif requires_new
|
188
|
-
create_savepoint
|
189
|
-
end
|
190
|
-
increment_open_transactions
|
191
|
-
transaction_open = true
|
192
|
-
@_current_transaction_records.push([])
|
193
|
-
end
|
194
|
-
yield
|
195
|
-
end
|
196
|
-
rescue Exception => database_transaction_rollback
|
197
|
-
if transaction_open && !outside_transaction?
|
198
|
-
transaction_open = false
|
199
|
-
decrement_open_transactions
|
200
|
-
if open_transactions == 0
|
201
|
-
rollback_db_transaction
|
202
|
-
rollback_transaction_records(true)
|
203
|
-
else
|
204
|
-
rollback_to_savepoint
|
205
|
-
rollback_transaction_records(false)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
|
209
|
-
end
|
210
|
-
ensure
|
211
|
-
@transaction_joinable = last_transaction_joinable
|
212
|
-
|
213
|
-
if outside_transaction?
|
214
|
-
@open_transactions = 0
|
215
|
-
elsif transaction_open
|
216
|
-
decrement_open_transactions
|
217
|
-
begin
|
218
|
-
if open_transactions == 0
|
219
|
-
commit_db_transaction
|
220
|
-
commit_transaction_records
|
221
|
-
else
|
222
|
-
release_savepoint
|
223
|
-
save_point_records = @_current_transaction_records.pop
|
224
|
-
unless save_point_records.blank?
|
225
|
-
@_current_transaction_records.push([]) if @_current_transaction_records.empty?
|
226
|
-
@_current_transaction_records.last.concat(save_point_records)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
rescue Exception => database_transaction_rollback
|
230
|
-
if open_transactions == 0
|
231
|
-
rollback_db_transaction
|
232
|
-
rollback_transaction_records(true)
|
233
|
-
else
|
234
|
-
rollback_to_savepoint
|
235
|
-
rollback_transaction_records(false)
|
236
|
-
end
|
237
|
-
raise
|
207
|
+
if !options[:requires_new] && current_transaction.joinable?
|
208
|
+
if options[:isolation]
|
209
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
238
210
|
end
|
211
|
+
yield
|
212
|
+
else
|
213
|
+
transaction_manager.within_new_transaction(options) { yield }
|
239
214
|
end
|
215
|
+
rescue ActiveRecord::Rollback
|
216
|
+
# rollbacks are silently swallowed
|
217
|
+
end
|
218
|
+
|
219
|
+
attr_reader :transaction_manager #:nodoc:
|
220
|
+
|
221
|
+
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager
|
222
|
+
|
223
|
+
def transaction_open?
|
224
|
+
current_transaction.open?
|
225
|
+
end
|
226
|
+
|
227
|
+
def reset_transaction #:nodoc:
|
228
|
+
@transaction_manager = TransactionManager.new(self)
|
240
229
|
end
|
241
230
|
|
242
231
|
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
243
232
|
# can be called.
|
244
233
|
def add_transaction_record(record)
|
245
|
-
|
246
|
-
|
234
|
+
current_transaction.add_record(record)
|
235
|
+
end
|
236
|
+
|
237
|
+
def transaction_state
|
238
|
+
current_transaction.state
|
247
239
|
end
|
248
240
|
|
249
241
|
# Begins the transaction (and turns off auto-committing).
|
250
242
|
def begin_db_transaction() end
|
251
243
|
|
244
|
+
def transaction_isolation_levels
|
245
|
+
{
|
246
|
+
read_uncommitted: "READ UNCOMMITTED",
|
247
|
+
read_committed: "READ COMMITTED",
|
248
|
+
repeatable_read: "REPEATABLE READ",
|
249
|
+
serializable: "SERIALIZABLE"
|
250
|
+
}
|
251
|
+
end
|
252
|
+
|
253
|
+
# Begins the transaction with the isolation level set. Raises an error by
|
254
|
+
# default; adapters that support setting the isolation level should implement
|
255
|
+
# this method.
|
256
|
+
def begin_isolated_db_transaction(isolation)
|
257
|
+
raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
|
258
|
+
end
|
259
|
+
|
252
260
|
# Commits the transaction (and turns on auto-committing).
|
253
261
|
def commit_db_transaction() end
|
254
262
|
|
255
263
|
# Rolls back the transaction (and turns on auto-committing). Must be
|
256
264
|
# done if the transaction block raises an exception or returns false.
|
257
|
-
def rollback_db_transaction
|
265
|
+
def rollback_db_transaction
|
266
|
+
exec_rollback_db_transaction
|
267
|
+
end
|
258
268
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
#
|
267
|
-
# This method is deprecated!! Stop using it!
|
268
|
-
#
|
269
|
-
# ===== Examples
|
270
|
-
# add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
|
271
|
-
# generates
|
272
|
-
# SELECT * FROM suppliers LIMIT 10 OFFSET 50
|
273
|
-
def add_limit_offset!(sql, options)
|
274
|
-
if limit = options[:limit]
|
275
|
-
sql << " LIMIT #{sanitize_limit(limit)}"
|
276
|
-
end
|
277
|
-
if offset = options[:offset]
|
278
|
-
sql << " OFFSET #{offset.to_i}"
|
279
|
-
end
|
280
|
-
sql
|
269
|
+
def exec_rollback_db_transaction() end #:nodoc:
|
270
|
+
|
271
|
+
def rollback_to_savepoint(name = nil)
|
272
|
+
exec_rollback_to_savepoint(name)
|
273
|
+
end
|
274
|
+
|
275
|
+
def exec_rollback_to_savepoint(name = nil) #:nodoc:
|
281
276
|
end
|
282
|
-
deprecate :add_limit_offset!
|
283
277
|
|
284
278
|
def default_sequence_name(table, column)
|
285
279
|
nil
|
@@ -287,48 +281,45 @@ module ActiveRecord
|
|
287
281
|
|
288
282
|
# Set the sequence to the max value of the table's column.
|
289
283
|
def reset_sequence!(table, column, sequence = nil)
|
290
|
-
# Do nothing by default.
|
284
|
+
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
|
291
285
|
end
|
292
286
|
|
293
287
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
294
288
|
# something beyond a simple insert (eg. Oracle).
|
295
289
|
def insert_fixture(fixture, table_name)
|
296
|
-
|
290
|
+
fixture = fixture.stringify_keys
|
291
|
+
columns = schema_cache.columns_hash(table_name)
|
297
292
|
|
298
293
|
key_list = []
|
299
294
|
value_list = fixture.map do |name, value|
|
300
|
-
|
301
|
-
|
295
|
+
if column = columns[name]
|
296
|
+
key_list << quote_column_name(name)
|
297
|
+
quote(value, column)
|
298
|
+
else
|
299
|
+
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
|
300
|
+
end
|
302
301
|
end
|
303
302
|
|
304
303
|
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
|
305
304
|
end
|
306
305
|
|
307
306
|
def empty_insert_statement_value
|
308
|
-
"VALUES
|
309
|
-
end
|
310
|
-
|
311
|
-
def case_sensitive_equality_operator
|
312
|
-
"="
|
313
|
-
end
|
314
|
-
|
315
|
-
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
316
|
-
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
|
307
|
+
"DEFAULT VALUES"
|
317
308
|
end
|
318
309
|
|
319
310
|
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
320
311
|
#
|
321
312
|
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
322
|
-
# should look like an integer, or a comma-delimited list of integers, or
|
313
|
+
# should look like an integer, or a comma-delimited list of integers, or
|
323
314
|
# an Arel SQL literal.
|
324
315
|
#
|
325
|
-
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
316
|
+
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
326
317
|
# Returns the sanitized limit parameter, either as an integer, or as a
|
327
318
|
# string which contains a comma-delimited list of integers.
|
328
319
|
def sanitize_limit(limit)
|
329
320
|
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
330
321
|
limit
|
331
|
-
elsif limit.to_s
|
322
|
+
elsif limit.to_s.include?(',')
|
332
323
|
Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
|
333
324
|
else
|
334
325
|
Integer(limit)
|
@@ -336,21 +327,35 @@ module ActiveRecord
|
|
336
327
|
end
|
337
328
|
|
338
329
|
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
|
339
|
-
# on
|
340
|
-
# an UPDATE statement, so in the
|
330
|
+
# on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
|
331
|
+
# an UPDATE statement, so in the MySQL adapters we redefine this to do that.
|
341
332
|
def join_to_update(update, select) #:nodoc:
|
342
|
-
|
343
|
-
subselect
|
333
|
+
key = update.key
|
334
|
+
subselect = subquery_for(key, select)
|
344
335
|
|
345
|
-
update.where
|
336
|
+
update.where key.in(subselect)
|
337
|
+
end
|
338
|
+
|
339
|
+
def join_to_delete(delete, select, key) #:nodoc:
|
340
|
+
subselect = subquery_for(key, select)
|
341
|
+
|
342
|
+
delete.where key.in(subselect)
|
346
343
|
end
|
347
344
|
|
348
345
|
protected
|
349
|
-
|
350
|
-
#
|
346
|
+
|
347
|
+
# Returns a subquery for the given key using the join information.
|
348
|
+
def subquery_for(key, select)
|
349
|
+
subselect = select.clone
|
350
|
+
subselect.projections = [key]
|
351
|
+
subselect
|
352
|
+
end
|
353
|
+
|
354
|
+
# Returns an ActiveRecord::Result instance.
|
351
355
|
def select(sql, name = nil, binds = [])
|
356
|
+
exec_query(sql, name, binds)
|
352
357
|
end
|
353
|
-
|
358
|
+
|
354
359
|
|
355
360
|
# Returns the last auto-generated ID from the affected table.
|
356
361
|
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
@@ -368,50 +373,21 @@ module ActiveRecord
|
|
368
373
|
update_sql(sql, name)
|
369
374
|
end
|
370
375
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
if rollback
|
375
|
-
records = @_current_transaction_records.flatten
|
376
|
-
@_current_transaction_records.clear
|
377
|
-
else
|
378
|
-
records = @_current_transaction_records.pop
|
379
|
-
end
|
376
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
377
|
+
[sql, binds]
|
378
|
+
end
|
380
379
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
record.rolledback!(rollback)
|
385
|
-
rescue Exception => e
|
386
|
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
|
387
|
-
end
|
388
|
-
end
|
389
|
-
end
|
380
|
+
def last_inserted_id(result)
|
381
|
+
row = result.rows.first
|
382
|
+
row && row.first
|
390
383
|
end
|
391
384
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
@_current_transaction_records.clear
|
396
|
-
unless records.blank?
|
397
|
-
records.uniq.each do |record|
|
398
|
-
begin
|
399
|
-
record.committed!
|
400
|
-
rescue Exception => e
|
401
|
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
|
402
|
-
end
|
403
|
-
end
|
385
|
+
def binds_from_relation(relation, binds)
|
386
|
+
if relation.is_a?(Relation) && binds.empty?
|
387
|
+
relation, binds = relation.arel, relation.bind_values
|
404
388
|
end
|
389
|
+
[relation, binds]
|
405
390
|
end
|
406
|
-
|
407
|
-
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
408
|
-
[sql, binds]
|
409
|
-
end
|
410
|
-
|
411
|
-
def last_inserted_id(result)
|
412
|
-
row = result.rows.first
|
413
|
-
row && row.first
|
414
|
-
end
|
415
391
|
end
|
416
392
|
end
|
417
393
|
end
|
@@ -2,17 +2,17 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters # :nodoc:
|
3
3
|
module QueryCache
|
4
4
|
class << self
|
5
|
-
def included(base)
|
6
|
-
dirties_query_cache base, :insert, :update, :delete
|
5
|
+
def included(base) #:nodoc:
|
6
|
+
dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
|
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}(*)
|
13
|
-
clear_query_cache if @query_cache_enabled
|
14
|
-
super
|
15
|
-
end
|
12
|
+
def #{method_name}(*)
|
13
|
+
clear_query_cache if @query_cache_enabled
|
14
|
+
super
|
15
|
+
end
|
16
16
|
end_code
|
17
17
|
end
|
18
18
|
end
|
@@ -20,13 +20,19 @@ module ActiveRecord
|
|
20
20
|
|
21
21
|
attr_reader :query_cache, :query_cache_enabled
|
22
22
|
|
23
|
+
def initialize(*)
|
24
|
+
super
|
25
|
+
@query_cache = Hash.new { |h,sql| h[sql] = {} }
|
26
|
+
@query_cache_enabled = false
|
27
|
+
end
|
28
|
+
|
23
29
|
# Enable the query cache within the block.
|
24
30
|
def cache
|
25
31
|
old, @query_cache_enabled = @query_cache_enabled, true
|
26
32
|
yield
|
27
33
|
ensure
|
28
|
-
clear_query_cache
|
29
34
|
@query_cache_enabled = old
|
35
|
+
clear_query_cache unless @query_cache_enabled
|
30
36
|
end
|
31
37
|
|
32
38
|
def enable_query_cache!
|
@@ -56,7 +62,8 @@ module ActiveRecord
|
|
56
62
|
end
|
57
63
|
|
58
64
|
def select_all(arel, name = nil, binds = [])
|
59
|
-
if @query_cache_enabled
|
65
|
+
if @query_cache_enabled && !locked?(arel)
|
66
|
+
arel, binds = binds_from_relation arel, binds
|
60
67
|
sql = to_sql(arel, binds)
|
61
68
|
cache_sql(sql, binds) { super(sql, name, binds) }
|
62
69
|
else
|
@@ -65,18 +72,24 @@ module ActiveRecord
|
|
65
72
|
end
|
66
73
|
|
67
74
|
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, :name => "CACHE", :connection_id => object_id)
|
73
|
-
@query_cache[sql][binds]
|
74
|
-
else
|
75
|
-
@query_cache[sql][binds] = yield
|
76
|
-
end
|
77
75
|
|
78
|
-
|
79
|
-
|
76
|
+
def cache_sql(sql, binds)
|
77
|
+
result =
|
78
|
+
if @query_cache[sql].key?(binds)
|
79
|
+
ActiveSupport::Notifications.instrument("sql.active_record",
|
80
|
+
:sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
|
81
|
+
@query_cache[sql][binds]
|
82
|
+
else
|
83
|
+
@query_cache[sql][binds] = yield
|
84
|
+
end
|
85
|
+
result.dup
|
86
|
+
end
|
87
|
+
|
88
|
+
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
|
89
|
+
# queries should not be cached.
|
90
|
+
def locked?(arel)
|
91
|
+
arel.respond_to?(:locked) && arel.locked
|
92
|
+
end
|
80
93
|
end
|
81
94
|
end
|
82
95
|
end
|