activerecord 5.0.2 → 5.0.3

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/lib/active_record/associations/association.rb +1 -1
  4. data/lib/active_record/associations/collection_association.rb +36 -44
  5. data/lib/active_record/associations/collection_proxy.rb +33 -13
  6. data/lib/active_record/associations/has_many_association.rb +1 -7
  7. data/lib/active_record/associations/has_many_through_association.rb +1 -5
  8. data/lib/active_record/collection_cache_key.rb +16 -6
  9. data/lib/active_record/connection_adapters/abstract/quoting.rb +8 -0
  10. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +3 -7
  11. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -3
  12. data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -7
  13. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +12 -12
  14. data/lib/active_record/connection_adapters/mysql/database_statements.rb +2 -2
  15. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  16. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  17. data/lib/active_record/connection_adapters/postgresql/quoting.rb +3 -3
  18. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  19. data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -13
  20. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -4
  21. data/lib/active_record/explain.rb +20 -9
  22. data/lib/active_record/gem_version.rb +1 -1
  23. data/lib/active_record/log_subscriber.rb +18 -15
  24. data/lib/active_record/migration.rb +25 -7
  25. data/lib/active_record/persistence.rb +23 -8
  26. data/lib/active_record/railties/databases.rake +7 -19
  27. data/lib/active_record/relation/calculations.rb +1 -1
  28. data/lib/active_record/relation/delegation.rb +2 -0
  29. data/lib/active_record/tasks/database_tasks.rb +2 -0
  30. data/lib/active_record/transactions.rb +1 -1
  31. data/lib/rails/generators/active_record/migration.rb +8 -0
  32. data/lib/rails/generators/active_record/migration/migration_generator.rb +0 -8
  33. data/lib/rails/generators/active_record/model/model_generator.rb +0 -8
  34. metadata +7 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68957771d5c52daac160a296c5168838ba53d9ce
4
- data.tar.gz: b61fc1a80cb8169883f3d727481ec38111702484
3
+ metadata.gz: c2aa37fd1fc57e36d5f14ee9009cf6ba7ec6b601
4
+ data.tar.gz: c974b336ea71a782c1381d480c0ef2687dfc32c3
5
5
  SHA512:
6
- metadata.gz: b05a93e230415562344d28db45ca476edbcf15bdb02a6dafcaab46951850b28359ff37d647b57530e79d5a66aa7be5b46d8c0bb217ceed69ff73a087e9f64bae
7
- data.tar.gz: 3c3fae72c7e4eeae41528004831b6ded872bec022fee1123f0ed7d0375dcd3b77292709dea36ca5a0bda321de3081614e3464b591314a744c864d6ea554ec348
6
+ metadata.gz: f27b1ef2da04f68ac793b7039dc8ff8e0c78d8cf8db60d0b554f00174882b025d41dde0a4ad1a86ad5dad95c9facd826852b398c666968965cb54154d7f27cc6
7
+ data.tar.gz: e792934d6d1a39b07fef4cbff53969729f292edab3e23ea33231ba087b23d7e8a3eed648135fa45fc4e023dc8c217e7260753f3d69defeca3ecf0e21cabe804c
@@ -1,3 +1,42 @@
1
+ * Check whether `Rails.application` defined before calling it
2
+
3
+ In #27674 we changed the migration generator to generate migrations at the
4
+ path defined in `Rails.application.config.paths` however the code checked
5
+ for the presence of the `Rails` constant but not the `Rails.application`
6
+ method which caused problems when using Active Record and generators outside
7
+ of the context of a Rails application.
8
+
9
+ Fixes #28325.
10
+
11
+ * Fix `deserialize` with JSON array.
12
+
13
+ Fixes #28285.
14
+
15
+ *Ryuta Kamizono*
16
+
17
+ * Fix `rake db:schema:load` with subdirectories.
18
+
19
+ *Ryuta Kamizono*
20
+
21
+ * Fix `rake db:migrate:status` with subdirectories.
22
+
23
+ *Ryuta Kamizono*
24
+
25
+ * Don't share options between reference id and type columns
26
+
27
+ When using a polymorphic reference column in a migration, sharing options
28
+ between the two columns doesn't make sense since they are different types.
29
+ The `reference_id` column is usually an integer and the `reference_type`
30
+ column a string so options like `unsigned: true` will result in an invalid
31
+ table definition.
32
+
33
+ *Ryuta Kamizono*
34
+
35
+ * Fix regression of #1969 with SELECT aliases in HAVING clause.
36
+
37
+ *Eugene Kenny*
38
+
39
+
1
40
  ## Rails 5.0.2 (March 01, 2017) ##
2
41
 
3
42
  * Fix `wait_timeout` to configurable for mysql2 adapter.
@@ -83,7 +83,7 @@ module ActiveRecord
83
83
  end
84
84
 
85
85
  def scope
86
- target_scope.merge(association_scope)
86
+ target_scope.merge!(association_scope)
87
87
  end
88
88
 
89
89
  # The scope for this association.
@@ -39,13 +39,7 @@ module ActiveRecord
39
39
  reload
40
40
  end
41
41
 
42
- if null_scope?
43
- # Cache the proxy separately before the owner has an id
44
- # or else a post-save proxy will still lack the id
45
- @null_proxy ||= CollectionProxy.create(klass, self)
46
- else
47
- @proxy ||= CollectionProxy.create(klass, self)
48
- end
42
+ CollectionProxy.create(klass, self)
49
43
  end
50
44
 
51
45
  # Implements the writer method, e.g. foo.items= for Foo.has_many :items
@@ -427,37 +421,9 @@ module ActiveRecord
427
421
  replace_on_target(record, index, skip_callbacks, &block)
428
422
  end
429
423
 
430
- def replace_on_target(record, index, skip_callbacks)
431
- callback(:before_add, record) unless skip_callbacks
432
-
433
- begin
434
- if index
435
- record_was = target[index]
436
- target[index] = record
437
- else
438
- target << record
439
- end
440
-
441
- yield(record) if block_given?
442
- rescue
443
- if index
444
- target[index] = record_was
445
- else
446
- target.delete(record)
447
- end
448
-
449
- raise
450
- end
451
-
452
- callback(:after_add, record) unless skip_callbacks
453
- set_inverse_instance(record)
454
-
455
- record
456
- end
457
-
458
- def scope(opts = {})
459
- scope = super()
460
- scope.none! if opts.fetch(:nullify, true) && null_scope?
424
+ def scope
425
+ scope = super
426
+ scope.none! if null_scope?
461
427
  scope
462
428
  end
463
429
 
@@ -528,15 +494,19 @@ module ActiveRecord
528
494
  transaction do
529
495
  add_to_target(build_record(attributes)) do |record|
530
496
  yield(record) if block_given?
531
- insert_record(record, true, raise)
497
+ insert_record(record, true, raise) { @_was_loaded = loaded? }
532
498
  end
533
499
  end
534
500
  end
535
501
  end
536
502
 
537
503
  # Do the relevant stuff to insert the given record into the association collection.
538
- def insert_record(record, validate = true, raise = false)
539
- raise NotImplementedError
504
+ def insert_record(record, validate = true, raise = false, &block)
505
+ if raise
506
+ record.save!(validate: validate, &block)
507
+ else
508
+ record.save(validate: validate, &block)
509
+ end
540
510
  end
541
511
 
542
512
  def create_scope
@@ -590,19 +560,41 @@ module ActiveRecord
590
560
  end
591
561
  end
592
562
 
593
- def concat_records(records, should_raise = false)
563
+ def concat_records(records, raise = false)
594
564
  result = true
595
565
 
596
566
  records.each do |record|
597
567
  raise_on_type_mismatch!(record)
598
- add_to_target(record) do |rec|
599
- result &&= insert_record(rec, true, should_raise) unless owner.new_record?
568
+ add_to_target(record) do
569
+ result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
600
570
  end
601
571
  end
602
572
 
603
573
  result && records
604
574
  end
605
575
 
576
+ def replace_on_target(record, index, skip_callbacks)
577
+ callback(:before_add, record) unless skip_callbacks
578
+
579
+ set_inverse_instance(record)
580
+
581
+ @_was_loaded = true
582
+
583
+ yield(record) if block_given?
584
+
585
+ if index
586
+ target[index] = record
587
+ elsif @_was_loaded || !loaded?
588
+ target << record
589
+ end
590
+
591
+ callback(:after_add, record) unless skip_callbacks
592
+
593
+ record
594
+ ensure
595
+ @_was_loaded = nil
596
+ end
597
+
606
598
  def callback(method, record)
607
599
  callbacks_for(method).each do |callback|
608
600
  callback.call(method, owner, record)
@@ -28,12 +28,9 @@ module ActiveRecord
28
28
  # is computed directly through SQL and does not trigger by itself the
29
29
  # instantiation of the actual post records.
30
30
  class CollectionProxy < Relation
31
- delegate :exists?, :update_all, :arel, to: :scope
32
-
33
31
  def initialize(klass, association) #:nodoc:
34
32
  @association = association
35
33
  super klass, klass.arel_table, klass.predicate_builder
36
- merge! association.scope(nullify: false)
37
34
  end
38
35
 
39
36
  def target
@@ -902,19 +899,10 @@ module ActiveRecord
902
899
  @association
903
900
  end
904
901
 
905
- # We don't want this object to be put on the scoping stack, because
906
- # that could create an infinite loop where we call an @association
907
- # method, which gets the current scope, which is this object, which
908
- # delegates to @association, and so on.
909
- def scoping
910
- @association.scope.scoping { yield }
911
- end
912
-
913
902
  # Returns a <tt>Relation</tt> object for the records in this association
914
903
  def scope
915
- @association.scope
904
+ @scope ||= @association.scope
916
905
  end
917
- alias spawn scope
918
906
 
919
907
  # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
920
908
  # contain the same number of elements and if each element is equal
@@ -1046,6 +1034,7 @@ module ActiveRecord
1046
1034
  # person.pets(true) # fetches pets from the database
1047
1035
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1048
1036
  def reload
1037
+ @scope = nil
1049
1038
  proxy_association.reload
1050
1039
  self
1051
1040
  end
@@ -1067,20 +1056,51 @@ module ActiveRecord
1067
1056
  # person.pets # fetches pets from the database
1068
1057
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1069
1058
  def reset
1059
+ @scope = nil
1070
1060
  proxy_association.reset
1071
1061
  proxy_association.reset_scope
1072
1062
  self
1073
1063
  end
1074
1064
 
1065
+ delegate_methods = [
1066
+ QueryMethods,
1067
+ SpawnMethods,
1068
+ ].flat_map { |klass|
1069
+ klass.public_instance_methods(false)
1070
+ } - self.public_instance_methods(false) + [:scoping]
1071
+
1072
+ delegate(*delegate_methods, to: :scope)
1073
+
1074
+ module DelegateExtending # :nodoc:
1075
+ private
1076
+ def method_missing(method, *args, &block)
1077
+ extending_values = association_scope.extending_values
1078
+ if extending_values.any? && (extending_values - self.class.included_modules).any?
1079
+ self.class.include(*extending_values)
1080
+ public_send(method, *args, &block)
1081
+ else
1082
+ super
1083
+ end
1084
+ end
1085
+ end
1086
+
1075
1087
  private
1076
1088
 
1077
1089
  def null_scope?
1078
1090
  @association.null_scope?
1079
1091
  end
1080
1092
 
1093
+ def association_scope
1094
+ @association.association_scope
1095
+ end
1096
+
1081
1097
  def exec_queries
1082
1098
  load_target
1083
1099
  end
1100
+
1101
+ def respond_to_missing?(method, _)
1102
+ association_scope.respond_to?(method) || super
1103
+ end
1084
1104
  end
1085
1105
  end
1086
1106
  end
@@ -40,13 +40,7 @@ module ActiveRecord
40
40
 
41
41
  def insert_record(record, validate = true, raise = false)
42
42
  set_owner_attributes(record)
43
- set_inverse_instance(record)
44
-
45
- if raise
46
- record.save!(:validate => validate)
47
- else
48
- record.save(:validate => validate)
49
- end
43
+ super
50
44
  end
51
45
 
52
46
  def empty?
@@ -39,11 +39,7 @@ module ActiveRecord
39
39
  ensure_not_nested
40
40
 
41
41
  if record.new_record? || record.changed?
42
- if raise
43
- record.save!(validate: validate)
44
- else
45
- return unless record.save(validate: validate)
46
- end
42
+ return unless super
47
43
  end
48
44
 
49
45
  save_through_record(record)
@@ -8,17 +8,27 @@ module ActiveRecord
8
8
  if collection.loaded?
9
9
  size = collection.size
10
10
  if size > 0
11
- timestamp = collection.max_by(&timestamp_column).public_send(timestamp_column)
11
+ timestamp = collection.max_by(&timestamp_column)._read_attribute(timestamp_column)
12
12
  end
13
13
  else
14
14
  column_type = type_for_attribute(timestamp_column.to_s)
15
15
  column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
16
+ select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
16
17
 
17
- query = collection
18
- .unscope(:select)
19
- .select("COUNT(*) AS #{connection.quote_column_name("size")}", "MAX(#{column}) AS timestamp")
20
- .unscope(:order)
21
- result = connection.select_one(query)
18
+ if collection.limit_value || collection.offset_value
19
+ query = collection.spawn
20
+ query.select_values = [column]
21
+ subquery_alias = "subquery_for_cache_key"
22
+ subquery_column = "#{subquery_alias}.#{timestamp_column}"
23
+ subquery = query.arel.as(subquery_alias)
24
+ arel = Arel::SelectManager.new(query.engine).project(select_values % subquery_column).from(subquery)
25
+ else
26
+ query = collection.unscope(:order)
27
+ query.select_values = [select_values % column]
28
+ arel = query.arel
29
+ end
30
+
31
+ result = connection.select_one(arel, nil, query.bound_attributes)
22
32
 
23
33
  if result.blank?
24
34
  size = 0
@@ -155,6 +155,14 @@ module ActiveRecord
155
155
  binds.map(&:value_for_database)
156
156
  end
157
157
 
158
+ def type_casted_binds(binds) # :nodoc:
159
+ if binds.first.is_a?(Array)
160
+ binds.map { |column, value| type_cast(value, column) }
161
+ else
162
+ binds.map { |attr| type_cast(attr.value_for_database) }
163
+ end
164
+ end
165
+
158
166
  private
159
167
 
160
168
  def types_which_need_no_typecasting
@@ -107,16 +107,12 @@ module ActiveRecord
107
107
 
108
108
  private
109
109
 
110
- def as_options(value, default = {})
111
- if value.is_a?(Hash)
112
- value
113
- else
114
- default
115
- end
110
+ def as_options(value)
111
+ value.is_a?(Hash) ? value : {}
116
112
  end
117
113
 
118
114
  def polymorphic_options
119
- as_options(polymorphic, options)
115
+ as_options(polymorphic)
120
116
  end
121
117
 
122
118
  def index_options
@@ -1022,9 +1022,8 @@ module ActiveRecord
1022
1022
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
1023
1023
 
1024
1024
  migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
1025
- paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
1026
- versions = Dir[*paths].map do |filename|
1027
- filename.split('/').last.split('_').first.to_i
1025
+ versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
1026
+ ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
1028
1027
  end
1029
1028
 
1030
1029
  unless migrated.include?(version)
@@ -425,7 +425,7 @@ module ActiveRecord
425
425
 
426
426
  # Provides access to the underlying database driver for this adapter. For
427
427
  # example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
428
- # and a PGconn object in case of PostgreSQLAdapter.
428
+ # and a PG::Connection object in case of PostgreSQLAdapter.
429
429
  #
430
430
  # This is useful for when you need to call a proprietary method such as
431
431
  # PostgreSQL's lo_* methods.
@@ -579,14 +579,15 @@ module ActiveRecord
579
579
  exception
580
580
  end
581
581
 
582
- def log(sql, name = "SQL", binds = [], statement_name = nil)
582
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
583
583
  @instrumenter.instrument(
584
584
  "sql.active_record",
585
- :sql => sql,
586
- :name => name,
587
- :connection_id => object_id,
588
- :statement_name => statement_name,
589
- :binds => binds) { yield }
585
+ sql: sql,
586
+ name: name,
587
+ binds: binds,
588
+ type_casted_binds: type_casted_binds,
589
+ statement_name: statement_name,
590
+ connection_id: object_id) { yield }
590
591
  rescue => e
591
592
  raise translate_exception_class(e, sql)
592
593
  end
@@ -324,7 +324,7 @@ module ActiveRecord
324
324
 
325
325
  def data_sources
326
326
  sql = "SELECT table_name FROM information_schema.tables "
327
- sql << "WHERE table_schema = #{quote(@config[:database])}"
327
+ sql << "WHERE table_schema = DATABASE()"
328
328
 
329
329
  select_values(sql, 'SCHEMA')
330
330
  end
@@ -350,7 +350,7 @@ module ActiveRecord
350
350
  schema, name = extract_schema_qualified_name(table_name)
351
351
 
352
352
  sql = "SELECT table_name FROM information_schema.tables "
353
- sql << "WHERE table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
353
+ sql << "WHERE table_schema = #{schema} AND table_name = #{name}"
354
354
 
355
355
  select_values(sql, 'SCHEMA').any?
356
356
  end
@@ -365,7 +365,7 @@ module ActiveRecord
365
365
  schema, name = extract_schema_qualified_name(view_name)
366
366
 
367
367
  sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
368
- sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
368
+ sql << " AND table_schema = #{schema} AND table_name = #{name}"
369
369
 
370
370
  select_values(sql, 'SCHEMA').any?
371
371
  end
@@ -414,8 +414,8 @@ module ActiveRecord
414
414
  select_value(<<-SQL.strip_heredoc, 'SCHEMA')
415
415
  SELECT table_comment
416
416
  FROM information_schema.tables
417
- WHERE table_schema = #{quote(schema)}
418
- AND table_name = #{quote(name)}
417
+ WHERE table_schema = #{schema}
418
+ AND table_name = #{name}
419
419
  SQL
420
420
  end
421
421
 
@@ -528,9 +528,9 @@ module ActiveRecord
528
528
  JOIN information_schema.referential_constraints rc
529
529
  USING (constraint_schema, constraint_name)
530
530
  WHERE fk.referenced_column_name IS NOT NULL
531
- AND fk.table_schema = #{quote(schema)}
532
- AND fk.table_name = #{quote(name)}
533
- AND rc.table_name = #{quote(name)}
531
+ AND fk.table_schema = #{schema}
532
+ AND fk.table_name = #{name}
533
+ AND rc.table_name = #{name}
534
534
  SQL
535
535
 
536
536
  fk_info.map do |row|
@@ -607,8 +607,8 @@ module ActiveRecord
607
607
  SELECT column_name
608
608
  FROM information_schema.key_column_usage
609
609
  WHERE constraint_name = 'PRIMARY'
610
- AND table_schema = #{quote(schema)}
611
- AND table_name = #{quote(name)}
610
+ AND table_schema = #{schema}
611
+ AND table_name = #{name}
612
612
  ORDER BY ordinal_position
613
613
  SQL
614
614
  end
@@ -904,8 +904,8 @@ module ActiveRecord
904
904
  end
905
905
 
906
906
  def extract_schema_qualified_name(string) # :nodoc:
907
- schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
908
- schema, name = @config[:database], schema unless name
907
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/).map { |s| quote(s) }
908
+ schema, name = "DATABASE()", schema unless name
909
909
  [schema, name]
910
910
  end
911
911
 
@@ -73,9 +73,9 @@ module ActiveRecord
73
73
  # made since we established the connection
74
74
  @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
75
75
 
76
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
76
+ type_casted_binds = type_casted_binds(binds)
77
77
 
78
- log(sql, name, binds) do
78
+ log(sql, name, binds, type_casted_binds) do
79
79
  if cache_stmt
80
80
  cache = @statements[sql] ||= {
81
81
  stmt: @connection.prepare(sql)
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  when ::String
24
24
  type_cast_array(@pg_decoder.decode(value), :deserialize)
25
25
  when Data
26
- deserialize(value.values)
26
+ type_cast_array(value.values, :deserialize)
27
27
  else
28
28
  super
29
29
  end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  def deserialize(value)
7
7
  return if value.nil?
8
8
  return value.to_s if value.is_a?(Type::Binary::Data)
9
- PGconn.unescape_bytea(super)
9
+ PG::Connection.unescape_bytea(super)
10
10
  end
11
11
  end
12
12
  end
@@ -33,7 +33,7 @@ module ActiveRecord
33
33
 
34
34
  # Quotes schema names for use in SQL queries.
35
35
  def quote_schema_name(name)
36
- PGconn.quote_ident(name)
36
+ PG::Connection.quote_ident(name)
37
37
  end
38
38
 
39
39
  def quote_table_name_for_assignment(table, attr)
@@ -42,7 +42,7 @@ module ActiveRecord
42
42
 
43
43
  # Quotes column names for use in SQL queries.
44
44
  def quote_column_name(name) # :nodoc:
45
- @quoted_column_names[name] ||= PGconn.quote_ident(super)
45
+ @quoted_column_names[name] ||= PG::Connection.quote_ident(super)
46
46
  end
47
47
 
48
48
  # Quote date/time values for use in SQL input.
@@ -103,7 +103,7 @@ module ActiveRecord
103
103
  case value
104
104
  when Type::Binary::Data
105
105
  # Return a bind param hash with format as binary.
106
- # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
106
+ # See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
107
107
  # for more information
108
108
  { value: value.to_s, format: 1 }
109
109
  when OID::Xml::Data, OID::Bit::Data
@@ -19,9 +19,9 @@ module ActiveRecord
19
19
 
20
20
  def quoted
21
21
  if schema
22
- PGconn.quote_ident(schema) << SEPARATOR << PGconn.quote_ident(identifier)
22
+ PG::Connection.quote_ident(schema) << SEPARATOR << PG::Connection.quote_ident(identifier)
23
23
  else
24
- PGconn.quote_ident(identifier)
24
+ PG::Connection.quote_ident(identifier)
25
25
  end
26
26
  end
27
27
 
@@ -28,11 +28,11 @@ module ActiveRecord
28
28
  conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
29
29
  conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
30
30
 
31
- # Forward only valid config params to PGconn.connect.
32
- valid_conn_param_keys = PGconn.conndefaults_hash.keys + [:requiressl]
31
+ # Forward only valid config params to PG::Connection.connect.
32
+ valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
33
33
  conn_params.slice!(*valid_conn_param_keys)
34
34
 
35
- # The postgres drivers don't allow the creation of an unconnected PGconn object,
35
+ # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
36
36
  # so just pass a nil connection object for the time being.
37
37
  ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
38
38
  end
@@ -198,8 +198,8 @@ module ActiveRecord
198
198
  end
199
199
 
200
200
  def connection_active?
201
- @connection.status == PGconn::CONNECTION_OK
202
- rescue PGError
201
+ @connection.status == PG::CONNECTION_OK
202
+ rescue PG::Error
203
203
  false
204
204
  end
205
205
  end
@@ -244,7 +244,7 @@ module ActiveRecord
244
244
  def active?
245
245
  @connection.query 'SELECT 1'
246
246
  true
247
- rescue PGError
247
+ rescue PG::Error
248
248
  false
249
249
  end
250
250
 
@@ -410,7 +410,7 @@ module ActiveRecord
410
410
  def translate_exception(exception, message)
411
411
  return exception unless exception.respond_to?(:result)
412
412
 
413
- case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
413
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
414
414
  when UNIQUE_VIOLATION
415
415
  RecordNotUnique.new(message)
416
416
  when FOREIGN_KEY_VIOLATION
@@ -594,15 +594,15 @@ module ActiveRecord
594
594
  end
595
595
 
596
596
  def exec_no_cache(sql, name, binds)
597
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
598
- log(sql, name, binds) { @connection.async_exec(sql, type_casted_binds) }
597
+ type_casted_binds = type_casted_binds(binds)
598
+ log(sql, name, binds, type_casted_binds) { @connection.async_exec(sql, type_casted_binds) }
599
599
  end
600
600
 
601
601
  def exec_cache(sql, name, binds)
602
602
  stmt_key = prepare_statement(sql)
603
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
603
+ type_casted_binds = type_casted_binds(binds)
604
604
 
605
- log(sql, name, binds, stmt_key) do
605
+ log(sql, name, binds, type_casted_binds, stmt_key) do
606
606
  @connection.exec_prepared(stmt_key, type_casted_binds)
607
607
  end
608
608
  rescue ActiveRecord::StatementInvalid => e
@@ -631,7 +631,7 @@ module ActiveRecord
631
631
  CACHED_PLAN_HEURISTIC = 'cached plan must not change result type'.freeze
632
632
  def is_cached_plan_failure?(e)
633
633
  pgerror = e.cause
634
- code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
634
+ code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
635
635
  code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
636
636
  rescue
637
637
  false
@@ -668,7 +668,7 @@ module ActiveRecord
668
668
  # Connects to a PostgreSQL server and sets up the adapter depending on the
669
669
  # connected server's characteristics.
670
670
  def connect
671
- @connection = PGconn.connect(@connection_parameters)
671
+ @connection = PG.connect(@connection_parameters)
672
672
  configure_connection
673
673
  rescue ::PG::Error => error
674
674
  if error.message.include?("does not exist")
@@ -188,9 +188,9 @@ module ActiveRecord
188
188
  end
189
189
 
190
190
  def exec_query(sql, name = nil, binds = [], prepare: false)
191
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
191
+ type_casted_binds = type_casted_binds(binds)
192
192
 
193
- log(sql, name, binds) do
193
+ log(sql, name, binds, type_casted_binds) do
194
194
  # Don't cache statements if they are not prepared
195
195
  unless prepare
196
196
  stmt = @connection.prepare(sql)
@@ -203,7 +203,6 @@ module ActiveRecord
203
203
  ensure
204
204
  stmt.close
205
205
  end
206
- stmt = records
207
206
  else
208
207
  cache = @statements[sql] ||= {
209
208
  :stmt => @connection.prepare(sql)
@@ -212,9 +211,10 @@ module ActiveRecord
212
211
  cols = cache[:cols] ||= stmt.columns
213
212
  stmt.reset!
214
213
  stmt.bind_params(type_casted_binds)
214
+ records = stmt.to_a
215
215
  end
216
216
 
217
- ActiveRecord::Result.new(cols, stmt.to_a)
217
+ ActiveRecord::Result.new(cols, records)
218
218
  end
219
219
  end
220
220
 
@@ -16,15 +16,14 @@ module ActiveRecord
16
16
  # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
17
17
  # Returns a formatted string ready to be logged.
18
18
  def exec_explain(queries) # :nodoc:
19
- str = queries.map do |sql, bind|
20
- [].tap do |msg|
21
- msg << "EXPLAIN for: #{sql}"
22
- unless bind.empty?
23
- bind_msg = bind.map {|col, val| [col.name, val]}.inspect
24
- msg.last << " #{bind_msg}"
25
- end
26
- msg << connection.explain(sql, bind)
27
- end.join("\n")
19
+ str = queries.map do |sql, binds|
20
+ msg = "EXPLAIN for: #{sql}"
21
+ unless binds.empty?
22
+ msg << " "
23
+ msg << binds.map { |attr| render_bind(attr) }.inspect
24
+ end
25
+ msg << "\n"
26
+ msg << connection.explain(sql, binds)
28
27
  end.join("\n")
29
28
 
30
29
  # Overriding inspect to be more human readable, especially in the console.
@@ -34,5 +33,17 @@ module ActiveRecord
34
33
 
35
34
  str
36
35
  end
36
+
37
+ private
38
+
39
+ def render_bind(attr)
40
+ value = if attr.type.binary? && attr.value
41
+ "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
42
+ else
43
+ connection.type_cast(attr.value_for_database)
44
+ end
45
+
46
+ [attr.name, value]
47
+ end
37
48
  end
38
49
  end
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  module VERSION
8
8
  MAJOR = 5
9
9
  MINOR = 0
10
- TINY = 2
10
+ TINY = 3
11
11
  PRE = nil
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -20,20 +20,6 @@ module ActiveRecord
20
20
  @odd = false
21
21
  end
22
22
 
23
- def render_bind(attribute)
24
- value = if attribute.type.binary? && attribute.value
25
- if attribute.value.is_a?(Hash)
26
- "<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
27
- else
28
- "<#{attribute.value.bytesize} bytes of binary data>"
29
- end
30
- else
31
- attribute.value_for_database
32
- end
33
-
34
- [attribute.name, value]
35
- end
36
-
37
23
  def sql(event)
38
24
  self.class.runtime += event.duration
39
25
  return unless logger.debug?
@@ -47,7 +33,10 @@ module ActiveRecord
47
33
  binds = nil
48
34
 
49
35
  unless (payload[:binds] || []).empty?
50
- binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect
36
+ casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
37
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
38
+ render_bind(attr, value)
39
+ }.inspect
51
40
  end
52
41
 
53
42
  name = colorize_payload_name(name, payload[:name])
@@ -58,6 +47,20 @@ module ActiveRecord
58
47
 
59
48
  private
60
49
 
50
+ def type_casted_binds(binds, casted_binds)
51
+ casted_binds || ActiveRecord::Base.connection.type_casted_binds(binds)
52
+ end
53
+
54
+ def render_bind(attr, value)
55
+ if attr.is_a?(Array)
56
+ attr = attr.first
57
+ elsif attr.type.binary? && attr.value
58
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
59
+ end
60
+
61
+ [attr && attr.name, value]
62
+ end
63
+
61
64
  def colorize_payload_name(name, payload_name)
62
65
  if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
63
66
  color(name, MAGENTA, true)
@@ -1057,10 +1057,6 @@ module ActiveRecord
1057
1057
  Array(@migrations_paths)
1058
1058
  end
1059
1059
 
1060
- def match_to_migration_filename?(filename) # :nodoc:
1061
- File.basename(filename) =~ Migration::MigrationFilenameRegexp
1062
- end
1063
-
1064
1060
  def parse_migration_filename(filename) # :nodoc:
1065
1061
  File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1066
1062
  end
@@ -1068,9 +1064,7 @@ module ActiveRecord
1068
1064
  def migrations(paths)
1069
1065
  paths = Array(paths)
1070
1066
 
1071
- files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
1072
-
1073
- migrations = files.map do |file|
1067
+ migrations = migration_files(paths).map do |file|
1074
1068
  version, name, scope = parse_migration_filename(file)
1075
1069
  raise IllegalMigrationNameError.new(file) unless version
1076
1070
  version = version.to_i
@@ -1082,6 +1076,30 @@ module ActiveRecord
1082
1076
  migrations.sort_by(&:version)
1083
1077
  end
1084
1078
 
1079
+ def migrations_status(paths)
1080
+ paths = Array(paths)
1081
+
1082
+ db_list = ActiveRecord::SchemaMigration.normalized_versions
1083
+
1084
+ file_list = migration_files(paths).map do |file|
1085
+ version, name, scope = parse_migration_filename(file)
1086
+ raise IllegalMigrationNameError.new(file) unless version
1087
+ version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
1088
+ status = db_list.delete(version) ? "up" : "down"
1089
+ [status, version, (name + scope).humanize]
1090
+ end.compact
1091
+
1092
+ db_list.map! do |version|
1093
+ ["up", version, "********** NO FILE **********"]
1094
+ end
1095
+
1096
+ (db_list + file_list).sort_by { |_, version, _| version }
1097
+ end
1098
+
1099
+ def migration_files(paths)
1100
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1101
+ end
1102
+
1085
1103
  private
1086
1104
 
1087
1105
  def move(direction, migrations_paths, steps)
@@ -100,6 +100,10 @@ module ActiveRecord
100
100
  !(@new_record || @destroyed)
101
101
  end
102
102
 
103
+ ##
104
+ # :call-seq:
105
+ # save(*args)
106
+ #
103
107
  # Saves the model.
104
108
  #
105
109
  # If the model is new, a record gets created in the database, otherwise
@@ -121,12 +125,16 @@ module ActiveRecord
121
125
  #
122
126
  # Attributes marked as readonly are silently ignored if the record is
123
127
  # being updated.
124
- def save(*args)
125
- create_or_update(*args)
128
+ def save(*args, &block)
129
+ create_or_update(*args, &block)
126
130
  rescue ActiveRecord::RecordInvalid
127
131
  false
128
132
  end
129
133
 
134
+ ##
135
+ # :call-seq:
136
+ # save!(*args)
137
+ #
130
138
  # Saves the model.
131
139
  #
132
140
  # If the model is new, a record gets created in the database, otherwise
@@ -148,8 +156,8 @@ module ActiveRecord
148
156
  #
149
157
  # Attributes marked as readonly are silently ignored if the record is
150
158
  # being updated.
151
- def save!(*args)
152
- create_or_update(*args) || raise(RecordNotSaved.new("Failed to save the record", self))
159
+ def save!(*args, &block)
160
+ create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
153
161
  end
154
162
 
155
163
  # Deletes the record in the database and freezes this instance to
@@ -535,9 +543,9 @@ module ActiveRecord
535
543
  self.class.unscoped.where(self.class.primary_key => id)
536
544
  end
537
545
 
538
- def create_or_update(*args)
546
+ def create_or_update(*args, &block)
539
547
  raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
540
- result = new_record? ? _create_record : _update_record(*args)
548
+ result = new_record? ? _create_record(&block) : _update_record(*args, &block)
541
549
  result != false
542
550
  end
543
551
 
@@ -546,10 +554,14 @@ module ActiveRecord
546
554
  def _update_record(attribute_names = self.attribute_names)
547
555
  attributes_values = arel_attributes_with_values_for_update(attribute_names)
548
556
  if attributes_values.empty?
549
- 0
557
+ rows_affected = 0
550
558
  else
551
- self.class.unscoped._update_record attributes_values, id, id_was
559
+ rows_affected = self.class.unscoped._update_record attributes_values, id, id_was
552
560
  end
561
+
562
+ yield(self) if block_given?
563
+
564
+ rows_affected
553
565
  end
554
566
 
555
567
  # Creates a record with values matching those of the instance attributes
@@ -561,6 +573,9 @@ module ActiveRecord
561
573
  self.id ||= new_id if self.class.primary_key
562
574
 
563
575
  @new_record = false
576
+
577
+ yield(self) if block_given?
578
+
564
579
  id
565
580
  end
566
581
 
@@ -77,6 +77,8 @@ db_namespace = namespace :db do
77
77
  namespace :migrate do
78
78
  # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
79
79
  task :redo => [:environment, :load_config] do
80
+ raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
81
+
80
82
  if ENV['VERSION']
81
83
  db_namespace['migrate:down'].invoke
82
84
  db_namespace['migrate:up'].invoke
@@ -91,16 +93,17 @@ db_namespace = namespace :db do
91
93
 
92
94
  # desc 'Runs the "up" for a given migration VERSION.'
93
95
  task :up => [:environment, :load_config] do
96
+ raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
97
+
94
98
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
95
- raise 'VERSION is required' unless version
96
99
  ActiveRecord::Migrator.run(:up, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
97
100
  db_namespace['_dump'].invoke
98
101
  end
99
102
 
100
103
  # desc 'Runs the "down" for a given migration VERSION.'
101
104
  task :down => [:environment, :load_config] do
105
+ raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty?
102
106
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
103
- raise 'VERSION is required - To go down one migration, run db:rollback' unless version
104
107
  ActiveRecord::Migrator.run(:down, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
105
108
  db_namespace['_dump'].invoke
106
109
  end
@@ -110,28 +113,13 @@ db_namespace = namespace :db do
110
113
  unless ActiveRecord::SchemaMigration.table_exists?
111
114
  abort 'Schema migrations table does not exist yet.'
112
115
  end
113
- db_list = ActiveRecord::SchemaMigration.normalized_versions
114
-
115
- file_list =
116
- ActiveRecord::Tasks::DatabaseTasks.migrations_paths.flat_map do |path|
117
- Dir.foreach(path).map do |file|
118
- next unless ActiveRecord::Migrator.match_to_migration_filename?(file)
119
-
120
- version, name, scope = ActiveRecord::Migrator.parse_migration_filename(file)
121
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
122
- status = db_list.delete(version) ? 'up' : 'down'
123
- [status, version, (name + scope).humanize]
124
- end.compact
125
- end
126
116
 
127
- db_list.map! do |version|
128
- ['up', version, '********** NO FILE **********']
129
- end
130
117
  # output
131
118
  puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
132
119
  puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
133
120
  puts "-" * 50
134
- (db_list + file_list).sort_by { |_, version, _| version }.each do |status, version, name|
121
+ paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
122
+ ActiveRecord::Migrator.migrations_status(paths).each do |status, version, name|
135
123
  puts "#{status.center(8)} #{version.ljust(14)} #{name}"
136
124
  end
137
125
  puts
@@ -282,7 +282,7 @@ module ActiveRecord
282
282
  operation,
283
283
  distinct).as(aggregate_alias)
284
284
  ]
285
- select_values += select_values unless having_clause.empty?
285
+ select_values += self.select_values unless having_clause.empty?
286
286
 
287
287
  select_values.concat group_columns.map { |aliaz, field|
288
288
  if field.respond_to?(:as)
@@ -24,6 +24,8 @@ module ActiveRecord
24
24
 
25
25
  def inherited(child_class)
26
26
  child_class.initialize_relation_delegate_cache
27
+ delegate = child_class.relation_delegate_class(ActiveRecord::Associations::CollectionProxy)
28
+ delegate.include ActiveRecord::Associations::CollectionProxy::DelegateExtending
27
29
  super
28
30
  end
29
31
  end
@@ -154,6 +154,8 @@ module ActiveRecord
154
154
  end
155
155
 
156
156
  def migrate
157
+ raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
158
+
157
159
  verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
158
160
  version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
159
161
  scope = ENV['SCOPE']
@@ -124,7 +124,7 @@ module ActiveRecord
124
124
  # # statement will cause a PostgreSQL error, even though the unique
125
125
  # # constraint is no longer violated:
126
126
  # Number.create(i: 1)
127
- # # => "PGError: ERROR: current transaction is aborted, commands
127
+ # # => "PG::Error: ERROR: current transaction is aborted, commands
128
128
  # # ignored until end of transaction block"
129
129
  # end
130
130
  #
@@ -20,6 +20,14 @@ module ActiveRecord
20
20
  key_type = options[:primary_key_type]
21
21
  ", id: :#{key_type}" if key_type
22
22
  end
23
+
24
+ def db_migrate_path
25
+ if defined?(Rails.application) && Rails.application
26
+ Rails.application.config.paths["db/migrate"].to_ary.first
27
+ else
28
+ "db/migrate"
29
+ end
30
+ end
23
31
  end
24
32
  end
25
33
  end
@@ -68,14 +68,6 @@ module ActiveRecord
68
68
  def normalize_table_name(_table_name)
69
69
  pluralize_table_names? ? _table_name.pluralize : _table_name.singularize
70
70
  end
71
-
72
- def db_migrate_path
73
- if defined?(Rails) && Rails.application
74
- Rails.application.config.paths["db/migrate"].to_ary.first
75
- else
76
- "db/migrate"
77
- end
78
- end
79
71
  end
80
72
  end
81
73
  end
@@ -64,14 +64,6 @@ module ActiveRecord
64
64
  'app/models/application_record.rb'
65
65
  end
66
66
  end
67
-
68
- def db_migrate_path
69
- if defined?(Rails) && Rails.application
70
- Rails.application.config.paths["db/migrate"].to_ary.first
71
- else
72
- "db/migrate"
73
- end
74
- end
75
67
  end
76
68
  end
77
69
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.2
4
+ version: 5.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-01 00:00:00.000000000 Z
11
+ date: 2017-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.0.2
19
+ version: 5.0.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.0.2
26
+ version: 5.0.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 5.0.2
33
+ version: 5.0.3
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 5.0.2
40
+ version: 5.0.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: arel
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -333,3 +333,4 @@ signing_key:
333
333
  specification_version: 4
334
334
  summary: Object-relational mapper framework (part of Rails).
335
335
  test_files: []
336
+ has_rdoc: