activerecord 3.2.19 → 5.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 +1715 -604
- data/MIT-LICENSE +2 -2
- data/README.rdoc +40 -45
- data/examples/performance.rb +33 -22
- data/examples/simple.rb +3 -4
- data/lib/active_record/aggregations.rb +76 -51
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +54 -40
- data/lib/active_record/associations/association.rb +76 -56
- data/lib/active_record/associations/association_scope.rb +125 -93
- data/lib/active_record/associations/belongs_to_association.rb +57 -28
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +120 -32
- data/lib/active_record/associations/builder/belongs_to.rb +115 -62
- data/lib/active_record/associations/builder/collection_association.rb +61 -53
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
- data/lib/active_record/associations/builder/has_many.rb +9 -65
- data/lib/active_record/associations/builder/has_one.rb +18 -52
- data/lib/active_record/associations/builder/singular_association.rb +18 -19
- data/lib/active_record/associations/collection_association.rb +268 -186
- data/lib/active_record/associations/collection_proxy.rb +1003 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +81 -41
- data/lib/active_record/associations/has_many_through_association.rb +76 -55
- data/lib/active_record/associations/has_one_association.rb +51 -21
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
- 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 +239 -155
- data/lib/active_record/associations/preloader/association.rb +97 -62
- data/lib/active_record/associations/preloader/collection_association.rb +2 -8
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +75 -33
- data/lib/active_record/associations/preloader.rb +111 -79
- data/lib/active_record/associations/singular_association.rb +35 -13
- data/lib/active_record/associations/through_association.rb +41 -19
- data/lib/active_record/associations.rb +727 -501
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +213 -0
- data/lib/active_record/attribute_assignment.rb +32 -162
- data/lib/active_record/attribute_decorators.rb +67 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -61
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +7 -6
- data/lib/active_record/attribute_methods/read.rb +56 -117
- data/lib/active_record/attribute_methods/serialization.rb +43 -96
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
- data/lib/active_record/attribute_methods/write.rb +34 -45
- data/lib/active_record/attribute_methods.rb +333 -144
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +108 -0
- data/lib/active_record/attribute_set.rb +108 -0
- data/lib/active_record/attributes.rb +265 -0
- data/lib/active_record/autosave_association.rb +285 -223
- data/lib/active_record/base.rb +95 -490
- data/lib/active_record/callbacks.rb +95 -61
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +28 -19
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
- data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
- data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
- data/lib/active_record/connection_adapters/column.rb +30 -259
- data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -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 +48 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -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/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -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 +31 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
- data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +155 -0
- data/lib/active_record/core.rb +561 -0
- data/lib/active_record/counter_cache.rb +146 -105
- data/lib/active_record/dynamic_matchers.rb +101 -64
- data/lib/active_record/enum.rb +234 -0
- data/lib/active_record/errors.rb +153 -56
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +10 -6
- data/lib/active_record/fixture_set/file.rb +77 -0
- data/lib/active_record/fixtures.rb +355 -232
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +144 -79
- data/lib/active_record/integration.rb +66 -13
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +77 -56
- data/lib/active_record/locking/pessimistic.rb +6 -6
- data/lib/active_record/log_subscriber.rb +53 -28
- data/lib/active_record/migration/command_recorder.rb +166 -33
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +792 -264
- data/lib/active_record/model_schema.rb +192 -130
- data/lib/active_record/nested_attributes.rb +238 -145
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +89 -0
- data/lib/active_record/persistence.rb +357 -157
- data/lib/active_record/query_cache.rb +22 -43
- data/lib/active_record/querying.rb +34 -23
- data/lib/active_record/railtie.rb +88 -48
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +5 -4
- data/lib/active_record/railties/databases.rake +170 -422
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -5
- data/lib/active_record/reflection.rb +715 -189
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +203 -50
- data/lib/active_record/relation/calculations.rb +203 -194
- data/lib/active_record/relation/delegation.rb +103 -25
- data/lib/active_record/relation/finder_methods.rb +457 -261
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +167 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +153 -48
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +1019 -194
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +46 -150
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +450 -245
- data/lib/active_record/result.rb +104 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +120 -94
- data/lib/active_record/schema.rb +28 -18
- data/lib/active_record/schema_dumper.rb +141 -74
- data/lib/active_record/schema_migration.rb +50 -0
- data/lib/active_record/scoping/default.rb +64 -57
- data/lib/active_record/scoping/named.rb +93 -108
- data/lib/active_record/scoping.rb +73 -121
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +7 -5
- data/lib/active_record/statement_cache.rb +113 -0
- data/lib/active_record/store.rb +173 -15
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +313 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
- data/lib/active_record/timestamp.rb +42 -24
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +233 -105
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +7 -0
- data/lib/active_record/type/date_time.rb +7 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +63 -0
- data/lib/active_record/type/time.rb +20 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type.rb +72 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +33 -18
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +66 -0
- data/lib/active_record/validations/uniqueness.rb +128 -68
- data/lib/active_record/validations.rb +48 -40
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +71 -47
- data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
- data/lib/rails/generators/active_record/migration.rb +18 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +188 -134
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- 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/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/mysql_adapter.rb +0 -441
- 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/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/model/templates/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
data/lib/active_record/result.rb
CHANGED
@@ -1,38 +1,130 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
###
|
3
|
-
# This class encapsulates a
|
4
|
-
#
|
3
|
+
# This class encapsulates a result returned from calling
|
4
|
+
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
|
5
|
+
# on any database connection adapter. For example:
|
5
6
|
#
|
6
|
-
#
|
7
|
-
#
|
7
|
+
# result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
|
8
|
+
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
9
|
+
#
|
10
|
+
# # Get the column names of the result:
|
11
|
+
# result.columns
|
12
|
+
# # => ["id", "title", "body"]
|
13
|
+
#
|
14
|
+
# # Get the record values of the result:
|
15
|
+
# result.rows
|
16
|
+
# # => [[1, "title_1", "body_1"],
|
17
|
+
# [2, "title_2", "body_2"],
|
18
|
+
# ...
|
19
|
+
# ]
|
20
|
+
#
|
21
|
+
# # Get an array of hashes representing the result (column => value):
|
22
|
+
# result.to_hash
|
23
|
+
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
|
24
|
+
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
|
25
|
+
# ...
|
26
|
+
# ]
|
27
|
+
#
|
28
|
+
# # ActiveRecord::Result also includes Enumerable.
|
29
|
+
# result.each do |row|
|
30
|
+
# puts row['title'] + " " + row['body']
|
31
|
+
# end
|
8
32
|
class Result
|
9
33
|
include Enumerable
|
10
34
|
|
11
|
-
|
35
|
+
IDENTITY_TYPE = Type::Value.new # :nodoc:
|
36
|
+
|
37
|
+
attr_reader :columns, :rows, :column_types
|
38
|
+
|
39
|
+
def initialize(columns, rows, column_types = {})
|
40
|
+
@columns = columns
|
41
|
+
@rows = rows
|
42
|
+
@hash_rows = nil
|
43
|
+
@column_types = column_types
|
44
|
+
end
|
12
45
|
|
13
|
-
def
|
14
|
-
@
|
15
|
-
@rows = rows
|
16
|
-
@hash_rows = nil
|
46
|
+
def length
|
47
|
+
@rows.length
|
17
48
|
end
|
18
49
|
|
19
50
|
def each
|
20
|
-
|
51
|
+
if block_given?
|
52
|
+
hash_rows.each { |row| yield row }
|
53
|
+
else
|
54
|
+
hash_rows.to_enum { @rows.size }
|
55
|
+
end
|
21
56
|
end
|
22
57
|
|
23
58
|
def to_hash
|
24
59
|
hash_rows
|
25
60
|
end
|
26
61
|
|
62
|
+
alias :map! :map
|
63
|
+
alias :collect! :map
|
64
|
+
|
65
|
+
# Returns true if there are no records.
|
66
|
+
def empty?
|
67
|
+
rows.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_ary
|
71
|
+
hash_rows
|
72
|
+
end
|
73
|
+
|
74
|
+
def [](idx)
|
75
|
+
hash_rows[idx]
|
76
|
+
end
|
77
|
+
|
78
|
+
def last
|
79
|
+
hash_rows.last
|
80
|
+
end
|
81
|
+
|
82
|
+
def cast_values(type_overrides = {}) # :nodoc:
|
83
|
+
types = columns.map { |name| column_type(name, type_overrides) }
|
84
|
+
result = rows.map do |values|
|
85
|
+
types.zip(values).map { |type, value| type.deserialize(value) }
|
86
|
+
end
|
87
|
+
|
88
|
+
columns.one? ? result.map!(&:first) : result
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize_copy(other)
|
92
|
+
@columns = columns.dup
|
93
|
+
@rows = rows.dup
|
94
|
+
@column_types = column_types.dup
|
95
|
+
@hash_rows = nil
|
96
|
+
end
|
97
|
+
|
27
98
|
private
|
99
|
+
|
100
|
+
def column_type(name, type_overrides = {})
|
101
|
+
type_overrides.fetch(name) do
|
102
|
+
column_types.fetch(name, IDENTITY_TYPE)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
28
106
|
def hash_rows
|
29
107
|
@hash_rows ||=
|
30
108
|
begin
|
31
109
|
# We freeze the strings to prevent them getting duped when
|
32
|
-
# used as keys in ActiveRecord::
|
110
|
+
# used as keys in ActiveRecord::Base's @attributes hash
|
33
111
|
columns = @columns.map { |c| c.dup.freeze }
|
34
112
|
@rows.map { |row|
|
35
|
-
Hash[columns.zip(row)]
|
113
|
+
# In the past we used Hash[columns.zip(row)]
|
114
|
+
# though elegant, the verbose way is much more efficient
|
115
|
+
# both time and memory wise cause it avoids a big array allocation
|
116
|
+
# this method is called a lot and needs to be micro optimised
|
117
|
+
hash = {}
|
118
|
+
|
119
|
+
index = 0
|
120
|
+
length = columns.length
|
121
|
+
|
122
|
+
while index < length
|
123
|
+
hash[columns[index]] = row[index]
|
124
|
+
index += 1
|
125
|
+
end
|
126
|
+
|
127
|
+
hash
|
36
128
|
}
|
37
129
|
end
|
38
130
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'active_support/per_thread_registry'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
# This is a thread locals registry for Active Record. For example:
|
5
|
+
#
|
6
|
+
# ActiveRecord::RuntimeRegistry.connection_handler
|
7
|
+
#
|
8
|
+
# returns the connection handler local to the current thread.
|
9
|
+
#
|
10
|
+
# See the documentation of ActiveSupport::PerThreadRegistry
|
11
|
+
# for further details.
|
12
|
+
class RuntimeRegistry # :nodoc:
|
13
|
+
extend ActiveSupport::PerThreadRegistry
|
14
|
+
|
15
|
+
attr_accessor :connection_handler, :sql_runtime, :connection_id
|
16
|
+
|
17
|
+
[:connection_handler, :sql_runtime, :connection_id].each do |val|
|
18
|
+
class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
|
19
|
+
class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,64 +1,100 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module Sanitization
|
5
3
|
extend ActiveSupport::Concern
|
6
4
|
|
7
5
|
module ClassMethods
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
|
13
|
-
def sanitize(object) #:nodoc:
|
6
|
+
# Used to sanitize objects before they're used in an SQL SELECT statement.
|
7
|
+
# Delegates to {connection.quote}[rdoc-ref:ConnectionAdapters::Quoting#quote].
|
8
|
+
def sanitize(object) # :nodoc:
|
14
9
|
connection.quote(object)
|
15
10
|
end
|
11
|
+
alias_method :quote_value, :sanitize
|
16
12
|
|
17
13
|
protected
|
18
14
|
|
19
|
-
# Accepts an array
|
15
|
+
# Accepts an array or string of SQL conditions and sanitizes
|
20
16
|
# them into a valid SQL fragment for a WHERE clause.
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
17
|
+
#
|
18
|
+
# sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
|
19
|
+
# # => "name='foo''bar' and group_id=4"
|
20
|
+
#
|
21
|
+
# sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
22
|
+
# # => "name='foo''bar' and group_id='4'"
|
23
|
+
#
|
24
|
+
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
25
|
+
# # => "name='foo''bar' and group_id='4'"
|
26
|
+
#
|
27
|
+
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
28
|
+
# # => "name='foo''bar' and group_id='4'"
|
29
|
+
def sanitize_sql_for_conditions(condition)
|
25
30
|
return nil if condition.blank?
|
26
31
|
|
27
32
|
case condition
|
28
33
|
when Array; sanitize_sql_array(condition)
|
29
|
-
when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
|
30
34
|
else condition
|
31
35
|
end
|
32
36
|
end
|
33
37
|
alias_method :sanitize_sql, :sanitize_sql_for_conditions
|
38
|
+
alias_method :sanitize_conditions, :sanitize_sql
|
34
39
|
|
35
40
|
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
36
41
|
# them into a valid SQL fragment for a SET clause.
|
37
|
-
#
|
38
|
-
|
42
|
+
#
|
43
|
+
# sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
|
44
|
+
# # => "name=NULL and group_id=4"
|
45
|
+
#
|
46
|
+
# sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
|
47
|
+
# # => "name=NULL and group_id=4"
|
48
|
+
#
|
49
|
+
# Post.send(:sanitize_sql_for_assignment, { name: nil, group_id: 4 })
|
50
|
+
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
|
51
|
+
#
|
52
|
+
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
|
53
|
+
# # => "name=NULL and group_id='4'"
|
54
|
+
def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
|
39
55
|
case assignments
|
40
|
-
|
41
|
-
|
42
|
-
|
56
|
+
when Array; sanitize_sql_array(assignments)
|
57
|
+
when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
|
58
|
+
else assignments
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Accepts an array, or string of SQL conditions and sanitizes
|
63
|
+
# them into a valid SQL fragment for an ORDER clause.
|
64
|
+
#
|
65
|
+
# sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
|
66
|
+
# # => "field(id, 1,3,2)"
|
67
|
+
#
|
68
|
+
# sanitize_sql_for_order("id ASC")
|
69
|
+
# # => "id ASC"
|
70
|
+
def sanitize_sql_for_order(condition)
|
71
|
+
if condition.is_a?(Array) && condition.first.to_s.include?('?')
|
72
|
+
sanitize_sql_array(condition)
|
73
|
+
else
|
74
|
+
condition
|
43
75
|
end
|
44
76
|
end
|
45
77
|
|
46
78
|
# Accepts a hash of SQL conditions and replaces those attributes
|
47
|
-
# that correspond to a
|
48
|
-
# aggregate attribute values.
|
79
|
+
# that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
|
80
|
+
# relationship with their expanded aggregate attribute values.
|
81
|
+
#
|
49
82
|
# Given:
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
83
|
+
#
|
84
|
+
# class Person < ActiveRecord::Base
|
85
|
+
# composed_of :address, class_name: "Address",
|
86
|
+
# mapping: [%w(address_street street), %w(address_city city)]
|
87
|
+
# end
|
88
|
+
#
|
54
89
|
# Then:
|
55
|
-
#
|
56
|
-
#
|
90
|
+
#
|
91
|
+
# { address: Address.new("813 abc st.", "chicago") }
|
92
|
+
# # => { address_street: "813 abc st.", address_city: "chicago" }
|
57
93
|
def expand_hash_conditions_for_aggregates(attrs)
|
58
94
|
expanded_attrs = {}
|
59
95
|
attrs.each do |attr, value|
|
60
|
-
|
61
|
-
mapping =
|
96
|
+
if aggregation = reflect_on_aggregation(attr.to_sym)
|
97
|
+
mapping = aggregation.mapping
|
62
98
|
mapping.each do |field_attr, aggregate_attr|
|
63
99
|
if mapping.size == 1 && !value.respond_to?(aggregate_attr)
|
64
100
|
expanded_attrs[field_attr] = value
|
@@ -73,42 +109,48 @@ module ActiveRecord
|
|
73
109
|
expanded_attrs
|
74
110
|
end
|
75
111
|
|
76
|
-
# Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
|
77
|
-
# { :name => "foo'bar", :group_id => 4 }
|
78
|
-
# # => "name='foo''bar' and group_id= 4"
|
79
|
-
# { :status => nil, :group_id => [1,2,3] }
|
80
|
-
# # => "status IS NULL and group_id IN (1,2,3)"
|
81
|
-
# { :age => 13..18 }
|
82
|
-
# # => "age BETWEEN 13 AND 18"
|
83
|
-
# { 'other_records.id' => 7 }
|
84
|
-
# # => "`other_records`.`id` = 7"
|
85
|
-
# { :other_records => { :id => 7 } }
|
86
|
-
# # => "`other_records`.`id` = 7"
|
87
|
-
# And for value objects on a composed_of relationship:
|
88
|
-
# { :address => Address.new("123 abc st.", "chicago") }
|
89
|
-
# # => "address_street='123 abc st.' and address_city='chicago'"
|
90
|
-
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
|
91
|
-
attrs = expand_hash_conditions_for_aggregates(attrs)
|
92
|
-
|
93
|
-
table = Arel::Table.new(table_name).alias(default_table_name)
|
94
|
-
PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
|
95
|
-
connection.visitor.accept b
|
96
|
-
}.join(' AND ')
|
97
|
-
end
|
98
|
-
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
|
99
|
-
|
100
112
|
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
101
|
-
#
|
102
|
-
#
|
103
|
-
|
113
|
+
#
|
114
|
+
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
115
|
+
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
116
|
+
def sanitize_sql_hash_for_assignment(attrs, table)
|
117
|
+
c = connection
|
104
118
|
attrs.map do |attr, value|
|
105
|
-
|
119
|
+
value = type_for_attribute(attr.to_s).serialize(value)
|
120
|
+
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
|
106
121
|
end.join(', ')
|
107
122
|
end
|
108
123
|
|
124
|
+
# Sanitizes a +string+ so that it is safe to use within an SQL
|
125
|
+
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
|
126
|
+
#
|
127
|
+
# sanitize_sql_like("100%")
|
128
|
+
# # => "100\\%"
|
129
|
+
#
|
130
|
+
# sanitize_sql_like("snake_cased_string")
|
131
|
+
# # => "snake\\_cased\\_string"
|
132
|
+
#
|
133
|
+
# sanitize_sql_like("100%", "!")
|
134
|
+
# # => "100!%"
|
135
|
+
#
|
136
|
+
# sanitize_sql_like("snake_cased_string", "!")
|
137
|
+
# # => "snake!_cased!_string"
|
138
|
+
def sanitize_sql_like(string, escape_character = "\\")
|
139
|
+
pattern = Regexp.union(escape_character, "%", "_")
|
140
|
+
string.gsub(pattern) { |x| [escape_character, x].join }
|
141
|
+
end
|
142
|
+
|
109
143
|
# Accepts an array of conditions. The array has each value
|
110
144
|
# sanitized and interpolated into the SQL statement.
|
111
|
-
#
|
145
|
+
#
|
146
|
+
# sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
|
147
|
+
# # => "name='foo''bar' and group_id=4"
|
148
|
+
#
|
149
|
+
# sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
150
|
+
# # => "name='foo''bar' and group_id=4"
|
151
|
+
#
|
152
|
+
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
|
153
|
+
# # => "name='foo''bar' and group_id='4'"
|
112
154
|
def sanitize_sql_array(ary)
|
113
155
|
statement, *values = ary
|
114
156
|
if values.first.is_a?(Hash) && statement =~ /:\w+/
|
@@ -122,45 +164,36 @@ module ActiveRecord
|
|
122
164
|
end
|
123
165
|
end
|
124
166
|
|
125
|
-
|
126
|
-
|
127
|
-
def replace_bind_variables(statement, values) #:nodoc:
|
167
|
+
def replace_bind_variables(statement, values) # :nodoc:
|
128
168
|
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
|
129
169
|
bound = values.dup
|
130
170
|
c = connection
|
131
|
-
statement.gsub(
|
171
|
+
statement.gsub(/\?/) do
|
172
|
+
replace_bind_variable(bound.shift, c)
|
173
|
+
end
|
132
174
|
end
|
133
175
|
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
quote_bound_value(bind_vars[match])
|
140
|
-
else
|
141
|
-
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
142
|
-
end
|
176
|
+
def replace_bind_variable(value, c = connection) # :nodoc:
|
177
|
+
if ActiveRecord::Relation === value
|
178
|
+
value.to_sql
|
179
|
+
else
|
180
|
+
quote_bound_value(value, c)
|
143
181
|
end
|
144
182
|
end
|
145
183
|
|
146
|
-
def
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
if var.is_a?(Range)
|
153
|
-
expanded << var.first
|
154
|
-
expanded << var.last
|
184
|
+
def replace_named_bind_variables(statement, bind_vars) # :nodoc:
|
185
|
+
statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
|
186
|
+
if $1 == ':' # skip postgresql casts
|
187
|
+
match # return the whole match
|
188
|
+
elsif bind_vars.include?(match = $2.to_sym)
|
189
|
+
replace_bind_variable(bind_vars[match])
|
155
190
|
else
|
156
|
-
|
191
|
+
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
157
192
|
end
|
158
193
|
end
|
159
|
-
|
160
|
-
expanded
|
161
194
|
end
|
162
195
|
|
163
|
-
def quote_bound_value(value, c = connection)
|
196
|
+
def quote_bound_value(value, c = connection) # :nodoc:
|
164
197
|
if value.respond_to?(:map) && !value.acts_like?(:string)
|
165
198
|
if value.respond_to?(:empty?) && value.empty?
|
166
199
|
c.quote(nil)
|
@@ -172,7 +205,7 @@ module ActiveRecord
|
|
172
205
|
end
|
173
206
|
end
|
174
207
|
|
175
|
-
def raise_if_bind_arity_mismatch(statement, expected, provided)
|
208
|
+
def raise_if_bind_arity_mismatch(statement, expected, provided) # :nodoc:
|
176
209
|
unless expected == provided
|
177
210
|
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
|
178
211
|
end
|
@@ -180,15 +213,8 @@ module ActiveRecord
|
|
180
213
|
end
|
181
214
|
|
182
215
|
# TODO: Deprecate this
|
183
|
-
def quoted_id
|
184
|
-
quote_value(
|
185
|
-
end
|
186
|
-
|
187
|
-
private
|
188
|
-
|
189
|
-
# Quote strings appropriately for SQL statements.
|
190
|
-
def quote_value(value, column = nil)
|
191
|
-
self.class.connection.quote(value, column)
|
216
|
+
def quoted_id # :nodoc:
|
217
|
+
self.class.quote_value(@attributes[self.class.primary_key].value_for_database)
|
192
218
|
end
|
193
219
|
end
|
194
220
|
end
|
data/lib/active_record/schema.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
|
-
# = Active Record Schema
|
2
|
+
# = Active Record \Schema
|
5
3
|
#
|
6
4
|
# Allows programmers to programmatically define a schema in a portable
|
7
5
|
# DSL. This means you can define tables, indexes, etc. without using SQL
|
@@ -12,16 +10,16 @@ module ActiveRecord
|
|
12
10
|
#
|
13
11
|
# ActiveRecord::Schema.define do
|
14
12
|
# create_table :authors do |t|
|
15
|
-
# t.string :name, :
|
13
|
+
# t.string :name, null: false
|
16
14
|
# end
|
17
15
|
#
|
18
16
|
# add_index :authors, :name, :unique
|
19
17
|
#
|
20
18
|
# create_table :posts do |t|
|
21
|
-
# t.integer :author_id, :
|
19
|
+
# t.integer :author_id, null: false
|
22
20
|
# t.string :subject
|
23
21
|
# t.text :body
|
24
|
-
# t.boolean :private, :
|
22
|
+
# t.boolean :private, default: false
|
25
23
|
# end
|
26
24
|
#
|
27
25
|
# add_index :posts, :author_id
|
@@ -29,30 +27,42 @@ module ActiveRecord
|
|
29
27
|
#
|
30
28
|
# ActiveRecord::Schema is only supported by database adapters that also
|
31
29
|
# support migrations, the two features being very similar.
|
32
|
-
class Schema < Migration
|
33
|
-
def migrations_paths
|
34
|
-
ActiveRecord::Migrator.migrations_paths
|
35
|
-
end
|
36
|
-
|
30
|
+
class Schema < Migration::Current
|
37
31
|
# Eval the given block. All methods available to the current connection
|
38
32
|
# adapter are available within the block, so you can easily use the
|
39
|
-
# database definition DSL to build up your schema (
|
40
|
-
#
|
33
|
+
# database definition DSL to build up your schema (
|
34
|
+
# {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
|
35
|
+
# {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
|
41
36
|
#
|
42
37
|
# The +info+ hash is optional, and if given is used to define metadata
|
43
38
|
# about the current schema (currently, only the schema's version):
|
44
39
|
#
|
45
|
-
# ActiveRecord::Schema.define(:
|
40
|
+
# ActiveRecord::Schema.define(version: 20380119000001) do
|
46
41
|
# ...
|
47
42
|
# end
|
48
43
|
def self.define(info={}, &block)
|
49
|
-
|
50
|
-
|
44
|
+
new.define(info, &block)
|
45
|
+
end
|
51
46
|
|
52
|
-
|
47
|
+
def define(info, &block) # :nodoc:
|
48
|
+
instance_eval(&block)
|
49
|
+
|
50
|
+
if info[:version].present?
|
53
51
|
initialize_schema_migrations_table
|
54
|
-
assume_migrated_upto_version(info[:version],
|
52
|
+
connection.assume_migrated_upto_version(info[:version], migrations_paths)
|
55
53
|
end
|
54
|
+
|
55
|
+
ActiveRecord::InternalMetadata.create_table
|
56
|
+
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
|
56
57
|
end
|
58
|
+
|
59
|
+
private
|
60
|
+
# Returns the migrations paths.
|
61
|
+
#
|
62
|
+
# ActiveRecord::Schema.new.migrations_paths
|
63
|
+
# # => ["db/migrate"] # Rails migration path by default.
|
64
|
+
def migrations_paths # :nodoc:
|
65
|
+
ActiveRecord::Migrator.migrations_paths
|
66
|
+
end
|
57
67
|
end
|
58
68
|
end
|