activerecord 5.1.0 → 5.2.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +596 -450
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations.rb +77 -85
- data/lib/active_record/associations/alias_tracker.rb +23 -32
- data/lib/active_record/associations/association.rb +49 -35
- data/lib/active_record/associations/association_scope.rb +55 -55
- data/lib/active_record/associations/belongs_to_association.rb +30 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +66 -53
- data/lib/active_record/associations/collection_proxy.rb +30 -73
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +13 -2
- data/lib/active_record/associations/has_many_through_association.rb +37 -19
- data/lib/active_record/associations/has_one_association.rb +14 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency.rb +52 -96
- data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/preloader/association.rb +53 -92
- data/lib/active_record/associations/preloader/through_association.rb +72 -73
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +27 -12
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +33 -216
- data/lib/active_record/attribute_methods/primary_key.rb +10 -13
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +9 -3
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +22 -19
- data/lib/active_record/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +15 -13
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +12 -6
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +2 -0
- data/lib/active_record/collection_cache_key.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
- data/lib/active_record/connection_adapters/column.rb +4 -2
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +41 -61
- data/lib/active_record/counter_cache.rb +20 -15
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +18 -13
- data/lib/active_record/errors.rb +60 -15
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +49 -19
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +30 -42
- data/lib/active_record/locking/pessimistic.rb +10 -7
- data/lib/active_record/log_subscriber.rb +46 -4
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +81 -29
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/model_schema.rb +74 -58
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +199 -54
- data/lib/active_record/query_cache.rb +8 -10
- data/lib/active_record/querying.rb +5 -3
- data/lib/active_record/railtie.rb +62 -6
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +48 -38
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +137 -207
- data/lib/active_record/relation.rb +132 -207
- data/lib/active_record/relation/batches.rb +32 -17
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/calculations.rb +66 -25
- data/lib/active_record/relation/delegation.rb +45 -29
- data/lib/active_record/relation/finder_methods.rb +76 -85
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +53 -23
- data/lib/active_record/relation/predicate_builder.rb +60 -79
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +135 -103
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +65 -67
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping.rb +12 -10
- data/lib/active_record/scoping/default.rb +10 -7
- data/lib/active_record/scoping/named.rb +40 -12
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +38 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +13 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +32 -27
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +6 -0
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +36 -6
- data/lib/active_record/version.rb +2 -0
- data/lib/rails/generators/active_record.rb +3 -1
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +24 -36
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -18
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute_mutation_tracker.rb +0 -113
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/attribute_set/builder.rb +0 -124
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,27 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
4
6
|
module ReferentialIntegrity # :nodoc:
|
5
|
-
def supports_disable_referential_integrity? # :nodoc:
|
6
|
-
true
|
7
|
-
end
|
8
|
-
|
9
7
|
def disable_referential_integrity # :nodoc:
|
10
|
-
|
11
|
-
original_exception = nil
|
8
|
+
original_exception = nil
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
rescue ActiveRecord::ActiveRecordError => e
|
18
|
-
original_exception = e
|
10
|
+
begin
|
11
|
+
transaction(requires_new: true) do
|
12
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
19
13
|
end
|
14
|
+
rescue ActiveRecord::ActiveRecordError => e
|
15
|
+
original_exception = e
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
begin
|
19
|
+
yield
|
20
|
+
rescue ActiveRecord::InvalidForeignKey => e
|
21
|
+
warn <<-WARNING
|
25
22
|
WARNING: Rails was not able to disable referential integrity.
|
26
23
|
|
27
24
|
This is most likely caused due to missing permissions.
|
@@ -30,17 +27,14 @@ Rails needs superuser privileges to disable referential integrity.
|
|
30
27
|
cause: #{original_exception.try(:message)}
|
31
28
|
|
32
29
|
WARNING
|
33
|
-
|
34
|
-
|
30
|
+
raise e
|
31
|
+
end
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
rescue ActiveRecord::ActiveRecordError
|
33
|
+
begin
|
34
|
+
transaction(requires_new: true) do
|
35
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
|
41
36
|
end
|
42
|
-
|
43
|
-
yield
|
37
|
+
rescue ActiveRecord::ActiveRecordError
|
44
38
|
end
|
45
39
|
end
|
46
40
|
end
|
@@ -1,8 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
4
6
|
class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
|
5
7
|
private
|
8
|
+
def visit_AlterTable(o)
|
9
|
+
super << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_AddForeignKey(o)
|
13
|
+
super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_ValidateConstraint(name)
|
17
|
+
"VALIDATE CONSTRAINT #{quote_column_name(name)}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_ChangeColumnDefinition(o)
|
21
|
+
column = o.column
|
22
|
+
column.sql_type = type_to_sql(column.type, column.options)
|
23
|
+
quoted_column_name = quote_column_name(o.name)
|
24
|
+
|
25
|
+
change_column_sql = "ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}".dup
|
26
|
+
|
27
|
+
options = column_options(column)
|
28
|
+
|
29
|
+
if options[:collation]
|
30
|
+
change_column_sql << " COLLATE \"#{options[:collation]}\""
|
31
|
+
end
|
32
|
+
|
33
|
+
if options[:using]
|
34
|
+
change_column_sql << " USING #{options[:using]}"
|
35
|
+
elsif options[:cast_as]
|
36
|
+
cast_as_type = type_to_sql(options[:cast_as], options)
|
37
|
+
change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
38
|
+
end
|
39
|
+
|
40
|
+
if options.key?(:default)
|
41
|
+
if options[:default].nil?
|
42
|
+
change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
|
43
|
+
else
|
44
|
+
quoted_default = quote_default_expression(options[:default], column)
|
45
|
+
change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if options.key?(:null)
|
50
|
+
change_column_sql << ", ALTER COLUMN #{quoted_column_name} #{options[:null] ? 'DROP' : 'SET'} NOT NULL"
|
51
|
+
end
|
52
|
+
|
53
|
+
change_column_sql
|
54
|
+
end
|
55
|
+
|
6
56
|
def add_column_options!(sql, options)
|
7
57
|
if options[:collation]
|
8
58
|
sql << " COLLATE \"#{options[:collation]}\""
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
@@ -42,15 +44,8 @@ module ActiveRecord
|
|
42
44
|
# a record (as primary keys cannot be +nil+). This might be done via the
|
43
45
|
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
|
44
46
|
def primary_key(name, type = :primary_key, **options)
|
45
|
-
options[:auto_increment] = true if [:integer, :bigint].include?(type) && !options.key?(:default)
|
46
47
|
if type == :uuid
|
47
48
|
options[:default] = options.fetch(:default, "gen_random_uuid()")
|
48
|
-
elsif options.delete(:auto_increment) == true && %i(integer bigint).include?(type)
|
49
|
-
type = if type == :bigint || options[:limit] == 8
|
50
|
-
:bigserial
|
51
|
-
else
|
52
|
-
:serial
|
53
|
-
end
|
54
49
|
end
|
55
50
|
|
56
51
|
super
|
@@ -100,10 +95,6 @@ module ActiveRecord
|
|
100
95
|
args.each { |name| column(name, :int8range, options) }
|
101
96
|
end
|
102
97
|
|
103
|
-
def json(*args, **options)
|
104
|
-
args.each { |name| column(name, :json, options) }
|
105
|
-
end
|
106
|
-
|
107
98
|
def jsonb(*args, **options)
|
108
99
|
args.each { |name| column(name, :jsonb, options) }
|
109
100
|
end
|
@@ -183,11 +174,33 @@ module ActiveRecord
|
|
183
174
|
|
184
175
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
185
176
|
include ColumnMethods
|
177
|
+
|
178
|
+
private
|
179
|
+
def integer_like_primary_key_type(type, options)
|
180
|
+
if type == :bigint || options[:limit] == 8
|
181
|
+
:bigserial
|
182
|
+
else
|
183
|
+
:serial
|
184
|
+
end
|
185
|
+
end
|
186
186
|
end
|
187
187
|
|
188
188
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
189
189
|
include ColumnMethods
|
190
190
|
end
|
191
|
+
|
192
|
+
class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
|
193
|
+
attr_reader :constraint_validations
|
194
|
+
|
195
|
+
def initialize(td)
|
196
|
+
super
|
197
|
+
@constraint_validations = []
|
198
|
+
end
|
199
|
+
|
200
|
+
def validate_constraint(name)
|
201
|
+
@constraint_validations << name
|
202
|
+
end
|
203
|
+
end
|
191
204
|
end
|
192
205
|
end
|
193
206
|
end
|
@@ -1,21 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
4
|
-
|
5
|
-
# Adds +:array+ option to the default set
|
6
|
-
def prepare_column_options(column)
|
7
|
-
spec = super
|
8
|
-
spec[:array] = "true" if column.array?
|
9
|
-
spec
|
10
|
-
end
|
11
|
-
|
12
|
-
# Adds +:array+ as a valid migration key
|
13
|
-
def migration_keys
|
14
|
-
super + [:array]
|
15
|
-
end
|
16
|
-
|
6
|
+
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
17
7
|
private
|
18
8
|
|
9
|
+
def extensions(stream)
|
10
|
+
extensions = @connection.extensions
|
11
|
+
if extensions.any?
|
12
|
+
stream.puts " # These are extensions that must be enabled in order to support this database"
|
13
|
+
extensions.sort.each do |extension|
|
14
|
+
stream.puts " enable_extension #{extension.inspect}"
|
15
|
+
end
|
16
|
+
stream.puts
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare_column_options(column)
|
21
|
+
spec = super
|
22
|
+
spec[:array] = "true" if column.array?
|
23
|
+
spec
|
24
|
+
end
|
25
|
+
|
19
26
|
def default_primary_key?(column)
|
20
27
|
schema_type(column) == :bigserial
|
21
28
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
@@ -38,7 +38,7 @@ module ActiveRecord
|
|
38
38
|
" TABLESPACE = \"#{value}\""
|
39
39
|
when :connection_limit
|
40
40
|
" CONNECTION LIMIT = #{value}"
|
41
|
-
|
41
|
+
else
|
42
42
|
""
|
43
43
|
end
|
44
44
|
end
|
@@ -60,20 +60,15 @@ module ActiveRecord
|
|
60
60
|
|
61
61
|
# Returns true if schema exists.
|
62
62
|
def schema_exists?(name)
|
63
|
-
|
63
|
+
query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
|
64
64
|
end
|
65
65
|
|
66
66
|
# Verifies existence of an index with a given name.
|
67
|
-
def index_name_exists?(table_name, index_name
|
68
|
-
unless default.nil?
|
69
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
70
|
-
Passing default to #index_name_exists? is deprecated without replacement.
|
71
|
-
MSG
|
72
|
-
end
|
67
|
+
def index_name_exists?(table_name, index_name)
|
73
68
|
table = quoted_scope(table_name)
|
74
69
|
index = quoted_scope(index_name)
|
75
70
|
|
76
|
-
|
71
|
+
query_value(<<-SQL, "SCHEMA").to_i > 0
|
77
72
|
SELECT COUNT(*)
|
78
73
|
FROM pg_class t
|
79
74
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
@@ -87,21 +82,12 @@ module ActiveRecord
|
|
87
82
|
end
|
88
83
|
|
89
84
|
# Returns an array of indexes for the given table.
|
90
|
-
def indexes(table_name
|
91
|
-
if name
|
92
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
93
|
-
Passing name to #indexes is deprecated without replacement.
|
94
|
-
MSG
|
95
|
-
end
|
96
|
-
|
85
|
+
def indexes(table_name) # :nodoc:
|
97
86
|
scope = quoted_scope(table_name)
|
98
87
|
|
99
88
|
result = query(<<-SQL, "SCHEMA")
|
100
89
|
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
101
|
-
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
102
|
-
(SELECT COUNT(*) FROM pg_opclass o
|
103
|
-
JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
|
104
|
-
ON o.oid = c.oid WHERE o.opcdefault = 'f')
|
90
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
105
91
|
FROM pg_class t
|
106
92
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
107
93
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
@@ -120,11 +106,13 @@ module ActiveRecord
|
|
120
106
|
inddef = row[3]
|
121
107
|
oid = row[4]
|
122
108
|
comment = row[5]
|
123
|
-
opclass = row[6]
|
124
109
|
|
125
|
-
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
|
110
|
+
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
|
111
|
+
|
112
|
+
orders = {}
|
113
|
+
opclasses = {}
|
126
114
|
|
127
|
-
if indkey.include?(0)
|
115
|
+
if indkey.include?(0)
|
128
116
|
columns = expressions
|
129
117
|
else
|
130
118
|
columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
|
@@ -134,33 +122,30 @@ module ActiveRecord
|
|
134
122
|
AND a.attnum IN (#{indkey.join(",")})
|
135
123
|
SQL
|
136
124
|
|
137
|
-
# add info on sort order
|
138
|
-
|
139
|
-
|
140
|
-
|
125
|
+
# add info on sort order (only desc order is explicitly specified, asc is the default)
|
126
|
+
# and non-default opclasses
|
127
|
+
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
|
128
|
+
opclasses[column] = opclass.to_sym if opclass
|
129
|
+
if nulls
|
130
|
+
orders[column] = [desc, nulls].compact.join(" ")
|
131
|
+
else
|
132
|
+
orders[column] = :desc if desc
|
133
|
+
end
|
134
|
+
end
|
141
135
|
end
|
142
136
|
|
143
|
-
IndexDefinition.new(
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
column_name,
|
156
|
-
default_value,
|
157
|
-
type_metadata,
|
158
|
-
!notnull,
|
159
|
-
table_name,
|
160
|
-
default_function,
|
161
|
-
collation,
|
162
|
-
comment: comment.presence
|
163
|
-
)
|
137
|
+
IndexDefinition.new(
|
138
|
+
table_name,
|
139
|
+
index_name,
|
140
|
+
unique,
|
141
|
+
columns,
|
142
|
+
orders: orders,
|
143
|
+
opclasses: opclasses,
|
144
|
+
where: where,
|
145
|
+
using: using.to_sym,
|
146
|
+
comment: comment.presence
|
147
|
+
)
|
148
|
+
end
|
164
149
|
end
|
165
150
|
|
166
151
|
def table_options(table_name) # :nodoc:
|
@@ -173,7 +158,7 @@ module ActiveRecord
|
|
173
158
|
def table_comment(table_name) # :nodoc:
|
174
159
|
scope = quoted_scope(table_name, type: "BASE TABLE")
|
175
160
|
if scope[:name]
|
176
|
-
|
161
|
+
query_value(<<-SQL.strip_heredoc, "SCHEMA")
|
177
162
|
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
|
178
163
|
FROM pg_catalog.pg_class c
|
179
164
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
@@ -186,32 +171,32 @@ module ActiveRecord
|
|
186
171
|
|
187
172
|
# Returns the current database name.
|
188
173
|
def current_database
|
189
|
-
|
174
|
+
query_value("SELECT current_database()", "SCHEMA")
|
190
175
|
end
|
191
176
|
|
192
177
|
# Returns the current schema name.
|
193
178
|
def current_schema
|
194
|
-
|
179
|
+
query_value("SELECT current_schema", "SCHEMA")
|
195
180
|
end
|
196
181
|
|
197
182
|
# Returns the current database encoding format.
|
198
183
|
def encoding
|
199
|
-
|
184
|
+
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
200
185
|
end
|
201
186
|
|
202
187
|
# Returns the current database collation.
|
203
188
|
def collation
|
204
|
-
|
189
|
+
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
205
190
|
end
|
206
191
|
|
207
192
|
# Returns the current database ctype.
|
208
193
|
def ctype
|
209
|
-
|
194
|
+
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
210
195
|
end
|
211
196
|
|
212
197
|
# Returns an array of schema names.
|
213
198
|
def schema_names
|
214
|
-
|
199
|
+
query_values(<<-SQL, "SCHEMA")
|
215
200
|
SELECT nspname
|
216
201
|
FROM pg_namespace
|
217
202
|
WHERE nspname !~ '^pg_.*'
|
@@ -232,7 +217,7 @@ module ActiveRecord
|
|
232
217
|
|
233
218
|
# Sets the schema search path to a string of comma-separated schema names.
|
234
219
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
235
|
-
# See:
|
220
|
+
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
236
221
|
#
|
237
222
|
# This should be not be called manually but set in database.yml.
|
238
223
|
def schema_search_path=(schema_csv)
|
@@ -244,12 +229,12 @@ module ActiveRecord
|
|
244
229
|
|
245
230
|
# Returns the active schema search path.
|
246
231
|
def schema_search_path
|
247
|
-
@schema_search_path ||=
|
232
|
+
@schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
|
248
233
|
end
|
249
234
|
|
250
235
|
# Returns the current client message level.
|
251
236
|
def client_min_messages
|
252
|
-
|
237
|
+
query_value("SHOW client_min_messages", "SCHEMA")
|
253
238
|
end
|
254
239
|
|
255
240
|
# Set the client message level.
|
@@ -267,7 +252,7 @@ module ActiveRecord
|
|
267
252
|
end
|
268
253
|
|
269
254
|
def serial_sequence(table, column)
|
270
|
-
|
255
|
+
query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
|
271
256
|
end
|
272
257
|
|
273
258
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -278,7 +263,7 @@ module ActiveRecord
|
|
278
263
|
if sequence
|
279
264
|
quoted_sequence = quote_table_name(sequence)
|
280
265
|
|
281
|
-
|
266
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
|
282
267
|
else
|
283
268
|
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
284
269
|
end
|
@@ -300,10 +285,16 @@ module ActiveRecord
|
|
300
285
|
|
301
286
|
if pk && sequence
|
302
287
|
quoted_sequence = quote_table_name(sequence)
|
288
|
+
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
289
|
+
if max_pk.nil?
|
290
|
+
if postgresql_version >= 100000
|
291
|
+
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
292
|
+
else
|
293
|
+
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
|
294
|
+
end
|
295
|
+
end
|
303
296
|
|
304
|
-
|
305
|
-
SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
|
306
|
-
end_sql
|
297
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
307
298
|
end
|
308
299
|
end
|
309
300
|
|
@@ -327,7 +318,7 @@ module ActiveRecord
|
|
327
318
|
AND seq.relnamespace = nsp.oid
|
328
319
|
AND cons.contype = 'p'
|
329
320
|
AND dep.classid = 'pg_class'::regclass
|
330
|
-
AND dep.refobjid =
|
321
|
+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
331
322
|
end_sql
|
332
323
|
|
333
324
|
if result.nil? || result.empty?
|
@@ -345,7 +336,7 @@ module ActiveRecord
|
|
345
336
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
346
337
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
347
338
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
348
|
-
WHERE t.oid =
|
339
|
+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
349
340
|
AND cons.contype = 'p'
|
350
341
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
351
342
|
end_sql
|
@@ -362,7 +353,7 @@ module ActiveRecord
|
|
362
353
|
end
|
363
354
|
|
364
355
|
def primary_keys(table_name) # :nodoc:
|
365
|
-
|
356
|
+
query_values(<<-SQL.strip_heredoc, "SCHEMA")
|
366
357
|
SELECT a.attname
|
367
358
|
FROM (
|
368
359
|
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
|
@@ -377,6 +368,31 @@ module ActiveRecord
|
|
377
368
|
SQL
|
378
369
|
end
|
379
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
|
+
|
380
396
|
# Renames a table.
|
381
397
|
# Also renames a table's primary key sequence if the sequence name exists and
|
382
398
|
# matches the Active Record default.
|
@@ -387,14 +403,15 @@ module ActiveRecord
|
|
387
403
|
clear_cache!
|
388
404
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
389
405
|
pk, seq = pk_and_sequence_for(new_name)
|
390
|
-
if
|
391
|
-
new_seq = "#{new_name}_#{pk}_seq"
|
406
|
+
if pk
|
392
407
|
idx = "#{table_name}_pkey"
|
393
408
|
new_idx = "#{new_name}_pkey"
|
394
|
-
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
395
409
|
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
410
|
+
if seq && seq.identifier == "#{table_name}_#{pk}_seq"
|
411
|
+
new_seq = "#{new_name}_#{pk}_seq"
|
412
|
+
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
413
|
+
end
|
396
414
|
end
|
397
|
-
|
398
415
|
rename_table_indexes(table_name, new_name)
|
399
416
|
end
|
400
417
|
|
@@ -406,50 +423,23 @@ module ActiveRecord
|
|
406
423
|
|
407
424
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
408
425
|
clear_cache!
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
413
|
-
if options[:collation]
|
414
|
-
sql << " COLLATE \"#{options[:collation]}\""
|
415
|
-
end
|
416
|
-
if options[:using]
|
417
|
-
sql << " USING #{options[:using]}"
|
418
|
-
elsif options[:cast_as]
|
419
|
-
cast_as_type = type_to_sql(options[:cast_as], options)
|
420
|
-
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
421
|
-
end
|
422
|
-
execute sql
|
423
|
-
|
424
|
-
change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
|
425
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
426
|
-
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
426
|
+
sqls, procs = Array(change_column_for_alter(table_name, column_name, type, options)).partition { |v| v.is_a?(String) }
|
427
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
|
428
|
+
procs.each(&:call)
|
427
429
|
end
|
428
430
|
|
429
431
|
# Changes the default value of a table column.
|
430
432
|
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
431
|
-
|
432
|
-
column = column_for(table_name, column_name)
|
433
|
-
return unless column
|
434
|
-
|
435
|
-
default = extract_new_default_value(default_or_changes)
|
436
|
-
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
437
|
-
if default.nil?
|
438
|
-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
439
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
440
|
-
execute alter_column_query % "DROP DEFAULT"
|
441
|
-
else
|
442
|
-
execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
443
|
-
end
|
433
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
444
434
|
end
|
445
435
|
|
446
436
|
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
447
437
|
clear_cache!
|
448
438
|
unless null || default.nil?
|
449
439
|
column = column_for(table_name, column_name)
|
450
|
-
execute
|
440
|
+
execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL" if column
|
451
441
|
end
|
452
|
-
execute
|
442
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
|
453
443
|
end
|
454
444
|
|
455
445
|
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
@@ -472,8 +462,8 @@ module ActiveRecord
|
|
472
462
|
end
|
473
463
|
|
474
464
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
475
|
-
index_name, index_type,
|
476
|
-
execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{
|
465
|
+
index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
|
466
|
+
execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns_and_opclasses})#{index_options}").tap do
|
477
467
|
execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
|
478
468
|
end
|
479
469
|
end
|
@@ -512,8 +502,8 @@ module ActiveRecord
|
|
512
502
|
|
513
503
|
def foreign_keys(table_name)
|
514
504
|
scope = quoted_scope(table_name)
|
515
|
-
fk_info =
|
516
|
-
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
|
505
|
+
fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
|
506
|
+
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
|
517
507
|
FROM pg_constraint c
|
518
508
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
519
509
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
@@ -535,17 +525,18 @@ module ActiveRecord
|
|
535
525
|
|
536
526
|
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
537
527
|
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
528
|
+
options[:validate] = row["valid"]
|
538
529
|
|
539
530
|
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
540
531
|
end
|
541
532
|
end
|
542
533
|
|
543
|
-
def
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
534
|
+
def foreign_tables
|
535
|
+
query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
|
536
|
+
end
|
537
|
+
|
538
|
+
def foreign_table_exists?(table_name)
|
539
|
+
query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
|
549
540
|
end
|
550
541
|
|
551
542
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
@@ -577,7 +568,7 @@ module ActiveRecord
|
|
577
568
|
super
|
578
569
|
end
|
579
570
|
|
580
|
-
sql
|
571
|
+
sql = "#{sql}[]" if array && type != :primary_key
|
581
572
|
sql
|
582
573
|
end
|
583
574
|
|
@@ -592,27 +583,163 @@ module ActiveRecord
|
|
592
583
|
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
|
593
584
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
594
585
|
|
595
|
-
|
586
|
+
(order_columns << super).join(", ")
|
587
|
+
end
|
588
|
+
|
589
|
+
def update_table_definition(table_name, base) # :nodoc:
|
590
|
+
PostgreSQL::Table.new(table_name, base)
|
596
591
|
end
|
597
592
|
|
598
|
-
def
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
593
|
+
def create_schema_dumper(options) # :nodoc:
|
594
|
+
PostgreSQL::SchemaDumper.create(self, options)
|
595
|
+
end
|
596
|
+
|
597
|
+
# Validates the given constraint.
|
598
|
+
#
|
599
|
+
# Validates the constraint named +constraint_name+ on +accounts+.
|
600
|
+
#
|
601
|
+
# validate_constraint :accounts, :constraint_name
|
602
|
+
def validate_constraint(table_name, constraint_name)
|
603
|
+
return unless supports_validate_constraints?
|
604
|
+
|
605
|
+
at = create_alter_table table_name
|
606
|
+
at.validate_constraint constraint_name
|
607
|
+
|
608
|
+
execute schema_creation.accept(at)
|
609
|
+
end
|
610
|
+
|
611
|
+
# Validates the given foreign key.
|
612
|
+
#
|
613
|
+
# Validates the foreign key on +accounts.branch_id+.
|
614
|
+
#
|
615
|
+
# validate_foreign_key :accounts, :branches
|
616
|
+
#
|
617
|
+
# Validates the foreign key on +accounts.owner_id+.
|
618
|
+
#
|
619
|
+
# validate_foreign_key :accounts, column: :owner_id
|
620
|
+
#
|
621
|
+
# Validates the foreign key named +special_fk_name+ on the +accounts+ table.
|
622
|
+
#
|
623
|
+
# validate_foreign_key :accounts, name: :special_fk_name
|
624
|
+
#
|
625
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
626
|
+
def validate_foreign_key(from_table, options_or_to_table = {})
|
627
|
+
return unless supports_validate_constraints?
|
628
|
+
|
629
|
+
fk_name_to_validate = foreign_key_for!(from_table, options_or_to_table).name
|
630
|
+
|
631
|
+
validate_constraint from_table, fk_name_to_validate
|
608
632
|
end
|
609
633
|
|
610
634
|
private
|
635
|
+
def schema_creation
|
636
|
+
PostgreSQL::SchemaCreation.new(self)
|
637
|
+
end
|
638
|
+
|
639
|
+
def create_table_definition(*args)
|
640
|
+
PostgreSQL::TableDefinition.new(*args)
|
641
|
+
end
|
642
|
+
|
643
|
+
def create_alter_table(name)
|
644
|
+
PostgreSQL::AlterTable.new create_table_definition(name)
|
645
|
+
end
|
646
|
+
|
647
|
+
def new_column_from_field(table_name, field)
|
648
|
+
column_name, type, default, notnull, oid, fmod, collation, comment = field
|
649
|
+
type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
|
650
|
+
default_value = extract_value_from_default(default)
|
651
|
+
default_function = extract_default_function(default_value, default)
|
652
|
+
|
653
|
+
PostgreSQLColumn.new(
|
654
|
+
column_name,
|
655
|
+
default_value,
|
656
|
+
type_metadata,
|
657
|
+
!notnull,
|
658
|
+
table_name,
|
659
|
+
default_function,
|
660
|
+
collation,
|
661
|
+
comment: comment.presence,
|
662
|
+
max_identifier_length: max_identifier_length
|
663
|
+
)
|
664
|
+
end
|
665
|
+
|
666
|
+
def fetch_type_metadata(column_name, sql_type, oid, fmod)
|
667
|
+
cast_type = get_oid_type(oid, fmod, column_name, sql_type)
|
668
|
+
simple_type = SqlTypeMetadata.new(
|
669
|
+
sql_type: sql_type,
|
670
|
+
type: cast_type.type,
|
671
|
+
limit: cast_type.limit,
|
672
|
+
precision: cast_type.precision,
|
673
|
+
scale: cast_type.scale,
|
674
|
+
)
|
675
|
+
PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
|
676
|
+
end
|
677
|
+
|
678
|
+
def extract_foreign_key_action(specifier)
|
679
|
+
case specifier
|
680
|
+
when "c"; :cascade
|
681
|
+
when "n"; :nullify
|
682
|
+
when "r"; :restrict
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
def add_column_for_alter(table_name, column_name, type, options = {})
|
687
|
+
return super unless options.key?(:comment)
|
688
|
+
[super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
|
689
|
+
end
|
690
|
+
|
691
|
+
def change_column_for_alter(table_name, column_name, type, options = {})
|
692
|
+
td = create_table_definition(table_name)
|
693
|
+
cd = td.new_column_definition(column_name, type, options)
|
694
|
+
sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
|
695
|
+
sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
|
696
|
+
sqls
|
697
|
+
end
|
698
|
+
|
699
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
700
|
+
column = column_for(table_name, column_name)
|
701
|
+
return unless column
|
702
|
+
|
703
|
+
default = extract_new_default_value(default_or_changes)
|
704
|
+
alter_column_query = "ALTER COLUMN #{quote_column_name(column_name)} %s"
|
705
|
+
if default.nil?
|
706
|
+
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
707
|
+
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
708
|
+
alter_column_query % "DROP DEFAULT"
|
709
|
+
else
|
710
|
+
alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
def change_column_null_for_alter(table_name, column_name, null, default = nil)
|
715
|
+
"ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
716
|
+
end
|
717
|
+
|
718
|
+
def add_timestamps_for_alter(table_name, options = {})
|
719
|
+
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
720
|
+
end
|
721
|
+
|
722
|
+
def remove_timestamps_for_alter(table_name, options = {})
|
723
|
+
[remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
|
724
|
+
end
|
725
|
+
|
726
|
+
def add_index_opclass(quoted_columns, **options)
|
727
|
+
opclasses = options_for_index_columns(options[:opclass])
|
728
|
+
quoted_columns.each do |name, column|
|
729
|
+
column << " #{opclasses[name]}" if opclasses[name].present?
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
734
|
+
quoted_columns = add_index_opclass(quoted_columns, options)
|
735
|
+
super
|
736
|
+
end
|
737
|
+
|
611
738
|
def data_source_sql(name = nil, type: nil)
|
612
739
|
scope = quoted_scope(name, type: type)
|
613
|
-
scope[:type] ||= "'r','v','m'" # (r)elation/table, (v)iew, (m)aterialized view
|
740
|
+
scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
|
614
741
|
|
615
|
-
sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
|
742
|
+
sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
|
616
743
|
sql << " WHERE n.nspname = #{scope[:schema]}"
|
617
744
|
sql << " AND c.relname = #{scope[:name]}" if scope[:name]
|
618
745
|
sql << " AND c.relkind IN (#{scope[:type]})"
|
@@ -624,9 +751,11 @@ module ActiveRecord
|
|
624
751
|
type = \
|
625
752
|
case type
|
626
753
|
when "BASE TABLE"
|
627
|
-
"'r'"
|
754
|
+
"'r','p'"
|
628
755
|
when "VIEW"
|
629
756
|
"'v','m'"
|
757
|
+
when "FOREIGN TABLE"
|
758
|
+
"'f'"
|
630
759
|
end
|
631
760
|
scope = {}
|
632
761
|
scope[:schema] = schema ? quote(schema) : "ANY (current_schemas(false))"
|