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
@@ -18,9 +18,9 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  # Returns the maximum allowed length for an index name. This
21
- # limit is enforced by rails and Is less than or equal to
22
- # <tt>index_name_length</tt>. The gap between
23
- # <tt>index_name_length</tt> is to allow internal rails
21
+ # limit is enforced by \Rails and is less than or equal to
22
+ # #index_name_length. The gap between
23
+ # #index_name_length is to allow internal \Rails
24
24
  # operations to use prefixes in temporary operations.
25
25
  def allowed_index_name_length
26
26
  index_name_length
@@ -27,9 +27,19 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  # Returns an ActiveRecord::Result instance.
30
- def select_all(arel, name = nil, binds = [])
30
+ def select_all(arel, name = nil, binds = [], preparable: nil)
31
31
  arel, binds = binds_from_relation arel, binds
32
- select(to_sql(arel, binds), name, binds)
32
+ sql = to_sql(arel, binds)
33
+ if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
34
+ preparable = false
35
+ else
36
+ preparable = visitor.preparable
37
+ end
38
+ if prepared_statements && preparable
39
+ select_prepared(sql, name, binds)
40
+ else
41
+ select(sql, name, binds)
42
+ end
33
43
  end
34
44
 
35
45
  # Returns a record hash with the column names as keys and column values
@@ -40,33 +50,39 @@ module ActiveRecord
40
50
 
41
51
  # Returns a single value from a record
42
52
  def select_value(arel, name = nil, binds = [])
43
- if result = select_one(arel, name, binds)
44
- result.values.first
53
+ arel, binds = binds_from_relation arel, binds
54
+ if result = select_rows(to_sql(arel, binds), name, binds).first
55
+ result.first
45
56
  end
46
57
  end
47
58
 
48
59
  # Returns an array of the values of the first column in a select:
49
60
  # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
50
- def select_values(arel, name = nil)
51
- arel, binds = binds_from_relation arel, []
61
+ def select_values(arel, name = nil, binds = [])
62
+ arel, binds = binds_from_relation arel, binds
52
63
  select_rows(to_sql(arel, binds), name, binds).map(&:first)
53
64
  end
54
65
 
55
66
  # Returns an array of arrays containing the field values.
56
67
  # Order is the same as that returned by +columns+.
57
68
  def select_rows(sql, name = nil, binds = [])
69
+ exec_query(sql, name, binds).rows
58
70
  end
59
- undef_method :select_rows
60
71
 
61
- # Executes the SQL statement in the context of this connection.
72
+ # Executes the SQL statement in the context of this connection and returns
73
+ # the raw result from the connection adapter.
74
+ # Note: depending on your database connector, the result returned by this
75
+ # method may be manually memory managed. Consider using the exec_query
76
+ # wrapper instead.
62
77
  def execute(sql, name = nil)
78
+ raise NotImplementedError
63
79
  end
64
- undef_method :execute
65
80
 
66
81
  # Executes +sql+ statement in the context of this connection using
67
82
  # +binds+ as the bind substitutes. +name+ is logged along with
68
83
  # the executed +sql+ statement.
69
- def exec_query(sql, name = 'SQL', binds = [])
84
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
85
+ raise NotImplementedError
70
86
  end
71
87
 
72
88
  # Executes insert +sql+ statement in the context of this connection using
@@ -95,7 +111,7 @@ module ActiveRecord
95
111
  exec_query(sql, name, binds)
96
112
  end
97
113
 
98
- # Returns the last auto-generated ID from the affected table.
114
+ # Executes an INSERT query and returns the new record's ID
99
115
  #
100
116
  # +id_value+ will be returned unless the value is nil, in
101
117
  # which case the database will attempt to calculate the last inserted
@@ -104,20 +120,27 @@ module ActiveRecord
104
120
  # If the next id was calculated in advance (as in Oracle), it should be
105
121
  # passed in as +id_value+.
106
122
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
107
- sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
108
- value = exec_insert(sql, name, binds, pk, sequence_name)
123
+ sql, binds, pk, sequence_name = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
124
+ value = exec_insert(sql, name, binds, pk, sequence_name)
109
125
  id_value || last_inserted_id(value)
110
126
  end
127
+ alias create insert
128
+ alias insert_sql insert
129
+ deprecate insert_sql: :insert
111
130
 
112
131
  # Executes the update statement and returns the number of rows affected.
113
132
  def update(arel, name = nil, binds = [])
114
133
  exec_update(to_sql(arel, binds), name, binds)
115
134
  end
135
+ alias update_sql update
136
+ deprecate update_sql: :update
116
137
 
117
138
  # Executes the delete statement and returns the number of rows affected.
118
139
  def delete(arel, name = nil, binds = [])
119
140
  exec_delete(to_sql(arel, binds), name, binds)
120
141
  end
142
+ alias delete_sql delete
143
+ deprecate delete_sql: :delete
121
144
 
122
145
  # Returns +true+ when the connection adapter supports prepared statement
123
146
  # caching, otherwise returns +false+
@@ -136,7 +159,7 @@ module ActiveRecord
136
159
  #
137
160
  # In order to get around this problem, #transaction will emulate the effect
138
161
  # of nested transactions, by using savepoints:
139
- # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
162
+ # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
140
163
  # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
141
164
  # supports savepoints.
142
165
  #
@@ -188,29 +211,25 @@ module ActiveRecord
188
211
  # You should consult the documentation for your database to understand the
189
212
  # semantics of these different levels:
190
213
  #
191
- # * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
192
- # * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
214
+ # * http://www.postgresql.org/docs/current/static/transaction-iso.html
215
+ # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
193
216
  #
194
- # An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
217
+ # An ActiveRecord::TransactionIsolationError will be raised if:
195
218
  #
196
219
  # * The adapter does not support setting the isolation level
197
220
  # * You are joining an existing open transaction
198
221
  # * You are creating a nested (savepoint) transaction
199
222
  #
200
- # The mysql, mysql2 and postgresql adapters support setting the transaction
201
- # isolation level. However, support is disabled for MySQL versions below 5,
202
- # because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
203
- # which means the isolation level gets persisted outside the transaction.
204
- def transaction(options = {})
205
- options.assert_valid_keys :requires_new, :joinable, :isolation
206
-
207
- if !options[:requires_new] && current_transaction.joinable?
208
- if options[:isolation]
223
+ # The mysql2 and postgresql adapters support setting the transaction
224
+ # isolation level.
225
+ def transaction(requires_new: nil, isolation: nil, joinable: true)
226
+ if !requires_new && current_transaction.joinable?
227
+ if isolation
209
228
  raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
210
229
  end
211
230
  yield
212
231
  else
213
- transaction_manager.within_new_transaction(options) { yield }
232
+ transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
214
233
  end
215
234
  rescue ActiveRecord::Rollback
216
235
  # rollbacks are silently swallowed
@@ -234,6 +253,10 @@ module ActiveRecord
234
253
  current_transaction.add_record(record)
235
254
  end
236
255
 
256
+ def transaction_state
257
+ current_transaction.state
258
+ end
259
+
237
260
  # Begins the transaction (and turns off auto-committing).
238
261
  def begin_db_transaction() end
239
262
 
@@ -258,7 +281,15 @@ module ActiveRecord
258
281
 
259
282
  # Rolls back the transaction (and turns on auto-committing). Must be
260
283
  # done if the transaction block raises an exception or returns false.
261
- def rollback_db_transaction() end
284
+ def rollback_db_transaction
285
+ exec_rollback_db_transaction
286
+ end
287
+
288
+ def exec_rollback_db_transaction() end #:nodoc:
289
+
290
+ def rollback_to_savepoint(name = nil)
291
+ exec_rollback_to_savepoint(name)
292
+ end
262
293
 
263
294
  def default_sequence_name(table, column)
264
295
  nil
@@ -272,12 +303,24 @@ module ActiveRecord
272
303
  # Inserts the given fixture into the table. Overridden in adapters that require
273
304
  # something beyond a simple insert (eg. Oracle).
274
305
  def insert_fixture(fixture, table_name)
275
- columns = schema_cache.columns_hash(table_name)
306
+ fixture = fixture.stringify_keys
276
307
 
277
- key_list = []
278
- value_list = fixture.map do |name, value|
279
- key_list << quote_column_name(name)
280
- quote(value, columns[name])
308
+ columns = schema_cache.columns_hash(table_name)
309
+ binds = fixture.map do |name, value|
310
+ if column = columns[name]
311
+ type = lookup_cast_type_from_column(column)
312
+ Relation::QueryAttribute.new(name, value, type)
313
+ else
314
+ raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
315
+ end
316
+ end
317
+ key_list = fixture.keys.map { |name| quote_column_name(name) }
318
+ value_list = prepare_binds_for_database(binds).map do |value|
319
+ begin
320
+ quote(value)
321
+ rescue TypeError
322
+ quote(YAML.dump(value))
323
+ end
281
324
  end
282
325
 
283
326
  execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
@@ -309,18 +352,12 @@ module ActiveRecord
309
352
  # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
310
353
  # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
311
354
  # an UPDATE statement, so in the MySQL adapters we redefine this to do that.
312
- def join_to_update(update, select) #:nodoc:
313
- key = update.key
355
+ def join_to_update(update, select, key) # :nodoc:
314
356
  subselect = subquery_for(key, select)
315
357
 
316
358
  update.where key.in(subselect)
317
359
  end
318
-
319
- def join_to_delete(delete, select, key) #:nodoc:
320
- subselect = subquery_for(key, select)
321
-
322
- delete.where key.in(subselect)
323
- end
360
+ alias join_to_delete join_to_update
324
361
 
325
362
  protected
326
363
 
@@ -333,28 +370,15 @@ module ActiveRecord
333
370
 
334
371
  # Returns an ActiveRecord::Result instance.
335
372
  def select(sql, name = nil, binds = [])
336
- exec_query(sql, name, binds)
337
- end
338
-
339
-
340
- # Returns the last auto-generated ID from the affected table.
341
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
342
- execute(sql, name)
343
- id_value
344
- end
345
-
346
- # Executes the update statement and returns the number of rows affected.
347
- def update_sql(sql, name = nil)
348
- execute(sql, name)
373
+ exec_query(sql, name, binds, prepare: false)
349
374
  end
350
375
 
351
- # Executes the delete statement and returns the number of rows affected.
352
- def delete_sql(sql, name = nil)
353
- update_sql(sql, name)
376
+ def select_prepared(sql, name = nil, binds = [])
377
+ exec_query(sql, name, binds, prepare: true)
354
378
  end
355
379
 
356
380
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
357
- [sql, binds]
381
+ [sql, binds, pk, sequence_name]
358
382
  end
359
383
 
360
384
  def last_inserted_id(result)
@@ -364,7 +388,7 @@ module ActiveRecord
364
388
 
365
389
  def binds_from_relation(relation, binds)
366
390
  if relation.is_a?(Relation) && binds.empty?
367
- relation, binds = relation.arel, relation.bind_values
391
+ relation, binds = relation.arel, relation.bound_attributes
368
392
  end
369
393
  [relation, binds]
370
394
  end
@@ -3,7 +3,7 @@ module ActiveRecord
3
3
  module QueryCache
4
4
  class << self
5
5
  def included(base) #:nodoc:
6
- dirties_query_cache base, :insert, :update, :delete
6
+ dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
7
7
  end
8
8
 
9
9
  def dirties_query_cache(base, *method_names)
@@ -61,11 +61,11 @@ module ActiveRecord
61
61
  @query_cache.clear
62
62
  end
63
63
 
64
- def select_all(arel, name = nil, binds = [])
64
+ def select_all(arel, name = nil, binds = [], preparable: nil)
65
65
  if @query_cache_enabled && !locked?(arel)
66
66
  arel, binds = binds_from_relation arel, binds
67
67
  sql = to_sql(arel, binds)
68
- cache_sql(sql, binds) { super(sql, name, binds) }
68
+ cache_sql(sql, binds) { super(sql, name, binds, preparable: preparable) }
69
69
  else
70
70
  super
71
71
  end
@@ -10,7 +10,13 @@ module ActiveRecord
10
10
  return value.quoted_id if value.respond_to?(:quoted_id)
11
11
 
12
12
  if column
13
- value = column.cast_type.type_cast_for_database(value)
13
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
14
+ Passing a column to `quote` has been deprecated. It is only used
15
+ for type casting, which should be handled elsewhere. See
16
+ https://github.com/rails/arel/commit/6160bfbda1d1781c3b08a33ec4955f170e95be11
17
+ for more information.
18
+ MSG
19
+ value = type_cast_from_column(column, value)
14
20
  end
15
21
 
16
22
  _quote(value)
@@ -19,13 +25,13 @@ module ActiveRecord
19
25
  # Cast a +value+ to a type that the database understands. For example,
20
26
  # SQLite does not understand dates, so this method will convert a Date
21
27
  # to a String.
22
- def type_cast(value, column)
28
+ def type_cast(value, column = nil)
23
29
  if value.respond_to?(:quoted_id) && value.respond_to?(:id)
24
30
  return value.id
25
31
  end
26
32
 
27
33
  if column
28
- value = column.cast_type.type_cast_for_database(value)
34
+ value = type_cast_from_column(column, value)
29
35
  end
30
36
 
31
37
  _type_cast(value)
@@ -34,15 +40,49 @@ module ActiveRecord
34
40
  raise TypeError, "can't cast #{value.class}#{to_type}"
35
41
  end
36
42
 
43
+ # If you are having to call this function, you are likely doing something
44
+ # wrong. The column does not have sufficient type information if the user
45
+ # provided a custom type on the class level either explicitly (via
46
+ # Attributes::ClassMethods#attribute) or implicitly (via
47
+ # AttributeMethods::Serialization::ClassMethods#serialize, +time_zone_aware_attributes+).
48
+ # In almost all cases, the sql type should only be used to change quoting behavior, when the primitive to
49
+ # represent the type doesn't sufficiently reflect the differences
50
+ # (varchar vs binary) for example. The type used to get this primitive
51
+ # should have been provided before reaching the connection adapter.
52
+ def type_cast_from_column(column, value) # :nodoc:
53
+ if column
54
+ type = lookup_cast_type_from_column(column)
55
+ type.serialize(value)
56
+ else
57
+ value
58
+ end
59
+ end
60
+
61
+ # See docs for #type_cast_from_column
62
+ def lookup_cast_type_from_column(column) # :nodoc:
63
+ lookup_cast_type(column.sql_type)
64
+ end
65
+
66
+ def fetch_type_metadata(sql_type)
67
+ cast_type = lookup_cast_type(sql_type)
68
+ SqlTypeMetadata.new(
69
+ sql_type: sql_type,
70
+ type: cast_type.type,
71
+ limit: cast_type.limit,
72
+ precision: cast_type.precision,
73
+ scale: cast_type.scale,
74
+ )
75
+ end
76
+
37
77
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
38
78
  # characters.
39
79
  def quote_string(s)
40
- s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
80
+ s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
41
81
  end
42
82
 
43
83
  # Quotes the column name. Defaults to no quoting.
44
84
  def quote_column_name(column_name)
45
- column_name
85
+ column_name.to_s
46
86
  end
47
87
 
48
88
  # Quotes the table name. Defaults to column name quoting.
@@ -53,7 +93,7 @@ module ActiveRecord
53
93
  # Override to return the quoted table name for assignment. Defaults to
54
94
  # table quoting.
55
95
  #
56
- # This works for mysql and mysql2 where table.column can be used to
96
+ # This works for mysql2 where table.column can be used to
57
97
  # resolve ambiguity.
58
98
  #
59
99
  # We override this in the sqlite3 and postgresql adapters to use only
@@ -62,6 +102,15 @@ module ActiveRecord
62
102
  quote_table_name("#{table}.#{attr}")
63
103
  end
64
104
 
105
+ def quote_default_expression(value, column) # :nodoc:
106
+ if value.is_a?(Proc)
107
+ value.call
108
+ else
109
+ value = lookup_cast_type(column.sql_type).serialize(value)
110
+ quote(value)
111
+ end
112
+ end
113
+
65
114
  def quoted_true
66
115
  "'t'"
67
116
  end
@@ -78,6 +127,8 @@ module ActiveRecord
78
127
  'f'
79
128
  end
80
129
 
130
+ # Quote date/time values for use in SQL input. Includes microseconds
131
+ # if the value is a Time responding to usec.
81
132
  def quoted_date(value)
82
133
  if value.acts_like?(:time)
83
134
  zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -87,7 +138,20 @@ module ActiveRecord
87
138
  end
88
139
  end
89
140
 
90
- value.to_s(:db)
141
+ result = value.to_s(:db)
142
+ if value.respond_to?(:usec) && value.usec > 0
143
+ "#{result}.#{sprintf("%06d", value.usec)}"
144
+ else
145
+ result
146
+ end
147
+ end
148
+
149
+ def quoted_time(value) # :nodoc:
150
+ quoted_date(value).sub(/\A2000-01-01 /, '')
151
+ end
152
+
153
+ def prepare_binds_for_database(binds) # :nodoc:
154
+ binds.map(&:value_for_database)
91
155
  end
92
156
 
93
157
  private
@@ -106,11 +170,11 @@ module ActiveRecord
106
170
  # BigDecimals need to be put in a non-normalized form and quoted.
107
171
  when BigDecimal then value.to_s('F')
108
172
  when Numeric, ActiveSupport::Duration then value.to_s
173
+ when Type::Time::Value then "'#{quoted_time(value)}'"
109
174
  when Date, Time then "'#{quoted_date(value)}'"
110
175
  when Symbol then "'#{quote_string(value.to_s)}'"
111
176
  when Class then "'#{value}'"
112
- else
113
- "'#{quote_string(YAML.dump(value))}'"
177
+ else raise TypeError, "can't quote #{value.class.name}"
114
178
  end
115
179
  end
116
180
 
@@ -122,6 +186,7 @@ module ActiveRecord
122
186
  when false then unquoted_false
123
187
  # BigDecimals need to be put in a non-normalized form and quoted.
124
188
  when BigDecimal then value.to_s('F')
189
+ when Type::Time::Value then quoted_time(value)
125
190
  when Date, Time then quoted_date(value)
126
191
  when *types_which_need_no_typecasting
127
192
  value
@@ -1,15 +1,15 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
- module Savepoints #:nodoc:
4
- def supports_savepoints?
5
- true
3
+ module Savepoints
4
+ def current_savepoint_name
5
+ current_transaction.savepoint_name
6
6
  end
7
7
 
8
8
  def create_savepoint(name = current_savepoint_name)
9
9
  execute("SAVEPOINT #{name}")
10
10
  end
11
11
 
12
- def rollback_to_savepoint(name = current_savepoint_name)
12
+ def exec_rollback_to_savepoint(name = current_savepoint_name)
13
13
  execute("ROLLBACK TO SAVEPOINT #{name}")
14
14
  end
15
15
 
@@ -14,38 +14,58 @@ module ActiveRecord
14
14
  send m, o
15
15
  end
16
16
 
17
- def visit_AddColumn(o)
18
- "ADD #{accept(o)}"
19
- end
17
+ delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
18
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options, to: :@conn
19
+ private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
20
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options
20
21
 
21
22
  private
22
23
 
23
24
  def visit_AlterTable(o)
24
25
  sql = "ALTER TABLE #{quote_table_name(o.name)} "
25
- sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
26
+ sql << o.adds.map { |col| accept col }.join(' ')
26
27
  sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(' ')
27
28
  sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(' ')
28
29
  end
29
30
 
30
31
  def visit_ColumnDefinition(o)
31
- sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
32
- column_sql = "#{quote_column_name(o.name)} #{sql_type}"
33
- add_column_options!(column_sql, column_options(o)) unless o.primary_key?
32
+ o.sql_type ||= type_to_sql(o.type, o.limit, o.precision, o.scale)
33
+ column_sql = "#{quote_column_name(o.name)} #{o.sql_type}"
34
+ add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
34
35
  column_sql
35
36
  end
36
37
 
38
+ def visit_AddColumnDefinition(o)
39
+ "ADD #{accept(o.column)}"
40
+ end
41
+
37
42
  def visit_TableDefinition(o)
38
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
39
- create_sql << "#{quote_table_name(o.name)} "
40
- create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
41
- create_sql << "#{o.options}"
43
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} "
44
+
45
+ statements = o.columns.map { |c| accept c }
46
+ statements << accept(o.primary_keys) if o.primary_keys
47
+
48
+ if supports_indexes_in_create?
49
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
50
+ end
51
+
52
+ if supports_foreign_keys?
53
+ statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
54
+ end
55
+
56
+ create_sql << "(#{statements.join(', ')})" if statements.present?
57
+ add_table_options!(create_sql, table_options(o))
42
58
  create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
43
59
  create_sql
44
60
  end
45
61
 
46
- def visit_AddForeignKey(o)
62
+ def visit_PrimaryKeyDefinition(o)
63
+ "PRIMARY KEY (#{o.name.join(', ')})"
64
+ end
65
+
66
+ def visit_ForeignKeyDefinition(o)
47
67
  sql = <<-SQL.strip_heredoc
48
- ADD CONSTRAINT #{quote_column_name(o.name)}
68
+ CONSTRAINT #{quote_column_name(o.name)}
49
69
  FOREIGN KEY (#{quote_column_name(o.column)})
50
70
  REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
51
71
  SQL
@@ -54,10 +74,27 @@ module ActiveRecord
54
74
  sql
55
75
  end
56
76
 
77
+ def visit_AddForeignKey(o)
78
+ "ADD #{accept(o)}"
79
+ end
80
+
57
81
  def visit_DropForeignKey(name)
58
82
  "DROP CONSTRAINT #{quote_column_name(name)}"
59
83
  end
60
84
 
85
+ def table_options(o)
86
+ table_options = {}
87
+ table_options[:comment] = o.comment
88
+ table_options[:options] = o.options
89
+ table_options
90
+ end
91
+
92
+ def add_table_options!(create_sql, options)
93
+ if options_sql = options[:options]
94
+ create_sql << " #{options_sql}"
95
+ end
96
+ end
97
+
61
98
  def column_options(o)
62
99
  column_options = {}
63
100
  column_options[:null] = o.null unless o.null.nil?
@@ -65,23 +102,15 @@ module ActiveRecord
65
102
  column_options[:column] = o
66
103
  column_options[:first] = o.first
67
104
  column_options[:after] = o.after
105
+ column_options[:auto_increment] = o.auto_increment
106
+ column_options[:primary_key] = o.primary_key
107
+ column_options[:collation] = o.collation
108
+ column_options[:comment] = o.comment
68
109
  column_options
69
110
  end
70
111
 
71
- def quote_column_name(name)
72
- @conn.quote_column_name name
73
- end
74
-
75
- def quote_table_name(name)
76
- @conn.quote_table_name name
77
- end
78
-
79
- def type_to_sql(type, limit, precision, scale)
80
- @conn.type_to_sql type.to_sym, limit, precision, scale
81
- end
82
-
83
112
  def add_column_options!(sql, options)
84
- sql << " DEFAULT #{quote_value(options[:default], options[:column])}" if options_include_default?(options)
113
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
85
114
  # must explicitly check for :null to allow change_column to work on migrations
86
115
  if options[:null] == false
87
116
  sql << " NOT NULL"
@@ -89,18 +118,15 @@ module ActiveRecord
89
118
  if options[:auto_increment] == true
90
119
  sql << " AUTO_INCREMENT"
91
120
  end
121
+ if options[:primary_key] == true
122
+ sql << " PRIMARY KEY"
123
+ end
92
124
  sql
93
125
  end
94
126
 
95
- def quote_value(value, column)
96
- column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
97
- column.cast_type ||= type_for_column(column)
98
-
99
- @conn.quote(value, column)
100
- end
101
-
102
- def options_include_default?(options)
103
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
127
+ def foreign_key_in_create(from_table, to_table, options)
128
+ options = foreign_key_options(from_table, to_table, options)
129
+ accept ForeignKeyDefinition.new(from_table, to_table, options)
104
130
  end
105
131
 
106
132
  def action_sql(action, dependency)
@@ -115,10 +141,6 @@ module ActiveRecord
115
141
  MSG
116
142
  end
117
143
  end
118
-
119
- def type_for_column(column)
120
- @conn.lookup_cast_type(column.sql_type)
121
- end
122
144
  end
123
145
  end
124
146
  end