activerecord 6.0.0.beta1 → 6.0.1.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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +529 -10
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +7 -1
  5. data/lib/active_record/association_relation.rb +15 -6
  6. data/lib/active_record/associations.rb +4 -3
  7. data/lib/active_record/associations/association.rb +27 -2
  8. data/lib/active_record/associations/builder/association.rb +14 -18
  9. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  10. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  12. data/lib/active_record/associations/builder/has_many.rb +2 -0
  13. data/lib/active_record/associations/builder/has_one.rb +35 -1
  14. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  15. data/lib/active_record/associations/collection_association.rb +5 -6
  16. data/lib/active_record/associations/collection_proxy.rb +13 -42
  17. data/lib/active_record/associations/has_many_association.rb +1 -9
  18. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  19. data/lib/active_record/associations/join_dependency.rb +14 -9
  20. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  21. data/lib/active_record/associations/preloader.rb +12 -7
  22. data/lib/active_record/associations/preloader/association.rb +37 -34
  23. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  24. data/lib/active_record/attribute_methods.rb +3 -53
  25. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  26. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  27. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  28. data/lib/active_record/attribute_methods/query.rb +2 -3
  29. data/lib/active_record/attribute_methods/read.rb +3 -9
  30. data/lib/active_record/attribute_methods/write.rb +6 -12
  31. data/lib/active_record/attributes.rb +13 -0
  32. data/lib/active_record/autosave_association.rb +21 -7
  33. data/lib/active_record/base.rb +0 -1
  34. data/lib/active_record/callbacks.rb +3 -3
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
  36. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  37. data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
  38. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
  39. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  40. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  41. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  42. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  43. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  44. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  45. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
  46. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
  47. data/lib/active_record/connection_adapters/column.rb +17 -13
  48. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  49. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  50. data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
  51. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  52. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  53. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  54. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  55. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  56. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
  59. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  60. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  61. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  62. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  63. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  64. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  66. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
  68. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  69. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  70. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  71. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  72. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  73. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
  74. data/lib/active_record/connection_handling.rb +40 -17
  75. data/lib/active_record/core.rb +35 -24
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  78. data/lib/active_record/database_configurations/url_config.rb +21 -16
  79. data/lib/active_record/dynamic_matchers.rb +1 -1
  80. data/lib/active_record/enum.rb +15 -0
  81. data/lib/active_record/errors.rb +18 -13
  82. data/lib/active_record/fixtures.rb +11 -6
  83. data/lib/active_record/gem_version.rb +2 -2
  84. data/lib/active_record/inheritance.rb +1 -1
  85. data/lib/active_record/insert_all.rb +179 -0
  86. data/lib/active_record/integration.rb +13 -1
  87. data/lib/active_record/internal_metadata.rb +5 -1
  88. data/lib/active_record/locking/optimistic.rb +3 -4
  89. data/lib/active_record/log_subscriber.rb +1 -1
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  92. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/migration/command_recorder.rb +28 -14
  95. data/lib/active_record/migration/compatibility.rb +72 -63
  96. data/lib/active_record/model_schema.rb +3 -0
  97. data/lib/active_record/persistence.rb +212 -19
  98. data/lib/active_record/querying.rb +18 -14
  99. data/lib/active_record/railtie.rb +9 -1
  100. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  101. data/lib/active_record/railties/databases.rake +124 -25
  102. data/lib/active_record/reflection.rb +18 -32
  103. data/lib/active_record/relation.rb +185 -35
  104. data/lib/active_record/relation/calculations.rb +40 -44
  105. data/lib/active_record/relation/delegation.rb +23 -31
  106. data/lib/active_record/relation/finder_methods.rb +23 -14
  107. data/lib/active_record/relation/merger.rb +11 -16
  108. data/lib/active_record/relation/query_attribute.rb +5 -3
  109. data/lib/active_record/relation/query_methods.rb +230 -69
  110. data/lib/active_record/relation/spawn_methods.rb +1 -1
  111. data/lib/active_record/relation/where_clause.rb +10 -10
  112. data/lib/active_record/sanitization.rb +33 -4
  113. data/lib/active_record/schema.rb +1 -1
  114. data/lib/active_record/schema_dumper.rb +10 -1
  115. data/lib/active_record/schema_migration.rb +1 -1
  116. data/lib/active_record/scoping.rb +6 -7
  117. data/lib/active_record/scoping/default.rb +7 -15
  118. data/lib/active_record/scoping/named.rb +10 -2
  119. data/lib/active_record/statement_cache.rb +2 -2
  120. data/lib/active_record/store.rb +48 -0
  121. data/lib/active_record/table_metadata.rb +9 -13
  122. data/lib/active_record/tasks/database_tasks.rb +109 -6
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  124. data/lib/active_record/test_databases.rb +1 -16
  125. data/lib/active_record/test_fixtures.rb +2 -2
  126. data/lib/active_record/timestamp.rb +35 -19
  127. data/lib/active_record/touch_later.rb +4 -2
  128. data/lib/active_record/transactions.rb +56 -46
  129. data/lib/active_record/type_caster/connection.rb +16 -10
  130. data/lib/active_record/validations.rb +1 -0
  131. data/lib/active_record/validations/uniqueness.rb +4 -4
  132. data/lib/arel.rb +18 -4
  133. data/lib/arel/insert_manager.rb +3 -3
  134. data/lib/arel/nodes.rb +2 -1
  135. data/lib/arel/nodes/and.rb +1 -1
  136. data/lib/arel/nodes/case.rb +1 -1
  137. data/lib/arel/nodes/comment.rb +29 -0
  138. data/lib/arel/nodes/select_core.rb +16 -12
  139. data/lib/arel/nodes/unary.rb +1 -0
  140. data/lib/arel/nodes/values_list.rb +2 -17
  141. data/lib/arel/select_manager.rb +10 -10
  142. data/lib/arel/visitors/depth_first.rb +7 -2
  143. data/lib/arel/visitors/dot.rb +7 -2
  144. data/lib/arel/visitors/ibm_db.rb +13 -0
  145. data/lib/arel/visitors/informix.rb +6 -0
  146. data/lib/arel/visitors/mssql.rb +15 -1
  147. data/lib/arel/visitors/oracle12.rb +4 -5
  148. data/lib/arel/visitors/postgresql.rb +4 -10
  149. data/lib/arel/visitors/to_sql.rb +107 -131
  150. data/lib/arel/visitors/visitor.rb +9 -5
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  152. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  154. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  155. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  156. metadata +19 -12
  157. data/lib/active_record/collection_cache_key.rb +0 -53
  158. data/lib/arel/nodes/values.rb +0 -16
@@ -287,7 +287,7 @@ module ActiveRecord
287
287
  quoted_sequence = quote_table_name(sequence)
288
288
  max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
289
289
  if max_pk.nil?
290
- if postgresql_version >= 100000
290
+ if database_version >= 100000
291
291
  minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
292
292
  else
293
293
  minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
@@ -368,31 +368,6 @@ module ActiveRecord
368
368
  SQL
369
369
  end
370
370
 
371
- def bulk_change_table(table_name, operations)
372
- sql_fragments = []
373
- non_combinable_operations = []
374
-
375
- operations.each do |command, args|
376
- table, arguments = args.shift, args
377
- method = :"#{command}_for_alter"
378
-
379
- if respond_to?(method, true)
380
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
381
- sql_fragments << sqls
382
- non_combinable_operations.concat(procs)
383
- else
384
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
385
- non_combinable_operations.each(&:call)
386
- sql_fragments = []
387
- non_combinable_operations = []
388
- send(command, table, *arguments)
389
- end
390
- end
391
-
392
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
393
- non_combinable_operations.each(&:call)
394
- end
395
-
396
371
  # Renames a table.
397
372
  # Also renames a table's primary key sequence if the sequence name exists and
398
373
  # matches the Active Record default.
@@ -443,14 +418,16 @@ module ActiveRecord
443
418
  end
444
419
 
445
420
  # Adds comment for given table column or drops it if +comment+ is a +nil+
446
- def change_column_comment(table_name, column_name, comment) # :nodoc:
421
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
447
422
  clear_cache!
423
+ comment = extract_new_comment_value(comment_or_changes)
448
424
  execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
449
425
  end
450
426
 
451
427
  # Adds comment for given table or drops it if +comment+ is a +nil+
452
- def change_table_comment(table_name, comment) # :nodoc:
428
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
453
429
  clear_cache!
430
+ comment = extract_new_comment_value(comment_or_changes)
454
431
  execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
455
432
  end
456
433
 
@@ -548,21 +525,21 @@ module ActiveRecord
548
525
  # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
549
526
  case limit
550
527
  when nil, 0..0x3fffffff; super(type)
551
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
528
+ else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
552
529
  end
553
530
  when "text"
554
531
  # PostgreSQL doesn't support limits on text columns.
555
532
  # The hard limit is 1GB, according to section 8.3 in the manual.
556
533
  case limit
557
534
  when nil, 0..0x3fffffff; super(type)
558
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
535
+ else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
559
536
  end
560
537
  when "integer"
561
538
  case limit
562
539
  when 1, 2; "smallint"
563
540
  when nil, 3, 4; "integer"
564
541
  when 5..8; "bigint"
565
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
542
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
566
543
  end
567
544
  else
568
545
  super
@@ -623,10 +600,10 @@ module ActiveRecord
623
600
  # validate_foreign_key :accounts, name: :special_fk_name
624
601
  #
625
602
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
626
- def validate_foreign_key(from_table, options_or_to_table = {})
603
+ def validate_foreign_key(from_table, to_table = nil, **options)
627
604
  return unless supports_validate_constraints?
628
605
 
629
- fk_name_to_validate = foreign_key_for!(from_table, options_or_to_table).name
606
+ fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
630
607
 
631
608
  validate_constraint from_table, fk_name_to_validate
632
609
  end
@@ -637,7 +614,7 @@ module ActiveRecord
637
614
  end
638
615
 
639
616
  def create_table_definition(*args)
640
- PostgreSQL::TableDefinition.new(*args)
617
+ PostgreSQL::TableDefinition.new(self, *args)
641
618
  end
642
619
 
643
620
  def create_alter_table(name)
@@ -650,16 +627,19 @@ module ActiveRecord
650
627
  default_value = extract_value_from_default(default)
651
628
  default_function = extract_default_function(default_value, default)
652
629
 
653
- PostgreSQLColumn.new(
630
+ if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
631
+ serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
632
+ end
633
+
634
+ PostgreSQL::Column.new(
654
635
  column_name,
655
636
  default_value,
656
637
  type_metadata,
657
638
  !notnull,
658
- table_name,
659
639
  default_function,
660
- collation,
640
+ collation: collation,
661
641
  comment: comment.presence,
662
- max_identifier_length: max_identifier_length
642
+ serial: serial
663
643
  )
664
644
  end
665
645
 
@@ -672,7 +652,23 @@ module ActiveRecord
672
652
  precision: cast_type.precision,
673
653
  scale: cast_type.scale,
674
654
  )
675
- PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
655
+ PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
656
+ end
657
+
658
+ def sequence_name_from_parts(table_name, column_name, suffix)
659
+ over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
660
+
661
+ if over_length > 0
662
+ column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
663
+ over_length -= column_name.length - column_name_length
664
+ column_name = column_name[0, column_name_length - [over_length, 0].min]
665
+ end
666
+
667
+ if over_length > 0
668
+ table_name = table_name[0, table_name.length - over_length]
669
+ end
670
+
671
+ "#{table_name}_#{column_name}_#{suffix}"
676
672
  end
677
673
 
678
674
  def extract_foreign_key_action(specifier)
@@ -683,38 +679,20 @@ module ActiveRecord
683
679
  end
684
680
  end
685
681
 
686
- def change_column_sql(table_name, column_name, type, options = {})
687
- quoted_column_name = quote_column_name(column_name)
688
- sql_type = type_to_sql(type, options)
689
- sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
690
- if options[:collation]
691
- sql << " COLLATE \"#{options[:collation]}\""
692
- end
693
- if options[:using]
694
- sql << " USING #{options[:using]}"
695
- elsif options[:cast_as]
696
- cast_as_type = type_to_sql(options[:cast_as], options)
697
- sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
698
- end
699
-
700
- sql
701
- end
702
-
703
682
  def add_column_for_alter(table_name, column_name, type, options = {})
704
683
  return super unless options.key?(:comment)
705
684
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
706
685
  end
707
686
 
708
687
  def change_column_for_alter(table_name, column_name, type, options = {})
709
- sqls = [change_column_sql(table_name, column_name, type, options)]
710
- sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default)
711
- sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
688
+ td = create_table_definition(table_name)
689
+ cd = td.new_column_definition(column_name, type, options)
690
+ sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
712
691
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
713
692
  sqls
714
693
  end
715
694
 
716
- # Changes the default value of a table column.
717
- def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
695
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
718
696
  column = column_for(table_name, column_name)
719
697
  return unless column
720
698
 
@@ -729,11 +707,17 @@ module ActiveRecord
729
707
  end
730
708
  end
731
709
 
732
- def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc:
733
- "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
710
+ def change_column_null_for_alter(table_name, column_name, null, default = nil)
711
+ "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
734
712
  end
735
713
 
736
714
  def add_timestamps_for_alter(table_name, options = {})
715
+ options[:null] = false if options[:null].nil?
716
+
717
+ if !options.key?(:precision) && supports_datetime_with_precision?
718
+ options[:precision] = 6
719
+ end
720
+
737
721
  [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
738
722
  end
739
723
 
@@ -3,38 +3,34 @@
3
3
  module ActiveRecord
4
4
  # :stopdoc:
5
5
  module ConnectionAdapters
6
- class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
7
- undef to_yaml if method_defined?(:to_yaml)
6
+ module PostgreSQL
7
+ class TypeMetadata < DelegateClass(SqlTypeMetadata)
8
+ undef to_yaml if method_defined?(:to_yaml)
8
9
 
9
- attr_reader :oid, :fmod, :array
10
+ attr_reader :oid, :fmod
10
11
 
11
- def initialize(type_metadata, oid: nil, fmod: nil)
12
- super(type_metadata)
13
- @type_metadata = type_metadata
14
- @oid = oid
15
- @fmod = fmod
16
- @array = /\[\]$/.match?(type_metadata.sql_type)
17
- end
18
-
19
- def sql_type
20
- super.gsub(/\[\]$/, "")
21
- end
22
-
23
- def ==(other)
24
- other.is_a?(PostgreSQLTypeMetadata) &&
25
- attributes_for_hash == other.attributes_for_hash
26
- end
27
- alias eql? ==
28
-
29
- def hash
30
- attributes_for_hash.hash
31
- end
12
+ def initialize(type_metadata, oid: nil, fmod: nil)
13
+ super(type_metadata)
14
+ @oid = oid
15
+ @fmod = fmod
16
+ end
32
17
 
33
- protected
18
+ def ==(other)
19
+ other.is_a?(TypeMetadata) &&
20
+ __getobj__ == other.__getobj__ &&
21
+ oid == other.oid &&
22
+ fmod == other.fmod
23
+ end
24
+ alias eql? ==
34
25
 
35
- def attributes_for_hash
36
- [self.class, @type_metadata, oid, fmod]
26
+ def hash
27
+ TypeMetadata.hash ^
28
+ __getobj__.hash ^
29
+ oid.hash ^
30
+ fmod.hash
37
31
  end
32
+ end
38
33
  end
34
+ PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
39
35
  end
40
36
  end
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  conn = PG.connect(conn_params)
47
47
  ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
48
  rescue ::PG::Error => error
49
- if error.message.include?("does not exist")
49
+ if error.message.include?(conn_params[:dbname])
50
50
  raise ActiveRecord::NoDatabaseError
51
51
  else
52
52
  raise
@@ -196,6 +196,17 @@ module ActiveRecord
196
196
  true
197
197
  end
198
198
 
199
+ def supports_insert_returning?
200
+ true
201
+ end
202
+
203
+ def supports_insert_on_conflict?
204
+ database_version >= 90500
205
+ end
206
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
207
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
208
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
209
+
199
210
  def index_algorithms
200
211
  { concurrently: "CONCURRENTLY" }
201
212
  end
@@ -240,9 +251,6 @@ module ActiveRecord
240
251
 
241
252
  configure_connection
242
253
  add_pg_encoders
243
- @statements = StatementPool.new @connection,
244
- self.class.type_cast_config_to_integer(config[:statement_limit])
245
-
246
254
  add_pg_decoders
247
255
 
248
256
  @type_map = Type::HashLookupTypeMap.new
@@ -251,15 +259,10 @@ module ActiveRecord
251
259
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
252
260
  end
253
261
 
254
- # Clears the prepared statements cache.
255
- def clear_cache!
256
- @lock.synchronize do
257
- @statements.clear
258
- end
259
- end
260
-
261
- def truncate(table_name, name = nil)
262
- exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
262
+ def self.database_exists?(config)
263
+ !!ActiveRecord::Base.postgresql_connection(config)
264
+ rescue ActiveRecord::NoDatabaseError
265
+ false
263
266
  end
264
267
 
265
268
  # Is this connection alive and ready for queries?
@@ -278,6 +281,8 @@ module ActiveRecord
278
281
  super
279
282
  @connection.reset
280
283
  configure_connection
284
+ rescue PG::ConnectionBad
285
+ connect
281
286
  end
282
287
  end
283
288
 
@@ -303,6 +308,7 @@ module ActiveRecord
303
308
  end
304
309
 
305
310
  def discard! # :nodoc:
311
+ super
306
312
  @connection.socket_io.reopen(IO::NULL) rescue nil
307
313
  @connection = nil
308
314
  end
@@ -345,7 +351,18 @@ module ActiveRecord
345
351
  end
346
352
 
347
353
  def supports_pgcrypto_uuid?
348
- postgresql_version >= 90400
354
+ database_version >= 90400
355
+ end
356
+
357
+ def supports_optimizer_hints?
358
+ unless defined?(@has_pg_hint_plan)
359
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
360
+ end
361
+ @has_pg_hint_plan
362
+ end
363
+
364
+ def supports_common_table_expressions?
365
+ true
349
366
  end
350
367
 
351
368
  def supports_lazy_transactions?
@@ -378,9 +395,12 @@ module ActiveRecord
378
395
  }
379
396
  end
380
397
 
398
+ def extension_available?(name)
399
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
400
+ end
401
+
381
402
  def extension_enabled?(name)
382
- res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
383
- res.cast_values.first
403
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
384
404
  end
385
405
 
386
406
  def extensions
@@ -391,8 +411,6 @@ module ActiveRecord
391
411
  def max_identifier_length
392
412
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
393
413
  end
394
- alias table_alias_length max_identifier_length
395
- alias index_name_length max_identifier_length
396
414
 
397
415
  # Set the authorized user for this session
398
416
  def session_auth=(user)
@@ -415,20 +433,36 @@ module ActiveRecord
415
433
  }
416
434
 
417
435
  # Returns the version of the connected PostgreSQL server.
418
- def postgresql_version
436
+ def get_database_version # :nodoc:
419
437
  @connection.server_version
420
438
  end
439
+ alias :postgresql_version :database_version
421
440
 
422
441
  def default_index_type?(index) # :nodoc:
423
442
  index.using == :btree || super
424
443
  end
425
444
 
426
- private
427
- def check_version
428
- if postgresql_version < 90300
429
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.3."
430
- end
445
+ def build_insert_sql(insert) # :nodoc:
446
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
447
+
448
+ if insert.skip_duplicates?
449
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
450
+ elsif insert.update_duplicates?
451
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
452
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
453
+ end
454
+
455
+ sql << " RETURNING #{insert.returning}" if insert.returning
456
+ sql
457
+ end
458
+
459
+ def check_version # :nodoc:
460
+ if database_version < 90300
461
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
431
462
  end
463
+ end
464
+
465
+ private
432
466
 
433
467
  # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
434
468
  VALUE_LIMIT_VIOLATION = "22001"
@@ -628,6 +662,10 @@ module ActiveRecord
628
662
  def exec_no_cache(sql, name, binds)
629
663
  materialize_transactions
630
664
 
665
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
666
+ # made since we established the connection
667
+ update_typemap_for_default_timezone
668
+
631
669
  type_casted_binds = type_casted_binds(binds)
632
670
  log(sql, name, binds, type_casted_binds) do
633
671
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -638,6 +676,7 @@ module ActiveRecord
638
676
 
639
677
  def exec_cache(sql, name, binds)
640
678
  materialize_transactions
679
+ update_typemap_for_default_timezone
641
680
 
642
681
  stmt_key = prepare_statement(sql, binds)
643
682
  type_casted_binds = type_casted_binds(binds)
@@ -716,6 +755,8 @@ module ActiveRecord
716
755
  def connect
717
756
  @connection = PG.connect(@connection_parameters)
718
757
  configure_connection
758
+ add_pg_encoders
759
+ add_pg_decoders
719
760
  end
720
761
 
721
762
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -796,6 +837,10 @@ module ActiveRecord
796
837
  Arel::Visitors::PostgreSQL.new(self)
797
838
  end
798
839
 
840
+ def build_statement_pool
841
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
842
+ end
843
+
799
844
  def can_perform_case_insensitive_comparison_for?(column)
800
845
  @case_insensitive_cache ||= {}
801
846
  @case_insensitive_cache[column.sql_type] ||= begin
@@ -826,7 +871,22 @@ module ActiveRecord
826
871
  @connection.type_map_for_queries = map
827
872
  end
828
873
 
874
+ def update_typemap_for_default_timezone
875
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
876
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
877
+ PG::TextDecoder::TimestampUtc :
878
+ PG::TextDecoder::TimestampWithoutTimeZone
879
+
880
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
881
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
882
+ @default_timezone = ActiveRecord::Base.default_timezone
883
+ end
884
+ end
885
+
829
886
  def add_pg_decoders
887
+ @default_timezone = nil
888
+ @timestamp_decoder = nil
889
+
830
890
  coders_by_name = {
831
891
  "int2" => PG::TextDecoder::Integer,
832
892
  "int4" => PG::TextDecoder::Integer,
@@ -836,6 +896,13 @@ module ActiveRecord
836
896
  "float8" => PG::TextDecoder::Float,
837
897
  "bool" => PG::TextDecoder::Boolean,
838
898
  }
899
+
900
+ if defined?(PG::TextDecoder::TimestampUtc)
901
+ # Use native PG encoders available since pg-1.1
902
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
903
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
904
+ end
905
+
839
906
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
840
907
  query = <<~SQL % known_coder_types.join(", ")
841
908
  SELECT t.oid, t.typname
@@ -851,6 +918,10 @@ module ActiveRecord
851
918
  map = PG::TypeMapByOid.new
852
919
  coders.each { |coder| map.add_coder(coder) }
853
920
  @connection.type_map_for_results = map
921
+
922
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
923
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
924
+ update_typemap_for_default_timezone
854
925
  end
855
926
 
856
927
  def construct_coder(row, coder_class)