activerecord 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,21 +1,22 @@
1
- require 'active_support/core_ext/object/blank'
2
1
  require 'arel/visitors/bind_visitor'
3
2
 
4
3
  module ActiveRecord
5
4
  module ConnectionAdapters
6
5
  class AbstractMysqlAdapter < AbstractAdapter
7
6
  class Column < ConnectionAdapters::Column # :nodoc:
8
- attr_reader :collation
7
+ attr_reader :collation, :strict
9
8
 
10
- def initialize(name, default, sql_type = nil, null = true, collation = nil)
11
- super(name, default, sql_type, null)
9
+ def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false)
10
+ @strict = strict
12
11
  @collation = collation
12
+
13
+ super(name, default, sql_type, null)
13
14
  end
14
15
 
15
16
  def extract_default(default)
16
- if sql_type =~ /blob/i || type == :text
17
+ if blob_or_text_column?
17
18
  if default.blank?
18
- return null ? nil : ''
19
+ null || strict ? nil : ''
19
20
  else
20
21
  raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
21
22
  end
@@ -27,10 +28,14 @@ module ActiveRecord
27
28
  end
28
29
 
29
30
  def has_default?
30
- return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
31
+ return false if blob_or_text_column? #mysql forbids defaults on blob and text columns
31
32
  super
32
33
  end
33
34
 
35
+ def blob_or_text_column?
36
+ sql_type =~ /blob/i || type == :text
37
+ end
38
+
34
39
  # Must return the relevant concrete adapter
35
40
  def adapter
36
41
  raise NotImplementedError
@@ -135,7 +140,7 @@ module ActiveRecord
135
140
  @connection_options, @config = connection_options, config
136
141
  @quoted_column_names, @quoted_table_names = {}, {}
137
142
 
138
- if config.fetch(:prepared_statements) { true }
143
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
139
144
  @visitor = Arel::Visitors::MySQL.new self
140
145
  else
141
146
  @visitor = BindSubstitution.new self
@@ -170,6 +175,14 @@ module ActiveRecord
170
175
  true
171
176
  end
172
177
 
178
+ # MySQL 4 technically support transaction isolation, but it is affected by a bug
179
+ # where the transaction level gets persisted for the whole session:
180
+ #
181
+ # http://bugs.mysql.com/bug.php?id=39170
182
+ def supports_transaction_isolation?
183
+ version[0] >= 5
184
+ end
185
+
173
186
  def native_database_types
174
187
  NATIVE_DATABASE_TYPES
175
188
  end
@@ -199,8 +212,6 @@ module ActiveRecord
199
212
  if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
200
213
  s = column.class.string_to_binary(value).unpack("H*")[0]
201
214
  "x'#{s}'"
202
- elsif value.kind_of?(BigDecimal)
203
- value.to_s("F")
204
215
  else
205
216
  super
206
217
  end
@@ -253,7 +264,7 @@ module ActiveRecord
253
264
  end
254
265
 
255
266
  # MysqlAdapter has to free a result after using it, so we use this method to write
256
- # stuff in a abstract way without concerning ourselves about whether it needs to be
267
+ # stuff in an abstract way without concerning ourselves about whether it needs to be
257
268
  # explicitly freed or not.
258
269
  def execute_and_free(sql, name = nil) #:nodoc:
259
270
  yield execute(sql, name)
@@ -266,19 +277,26 @@ module ActiveRecord
266
277
 
267
278
  def begin_db_transaction
268
279
  execute "BEGIN"
269
- rescue Exception
280
+ rescue
281
+ # Transactions aren't supported
282
+ end
283
+
284
+ def begin_isolated_db_transaction(isolation)
285
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
286
+ begin_db_transaction
287
+ rescue
270
288
  # Transactions aren't supported
271
289
  end
272
290
 
273
291
  def commit_db_transaction #:nodoc:
274
292
  execute "COMMIT"
275
- rescue Exception
293
+ rescue
276
294
  # Transactions aren't supported
277
295
  end
278
296
 
279
297
  def rollback_db_transaction #:nodoc:
280
298
  execute "ROLLBACK"
281
- rescue Exception
299
+ rescue
282
300
  # Transactions aren't supported
283
301
  end
284
302
 
@@ -296,25 +314,20 @@ module ActiveRecord
296
314
 
297
315
  # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
298
316
  # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
299
- # these, we must use a subquery. However, MySQL is too stupid to create a
300
- # temporary table for this automatically, so we have to give it some prompting
301
- # in the form of a subsubquery. Ugh!
317
+ # these, we must use a subquery.
302
318
  def join_to_update(update, select) #:nodoc:
303
319
  if select.limit || select.offset || select.orders.any?
304
- subsubselect = select.clone
305
- subsubselect.projections = [update.key]
306
-
307
- subselect = Arel::SelectManager.new(select.engine)
308
- subselect.project Arel.sql(update.key.name)
309
- subselect.from subsubselect.as('__active_record_temp')
310
-
311
- update.where update.key.in(subselect)
320
+ super
312
321
  else
313
322
  update.table select.source
314
323
  update.wheres = select.constraints
315
324
  end
316
325
  end
317
326
 
327
+ def empty_insert_statement_value
328
+ "VALUES ()"
329
+ end
330
+
318
331
  # SCHEMA STATEMENTS ========================================
319
332
 
320
333
  def structure_dump #:nodoc:
@@ -324,10 +337,10 @@ module ActiveRecord
324
337
  sql = "SHOW TABLES"
325
338
  end
326
339
 
327
- select_all(sql).map { |table|
340
+ select_all(sql, 'SCHEMA').map { |table|
328
341
  table.delete('Table_type')
329
342
  sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
330
- exec_query(sql).first['Create Table'] + ";\n\n"
343
+ exec_query(sql, 'SCHEMA').first['Create Table'] + ";\n\n"
331
344
  }.join
332
345
  end
333
346
 
@@ -342,9 +355,9 @@ module ActiveRecord
342
355
  # Charset defaults to utf8.
343
356
  #
344
357
  # Example:
345
- # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
358
+ # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
346
359
  # create_database 'matt_development'
347
- # create_database 'matt_development', :charset => :big5
360
+ # create_database 'matt_development', charset: :big5
348
361
  def create_database(name, options = {})
349
362
  if options[:collation]
350
363
  execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
@@ -421,7 +434,7 @@ module ActiveRecord
421
434
  end
422
435
 
423
436
  # Returns an array of +Column+ objects for the table specified by +table_name+.
424
- def columns(table_name, name = nil)#:nodoc:
437
+ def columns(table_name)#:nodoc:
425
438
  sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
426
439
  execute_and_free(sql, 'SCHEMA') do |result|
427
440
  each_hash(result).map do |field|
@@ -455,6 +468,7 @@ module ActiveRecord
455
468
  # rename_table('octopuses', 'octopi')
456
469
  def rename_table(table_name, new_name)
457
470
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
471
+ rename_table_indexes(table_name, new_name)
458
472
  end
459
473
 
460
474
  def add_column(table_name, column_name, type, options = {})
@@ -482,11 +496,19 @@ module ActiveRecord
482
496
 
483
497
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
484
498
  execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
499
+ rename_column_indexes(table_name, column_name, new_column_name)
485
500
  end
486
501
 
487
502
  # Maps logical Rails types to MySQL-specific data types.
488
503
  def type_to_sql(type, limit = nil, precision = nil, scale = nil)
489
504
  case type.to_s
505
+ when 'binary'
506
+ case limit
507
+ when 0..0xfff; "varbinary(#{limit})"
508
+ when nil; "blob"
509
+ when 0x1000..0xffffffff; "blob(#{limit})"
510
+ else raise(ActiveRecordError, "No binary type has character length #{limit}")
511
+ end
490
512
  when 'integer'
491
513
  case limit
492
514
  when 1; 'tinyint'
@@ -519,7 +541,7 @@ module ActiveRecord
519
541
 
520
542
  # SHOW VARIABLES LIKE 'name'
521
543
  def show_variable(name)
522
- variables = select_all("SHOW VARIABLES LIKE '#{name}'")
544
+ variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
523
545
  variables.first['Value'] unless variables.empty?
524
546
  end
525
547
 
@@ -528,7 +550,7 @@ module ActiveRecord
528
550
  execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
529
551
  create_table = each_hash(result).first[:"Create Table"]
530
552
  if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
531
- keys = $1.split(",").map { |key| key.gsub(/[`"]/, "") }
553
+ keys = $1.split(",").map { |key| key.delete('`"') }
532
554
  keys.length == 1 ? [keys.first, nil] : nil
533
555
  else
534
556
  nil
@@ -558,8 +580,23 @@ module ActiveRecord
558
580
  where_sql
559
581
  end
560
582
 
583
+ def strict_mode?
584
+ self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
585
+ end
586
+
561
587
  protected
562
588
 
589
+ # MySQL is too stupid to create a temporary table for use subquery, so we have
590
+ # to give it some prompting in the form of a subsubquery. Ugh!
591
+ def subquery_for(key, select)
592
+ subsubselect = select.clone
593
+ subsubselect.projections = [key]
594
+
595
+ subselect = Arel::SelectManager.new(select.engine)
596
+ subselect.project Arel.sql(key.name)
597
+ subselect.from subsubselect.as('__active_record_temp')
598
+ end
599
+
563
600
  def add_index_length(option_strings, column_names, options = {})
564
601
  if options.is_a?(Hash) && length = options[:length]
565
602
  case length
@@ -630,16 +667,19 @@ module ActiveRecord
630
667
  raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
631
668
  end
632
669
 
633
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
670
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
634
671
  rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
635
672
  add_column_options!(rename_column_sql, options)
636
673
  rename_column_sql
637
674
  end
638
675
 
639
- def remove_column_sql(table_name, *column_names)
640
- columns_for_remove(table_name, *column_names).map {|column_name| "DROP #{column_name}" }
676
+ def remove_column_sql(table_name, column_name, type = nil, options = {})
677
+ "DROP #{quote_column_name(column_name)}"
678
+ end
679
+
680
+ def remove_columns_sql(table_name, *column_names)
681
+ column_names.map {|column_name| remove_column_sql(table_name, column_name) }
641
682
  end
642
- alias :remove_columns_sql :remove_column
643
683
 
644
684
  def add_index_sql(table_name, column_name, options = {})
645
685
  index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
@@ -671,6 +711,44 @@ module ActiveRecord
671
711
  end
672
712
  column
673
713
  end
714
+
715
+ def configure_connection
716
+ variables = @config[:variables] || {}
717
+
718
+ # By default, MySQL 'where id is null' selects the last inserted id.
719
+ # Turn this off. http://dev.rubyonrails.org/ticket/6778
720
+ variables[:sql_auto_is_null] = 0
721
+
722
+ # Increase timeout so the server doesn't disconnect us.
723
+ wait_timeout = @config[:wait_timeout]
724
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
725
+ variables[:wait_timeout] = self.class.type_cast_config_to_integer(wait_timeout)
726
+
727
+ # Make MySQL reject illegal values rather than truncating or blanking them, see
728
+ # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
729
+ # If the user has provided another value for sql_mode, don't replace it.
730
+ if strict_mode? && !variables.has_key?(:sql_mode)
731
+ variables[:sql_mode] = 'STRICT_ALL_TABLES'
732
+ end
733
+
734
+ # NAMES does not have an equals sign, see
735
+ # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
736
+ # (trailing comma because variable_assignments will always have content)
737
+ encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding]
738
+
739
+ # Gather up all of the SET variables...
740
+ variable_assignments = variables.map do |k, v|
741
+ if v == ':default' || v == :default
742
+ "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
743
+ elsif !v.nil?
744
+ "@@SESSION.#{k.to_s} = #{quote(v)}"
745
+ end
746
+ # or else nil; compact to clear nils out
747
+ end.compact.join(', ')
748
+
749
+ # ...and send them all in one query
750
+ execute("SET #{encoding} #{variable_assignments}", :skip_logging)
751
+ end
674
752
  end
675
753
  end
676
754
  end
@@ -66,6 +66,26 @@ module ActiveRecord
66
66
  end
67
67
  end
68
68
 
69
+ def binary?
70
+ type == :binary
71
+ end
72
+
73
+ # Casts a Ruby value to something appropriate for writing to the database.
74
+ def type_cast_for_write(value)
75
+ return value unless number?
76
+
77
+ case value
78
+ when FalseClass
79
+ 0
80
+ when TrueClass
81
+ 1
82
+ when String
83
+ value.presence
84
+ else
85
+ value
86
+ end
87
+ end
88
+
69
89
  # Casts value (which is a String) to an appropriate instance.
70
90
  def type_cast(value)
71
91
  return nil if value.nil?
@@ -80,7 +100,7 @@ module ActiveRecord
80
100
  when :decimal then klass.value_to_decimal(value)
81
101
  when :datetime, :timestamp then klass.string_to_time(value)
82
102
  when :time then klass.string_to_dummy_time(value)
83
- when :date then klass.string_to_date(value)
103
+ when :date then klass.value_to_date(value)
84
104
  when :binary then klass.binary_to_string(value)
85
105
  when :boolean then klass.value_to_boolean(value)
86
106
  else value
@@ -88,6 +108,10 @@ module ActiveRecord
88
108
  end
89
109
 
90
110
  def type_cast_code(var_name)
111
+ message = "Column#type_cast_code is deprecated in favor of using Column#type_cast only, " \
112
+ "and it is going to be removed in future Rails versions."
113
+ ActiveSupport::Deprecation.warn message
114
+
91
115
  klass = self.class.name
92
116
 
93
117
  case type
@@ -97,9 +121,12 @@ module ActiveRecord
97
121
  when :decimal then "#{klass}.value_to_decimal(#{var_name})"
98
122
  when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
99
123
  when :time then "#{klass}.string_to_dummy_time(#{var_name})"
100
- when :date then "#{klass}.string_to_date(#{var_name})"
124
+ when :date then "#{klass}.value_to_date(#{var_name})"
101
125
  when :binary then "#{klass}.binary_to_string(#{var_name})"
102
126
  when :boolean then "#{klass}.value_to_boolean(#{var_name})"
127
+ when :hstore then "#{klass}.string_to_hstore(#{var_name})"
128
+ when :inet, :cidr then "#{klass}.string_to_cidr(#{var_name})"
129
+ when :json then "#{klass}.string_to_json(#{var_name})"
103
130
  else var_name
104
131
  end
105
132
  end
@@ -132,23 +159,27 @@ module ActiveRecord
132
159
  value
133
160
  end
134
161
 
135
- def string_to_date(string)
136
- return string unless string.is_a?(String)
137
- return nil if string.empty?
138
-
139
- fast_string_to_date(string) || fallback_string_to_date(string)
162
+ def value_to_date(value)
163
+ if value.is_a?(String)
164
+ return nil if value.blank?
165
+ fast_string_to_date(value) || fallback_string_to_date(value)
166
+ elsif value.respond_to?(:to_date)
167
+ value.to_date
168
+ else
169
+ value
170
+ end
140
171
  end
141
172
 
142
173
  def string_to_time(string)
143
174
  return string unless string.is_a?(String)
144
- return nil if string.empty?
175
+ return nil if string.blank?
145
176
 
146
177
  fast_string_to_time(string) || fallback_string_to_time(string)
147
178
  end
148
179
 
149
180
  def string_to_dummy_time(string)
150
181
  return string unless string.is_a?(String)
151
- return nil if string.empty?
182
+ return nil if string.blank?
152
183
 
153
184
  dummy_time_string = "2000-01-01 #{string}"
154
185
 
@@ -210,7 +241,7 @@ module ActiveRecord
210
241
  # Treat 0000-00-00 00:00:00 as nil.
211
242
  return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
212
243
 
213
- Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
244
+ Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
214
245
  end
215
246
 
216
247
  def fast_string_to_date(string)
@@ -219,20 +250,11 @@ module ActiveRecord
219
250
  end
220
251
  end
221
252
 
222
- if RUBY_VERSION >= '1.9'
223
- # Doesn't handle time zones.
224
- def fast_string_to_time(string)
225
- if string =~ Format::ISO_DATETIME
226
- microsec = ($7.to_r * 1_000_000).to_i
227
- new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
228
- end
229
- end
230
- else
231
- def fast_string_to_time(string)
232
- if string =~ Format::ISO_DATETIME
233
- microsec = ($7.to_f * 1_000_000).round.to_i
234
- new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
235
- end
253
+ # Doesn't handle time zones.
254
+ def fast_string_to_time(string)
255
+ if string =~ Format::ISO_DATETIME
256
+ microsec = ($7.to_r * 1_000_000).to_i
257
+ new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
236
258
  end
237
259
  end
238
260
 
@@ -0,0 +1,96 @@
1
+ require 'uri'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class ConnectionSpecification #:nodoc:
6
+ attr_reader :config, :adapter_method
7
+
8
+ def initialize(config, adapter_method)
9
+ @config, @adapter_method = config, adapter_method
10
+ end
11
+
12
+ def initialize_dup(original)
13
+ @config = original.config.dup
14
+ end
15
+
16
+ ##
17
+ # Builds a ConnectionSpecification from user input
18
+ class Resolver # :nodoc:
19
+ attr_reader :config, :klass, :configurations
20
+
21
+ def initialize(config, configurations)
22
+ @config = config
23
+ @configurations = configurations
24
+ end
25
+
26
+ def spec
27
+ case config
28
+ when nil
29
+ raise AdapterNotSpecified unless defined?(Rails.env)
30
+ resolve_string_connection Rails.env
31
+ when Symbol, String
32
+ resolve_string_connection config.to_s
33
+ when Hash
34
+ resolve_hash_connection config
35
+ end
36
+ end
37
+
38
+ private
39
+ def resolve_string_connection(spec) # :nodoc:
40
+ hash = configurations.fetch(spec) do |k|
41
+ self.class.connection_url_to_hash(k)
42
+ end
43
+
44
+ raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
45
+
46
+ resolve_hash_connection hash
47
+ end
48
+
49
+ def resolve_hash_connection(spec) # :nodoc:
50
+ spec = spec.symbolize_keys
51
+
52
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
53
+
54
+ path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
55
+ begin
56
+ require path_to_adapter
57
+ rescue Gem::LoadError => e
58
+ raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile."
59
+ rescue LoadError => e
60
+ raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
61
+ end
62
+
63
+ adapter_method = "#{spec[:adapter]}_connection"
64
+
65
+ ConnectionSpecification.new(spec, adapter_method)
66
+ end
67
+
68
+ def self.connection_url_to_hash(url) # :nodoc:
69
+ config = URI.parse url
70
+ adapter = config.scheme
71
+ adapter = "postgresql" if adapter == "postgres"
72
+ spec = { :adapter => adapter,
73
+ :username => config.user,
74
+ :password => config.password,
75
+ :port => config.port,
76
+ :database => config.path.sub(%r{^/},""),
77
+ :host => config.host }
78
+
79
+ spec.reject!{ |_,value| value.blank? }
80
+
81
+ uri_parser = URI::Parser.new
82
+
83
+ spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) }
84
+
85
+ if config.query
86
+ options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
87
+
88
+ spec.merge!(options)
89
+ end
90
+
91
+ spec
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end