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
|
@@ -41,8 +41,10 @@ module ActiveRecord
|
|
|
41
41
|
cattr_accessor :unique_ignore_pattern, default: /^uniq_rails_[0-9a-f]{10}$/
|
|
42
42
|
|
|
43
43
|
class << self
|
|
44
|
-
def dump(
|
|
45
|
-
connection
|
|
44
|
+
def dump(pool = ActiveRecord::Base.connection_pool, stream = $stdout, config = ActiveRecord::Base)
|
|
45
|
+
pool.with_connection do |connection|
|
|
46
|
+
connection.create_schema_dumper(generate_options(config)).dump(stream)
|
|
47
|
+
end
|
|
46
48
|
stream
|
|
47
49
|
end
|
|
48
50
|
|
|
@@ -61,6 +63,7 @@ module ActiveRecord
|
|
|
61
63
|
extensions(stream)
|
|
62
64
|
types(stream)
|
|
63
65
|
tables(stream)
|
|
66
|
+
virtual_tables(stream)
|
|
64
67
|
trailer(stream)
|
|
65
68
|
stream
|
|
66
69
|
end
|
|
@@ -70,7 +73,7 @@ module ActiveRecord
|
|
|
70
73
|
|
|
71
74
|
def initialize(connection, options = {})
|
|
72
75
|
@connection = connection
|
|
73
|
-
@version = connection.migration_context.current_version rescue nil
|
|
76
|
+
@version = connection.pool.migration_context.current_version rescue nil
|
|
74
77
|
@options = options
|
|
75
78
|
@ignore_tables = [
|
|
76
79
|
ActiveRecord::Base.schema_migrations_table_name,
|
|
@@ -124,18 +127,31 @@ module ActiveRecord
|
|
|
124
127
|
def schemas(stream)
|
|
125
128
|
end
|
|
126
129
|
|
|
130
|
+
# virtual tables are only supported by SQLite
|
|
131
|
+
def virtual_tables(stream)
|
|
132
|
+
end
|
|
133
|
+
|
|
127
134
|
def tables(stream)
|
|
128
135
|
sorted_tables = @connection.tables.sort
|
|
129
136
|
|
|
130
|
-
sorted_tables.
|
|
131
|
-
|
|
137
|
+
not_ignored_tables = sorted_tables.reject { |table_name| ignored?(table_name) }
|
|
138
|
+
|
|
139
|
+
not_ignored_tables.each_with_index do |table_name, index|
|
|
140
|
+
table(table_name, stream)
|
|
141
|
+
stream.puts if index < not_ignored_tables.count - 1
|
|
132
142
|
end
|
|
133
143
|
|
|
134
144
|
# dump foreign keys at the end to make sure all dependent tables exist.
|
|
135
|
-
if @connection.
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
if @connection.supports_foreign_keys?
|
|
146
|
+
foreign_keys_stream = StringIO.new
|
|
147
|
+
not_ignored_tables.each do |tbl|
|
|
148
|
+
foreign_keys(tbl, foreign_keys_stream)
|
|
138
149
|
end
|
|
150
|
+
|
|
151
|
+
foreign_keys_string = foreign_keys_stream.string
|
|
152
|
+
stream.puts if foreign_keys_string.length > 0
|
|
153
|
+
|
|
154
|
+
stream.print foreign_keys_string
|
|
139
155
|
end
|
|
140
156
|
end
|
|
141
157
|
|
|
@@ -149,7 +165,7 @@ module ActiveRecord
|
|
|
149
165
|
# first dump primary key column
|
|
150
166
|
pk = @connection.primary_key(table)
|
|
151
167
|
|
|
152
|
-
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
|
|
168
|
+
tbl.print " create_table #{relation_name(remove_prefix_and_suffix(table)).inspect}"
|
|
153
169
|
|
|
154
170
|
case pk
|
|
155
171
|
when String
|
|
@@ -176,7 +192,7 @@ module ActiveRecord
|
|
|
176
192
|
tbl.puts ", force: :cascade do |t|"
|
|
177
193
|
|
|
178
194
|
# then dump all non-primary key columns
|
|
179
|
-
columns.each do |column|
|
|
195
|
+
columns.sort_by(&:name).each do |column|
|
|
180
196
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
|
|
181
197
|
next if column.name == pk
|
|
182
198
|
|
|
@@ -191,12 +207,16 @@ module ActiveRecord
|
|
|
191
207
|
end
|
|
192
208
|
|
|
193
209
|
indexes_in_create(table, tbl)
|
|
194
|
-
check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
|
210
|
+
remaining = check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
|
195
211
|
exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
|
|
196
212
|
unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
|
|
197
213
|
|
|
198
214
|
tbl.puts " end"
|
|
199
|
-
|
|
215
|
+
|
|
216
|
+
if remaining
|
|
217
|
+
tbl.puts
|
|
218
|
+
tbl.print remaining.string
|
|
219
|
+
end
|
|
200
220
|
|
|
201
221
|
stream.print tbl.string
|
|
202
222
|
rescue => e
|
|
@@ -212,8 +232,8 @@ module ActiveRecord
|
|
|
212
232
|
def indexes(table, stream)
|
|
213
233
|
if (indexes = @connection.indexes(table)).any?
|
|
214
234
|
add_index_statements = indexes.map do |index|
|
|
215
|
-
table_name = remove_prefix_and_suffix(index.table)
|
|
216
|
-
" add_index #{([table_name] + index_parts(index)).join(', ')}"
|
|
235
|
+
table_name = remove_prefix_and_suffix(index.table)
|
|
236
|
+
" add_index #{([relation_name(table_name).inspect] + index_parts(index)).join(', ')}"
|
|
217
237
|
end
|
|
218
238
|
|
|
219
239
|
stream.puts add_index_statements.sort.join("\n")
|
|
@@ -257,35 +277,49 @@ module ActiveRecord
|
|
|
257
277
|
index_parts << "nulls_not_distinct: #{index.nulls_not_distinct.inspect}" if index.nulls_not_distinct
|
|
258
278
|
index_parts << "type: #{index.type.inspect}" if index.type
|
|
259
279
|
index_parts << "comment: #{index.comment.inspect}" if index.comment
|
|
280
|
+
index_parts << "enabled: #{index.enabled.inspect}" if @connection.supports_disabling_indexes? && index.disabled?
|
|
260
281
|
index_parts
|
|
261
282
|
end
|
|
262
283
|
|
|
263
284
|
def check_constraints_in_create(table, stream)
|
|
264
285
|
if (check_constraints = @connection.check_constraints(table)).any?
|
|
265
|
-
|
|
266
|
-
parts = [
|
|
267
|
-
"t.check_constraint #{check_constraint.expression.inspect}"
|
|
268
|
-
]
|
|
286
|
+
check_valid, check_invalid = check_constraints.partition { |chk| chk.validate? }
|
|
269
287
|
|
|
270
|
-
|
|
271
|
-
|
|
288
|
+
unless check_valid.empty?
|
|
289
|
+
check_constraint_statements = check_valid.map do |check|
|
|
290
|
+
" t.check_constraint #{check_parts(check).join(', ')}"
|
|
272
291
|
end
|
|
273
292
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
" #{parts.join(', ')}"
|
|
293
|
+
stream.puts check_constraint_statements.sort.join("\n")
|
|
277
294
|
end
|
|
278
295
|
|
|
279
|
-
|
|
296
|
+
unless check_invalid.empty?
|
|
297
|
+
remaining = StringIO.new
|
|
298
|
+
table_name = remove_prefix_and_suffix(table).inspect
|
|
299
|
+
|
|
300
|
+
add_check_constraint_statements = check_invalid.map do |check|
|
|
301
|
+
" add_check_constraint #{([table_name] + check_parts(check)).join(', ')}"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
remaining.puts add_check_constraint_statements.sort.join("\n")
|
|
305
|
+
remaining
|
|
306
|
+
end
|
|
280
307
|
end
|
|
281
308
|
end
|
|
282
309
|
|
|
310
|
+
def check_parts(check)
|
|
311
|
+
check_parts = [ check.expression.inspect ]
|
|
312
|
+
check_parts << "name: #{check.name.inspect}" if check.export_name_on_schema_dump?
|
|
313
|
+
check_parts << "validate: #{check.validate?.inspect}" unless check.validate?
|
|
314
|
+
check_parts
|
|
315
|
+
end
|
|
316
|
+
|
|
283
317
|
def foreign_keys(table, stream)
|
|
284
318
|
if (foreign_keys = @connection.foreign_keys(table)).any?
|
|
285
319
|
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
|
286
320
|
parts = [
|
|
287
|
-
|
|
288
|
-
remove_prefix_and_suffix(foreign_key.to_table).inspect,
|
|
321
|
+
relation_name(remove_prefix_and_suffix(foreign_key.from_table)).inspect,
|
|
322
|
+
relation_name(remove_prefix_and_suffix(foreign_key.to_table)).inspect,
|
|
289
323
|
]
|
|
290
324
|
|
|
291
325
|
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
|
|
@@ -296,16 +330,13 @@ module ActiveRecord
|
|
|
296
330
|
parts << "primary_key: #{foreign_key.primary_key.inspect}"
|
|
297
331
|
end
|
|
298
332
|
|
|
299
|
-
if foreign_key.export_name_on_schema_dump?
|
|
300
|
-
parts << "name: #{foreign_key.name.inspect}"
|
|
301
|
-
end
|
|
302
|
-
|
|
333
|
+
parts << "name: #{foreign_key.name.inspect}" if foreign_key.export_name_on_schema_dump?
|
|
303
334
|
parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
|
|
304
335
|
parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
|
|
305
336
|
parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
|
|
306
337
|
parts << "validate: #{foreign_key.validate?.inspect}" unless foreign_key.validate?
|
|
307
338
|
|
|
308
|
-
" #{parts.join(', ')}"
|
|
339
|
+
" add_foreign_key #{parts.join(', ')}"
|
|
309
340
|
end
|
|
310
341
|
|
|
311
342
|
stream.puts add_foreign_key_statements.sort.join("\n")
|
|
@@ -330,6 +361,10 @@ module ActiveRecord
|
|
|
330
361
|
end
|
|
331
362
|
end
|
|
332
363
|
|
|
364
|
+
def relation_name(name)
|
|
365
|
+
name
|
|
366
|
+
end
|
|
367
|
+
|
|
333
368
|
def remove_prefix_and_suffix(table)
|
|
334
369
|
# This method appears at the top when profiling active_record test cases run.
|
|
335
370
|
# Avoid costly calculation when there are no prefix and suffix.
|
|
@@ -9,29 +9,36 @@ module ActiveRecord
|
|
|
9
9
|
class NullSchemaMigration # :nodoc:
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
attr_reader :
|
|
12
|
+
attr_reader :arel_table
|
|
13
13
|
|
|
14
|
-
def initialize(
|
|
15
|
-
@
|
|
14
|
+
def initialize(pool)
|
|
15
|
+
@pool = pool
|
|
16
16
|
@arel_table = Arel::Table.new(table_name)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def create_version(version)
|
|
20
20
|
im = Arel::InsertManager.new(arel_table)
|
|
21
21
|
im.insert(arel_table[primary_key] => version)
|
|
22
|
-
|
|
22
|
+
@pool.with_connection do |connection|
|
|
23
|
+
connection.insert(im, "#{self.class} Create", primary_key, version)
|
|
24
|
+
end
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def delete_version(version)
|
|
26
28
|
dm = Arel::DeleteManager.new(arel_table)
|
|
27
29
|
dm.wheres = [arel_table[primary_key].eq(version)]
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
@pool.with_connection do |connection|
|
|
32
|
+
connection.delete(dm, "#{self.class} Destroy")
|
|
33
|
+
end
|
|
30
34
|
end
|
|
31
35
|
|
|
32
36
|
def delete_all_versions
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
# Eagerly check in connection to avoid checking in/out many times in the called method.
|
|
38
|
+
@pool.with_connection do
|
|
39
|
+
versions.each do |version|
|
|
40
|
+
delete_version(version)
|
|
41
|
+
end
|
|
35
42
|
end
|
|
36
43
|
end
|
|
37
44
|
|
|
@@ -44,15 +51,19 @@ module ActiveRecord
|
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
def create_table
|
|
47
|
-
|
|
48
|
-
connection.
|
|
49
|
-
|
|
54
|
+
@pool.with_connection do |connection|
|
|
55
|
+
unless connection.table_exists?(table_name)
|
|
56
|
+
connection.create_table(table_name, id: false) do |t|
|
|
57
|
+
t.string :version, **connection.internal_string_options_for_primary_key
|
|
58
|
+
end
|
|
50
59
|
end
|
|
51
60
|
end
|
|
52
61
|
end
|
|
53
62
|
|
|
54
63
|
def drop_table
|
|
55
|
-
|
|
64
|
+
@pool.with_connection do |connection|
|
|
65
|
+
connection.drop_table table_name, if_exists: true
|
|
66
|
+
end
|
|
56
67
|
end
|
|
57
68
|
|
|
58
69
|
def normalize_migration_number(number)
|
|
@@ -68,7 +79,9 @@ module ActiveRecord
|
|
|
68
79
|
sm.project(arel_table[primary_key])
|
|
69
80
|
sm.order(arel_table[primary_key].asc)
|
|
70
81
|
|
|
71
|
-
|
|
82
|
+
@pool.with_connection do |connection|
|
|
83
|
+
connection.select_values(sm, "#{self.class} Load")
|
|
84
|
+
end
|
|
72
85
|
end
|
|
73
86
|
|
|
74
87
|
def integer_versions
|
|
@@ -79,11 +92,15 @@ module ActiveRecord
|
|
|
79
92
|
sm = Arel::SelectManager.new(arel_table)
|
|
80
93
|
sm.project(*Arel::Nodes::Count.new([Arel.star]))
|
|
81
94
|
|
|
82
|
-
|
|
95
|
+
@pool.with_connection do |connection|
|
|
96
|
+
connection.select_values(sm, "#{self.class} Count").first
|
|
97
|
+
end
|
|
83
98
|
end
|
|
84
99
|
|
|
85
100
|
def table_exists?
|
|
86
|
-
connection
|
|
101
|
+
@pool.with_connection do |connection|
|
|
102
|
+
connection.data_source_exists?(table_name)
|
|
103
|
+
end
|
|
87
104
|
end
|
|
88
105
|
end
|
|
89
106
|
end
|
|
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
|
23
23
|
scope = current_scope
|
|
24
24
|
|
|
25
25
|
if scope
|
|
26
|
-
if self == scope.
|
|
26
|
+
if self == scope.model
|
|
27
27
|
scope.clone
|
|
28
28
|
else
|
|
29
29
|
relation.merge!(scope)
|
|
@@ -190,7 +190,11 @@ module ActiveRecord
|
|
|
190
190
|
|
|
191
191
|
private
|
|
192
192
|
def singleton_method_added(name)
|
|
193
|
-
|
|
193
|
+
super
|
|
194
|
+
# Most Kernel extends are both singleton and instance methods so
|
|
195
|
+
# respond_to is a fast check, but we don't want to define methods
|
|
196
|
+
# only on the module (ex. Module#name)
|
|
197
|
+
generate_relation_method(name) if Kernel.respond_to?(name) && (Kernel.method_defined?(name) || Kernel.private_method_defined?(name)) && !ActiveRecord::Relation.method_defined?(name)
|
|
194
198
|
end
|
|
195
199
|
end
|
|
196
200
|
end
|
|
@@ -30,13 +30,13 @@ module ActiveRecord
|
|
|
30
30
|
# {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
|
|
31
31
|
# You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
|
|
32
32
|
#
|
|
33
|
-
#
|
|
33
|
+
# ==== Options
|
|
34
34
|
#
|
|
35
|
-
# [
|
|
35
|
+
# [+:length+]
|
|
36
36
|
# Length of the Secure Random, with a minimum of 24 characters. It will
|
|
37
37
|
# default to 24.
|
|
38
38
|
#
|
|
39
|
-
# [
|
|
39
|
+
# [+:on+]
|
|
40
40
|
# The callback when the value is generated. When called with <tt>on:
|
|
41
41
|
# :initialize</tt>, the value is generated in an
|
|
42
42
|
# <tt>after_initialize</tt> callback, otherwise the value will be used
|
|
@@ -6,11 +6,37 @@ module ActiveRecord
|
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
8
|
included do
|
|
9
|
+
class_attribute :_signed_id_verifier, instance_accessor: false, instance_predicate: false
|
|
10
|
+
|
|
9
11
|
##
|
|
10
12
|
# :singleton-method:
|
|
11
13
|
# Set the secret used for the signed id verifier instance when using Active Record outside of \Rails.
|
|
12
14
|
# Within \Rails, this is automatically set using the \Rails application key generator.
|
|
13
15
|
class_attribute :signed_id_verifier_secret, instance_writer: false
|
|
16
|
+
module DeprecateSignedIdVerifierSecret
|
|
17
|
+
def signed_id_verifier_secret=(secret)
|
|
18
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
|
19
|
+
ActiveRecord::Base.signed_id_verifier_secret is deprecated and will be removed in Rails 8.2.
|
|
20
|
+
|
|
21
|
+
If the secret is model-specific, set Model.signed_id_verifier instead.
|
|
22
|
+
|
|
23
|
+
Otherwise, configure Rails.application.message_verifiers (or ActiveRecord.message_verifiers) with the secret.
|
|
24
|
+
MSG
|
|
25
|
+
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
singleton_class.prepend DeprecateSignedIdVerifierSecret
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module RelationMethods # :nodoc:
|
|
33
|
+
def find_signed(...)
|
|
34
|
+
scoping { model.find_signed(...) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def find_signed!(...)
|
|
38
|
+
scoping { model.find_signed!(...) }
|
|
39
|
+
end
|
|
14
40
|
end
|
|
15
41
|
|
|
16
42
|
module ClassMethods
|
|
@@ -39,53 +65,66 @@ module ActiveRecord
|
|
|
39
65
|
#
|
|
40
66
|
# travel_back
|
|
41
67
|
# User.find_signed signed_id, purpose: :password_reset # => User.first
|
|
42
|
-
def find_signed(signed_id, purpose: nil)
|
|
68
|
+
def find_signed(signed_id, purpose: nil, on_rotation: nil)
|
|
43
69
|
raise UnknownPrimaryKey.new(self) if primary_key.nil?
|
|
44
70
|
|
|
45
|
-
|
|
71
|
+
options = { on_rotation: on_rotation }.compact
|
|
72
|
+
if id = signed_id_verifier.verified(signed_id, purpose: combine_signed_id_purposes(purpose), **options)
|
|
46
73
|
find_by primary_key => id
|
|
47
74
|
end
|
|
48
75
|
end
|
|
49
76
|
|
|
50
|
-
# Works like find_signed, but will raise an
|
|
77
|
+
# Works like find_signed, but will raise an ActiveSupport::MessageVerifier::InvalidSignature
|
|
51
78
|
# exception if the +signed_id+ has either expired, has a purpose mismatch, is for another record,
|
|
52
|
-
# or has been tampered with. It will also raise an
|
|
79
|
+
# or has been tampered with. It will also raise an ActiveRecord::RecordNotFound exception if
|
|
53
80
|
# the valid signed id can't find a record.
|
|
54
81
|
#
|
|
55
|
-
#
|
|
82
|
+
# ==== Examples
|
|
56
83
|
#
|
|
57
84
|
# User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature
|
|
58
85
|
#
|
|
59
86
|
# signed_id = User.first.signed_id
|
|
60
87
|
# User.first.destroy
|
|
61
88
|
# User.find_signed! signed_id # => ActiveRecord::RecordNotFound
|
|
62
|
-
def find_signed!(signed_id, purpose: nil)
|
|
63
|
-
|
|
89
|
+
def find_signed!(signed_id, purpose: nil, on_rotation: nil)
|
|
90
|
+
options = { on_rotation: on_rotation }.compact
|
|
91
|
+
if id = signed_id_verifier.verify(signed_id, purpose: combine_signed_id_purposes(purpose), **options)
|
|
64
92
|
find(id)
|
|
65
93
|
end
|
|
66
94
|
end
|
|
67
95
|
|
|
68
|
-
# The verifier instance that all signed ids are generated and verified from. By default, it'll be initialized
|
|
69
|
-
# with the class-level +signed_id_verifier_secret+, which within \Rails comes from the
|
|
70
|
-
# Rails.application.key_generator. By default, it's SHA256 for the digest and JSON for the serialization.
|
|
71
96
|
def signed_id_verifier
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
if signed_id_verifier_secret
|
|
98
|
+
@signed_id_verifier ||= begin
|
|
99
|
+
secret = signed_id_verifier_secret
|
|
100
|
+
secret = secret.call if secret.respond_to?(:call)
|
|
101
|
+
|
|
102
|
+
if secret.nil?
|
|
103
|
+
raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed IDs"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON, url_safe: true
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
return _signed_id_verifier if _signed_id_verifier
|
|
110
|
+
|
|
111
|
+
if ActiveRecord.message_verifiers.nil?
|
|
112
|
+
raise "You must set ActiveRecord.message_verifiers to use signed IDs"
|
|
80
113
|
end
|
|
114
|
+
|
|
115
|
+
ActiveRecord.message_verifiers["active_record/signed_id"]
|
|
81
116
|
end
|
|
82
117
|
end
|
|
83
118
|
|
|
84
119
|
# Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different
|
|
85
120
|
# verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare
|
|
86
|
-
# your custom verifier for that in advance. See
|
|
121
|
+
# your custom verifier for that in advance. See ActiveSupport::MessageVerifier for details.
|
|
87
122
|
def signed_id_verifier=(verifier)
|
|
88
|
-
|
|
123
|
+
if signed_id_verifier_secret
|
|
124
|
+
@signed_id_verifier = verifier
|
|
125
|
+
else
|
|
126
|
+
self._signed_id_verifier = verifier
|
|
127
|
+
end
|
|
89
128
|
end
|
|
90
129
|
|
|
91
130
|
# :nodoc:
|
|
@@ -96,7 +135,16 @@ module ActiveRecord
|
|
|
96
135
|
|
|
97
136
|
|
|
98
137
|
# Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
|
|
138
|
+
#
|
|
99
139
|
# This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
|
|
140
|
+
# However, as with any message signed with a +ActiveSupport::MessageVerifier+,
|
|
141
|
+
# {the signed id is not encrypted}[link:classes/ActiveSupport/MessageVerifier.html#class-ActiveSupport::MessageVerifier-label-Signing+is+not+encryption].
|
|
142
|
+
# It's just encoded and protected against tampering.
|
|
143
|
+
#
|
|
144
|
+
# This means that the ID can be decoded by anyone; however, if tampered with (so to point to a different ID),
|
|
145
|
+
# the cryptographic signature will no longer match, and the signed id will be considered invalid and return nil
|
|
146
|
+
# when passed to +find_signed+ (or raise with +find_signed!+).
|
|
147
|
+
#
|
|
100
148
|
# It can furthermore be set to expire (the default is not to expire), and scoped down with a specific purpose.
|
|
101
149
|
# If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
|
|
102
150
|
# record. If a purpose is set, this too must match.
|
|
@@ -4,14 +4,14 @@ module ActiveRecord
|
|
|
4
4
|
# Statement cache is used to cache a single statement in order to avoid creating the AST again.
|
|
5
5
|
# Initializing the cache is done by passing the statement in the create block:
|
|
6
6
|
#
|
|
7
|
-
# cache = StatementCache.create(
|
|
7
|
+
# cache = StatementCache.create(ClothingItem.lease_connection) do |params|
|
|
8
8
|
# Book.where(name: "my book").where("author_id > 3")
|
|
9
9
|
# end
|
|
10
10
|
#
|
|
11
11
|
# The cached statement is executed by using the
|
|
12
12
|
# {connection.execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute] method:
|
|
13
13
|
#
|
|
14
|
-
# cache.execute([],
|
|
14
|
+
# cache.execute([], ClothingItem.lease_connection)
|
|
15
15
|
#
|
|
16
16
|
# The relation returned by the block is cached, and for each
|
|
17
17
|
# {execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute]
|
|
@@ -20,19 +20,22 @@ module ActiveRecord
|
|
|
20
20
|
# If you want to cache the statement without the values you can use the +bind+ method of the
|
|
21
21
|
# block parameter.
|
|
22
22
|
#
|
|
23
|
-
# cache = StatementCache.create(
|
|
23
|
+
# cache = StatementCache.create(ClothingItem.lease_connection) do |params|
|
|
24
24
|
# Book.where(name: params.bind)
|
|
25
25
|
# end
|
|
26
26
|
#
|
|
27
27
|
# And pass the bind values as the first argument of +execute+ call.
|
|
28
28
|
#
|
|
29
|
-
# cache.execute(["my book"],
|
|
29
|
+
# cache.execute(["my book"], ClothingItem.lease_connection)
|
|
30
30
|
class StatementCache # :nodoc:
|
|
31
31
|
class Substitute; end # :nodoc:
|
|
32
32
|
|
|
33
33
|
class Query # :nodoc:
|
|
34
|
-
|
|
34
|
+
attr_reader :retryable
|
|
35
|
+
|
|
36
|
+
def initialize(sql, retryable:)
|
|
35
37
|
@sql = sql
|
|
38
|
+
@retryable = retryable
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
def sql_for(binds, connection)
|
|
@@ -41,11 +44,12 @@ module ActiveRecord
|
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
class PartialQuery < Query # :nodoc:
|
|
44
|
-
def initialize(values)
|
|
47
|
+
def initialize(values, retryable:)
|
|
45
48
|
@values = values
|
|
46
49
|
@indexes = values.each_with_index.find_all { |thing, i|
|
|
47
50
|
Substitute === thing
|
|
48
51
|
}.map(&:last)
|
|
52
|
+
@retryable = retryable
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
def sql_for(binds, connection)
|
|
@@ -62,7 +66,7 @@ module ActiveRecord
|
|
|
62
66
|
end
|
|
63
67
|
|
|
64
68
|
class PartialQueryCollector
|
|
65
|
-
attr_accessor :preparable
|
|
69
|
+
attr_accessor :preparable, :retryable
|
|
66
70
|
|
|
67
71
|
def initialize
|
|
68
72
|
@parts = []
|
|
@@ -74,13 +78,13 @@ module ActiveRecord
|
|
|
74
78
|
self
|
|
75
79
|
end
|
|
76
80
|
|
|
77
|
-
def add_bind(obj)
|
|
81
|
+
def add_bind(obj, &)
|
|
78
82
|
@binds << obj
|
|
79
83
|
@parts << Substitute.new
|
|
80
84
|
self
|
|
81
85
|
end
|
|
82
86
|
|
|
83
|
-
def add_binds(binds, proc_for_binds = nil)
|
|
87
|
+
def add_binds(binds, proc_for_binds = nil, &)
|
|
84
88
|
@binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
|
|
85
89
|
binds.size.times do |i|
|
|
86
90
|
@parts << ", " unless i == 0
|
|
@@ -94,12 +98,12 @@ module ActiveRecord
|
|
|
94
98
|
end
|
|
95
99
|
end
|
|
96
100
|
|
|
97
|
-
def self.query(
|
|
98
|
-
Query.new(
|
|
101
|
+
def self.query(...)
|
|
102
|
+
Query.new(...)
|
|
99
103
|
end
|
|
100
104
|
|
|
101
|
-
def self.partial_query(
|
|
102
|
-
PartialQuery.new(
|
|
105
|
+
def self.partial_query(...)
|
|
106
|
+
PartialQuery.new(...)
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
def self.partial_query_collector
|
|
@@ -133,23 +137,26 @@ module ActiveRecord
|
|
|
133
137
|
relation = (callable || block).call Params.new
|
|
134
138
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
|
135
139
|
bind_map = BindMap.new(binds)
|
|
136
|
-
new(query_builder, bind_map, relation.
|
|
140
|
+
new(query_builder, bind_map, relation.model)
|
|
137
141
|
end
|
|
138
142
|
|
|
139
|
-
def initialize(query_builder, bind_map,
|
|
143
|
+
def initialize(query_builder, bind_map, model)
|
|
140
144
|
@query_builder = query_builder
|
|
141
145
|
@bind_map = bind_map
|
|
142
|
-
@
|
|
146
|
+
@model = model
|
|
143
147
|
end
|
|
144
148
|
|
|
145
|
-
def execute(params, connection, &block)
|
|
146
|
-
bind_values = bind_map.bind params
|
|
147
|
-
|
|
148
|
-
sql = query_builder.sql_for bind_values, connection
|
|
149
|
+
def execute(params, connection, async: false, &block)
|
|
150
|
+
bind_values = @bind_map.bind params
|
|
151
|
+
sql = @query_builder.sql_for bind_values, connection
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
if async
|
|
154
|
+
@model.async_find_by_sql(sql, bind_values, preparable: true, allow_retry: @query_builder.retryable, &block)
|
|
155
|
+
else
|
|
156
|
+
@model.find_by_sql(sql, bind_values, preparable: true, allow_retry: @query_builder.retryable, &block)
|
|
157
|
+
end
|
|
151
158
|
rescue ::RangeError
|
|
152
|
-
[]
|
|
159
|
+
async ? Promise.wrap([]) : []
|
|
153
160
|
end
|
|
154
161
|
|
|
155
162
|
def self.unsupported_value?(value)
|
|
@@ -157,8 +164,5 @@ module ActiveRecord
|
|
|
157
164
|
when NilClass, Array, Range, Hash, Relation, Base then true
|
|
158
165
|
end
|
|
159
166
|
end
|
|
160
|
-
|
|
161
|
-
private
|
|
162
|
-
attr_reader :query_builder, :bind_map, :klass
|
|
163
167
|
end
|
|
164
168
|
end
|