activerecord 3.0.0 → 4.0.0
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 +7 -0
- data/CHANGELOG.md +2102 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +35 -44
- data/examples/performance.rb +110 -100
- data/lib/active_record/aggregations.rb +59 -75
- data/lib/active_record/associations/alias_tracker.rb +76 -0
- data/lib/active_record/associations/association.rb +248 -0
- data/lib/active_record/associations/association_scope.rb +135 -0
- data/lib/active_record/associations/belongs_to_association.rb +60 -59
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
- data/lib/active_record/associations/builder/association.rb +108 -0
- data/lib/active_record/associations/builder/belongs_to.rb +98 -0
- data/lib/active_record/associations/builder/collection_association.rb +89 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +25 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +608 -0
- data/lib/active_record/associations/collection_proxy.rb +986 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
- data/lib/active_record/associations/has_many_association.rb +83 -76
- data/lib/active_record/associations/has_many_through_association.rb +147 -66
- data/lib/active_record/associations/has_one_association.rb +67 -108
- data/lib/active_record/associations/has_one_through_association.rb +21 -25
- data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
- data/lib/active_record/associations/join_dependency.rb +235 -0
- data/lib/active_record/associations/join_helper.rb +45 -0
- data/lib/active_record/associations/preloader/association.rb +121 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +63 -0
- data/lib/active_record/associations/preloader.rb +178 -0
- data/lib/active_record/associations/singular_association.rb +64 -0
- data/lib/active_record/associations/through_association.rb +87 -0
- data/lib/active_record/associations.rb +512 -1224
- data/lib/active_record/attribute_assignment.rb +201 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
- data/lib/active_record/attribute_methods/dirty.rb +51 -28
- data/lib/active_record/attribute_methods/primary_key.rb +94 -22
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +63 -72
- data/lib/active_record/attribute_methods/serialization.rb +162 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
- data/lib/active_record/attribute_methods/write.rb +39 -13
- data/lib/active_record/attribute_methods.rb +362 -29
- data/lib/active_record/autosave_association.rb +132 -75
- data/lib/active_record/base.rb +83 -1627
- data/lib/active_record/callbacks.rb +69 -47
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
- data/lib/active_record/connection_adapters/column.rb +318 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
- data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +463 -0
- data/lib/active_record/counter_cache.rb +108 -101
- data/lib/active_record/dynamic_matchers.rb +131 -0
- data/lib/active_record/errors.rb +54 -13
- 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 +55 -0
- data/lib/active_record/fixtures.rb +703 -785
- data/lib/active_record/inheritance.rb +200 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +69 -60
- data/lib/active_record/locking/pessimistic.rb +34 -12
- data/lib/active_record/log_subscriber.rb +40 -6
- data/lib/active_record/migration/command_recorder.rb +164 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +614 -216
- data/lib/active_record/model_schema.rb +345 -0
- data/lib/active_record/nested_attributes.rb +248 -119
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +275 -57
- data/lib/active_record/query_cache.rb +29 -9
- data/lib/active_record/querying.rb +62 -0
- data/lib/active_record/railtie.rb +135 -21
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +17 -5
- data/lib/active_record/railties/databases.rake +249 -359
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +30 -0
- data/lib/active_record/reflection.rb +283 -103
- data/lib/active_record/relation/batches.rb +38 -34
- data/lib/active_record/relation/calculations.rb +252 -139
- data/lib/active_record/relation/delegation.rb +125 -0
- data/lib/active_record/relation/finder_methods.rb +182 -188
- data/lib/active_record/relation/merger.rb +161 -0
- data/lib/active_record/relation/predicate_builder.rb +86 -21
- data/lib/active_record/relation/query_methods.rb +917 -134
- data/lib/active_record/relation/spawn_methods.rb +53 -92
- data/lib/active_record/relation.rb +405 -143
- data/lib/active_record/result.rb +67 -0
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +168 -0
- data/lib/active_record/schema.rb +20 -14
- data/lib/active_record/schema_dumper.rb +55 -46
- data/lib/active_record/schema_migration.rb +39 -0
- data/lib/active_record/scoping/default.rb +146 -0
- data/lib/active_record/scoping/named.rb +175 -0
- data/lib/active_record/scoping.rb +82 -0
- data/lib/active_record/serialization.rb +8 -46
- data/lib/active_record/serializers/xml_serializer.rb +21 -68
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +156 -0
- data/lib/active_record/tasks/database_tasks.rb +203 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +143 -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 +57 -28
- data/lib/active_record/timestamp.rb +49 -18
- data/lib/active_record/transactions.rb +106 -63
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +25 -24
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +123 -83
- data/lib/active_record/validations.rb +29 -29
- data/lib/active_record/version.rb +7 -5
- data/lib/active_record.rb +83 -34
- data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -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 +30 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
- data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
- data/lib/rails/generators/active_record.rb +4 -8
- metadata +163 -121
- data/CHANGELOG +0 -6023
- data/examples/associations.png +0 -0
- data/lib/active_record/association_preload.rb +0 -403
- data/lib/active_record/associations/association_collection.rb +0 -562
- data/lib/active_record/associations/association_proxy.rb +0 -295
- data/lib/active_record/associations/through_association_scope.rb +0 -154
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
- data/lib/active_record/dynamic_finder_match.rb +0 -53
- data/lib/active_record/dynamic_scope_match.rb +0 -32
- data/lib/active_record/named_scope.rb +0 -138
- data/lib/active_record/observer.rb +0 -140
- data/lib/active_record/session_store.rb +0 -340
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -2,52 +2,62 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters # :nodoc:
|
3
3
|
module DatabaseLimits
|
4
4
|
|
5
|
-
# the maximum length of a table alias
|
5
|
+
# Returns the maximum length of a table alias.
|
6
6
|
def table_alias_length
|
7
7
|
255
|
8
8
|
end
|
9
9
|
|
10
|
-
# the maximum length of a column name
|
10
|
+
# Returns the maximum length of a column name.
|
11
11
|
def column_name_length
|
12
12
|
64
|
13
13
|
end
|
14
14
|
|
15
|
-
# the maximum length of a table name
|
15
|
+
# Returns the maximum length of a table name.
|
16
16
|
def table_name_length
|
17
17
|
64
|
18
18
|
end
|
19
19
|
|
20
|
-
# the maximum length
|
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
|
+
|
29
|
+
# Returns the maximum length of an index name.
|
21
30
|
def index_name_length
|
22
31
|
64
|
23
32
|
end
|
24
33
|
|
25
|
-
# the maximum number of columns per table
|
34
|
+
# Returns the maximum number of columns per table.
|
26
35
|
def columns_per_table
|
27
36
|
1024
|
28
37
|
end
|
29
38
|
|
30
|
-
# the maximum number of indexes per table
|
39
|
+
# Returns the maximum number of indexes per table.
|
31
40
|
def indexes_per_table
|
32
41
|
16
|
33
42
|
end
|
34
43
|
|
35
|
-
# the maximum number of columns in a multicolumn index
|
44
|
+
# Returns the maximum number of columns in a multicolumn index.
|
36
45
|
def columns_per_multicolumn_index
|
37
46
|
16
|
38
47
|
end
|
39
48
|
|
40
|
-
# the maximum number of elements in an IN (x,y,z) clause
|
49
|
+
# Returns the maximum number of elements in an IN (x,y,z) clause.
|
50
|
+
# nil means no limit.
|
41
51
|
def in_clause_length
|
42
|
-
|
52
|
+
nil
|
43
53
|
end
|
44
54
|
|
45
|
-
# the maximum length of an SQL query
|
55
|
+
# Returns the maximum length of an SQL query.
|
46
56
|
def sql_query_length
|
47
57
|
1048575
|
48
58
|
end
|
49
59
|
|
50
|
-
# maximum number of joins in a single query
|
60
|
+
# Returns maximum number of joins in a single query.
|
51
61
|
def joins_per_query
|
52
62
|
256
|
53
63
|
end
|
@@ -1,30 +1,47 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters # :nodoc:
|
3
3
|
module DatabaseStatements
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
reset_transaction
|
7
|
+
end
|
8
|
+
|
9
|
+
# Converts an arel AST to SQL
|
10
|
+
def to_sql(arel, binds = [])
|
11
|
+
if arel.respond_to?(:ast)
|
12
|
+
binds = binds.dup
|
13
|
+
visitor.accept(arel.ast) do
|
14
|
+
quote(*binds.shift.reverse)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
arel
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
4
21
|
# Returns an array of record hashes with the column names as keys and
|
5
22
|
# column values as values.
|
6
|
-
def select_all(
|
7
|
-
select(
|
23
|
+
def select_all(arel, name = nil, binds = [])
|
24
|
+
select(to_sql(arel, binds), name, binds)
|
8
25
|
end
|
9
26
|
|
10
27
|
# Returns a record hash with the column names as keys and column values
|
11
28
|
# as values.
|
12
|
-
def select_one(
|
13
|
-
result = select_all(
|
29
|
+
def select_one(arel, name = nil, binds = [])
|
30
|
+
result = select_all(arel, name, binds)
|
14
31
|
result.first if result
|
15
32
|
end
|
16
33
|
|
17
34
|
# Returns a single value from a record
|
18
|
-
def select_value(
|
19
|
-
if result = select_one(
|
35
|
+
def select_value(arel, name = nil, binds = [])
|
36
|
+
if result = select_one(arel, name, binds)
|
20
37
|
result.values.first
|
21
38
|
end
|
22
39
|
end
|
23
40
|
|
24
41
|
# Returns an array of the values of the first column in a select:
|
25
42
|
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
26
|
-
def select_values(
|
27
|
-
result = select_rows(
|
43
|
+
def select_values(arel, name = nil)
|
44
|
+
result = select_rows(to_sql(arel, []), name)
|
28
45
|
result.map { |v| v[0] }
|
29
46
|
end
|
30
47
|
|
@@ -35,37 +52,65 @@ module ActiveRecord
|
|
35
52
|
undef_method :select_rows
|
36
53
|
|
37
54
|
# Executes the SQL statement in the context of this connection.
|
38
|
-
def execute(sql, name = nil
|
55
|
+
def execute(sql, name = nil)
|
39
56
|
end
|
40
57
|
undef_method :execute
|
41
58
|
|
59
|
+
# Executes +sql+ statement in the context of this connection using
|
60
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
61
|
+
# the executed +sql+ statement.
|
62
|
+
def exec_query(sql, name = 'SQL', binds = [])
|
63
|
+
end
|
64
|
+
|
65
|
+
# Executes insert +sql+ statement in the context of this connection using
|
66
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
67
|
+
# the executed +sql+ statement.
|
68
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
69
|
+
exec_query(sql, name, binds)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Executes delete +sql+ statement in the context of this connection using
|
73
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
74
|
+
# the executed +sql+ statement.
|
75
|
+
def exec_delete(sql, name, binds)
|
76
|
+
exec_query(sql, name, binds)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Executes update +sql+ statement in the context of this connection using
|
80
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
81
|
+
# the executed +sql+ statement.
|
82
|
+
def exec_update(sql, name, binds)
|
83
|
+
exec_query(sql, name, binds)
|
84
|
+
end
|
85
|
+
|
42
86
|
# Returns the last auto-generated ID from the affected table.
|
43
|
-
|
44
|
-
|
87
|
+
#
|
88
|
+
# +id_value+ will be returned unless the value is nil, in
|
89
|
+
# which case the database will attempt to calculate the last inserted
|
90
|
+
# id and return that value.
|
91
|
+
#
|
92
|
+
# If the next id was calculated in advance (as in Oracle), it should be
|
93
|
+
# passed in as +id_value+.
|
94
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
95
|
+
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
96
|
+
value = exec_insert(sql, name, binds, pk, sequence_name)
|
97
|
+
id_value || last_inserted_id(value)
|
45
98
|
end
|
46
99
|
|
47
100
|
# Executes the update statement and returns the number of rows affected.
|
48
|
-
def update(
|
49
|
-
|
101
|
+
def update(arel, name = nil, binds = [])
|
102
|
+
exec_update(to_sql(arel, binds), name, binds)
|
50
103
|
end
|
51
104
|
|
52
105
|
# Executes the delete statement and returns the number of rows affected.
|
53
|
-
def delete(
|
54
|
-
|
106
|
+
def delete(arel, name = nil, binds = [])
|
107
|
+
exec_delete(to_sql(arel, binds), name, binds)
|
55
108
|
end
|
56
109
|
|
57
|
-
#
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
# Returns true if there is no transaction active, false if there is a
|
63
|
-
# transaction active, and nil if this information is unknown.
|
64
|
-
#
|
65
|
-
# Not all adapters supports transaction state introspection. Currently,
|
66
|
-
# only the PostgreSQL adapter supports this.
|
67
|
-
def outside_transaction?
|
68
|
-
nil
|
110
|
+
# Returns +true+ when the connection adapter supports prepared statement
|
111
|
+
# caching, otherwise returns +false+
|
112
|
+
def supports_statement_cache?
|
113
|
+
false
|
69
114
|
end
|
70
115
|
|
71
116
|
# Runs the given block in a database transaction, and returns the result
|
@@ -79,8 +124,9 @@ module ActiveRecord
|
|
79
124
|
#
|
80
125
|
# In order to get around this problem, #transaction will emulate the effect
|
81
126
|
# of nested transactions, by using savepoints:
|
82
|
-
# http://dev.mysql.com/doc/refman/5.0/en/
|
83
|
-
# Savepoints are supported by MySQL and PostgreSQL
|
127
|
+
# http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
|
128
|
+
# Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
|
129
|
+
# supports savepoints.
|
84
130
|
#
|
85
131
|
# It is safe to call this method if a database transaction is already open,
|
86
132
|
# i.e. if #transaction is called within another #transaction block. In case
|
@@ -105,95 +151,124 @@ module ActiveRecord
|
|
105
151
|
# already-automatically-released savepoints:
|
106
152
|
#
|
107
153
|
# Model.connection.transaction do # BEGIN
|
108
|
-
# Model.connection.transaction(:
|
154
|
+
# Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
109
155
|
# Model.connection.create_table(...)
|
110
156
|
# # active_record_1 now automatically released
|
111
157
|
# end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
|
112
158
|
# end
|
159
|
+
#
|
160
|
+
# == Transaction isolation
|
161
|
+
#
|
162
|
+
# If your database supports setting the isolation level for a transaction, you can set
|
163
|
+
# it like so:
|
164
|
+
#
|
165
|
+
# Post.transaction(isolation: :serializable) do
|
166
|
+
# # ...
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# Valid isolation levels are:
|
170
|
+
#
|
171
|
+
# * <tt>:read_uncommitted</tt>
|
172
|
+
# * <tt>:read_committed</tt>
|
173
|
+
# * <tt>:repeatable_read</tt>
|
174
|
+
# * <tt>:serializable</tt>
|
175
|
+
#
|
176
|
+
# You should consult the documentation for your database to understand the
|
177
|
+
# semantics of these different levels:
|
178
|
+
#
|
179
|
+
# * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
|
180
|
+
# * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
|
181
|
+
#
|
182
|
+
# An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
|
183
|
+
#
|
184
|
+
# * The adapter does not support setting the isolation level
|
185
|
+
# * You are joining an existing open transaction
|
186
|
+
# * You are creating a nested (savepoint) transaction
|
187
|
+
#
|
188
|
+
# The mysql, mysql2 and postgresql adapters support setting the transaction
|
189
|
+
# isolation level. However, support is disabled for mysql versions below 5,
|
190
|
+
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
|
191
|
+
# which means the isolation level gets persisted outside the transaction.
|
113
192
|
def transaction(options = {})
|
114
|
-
options.assert_valid_keys :requires_new, :joinable
|
193
|
+
options.assert_valid_keys :requires_new, :joinable, :isolation
|
194
|
+
|
195
|
+
if !options[:requires_new] && current_transaction.joinable?
|
196
|
+
if options[:isolation]
|
197
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
198
|
+
end
|
115
199
|
|
116
|
-
|
117
|
-
if options.has_key?(:joinable)
|
118
|
-
@transaction_joinable = options[:joinable]
|
200
|
+
yield
|
119
201
|
else
|
120
|
-
|
202
|
+
within_new_transaction(options) { yield }
|
121
203
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
@_current_transaction_records ||= []
|
204
|
+
rescue ActiveRecord::Rollback
|
205
|
+
# rollbacks are silently swallowed
|
206
|
+
end
|
126
207
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
create_savepoint
|
134
|
-
end
|
135
|
-
increment_open_transactions
|
136
|
-
transaction_open = true
|
137
|
-
@_current_transaction_records.push([])
|
138
|
-
end
|
139
|
-
yield
|
140
|
-
end
|
141
|
-
rescue Exception => database_transaction_rollback
|
142
|
-
if transaction_open && !outside_transaction?
|
143
|
-
transaction_open = false
|
144
|
-
decrement_open_transactions
|
145
|
-
if open_transactions == 0
|
146
|
-
rollback_db_transaction
|
147
|
-
rollback_transaction_records(true)
|
148
|
-
else
|
149
|
-
rollback_to_savepoint
|
150
|
-
rollback_transaction_records(false)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
|
154
|
-
end
|
208
|
+
def within_new_transaction(options = {}) #:nodoc:
|
209
|
+
transaction = begin_transaction(options)
|
210
|
+
yield
|
211
|
+
rescue Exception => error
|
212
|
+
rollback_transaction if transaction
|
213
|
+
raise
|
155
214
|
ensure
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
decrement_open_transactions
|
162
|
-
begin
|
163
|
-
if open_transactions == 0
|
164
|
-
commit_db_transaction
|
165
|
-
commit_transaction_records
|
166
|
-
else
|
167
|
-
release_savepoint
|
168
|
-
save_point_records = @_current_transaction_records.pop
|
169
|
-
unless save_point_records.blank?
|
170
|
-
@_current_transaction_records.push([]) if @_current_transaction_records.empty?
|
171
|
-
@_current_transaction_records.last.concat(save_point_records)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
rescue Exception => database_transaction_rollback
|
175
|
-
if open_transactions == 0
|
176
|
-
rollback_db_transaction
|
177
|
-
rollback_transaction_records(true)
|
178
|
-
else
|
179
|
-
rollback_to_savepoint
|
180
|
-
rollback_transaction_records(false)
|
181
|
-
end
|
182
|
-
raise
|
183
|
-
end
|
215
|
+
begin
|
216
|
+
commit_transaction unless error
|
217
|
+
rescue Exception
|
218
|
+
rollback_transaction
|
219
|
+
raise
|
184
220
|
end
|
185
221
|
end
|
186
222
|
|
223
|
+
def current_transaction #:nodoc:
|
224
|
+
@transaction
|
225
|
+
end
|
226
|
+
|
227
|
+
def transaction_open?
|
228
|
+
@transaction.open?
|
229
|
+
end
|
230
|
+
|
231
|
+
def begin_transaction(options = {}) #:nodoc:
|
232
|
+
@transaction = @transaction.begin(options)
|
233
|
+
end
|
234
|
+
|
235
|
+
def commit_transaction #:nodoc:
|
236
|
+
@transaction = @transaction.commit
|
237
|
+
end
|
238
|
+
|
239
|
+
def rollback_transaction #:nodoc:
|
240
|
+
@transaction = @transaction.rollback
|
241
|
+
end
|
242
|
+
|
243
|
+
def reset_transaction #:nodoc:
|
244
|
+
@transaction = ClosedTransaction.new(self)
|
245
|
+
end
|
246
|
+
|
187
247
|
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
188
248
|
# can be called.
|
189
249
|
def add_transaction_record(record)
|
190
|
-
|
191
|
-
last_batch << record if last_batch
|
250
|
+
@transaction.add_record(record)
|
192
251
|
end
|
193
252
|
|
194
253
|
# Begins the transaction (and turns off auto-committing).
|
195
254
|
def begin_db_transaction() end
|
196
255
|
|
256
|
+
def transaction_isolation_levels
|
257
|
+
{
|
258
|
+
read_uncommitted: "READ UNCOMMITTED",
|
259
|
+
read_committed: "READ COMMITTED",
|
260
|
+
repeatable_read: "REPEATABLE READ",
|
261
|
+
serializable: "SERIALIZABLE"
|
262
|
+
}
|
263
|
+
end
|
264
|
+
|
265
|
+
# Begins the transaction with the isolation level set. Raises an error by
|
266
|
+
# default; adapters that support setting the isolation level should implement
|
267
|
+
# this method.
|
268
|
+
def begin_isolated_db_transaction(isolation)
|
269
|
+
raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
|
270
|
+
end
|
271
|
+
|
197
272
|
# Commits the transaction (and turns on auto-committing).
|
198
273
|
def commit_db_transaction() end
|
199
274
|
|
@@ -201,46 +276,31 @@ module ActiveRecord
|
|
201
276
|
# done if the transaction block raises an exception or returns false.
|
202
277
|
def rollback_db_transaction() end
|
203
278
|
|
204
|
-
# Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
|
205
|
-
# fragment that has the same semantics as LIMIT and OFFSET.
|
206
|
-
#
|
207
|
-
# +options+ must be a Hash which contains a +:limit+ option
|
208
|
-
# and an +:offset+ option.
|
209
|
-
#
|
210
|
-
# This method *modifies* the +sql+ parameter.
|
211
|
-
#
|
212
|
-
# ===== Examples
|
213
|
-
# add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
|
214
|
-
# generates
|
215
|
-
# SELECT * FROM suppliers LIMIT 10 OFFSET 50
|
216
|
-
|
217
|
-
def add_limit_offset!(sql, options)
|
218
|
-
if limit = options[:limit]
|
219
|
-
sql << " LIMIT #{sanitize_limit(limit)}"
|
220
|
-
end
|
221
|
-
if offset = options[:offset]
|
222
|
-
sql << " OFFSET #{offset.to_i}"
|
223
|
-
end
|
224
|
-
sql
|
225
|
-
end
|
226
|
-
|
227
279
|
def default_sequence_name(table, column)
|
228
280
|
nil
|
229
281
|
end
|
230
282
|
|
231
283
|
# Set the sequence to the max value of the table's column.
|
232
284
|
def reset_sequence!(table, column, sequence = nil)
|
233
|
-
# Do nothing by default.
|
285
|
+
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
|
234
286
|
end
|
235
287
|
|
236
288
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
237
289
|
# something beyond a simple insert (eg. Oracle).
|
238
290
|
def insert_fixture(fixture, table_name)
|
239
|
-
|
291
|
+
columns = schema_cache.columns_hash(table_name)
|
292
|
+
|
293
|
+
key_list = []
|
294
|
+
value_list = fixture.map do |name, value|
|
295
|
+
key_list << quote_column_name(name)
|
296
|
+
quote(value, columns[name])
|
297
|
+
end
|
298
|
+
|
299
|
+
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
|
240
300
|
end
|
241
301
|
|
242
302
|
def empty_insert_statement_value
|
243
|
-
"VALUES
|
303
|
+
"DEFAULT VALUES"
|
244
304
|
end
|
245
305
|
|
246
306
|
def case_sensitive_equality_operator
|
@@ -251,10 +311,53 @@ module ActiveRecord
|
|
251
311
|
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
|
252
312
|
end
|
253
313
|
|
314
|
+
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
315
|
+
#
|
316
|
+
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
317
|
+
# should look like an integer, or a comma-delimited list of integers, or
|
318
|
+
# an Arel SQL literal.
|
319
|
+
#
|
320
|
+
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
321
|
+
# Returns the sanitized limit parameter, either as an integer, or as a
|
322
|
+
# string which contains a comma-delimited list of integers.
|
323
|
+
def sanitize_limit(limit)
|
324
|
+
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
325
|
+
limit
|
326
|
+
elsif limit.to_s =~ /,/
|
327
|
+
Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
|
328
|
+
else
|
329
|
+
Integer(limit)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
|
334
|
+
# on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
|
335
|
+
# an UPDATE statement, so in the mysql adapters we redefine this to do that.
|
336
|
+
def join_to_update(update, select) #:nodoc:
|
337
|
+
key = update.key
|
338
|
+
subselect = subquery_for(key, select)
|
339
|
+
|
340
|
+
update.where key.in(subselect)
|
341
|
+
end
|
342
|
+
|
343
|
+
def join_to_delete(delete, select, key) #:nodoc:
|
344
|
+
subselect = subquery_for(key, select)
|
345
|
+
|
346
|
+
delete.where key.in(subselect)
|
347
|
+
end
|
348
|
+
|
254
349
|
protected
|
350
|
+
|
351
|
+
# Return a subquery for the given key using the join information.
|
352
|
+
def subquery_for(key, select)
|
353
|
+
subselect = select.clone
|
354
|
+
subselect.projections = [key]
|
355
|
+
subselect
|
356
|
+
end
|
357
|
+
|
255
358
|
# Returns an array of record hashes with the column names as keys and
|
256
359
|
# column values as values.
|
257
|
-
def select(sql, name = nil)
|
360
|
+
def select(sql, name = nil, binds = [])
|
258
361
|
end
|
259
362
|
undef_method :select
|
260
363
|
|
@@ -274,56 +377,14 @@ module ActiveRecord
|
|
274
377
|
update_sql(sql, name)
|
275
378
|
end
|
276
379
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
# should look like an integer, or a comma-delimited list of integers.
|
281
|
-
#
|
282
|
-
# Returns the sanitized limit parameter, either as an integer, or as a
|
283
|
-
# string which contains a comma-delimited list of integers.
|
284
|
-
def sanitize_limit(limit)
|
285
|
-
if limit.to_s =~ /,/
|
286
|
-
limit.to_s.split(',').map{ |i| i.to_i }.join(',')
|
287
|
-
else
|
288
|
-
limit.to_i
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
# Send a rollback message to all records after they have been rolled back. If rollback
|
293
|
-
# is false, only rollback records since the last save point.
|
294
|
-
def rollback_transaction_records(rollback) #:nodoc
|
295
|
-
if rollback
|
296
|
-
records = @_current_transaction_records.flatten
|
297
|
-
@_current_transaction_records.clear
|
298
|
-
else
|
299
|
-
records = @_current_transaction_records.pop
|
300
|
-
end
|
301
|
-
|
302
|
-
unless records.blank?
|
303
|
-
records.uniq.each do |record|
|
304
|
-
begin
|
305
|
-
record.rolledback!(rollback)
|
306
|
-
rescue Exception => e
|
307
|
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|
311
|
-
end
|
380
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
381
|
+
[sql, binds]
|
382
|
+
end
|
312
383
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
unless records.blank?
|
318
|
-
records.uniq.each do |record|
|
319
|
-
begin
|
320
|
-
record.committed!
|
321
|
-
rescue Exception => e
|
322
|
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
384
|
+
def last_inserted_id(result)
|
385
|
+
row = result.rows.first
|
386
|
+
row && row.first
|
387
|
+
end
|
327
388
|
end
|
328
389
|
end
|
329
390
|
end
|
@@ -1,19 +1,17 @@
|
|
1
|
-
require 'active_support/core_ext/object/duplicable'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module ConnectionAdapters # :nodoc:
|
5
3
|
module QueryCache
|
6
4
|
class << self
|
7
|
-
def included(base)
|
5
|
+
def included(base) #:nodoc:
|
8
6
|
dirties_query_cache base, :insert, :update, :delete
|
9
7
|
end
|
10
8
|
|
11
9
|
def dirties_query_cache(base, *method_names)
|
12
10
|
method_names.each do |method_name|
|
13
11
|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
|
14
|
-
def #{method_name}(*) # def update_with_query_dirty(*
|
12
|
+
def #{method_name}(*) # def update_with_query_dirty(*)
|
15
13
|
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
|
16
|
-
super #
|
14
|
+
super # super
|
17
15
|
end # end
|
18
16
|
end_code
|
19
17
|
end
|
@@ -31,6 +29,14 @@ module ActiveRecord
|
|
31
29
|
@query_cache_enabled = old
|
32
30
|
end
|
33
31
|
|
32
|
+
def enable_query_cache!
|
33
|
+
@query_cache_enabled = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def disable_query_cache!
|
37
|
+
@query_cache_enabled = false
|
38
|
+
end
|
39
|
+
|
34
40
|
# Disable the query cache within the block.
|
35
41
|
def uncached
|
36
42
|
old, @query_cache_enabled = @query_cache_enabled, false
|
@@ -49,33 +55,41 @@ module ActiveRecord
|
|
49
55
|
@query_cache.clear
|
50
56
|
end
|
51
57
|
|
52
|
-
def select_all(
|
53
|
-
if @query_cache_enabled
|
54
|
-
|
58
|
+
def select_all(arel, name = nil, binds = [])
|
59
|
+
if @query_cache_enabled && !locked?(arel)
|
60
|
+
sql = to_sql(arel, binds)
|
61
|
+
cache_sql(sql, binds) { super(sql, name, binds) }
|
55
62
|
else
|
56
63
|
super
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
67
|
private
|
61
|
-
def cache_sql(sql)
|
62
|
-
result =
|
63
|
-
if @query_cache.has_key?(sql)
|
64
|
-
ActiveSupport::Notifications.instrument("sql.active_record",
|
65
|
-
:sql => sql, :name => "CACHE", :connection_id => self.object_id)
|
66
|
-
@query_cache[sql]
|
67
|
-
else
|
68
|
-
@query_cache[sql] = yield
|
69
|
-
end
|
70
68
|
|
71
|
-
|
72
|
-
|
69
|
+
def cache_sql(sql, binds)
|
70
|
+
result =
|
71
|
+
if @query_cache[sql].key?(binds)
|
72
|
+
ActiveSupport::Notifications.instrument("sql.active_record",
|
73
|
+
:sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
|
74
|
+
@query_cache[sql][binds]
|
73
75
|
else
|
74
|
-
|
76
|
+
@query_cache[sql][binds] = yield
|
75
77
|
end
|
76
|
-
|
77
|
-
|
78
|
+
|
79
|
+
# FIXME: we should guarantee that all cached items are Result
|
80
|
+
# objects. Then we can avoid this conditional
|
81
|
+
if ActiveRecord::Result === result
|
82
|
+
result.dup
|
83
|
+
else
|
84
|
+
result.collect { |row| row.dup }
|
78
85
|
end
|
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
|
79
93
|
end
|
80
94
|
end
|
81
95
|
end
|