activerecord 6.1.4.1 → 7.0.1
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 +1132 -936
- 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/join_dependency.rb +6 -2
- 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 +118 -90
- 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 +78 -22
- 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 +37 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -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 +21 -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 +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 +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 +29 -18
- 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 +27 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -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 +107 -3
- 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 +43 -38
- 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 +243 -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 +184 -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 +4 -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 +120 -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
- 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 +59 -14
@@ -26,33 +26,30 @@ 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)
|
29
31
|
end
|
30
32
|
|
31
33
|
def explain(arel, binds = [])
|
32
34
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
33
|
-
start =
|
35
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
34
36
|
result = exec_query(sql, "EXPLAIN", binds)
|
35
|
-
elapsed =
|
37
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
36
38
|
|
37
39
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
38
40
|
end
|
39
41
|
|
40
42
|
# Executes the SQL statement in the context of this connection.
|
41
|
-
def execute(sql, name = nil)
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
47
|
-
# made since we established the connection
|
48
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
43
|
+
def execute(sql, name = nil, async: false)
|
44
|
+
sql = transform_query(sql)
|
45
|
+
check_if_write_query(sql)
|
49
46
|
|
50
|
-
|
47
|
+
raw_execute(sql, name, async: async)
|
51
48
|
end
|
52
49
|
|
53
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
50
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
54
51
|
if without_prepared_statement?(binds)
|
55
|
-
execute_and_free(sql, name) do |result|
|
52
|
+
execute_and_free(sql, name, async: async) do |result|
|
56
53
|
if result
|
57
54
|
build_result(columns: result.fields, rows: result.to_a)
|
58
55
|
else
|
@@ -60,7 +57,7 @@ module ActiveRecord
|
|
60
57
|
end
|
61
58
|
end
|
62
59
|
else
|
63
|
-
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
|
60
|
+
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
|
64
61
|
if result
|
65
62
|
build_result(columns: result.fields, rows: result.to_a)
|
66
63
|
else
|
@@ -70,7 +67,7 @@ module ActiveRecord
|
|
70
67
|
end
|
71
68
|
end
|
72
69
|
|
73
|
-
def exec_delete(sql, name = nil, binds = [])
|
70
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
74
71
|
if without_prepared_statement?(binds)
|
75
72
|
@lock.synchronize do
|
76
73
|
execute_and_free(sql, name) { @connection.affected_rows }
|
@@ -81,10 +78,28 @@ module ActiveRecord
|
|
81
78
|
end
|
82
79
|
alias :exec_update :exec_delete
|
83
80
|
|
81
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
|
82
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
|
83
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
|
84
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
85
|
+
|
86
|
+
def high_precision_current_timestamp
|
87
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
88
|
+
end
|
89
|
+
|
84
90
|
private
|
91
|
+
def raw_execute(sql, name, async: false)
|
92
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
93
|
+
# made since we established the connection
|
94
|
+
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
95
|
+
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
85
99
|
def execute_batch(statements, name = nil)
|
100
|
+
statements = statements.map { |sql| transform_query(sql) }
|
86
101
|
combine_multi_statements(statements).each do |statement|
|
87
|
-
|
102
|
+
raw_execute(statement, name)
|
88
103
|
end
|
89
104
|
@connection.abandon_results!
|
90
105
|
end
|
@@ -148,21 +163,20 @@ module ActiveRecord
|
|
148
163
|
@max_allowed_packet ||= show_variable("max_allowed_packet")
|
149
164
|
end
|
150
165
|
|
151
|
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
152
|
-
|
153
|
-
|
154
|
-
end
|
166
|
+
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
|
167
|
+
sql = transform_query(sql)
|
168
|
+
check_if_write_query(sql)
|
155
169
|
|
156
170
|
materialize_transactions
|
157
171
|
mark_transaction_written_if_write(sql)
|
158
172
|
|
159
|
-
# make sure we carry over any changes to ActiveRecord
|
173
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
160
174
|
# made since we established the connection
|
161
|
-
@connection.query_options[:database_timezone] = ActiveRecord
|
175
|
+
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
162
176
|
|
163
177
|
type_casted_binds = type_casted_binds(binds)
|
164
178
|
|
165
|
-
log(sql, name, binds, type_casted_binds) do
|
179
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
166
180
|
if cache_stmt
|
167
181
|
stmt = @statements[sql] ||= @connection.prepare(sql)
|
168
182
|
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
|
@@ -34,6 +49,26 @@ module ActiveRecord
|
|
34
49
|
"x'#{value.hex}'"
|
35
50
|
end
|
36
51
|
|
52
|
+
# Override +type_cast+ we pass to mysql2 Date and Time objects instead
|
53
|
+
# of Strings since mysql2 is able to handle those classes more efficiently.
|
54
|
+
def type_cast(value) # :nodoc:
|
55
|
+
case value
|
56
|
+
when ActiveSupport::TimeWithZone
|
57
|
+
# We need to check explicitly for ActiveSupport::TimeWithZone because
|
58
|
+
# we need to transform it to Time objects but we don't want to
|
59
|
+
# transform Time objects to themselves.
|
60
|
+
if ActiveRecord.default_timezone == :utc
|
61
|
+
value.getutc
|
62
|
+
else
|
63
|
+
value.getlocal
|
64
|
+
end
|
65
|
+
when Date, Time
|
66
|
+
value
|
67
|
+
else
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
37
72
|
def column_name_matcher
|
38
73
|
COLUMN_NAME
|
39
74
|
end
|
@@ -69,27 +104,6 @@ module ActiveRecord
|
|
69
104
|
/ix
|
70
105
|
|
71
106
|
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
72
|
-
|
73
|
-
private
|
74
|
-
# Override +_type_cast+ we pass to mysql2 Date and Time objects instead
|
75
|
-
# of Strings since mysql2 is able to handle those classes more efficiently.
|
76
|
-
def _type_cast(value)
|
77
|
-
case value
|
78
|
-
when ActiveSupport::TimeWithZone
|
79
|
-
# We need to check explicitly for ActiveSupport::TimeWithZone because
|
80
|
-
# we need to transform it to Time objects but we don't want to
|
81
|
-
# transform Time objects to themselves.
|
82
|
-
if ActiveRecord::Base.default_timezone == :utc
|
83
|
-
value.getutc
|
84
|
-
else
|
85
|
-
value.getlocal
|
86
|
-
end
|
87
|
-
when Date, Time
|
88
|
-
value
|
89
|
-
else
|
90
|
-
super
|
91
|
-
end
|
92
|
-
end
|
93
107
|
end
|
94
108
|
end
|
95
109
|
end
|
@@ -163,10 +163,14 @@ module ActiveRecord
|
|
163
163
|
default, default_function = field[:Default], nil
|
164
164
|
|
165
165
|
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
166
|
+
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[:Extra])
|
166
167
|
default, default_function = nil, default
|
167
168
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
168
169
|
default = +"(#{default})" unless default.start_with?("(")
|
169
170
|
default, default_function = nil, default
|
171
|
+
elsif type_metadata.type == :text && default
|
172
|
+
# strip and unescape quotes
|
173
|
+
default = default[1...-1].gsub("\\'", "'")
|
170
174
|
end
|
171
175
|
|
172
176
|
MySQL::Column.new(
|
@@ -203,7 +207,7 @@ module ActiveRecord
|
|
203
207
|
def data_source_sql(name = nil, type: nil)
|
204
208
|
scope = quoted_scope(name, type: type)
|
205
209
|
|
206
|
-
sql = +"SELECT table_name FROM (SELECT
|
210
|
+
sql = +"SELECT table_name FROM (SELECT table_name, table_type FROM information_schema.tables "
|
207
211
|
sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
|
208
212
|
if scope[:type] || scope[:name]
|
209
213
|
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
|
@@ -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,25 +1,41 @@
|
|
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
|
@@ -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,6 +28,8 @@ 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)
|
31
33
|
end
|
32
34
|
|
33
35
|
# Executes an SQL statement, returning a PG::Result object on success
|
@@ -35,9 +37,8 @@ module ActiveRecord
|
|
35
37
|
# Note: the PG::Result object is manually memory managed; if you don't
|
36
38
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
37
39
|
def execute(sql, name = nil)
|
38
|
-
|
39
|
-
|
40
|
-
end
|
40
|
+
sql = transform_query(sql)
|
41
|
+
check_if_write_query(sql)
|
41
42
|
|
42
43
|
materialize_transactions
|
43
44
|
mark_transaction_written_if_write(sql)
|
@@ -49,8 +50,8 @@ module ActiveRecord
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
53
|
-
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|
|
54
55
|
types = {}
|
55
56
|
fields = result.fields
|
56
57
|
fields.each_with_index do |fname, i|
|
@@ -66,7 +67,7 @@ module ActiveRecord
|
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
69
|
-
def exec_delete(sql, name = nil, binds = [])
|
70
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
70
71
|
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
71
72
|
end
|
72
73
|
alias :exec_update :exec_delete
|
@@ -86,7 +87,7 @@ module ActiveRecord
|
|
86
87
|
end
|
87
88
|
private :sql_for_insert
|
88
89
|
|
89
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
90
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
|
90
91
|
if use_insert_returning? || pk == false
|
91
92
|
super
|
92
93
|
else
|
@@ -105,25 +106,33 @@ module ActiveRecord
|
|
105
106
|
end
|
106
107
|
|
107
108
|
# Begins a transaction.
|
108
|
-
def begin_db_transaction
|
109
|
+
def begin_db_transaction # :nodoc:
|
109
110
|
execute("BEGIN", "TRANSACTION")
|
110
111
|
end
|
111
112
|
|
112
|
-
def begin_isolated_db_transaction(isolation)
|
113
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
113
114
|
begin_db_transaction
|
114
115
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
115
116
|
end
|
116
117
|
|
117
118
|
# Commits a transaction.
|
118
|
-
def commit_db_transaction
|
119
|
+
def commit_db_transaction # :nodoc:
|
119
120
|
execute("COMMIT", "TRANSACTION")
|
120
121
|
end
|
121
122
|
|
122
123
|
# Aborts a transaction.
|
123
|
-
def exec_rollback_db_transaction
|
124
|
+
def exec_rollback_db_transaction # :nodoc:
|
124
125
|
execute("ROLLBACK", "TRANSACTION")
|
125
126
|
end
|
126
127
|
|
128
|
+
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
129
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
130
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
131
|
+
|
132
|
+
def high_precision_current_timestamp
|
133
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
134
|
+
end
|
135
|
+
|
127
136
|
private
|
128
137
|
def execute_batch(statements, name = nil)
|
129
138
|
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,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)
|
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"
|