activerecord 4.2.11.3 → 5.0.0.beta1

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 (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. 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
@@ -29,7 +29,17 @@ module ActiveRecord
29
29
  # Returns an ActiveRecord::Result instance.
30
30
  def select_all(arel, name = nil, binds = [])
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 arel.is_a?(String)
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,8 +50,9 @@ 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
 
@@ -66,7 +77,7 @@ module ActiveRecord
66
77
  # Executes +sql+ statement in the context of this connection using
67
78
  # +binds+ as the bind substitutes. +name+ is logged along with
68
79
  # the executed +sql+ statement.
69
- def exec_query(sql, name = 'SQL', binds = [])
80
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
70
81
  end
71
82
 
72
83
  # Executes insert +sql+ statement in the context of this connection using
@@ -136,7 +147,7 @@ module ActiveRecord
136
147
  #
137
148
  # In order to get around this problem, #transaction will emulate the effect
138
149
  # of nested transactions, by using savepoints:
139
- # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
150
+ # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
140
151
  # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
141
152
  # supports savepoints.
142
153
  #
@@ -188,10 +199,10 @@ module ActiveRecord
188
199
  # You should consult the documentation for your database to understand the
189
200
  # semantics of these different levels:
190
201
  #
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
202
+ # * http://www.postgresql.org/docs/current/static/transaction-iso.html
203
+ # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
193
204
  #
194
- # An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
205
+ # An ActiveRecord::TransactionIsolationError will be raised if:
195
206
  #
196
207
  # * The adapter does not support setting the isolation level
197
208
  # * You are joining an existing open transaction
@@ -201,16 +212,14 @@ module ActiveRecord
201
212
  # isolation level. However, support is disabled for MySQL versions below 5,
202
213
  # because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
203
214
  # 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]
215
+ def transaction(requires_new: nil, isolation: nil, joinable: true)
216
+ if !requires_new && current_transaction.joinable?
217
+ if isolation
209
218
  raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
210
219
  end
211
220
  yield
212
221
  else
213
- transaction_manager.within_new_transaction(options) { yield }
222
+ transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
214
223
  end
215
224
  rescue ActiveRecord::Rollback
216
225
  # rollbacks are silently swallowed
@@ -287,16 +296,22 @@ module ActiveRecord
287
296
  # Inserts the given fixture into the table. Overridden in adapters that require
288
297
  # something beyond a simple insert (eg. Oracle).
289
298
  def insert_fixture(fixture, table_name)
290
- fixture = fixture.stringify_keys
291
299
  columns = schema_cache.columns_hash(table_name)
292
300
 
293
- key_list = []
294
- value_list = fixture.map do |name, value|
301
+ binds = fixture.map do |name, value|
295
302
  if column = columns[name]
296
- key_list << quote_column_name(name)
297
- quote(value, column)
303
+ type = lookup_cast_type_from_column(column)
304
+ Relation::QueryAttribute.new(name, value, type)
298
305
  else
299
- raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
306
+ raise Fixture::FixtureError, %(table "#{table_name}" has no column named "#{name}".)
307
+ end
308
+ end
309
+ key_list = fixture.keys.map { |name| quote_column_name(name) }
310
+ value_list = prepare_binds_for_database(binds).map do |value|
311
+ begin
312
+ quote(value)
313
+ rescue TypeError
314
+ quote(YAML.dump(value))
300
315
  end
301
316
  end
302
317
 
@@ -353,9 +368,12 @@ module ActiveRecord
353
368
 
354
369
  # Returns an ActiveRecord::Result instance.
355
370
  def select(sql, name = nil, binds = [])
356
- exec_query(sql, name, binds)
371
+ exec_query(sql, name, binds, prepare: false)
357
372
  end
358
373
 
374
+ def select_prepared(sql, name = nil, binds = [])
375
+ exec_query(sql, name, binds, prepare: true)
376
+ end
359
377
 
360
378
  # Returns the last auto-generated ID from the affected table.
361
379
  def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
@@ -384,7 +402,7 @@ module ActiveRecord
384
402
 
385
403
  def binds_from_relation(relation, binds)
386
404
  if relation.is_a?(Relation) && binds.empty?
387
- relation, binds = relation.arel, relation.bind_values
405
+ relation, binds = relation.arel, relation.bound_attributes
388
406
  end
389
407
  [relation, binds]
390
408
  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,10 +40,44 @@ 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.
@@ -63,6 +102,11 @@ module ActiveRecord
63
102
  quote_table_name("#{table}.#{attr}")
64
103
  end
65
104
 
105
+ def quote_default_expression(value, column) #:nodoc:
106
+ value = lookup_cast_type(column.sql_type).serialize(value)
107
+ quote(value)
108
+ end
109
+
66
110
  def quoted_true
67
111
  "'t'"
68
112
  end
@@ -79,6 +123,8 @@ module ActiveRecord
79
123
  'f'
80
124
  end
81
125
 
126
+ # Quote date/time values for use in SQL input. Includes microseconds
127
+ # if the value is a Time responding to usec.
82
128
  def quoted_date(value)
83
129
  if value.acts_like?(:time)
84
130
  zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -88,7 +134,16 @@ module ActiveRecord
88
134
  end
89
135
  end
90
136
 
91
- value.to_s(:db)
137
+ result = value.to_s(:db)
138
+ if value.respond_to?(:usec) && value.usec > 0
139
+ "#{result}.#{sprintf("%06d", value.usec)}"
140
+ else
141
+ result
142
+ end
143
+ end
144
+
145
+ def prepare_binds_for_database(binds) # :nodoc:
146
+ binds.map(&:value_for_database)
92
147
  end
93
148
 
94
149
  private
@@ -110,8 +165,7 @@ module ActiveRecord
110
165
  when Date, Time then "'#{quoted_date(value)}'"
111
166
  when Symbol then "'#{quote_string(value.to_s)}'"
112
167
  when Class then "'#{value}'"
113
- else
114
- "'#{quote_string(YAML.dump(value))}'"
168
+ else raise TypeError, "can't quote #{value.class.name}"
115
169
  end
116
170
  end
117
171
 
@@ -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
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?
41
57
  create_sql << "#{o.options}"
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,6 +74,10 @@ 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
@@ -65,23 +89,14 @@ module ActiveRecord
65
89
  column_options[:column] = o
66
90
  column_options[:first] = o.first
67
91
  column_options[:after] = o.after
92
+ column_options[:auto_increment] = o.auto_increment
93
+ column_options[:primary_key] = o.primary_key
94
+ column_options[:collation] = o.collation
68
95
  column_options
69
96
  end
70
97
 
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
98
  def add_column_options!(sql, options)
84
- sql << " DEFAULT #{quote_value(options[:default], options[:column])}" if options_include_default?(options)
99
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
85
100
  # must explicitly check for :null to allow change_column to work on migrations
86
101
  if options[:null] == false
87
102
  sql << " NOT NULL"
@@ -89,18 +104,15 @@ module ActiveRecord
89
104
  if options[:auto_increment] == true
90
105
  sql << " AUTO_INCREMENT"
91
106
  end
107
+ if options[:primary_key] == true
108
+ sql << " PRIMARY KEY"
109
+ end
92
110
  sql
93
111
  end
94
112
 
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?)
113
+ def foreign_key_in_create(from_table, to_table, options)
114
+ options = foreign_key_options(from_table, to_table, options)
115
+ accept ForeignKeyDefinition.new(from_table, to_table, options)
104
116
  end
105
117
 
106
118
  def action_sql(action, dependency)
@@ -115,10 +127,6 @@ module ActiveRecord
115
127
  MSG
116
128
  end
117
129
  end
118
-
119
- def type_for_column(column)
120
- @conn.lookup_cast_type(column.sql_type)
121
- end
122
130
  end
123
131
  end
124
132
  end
@@ -1,8 +1,3 @@
1
- require 'date'
2
- require 'set'
3
- require 'bigdecimal'
4
- require 'bigdecimal/util'
5
-
6
1
  module ActiveRecord
7
2
  module ConnectionAdapters #:nodoc:
8
3
  # Abstract representation of an index definition on a table. Instances of
@@ -15,14 +10,20 @@ module ActiveRecord
15
10
  # are typically created by methods in TableDefinition, and added to the
16
11
  # +columns+ attribute of said TableDefinition object, in order to be used
17
12
  # for generating a number of table creation or table changing SQL statements.
18
- class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type, :cast_type) #:nodoc:
13
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type) #:nodoc:
19
14
 
20
15
  def primary_key?
21
16
  primary_key || type.to_sym == :primary_key
22
17
  end
23
18
  end
24
19
 
25
- class ChangeColumnDefinition < Struct.new(:column, :type, :options) #:nodoc:
20
+ class AddColumnDefinition < Struct.new(:column) # :nodoc:
21
+ end
22
+
23
+ class ChangeColumnDefinition < Struct.new(:column, :name) #:nodoc:
24
+ end
25
+
26
+ class PrimaryKeyDefinition < Struct.new(:name) # :nodoc:
26
27
  end
27
28
 
28
29
  class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
@@ -50,31 +51,146 @@ module ActiveRecord
50
51
  options[:primary_key] != default_primary_key
51
52
  end
52
53
 
54
+ def defined_for?(options_or_to_table = {})
55
+ if options_or_to_table.is_a?(Hash)
56
+ options_or_to_table.all? {|key, value| options[key].to_s == value.to_s }
57
+ else
58
+ to_table == options_or_to_table.to_s
59
+ end
60
+ end
61
+
53
62
  private
54
63
  def default_primary_key
55
64
  "id"
56
65
  end
57
66
  end
58
67
 
59
- module TimestampDefaultDeprecation # :nodoc:
60
- def emit_warning_if_null_unspecified(sym, options)
61
- return if options.key?(:null)
68
+ class ReferenceDefinition # :nodoc:
69
+ def initialize(
70
+ name,
71
+ polymorphic: false,
72
+ index: false,
73
+ foreign_key: false,
74
+ type: :integer,
75
+ **options
76
+ )
77
+ @name = name
78
+ @polymorphic = polymorphic
79
+ @index = index
80
+ @foreign_key = foreign_key
81
+ @type = type
82
+ @options = options
83
+
84
+ if polymorphic && foreign_key
85
+ raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
86
+ end
87
+ end
88
+
89
+ def add_to(table)
90
+ columns.each do |column_options|
91
+ table.column(*column_options)
92
+ end
93
+
94
+ if index
95
+ table.index(column_names, index_options)
96
+ end
97
+
98
+ if foreign_key
99
+ table.foreign_key(foreign_table_name, foreign_key_options)
100
+ end
101
+ end
102
+
103
+ protected
104
+
105
+ attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
106
+
107
+ private
108
+
109
+ def as_options(value, default = {})
110
+ if value.is_a?(Hash)
111
+ value
112
+ else
113
+ default
114
+ end
115
+ end
116
+
117
+ def polymorphic_options
118
+ as_options(polymorphic, options)
119
+ end
120
+
121
+ def index_options
122
+ as_options(index)
123
+ end
124
+
125
+ def foreign_key_options
126
+ as_options(foreign_key).merge(column: column_name)
127
+ end
128
+
129
+ def columns
130
+ result = [[column_name, type, options]]
131
+ if polymorphic
132
+ result.unshift(["#{name}_type", :string, polymorphic_options])
133
+ end
134
+ result
135
+ end
136
+
137
+ def column_name
138
+ "#{name}_id"
139
+ end
140
+
141
+ def column_names
142
+ columns.map(&:first)
143
+ end
144
+
145
+ def foreign_table_name
146
+ foreign_key_options.fetch(:to_table) do
147
+ Base.pluralize_table_names ? name.to_s.pluralize : name
148
+ end
149
+ end
150
+ end
151
+
152
+ module ColumnMethods
153
+ # Appends a primary key definition to the table definition.
154
+ # Can be called multiple times, but this is probably not a good idea.
155
+ def primary_key(name, type = :primary_key, **options)
156
+ column(name, type, options.merge(primary_key: true))
157
+ end
62
158
 
63
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
64
- `##{sym}` was called without specifying an option for `null`. In Rails 5,
65
- this behavior will change to `null: false`. You should manually specify
66
- `null: true` to prevent the behavior of your existing migrations from changing.
67
- MSG
159
+ # Appends a column or columns of a specified type.
160
+ #
161
+ # t.string(:goat)
162
+ # t.string(:goat, :sheep)
163
+ #
164
+ # See TableDefinition#column
165
+ [
166
+ :bigint,
167
+ :binary,
168
+ :boolean,
169
+ :date,
170
+ :datetime,
171
+ :decimal,
172
+ :float,
173
+ :integer,
174
+ :string,
175
+ :text,
176
+ :time,
177
+ :timestamp,
178
+ ].each do |column_type|
179
+ module_eval <<-CODE, __FILE__, __LINE__ + 1
180
+ def #{column_type}(*args, **options)
181
+ args.each { |name| column(name, :#{column_type}, options) }
182
+ end
183
+ CODE
68
184
  end
69
185
  end
70
186
 
71
187
  # Represents the schema of an SQL table in an abstract way. This class
72
188
  # provides methods for manipulating the schema representation.
73
189
  #
74
- # Inside migration files, the +t+ object in +create_table+
190
+ # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
75
191
  # is actually of this type:
76
192
  #
77
- # class SomeMigration < ActiveRecord::Migration
193
+ # class SomeMigration < ActiveRecord::Migration[5.0]
78
194
  # def up
79
195
  # create_table :foo do |t|
80
196
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -86,125 +202,54 @@ module ActiveRecord
86
202
  # end
87
203
  # end
88
204
  #
89
- # The table definitions
90
- # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
91
205
  class TableDefinition
92
- include TimestampDefaultDeprecation
206
+ include ColumnMethods
93
207
 
94
- # An array of ColumnDefinition objects, representing the column changes
95
- # that have been defined.
96
208
  attr_accessor :indexes
97
209
  attr_reader :name, :temporary, :options, :as, :foreign_keys
98
210
 
99
- def initialize(types, name, temporary, options, as = nil)
211
+ def initialize(name, temporary, options, as = nil)
100
212
  @columns_hash = {}
101
213
  @indexes = {}
102
- @foreign_keys = []
103
- @native = types
214
+ @foreign_keys = {}
215
+ @primary_keys = nil
104
216
  @temporary = temporary
105
217
  @options = options
106
218
  @as = as
107
219
  @name = name
108
220
  end
109
221
 
110
- def columns; @columns_hash.values; end
111
-
112
- # Appends a primary key definition to the table definition.
113
- # Can be called multiple times, but this is probably not a good idea.
114
- def primary_key(name, type = :primary_key, options = {})
115
- column(name, type, options.merge(:primary_key => true))
222
+ def primary_keys(name = nil) # :nodoc:
223
+ @primary_keys = PrimaryKeyDefinition.new(name) if name
224
+ @primary_keys
116
225
  end
117
226
 
227
+ # Returns an array of ColumnDefinition objects for the columns of the table.
228
+ def columns; @columns_hash.values; end
229
+
118
230
  # Returns a ColumnDefinition for the column with name +name+.
119
231
  def [](name)
120
232
  @columns_hash[name.to_s]
121
233
  end
122
234
 
123
235
  # Instantiates a new column for the table.
124
- # The +type+ parameter is normally one of the migrations native types,
125
- # which is one of the following:
126
- # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
127
- # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
128
- # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
129
- # <tt>:binary</tt>, <tt>:boolean</tt>.
130
- #
131
- # You may use a type not in this list as long as it is supported by your
132
- # database (for example, "polygon" in MySQL), but this will not be database
133
- # agnostic and should usually be avoided.
134
- #
135
- # Available options are (none of these exists by default):
136
- # * <tt>:limit</tt> -
137
- # Requests a maximum column length. This is number of characters for <tt>:string</tt> and
138
- # <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
139
- # * <tt>:default</tt> -
140
- # The column's default value. Use nil for NULL.
141
- # * <tt>:null</tt> -
142
- # Allows or disallows +NULL+ values in the column. This option could
143
- # have been named <tt>:null_allowed</tt>.
144
- # * <tt>:precision</tt> -
145
- # Specifies the precision for a <tt>:decimal</tt> column.
146
- # * <tt>:scale</tt> -
147
- # Specifies the scale for a <tt>:decimal</tt> column.
236
+ # See {connection.add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column]
237
+ # for available options.
238
+ #
239
+ # Additional options are:
148
240
  # * <tt>:index</tt> -
149
241
  # Create an index for the column. Can be either <tt>true</tt> or an options hash.
150
242
  #
151
- # Note: The precision is the total number of significant digits
152
- # and the scale is the number of digits that can be stored following
153
- # the decimal point. For example, the number 123.45 has a precision of 5
154
- # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
155
- # range from -999.99 to 999.99.
156
- #
157
- # Please be aware of different RDBMS implementations behavior with
158
- # <tt>:decimal</tt> columns:
159
- # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
160
- # <tt>:precision</tt>, and makes no comments about the requirements of
161
- # <tt>:precision</tt>.
162
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
163
- # Default is (10,0).
164
- # * PostgreSQL: <tt>:precision</tt> [1..infinity],
165
- # <tt>:scale</tt> [0..infinity]. No default.
166
- # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
167
- # Internal storage as strings. No default.
168
- # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
169
- # but the maximum supported <tt>:precision</tt> is 16. No default.
170
- # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
171
- # Default is (38,0).
172
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
173
- # Default unknown.
174
- # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
175
- # Default (38,0).
176
- #
177
243
  # This method returns <tt>self</tt>.
178
244
  #
179
245
  # == Examples
180
- # # Assuming +td+ is an instance of TableDefinition
181
- # td.column(:granted, :boolean)
182
- # # granted BOOLEAN
183
- #
184
- # td.column(:picture, :binary, limit: 2.megabytes)
185
- # # => picture BLOB(2097152)
186
246
  #
187
- # td.column(:sales_stage, :string, limit: 20, default: 'new', null: false)
188
- # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
189
- #
190
- # td.column(:bill_gates_money, :decimal, precision: 15, scale: 2)
191
- # # => bill_gates_money DECIMAL(15,2)
192
- #
193
- # td.column(:sensor_reading, :decimal, precision: 30, scale: 20)
194
- # # => sensor_reading DECIMAL(30,20)
195
- #
196
- # # While <tt>:scale</tt> defaults to zero on most databases, it
197
- # # probably wouldn't hurt to include it.
198
- # td.column(:huge_integer, :decimal, precision: 30)
199
- # # => huge_integer DECIMAL(30)
200
- #
201
- # # Defines a column with a database-specific type.
202
- # td.column(:foo, 'polygon')
203
- # # => foo polygon
247
+ # # Assuming +td+ is an instance of TableDefinition
248
+ # td.column(:granted, :boolean, index: true)
204
249
  #
205
250
  # == Short-hand examples
206
251
  #
207
- # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
252
+ # Instead of calling #column directly, you can also work with the short-hand definitions for the default types.
208
253
  # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
209
254
  # in a single statement.
210
255
  #
@@ -236,7 +281,8 @@ module ActiveRecord
236
281
  # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
237
282
  # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
238
283
  # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
239
- # will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this:
284
+ # will also create an index, similar to calling {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index].
285
+ # So what can be written like this:
240
286
  #
241
287
  # create_table :taggings do |t|
242
288
  # t.integer :tag_id, :tagger_id, :taggable_id
@@ -268,18 +314,12 @@ module ActiveRecord
268
314
  self
269
315
  end
270
316
 
317
+ # remove the column +name+ from the table.
318
+ # remove_column(:account_id)
271
319
  def remove_column(name)
272
320
  @columns_hash.delete name.to_s
273
321
  end
274
322
 
275
- [:string, :text, :integer, :bigint, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
276
- define_method column_type do |*args|
277
- options = args.extract_options!
278
- column_names = args
279
- column_names.each { |name| column(name, column_type, options) }
280
- end
281
- end
282
-
283
323
  # Adds index options to the indexes hash, keyed by column name
284
324
  # This is primarily used to track indexes that need to be created after the table
285
325
  #
@@ -289,16 +329,18 @@ module ActiveRecord
289
329
  end
290
330
 
291
331
  def foreign_key(table_name, options = {}) # :nodoc:
292
- foreign_keys.push([table_name, options])
332
+ foreign_keys[table_name] = options
293
333
  end
294
334
 
295
335
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
296
- # <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps
336
+ # <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
297
337
  #
298
338
  # t.timestamps null: false
299
339
  def timestamps(*args)
300
340
  options = args.extract_options!
301
- emit_warning_if_null_unspecified(:timestamps, options)
341
+
342
+ options[:null] = false if options[:null].nil?
343
+
302
344
  column(:created_at, :datetime, options)
303
345
  column(:updated_at, :datetime, options)
304
346
  end
@@ -308,26 +350,10 @@ module ActiveRecord
308
350
  # t.references(:user)
309
351
  # t.belongs_to(:supplier, foreign_key: true)
310
352
  #
311
- # See SchemaStatements#add_reference for details of the options you can use.
312
- def references(*args)
313
- options = args.extract_options!
314
- polymorphic = options.delete(:polymorphic)
315
- index_options = options.delete(:index)
316
- foreign_key_options = options.delete(:foreign_key)
317
- type = options.delete(:type) || :integer
318
-
319
- if polymorphic && foreign_key_options
320
- raise ArgumentError, "Cannot add a foreign key on a polymorphic relation"
321
- end
322
-
353
+ # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
354
+ def references(*args, **options)
323
355
  args.each do |col|
324
- column("#{col}_id", type, options)
325
- column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
326
- index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
327
- if foreign_key_options
328
- to_table = Base.pluralize_table_names ? col.to_s.pluralize : col.to_s
329
- foreign_key(to_table, foreign_key_options.is_a?(Hash) ? foreign_key_options : {})
330
- end
356
+ ReferenceDefinition.new(col, **options).add_to(self)
331
357
  end
332
358
  end
333
359
  alias :belongs_to :references
@@ -335,18 +361,17 @@ module ActiveRecord
335
361
  def new_column_definition(name, type, options) # :nodoc:
336
362
  type = aliased_types(type.to_s, type)
337
363
  column = create_column_definition name, type
338
- limit = options.fetch(:limit) do
339
- native[type][:limit] if native[type].is_a?(Hash)
340
- end
341
364
 
342
- column.limit = limit
365
+ column.limit = options[:limit]
343
366
  column.precision = options[:precision]
344
367
  column.scale = options[:scale]
345
368
  column.default = options[:default]
346
369
  column.null = options[:null]
347
370
  column.first = options[:first]
348
371
  column.after = options[:after]
372
+ column.auto_increment = options[:auto_increment]
349
373
  column.primary_key = type == :primary_key || options[:primary_key]
374
+ column.collation = options[:collation]
350
375
  column
351
376
  end
352
377
 
@@ -355,10 +380,6 @@ module ActiveRecord
355
380
  ColumnDefinition.new name, type
356
381
  end
357
382
 
358
- def native
359
- @native
360
- end
361
-
362
383
  def aliased_types(name, fallback)
363
384
  'timestamp' == name ? :datetime : fallback
364
385
  end
@@ -389,16 +410,17 @@ module ActiveRecord
389
410
  def add_column(name, type, options)
390
411
  name = name.to_s
391
412
  type = type.to_sym
392
- @adds << @td.new_column_definition(name, type, options)
413
+ @adds << AddColumnDefinition.new(@td.new_column_definition(name, type, options))
393
414
  end
394
415
  end
395
416
 
396
417
  # Represents an SQL table in an abstract way for updating a table.
397
- # Also see TableDefinition and SchemaStatements#create_table
418
+ # Also see TableDefinition and {connection.create_table}[rdoc-ref:SchemaStatements#create_table]
398
419
  #
399
420
  # Available transformations are:
400
421
  #
401
422
  # change_table :table do |t|
423
+ # t.primary_key
402
424
  # t.column
403
425
  # t.index
404
426
  # t.rename_index
@@ -411,6 +433,7 @@ module ActiveRecord
411
433
  # t.string
412
434
  # t.text
413
435
  # t.integer
436
+ # t.bigint
414
437
  # t.float
415
438
  # t.decimal
416
439
  # t.datetime
@@ -427,6 +450,8 @@ module ActiveRecord
427
450
  # end
428
451
  #
429
452
  class Table
453
+ include ColumnMethods
454
+
430
455
  attr_reader :name
431
456
 
432
457
  def initialize(table_name, base)
@@ -435,33 +460,42 @@ module ActiveRecord
435
460
  end
436
461
 
437
462
  # Adds a new column to the named table.
438
- # See TableDefinition#column for details of the options you can use.
439
463
  #
440
- # ====== Creating a simple column
441
464
  # t.column(:name, :string)
465
+ #
466
+ # See TableDefinition#column for details of the options you can use.
442
467
  def column(column_name, type, options = {})
443
468
  @base.add_column(name, column_name, type, options)
444
469
  end
445
470
 
446
- # Checks to see if a column exists. See SchemaStatements#column_exists?
471
+ # Checks to see if a column exists.
472
+ #
473
+ # t.string(:name) unless t.column_exists?(:name, :string)
474
+ #
475
+ # See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?]
447
476
  def column_exists?(column_name, type = nil, options = {})
448
477
  @base.column_exists?(name, column_name, type, options)
449
478
  end
450
479
 
451
480
  # Adds a new index to the table. +column_name+ can be a single Symbol, or
452
- # an Array of Symbols. See SchemaStatements#add_index
481
+ # an Array of Symbols.
453
482
  #
454
- # ====== Creating a simple index
455
483
  # t.index(:name)
456
- # ====== Creating a unique index
457
484
  # t.index([:branch_id, :party_id], unique: true)
458
- # ====== Creating a named index
459
485
  # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
486
+ #
487
+ # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use.
460
488
  def index(column_name, options = {})
461
489
  @base.add_index(name, column_name, options)
462
490
  end
463
491
 
464
- # Checks to see if an index exists. See SchemaStatements#index_exists?
492
+ # Checks to see if an index exists.
493
+ #
494
+ # unless t.index_exists?(:branch_id)
495
+ # t.index(:branch_id)
496
+ # end
497
+ #
498
+ # See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
465
499
  def index_exists?(column_name, options = {})
466
500
  @base.index_exists?(name, column_name, options)
467
501
  end
@@ -469,52 +503,59 @@ module ActiveRecord
469
503
  # Renames the given index on the table.
470
504
  #
471
505
  # t.rename_index(:user_id, :account_id)
506
+ #
507
+ # See {connection.rename_index}[rdoc-ref:SchemaStatements#rename_index]
472
508
  def rename_index(index_name, new_index_name)
473
509
  @base.rename_index(name, index_name, new_index_name)
474
510
  end
475
511
 
476
- # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
512
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the table.
477
513
  #
478
- # t.timestamps null: false
514
+ # t.timestamps(null: false)
515
+ #
516
+ # See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
479
517
  def timestamps(options = {})
480
518
  @base.add_timestamps(name, options)
481
519
  end
482
520
 
483
521
  # Changes the column's definition according to the new options.
484
- # See TableDefinition#column for details of the options you can use.
485
522
  #
486
523
  # t.change(:name, :string, limit: 80)
487
524
  # t.change(:description, :text)
525
+ #
526
+ # See TableDefinition#column for details of the options you can use.
488
527
  def change(column_name, type, options = {})
489
528
  @base.change_column(name, column_name, type, options)
490
529
  end
491
530
 
492
- # Sets a new default value for a column. See SchemaStatements#change_column_default
531
+ # Sets a new default value for a column.
493
532
  #
494
533
  # t.change_default(:qualification, 'new')
495
534
  # t.change_default(:authorized, 1)
496
- def change_default(column_name, default)
497
- @base.change_column_default(name, column_name, default)
535
+ # t.change_default(:status, from: nil, to: "draft")
536
+ #
537
+ # See {connection.change_column_default}[rdoc-ref:SchemaStatements#change_column_default]
538
+ def change_default(column_name, default_or_changes)
539
+ @base.change_column_default(name, column_name, default_or_changes)
498
540
  end
499
541
 
500
542
  # Removes the column(s) from the table definition.
501
543
  #
502
544
  # t.remove(:qualification)
503
545
  # t.remove(:qualification, :experience)
546
+ #
547
+ # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
504
548
  def remove(*column_names)
505
549
  @base.remove_columns(name, *column_names)
506
550
  end
507
551
 
508
552
  # Removes the given index from the table.
509
553
  #
510
- # ====== Remove the index_table_name_on_column in the table_name table
511
- # t.remove_index :column
512
- # ====== Remove the index named index_table_name_on_branch_id in the table_name table
513
- # t.remove_index column: :branch_id
514
- # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
515
- # t.remove_index column: [:branch_id, :party_id]
516
- # ====== Remove the index named by_branch_party in the table_name table
517
- # t.remove_index name: :by_branch_party
554
+ # t.remove_index(:branch_id)
555
+ # t.remove_index(column: [:branch_id, :party_id])
556
+ # t.remove_index(name: :by_branch_party)
557
+ #
558
+ # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index]
518
559
  def remove_index(options = {})
519
560
  @base.remove_index(name, options)
520
561
  end
@@ -522,6 +563,8 @@ module ActiveRecord
522
563
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
523
564
  #
524
565
  # t.remove_timestamps
566
+ #
567
+ # See {connection.remove_timestamps}[rdoc-ref:SchemaStatements#remove_timestamps]
525
568
  def remove_timestamps(options = {})
526
569
  @base.remove_timestamps(name, options)
527
570
  end
@@ -529,6 +572,8 @@ module ActiveRecord
529
572
  # Renames a column.
530
573
  #
531
574
  # t.rename(:description, :name)
575
+ #
576
+ # See {connection.rename_column}[rdoc-ref:SchemaStatements#rename_column]
532
577
  def rename(column_name, new_column_name)
533
578
  @base.rename_column(name, column_name, new_column_name)
534
579
  end
@@ -538,7 +583,7 @@ module ActiveRecord
538
583
  # t.references(:user)
539
584
  # t.belongs_to(:supplier, foreign_key: true)
540
585
  #
541
- # See SchemaStatements#add_reference for details of the options you can use.
586
+ # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
542
587
  def references(*args)
543
588
  options = args.extract_options!
544
589
  args.each do |ref_name|
@@ -548,12 +593,11 @@ module ActiveRecord
548
593
  alias :belongs_to :references
549
594
 
550
595
  # Removes a reference. Optionally removes a +type+ column.
551
- # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
552
596
  #
553
597
  # t.remove_references(:user)
554
598
  # t.remove_belongs_to(:supplier, polymorphic: true)
555
599
  #
556
- # See SchemaStatements#remove_reference
600
+ # See {connection.remove_reference}[rdoc-ref:SchemaStatements#remove_reference]
557
601
  def remove_references(*args)
558
602
  options = args.extract_options!
559
603
  args.each do |ref_name|
@@ -562,23 +606,23 @@ module ActiveRecord
562
606
  end
563
607
  alias :remove_belongs_to :remove_references
564
608
 
565
- # Adds a column or columns of a specified type
609
+ # Adds a foreign key.
566
610
  #
567
- # t.string(:goat)
568
- # t.string(:goat, :sheep)
569
- [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
570
- define_method column_type do |*args|
571
- options = args.extract_options!
572
- args.each do |column_name|
573
- @base.add_column(name, column_name, column_type, options)
574
- end
575
- end
611
+ # t.foreign_key(:authors)
612
+ #
613
+ # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
614
+ def foreign_key(*args) # :nodoc:
615
+ @base.add_foreign_key(name, *args)
576
616
  end
577
617
 
578
- private
579
- def native
580
- @base.native_database_types
581
- end
618
+ # Checks to see if a foreign key exists.
619
+ #
620
+ # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
621
+ #
622
+ # See {connection.foreign_key_exists?}[rdoc-ref:SchemaStatements#foreign_key_exists?]
623
+ def foreign_key_exists?(*args) # :nodoc:
624
+ @base.foreign_key_exists?(name, *args)
625
+ end
582
626
  end
583
627
  end
584
628
  end