activerecord 6.1.4.1 → 7.0.0.rc2
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 +1050 -981
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +34 -27
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +187 -55
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +90 -82
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +122 -132
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +16 -32
- data/lib/active_record/delegated_type.rb +52 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +49 -42
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +17 -20
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +83 -1
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +45 -58
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +40 -36
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +235 -61
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +171 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +116 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +4 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- metadata +55 -12
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Queries the database and returns the results in an Array-like object
|
13
|
-
def query(sql, name = nil)
|
13
|
+
def query(sql, name = nil) # :nodoc:
|
14
14
|
materialize_transactions
|
15
15
|
mark_transaction_written_if_write(sql)
|
16
16
|
|
@@ -35,9 +35,8 @@ module ActiveRecord
|
|
35
35
|
# Note: the PG::Result object is manually memory managed; if you don't
|
36
36
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
37
37
|
def execute(sql, name = nil)
|
38
|
-
|
39
|
-
|
40
|
-
end
|
38
|
+
sql = transform_query(sql)
|
39
|
+
check_if_write_query(sql)
|
41
40
|
|
42
41
|
materialize_transactions
|
43
42
|
mark_transaction_written_if_write(sql)
|
@@ -49,8 +48,8 @@ module ActiveRecord
|
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
53
|
-
execute_and_clear(sql, name, binds, prepare: prepare) do |result|
|
51
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
52
|
+
execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
|
54
53
|
types = {}
|
55
54
|
fields = result.fields
|
56
55
|
fields.each_with_index do |fname, i|
|
@@ -66,7 +65,7 @@ module ActiveRecord
|
|
66
65
|
end
|
67
66
|
end
|
68
67
|
|
69
|
-
def exec_delete(sql, name = nil, binds = [])
|
68
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
70
69
|
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
71
70
|
end
|
72
71
|
alias :exec_update :exec_delete
|
@@ -86,7 +85,7 @@ module ActiveRecord
|
|
86
85
|
end
|
87
86
|
private :sql_for_insert
|
88
87
|
|
89
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
88
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
|
90
89
|
if use_insert_returning? || pk == false
|
91
90
|
super
|
92
91
|
else
|
@@ -105,25 +104,33 @@ module ActiveRecord
|
|
105
104
|
end
|
106
105
|
|
107
106
|
# Begins a transaction.
|
108
|
-
def begin_db_transaction
|
107
|
+
def begin_db_transaction # :nodoc:
|
109
108
|
execute("BEGIN", "TRANSACTION")
|
110
109
|
end
|
111
110
|
|
112
|
-
def begin_isolated_db_transaction(isolation)
|
111
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
113
112
|
begin_db_transaction
|
114
113
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
115
114
|
end
|
116
115
|
|
117
116
|
# Commits a transaction.
|
118
|
-
def commit_db_transaction
|
117
|
+
def commit_db_transaction # :nodoc:
|
119
118
|
execute("COMMIT", "TRANSACTION")
|
120
119
|
end
|
121
120
|
|
122
121
|
# Aborts a transaction.
|
123
|
-
def exec_rollback_db_transaction
|
122
|
+
def exec_rollback_db_transaction # :nodoc:
|
124
123
|
execute("ROLLBACK", "TRANSACTION")
|
125
124
|
end
|
126
125
|
|
126
|
+
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
127
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
128
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
129
|
+
|
130
|
+
def high_precision_current_timestamp
|
131
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
132
|
+
end
|
133
|
+
|
127
134
|
private
|
128
135
|
def execute_batch(statements, name = nil)
|
129
136
|
execute(combine_multi_statements(statements))
|
@@ -16,6 +16,14 @@ module ActiveRecord
|
|
16
16
|
super
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
def type_cast_for_schema(value)
|
21
|
+
case value
|
22
|
+
when ::Float::INFINITY then "::Float::INFINITY"
|
23
|
+
when -::Float::INFINITY then "-::Float::INFINITY"
|
24
|
+
else super
|
25
|
+
end
|
26
|
+
end
|
19
27
|
end
|
20
28
|
end
|
21
29
|
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "strscan"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
module PostgreSQL
|
6
8
|
module OID # :nodoc:
|
7
9
|
class Hstore < Type::Value # :nodoc:
|
10
|
+
ERROR = "Invalid Hstore document: %s"
|
11
|
+
|
8
12
|
include ActiveModel::Type::Helpers::Mutable
|
9
13
|
|
10
14
|
def type
|
@@ -12,15 +16,56 @@ module ActiveRecord
|
|
12
16
|
end
|
13
17
|
|
14
18
|
def deserialize(value)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
return value unless value.is_a?(::String)
|
20
|
+
|
21
|
+
scanner = StringScanner.new(value)
|
22
|
+
hash = {}
|
23
|
+
|
24
|
+
until scanner.eos?
|
25
|
+
unless scanner.skip(/"/)
|
26
|
+
raise(ArgumentError, ERROR % scanner.string.inspect)
|
27
|
+
end
|
28
|
+
|
29
|
+
unless key = scanner.scan_until(/(?<!\\)(?=")/)
|
30
|
+
raise(ArgumentError, ERROR % scanner.string.inspect)
|
31
|
+
end
|
32
|
+
|
33
|
+
unless scanner.skip(/"=>?/)
|
34
|
+
raise(ArgumentError, ERROR % scanner.string.inspect)
|
35
|
+
end
|
36
|
+
|
37
|
+
if scanner.scan(/NULL/)
|
38
|
+
value = nil
|
39
|
+
else
|
40
|
+
unless scanner.skip(/"/)
|
41
|
+
raise(ArgumentError, ERROR % scanner.string.inspect)
|
42
|
+
end
|
43
|
+
|
44
|
+
unless value = scanner.scan_until(/(?<!\\)(?=")/)
|
45
|
+
raise(ArgumentError, ERROR % scanner.string.inspect)
|
46
|
+
end
|
47
|
+
|
48
|
+
unless scanner.skip(/"/)
|
49
|
+
raise(ArgumentError, ERROR % scanner.string.inspect)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
key.gsub!('\"', '"')
|
54
|
+
key.gsub!("\\\\", "\\")
|
55
|
+
|
56
|
+
if value
|
57
|
+
value.gsub!('\"', '"')
|
58
|
+
value.gsub!("\\\\", "\\")
|
59
|
+
end
|
60
|
+
|
61
|
+
hash[key] = value
|
62
|
+
|
63
|
+
unless scanner.skip(/, /) || scanner.eos?
|
64
|
+
raise(ArgumentError, ERROR % scanner.string.inspect)
|
65
|
+
end
|
23
66
|
end
|
67
|
+
|
68
|
+
hash
|
24
69
|
end
|
25
70
|
|
26
71
|
def serialize(value)
|
@@ -46,12 +91,6 @@ module ActiveRecord
|
|
46
91
|
end
|
47
92
|
|
48
93
|
private
|
49
|
-
HstorePair = begin
|
50
|
-
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
|
51
|
-
unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
|
52
|
-
/(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
|
53
|
-
end
|
54
|
-
|
55
94
|
def escape_hstore(value)
|
56
95
|
if value.nil?
|
57
96
|
"NULL"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module PostgreSQL
|
6
|
+
module OID # :nodoc:
|
7
|
+
class Timestamp < DateTime # :nodoc:
|
8
|
+
def type
|
9
|
+
real_type_unless_aliased(:timestamp)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module PostgreSQL
|
6
|
+
module OID # :nodoc:
|
7
|
+
class TimestampWithTimeZone < DateTime # :nodoc:
|
8
|
+
def type
|
9
|
+
real_type_unless_aliased(:timestamptz)
|
10
|
+
end
|
11
|
+
|
12
|
+
def cast_value(value)
|
13
|
+
time = super
|
14
|
+
return time if time.is_a?(ActiveSupport::TimeWithZone)
|
15
|
+
|
16
|
+
# While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to Postgres in UTC.
|
17
|
+
# We prefer times always in UTC, so here we convert back.
|
18
|
+
if is_utc?
|
19
|
+
time.getutc
|
20
|
+
else
|
21
|
+
time.getlocal
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -33,15 +33,27 @@ module ActiveRecord
|
|
33
33
|
composites.each { |row| register_composite_type(row) }
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def query_conditions_for_known_type_names
|
37
37
|
known_type_names = @store.keys.map { |n| "'#{n}'" }
|
38
|
-
|
39
|
-
<<~SQL % [known_type_names.join(", "), known_type_types.join(", ")]
|
38
|
+
<<~SQL % known_type_names.join(", ")
|
40
39
|
WHERE
|
41
40
|
t.typname IN (%s)
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
SQL
|
42
|
+
end
|
43
|
+
|
44
|
+
def query_conditions_for_known_type_types
|
45
|
+
known_type_types = %w('r' 'e' 'd')
|
46
|
+
<<~SQL % known_type_types.join(", ")
|
47
|
+
WHERE
|
48
|
+
t.typtype IN (%s)
|
49
|
+
SQL
|
50
|
+
end
|
51
|
+
|
52
|
+
def query_conditions_for_array_types
|
53
|
+
known_type_oids = @store.keys.reject { |k| k.is_a?(String) }
|
54
|
+
<<~SQL % [known_type_oids.join(", ")]
|
55
|
+
WHERE
|
56
|
+
t.typelem IN (%s)
|
45
57
|
SQL
|
46
58
|
end
|
47
59
|
|
@@ -20,6 +20,8 @@ require "active_record/connection_adapters/postgresql/oid/point"
|
|
20
20
|
require "active_record/connection_adapters/postgresql/oid/legacy_point"
|
21
21
|
require "active_record/connection_adapters/postgresql/oid/range"
|
22
22
|
require "active_record/connection_adapters/postgresql/oid/specialized_string"
|
23
|
+
require "active_record/connection_adapters/postgresql/oid/timestamp"
|
24
|
+
require "active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone"
|
23
25
|
require "active_record/connection_adapters/postgresql/oid/uuid"
|
24
26
|
require "active_record/connection_adapters/postgresql/oid/vector"
|
25
27
|
require "active_record/connection_adapters/postgresql/oid/xml"
|
@@ -16,8 +16,33 @@ module ActiveRecord
|
|
16
16
|
@connection.unescape_bytea(value) if value
|
17
17
|
end
|
18
18
|
|
19
|
+
def quote(value) # :nodoc:
|
20
|
+
case value
|
21
|
+
when OID::Xml::Data
|
22
|
+
"xml '#{quote_string(value.to_s)}'"
|
23
|
+
when OID::Bit::Data
|
24
|
+
if value.binary?
|
25
|
+
"B'#{value}'"
|
26
|
+
elsif value.hex?
|
27
|
+
"X'#{value}'"
|
28
|
+
end
|
29
|
+
when Numeric
|
30
|
+
if value.finite?
|
31
|
+
super
|
32
|
+
else
|
33
|
+
"'#{value}'"
|
34
|
+
end
|
35
|
+
when OID::Array::Data
|
36
|
+
quote(encode_array(value))
|
37
|
+
when Range
|
38
|
+
quote(encode_range(value))
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
19
44
|
# Quotes strings for use in SQL input.
|
20
|
-
def quote_string(s)
|
45
|
+
def quote_string(s) # :nodoc:
|
21
46
|
PG::Connection.escape(s)
|
22
47
|
end
|
23
48
|
|
@@ -48,7 +73,7 @@ module ActiveRecord
|
|
48
73
|
end
|
49
74
|
|
50
75
|
# Quote date/time values for use in SQL input.
|
51
|
-
def quoted_date(value)
|
76
|
+
def quoted_date(value) # :nodoc:
|
52
77
|
if value.year <= 0
|
53
78
|
bce_year = format("%04d", -value.year + 1)
|
54
79
|
super.sub(/^-?\d+/, bce_year) + " BC"
|
@@ -74,6 +99,24 @@ module ActiveRecord
|
|
74
99
|
end
|
75
100
|
end
|
76
101
|
|
102
|
+
def type_cast(value) # :nodoc:
|
103
|
+
case value
|
104
|
+
when Type::Binary::Data
|
105
|
+
# Return a bind param hash with format as binary.
|
106
|
+
# See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
|
107
|
+
# for more information
|
108
|
+
{ value: value.to_s, format: 1 }
|
109
|
+
when OID::Xml::Data, OID::Bit::Data
|
110
|
+
value.to_s
|
111
|
+
when OID::Array::Data
|
112
|
+
encode_array(value)
|
113
|
+
when Range
|
114
|
+
encode_range(value)
|
115
|
+
else
|
116
|
+
super
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
77
120
|
def lookup_cast_type_from_column(column) # :nodoc:
|
78
121
|
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
79
122
|
end
|
@@ -90,8 +133,8 @@ module ActiveRecord
|
|
90
133
|
\A
|
91
134
|
(
|
92
135
|
(?:
|
93
|
-
# "table_name"."column_name"::type_name | function(one or no argument)::type_name
|
94
|
-
((?:\w+\.|"\w+"\.)
|
136
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
137
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
95
138
|
)
|
96
139
|
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
97
140
|
)
|
@@ -103,8 +146,8 @@ module ActiveRecord
|
|
103
146
|
\A
|
104
147
|
(
|
105
148
|
(?:
|
106
|
-
# "table_name"."column_name"::type_name | function(one or no argument)::type_name
|
107
|
-
((?:\w+\.|"\w+"\.)
|
149
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
150
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
108
151
|
)
|
109
152
|
(?:\s+ASC|\s+DESC)?
|
110
153
|
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
@@ -120,49 +163,6 @@ module ActiveRecord
|
|
120
163
|
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
121
164
|
end
|
122
165
|
|
123
|
-
def _quote(value)
|
124
|
-
case value
|
125
|
-
when OID::Xml::Data
|
126
|
-
"xml '#{quote_string(value.to_s)}'"
|
127
|
-
when OID::Bit::Data
|
128
|
-
if value.binary?
|
129
|
-
"B'#{value}'"
|
130
|
-
elsif value.hex?
|
131
|
-
"X'#{value}'"
|
132
|
-
end
|
133
|
-
when Numeric
|
134
|
-
if value.finite?
|
135
|
-
super
|
136
|
-
else
|
137
|
-
"'#{value}'"
|
138
|
-
end
|
139
|
-
when OID::Array::Data
|
140
|
-
_quote(encode_array(value))
|
141
|
-
when Range
|
142
|
-
_quote(encode_range(value))
|
143
|
-
else
|
144
|
-
super
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def _type_cast(value)
|
149
|
-
case value
|
150
|
-
when Type::Binary::Data
|
151
|
-
# Return a bind param hash with format as binary.
|
152
|
-
# See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
|
153
|
-
# for more information
|
154
|
-
{ value: value.to_s, format: 1 }
|
155
|
-
when OID::Xml::Data, OID::Bit::Data
|
156
|
-
value.to_s
|
157
|
-
when OID::Array::Data
|
158
|
-
encode_array(value)
|
159
|
-
when Range
|
160
|
-
encode_range(value)
|
161
|
-
else
|
162
|
-
super
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
166
|
def encode_array(array_data)
|
167
167
|
encoder = array_data.encoder
|
168
168
|
values = type_cast_array(array_data.values)
|
@@ -188,7 +188,7 @@ module ActiveRecord
|
|
188
188
|
def type_cast_array(values)
|
189
189
|
case values
|
190
190
|
when ::Array then values.map { |item| type_cast_array(item) }
|
191
|
-
else
|
191
|
+
else type_cast(values)
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
@@ -37,6 +37,38 @@ Rails needs superuser privileges to disable referential integrity.
|
|
37
37
|
rescue ActiveRecord::ActiveRecordError
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
def all_foreign_keys_valid? # :nodoc:
|
42
|
+
sql = <<~SQL
|
43
|
+
do $$
|
44
|
+
declare r record;
|
45
|
+
BEGIN
|
46
|
+
FOR r IN (
|
47
|
+
SELECT FORMAT(
|
48
|
+
'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I''; ALTER TABLE %I VALIDATE CONSTRAINT %I;',
|
49
|
+
constraint_name,
|
50
|
+
table_name,
|
51
|
+
constraint_name
|
52
|
+
) AS constraint_check
|
53
|
+
FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY'
|
54
|
+
)
|
55
|
+
LOOP
|
56
|
+
EXECUTE (r.constraint_check);
|
57
|
+
END LOOP;
|
58
|
+
END;
|
59
|
+
$$;
|
60
|
+
SQL
|
61
|
+
|
62
|
+
begin
|
63
|
+
transaction(requires_new: true) do
|
64
|
+
execute(sql)
|
65
|
+
end
|
66
|
+
|
67
|
+
true
|
68
|
+
rescue ActiveRecord::StatementInvalid
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
40
72
|
end
|
41
73
|
end
|
42
74
|
end
|
@@ -10,7 +10,14 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def visit_AddForeignKey(o)
|
13
|
-
super.dup.tap
|
13
|
+
super.dup.tap do |sql|
|
14
|
+
if o.deferrable
|
15
|
+
sql << " DEFERRABLE"
|
16
|
+
sql << " INITIALLY #{o.deferrable.to_s.upcase}" unless o.deferrable == true
|
17
|
+
end
|
18
|
+
|
19
|
+
sql << " NOT VALID" unless o.validate?
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
16
23
|
def visit_CheckConstraintDefinition(o)
|
@@ -61,6 +68,19 @@ module ActiveRecord
|
|
61
68
|
if options[:collation]
|
62
69
|
sql << " COLLATE \"#{options[:collation]}\""
|
63
70
|
end
|
71
|
+
|
72
|
+
if as = options[:as]
|
73
|
+
sql << " GENERATED ALWAYS AS (#{as})"
|
74
|
+
|
75
|
+
if options[:stored]
|
76
|
+
sql << " STORED"
|
77
|
+
else
|
78
|
+
raise ArgumentError, <<~MSG
|
79
|
+
PostgreSQL currently does not support VIRTUAL (not persisted) generated columns.
|
80
|
+
Specify 'stored: true' option for '#{options[:column].name}'
|
81
|
+
MSG
|
82
|
+
end
|
83
|
+
end
|
64
84
|
super
|
65
85
|
end
|
66
86
|
|
@@ -173,11 +173,19 @@ module ActiveRecord
|
|
173
173
|
# :method: xml
|
174
174
|
# :call-seq: xml(*names, **options)
|
175
175
|
|
176
|
+
##
|
177
|
+
# :method: timestamptz
|
178
|
+
# :call-seq: timestamptz(*names, **options)
|
179
|
+
|
180
|
+
##
|
181
|
+
# :method: enum
|
182
|
+
# :call-seq: enum(*names, **options)
|
183
|
+
|
176
184
|
included do
|
177
185
|
define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
|
178
186
|
:hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
|
179
187
|
:money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
|
180
|
-
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml
|
188
|
+
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
|
181
189
|
end
|
182
190
|
end
|
183
191
|
|
@@ -191,7 +199,20 @@ module ActiveRecord
|
|
191
199
|
@unlogged = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables
|
192
200
|
end
|
193
201
|
|
202
|
+
def new_column_definition(name, type, **options) # :nodoc:
|
203
|
+
case type
|
204
|
+
when :virtual
|
205
|
+
type = options[:type]
|
206
|
+
end
|
207
|
+
|
208
|
+
super
|
209
|
+
end
|
210
|
+
|
194
211
|
private
|
212
|
+
def aliased_types(name, fallback)
|
213
|
+
fallback
|
214
|
+
end
|
215
|
+
|
195
216
|
def integer_like_primary_key_type(type, options)
|
196
217
|
if type == :bigint || options[:limit] == 8
|
197
218
|
:bigserial
|
@@ -16,9 +16,30 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
def types(stream)
|
20
|
+
types = @connection.enum_types
|
21
|
+
if types.any?
|
22
|
+
stream.puts " # Custom types defined in this database."
|
23
|
+
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
|
24
|
+
types.sort.each do |name, values|
|
25
|
+
stream.puts " create_enum #{name.inspect}, #{values.split(",").inspect}"
|
26
|
+
end
|
27
|
+
stream.puts
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
19
31
|
def prepare_column_options(column)
|
20
32
|
spec = super
|
21
33
|
spec[:array] = "true" if column.array?
|
34
|
+
|
35
|
+
if @connection.supports_virtual_columns? && column.virtual?
|
36
|
+
spec[:as] = extract_expression_for_virtual_column(column)
|
37
|
+
spec[:stored] = true
|
38
|
+
spec = { type: schema_type(column).inspect }.merge!(spec)
|
39
|
+
end
|
40
|
+
|
41
|
+
spec[:enum_type] = "\"#{column.sql_type}\"" if column.enum?
|
42
|
+
|
22
43
|
spec
|
23
44
|
end
|
24
45
|
|
@@ -43,6 +64,10 @@ module ActiveRecord
|
|
43
64
|
def schema_expression(column)
|
44
65
|
super unless column.serial?
|
45
66
|
end
|
67
|
+
|
68
|
+
def extract_expression_for_virtual_column(column)
|
69
|
+
column.default_function.inspect
|
70
|
+
end
|
46
71
|
end
|
47
72
|
end
|
48
73
|
end
|