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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +529 -10
- data/README.rdoc +3 -1
- data/lib/active_record.rb +7 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +27 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
- data/lib/active_record/connection_handling.rb +40 -17
- data/lib/active_record/core.rb +35 -24
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation.rb +185 -35
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +23 -14
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +230 -69
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/arel.rb +18 -4
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +19 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- 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
|
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,
|
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,
|
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
|
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
|
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
|
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,
|
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,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
710
|
-
|
711
|
-
sqls
|
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
|
-
|
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)
|
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
|
-
|
7
|
-
|
6
|
+
module PostgreSQL
|
7
|
+
class TypeMetadata < DelegateClass(SqlTypeMetadata)
|
8
|
+
undef to_yaml if method_defined?(:to_yaml)
|
8
9
|
|
9
|
-
|
10
|
+
attr_reader :oid, :fmod
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
36
|
-
|
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?(
|
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
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
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)
|