activerecord 5.0.0.beta3 → 5.0.0.beta4

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +225 -8
  3. data/examples/performance.rb +0 -1
  4. data/examples/simple.rb +0 -1
  5. data/lib/active_record.rb +0 -1
  6. data/lib/active_record/associations.rb +10 -6
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  9. data/lib/active_record/associations/join_dependency.rb +1 -1
  10. data/lib/active_record/associations/preloader.rb +1 -0
  11. data/lib/active_record/associations/preloader/through_association.rb +15 -8
  12. data/lib/active_record/attribute/user_provided_default.rb +10 -5
  13. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  14. data/lib/active_record/attributes.rb +3 -3
  15. data/lib/active_record/autosave_association.rb +1 -1
  16. data/lib/active_record/base.rb +2 -1
  17. data/lib/active_record/collection_cache_key.rb +1 -1
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -19
  19. data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -8
  20. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -1
  22. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  23. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +16 -2
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -4
  25. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +20 -10
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -20
  27. data/lib/active_record/connection_adapters/abstract/transaction.rb +13 -1
  28. data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -16
  29. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +119 -108
  30. data/lib/active_record/connection_adapters/column.rb +5 -6
  31. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  32. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  33. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +27 -6
  34. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -13
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -55
  36. data/lib/active_record/connection_adapters/postgresql/column.rb +0 -1
  37. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  38. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  39. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +7 -10
  40. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -25
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +59 -30
  42. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  43. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +7 -0
  44. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -56
  45. data/lib/active_record/connection_adapters/statement_pool.rb +6 -4
  46. data/lib/active_record/core.rb +13 -4
  47. data/lib/active_record/enum.rb +1 -1
  48. data/lib/active_record/errors.rb +9 -0
  49. data/lib/active_record/fixture_set/file.rb +7 -1
  50. data/lib/active_record/gem_version.rb +1 -1
  51. data/lib/active_record/locking/optimistic.rb +4 -0
  52. data/lib/active_record/log_subscriber.rb +1 -1
  53. data/lib/active_record/migration.rb +4 -4
  54. data/lib/active_record/migration/compatibility.rb +1 -1
  55. data/lib/active_record/model_schema.rb +12 -0
  56. data/lib/active_record/nested_attributes.rb +1 -1
  57. data/lib/active_record/persistence.rb +1 -1
  58. data/lib/active_record/query_cache.rb +13 -16
  59. data/lib/active_record/querying.rb +1 -1
  60. data/lib/active_record/railtie.rb +8 -11
  61. data/lib/active_record/railties/databases.rake +5 -5
  62. data/lib/active_record/reflection.rb +21 -4
  63. data/lib/active_record/relation.rb +10 -10
  64. data/lib/active_record/relation/batches.rb +29 -9
  65. data/lib/active_record/relation/delegation.rb +0 -1
  66. data/lib/active_record/relation/finder_methods.rb +27 -8
  67. data/lib/active_record/relation/predicate_builder.rb +4 -2
  68. data/lib/active_record/relation/where_clause.rb +2 -1
  69. data/lib/active_record/schema_dumper.rb +39 -24
  70. data/lib/active_record/scoping/default.rb +2 -1
  71. data/lib/active_record/suppressor.rb +5 -1
  72. data/lib/active_record/tasks/database_tasks.rb +6 -4
  73. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  74. data/lib/active_record/type.rb +1 -1
  75. data/lib/active_record/type/internal/abstract_json.rb +1 -5
  76. data/lib/active_record/type/time.rb +12 -0
  77. data/lib/active_record/validations/uniqueness.rb +1 -4
  78. data/lib/rails/generators/active_record/model/model_generator.rb +15 -11
  79. data/lib/rails/generators/active_record/model/templates/application_record.rb +2 -0
  80. metadata +11 -8
@@ -188,7 +188,10 @@ module ActiveRecord
188
188
  transaction = begin_transaction options
189
189
  yield
190
190
  rescue Exception => error
191
- rollback_transaction if transaction
191
+ if transaction
192
+ rollback_transaction
193
+ after_failure_actions(transaction, error)
194
+ end
192
195
  raise
193
196
  ensure
194
197
  unless error
@@ -214,7 +217,16 @@ module ActiveRecord
214
217
  end
215
218
 
216
219
  private
220
+
217
221
  NULL_TRANSACTION = NullTransaction.new
222
+
223
+ # Deallocate invalidated prepared statements outside of the transaction
224
+ def after_failure_actions(transaction, error)
225
+ return unless transaction.is_a?(RealTransaction)
226
+ return unless error.is_a?(ActiveRecord::PreparedStatementCacheExpired)
227
+ @connection.clear_cache!
228
+ end
229
+
218
230
  end
219
231
  end
220
232
  end
@@ -27,7 +27,6 @@ module ActiveRecord
27
27
 
28
28
  autoload_at 'active_record/connection_adapters/abstract/connection_pool' do
29
29
  autoload :ConnectionHandler
30
- autoload :ConnectionManagement
31
30
  end
32
31
 
33
32
  autoload_under 'abstract' do
@@ -68,6 +67,7 @@ module ActiveRecord
68
67
  include QueryCache
69
68
  include ActiveSupport::Callbacks
70
69
  include ColumnDumper
70
+ include Savepoints
71
71
 
72
72
  SIMPLE_INT = /\A\d+\z/
73
73
 
@@ -105,8 +105,15 @@ module ActiveRecord
105
105
  @config = config
106
106
  @pool = nil
107
107
  @schema_cache = SchemaCache.new self
108
- @visitor = nil
109
- @prepared_statements = false
108
+ @quoted_column_names, @quoted_table_names = {}, {}
109
+ @visitor = arel_visitor
110
+
111
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
112
+ @prepared_statements = true
113
+ @visitor.extend(DetermineIfPreparableVisitor)
114
+ else
115
+ @prepared_statements = false
116
+ end
110
117
  end
111
118
 
112
119
  class Version
@@ -142,8 +149,12 @@ module ActiveRecord
142
149
  end
143
150
  end
144
151
 
152
+ def arel_visitor # :nodoc:
153
+ Arel::Visitors::ToSql.new(self)
154
+ end
155
+
145
156
  def valid_type?(type)
146
- true
157
+ false
147
158
  end
148
159
 
149
160
  def schema_creation
@@ -237,6 +248,11 @@ module ActiveRecord
237
248
  false
238
249
  end
239
250
 
251
+ # Does this adapter support expression indices?
252
+ def supports_expression_index?
253
+ false
254
+ end
255
+
240
256
  # Does this adapter support explain?
241
257
  def supports_explain?
242
258
  false
@@ -278,6 +294,21 @@ module ActiveRecord
278
294
  false
279
295
  end
280
296
 
297
+ # Does this adapter support metadata comments on database objects (tables, columns, indexes)?
298
+ def supports_comments?
299
+ false
300
+ end
301
+
302
+ # Can comments for tables, columns, and indexes be specified in create/alter table statements?
303
+ def supports_comments_in_create?
304
+ false
305
+ end
306
+
307
+ # Does this adapter support multi-value insert?
308
+ def supports_multi_insert?
309
+ true
310
+ end
311
+
281
312
  # This is meant to be implemented by the adapters that support extensions
282
313
  def disable_extension(name)
283
314
  end
@@ -379,12 +410,6 @@ module ActiveRecord
379
410
  @connection
380
411
  end
381
412
 
382
- def create_savepoint(name = nil)
383
- end
384
-
385
- def release_savepoint(name = nil)
386
- end
387
-
388
413
  def case_sensitive_comparison(table, attribute, column, value)
389
414
  if value.nil?
390
415
  table[attribute].eq(value)
@@ -406,10 +431,6 @@ module ActiveRecord
406
431
  end
407
432
  private :can_perform_case_insensitive_comparison_for?
408
433
 
409
- def current_savepoint_name
410
- current_transaction.savepoint_name
411
- end
412
-
413
434
  # Check the connection back in to the connection pool
414
435
  def close
415
436
  pool.checkin self
@@ -421,8 +442,8 @@ module ActiveRecord
421
442
  end
422
443
  end
423
444
 
424
- def new_column(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil)
425
- Column.new(name, default, sql_type_metadata, null, default_function, collation)
445
+ def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil) # :nodoc:
446
+ Column.new(name, default, sql_type_metadata, null, table_name, default_function, collation)
426
447
  end
427
448
 
428
449
  def lookup_cast_type(sql_type) # :nodoc:
@@ -1,6 +1,8 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
+ require 'active_record/connection_adapters/statement_pool'
2
3
  require 'active_record/connection_adapters/mysql/column'
3
4
  require 'active_record/connection_adapters/mysql/explain_pretty_printer'
5
+ require 'active_record/connection_adapters/mysql/quoting'
4
6
  require 'active_record/connection_adapters/mysql/schema_creation'
5
7
  require 'active_record/connection_adapters/mysql/schema_definitions'
6
8
  require 'active_record/connection_adapters/mysql/schema_dumper'
@@ -11,8 +13,8 @@ require 'active_support/core_ext/string/strip'
11
13
  module ActiveRecord
12
14
  module ConnectionAdapters
13
15
  class AbstractMysqlAdapter < AbstractAdapter
16
+ include MySQL::Quoting
14
17
  include MySQL::ColumnDumper
15
- include Savepoints
16
18
 
17
19
  def update_table_definition(table_name, base) # :nodoc:
18
20
  MySQL::Table.new(table_name, base)
@@ -22,6 +24,10 @@ module ActiveRecord
22
24
  MySQL::SchemaCreation.new(self)
23
25
  end
24
26
 
27
+ def arel_visitor # :nodoc:
28
+ Arel::Visitors::MySQL.new(self)
29
+ end
30
+
25
31
  ##
26
32
  # :singleton-method:
27
33
  # By default, the Mysql2Adapter will consider all columns of type <tt>tinyint(1)</tt>
@@ -32,8 +38,6 @@ module ActiveRecord
32
38
  class_attribute :emulate_booleans
33
39
  self.emulate_booleans = true
34
40
 
35
- QUOTED_TRUE, QUOTED_FALSE = '1', '0'
36
-
37
41
  NATIVE_DATABASE_TYPES = {
38
42
  primary_key: "int auto_increment PRIMARY KEY",
39
43
  string: { name: "varchar", limit: 255 },
@@ -52,18 +56,16 @@ module ActiveRecord
52
56
  INDEX_TYPES = [:fulltext, :spatial]
53
57
  INDEX_USINGS = [:btree, :hash]
54
58
 
59
+ class StatementPool < ConnectionAdapters::StatementPool
60
+ private def dealloc(stmt)
61
+ stmt[:stmt].close
62
+ end
63
+ end
64
+
55
65
  def initialize(connection, logger, connection_options, config)
56
66
  super(connection, logger, config)
57
- @quoted_column_names, @quoted_table_names = {}, {}
58
67
 
59
- @visitor = Arel::Visitors::MySQL.new self
60
-
61
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
62
- @prepared_statements = true
63
- @visitor.extend(DetermineIfPreparableVisitor)
64
- else
65
- @prepared_statements = false
66
- end
68
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
67
69
 
68
70
  if version < '5.0.0'
69
71
  raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.0."
@@ -78,10 +80,14 @@ module ActiveRecord
78
80
  }
79
81
  end
80
82
 
81
- def version
83
+ def version #:nodoc:
82
84
  @version ||= Version.new(full_version.match(/^\d+\.\d+\.\d+/)[0])
83
85
  end
84
86
 
87
+ def mariadb? # :nodoc:
88
+ full_version =~ /mariadb/i
89
+ end
90
+
85
91
  # Returns true, since this connection adapter supports migrations.
86
92
  def supports_migrations?
87
93
  true
@@ -95,6 +101,12 @@ module ActiveRecord
95
101
  true
96
102
  end
97
103
 
104
+ # Returns true, since this connection adapter supports prepared statement
105
+ # caching.
106
+ def supports_statement_cache?
107
+ true
108
+ end
109
+
98
110
  # Technically MySQL allows to create indexes with the sort order syntax
99
111
  # but at the moment (5.5) it doesn't yet implement them
100
112
  def supports_index_sort_order?
@@ -122,7 +134,11 @@ module ActiveRecord
122
134
  end
123
135
 
124
136
  def supports_datetime_with_precision?
125
- version >= '5.6.4'
137
+ if mariadb?
138
+ version >= '5.3.0'
139
+ else
140
+ version >= '5.6.4'
141
+ end
126
142
  end
127
143
 
128
144
  def supports_advisory_locks?
@@ -153,8 +169,8 @@ module ActiveRecord
153
169
  raise NotImplementedError
154
170
  end
155
171
 
156
- def new_column(field, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil) # :nodoc:
157
- MySQL::Column.new(field, default, sql_type_metadata, null, default_function, collation)
172
+ def new_column(*args) #:nodoc:
173
+ MySQL::Column.new(*args)
158
174
  end
159
175
 
160
176
  # Must return the MySQL error number from the exception, if the exception has an
@@ -163,48 +179,6 @@ module ActiveRecord
163
179
  raise NotImplementedError
164
180
  end
165
181
 
166
- # QUOTING ==================================================
167
-
168
- def _quote(value) # :nodoc:
169
- if value.is_a?(Type::Binary::Data)
170
- "x'#{value.hex}'"
171
- else
172
- super
173
- end
174
- end
175
-
176
- def quote_column_name(name) #:nodoc:
177
- @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
178
- end
179
-
180
- def quote_table_name(name) #:nodoc:
181
- @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
182
- end
183
-
184
- def quoted_true
185
- QUOTED_TRUE
186
- end
187
-
188
- def unquoted_true
189
- 1
190
- end
191
-
192
- def quoted_false
193
- QUOTED_FALSE
194
- end
195
-
196
- def unquoted_false
197
- 0
198
- end
199
-
200
- def quoted_date(value)
201
- if supports_datetime_with_precision?
202
- super
203
- else
204
- super.sub(/\.\d{6}\z/, '')
205
- end
206
- end
207
-
208
182
  # REFERENTIAL INTEGRITY ====================================
209
183
 
210
184
  def disable_referential_integrity #:nodoc:
@@ -218,6 +192,14 @@ module ActiveRecord
218
192
  end
219
193
  end
220
194
 
195
+ # CONNECTION MANAGEMENT ====================================
196
+
197
+ # Clears the prepared statements cache.
198
+ def clear_cache!
199
+ reload_type_map
200
+ @statements.clear
201
+ end
202
+
221
203
  #--
222
204
  # DATABASE STATEMENTS ======================================
223
205
  #++
@@ -231,11 +213,6 @@ module ActiveRecord
231
213
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
232
214
  end
233
215
 
234
- def clear_cache!
235
- super
236
- reload_type_map
237
- end
238
-
239
216
  # Executes the SQL statement in the context of this connection.
240
217
  def execute(sql, name = nil)
241
218
  log(sql, name) { @connection.query(sql) }
@@ -301,9 +278,9 @@ module ActiveRecord
301
278
  # create_database 'matt_development', charset: :big5
302
279
  def create_database(name, options = {})
303
280
  if options[:collation]
304
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
281
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')} COLLATE #{quote_table_name(options[:collation])}"
305
282
  else
306
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
283
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
307
284
  end
308
285
  end
309
286
 
@@ -312,7 +289,7 @@ module ActiveRecord
312
289
  # Example:
313
290
  # drop_database('sebastian_development')
314
291
  def drop_database(name) #:nodoc:
315
- execute "DROP DATABASE IF EXISTS `#{name}`"
292
+ execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
316
293
  end
317
294
 
318
295
  def current_database
@@ -370,8 +347,7 @@ module ActiveRecord
370
347
  def data_source_exists?(table_name)
371
348
  return false unless table_name.present?
372
349
 
373
- schema, name = table_name.to_s.split('.', 2)
374
- schema, name = @config[:database], schema unless name # A table was provided without a schema
350
+ schema, name = extract_schema_qualified_name(table_name)
375
351
 
376
352
  sql = "SELECT table_name FROM information_schema.tables "
377
353
  sql << "WHERE table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
@@ -386,8 +362,7 @@ module ActiveRecord
386
362
  def view_exists?(view_name) # :nodoc:
387
363
  return false unless view_name.present?
388
364
 
389
- schema, name = view_name.to_s.split('.', 2)
390
- schema, name = @config[:database], schema unless name # A view was provided without a schema
365
+ schema, name = extract_schema_qualified_name(view_name)
391
366
 
392
367
  sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
393
368
  sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
@@ -408,7 +383,7 @@ module ActiveRecord
408
383
  mysql_index_type = row[:Index_type].downcase.to_sym
409
384
  index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
410
385
  index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
411
- indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
386
+ indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using, row[:Index_comment].presence)
412
387
  end
413
388
 
414
389
  indexes.last.columns << row[:Column_name]
@@ -421,21 +396,28 @@ module ActiveRecord
421
396
 
422
397
  # Returns an array of +Column+ objects for the table specified by +table_name+.
423
398
  def columns(table_name) # :nodoc:
424
- sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
425
- execute_and_free(sql, 'SCHEMA') do |result|
426
- each_hash(result).map do |field|
427
- type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
428
- if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP"
429
- new_column(field[:Field], nil, type_metadata, field[:Null] == "YES", field[:Default], field[:Collation])
430
- else
431
- new_column(field[:Field], field[:Default], type_metadata, field[:Null] == "YES", nil, field[:Collation])
432
- end
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] == "CURRENT_TIMESTAMP"
403
+ default, default_function = nil, field[:Default]
404
+ else
405
+ default, default_function = field[:Default], nil
433
406
  end
407
+ new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
434
408
  end
435
409
  end
436
410
 
437
- def create_table(table_name, options = {}) #:nodoc:
438
- super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
411
+ def table_comment(table_name) # :nodoc:
412
+ select_value(<<-SQL.strip_heredoc, 'SCHEMA')
413
+ SELECT table_comment
414
+ FROM information_schema.tables
415
+ WHERE table_name=#{quote(table_name)}
416
+ SQL
417
+ end
418
+
419
+ def create_table(table_name, **options) #:nodoc:
420
+ super(table_name, options: 'ENGINE=InnoDB', **options)
439
421
  end
440
422
 
441
423
  def bulk_change_table(table_name, operations) #:nodoc:
@@ -518,11 +500,17 @@ module ActiveRecord
518
500
  end
519
501
 
520
502
  def add_index(table_name, column_name, options = {}) #:nodoc:
521
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
522
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
503
+ index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
504
+ sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
505
+ sql << " COMMENT #{quote(comment)}" if comment
506
+ execute sql
523
507
  end
524
508
 
525
509
  def foreign_keys(table_name)
510
+ raise ArgumentError unless table_name.present?
511
+
512
+ schema, name = extract_schema_qualified_name(table_name)
513
+
526
514
  fk_info = select_all <<-SQL.strip_heredoc
527
515
  SELECT fk.referenced_table_name as 'to_table'
528
516
  ,fk.referenced_column_name as 'primary_key'
@@ -530,8 +518,8 @@ module ActiveRecord
530
518
  ,fk.constraint_name as 'name'
531
519
  FROM information_schema.key_column_usage fk
532
520
  WHERE fk.referenced_column_name is not null
533
- AND fk.table_schema = '#{@config[:database]}'
534
- AND fk.table_name = '#{table_name}'
521
+ AND fk.table_schema = #{quote(schema)}
522
+ AND fk.table_name = #{quote(name)}
535
523
  SQL
536
524
 
537
525
  create_table_info = create_table_info(table_name)
@@ -557,7 +545,12 @@ module ActiveRecord
557
545
  raw_table_options = create_table_info.sub(/\A.*\n\) /m, '').sub(/\n\/\*!.*\*\/\n\z/m, '').strip
558
546
 
559
547
  # strip AUTO_INCREMENT
560
- raw_table_options.sub(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
548
+ raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
549
+
550
+ # strip COMMENT
551
+ raw_table_options.sub!(/ COMMENT='.+'/, '')
552
+
553
+ raw_table_options
561
554
  end
562
555
 
563
556
  # Maps logical Rails types to MySQL-specific data types.
@@ -585,8 +578,7 @@ module ActiveRecord
585
578
 
586
579
  # SHOW VARIABLES LIKE 'name'
587
580
  def show_variable(name)
588
- variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
589
- variables.first['Value'] unless variables.empty?
581
+ select_value("SELECT @@#{name}", 'SCHEMA')
590
582
  rescue ActiveRecord::StatementInvalid
591
583
  nil
592
584
  end
@@ -594,8 +586,7 @@ module ActiveRecord
594
586
  def primary_keys(table_name) # :nodoc:
595
587
  raise ArgumentError unless table_name.present?
596
588
 
597
- schema, name = table_name.to_s.split('.', 2)
598
- schema, name = @config[:database], schema unless name # A table was provided without a schema
589
+ schema, name = extract_schema_qualified_name(table_name)
599
590
 
600
591
  select_values(<<-SQL.strip_heredoc, 'SCHEMA')
601
592
  SELECT column_name
@@ -608,10 +599,10 @@ module ActiveRecord
608
599
  end
609
600
 
610
601
  def case_sensitive_comparison(table, attribute, column, value)
611
- if value.nil? || column.case_sensitive?
612
- super
613
- else
602
+ if !value.nil? && column.collation && !column.case_sensitive?
614
603
  table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
604
+ else
605
+ super
615
606
  end
616
607
  end
617
608
 
@@ -668,7 +659,7 @@ module ActiveRecord
668
659
  register_integer_type m, %r(^smallint)i, limit: 2
669
660
  register_integer_type m, %r(^tinyint)i, limit: 1
670
661
 
671
- m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
662
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
672
663
  m.alias_type %r(year)i, 'integer'
673
664
  m.alias_type %r(bit)i, 'binary'
674
665
 
@@ -738,6 +729,8 @@ module ActiveRecord
738
729
  RecordNotUnique.new(message)
739
730
  when 1452
740
731
  InvalidForeignKey.new(message)
732
+ when 1406
733
+ ValueTooLong.new(message)
741
734
  else
742
735
  super
743
736
  end
@@ -823,10 +816,6 @@ module ActiveRecord
823
816
  subselect.from subsubselect.as('__active_record_temp')
824
817
  end
825
818
 
826
- def mariadb?
827
- full_version =~ /mariadb/i
828
- end
829
-
830
819
  def supports_rename_index?
831
820
  mariadb? ? false : version >= '5.7.6'
832
821
  end
@@ -847,9 +836,19 @@ module ActiveRecord
847
836
  # Make MySQL reject illegal values rather than truncating or blanking them, see
848
837
  # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
849
838
  # If the user has provided another value for sql_mode, don't replace it.
850
- unless variables.has_key?('sql_mode') || defaults.include?(@config[:strict])
851
- variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
839
+ if sql_mode = variables.delete('sql_mode')
840
+ sql_mode = quote(sql_mode)
841
+ elsif !defaults.include?(strict_mode?)
842
+ if strict_mode?
843
+ sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
844
+ else
845
+ sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
846
+ sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
847
+ sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
848
+ end
849
+ sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
852
850
  end
851
+ sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
853
852
 
854
853
  # NAMES does not have an equals sign, see
855
854
  # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
@@ -871,7 +870,13 @@ module ActiveRecord
871
870
  end.compact.join(', ')
872
871
 
873
872
  # ...and send them all in one query
874
- @connection.query "SET #{encoding} #{variable_assignments}"
873
+ @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
874
+ end
875
+
876
+ def column_definitions(table_name) # :nodoc:
877
+ execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
878
+ each_hash(result)
879
+ end
875
880
  end
876
881
 
877
882
  def extract_foreign_key_action(structure, name, action) # :nodoc:
@@ -891,8 +896,14 @@ module ActiveRecord
891
896
  create_table_info_cache[table_name] ||= select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
892
897
  end
893
898
 
894
- def create_table_definition(name, temporary = false, options = nil, as = nil) # :nodoc:
895
- MySQL::TableDefinition.new(name, temporary, options, as)
899
+ def create_table_definition(*args) # :nodoc:
900
+ MySQL::TableDefinition.new(*args)
901
+ end
902
+
903
+ def extract_schema_qualified_name(string) # :nodoc:
904
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
905
+ schema, name = @config[:database], schema unless name
906
+ [schema, name]
896
907
  end
897
908
 
898
909
  def integer_to_sql(limit) # :nodoc:
@@ -937,8 +948,8 @@ module ActiveRecord
937
948
  class MysqlString < Type::String # :nodoc:
938
949
  def serialize(value)
939
950
  case value
940
- when true then "1"
941
- when false then "0"
951
+ when true then MySQL::Quoting::QUOTED_TRUE
952
+ when false then MySQL::Quoting::QUOTED_FALSE
942
953
  else super
943
954
  end
944
955
  end
@@ -947,8 +958,8 @@ module ActiveRecord
947
958
 
948
959
  def cast_value(value)
949
960
  case value
950
- when true then "1"
951
- when false then "0"
961
+ when true then MySQL::Quoting::QUOTED_TRUE
962
+ when false then MySQL::Quoting::QUOTED_FALSE
952
963
  else super
953
964
  end
954
965
  end