activerecord 5.0.7.2 → 5.1.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 (216) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +389 -2252
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record.rb +20 -20
  8. data/lib/active_record/aggregations.rb +244 -244
  9. data/lib/active_record/association_relation.rb +5 -5
  10. data/lib/active_record/associations.rb +1579 -1569
  11. data/lib/active_record/associations/alias_tracker.rb +1 -1
  12. data/lib/active_record/associations/association.rb +23 -15
  13. data/lib/active_record/associations/association_scope.rb +83 -81
  14. data/lib/active_record/associations/belongs_to_association.rb +0 -1
  15. data/lib/active_record/associations/builder/belongs_to.rb +16 -14
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  18. data/lib/active_record/associations/collection_association.rb +74 -241
  19. data/lib/active_record/associations/collection_proxy.rb +144 -70
  20. data/lib/active_record/associations/has_many_association.rb +15 -19
  21. data/lib/active_record/associations/has_many_through_association.rb +12 -5
  22. data/lib/active_record/associations/has_one_association.rb +22 -28
  23. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  24. data/lib/active_record/associations/join_dependency.rb +117 -115
  25. data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
  26. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  28. data/lib/active_record/associations/preloader.rb +94 -94
  29. data/lib/active_record/associations/preloader/association.rb +87 -64
  30. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  31. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  32. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  33. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +34 -41
  35. data/lib/active_record/associations/singular_association.rb +8 -25
  36. data/lib/active_record/associations/through_association.rb +3 -6
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  39. data/lib/active_record/attribute_assignment.rb +61 -61
  40. data/lib/active_record/attribute_decorators.rb +35 -13
  41. data/lib/active_record/attribute_methods.rb +56 -65
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  43. data/lib/active_record/attribute_methods/dirty.rb +216 -34
  44. data/lib/active_record/attribute_methods/primary_key.rb +78 -73
  45. data/lib/active_record/attribute_methods/read.rb +39 -35
  46. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  48. data/lib/active_record/attribute_methods/write.rb +36 -30
  49. data/lib/active_record/attribute_mutation_tracker.rb +53 -10
  50. data/lib/active_record/attribute_set.rb +9 -6
  51. data/lib/active_record/attribute_set/builder.rb +41 -49
  52. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  53. data/lib/active_record/attributes.rb +21 -21
  54. data/lib/active_record/autosave_association.rb +13 -13
  55. data/lib/active_record/base.rb +24 -22
  56. data/lib/active_record/callbacks.rb +52 -14
  57. data/lib/active_record/coders/yaml_column.rb +9 -11
  58. data/lib/active_record/collection_cache_key.rb +6 -17
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
  71. data/lib/active_record/connection_adapters/column.rb +27 -5
  72. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  73. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -43
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +49 -31
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  94. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  97. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +28 -30
  98. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +38 -36
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +161 -170
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -20
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +187 -130
  116. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  117. data/lib/active_record/connection_handling.rb +14 -26
  118. data/lib/active_record/core.rb +110 -93
  119. data/lib/active_record/counter_cache.rb +62 -13
  120. data/lib/active_record/define_callbacks.rb +20 -0
  121. data/lib/active_record/dynamic_matchers.rb +80 -79
  122. data/lib/active_record/enum.rb +8 -6
  123. data/lib/active_record/errors.rb +58 -15
  124. data/lib/active_record/explain.rb +1 -2
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +7 -4
  127. data/lib/active_record/fixture_set/file.rb +11 -8
  128. data/lib/active_record/fixtures.rb +66 -53
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +93 -79
  131. data/lib/active_record/integration.rb +7 -7
  132. data/lib/active_record/internal_metadata.rb +3 -16
  133. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  134. data/lib/active_record/locking/optimistic.rb +64 -56
  135. data/lib/active_record/locking/pessimistic.rb +10 -1
  136. data/lib/active_record/log_subscriber.rb +29 -29
  137. data/lib/active_record/migration.rb +155 -172
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +76 -37
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/model_schema.rb +85 -119
  142. data/lib/active_record/nested_attributes.rb +200 -199
  143. data/lib/active_record/null_relation.rb +10 -33
  144. data/lib/active_record/persistence.rb +45 -38
  145. data/lib/active_record/query_cache.rb +4 -8
  146. data/lib/active_record/querying.rb +2 -3
  147. data/lib/active_record/railtie.rb +16 -17
  148. data/lib/active_record/railties/controller_runtime.rb +6 -2
  149. data/lib/active_record/railties/databases.rake +125 -140
  150. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  151. data/lib/active_record/readonly_attributes.rb +2 -2
  152. data/lib/active_record/reflection.rb +79 -96
  153. data/lib/active_record/relation.rb +72 -115
  154. data/lib/active_record/relation/batches.rb +87 -58
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  156. data/lib/active_record/relation/calculations.rb +154 -160
  157. data/lib/active_record/relation/delegation.rb +30 -29
  158. data/lib/active_record/relation/finder_methods.rb +195 -226
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder.rb +92 -89
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  165. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  166. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +247 -295
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +79 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/result.rb +29 -31
  174. data/lib/active_record/runtime_registry.rb +3 -3
  175. data/lib/active_record/sanitization.rb +182 -197
  176. data/lib/active_record/schema.rb +3 -3
  177. data/lib/active_record/schema_dumper.rb +14 -37
  178. data/lib/active_record/schema_migration.rb +3 -3
  179. data/lib/active_record/scoping.rb +9 -10
  180. data/lib/active_record/scoping/default.rb +87 -91
  181. data/lib/active_record/scoping/named.rb +16 -28
  182. data/lib/active_record/secure_token.rb +2 -2
  183. data/lib/active_record/statement_cache.rb +13 -15
  184. data/lib/active_record/store.rb +31 -32
  185. data/lib/active_record/suppressor.rb +2 -1
  186. data/lib/active_record/table_metadata.rb +9 -5
  187. data/lib/active_record/tasks/database_tasks.rb +72 -65
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  191. data/lib/active_record/timestamp.rb +39 -25
  192. data/lib/active_record/touch_later.rb +1 -2
  193. data/lib/active_record/transactions.rb +98 -110
  194. data/lib/active_record/type.rb +17 -13
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +9 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/serialized.rb +8 -8
  199. data/lib/active_record/type/text.rb +9 -0
  200. data/lib/active_record/type/time.rb +0 -1
  201. data/lib/active_record/type/type_map.rb +11 -15
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type_caster.rb +2 -2
  204. data/lib/active_record/type_caster/connection.rb +8 -6
  205. data/lib/active_record/type_caster/map.rb +3 -1
  206. data/lib/active_record/validations.rb +4 -4
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/presence.rb +2 -2
  209. data/lib/active_record/validations/uniqueness.rb +8 -39
  210. data/lib/active_record/version.rb +1 -1
  211. data/lib/rails/generators/active_record.rb +4 -4
  212. data/lib/rails/generators/active_record/migration.rb +2 -2
  213. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  215. metadata +22 -13
  216. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,14 +1,14 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_record/connection_adapters/statement_pool'
3
- require 'active_record/connection_adapters/mysql/column'
4
- require 'active_record/connection_adapters/mysql/explain_pretty_printer'
5
- require 'active_record/connection_adapters/mysql/quoting'
6
- require 'active_record/connection_adapters/mysql/schema_creation'
7
- require 'active_record/connection_adapters/mysql/schema_definitions'
8
- require 'active_record/connection_adapters/mysql/schema_dumper'
9
- require 'active_record/connection_adapters/mysql/type_metadata'
10
-
11
- require 'active_support/core_ext/string/strip'
1
+ require "active_record/connection_adapters/abstract_adapter"
2
+ require "active_record/connection_adapters/statement_pool"
3
+ require "active_record/connection_adapters/mysql/column"
4
+ require "active_record/connection_adapters/mysql/explain_pretty_printer"
5
+ require "active_record/connection_adapters/mysql/quoting"
6
+ require "active_record/connection_adapters/mysql/schema_creation"
7
+ require "active_record/connection_adapters/mysql/schema_definitions"
8
+ require "active_record/connection_adapters/mysql/schema_dumper"
9
+ require "active_record/connection_adapters/mysql/type_metadata"
10
+
11
+ require "active_support/core_ext/string/strip"
12
12
 
13
13
  module ActiveRecord
14
14
  module ConnectionAdapters
@@ -39,16 +39,17 @@ module ActiveRecord
39
39
  self.emulate_booleans = true
40
40
 
41
41
  NATIVE_DATABASE_TYPES = {
42
- primary_key: "int auto_increment PRIMARY KEY",
42
+ primary_key: "bigint auto_increment PRIMARY KEY",
43
43
  string: { name: "varchar", limit: 255 },
44
- text: { name: "text" },
44
+ text: { name: "text", limit: 65535 },
45
45
  integer: { name: "int", limit: 4 },
46
46
  float: { name: "float" },
47
47
  decimal: { name: "decimal" },
48
48
  datetime: { name: "datetime" },
49
+ timestamp: { name: "timestamp" },
49
50
  time: { name: "time" },
50
51
  date: { name: "date" },
51
- binary: { name: "blob" },
52
+ binary: { name: "blob", limit: 65535 },
52
53
  boolean: { name: "tinyint", limit: 1 },
53
54
  json: { name: "json" },
54
55
  }
@@ -67,25 +68,25 @@ module ActiveRecord
67
68
 
68
69
  @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
69
70
 
70
- if version < '5.0.0'
71
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.0."
71
+ if version < "5.1.10"
72
+ raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.1.10."
72
73
  end
73
74
  end
74
75
 
75
- CHARSETS_OF_4BYTES_MAXLEN = ['utf8mb4', 'utf16', 'utf16le', 'utf32']
76
+ CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
76
77
 
77
78
  def internal_string_options_for_primary_key # :nodoc:
78
79
  super.tap { |options|
79
- options[:collation] = collation.sub(/\A[^_]+/, 'utf8') if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
80
+ options[:collation] = collation.sub(/\A[^_]+/, "utf8") if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
80
81
  }
81
82
  end
82
83
 
83
84
  def version #:nodoc:
84
- @version ||= Version.new(version_string)
85
+ @version ||= Version.new(full_version.match(/^\d+\.\d+\.\d+/)[0])
85
86
  end
86
87
 
87
88
  def mariadb? # :nodoc:
88
- full_version =~ /mariadb/i
89
+ /mariadb/i.match?(full_version)
89
90
  end
90
91
 
91
92
  # Returns true, since this connection adapter supports migrations.
@@ -93,10 +94,6 @@ module ActiveRecord
93
94
  true
94
95
  end
95
96
 
96
- def supports_primary_key?
97
- true
98
- end
99
-
100
97
  def supports_bulk_alter? #:nodoc:
101
98
  true
102
99
  end
@@ -135,9 +132,17 @@ module ActiveRecord
135
132
 
136
133
  def supports_datetime_with_precision?
137
134
  if mariadb?
138
- version >= '5.3.0'
135
+ version >= "5.3.0"
139
136
  else
140
- version >= '5.6.4'
137
+ version >= "5.6.4"
138
+ end
139
+ end
140
+
141
+ def supports_virtual_columns?
142
+ if mariadb?
143
+ version >= "5.2.0"
144
+ else
145
+ version >= "5.7.5"
141
146
  end
142
147
  end
143
148
 
@@ -146,11 +151,11 @@ module ActiveRecord
146
151
  end
147
152
 
148
153
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
149
- select_value("SELECT GET_LOCK('#{lock_name}', #{timeout});").to_s == '1'
154
+ select_value("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})") == 1
150
155
  end
151
156
 
152
157
  def release_advisory_lock(lock_name) # :nodoc:
153
- select_value("SELECT RELEASE_LOCK('#{lock_name}')").to_s == '1'
158
+ select_value("SELECT RELEASE_LOCK(#{quote(lock_name)})") == 1
154
159
  end
155
160
 
156
161
  def native_database_types
@@ -158,7 +163,7 @@ module ActiveRecord
158
163
  end
159
164
 
160
165
  def index_algorithms
161
- { default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
166
+ { default: "ALGORITHM = DEFAULT", copy: "ALGORITHM = COPY", inplace: "ALGORITHM = INPLACE" }
162
167
  end
163
168
 
164
169
  # HELPER METHODS ===========================================
@@ -207,7 +212,7 @@ module ActiveRecord
207
212
  def explain(arel, binds = [])
208
213
  sql = "EXPLAIN #{to_sql(arel, binds)}"
209
214
  start = Time.now
210
- result = exec_query(sql, 'EXPLAIN', binds)
215
+ result = exec_query(sql, "EXPLAIN", binds)
211
216
  elapsed = Time.now - start
212
217
 
213
218
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
@@ -215,7 +220,11 @@ module ActiveRecord
215
220
 
216
221
  # Executes the SQL statement in the context of this connection.
217
222
  def execute(sql, name = nil)
218
- log(sql, name) { @connection.query(sql) }
223
+ log(sql, name) do
224
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
225
+ @connection.query(sql)
226
+ end
227
+ end
219
228
  end
220
229
 
221
230
  # Mysql2Adapter doesn't have to free a result after using it, but we use this method
@@ -293,70 +302,57 @@ module ActiveRecord
293
302
  end
294
303
 
295
304
  def current_database
296
- select_value 'SELECT DATABASE() as db'
305
+ select_value "SELECT DATABASE() as db"
297
306
  end
298
307
 
299
308
  # Returns the database character set.
300
309
  def charset
301
- show_variable 'character_set_database'
310
+ show_variable "character_set_database"
302
311
  end
303
312
 
304
313
  # Returns the database collation strategy.
305
314
  def collation
306
- show_variable 'collation_database'
315
+ show_variable "collation_database"
307
316
  end
308
317
 
309
- def tables(name = nil) # :nodoc:
310
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
311
- #tables currently returns both tables and views.
312
- This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
313
- Use #data_sources instead.
314
- MSG
318
+ def tables # :nodoc:
319
+ sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
320
+ sql << " AND table_schema = #{quote(@config[:database])}"
315
321
 
316
- if name
317
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
318
- Passing arguments to #tables is deprecated without replacement.
319
- MSG
320
- end
322
+ select_values(sql, "SCHEMA")
323
+ end
321
324
 
322
- data_sources
325
+ def views # :nodoc:
326
+ select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", "SCHEMA")
323
327
  end
324
328
 
325
- def data_sources
329
+ def data_sources # :nodoc:
326
330
  sql = "SELECT table_name FROM information_schema.tables "
327
- sql << "WHERE table_schema = DATABASE()"
331
+ sql << "WHERE table_schema = #{quote(@config[:database])}"
328
332
 
329
- select_values(sql, 'SCHEMA')
333
+ select_values(sql, "SCHEMA")
330
334
  end
331
335
 
332
- def truncate(table_name, name = nil)
333
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
334
- end
336
+ def table_exists?(table_name) # :nodoc:
337
+ return false unless table_name.present?
338
+
339
+ schema, name = extract_schema_qualified_name(table_name)
335
340
 
336
- def table_exists?(table_name)
337
- # Update lib/active_record/internal_metadata.rb when this gets removed
338
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
339
- #table_exists? currently checks both tables and views.
340
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
341
- Use #data_source_exists? instead.
342
- MSG
341
+ sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
342
+ sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
343
343
 
344
- data_source_exists?(table_name)
344
+ select_values(sql, "SCHEMA").any?
345
345
  end
346
346
 
347
- def data_source_exists?(table_name)
347
+ def data_source_exists?(table_name) # :nodoc:
348
348
  return false unless table_name.present?
349
349
 
350
350
  schema, name = extract_schema_qualified_name(table_name)
351
351
 
352
352
  sql = "SELECT table_name FROM information_schema.tables "
353
- sql << "WHERE table_schema = #{schema} AND table_name = #{name}"
353
+ sql << "WHERE table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
354
354
 
355
- select_values(sql, 'SCHEMA').any?
356
- end
357
-
358
- def views # :nodoc:
359
- select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", 'SCHEMA')
355
+ select_values(sql, "SCHEMA").any?
360
356
  end
361
357
 
362
358
  def view_exists?(view_name) # :nodoc:
@@ -365,19 +361,29 @@ module ActiveRecord
365
361
  schema, name = extract_schema_qualified_name(view_name)
366
362
 
367
363
  sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
368
- sql << " AND table_schema = #{schema} AND table_name = #{name}"
364
+ sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
369
365
 
370
- select_values(sql, 'SCHEMA').any?
366
+ select_values(sql, "SCHEMA").any?
367
+ end
368
+
369
+ def truncate(table_name, name = nil)
370
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
371
371
  end
372
372
 
373
373
  # Returns an array of indexes for the given table.
374
374
  def indexes(table_name, name = nil) #:nodoc:
375
+ if name
376
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
377
+ Passing name to #indexes is deprecated without replacement.
378
+ MSG
379
+ end
380
+
375
381
  indexes = []
376
382
  current_index = nil
377
- execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
383
+ execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
378
384
  each_hash(result) do |row|
379
385
  if current_index != row[:Key_name]
380
- next if row[:Key_name] == 'PRIMARY' # skip the primary key
386
+ next if row[:Key_name] == "PRIMARY" # skip the primary key
381
387
  current_index = row[:Key_name]
382
388
 
383
389
  mysql_index_type = row[:Index_type].downcase.to_sym
@@ -394,33 +400,29 @@ module ActiveRecord
394
400
  indexes
395
401
  end
396
402
 
397
- # Returns an array of +Column+ objects for the table specified by +table_name+.
398
- def columns(table_name) # :nodoc:
399
- table_name = table_name.to_s
400
- column_definitions(table_name).map do |field|
401
- type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
402
- if type_metadata.type == :datetime && field[:Default] =~ /\ACURRENT_TIMESTAMP(?:\(\))?\z/i
403
- default, default_function = nil, "CURRENT_TIMESTAMP"
404
- else
405
- default, default_function = field[:Default], nil
406
- end
407
- new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
403
+ def new_column_from_field(table_name, field) # :nodoc:
404
+ type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
405
+ if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP"
406
+ default, default_function = nil, field[:Default]
407
+ else
408
+ default, default_function = field[:Default], nil
408
409
  end
410
+ new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
409
411
  end
410
412
 
411
413
  def table_comment(table_name) # :nodoc:
412
414
  schema, name = extract_schema_qualified_name(table_name)
413
415
 
414
- select_value(<<-SQL.strip_heredoc, 'SCHEMA')
416
+ select_value(<<-SQL.strip_heredoc, "SCHEMA")
415
417
  SELECT table_comment
416
418
  FROM information_schema.tables
417
- WHERE table_schema = #{schema}
418
- AND table_name = #{name}
419
+ WHERE table_schema = #{quote(schema)}
420
+ AND table_name = #{quote(name)}
419
421
  SQL
420
422
  end
421
423
 
422
424
  def create_table(table_name, **options) #:nodoc:
423
- super(table_name, options: 'ENGINE=InnoDB', **options)
425
+ super(table_name, options: "ENGINE=InnoDB", **options)
424
426
  end
425
427
 
426
428
  def bulk_change_table(table_name, operations) #:nodoc:
@@ -479,7 +481,7 @@ module ActiveRecord
479
481
  def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
480
482
  default = extract_new_default_value(default_or_changes)
481
483
  column = column_for(table_name, column_name)
482
- change_column table_name, column_name, column.sql_type, :default => default
484
+ change_column table_name, column_name, column.sql_type, default: default
483
485
  end
484
486
 
485
487
  def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
@@ -489,7 +491,7 @@ module ActiveRecord
489
491
  execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
490
492
  end
491
493
 
492
- change_column table_name, column_name, column.sql_type, :null => null
494
+ change_column table_name, column_name, column.sql_type, null: null
493
495
  end
494
496
 
495
497
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
@@ -517,7 +519,7 @@ module ActiveRecord
517
519
 
518
520
  schema, name = extract_schema_qualified_name(table_name)
519
521
 
520
- fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
522
+ fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
521
523
  SELECT fk.referenced_table_name AS 'to_table',
522
524
  fk.referenced_column_name AS 'primary_key',
523
525
  fk.column_name AS 'column',
@@ -528,22 +530,22 @@ module ActiveRecord
528
530
  JOIN information_schema.referential_constraints rc
529
531
  USING (constraint_schema, constraint_name)
530
532
  WHERE fk.referenced_column_name IS NOT NULL
531
- AND fk.table_schema = #{schema}
532
- AND fk.table_name = #{name}
533
- AND rc.table_name = #{name}
533
+ AND fk.table_schema = #{quote(schema)}
534
+ AND fk.table_name = #{quote(name)}
535
+ AND rc.table_name = #{quote(name)}
534
536
  SQL
535
537
 
536
538
  fk_info.map do |row|
537
539
  options = {
538
- column: row['column'],
539
- name: row['name'],
540
- primary_key: row['primary_key']
540
+ column: row["column"],
541
+ name: row["name"],
542
+ primary_key: row["primary_key"]
541
543
  }
542
544
 
543
- options[:on_update] = extract_foreign_key_action(row['on_update'])
544
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
545
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
546
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
545
547
 
546
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
548
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
547
549
  end
548
550
  end
549
551
 
@@ -553,7 +555,7 @@ module ActiveRecord
553
555
  create_table_info = create_table_info(table_name)
554
556
 
555
557
  # strip create_definitions and partition_options
556
- raw_table_options = create_table_info.sub(/\A.*\n\) /m, '').sub(/\n\/\*!.*\*\/\n\z/m, '').strip
558
+ raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
557
559
 
558
560
  # strip AUTO_INCREMENT
559
561
  raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
@@ -561,7 +563,7 @@ module ActiveRecord
561
563
  table_options[:options] = raw_table_options
562
564
 
563
565
  # strip COMMENT
564
- if raw_table_options.sub!(/ COMMENT='.+'/, '')
566
+ if raw_table_options.sub!(/ COMMENT='.+'/, "")
565
567
  table_options[:comment] = table_comment(table_name)
566
568
  end
567
569
 
@@ -569,31 +571,32 @@ module ActiveRecord
569
571
  end
570
572
 
571
573
  # Maps logical Rails types to MySQL-specific data types.
572
- def type_to_sql(type, limit = nil, precision = nil, scale = nil, unsigned = nil)
573
- sql = case type.to_s
574
- when 'integer'
575
- integer_to_sql(limit)
576
- when 'text'
577
- text_to_sql(limit)
578
- when 'blob'
579
- binary_to_sql(limit)
580
- when 'binary'
581
- if (0..0xfff) === limit
582
- "varbinary(#{limit})"
583
- else
574
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
575
+ sql = \
576
+ case type.to_s
577
+ when "integer"
578
+ integer_to_sql(limit)
579
+ when "text"
580
+ text_to_sql(limit)
581
+ when "blob"
584
582
  binary_to_sql(limit)
583
+ when "binary"
584
+ if (0..0xfff) === limit
585
+ "varbinary(#{limit})"
586
+ else
587
+ binary_to_sql(limit)
588
+ end
589
+ else
590
+ super
585
591
  end
586
- else
587
- super(type, limit, precision, scale)
588
- end
589
592
 
590
- sql << ' unsigned' if unsigned && type != :primary_key
593
+ sql << " unsigned" if unsigned && type != :primary_key
591
594
  sql
592
595
  end
593
596
 
594
597
  # SHOW VARIABLES LIKE 'name'
595
598
  def show_variable(name)
596
- select_value("SELECT @@#{name}", 'SCHEMA')
599
+ select_value("SELECT @@#{name}", "SCHEMA")
597
600
  rescue ActiveRecord::StatementInvalid
598
601
  nil
599
602
  end
@@ -603,19 +606,19 @@ module ActiveRecord
603
606
 
604
607
  schema, name = extract_schema_qualified_name(table_name)
605
608
 
606
- select_values(<<-SQL.strip_heredoc, 'SCHEMA')
609
+ select_values(<<-SQL.strip_heredoc, "SCHEMA")
607
610
  SELECT column_name
608
611
  FROM information_schema.key_column_usage
609
612
  WHERE constraint_name = 'PRIMARY'
610
- AND table_schema = #{schema}
611
- AND table_name = #{name}
613
+ AND table_schema = #{quote(schema)}
614
+ AND table_name = #{quote(name)}
612
615
  ORDER BY ordinal_position
613
616
  SQL
614
617
  end
615
618
 
616
- def case_sensitive_comparison(table, attribute, column, value)
617
- if !value.nil? && column.collation && !column.case_sensitive?
618
- table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
619
+ def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
620
+ if column.collation && !column.case_sensitive?
621
+ table[attribute].eq(Arel::Nodes::Bin.new(value))
619
622
  else
620
623
  super
621
624
  end
@@ -635,10 +638,10 @@ module ActiveRecord
635
638
  # Convert Arel node to string
636
639
  s = s.to_sql unless s.is_a?(String)
637
640
  # Remove any ASC/DESC modifiers
638
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
641
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
639
642
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
640
643
 
641
- [super, *order_columns].join(', ')
644
+ [super, *order_columns].join(", ")
642
645
  end
643
646
 
644
647
  def strict_mode?
@@ -649,332 +652,371 @@ module ActiveRecord
649
652
  !native_database_types[type].nil?
650
653
  end
651
654
 
652
- protected
653
-
654
- def initialize_type_map(m) # :nodoc:
655
- super
656
-
657
- register_class_with_limit m, %r(char)i, MysqlString
655
+ def default_index_type?(index) # :nodoc:
656
+ index.using == :btree || super
657
+ end
658
658
 
659
- m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
660
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
661
- m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
662
- m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
663
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
664
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
665
- m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
666
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
667
- m.register_type %r(^float)i, Type::Float.new(limit: 24)
668
- m.register_type %r(^double)i, Type::Float.new(limit: 53)
669
- m.register_type %r(^json)i, MysqlJson.new
659
+ private
670
660
 
671
- register_integer_type m, %r(^bigint)i, limit: 8
672
- register_integer_type m, %r(^int)i, limit: 4
673
- register_integer_type m, %r(^mediumint)i, limit: 3
674
- register_integer_type m, %r(^smallint)i, limit: 2
675
- register_integer_type m, %r(^tinyint)i, limit: 1
661
+ def initialize_type_map(m)
662
+ super
676
663
 
677
- m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
678
- m.alias_type %r(year)i, 'integer'
679
- m.alias_type %r(bit)i, 'binary'
664
+ register_class_with_limit m, %r(char)i, MysqlString
665
+
666
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
667
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
668
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
669
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
670
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
671
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
672
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
673
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
674
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
675
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
676
+ m.register_type %r(^json)i, MysqlJson.new
677
+
678
+ register_integer_type m, %r(^bigint)i, limit: 8
679
+ register_integer_type m, %r(^int)i, limit: 4
680
+ register_integer_type m, %r(^mediumint)i, limit: 3
681
+ register_integer_type m, %r(^smallint)i, limit: 2
682
+ register_integer_type m, %r(^tinyint)i, limit: 1
683
+
684
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
685
+ m.alias_type %r(year)i, "integer"
686
+ m.alias_type %r(bit)i, "binary"
687
+
688
+ m.register_type(%r(enum)i) do |sql_type|
689
+ limit = sql_type[/^enum\((.+)\)/i, 1]
690
+ .split(",").map { |enum| enum.strip.length - 2 }.max
691
+ MysqlString.new(limit: limit)
692
+ end
680
693
 
681
- m.register_type(%r(enum)i) do |sql_type|
682
- limit = sql_type[/^enum\((.+)\)/i, 1]
683
- .split(',').map{|enum| enum.strip.length - 2}.max
684
- MysqlString.new(limit: limit)
694
+ m.register_type(%r(^set)i) do |sql_type|
695
+ limit = sql_type[/^set\((.+)\)/i, 1]
696
+ .split(",").map { |set| set.strip.length - 1 }.sum - 1
697
+ MysqlString.new(limit: limit)
698
+ end
685
699
  end
686
700
 
687
- m.register_type(%r(^set)i) do |sql_type|
688
- limit = sql_type[/^set\((.+)\)/i, 1]
689
- .split(',').map{|set| set.strip.length - 1}.sum - 1
690
- MysqlString.new(limit: limit)
701
+ def register_integer_type(mapping, key, options)
702
+ mapping.register_type(key) do |sql_type|
703
+ if /\bunsigned\b/.match?(sql_type)
704
+ Type::UnsignedInteger.new(options)
705
+ else
706
+ Type::Integer.new(options)
707
+ end
708
+ end
691
709
  end
692
- end
693
710
 
694
- def register_integer_type(mapping, key, options) # :nodoc:
695
- mapping.register_type(key) do |sql_type|
696
- if /\bunsigned\b/ === sql_type
697
- Type::UnsignedInteger.new(options)
711
+ def extract_precision(sql_type)
712
+ if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
713
+ super || 0
698
714
  else
699
- Type::Integer.new(options)
715
+ super
700
716
  end
701
717
  end
702
- end
703
718
 
704
- def extract_precision(sql_type)
705
- if /time/ === sql_type
706
- super || 0
707
- else
708
- super
719
+ def fetch_type_metadata(sql_type, extra = "")
720
+ MySQL::TypeMetadata.new(super(sql_type), extra: extra)
709
721
  end
710
- end
711
-
712
- def fetch_type_metadata(sql_type, extra = "")
713
- MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
714
- end
715
722
 
716
- def add_index_length(quoted_columns, **options)
717
- if length = options[:length]
718
- case length
719
- when Hash
720
- length = length.symbolize_keys
721
- quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
722
- when Integer
723
- quoted_columns.each { |name, column| column << "(#{length})" }
723
+ def add_index_length(quoted_columns, **options)
724
+ if length = options[:length]
725
+ case length
726
+ when Hash
727
+ length = length.symbolize_keys
728
+ quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
729
+ when Integer
730
+ quoted_columns.each { |name, column| column << "(#{length})" }
731
+ end
724
732
  end
725
- end
726
-
727
- quoted_columns
728
- end
729
733
 
730
- def add_options_for_index_columns(quoted_columns, **options)
731
- quoted_columns = add_index_length(quoted_columns, options)
732
- super
733
- end
734
+ quoted_columns
735
+ end
734
736
 
735
- def translate_exception(exception, message)
736
- case error_number(exception)
737
- when 1062
738
- RecordNotUnique.new(message)
739
- when 1452
740
- InvalidForeignKey.new(message)
741
- when 1406
742
- ValueTooLong.new(message)
743
- else
737
+ def add_options_for_index_columns(quoted_columns, **options)
738
+ quoted_columns = add_index_length(quoted_columns, options)
744
739
  super
745
740
  end
746
- end
747
-
748
- def add_column_sql(table_name, column_name, type, options = {})
749
- td = create_table_definition(table_name)
750
- cd = td.new_column_definition(column_name, type, options)
751
- schema_creation.accept(AddColumnDefinition.new(cd))
752
- end
753
-
754
- def change_column_sql(table_name, column_name, type, options = {})
755
- column = column_for(table_name, column_name)
756
741
 
757
- unless options_include_default?(options)
758
- options[:default] = column.default
742
+ # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
743
+ ER_DUP_ENTRY = 1062
744
+ ER_NOT_NULL_VIOLATION = 1048
745
+ ER_DO_NOT_HAVE_DEFAULT = 1364
746
+ ER_NO_REFERENCED_ROW_2 = 1452
747
+ ER_DATA_TOO_LONG = 1406
748
+ ER_OUT_OF_RANGE = 1264
749
+ ER_LOCK_DEADLOCK = 1213
750
+ ER_CANNOT_ADD_FOREIGN = 1215
751
+ ER_CANNOT_CREATE_TABLE = 1005
752
+
753
+ def translate_exception(exception, message)
754
+ case error_number(exception)
755
+ when ER_DUP_ENTRY
756
+ RecordNotUnique.new(message)
757
+ when ER_NO_REFERENCED_ROW_2
758
+ InvalidForeignKey.new(message)
759
+ when ER_CANNOT_ADD_FOREIGN
760
+ mismatched_foreign_key(message)
761
+ when ER_CANNOT_CREATE_TABLE
762
+ if message.include?("errno: 150")
763
+ mismatched_foreign_key(message)
764
+ else
765
+ super
766
+ end
767
+ when ER_DATA_TOO_LONG
768
+ ValueTooLong.new(message)
769
+ when ER_OUT_OF_RANGE
770
+ RangeError.new(message)
771
+ when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
772
+ NotNullViolation.new(message)
773
+ when ER_LOCK_DEADLOCK
774
+ Deadlocked.new(message)
775
+ else
776
+ super
777
+ end
759
778
  end
760
779
 
761
- unless options.has_key?(:null)
762
- options[:null] = column.null
780
+ def add_column_sql(table_name, column_name, type, options = {})
781
+ td = create_table_definition(table_name)
782
+ cd = td.new_column_definition(column_name, type, options)
783
+ schema_creation.accept(AddColumnDefinition.new(cd))
763
784
  end
764
785
 
765
- td = create_table_definition(table_name)
766
- cd = td.new_column_definition(column.name, type, options)
767
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
768
- end
786
+ def change_column_sql(table_name, column_name, type, options = {})
787
+ column = column_for(table_name, column_name)
769
788
 
770
- def rename_column_sql(table_name, column_name, new_column_name)
771
- column = column_for(table_name, column_name)
772
- options = {
773
- default: column.default,
774
- null: column.null,
775
- auto_increment: column.auto_increment?
776
- }
789
+ unless options_include_default?(options)
790
+ options[:default] = column.default
791
+ end
777
792
 
778
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
779
- td = create_table_definition(table_name)
780
- cd = td.new_column_definition(new_column_name, current_type, options)
781
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
782
- end
793
+ unless options.has_key?(:null)
794
+ options[:null] = column.null
795
+ end
783
796
 
784
- def remove_column_sql(table_name, column_name, type = nil, options = {})
785
- "DROP #{quote_column_name(column_name)}"
786
- end
797
+ unless options.key?(:comment)
798
+ options[:comment] = column.comment
799
+ end
787
800
 
788
- def remove_columns_sql(table_name, *column_names)
789
- column_names.map {|column_name| remove_column_sql(table_name, column_name) }
790
- end
801
+ td = create_table_definition(table_name)
802
+ cd = td.new_column_definition(column.name, type, options)
803
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
804
+ end
791
805
 
792
- def add_index_sql(table_name, column_name, options = {})
793
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
794
- index_algorithm[0, 0] = ", " if index_algorithm.present?
795
- "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
796
- end
806
+ def rename_column_sql(table_name, column_name, new_column_name)
807
+ column = column_for(table_name, column_name)
808
+ options = {
809
+ default: column.default,
810
+ null: column.null,
811
+ auto_increment: column.auto_increment?
812
+ }
797
813
 
798
- def remove_index_sql(table_name, options = {})
799
- index_name = index_name_for_remove(table_name, options)
800
- "DROP INDEX #{index_name}"
801
- end
814
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", "SCHEMA")["Type"]
815
+ td = create_table_definition(table_name)
816
+ cd = td.new_column_definition(new_column_name, current_type, options)
817
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
818
+ end
802
819
 
803
- def add_timestamps_sql(table_name, options = {})
804
- [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
805
- end
820
+ def remove_column_sql(table_name, column_name, type = nil, options = {})
821
+ "DROP #{quote_column_name(column_name)}"
822
+ end
806
823
 
807
- def remove_timestamps_sql(table_name, options = {})
808
- [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
809
- end
824
+ def remove_columns_sql(table_name, *column_names)
825
+ column_names.map { |column_name| remove_column_sql(table_name, column_name) }
826
+ end
810
827
 
811
- private
828
+ def add_index_sql(table_name, column_name, options = {})
829
+ index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
830
+ index_algorithm[0, 0] = ", " if index_algorithm.present?
831
+ "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
832
+ end
812
833
 
813
- # MySQL is too stupid to create a temporary table for use subquery, so we have
814
- # to give it some prompting in the form of a subsubquery. Ugh!
815
- def subquery_for(key, select)
816
- subsubselect = select.clone
817
- subsubselect.projections = [key]
834
+ def remove_index_sql(table_name, options = {})
835
+ index_name = index_name_for_remove(table_name, options)
836
+ "DROP INDEX #{index_name}"
837
+ end
818
838
 
819
- # Materialize subquery by adding distinct
820
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
821
- subsubselect.distinct unless select.limit || select.offset || select.orders.any?
839
+ def add_timestamps_sql(table_name, options = {})
840
+ [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
841
+ end
822
842
 
823
- subselect = Arel::SelectManager.new(select.engine)
824
- subselect.project Arel.sql(key.name)
825
- subselect.from subsubselect.as('__active_record_temp')
826
- end
843
+ def remove_timestamps_sql(table_name, options = {})
844
+ [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
845
+ end
827
846
 
828
- def supports_rename_index?
829
- mariadb? ? false : version >= '5.7.6'
830
- end
847
+ # MySQL is too stupid to create a temporary table for use subquery, so we have
848
+ # to give it some prompting in the form of a subsubquery. Ugh!
849
+ def subquery_for(key, select)
850
+ subsubselect = select.clone
851
+ subsubselect.projections = [key]
831
852
 
832
- def configure_connection
833
- variables = @config.fetch(:variables, {}).stringify_keys
853
+ # Materialize subquery by adding distinct
854
+ # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
855
+ subsubselect.distinct unless select.limit || select.offset || select.orders.any?
834
856
 
835
- # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
836
- variables['sql_auto_is_null'] = 0
857
+ subselect = Arel::SelectManager.new(select.engine)
858
+ subselect.project Arel.sql(key.name)
859
+ subselect.from subsubselect.as("__active_record_temp")
860
+ end
837
861
 
838
- # Increase timeout so the server doesn't disconnect us.
839
- wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
840
- wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
841
- variables["wait_timeout"] = wait_timeout
862
+ def supports_rename_index?
863
+ mariadb? ? false : version >= "5.7.6"
864
+ end
842
865
 
843
- defaults = [':default', :default].to_set
866
+ def configure_connection
867
+ variables = @config.fetch(:variables, {}).stringify_keys
844
868
 
845
- # Make MySQL reject illegal values rather than truncating or blanking them, see
846
- # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
847
- # If the user has provided another value for sql_mode, don't replace it.
848
- if sql_mode = variables.delete('sql_mode')
849
- sql_mode = quote(sql_mode)
850
- elsif !defaults.include?(strict_mode?)
851
- if strict_mode?
852
- sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
853
- else
854
- sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
855
- sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
856
- sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
857
- end
858
- sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
859
- end
860
- sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
869
+ # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
870
+ variables["sql_auto_is_null"] = 0
861
871
 
862
- # NAMES does not have an equals sign, see
863
- # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
864
- # (trailing comma because variable_assignments will always have content)
865
- if @config[:encoding]
866
- encoding = "NAMES #{@config[:encoding]}"
867
- encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
868
- encoding << ", "
869
- end
872
+ # Increase timeout so the server doesn't disconnect us.
873
+ wait_timeout = @config[:wait_timeout]
874
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
875
+ variables["wait_timeout"] = self.class.type_cast_config_to_integer(wait_timeout)
870
876
 
871
- # Gather up all of the SET variables...
872
- variable_assignments = variables.map do |k, v|
873
- if defaults.include?(v)
874
- "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
875
- elsif !v.nil?
876
- "@@SESSION.#{k} = #{quote(v)}"
877
+ defaults = [":default", :default].to_set
878
+
879
+ # Make MySQL reject illegal values rather than truncating or blanking them, see
880
+ # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
881
+ # If the user has provided another value for sql_mode, don't replace it.
882
+ if sql_mode = variables.delete("sql_mode")
883
+ sql_mode = quote(sql_mode)
884
+ elsif !defaults.include?(strict_mode?)
885
+ if strict_mode?
886
+ sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
887
+ else
888
+ sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
889
+ sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
890
+ sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
891
+ end
892
+ sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
893
+ end
894
+ sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
895
+
896
+ # NAMES does not have an equals sign, see
897
+ # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
898
+ # (trailing comma because variable_assignments will always have content)
899
+ if @config[:encoding]
900
+ encoding = "NAMES #{@config[:encoding]}"
901
+ encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
902
+ encoding << ", "
877
903
  end
878
- # or else nil; compact to clear nils out
879
- end.compact.join(', ')
880
904
 
881
- # ...and send them all in one query
882
- @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
883
- end
905
+ # Gather up all of the SET variables...
906
+ variable_assignments = variables.map do |k, v|
907
+ if defaults.include?(v)
908
+ "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
909
+ elsif !v.nil?
910
+ "@@SESSION.#{k} = #{quote(v)}"
911
+ end
912
+ # or else nil; compact to clear nils out
913
+ end.compact.join(", ")
884
914
 
885
- def column_definitions(table_name) # :nodoc:
886
- execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
887
- each_hash(result)
915
+ # ...and send them all in one query
916
+ execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
888
917
  end
889
- end
890
918
 
891
- def extract_foreign_key_action(specifier) # :nodoc:
892
- case specifier
893
- when 'CASCADE'; :cascade
894
- when 'SET NULL'; :nullify
919
+ def column_definitions(table_name) # :nodoc:
920
+ execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
921
+ each_hash(result)
922
+ end
895
923
  end
896
- end
897
-
898
- def create_table_info(table_name) # :nodoc:
899
- select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
900
- end
901
924
 
902
- def create_table_definition(*args) # :nodoc:
903
- MySQL::TableDefinition.new(*args)
904
- end
925
+ def extract_foreign_key_action(specifier) # :nodoc:
926
+ case specifier
927
+ when "CASCADE"; :cascade
928
+ when "SET NULL"; :nullify
929
+ end
930
+ end
905
931
 
906
- def extract_schema_qualified_name(string) # :nodoc:
907
- schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/).map { |s| quote(s) }
908
- schema, name = "DATABASE()", schema unless name
909
- [schema, name]
910
- end
932
+ def create_table_info(table_name) # :nodoc:
933
+ select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
934
+ end
911
935
 
912
- def integer_to_sql(limit) # :nodoc:
913
- case limit
914
- when 1; 'tinyint'
915
- when 2; 'smallint'
916
- when 3; 'mediumint'
917
- when nil, 4; 'int'
918
- when 5..8; 'bigint'
919
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
936
+ def create_table_definition(*args) # :nodoc:
937
+ MySQL::TableDefinition.new(*args)
920
938
  end
921
- end
922
939
 
923
- def text_to_sql(limit) # :nodoc:
924
- case limit
925
- when 0..0xff; 'tinytext'
926
- when nil, 0x100..0xffff; 'text'
927
- when 0x10000..0xffffff; 'mediumtext'
928
- when 0x1000000..0xffffffff; 'longtext'
929
- else raise(ActiveRecordError, "No text type has byte length #{limit}")
940
+ def mismatched_foreign_key(message)
941
+ parts = message.scan(/`(\w+)`[ $)]/).flatten
942
+ MismatchedForeignKey.new(
943
+ self,
944
+ message: message,
945
+ table: parts[0],
946
+ foreign_key: parts[1],
947
+ target_table: parts[2],
948
+ primary_key: parts[3],
949
+ )
930
950
  end
931
- end
932
951
 
933
- def binary_to_sql(limit) # :nodoc:
934
- case limit
935
- when 0..0xff; 'tinyblob'
936
- when nil, 0x100..0xffff; 'blob'
937
- when 0x10000..0xffffff; 'mediumblob'
938
- when 0x1000000..0xffffffff; 'longblob'
939
- else raise(ActiveRecordError, "No binary type has byte length #{limit}")
952
+ def extract_schema_qualified_name(string) # :nodoc:
953
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
954
+ schema, name = @config[:database], schema unless name
955
+ [schema, name]
940
956
  end
941
- end
942
957
 
943
- def version_string
944
- full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
945
- end
958
+ def integer_to_sql(limit) # :nodoc:
959
+ case limit
960
+ when 1; "tinyint"
961
+ when 2; "smallint"
962
+ when 3; "mediumint"
963
+ when nil, 4; "int"
964
+ when 5..8; "bigint"
965
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
966
+ end
967
+ end
946
968
 
947
- class MysqlJson < Type::Internal::AbstractJson # :nodoc:
948
- def changed_in_place?(raw_old_value, new_value)
949
- # Normalization is required because MySQL JSON data format includes
950
- # the space between the elements.
951
- super(serialize(deserialize(raw_old_value)), new_value)
969
+ def text_to_sql(limit) # :nodoc:
970
+ case limit
971
+ when 0..0xff; "tinytext"
972
+ when nil, 0x100..0xffff; "text"
973
+ when 0x10000..0xffffff; "mediumtext"
974
+ when 0x1000000..0xffffffff; "longtext"
975
+ else raise(ActiveRecordError, "No text type has byte length #{limit}")
976
+ end
952
977
  end
953
- end
954
978
 
955
- class MysqlString < Type::String # :nodoc:
956
- def serialize(value)
957
- case value
958
- when true then MySQL::Quoting::QUOTED_TRUE
959
- when false then MySQL::Quoting::QUOTED_FALSE
960
- else super
979
+ def binary_to_sql(limit) # :nodoc:
980
+ case limit
981
+ when 0..0xff; "tinyblob"
982
+ when nil, 0x100..0xffff; "blob"
983
+ when 0x10000..0xffffff; "mediumblob"
984
+ when 0x1000000..0xffffffff; "longblob"
985
+ else raise(ActiveRecordError, "No binary type has byte length #{limit}")
961
986
  end
962
987
  end
963
988
 
964
- private
989
+ class MysqlJson < Type::Internal::AbstractJson # :nodoc:
990
+ def changed_in_place?(raw_old_value, new_value)
991
+ # Normalization is required because MySQL JSON data format includes
992
+ # the space between the elements.
993
+ super(serialize(deserialize(raw_old_value)), new_value)
994
+ end
995
+ end
965
996
 
966
- def cast_value(value)
967
- case value
968
- when true then MySQL::Quoting::QUOTED_TRUE
969
- when false then MySQL::Quoting::QUOTED_FALSE
970
- else super
997
+ class MysqlString < Type::String # :nodoc:
998
+ def serialize(value)
999
+ case value
1000
+ when true then MySQL::Quoting::QUOTED_TRUE
1001
+ when false then MySQL::Quoting::QUOTED_FALSE
1002
+ else super
1003
+ end
971
1004
  end
1005
+
1006
+ private
1007
+
1008
+ def cast_value(value)
1009
+ case value
1010
+ when true then MySQL::Quoting::QUOTED_TRUE
1011
+ when false then MySQL::Quoting::QUOTED_FALSE
1012
+ else super
1013
+ end
1014
+ end
972
1015
  end
973
- end
974
1016
 
975
- ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
976
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
977
- ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
1017
+ ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
1018
+ ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
1019
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
978
1020
  end
979
1021
  end
980
1022
  end