activerecord 4.2.0 → 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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -3,28 +3,34 @@ module ActiveRecord
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  module ClassMethods
6
- def quote_value(value, column) #:nodoc:
7
- connection.quote(value, column)
8
- end
9
-
10
- # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
11
- 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:
12
9
  connection.quote(object)
13
10
  end
11
+ alias_method :quote_value, :sanitize
14
12
 
15
13
  protected
16
14
 
17
- # Accepts an array, hash, or string of SQL conditions and sanitizes
15
+ # Accepts an array or string of SQL conditions and sanitizes
18
16
  # them into a valid SQL fragment for a WHERE clause.
19
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
20
- # { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
21
- # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
22
- 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)
23
30
  return nil if condition.blank?
24
31
 
25
32
  case condition
26
33
  when Array; sanitize_sql_array(condition)
27
- when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
28
34
  else condition
29
35
  end
30
36
  end
@@ -33,7 +39,18 @@ module ActiveRecord
33
39
 
34
40
  # Accepts an array, hash, or string of SQL conditions and sanitizes
35
41
  # them into a valid SQL fragment for a SET clause.
36
- # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
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'"
37
54
  def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
38
55
  case assignments
39
56
  when Array; sanitize_sql_array(assignments)
@@ -42,17 +59,37 @@ module ActiveRecord
42
59
  end
43
60
  end
44
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
75
+ end
76
+ end
77
+
45
78
  # Accepts a hash of SQL conditions and replaces those attributes
46
- # that correspond to a +composed_of+ relationship with their expanded
47
- # 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
+ #
48
82
  # Given:
49
- # class Person < ActiveRecord::Base
50
- # composed_of :address, class_name: "Address",
51
- # mapping: [%w(address_street street), %w(address_city city)]
52
- # 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
+ #
53
89
  # Then:
54
- # { address: Address.new("813 abc st.", "chicago") }
55
- # # => { 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" }
56
93
  def expand_hash_conditions_for_aggregates(attrs)
57
94
  expanded_attrs = {}
58
95
  attrs.each do |attr, value|
@@ -72,46 +109,32 @@ module ActiveRecord
72
109
  expanded_attrs
73
110
  end
74
111
 
75
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
76
- # { name: "foo'bar", group_id: 4 }
77
- # # => "name='foo''bar' and group_id= 4"
78
- # { status: nil, group_id: [1,2,3] }
79
- # # => "status IS NULL and group_id IN (1,2,3)"
80
- # { age: 13..18 }
81
- # # => "age BETWEEN 13 AND 18"
82
- # { 'other_records.id' => 7 }
83
- # # => "`other_records`.`id` = 7"
84
- # { other_records: { id: 7 } }
85
- # # => "`other_records`.`id` = 7"
86
- # And for value objects on a composed_of relationship:
87
- # { address: Address.new("123 abc st.", "chicago") }
88
- # # => "address_street='123 abc st.' and address_city='chicago'"
89
- def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
90
- ActiveSupport::Deprecation.warn(<<-EOWARN)
91
- sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
92
- EOWARN
93
- attrs = PredicateBuilder.resolve_column_aliases self, attrs
94
- attrs = expand_hash_conditions_for_aggregates(attrs)
95
-
96
- table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
97
- PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
98
- connection.visitor.compile b
99
- }.join(' AND ')
100
- end
101
- alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
102
-
103
112
  # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
104
- # { status: nil, group_id: 1 }
105
- # # => "status = NULL , group_id = 1"
113
+ #
114
+ # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
115
+ # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
106
116
  def sanitize_sql_hash_for_assignment(attrs, table)
107
117
  c = connection
108
118
  attrs.map do |attr, value|
109
- "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
119
+ value = type_for_attribute(attr.to_s).serialize(value)
120
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
110
121
  end.join(', ')
111
122
  end
112
123
 
113
124
  # Sanitizes a +string+ so that it is safe to use within an SQL
114
- # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
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"
115
138
  def sanitize_sql_like(string, escape_character = "\\")
116
139
  pattern = Regexp.union(escape_character, "%", "_")
117
140
  string.gsub(pattern) { |x| [escape_character, x].join }
@@ -119,7 +142,15 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
119
142
 
120
143
  # Accepts an array of conditions. The array has each value
121
144
  # sanitized and interpolated into the SQL statement.
122
- # ["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'"
123
154
  def sanitize_sql_array(ary)
124
155
  statement, *values = ary
125
156
  if values.first.is_a?(Hash) && statement =~ /:\w+/
@@ -133,7 +164,7 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
133
164
  end
134
165
  end
135
166
 
136
- def replace_bind_variables(statement, values) #:nodoc:
167
+ def replace_bind_variables(statement, values) # :nodoc:
137
168
  raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
138
169
  bound = values.dup
139
170
  c = connection
@@ -142,7 +173,7 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
142
173
  end
143
174
  end
144
175
 
145
- def replace_bind_variable(value, c = connection) #:nodoc:
176
+ def replace_bind_variable(value, c = connection) # :nodoc:
146
177
  if ActiveRecord::Relation === value
147
178
  value.to_sql
148
179
  else
@@ -150,10 +181,10 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
150
181
  end
151
182
  end
152
183
 
153
- def replace_named_bind_variables(statement, bind_vars) #:nodoc:
154
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
184
+ def replace_named_bind_variables(statement, bind_vars) # :nodoc:
185
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
155
186
  if $1 == ':' # skip postgresql casts
156
- $& # return the whole match
187
+ match # return the whole match
157
188
  elsif bind_vars.include?(match = $2.to_sym)
158
189
  replace_bind_variable(bind_vars[match])
159
190
  else
@@ -162,10 +193,8 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
162
193
  end
163
194
  end
164
195
 
165
- def quote_bound_value(value, c = connection, column = nil) #:nodoc:
166
- if column
167
- c.quote(value, column)
168
- elsif value.respond_to?(:map) && !value.acts_like?(:string)
196
+ def quote_bound_value(value, c = connection) # :nodoc:
197
+ if value.respond_to?(:map) && !value.acts_like?(:string)
169
198
  if value.respond_to?(:empty?) && value.empty?
170
199
  c.quote(nil)
171
200
  else
@@ -176,7 +205,7 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
176
205
  end
177
206
  end
178
207
 
179
- def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
208
+ def raise_if_bind_arity_mismatch(statement, expected, provided) # :nodoc:
180
209
  unless expected == provided
181
210
  raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
182
211
  end
@@ -184,8 +213,8 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
184
213
  end
185
214
 
186
215
  # TODO: Deprecate this
187
- def quoted_id
188
- self.class.quote_value(id, column_for_attribute(self.class.primary_key))
216
+ def quoted_id # :nodoc:
217
+ self.class.quote_value(@attributes[self.class.primary_key].value_for_database)
189
218
  end
190
219
  end
191
220
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
- # = Active Record Schema
2
+ # = Active Record \Schema
3
3
  #
4
4
  # Allows programmers to programmatically define a schema in a portable
5
5
  # DSL. This means you can define tables, indexes, etc. without using SQL
@@ -27,29 +27,12 @@ module ActiveRecord
27
27
  #
28
28
  # ActiveRecord::Schema is only supported by database adapters that also
29
29
  # support migrations, the two features being very similar.
30
- class Schema < Migration
31
-
32
- # Returns the migrations paths.
33
- #
34
- # ActiveRecord::Schema.new.migrations_paths
35
- # # => ["db/migrate"] # Rails migration path by default.
36
- def migrations_paths
37
- ActiveRecord::Migrator.migrations_paths
38
- end
39
-
40
- def define(info, &block) # :nodoc:
41
- instance_eval(&block)
42
-
43
- unless info[:version].blank?
44
- initialize_schema_migrations_table
45
- connection.assume_migrated_upto_version(info[:version], migrations_paths)
46
- end
47
- end
48
-
30
+ class Schema < Migration::Current
49
31
  # Eval the given block. All methods available to the current connection
50
32
  # adapter are available within the block, so you can easily use the
51
- # database definition DSL to build up your schema (+create_table+,
52
- # +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.).
53
36
  #
54
37
  # The +info+ hash is optional, and if given is used to define metadata
55
38
  # about the current schema (currently, only the schema's version):
@@ -60,5 +43,26 @@ module ActiveRecord
60
43
  def self.define(info={}, &block)
61
44
  new.define(info, &block)
62
45
  end
46
+
47
+ def define(info, &block) # :nodoc:
48
+ instance_eval(&block)
49
+
50
+ if info[:version].present?
51
+ initialize_schema_migrations_table
52
+ connection.assume_migrated_upto_version(info[:version], migrations_paths)
53
+ end
54
+
55
+ ActiveRecord::InternalMetadata.create_table
56
+ ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
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
63
67
  end
64
68
  end
@@ -1,5 +1,4 @@
1
1
  require 'stringio'
2
- require 'active_support/core_ext/big_decimal'
3
2
 
4
3
  module ActiveRecord
5
4
  # = Active Record Schema Dumper
@@ -44,7 +43,6 @@ module ActiveRecord
44
43
 
45
44
  def initialize(connection, options = {})
46
45
  @connection = connection
47
- @types = @connection.native_database_types
48
46
  @version = Migrator::current_version rescue nil
49
47
  @options = options
50
48
  end
@@ -52,10 +50,6 @@ module ActiveRecord
52
50
  def header(stream)
53
51
  define_params = @version ? "version: #{@version}" : ""
54
52
 
55
- if stream.respond_to?(:external_encoding) && stream.external_encoding
56
- stream.puts "# encoding: #{stream.external_encoding.name}"
57
- end
58
-
59
53
  stream.puts <<HEADER
60
54
  # This file is auto-generated from the current state of the database. Instead
61
55
  # of editing this file, please use the migrations feature of Active Record to
@@ -91,7 +85,7 @@ HEADER
91
85
  end
92
86
 
93
87
  def tables(stream)
94
- sorted_tables = @connection.tables.sort
88
+ sorted_tables = @connection.data_sources.sort - @connection.views
95
89
 
96
90
  sorted_tables.each do |table_name|
97
91
  table(table_name, stream) unless ignored?(table_name)
@@ -111,30 +105,46 @@ HEADER
111
105
  tbl = StringIO.new
112
106
 
113
107
  # first dump primary key column
114
- pk = @connection.primary_key(table)
108
+ if @connection.respond_to?(:primary_keys)
109
+ pk = @connection.primary_keys(table)
110
+ pk = pk.first unless pk.size > 1
111
+ else
112
+ pk = @connection.primary_key(table)
113
+ end
115
114
 
116
115
  tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
117
- pkcol = columns.detect { |c| c.name == pk }
118
- if pkcol
119
- if pk != 'id'
120
- tbl.print %Q(, primary_key: "#{pk}")
121
- elsif pkcol.sql_type == 'bigint'
122
- tbl.print ", id: :bigserial"
123
- elsif pkcol.sql_type == 'uuid'
124
- tbl.print ", id: :uuid"
125
- tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function
116
+
117
+ case pk
118
+ when String
119
+ tbl.print ", primary_key: #{pk.inspect}" unless pk == 'id'
120
+ pkcol = columns.detect { |c| c.name == pk }
121
+ pkcolspec = @connection.column_spec_for_primary_key(pkcol)
122
+ if pkcolspec.present?
123
+ pkcolspec.each do |key, value|
124
+ tbl.print ", #{key}: #{value}"
125
+ end
126
126
  end
127
+ when Array
128
+ tbl.print ", primary_key: #{pk.inspect}"
127
129
  else
128
130
  tbl.print ", id: false"
129
131
  end
130
132
  tbl.print ", force: :cascade"
133
+
134
+ table_options = @connection.table_options(table)
135
+ tbl.print ", options: #{table_options.inspect}" unless table_options.blank?
136
+
137
+ if comment = @connection.table_comment(table).presence
138
+ tbl.print ", comment: #{comment.inspect}"
139
+ end
140
+
131
141
  tbl.puts " do |t|"
132
142
 
133
143
  # then dump all non-primary key columns
134
144
  column_specs = columns.map do |column|
135
145
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
136
146
  next if column.name == pk
137
- @connection.column_spec(column, @types)
147
+ @connection.column_spec(column)
138
148
  end.compact
139
149
 
140
150
  # find all migration keys used in this table
@@ -165,11 +175,11 @@ HEADER
165
175
  tbl.puts
166
176
  end
167
177
 
178
+ indexes_in_create(table, tbl)
179
+
168
180
  tbl.puts " end"
169
181
  tbl.puts
170
182
 
171
- indexes(table, tbl)
172
-
173
183
  tbl.rewind
174
184
  stream.print tbl.read
175
185
  rescue => e
@@ -181,26 +191,12 @@ HEADER
181
191
  stream
182
192
  end
183
193
 
194
+ # Keep it for indexing materialized views
184
195
  def indexes(table, stream)
185
196
  if (indexes = @connection.indexes(table)).any?
186
197
  add_index_statements = indexes.map do |index|
187
- statement_parts = [
188
- "add_index #{remove_prefix_and_suffix(index.table).inspect}",
189
- index.columns.inspect,
190
- "name: #{index.name.inspect}",
191
- ]
192
- statement_parts << 'unique: true' if index.unique
193
-
194
- index_lengths = (index.lengths || []).compact
195
- statement_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
196
-
197
- index_orders = index.orders || {}
198
- statement_parts << "order: #{index.orders.inspect}" if index_orders.any?
199
- statement_parts << "where: #{index.where.inspect}" if index.where
200
- statement_parts << "using: #{index.using.inspect}" if index.using
201
- statement_parts << "type: #{index.type.inspect}" if index.type
202
-
203
- " #{statement_parts.join(', ')}"
198
+ table_name = remove_prefix_and_suffix(index.table).inspect
199
+ " add_index #{([table_name]+index_parts(index)).join(', ')}"
204
200
  end
205
201
 
206
202
  stream.puts add_index_statements.sort.join("\n")
@@ -208,6 +204,34 @@ HEADER
208
204
  end
209
205
  end
210
206
 
207
+ def indexes_in_create(table, stream)
208
+ if (indexes = @connection.indexes(table)).any?
209
+ index_statements = indexes.map do |index|
210
+ " t.index #{index_parts(index).join(', ')}"
211
+ end
212
+ stream.puts index_statements.sort.join("\n")
213
+ end
214
+ end
215
+
216
+ def index_parts(index)
217
+ index_parts = [
218
+ index.columns.inspect,
219
+ "name: #{index.name.inspect}",
220
+ ]
221
+ index_parts << 'unique: true' if index.unique
222
+
223
+ index_lengths = (index.lengths || []).compact
224
+ index_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
225
+
226
+ index_orders = index.orders || {}
227
+ index_parts << "order: #{index.orders.inspect}" if index_orders.any?
228
+ index_parts << "where: #{index.where.inspect}" if index.where
229
+ index_parts << "using: #{index.using.inspect}" if index.using
230
+ index_parts << "type: #{index.type.inspect}" if index.type
231
+ index_parts << "comment: #{index.comment.inspect}" if index.comment
232
+ index_parts
233
+ end
234
+
211
235
  def foreign_keys(table, stream)
212
236
  if (foreign_keys = @connection.foreign_keys(table)).any?
213
237
  add_foreign_key_statements = foreign_keys.map do |foreign_key|
@@ -243,7 +267,7 @@ HEADER
243
267
  end
244
268
 
245
269
  def ignored?(table_name)
246
- ['schema_migrations', ignore_tables].flatten.any? do |ignored|
270
+ [ActiveRecord::Base.schema_migrations_table_name, ActiveRecord::Base.internal_metadata_table_name, ignore_tables].flatten.any? do |ignored|
247
271
  ignored === remove_prefix_and_suffix(table_name)
248
272
  end
249
273
  end
@@ -1,43 +1,37 @@
1
1
  require 'active_record/scoping/default'
2
2
  require 'active_record/scoping/named'
3
- require 'active_record/base'
4
3
 
5
4
  module ActiveRecord
6
- class SchemaMigration < ActiveRecord::Base
5
+ # This class is used to create a table that keeps track of which migrations
6
+ # have been applied to a given database. When a migration is run, its schema
7
+ # number is inserted in to the `SchemaMigration.table_name` so it doesn't need
8
+ # to be executed the next time.
9
+ class SchemaMigration < ActiveRecord::Base # :nodoc:
7
10
  class << self
8
11
  def primary_key
9
- nil
12
+ "version"
10
13
  end
11
14
 
12
15
  def table_name
13
16
  "#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
14
17
  end
15
18
 
16
- def index_name
17
- "#{table_name_prefix}unique_#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
18
- end
19
-
20
19
  def table_exists?
21
- connection.table_exists?(table_name)
20
+ ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
22
21
  end
23
22
 
24
- def create_table(limit=nil)
23
+ def create_table
25
24
  unless table_exists?
26
- version_options = {null: false}
27
- version_options[:limit] = limit if limit
25
+ version_options = connection.internal_string_options_for_primary_key
28
26
 
29
27
  connection.create_table(table_name, id: false) do |t|
30
- t.column :version, :string, version_options
28
+ t.string :version, version_options
31
29
  end
32
- connection.add_index table_name, :version, unique: true, name: index_name
33
30
  end
34
31
  end
35
32
 
36
33
  def drop_table
37
- if table_exists?
38
- connection.remove_index table_name, name: index_name
39
- connection.drop_table(table_name)
40
- end
34
+ connection.drop_table table_name, if_exists: true
41
35
  end
42
36
 
43
37
  def normalize_migration_number(number)
@@ -6,8 +6,10 @@ module ActiveRecord
6
6
  included do
7
7
  # Stores the default scope for the class.
8
8
  class_attribute :default_scopes, instance_writer: false, instance_predicate: false
9
+ class_attribute :default_scope_override, instance_writer: false, instance_predicate: false
9
10
 
10
11
  self.default_scopes = []
12
+ self.default_scope_override = nil
11
13
  end
12
14
 
13
15
  module ClassMethods
@@ -15,7 +17,7 @@ module ActiveRecord
15
17
  #
16
18
  # class Post < ActiveRecord::Base
17
19
  # def self.default_scope
18
- # where published: true
20
+ # where(published: true)
19
21
  # end
20
22
  # end
21
23
  #
@@ -33,6 +35,11 @@ module ActiveRecord
33
35
  block_given? ? relation.scoping { yield } : relation
34
36
  end
35
37
 
38
+ # Are there attributes associated with this scope?
39
+ def scope_attributes? # :nodoc:
40
+ super || default_scopes.any? || respond_to?(:default_scope)
41
+ end
42
+
36
43
  def before_remove_const #:nodoc:
37
44
  self.current_scope = nil
38
45
  end
@@ -48,7 +55,7 @@ module ActiveRecord
48
55
  #
49
56
  # Article.all # => SELECT * FROM articles WHERE published = true
50
57
  #
51
- # The +default_scope+ is also applied while creating/building a record.
58
+ # The #default_scope is also applied while creating/building a record.
52
59
  # It is not applied while updating a record.
53
60
  #
54
61
  # Article.new.published # => true
@@ -58,7 +65,7 @@ module ActiveRecord
58
65
  # +default_scope+ macro, and it will be called when building the
59
66
  # default scope.)
60
67
  #
61
- # If you use multiple +default_scope+ declarations in your model then
68
+ # If you use multiple #default_scope declarations in your model then
62
69
  # they will be merged together:
63
70
  #
64
71
  # class Article < ActiveRecord::Base
@@ -69,7 +76,7 @@ module ActiveRecord
69
76
  # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
70
77
  #
71
78
  # This is also the case with inheritance and module includes where the
72
- # parent or module defines a +default_scope+ and the child or including
79
+ # parent or module defines a #default_scope and the child or including
73
80
  # class defines a second one.
74
81
  #
75
82
  # If you need to do more complex things with a default scope, you can
@@ -94,25 +101,33 @@ module ActiveRecord
94
101
  self.default_scopes += [scope]
95
102
  end
96
103
 
97
- def build_default_scope(base_rel = relation) # :nodoc:
98
- if !Base.is_a?(method(:default_scope).owner)
104
+ def build_default_scope(base_rel = nil) # :nodoc:
105
+ return if abstract_class?
106
+
107
+ if self.default_scope_override.nil?
108
+ self.default_scope_override = !Base.is_a?(method(:default_scope).owner)
109
+ end
110
+
111
+ if self.default_scope_override
99
112
  # The user has defined their own default scope method, so call that
100
113
  evaluate_default_scope { default_scope }
101
114
  elsif default_scopes.any?
115
+ base_rel ||= relation
102
116
  evaluate_default_scope do
103
117
  default_scopes.inject(base_rel) do |default_scope, scope|
104
- default_scope.merge(base_rel.scoping { scope.call })
118
+ scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
119
+ default_scope.merge(base_rel.instance_exec(&scope))
105
120
  end
106
121
  end
107
122
  end
108
123
  end
109
124
 
110
125
  def ignore_default_scope? # :nodoc:
111
- ScopeRegistry.value_for(:ignore_default_scope, self)
126
+ ScopeRegistry.value_for(:ignore_default_scope, base_class)
112
127
  end
113
128
 
114
129
  def ignore_default_scope=(ignore) # :nodoc:
115
- ScopeRegistry.set_value_for(:ignore_default_scope, self, ignore)
130
+ ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore)
116
131
  end
117
132
 
118
133
  # The ignore_default_scope flag is used to prevent an infinite recursion