activerecord 4.1.15 → 4.2.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 (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2176
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/association_scope.rb +53 -21
  7. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  8. data/lib/active_record/associations/builder/association.rb +16 -5
  9. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  10. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  11. data/lib/active_record/associations/builder/has_one.rb +2 -2
  12. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  13. data/lib/active_record/associations/collection_association.rb +32 -44
  14. data/lib/active_record/associations/collection_proxy.rb +1 -10
  15. data/lib/active_record/associations/has_many_association.rb +60 -14
  16. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  17. data/lib/active_record/associations/has_one_association.rb +0 -1
  18. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  19. data/lib/active_record/associations/join_dependency.rb +7 -9
  20. data/lib/active_record/associations/preloader/association.rb +9 -5
  21. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/singular_association.rb +16 -1
  24. data/lib/active_record/associations/through_association.rb +6 -22
  25. data/lib/active_record/associations.rb +58 -33
  26. data/lib/active_record/attribute.rb +131 -0
  27. data/lib/active_record/attribute_assignment.rb +19 -11
  28. data/lib/active_record/attribute_decorators.rb +66 -0
  29. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  30. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  31. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  32. data/lib/active_record/attribute_methods/read.rb +14 -57
  33. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  35. data/lib/active_record/attribute_methods/write.rb +8 -23
  36. data/lib/active_record/attribute_methods.rb +53 -90
  37. data/lib/active_record/attribute_set/builder.rb +32 -0
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attributes.rb +122 -0
  40. data/lib/active_record/autosave_association.rb +11 -21
  41. data/lib/active_record/base.rb +9 -19
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  52. data/lib/active_record/connection_adapters/column.rb +13 -244
  53. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  55. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  56. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  59. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  60. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  85. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  86. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  87. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  88. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  89. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  90. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  91. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  93. data/lib/active_record/connection_handling.rb +1 -1
  94. data/lib/active_record/core.rb +119 -22
  95. data/lib/active_record/counter_cache.rb +60 -6
  96. data/lib/active_record/enum.rb +9 -10
  97. data/lib/active_record/errors.rb +27 -26
  98. data/lib/active_record/explain.rb +1 -1
  99. data/lib/active_record/fixtures.rb +52 -45
  100. data/lib/active_record/gem_version.rb +3 -3
  101. data/lib/active_record/inheritance.rb +33 -8
  102. data/lib/active_record/integration.rb +4 -4
  103. data/lib/active_record/locking/optimistic.rb +34 -16
  104. data/lib/active_record/migration/command_recorder.rb +19 -2
  105. data/lib/active_record/migration/join_table.rb +1 -1
  106. data/lib/active_record/migration.rb +22 -32
  107. data/lib/active_record/model_schema.rb +39 -48
  108. data/lib/active_record/nested_attributes.rb +8 -18
  109. data/lib/active_record/persistence.rb +39 -22
  110. data/lib/active_record/query_cache.rb +3 -3
  111. data/lib/active_record/querying.rb +1 -8
  112. data/lib/active_record/railtie.rb +17 -10
  113. data/lib/active_record/railties/databases.rake +47 -42
  114. data/lib/active_record/readonly_attributes.rb +0 -1
  115. data/lib/active_record/reflection.rb +225 -92
  116. data/lib/active_record/relation/batches.rb +0 -2
  117. data/lib/active_record/relation/calculations.rb +28 -32
  118. data/lib/active_record/relation/delegation.rb +1 -1
  119. data/lib/active_record/relation/finder_methods.rb +42 -20
  120. data/lib/active_record/relation/merger.rb +0 -1
  121. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  122. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/query_methods.rb +98 -62
  125. data/lib/active_record/relation/spawn_methods.rb +6 -7
  126. data/lib/active_record/relation.rb +35 -11
  127. data/lib/active_record/result.rb +16 -9
  128. data/lib/active_record/sanitization.rb +8 -1
  129. data/lib/active_record/schema.rb +0 -1
  130. data/lib/active_record/schema_dumper.rb +51 -9
  131. data/lib/active_record/schema_migration.rb +4 -0
  132. data/lib/active_record/scoping/default.rb +5 -4
  133. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  134. data/lib/active_record/statement_cache.rb +79 -5
  135. data/lib/active_record/store.rb +5 -5
  136. data/lib/active_record/tasks/database_tasks.rb +37 -5
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  138. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  139. data/lib/active_record/timestamp.rb +9 -7
  140. data/lib/active_record/transactions.rb +35 -21
  141. data/lib/active_record/type/binary.rb +40 -0
  142. data/lib/active_record/type/boolean.rb +19 -0
  143. data/lib/active_record/type/date.rb +46 -0
  144. data/lib/active_record/type/date_time.rb +43 -0
  145. data/lib/active_record/type/decimal.rb +40 -0
  146. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  147. data/lib/active_record/type/float.rb +19 -0
  148. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  149. data/lib/active_record/type/integer.rb +23 -0
  150. data/lib/active_record/type/mutable.rb +16 -0
  151. data/lib/active_record/type/numeric.rb +36 -0
  152. data/lib/active_record/type/serialized.rb +51 -0
  153. data/lib/active_record/type/string.rb +36 -0
  154. data/lib/active_record/type/text.rb +11 -0
  155. data/lib/active_record/type/time.rb +26 -0
  156. data/lib/active_record/type/time_value.rb +38 -0
  157. data/lib/active_record/type/type_map.rb +48 -0
  158. data/lib/active_record/type/value.rb +101 -0
  159. data/lib/active_record/type.rb +20 -0
  160. data/lib/active_record/validations/uniqueness.rb +9 -23
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record.rb +2 -1
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -6,12 +6,29 @@ module ActiveRecord
6
6
  include Savepoints
7
7
 
8
8
  class SchemaCreation < AbstractAdapter::SchemaCreation
9
-
10
9
  def visit_AddColumn(o)
11
10
  add_column_position!(super, column_options(o))
12
11
  end
13
12
 
14
13
  private
14
+
15
+ def visit_DropForeignKey(name)
16
+ "DROP FOREIGN KEY #{name}"
17
+ end
18
+
19
+ def visit_TableDefinition(o)
20
+ name = o.name
21
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
22
+
23
+ statements = o.columns.map { |c| accept c }
24
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
25
+
26
+ create_sql << "(#{statements.join(', ')}) " if statements.present?
27
+ create_sql << "#{o.options}"
28
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
29
+ create_sql
30
+ end
31
+
15
32
  def visit_ChangeColumnDefinition(o)
16
33
  column = o.column
17
34
  options = o.options
@@ -29,6 +46,11 @@ module ActiveRecord
29
46
  end
30
47
  sql
31
48
  end
49
+
50
+ def index_in_create(table_name, column_name, options)
51
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
52
+ "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
53
+ end
32
54
  end
33
55
 
34
56
  def schema_creation
@@ -38,29 +60,25 @@ module ActiveRecord
38
60
  class Column < ConnectionAdapters::Column # :nodoc:
39
61
  attr_reader :collation, :strict, :extra
40
62
 
41
- def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
63
+ def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
42
64
  @strict = strict
43
65
  @collation = collation
44
66
  @extra = extra
45
- super(name, default, sql_type, null)
67
+ super(name, default, cast_type, sql_type, null)
68
+ assert_valid_default(default)
69
+ extract_default
46
70
  end
47
71
 
48
- def extract_default(default)
72
+ def extract_default
49
73
  if blob_or_text_column?
50
- if default.blank?
51
- null || strict ? nil : ''
52
- else
53
- raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
54
- end
55
- elsif missing_default_forged_as_empty_string?(default)
56
- nil
57
- else
58
- super
74
+ @default = null || strict ? nil : ''
75
+ elsif missing_default_forged_as_empty_string?(@default)
76
+ @default = nil
59
77
  end
60
78
  end
61
79
 
62
80
  def has_default?
63
- return false if blob_or_text_column? #mysql forbids defaults on blob and text columns
81
+ return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
64
82
  super
65
83
  end
66
84
 
@@ -68,56 +86,12 @@ module ActiveRecord
68
86
  sql_type =~ /blob/i || type == :text
69
87
  end
70
88
 
71
- # Must return the relevant concrete adapter
72
- def adapter
73
- raise NotImplementedError
74
- end
75
-
76
89
  def case_sensitive?
77
90
  collation && !collation.match(/_ci$/)
78
91
  end
79
92
 
80
93
  private
81
94
 
82
- def simplified_type(field_type)
83
- return :boolean if adapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
84
-
85
- case field_type
86
- when /enum/i, /set/i then :string
87
- when /year/i then :integer
88
- when /bit/i then :binary
89
- else
90
- super
91
- end
92
- end
93
-
94
- def extract_limit(sql_type)
95
- case sql_type
96
- when /^enum\((.+)\)/i
97
- $1.split(',').map{|enum| enum.strip.length - 2}.max
98
- when /blob|text/i
99
- case sql_type
100
- when /tiny/i
101
- 255
102
- when /medium/i
103
- 16777215
104
- when /long/i
105
- 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
106
- else
107
- super # we could return 65535 here, but we leave it undecorated by default
108
- end
109
- when /^bigint/i; 8
110
- when /^int/i; 4
111
- when /^mediumint/i; 3
112
- when /^smallint/i; 2
113
- when /^tinyint/i; 1
114
- when /^float/i; 24
115
- when /^double/i; 53
116
- else
117
- super
118
- end
119
- end
120
-
121
95
  # MySQL misreports NOT NULL column default when none is given.
122
96
  # We can't detect this for columns which may have a legitimate ''
123
97
  # default (string) but we can for others (integer, datetime, boolean,
@@ -128,6 +102,12 @@ module ActiveRecord
128
102
  def missing_default_forged_as_empty_string?(default)
129
103
  type != :string && !null && default == ''
130
104
  end
105
+
106
+ def assert_valid_default(default)
107
+ if blob_or_text_column? && default.present?
108
+ raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
109
+ end
110
+ end
131
111
  end
132
112
 
133
113
  ##
@@ -157,7 +137,6 @@ module ActiveRecord
157
137
  :float => { :name => "float" },
158
138
  :decimal => { :name => "decimal" },
159
139
  :datetime => { :name => "datetime" },
160
- :timestamp => { :name => "datetime" },
161
140
  :time => { :name => "time" },
162
141
  :date => { :name => "date" },
163
142
  :binary => { :name => "blob" },
@@ -167,21 +146,18 @@ module ActiveRecord
167
146
  INDEX_TYPES = [:fulltext, :spatial]
168
147
  INDEX_USINGS = [:btree, :hash]
169
148
 
170
- class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
171
- include Arel::Visitors::BindVisitor
172
- end
173
-
174
149
  # FIXME: Make the first parameter more similar for the two adapters
175
150
  def initialize(connection, logger, connection_options, config)
176
151
  super(connection, logger)
177
152
  @connection_options, @config = connection_options, config
178
153
  @quoted_column_names, @quoted_table_names = {}, {}
179
154
 
155
+ @visitor = Arel::Visitors::MySQL.new self
156
+
180
157
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
181
158
  @prepared_statements = true
182
- @visitor = Arel::Visitors::MySQL.new self
183
159
  else
184
- @visitor = unprepared_visitor
160
+ @prepared_statements = false
185
161
  end
186
162
  end
187
163
 
@@ -208,17 +184,6 @@ module ActiveRecord
208
184
  true
209
185
  end
210
186
 
211
- def type_cast(value, column)
212
- case value
213
- when TrueClass
214
- 1
215
- when FalseClass
216
- 0
217
- else
218
- super
219
- end
220
- end
221
-
222
187
  # MySQL 4 technically support transaction isolation, but it is affected by a bug
223
188
  # where the transaction level gets persisted for the whole session:
224
189
  #
@@ -227,6 +192,14 @@ module ActiveRecord
227
192
  version[0] >= 5
228
193
  end
229
194
 
195
+ def supports_indexes_in_create?
196
+ true
197
+ end
198
+
199
+ def supports_foreign_keys?
200
+ true
201
+ end
202
+
230
203
  def native_database_types
231
204
  NATIVE_DATABASE_TYPES
232
205
  end
@@ -243,12 +216,11 @@ module ActiveRecord
243
216
  raise NotImplementedError
244
217
  end
245
218
 
246
- # Overridden by the adapters to instantiate their specific Column type.
247
- def new_column(field, default, type, null, collation, extra = "") # :nodoc:
248
- Column.new(field, default, type, null, collation, extra)
219
+ def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "") # :nodoc:
220
+ Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
249
221
  end
250
222
 
251
- # Must return the Mysql error number from the exception, if the exception has an
223
+ # Must return the MySQL error number from the exception, if the exception has an
252
224
  # error number.
253
225
  def error_number(exception) # :nodoc:
254
226
  raise NotImplementedError
@@ -256,12 +228,9 @@ module ActiveRecord
256
228
 
257
229
  # QUOTING ==================================================
258
230
 
259
- def quote(value, column = nil)
260
- if value.kind_of?(String) && column && column.type == :binary
261
- s = value.unpack("H*")[0]
262
- "x'#{s}'"
263
- elsif value.kind_of?(BigDecimal)
264
- value.to_s("F")
231
+ def _quote(value) # :nodoc:
232
+ if value.is_a?(Type::Binary::Data)
233
+ "x'#{value.hex}'"
265
234
  else
266
235
  super
267
236
  end
@@ -279,10 +248,18 @@ module ActiveRecord
279
248
  QUOTED_TRUE
280
249
  end
281
250
 
251
+ def unquoted_true
252
+ 1
253
+ end
254
+
282
255
  def quoted_false
283
256
  QUOTED_FALSE
284
257
  end
285
258
 
259
+ def unquoted_false
260
+ 0
261
+ end
262
+
286
263
  # REFERENTIAL INTEGRITY ====================================
287
264
 
288
265
  def disable_referential_integrity #:nodoc:
@@ -298,6 +275,11 @@ module ActiveRecord
298
275
 
299
276
  # DATABASE STATEMENTS ======================================
300
277
 
278
+ def clear_cache!
279
+ super
280
+ reload_type_map
281
+ end
282
+
301
283
  # Executes the SQL statement in the context of this connection.
302
284
  def execute(sql, name = nil)
303
285
  log(sql, name) { @connection.query(sql) }
@@ -407,7 +389,7 @@ module ActiveRecord
407
389
  end
408
390
 
409
391
  def table_exists?(name)
410
- return false unless name
392
+ return false unless name.present?
411
393
  return true if tables(nil, nil, name).any?
412
394
 
413
395
  name = name.to_s
@@ -451,7 +433,9 @@ module ActiveRecord
451
433
  execute_and_free(sql, 'SCHEMA') do |result|
452
434
  each_hash(result).map do |field|
453
435
  field_name = set_field_encoding(field[:Field])
454
- new_column(field_name, field[:Default], field[:Type], field[:Null] == "YES", field[:Collation], field[:Extra])
436
+ sql_type = field[:Type]
437
+ cast_type = lookup_cast_type(sql_type)
438
+ new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
455
439
  end
456
440
  end
457
441
  end
@@ -461,7 +445,7 @@ module ActiveRecord
461
445
  end
462
446
 
463
447
  def bulk_change_table(table_name, operations) #:nodoc:
464
- sqls = operations.map do |command, args|
448
+ sqls = operations.flat_map do |command, args|
465
449
  table, arguments = args.shift, args
466
450
  method = :"#{command}_sql"
467
451
 
@@ -470,7 +454,7 @@ module ActiveRecord
470
454
  else
471
455
  raise "Unknown method called : #{method}(#{arguments.inspect})"
472
456
  end
473
- end.flatten.join(", ")
457
+ end.join(", ")
474
458
 
475
459
  execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
476
460
  end
@@ -525,6 +509,34 @@ module ActiveRecord
525
509
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options} #{index_algorithm}"
526
510
  end
527
511
 
512
+ def foreign_keys(table_name)
513
+ fk_info = select_all <<-SQL.strip_heredoc
514
+ SELECT fk.referenced_table_name as 'to_table'
515
+ ,fk.referenced_column_name as 'primary_key'
516
+ ,fk.column_name as 'column'
517
+ ,fk.constraint_name as 'name'
518
+ FROM information_schema.key_column_usage fk
519
+ WHERE fk.referenced_column_name is not null
520
+ AND fk.table_schema = '#{@config[:database]}'
521
+ AND fk.table_name = '#{table_name}'
522
+ SQL
523
+
524
+ create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
525
+
526
+ fk_info.map do |row|
527
+ options = {
528
+ column: row['column'],
529
+ name: row['name'],
530
+ primary_key: row['primary_key']
531
+ }
532
+
533
+ options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
534
+ options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")
535
+
536
+ ForeignKeyDefinition.new(table_name, row['to_table'], options)
537
+ end
538
+ end
539
+
528
540
  # Maps logical Rails types to MySQL-specific data types.
529
541
  def type_to_sql(type, limit = nil, precision = nil, scale = nil)
530
542
  case type.to_s
@@ -590,10 +602,19 @@ module ActiveRecord
590
602
  pk_and_sequence && pk_and_sequence.first
591
603
  end
592
604
 
593
- def case_sensitive_modifier(node)
605
+ def case_sensitive_modifier(node, table_attribute)
606
+ node = Arel::Nodes.build_quoted node, table_attribute
594
607
  Arel::Nodes::Bin.new(node)
595
608
  end
596
609
 
610
+ def case_sensitive_comparison(table, attribute, column, value)
611
+ if column.case_sensitive?
612
+ table[attribute].eq(value)
613
+ else
614
+ super
615
+ end
616
+ end
617
+
597
618
  def case_insensitive_comparison(table, attribute, column, value)
598
619
  if column.case_sensitive?
599
620
  super
@@ -616,6 +637,34 @@ module ActiveRecord
616
637
 
617
638
  protected
618
639
 
640
+ def initialize_type_map(m) # :nodoc:
641
+ super
642
+ m.register_type(%r(enum)i) do |sql_type|
643
+ limit = sql_type[/^enum\((.+)\)/i, 1]
644
+ .split(',').map{|enum| enum.strip.length - 2}.max
645
+ Type::String.new(limit: limit)
646
+ end
647
+
648
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 255)
649
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 255)
650
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 16777215)
651
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 16777215)
652
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2147483647)
653
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2147483647)
654
+ m.register_type %r(^bigint)i, Type::Integer.new(limit: 8)
655
+ m.register_type %r(^int)i, Type::Integer.new(limit: 4)
656
+ m.register_type %r(^mediumint)i, Type::Integer.new(limit: 3)
657
+ m.register_type %r(^smallint)i, Type::Integer.new(limit: 2)
658
+ m.register_type %r(^tinyint)i, Type::Integer.new(limit: 1)
659
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
660
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
661
+
662
+ m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
663
+ m.alias_type %r(set)i, 'varchar'
664
+ m.alias_type %r(year)i, 'integer'
665
+ m.alias_type %r(bit)i, 'binary'
666
+ end
667
+
619
668
  # MySQL is too stupid to create a temporary table for use subquery, so we have
620
669
  # to give it some prompting in the form of a subsubquery. Ugh!
621
670
  def subquery_for(key, select)
@@ -685,15 +734,13 @@ module ActiveRecord
685
734
  end
686
735
 
687
736
  def rename_column_sql(table_name, column_name, new_column_name)
688
- options = { name: new_column_name }
689
-
690
- if column = columns(table_name).find { |c| c.name == column_name.to_s }
691
- options[:default] = column.default
692
- options[:null] = column.null
693
- options[:auto_increment] = (column.extra == "auto_increment")
694
- else
695
- raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
696
- end
737
+ column = column_for(table_name, column_name)
738
+ options = {
739
+ name: new_column_name,
740
+ default: column.default,
741
+ null: column.null,
742
+ auto_increment: column.extra == "auto_increment"
743
+ }
697
744
 
698
745
  current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
699
746
  schema_creation.accept ChangeColumnDefinition.new column, current_type, options
@@ -743,13 +790,6 @@ module ActiveRecord
743
790
  mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
744
791
  end
745
792
 
746
- def column_for(table_name, column_name)
747
- unless column = columns(table_name).find { |c| c.name == column_name.to_s }
748
- raise "No such column: #{table_name}.#{column_name}"
749
- end
750
- column
751
- end
752
-
753
793
  def configure_connection
754
794
  variables = @config.fetch(:variables, {}).stringify_keys
755
795
 
@@ -787,6 +827,15 @@ module ActiveRecord
787
827
  # ...and send them all in one query
788
828
  @connection.query "SET #{encoding} #{variable_assignments}"
789
829
  end
830
+
831
+ def extract_foreign_key_action(structure, name, action) # :nodoc:
832
+ if structure =~ /CONSTRAINT #{quote_column_name(name)} FOREIGN KEY .* REFERENCES .* ON #{action} (CASCADE|SET NULL|RESTRICT)/
833
+ case $1
834
+ when 'CASCADE'; :cascade
835
+ when 'SET NULL'; :nullify
836
+ end
837
+ end
838
+ end
790
839
  end
791
840
  end
792
841
  end