activerecord 7.1.5.2 → 8.1.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 +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- 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 +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- 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 +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- 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 +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- 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/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
data/lib/active_record/errors.rb
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
module ActiveRecord
|
|
5
|
+
include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
|
6
|
+
|
|
4
7
|
# = Active Record Errors
|
|
5
8
|
#
|
|
6
9
|
# Generic Active Record exception class.
|
|
7
10
|
class ActiveRecordError < StandardError
|
|
8
11
|
end
|
|
9
12
|
|
|
10
|
-
# DEPRECATED: Previously raised when trying to use a feature in Active Record which
|
|
11
|
-
# requires Active Job but the gem is not present. Now raises a NameError.
|
|
12
|
-
include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
|
13
|
-
DeprecatedActiveJobRequiredError = Class.new(ActiveRecordError) # :nodoc:
|
|
14
|
-
deprecate_constant "ActiveJobRequiredError", "ActiveRecord::DeprecatedActiveJobRequiredError",
|
|
15
|
-
message: "ActiveRecord::ActiveJobRequiredError has been deprecated. If Active Job is not present, a NameError will be raised instead.",
|
|
16
|
-
deprecator: ActiveRecord.deprecator
|
|
17
|
-
|
|
18
13
|
# Raised when the single-table inheritance mechanism fails to locate the subclass
|
|
19
14
|
# (for example due to improper usage of column that
|
|
20
|
-
# {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema
|
|
15
|
+
# {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema.inheritance_column]
|
|
21
16
|
# points to).
|
|
22
17
|
class SubclassNotFound < ActiveRecordError
|
|
23
18
|
end
|
|
@@ -66,7 +61,7 @@ module ActiveRecord
|
|
|
66
61
|
end
|
|
67
62
|
|
|
68
63
|
# Raised when connection to the database could not been established (for example when
|
|
69
|
-
# {ActiveRecord::Base.
|
|
64
|
+
# {ActiveRecord::Base.lease_connection=}[rdoc-ref:ConnectionHandling#lease_connection]
|
|
70
65
|
# is given a +nil+ object).
|
|
71
66
|
class ConnectionNotEstablished < AdapterError
|
|
72
67
|
def initialize(message = nil, connection_pool: nil)
|
|
@@ -88,6 +83,19 @@ module ActiveRecord
|
|
|
88
83
|
class ConnectionTimeoutError < ConnectionNotEstablished
|
|
89
84
|
end
|
|
90
85
|
|
|
86
|
+
# Raised when a database connection pool is requested but
|
|
87
|
+
# has not been defined.
|
|
88
|
+
class ConnectionNotDefined < ConnectionNotEstablished
|
|
89
|
+
def initialize(message = nil, connection_name: nil, role: nil, shard: nil)
|
|
90
|
+
super(message)
|
|
91
|
+
@connection_name = connection_name
|
|
92
|
+
@role = role
|
|
93
|
+
@shard = shard
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
attr_reader :connection_name, :role, :shard
|
|
97
|
+
end
|
|
98
|
+
|
|
91
99
|
# Raised when connection to the database could not been established because it was not
|
|
92
100
|
# able to connect to the host or when the authorization failed.
|
|
93
101
|
class DatabaseConnectionError < ConnectionNotEstablished
|
|
@@ -137,8 +145,18 @@ module ActiveRecord
|
|
|
137
145
|
end
|
|
138
146
|
|
|
139
147
|
# Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
|
|
140
|
-
# {ActiveRecord::Base.
|
|
141
|
-
# methods when a record
|
|
148
|
+
# {ActiveRecord::Base.update_attribute!}[rdoc-ref:Persistence#update_attribute!]
|
|
149
|
+
# methods when a record failed to validate or cannot be saved due to any of the
|
|
150
|
+
# <tt>before_*</tt> callbacks throwing +:abort+. See
|
|
151
|
+
# ActiveRecord::Callbacks for further details.
|
|
152
|
+
#
|
|
153
|
+
# class Product < ActiveRecord::Base
|
|
154
|
+
# before_save do
|
|
155
|
+
# throw :abort if price < 0
|
|
156
|
+
# end
|
|
157
|
+
# end
|
|
158
|
+
#
|
|
159
|
+
# Product.create! # => raises an ActiveRecord::RecordNotSaved
|
|
142
160
|
class RecordNotSaved < ActiveRecordError
|
|
143
161
|
attr_reader :record
|
|
144
162
|
|
|
@@ -149,15 +167,17 @@ module ActiveRecord
|
|
|
149
167
|
end
|
|
150
168
|
|
|
151
169
|
# Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
|
|
152
|
-
# when a
|
|
153
|
-
#
|
|
170
|
+
# when a record cannot be destroyed due to any of the
|
|
171
|
+
# <tt>before_destroy</tt> callbacks throwing +:abort+. See
|
|
172
|
+
# ActiveRecord::Callbacks for further details.
|
|
154
173
|
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
174
|
+
# class User < ActiveRecord::Base
|
|
175
|
+
# before_destroy do
|
|
176
|
+
# throw :abort if still_active?
|
|
177
|
+
# end
|
|
159
178
|
# end
|
|
160
179
|
#
|
|
180
|
+
# User.first.destroy! # => raises an ActiveRecord::RecordNotDestroyed
|
|
161
181
|
class RecordNotDestroyed < ActiveRecordError
|
|
162
182
|
attr_reader :record
|
|
163
183
|
|
|
@@ -272,6 +292,14 @@ module ActiveRecord
|
|
|
272
292
|
class NotNullViolation < StatementInvalid
|
|
273
293
|
end
|
|
274
294
|
|
|
295
|
+
# Raised when a record cannot be inserted or updated because it would violate a check constraint.
|
|
296
|
+
class CheckViolation < StatementInvalid
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Raised when a record cannot be inserted or updated because it would violate an exclusion constraint.
|
|
300
|
+
class ExclusionViolation < StatementInvalid
|
|
301
|
+
end
|
|
302
|
+
|
|
275
303
|
# Raised when a record cannot be inserted or updated because a value too long for a column type.
|
|
276
304
|
class ValueTooLong < StatementInvalid
|
|
277
305
|
end
|
|
@@ -318,15 +346,15 @@ module ActiveRecord
|
|
|
318
346
|
class << self
|
|
319
347
|
def db_error(db_name)
|
|
320
348
|
NoDatabaseError.new(<<~MSG)
|
|
321
|
-
|
|
349
|
+
Database not found: #{db_name}. Available database configurations can be found in config/database.yml.
|
|
322
350
|
|
|
323
351
|
To resolve this error:
|
|
324
352
|
|
|
325
|
-
-
|
|
353
|
+
- Create the database by running:
|
|
326
354
|
|
|
327
355
|
bin/rails db:create
|
|
328
356
|
|
|
329
|
-
-
|
|
357
|
+
- Verify that config/database.yml contains the correct database name.
|
|
330
358
|
MSG
|
|
331
359
|
end
|
|
332
360
|
end
|
|
@@ -374,6 +402,12 @@ module ActiveRecord
|
|
|
374
402
|
end
|
|
375
403
|
|
|
376
404
|
# Raised on attempt to lazily load records that are marked as strict loading.
|
|
405
|
+
#
|
|
406
|
+
# You can resolve this error by eager loading marked records before accessing
|
|
407
|
+
# them. The
|
|
408
|
+
# {Eager Loading Associations}[https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations]
|
|
409
|
+
# guide covers solutions, such as using
|
|
410
|
+
# {ActiveRecord::Base.includes}[rdoc-ref:QueryMethods#includes].
|
|
377
411
|
class StrictLoadingViolationError < ActiveRecordError
|
|
378
412
|
end
|
|
379
413
|
|
|
@@ -417,7 +451,7 @@ module ActiveRecord
|
|
|
417
451
|
UnknownAttributeError = ActiveModel::UnknownAttributeError
|
|
418
452
|
|
|
419
453
|
# Raised when an error occurred while doing a mass assignment to an attribute through the
|
|
420
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
|
454
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
|
|
421
455
|
# The exception has an +attribute+ property that is the name of the offending attribute.
|
|
422
456
|
class AttributeAssignmentError < ActiveRecordError
|
|
423
457
|
attr_reader :exception, :attribute
|
|
@@ -430,7 +464,7 @@ module ActiveRecord
|
|
|
430
464
|
end
|
|
431
465
|
|
|
432
466
|
# Raised when there are multiple errors while doing a mass assignment through the
|
|
433
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
|
|
467
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=]
|
|
434
468
|
# method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
|
|
435
469
|
# objects, each corresponding to the error while assigning to an attribute.
|
|
436
470
|
class MultiparameterAssignmentErrors < ActiveRecordError
|
|
@@ -463,12 +497,13 @@ module ActiveRecord
|
|
|
463
497
|
# end
|
|
464
498
|
#
|
|
465
499
|
# relation = Task.all
|
|
500
|
+
# relation.load
|
|
466
501
|
# relation.loaded? # => true
|
|
467
502
|
#
|
|
468
503
|
# # Methods which try to mutate a loaded relation fail.
|
|
469
|
-
# relation.where!(title: 'TODO') # => ActiveRecord::
|
|
470
|
-
# relation.limit!(5) # => ActiveRecord::
|
|
471
|
-
class
|
|
504
|
+
# relation.where!(title: 'TODO') # => ActiveRecord::UnmodifiableRelation
|
|
505
|
+
# relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
|
|
506
|
+
class UnmodifiableRelation < ActiveRecordError
|
|
472
507
|
end
|
|
473
508
|
|
|
474
509
|
# TransactionIsolationError will be raised under the following conditions:
|
|
@@ -525,6 +560,11 @@ module ActiveRecord
|
|
|
525
560
|
class Deadlocked < TransactionRollbackError
|
|
526
561
|
end
|
|
527
562
|
|
|
563
|
+
# MissingRequiredOrderError is raised when a relation requires ordering but
|
|
564
|
+
# lacks any +order+ values in scope or any model order columns to use.
|
|
565
|
+
class MissingRequiredOrderError < ActiveRecordError
|
|
566
|
+
end
|
|
567
|
+
|
|
528
568
|
# IrreversibleOrderError is raised when a relation's order is too complex for
|
|
529
569
|
# +reverse_order+ to automatically reverse.
|
|
530
570
|
class IrreversibleOrderError < ActiveRecordError
|
|
@@ -577,4 +617,14 @@ module ActiveRecord
|
|
|
577
617
|
# values, such as request parameters or model attributes to query methods.
|
|
578
618
|
class UnknownAttributeReference < ActiveRecordError
|
|
579
619
|
end
|
|
620
|
+
|
|
621
|
+
# DatabaseVersionError will be raised when the database version is not supported, or when
|
|
622
|
+
# the database version cannot be determined.
|
|
623
|
+
class DatabaseVersionError < ActiveRecordError
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
class DeprecatedAssociationError < ActiveRecordError
|
|
627
|
+
end
|
|
580
628
|
end
|
|
629
|
+
|
|
630
|
+
require "active_record/associations/errors"
|
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
# Executes the block with the collect flag enabled. Queries are collected
|
|
8
8
|
# asynchronously by the subscriber and returned.
|
|
9
9
|
def collecting_queries_for_explain # :nodoc:
|
|
10
|
-
ExplainRegistry.
|
|
10
|
+
ExplainRegistry.start
|
|
11
11
|
yield
|
|
12
12
|
ExplainRegistry.queries
|
|
13
13
|
ensure
|
|
@@ -17,16 +17,17 @@ module ActiveRecord
|
|
|
17
17
|
# Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
|
|
18
18
|
# Returns a formatted string ready to be logged.
|
|
19
19
|
def exec_explain(queries, options = []) # :nodoc:
|
|
20
|
-
str =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
str = with_connection do |c|
|
|
21
|
+
queries.map do |sql, binds|
|
|
22
|
+
msg = +"#{build_explain_clause(c, options)} #{sql}"
|
|
23
|
+
unless binds.empty?
|
|
24
|
+
msg << " "
|
|
25
|
+
msg << binds.map { |attr| render_bind(c, attr) }.inspect
|
|
26
|
+
end
|
|
27
|
+
msg << "\n"
|
|
28
|
+
msg << c.explain(sql, binds, options)
|
|
29
|
+
end.join("\n")
|
|
30
|
+
end
|
|
30
31
|
# Overriding inspect to be more human readable, especially in the console.
|
|
31
32
|
def str.inspect
|
|
32
33
|
self
|
|
@@ -36,7 +37,7 @@ module ActiveRecord
|
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
private
|
|
39
|
-
def render_bind(attr)
|
|
40
|
+
def render_bind(connection, attr)
|
|
40
41
|
if ActiveModel::Attribute === attr
|
|
41
42
|
value = if attr.type.binary? && attr.value
|
|
42
43
|
"<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
|
@@ -51,24 +52,12 @@ module ActiveRecord
|
|
|
51
52
|
[attr&.name, value]
|
|
52
53
|
end
|
|
53
54
|
|
|
54
|
-
def build_explain_clause(options = [])
|
|
55
|
+
def build_explain_clause(connection, options = [])
|
|
55
56
|
if connection.respond_to?(:build_explain_clause, true)
|
|
56
57
|
connection.build_explain_clause(options)
|
|
57
58
|
else
|
|
58
59
|
"EXPLAIN for:"
|
|
59
60
|
end
|
|
60
61
|
end
|
|
61
|
-
|
|
62
|
-
def connection_explain(sql, binds, options)
|
|
63
|
-
if connection.method(:explain).parameters.size == 2
|
|
64
|
-
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
|
65
|
-
The current database adapter, #{connection.adapter_name}, does not support explain options.
|
|
66
|
-
To remove this warning, the adapter must implement `build_explain_clause(options = [])`.
|
|
67
|
-
MSG
|
|
68
|
-
connection.explain(sql, binds)
|
|
69
|
-
else
|
|
70
|
-
connection.explain(sql, binds, options)
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
62
|
end
|
|
74
63
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_support/core_ext/module/delegation"
|
|
4
3
|
|
|
5
4
|
module ActiveRecord
|
|
6
5
|
# This is a thread locals registry for EXPLAIN. For example
|
|
@@ -9,8 +8,53 @@ module ActiveRecord
|
|
|
9
8
|
#
|
|
10
9
|
# returns the collected queries local to the current thread.
|
|
11
10
|
class ExplainRegistry # :nodoc:
|
|
11
|
+
class Subscriber
|
|
12
|
+
MUTEX = Mutex.new
|
|
13
|
+
@subscribed = false
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
def ensure_subscribed
|
|
17
|
+
return if @subscribed
|
|
18
|
+
MUTEX.synchronize do
|
|
19
|
+
return if @subscribed
|
|
20
|
+
|
|
21
|
+
ActiveSupport::Notifications.subscribe("sql.active_record", new)
|
|
22
|
+
@subscribed = true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def start(name, id, payload)
|
|
28
|
+
# unused
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def finish(name, id, payload)
|
|
32
|
+
if ExplainRegistry.collect? && !ignore_payload?(payload)
|
|
33
|
+
ExplainRegistry.queries << payload.values_at(:sql, :binds)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def silenced?(_name)
|
|
38
|
+
!ExplainRegistry.collect?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
|
|
42
|
+
# our own EXPLAINs no matter how loopingly beautiful that would be.
|
|
43
|
+
#
|
|
44
|
+
# On the other hand, we want to monitor the performance of our real database
|
|
45
|
+
# queries, not the performance of the access to the query cache.
|
|
46
|
+
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
|
|
47
|
+
EXPLAINED_SQLS = /\A\s*(\/\*.*\*\/)?\s*(with|select|update|delete|insert)\b/i
|
|
48
|
+
def ignore_payload?(payload)
|
|
49
|
+
payload[:exception] ||
|
|
50
|
+
payload[:cached] ||
|
|
51
|
+
IGNORED_PAYLOADS.include?(payload[:name]) ||
|
|
52
|
+
!payload[:sql].match?(EXPLAINED_SQLS)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
12
56
|
class << self
|
|
13
|
-
delegate :reset, :collect, :collect=, :collect?, :queries, to: :instance
|
|
57
|
+
delegate :start, :reset, :collect, :collect=, :collect?, :queries, to: :instance
|
|
14
58
|
|
|
15
59
|
private
|
|
16
60
|
def instance
|
|
@@ -25,6 +69,11 @@ module ActiveRecord
|
|
|
25
69
|
reset
|
|
26
70
|
end
|
|
27
71
|
|
|
72
|
+
def start
|
|
73
|
+
Subscriber.ensure_subscribed
|
|
74
|
+
@collect = true
|
|
75
|
+
end
|
|
76
|
+
|
|
28
77
|
def collect?
|
|
29
78
|
@collect
|
|
30
79
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class FilterAttributeHandler # :nodoc:
|
|
5
|
+
class << self
|
|
6
|
+
def on_sensitive_attribute_declared(&block)
|
|
7
|
+
@sensitive_attribute_declaration_listeners ||= Concurrent::Array.new
|
|
8
|
+
@sensitive_attribute_declaration_listeners << block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def sensitive_attribute_was_declared(klass, list)
|
|
12
|
+
@sensitive_attribute_declaration_listeners&.each do |block|
|
|
13
|
+
block.call(klass, list)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(app)
|
|
19
|
+
@app = app
|
|
20
|
+
@attributes_by_class = Concurrent::Map.new
|
|
21
|
+
@collecting = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def enable
|
|
25
|
+
install_collecting_hook
|
|
26
|
+
|
|
27
|
+
apply_collected_attributes
|
|
28
|
+
@collecting = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
attr_reader :app
|
|
33
|
+
|
|
34
|
+
def install_collecting_hook
|
|
35
|
+
self.class.on_sensitive_attribute_declared do |klass, list|
|
|
36
|
+
attribute_was_declared(klass, list)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def attribute_was_declared(klass, list)
|
|
41
|
+
if collecting?
|
|
42
|
+
collect_for_later(klass, list)
|
|
43
|
+
else
|
|
44
|
+
apply_filter(klass, list)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def apply_collected_attributes
|
|
49
|
+
@attributes_by_class.each do |klass, list|
|
|
50
|
+
apply_filter(klass, list)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def collecting?
|
|
55
|
+
@collecting
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def collect_for_later(klass, list)
|
|
59
|
+
@attributes_by_class[klass] ||= Concurrent::Array.new
|
|
60
|
+
@attributes_by_class[klass] += list
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def apply_filter(klass, list)
|
|
64
|
+
list.each do |attribute|
|
|
65
|
+
next if klass.abstract_class? || klass == Base
|
|
66
|
+
|
|
67
|
+
klass_name = klass.name ? klass.model_name.element : nil
|
|
68
|
+
filter = [klass_name, attribute.to_s].compact.join(".")
|
|
69
|
+
app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -193,8 +193,25 @@ module ActiveRecord
|
|
|
193
193
|
|
|
194
194
|
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
|
195
195
|
joins = targets.map do |target|
|
|
196
|
-
join = {
|
|
197
|
-
|
|
196
|
+
join = {}
|
|
197
|
+
|
|
198
|
+
if rhs_key.is_a?(Array)
|
|
199
|
+
composite_key = ActiveRecord::FixtureSet.composite_identify(target, rhs_key)
|
|
200
|
+
composite_key.each do |column, value|
|
|
201
|
+
join[column] = value
|
|
202
|
+
end
|
|
203
|
+
else
|
|
204
|
+
join[rhs_key] = ActiveRecord::FixtureSet.identify(target, column_type)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
if lhs_key.is_a?(Array)
|
|
208
|
+
lhs_key.zip(model_metadata.primary_key_name).each do |fkey, pkey|
|
|
209
|
+
join[fkey] = @row[pkey]
|
|
210
|
+
end
|
|
211
|
+
else
|
|
212
|
+
join[lhs_key] = @row[model_metadata.primary_key_name]
|
|
213
|
+
end
|
|
214
|
+
|
|
198
215
|
association.timestamp_column_names.each do |col|
|
|
199
216
|
join[col] = @now
|
|
200
217
|
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "erb"
|
|
4
4
|
require "yaml"
|
|
5
|
-
require "zlib"
|
|
6
|
-
require "set"
|
|
7
5
|
require "active_support/dependencies"
|
|
8
6
|
require "active_support/core_ext/digest/uuid"
|
|
9
7
|
require "active_record/test_fixtures"
|
|
@@ -110,6 +108,12 @@ module ActiveRecord
|
|
|
110
108
|
# assert_raise(StandardError) { web_sites(:reddit) }
|
|
111
109
|
# end
|
|
112
110
|
#
|
|
111
|
+
# If the model names conflicts with a +TestCase+ methods, you can use the generic +fixture+ accessor
|
|
112
|
+
#
|
|
113
|
+
# test "generic find" do
|
|
114
|
+
# assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
|
|
115
|
+
# end
|
|
116
|
+
#
|
|
113
117
|
# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
|
|
114
118
|
# following tests:
|
|
115
119
|
#
|
|
@@ -238,10 +242,10 @@ module ActiveRecord
|
|
|
238
242
|
# and one for the humans. Why don't we generate the primary key instead?
|
|
239
243
|
# Hashing each fixture's label yields a consistent ID:
|
|
240
244
|
#
|
|
241
|
-
# george: # generated id:
|
|
245
|
+
# george: # generated id: 380982691
|
|
242
246
|
# name: George the Monkey
|
|
243
247
|
#
|
|
244
|
-
# reginald: # generated id:
|
|
248
|
+
# reginald: # generated id: 41001176
|
|
245
249
|
# name: Reginald the Pirate
|
|
246
250
|
#
|
|
247
251
|
# Active Record looks at the fixture's model class, discovers the correct
|
|
@@ -492,8 +496,8 @@ module ActiveRecord
|
|
|
492
496
|
# # app/models/book_orders.rb
|
|
493
497
|
# class BookOrder < ApplicationRecord
|
|
494
498
|
# self.primary_key = [:shop_id, :id]
|
|
495
|
-
# belongs_to :order,
|
|
496
|
-
# belongs_to :book,
|
|
499
|
+
# belongs_to :order, foreign_key: [:shop_id, :order_id]
|
|
500
|
+
# belongs_to :book, foreign_key: [:author_id, :book_id]
|
|
497
501
|
# end
|
|
498
502
|
#
|
|
499
503
|
# <code></code>
|
|
@@ -553,24 +557,24 @@ module ActiveRecord
|
|
|
553
557
|
@@all_cached_fixtures.clear
|
|
554
558
|
end
|
|
555
559
|
|
|
556
|
-
def
|
|
557
|
-
@@all_cached_fixtures[
|
|
560
|
+
def cache_for_connection_pool(connection_pool)
|
|
561
|
+
@@all_cached_fixtures[connection_pool]
|
|
558
562
|
end
|
|
559
563
|
|
|
560
|
-
def fixture_is_cached?(
|
|
561
|
-
|
|
564
|
+
def fixture_is_cached?(connection_pool, table_name)
|
|
565
|
+
cache_for_connection_pool(connection_pool)[table_name]
|
|
562
566
|
end
|
|
563
567
|
|
|
564
|
-
def cached_fixtures(
|
|
568
|
+
def cached_fixtures(connection_pool, keys_to_fetch = nil)
|
|
565
569
|
if keys_to_fetch
|
|
566
|
-
|
|
570
|
+
cache_for_connection_pool(connection_pool).values_at(*keys_to_fetch)
|
|
567
571
|
else
|
|
568
|
-
|
|
572
|
+
cache_for_connection_pool(connection_pool).values
|
|
569
573
|
end
|
|
570
574
|
end
|
|
571
575
|
|
|
572
|
-
def cache_fixtures(
|
|
573
|
-
|
|
576
|
+
def cache_fixtures(connection_pool, fixtures_map)
|
|
577
|
+
cache_for_connection_pool(connection_pool).update(fixtures_map)
|
|
574
578
|
end
|
|
575
579
|
|
|
576
580
|
def instantiate_fixtures(object, fixture_set, load_instances = true)
|
|
@@ -588,15 +592,13 @@ module ActiveRecord
|
|
|
588
592
|
end
|
|
589
593
|
end
|
|
590
594
|
|
|
591
|
-
def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base
|
|
595
|
+
def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
|
|
592
596
|
fixture_set_names = Array(fixture_set_names).map(&:to_s)
|
|
593
597
|
class_names.stringify_keys!
|
|
594
598
|
|
|
595
|
-
|
|
596
|
-
connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
|
|
597
|
-
|
|
599
|
+
connection_pool = config.connection_pool
|
|
598
600
|
fixture_files_to_read = fixture_set_names.reject do |fs_name|
|
|
599
|
-
fixture_is_cached?(
|
|
601
|
+
fixture_is_cached?(connection_pool, fs_name)
|
|
600
602
|
end
|
|
601
603
|
|
|
602
604
|
if fixture_files_to_read.any?
|
|
@@ -604,11 +606,11 @@ module ActiveRecord
|
|
|
604
606
|
Array(fixtures_directories),
|
|
605
607
|
fixture_files_to_read,
|
|
606
608
|
class_names,
|
|
607
|
-
|
|
609
|
+
connection_pool,
|
|
608
610
|
)
|
|
609
|
-
cache_fixtures(
|
|
611
|
+
cache_fixtures(connection_pool, fixtures_map)
|
|
610
612
|
end
|
|
611
|
-
cached_fixtures(
|
|
613
|
+
cached_fixtures(connection_pool, fixture_set_names)
|
|
612
614
|
end
|
|
613
615
|
|
|
614
616
|
# Returns a consistent, platform-independent identifier for +label+.
|
|
@@ -641,7 +643,7 @@ module ActiveRecord
|
|
|
641
643
|
end
|
|
642
644
|
|
|
643
645
|
private
|
|
644
|
-
def read_and_insert(fixtures_directories, fixture_files, class_names,
|
|
646
|
+
def read_and_insert(fixtures_directories, fixture_files, class_names, connection_pool) # :nodoc:
|
|
645
647
|
fixtures_map = {}
|
|
646
648
|
directory_glob = "{#{fixtures_directories.join(",")}}"
|
|
647
649
|
fixture_sets = fixture_files.map do |fixture_set_name|
|
|
@@ -655,21 +657,21 @@ module ActiveRecord
|
|
|
655
657
|
end
|
|
656
658
|
update_all_loaded_fixtures(fixtures_map)
|
|
657
659
|
|
|
658
|
-
insert(fixture_sets,
|
|
660
|
+
insert(fixture_sets, connection_pool)
|
|
659
661
|
|
|
660
662
|
fixtures_map
|
|
661
663
|
end
|
|
662
664
|
|
|
663
|
-
def insert(fixture_sets,
|
|
664
|
-
|
|
665
|
+
def insert(fixture_sets, connection_pool) # :nodoc:
|
|
666
|
+
fixture_sets_by_pool = fixture_sets.group_by do |fixture_set|
|
|
665
667
|
if fixture_set.model_class
|
|
666
|
-
fixture_set.model_class.
|
|
668
|
+
fixture_set.model_class.connection_pool
|
|
667
669
|
else
|
|
668
|
-
|
|
670
|
+
connection_pool
|
|
669
671
|
end
|
|
670
672
|
end
|
|
671
673
|
|
|
672
|
-
|
|
674
|
+
fixture_sets_by_pool.each do |pool, set|
|
|
673
675
|
table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
|
|
674
676
|
|
|
675
677
|
set.each do |fixture_set|
|
|
@@ -678,13 +680,15 @@ module ActiveRecord
|
|
|
678
680
|
end
|
|
679
681
|
end
|
|
680
682
|
|
|
681
|
-
|
|
683
|
+
pool.with_connection do |conn|
|
|
684
|
+
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
|
682
685
|
|
|
683
|
-
|
|
686
|
+
check_all_foreign_keys_valid!(conn)
|
|
684
687
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
+
# Cap primary key sequences to max(pk).
|
|
689
|
+
if conn.respond_to?(:reset_pk_sequence!)
|
|
690
|
+
set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
|
|
691
|
+
end
|
|
688
692
|
end
|
|
689
693
|
end
|
|
690
694
|
end
|