activerecord 7.0.0 → 7.2.1
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 +515 -1268
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +28 -17
- data/lib/active_record/associations/collection_proxy.rb +36 -13
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +18 -14
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +2 -4
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +378 -491
- data/lib/active_record/attribute_assignment.rb +1 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +153 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -40
- data/lib/active_record/attributes.rb +63 -48
- data/lib/active_record/autosave_association.rb +70 -38
- data/lib/active_record/base.rb +12 -8
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +124 -132
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +319 -135
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +27 -140
- data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +98 -106
- data/lib/active_record/core.rb +220 -177
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +88 -35
- data/lib/active_record/delegated_type.rb +40 -11
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +47 -25
- data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
- data/lib/active_record/encryption/encryptor.rb +25 -10
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +23 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +131 -27
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +169 -99
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +39 -24
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +28 -27
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +110 -13
- data/lib/active_record/migration/compatibility.rb +174 -64
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +292 -125
- data/lib/active_record/model_schema.rb +113 -112
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +177 -345
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +102 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +34 -9
- data/lib/active_record/railtie.rb +153 -100
- data/lib/active_record/railties/controller_runtime.rb +24 -10
- data/lib/active_record/railties/databases.rake +148 -152
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +278 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +293 -108
- data/lib/active_record/relation/delegation.rb +31 -20
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +625 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +602 -96
- data/lib/active_record/result.rb +55 -52
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +76 -30
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +82 -30
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +29 -8
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +191 -121
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +174 -152
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +109 -27
- data/lib/active_record/translation.rb +1 -3
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +12 -6
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +63 -14
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +266 -30
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +59 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -4,19 +4,22 @@ module ActiveRecord
|
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module PostgreSQL
|
|
6
6
|
module DatabaseStatements
|
|
7
|
-
def explain(arel, binds = [])
|
|
8
|
-
sql
|
|
9
|
-
|
|
7
|
+
def explain(arel, binds = [], options = [])
|
|
8
|
+
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
|
|
9
|
+
result = internal_exec_query(sql, "EXPLAIN", binds)
|
|
10
|
+
PostgreSQL::ExplainPrettyPrinter.new.pp(result)
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
# Queries the database and returns the results in an Array-like object
|
|
13
14
|
def query(sql, name = nil) # :nodoc:
|
|
14
|
-
materialize_transactions
|
|
15
15
|
mark_transaction_written_if_write(sql)
|
|
16
16
|
|
|
17
|
-
log(sql, name) do
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
log(sql, name) do |notification_payload|
|
|
18
|
+
with_raw_connection do |conn|
|
|
19
|
+
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
|
20
|
+
verified!
|
|
21
|
+
notification_payload[:row_count] = result.count
|
|
22
|
+
result
|
|
20
23
|
end
|
|
21
24
|
end
|
|
22
25
|
end
|
|
@@ -34,36 +37,41 @@ module ActiveRecord
|
|
|
34
37
|
|
|
35
38
|
# Executes an SQL statement, returning a PG::Result object on success
|
|
36
39
|
# or raising a PG::Error exception otherwise.
|
|
40
|
+
#
|
|
41
|
+
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
|
42
|
+
# executing the SQL statement in case of a connection-related exception.
|
|
43
|
+
# This option should only be enabled for known idempotent queries.
|
|
44
|
+
#
|
|
37
45
|
# Note: the PG::Result object is manually memory managed; if you don't
|
|
38
46
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
|
39
|
-
def execute(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
mark_transaction_written_if_write(sql)
|
|
47
|
+
def execute(...) # :nodoc:
|
|
48
|
+
super
|
|
49
|
+
ensure
|
|
50
|
+
@notice_receiver_sql_warnings = []
|
|
51
|
+
end
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
|
54
|
+
log(sql, name, async: async) do |notification_payload|
|
|
55
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
56
|
+
result = conn.async_exec(sql)
|
|
57
|
+
verified!
|
|
58
|
+
handle_warnings(result)
|
|
59
|
+
notification_payload[:row_count] = result.count
|
|
60
|
+
result
|
|
49
61
|
end
|
|
50
62
|
end
|
|
51
63
|
end
|
|
52
64
|
|
|
53
|
-
def
|
|
54
|
-
execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
|
|
65
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
|
66
|
+
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
|
55
67
|
types = {}
|
|
56
68
|
fields = result.fields
|
|
57
69
|
fields.each_with_index do |fname, i|
|
|
58
70
|
ftype = result.ftype i
|
|
59
71
|
fmod = result.fmod i
|
|
60
|
-
|
|
61
|
-
when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
|
|
62
|
-
# skip if a column has already been type casted by pg decoders
|
|
63
|
-
else types[fname] = type
|
|
64
|
-
end
|
|
72
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
|
65
73
|
end
|
|
66
|
-
build_result(columns: fields, rows: result.values, column_types: types)
|
|
74
|
+
build_result(columns: fields, rows: result.values, column_types: types.freeze)
|
|
67
75
|
end
|
|
68
76
|
end
|
|
69
77
|
|
|
@@ -72,26 +80,11 @@ module ActiveRecord
|
|
|
72
80
|
end
|
|
73
81
|
alias :exec_update :exec_delete
|
|
74
82
|
|
|
75
|
-
def
|
|
76
|
-
if pk.nil?
|
|
77
|
-
# Extract the table from the insert sql. Yuck.
|
|
78
|
-
table_ref = extract_table_ref_from_insert_sql(sql)
|
|
79
|
-
pk = primary_key(table_ref) if table_ref
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
if pk = suppress_composite_primary_key(pk)
|
|
83
|
-
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
super
|
|
87
|
-
end
|
|
88
|
-
private :sql_for_insert
|
|
89
|
-
|
|
90
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
|
|
83
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
|
91
84
|
if use_insert_returning? || pk == false
|
|
92
85
|
super
|
|
93
86
|
else
|
|
94
|
-
result =
|
|
87
|
+
result = internal_exec_query(sql, name, binds)
|
|
95
88
|
unless sequence_name
|
|
96
89
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
|
97
90
|
if table_ref
|
|
@@ -107,33 +100,76 @@ module ActiveRecord
|
|
|
107
100
|
|
|
108
101
|
# Begins a transaction.
|
|
109
102
|
def begin_db_transaction # :nodoc:
|
|
110
|
-
|
|
103
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
111
104
|
end
|
|
112
105
|
|
|
113
106
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
|
114
|
-
|
|
115
|
-
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
|
107
|
+
internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
116
108
|
end
|
|
117
109
|
|
|
118
110
|
# Commits a transaction.
|
|
119
111
|
def commit_db_transaction # :nodoc:
|
|
120
|
-
|
|
112
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
121
113
|
end
|
|
122
114
|
|
|
123
115
|
# Aborts a transaction.
|
|
124
116
|
def exec_rollback_db_transaction # :nodoc:
|
|
125
|
-
|
|
117
|
+
cancel_any_running_query
|
|
118
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def exec_restart_db_transaction # :nodoc:
|
|
122
|
+
cancel_any_running_query
|
|
123
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
126
124
|
end
|
|
127
125
|
|
|
128
126
|
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
|
129
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
|
127
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP", retryable: true).freeze # :nodoc:
|
|
130
128
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
131
129
|
|
|
132
130
|
def high_precision_current_timestamp
|
|
133
131
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
134
132
|
end
|
|
135
133
|
|
|
134
|
+
def build_explain_clause(options = [])
|
|
135
|
+
return "EXPLAIN" if options.empty?
|
|
136
|
+
|
|
137
|
+
"EXPLAIN (#{options.join(", ").upcase})"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Set when constraints will be checked for the current transaction.
|
|
141
|
+
#
|
|
142
|
+
# Not passing any specific constraint names will set the value for all deferrable constraints.
|
|
143
|
+
#
|
|
144
|
+
# [<tt>deferred</tt>]
|
|
145
|
+
# Valid values are +:deferred+ or +:immediate+.
|
|
146
|
+
#
|
|
147
|
+
# See https://www.postgresql.org/docs/current/sql-set-constraints.html
|
|
148
|
+
def set_constraints(deferred, *constraints)
|
|
149
|
+
unless %i[deferred immediate].include?(deferred)
|
|
150
|
+
raise ArgumentError, "deferred must be :deferred or :immediate"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
constraints = if constraints.empty?
|
|
154
|
+
"ALL"
|
|
155
|
+
else
|
|
156
|
+
constraints.map { |c| quote_table_name(c) }.join(", ")
|
|
157
|
+
end
|
|
158
|
+
execute("SET CONSTRAINTS #{constraints} #{deferred.to_s.upcase}")
|
|
159
|
+
end
|
|
160
|
+
|
|
136
161
|
private
|
|
162
|
+
IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
|
|
163
|
+
private_constant :IDLE_TRANSACTION_STATUSES
|
|
164
|
+
|
|
165
|
+
def cancel_any_running_query
|
|
166
|
+
return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
|
|
167
|
+
|
|
168
|
+
@raw_connection.cancel
|
|
169
|
+
@raw_connection.block
|
|
170
|
+
rescue PG::Error
|
|
171
|
+
end
|
|
172
|
+
|
|
137
173
|
def execute_batch(statements, name = nil)
|
|
138
174
|
execute(combine_multi_statements(statements))
|
|
139
175
|
end
|
|
@@ -144,12 +180,29 @@ module ActiveRecord
|
|
|
144
180
|
|
|
145
181
|
# Returns the current ID of a table's sequence.
|
|
146
182
|
def last_insert_id_result(sequence_name)
|
|
147
|
-
|
|
183
|
+
internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def returning_column_values(result)
|
|
187
|
+
result.rows.first
|
|
148
188
|
end
|
|
149
189
|
|
|
150
190
|
def suppress_composite_primary_key(pk)
|
|
151
191
|
pk unless pk.is_a?(Array)
|
|
152
192
|
end
|
|
193
|
+
|
|
194
|
+
def handle_warnings(sql)
|
|
195
|
+
@notice_receiver_sql_warnings.each do |warning|
|
|
196
|
+
next if warning_ignored?(warning)
|
|
197
|
+
|
|
198
|
+
warning.sql = sql
|
|
199
|
+
ActiveRecord.db_warnings_action.call(warning)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def warning_ignored?(warning)
|
|
204
|
+
["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
|
|
205
|
+
end
|
|
153
206
|
end
|
|
154
207
|
end
|
|
155
208
|
end
|
|
@@ -28,6 +28,12 @@ module ActiveRecord
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
|
32
|
+
# https://github.com/ruby/ipaddr/issues/21
|
|
33
|
+
def changed?(old_value, new_value, _new_value_before_type_cast)
|
|
34
|
+
super || !old_value.nil? && old_value.prefix != new_value.prefix
|
|
35
|
+
end
|
|
36
|
+
|
|
31
37
|
def cast_value(value)
|
|
32
38
|
if value.nil?
|
|
33
39
|
nil
|
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
|
26
26
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
unless key = scanner.
|
|
29
|
+
unless key = scanner.scan(/^(\\[\\"]|[^\\"])*?(?=")/)
|
|
30
30
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -41,7 +41,7 @@ module ActiveRecord
|
|
|
41
41
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
unless value = scanner.
|
|
44
|
+
unless value = scanner.scan(/^(\\[\\"]|[^\\"])*?(?=")/)
|
|
45
45
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
|
33
33
|
when ::Numeric
|
|
34
34
|
# Sometimes operations on Times returns just float number of seconds so we need to handle that.
|
|
35
35
|
# Example: Time.current - (Time.current + 1.hour) # => -3600.000001776 (Float)
|
|
36
|
-
value.
|
|
36
|
+
ActiveSupport::Duration.build(value).iso8601(precision: self.precision)
|
|
37
37
|
else
|
|
38
38
|
super
|
|
39
39
|
end
|
|
@@ -27,9 +27,10 @@ module ActiveRecord
|
|
|
27
27
|
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
|
|
28
28
|
case value
|
|
29
29
|
when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
|
|
30
|
-
value.
|
|
30
|
+
value.delete!("^-0-9.")
|
|
31
31
|
when /^-?\D*+[\d.]+,\d{2}$/ # (2)
|
|
32
|
-
value.
|
|
32
|
+
value.delete!("^-0-9,")
|
|
33
|
+
value.tr!(",", ".")
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
super(value)
|
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def cast_value(value)
|
|
21
|
-
return if
|
|
21
|
+
return if ["empty", ""].include? value
|
|
22
22
|
return value unless value.is_a?(::String)
|
|
23
23
|
|
|
24
24
|
extracted = extract_bounds(value)
|
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
|
28
28
|
if !infinity?(from) && extracted[:exclude_start]
|
|
29
29
|
raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
|
|
30
30
|
end
|
|
31
|
-
::Range.new(from, to, extracted[:exclude_end])
|
|
31
|
+
::Range.new(*sanitize_bounds(from, to), extracted[:exclude_end])
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def serialize(value)
|
|
@@ -76,6 +76,15 @@ module ActiveRecord
|
|
|
76
76
|
}
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
INFINITE_FLOAT_RANGE = (-::Float::INFINITY)..(::Float::INFINITY) # :nodoc:
|
|
80
|
+
|
|
81
|
+
def sanitize_bounds(from, to)
|
|
82
|
+
[
|
|
83
|
+
(from == -::Float::INFINITY && !INFINITE_FLOAT_RANGE.cover?(to)) ? nil : from,
|
|
84
|
+
(to == ::Float::INFINITY && !INFINITE_FLOAT_RANGE.cover?(from)) ? nil : to
|
|
85
|
+
]
|
|
86
|
+
end
|
|
87
|
+
|
|
79
88
|
# When formatting the bound values of range types, PostgreSQL quotes
|
|
80
89
|
# the bound value using double-quotes in certain conditions. Within
|
|
81
90
|
# a double-quoted string, literal " and \ characters are themselves
|
|
@@ -10,10 +10,12 @@ module ActiveRecord
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def cast_value(value)
|
|
13
|
+
return if value.blank?
|
|
14
|
+
|
|
13
15
|
time = super
|
|
14
|
-
return time if time.is_a?(ActiveSupport::TimeWithZone)
|
|
16
|
+
return time if time.is_a?(ActiveSupport::TimeWithZone) || !time.acts_like?(:time)
|
|
15
17
|
|
|
16
|
-
# While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to
|
|
18
|
+
# While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to PostgreSQL in UTC.
|
|
17
19
|
# We prefer times always in UTC, so here we convert back.
|
|
18
20
|
if is_utc?
|
|
19
21
|
time.getutc
|
|
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
|
6
6
|
module OID # :nodoc:
|
|
7
7
|
class Uuid < Type::Value # :nodoc:
|
|
8
8
|
ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
|
|
9
|
+
CANONICAL_UUID = %r{\A[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}\z}
|
|
9
10
|
|
|
10
11
|
alias :serialize :deserialize
|
|
11
12
|
|
|
@@ -15,18 +16,27 @@ module ActiveRecord
|
|
|
15
16
|
|
|
16
17
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
|
17
18
|
old_value.class != new_value.class ||
|
|
18
|
-
new_value
|
|
19
|
+
new_value != old_value
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def changed_in_place?(raw_old_value, new_value)
|
|
22
23
|
raw_old_value.class != new_value.class ||
|
|
23
|
-
new_value
|
|
24
|
+
new_value != raw_old_value
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
private
|
|
27
28
|
def cast_value(value)
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
value = value.to_s
|
|
30
|
+
format_uuid(value) if value.match?(ACCEPTABLE_UUID)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def format_uuid(uuid)
|
|
34
|
+
if uuid.match?(CANONICAL_UUID)
|
|
35
|
+
uuid
|
|
36
|
+
else
|
|
37
|
+
uuid = uuid.delete("{}-").downcase
|
|
38
|
+
"#{uuid[..7]}-#{uuid[8..11]}-#{uuid[12..15]}-#{uuid[16..19]}-#{uuid[20..]}"
|
|
39
|
+
end
|
|
30
40
|
end
|
|
31
41
|
end
|
|
32
42
|
end
|
|
@@ -4,19 +4,101 @@ module ActiveRecord
|
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module PostgreSQL
|
|
6
6
|
module Quoting
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
|
10
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
|
11
|
+
|
|
12
|
+
module ClassMethods # :nodoc:
|
|
13
|
+
def column_name_matcher
|
|
14
|
+
/
|
|
15
|
+
\A
|
|
16
|
+
(
|
|
17
|
+
(?:
|
|
18
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
|
19
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
|
20
|
+
)
|
|
21
|
+
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
|
22
|
+
)
|
|
23
|
+
(?:\s*,\s*\g<1>)*
|
|
24
|
+
\z
|
|
25
|
+
/ix
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def column_name_with_order_matcher
|
|
29
|
+
/
|
|
30
|
+
\A
|
|
31
|
+
(
|
|
32
|
+
(?:
|
|
33
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
|
34
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
|
35
|
+
)
|
|
36
|
+
(?:\s+COLLATE\s+"\w+")?
|
|
37
|
+
(?:\s+ASC|\s+DESC)?
|
|
38
|
+
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
|
39
|
+
)
|
|
40
|
+
(?:\s*,\s*\g<1>)*
|
|
41
|
+
\z
|
|
42
|
+
/ix
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Quotes column names for use in SQL queries.
|
|
46
|
+
def quote_column_name(name) # :nodoc:
|
|
47
|
+
QUOTED_COLUMN_NAMES[name] ||= PG::Connection.quote_ident(name.to_s).freeze
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Checks the following cases:
|
|
51
|
+
#
|
|
52
|
+
# - table_name
|
|
53
|
+
# - "table.name"
|
|
54
|
+
# - schema_name.table_name
|
|
55
|
+
# - schema_name."table.name"
|
|
56
|
+
# - "schema.name".table_name
|
|
57
|
+
# - "schema.name"."table.name"
|
|
58
|
+
def quote_table_name(name) # :nodoc:
|
|
59
|
+
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class IntegerOutOf64BitRange < StandardError
|
|
64
|
+
def initialize(msg)
|
|
65
|
+
super(msg)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
7
69
|
# Escapes binary strings for bytea input to the database.
|
|
8
70
|
def escape_bytea(value)
|
|
9
|
-
|
|
71
|
+
valid_raw_connection.escape_bytea(value) if value
|
|
10
72
|
end
|
|
11
73
|
|
|
12
74
|
# Unescapes bytea output from a database to the binary string it represents.
|
|
13
75
|
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
|
|
14
76
|
# on escaped binary output from database drive.
|
|
15
77
|
def unescape_bytea(value)
|
|
16
|
-
|
|
78
|
+
valid_raw_connection.unescape_bytea(value) if value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def check_int_in_range(value)
|
|
82
|
+
if value.to_int > 9223372036854775807 || value.to_int < -9223372036854775808
|
|
83
|
+
exception = <<~ERROR
|
|
84
|
+
Provided value outside of the range of a signed 64bit integer.
|
|
85
|
+
|
|
86
|
+
PostgreSQL will treat the column type in question as a numeric.
|
|
87
|
+
This may result in a slow sequential scan due to a comparison
|
|
88
|
+
being performed between an integer or bigint value and a numeric value.
|
|
89
|
+
|
|
90
|
+
To allow for this potentially unwanted behavior, set
|
|
91
|
+
ActiveRecord.raise_int_wider_than_64bit to false.
|
|
92
|
+
ERROR
|
|
93
|
+
raise IntegerOutOf64BitRange.new exception
|
|
94
|
+
end
|
|
17
95
|
end
|
|
18
96
|
|
|
19
97
|
def quote(value) # :nodoc:
|
|
98
|
+
if ActiveRecord.raise_int_wider_than_64bit && value.is_a?(Integer)
|
|
99
|
+
check_int_in_range(value)
|
|
100
|
+
end
|
|
101
|
+
|
|
20
102
|
case value
|
|
21
103
|
when OID::Xml::Data
|
|
22
104
|
"xml '#{quote_string(value.to_s)}'"
|
|
@@ -43,33 +125,18 @@ module ActiveRecord
|
|
|
43
125
|
|
|
44
126
|
# Quotes strings for use in SQL input.
|
|
45
127
|
def quote_string(s) # :nodoc:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# Checks the following cases:
|
|
50
|
-
#
|
|
51
|
-
# - table_name
|
|
52
|
-
# - "table.name"
|
|
53
|
-
# - schema_name.table_name
|
|
54
|
-
# - schema_name."table.name"
|
|
55
|
-
# - "schema.name".table_name
|
|
56
|
-
# - "schema.name"."table.name"
|
|
57
|
-
def quote_table_name(name) # :nodoc:
|
|
58
|
-
self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Quotes schema names for use in SQL queries.
|
|
62
|
-
def quote_schema_name(name)
|
|
63
|
-
PG::Connection.quote_ident(name)
|
|
128
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
|
129
|
+
connection.escape(s)
|
|
130
|
+
end
|
|
64
131
|
end
|
|
65
132
|
|
|
66
133
|
def quote_table_name_for_assignment(table, attr)
|
|
67
134
|
quote_column_name(attr)
|
|
68
135
|
end
|
|
69
136
|
|
|
70
|
-
# Quotes
|
|
71
|
-
def
|
|
72
|
-
|
|
137
|
+
# Quotes schema names for use in SQL queries.
|
|
138
|
+
def quote_schema_name(schema_name)
|
|
139
|
+
quote_column_name(schema_name)
|
|
73
140
|
end
|
|
74
141
|
|
|
75
142
|
# Quote date/time values for use in SQL input.
|
|
@@ -89,7 +156,7 @@ module ActiveRecord
|
|
|
89
156
|
def quote_default_expression(value, column) # :nodoc:
|
|
90
157
|
if value.is_a?(Proc)
|
|
91
158
|
value.call
|
|
92
|
-
elsif column.type == :uuid && value.is_a?(String) &&
|
|
159
|
+
elsif column.type == :uuid && value.is_a?(String) && value.include?("()")
|
|
93
160
|
value # Does not quote function default values for UUID columns
|
|
94
161
|
elsif column.respond_to?(:array?)
|
|
95
162
|
type = lookup_cast_type_from_column(column)
|
|
@@ -112,52 +179,18 @@ module ActiveRecord
|
|
|
112
179
|
encode_array(value)
|
|
113
180
|
when Range
|
|
114
181
|
encode_range(value)
|
|
182
|
+
when Rational
|
|
183
|
+
value.to_f
|
|
115
184
|
else
|
|
116
185
|
super
|
|
117
186
|
end
|
|
118
187
|
end
|
|
119
188
|
|
|
120
189
|
def lookup_cast_type_from_column(column) # :nodoc:
|
|
190
|
+
verify! if type_map.nil?
|
|
121
191
|
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
|
122
192
|
end
|
|
123
193
|
|
|
124
|
-
def column_name_matcher
|
|
125
|
-
COLUMN_NAME
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def column_name_with_order_matcher
|
|
129
|
-
COLUMN_NAME_WITH_ORDER
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
COLUMN_NAME = /
|
|
133
|
-
\A
|
|
134
|
-
(
|
|
135
|
-
(?:
|
|
136
|
-
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
|
137
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
|
138
|
-
)
|
|
139
|
-
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
|
140
|
-
)
|
|
141
|
-
(?:\s*,\s*\g<1>)*
|
|
142
|
-
\z
|
|
143
|
-
/ix
|
|
144
|
-
|
|
145
|
-
COLUMN_NAME_WITH_ORDER = /
|
|
146
|
-
\A
|
|
147
|
-
(
|
|
148
|
-
(?:
|
|
149
|
-
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
|
150
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
|
|
151
|
-
)
|
|
152
|
-
(?:\s+ASC|\s+DESC)?
|
|
153
|
-
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
|
154
|
-
)
|
|
155
|
-
(?:\s*,\s*\g<1>)*
|
|
156
|
-
\z
|
|
157
|
-
/ix
|
|
158
|
-
|
|
159
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
|
160
|
-
|
|
161
194
|
private
|
|
162
195
|
def lookup_cast_type(sql_type)
|
|
163
196
|
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
|
@@ -38,15 +38,17 @@ Rails needs superuser privileges to disable referential integrity.
|
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def
|
|
41
|
+
def check_all_foreign_keys_valid! # :nodoc:
|
|
42
42
|
sql = <<~SQL
|
|
43
43
|
do $$
|
|
44
44
|
declare r record;
|
|
45
45
|
BEGIN
|
|
46
46
|
FOR r IN (
|
|
47
47
|
SELECT FORMAT(
|
|
48
|
-
'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I''; ALTER TABLE %I VALIDATE CONSTRAINT %I;',
|
|
48
|
+
'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I'' AND connamespace::regnamespace = ''%I''::regnamespace; ALTER TABLE %I.%I VALIDATE CONSTRAINT %I;',
|
|
49
49
|
constraint_name,
|
|
50
|
+
table_schema,
|
|
51
|
+
table_schema,
|
|
50
52
|
table_name,
|
|
51
53
|
constraint_name
|
|
52
54
|
) AS constraint_check
|
|
@@ -59,14 +61,8 @@ Rails needs superuser privileges to disable referential integrity.
|
|
|
59
61
|
$$;
|
|
60
62
|
SQL
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
execute(sql)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
true
|
|
68
|
-
rescue ActiveRecord::StatementInvalid
|
|
69
|
-
false
|
|
64
|
+
transaction(requires_new: true) do
|
|
65
|
+
execute(sql)
|
|
70
66
|
end
|
|
71
67
|
end
|
|
72
68
|
end
|