activerecord 6.1.7.10 → 7.0.0.alpha1
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 +726 -1404
- 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 +31 -9
- 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 +1 -1
- 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 +14 -23
- 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 +161 -47
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -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 +41 -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 +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +2 -14
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -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 +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -14
- 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 +6 -32
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +159 -102
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
- 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 +20 -38
- data/lib/active_record/core.rb +111 -125
- data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +17 -9
- data/lib/active_record/delegated_type.rb +33 -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 +29 -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 +80 -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 +41 -41
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- 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 +34 -5
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- 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/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +89 -10
- 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 -31
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +72 -48
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- 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 +39 -26
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -22
- 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 +230 -49
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +8 -4
- data/lib/active_record/relation.rb +166 -77
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -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 +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/store.rb +1 -6
- data/lib/active_record/tasks/database_tasks.rb +106 -22
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -13
- 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.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- 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 +3 -2
- data/lib/arel/predications.rb +1 -1
- 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 +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +43 -2
- data/lib/arel.rb +1 -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 +52 -14
@@ -26,8 +26,6 @@ module ActiveRecord
|
|
26
26
|
|
27
27
|
def write_query?(sql) # :nodoc:
|
28
28
|
!READ_QUERY.match?(sql)
|
29
|
-
rescue ArgumentError # Invalid encoding
|
30
|
-
!READ_QUERY.match?(sql.b)
|
31
29
|
end
|
32
30
|
|
33
31
|
def explain(arel, binds = [])
|
@@ -40,21 +38,16 @@ module ActiveRecord
|
|
40
38
|
end
|
41
39
|
|
42
40
|
# Executes the SQL statement in the context of this connection.
|
43
|
-
def execute(sql, name = nil)
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
49
|
-
# made since we established the connection
|
50
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
41
|
+
def execute(sql, name = nil, async: false)
|
42
|
+
sql = transform_query(sql)
|
43
|
+
check_if_write_query(sql)
|
51
44
|
|
52
|
-
|
45
|
+
raw_execute(sql, name, async: async)
|
53
46
|
end
|
54
47
|
|
55
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
48
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
56
49
|
if without_prepared_statement?(binds)
|
57
|
-
execute_and_free(sql, name) do |result|
|
50
|
+
execute_and_free(sql, name, async: async) do |result|
|
58
51
|
if result
|
59
52
|
build_result(columns: result.fields, rows: result.to_a)
|
60
53
|
else
|
@@ -62,7 +55,7 @@ module ActiveRecord
|
|
62
55
|
end
|
63
56
|
end
|
64
57
|
else
|
65
|
-
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
|
58
|
+
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
|
66
59
|
if result
|
67
60
|
build_result(columns: result.fields, rows: result.to_a)
|
68
61
|
else
|
@@ -72,7 +65,7 @@ module ActiveRecord
|
|
72
65
|
end
|
73
66
|
end
|
74
67
|
|
75
|
-
def exec_delete(sql, name = nil, binds = [])
|
68
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
76
69
|
if without_prepared_statement?(binds)
|
77
70
|
@lock.synchronize do
|
78
71
|
execute_and_free(sql, name) { @connection.affected_rows }
|
@@ -83,10 +76,28 @@ module ActiveRecord
|
|
83
76
|
end
|
84
77
|
alias :exec_update :exec_delete
|
85
78
|
|
79
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
|
80
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
|
81
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
|
82
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
83
|
+
|
84
|
+
def high_precision_current_timestamp
|
85
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
86
|
+
end
|
87
|
+
|
86
88
|
private
|
89
|
+
def raw_execute(sql, name, async: false)
|
90
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
91
|
+
# made since we established the connection
|
92
|
+
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
93
|
+
|
94
|
+
super
|
95
|
+
end
|
96
|
+
|
87
97
|
def execute_batch(statements, name = nil)
|
98
|
+
statements = statements.map { |sql| transform_query(sql) }
|
88
99
|
combine_multi_statements(statements).each do |statement|
|
89
|
-
|
100
|
+
raw_execute(statement, name)
|
90
101
|
end
|
91
102
|
@connection.abandon_results!
|
92
103
|
end
|
@@ -150,21 +161,20 @@ module ActiveRecord
|
|
150
161
|
@max_allowed_packet ||= show_variable("max_allowed_packet")
|
151
162
|
end
|
152
163
|
|
153
|
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
154
|
-
|
155
|
-
|
156
|
-
end
|
164
|
+
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
|
165
|
+
sql = transform_query(sql)
|
166
|
+
check_if_write_query(sql)
|
157
167
|
|
158
168
|
materialize_transactions
|
159
169
|
mark_transaction_written_if_write(sql)
|
160
170
|
|
161
|
-
# make sure we carry over any changes to ActiveRecord
|
171
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
162
172
|
# made since we established the connection
|
163
|
-
@connection.query_options[:database_timezone] = ActiveRecord
|
173
|
+
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
164
174
|
|
165
175
|
type_casted_binds = type_casted_binds(binds)
|
166
176
|
|
167
|
-
log(sql, name, binds, type_casted_binds) do
|
177
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
168
178
|
if cache_stmt
|
169
179
|
stmt = @statements[sql] ||= @connection.prepare(sql)
|
170
180
|
else
|
@@ -6,6 +6,21 @@ module ActiveRecord
|
|
6
6
|
module ConnectionAdapters
|
7
7
|
module MySQL
|
8
8
|
module Quoting # :nodoc:
|
9
|
+
def quote_bound_value(value)
|
10
|
+
case value
|
11
|
+
when Numeric
|
12
|
+
_quote(value.to_s)
|
13
|
+
when BigDecimal
|
14
|
+
_quote(value.to_s("F"))
|
15
|
+
when true
|
16
|
+
"'1'"
|
17
|
+
when false
|
18
|
+
"'0'"
|
19
|
+
else
|
20
|
+
_quote(value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
9
24
|
def quote_column_name(name)
|
10
25
|
self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
|
11
26
|
end
|
@@ -79,7 +94,7 @@ module ActiveRecord
|
|
79
94
|
# We need to check explicitly for ActiveSupport::TimeWithZone because
|
80
95
|
# we need to transform it to Time objects but we don't want to
|
81
96
|
# transform Time objects to themselves.
|
82
|
-
if ActiveRecord
|
97
|
+
if ActiveRecord.default_timezone == :utc
|
83
98
|
value.getutc
|
84
99
|
else
|
85
100
|
value.getlocal
|
@@ -206,7 +206,7 @@ module ActiveRecord
|
|
206
206
|
def data_source_sql(name = nil, type: nil)
|
207
207
|
scope = quoted_scope(name, type: type)
|
208
208
|
|
209
|
-
sql = +"SELECT table_name FROM (SELECT
|
209
|
+
sql = +"SELECT table_name FROM (SELECT * FROM information_schema.tables "
|
210
210
|
sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
|
211
211
|
if scope[:type] || scope[:name]
|
212
212
|
conditions = []
|
@@ -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
|
@@ -26,9 +26,7 @@ module ActiveRecord
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def connection_specification_name
|
29
|
-
if connection_klass.
|
30
|
-
connection_klass
|
31
|
-
elsif connection_klass.primary_class?
|
29
|
+
if connection_klass.primary_class?
|
32
30
|
"ActiveRecord::Base"
|
33
31
|
else
|
34
32
|
connection_klass.name
|
@@ -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
|
|
@@ -28,8 +28,6 @@ module ActiveRecord
|
|
28
28
|
|
29
29
|
def write_query?(sql) # :nodoc:
|
30
30
|
!READ_QUERY.match?(sql)
|
31
|
-
rescue ArgumentError # Invalid encoding
|
32
|
-
!READ_QUERY.match?(sql.b)
|
33
31
|
end
|
34
32
|
|
35
33
|
# Executes an SQL statement, returning a PG::Result object on success
|
@@ -37,9 +35,8 @@ module ActiveRecord
|
|
37
35
|
# Note: the PG::Result object is manually memory managed; if you don't
|
38
36
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
39
37
|
def execute(sql, name = nil)
|
40
|
-
|
41
|
-
|
42
|
-
end
|
38
|
+
sql = transform_query(sql)
|
39
|
+
check_if_write_query(sql)
|
43
40
|
|
44
41
|
materialize_transactions
|
45
42
|
mark_transaction_written_if_write(sql)
|
@@ -51,8 +48,8 @@ module ActiveRecord
|
|
51
48
|
end
|
52
49
|
end
|
53
50
|
|
54
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
55
|
-
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|
|
56
53
|
types = {}
|
57
54
|
fields = result.fields
|
58
55
|
fields.each_with_index do |fname, i|
|
@@ -68,7 +65,7 @@ module ActiveRecord
|
|
68
65
|
end
|
69
66
|
end
|
70
67
|
|
71
|
-
def exec_delete(sql, name = nil, binds = [])
|
68
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
72
69
|
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
73
70
|
end
|
74
71
|
alias :exec_update :exec_delete
|
@@ -88,7 +85,7 @@ module ActiveRecord
|
|
88
85
|
end
|
89
86
|
private :sql_for_insert
|
90
87
|
|
91
|
-
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:
|
92
89
|
if use_insert_returning? || pk == false
|
93
90
|
super
|
94
91
|
else
|
@@ -107,25 +104,33 @@ module ActiveRecord
|
|
107
104
|
end
|
108
105
|
|
109
106
|
# Begins a transaction.
|
110
|
-
def begin_db_transaction
|
107
|
+
def begin_db_transaction # :nodoc:
|
111
108
|
execute("BEGIN", "TRANSACTION")
|
112
109
|
end
|
113
110
|
|
114
|
-
def begin_isolated_db_transaction(isolation)
|
111
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
115
112
|
begin_db_transaction
|
116
113
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
117
114
|
end
|
118
115
|
|
119
116
|
# Commits a transaction.
|
120
|
-
def commit_db_transaction
|
117
|
+
def commit_db_transaction # :nodoc:
|
121
118
|
execute("COMMIT", "TRANSACTION")
|
122
119
|
end
|
123
120
|
|
124
121
|
# Aborts a transaction.
|
125
|
-
def exec_rollback_db_transaction
|
122
|
+
def exec_rollback_db_transaction # :nodoc:
|
126
123
|
execute("ROLLBACK", "TRANSACTION")
|
127
124
|
end
|
128
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
|
+
|
129
134
|
private
|
130
135
|
def execute_batch(statements, name = nil)
|
131
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"
|
@@ -4,12 +4,6 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
6
|
module Quoting
|
7
|
-
class IntegerOutOf64BitRange < StandardError
|
8
|
-
def initialize(msg)
|
9
|
-
super(msg)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
7
|
# Escapes binary strings for bytea input to the database.
|
14
8
|
def escape_bytea(value)
|
15
9
|
@connection.escape_bytea(value) if value
|
@@ -23,7 +17,7 @@ module ActiveRecord
|
|
23
17
|
end
|
24
18
|
|
25
19
|
# Quotes strings for use in SQL input.
|
26
|
-
def quote_string(s)
|
20
|
+
def quote_string(s) # :nodoc:
|
27
21
|
PG::Connection.escape(s)
|
28
22
|
end
|
29
23
|
|
@@ -54,7 +48,7 @@ module ActiveRecord
|
|
54
48
|
end
|
55
49
|
|
56
50
|
# Quote date/time values for use in SQL input.
|
57
|
-
def quoted_date(value)
|
51
|
+
def quoted_date(value) # :nodoc:
|
58
52
|
if value.year <= 0
|
59
53
|
bce_year = format("%04d", -value.year + 1)
|
60
54
|
super.sub(/^-?\d+/, bce_year) + " BC"
|
@@ -96,8 +90,8 @@ module ActiveRecord
|
|
96
90
|
\A
|
97
91
|
(
|
98
92
|
(?:
|
99
|
-
# "table_name"."column_name"::type_name | function(one or no argument)::type_name
|
100
|
-
((?:\w+\.|"\w+"\.)
|
93
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
94
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
101
95
|
)
|
102
96
|
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
103
97
|
)
|
@@ -109,8 +103,8 @@ module ActiveRecord
|
|
109
103
|
\A
|
110
104
|
(
|
111
105
|
(?:
|
112
|
-
# "table_name"."column_name"::type_name | function(one or no argument)::type_name
|
113
|
-
((?:\w+\.|"\w+"\.)
|
106
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
107
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
114
108
|
)
|
115
109
|
(?:\s+ASC|\s+DESC)?
|
116
110
|
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
@@ -126,27 +120,7 @@ module ActiveRecord
|
|
126
120
|
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
127
121
|
end
|
128
122
|
|
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
123
|
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
124
|
case value
|
151
125
|
when OID::Xml::Data
|
152
126
|
"xml '#{quote_string(value.to_s)}'"
|
@@ -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
|
@@ -177,7 +177,7 @@ module ActiveRecord
|
|
177
177
|
define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
|
178
178
|
:hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
|
179
179
|
:money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
|
180
|
-
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml
|
180
|
+
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz
|
181
181
|
end
|
182
182
|
end
|
183
183
|
|
@@ -192,6 +192,10 @@ module ActiveRecord
|
|
192
192
|
end
|
193
193
|
|
194
194
|
private
|
195
|
+
def aliased_types(name, fallback)
|
196
|
+
fallback
|
197
|
+
end
|
198
|
+
|
195
199
|
def integer_like_primary_key_type(type, options)
|
196
200
|
if type == :bigint || options[:limit] == 8
|
197
201
|
:bigserial
|