activerecord 5.0.7 → 5.1.7

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