activerecord 4.2.11.3 → 5.0.0.1

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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1281 -1204
  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 +35 -24
  8. data/lib/active_record/association_relation.rb +3 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +11 -9
  11. data/lib/active_record/associations/association_scope.rb +73 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  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 +7 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  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 +49 -41
  21. data/lib/active_record/associations/collection_proxy.rb +67 -27
  22. data/lib/active_record/associations/foreign_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_association.rb +20 -71
  24. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  25. data/lib/active_record/associations/has_one_association.rb +12 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  27. data/lib/active_record/associations/join_dependency.rb +29 -19
  28. data/lib/active_record/associations/preloader/association.rb +46 -52
  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 +14 -4
  34. data/lib/active_record/associations/singular_association.rb +7 -1
  35. data/lib/active_record/associations/through_association.rb +11 -3
  36. data/lib/active_record/associations.rb +317 -209
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute_assignment.rb +19 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  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 +61 -14
  48. data/lib/active_record/attribute_methods/write.rb +13 -37
  49. data/lib/active_record/attribute_methods.rb +76 -47
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -81
  54. data/lib/active_record/autosave_association.rb +49 -16
  55. data/lib/active_record/base.rb +32 -23
  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 +452 -182
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -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 +380 -141
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  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 +29 -166
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  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 -57
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -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 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  100. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  105. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  106. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  107. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  108. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  110. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  111. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  112. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  113. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  114. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  115. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  117. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  118. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  119. data/lib/active_record/connection_handling.rb +37 -14
  120. data/lib/active_record/core.rb +89 -107
  121. data/lib/active_record/counter_cache.rb +13 -24
  122. data/lib/active_record/dynamic_matchers.rb +1 -20
  123. data/lib/active_record/enum.rb +113 -76
  124. data/lib/active_record/errors.rb +87 -48
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +26 -5
  128. data/lib/active_record/fixtures.rb +76 -40
  129. data/lib/active_record/gem_version.rb +4 -4
  130. data/lib/active_record/inheritance.rb +32 -40
  131. data/lib/active_record/integration.rb +4 -4
  132. data/lib/active_record/internal_metadata.rb +56 -0
  133. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  134. data/lib/active_record/locale/en.yml +3 -2
  135. data/lib/active_record/locking/optimistic.rb +15 -15
  136. data/lib/active_record/locking/pessimistic.rb +1 -1
  137. data/lib/active_record/log_subscriber.rb +43 -21
  138. data/lib/active_record/migration/command_recorder.rb +59 -18
  139. data/lib/active_record/migration/compatibility.rb +126 -0
  140. data/lib/active_record/migration.rb +363 -133
  141. data/lib/active_record/model_schema.rb +129 -41
  142. data/lib/active_record/nested_attributes.rb +58 -29
  143. data/lib/active_record/null_relation.rb +16 -8
  144. data/lib/active_record/persistence.rb +121 -80
  145. data/lib/active_record/query_cache.rb +15 -18
  146. data/lib/active_record/querying.rb +10 -9
  147. data/lib/active_record/railtie.rb +23 -16
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +69 -46
  150. data/lib/active_record/readonly_attributes.rb +1 -1
  151. data/lib/active_record/reflection.rb +282 -115
  152. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  153. data/lib/active_record/relation/batches.rb +139 -34
  154. data/lib/active_record/relation/calculations.rb +79 -108
  155. data/lib/active_record/relation/delegation.rb +7 -20
  156. data/lib/active_record/relation/finder_methods.rb +163 -81
  157. data/lib/active_record/relation/from_clause.rb +32 -0
  158. data/lib/active_record/relation/merger.rb +16 -42
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  160. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  163. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  166. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  167. data/lib/active_record/relation/predicate_builder.rb +120 -107
  168. data/lib/active_record/relation/query_attribute.rb +19 -0
  169. data/lib/active_record/relation/query_methods.rb +308 -244
  170. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  171. data/lib/active_record/relation/spawn_methods.rb +4 -7
  172. data/lib/active_record/relation/where_clause.rb +174 -0
  173. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  174. data/lib/active_record/relation.rb +176 -116
  175. data/lib/active_record/result.rb +4 -3
  176. data/lib/active_record/runtime_registry.rb +1 -1
  177. data/lib/active_record/sanitization.rb +95 -66
  178. data/lib/active_record/schema.rb +26 -22
  179. data/lib/active_record/schema_dumper.rb +62 -38
  180. data/lib/active_record/schema_migration.rb +11 -14
  181. data/lib/active_record/scoping/default.rb +23 -9
  182. data/lib/active_record/scoping/named.rb +49 -28
  183. data/lib/active_record/scoping.rb +32 -15
  184. data/lib/active_record/secure_token.rb +38 -0
  185. data/lib/active_record/serialization.rb +2 -4
  186. data/lib/active_record/statement_cache.rb +16 -14
  187. data/lib/active_record/store.rb +8 -3
  188. data/lib/active_record/suppressor.rb +58 -0
  189. data/lib/active_record/table_metadata.rb +68 -0
  190. data/lib/active_record/tasks/database_tasks.rb +57 -43
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  193. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  194. data/lib/active_record/timestamp.rb +20 -9
  195. data/lib/active_record/touch_later.rb +58 -0
  196. data/lib/active_record/transactions.rb +138 -56
  197. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  198. data/lib/active_record/type/date.rb +2 -45
  199. data/lib/active_record/type/date_time.rb +2 -49
  200. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  201. data/lib/active_record/type/internal/timezone.rb +15 -0
  202. data/lib/active_record/type/serialized.rb +15 -14
  203. data/lib/active_record/type/time.rb +10 -16
  204. data/lib/active_record/type/type_map.rb +4 -4
  205. data/lib/active_record/type.rb +66 -17
  206. data/lib/active_record/type_caster/connection.rb +29 -0
  207. data/lib/active_record/type_caster/map.rb +19 -0
  208. data/lib/active_record/type_caster.rb +7 -0
  209. data/lib/active_record/validations/absence.rb +23 -0
  210. data/lib/active_record/validations/associated.rb +10 -3
  211. data/lib/active_record/validations/length.rb +24 -0
  212. data/lib/active_record/validations/presence.rb +11 -12
  213. data/lib/active_record/validations/uniqueness.rb +30 -29
  214. data/lib/active_record/validations.rb +33 -32
  215. data/lib/active_record.rb +8 -4
  216. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  217. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  218. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  219. data/lib/rails/generators/active_record/migration.rb +7 -0
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +60 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -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
@@ -272,9 +291,6 @@ module ActiveRecord
272
291
  exec_rollback_to_savepoint(name)
273
292
  end
274
293
 
275
- def exec_rollback_to_savepoint(name = nil) #:nodoc:
276
- end
277
-
278
294
  def default_sequence_name(table, column)
279
295
  nil
280
296
  end
@@ -288,17 +304,24 @@ module ActiveRecord
288
304
  # something beyond a simple insert (eg. Oracle).
289
305
  def insert_fixture(fixture, table_name)
290
306
  fixture = fixture.stringify_keys
291
- columns = schema_cache.columns_hash(table_name)
292
307
 
293
- key_list = []
294
- value_list = fixture.map do |name, value|
308
+ columns = schema_cache.columns_hash(table_name)
309
+ binds = fixture.map do |name, value|
295
310
  if column = columns[name]
296
- key_list << quote_column_name(name)
297
- quote(value, column)
311
+ type = lookup_cast_type_from_column(column)
312
+ Relation::QueryAttribute.new(name, value, type)
298
313
  else
299
314
  raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
300
315
  end
301
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
324
+ end
302
325
 
303
326
  execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
304
327
  end
@@ -329,18 +352,12 @@ module ActiveRecord
329
352
  # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
330
353
  # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
331
354
  # an UPDATE statement, so in the MySQL adapters we redefine this to do that.
332
- def join_to_update(update, select) #:nodoc:
333
- key = update.key
355
+ def join_to_update(update, select, key) # :nodoc:
334
356
  subselect = subquery_for(key, select)
335
357
 
336
358
  update.where key.in(subselect)
337
359
  end
338
-
339
- def join_to_delete(delete, select, key) #:nodoc:
340
- subselect = subquery_for(key, select)
341
-
342
- delete.where key.in(subselect)
343
- end
360
+ alias join_to_delete join_to_update
344
361
 
345
362
  protected
346
363
 
@@ -353,28 +370,15 @@ module ActiveRecord
353
370
 
354
371
  # Returns an ActiveRecord::Result instance.
355
372
  def select(sql, name = nil, binds = [])
356
- exec_query(sql, name, binds)
357
- end
358
-
359
-
360
- # Returns the last auto-generated ID from the affected table.
361
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
362
- execute(sql, name)
363
- id_value
364
- end
365
-
366
- # Executes the update statement and returns the number of rows affected.
367
- def update_sql(sql, name = nil)
368
- execute(sql, name)
373
+ exec_query(sql, name, binds, prepare: false)
369
374
  end
370
375
 
371
- # Executes the delete statement and returns the number of rows affected.
372
- def delete_sql(sql, name = nil)
373
- update_sql(sql, name)
376
+ def select_prepared(sql, name = nil, binds = [])
377
+ exec_query(sql, name, binds, prepare: true)
374
378
  end
375
379
 
376
380
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
377
- [sql, binds]
381
+ [sql, binds, pk, sequence_name]
378
382
  end
379
383
 
380
384
  def last_inserted_id(result)
@@ -384,7 +388,7 @@ module ActiveRecord
384
388
 
385
389
  def binds_from_relation(relation, binds)
386
390
  if relation.is_a?(Relation) && binds.empty?
387
- relation, binds = relation.arel, relation.bind_values
391
+ relation, binds = relation.arel, relation.bound_attributes
388
392
  end
389
393
  [relation, binds]
390
394
  end
@@ -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
@@ -1,5 +1,4 @@
1
1
  require 'active_support/core_ext/big_decimal/conversions'
2
- require "active_support/multibyte/chars"
3
2
 
4
3
  module ActiveRecord
5
4
  module ConnectionAdapters # :nodoc:
@@ -11,7 +10,13 @@ module ActiveRecord
11
10
  return value.quoted_id if value.respond_to?(:quoted_id)
12
11
 
13
12
  if column
14
- 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)
15
20
  end
16
21
 
17
22
  _quote(value)
@@ -20,13 +25,13 @@ module ActiveRecord
20
25
  # Cast a +value+ to a type that the database understands. For example,
21
26
  # SQLite does not understand dates, so this method will convert a Date
22
27
  # to a String.
23
- def type_cast(value, column)
28
+ def type_cast(value, column = nil)
24
29
  if value.respond_to?(:quoted_id) && value.respond_to?(:id)
25
30
  return value.id
26
31
  end
27
32
 
28
33
  if column
29
- value = column.cast_type.type_cast_for_database(value)
34
+ value = type_cast_from_column(column, value)
30
35
  end
31
36
 
32
37
  _type_cast(value)
@@ -35,15 +40,49 @@ module ActiveRecord
35
40
  raise TypeError, "can't cast #{value.class}#{to_type}"
36
41
  end
37
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
+
38
77
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
39
78
  # characters.
40
79
  def quote_string(s)
41
- s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
80
+ s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
42
81
  end
43
82
 
44
83
  # Quotes the column name. Defaults to no quoting.
45
84
  def quote_column_name(column_name)
46
- column_name
85
+ column_name.to_s
47
86
  end
48
87
 
49
88
  # Quotes the table name. Defaults to column name quoting.
@@ -54,7 +93,7 @@ module ActiveRecord
54
93
  # Override to return the quoted table name for assignment. Defaults to
55
94
  # table quoting.
56
95
  #
57
- # 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
58
97
  # resolve ambiguity.
59
98
  #
60
99
  # We override this in the sqlite3 and postgresql adapters to use only
@@ -63,6 +102,15 @@ module ActiveRecord
63
102
  quote_table_name("#{table}.#{attr}")
64
103
  end
65
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
+
66
114
  def quoted_true
67
115
  "'t'"
68
116
  end
@@ -79,6 +127,8 @@ module ActiveRecord
79
127
  'f'
80
128
  end
81
129
 
130
+ # Quote date/time values for use in SQL input. Includes microseconds
131
+ # if the value is a Time responding to usec.
82
132
  def quoted_date(value)
83
133
  if value.acts_like?(:time)
84
134
  zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -88,7 +138,20 @@ module ActiveRecord
88
138
  end
89
139
  end
90
140
 
91
- 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)
92
155
  end
93
156
 
94
157
  private
@@ -107,11 +170,11 @@ module ActiveRecord
107
170
  # BigDecimals need to be put in a non-normalized form and quoted.
108
171
  when BigDecimal then value.to_s('F')
109
172
  when Numeric, ActiveSupport::Duration then value.to_s
173
+ when Type::Time::Value then "'#{quoted_time(value)}'"
110
174
  when Date, Time then "'#{quoted_date(value)}'"
111
175
  when Symbol then "'#{quote_string(value.to_s)}'"
112
176
  when Class then "'#{value}'"
113
- else
114
- "'#{quote_string(YAML.dump(value))}'"
177
+ else raise TypeError, "can't quote #{value.class.name}"
115
178
  end
116
179
  end
117
180
 
@@ -123,6 +186,7 @@ module ActiveRecord
123
186
  when false then unquoted_false
124
187
  # BigDecimals need to be put in a non-normalized form and quoted.
125
188
  when BigDecimal then value.to_s('F')
189
+ when Type::Time::Value then quoted_time(value)
126
190
  when Date, Time then quoted_date(value)
127
191
  when *types_which_need_no_typecasting
128
192
  value
@@ -1,8 +1,8 @@
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)
@@ -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