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.

Files changed (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,38 +1,130 @@
1
1
  module ActiveRecord
2
2
  ###
3
- # This class encapsulates a Result returned from calling +exec_query+ on any
4
- # database connection adapter. For example:
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
- # x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
7
- # x # => #<ActiveRecord::Result:0xdeadbeef>
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
- attr_reader :columns, :rows
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 initialize(columns, rows)
14
- @columns = columns
15
- @rows = rows
16
- @hash_rows = nil
46
+ def length
47
+ @rows.length
17
48
  end
18
49
 
19
50
  def each
20
- hash_rows.each { |row| yield row }
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::Model's @attributes hash
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
- def quote_value(value, column = nil) #:nodoc:
9
- connection.quote(value,column)
10
- end
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, hash, or string of SQL conditions and sanitizes
15
+ # Accepts an array or string of SQL conditions and sanitizes
20
16
  # them into a valid SQL fragment for a WHERE clause.
21
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
22
- # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
23
- # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
24
- def sanitize_sql_for_conditions(condition, table_name = self.table_name)
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
- # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
38
- def sanitize_sql_for_assignment(assignments)
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
- when Array; sanitize_sql_array(assignments)
41
- when Hash; sanitize_sql_hash_for_assignment(assignments)
42
- else assignments
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 +composed_of+ relationship with their expanded
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
- # class Person < ActiveRecord::Base
51
- # composed_of :address, :class_name => "Address",
52
- # :mapping => [%w(address_street street), %w(address_city city)]
53
- # end
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
- # { :address => Address.new("813 abc st.", "chicago") }
56
- # # => { :address_street => "813 abc st.", :address_city => "chicago" }
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
- unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
61
- mapping = aggregate_mapping(aggregation)
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
- # { :status => nil, :group_id => 1 }
102
- # # => "status = NULL , group_id = 1"
103
- def sanitize_sql_hash_for_assignment(attrs)
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
- "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
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
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
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
- alias_method :sanitize_conditions, :sanitize_sql
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('?') { quote_bound_value(bound.shift, c) }
171
+ statement.gsub(/\?/) do
172
+ replace_bind_variable(bound.shift, c)
173
+ end
132
174
  end
133
175
 
134
- def replace_named_bind_variables(statement, bind_vars) #:nodoc:
135
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
136
- if $1 == ':' # skip postgresql casts
137
- $& # return the whole match
138
- elsif bind_vars.include?(match = $2.to_sym)
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 expand_range_bind_variables(bind_vars) #:nodoc:
147
- expanded = []
148
-
149
- bind_vars.each do |var|
150
- next if var.is_a?(Hash)
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
- expanded << var
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) #:nodoc:
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) #:nodoc:
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 #:nodoc:
184
- quote_value(id, column_for_attribute(self.class.primary_key))
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
@@ -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, :null => false
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, :null => false
19
+ # t.integer :author_id, null: false
22
20
  # t.string :subject
23
21
  # t.text :body
24
- # t.boolean :private, :default => false
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 (+create_table+,
40
- # +add_index+, etc.).
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(:version => 20380119000001) do
40
+ # ActiveRecord::Schema.define(version: 20380119000001) do
46
41
  # ...
47
42
  # end
48
43
  def self.define(info={}, &block)
49
- schema = new
50
- schema.instance_eval(&block)
44
+ new.define(info, &block)
45
+ end
51
46
 
52
- unless info[:version].blank?
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], schema.migrations_paths)
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