activerecord 8.0.0 → 8.1.4
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 +1381 -241
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +35 -17
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +20 -4
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +36 -8
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +9 -5
- data/lib/active_record/associations/collection_proxy.rb +26 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +5 -4
- data/lib/active_record/associations/preloader/association.rb +1 -1
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/primary_key.rb +2 -1
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +19 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attribute_methods.rb +24 -17
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +22 -12
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/callbacks.rb +1 -2
- data/lib/active_record/coders/column_serializer.rb +1 -1
- data/lib/active_record/coders/json.rb +16 -6
- data/lib/active_record/coders/yaml_column.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
- data/lib/active_record/connection_adapters/column.rb +14 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +15 -10
- data/lib/active_record/core.rb +44 -12
- data/lib/active_record/counter_cache.rb +36 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +66 -9
- data/lib/active_record/database_configurations/url_config.rb +15 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +19 -19
- data/lib/active_record/disable_joins_association_relation.rb +5 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
- data/lib/active_record/encryption/encryptor.rb +39 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +23 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +76 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/future_result.rb +5 -3
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +9 -4
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +30 -22
- data/lib/active_record/message_pack.rb +13 -0
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +32 -6
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +31 -21
- data/lib/active_record/model_schema.rb +38 -12
- data/lib/active_record/nested_attributes.rb +10 -6
- data/lib/active_record/persistence.rb +35 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +7 -7
- data/lib/active_record/querying.rb +17 -5
- data/lib/active_record/railtie.rb +35 -6
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +24 -20
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +36 -1
- data/lib/active_record/relation/batches.rb +25 -11
- data/lib/active_record/relation/calculations.rb +62 -41
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +76 -45
- data/lib/active_record/relation/merger.rb +4 -2
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
- data/lib/active_record/relation/predicate_builder.rb +10 -8
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +106 -45
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +63 -31
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +41 -21
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +48 -19
- data/lib/active_record/statement_cache.rb +16 -12
- data/lib/active_record/store.rb +46 -19
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +5 -20
- data/lib/active_record/tasks/abstract_tasks.rb +83 -0
- data/lib/active_record/tasks/database_tasks.rb +44 -45
- data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +18 -4
- data/lib/active_record/test_fixtures.rb +40 -6
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +58 -32
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +72 -7
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +7 -23
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -15
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -48,30 +48,64 @@ module ActiveRecord
|
|
|
48
48
|
# made since we established the connection
|
|
49
49
|
raw_connection.query_options[:database_timezone] = default_timezone
|
|
50
50
|
|
|
51
|
-
result =
|
|
52
|
-
|
|
51
|
+
result = nil
|
|
52
|
+
if binds.nil? || binds.empty?
|
|
53
|
+
result = raw_connection.query(sql)
|
|
54
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
|
55
|
+
# As of mysql2 0.5.6 `#affected_rows` might raise Mysql2::Error if a prepared statement
|
|
56
|
+
# from that same connection was GCed while `#query` released the GVL.
|
|
57
|
+
# By avoiding to call `#affected_rows` when we have a result, we reduce the likeliness
|
|
58
|
+
# of hitting the bug.
|
|
59
|
+
@affected_rows_before_warnings = result&.size || raw_connection.affected_rows
|
|
60
|
+
elsif prepare
|
|
61
|
+
retry_count = 1
|
|
62
|
+
begin
|
|
63
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
|
64
|
+
result = stmt.execute(*type_casted_binds)
|
|
65
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
|
66
|
+
rescue ::Mysql2::Error => error
|
|
67
|
+
@statements.delete(sql)
|
|
68
|
+
# Sometimes for an unknown reason, we get that error.
|
|
69
|
+
# It suggest somehow that the prepared statement was deallocated
|
|
70
|
+
# but the client doesn't know it.
|
|
71
|
+
# But we know that this error is safe to retry, so we do so after
|
|
72
|
+
# getting rid of the originally cached statement.
|
|
73
|
+
if error.error_number == Mysql2Adapter::ER_UNKNOWN_STMT_HANDLER
|
|
74
|
+
if retry_count.positive?
|
|
75
|
+
retry_count -= 1
|
|
76
|
+
retry
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
raise
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
stmt = raw_connection.prepare(sql)
|
|
53
83
|
|
|
54
84
|
begin
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
result = stmt.execute(*type_casted_binds)
|
|
86
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
|
87
|
+
|
|
88
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
|
89
|
+
# by eagerly closing uncached prepared statements, we also reduce the chances of
|
|
90
|
+
# that bug happening. It can still happen if `#execute` is used as we have no callback
|
|
91
|
+
# to eagerly close the statement.
|
|
92
|
+
if result
|
|
93
|
+
result.instance_variable_set(:@_ar_stmt_to_close, stmt)
|
|
94
|
+
else
|
|
95
|
+
stmt.close
|
|
57
96
|
end
|
|
58
97
|
rescue ::Mysql2::Error
|
|
59
|
-
@statements.delete(sql)
|
|
60
98
|
stmt.close
|
|
61
99
|
raise
|
|
62
100
|
end
|
|
63
|
-
verified!
|
|
64
|
-
else
|
|
65
|
-
raw_connection.query(sql)
|
|
66
101
|
end
|
|
67
102
|
|
|
103
|
+
notification_payload[:affected_rows] = @affected_rows_before_warnings
|
|
68
104
|
notification_payload[:row_count] = result&.size || 0
|
|
69
105
|
|
|
70
|
-
@affected_rows_before_warnings = raw_connection.affected_rows
|
|
71
106
|
raw_connection.abandon_results!
|
|
72
107
|
|
|
73
108
|
verified!
|
|
74
|
-
handle_warnings(sql)
|
|
75
109
|
result
|
|
76
110
|
ensure
|
|
77
111
|
if reset_multi_statement && active?
|
|
@@ -79,17 +113,34 @@ module ActiveRecord
|
|
|
79
113
|
end
|
|
80
114
|
end
|
|
81
115
|
|
|
82
|
-
def cast_result(
|
|
83
|
-
|
|
84
|
-
|
|
116
|
+
def cast_result(raw_result)
|
|
117
|
+
return ActiveRecord::Result.empty(affected_rows: @affected_rows_before_warnings) if raw_result.nil?
|
|
118
|
+
|
|
119
|
+
fields = raw_result.fields
|
|
120
|
+
|
|
121
|
+
result = if fields.empty?
|
|
122
|
+
ActiveRecord::Result.empty(affected_rows: @affected_rows_before_warnings)
|
|
85
123
|
else
|
|
86
|
-
ActiveRecord::Result.new(
|
|
124
|
+
ActiveRecord::Result.new(fields, raw_result.to_a)
|
|
87
125
|
end
|
|
126
|
+
|
|
127
|
+
free_raw_result(raw_result)
|
|
128
|
+
|
|
129
|
+
result
|
|
88
130
|
end
|
|
89
131
|
|
|
90
|
-
def affected_rows(
|
|
132
|
+
def affected_rows(raw_result)
|
|
133
|
+
free_raw_result(raw_result) if raw_result
|
|
134
|
+
|
|
91
135
|
@affected_rows_before_warnings
|
|
92
136
|
end
|
|
137
|
+
|
|
138
|
+
def free_raw_result(raw_result)
|
|
139
|
+
raw_result.free
|
|
140
|
+
if stmt = raw_result.instance_variable_get(:@_ar_stmt_to_close)
|
|
141
|
+
stmt.close
|
|
142
|
+
end
|
|
143
|
+
end
|
|
93
144
|
end
|
|
94
145
|
end
|
|
95
146
|
end
|
|
@@ -13,6 +13,7 @@ module ActiveRecord
|
|
|
13
13
|
ER_BAD_DB_ERROR = 1049
|
|
14
14
|
ER_DBACCESS_DENIED_ERROR = 1044
|
|
15
15
|
ER_ACCESS_DENIED_ERROR = 1045
|
|
16
|
+
ER_UNKNOWN_STMT_HANDLER = 1243
|
|
16
17
|
ER_CONN_HOST_ERROR = 2003
|
|
17
18
|
ER_UNKNOWN_HOST_ERROR = 2005
|
|
18
19
|
|
|
@@ -91,8 +92,6 @@ module ActiveRecord
|
|
|
91
92
|
true
|
|
92
93
|
end
|
|
93
94
|
|
|
94
|
-
# HELPER METHODS ===========================================
|
|
95
|
-
|
|
96
95
|
def error_number(exception)
|
|
97
96
|
exception.error_number if exception.respond_to?(:error_number)
|
|
98
97
|
end
|
|
@@ -106,7 +105,14 @@ module ActiveRecord
|
|
|
106
105
|
end
|
|
107
106
|
|
|
108
107
|
def active?
|
|
109
|
-
connected?
|
|
108
|
+
if connected?
|
|
109
|
+
@lock.synchronize do
|
|
110
|
+
if @raw_connection&.ping
|
|
111
|
+
verified!
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end || false
|
|
110
116
|
end
|
|
111
117
|
|
|
112
118
|
alias :reset! :reconnect!
|
|
@@ -5,9 +5,8 @@ module ActiveRecord
|
|
|
5
5
|
class PoolConfig # :nodoc:
|
|
6
6
|
include MonitorMixin
|
|
7
7
|
|
|
8
|
-
attr_reader :db_config, :role, :shard
|
|
8
|
+
attr_reader :db_config, :role, :shard, :connection_descriptor
|
|
9
9
|
attr_writer :schema_reflection, :server_version
|
|
10
|
-
attr_accessor :connection_class
|
|
11
10
|
|
|
12
11
|
def schema_reflection
|
|
13
12
|
@schema_reflection ||= SchemaReflection.new(db_config.lazy_schema_cache_path)
|
|
@@ -29,7 +28,7 @@ module ActiveRecord
|
|
|
29
28
|
def initialize(connection_class, db_config, role, shard)
|
|
30
29
|
super()
|
|
31
30
|
@server_version = nil
|
|
32
|
-
|
|
31
|
+
self.connection_descriptor = connection_class
|
|
33
32
|
@db_config = db_config
|
|
34
33
|
@role = role
|
|
35
34
|
@shard = shard
|
|
@@ -41,11 +40,12 @@ module ActiveRecord
|
|
|
41
40
|
@server_version || synchronize { @server_version ||= connection.get_database_version }
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
def connection_descriptor=(connection_descriptor)
|
|
44
|
+
case connection_descriptor
|
|
45
|
+
when ConnectionHandler::ConnectionDescriptor
|
|
46
|
+
@connection_descriptor = connection_descriptor
|
|
47
47
|
else
|
|
48
|
-
|
|
48
|
+
@connection_descriptor = ConnectionHandler::ConnectionDescriptor.new(connection_descriptor.name, connection_descriptor.primary_class?)
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -30,6 +30,10 @@ module ActiveRecord
|
|
|
30
30
|
@generated.present?
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def virtual_stored?
|
|
34
|
+
@generated == "s"
|
|
35
|
+
end
|
|
36
|
+
|
|
33
37
|
def has_default?
|
|
34
38
|
super && !virtual?
|
|
35
39
|
end
|
|
@@ -65,7 +69,8 @@ module ActiveRecord
|
|
|
65
69
|
other.is_a?(Column) &&
|
|
66
70
|
super &&
|
|
67
71
|
identity? == other.identity? &&
|
|
68
|
-
serial? == other.serial?
|
|
72
|
+
serial? == other.serial? &&
|
|
73
|
+
virtual? == other.virtual?
|
|
69
74
|
end
|
|
70
75
|
alias :eql? :==
|
|
71
76
|
|
|
@@ -73,7 +78,8 @@ module ActiveRecord
|
|
|
73
78
|
Column.hash ^
|
|
74
79
|
super.hash ^
|
|
75
80
|
identity?.hash ^
|
|
76
|
-
serial?.hash
|
|
81
|
+
serial?.hash ^
|
|
82
|
+
virtual?.hash
|
|
77
83
|
end
|
|
78
84
|
end
|
|
79
85
|
end
|
|
@@ -11,8 +11,8 @@ module ActiveRecord
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# Queries the database and returns the results in an Array-like object
|
|
14
|
-
def query(sql, name = nil) # :nodoc:
|
|
15
|
-
result = internal_execute(sql, name)
|
|
14
|
+
def query(sql, name = nil, allow_retry: true, materialize_transactions: true) # :nodoc:
|
|
15
|
+
result = internal_execute(sql, name, allow_retry:, materialize_transactions:)
|
|
16
16
|
result.map_types!(@type_map_for_results).values
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -27,21 +27,6 @@ module ActiveRecord
|
|
|
27
27
|
!READ_QUERY.match?(sql.b)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
# Executes an SQL statement, returning a PG::Result object on success
|
|
31
|
-
# or raising a PG::Error exception otherwise.
|
|
32
|
-
#
|
|
33
|
-
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
|
34
|
-
# executing the SQL statement in case of a connection-related exception.
|
|
35
|
-
# This option should only be enabled for known idempotent queries.
|
|
36
|
-
#
|
|
37
|
-
# Note: the PG::Result object is manually memory managed; if you don't
|
|
38
|
-
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
|
39
|
-
def execute(...) # :nodoc:
|
|
40
|
-
super
|
|
41
|
-
ensure
|
|
42
|
-
@notice_receiver_sql_warnings = []
|
|
43
|
-
end
|
|
44
|
-
|
|
45
30
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
|
46
31
|
if use_insert_returning? || pk == false
|
|
47
32
|
super
|
|
@@ -127,7 +112,14 @@ module ActiveRecord
|
|
|
127
112
|
def cancel_any_running_query
|
|
128
113
|
return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
|
|
129
114
|
|
|
130
|
-
@raw_connection.cancel
|
|
115
|
+
# Skip @raw_connection.cancel (PG::Connection#cancel) when using libpq >= 18 with pg < 1.6.0,
|
|
116
|
+
# because the pg gem cannot obtain the backend_key in that case.
|
|
117
|
+
# This method is only called from exec_rollback_db_transaction and exec_restart_db_transaction.
|
|
118
|
+
# Even without cancel, rollback will still run. However, since any running
|
|
119
|
+
# query must finish first, the rollback may take longer.
|
|
120
|
+
if !(PG.library_version >= 18_00_00 && Gem::Version.new(PG::VERSION) < Gem::Version.new("1.6.0"))
|
|
121
|
+
@raw_connection.cancel
|
|
122
|
+
end
|
|
131
123
|
@raw_connection.block
|
|
132
124
|
rescue PG::Error
|
|
133
125
|
end
|
|
@@ -163,25 +155,27 @@ module ActiveRecord
|
|
|
163
155
|
end
|
|
164
156
|
|
|
165
157
|
verified!
|
|
166
|
-
|
|
167
|
-
notification_payload[:
|
|
158
|
+
|
|
159
|
+
notification_payload[:affected_rows] = result.cmd_tuples
|
|
160
|
+
notification_payload[:row_count] = result.ntuples
|
|
168
161
|
result
|
|
169
162
|
end
|
|
170
163
|
|
|
171
164
|
def cast_result(result)
|
|
172
|
-
if result.fields.empty?
|
|
173
|
-
result.
|
|
174
|
-
|
|
175
|
-
|
|
165
|
+
ar_result = if result.fields.empty?
|
|
166
|
+
ActiveRecord::Result.empty(affected_rows: result.cmd_tuples)
|
|
167
|
+
else
|
|
168
|
+
fields = result.fields
|
|
169
|
+
types = Array.new(fields.size)
|
|
170
|
+
fields.size.times do |index|
|
|
171
|
+
ftype = result.ftype(index)
|
|
172
|
+
fmod = result.fmod(index)
|
|
173
|
+
types[index] = get_oid_type(ftype, fmod, fields[index])
|
|
174
|
+
end
|
|
176
175
|
|
|
177
|
-
|
|
178
|
-
fields = result.fields
|
|
179
|
-
fields.each_with_index do |fname, i|
|
|
180
|
-
ftype = result.ftype i
|
|
181
|
-
fmod = result.fmod i
|
|
182
|
-
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
|
176
|
+
ActiveRecord::Result.new(fields, result.values, types.freeze, affected_rows: result.cmd_tuples)
|
|
183
177
|
end
|
|
184
|
-
|
|
178
|
+
|
|
185
179
|
result.clear
|
|
186
180
|
ar_result
|
|
187
181
|
end
|
|
@@ -213,8 +207,10 @@ module ActiveRecord
|
|
|
213
207
|
pk unless pk.is_a?(Array)
|
|
214
208
|
end
|
|
215
209
|
|
|
216
|
-
def handle_warnings(sql)
|
|
217
|
-
@notice_receiver_sql_warnings
|
|
210
|
+
def handle_warnings(result, sql)
|
|
211
|
+
warnings, @notice_receiver_sql_warnings = @notice_receiver_sql_warnings, []
|
|
212
|
+
|
|
213
|
+
warnings.each do |warning|
|
|
218
214
|
next if warning_ignored?(warning)
|
|
219
215
|
|
|
220
216
|
warning.sql = sql
|
|
@@ -16,8 +16,8 @@ module ActiveRecord
|
|
|
16
16
|
@subtype = subtype
|
|
17
17
|
@delimiter = delimiter
|
|
18
18
|
|
|
19
|
-
@pg_encoder = PG::TextEncoder::Array.new
|
|
20
|
-
@pg_decoder = PG::TextDecoder::Array.new
|
|
19
|
+
@pg_encoder = PG::TextEncoder::Array.new(name: "#{type}[]".freeze, delimiter: delimiter).freeze
|
|
20
|
+
@pg_decoder = PG::TextDecoder::Array.new(name: "#{type}[]".freeze, delimiter: delimiter).freeze
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def deserialize(value)
|
|
@@ -14,7 +14,10 @@ module ActiveRecord
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def type_cast_for_schema(value)
|
|
17
|
-
value.
|
|
17
|
+
from = bound_for_schema(value.begin)
|
|
18
|
+
to = bound_for_schema(value.end)
|
|
19
|
+
op = value.exclude_end? ? "..." : ".."
|
|
20
|
+
"#{from}#{op}#{to}"
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
def cast_value(value)
|
|
@@ -58,6 +61,19 @@ module ActiveRecord
|
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
private
|
|
64
|
+
def bound_for_schema(bound)
|
|
65
|
+
case bound
|
|
66
|
+
when nil
|
|
67
|
+
"nil"
|
|
68
|
+
when ::Float::INFINITY
|
|
69
|
+
"::Float::INFINITY"
|
|
70
|
+
when -::Float::INFINITY
|
|
71
|
+
"-::Float::INFINITY"
|
|
72
|
+
else
|
|
73
|
+
@subtype.type_cast_for_schema(bound)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
61
77
|
def type_cast_single(value)
|
|
62
78
|
infinity?(value) ? value : @subtype.deserialize(value)
|
|
63
79
|
end
|
|
@@ -67,7 +83,7 @@ module ActiveRecord
|
|
|
67
83
|
end
|
|
68
84
|
|
|
69
85
|
def extract_bounds(value)
|
|
70
|
-
from, to = value[1..-2]
|
|
86
|
+
from, to = split_bounds(value[1..-2])
|
|
71
87
|
{
|
|
72
88
|
from: (from == "" || from == "-infinity") ? infinity(negative: true) : unquote(from),
|
|
73
89
|
to: (to == "" || to == "infinity") ? infinity : unquote(to),
|
|
@@ -76,6 +92,33 @@ module ActiveRecord
|
|
|
76
92
|
}
|
|
77
93
|
end
|
|
78
94
|
|
|
95
|
+
# Matches the comma-separated lower and upper bounds of a range's
|
|
96
|
+
# textual representation when a bound is double-quoted. A quoted
|
|
97
|
+
# bound can itself contain a comma, so a naive split on the first
|
|
98
|
+
# comma would corrupt such values. Within a double-quoted bound,
|
|
99
|
+
# literal " and \ are escaped (as "" / \" and \\ respectively), so
|
|
100
|
+
# those escapes are skipped while scanning for the separating comma.
|
|
101
|
+
#
|
|
102
|
+
# An unquoted bound never contains a comma or a double-quote
|
|
103
|
+
# (PostgreSQL quotes the whole bound when it would), so the
|
|
104
|
+
# alternation matches an unquoted run with [^,"] rather than [^,].
|
|
105
|
+
# Excluding " keeps the two alternatives mutually exclusive, which
|
|
106
|
+
# removes any ambiguity over how a "-prefixed bound is consumed. The
|
|
107
|
+
# /m flag lets the captured upper bound (and quoted bounds) span
|
|
108
|
+
# newlines.
|
|
109
|
+
BOUNDS = /\A((?:"(?:[^"\\]|""|\\.)*"|[^,"])*),(.*)\z/m # :nodoc:
|
|
110
|
+
|
|
111
|
+
def split_bounds(value)
|
|
112
|
+
# Fast path: an unquoted representation (every built-in range
|
|
113
|
+
# type -- int/num/date/timestamp) has no embedded comma, so a
|
|
114
|
+
# plain split is correct and avoids the regexp. Only quoted
|
|
115
|
+
# bounds (custom text/varchar/money/... ranges) need the
|
|
116
|
+
# comma-skipping scan.
|
|
117
|
+
return value.split(",", 2) unless value.include?('"')
|
|
118
|
+
|
|
119
|
+
(match = BOUNDS.match(value)) ? [match[1], match[2]] : [value, nil]
|
|
120
|
+
end
|
|
121
|
+
|
|
79
122
|
INFINITE_FLOAT_RANGE = (-::Float::INFINITY)..(::Float::INFINITY) # :nodoc:
|
|
80
123
|
|
|
81
124
|
def sanitize_bounds(from, to)
|
|
@@ -94,7 +137,7 @@ module ActiveRecord
|
|
|
94
137
|
# * https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO
|
|
95
138
|
# * https://www.postgresql.org/docs/current/rowtypes.html#ROWTYPES-IO-SYNTAX
|
|
96
139
|
def unquote(value)
|
|
97
|
-
if value.start_with?('"') && value.end_with?('"')
|
|
140
|
+
if value && value.start_with?('"') && value.end_with?('"')
|
|
98
141
|
unquoted_value = value[1..-2]
|
|
99
142
|
unquoted_value.gsub!('""', '"')
|
|
100
143
|
unquoted_value.gsub!("\\\\", "\\")
|
|
@@ -153,14 +153,15 @@ module ActiveRecord
|
|
|
153
153
|
"'#{escape_bytea(value.to_s)}'"
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
+
# `column` may be either an instance of Column or ColumnDefinition.
|
|
156
157
|
def quote_default_expression(value, column) # :nodoc:
|
|
157
158
|
if value.is_a?(Proc)
|
|
158
159
|
value.call
|
|
159
160
|
elsif column.type == :uuid && value.is_a?(String) && value.include?("()")
|
|
160
161
|
value # Does not quote function default values for UUID columns
|
|
161
162
|
elsif column.respond_to?(:array?)
|
|
162
|
-
|
|
163
|
-
quote(
|
|
163
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
164
|
+
quote(column.fetch_cast_type(self).serialize(value))
|
|
164
165
|
else
|
|
165
166
|
super
|
|
166
167
|
end
|
|
@@ -186,16 +187,12 @@ module ActiveRecord
|
|
|
186
187
|
end
|
|
187
188
|
end
|
|
188
189
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
# TODO: Make this method private after we release 8.1.
|
|
191
|
+
def lookup_cast_type(sql_type) # :nodoc:
|
|
192
|
+
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
|
192
193
|
end
|
|
193
194
|
|
|
194
195
|
private
|
|
195
|
-
def lookup_cast_type(sql_type)
|
|
196
|
-
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
|
197
|
-
end
|
|
198
|
-
|
|
199
196
|
def encode_array(array_data)
|
|
200
197
|
encoder = array_data.encoder
|
|
201
198
|
values = type_cast_array(array_data.values)
|
|
@@ -208,7 +205,17 @@ module ActiveRecord
|
|
|
208
205
|
end
|
|
209
206
|
|
|
210
207
|
def encode_range(range)
|
|
211
|
-
|
|
208
|
+
lower_bound = type_cast_range_value(range.begin)
|
|
209
|
+
upper_bound = if date_or_time_range?(range)
|
|
210
|
+
# Postgres will convert `[today,]` to `[today,)`, making it exclusive.
|
|
211
|
+
# We can use the special timestamp value `infinity` to force inclusion.
|
|
212
|
+
# https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-INFINITE
|
|
213
|
+
range.end.nil? ? "infinity" : type_cast(range.end)
|
|
214
|
+
else
|
|
215
|
+
type_cast_range_value(range.end)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
"[#{lower_bound},#{upper_bound}#{range.exclude_end? ? ')' : ']'}"
|
|
212
219
|
end
|
|
213
220
|
|
|
214
221
|
def determine_encoding_of_strings_in_array(value)
|
|
@@ -232,6 +239,10 @@ module ActiveRecord
|
|
|
232
239
|
def infinity?(value)
|
|
233
240
|
value.respond_to?(:infinite?) && value.infinite?
|
|
234
241
|
end
|
|
242
|
+
|
|
243
|
+
def date_or_time_range?(range)
|
|
244
|
+
[range.begin.class, range.end.class].intersect?([Date, DateTime, Time])
|
|
245
|
+
end
|
|
235
246
|
end
|
|
236
247
|
end
|
|
237
248
|
end
|
|
@@ -45,7 +45,7 @@ Rails needs superuser privileges to disable referential integrity.
|
|
|
45
45
|
BEGIN
|
|
46
46
|
FOR r IN (
|
|
47
47
|
SELECT FORMAT(
|
|
48
|
-
'UPDATE pg_catalog.pg_constraint SET convalidated=false WHERE conname = ''%1$I'' AND connamespace::regnamespace = ''%2$I''::regnamespace; ALTER TABLE %2$I.%3$I VALIDATE CONSTRAINT %1$I;',
|
|
48
|
+
'UPDATE pg_catalog.pg_constraint SET convalidated=false WHERE conname = ''%1$I'' AND connamespace::regnamespace = ''%2$I''::regnamespace AND conrelid::regclass = ''%3$I''::regclass; ALTER TABLE %2$I.%3$I VALIDATE CONSTRAINT %1$I;',
|
|
49
49
|
constraint_name,
|
|
50
50
|
table_schema,
|
|
51
51
|
table_name
|
|
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
|
6
6
|
class SchemaCreation < SchemaCreation # :nodoc:
|
|
7
7
|
private
|
|
8
8
|
delegate :quoted_include_columns_for_index, to: :@conn
|
|
9
|
+
delegate :database_version, to: :@conn
|
|
9
10
|
|
|
10
11
|
def visit_AlterTable(o)
|
|
11
12
|
sql = super
|
|
@@ -99,7 +100,7 @@ module ActiveRecord
|
|
|
99
100
|
if options[:default].nil?
|
|
100
101
|
change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
|
|
101
102
|
else
|
|
102
|
-
quoted_default =
|
|
103
|
+
quoted_default = quote_default_expression_for_column_definition(options[:default], column)
|
|
103
104
|
change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
|
|
104
105
|
end
|
|
105
106
|
end
|
|
@@ -126,16 +127,17 @@ module ActiveRecord
|
|
|
126
127
|
end
|
|
127
128
|
|
|
128
129
|
if as = options[:as]
|
|
129
|
-
|
|
130
|
+
stored = options[:stored]
|
|
130
131
|
|
|
131
|
-
if
|
|
132
|
-
sql << " STORED"
|
|
133
|
-
else
|
|
132
|
+
if stored != true && database_version < 18_00_00
|
|
134
133
|
raise ArgumentError, <<~MSG
|
|
135
|
-
PostgreSQL
|
|
134
|
+
PostgreSQL versions before 18 do not support VIRTUAL (not persisted) generated columns.
|
|
136
135
|
Specify 'stored: true' option for '#{options[:column].name}'
|
|
137
136
|
MSG
|
|
138
137
|
end
|
|
138
|
+
|
|
139
|
+
sql << " GENERATED ALWAYS AS (#{as})"
|
|
140
|
+
sql << (stored ? " STORED" : " VIRTUAL")
|
|
139
141
|
end
|
|
140
142
|
super
|
|
141
143
|
end
|
|
@@ -5,6 +5,7 @@ module ActiveRecord
|
|
|
5
5
|
module PostgreSQL
|
|
6
6
|
module ColumnMethods
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
|
+
extend ConnectionAdapters::ColumnMethods::ClassMethods
|
|
8
9
|
|
|
9
10
|
# Defines the primary key field.
|
|
10
11
|
# Use of the native PostgreSQL UUID type is supported, and can be used
|
|
@@ -15,22 +16,10 @@ module ActiveRecord
|
|
|
15
16
|
# t.timestamps
|
|
16
17
|
# end
|
|
17
18
|
#
|
|
18
|
-
# By default, this will use the <tt>gen_random_uuid()</tt> function
|
|
19
|
-
# +pgcrypto+ extension. As that extension is only available in
|
|
20
|
-
# PostgreSQL 9.4+, for earlier versions an explicit default can be set
|
|
21
|
-
# to use <tt>uuid_generate_v4()</tt> from the +uuid-ossp+ extension instead:
|
|
19
|
+
# By default, this will use the <tt>gen_random_uuid()</tt> function.
|
|
22
20
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# t.uuid :foo_id
|
|
26
|
-
# t.timestamps
|
|
27
|
-
# end
|
|
28
|
-
#
|
|
29
|
-
# To enable the appropriate extension, which is a requirement, use
|
|
30
|
-
# the +enable_extension+ method in your migrations.
|
|
31
|
-
#
|
|
32
|
-
# To use a UUID primary key without any of the extensions, set the
|
|
33
|
-
# +:default+ option to +nil+:
|
|
21
|
+
# To use a UUID primary key without any defaults, set the +:default+
|
|
22
|
+
# option to +nil+:
|
|
34
23
|
#
|
|
35
24
|
# create_table :stuffs, id: false do |t|
|
|
36
25
|
# t.primary_key :id, :uuid, default: nil
|
|
@@ -181,12 +170,10 @@ module ActiveRecord
|
|
|
181
170
|
# :method: enum
|
|
182
171
|
# :call-seq: enum(*names, **options)
|
|
183
172
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
|
|
189
|
-
end
|
|
173
|
+
define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
|
|
174
|
+
:hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
|
|
175
|
+
:money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
|
|
176
|
+
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
|
|
190
177
|
end
|
|
191
178
|
|
|
192
179
|
ExclusionConstraintDefinition = Struct.new(:table_name, :expression, :options) do
|
|
@@ -233,6 +220,8 @@ module ActiveRecord
|
|
|
233
220
|
end
|
|
234
221
|
|
|
235
222
|
def defined_for?(name: nil, column: nil, **options)
|
|
223
|
+
options = options.slice(*self.options.keys)
|
|
224
|
+
|
|
236
225
|
(name.nil? || self.name == name.to_s) &&
|
|
237
226
|
(column.nil? || Array(self.column) == Array(column).map(&:to_s)) &&
|
|
238
227
|
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
|
@@ -306,8 +295,8 @@ module ActiveRecord
|
|
|
306
295
|
# t.exclusion_constraint("price WITH =, availability_range WITH &&", using: :gist, name: "price_check")
|
|
307
296
|
#
|
|
308
297
|
# See {connection.add_exclusion_constraint}[rdoc-ref:SchemaStatements#add_exclusion_constraint]
|
|
309
|
-
def exclusion_constraint(
|
|
310
|
-
@base.add_exclusion_constraint(name,
|
|
298
|
+
def exclusion_constraint(...)
|
|
299
|
+
@base.add_exclusion_constraint(name, ...)
|
|
311
300
|
end
|
|
312
301
|
|
|
313
302
|
# Removes the given exclusion constraint from the table.
|
|
@@ -315,8 +304,8 @@ module ActiveRecord
|
|
|
315
304
|
# t.remove_exclusion_constraint(name: "price_check")
|
|
316
305
|
#
|
|
317
306
|
# See {connection.remove_exclusion_constraint}[rdoc-ref:SchemaStatements#remove_exclusion_constraint]
|
|
318
|
-
def remove_exclusion_constraint(
|
|
319
|
-
@base.remove_exclusion_constraint(name,
|
|
307
|
+
def remove_exclusion_constraint(...)
|
|
308
|
+
@base.remove_exclusion_constraint(name, ...)
|
|
320
309
|
end
|
|
321
310
|
|
|
322
311
|
# Adds a unique constraint.
|
|
@@ -324,8 +313,8 @@ module ActiveRecord
|
|
|
324
313
|
# t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred, nulls_not_distinct: true)
|
|
325
314
|
#
|
|
326
315
|
# See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
|
|
327
|
-
def unique_constraint(
|
|
328
|
-
@base.add_unique_constraint(name,
|
|
316
|
+
def unique_constraint(...)
|
|
317
|
+
@base.add_unique_constraint(name, ...)
|
|
329
318
|
end
|
|
330
319
|
|
|
331
320
|
# Removes the given unique constraint from the table.
|
|
@@ -333,8 +322,8 @@ module ActiveRecord
|
|
|
333
322
|
# t.remove_unique_constraint(name: "unique_position")
|
|
334
323
|
#
|
|
335
324
|
# See {connection.remove_unique_constraint}[rdoc-ref:SchemaStatements#remove_unique_constraint]
|
|
336
|
-
def remove_unique_constraint(
|
|
337
|
-
@base.remove_unique_constraint(name,
|
|
325
|
+
def remove_unique_constraint(...)
|
|
326
|
+
@base.remove_unique_constraint(name, ...)
|
|
338
327
|
end
|
|
339
328
|
|
|
340
329
|
# Validates the given constraint on the table.
|
|
@@ -343,8 +332,8 @@ module ActiveRecord
|
|
|
343
332
|
# t.validate_constraint "price_check"
|
|
344
333
|
#
|
|
345
334
|
# See {connection.validate_constraint}[rdoc-ref:SchemaStatements#validate_constraint]
|
|
346
|
-
def validate_constraint(
|
|
347
|
-
@base.validate_constraint(name,
|
|
335
|
+
def validate_constraint(...)
|
|
336
|
+
@base.validate_constraint(name, ...)
|
|
348
337
|
end
|
|
349
338
|
|
|
350
339
|
# Validates the given check constraint on the table
|
|
@@ -353,8 +342,8 @@ module ActiveRecord
|
|
|
353
342
|
# t.validate_check_constraint name: "price_check"
|
|
354
343
|
#
|
|
355
344
|
# See {connection.validate_check_constraint}[rdoc-ref:SchemaStatements#validate_check_constraint]
|
|
356
|
-
def validate_check_constraint(
|
|
357
|
-
@base.validate_check_constraint(name,
|
|
345
|
+
def validate_check_constraint(...)
|
|
346
|
+
@base.validate_check_constraint(name, ...)
|
|
358
347
|
end
|
|
359
348
|
end
|
|
360
349
|
|