activerecord 5.1.7 → 5.2.6
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 +583 -673
- 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/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations/alias_tracker.rb +19 -27
- data/lib/active_record/associations/association.rb +41 -37
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +27 -8
- 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 +12 -4
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- 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 +59 -47
- data/lib/active_record/associations/collection_proxy.rb +20 -49
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +12 -1
- data/lib/active_record/associations/has_many_through_association.rb +36 -30
- data/lib/active_record/associations/has_one_association.rb +12 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
- 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/join_dependency.rb +48 -93
- data/lib/active_record/associations/preloader/association.rb +45 -61
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- data/lib/active_record/associations/preloader.rb +18 -38
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +26 -11
- data/lib/active_record/associations.rb +40 -63
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +30 -214
- data/lib/active_record/attribute_methods/primary_key.rb +7 -6
- 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 +21 -9
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +35 -19
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -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 +12 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
- data/lib/active_record/connection_adapters/column.rb +3 -1
- 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 +47 -2
- 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 -30
- 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 +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
- 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 +2 -0
- 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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -3
- 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 +4 -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/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
- 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 +233 -111
- 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 +57 -73
- 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 +22 -0
- 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 +81 -94
- 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 +10 -3
- 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 +42 -3
- 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 +14 -17
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +43 -0
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +47 -9
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/model_schema.rb +16 -21
- 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 +167 -16
- data/lib/active_record/query_cache.rb +6 -8
- data/lib/active_record/querying.rb +4 -2
- 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 +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +108 -194
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/calculations.rb +45 -19
- data/lib/active_record/relation/delegation.rb +45 -27
- data/lib/active_record/relation/finder_methods.rb +75 -76
- 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/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/predicate_builder.rb +60 -79
- data/lib/active_record/relation/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +128 -99
- 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 -68
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +120 -214
- 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/default.rb +8 -9
- data/lib/active_record/scoping/named.rb +23 -7
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +23 -13
- 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 +25 -14
- data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
- data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +6 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +33 -28
- data/lib/active_record/translation.rb +2 -0
- 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 +2 -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.rb +4 -1
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.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 +35 -5
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/version.rb +2 -0
- data/lib/active_record.rb +11 -4
- 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/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/migration.rb +2 -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
- data/lib/rails/generators/active_record.rb +3 -1
- metadata +23 -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/user_provided_default.rb +0 -30
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute_mutation_tracker.rb +0 -122
- data/lib/active_record/attribute_set/builder.rb +0 -126
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/attribute_set.rb +0 -113
- 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 -37
@@ -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
|
@@ -64,12 +64,7 @@ module ActiveRecord
|
|
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
|
|
@@ -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
110
|
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
|
126
111
|
|
127
|
-
|
112
|
+
orders = {}
|
113
|
+
opclasses = {}
|
114
|
+
|
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,34 +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
|
-
max_identifier_length: max_identifier_length
|
164
|
-
)
|
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
|
165
149
|
end
|
166
150
|
|
167
151
|
def table_options(table_name) # :nodoc:
|
@@ -233,7 +217,7 @@ module ActiveRecord
|
|
233
217
|
|
234
218
|
# Sets the schema search path to a string of comma-separated schema names.
|
235
219
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
236
|
-
# See:
|
220
|
+
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
237
221
|
#
|
238
222
|
# This should be not be called manually but set in database.yml.
|
239
223
|
def schema_search_path=(schema_csv)
|
@@ -334,7 +318,7 @@ module ActiveRecord
|
|
334
318
|
AND seq.relnamespace = nsp.oid
|
335
319
|
AND cons.contype = 'p'
|
336
320
|
AND dep.classid = 'pg_class'::regclass
|
337
|
-
AND dep.refobjid =
|
321
|
+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
338
322
|
end_sql
|
339
323
|
|
340
324
|
if result.nil? || result.empty?
|
@@ -352,7 +336,7 @@ module ActiveRecord
|
|
352
336
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
353
337
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
354
338
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
355
|
-
WHERE t.oid =
|
339
|
+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
356
340
|
AND cons.contype = 'p'
|
357
341
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
358
342
|
end_sql
|
@@ -384,6 +368,31 @@ module ActiveRecord
|
|
384
368
|
SQL
|
385
369
|
end
|
386
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
|
+
|
387
396
|
# Renames a table.
|
388
397
|
# Also renames a table's primary key sequence if the sequence name exists and
|
389
398
|
# matches the Active Record default.
|
@@ -394,14 +403,15 @@ module ActiveRecord
|
|
394
403
|
clear_cache!
|
395
404
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
396
405
|
pk, seq = pk_and_sequence_for(new_name)
|
397
|
-
if
|
398
|
-
new_seq = "#{new_name}_#{pk}_seq"
|
406
|
+
if pk
|
399
407
|
idx = "#{table_name}_pkey"
|
400
408
|
new_idx = "#{new_name}_pkey"
|
401
|
-
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
402
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
|
403
414
|
end
|
404
|
-
|
405
415
|
rename_table_indexes(table_name, new_name)
|
406
416
|
end
|
407
417
|
|
@@ -413,50 +423,23 @@ module ActiveRecord
|
|
413
423
|
|
414
424
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
415
425
|
clear_cache!
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
420
|
-
if options[:collation]
|
421
|
-
sql << " COLLATE \"#{options[:collation]}\""
|
422
|
-
end
|
423
|
-
if options[:using]
|
424
|
-
sql << " USING #{options[:using]}"
|
425
|
-
elsif options[:cast_as]
|
426
|
-
cast_as_type = type_to_sql(options[:cast_as], options)
|
427
|
-
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
428
|
-
end
|
429
|
-
execute sql
|
430
|
-
|
431
|
-
change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
|
432
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
433
|
-
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)
|
434
429
|
end
|
435
430
|
|
436
431
|
# Changes the default value of a table column.
|
437
432
|
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
438
|
-
|
439
|
-
column = column_for(table_name, column_name)
|
440
|
-
return unless column
|
441
|
-
|
442
|
-
default = extract_new_default_value(default_or_changes)
|
443
|
-
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
444
|
-
if default.nil?
|
445
|
-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
446
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
447
|
-
execute alter_column_query % "DROP DEFAULT"
|
448
|
-
else
|
449
|
-
execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
450
|
-
end
|
433
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
451
434
|
end
|
452
435
|
|
453
436
|
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
454
437
|
clear_cache!
|
455
438
|
unless null || default.nil?
|
456
439
|
column = column_for(table_name, column_name)
|
457
|
-
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
|
458
441
|
end
|
459
|
-
execute
|
442
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
|
460
443
|
end
|
461
444
|
|
462
445
|
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
@@ -479,8 +462,8 @@ module ActiveRecord
|
|
479
462
|
end
|
480
463
|
|
481
464
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
482
|
-
index_name, index_type,
|
483
|
-
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
|
484
467
|
execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
|
485
468
|
end
|
486
469
|
end
|
@@ -520,7 +503,7 @@ module ActiveRecord
|
|
520
503
|
def foreign_keys(table_name)
|
521
504
|
scope = quoted_scope(table_name)
|
522
505
|
fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
|
523
|
-
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
|
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
|
524
507
|
FROM pg_constraint c
|
525
508
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
526
509
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
@@ -542,17 +525,18 @@ module ActiveRecord
|
|
542
525
|
|
543
526
|
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
544
527
|
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
528
|
+
options[:validate] = row["valid"]
|
545
529
|
|
546
530
|
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
547
531
|
end
|
548
532
|
end
|
549
533
|
|
550
|
-
def
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
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?
|
556
540
|
end
|
557
541
|
|
558
542
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
@@ -584,7 +568,7 @@ module ActiveRecord
|
|
584
568
|
super
|
585
569
|
end
|
586
570
|
|
587
|
-
sql
|
571
|
+
sql = "#{sql}[]" if array && type != :primary_key
|
588
572
|
sql
|
589
573
|
end
|
590
574
|
|
@@ -599,27 +583,163 @@ module ActiveRecord
|
|
599
583
|
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
|
600
584
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
601
585
|
|
602
|
-
|
586
|
+
(order_columns << super).join(", ")
|
603
587
|
end
|
604
588
|
|
605
|
-
def
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
589
|
+
def update_table_definition(table_name, base) # :nodoc:
|
590
|
+
PostgreSQL::Table.new(table_name, base)
|
591
|
+
end
|
592
|
+
|
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
|
615
632
|
end
|
616
633
|
|
617
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
|
+
|
618
738
|
def data_source_sql(name = nil, type: nil)
|
619
739
|
scope = quoted_scope(name, type: type)
|
620
|
-
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
|
621
741
|
|
622
|
-
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
|
623
743
|
sql << " WHERE n.nspname = #{scope[:schema]}"
|
624
744
|
sql << " AND c.relname = #{scope[:name]}" if scope[:name]
|
625
745
|
sql << " AND c.relkind IN (#{scope[:type]})"
|
@@ -631,9 +751,11 @@ module ActiveRecord
|
|
631
751
|
type = \
|
632
752
|
case type
|
633
753
|
when "BASE TABLE"
|
634
|
-
"'r'"
|
754
|
+
"'r','p'"
|
635
755
|
when "VIEW"
|
636
756
|
"'v','m'"
|
757
|
+
when "FOREIGN TABLE"
|
758
|
+
"'f'"
|
637
759
|
end
|
638
760
|
scope = {}
|
639
761
|
scope[:schema] = schema ? quote(schema) : "ANY (current_schemas(false))"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
@@ -66,7 +68,7 @@ module ActiveRecord
|
|
66
68
|
# * <tt>"schema_name".table_name</tt>
|
67
69
|
# * <tt>"schema.name"."table name"</tt>
|
68
70
|
def extract_schema_qualified_name(string)
|
69
|
-
schema, table = string.scan(/[^"
|
71
|
+
schema, table = string.scan(/[^".]+|"[^"]*"/)
|
70
72
|
if table.nil?
|
71
73
|
table = schema
|
72
74
|
schema = nil
|