activerecord 4.2.0.beta4 → 4.2.0.rc1

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +107 -34
  3. data/lib/active_record/aggregations.rb +2 -2
  4. data/lib/active_record/associations.rb +1 -1
  5. data/lib/active_record/associations/alias_tracker.rb +3 -12
  6. data/lib/active_record/associations/association_scope.rb +1 -2
  7. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  8. data/lib/active_record/associations/collection_association.rb +29 -6
  9. data/lib/active_record/associations/has_many_association.rb +1 -1
  10. data/lib/active_record/associations/has_many_through_association.rb +2 -2
  11. data/lib/active_record/associations/join_dependency.rb +1 -1
  12. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  13. data/lib/active_record/associations/preloader.rb +1 -0
  14. data/lib/active_record/associations/preloader/association.rb +3 -8
  15. data/lib/active_record/associations/preloader/through_association.rb +1 -0
  16. data/lib/active_record/associations/singular_association.rb +2 -1
  17. data/lib/active_record/attribute_methods.rb +2 -2
  18. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  19. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  20. data/lib/active_record/attribute_methods/read.rb +13 -5
  21. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  22. data/lib/active_record/attribute_set.rb +7 -11
  23. data/lib/active_record/attribute_set/builder.rb +66 -17
  24. data/lib/active_record/attributes.rb +20 -3
  25. data/lib/active_record/connection_adapters/abstract/database_statements.rb +0 -4
  26. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +1 -3
  27. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +25 -24
  28. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -3
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -7
  30. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +25 -13
  31. data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
  32. data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -0
  33. data/lib/active_record/connection_adapters/mysql_adapter.rb +6 -2
  34. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +0 -4
  35. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -16
  36. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +29 -7
  37. data/lib/active_record/connection_adapters/postgresql/utils.rb +15 -4
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +15 -6
  39. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +5 -7
  40. data/lib/active_record/connection_handling.rb +1 -1
  41. data/lib/active_record/core.rb +5 -3
  42. data/lib/active_record/enum.rb +1 -1
  43. data/lib/active_record/errors.rb +21 -0
  44. data/lib/active_record/fixtures.rb +4 -2
  45. data/lib/active_record/gem_version.rb +1 -1
  46. data/lib/active_record/locking/optimistic.rb +1 -1
  47. data/lib/active_record/migration.rb +15 -4
  48. data/lib/active_record/model_schema.rb +8 -4
  49. data/lib/active_record/persistence.rb +5 -5
  50. data/lib/active_record/railtie.rb +0 -2
  51. data/lib/active_record/railties/databases.rake +7 -6
  52. data/lib/active_record/reflection.rb +2 -2
  53. data/lib/active_record/relation.rb +21 -13
  54. data/lib/active_record/relation/calculations.rb +1 -0
  55. data/lib/active_record/relation/finder_methods.rb +8 -5
  56. data/lib/active_record/relation/merger.rb +0 -12
  57. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  58. data/lib/active_record/relation/query_methods.rb +30 -18
  59. data/lib/active_record/sanitization.rb +4 -1
  60. data/lib/active_record/schema_dumper.rb +1 -6
  61. data/lib/active_record/scoping/named.rb +1 -1
  62. data/lib/active_record/statement_cache.rb +21 -10
  63. data/lib/active_record/tasks/database_tasks.rb +17 -2
  64. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -0
  65. data/lib/active_record/type.rb +1 -0
  66. data/lib/active_record/type/big_integer.rb +13 -0
  67. data/lib/active_record/type/decimal_without_scale.rb +2 -2
  68. data/lib/active_record/type/hash_lookup_type_map.rb +5 -7
  69. data/lib/active_record/type/integer.rb +29 -1
  70. data/lib/active_record/type/serialized.rb +1 -1
  71. data/lib/active_record/type/string.rb +4 -4
  72. data/lib/active_record/type/type_map.rb +23 -7
  73. data/lib/active_record/validations.rb +4 -3
  74. data/lib/active_record/validations/uniqueness.rb +1 -1
  75. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +3 -0
  76. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  77. metadata +15 -20
@@ -571,6 +571,9 @@ module ActiveRecord
571
571
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
572
572
  #
573
573
  def rename_index(table_name, old_name, new_name)
574
+ if new_name.length > allowed_index_name_length
575
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
576
+ end
574
577
  # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
575
578
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
576
579
  return unless old_index_def
@@ -740,7 +743,7 @@ module ActiveRecord
740
743
  end
741
744
 
742
745
  fk_name_to_delete = options.fetch(:name) do
743
- fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column] }
746
+ fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
744
747
 
745
748
  if fk_to_delete
746
749
  fk_to_delete.name
@@ -835,11 +838,14 @@ module ActiveRecord
835
838
  columns
836
839
  end
837
840
 
838
- # Adds timestamps (+created_at+ and +updated_at+) columns to the named table.
841
+ include TimestampDefaultDeprecation
842
+ # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
843
+ # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
839
844
  #
840
- # add_timestamps(:suppliers)
845
+ # add_timestamps(:suppliers, null: false)
841
846
  #
842
847
  def add_timestamps(table_name, options = {})
848
+ emit_warning_if_null_unspecified(options)
843
849
  add_column table_name, :created_at, :datetime, options
844
850
  add_column table_name, :updated_at, :datetime, options
845
851
  end
@@ -14,10 +14,7 @@ module ActiveRecord
14
14
  module ConnectionAdapters # :nodoc:
15
15
  extend ActiveSupport::Autoload
16
16
 
17
- autoload_at 'active_record/connection_adapters/column' do
18
- autoload :Column
19
- autoload :NullColumn
20
- end
17
+ autoload :Column
21
18
  autoload :ConnectionSpecification
22
19
 
23
20
  autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
@@ -27,6 +24,7 @@ module ActiveRecord
27
24
  autoload :TableDefinition
28
25
  autoload :Table
29
26
  autoload :AlterTable
27
+ autoload :TimestampDefaultDeprecation
30
28
  end
31
29
 
32
30
  autoload_at 'active_record/connection_adapters/abstract/connection_pool' do
@@ -265,10 +263,10 @@ module ActiveRecord
265
263
 
266
264
  # QUOTING ==================================================
267
265
 
268
- # Returns a bind substitution value given a bind +index+ and +column+
266
+ # Returns a bind substitution value given a bind +column+
269
267
  # NOTE: The column param is currently being used by the sqlserver-adapter
270
- def substitute_at(column, index)
271
- Arel::Nodes::BindParam.new '?'
268
+ def substitute_at(column, _unused = 0)
269
+ Arel::Nodes::BindParam.new
272
270
  end
273
271
 
274
272
  # REFERENTIAL INTEGRITY ====================================
@@ -386,6 +384,10 @@ module ActiveRecord
386
384
  type_map.lookup(sql_type)
387
385
  end
388
386
 
387
+ def column_name_for_operation(operation, node) # :nodoc:
388
+ node.to_sql
389
+ end
390
+
389
391
  protected
390
392
 
391
393
  def initialize_type_map(m) # :nodoc:
@@ -285,7 +285,9 @@ module ActiveRecord
285
285
  end
286
286
  end
287
287
 
288
+ #--
288
289
  # DATABASE STATEMENTS ======================================
290
+ #++
289
291
 
290
292
  def clear_cache!
291
293
  super
@@ -585,14 +587,6 @@ module ActiveRecord
585
587
  end
586
588
  end
587
589
 
588
- def add_column_position!(sql, options)
589
- if options[:first]
590
- sql << " FIRST"
591
- elsif options[:after]
592
- sql << " AFTER #{quote_column_name(options[:after])}"
593
- end
594
- end
595
-
596
590
  # SHOW VARIABLES LIKE 'name'
597
591
  def show_variable(name)
598
592
  variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
@@ -639,10 +633,6 @@ module ActiveRecord
639
633
  end
640
634
  end
641
635
 
642
- def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
643
- where_sql
644
- end
645
-
646
636
  def strict_mode?
647
637
  self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
648
638
  end
@@ -656,6 +646,8 @@ module ActiveRecord
656
646
  def initialize_type_map(m) # :nodoc:
657
647
  super
658
648
 
649
+ register_class_with_limit m, %r(char)i, MysqlString
650
+
659
651
  m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
660
652
  m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
661
653
  m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
@@ -680,7 +672,7 @@ module ActiveRecord
680
672
  m.register_type(%r(enum)i) do |sql_type|
681
673
  limit = sql_type[/^enum\((.+)\)/i, 1]
682
674
  .split(',').map{|enum| enum.strip.length - 2}.max
683
- Type::String.new(limit: limit)
675
+ MysqlString.new(limit: limit)
684
676
  end
685
677
  end
686
678
 
@@ -855,6 +847,26 @@ module ActiveRecord
855
847
  end
856
848
  end
857
849
  end
850
+
851
+ class MysqlString < Type::String # :nodoc:
852
+ def type_cast_for_database(value)
853
+ case value
854
+ when true then "1"
855
+ when false then "0"
856
+ else super
857
+ end
858
+ end
859
+
860
+ private
861
+
862
+ def cast_value(value)
863
+ case value
864
+ when true then "1"
865
+ when false then "0"
866
+ else super
867
+ end
868
+ end
869
+ end
858
870
  end
859
871
  end
860
872
  end
@@ -234,7 +234,7 @@ module ActiveRecord
234
234
  end
235
235
  end
236
236
 
237
- # Takes the environment such as `:production` or `:development`.
237
+ # Takes the environment such as +:production+ or +:development+.
238
238
  # This requires that the @configurations was initialized with a key that
239
239
  # matches.
240
240
  #
@@ -66,7 +66,9 @@ module ActiveRecord
66
66
  exception.error_number if exception.respond_to?(:error_number)
67
67
  end
68
68
 
69
+ #--
69
70
  # QUOTING ==================================================
71
+ #++
70
72
 
71
73
  def quote_string(string)
72
74
  @connection.escape(string)
@@ -80,7 +82,9 @@ module ActiveRecord
80
82
  end
81
83
  end
82
84
 
85
+ #--
83
86
  # CONNECTION MANAGEMENT ====================================
87
+ #++
84
88
 
85
89
  def active?
86
90
  return false unless @connection
@@ -104,7 +108,9 @@ module ActiveRecord
104
108
  end
105
109
  end
106
110
 
111
+ #--
107
112
  # DATABASE STATEMENTS ======================================
113
+ #++
108
114
 
109
115
  def explain(arel, binds = [])
110
116
  sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
@@ -58,7 +58,7 @@ module ActiveRecord
58
58
  # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
59
59
  # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
60
60
  # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html)
61
- # * <tt>:variables</tt> - (Optional) A hash session variables to send as `SET @@SESSION.key = value` on each database connection. Use the value `:default` to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
61
+ # * <tt>:variables</tt> - (Optional) A hash session variables to send as <tt>SET @@SESSION.key = value</tt> on each database connection. Use the value +:default+ to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
62
62
  # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
63
63
  # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
64
64
  # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
@@ -88,7 +88,7 @@ module ActiveRecord
88
88
  end
89
89
 
90
90
  def clear
91
- cache.values.each do |hash|
91
+ cache.each_value do |hash|
92
92
  hash[:stmt].close
93
93
  end
94
94
  cache.clear
@@ -137,7 +137,9 @@ module ActiveRecord
137
137
  @connection.quote(string)
138
138
  end
139
139
 
140
+ #--
140
141
  # CONNECTION MANAGEMENT ====================================
142
+ #++
141
143
 
142
144
  def active?
143
145
  if @connection.respond_to?(:stat)
@@ -178,7 +180,9 @@ module ActiveRecord
178
180
  end
179
181
  end
180
182
 
183
+ #--
181
184
  # DATABASE STATEMENTS ======================================
185
+ #++
182
186
 
183
187
  def select_rows(sql, name = nil, binds = [])
184
188
  @connection.query_with_result = true
@@ -156,10 +156,6 @@ module ActiveRecord
156
156
  end
157
157
  end
158
158
 
159
- def substitute_at(column, index)
160
- Arel::Nodes::BindParam.new "$#{index + 1}"
161
- end
162
-
163
159
  def exec_query(sql, name = 'SQL', binds = [])
164
160
  execute_and_clear(sql, name, binds) do |result|
165
161
  types = {}
@@ -14,22 +14,6 @@ module ActiveRecord
14
14
  @connection.unescape_bytea(value) if value
15
15
  end
16
16
 
17
- # Quotes PostgreSQL-specific data types for SQL input.
18
- def quote(value, column = nil) #:nodoc:
19
- return super unless column
20
-
21
- case value
22
- when Float
23
- if value.infinite? || value.nan?
24
- "'#{value}'"
25
- else
26
- super
27
- end
28
- else
29
- super
30
- end
31
- end
32
-
33
17
  # Quotes strings for use in SQL input.
34
18
  def quote_string(s) #:nodoc:
35
19
  @connection.escape(s)
@@ -94,6 +78,12 @@ module ActiveRecord
94
78
  elsif value.hex?
95
79
  "X'#{value}'"
96
80
  end
81
+ when Float
82
+ if value.infinite? || value.nan?
83
+ "'#{value}'"
84
+ else
85
+ super
86
+ end
97
87
  else
98
88
  super
99
89
  end
@@ -4,12 +4,6 @@ module ActiveRecord
4
4
  class SchemaCreation < AbstractAdapter::SchemaCreation
5
5
  private
6
6
 
7
- def visit_AddColumn(o)
8
- sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
9
- sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
10
- add_column_options!(sql, column_options(o))
11
- end
12
-
13
7
  def visit_ColumnDefinition(o)
14
8
  sql = super
15
9
  if o.primary_key? && o.type != :primary_key
@@ -293,6 +287,23 @@ module ActiveRecord
293
287
  result.rows.first.first
294
288
  end
295
289
 
290
+ # Sets the sequence of a table's primary key to the specified value.
291
+ def set_pk_sequence!(table, value) #:nodoc:
292
+ pk, sequence = pk_and_sequence_for(table)
293
+
294
+ if pk
295
+ if sequence
296
+ quoted_sequence = quote_table_name(sequence)
297
+
298
+ select_value <<-end_sql, 'SCHEMA'
299
+ SELECT setval('#{quoted_sequence}', #{value})
300
+ end_sql
301
+ else
302
+ @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
303
+ end
304
+ end
305
+ end
306
+
296
307
  # Resets the sequence of a table's primary key to the maximum value.
297
308
  def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
298
309
  unless pk and sequence
@@ -394,7 +405,10 @@ module ActiveRecord
394
405
  pk, seq = pk_and_sequence_for(new_name)
395
406
  if seq && seq.identifier == "#{table_name}_#{pk}_seq"
396
407
  new_seq = "#{new_name}_#{pk}_seq"
408
+ idx = "#{table_name}_pkey"
409
+ new_idx = "#{new_name}_pkey"
397
410
  execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
411
+ execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
398
412
  end
399
413
 
400
414
  rename_table_indexes(table_name, new_name)
@@ -413,7 +427,12 @@ module ActiveRecord
413
427
  quoted_table_name = quote_table_name(table_name)
414
428
  sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
415
429
  sql_type << "[]" if options[:array]
416
- execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
430
+ sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
431
+ sql << " USING #{options[:using]}" if options[:using]
432
+ if options[:cast_as]
433
+ sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
434
+ end
435
+ execute sql
417
436
 
418
437
  change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
419
438
  change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
@@ -461,6 +480,9 @@ module ActiveRecord
461
480
  end
462
481
 
463
482
  def rename_index(table_name, old_name, new_name)
483
+ if new_name.length > allowed_index_name_length
484
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
485
+ end
464
486
  execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
465
487
  end
466
488
 
@@ -18,7 +18,11 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  def quoted
21
- parts.map { |p| PGconn.quote_ident(p) }.join SEPARATOR
21
+ if schema
22
+ PGconn.quote_ident(schema) << SEPARATOR << PGconn.quote_ident(identifier)
23
+ else
24
+ PGconn.quote_ident(identifier)
25
+ end
22
26
  end
23
27
 
24
28
  def ==(o)
@@ -32,8 +36,11 @@ module ActiveRecord
32
36
 
33
37
  protected
34
38
  def unquote(part)
35
- return unless part
36
- part.gsub(/(^"|"$)/,'')
39
+ if part && part.start_with?('"')
40
+ part[1..-2]
41
+ else
42
+ part
43
+ end
37
44
  end
38
45
 
39
46
  def parts
@@ -57,7 +64,11 @@ module ActiveRecord
57
64
  # * <tt>"schema_name".table_name</tt>
58
65
  # * <tt>"schema.name"."table name"</tt>
59
66
  def extract_schema_qualified_name(string)
60
- table, schema = string.scan(/[^".\s]+|"[^"]*"/)[0..1].reverse
67
+ schema, table = string.scan(/[^".\s]+|"[^"]*"/)
68
+ if table.nil?
69
+ table = schema
70
+ schema = nil
71
+ end
61
72
  PostgreSQL::Name.new(schema, table)
62
73
  end
63
74
  end
@@ -13,7 +13,7 @@ require 'active_record/connection_adapters/postgresql/database_statements'
13
13
  require 'arel/visitors/bind_visitor'
14
14
 
15
15
  # Make sure we're using pg high enough for PGResult#values
16
- gem 'pg', '~> 0.11'
16
+ gem 'pg', '~> 0.15'
17
17
  require 'pg'
18
18
 
19
19
  require 'ipaddr'
@@ -104,6 +104,7 @@ module ActiveRecord
104
104
  macaddr: { name: "macaddr" },
105
105
  uuid: { name: "uuid" },
106
106
  json: { name: "json" },
107
+ jsonb: { name: "jsonb" },
107
108
  ltree: { name: "ltree" },
108
109
  citext: { name: "citext" },
109
110
  point: { name: "point" },
@@ -124,7 +125,7 @@ module ActiveRecord
124
125
  PostgreSQL::SchemaCreation.new self
125
126
  end
126
127
 
127
- # Adds `:array` option to the default set provided by the
128
+ # Adds +:array+ option to the default set provided by the
128
129
  # AbstractAdapter
129
130
  def prepare_column_options(column, types) # :nodoc:
130
131
  spec = super
@@ -133,7 +134,7 @@ module ActiveRecord
133
134
  spec
134
135
  end
135
136
 
136
- # Adds `:array` as a valid migration key
137
+ # Adds +:array+ as a valid migration key
137
138
  def migration_keys
138
139
  super + [:array]
139
140
  end
@@ -393,6 +394,16 @@ module ActiveRecord
393
394
  super(oid)
394
395
  end
395
396
 
397
+ def column_name_for_operation(operation, node) # :nodoc:
398
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
399
+ end
400
+
401
+ OPERATION_ALIASES = { # :nodoc:
402
+ "maximum" => "max",
403
+ "minimum" => "min",
404
+ "average" => "avg",
405
+ }
406
+
396
407
  protected
397
408
 
398
409
  # Returns the version of the connected PostgreSQL server.
@@ -585,9 +596,7 @@ module ActiveRecord
585
596
  }
586
597
 
587
598
  log(sql, name, type_casted_binds, stmt_key) do
588
- @connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
589
- @connection.block
590
- @connection.get_last_result
599
+ @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val })
591
600
  end
592
601
  rescue ActiveRecord::StatementInvalid => e
593
602
  pgerror = e.original_exception