activerecord 6.1.7.6 → 7.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1570 -1016
- data/README.rdoc +3 -3
- 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 +20 -22
- data/lib/active_record/associations/collection_proxy.rb +15 -5
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +8 -5
- 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/join_dependency.rb +23 -15
- data/lib/active_record/associations/preloader/association.rb +186 -52
- 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 +50 -14
- 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 +138 -100
- 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 +8 -6
- data/lib/active_record/attribute_methods/serialization.rb +57 -19
- 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 +19 -22
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +8 -23
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +14 -16
- data/lib/active_record/coders/yaml_column.rb +4 -8
- 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 +52 -23
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +144 -82
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +115 -85
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -25
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -23
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -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 +19 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- 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 +30 -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 +76 -73
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -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 +40 -21
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +33 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +19 -17
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +98 -36
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +49 -55
- data/lib/active_record/core.rb +123 -148
- 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 +15 -32
- data/lib/active_record/delegated_type.rb +53 -12
- 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 +67 -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 +206 -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 +50 -43
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- 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 +20 -23
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +5 -5
- 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 +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +10 -4
- data/lib/active_record/log_subscriber.rb +23 -7
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +18 -6
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +8 -9
- data/lib/active_record/migration/compatibility.rb +93 -46
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +167 -87
- data/lib/active_record/model_schema.rb +58 -59
- 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 +231 -61
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +149 -0
- data/lib/active_record/querying.rb +16 -6
- data/lib/active_record/railtie.rb +136 -22
- data/lib/active_record/railties/controller_runtime.rb +4 -5
- data/lib/active_record/railties/databases.rake +78 -136
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +80 -49
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +6 -6
- data/lib/active_record/relation/calculations.rb +92 -60
- data/lib/active_record/relation/delegation.rb +7 -7
- 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/association_query_value.rb +20 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +28 -11
- data/lib/active_record/relation/query_methods.rb +304 -68
- 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 +189 -88
- data/lib/active_record/result.rb +23 -11
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +17 -12
- data/lib/active_record/schema.rb +38 -23
- data/lib/active_record/schema_dumper.rb +29 -19
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +60 -13
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +3 -3
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/table_metadata.rb +6 -2
- data/lib/active_record/tasks/database_tasks.rb +127 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -6
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +12 -17
- data/lib/active_record/translation.rb +3 -3
- 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 +9 -5
- 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 +4 -4
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +225 -27
- 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/and.rb +4 -0
- 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
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +55 -11
@@ -30,7 +30,11 @@ module ActiveRecord
|
|
30
30
|
|
31
31
|
module ConnectionAdapters
|
32
32
|
class Mysql2Adapter < AbstractMysqlAdapter
|
33
|
-
ER_BAD_DB_ERROR
|
33
|
+
ER_BAD_DB_ERROR = 1049
|
34
|
+
ER_ACCESS_DENIED_ERROR = 1045
|
35
|
+
ER_CONN_HOST_ERROR = 2003
|
36
|
+
ER_UNKNOWN_HOST_ERROR = 2005
|
37
|
+
|
34
38
|
ADAPTER_NAME = "Mysql2"
|
35
39
|
|
36
40
|
include MySQL::DatabaseStatements
|
@@ -40,7 +44,11 @@ module ActiveRecord
|
|
40
44
|
Mysql2::Client.new(config)
|
41
45
|
rescue Mysql2::Error => error
|
42
46
|
if error.error_number == ConnectionAdapters::Mysql2Adapter::ER_BAD_DB_ERROR
|
43
|
-
raise ActiveRecord::NoDatabaseError
|
47
|
+
raise ActiveRecord::NoDatabaseError.db_error(config[:database])
|
48
|
+
elsif error.error_number == ConnectionAdapters::Mysql2Adapter::ER_ACCESS_DENIED_ERROR
|
49
|
+
raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
|
50
|
+
elsif [ConnectionAdapters::Mysql2Adapter::ER_CONN_HOST_ERROR, ConnectionAdapters::Mysql2Adapter::ER_UNKNOWN_HOST_ERROR].include?(error.error_number)
|
51
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
|
44
52
|
else
|
45
53
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
46
54
|
end
|
@@ -81,11 +89,9 @@ module ActiveRecord
|
|
81
89
|
|
82
90
|
# HELPER METHODS ===========================================
|
83
91
|
|
84
|
-
def each_hash(result) # :nodoc:
|
92
|
+
def each_hash(result, &block) # :nodoc:
|
85
93
|
if block_given?
|
86
|
-
result.each(as: :hash, symbolize_keys: true)
|
87
|
-
yield row
|
88
|
-
end
|
94
|
+
result.each(as: :hash, symbolize_keys: true, &block)
|
89
95
|
else
|
90
96
|
to_enum(:each_hash, result)
|
91
97
|
end
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
class PoolConfig # :nodoc:
|
6
6
|
include Mutex_m
|
7
7
|
|
8
|
-
attr_reader :db_config, :
|
8
|
+
attr_reader :db_config, :connection_class, :role, :shard
|
9
9
|
attr_accessor :schema_cache
|
10
10
|
|
11
11
|
INSTANCES = ObjectSpace::WeakMap.new
|
@@ -17,21 +17,21 @@ module ActiveRecord
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def initialize(
|
20
|
+
def initialize(connection_class, db_config, role, shard)
|
21
21
|
super()
|
22
|
-
@
|
22
|
+
@connection_class = connection_class
|
23
23
|
@db_config = db_config
|
24
|
+
@role = role
|
25
|
+
@shard = shard
|
24
26
|
@pool = nil
|
25
27
|
INSTANCES[self] = self
|
26
28
|
end
|
27
29
|
|
28
30
|
def connection_specification_name
|
29
|
-
if
|
30
|
-
connection_klass
|
31
|
-
elsif connection_klass.primary_class?
|
31
|
+
if connection_class.primary_class?
|
32
32
|
"ActiveRecord::Base"
|
33
33
|
else
|
34
|
-
|
34
|
+
connection_class.name
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -1,36 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
module PostgreSQL
|
6
8
|
class Column < ConnectionAdapters::Column # :nodoc:
|
7
9
|
delegate :oid, :fmod, to: :sql_type_metadata
|
8
10
|
|
9
|
-
def initialize(*, serial: nil, **)
|
11
|
+
def initialize(*, serial: nil, generated: nil, **)
|
10
12
|
super
|
11
13
|
@serial = serial
|
14
|
+
@generated = generated
|
12
15
|
end
|
13
16
|
|
14
17
|
def serial?
|
15
18
|
@serial
|
16
19
|
end
|
17
20
|
|
21
|
+
def virtual?
|
22
|
+
# We assume every generated column is virtual, no matter the concrete type
|
23
|
+
@generated.present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_default?
|
27
|
+
super && !virtual?
|
28
|
+
end
|
29
|
+
|
18
30
|
def array
|
19
31
|
sql_type_metadata.sql_type.end_with?("[]")
|
20
32
|
end
|
21
33
|
alias :array? :array
|
22
34
|
|
35
|
+
def enum?
|
36
|
+
type == :enum
|
37
|
+
end
|
38
|
+
|
23
39
|
def sql_type
|
24
40
|
super.delete_suffix("[]")
|
25
41
|
end
|
26
42
|
|
27
43
|
def init_with(coder)
|
28
44
|
@serial = coder["serial"]
|
45
|
+
@generated = coder["generated"]
|
29
46
|
super
|
30
47
|
end
|
31
48
|
|
32
49
|
def encode_with(coder)
|
33
50
|
coder["serial"] = @serial
|
51
|
+
coder["generated"] = @generated
|
34
52
|
super
|
35
53
|
end
|
36
54
|
|
@@ -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
|
|
@@ -37,9 +37,8 @@ module ActiveRecord
|
|
37
37
|
# Note: the PG::Result object is manually memory managed; if you don't
|
38
38
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
39
39
|
def execute(sql, name = nil)
|
40
|
-
|
41
|
-
|
42
|
-
end
|
40
|
+
sql = transform_query(sql)
|
41
|
+
check_if_write_query(sql)
|
43
42
|
|
44
43
|
materialize_transactions
|
45
44
|
mark_transaction_written_if_write(sql)
|
@@ -51,24 +50,20 @@ module ActiveRecord
|
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
54
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
55
|
-
execute_and_clear(sql, name, binds, prepare: prepare) do |result|
|
53
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
54
|
+
execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
|
56
55
|
types = {}
|
57
56
|
fields = result.fields
|
58
57
|
fields.each_with_index do |fname, i|
|
59
58
|
ftype = result.ftype i
|
60
59
|
fmod = result.fmod i
|
61
|
-
|
62
|
-
when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
|
63
|
-
# skip if a column has already been type casted by pg decoders
|
64
|
-
else types[fname] = type
|
65
|
-
end
|
60
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
66
61
|
end
|
67
62
|
build_result(columns: fields, rows: result.values, column_types: types)
|
68
63
|
end
|
69
64
|
end
|
70
65
|
|
71
|
-
def exec_delete(sql, name = nil, binds = [])
|
66
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
72
67
|
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
73
68
|
end
|
74
69
|
alias :exec_update :exec_delete
|
@@ -88,7 +83,7 @@ module ActiveRecord
|
|
88
83
|
end
|
89
84
|
private :sql_for_insert
|
90
85
|
|
91
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
86
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
|
92
87
|
if use_insert_returning? || pk == false
|
93
88
|
super
|
94
89
|
else
|
@@ -107,25 +102,33 @@ module ActiveRecord
|
|
107
102
|
end
|
108
103
|
|
109
104
|
# Begins a transaction.
|
110
|
-
def begin_db_transaction
|
105
|
+
def begin_db_transaction # :nodoc:
|
111
106
|
execute("BEGIN", "TRANSACTION")
|
112
107
|
end
|
113
108
|
|
114
|
-
def begin_isolated_db_transaction(isolation)
|
109
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
115
110
|
begin_db_transaction
|
116
111
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
117
112
|
end
|
118
113
|
|
119
114
|
# Commits a transaction.
|
120
|
-
def commit_db_transaction
|
115
|
+
def commit_db_transaction # :nodoc:
|
121
116
|
execute("COMMIT", "TRANSACTION")
|
122
117
|
end
|
123
118
|
|
124
119
|
# Aborts a transaction.
|
125
|
-
def exec_rollback_db_transaction
|
120
|
+
def exec_rollback_db_transaction # :nodoc:
|
126
121
|
execute("ROLLBACK", "TRANSACTION")
|
127
122
|
end
|
128
123
|
|
124
|
+
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
125
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
126
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
127
|
+
|
128
|
+
def high_precision_current_timestamp
|
129
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
130
|
+
end
|
131
|
+
|
129
132
|
private
|
130
133
|
def execute_batch(statements, name = nil)
|
131
134
|
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(/^(\\[\\"]|[^\\"])*?(?=")/)
|
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(/^(\\[\\"]|[^\\"])*?(?=")/)
|
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,30 @@
|
|
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
|
+
return if value.blank?
|
14
|
+
|
15
|
+
time = super
|
16
|
+
return time if time.is_a?(ActiveSupport::TimeWithZone) || !time.acts_like?(:time)
|
17
|
+
|
18
|
+
# While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to Postgres in UTC.
|
19
|
+
# We prefer times always in UTC, so here we convert back.
|
20
|
+
if is_utc?
|
21
|
+
time.getutc
|
22
|
+
else
|
23
|
+
time.getlocal
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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"
|
@@ -4,6 +4,9 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
6
|
module Quoting
|
7
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
8
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
9
|
+
|
7
10
|
class IntegerOutOf64BitRange < StandardError
|
8
11
|
def initialize(msg)
|
9
12
|
super(msg)
|
@@ -22,9 +25,54 @@ module ActiveRecord
|
|
22
25
|
@connection.unescape_bytea(value) if value
|
23
26
|
end
|
24
27
|
|
28
|
+
def check_int_in_range(value)
|
29
|
+
if value.to_int > 9223372036854775807 || value.to_int < -9223372036854775808
|
30
|
+
exception = <<~ERROR
|
31
|
+
Provided value outside of the range of a signed 64bit integer.
|
32
|
+
|
33
|
+
PostgreSQL will treat the column type in question as a numeric.
|
34
|
+
This may result in a slow sequential scan due to a comparison
|
35
|
+
being performed between an integer or bigint value and a numeric value.
|
36
|
+
|
37
|
+
To allow for this potentially unwanted behavior, set
|
38
|
+
ActiveRecord.raise_int_wider_than_64bit to false.
|
39
|
+
ERROR
|
40
|
+
raise IntegerOutOf64BitRange.new exception
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def quote(value) # :nodoc:
|
45
|
+
if ActiveRecord.raise_int_wider_than_64bit && value.is_a?(Integer)
|
46
|
+
check_int_in_range(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
case value
|
50
|
+
when OID::Xml::Data
|
51
|
+
"xml '#{quote_string(value.to_s)}'"
|
52
|
+
when OID::Bit::Data
|
53
|
+
if value.binary?
|
54
|
+
"B'#{value}'"
|
55
|
+
elsif value.hex?
|
56
|
+
"X'#{value}'"
|
57
|
+
end
|
58
|
+
when Numeric
|
59
|
+
if value.finite?
|
60
|
+
super
|
61
|
+
else
|
62
|
+
"'#{value}'"
|
63
|
+
end
|
64
|
+
when OID::Array::Data
|
65
|
+
quote(encode_array(value))
|
66
|
+
when Range
|
67
|
+
quote(encode_range(value))
|
68
|
+
else
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
25
73
|
# Quotes strings for use in SQL input.
|
26
|
-
def quote_string(s)
|
27
|
-
|
74
|
+
def quote_string(s) # :nodoc:
|
75
|
+
@connection.escape(s)
|
28
76
|
end
|
29
77
|
|
30
78
|
# Checks the following cases:
|
@@ -36,7 +84,7 @@ module ActiveRecord
|
|
36
84
|
# - "schema.name".table_name
|
37
85
|
# - "schema.name"."table.name"
|
38
86
|
def quote_table_name(name) # :nodoc:
|
39
|
-
|
87
|
+
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
40
88
|
end
|
41
89
|
|
42
90
|
# Quotes schema names for use in SQL queries.
|
@@ -50,11 +98,11 @@ module ActiveRecord
|
|
50
98
|
|
51
99
|
# Quotes column names for use in SQL queries.
|
52
100
|
def quote_column_name(name) # :nodoc:
|
53
|
-
|
101
|
+
QUOTED_COLUMN_NAMES[name] ||= PG::Connection.quote_ident(super).freeze
|
54
102
|
end
|
55
103
|
|
56
104
|
# Quote date/time values for use in SQL input.
|
57
|
-
def quoted_date(value)
|
105
|
+
def quoted_date(value) # :nodoc:
|
58
106
|
if value.year <= 0
|
59
107
|
bce_year = format("%04d", -value.year + 1)
|
60
108
|
super.sub(/^-?\d+/, bce_year) + " BC"
|
@@ -80,6 +128,24 @@ module ActiveRecord
|
|
80
128
|
end
|
81
129
|
end
|
82
130
|
|
131
|
+
def type_cast(value) # :nodoc:
|
132
|
+
case value
|
133
|
+
when Type::Binary::Data
|
134
|
+
# Return a bind param hash with format as binary.
|
135
|
+
# See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
|
136
|
+
# for more information
|
137
|
+
{ value: value.to_s, format: 1 }
|
138
|
+
when OID::Xml::Data, OID::Bit::Data
|
139
|
+
value.to_s
|
140
|
+
when OID::Array::Data
|
141
|
+
encode_array(value)
|
142
|
+
when Range
|
143
|
+
encode_range(value)
|
144
|
+
else
|
145
|
+
super
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
83
149
|
def lookup_cast_type_from_column(column) # :nodoc:
|
84
150
|
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
85
151
|
end
|
@@ -96,8 +162,8 @@ module ActiveRecord
|
|
96
162
|
\A
|
97
163
|
(
|
98
164
|
(?:
|
99
|
-
# "table_name"."column_name"::type_name | function(one or no argument)::type_name
|
100
|
-
((?:\w+\.|"\w+"\.)
|
165
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
166
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
101
167
|
)
|
102
168
|
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
103
169
|
)
|
@@ -109,8 +175,8 @@ module ActiveRecord
|
|
109
175
|
\A
|
110
176
|
(
|
111
177
|
(?:
|
112
|
-
# "table_name"."column_name"::type_name | function(one or no argument)::type_name
|
113
|
-
((?:\w+\.|"\w+"\.)
|
178
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
179
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
114
180
|
)
|
115
181
|
(?:\s+ASC|\s+DESC)?
|
116
182
|
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
@@ -126,69 +192,6 @@ module ActiveRecord
|
|
126
192
|
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
127
193
|
end
|
128
194
|
|
129
|
-
def check_int_in_range(value)
|
130
|
-
if value.to_int > 9223372036854775807 || value.to_int < -9223372036854775808
|
131
|
-
exception = <<~ERROR
|
132
|
-
Provided value outside of the range of a signed 64bit integer.
|
133
|
-
|
134
|
-
PostgreSQL will treat the column type in question as a numeric.
|
135
|
-
This may result in a slow sequential scan due to a comparison
|
136
|
-
being performed between an integer or bigint value and a numeric value.
|
137
|
-
|
138
|
-
To allow for this potentially unwanted behavior, set
|
139
|
-
ActiveRecord::Base.raise_int_wider_than_64bit to false.
|
140
|
-
ERROR
|
141
|
-
raise IntegerOutOf64BitRange.new exception
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def _quote(value)
|
146
|
-
if ActiveRecord::Base.raise_int_wider_than_64bit && value.is_a?(Integer)
|
147
|
-
check_int_in_range(value)
|
148
|
-
end
|
149
|
-
|
150
|
-
case value
|
151
|
-
when OID::Xml::Data
|
152
|
-
"xml '#{quote_string(value.to_s)}'"
|
153
|
-
when OID::Bit::Data
|
154
|
-
if value.binary?
|
155
|
-
"B'#{value}'"
|
156
|
-
elsif value.hex?
|
157
|
-
"X'#{value}'"
|
158
|
-
end
|
159
|
-
when Numeric
|
160
|
-
if value.finite?
|
161
|
-
super
|
162
|
-
else
|
163
|
-
"'#{value}'"
|
164
|
-
end
|
165
|
-
when OID::Array::Data
|
166
|
-
_quote(encode_array(value))
|
167
|
-
when Range
|
168
|
-
_quote(encode_range(value))
|
169
|
-
else
|
170
|
-
super
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def _type_cast(value)
|
175
|
-
case value
|
176
|
-
when Type::Binary::Data
|
177
|
-
# Return a bind param hash with format as binary.
|
178
|
-
# See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
|
179
|
-
# for more information
|
180
|
-
{ value: value.to_s, format: 1 }
|
181
|
-
when OID::Xml::Data, OID::Bit::Data
|
182
|
-
value.to_s
|
183
|
-
when OID::Array::Data
|
184
|
-
encode_array(value)
|
185
|
-
when Range
|
186
|
-
encode_range(value)
|
187
|
-
else
|
188
|
-
super
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
195
|
def encode_array(array_data)
|
193
196
|
encoder = array_data.encoder
|
194
197
|
values = type_cast_array(array_data.values)
|
@@ -214,7 +217,7 @@ module ActiveRecord
|
|
214
217
|
def type_cast_array(values)
|
215
218
|
case values
|
216
219
|
when ::Array then values.map { |item| type_cast_array(item) }
|
217
|
-
else
|
220
|
+
else type_cast(values)
|
218
221
|
end
|
219
222
|
end
|
220
223
|
|