activerecord 7.2.2.1 → 8.0.5

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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +523 -677
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -0
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +34 -10
  7. data/lib/active_record/associations/belongs_to_association.rb +18 -2
  8. data/lib/active_record/associations/builder/association.rb +7 -6
  9. data/lib/active_record/associations/collection_association.rb +10 -8
  10. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  11. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  12. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  13. data/lib/active_record/associations/join_dependency.rb +2 -2
  14. data/lib/active_record/associations/preloader/association.rb +2 -2
  15. data/lib/active_record/associations/preloader/batch.rb +7 -1
  16. data/lib/active_record/associations/singular_association.rb +8 -3
  17. data/lib/active_record/associations.rb +34 -4
  18. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  19. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  20. data/lib/active_record/attribute_methods/query.rb +34 -0
  21. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  22. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  23. data/lib/active_record/attribute_methods.rb +24 -19
  24. data/lib/active_record/attributes.rb +37 -26
  25. data/lib/active_record/autosave_association.rb +91 -39
  26. data/lib/active_record/base.rb +2 -2
  27. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  28. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  29. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  30. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +55 -47
  31. data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
  32. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -8
  33. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  34. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  35. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  36. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +40 -10
  37. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  38. data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -70
  39. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +35 -46
  40. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
  41. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  42. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  43. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  44. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -9
  45. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  46. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
  47. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  48. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  49. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  50. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  51. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  52. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -24
  53. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  54. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +60 -17
  55. data/lib/active_record/connection_adapters/postgresql_adapter.rb +61 -99
  56. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  57. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  58. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  59. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
  60. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  61. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  62. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +10 -1
  63. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +63 -17
  64. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  65. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  66. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
  67. data/lib/active_record/connection_adapters.rb +0 -56
  68. data/lib/active_record/connection_handling.rb +35 -9
  69. data/lib/active_record/core.rb +57 -22
  70. data/lib/active_record/counter_cache.rb +1 -1
  71. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  72. data/lib/active_record/database_configurations/database_config.rb +4 -0
  73. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  74. data/lib/active_record/delegated_type.rb +18 -18
  75. data/lib/active_record/encryption/config.rb +3 -1
  76. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  77. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  78. data/lib/active_record/encryption/encryptor.rb +37 -28
  79. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  80. data/lib/active_record/encryption/scheme.rb +8 -1
  81. data/lib/active_record/enum.rb +22 -34
  82. data/lib/active_record/errors.rb +16 -8
  83. data/lib/active_record/fixture_set/table_row.rb +19 -2
  84. data/lib/active_record/fixtures.rb +0 -2
  85. data/lib/active_record/future_result.rb +15 -9
  86. data/lib/active_record/gem_version.rb +4 -4
  87. data/lib/active_record/insert_all.rb +3 -3
  88. data/lib/active_record/locking/optimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +5 -11
  90. data/lib/active_record/migration/command_recorder.rb +32 -12
  91. data/lib/active_record/migration/compatibility.rb +5 -2
  92. data/lib/active_record/migration.rb +40 -43
  93. data/lib/active_record/model_schema.rb +3 -4
  94. data/lib/active_record/nested_attributes.rb +4 -6
  95. data/lib/active_record/persistence.rb +128 -130
  96. data/lib/active_record/query_logs.rb +106 -50
  97. data/lib/active_record/query_logs_formatter.rb +17 -28
  98. data/lib/active_record/querying.rb +12 -12
  99. data/lib/active_record/railtie.rb +4 -28
  100. data/lib/active_record/railties/databases.rake +11 -34
  101. data/lib/active_record/reflection.rb +18 -21
  102. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  103. data/lib/active_record/relation/batches.rb +132 -72
  104. data/lib/active_record/relation/calculations.rb +71 -65
  105. data/lib/active_record/relation/delegation.rb +25 -14
  106. data/lib/active_record/relation/finder_methods.rb +32 -31
  107. data/lib/active_record/relation/merger.rb +8 -8
  108. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  109. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
  110. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  111. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  112. data/lib/active_record/relation/predicate_builder.rb +13 -0
  113. data/lib/active_record/relation/query_attribute.rb +1 -1
  114. data/lib/active_record/relation/query_methods.rb +121 -70
  115. data/lib/active_record/relation/spawn_methods.rb +7 -7
  116. data/lib/active_record/relation/where_clause.rb +9 -3
  117. data/lib/active_record/relation.rb +95 -67
  118. data/lib/active_record/result.rb +66 -4
  119. data/lib/active_record/sanitization.rb +7 -6
  120. data/lib/active_record/schema_dumper.rb +34 -11
  121. data/lib/active_record/schema_migration.rb +2 -1
  122. data/lib/active_record/scoping/named.rb +5 -2
  123. data/lib/active_record/secure_token.rb +3 -3
  124. data/lib/active_record/signed_id.rb +7 -6
  125. data/lib/active_record/statement_cache.rb +14 -14
  126. data/lib/active_record/store.rb +7 -3
  127. data/lib/active_record/table_metadata.rb +1 -3
  128. data/lib/active_record/tasks/database_tasks.rb +69 -60
  129. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  130. data/lib/active_record/tasks/postgresql_database_tasks.rb +9 -1
  131. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  132. data/lib/active_record/test_databases.rb +1 -1
  133. data/lib/active_record/test_fixtures.rb +12 -0
  134. data/lib/active_record/token_for.rb +1 -1
  135. data/lib/active_record/transactions.rb +8 -7
  136. data/lib/active_record/type/serialized.rb +5 -0
  137. data/lib/active_record/validations/uniqueness.rb +8 -8
  138. data/lib/active_record.rb +22 -49
  139. data/lib/arel/collectors/bind.rb +2 -2
  140. data/lib/arel/collectors/sql_string.rb +1 -1
  141. data/lib/arel/collectors/substitute_binds.rb +2 -2
  142. data/lib/arel/crud.rb +2 -0
  143. data/lib/arel/delete_manager.rb +5 -0
  144. data/lib/arel/nodes/binary.rb +1 -1
  145. data/lib/arel/nodes/delete_statement.rb +4 -2
  146. data/lib/arel/nodes/node.rb +1 -1
  147. data/lib/arel/nodes/sql_literal.rb +1 -1
  148. data/lib/arel/nodes/update_statement.rb +4 -2
  149. data/lib/arel/predications.rb +1 -3
  150. data/lib/arel/select_manager.rb +6 -2
  151. data/lib/arel/table.rb +3 -7
  152. data/lib/arel/update_manager.rb +5 -0
  153. data/lib/arel/visitors/dot.rb +2 -0
  154. data/lib/arel/visitors/to_sql.rb +3 -1
  155. metadata +11 -15
  156. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -27,6 +27,7 @@ module ActiveRecord
27
27
  col["name"]
28
28
  end
29
29
 
30
+ where = where.sub(/\s*\/\*.*\*\/\z/, "") if where
30
31
  orders = {}
31
32
 
32
33
  if columns.any?(&:nil?) # index created with an expression
@@ -74,6 +75,7 @@ module ActiveRecord
74
75
  Base.pluralize_table_names ? table.pluralize : table
75
76
  end
76
77
  table = strip_table_name_prefix_and_suffix(table)
78
+ options = options.slice(*fk.options.keys)
77
79
  fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
78
80
  fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
79
81
  end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
@@ -82,6 +84,10 @@ module ActiveRecord
82
84
  alter_table(from_table, foreign_keys)
83
85
  end
84
86
 
87
+ def virtual_table_exists?(table_name)
88
+ query_values(data_source_sql(table_name, type: "VIRTUAL TABLE"), "SCHEMA").any?
89
+ end
90
+
85
91
  def check_constraints(table_name)
86
92
  table_sql = query_value(<<-SQL, "SCHEMA")
87
93
  SELECT sql
@@ -176,7 +182,8 @@ module ActiveRecord
176
182
  scope = quoted_scope(name, type: type)
177
183
  scope[:type] ||= "'table','view'"
178
184
 
179
- sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
185
+ sql = +"SELECT name FROM pragma_table_list WHERE schema <> 'temp'"
186
+ sql << " AND name NOT IN ('sqlite_sequence', 'sqlite_schema')"
180
187
  sql << " AND name = #{scope[:name]}" if scope[:name]
181
188
  sql << " AND type IN (#{scope[:type]})"
182
189
  sql
@@ -189,6 +196,8 @@ module ActiveRecord
189
196
  "'table'"
190
197
  when "VIEW"
191
198
  "'view'"
199
+ when "VIRTUAL TABLE"
200
+ "'virtual'"
192
201
  end
193
202
  scope = {}
194
203
  scope[:name] = quote(name) if name
@@ -11,9 +11,12 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
11
11
  require "active_record/connection_adapters/sqlite3/schema_dumper"
12
12
  require "active_record/connection_adapters/sqlite3/schema_statements"
13
13
 
14
- gem "sqlite3", ">= 1.4"
14
+ gem "sqlite3", ">= 2.1"
15
15
  require "sqlite3"
16
16
 
17
+ # Suppress the warning that SQLite3 issues when open writable connections are carried across fork()
18
+ SQLite3::ForkSafety.suppress_warnings!
19
+
17
20
  module ActiveRecord
18
21
  module ConnectionAdapters # :nodoc:
19
22
  # = Active Record SQLite3 Adapter
@@ -21,7 +24,7 @@ module ActiveRecord
21
24
  # The SQLite3 adapter works with the sqlite3-ruby drivers
22
25
  # (available as gem from https://rubygems.org/gems/sqlite3).
23
26
  #
24
- # Options:
27
+ # ==== Options
25
28
  #
26
29
  # * <tt>:database</tt> - Path to the database file.
27
30
  class SQLite3Adapter < AbstractAdapter
@@ -45,7 +48,7 @@ module ActiveRecord
45
48
  args << "-header" if options[:header]
46
49
  args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)
47
50
 
48
- find_cmd_and_exec("sqlite3", *args)
51
+ find_cmd_and_exec(ActiveRecord.database_cli[:sqlite], *args)
49
52
  end
50
53
  end
51
54
 
@@ -119,9 +122,14 @@ module ActiveRecord
119
122
  end
120
123
  end
121
124
 
125
+ @last_affected_rows = nil
126
+ @previous_read_uncommitted = nil
122
127
  @config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
123
- @connection_parameters = @config.merge(database: @config[:database].to_s, results_as_hash: true)
124
- @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
128
+ @connection_parameters = @config.merge(
129
+ database: @config[:database].to_s,
130
+ results_as_hash: true,
131
+ default_transaction_mode: :immediate,
132
+ )
125
133
  end
126
134
 
127
135
  def database_exists?
@@ -199,7 +207,12 @@ module ActiveRecord
199
207
  !(@raw_connection.nil? || @raw_connection.closed?)
200
208
  end
201
209
 
202
- alias_method :active?, :connected?
210
+ def active?
211
+ if connected?
212
+ verified!
213
+ true
214
+ end
215
+ end
203
216
 
204
217
  alias :reset! :reconnect!
205
218
 
@@ -278,6 +291,38 @@ module ActiveRecord
278
291
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
279
292
  end
280
293
 
294
+ VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((.*)\)/i
295
+
296
+ # Returns a list of defined virtual tables
297
+ def virtual_tables
298
+ query = <<~SQL
299
+ SELECT name, sql FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL %';
300
+ SQL
301
+
302
+ exec_query(query, "SCHEMA").cast_values.each_with_object({}) do |row, memo|
303
+ table_name, sql = row[0], row[1]
304
+ _, module_name, arguments = sql.match(VIRTUAL_TABLE_REGEX).to_a
305
+ memo[table_name] = [module_name, arguments]
306
+ end.to_a
307
+ end
308
+
309
+ # Creates a virtual table
310
+ #
311
+ # Example:
312
+ # create_virtual_table :emails, :fts5, ['sender', 'title', 'body']
313
+ def create_virtual_table(table_name, module_name, values)
314
+ exec_query "CREATE VIRTUAL TABLE IF NOT EXISTS #{table_name} USING #{module_name} (#{values.join(", ")})"
315
+ end
316
+
317
+ # Drops a virtual table
318
+ #
319
+ # Although this command ignores +module_name+ and +values+,
320
+ # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
321
+ # In that case, +module_name+, +values+ and +options+ will be used by #create_virtual_table.
322
+ def drop_virtual_table(table_name, module_name, values, **options)
323
+ drop_table(table_name)
324
+ end
325
+
281
326
  # Renames a table.
282
327
  #
283
328
  # Example:
@@ -367,7 +412,7 @@ module ActiveRecord
367
412
  end
368
413
  alias :add_belongs_to :add_reference
369
414
 
370
- FK_REGEX = /.*FOREIGN KEY\s+\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
415
+ FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
371
416
  DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
372
417
  def foreign_keys(table_name)
373
418
  # SQLite returns 1 row for each column of composite foreign keys.
@@ -428,10 +473,6 @@ module ActiveRecord
428
473
  @config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
429
474
  end
430
475
 
431
- def use_insert_returning?
432
- @use_insert_returning
433
- end
434
-
435
476
  def get_database_version # :nodoc:
436
477
  SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
437
478
  end
@@ -542,8 +583,8 @@ module ActiveRecord
542
583
  yield definition if block_given?
543
584
  end
544
585
 
545
- transaction do
546
- disable_referential_integrity do
586
+ disable_referential_integrity do
587
+ transaction do
547
588
  move_table(table_name, altered_table_name, options.merge(temporary: true))
548
589
  move_table(altered_table_name, table_name, &caller)
549
590
  end
@@ -661,6 +702,8 @@ module ActiveRecord
661
702
  InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
662
703
  elsif exception.message.match?(/called on a closed database/i)
663
704
  ConnectionNotEstablished.new(exception, connection_pool: @pool)
705
+ elsif exception.is_a?(::SQLite3::BusyException)
706
+ StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
664
707
  else
665
708
  super
666
709
  end
@@ -778,12 +821,15 @@ module ActiveRecord
778
821
  if @config[:timeout] && @config[:retries]
779
822
  raise ArgumentError, "Cannot specify both timeout and retries arguments"
780
823
  elsif @config[:timeout]
781
- @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
824
+ timeout = self.class.type_cast_config_to_integer(@config[:timeout])
825
+ raise TypeError, "timeout must be integer, not #{timeout}" unless timeout.is_a?(Integer)
826
+ @raw_connection.busy_handler_timeout = timeout
782
827
  elsif @config[:retries]
828
+ ActiveRecord.deprecator.warn(<<~MSG)
829
+ The retries option is deprecated and will be removed in Rails 8.1. Use timeout instead.
830
+ MSG
783
831
  retries = self.class.type_cast_config_to_integer(@config[:retries])
784
- raw_connection.busy_handler do |count|
785
- count <= retries
786
- end
832
+ raw_connection.busy_handler { |count| count <= retries }
787
833
  end
788
834
 
789
835
  super
@@ -50,8 +50,10 @@ module ActiveRecord
50
50
  end
51
51
 
52
52
  def delete(key)
53
- dealloc cache[key]
54
- cache.delete(key)
53
+ if stmt = cache.delete(key)
54
+ dealloc(stmt)
55
+ end
56
+ stmt
55
57
  end
56
58
 
57
59
  private
@@ -4,93 +4,63 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module Trilogy
6
6
  module DatabaseStatements
7
- def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
8
- sql = transform_query(sql)
9
- check_if_write_query(sql)
10
- mark_transaction_written_if_write(sql)
11
-
12
- result = raw_execute(sql, name, async: async, allow_retry: allow_retry)
13
- ActiveRecord::Result.new(result.fields, result.to_a)
14
- end
15
-
16
7
  def exec_insert(sql, name, binds, pk = nil, sequence_name = nil, returning: nil) # :nodoc:
17
- sql = transform_query(sql)
18
- check_if_write_query(sql)
19
- mark_transaction_written_if_write(sql)
20
-
21
8
  sql, _binds = sql_for_insert(sql, pk, binds, returning)
22
- raw_execute(sql, name)
23
- end
24
-
25
- def exec_delete(sql, name = nil, binds = []) # :nodoc:
26
- sql = transform_query(sql)
27
- check_if_write_query(sql)
28
- mark_transaction_written_if_write(sql)
29
-
30
- result = raw_execute(to_sql(sql, binds), name)
31
- result.affected_rows
9
+ internal_execute(sql, name)
32
10
  end
33
11
 
34
- alias :exec_update :exec_delete # :nodoc:
35
-
36
12
  private
37
- def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
38
- log(sql, name, async: async) do |notification_payload|
39
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
40
- sync_timezone_changes(conn)
41
- result = conn.query(sql)
42
- while conn.more_results_exist?
43
- conn.next_result
44
- end
45
- verified!
46
- handle_warnings(sql)
47
- notification_payload[:row_count] = result.count
48
- result
49
- end
13
+ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
14
+ reset_multi_statement = if batch && !@config[:multi_statement]
15
+ raw_connection.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
16
+ true
50
17
  end
51
- end
52
18
 
53
- def last_inserted_id(result)
54
- if supports_insert_returning?
55
- super
19
+ # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
20
+ # made since we established the connection
21
+ if default_timezone == :local
22
+ raw_connection.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
56
23
  else
57
- result.last_insert_id
24
+ raw_connection.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
58
25
  end
59
- end
60
26
 
61
- def sync_timezone_changes(conn)
62
- # Sync any changes since connection last established.
63
- if default_timezone == :local
64
- conn.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
65
- else
66
- conn.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
27
+ result = raw_connection.query(sql)
28
+ while raw_connection.more_results_exist?
29
+ raw_connection.next_result
30
+ end
31
+ verified!
32
+ handle_warnings(sql)
33
+ notification_payload[:row_count] = result.count
34
+ result
35
+ ensure
36
+ if reset_multi_statement && active?
37
+ raw_connection.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
67
38
  end
68
39
  end
69
40
 
70
- def execute_batch(statements, name = nil)
71
- statements = statements.map { |sql| transform_query(sql) }
72
- combine_multi_statements(statements).each do |statement|
73
- with_raw_connection do |conn|
74
- raw_execute(statement, name)
75
- end
41
+ def cast_result(result)
42
+ if result.fields.empty?
43
+ ActiveRecord::Result.empty
44
+ else
45
+ ActiveRecord::Result.new(result.fields, result.rows)
76
46
  end
77
47
  end
78
48
 
79
- def multi_statements_enabled?
80
- !!@config[:multi_statement]
49
+ def affected_rows(result)
50
+ result.affected_rows
81
51
  end
82
52
 
83
- def with_multi_statements
84
- if multi_statements_enabled?
85
- return yield
53
+ def last_inserted_id(result)
54
+ if supports_insert_returning?
55
+ super
56
+ else
57
+ result.last_insert_id
86
58
  end
59
+ end
87
60
 
88
- with_raw_connection do |conn|
89
- conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
90
-
91
- yield
92
- ensure
93
- conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF) if active?
61
+ def execute_batch(statements, name = nil, **kwargs)
62
+ combine_multi_statements(statements).each do |statement|
63
+ raw_execute(statement, name, batch: true, **kwargs)
94
64
  end
95
65
  end
96
66
  end
@@ -121,7 +121,7 @@ module ActiveRecord
121
121
  end
122
122
 
123
123
  def active?
124
- connected? && @lock.synchronize { @raw_connection&.ping } || false
124
+ connected? && @lock.synchronize { @raw_connection&.ping; verified! } || false
125
125
  rescue ::Trilogy::Error
126
126
  false
127
127
  end
@@ -149,23 +149,6 @@ module ActiveRecord
149
149
  TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
150
150
  end
151
151
 
152
- def each_hash(result)
153
- return to_enum(:each_hash, result) unless block_given?
154
-
155
- keys = result.fields.map(&:to_sym)
156
- result.rows.each do |row|
157
- hash = {}
158
- idx = 0
159
- row.each do |value|
160
- hash[keys[idx]] = value
161
- idx += 1
162
- end
163
- yield hash
164
- end
165
-
166
- nil
167
- end
168
-
169
152
  def error_number(exception)
170
153
  exception.error_code if exception.respond_to?(:error_code)
171
154
  end
@@ -31,62 +31,6 @@ module ActiveRecord
31
31
  class_name, path_to_adapter = @adapters[adapter_name.to_s]
32
32
 
33
33
  unless class_name
34
- # To provide better error messages for adapters expecting the pre-7.2 adapter registration API, we attempt
35
- # to load the adapter file from the old location which was required by convention, and then raise an error
36
- # describing how to upgrade the adapter to the new API.
37
- legacy_adapter_path = "active_record/connection_adapters/#{adapter_name}_adapter"
38
- legacy_adapter_connection_method_name = "#{adapter_name}_connection".to_sym
39
-
40
- begin
41
- require legacy_adapter_path
42
- # If we reach here it means we found the found a file that may be the legacy adapter and should raise.
43
- if ActiveRecord::ConnectionHandling.method_defined?(legacy_adapter_connection_method_name)
44
- # If we find the connection method then we care certain it is a legacy adapter.
45
- deprecation_message = <<~MSG.squish
46
- Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
47
- Rails 7.2 has changed the way Active Record database adapters are loaded. The adapter needs to be
48
- updated to register itself rather than being loaded by convention.
49
- Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
50
- be modified.
51
- See:
52
- https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
53
- MSG
54
-
55
- exception_message = <<~MSG.squish
56
- Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
57
- Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
58
- be modified.
59
- MSG
60
- else
61
- # If we do not find the connection method we are much less certain it is a legacy adapter. Even though the
62
- # file exists in the location defined by convenntion, it does not necessarily mean that file is supposed
63
- # to define the adapter the legacy way. So raise an error that explains both possibilities.
64
- deprecation_message = <<~MSG.squish
65
- Database configuration specifies nonexistent '#{adapter_name}' adapter.
66
- Available adapters are: #{@adapters.keys.sort.join(", ")}.
67
- Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
68
- adapter gem to your Gemfile if it's not in the list of available adapters.
69
- Rails 7.2 has changed the way Active Record database adapters are loaded. Ensure that the adapter in
70
- the Gemfile is at the latest version. If it is up to date, the adapter may need to be modified.
71
- See:
72
- https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
73
- MSG
74
-
75
- exception_message = <<~MSG.squish
76
- Database configuration specifies nonexistent '#{adapter_name}' adapter.
77
- Available adapters are: #{@adapters.keys.sort.join(", ")}.
78
- Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
79
- adapter gem to your Gemfile and that it is at its latest version. If it is up to date, the adapter may
80
- need to be modified.
81
- MSG
82
- end
83
-
84
- ActiveRecord.deprecator.warn(deprecation_message)
85
- raise AdapterNotFound, exception_message
86
- rescue LoadError => error
87
- # The adapter was not found in the legacy location so fall through to the error handling for a missing adapter.
88
- end
89
-
90
34
  raise AdapterNotFound, <<~MSG.squish
91
35
  Database configuration specifies nonexistent '#{adapter_name}' adapter.
92
36
  Available adapters are: #{@adapters.keys.sort.join(", ")}.
@@ -87,6 +87,8 @@ module ActiveRecord
87
87
 
88
88
  connections = []
89
89
 
90
+ @shard_keys = shards.keys
91
+
90
92
  if shards.empty?
91
93
  shards[:default] = database
92
94
  end
@@ -170,9 +172,23 @@ module ActiveRecord
170
172
  prevent_writes = true if role == ActiveRecord.reading_role
171
173
 
172
174
  append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
173
- yield
174
- ensure
175
- connected_to_stack.pop
175
+ begin
176
+ yield
177
+ ensure
178
+ connected_to_stack.pop
179
+ end
180
+ end
181
+
182
+ # Passes the block to +connected_to+ for every +shard+ the
183
+ # model is configured to connect to (if any), and returns the
184
+ # results in an array.
185
+ #
186
+ # Optionally, +role+ and/or +prevent_writes+ can be passed which
187
+ # will be forwarded to each +connected_to+ call.
188
+ def connected_to_all_shards(role: nil, prevent_writes: false, &blk)
189
+ shard_keys.map do |shard|
190
+ connected_to(shard: shard, role: role, prevent_writes: prevent_writes, &blk)
191
+ end
176
192
  end
177
193
 
178
194
  # Use a specified connection.
@@ -287,7 +303,7 @@ module ActiveRecord
287
303
 
288
304
  # Checkouts a connection from the pool, yield it and then check it back in.
289
305
  # If a connection was already leased via #lease_connection or a parent call to
290
- # #with_connection, that same connection is yieled.
306
+ # #with_connection, that same connection is yielded.
291
307
  # If #lease_connection is called inside the block, the connection won't be checked
292
308
  # back in.
293
309
  # If #connection is called inside the block, the connection won't be checked back in
@@ -359,6 +375,14 @@ module ActiveRecord
359
375
  connection_pool.schema_cache.clear!
360
376
  end
361
377
 
378
+ def shard_keys
379
+ connection_class_for_self.instance_variable_get(:@shard_keys) || []
380
+ end
381
+
382
+ def sharded?
383
+ shard_keys.any?
384
+ end
385
+
362
386
  private
363
387
  def resolve_config_for_connection(config_or_env)
364
388
  raise "Anonymous class is not allowed." unless name
@@ -373,11 +397,13 @@ module ActiveRecord
373
397
  prevent_writes = true if role == ActiveRecord.reading_role
374
398
 
375
399
  append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
376
- return_value = yield
377
- return_value.load if return_value.is_a? ActiveRecord::Relation
378
- return_value
379
- ensure
380
- self.connected_to_stack.pop
400
+ begin
401
+ return_value = yield
402
+ return_value.load if return_value.is_a? ActiveRecord::Relation
403
+ return_value
404
+ ensure
405
+ self.connected_to_stack.pop
406
+ end
381
407
  end
382
408
 
383
409
  def append_to_connected_to_stack(entry)